@omega.js/desktop 0.51.0 → 0.53.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 +18 -13
- 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.js +1 -1
- package/dist/commands/build.js +2 -2
- package/dist/commands/cdp/capture.js +2 -2
- 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/clean.js +2 -2
- package/dist/commands/deploy.js +4 -4
- package/dist/commands/finalize-release.js +4 -4
- package/dist/commands/install.js +3 -3
- package/dist/commands/launch.js +2 -2
- package/dist/commands/lib/deploy-precheck.js +3 -3
- package/dist/commands/lib/ensure-target.js +6 -6
- package/dist/commands/package.js +2 -2
- package/dist/commands/publish.js +2 -2
- package/dist/commands/release.js +3 -3
- package/dist/commands/runner.js +145 -48
- package/dist/commands/sign-windows.js +5 -5
- package/dist/commands/test.js +4 -4
- package/dist/commands/update.js +2 -2
- package/dist/commands/validate-certs.js +4 -4
- package/dist/commands/version.js +3 -3
- package/dist/defaults/.github/workflows/build.yml +2 -0
- package/dist/defaults/AGENTS.md +17 -8
- package/dist/defaults/config/omega.json5 +7 -7
- package/dist/defaults/hooks/build/post.js +1 -1
- package/dist/defaults/hooks/build/pre.js +1 -1
- package/dist/defaults/hooks/deploy/pre.js +1 -1
- package/dist/defaults/hooks/release/post.js +1 -1
- package/dist/defaults/hooks/release/pre.js +1 -1
- 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/integrations/context-menu/index.js +2 -2
- package/dist/defaults/src/integrations/menu/index.js +3 -3
- package/dist/defaults/src/integrations/tray/index.js +3 -3
- package/dist/defaults/src/main.js +3 -5
- package/dist/defaults/src/preload.js +3 -5
- package/dist/defaults/test/README.md +2 -2
- package/dist/gulp/main.js +9 -10
- package/dist/gulp/tasks/audit.js +7 -7
- 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 +28 -28
- 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 +398 -483
- package/dist/preload.js +236 -176
- package/dist/renderer.js +417 -391
- package/dist/runner/job-started.js +1 -1
- 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 +1 -1
- 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/cli.test.js +2 -2
- package/dist/test/suites/build/config-schema.test.js +5 -5
- package/dist/test/suites/build/defaults-scaffold.test.js +2 -2
- package/dist/test/suites/build/deploy-hook.test.js +3 -3
- package/dist/test/suites/build/ensure-target.test.js +2 -2
- 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 +1 -1
- 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/renderer-auth-bridge.test.js +211 -78
- package/dist/test/suites/build/runner.test.js +612 -5
- package/dist/test/suites/build/sentry.test.js +2 -2
- 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 +2 -2
- 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} +27 -7
- package/dist/test/utils/extended-mode-warning.js +1 -1
- package/dist/utils/boot-harness.js +56 -0
- package/dist/utils/mode-helpers.js +2 -15
- 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/environment.js +11 -30
- package/dist/vendor/config/index.js +13 -12
- package/dist/vendor/config/load.js +1 -2
- package/dist/vendor/config/platforms.js +1 -1
- package/dist/vendor/config/repo.js +24 -0
- package/dist/vendor/config/retired-keys.js +2 -2
- package/dist/vendor/config/schema.js +5 -8
- package/dist/vendor/config/site-global.js +2 -3
- package/dist/vendor/config/validate.js +1 -2
- package/dist/vendor/config/winback.js +1 -1
- package/dist/vendor/devkit/actions-secrets.js +1 -1
- package/dist/vendor/devkit/attach-log-file.js +1 -1
- package/dist/vendor/devkit/brand-version.js +162 -1
- package/dist/vendor/devkit/build-json.js +1 -1
- package/dist/vendor/devkit/cli-router.js +3 -4
- package/dist/vendor/devkit/defaults-engine.js +9 -11
- package/dist/vendor/devkit/deploy.js +64 -4
- package/dist/vendor/devkit/git-remote.js +78 -1
- package/dist/vendor/devkit/local.js +53 -3
- package/dist/vendor/devkit/lockfile.js +127 -0
- package/dist/vendor/devkit/merge-line-files.js +2 -3
- package/dist/vendor/devkit/pack-local.js +4 -7
- package/dist/vendor/devkit/preludes/origin-heal.js +35 -49
- package/dist/vendor/devkit/target-secrets.js +34 -45
- package/dist/vendor/devkit/test/runner-core.js +6 -6
- 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/docs/analytics.md +8 -8
- package/docs/app-state.md +19 -19
- package/docs/audit.md +4 -4
- package/docs/{client-bridge.md → auth.md} +88 -73
- package/docs/auto-updater.md +8 -8
- package/docs/boot-sequence.md +11 -6
- package/docs/build-system.md +1 -1
- package/docs/cdp-debugging.md +1 -1
- package/docs/common-mistakes.md +7 -7
- package/docs/config-schema.md +1 -1
- package/docs/context-menu.md +13 -13
- package/docs/context.md +11 -11
- package/docs/css.md +3 -9
- package/docs/deep-link.md +25 -25
- package/docs/environment-detection.md +16 -16
- package/docs/fontawesome.md +7 -5
- package/docs/hooks.md +8 -8
- package/docs/index.md +39 -28
- package/docs/ipc.md +10 -10
- package/docs/lib-modules.md +9 -9
- package/docs/logging.md +12 -14
- package/docs/menu.md +18 -18
- package/docs/releasing.md +4 -2
- package/docs/remote-config.md +9 -9
- package/docs/remote-scripts.md +12 -12
- package/docs/restart-manager.md +8 -8
- package/docs/runner.md +12 -10
- package/docs/sentry.md +4 -4
- package/docs/shared/analytics.md +1 -1
- package/docs/shared/brands.md +1 -1
- package/docs/shared/breaking-changes.md +79 -13
- package/docs/shared/config.md +24 -21
- package/docs/shared/deploys.md +30 -7
- package/docs/shared/local-dev.md +5 -3
- package/docs/shared/logging.md +1 -1
- package/docs/shared/monitoring.md +5 -5
- package/docs/shared/testing.md +2 -2
- package/docs/shared/theming.md +1 -1
- package/docs/shared/translation.md +19 -10
- package/docs/signing.md +1 -1
- package/docs/startup.md +16 -16
- package/docs/storage.md +11 -11
- package/docs/templating.md +3 -3
- package/docs/test-boot-layer.md +12 -12
- package/docs/test-framework.md +17 -17
- package/docs/themes.md +7 -7
- package/docs/tooltips.md +2 -2
- package/docs/tray.md +31 -31
- package/docs/usage.md +7 -7
- package/docs/verts.md +1 -1
- package/docs/windows.md +22 -22
- package/package.json +3 -4
- 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/docs/audit.md
CHANGED
|
@@ -24,10 +24,10 @@ Mirrored across all four OMEGA frameworks (UJM / @omega.js/backend / BXM / @omeg
|
|
|
24
24
|
|----|-----|-------|-------|
|
|
25
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
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
|
|
28
|
-
| U-04 | HIGH | B | @omega.js/client owns Firebase
|
|
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 @omega.js/web and @omega.js/extension `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` / `omega.firestore`, main uses `omega.auth` ([common-mistakes.md](common-mistakes.md), [auth.md](auth.md)) |
|
|
29
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
|
|
30
|
+
| U-06 | HIGH | B | Env behavior gated on the INTENTIONAL check: `isProduction()` or `isDevelopment() \|\| isTesting()`, never `!isDevelopment()`; no ad-hoc `process.env` reads where a helper exists ([environment-detection.md](environment-detection.md)) |
|
|
31
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
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
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)) |
|
|
@@ -44,7 +44,7 @@ Mirrored across all four OMEGA frameworks (UJM / @omega.js/backend / BXM / @omeg
|
|
|
44
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
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
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
|
|
47
|
+
| DSK-04 | HIGH | B | Zero-trust IPC: all channels go through `omega.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
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
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
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)) |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Auth: `omega.auth` and the state sync
|
|
2
2
|
|
|
3
|
-
@omega.js/desktop keeps Firebase auth state in sync across all processes (main + every renderer window).
|
|
3
|
+
@omega.js/desktop keeps Firebase auth state in sync across all processes (main + every renderer window). **Main is the source of truth**, renderers reflect: the same pattern as @omega.js/extension's background and page contexts. In main the lib is `omega.auth` ([src/lib/auth.js](../src/lib/auth.js)); in a renderer `omega.auth` is @omega.js/client's Auth module. Both sides hold the account as one `User` (`@omega.js/account`), never null.
|
|
4
4
|
|
|
5
5
|
## Why this exists
|
|
6
6
|
|
|
@@ -16,7 +16,7 @@ The bridge handles all three.
|
|
|
16
16
|
|
|
17
17
|
```
|
|
18
18
|
┌─────────────────────────────────────────────────────────────┐
|
|
19
|
-
│ MAIN (
|
|
19
|
+
│ MAIN (lib/auth.js, omega.auth) │
|
|
20
20
|
│ - Owns Firebase Auth instance ("omega-auth" app) │
|
|
21
21
|
│ - Source of truth for auth state │
|
|
22
22
|
│ - Listens for desktop:auth:* IPC from renderers │
|
|
@@ -32,13 +32,13 @@ The bridge handles all three.
|
|
|
32
32
|
|
|
33
33
|
### Auth flow: deep-link → all processes signed in
|
|
34
34
|
|
|
35
|
-
**Consumers never collect credentials.** There is no login form to build
|
|
35
|
+
**Consumers never collect credentials.** There is no login form to build: call `omega.openAuthFlow()` (documented in `lib/auth-flow.js`), which opens the brand website's sign-in page in the user's browser and receives the result through the deep link below.
|
|
36
36
|
|
|
37
|
-
1. User signs in on the website.
|
|
38
|
-
2. @omega.js/desktop's deep-link `auth/token` built-in
|
|
37
|
+
1. User signs in on the website. The website's token page mints a custom token and opens `myapp://auth/token?authToken=XYZ` (deep link).
|
|
38
|
+
2. @omega.js/desktop's deep-link `auth/token` built-in calls `omega.auth.handleToken(token)`.
|
|
39
39
|
3. Main calls `signInWithCustomToken(auth, token)` against its own Firebase Auth → main is now signed in.
|
|
40
40
|
4. Main broadcasts `desktop:auth:sign-in-with-token` IPC with the same token to all renderer windows.
|
|
41
|
-
5. Each renderer receives the broadcast, calls `omega.auth
|
|
41
|
+
5. Each renderer receives the broadcast, calls `omega.auth.signInWithCustomToken(token)` against its own (@omega.js/client-managed) Firebase Auth → all renderers signed in with the same user.
|
|
42
42
|
6. Tokens are NOT stored — they expire in 1 hour. Auth state persists via Firebase's built-in IndexedDB persistence.
|
|
43
43
|
|
|
44
44
|
### Auth flow: renderer load → sync with main
|
|
@@ -53,68 +53,79 @@ When a renderer window opens (cold or warm), it asks main for the current state:
|
|
|
53
53
|
|
|
54
54
|
### Sign-out flow
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
Main code calls `omega.auth.signOut()`; a renderer calls `omega.signOut()`, which a click on any `.omega-signout` element runs (@omega.js/client's trigger: confirm, then `desktop:auth:sign-out` to main). Either way:
|
|
57
57
|
|
|
58
58
|
1. Main signs out its own Firebase.
|
|
59
59
|
2. Main broadcasts `desktop:auth:sign-out` IPC to all renderers.
|
|
60
|
-
3. Each renderer signs out its own Firebase.
|
|
60
|
+
3. Each renderer signs out its own Firebase, on that broadcast alone (the clicked window included), so nothing signs out twice.
|
|
61
61
|
|
|
62
62
|
## Public API
|
|
63
63
|
|
|
64
|
-
### Main process (`
|
|
64
|
+
### Main process (`omega.auth`)
|
|
65
65
|
|
|
66
66
|
```js
|
|
67
|
-
//
|
|
68
|
-
|
|
67
|
+
// The account, always a `User`: signed out until a renderer pushes the account
|
|
68
|
+
// document of the uid main's session holds, and signed out again the moment that
|
|
69
|
+
// session ends.
|
|
70
|
+
omega.auth.user;
|
|
71
|
+
// → User: .authenticated .uid .email .plan .active .trialing .cancelling .everPaid,
|
|
72
|
+
// .roles, .subscription, ..., .profile { displayName, photoURL, emailVerified }
|
|
73
|
+
|
|
74
|
+
// Subscribe to state changes (e.g. to refresh tray/menu items). Called with
|
|
75
|
+
// `{ user }`, plus a catch-up with the current state after listen() returns.
|
|
76
|
+
const off = omega.auth.listen(({ user }) => {
|
|
77
|
+
omega.tray.refresh();
|
|
78
|
+
omega.menu.refresh();
|
|
79
|
+
});
|
|
80
|
+
off(); // unsubscribe
|
|
69
81
|
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
-
// → { uid, email, displayName, photoURL, emailVerified } | null
|
|
82
|
+
// Sign in via a custom token (called automatically by the auth/token deep-link route).
|
|
83
|
+
await omega.auth.handleToken(token);
|
|
73
84
|
|
|
74
85
|
// Fresh Firebase ID token for calling authenticated backend routes from main
|
|
75
86
|
// (send as `Authorization: Bearer <token>`). null when signed out.
|
|
76
|
-
await
|
|
87
|
+
await omega.auth.getIdToken();
|
|
77
88
|
|
|
78
|
-
//
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
manager.menu.refresh();
|
|
82
|
-
});
|
|
83
|
-
off(); // unsubscribe
|
|
89
|
+
// Or let the instance fetch: @omega.js/client's request, built once on main,
|
|
90
|
+
// attaches that token itself (the renderer's and the extension's shape).
|
|
91
|
+
await omega.request('/notes', { method: 'POST', body: { text: 'hi' } });
|
|
84
92
|
|
|
85
|
-
// Sign out
|
|
86
|
-
await
|
|
87
|
-
|
|
88
|
-
// The renderer-resolved subscription — THE main-side plan source. Renderers run
|
|
89
|
-
// @omega.js/client's full auth cycle (Firestore account fetch → resolveSubscription)
|
|
90
|
-
// and push the result to main (desktop:auth:account-resolved, uid-guarded); main can't
|
|
91
|
-
// run Firestore itself. null until a renderer has resolved.
|
|
92
|
-
manager.omega.getResolvedPlan();
|
|
93
|
-
// → { plan, active, trialing, cancelling } | null
|
|
94
|
-
manager.omega.getResolvedRoles();
|
|
95
|
-
// → { admin, betaTester, ... } | null
|
|
93
|
+
// Sign main out and broadcast the sign-out to every renderer.
|
|
94
|
+
await omega.auth.signOut();
|
|
96
95
|
```
|
|
97
96
|
|
|
98
|
-
|
|
97
|
+
Main can't run Firestore, so the account crosses from the renderers: each renderer runs @omega.js/client's full auth cycle and pushes the WHOLE stored document (`desktop:auth:account-resolved`, `{ uid, document, identity }`, uid-guarded), and main builds its `omega.auth.user` from it. `user.plan`, `user.active` and `user.roles.admin` read the same in main as in the renderer.
|
|
98
|
+
|
|
99
|
+
### Renderer process (the renderer's `omega`)
|
|
99
100
|
|
|
100
101
|
```js
|
|
101
|
-
//
|
|
102
|
-
|
|
102
|
+
// The renderer's own account, from @omega.js/client
|
|
103
|
+
omega.auth.user; // a User, the same class main holds
|
|
104
|
+
omega.auth.listen(({ user, denied }) => { });
|
|
105
|
+
|
|
106
|
+
// Main's authoritative account: { uid, document, identity }
|
|
107
|
+
const main = await omega.getMainUser();
|
|
103
108
|
|
|
104
|
-
// Sign out
|
|
105
|
-
|
|
109
|
+
// Sign out through main (broadcasts to every renderer). omega.auth.signOut()
|
|
110
|
+
// signs out this renderer alone.
|
|
111
|
+
await omega.signOut();
|
|
106
112
|
```
|
|
107
113
|
|
|
108
|
-
The renderer's `
|
|
114
|
+
The renderer's `omega.initialize()` automatically:
|
|
109
115
|
- Boots @omega.js/client (so renderer-side Firebase is available).
|
|
110
116
|
- Wires the auth bridge (`desktop:auth:sync-request` on load + listens for broadcasts).
|
|
111
|
-
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
(
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
117
|
+
- Registers the auth click triggers the extension's pages carry, so a view signs in with markup
|
|
118
|
+
alone: `.omega-signin` runs main's `omega.openAuthFlow()` (`desktop:auth:open-flow`),
|
|
119
|
+
`.omega-account` opens the website's `/account` page in the user's browser
|
|
120
|
+
(`desktop:auth:open-account`), and `.omega-signout` (@omega.js/client's) runs `omega.signOut()`,
|
|
121
|
+
signing the whole app out through main (`desktop:auth:sign-out`).
|
|
122
|
+
- Runs @omega.js/client's **full auth cycle** (`omega.auth.listen()`): waits for auth to settle,
|
|
123
|
+
fetches the Firestore account, lands one `User`, and auto-populates the
|
|
124
|
+
**`data-omega-bind` bindings**, so @omega.js/desktop app views use the same reactive HTML as
|
|
125
|
+
every OMEGA browser surface (`@show auth.user.authenticated`, `@text auth.user.plan`,
|
|
126
|
+
`@show auth.user.plan === 'premium'`, see @omega.js/client's docs/bindings.md). Each signed-in
|
|
127
|
+
state pushes its account to main (`desktop:auth:account-resolved`) and re-offers it whenever
|
|
128
|
+
main announces a state change, so a renderer that resolved before main signed in still delivers.
|
|
118
129
|
|
|
119
130
|
You don't write any of this — it just works.
|
|
120
131
|
|
|
@@ -148,7 +159,7 @@ alike: the harness never signs a real user in, so it never asks the OS keychain
|
|
|
148
159
|
|
|
149
160
|
Storage only — distribution across processes stays the IPC sync protocol above.
|
|
150
161
|
The session restores at boot (offline included: no network round trip), so a restart
|
|
151
|
-
keeps the user signed in; renderers then re-resolve the account and re-push
|
|
162
|
+
keeps the user signed in; renderers then re-resolve the account and re-push it.
|
|
152
163
|
|
|
153
164
|
## Config
|
|
154
165
|
|
|
@@ -166,11 +177,11 @@ keeps the user signed in; renderers then re-resolve the account and re-push the
|
|
|
166
177
|
}
|
|
167
178
|
```
|
|
168
179
|
|
|
169
|
-
If `cloud.config` is empty/missing,
|
|
180
|
+
If `cloud.config` is empty/missing, `omega.auth` logs a warning and runs in no-op mode (`user` stays the signed-out `User`, everything else returns harmless defaults).
|
|
170
181
|
|
|
171
182
|
## Firebase (bundled)
|
|
172
183
|
|
|
173
|
-
Firebase is **bundled from @omega.js/desktop's module context** (@omega.js/client owns it in @omega.js/desktop's dependency tree)
|
|
184
|
+
Firebase is **bundled from @omega.js/desktop's module context** (@omega.js/client owns it in @omega.js/desktop's dependency tree), the same treatment `json5` gets in main.
|
|
174
185
|
|
|
175
186
|
If you're building a no-auth Electron app, just leave `cloud.config` empty — the bridge is a clean no-op.
|
|
176
187
|
|
|
@@ -181,24 +192,24 @@ In a TESTING run (`OMEGA_ENVIRONMENT=testing`) the bridge connects its auth inst
|
|
|
181
192
|
### Refresh tray when auth state changes
|
|
182
193
|
|
|
183
194
|
```js
|
|
184
|
-
// In src/tray/index.js or
|
|
185
|
-
|
|
186
|
-
|
|
195
|
+
// In src/integrations/tray/index.js, or anywhere main code reaches omega:
|
|
196
|
+
omega.auth.listen(() => {
|
|
197
|
+
omega.tray.refresh(); // re-evaluates dynamic labels
|
|
187
198
|
});
|
|
188
199
|
```
|
|
189
200
|
|
|
190
201
|
```js
|
|
191
|
-
// In src/tray/index.js:
|
|
202
|
+
// In src/integrations/tray/index.js:
|
|
192
203
|
tray.item({
|
|
193
204
|
label: () => {
|
|
194
|
-
const user =
|
|
195
|
-
return user ? `Signed in as ${user.email}` : 'Sign in';
|
|
205
|
+
const { user } = omega.auth;
|
|
206
|
+
return user.authenticated ? `Signed in as ${user.email}` : 'Sign in';
|
|
196
207
|
},
|
|
197
208
|
click: () => {
|
|
198
|
-
if (
|
|
199
|
-
|
|
209
|
+
if (omega.auth.user.authenticated) {
|
|
210
|
+
omega.auth.signOut();
|
|
200
211
|
} else {
|
|
201
|
-
|
|
212
|
+
omega.openAuthFlow();
|
|
202
213
|
}
|
|
203
214
|
},
|
|
204
215
|
});
|
|
@@ -207,14 +218,14 @@ tray.item({
|
|
|
207
218
|
### Gate a deep-link route on auth
|
|
208
219
|
|
|
209
220
|
```js
|
|
210
|
-
|
|
211
|
-
if (!
|
|
212
|
-
|
|
221
|
+
omega.deepLink.on('user/profile/:id', (ctx) => {
|
|
222
|
+
if (!omega.auth.user.authenticated) {
|
|
223
|
+
omega.openAuthFlow();
|
|
213
224
|
ctx.handled = true;
|
|
214
225
|
return;
|
|
215
226
|
}
|
|
216
|
-
|
|
217
|
-
|
|
227
|
+
omega.windows.show('main');
|
|
228
|
+
omega.windows.get('main').webContents.send('navigate', { to: `/profile/${ctx.params.id}` });
|
|
218
229
|
});
|
|
219
230
|
```
|
|
220
231
|
|
|
@@ -224,7 +235,7 @@ manager.deepLink.on('user/profile/:id', (ctx) => {
|
|
|
224
235
|
<button id="signout">Sign out</button>
|
|
225
236
|
<script>
|
|
226
237
|
document.getElementById('signout').addEventListener('click', async () => {
|
|
227
|
-
await
|
|
238
|
+
await omega.signOut(); // goes through main, propagates everywhere
|
|
228
239
|
});
|
|
229
240
|
</script>
|
|
230
241
|
```
|
|
@@ -234,8 +245,12 @@ manager.deepLink.on('user/profile/:id', (ctx) => {
|
|
|
234
245
|
| Channel | Direction | Payload | Description |
|
|
235
246
|
|---|---|---|---|
|
|
236
247
|
| `desktop:auth:sync-request` | renderer → main | `{ contextUid }` | "I'm at this UID, are we in sync?" |
|
|
237
|
-
| `desktop:auth:sign-out` | renderer → main | (none) | "Sign me (and everyone) out." |
|
|
238
|
-
| `desktop:auth:get-user` | renderer → main | (none) | Read main's
|
|
248
|
+
| `desktop:auth:sign-out` | renderer → main | (none) | "Sign me (and everyone) out." The `.omega-signout` trigger and `omega.signOut()`; main runs `omega.auth.signOut()`. |
|
|
249
|
+
| `desktop:auth:get-user` | renderer → main | (none) | Read main's account: `{ uid, document, identity }`. |
|
|
250
|
+
| `desktop:auth:account-resolved` | renderer → main | `{ uid, document, identity }` | The account this renderer's client resolved; main builds `omega.auth.user` from it (uid-guarded). |
|
|
251
|
+
| `desktop:auth:open-flow` | renderer → main | (none) | The `.omega-signin` trigger: main runs `omega.openAuthFlow()`. |
|
|
252
|
+
| `desktop:auth:open-account` | renderer → main | (none) | The `.omega-account` trigger: main opens `<getWebsiteUrl()>/account` in the user's browser. |
|
|
253
|
+
| `desktop:auth:plan-changed` | main → all renderers | `{ document }` | Main landed a new account. |
|
|
239
254
|
| `desktop:auth:sign-in-with-token` | main → all renderers | `{ token }` | "Sign in with this custom token now." |
|
|
240
255
|
| `desktop:auth:sign-out` | main → all renderers | `{}` | "Sign out now." |
|
|
241
256
|
| `desktop:auth:state-changed` | main → all renderers | `{ uid, email, ... } \| null` | Auth state changed (informational). |
|
|
@@ -244,7 +259,7 @@ manager.deepLink.on('user/profile/:id', (ctx) => {
|
|
|
244
259
|
|
|
245
260
|
### Unit tests (always run)
|
|
246
261
|
|
|
247
|
-
`
|
|
262
|
+
`auth.test.js` covers the dispatch logic, IPC handler shape, sync-request comparison, and the `auth/token` deep-link integration, all without hitting Firebase.
|
|
248
263
|
|
|
249
264
|
### The real-surface e2e lane (monorepo root)
|
|
250
265
|
|
|
@@ -252,18 +267,18 @@ manager.deepLink.on('user/profile/:id', (ctx) => {
|
|
|
252
267
|
|
|
253
268
|
### Extended tests (skip without the opt-in)
|
|
254
269
|
|
|
255
|
-
`
|
|
270
|
+
`auth.integration.test.js` talks to REAL Firebase, so it is gated behind extended mode (the cross-framework `TEST_EXTENDED_MODE` opt-in; see [test-framework.md](test-framework.md#extended-vs-normal-mode)):
|
|
256
271
|
|
|
257
272
|
```bash
|
|
258
273
|
npx omega test --extended # or: TEST_EXTENDED_MODE=true npx omega test
|
|
259
274
|
```
|
|
260
275
|
|
|
261
|
-
It asks for NO credential of its own
|
|
276
|
+
It asks for NO credential of its own. The SIGN-IN proof belongs to [#904](https://github.com/Omega-JS-Stack/omega/issues/904), which signs desktop in as a persona the backend emulator seeds, the same mechanism web and the extension use. Without the opt-in the suite skips cleanly with a reason, so CI stays green.
|
|
262
277
|
|
|
263
278
|
## Implementation notes
|
|
264
279
|
|
|
265
280
|
- Firebase app name in main is `omega-auth` (avoids clashes if a consumer's main code also wants its own Firebase instance).
|
|
266
|
-
- The bridge does NOT persist user info to @omega.js/desktop storage
|
|
267
|
-
- Custom tokens are NEVER stored. Renderers receive them once via broadcast, sign in, discard. Fresh tokens are minted on demand from `POST /omega/user/token`
|
|
268
|
-
- `
|
|
269
|
-
- All sensitive Firebase user fields (`stsTokenManager`, `providerData`, etc.) are stripped before sending over IPC. Only `{uid, email, displayName, photoURL, emailVerified}` cross the bridge.
|
|
281
|
+
- The bridge does NOT persist user info to @omega.js/desktop storage: the session vault (above) and the renderers' IndexedDB persistence handle session restoration, the same as @omega.js/extension.
|
|
282
|
+
- Custom tokens are NEVER stored. Renderers receive them once via broadcast, sign in, discard. Fresh tokens are minted on demand from `POST /omega/user/token` through main's `omega.request()` (@omega.js/client's `createRequest`, built once on the instance), the same code path as the extension background's token sync.
|
|
283
|
+
- `omega.getApiUrl()` returns the dev or prod URL, so the bridge automatically hits the right backend. Available on all three process instances (main / renderer / preload) via the shared `src/utils/url-helpers.js` module, the same code path everywhere. See the Cross-context helpers section of the framework guide ([docs/desktop/index.md](../../../docs/desktop/index.md)).
|
|
284
|
+
- All sensitive Firebase user fields (`stsTokenManager`, `providerData`, etc.) are stripped before sending over IPC. Only the identity `{uid, email, displayName, photoURL, emailVerified}` and the stored account document cross the bridge.
|
package/docs/auto-updater.md
CHANGED
|
@@ -10,7 +10,7 @@ Wraps `electron-updater` with three triggers: startup check, periodic check, and
|
|
|
10
10
|
| **Feed check** | Every `feedCheckIntervalMs` (default 1h) | HTTP poll of the release feed; also re-evaluates the 30-day gate each tick. |
|
|
11
11
|
| **Idle evaluation** | Every `idleEvalIntervalMs` (default 60s) | Cheap in-process check: install a downloaded update once the user has been idle long enough. |
|
|
12
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** | `
|
|
13
|
+
| **Manual check** | `omega.autoUpdater.checkNow()` (main) or `window.desktop.autoUpdater.checkNow()` (renderer) | Same as a periodic check but `userInitiated: true`. |
|
|
14
14
|
|
|
15
15
|
## State machine
|
|
16
16
|
|
|
@@ -93,7 +93,7 @@ Subtle: `_userInitiated` is only flipped AFTER the `_readyToCheck` guard. So a u
|
|
|
93
93
|
Consumers can force-bump the activity timestamp from anywhere:
|
|
94
94
|
|
|
95
95
|
```js
|
|
96
|
-
|
|
96
|
+
omega.autoUpdater.markActive();
|
|
97
97
|
```
|
|
98
98
|
|
|
99
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.
|
|
@@ -110,7 +110,7 @@ Long enough that an actively-used app won't surprise-quit mid-task. Short enough
|
|
|
110
110
|
|
|
111
111
|
### Test mode behavior
|
|
112
112
|
|
|
113
|
-
When `
|
|
113
|
+
When `omega.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
114
|
|
|
115
115
|
- **Idle threshold**: `IDLE_INSTALL_THRESHOLD_MS_TESTING = 3000ms` (3 sec) instead of 15 min.
|
|
116
116
|
- **Both timers**: `IDLE_TICK_MS_TESTING = 500ms` replaces `feedCheckIntervalMs` and `idleEvalIntervalMs`.
|
|
@@ -133,7 +133,7 @@ This lets the framework's own integration tests drive the full sequence (`OMEGA_
|
|
|
133
133
|
|
|
134
134
|
Click handler defaults to `checkNow()` when not yet downloaded; `installNow()` when downloaded.
|
|
135
135
|
|
|
136
|
-
Consumers can find / move / remove the item via `
|
|
136
|
+
Consumers can find / move / remove the item via `omega.menu.findItem('desktop:check-for-updates')` etc.: see [docs/menu.md](menu.md).
|
|
137
137
|
|
|
138
138
|
## Renderer surface
|
|
139
139
|
|
|
@@ -187,10 +187,10 @@ Relaunching once per scenario is a slow way to walk three outcomes, so the defau
|
|
|
187
187
|
| No update available | `view/developer/simulate-update/unavailable` | lands in `not-available` |
|
|
188
188
|
| Update error | `view/developer/simulate-update/error` | lands in `error` |
|
|
189
189
|
|
|
190
|
-
Each item calls `
|
|
190
|
+
Each item calls `omega.autoUpdater.simulate(scenario)`, which is callable from anywhere in main:
|
|
191
191
|
|
|
192
192
|
```js
|
|
193
|
-
await
|
|
193
|
+
await omega.autoUpdater.simulate('available');
|
|
194
194
|
```
|
|
195
195
|
|
|
196
196
|
Rules of the road:
|
|
@@ -212,11 +212,11 @@ Because the synthetic library stays wired, every LATER trigger drives it too: th
|
|
|
212
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
213
|
|
|
214
214
|
- `_evaluateIdleInstall()` bails, so no native "restart to update" prompt fires for an update that does not exist.
|
|
215
|
-
- `installNow()` bails before `
|
|
215
|
+
- `installNow()` bails before `omega._allowQuit = true` and `quitAndInstall()`.
|
|
216
216
|
|
|
217
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
218
|
|
|
219
|
-
The submenu is dev-only (same gate as `view/developer/toggle-devtools`) and is an ordinary menu item, so `
|
|
219
|
+
The submenu is dev-only (same gate as `view/developer/toggle-devtools`) and is an ordinary menu item, so `omega.menu.remove('view/developer/simulate-update')` drops it like any other.
|
|
220
220
|
|
|
221
221
|
## Production: how electron-updater finds the feed
|
|
222
222
|
|
package/docs/boot-sequence.md
CHANGED
|
@@ -1,30 +1,35 @@
|
|
|
1
1
|
# Boot Sequence
|
|
2
2
|
|
|
3
|
-
`
|
|
3
|
+
`omega.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
4
|
|
|
5
5
|
## Order
|
|
6
6
|
|
|
7
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
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
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
|
|
10
|
+
2. **`app.on('before-quit')`** wired: sets `omega._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
11
|
3. **`ipc`** — typed channel bus online before any feature can register handlers.
|
|
12
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
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
|
+
4c. **`fontawesome`**: serves the bundled icon SVGs to renderers over IPC (`desktop:fontawesome:get`). Needs ipc only.
|
|
14
15
|
5. **`sentry`** — earliest catchable global handler.
|
|
15
16
|
6. **`protocol`** — single-instance lock + custom scheme register.
|
|
16
17
|
7. **`deepLink`** — argv parse for cold-start, second-instance handler.
|
|
18
|
+
7b. **`authFlow`**: the sign-in round trip in the user's default browser (`omega.openAuthFlow()`); dev/test return through a loopback listener, since the scheme isn't OS-registered there.
|
|
17
19
|
8. **`appState`** — first-launch / launch-count / crash-sentinel / version-change.
|
|
20
|
+
8b. **`context`**: session id, deviceId, OS info, the async geolocation fetch. After storage (it writes deviceId), before analytics (which reads it).
|
|
21
|
+
8c. **`usage`**: opens / hours-total / hours-this-session, recorded on quit.
|
|
18
22
|
9. `await app.whenReady()`.
|
|
19
23
|
10. **`autoUpdater`** — electron-updater, never blocks.
|
|
20
|
-
11. **`tray`**, **`menu`**, **`contextMenu
|
|
24
|
+
11. **`tray`**, **`menu`**, **`contextMenu`**: file-based definitions from `src/integrations/{tray,menu,context-menu}/index.js`. Disable any of them at runtime via `omega.<name>.disable()` (no config flag).
|
|
21
25
|
12. **`startup.initialize`** — applies `setLoginItemSettings`.
|
|
22
|
-
13. **`omega
|
|
26
|
+
13. **`auth`**: `omega.auth`, the main-side Firebase Auth source of truth, and the IPC handlers every renderer syncs through ([auth.md](auth.md)).
|
|
23
27
|
13b. **`remoteConfig`** — hot config from `<brand.url>/data/resources/main.json`. Non-blocking fire-and-forget fetch.
|
|
24
28
|
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
|
|
29
|
+
13d. **`analytics`**: GA4 Measurement Protocol. Wired AFTER `auth` so it can subscribe with `omega.auth.listen()`.
|
|
26
30
|
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
|
|
31
|
+
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 `omega.windows.create('main', { show: !startup.isLaunchHidden() })` from inside `omega.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).
|
|
32
|
+
15. **`deepLink.markOmegaReady()`**: releases the deep-link dispatch queue. Cold-start URLs (and any early `open-url`) wait here, so a route like `auth/token` never fires before `omega.auth` has Firebase up.
|
|
28
33
|
|
|
29
34
|
## Why this order
|
|
30
35
|
|
package/docs/build-system.md
CHANGED
|
@@ -99,7 +99,7 @@ The renderer runs with `contextIsolation: true` — a browser-like environment w
|
|
|
99
99
|
|
|
100
100
|
### OMEGA_BUILD_JSON: a define for Node, one file for the browser
|
|
101
101
|
|
|
102
|
-
The wrapper is the ONE shape every OMEGA browser surface carries, `{ config, package, mode, license, builtAt }` ([#894](https://github.com/Omega-JS-Stack/omega/issues/894)), with `mode` the same three keys everywhere (`{ environment, build, publish }`; desktop's own `server` verdict stays inside `
|
|
102
|
+
The wrapper is the ONE shape every OMEGA browser surface carries, `{ config, package, mode, license, builtAt }` ([#894](https://github.com/Omega-JS-Stack/omega/issues/894)), with `mode` the same three keys everywhere (`{ environment, build, publish }`; desktop's own `server` verdict stays inside `build.getMode()`). Two blobs come out of one composition, off one set of build facts:
|
|
103
103
|
|
|
104
104
|
- `composeBuildJson()` → main and preload, as an esbuild `define` (the bare identifier becomes the literal at compile time) plus a `banner` that assigns it to `globalThis`. Its `config` is the WHOLE resolved config, because the main process boots from it in a packaged app, and both bundles are Node rather than a public surface. `process.env.NODE_ENV` is defined the same way: webpack derived it from its `mode`, esbuild has no modes, so the build states it.
|
|
105
105
|
- `composeClientBuildJson()` → the renderer, written ONCE as `dist/build.js` through `@omega.js/devkit/build-json` ([#743](https://github.com/Omega-JS-Stack/omega/issues/743)). Its `config` is `clientConfig(resolved)` from `@omega.js/config`, the browser-safe subset every OMEGA browser surface carries: a renderer is readable from DevTools, so the GCP account facts, the signing certificates and the account admins stay out of it. The page template loads the file with `<script src="../../build.js">` as the view's FIRST script, ahead of the view bundle (`dist/views/<view>/` → `dist/`, resolved inside a packaged asar exactly as the bundle tag beside it is), and the renderer bundle carries no define and no banner of its own.
|
package/docs/cdp-debugging.md
CHANGED
|
@@ -36,7 +36,7 @@ npx omega cdp status # running? targets, window rect, t
|
|
|
36
36
|
npx omega cdp eval <match> '<expr>' # evaluate JS in any webContents
|
|
37
37
|
npx omega cdp shot <match> <out.png> # ONE renderer's own pixels
|
|
38
38
|
npx omega cdp capture <out.png> # the COMPOSITED window (macOS)
|
|
39
|
-
npx omega cdp theme <dark|light|system> # flip the live theme (
|
|
39
|
+
npx omega cdp theme <dark|light|system> # flip the live theme (omega.theme)
|
|
40
40
|
npx omega cdp relaunch # quit → npm start → wait for boot
|
|
41
41
|
npx omega cdp quit # quit + wait for the process tree to drain
|
|
42
42
|
```
|
package/docs/common-mistakes.md
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
# Common Mistakes to Avoid
|
|
2
2
|
|
|
3
|
-
1. **Auto-creating windows in main.js
|
|
3
|
+
1. **Auto-creating windows in main.js**: @omega.js/desktop does NOT auto-create windows. The consumer's main.js must call `omega.windows.create('main', { show: !startup.isLaunchHidden() })` inside `omega.initialize().then()`. Always create `main`: even in hidden launches, with `show: false`, so the activate/second-instance handlers can surface UI on user re-launch.
|
|
4
4
|
2. **Putting JS logic in config** — Trays / menus / context-menus are file-based (`src/integrations/<name>/index.js`). Click handlers, dynamic labels, conditional visibility — all live in the JS file. Don't try to express them in `omega.json5`.
|
|
5
5
|
3. **Shipping `<slot>@2x.png` files** — Ship ONE file at the native (@2x) size; @omega.js/desktop downscales the @1x sibling. Bundled defaults work the same way. See [icons.md](icons.md).
|
|
6
6
|
4. **Naming the macOS tray icon source `trayTemplate.png`** — The input filename is `tray.png` (matches Windows/Linux). @omega.js/desktop owns the `Template` magic when writing to dist.
|
|
7
7
|
5. **Reading `process.cwd()` from packaged-app runtime code** — It's `/` in packaged apps. Use `require('./utils/app-root.js')()` (tries `app.getAppPath()` first, falls back for tests/non-Electron contexts).
|
|
8
8
|
6. **Setting `enabled: true` to turn on sentry/analytics** — Wrong convention. Set the credentials (`monitoring.providers.sentry.dsn = '...'`, `analytics.providers.google.id = '...'`); presence enables. Same for `cloud.config`.
|
|
9
|
-
7. **Defining cross-context
|
|
10
|
-
8. **Trying to share
|
|
9
|
+
7. **Defining a cross-context helper on one process's class alone**: write it as a plain function in `src/utils/<topic>-helpers.js` and call it from each process class (main, preload, renderer) and the build module, so they all share the same code path.
|
|
10
|
+
8. **Trying to share an `omega` instance across processes**: each process has its own. They communicate via IPC (`omega.ipc.invoke/handle` in main, `omega.desktop.ipc` in a renderer).
|
|
11
11
|
9. **Calling `shell.openExternal(url)` directly with a dynamic URL** — Gate through `require('./utils/sanitize-url.js')` first (returns `''` for non-http(s) protocols). Any dynamic URL must have its protocol filtered before navigation.
|
|
12
12
|
10. **Hand-editing `dist/electron-builder.yml` or `dist/config/entitlements.mac.plist`** — Both are generated by `gulp/build-config` from `config/omega.json5` + @omega.js/desktop defaults. Edit the source config; the YAML/plist regenerate every build.
|
|
13
13
|
11. **Forgetting that "build" failed but the .app launched anyway** — `ELECTRON_RUN_AS_NODE=1` makes Electron silently run as Node: `app` is undefined, no BrowserWindow, no window appears. The CLI boundary strips this var; if you see weird "nothing happens" launches in dev, check whether your shell has it set.
|
|
14
|
-
12. **
|
|
15
|
-
13. **Installing @omega.js/desktop's dependencies as direct consumer deps
|
|
16
|
-
14. **Touching Firebase directly in consumer code
|
|
14
|
+
12. **Reading env vars ad-hoc in source**: use `omega.isDevelopment()`, `omega.isProduction()`, `omega.isTesting()`, `omega.getEnvironment()` instead. See [environment-detection.md](environment-detection.md).
|
|
15
|
+
13. **Installing @omega.js/desktop's dependencies as direct consumer deps**: consumer projects must NOT `npm install firebase`, `fs-jetpack`, `@omega.js/client`, or any other @omega.js/desktop/@omega.js/client transitive dep. The bundler re-resolves every name @omega.js/desktop DECLARES from the framework's own installation (`@omega.js/devkit/bundle`'s framework-deps hook). If a dependency isn't resolving, the fix is in @omega.js/desktop's `package.json` or its bundle task, not the consumer's `package.json`. Same rule on every OMEGA framework.
|
|
16
|
+
14. **Touching Firebase directly in consumer code**: Firebase is owned by @omega.js/client. Consumer code NEVER does `require('firebase')` or `import('firebase/app')`. In renderers use `omega.auth` and `omega.firestore` on the renderer instance. In the main process, use `omega.auth` (the @omega.js/desktop bridge). Same rule on every OMEGA browser surface.
|
|
17
17
|
15. **Forgetting `await` on `windows.create()`** — It's async and returns a Promise, not a BrowserWindow. Passing the Promise to code that calls `win.on(...)` silently fails with "is not a function".
|
|
18
18
|
16. **Using raw `import()` for ESM-only deps** — Use `importESM(specifier)` from `utils/import-esm.js`. It tries the consumer's `node_modules/` first, then falls back to @omega.js/desktop's own copy. This means consumers don't need to install @omega.js/desktop's transitive ESM deps (they're resolved from @omega.js/desktop's `node_modules/` automatically). A dep import the bundler cannot inline (a variable specifier) only resolves from the consumer — if the dep isn't installed there, it fails silently.
|
|
19
19
|
17. **Touching `process` in code shared with renderers** — With `contextIsolation: true` and `nodeIntegration: false` (the defaults), `process` does not exist in renderers — `process.platform` in a shared util throws a `ReferenceError`, it does not return `undefined`. Branch platform/env logic in main (or the preload) and hand the RESULT to the renderer (IPC, preload-exposed value, or a data attribute) — never share a util that dereferences `process` across contexts.
|
|
20
20
|
18. **Expecting `target="_blank"` to work in a `file://` renderer** — Anchors with `target="_blank"` silently do nothing; there is no browser to open. External links go through `shell.openExternal` (gated per mistake 9) — wire a click handler or use the framework's external-link binding rather than a bare anchor.
|
|
21
|
-
19. **Registering the brand-scheme handler on the default session only
|
|
21
|
+
19. **Registering the brand-scheme handler on the default session only**: `protocol.handle('<brand.id>', handler)` covers ONLY the default session. Sessions from `session.fromPartition(...)` do NOT inherit it, and Electron auto-opens external protocols: a `brand://` load in a partitioned webContents is classified by Chromium as an EXTERNAL protocol and handed to the OS: macOS Launch Services then launches whatever installed app owns the scheme (an old production copy of YOUR app, mid-run). Silent in dev until it isn't. If your app uses partitions, register the same handler on every partition you vend, idempotently: e.g. a session-manager whose `getElectronSession()` does `ses.protocol.handle(scheme, handler)` guarded by a `WeakSet`. There is no Electron API to read a registered handler back, so this cannot be automated after the fact; register through one owned code path. (Candidate future @omega.js/desktop API: opt-in `omega.protocol.handle(handler)` that applies the handler to the default session + every `session-created`, see TODO.md.) The TEST harness already contains this class of escape during `mgr test` (stub brand handler + `openExternal` denied, see [test-framework.md](test-framework.md)), but that protects tests only, not your packaged app.
|
package/docs/config-schema.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
Validation runs in two places:
|
|
6
6
|
|
|
7
|
-
1. **`
|
|
7
|
+
1. **`omega.initialize()` (boot, main)**: hard-fails the app at boot if any required field is missing or any present field is invalid. So a misconfigured app never reaches the "white window of confusion" phase: it tells you exactly which field is broken.
|
|
8
8
|
2. **`gulp audit` (build)**: same schema, plus build-pipeline-specific extras (file-existence for icons, an addressable releases repo in publish mode, etc.).
|
|
9
9
|
|
|
10
10
|
## Schema entry shape
|
package/docs/context-menu.md
CHANGED
|
@@ -4,13 +4,13 @@ File-based context menu. Unlike tray and application menu (called once at boot),
|
|
|
4
4
|
|
|
5
5
|
## Config
|
|
6
6
|
|
|
7
|
-
No config block. Path is conventional: `src/integrations/context-menu/index.js`. To opt out, call `
|
|
7
|
+
No config block. Path is conventional: `src/integrations/context-menu/index.js`. To opt out, call `omega.contextMenu.disable()` from your main entry: after that, right-click events are silently swallowed.
|
|
8
8
|
|
|
9
9
|
## Definition file
|
|
10
10
|
|
|
11
11
|
```js
|
|
12
12
|
// src/integrations/context-menu/index.js
|
|
13
|
-
module.exports = ({
|
|
13
|
+
module.exports = ({ omega, menu, params, webContents }) => {
|
|
14
14
|
// Easiest: start from @omega.js/desktop's defaults, then customize per event.
|
|
15
15
|
menu.useDefaults();
|
|
16
16
|
|
|
@@ -61,7 +61,7 @@ Same shape across menu / tray / context-menu. Available **inside the definition
|
|
|
61
61
|
|
|
62
62
|
Context-menu ids are **flat** — no `context/` prefix needed (the lib namespace is implicit). Submenus you build with `menu.submenu(...)` are addressable as `parent/child` paths via the resolver.
|
|
63
63
|
|
|
64
|
-
(Runtime-on-`
|
|
64
|
+
(Runtime-on-`omega.contextMenu` mutators don't apply here: items are rebuilt every event. Mutate inside the definition fn instead.)
|
|
65
65
|
|
|
66
66
|
## Default template ids
|
|
67
67
|
|
|
@@ -74,33 +74,33 @@ Context-menu ids are **flat** — no `context/` prefix needed (the lib namespace
|
|
|
74
74
|
| `copy` | `params.selectionText` (read-only) |
|
|
75
75
|
| `open-link`, `copy-link` | `params.linkURL` |
|
|
76
76
|
| `reload` | always |
|
|
77
|
-
| `inspect`, `toggle-devtools` | `
|
|
77
|
+
| `inspect`, `toggle-devtools` | `omega.isDevelopment()` only |
|
|
78
78
|
|
|
79
79
|
## Definition fn arguments
|
|
80
80
|
|
|
81
81
|
| Arg | Description |
|
|
82
82
|
|---|---|
|
|
83
|
-
| `
|
|
83
|
+
| `omega` | The running @omega.js/desktop main-process instance |
|
|
84
84
|
| `menu` | Per-event builder + id-path API |
|
|
85
85
|
| `params` | Electron's [`ContextMenuParams`](https://www.electronjs.org/docs/latest/api/web-contents#event-context-menu) — `selectionText`, `isEditable`, `linkURL`, `srcURL`, `mediaType`, `editFlags`, `x`, `y`, etc. |
|
|
86
86
|
| `webContents` | The `webContents` that fired the event |
|
|
87
87
|
|
|
88
88
|
## Auto-attach
|
|
89
89
|
|
|
90
|
-
Every window created via `
|
|
90
|
+
Every window created via `omega.windows.createNamed()` is automatically wired up with the context-menu listener. Idempotent per `webContents` (uses a `WeakSet`). For windows you create directly with `new BrowserWindow()`, call:
|
|
91
91
|
|
|
92
92
|
```js
|
|
93
|
-
|
|
93
|
+
omega.contextMenu.attach(win.webContents);
|
|
94
94
|
```
|
|
95
95
|
|
|
96
|
-
## Runtime API on `
|
|
96
|
+
## Runtime API on `omega.contextMenu`
|
|
97
97
|
|
|
98
98
|
```js
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
99
|
+
omega.contextMenu.define(fn) // replace the definition at runtime
|
|
100
|
+
omega.contextMenu.disable() // ignore future right-click events (idempotent)
|
|
101
|
+
omega.contextMenu.attach(webContents) // manual attach
|
|
102
|
+
omega.contextMenu.buildItems(params, wc) // run the definition without popping a menu (useful for tests)
|
|
103
|
+
omega.contextMenu.hasCustomDefinition() // false → using the built-in default fn
|
|
104
104
|
```
|
|
105
105
|
|
|
106
106
|
## Default fn
|