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 +214 -33
- package/README.md +3 -5
- package/dist/main/index.cjs +266 -0
- package/dist/main/index.d.cts +153 -0
- package/dist/main/index.js +14 -8
- package/dist/preload/index.cjs +63 -0
- package/dist/preload/index.d.cts +2 -0
- package/dist/preload/index.js +5 -4
- package/dist/renderer/index.cjs +268 -0
- package/dist/renderer/index.d.cts +123 -0
- package/dist/renderer/index.js +51 -5
- package/package.json +12 -8
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 >=
|
|
10
|
-
- npm
|
|
27
|
+
- Node.js >= 22.14.0
|
|
28
|
+
- npm >= 10.x
|
|
11
29
|
- Git
|
|
12
30
|
|
|
13
31
|
### Initial Setup
|
|
14
32
|
|
|
15
|
-
1. **Fork
|
|
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/
|
|
40
|
+
git clone https://github.com/YOUR_USERNAME/electron-pinia-sync.git
|
|
19
41
|
cd electron-pinia-sync
|
|
20
42
|
```
|
|
21
43
|
|
|
22
|
-
|
|
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. **
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
268
|
+
5. **Commit Your Changes**
|
|
269
|
+
|
|
270
|
+
Use [Conventional Commits](https://www.conventionalcommits.org/) format:
|
|
235
271
|
|
|
236
272
|
```bash
|
|
237
|
-
|
|
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
|
-
##
|
|
417
|
+
## Commit Message Convention
|
|
354
418
|
|
|
355
|
-
|
|
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
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
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
|
|
365
|
-
|
|
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: [
|
|
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
|
-
- 📦 **
|
|
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 >=
|
|
38
|
+
- Electron >= 40
|
|
39
39
|
- Pinia >= 3.0
|
|
40
40
|
- Vue >= 3.5
|
|
41
|
-
- Node.js >=
|
|
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
|
+
});
|