electron-pinia-sync 1.0.0 → 1.2.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/CONTRIBUTING.md CHANGED
@@ -2,24 +2,52 @@
2
2
 
3
3
  Thank you for your interest in contributing! This guide will help you get started with local development.
4
4
 
5
+ > 🚀 **New to contributing?** Check out our [Quick Start Guide](.github/CONTRIBUTING-QUICK-START.md) for a condensed version!
6
+
7
+ **Important for External Contributors:**
8
+ - The `main` branch is protected - you cannot push directly to it
9
+ - All contributions must go through Pull Requests
10
+ - All CI checks must pass before a PR can be merged
11
+ - Use [Conventional Commits](https://www.conventionalcommits.org/) format for all commits
12
+ - CHANGELOG.md is auto-generated - do not edit it manually
13
+
14
+ ## Quick Start for Contributors
15
+
16
+ 1. **Fork** the repository on GitHub
17
+ 2. **Clone** your fork locally
18
+ 3. **Create a feature branch** from `develop` (or `main`)
19
+ 4. **Make your changes** and commit using conventional commit format
20
+ 5. **Push** to your fork
21
+ 6. **Open a Pull Request** to the original repository
22
+
5
23
  ## Development Setup
6
24
 
7
25
  ### Prerequisites
8
26
 
9
- - Node.js >= 18.0.0
10
- - npm (recommended) or npm/yarn
27
+ - Node.js >= 22.14.0
28
+ - npm >= 10.x
11
29
  - Git
12
30
 
13
31
  ### Initial Setup
14
32
 
15
- 1. **Fork and Clone**
33
+ 1. **Fork the Repository**
34
+
35
+ Visit [https://github.com/simpli-fyi/electron-pinia-sync](https://github.com/simpli-fyi/electron-pinia-sync) and click the "Fork" button.
36
+
37
+ 2. **Clone Your Fork**
16
38
 
17
39
  ```bash
18
- git clone https://github.com/simpli-fyi/electron-pinia-sync.git
40
+ git clone https://github.com/YOUR_USERNAME/electron-pinia-sync.git
19
41
  cd electron-pinia-sync
20
42
  ```
21
43
 
22
- 2. **Install Dependencies**
44
+ 3. **Add Upstream Remote**
45
+
46
+ ```bash
47
+ git remote add upstream https://github.com/simpli-fyi/electron-pinia-sync.git
48
+ ```
49
+
50
+ 4. **Install Dependencies**
23
51
 
24
52
  ```bash
25
53
  npm install
@@ -61,13 +89,6 @@ Build all modules:
61
89
  npm run build
62
90
  ```
63
91
 
64
- Build specific modules:
65
-
66
- ```bash
67
- npm run build:main
68
- npm run build:renderer
69
- npm run build:preload
70
- ```
71
92
 
72
93
  Watch mode for development:
73
94
 
@@ -212,29 +233,72 @@ export function createMainSync(options?: MainSyncOptions): MainSync {
212
233
 
213
234
  ### Before Submitting
214
235
 
215
- 1. **Create a feature branch**
236
+ 1. **Sync Your Fork** (important!)
237
+
238
+ ```bash
239
+ git fetch upstream
240
+ git checkout main
241
+ git merge upstream/main
242
+ git push origin main
243
+ ```
244
+
245
+ 2. **Create a Feature Branch**
216
246
 
217
247
  ```bash
218
248
  git checkout -b feature/your-feature-name
249
+ # or
250
+ git checkout -b fix/your-bug-fix
219
251
  ```
220
252
 
221
- 2. **Make your changes**
253
+ 3. **Make Your Changes**
222
254
  - Write code
223
255
  - Add/update tests
224
- - Update documentation
256
+ - Update documentation (README.md if API changes)
257
+ - Use conventional commit format (see below)
225
258
 
226
- 3. **Run quality checks**
259
+ 4. **Run Quality Checks**
227
260
 
228
261
  ```bash
229
- npm run typecheck
230
- npm run lint
231
- npm test
262
+ npm run typecheck # Must pass
263
+ npm run lint # Must pass (use lint:fix for auto-fixes)
264
+ npm test # Must pass
265
+ npm run build # Must succeed
232
266
  ```
233
267
 
234
- 4. **Build the project**
268
+ 5. **Commit Your Changes**
269
+
270
+ Use [Conventional Commits](https://www.conventionalcommits.org/) format:
235
271
 
236
272
  ```bash
237
- npm run build
273
+ git add .
274
+ git commit -m "feat: add support for encrypted storage"
275
+ # or
276
+ git commit -m "fix: prevent race condition during initialization"
277
+ ```
278
+
279
+ 6. **Push to Your Fork**
280
+
281
+ ```bash
282
+ git push origin feature/your-feature-name
283
+ ```
284
+
285
+ 7. **Create Pull Request**
286
+
287
+ - Go to your fork on GitHub
288
+ - Click "New Pull Request"
289
+ - Select your feature branch
290
+ - Fill out the PR template
291
+ - Click "Create Pull Request"
292
+
293
+ ### Keeping Your Fork Updated
294
+
295
+ Before starting new work, always sync with upstream:
296
+
297
+ ```bash
298
+ git checkout main
299
+ git fetch upstream
300
+ git merge upstream/main
301
+ git push origin main
238
302
  ```
239
303
 
240
304
  ### PR Guidelines
@@ -339,7 +403,7 @@ All public APIs should have JSDoc comments:
339
403
  * mainSync.registerStore('counter', counterStore, {
340
404
  * persist: true
341
405
  * });
342
- * ```
406
+ *
343
407
  */
344
408
  public registerStore(
345
409
  storeId: string,
@@ -350,22 +414,139 @@ public registerStore(
350
414
  }
351
415
  ```
352
416
 
353
- ## Release Process
417
+ ## Commit Message Convention
354
418
 
355
- (For maintainers)
419
+ This project uses **Conventional Commits**. This is crucial because our release process is fully automated. The type of commit determines how the version number is bumped:
356
420
 
357
- 1. Update version in `package.json`
358
- 2. Update CHANGELOG.md
359
- 3. Create a git tag
360
- 4. Push to GitHub
361
- 5. Publish to npm
421
+ * **`fix:`** Bumps the **patch** version (e.g., `1.0.0` -> `1.0.1`).
422
+ * **`feat:`** Bumps the **minor** version (e.g., `1.0.0` -> `1.1.0`).
423
+ * **`feat!:`** or **`BREAKING CHANGE:`** Bumps the **major** version (e.g., `1.0.0` -> `2.0.0`).
424
+ * **`chore:`, `docs:`, `style:`, `refactor:`, `test:**` Do not trigger a new release.
362
425
 
426
+ > **Note:** Always use lowercase for the type. Scope is optional but recommended (e.g., `feat(main): ...`).
427
+
428
+ ---
429
+
430
+ ## GitHub Workflows
431
+
432
+ This project uses several automated workflows to ensure code quality and streamline releases. Understanding these workflows helps you contribute effectively.
433
+
434
+ ### For Contributors
435
+
436
+ When you submit a Pull Request, the following automated checks will run:
437
+
438
+ #### 1. **CI Workflow** (`.github/workflows/ci.yml`)
439
+ Runs on every push and PR to `main` and `develop` branches.
440
+
441
+ **What it does:**
442
+ - Tests on Node.js 22.x and 24.x
443
+ - Runs TypeScript type checking
444
+ - Runs ESLint
445
+ - Runs unit tests
446
+ - Builds the package
447
+
448
+ **How to fix failures:**
449
+ ```bash
450
+ npm run typecheck # Fix TypeScript errors
451
+ npm run lint:fix # Fix linting issues
452
+ npm test # Fix test failures
453
+ npm run build # Fix build issues
454
+ ```
455
+
456
+ #### 2. **E2E Tests Workflow** (`.github/workflows/e2e.yml`)
457
+ Runs end-to-end tests on Ubuntu, macOS, and Windows.
458
+
459
+ **What it does:**
460
+ - Builds the library
461
+ - Installs Playwright
462
+ - Runs E2E tests across different operating systems
463
+
464
+ **How to run locally:**
363
465
  ```bash
364
- npm version patch|minor|major
365
- git push --follow-tags
366
- npm publish
466
+ npm run build
467
+ npm run test:e2e
367
468
  ```
368
469
 
470
+ ### Branch Protection
471
+
472
+ The `main` branch is protected and requires:
473
+ - ✅ All CI checks to pass
474
+ - ✅ At least one maintainer approval
475
+ - ✅ Up-to-date with base branch
476
+ - ✅ No direct pushes (must use Pull Requests)
477
+
478
+ **Important:** You cannot push directly to `main`. Always work in a feature branch and create a Pull Request.
479
+
480
+ ## Release Process
481
+
482
+ (For maintainers)
483
+
484
+ We use **Semantic Release** to fully automate versioning and publishing. The process is **commit-based** - no manual version changes needed!
485
+
486
+ ### How it works
487
+
488
+ 1. **Merge to Main**: Once a Pull Request is merged into the `main` branch, a GitHub Action is triggered.
489
+ 2. **Semantic Release** (`.github/workflows/prepare-release.yml`):
490
+ - Analyzes all new commits since the last release
491
+ - Determines the next version based on commit types
492
+ - Generates CHANGELOG.md content
493
+ - Creates a Git tag (e.g., `v1.1.0`)
494
+ - Creates a GitHub Release with release notes
495
+ - **Note**: Does NOT commit back to `main` (respects branch protection)
496
+ 3. **NPM Publish** (`.github/workflows/publish.yml`):
497
+ - Triggered when a new GitHub Release is created
498
+ - Updates package.json version
499
+ - Runs all tests and quality checks
500
+ - Publishes to NPM with provenance
501
+ - Requires `NPM_TOKEN` secret
502
+
503
+ ### Version Bumping Rules
504
+
505
+ The commit type determines the version bump:
506
+ - **`fix:`** → Patch version (1.0.0 → 1.0.1)
507
+ - **`feat:`** → Minor version (1.0.0 → 1.1.0)
508
+ - **`feat!:`** or **`BREAKING CHANGE:`** → Major version (1.0.0 → 2.0.0)
509
+ - **`chore:`, `docs:`, `style:`, `refactor:`, `test:`** → No release
510
+
511
+ ### Important: Branch Protection Compatible
512
+
513
+ Our Semantic Release setup is **compatible with branch protection**:
514
+ - ✅ Creates GitHub Releases directly (no commit to main needed)
515
+ - ✅ Tag is created by GitHub (not pushed to main)
516
+ - ✅ CHANGELOG is generated in the release notes
517
+ - ✅ package.json version is updated during NPM publish workflow (not in repo)
518
+
519
+ **Version Management:**
520
+ - The `package.json` in the main branch stays at the initial version (e.g., 1.0.0)
521
+ - The **actual version** is managed via Git Tags (e.g., v1.1.0)
522
+ - The NPM package will have the correct version from the Git Tag
523
+ - This is intentional to avoid Branch Protection conflicts
524
+
525
+ **To find the current version:**
526
+ - Check Git Tags: `git fetch --tags && git tag -l`
527
+ - Check GitHub Releases: https://github.com/simpli-fyi/electron-pinia-sync/releases
528
+ - Check NPM: `npm view electron-pinia-sync version`
529
+
530
+ See `.github/RELEASE-WORKFLOW-EXPLAINED.md` for detailed information.
531
+
532
+ **Skip a release:**
533
+ Include `[skip ci]` in your merge commit message.
534
+
535
+ **Manual release (emergency only):**
536
+ Semantic Release runs automatically. Manual releases should only be done if the automation fails.
537
+
538
+ **Check release status:**
539
+ - View releases: https://github.com/simpli-fyi/electron-pinia-sync/releases
540
+ - View workflow runs: https://github.com/simpli-fyi/electron-pinia-sync/actions
541
+
542
+ ### Dependabot
543
+
544
+ Dependabot automatically creates PRs for dependency updates:
545
+ - **npm packages:** Weekly (Mondays)
546
+ - **GitHub Actions:** Monthly
547
+ - Dependencies are grouped (dev vs core) for easier review
548
+ - Security updates are created immediately
549
+
369
550
  ## Getting Help
370
551
 
371
552
  - **Issues**: Open an issue for bugs or feature requests
@@ -399,7 +580,7 @@ Feel free to reach out:
399
580
 
400
581
  - Open an issue
401
582
  - Start a discussion
402
- - Email: [your-email@example.com]
583
+ - Email: [hello@simpli.fyi]
403
584
 
404
585
  Thank you for contributing! 🎉
405
586
 
package/README.md CHANGED
@@ -13,7 +13,7 @@
13
13
  - 🔒 **Type-Safe**: Full TypeScript support with strict mode
14
14
  - 🚀 **Zero Config**: Works out of the box with sensible defaults
15
15
  - 🔁 **Echo Prevention**: Intelligent transaction tracking prevents infinite loops
16
- - 📦 **ESM-Only**: Modern ES Modules build (~4 KB per module)
16
+ - 📦 **Dual Package**: ESM and CommonJS builds (~4 KB per module)
17
17
  - ⚡ **Performance**: Efficient diffing with `microdiff` minimizes data transfer
18
18
 
19
19
  ## Installation
@@ -35,10 +35,10 @@ npm install electron pinia vue
35
35
  ```
36
36
 
37
37
  **Required versions**:
38
- - Electron >= 28
38
+ - Electron >= 40
39
39
  - Pinia >= 3.0
40
40
  - Vue >= 3.5
41
- - Node.js >= 20
41
+ - Node.js >= 22.14
42
42
 
43
43
  **Why?** This keeps the bundle size small and prevents dependency conflicts. You use your own versions of Electron and Pinia.
44
44
 
@@ -402,8 +402,6 @@ pinia.use(createRendererSync({
402
402
  - `true`: Important operations
403
403
  - `'verbose'`: Detailed logs with state diffs
404
404
 
405
- For advanced debugging, see [DEBUG.md](./DEBUG.md).
406
-
407
405
  ## Troubleshooting
408
406
 
409
407
  ### Store not syncing
@@ -0,0 +1,266 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/main/index.ts
31
+ var main_exports = {};
32
+ __export(main_exports, {
33
+ MainSync: () => MainSync,
34
+ createDebugLogger: () => createDebugLogger,
35
+ createMainSync: () => createMainSync,
36
+ formatPatchForDebug: () => formatPatchForDebug,
37
+ formatStateForDebug: () => formatStateForDebug
38
+ });
39
+ module.exports = __toCommonJS(main_exports);
40
+ var import_electron = require("electron");
41
+ var import_pinia = require("pinia");
42
+ var import_electron_store = __toESM(require("electron-store"), 1);
43
+
44
+ // src/types.ts
45
+ var IPC_CHANNELS = {
46
+ /** Renderer requests initial state from Main */
47
+ STATE_PULL: "pinia-sync:state-pull",
48
+ /** Renderer sends patch to Main */
49
+ STATE_PATCH: "pinia-sync:state-patch",
50
+ /** Main broadcasts state update to all Renderers */
51
+ STATE_UPDATED: "pinia-sync:state-updated",
52
+ /** Renderer requests full state sync */
53
+ STATE_SYNC: "pinia-sync:state-sync"
54
+ };
55
+
56
+ // src/debug.ts
57
+ function createDebugLogger(namespace, debugLevel = false, customLogger) {
58
+ const isEnabled = debugLevel !== false;
59
+ const isVerbose = debugLevel === "verbose" || debugLevel === true;
60
+ const prefix = `[${namespace}]`;
61
+ const noop = () => {
62
+ };
63
+ const logger = customLogger || console;
64
+ return {
65
+ log: logger.log?.bind(logger, prefix) || console.log.bind(console, prefix),
66
+ warn: logger.warn?.bind(logger, prefix) || console.warn.bind(console, prefix),
67
+ error: logger.error?.bind(logger, prefix) || console.error.bind(console, prefix),
68
+ debug: isEnabled ? logger.log?.bind(logger, prefix) || console.log.bind(console, prefix) : noop,
69
+ verbose: isVerbose ? logger.log?.bind(logger, `${prefix}[VERBOSE]`) || console.log.bind(console, `${prefix}[VERBOSE]`) : noop
70
+ };
71
+ }
72
+ function formatStateForDebug(state, maxLength = 200) {
73
+ try {
74
+ const json = JSON.stringify(state);
75
+ if (json.length > maxLength) {
76
+ return json.substring(0, maxLength) + `... (${json.length} chars total)`;
77
+ }
78
+ return json;
79
+ } catch {
80
+ return "[Circular or non-serializable]";
81
+ }
82
+ }
83
+ function formatPatchForDebug(patch) {
84
+ try {
85
+ const keys = Object.keys(patch);
86
+ if (keys.length === 0) {
87
+ return "{}";
88
+ }
89
+ if (keys.length > 5) {
90
+ return `{ ${keys.slice(0, 5).join(", ")}, ... (${keys.length} keys) }`;
91
+ }
92
+ return JSON.stringify(patch, null, 2);
93
+ } catch {
94
+ return "[Invalid patch]";
95
+ }
96
+ }
97
+
98
+ // src/utils/toRawState.ts
99
+ function toRawState(state) {
100
+ return JSON.parse(JSON.stringify(state));
101
+ }
102
+
103
+ // src/main/index.ts
104
+ var MainSync = class {
105
+ pinia;
106
+ electronStore;
107
+ storeMetadata = /* @__PURE__ */ new Map();
108
+ processingTransactions = /* @__PURE__ */ new Set();
109
+ MAX_TRANSACTION_HISTORY = 100;
110
+ debug;
111
+ constructor(options = {}) {
112
+ this.debug = createDebugLogger("electron-pinia-sync:main", options.debug ?? false, options.logger);
113
+ this.debug.debug("Initializing MainSync");
114
+ this.pinia = options.pinia ?? (0, import_pinia.createPinia)();
115
+ this.electronStore = new import_electron_store.default({
116
+ name: "pinia-sync-store",
117
+ ...options.storeOptions
118
+ });
119
+ this.debug.verbose("electron-store initialized with options:", options.storeOptions);
120
+ this.setupIpcHandlers();
121
+ this.debug.debug("MainSync initialized successfully");
122
+ }
123
+ /**
124
+ * Get the Pinia instance managed by this sync manager
125
+ */
126
+ getPinia() {
127
+ return this.pinia;
128
+ }
129
+ /**
130
+ * Register a store with the sync manager
131
+ */
132
+ registerStore(storeId, store, options = {}) {
133
+ this.debug.debug(`Registering store: ${storeId}`);
134
+ const persistConfig = this.normalizePersistOptions(options.persist);
135
+ this.storeMetadata.set(storeId, {
136
+ persist: persistConfig
137
+ });
138
+ if (persistConfig) {
139
+ const key = persistConfig.key ?? storeId;
140
+ const persistedState = this.electronStore.get(key);
141
+ if (persistedState) {
142
+ this.debug.verbose(`Loading persisted state for ${storeId}:`, formatStateForDebug(persistedState));
143
+ store.$patch(persistedState);
144
+ } else {
145
+ this.debug.verbose(`No persisted state found for ${storeId}`);
146
+ }
147
+ }
148
+ store.$subscribe((_mutation, state) => {
149
+ this.debug.verbose(`Store ${storeId} changed:`, formatStateForDebug(state));
150
+ const serializedState = toRawState(state);
151
+ if (persistConfig) {
152
+ const key = persistConfig.key ?? storeId;
153
+ this.electronStore.set(key, serializedState);
154
+ this.debug.verbose(`Persisted state for ${storeId} to key: ${key}`);
155
+ }
156
+ this.broadcastStateUpdate(storeId, serializedState);
157
+ }, { detached: true });
158
+ this.debug.debug(`Store ${storeId} registered successfully (persist: ${!!persistConfig})`);
159
+ }
160
+ /**
161
+ * Normalize persist options to standard format
162
+ */
163
+ normalizePersistOptions(persist) {
164
+ if (persist === true) {
165
+ return { enabled: true };
166
+ } else if (persist === false || persist === void 0) {
167
+ return false;
168
+ } else {
169
+ return persist;
170
+ }
171
+ }
172
+ /**
173
+ * Setup IPC handlers for communication with renderers
174
+ */
175
+ setupIpcHandlers() {
176
+ this.debug.debug("Setting up IPC handlers");
177
+ import_electron.ipcMain.handle(
178
+ IPC_CHANNELS.STATE_PULL,
179
+ async (_event, request) => {
180
+ this.debug.debug(`IPC handler called: STATE_PULL for store: ${request.storeId}`);
181
+ const store = this.pinia._s.get(request.storeId);
182
+ if (store) {
183
+ this.debug.verbose(`Sending state for ${request.storeId}:`, formatStateForDebug(store.$state));
184
+ } else {
185
+ this.debug.warn(`Store "${request.storeId}" not found in Main process`);
186
+ }
187
+ return {
188
+ storeId: request.storeId,
189
+ state: store ? toRawState(store.$state) : null
190
+ };
191
+ }
192
+ );
193
+ this.debug.debug(`IPC handler registered: ${IPC_CHANNELS.STATE_PULL}`);
194
+ import_electron.ipcMain.handle(
195
+ IPC_CHANNELS.STATE_PATCH,
196
+ async (_event, message) => {
197
+ this.debug.debug(`IPC handler called: STATE_PATCH for store: ${message.storeId}, transaction: ${message.transactionId}`);
198
+ this.debug.verbose(`Patch data:`, formatPatchForDebug(message.patch));
199
+ const store = this.pinia._s.get(message.storeId);
200
+ if (!store) {
201
+ this.debug.warn(`Store "${message.storeId}" not found in Main process`);
202
+ return;
203
+ }
204
+ this.addTransaction(message.transactionId);
205
+ try {
206
+ store.$patch(message.patch);
207
+ this.debug.debug(`Successfully applied patch to store: ${message.storeId}`);
208
+ } catch (error) {
209
+ this.debug.error(`Failed to patch store "${message.storeId}":`, error);
210
+ }
211
+ }
212
+ );
213
+ this.debug.debug(`IPC handler registered: ${IPC_CHANNELS.STATE_PATCH}`);
214
+ this.debug.debug("IPC handlers setup complete");
215
+ }
216
+ addTransaction(id) {
217
+ this.processingTransactions.add(id);
218
+ if (this.processingTransactions.size > this.MAX_TRANSACTION_HISTORY) {
219
+ const first = this.processingTransactions.values().next().value;
220
+ if (first) this.processingTransactions.delete(first);
221
+ }
222
+ setTimeout(() => this.processingTransactions.delete(id), 5e3);
223
+ }
224
+ /**
225
+ * Broadcast state update to all renderer processes
226
+ */
227
+ broadcastStateUpdate(storeId, state, transactionId) {
228
+ this.debug.verbose(`Broadcasting state update for store: ${storeId}`, transactionId ? `(transaction: ${transactionId})` : "");
229
+ const message = {
230
+ storeId,
231
+ state,
232
+ transactionId
233
+ };
234
+ const windows = import_electron.BrowserWindow.getAllWindows();
235
+ this.debug.verbose(`Broadcasting to ${windows.length} window(s)`);
236
+ windows.forEach((window, index) => {
237
+ if (!window.isDestroyed()) {
238
+ this.debug.verbose(`Sending ${IPC_CHANNELS.STATE_UPDATED} to window ${index + 1}`);
239
+ window.webContents.send(IPC_CHANNELS.STATE_UPDATED, message);
240
+ } else {
241
+ this.debug.verbose(`Skipping destroyed window ${index + 1}`);
242
+ }
243
+ });
244
+ this.debug.debug(`Broadcast complete for store: ${storeId}`);
245
+ }
246
+ /**
247
+ * Cleanup IPC handlers
248
+ */
249
+ destroy() {
250
+ this.debug.debug("Destroying MainSync, cleaning up IPC handlers");
251
+ import_electron.ipcMain.removeHandler(IPC_CHANNELS.STATE_PULL);
252
+ import_electron.ipcMain.removeHandler(IPC_CHANNELS.STATE_PATCH);
253
+ this.debug.debug("MainSync destroyed");
254
+ }
255
+ };
256
+ function createMainSync(options) {
257
+ return new MainSync(options);
258
+ }
259
+ // Annotate the CommonJS export names for ESM import in node:
260
+ 0 && (module.exports = {
261
+ MainSync,
262
+ createDebugLogger,
263
+ createMainSync,
264
+ formatPatchForDebug,
265
+ formatStateForDebug
266
+ });