@omega.js/desktop 0.52.0 → 0.54.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/README.md +53 -48
- package/dist/assets/css/core/_initialize.scss +1 -1
- package/dist/assets/js/core/app-shell.js +14 -15
- package/dist/assets/themes/_template/_theme.js +6 -6
- package/dist/assets/themes/base/_includes/global/sections/account.html +4 -4
- package/dist/assets/themes/base/_includes/global/sections/app-sidebar.html +5 -5
- package/dist/assets/themes/base/_layouts/frontend/pages/account/index.html +3 -3
- package/dist/assets/themes/base/_layouts/frontend/pages/alternatives/index.html +1 -1
- package/dist/assets/themes/base/_layouts/frontend/pages/blog/tags/tag.html +1 -1
- package/dist/assets/themes/base/_layouts/frontend/pages/payment/confirmation.html +1 -1
- package/dist/assets/themes/base/_sections/marketing/newsletter-cta/section.js +2 -3
- package/dist/assets/themes/base/_sections/verts/unit/section.html +1 -1
- package/dist/assets/themes/base/_sections/verts/unit/section.js +3 -4
- package/dist/assets/themes/base/_theme.js +4 -3
- package/dist/assets/themes/bootstrap/_theme.js +2 -2
- package/dist/assets/themes/bootstrap/overrides/_links.scss +1 -1
- package/dist/assets/themes/classy/_theme.js +8 -6
- package/dist/assets/themes/classy/css/marketing/_sections.scss +1 -2
- package/dist/assets/themes/classy/js/hero-demo-form.js +3 -2
- package/dist/assets/themes/neobrutalism/_theme.js +5 -5
- package/dist/assets/themes/neobrutalism/js/pages/test/libraries/layers/index.js +1 -1
- package/dist/assets/themes/newsflash/_theme.js +6 -6
- package/dist/assets/themes/newsflash/js/pages/test/libraries/layers/index.js +1 -1
- package/dist/build.js +87 -111
- package/dist/cli-run.js +4 -1
- package/dist/cli.js +3 -3
- package/dist/commands/build.js +2 -2
- package/dist/commands/cdp/capture.js +2 -2
- package/dist/commands/cdp/client.js +1 -1
- package/dist/commands/cdp/quit.js +2 -2
- package/dist/commands/cdp/relaunch.js +2 -2
- package/dist/commands/cdp/theme.js +1 -1
- package/dist/commands/cdp.js +1 -1
- package/dist/commands/clean.js +4 -5
- package/dist/commands/deploy.js +4 -4
- package/dist/commands/dev.js +25 -0
- package/dist/commands/finalize-release.js +4 -4
- package/dist/commands/launch.js +2 -2
- package/dist/commands/lib/deploy-precheck.js +3 -3
- package/dist/commands/lib/ensure-target.js +18 -23
- package/dist/commands/lib/migrate.js +17 -0
- package/dist/commands/logs.js +1 -1
- package/dist/commands/package.js +2 -2
- package/dist/commands/publish.js +2 -2
- package/dist/commands/release.js +4 -4
- package/dist/commands/runner.js +11 -11
- package/dist/commands/sign-windows.js +5 -5
- package/dist/commands/test.js +8 -8
- package/dist/commands/update.js +7 -6
- package/dist/commands/validate-certs.js +4 -4
- package/dist/commands/version.js +3 -3
- package/dist/defaults/.github/workflows/build.yml +18 -18
- package/dist/defaults/_.gitignore +0 -2
- package/dist/defaults/_mas/README.md +3 -3
- package/dist/defaults/config/certs/README.md +1 -1
- package/dist/defaults/config/omega.json5 +43 -43
- package/dist/defaults/docs/README.md +3 -3
- package/dist/defaults/gulpfile.js +1 -1
- package/dist/defaults/hooks/build/post.js +2 -2
- package/dist/defaults/hooks/build/pre.js +2 -2
- package/dist/defaults/hooks/deploy/pre.js +1 -1
- package/dist/defaults/hooks/notarize/post.js +2 -2
- package/dist/defaults/hooks/release/post.js +2 -2
- package/dist/defaults/hooks/release/pre.js +2 -2
- package/dist/defaults/src/assets/js/components/about/index.js +3 -5
- package/dist/defaults/src/assets/js/components/main/index.js +3 -5
- package/dist/defaults/src/assets/js/components/settings/index.js +3 -5
- package/dist/defaults/src/assets/scss/pages/about.scss +1 -1
- package/dist/defaults/src/assets/scss/pages/main.scss +1 -1
- package/dist/defaults/src/assets/scss/pages/settings.scss +1 -1
- package/dist/defaults/src/integrations/context-menu/index.js +13 -13
- package/dist/defaults/src/integrations/menu/index.js +8 -8
- package/dist/defaults/src/integrations/tray/index.js +12 -12
- package/dist/defaults/src/main.js +5 -7
- package/dist/defaults/src/preload.js +4 -6
- package/dist/defaults/test/README.md +5 -5
- package/dist/defaults/test/_init.js +1 -1
- package/dist/gulp/main.js +9 -10
- package/dist/gulp/tasks/audit.js +11 -14
- package/dist/gulp/tasks/build-config.js +8 -8
- package/dist/gulp/tasks/bundle.js +16 -16
- package/dist/gulp/tasks/defaults.js +3 -3
- package/dist/gulp/tasks/distribute.js +2 -2
- package/dist/gulp/tasks/html.js +9 -9
- package/dist/gulp/tasks/package-quick.js +3 -3
- package/dist/gulp/tasks/package.js +3 -3
- package/dist/gulp/tasks/release.js +3 -3
- package/dist/gulp/tasks/sass.js +6 -6
- package/dist/gulp/tasks/serve.js +4 -4
- package/dist/hooks/notarize-artifacts.js +1 -1
- package/dist/hooks/notarize.js +1 -1
- package/dist/index.js +5 -8
- package/dist/lib/_environment-mixin.js +50 -0
- package/dist/lib/_lifecycle-mixin.js +45 -0
- package/dist/lib/analytics.js +33 -35
- package/dist/lib/app-state.js +14 -14
- package/dist/lib/auth-flow.js +18 -18
- package/dist/lib/auth-persistence.js +12 -12
- package/dist/lib/auth.js +421 -0
- package/dist/lib/auto-updater.js +49 -49
- package/dist/lib/context-menu.js +13 -13
- package/dist/lib/context.js +19 -19
- package/dist/lib/deep-link.js +34 -34
- package/dist/lib/fontawesome.js +5 -5
- package/dist/lib/ipc.js +4 -4
- package/dist/lib/menu.js +25 -25
- package/dist/lib/protocol.js +5 -5
- package/dist/lib/remote-config.js +22 -22
- package/dist/lib/remote-scripts.js +21 -21
- package/dist/lib/restart-manager/index.js +29 -29
- package/dist/lib/restart-manager/install.js +1 -1
- package/dist/lib/restart-manager/protocol.js +1 -1
- package/dist/lib/sign-helpers/exec-with-limit.js +1 -1
- package/dist/lib/sign-helpers/sign-events.js +1 -1
- package/dist/lib/startup.js +18 -13
- package/dist/lib/storage.js +10 -10
- package/dist/lib/templating.js +16 -16
- package/dist/lib/theme.js +10 -10
- package/dist/lib/tray.js +27 -27
- package/dist/lib/usage.js +11 -11
- package/dist/lib/window-manager.js +26 -26
- package/dist/main.js +399 -483
- package/dist/preload.js +236 -176
- package/dist/renderer.js +417 -391
- package/dist/test/fixtures/consumer-app/config/omega.json5 +1 -1
- package/dist/test/fixtures/consumer-app/src/assets/js/components/main/index.js +8 -11
- package/dist/test/fixtures/consumer-app/src/main.js +5 -7
- package/dist/test/fixtures/consumer-app/src/preload.js +2 -2
- package/dist/test/harness/boot-entry.js +22 -20
- package/dist/test/harness/main-entry.js +31 -30
- package/dist/test/harness/renderer-entry.js +5 -5
- package/dist/test/harness/renderer-preload.js +137 -141
- package/dist/test/index.js +10 -10
- package/dist/test/runner.js +2 -2
- package/dist/test/runners/boot.js +7 -6
- package/dist/test/runners/electron.js +3 -2
- package/dist/test/runners/render-event.js +2 -2
- package/dist/test/suites/boot/consumer-app-boots.test.js +57 -26
- package/dist/test/suites/boot/restart-manager.test.js +2 -2
- package/dist/test/suites/boot/storage-bundled.test.js +5 -5
- package/dist/test/suites/boot/theme.test.js +13 -13
- package/dist/test/suites/build/audit.test.js +21 -8
- package/dist/test/suites/build/auth-persistence-resolve.test.js +6 -6
- package/dist/test/suites/build/boot-fixture.test.js +2 -2
- package/dist/test/suites/build/boot-runner-timeout.test.js +6 -5
- package/dist/test/suites/build/brand-scss.test.js +1 -1
- package/dist/test/suites/build/build-json-bake.test.js +1 -1
- package/dist/test/suites/build/build-workflow-jobs.test.js +2 -2
- package/dist/test/suites/build/cli.test.js +30 -2
- package/dist/test/suites/build/config-schema.test.js +5 -5
- package/dist/test/suites/build/defaults-em-dash.test.js +22 -0
- package/dist/test/suites/build/defaults-scaffold.test.js +21 -7
- package/dist/test/suites/build/deploy-direct.test.js +7 -5
- package/dist/test/suites/build/deploy-dispatch.test.js +2 -1
- package/dist/test/suites/build/deploy-hook.test.js +7 -5
- package/dist/test/suites/build/dev-verb.test.js +67 -0
- package/dist/test/suites/build/ensure-target.test.js +13 -5
- package/dist/test/suites/build/env-delivery.test.js +2 -2
- package/dist/test/suites/build/esm-only-dependency.test.js +2 -2
- package/dist/test/suites/build/exports.test.js +9 -8
- package/dist/test/suites/build/get-config.test.js +4 -4
- package/dist/test/suites/build/manifest-deps.test.js +1 -1
- package/dist/test/suites/build/merge-line-files.test.js +7 -7
- package/dist/test/suites/build/migrate.test.js +29 -0
- package/dist/test/suites/build/omega-shell.test.js +34 -2
- package/dist/test/suites/build/omega.test.js +350 -0
- package/dist/test/suites/build/project-scripts-deps.test.js +6 -10
- package/dist/test/suites/build/renderer-auth-bridge.test.js +211 -78
- package/dist/test/suites/build/runner-env-write.test.js +73 -0
- package/dist/test/suites/build/runner.test.js +11 -10
- package/dist/test/suites/build/sentry.test.js +2 -2
- package/dist/test/suites/build/setup-scripts.test.js +27 -0
- package/dist/test/suites/build/sign-windows-e2e.test.js +2 -2
- package/dist/test/suites/build/templating.test.js +3 -3
- package/dist/test/suites/build/test-stealth.test.js +7 -9
- package/dist/test/suites/build/url-helpers.test.js +55 -56
- package/dist/test/suites/build/validate-config.test.js +15 -4
- package/dist/test/suites/build/verb-logs.test.js +20 -0
- package/dist/test/suites/build/wave5-pins.test.js +2 -2
- package/dist/test/suites/main/analytics.test.js +59 -59
- package/dist/test/suites/main/app-state.test.js +66 -66
- package/dist/test/suites/main/auth-flow.test.js +41 -41
- package/dist/test/suites/main/auth-persistence.test.js +54 -43
- package/dist/test/suites/main/{client-bridge.integration.test.js → auth.integration.test.js} +10 -9
- package/dist/test/suites/main/auth.test.js +336 -0
- package/dist/test/suites/main/auto-updater.test.js +134 -134
- package/dist/test/suites/main/boot-sequence.test.js +37 -49
- package/dist/test/suites/main/context-menu.test.js +51 -50
- package/dist/test/suites/main/context.test.js +25 -25
- package/dist/test/suites/main/deep-link.test.js +74 -74
- package/dist/test/suites/main/fontawesome.test.js +27 -27
- package/dist/test/suites/main/ipc.test.js +35 -35
- package/dist/test/suites/main/menu.test.js +101 -100
- package/dist/test/suites/main/protocol.test.js +19 -19
- package/dist/test/suites/main/remote-config.test.js +63 -63
- package/dist/test/suites/main/remote-scripts.test.js +103 -103
- package/dist/test/suites/main/request.test.js +71 -0
- package/dist/test/suites/main/restart-manager.test.js +33 -33
- package/dist/test/suites/main/startup-paths-and-ua.test.js +4 -4
- package/dist/test/suites/main/startup.test.js +26 -26
- package/dist/test/suites/main/stealth-window.test.js +1 -1
- package/dist/test/suites/main/storage.test.js +25 -25
- package/dist/test/suites/main/theme.test.js +34 -34
- package/dist/test/suites/main/tray.test.js +79 -79
- package/dist/test/suites/main/url-helpers.test.js +133 -133
- package/dist/test/suites/main/usage.test.js +25 -25
- package/dist/test/suites/main/window-bounds.test.js +27 -27
- package/dist/test/suites/main/window-manager.test.js +44 -44
- package/dist/test/suites/renderer/analytics-bridge.test.js +5 -5
- package/dist/test/suites/renderer/cross-context-helpers.test.js +37 -31
- package/dist/test/suites/renderer/round-trip.test.js +3 -3
- package/dist/test/suites/renderer/tooltips.test.js +15 -15
- package/dist/test/suites/renderer/{window-em-surface.test.js → window-desktop-surface.test.js} +28 -8
- package/dist/test/utils/extended-mode-warning.js +1 -1
- package/dist/utils/boot-harness.js +56 -0
- package/dist/utils/build-pipeline.js +4 -4
- package/dist/utils/mode-helpers.js +2 -15
- package/dist/utils/runner-env.js +13 -28
- package/dist/utils/ship-keys.js +3 -3
- package/dist/utils/signing-status.js +51 -0
- package/dist/utils/test-events.js +7 -0
- package/dist/utils/test-stealth.js +6 -6
- package/dist/utils/url-helpers.js +52 -42
- package/dist/utils/user-agent.js +44 -0
- package/dist/vendor/account/engine.js +3 -3
- package/dist/vendor/account/index.js +14 -45
- package/dist/vendor/account/resolve-account.js +44 -0
- package/dist/vendor/account/schema.js +1 -1
- package/dist/vendor/account/user.js +99 -0
- package/dist/vendor/config/client-config.js +1 -1
- package/dist/vendor/config/company.js +46 -14
- package/dist/vendor/config/defaults.js +30 -7
- package/dist/vendor/config/edit.js +25 -3
- package/dist/vendor/config/env-delivery.js +1 -1
- package/dist/vendor/config/env-schema.js +3 -6
- package/dist/vendor/config/env.js +34 -22
- package/dist/vendor/config/environment.js +11 -30
- package/dist/vendor/config/index.js +21 -28
- package/dist/vendor/config/load.js +16 -9
- package/dist/vendor/config/platforms.js +1 -1
- package/dist/vendor/config/repo.js +10 -27
- package/dist/vendor/config/schema-client.js +64 -0
- package/dist/vendor/config/schema-cloud.js +38 -0
- package/dist/vendor/config/schema-manager.js +118 -0
- package/dist/vendor/config/schema-overrides.js +68 -0
- package/dist/vendor/config/schema.js +104 -160
- package/dist/vendor/config/site-global.js +2 -3
- package/dist/vendor/config/validate.js +97 -78
- package/dist/vendor/config/winback.js +1 -1
- package/dist/vendor/devkit/actions-secrets.js +1 -1
- package/dist/vendor/devkit/agents-md.js +233 -0
- package/dist/vendor/devkit/attach-log-file.js +16 -2
- package/dist/vendor/devkit/build-json.js +1 -1
- package/dist/vendor/devkit/ci-workflows.js +30 -30
- package/dist/vendor/devkit/cli-router.js +16 -11
- package/dist/vendor/devkit/defaults-engine.js +15 -51
- package/dist/vendor/devkit/deploy-snapshot.js +44 -9
- package/dist/vendor/devkit/env-lines.js +183 -0
- package/dist/vendor/devkit/local.js +64 -10
- package/dist/vendor/devkit/lockfile.js +32 -13
- package/dist/vendor/devkit/logger.js +7 -2
- package/dist/vendor/devkit/merge-line-files.js +219 -177
- package/dist/vendor/devkit/omega-bin.js +208 -111
- package/dist/vendor/devkit/preludes/docs-sync.js +52 -0
- package/dist/vendor/devkit/preludes/index.js +1 -0
- package/dist/vendor/devkit/target-picker.js +45 -0
- package/dist/vendor/devkit/test/dashed-files.js +37 -0
- package/dist/vendor/devkit/test/run-verb-under-tee.js +71 -0
- package/dist/vendor/devkit/test/runner-core.js +6 -6
- package/dist/vendor/devkit/update.js +15 -15
- package/dist/vendor/devkit/verb-scripts.js +40 -0
- package/dist/vendor/devkit/verbs.js +170 -0
- package/dist/vendor/monitoring/env.js +2 -2
- package/dist/vendor/monitoring/index.js +1 -1
- package/dist/vendor/monitoring/main.js +1 -1
- package/dist/vendor/monitoring/preload.js +1 -1
- package/dist/vendor/monitoring/renderer.js +1 -1
- package/package.json +19 -26
- package/dist/commands/install.js +0 -37
- package/dist/defaults/AGENTS.md +0 -110
- package/dist/defaults/CLAUDE.md +0 -1
- package/dist/lib/client-bridge.js +0 -374
- package/dist/lib/logger.js +0 -4
- package/dist/test/suites/build/manager.test.js +0 -213
- package/dist/test/suites/main/client-bridge.test.js +0 -262
- package/dist/vendor/config/env-retired.js +0 -137
- package/dist/vendor/config/retired-keys.js +0 -635
- package/docs/analytics.md +0 -140
- package/docs/app-state.md +0 -92
- package/docs/audit.md +0 -69
- package/docs/auto-updater.md +0 -243
- package/docs/boot-sequence.md +0 -39
- package/docs/build-system.md +0 -169
- package/docs/cdp-debugging.md +0 -169
- package/docs/client-bridge.md +0 -269
- package/docs/common-mistakes.md +0 -21
- package/docs/config-schema.md +0 -120
- package/docs/context-menu.md +0 -112
- package/docs/context.md +0 -81
- package/docs/css.md +0 -90
- package/docs/deep-link.md +0 -186
- package/docs/environment-detection.md +0 -112
- package/docs/fontawesome.md +0 -107
- package/docs/hooks.md +0 -89
- package/docs/icons.md +0 -79
- package/docs/index.md +0 -317
- package/docs/installer-options.md +0 -165
- package/docs/ipc.md +0 -61
- package/docs/lib-modules.md +0 -53
- package/docs/logging.md +0 -229
- package/docs/menu.md +0 -160
- package/docs/releasing.md +0 -239
- package/docs/remote-config.md +0 -118
- package/docs/remote-scripts.md +0 -144
- package/docs/restart-manager.md +0 -144
- package/docs/runner.md +0 -290
- package/docs/sentry.md +0 -97
- package/docs/shared/agent-docs.md +0 -89
- package/docs/shared/analytics.md +0 -612
- package/docs/shared/brands.md +0 -57
- package/docs/shared/breaking-changes.md +0 -851
- package/docs/shared/config.md +0 -1952
- package/docs/shared/deploys.md +0 -341
- package/docs/shared/icons.md +0 -219
- package/docs/shared/local-dev.md +0 -167
- package/docs/shared/logging.md +0 -205
- package/docs/shared/monitoring.md +0 -167
- package/docs/shared/publishing.md +0 -187
- package/docs/shared/rulings.md +0 -34
- package/docs/shared/testing.md +0 -147
- package/docs/shared/theming.md +0 -629
- package/docs/shared/translation.md +0 -333
- package/docs/shared/updates.md +0 -61
- package/docs/signing.md +0 -293
- package/docs/startup.md +0 -142
- package/docs/storage.md +0 -59
- package/docs/templating.md +0 -101
- package/docs/test-boot-layer.md +0 -157
- package/docs/test-framework.md +0 -362
- package/docs/themes.md +0 -149
- package/docs/tooltips.md +0 -99
- package/docs/tray.md +0 -164
- package/docs/usage.md +0 -58
- package/docs/verts.md +0 -62
- package/docs/windows.md +0 -149
package/docs/analytics.md
DELETED
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
# Analytics
|
|
2
|
-
|
|
3
|
-
GA4 Measurement Protocol with cross-platform identity. The same human gets unified events across desktop (@omega.js/desktop), web (UJM/@omega.js/client), and backend (@omega.js/backend) — provided all four reference the same Firebase project ID.
|
|
4
|
-
|
|
5
|
-
## How identity works
|
|
6
|
-
|
|
7
|
-
Every event ships with two GA4 fields:
|
|
8
|
-
|
|
9
|
-
- **`client_id`** — uniquely identifies a *desktop install*. Stable per-install, anonymous.
|
|
10
|
-
- **`user_id`** — uniquely identifies a *human*. Set when the user is signed in via Firebase Auth. It rides **alongside** the `client_id`, never in place of it: GA stitches sessions by the client id.
|
|
11
|
-
|
|
12
|
-
@omega.js/desktop derives both via `uuidv5(input, namespace)` where:
|
|
13
|
-
|
|
14
|
-
- `namespace = uuidv5(cloud.config.projectId, uuidv5.URL)` — same projectId in @omega.js/backend/UJM/@omega.js/client → same namespace everywhere.
|
|
15
|
-
- `client_id = uuidv5(deviceId, namespace)` — the `deviceId` comes from the ONE shared derivation, `@omega.js/analytics`' `deriveDeviceId` ([#396](https://github.com/Omega-JS-Stack/omega/issues/396)), with desktop injecting its storage and its MAC seed ([context.md](context.md)).
|
|
16
|
-
- `user_id = uuidv5(firebaseUid, namespace)` — set automatically when `omega.onAuthChange` fires with a uid; cleared on logout.
|
|
17
|
-
|
|
18
|
-
Why this matters: the same Firebase user signing into the desktop app, the web app, and triggering backend events produces **identical `user_id` values** in every Measurement Protocol call. GA4 stitches the events into one user journey across all surfaces.
|
|
19
|
-
|
|
20
|
-
What does NOT cross surfaces is the machine. The desktop app's deviceId lives in electron-store and a browser's lives in that browser's localStorage, so one machine is two `client_id`s — the human is the link, and always was.
|
|
21
|
-
|
|
22
|
-
## Config
|
|
23
|
-
|
|
24
|
-
```jsonc
|
|
25
|
-
analytics: {
|
|
26
|
-
enabled: true, // default true
|
|
27
|
-
providers: {
|
|
28
|
-
google: {
|
|
29
|
-
id: 'G-XXXXXXXXXX', // Measurement ID — REQUIRED
|
|
30
|
-
},
|
|
31
|
-
},
|
|
32
|
-
}
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
The API secret is read from `process.env.GOOGLE_ANALYTICS_SECRET` — never committed. Mirrors @omega.js/backend's convention.
|
|
36
|
-
|
|
37
|
-
### Local dev
|
|
38
|
-
|
|
39
|
-
Add to `.env`:
|
|
40
|
-
|
|
41
|
-
```bash
|
|
42
|
-
GOOGLE_ANALYTICS_SECRET=your_secret_here
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
Mint the secret in GA4 Admin → Data Streams → your stream → **Measurement Protocol API secrets**.
|
|
46
|
-
|
|
47
|
-
### Production builds
|
|
48
|
-
|
|
49
|
-
The `bundle` task's esbuild `define` bakes `process.env.GOOGLE_ANALYTICS_SECRET` into the bundled main process at build time, so packaged apps don't need `.env` at runtime. The build runs with the secret set (CI does this via the GitHub Actions secret `omega deploy`'s precheck pushes).
|
|
50
|
-
|
|
51
|
-
## API
|
|
52
|
-
|
|
53
|
-
```js
|
|
54
|
-
manager.analytics.event('button_click', { button_id: 'cta' });
|
|
55
|
-
manager.analytics.pageview('/settings');
|
|
56
|
-
manager.analytics.screenview('SettingsScreen');
|
|
57
|
-
manager.analytics.setUserProperties({ plan: 'premium', trial: false });
|
|
58
|
-
manager.analytics.setUserId('firebase-uid-abc'); // usually wired automatically
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
Same surface in renderer:
|
|
62
|
-
|
|
63
|
-
```js
|
|
64
|
-
window.desktop.analytics.event('button_click', { button_id: 'cta' });
|
|
65
|
-
window.desktop.analytics.pageview('/settings');
|
|
66
|
-
window.desktop.analytics.setUserProperties({ plan: 'premium' });
|
|
67
|
-
const status = await window.desktop.analytics.getStatus(); // { enabled, measurementId, clientId, userId, queueLength }
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
The renderer surface is fire-and-forget IPC (`ipcRenderer.send`) for events; only `getStatus` round-trips via `invoke`.
|
|
71
|
-
|
|
72
|
-
## The renderer NEVER sends — it forwards ([#411](https://github.com/Omega-JS-Stack/omega/issues/411))
|
|
73
|
-
|
|
74
|
-
A renderer's own `omega.analytics().event(...)` — the embedded @omega.js/client, the surface a vert click or a permission prompt fires through — routes to the bridge above and is delivered by the MAIN process's sender. There is exactly one sender per install:
|
|
75
|
-
|
|
76
|
-
```
|
|
77
|
-
renderer: omega.analytics().event('vert_click', { … })
|
|
78
|
-
→ @omega.js/client reads config.analyticsBridge (src/renderer.js injects the
|
|
79
|
-
preload's window.desktop.analytics when it boots the client)
|
|
80
|
-
→ ipcRenderer.send('desktop:analytics:event', { name, params })
|
|
81
|
-
→ main: analytics.event(name, params) → catalog → Measurement Protocol
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
The bridge is **injected, never sniffed**: the client reads that one config key and no global, so a page that merely carries a `window.desktop` can never route a brand's analytics into a void.
|
|
85
|
-
|
|
86
|
-
Main owns identity end to end: the device id from electron-store, the session id minted once per launch, the real engagement time, and the app's `page_location` / `page_title` (a renderer's `file://` href is not GA's business). Only the canonical name and the caller's params cross.
|
|
87
|
-
|
|
88
|
-
What does NOT cross the bridge:
|
|
89
|
-
|
|
90
|
-
- **The fire's `options`** (`{ eventId, providers }`) — they exist to deduplicate a browser pixel against its server-side half, and GA4 through main is the one lane a desktop window has.
|
|
91
|
-
- **The page data** the web path merges in (`page_path` / `page_title` / `page_location`) — main supplies the app's own.
|
|
92
|
-
|
|
93
|
-
What the bridged client does NOT do: mint a `client_id` of its own (no `localStorage._omega_device_id` in a desktop renderer), hold an api_secret, or fire `login` / `logout` — main's auth bridge already fires those off the same Firebase user, and a second pair would double-count every sign-in.
|
|
94
|
-
|
|
95
|
-
An uncatalogued event name never leaves the renderer: the catalog check runs before the forward, so a typo throws at the call site in development (and is logged-and-skipped in a packaged app) instead of surfacing as an unattributable warning in main's `runtime.log`. A forward that IPC cannot carry (a non-cloneable param) is warned about, never thrown at the user mid-action.
|
|
96
|
-
|
|
97
|
-
> 🚫 **Never inject `GOOGLE_ANALYTICS_SECRET` into a renderer's config.** Desktop's config carries the measurement id alone. A renderer with its own secret would become a second Measurement Protocol sender with its own device id and its own session id — one install counted as two GA clients ([#396](https://github.com/Omega-JS-Stack/omega/issues/396) class). The client backs the rule with a guard: a bridged renderer drops any secret handed to it.
|
|
98
|
-
|
|
99
|
-
## Auto-fired events
|
|
100
|
-
|
|
101
|
-
| Event | When | Notes |
|
|
102
|
-
|---|---|---|
|
|
103
|
-
| `app_launch` | At end of `analytics.initialize()` (main process) | Fires once per launch |
|
|
104
|
-
| `login` | On `omega.onAuthChange({uid: ...})` transition from null → uid | `params.method = providerId` |
|
|
105
|
-
| `logout` | On `omega.onAuthChange({uid: null})` after a previous uid | — |
|
|
106
|
-
|
|
107
|
-
## Queueing
|
|
108
|
-
|
|
109
|
-
Calls before init complete are queued (up to 200 events). On init, the queue is drained. After init, calls send immediately.
|
|
110
|
-
|
|
111
|
-
## Disabled paths
|
|
112
|
-
|
|
113
|
-
`analytics._enabled = false` whenever:
|
|
114
|
-
|
|
115
|
-
- `config.analytics.enabled === false`
|
|
116
|
-
- No measurement ID configured
|
|
117
|
-
- No `GOOGLE_ANALYTICS_SECRET` env var
|
|
118
|
-
|
|
119
|
-
In all three cases, `event()` is a silent no-op (no throws, no warns past init).
|
|
120
|
-
|
|
121
|
-
## Event names come from the catalog
|
|
122
|
-
|
|
123
|
-
`event(name, params)` takes a CANONICAL name from `@omega.js/analytics`' shared
|
|
124
|
-
catalog — the same vocabulary the web pages, the extension and the Cloud
|
|
125
|
-
Functions speak ([docs/shared/analytics.md](../../../docs/shared/analytics.md)).
|
|
126
|
-
The catalog's GA4 mapping decides the native name and payload; this module keeps
|
|
127
|
-
only what is desktop's: the Measurement Protocol transport, the pre-init queue,
|
|
128
|
-
the session/engagement enrichment, and the IPC bridge (unchanged — the preload
|
|
129
|
-
API is byte-compatible).
|
|
130
|
-
|
|
131
|
-
A name no catalog entry declares is a programmer error: it throws in development
|
|
132
|
-
and is logged-and-skipped in a packaged app, where a throw would take the user's
|
|
133
|
-
action with it. Adding an event is one catalog entry plus its test, never a
|
|
134
|
-
free-typed string here.
|
|
135
|
-
|
|
136
|
-
## Tests
|
|
137
|
-
|
|
138
|
-
- `src/test/suites/main/analytics.test.js` — disabled paths, uuidv5 stability, the catalog contract (canonical name in, GA4 descriptor out; unknown names never post), queueing, auth-bridge wiring, IPC handlers, secret-not-leaked guard.
|
|
139
|
-
- `src/test/suites/renderer/analytics-bridge.test.js` — renderer-side surface shape, `getStatus` round-trip, and the #411 pin: a renderer-originated `omega.analytics().event(...)` reaches main's sender exactly once, and the payload GA would receive carries main's `client_id` and main's session id, while the bridged client holds no secret and no device id of its own (the harness hands it credentials on purpose). The harness taps main's transport (`harness/main-entry.js`) so a renderer suite can read back what the sender was handed — a test run never reaches a real GA property. Harness fidelity, stated plainly: it drives the real client and the real IPC channel into the real main-process sender, but the client runs in the preload world holding the bridge object directly, not the contextBridge proxy a page bundle gets — the proxy hop is the one link this pin does not exercise.
|
|
140
|
-
- `packages/client/test/analytics.test.js` — the client half: the bridge is the injected config value alone (a planted `window.desktop` bridges nothing), a bridged client drops credentials and mints no device id, an uncatalogued name never leaves the renderer, and a forward that throws never reaches the caller.
|
package/docs/app-state.md
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
# App State
|
|
2
|
-
|
|
3
|
-
Storage-backed launch flags + crash sentinel. Tells you *whether* this is the first launch ever, *how many* times the app has launched, *whether* the previous run crashed, and *whether* the version changed.
|
|
4
|
-
|
|
5
|
-
## Public API on `manager.appState`
|
|
6
|
-
|
|
7
|
-
```js
|
|
8
|
-
manager.appState.isFirstLaunch() // boolean — true ONLY on the very first boot
|
|
9
|
-
manager.appState.getLaunchCount() // number — total successful launches (including this one)
|
|
10
|
-
manager.appState.getInstalledAt() // Date — first ever launch timestamp
|
|
11
|
-
manager.appState.getLastLaunchAt() // Date | null — previous launch (null on first)
|
|
12
|
-
manager.appState.getLastQuitAt() // Date | null — previous graceful quit; null if it crashed
|
|
13
|
-
manager.appState.recoveredFromCrash() // boolean — previous run did not exit cleanly
|
|
14
|
-
|
|
15
|
-
manager.appState.getVersion() // string | null — package version of this launch
|
|
16
|
-
manager.appState.getPreviousVersion() // string | null — version before this launch
|
|
17
|
-
manager.appState.wasUpgraded() // boolean — true if THIS launch's version differs from the prior
|
|
18
|
-
|
|
19
|
-
manager.appState.launchedAtLogin() // boolean — OS booted us via openAtLogin
|
|
20
|
-
manager.appState.launchedFromDeepLink() // boolean — argv had a deep-link payload (set by lib/deep-link)
|
|
21
|
-
|
|
22
|
-
manager.appState.reset() // wipe persisted state (test helper / factory-reset command)
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
## Storage shape (key `appState`)
|
|
26
|
-
|
|
27
|
-
```js
|
|
28
|
-
{
|
|
29
|
-
installedAt: 1700000000000, // first ever boot timestamp (ms epoch)
|
|
30
|
-
launchCount: 42,
|
|
31
|
-
lastLaunchAt: 1700000123456, // THIS launch's timestamp; previous launch's value exposed via getLastLaunchAt()
|
|
32
|
-
lastQuitAt: null, // null while running; set on graceful quit
|
|
33
|
-
version: '1.2.3',
|
|
34
|
-
previousVersion: '1.2.2', // preserved across no-change launches (see "Upgrade detection")
|
|
35
|
-
sentinel: true // true while running, cleared on graceful quit
|
|
36
|
-
}
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
## Crash detection
|
|
40
|
-
|
|
41
|
-
- On every boot, `appState.initialize()` reads the previous `sentinel` and `lastQuitAt` values BEFORE overwriting them.
|
|
42
|
-
- If `sentinel === true` AND `lastQuitAt === null`, the previous run never made it to `before-quit` / `will-quit` → it crashed. `recoveredFromCrash()` returns `true` for this launch only.
|
|
43
|
-
- First launch is exempt (no prior state to compare).
|
|
44
|
-
- Graceful-quit cleanup is wired up via `app.on('before-quit')` and `app.on('will-quit')`. Both fire under different shutdown paths; either one clears the sentinel and writes `lastQuitAt`.
|
|
45
|
-
|
|
46
|
-
## Upgrade detection
|
|
47
|
-
|
|
48
|
-
`wasUpgraded()` is true **only** if THIS launch's version differs from the prior launch's version. Subsequent launches at the same version return `false`, but `getPreviousVersion()` keeps returning the historical value so you can show a "what's new" UI on the second launch too.
|
|
49
|
-
|
|
50
|
-
```js
|
|
51
|
-
// install 1.0.0
|
|
52
|
-
appState.wasUpgraded() // false (first launch)
|
|
53
|
-
appState.getPreviousVersion() // null
|
|
54
|
-
|
|
55
|
-
// upgrade to 1.0.1, launch again
|
|
56
|
-
appState.wasUpgraded() // true
|
|
57
|
-
appState.getPreviousVersion() // '1.0.0'
|
|
58
|
-
|
|
59
|
-
// launch again at 1.0.1, no change
|
|
60
|
-
appState.wasUpgraded() // false
|
|
61
|
-
appState.getPreviousVersion() // '1.0.0' (preserved — useful for "what's new" UIs)
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
## Common patterns
|
|
65
|
-
|
|
66
|
-
### Show onboarding on first launch
|
|
67
|
-
|
|
68
|
-
```js
|
|
69
|
-
if (manager.appState.isFirstLaunch()) {
|
|
70
|
-
manager.windows.show('onboarding');
|
|
71
|
-
}
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
### Crash report ping
|
|
75
|
-
|
|
76
|
-
```js
|
|
77
|
-
if (manager.appState.recoveredFromCrash()) {
|
|
78
|
-
manager.sentry.captureMessage('recovered from crash', 'warning');
|
|
79
|
-
}
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
### What's new modal after upgrade
|
|
83
|
-
|
|
84
|
-
```js
|
|
85
|
-
if (manager.appState.wasUpgraded()) {
|
|
86
|
-
manager.windows.show('changelog');
|
|
87
|
-
}
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
## Test helper
|
|
91
|
-
|
|
92
|
-
`appState.reset()` wipes the persisted state and resets the in-memory snapshot. Useful in test suites and as a real `Settings → Reset to factory defaults` command.
|
package/docs/audit.md
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
# Audit Workflow
|
|
2
|
-
|
|
3
|
-
Full-project audit for @omega.js/desktop — runs against a CONSUMER project or the FRAMEWORK repo itself (scope auto-detected). Invoked via the `omega:desktop` skill (`/omega:desktop audit`) or any "audit this app/project" request.
|
|
4
|
-
|
|
5
|
-
Every check has a stable ID, a severity, and a scope. Findings are reported as `ID @ file:line`, fixed one at a time, then re-verified. The tables below do NOT restate the rules — each check links to the doc that owns the rule and the fix.
|
|
6
|
-
|
|
7
|
-
## Protocol
|
|
8
|
-
|
|
9
|
-
1. **Detect scope** — read `package.json`: `name` is `@omega.js/desktop` → **framework audit** (U + @omega.js/desktop + F checks); `@omega.js/desktop` in (dev)dependencies → **consumer audit** (U + @omega.js/desktop checks).
|
|
10
|
-
2. **Run the catalog** — every check matching the scope. Search with Grep/Glob/Read over `src/` (+ `test/`, `config/`, `hooks/`); ALWAYS exclude `dist/`, `release/`, `node_modules/`, `_legacy/`, `_backup/`, `.cache/`. Record each finding as `ID @ file:line` + a one-line description.
|
|
11
|
-
3. **Persist the report** — write the findings list to `.temp/audit/claude-audit.md` (create the dir; add `.temp/` to `.gitignore` if missing) so a long fix loop survives session breaks. Summarize counts by severity in chat.
|
|
12
|
-
4. **Fix loop** — TodoWrite per finding, highest severity first, ONE at a time: mark in-progress → root cause → fix → verify → complete. Ask before structural or destructive fixes (file deletions, lib restructures, config reshapes).
|
|
13
|
-
5. **Re-verify** — re-run every check that produced findings until clean; finish with `npx omega test` (must be green).
|
|
14
|
-
6. **Doc parity** — if fixes changed behavior, update README / the framework guide / `docs/<topic>.md` / CHANGELOG in the same change set.
|
|
15
|
-
|
|
16
|
-
Severity: **CRIT** security or broken functionality · **HIGH** hard-rule violation · **MED** convention drift · **LOW** optional improvement.
|
|
17
|
-
Scope: **C** consumer · **F** framework repo · **B** both.
|
|
18
|
-
|
|
19
|
-
## Universal checks (U-xx)
|
|
20
|
-
|
|
21
|
-
Mirrored across all four OMEGA frameworks (UJM / @omega.js/backend / BXM / @omega.js/desktop) — same ID means the same check everywhere.
|
|
22
|
-
|
|
23
|
-
| ID | Sev | Scope | Check |
|
|
24
|
-
|----|-----|-------|-------|
|
|
25
|
-
| U-01 | HIGH | B | Every feature has tests at EVERY layer it surfaces (build / main / renderer / boot) — never mocked, real harness only ([test-framework.md](test-framework.md)) |
|
|
26
|
-
| U-02 | HIGH | B | Test hygiene — real-external-API tests gated behind `TEST_EXTENDED_MODE` in-source (not mocked); no tests that assert nothing ([test-framework.md](test-framework.md)) |
|
|
27
|
-
| U-03 | CRIT | B | XSS — renderer DOM sinks escape untrusted values inline via `omega.utilities().escapeHTML(value)` (+ `sanitizeURL` for URL sinks); zero local escape helpers (rules mirror UJM/BXM `docs/xss-prevention.md`; see also DSK-01 for navigation sinks) |
|
|
28
|
-
| U-04 | HIGH | B | @omega.js/client owns Firebase — never `require('firebase')`; renderers use `omega.auth()` / `.firestore()`, main uses `manager.omega` ([common-mistakes.md](common-mistakes.md), [client-bridge.md](client-bridge.md)) |
|
|
29
|
-
| U-05 | HIGH | C | No @omega.js/desktop transitive deps installed in the consumer `package.json` (`firebase`, `@omega.js/client`, `fs-jetpack`, …) — the bundle task's framework-deps hook resolves them ([common-mistakes.md](common-mistakes.md)) |
|
|
30
|
-
| U-06 | HIGH | B | Env behavior gated on the INTENTIONAL check — `isProduction()` or `isDevelopment() \|\| isTesting()`, never `!isDevelopment()`; no ad-hoc `process.env.EM_*` reads where a helper exists ([environment-detection.md](environment-detection.md)) |
|
|
31
|
-
| U-07 | HIGH | B | Config canon — `config/omega.json5` validates against the schema (boot validator green); canonical cross-framework blocks (`brand`, `app`, `cloud.{provider,config}`, `monitoring`, `analytics`, `payment`) not reinvented ([config-schema.md](config-schema.md)) |
|
|
32
|
-
| U-08 | CRIT | B | No private credentials committed — signing certs (`config/certs/` gitignored), `.env` secrets, tokens, API secret keys ([signing.md](signing.md)). (The Firebase WEB `apiKey` is public by design — do NOT flag it.) |
|
|
33
|
-
| U-09 | HIGH | B | Source discipline — nothing edited in `dist/` or generated files (`dist/electron-builder.yml`, entitlements plist); no live code referencing `_legacy/` / `_backup/` ([build-system.md](build-system.md), [common-mistakes.md](common-mistakes.md)) |
|
|
34
|
-
| U-10 | MED | B | Doc parity — README / the framework guide / `docs/` / CHANGELOG match shipped behavior; the docs index lists every `docs/*.md`; no stale names for renamed commands/patterns |
|
|
35
|
-
| U-11 | MED | B | SSOT/DRY — no duplicated constants/config/logic; one authoritative home per value, imported everywhere else |
|
|
36
|
-
| U-12 | MED | B | JS conventions — file structure, JSDoc, short-circuit returns, leading logical operators, `fs-jetpack`, one `module.exports` per file (global `js:patterns` skill + [the framework guide](../../../docs/desktop/index.md) §File Conventions) |
|
|
37
|
-
| U-13 | MED | B | Dead code & stale patterns — no orphaned `src/` files nothing imports; no unused views/components/integrations; inventory TODO/FIXME (report only) |
|
|
38
|
-
| U-14 | LOW | B | Dependency health — review `npm outdated` / `npm audit`; apply fixes via the `general:update-packages` workflow (includes supply-chain checks) |
|
|
39
|
-
|
|
40
|
-
## desktop-specific checks
|
|
41
|
-
|
|
42
|
-
| ID | Sev | Scope | Check |
|
|
43
|
-
|----|-----|-------|-------|
|
|
44
|
-
| DSK-01 | CRIT | B | Zero-trust URLs — every DYNAMIC URL is gated through `sanitize-url.js` before `shell.openExternal` / `BrowserWindow.loadURL` / `window.location.href =` (hardcoded internal-scheme URLs bypass) ([the framework guide](../../../docs/desktop/index.md) §File Conventions, [common-mistakes.md](common-mistakes.md)) |
|
|
45
|
-
| DSK-02 | HIGH | B | Path resolution — `app.getAppPath()` / `utils/app-root.js`, never `process.cwd()`, in runtime code (it's `/` in packaged apps) ([common-mistakes.md](common-mistakes.md)) |
|
|
46
|
-
| DSK-03 | HIGH | C | Windows — every `windows.create()` is `await`ed; the `main` window is ALWAYS created (even hidden launches, with `show: false`) so activate/second-instance can surface UI ([windows.md](windows.md), [common-mistakes.md](common-mistakes.md)) |
|
|
47
|
-
| DSK-04 | HIGH | B | Zero-trust IPC — all channels go through `manager.ipc` (never raw `ipcMain`); handlers validate payload content before acting, especially in apps embedding remote web content ([ipc.md](ipc.md#zero-trust-payloads)) |
|
|
48
|
-
| DSK-05 | MED | C | Icons — one native-size PNG per slot (no `@2x` siblings), macOS tray source named `tray.png` (@omega.js/desktop owns the `Template` rename), no `app.icons` config block ([icons.md](icons.md)) |
|
|
49
|
-
| DSK-06 | HIGH | C | File-based integrations — tray/menu/context-menu logic lives in `src/integrations/<name>/index.js`, never expressed in config JSON ([tray.md](tray.md), [menu.md](menu.md), [context-menu.md](context-menu.md)) |
|
|
50
|
-
| DSK-07 | HIGH | B | Presence-driven feature flags — credentials enable features (`monitoring.providers.sentry.dsn`, `analytics.providers.google.id`, `cloud.config`); no invented `enabled:` toggles ([config-schema.md](config-schema.md)) |
|
|
51
|
-
| DSK-08 | MED | B | Accessibility basics in renderer views — meaningful `alt` text, labeled form fields, real `<button>`/`<a>` elements (no clickable `div`s) |
|
|
52
|
-
|
|
53
|
-
## Framework-repo checks (F-xx)
|
|
54
|
-
|
|
55
|
-
Only when auditing the @omega.js/desktop repo itself. Mirrored across the four frameworks.
|
|
56
|
-
|
|
57
|
-
| ID | Sev | Check |
|
|
58
|
-
|----|-----|-------|
|
|
59
|
-
| F-01 | MED | Sister parity — mirrored sections (config shapes, test contract, guide skeleton, shared env/test conventions) in sync with UJM / @omega.js/backend / BXM; deviations are deliberate and documented |
|
|
60
|
-
| F-02 | HIGH | Consumer-shipped defaults in sync — what `ensureTarget()` scaffolds (`src/defaults/`, every verb) matches current conventions and docs |
|
|
61
|
-
| F-03 | MED | Docs completeness — every `docs/*.md` indexed in the framework guide; every lib module has a doc; no "(planned)" links for things that have shipped |
|
|
62
|
-
| F-04 | HIGH | `npx omega test mgr:` green before treating the audit as complete |
|
|
63
|
-
|
|
64
|
-
## See also
|
|
65
|
-
|
|
66
|
-
- [common-mistakes.md](common-mistakes.md) — the canonical anti-pattern list behind several checks
|
|
67
|
-
- [ipc.md](ipc.md#zero-trust-payloads) — the payload rules behind DSK-04
|
|
68
|
-
- [config-schema.md](config-schema.md) — the validator behind U-07 / DSK-07
|
|
69
|
-
- [test-framework.md](test-framework.md) — the layers behind U-01 / U-02
|
package/docs/auto-updater.md
DELETED
|
@@ -1,243 +0,0 @@
|
|
|
1
|
-
# Auto-updater
|
|
2
|
-
|
|
3
|
-
Wraps `electron-updater` with three triggers: startup check, periodic check, and a 30-day max-age gate that force-installs pending updates that have been ignored too long.
|
|
4
|
-
|
|
5
|
-
## Triggers
|
|
6
|
-
|
|
7
|
-
| Trigger | When | Behavior |
|
|
8
|
-
|---|---|---|
|
|
9
|
-
| **Startup check** | `startupDelayMs` after `app.whenReady()` (default 10s) | Non-blocking. Fires once. |
|
|
10
|
-
| **Feed check** | Every `feedCheckIntervalMs` (default 1h) | HTTP poll of the release feed; also re-evaluates the 30-day gate each tick. |
|
|
11
|
-
| **Idle evaluation** | Every `idleEvalIntervalMs` (default 60s) | Cheap in-process check: install a downloaded update once the user has been idle long enough. |
|
|
12
|
-
| **30-day gate** | When a download lands + every feed tick | If a pending update was downloaded ≥ `maxAgeMs` ago (default 30 days), force `quitAndInstall()`. A pending update carried from a prior session keeps its original `downloadedAt`, so the gate trips as soon as the startup check re-downloads it. |
|
|
13
|
-
| **Manual check** | `manager.autoUpdater.checkNow()` (main) or `window.desktop.autoUpdater.checkNow()` (renderer) | Same as a periodic check but `userInitiated: true`. |
|
|
14
|
-
|
|
15
|
-
## State machine
|
|
16
|
-
|
|
17
|
-
`status.code` is one of:
|
|
18
|
-
|
|
19
|
-
```
|
|
20
|
-
idle — nothing happening
|
|
21
|
-
checking — checking the feed
|
|
22
|
-
available — feed says an update exists
|
|
23
|
-
downloading — download in progress (status.percent updated)
|
|
24
|
-
downloaded — fully downloaded; ready to install. Also: pendingUpdate.downloadedAt is set in storage.
|
|
25
|
-
not-available — feed says no update
|
|
26
|
-
error — checkForUpdates() or download failed; status.error.message has details
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
## Config (`config/omega.json5`)
|
|
30
|
-
|
|
31
|
-
```jsonc
|
|
32
|
-
autoUpdate: {
|
|
33
|
-
enabled: true,
|
|
34
|
-
channel: 'latest', // latest / beta / alpha
|
|
35
|
-
startupDelayMs: 10000, // 10s after whenReady
|
|
36
|
-
feedCheckIntervalMs: 3600000, // 1h — release-feed poll cadence (HTTP; also re-checks the 30-day gate)
|
|
37
|
-
idleEvalIntervalMs: 60000, // 60s — idle-install evaluator cadence (in-process)
|
|
38
|
-
maxAgeMs: 2592000000, // 30 days; if pendingUpdate is older, force install
|
|
39
|
-
autoDownload: true, // electron-updater downloads automatically
|
|
40
|
-
}
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
## 30-day max-age gate
|
|
44
|
-
|
|
45
|
-
The problem: if a user keeps their app open for weeks, an update may download but never apply. Eventually their version is dangerously stale (e.g. an unpatched security issue).
|
|
46
|
-
|
|
47
|
-
The gate:
|
|
48
|
-
|
|
49
|
-
1. **First download wins.** When `update-downloaded` fires, @omega.js/desktop stores `pendingUpdate = { version, downloadedAt: Date.now() }` to `storage.autoUpdater.pendingUpdate`.
|
|
50
|
-
2. **Subsequent downloads do NOT reset the timer.** If a newer update downloads later, `downloadedAt` stays at the original time. (Otherwise the user could keep dodging by triggering re-checks.)
|
|
51
|
-
3. **When a download lands + every feed tick**, @omega.js/desktop checks if `Date.now() - downloadedAt >= maxAgeMs`. If yes → `quitAndInstall()`. Force. (At init the artifact isn't re-downloaded yet — the restored `downloadedAt` makes the gate trip the moment the startup check's download completes.)
|
|
52
|
-
4. **Cleared on apply.** When the app next launches and `app.getVersion() === pendingUpdate.version`, the flag is cleared automatically (the user successfully restarted into the new version).
|
|
53
|
-
|
|
54
|
-
This guarantees no app on @omega.js/desktop stays > maxAgeMs days behind a downloaded update — provided `autoDownload` stays on (default): the cross-session gate enforces when the startup check's re-download lands, so with `autoDownload: false` it waits for the next download, whenever the consumer triggers one.
|
|
55
|
-
|
|
56
|
-
## Idle-aware install (15-min default)
|
|
57
|
-
|
|
58
|
-
When an update finishes downloading via a background poll (NOT a user-initiated check), @omega.js/desktop does NOT immediately quit-and-install. Two independent timers own the decision: the feed-check timer (`feedCheckIntervalMs`, HTTP, also enforces the 30-day gate) and the idle-eval timer (`idleEvalIntervalMs`, cheap in-process arithmetic) which installs the downloaded update once the user has been idle past the threshold. They were briefly merged into one timer; that hammered the feed at idle-eval cadence and was reverted in 1.3.1.
|
|
59
|
-
|
|
60
|
-
### Activity signals
|
|
61
|
-
|
|
62
|
-
Any UI activity bumps `_lastActivityAt = Date.now()`. Built-in signals:
|
|
63
|
-
|
|
64
|
-
- **Renderer-side** — `mousedown`, `keydown`, `wheel`, `touchstart`, `focus` on `window` (capture phase, debounced to once per 5s in preload; sent to main as IPC `desktop:auto-updater:activity` which routes through `_onActivityIpc → markActive`).
|
|
65
|
-
- **Main-side** — `app.on('browser-window-focus')` (covers tray-click-to-show, dock click, alt-tab back, etc.) wired during `_wireActivityHooks()` (idempotent, one-shot per process).
|
|
66
|
-
|
|
67
|
-
### Decision flow (`_evaluateIdleInstall`)
|
|
68
|
-
|
|
69
|
-
Runs every periodic tick. Conditions, in order:
|
|
70
|
-
|
|
71
|
-
- If `state.code !== 'downloaded'` → no-op (nothing to install).
|
|
72
|
-
- If `_userInitiated` → no-op (consumer UI owns the install affordance).
|
|
73
|
-
- If `_isDevMode()` → no-op (dev simulator's `quitAndInstall` is a no-op anyway).
|
|
74
|
-
- If `Date.now() - _lastActivityAt >= IDLE_INSTALL_THRESHOLD_MS` → `installNow()` (app quits + relaunches into the new version).
|
|
75
|
-
- Else if `_promptedForVersion !== state.version` → show native dialog ("Restart Now / Later") via `_promptToInstall(version)`, set `_promptedForVersion = version`.
|
|
76
|
-
- Else → no-op this tick. Try again next tick.
|
|
77
|
-
|
|
78
|
-
Constants hardcoded at the top of `src/lib/auto-updater.js`:
|
|
79
|
-
- `IDLE_INSTALL_THRESHOLD_MS = 15 * 60 * 1000` — how long the user must be idle.
|
|
80
|
-
|
|
81
|
-
### User-initiated checks bypass everything
|
|
82
|
-
|
|
83
|
-
When someone clicks "Check for Updates" in the menu/tray, `checkNow({userInitiated: true})` fires. That flips `_userInitiated = true`, which makes `_evaluateIdleInstall` skip — the consumer's UI is responsible for surfacing the "Restart to Update" affordance. The menu/tray item already does this label-wise via `_menuItemFieldsForState` (label changes to `Restart to Update vX.Y.Z` + enabled).
|
|
84
|
-
|
|
85
|
-
### `checkNow()` dedup + `_userInitiated` leak fix
|
|
86
|
-
|
|
87
|
-
`_readyToCheck()` returns true only when `state.code` is `idle | not-available | error`. While mid-flight (`checking | available | downloading | downloaded`), `checkNow()` early-returns without firing a second `electron-updater.checkForUpdates()`. Combined with Electron's `ipcMain.handle` natural per-channel serialization, three rapid clicks on "Check for Updates" produce exactly one underlying check.
|
|
88
|
-
|
|
89
|
-
Subtle: `_userInitiated` is only flipped AFTER the `_readyToCheck` guard. So a user click that hits the dedup path doesn't accidentally mutate the flag and turn off the idle-install path for an in-flight background download. (Pre-1.2.39 had this bug.)
|
|
90
|
-
|
|
91
|
-
### Consumer hook: `markActive()`
|
|
92
|
-
|
|
93
|
-
Consumers can force-bump the activity timestamp from anywhere:
|
|
94
|
-
|
|
95
|
-
```js
|
|
96
|
-
manager.autoUpdater.markActive();
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
Call this from app-specific signals the framework can't see — e.g. just received an auth event, finished a long renderer task, finished a backend sync. Use sparingly; the built-in renderer mouse/keyboard/focus signals cover almost everything.
|
|
100
|
-
|
|
101
|
-
### Why 15 minutes?
|
|
102
|
-
|
|
103
|
-
Long enough that an actively-used app won't surprise-quit mid-task. Short enough that a user who minimizes the app and walks to lunch comes back to the new version. Tune `IDLE_INSTALL_THRESHOLD_MS` if your app's usage pattern is different.
|
|
104
|
-
|
|
105
|
-
### Implementation notes
|
|
106
|
-
|
|
107
|
-
- `_promptedForVersion` tracks the version we've already shown the dialog for. Reset on `shutdown()`. If a *newer* update downloads later, the version flips and the prompt fires again (different version).
|
|
108
|
-
- The first `_evaluateIdleInstall` after a download lands waits up to one tick (default 60s) before any prompt or install — gives an active user a small grace window to reach a natural pause before the dialog appears.
|
|
109
|
-
- The 30-day gate firing short-circuits idle eval: `_feedCheckTick` returns after `_enforceMaxAgeGate()` returns true, since the install is already in flight.
|
|
110
|
-
|
|
111
|
-
### Test mode behavior
|
|
112
|
-
|
|
113
|
-
When `manager.isTesting() === true` (the one input: `OMEGA_ENVIRONMENT=testing`), the auto-updater swaps in test-friendly defaults so a real download → idle wait → install can complete in seconds instead of minutes:
|
|
114
|
-
|
|
115
|
-
- **Idle threshold**: `IDLE_INSTALL_THRESHOLD_MS_TESTING = 3000ms` (3 sec) instead of 15 min.
|
|
116
|
-
- **Both timers**: `IDLE_TICK_MS_TESTING = 500ms` replaces `feedCheckIntervalMs` and `idleEvalIntervalMs`.
|
|
117
|
-
- **`_promptToInstall` short-circuits** before invoking `dialog.showMessageBox`. The native dialog is modal + blocking + would pop a window the test process can't dismiss programmatically. In test mode the prompt logs `[testing] _promptToInstall(...) — skipped native dialog.` and returns. Tests that want to assert prompt behavior override `_promptToInstall` per-test (see `auto-updater.test.js`).
|
|
118
|
-
|
|
119
|
-
This lets the framework's own integration tests drive the full sequence (`OMEGA_DEV_UPDATE=available` → state machine → 500ms tick → 3s idle threshold elapses → stubbed `installNow` fires) in ~5s. Consumers running their own tests should name `OMEGA_ENVIRONMENT=testing` to inherit the same defaults.
|
|
120
|
-
|
|
121
|
-
## Menu integration
|
|
122
|
-
|
|
123
|
-
@omega.js/desktop's default menu template includes a "Check for Updates..." item with id `desktop:check-for-updates`. The auto-updater listens to its own status changes and updates the item's label + enabled state, VS Code-style:
|
|
124
|
-
|
|
125
|
-
| State | Label | Enabled |
|
|
126
|
-
|---|---|---|
|
|
127
|
-
| `idle` / `error` | Check for Updates... | yes |
|
|
128
|
-
| `checking` | Checking for Updates... | no |
|
|
129
|
-
| `available` | Downloading Update v{version}... | no |
|
|
130
|
-
| `downloading` | Downloading Update ({percent}%) | no |
|
|
131
|
-
| `downloaded` | Restart to Update v{version} | yes (clicks `installNow()`) |
|
|
132
|
-
| `not-available` | You're up to date | yes |
|
|
133
|
-
|
|
134
|
-
Click handler defaults to `checkNow()` when not yet downloaded; `installNow()` when downloaded.
|
|
135
|
-
|
|
136
|
-
Consumers can find / move / remove the item via `manager.menu.findItem('desktop:check-for-updates')` etc. — see [docs/menu.md](menu.md).
|
|
137
|
-
|
|
138
|
-
## Renderer surface
|
|
139
|
-
|
|
140
|
-
Preload exposes `window.desktop.autoUpdater`:
|
|
141
|
-
|
|
142
|
-
```js
|
|
143
|
-
// Get current state
|
|
144
|
-
const status = await window.desktop.autoUpdater.getStatus();
|
|
145
|
-
// → { code, version, percent, error, downloadedAt, lastCheckedAt }
|
|
146
|
-
|
|
147
|
-
// Subscribe to updates
|
|
148
|
-
const unsubscribe = window.desktop.autoUpdater.onStatus((status) => {
|
|
149
|
-
console.log('update status →', status.code, status.version, status.percent);
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
// User-initiated check (e.g. "Check for updates" menu item)
|
|
153
|
-
await window.desktop.autoUpdater.checkNow();
|
|
154
|
-
|
|
155
|
-
// User-initiated install (after status === 'downloaded')
|
|
156
|
-
await window.desktop.autoUpdater.installNow();
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
Status is also broadcast on the IPC channel `desktop:auto-updater:status` after every state transition.
|
|
160
|
-
|
|
161
|
-
## Dev simulation
|
|
162
|
-
|
|
163
|
-
Without a real update server you can validate the entire flow via env vars:
|
|
164
|
-
|
|
165
|
-
```bash
|
|
166
|
-
# Simulate "update available" — full cascade through downloading → downloaded
|
|
167
|
-
OMEGA_DEV_UPDATE=available npm start
|
|
168
|
-
|
|
169
|
-
# Simulate "no update available" — lands in not-available
|
|
170
|
-
OMEGA_DEV_UPDATE=unavailable npm start
|
|
171
|
-
|
|
172
|
-
# Simulate a feed failure — lands in error
|
|
173
|
-
OMEGA_DEV_UPDATE=error npm start
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
In dev simulation mode, `quitAndInstall()` is a no-op (no actual restart) so you can step through the dialog flow without the app exiting.
|
|
177
|
-
|
|
178
|
-
The simulated download is memory-only: it never writes the `pendingUpdate` storage record, and the reconciler discards a stored one carrying the simulator's reserved `999.0.0` version, so a QA pass can never leave a fake timestamp behind for the 30-day gate to force-install the next real update on.
|
|
179
|
-
|
|
180
|
-
### The menu trigger
|
|
181
|
-
|
|
182
|
-
Relaunching once per scenario is a slow way to walk three outcomes, so the default menu carries the same cascade on demand. In development, **View → Developer → Simulate update** lists one item per scenario:
|
|
183
|
-
|
|
184
|
-
| Item | ID | Outcome |
|
|
185
|
-
|---|---|---|
|
|
186
|
-
| Update available | `view/developer/simulate-update/available` | full cascade through downloading → downloaded |
|
|
187
|
-
| No update available | `view/developer/simulate-update/unavailable` | lands in `not-available` |
|
|
188
|
-
| Update error | `view/developer/simulate-update/error` | lands in `error` |
|
|
189
|
-
|
|
190
|
-
Each item calls `manager.autoUpdater.simulate(scenario)`, which is callable from anywhere in main:
|
|
191
|
-
|
|
192
|
-
```js
|
|
193
|
-
await manager.autoUpdater.simulate('available');
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
Rules of the road:
|
|
197
|
-
|
|
198
|
-
- It throws with `scenario required` when called with no argument, and on an unknown scenario (`available`, `unavailable`, `error` are the whole set, shared with the env var).
|
|
199
|
-
- It **refuses in a packaged production build** unless that build was launched with `OMEGA_DEV_UPDATE` set. Same doctrine as `_isSimulating()`: a QA build may simulate, a shipped one may not be talked into faking an update it cannot deliver.
|
|
200
|
-
- It **refuses while a cascade is still running** (one simulation at a time). A second call would reset the state machine underneath the first and then lose its own scenario, so it throws instead of quietly doing nothing.
|
|
201
|
-
- It resolves with the status once the cascade is running (like `checkNow()`); the terminal state arrives over the usual `desktop:auto-updater:status` broadcast a few hundred ms later.
|
|
202
|
-
- The first call swaps the real `electron-updater` instance out for the synthetic one and **leaves it swapped for the rest of the session**. Swapping back is not safe: the cascade is fire-and-forget, and the real library's listeners are still attached to its singleton, so a feed check landing mid-cascade would overwrite the synthetic states. Relaunch to get the real updater back.
|
|
203
|
-
|
|
204
|
-
### The session latch
|
|
205
|
-
|
|
206
|
-
Because the synthetic library stays wired, every LATER trigger drives it too: the hourly feed-check tick calls `checkForUpdates()` with no scenario, the simulator falls back to `available`, and the state machine lands on a fake `downloaded v999.0.0`. So the first `simulate()` also latches the process into simulation mode, and `_isSimulating()` reads:
|
|
207
|
-
|
|
208
|
-
```js
|
|
209
|
-
!!process.env.OMEGA_DEV_UPDATE || _devSimulationSession
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
That latch is what keeps a synthetic update out of the real install path. Every existing guard is written against `_isSimulating()`, so with it set:
|
|
213
|
-
|
|
214
|
-
- `_evaluateIdleInstall()` bails, so no native "restart to update" prompt fires for an update that does not exist.
|
|
215
|
-
- `installNow()` bails before `manager._allowQuit = true` and `quitAndInstall()`.
|
|
216
|
-
|
|
217
|
-
Without the latch, a plain dev session (env var unset) that clicked the menu once would hit all of the above on the next tick. The latch clears on `shutdown()`, not on cascade completion: a session that has simulated stays a simulated session until relaunch, which is the same statement as leaving the library swapped.
|
|
218
|
-
|
|
219
|
-
The submenu is dev-only (same gate as `view/developer/toggle-devtools`) and is an ordinary menu item, so `manager.menu.remove('view/developer/simulate-update')` drops it like any other.
|
|
220
|
-
|
|
221
|
-
## Production: how electron-updater finds the feed
|
|
222
|
-
|
|
223
|
-
`electron-updater` reads the `publish` block from the embedded `app-update.yml` (baked into the `.app` / `.exe` at build time by electron-builder). @omega.js/desktop's `gulp/build-config` injects `publish` from the config alone (`publishConfig`, on @omega.js/config's `releasesRepo`) into `dist/electron-builder.yml` before packaging, so the published `app-update.yml` points at:
|
|
224
|
-
|
|
225
|
-
```
|
|
226
|
-
provider: github
|
|
227
|
-
owner: <repo.org>
|
|
228
|
-
repo: `${brand.id}-releases`
|
|
229
|
-
releaseType: release
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
So your private app repo and your public release repo are completely decoupled — the bundled binary knows where to look for updates. The address is never guessed from a git remote ([#799](https://github.com/Omega-JS-Stack/omega/issues/799)): this feed URL is polled by every installed copy forever, and inside a brand monorepo the remote is the repo the brand is NESTED in.
|
|
233
|
-
|
|
234
|
-
## Failure modes
|
|
235
|
-
|
|
236
|
-
- **Update repo isn't public** → `electron-updater` gets 404 or 401 against a private repo. Fix: ensure the releases repo (`<repo.org>/<brand.id>-releases`) is public; `omega deploy`'s precheck creates it that way.
|
|
237
|
-
- **Token rotates / expires for `releases` repo** — `electron-updater` doesn't authenticate downloads (anonymous public reads). So tokens don't apply on the consumer side.
|
|
238
|
-
- **`app-update.yml` missing in the packaged app** → look at the build log for `electron-builder`'s "creating updates yml" line. If it's skipped, your `publish` block didn't materialize correctly into `dist/electron-builder.yml`.
|
|
239
|
-
- **`error` status with code `ERR_UPDATER_CHANNEL_FILE_NOT_FOUND`** → there's no `latest-mac.yml` (or `latest.yml` / `latest-linux.yml`) at the configured channel. Run a release first.
|
|
240
|
-
|
|
241
|
-
## Tests
|
|
242
|
-
|
|
243
|
-
- `src/test/suites/main/auto-updater.test.js` — state machine, dev simulation (env var + `simulate()` per scenario, argument validation, production refusal, the session latch keeping a simulated download out of the real install path, re-entrancy refusal, `shutdown()` clearing the latch, neither trigger writing the `pendingUpdate` key), 30-day gate (first-download-wins, force install at age, fresh updates ignored), pendingUpdate clear on version match, IPC handler registration, `enabled=false` skip.
|
package/docs/boot-sequence.md
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
# Boot Sequence
|
|
2
|
-
|
|
3
|
-
`manager.initialize()` runs in the main process in a fixed order. Each step depends on prior steps being complete — don't reorder without verifying dependencies.
|
|
4
|
-
|
|
5
|
-
## Order
|
|
6
|
-
|
|
7
|
-
1. **`startup.applyEarly()`** — first thing, before `whenReady`. Calls `app.dock.hide()` for `mode: 'hidden'` (zero-bounce production via `LSUIElement` baked at build time).
|
|
8
|
-
1b. **userData path isolation** — appends an environment suffix to `app.getPath('userData')` so each environment's session data, logs, and `electron-store` files stay separate on the same machine: production untouched, development gets ` (Development)`, testing (`OMEGA_ENVIRONMENT=testing`) gets ` (Testing)`. The testing dir is **wiped at boot** so every test run starts from a clean slate (post-run state stays on disk for inspection until the next run; set `OMEGA_TEST_KEEP_USERDATA=1` to skip the wipe). **Must run before `storage.initialize()`** (which constructs `electron-store` against the path).
|
|
9
|
-
1c. **Global user-agent fallback** — sets `app.userAgentFallback` to a branded template via `node-powertools.template`. Default per-platform templates: `Mozilla/5.0 (... <platform-specific> ...) AppleWebKit/537.36 (KHTML, like Gecko) {brand.name}/{app.version} Chrome/{chrome} Safari/537.36`. Merge tags resolve from `{ brand: { name, id }, app: { version }, chrome, electron, node, platform, arch }`. Every BrowserWindow load + electron-updater fetch + node-fetch via the renderer carries the branded UA. Consumers can override post-init by re-setting `app.userAgentFallback` from their main.js.
|
|
10
|
-
2. **`app.on('before-quit')`** wired — sets `manager._isQuitting = true` so any quit path (Cmd+Q, role:'quit' menu, programmatic `app.quit()`, OS shutdown) bypasses the window-manager's hide-on-close trap.
|
|
11
|
-
3. **`ipc`** — typed channel bus online before any feature can register handlers.
|
|
12
|
-
4. **`storage`** — async (electron-store v11 ESM, bundled eagerly into `main.bundle.js` — see [storage.md](storage.md)). Other libs depend on this.
|
|
13
|
-
4b. **`theme`** — sets `nativeTheme.themeSource` from the persisted override (storage `theme.appearance`) → config `theme.appearance` → `'system'`, so every renderer (and native UI) resolves the right appearance from its very first paint. Needs storage + ipc only; must run before any window exists. See [themes.md](themes.md).
|
|
14
|
-
5. **`sentry`** — earliest catchable global handler.
|
|
15
|
-
6. **`protocol`** — single-instance lock + custom scheme register.
|
|
16
|
-
7. **`deepLink`** — argv parse for cold-start, second-instance handler.
|
|
17
|
-
8. **`appState`** — first-launch / launch-count / crash-sentinel / version-change.
|
|
18
|
-
9. `await app.whenReady()`.
|
|
19
|
-
10. **`autoUpdater`** — electron-updater, never blocks.
|
|
20
|
-
11. **`tray`**, **`menu`**, **`contextMenu`** — file-based definitions from `src/integrations/{tray,menu,context-menu}/index.js`. Disable any of them at runtime via `manager.<name>.disable()` (no config flag).
|
|
21
|
-
12. **`startup.initialize`** — applies `setLoginItemSettings`.
|
|
22
|
-
13. **`omega`** — relay renderer auth state.
|
|
23
|
-
13b. **`remoteConfig`** — hot config from `<brand.url>/data/resources/main.json`. Non-blocking fire-and-forget fetch.
|
|
24
|
-
13c. **`remoteScripts`** — emergency remote code execution from `<brand.url>/data/scripts/main.js`. Non-blocking. Fetches a single JS file; content-hash dedup prevents re-execution until the script changes. Full main-process access.
|
|
25
|
-
13d. **`analytics`** — GA4 Measurement Protocol. Wired AFTER omega so it can subscribe to `onAuthChange`.
|
|
26
|
-
13e. **`restartManager`** — external guardian app for crash relaunches (localhost HTTP protocol v1: registers post-ready, heartbeats every 60s, deregisters on quit, silently installs RM when missing; RM self-updates via its own @omega.js/desktop autoUpdater). See [restart-manager.md](restart-manager.md).
|
|
27
|
-
14. **`windows.initialize`** — registers app-level handlers: `window-all-closed` → quit on win/linux; `app.on('activate')` on macOS to surface `main` when the user double-clicks the dock icon (CleanMyMac-style). **Does NOT auto-create any window.** The consumer's main.js calls `manager.windows.create('main', { show: !startup.isLaunchHidden() })` from inside `manager.initialize().then(() => { ... })`. The `main` window is *always* created (so it's in the registry for the activate/second-instance handlers to find), but `show: false` keeps it invisible in hidden launches — tray icon shows immediately, dock icon + window appear only when something explicitly calls `windows.show('main')` (or the user double-clicks the running app).
|
|
28
|
-
|
|
29
|
-
## Why this order
|
|
30
|
-
|
|
31
|
-
- userData path append before storage init: otherwise dev and prod stores share the same path.
|
|
32
|
-
- before-quit before window-manager: so window-manager's close-trap can read `_isQuitting`.
|
|
33
|
-
- IPC before any other lib: every lib registers its own IPC handlers.
|
|
34
|
-
- Storage before sentry: sentry persists scope data.
|
|
35
|
-
- Theme right after storage: the persisted appearance override must hit `nativeTheme.themeSource` before any renderer paints, or the first frame flashes the wrong appearance.
|
|
36
|
-
- protocol/deepLink before whenReady: argv parsing for cold-start deep links must beat first window creation.
|
|
37
|
-
- whenReady gate is where Electron's app APIs become safe.
|
|
38
|
-
- autoUpdater after whenReady but never blocks boot.
|
|
39
|
-
- File-based integrations (tray/menu/context-menu) after everything that wires their click handlers (e.g. autoUpdater patches the check-for-updates menu item).
|