@omega.js/desktop 0.52.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.
Files changed (273) hide show
  1. package/README.md +17 -12
  2. package/dist/assets/css/core/_initialize.scss +1 -1
  3. package/dist/assets/js/core/app-shell.js +14 -15
  4. package/dist/assets/themes/_template/_theme.js +6 -6
  5. package/dist/assets/themes/base/_includes/global/sections/account.html +4 -4
  6. package/dist/assets/themes/base/_includes/global/sections/app-sidebar.html +5 -5
  7. package/dist/assets/themes/base/_layouts/frontend/pages/account/index.html +3 -3
  8. package/dist/assets/themes/base/_layouts/frontend/pages/alternatives/index.html +1 -1
  9. package/dist/assets/themes/base/_layouts/frontend/pages/blog/tags/tag.html +1 -1
  10. package/dist/assets/themes/base/_layouts/frontend/pages/payment/confirmation.html +1 -1
  11. package/dist/assets/themes/base/_sections/marketing/newsletter-cta/section.js +2 -3
  12. package/dist/assets/themes/base/_sections/verts/unit/section.html +1 -1
  13. package/dist/assets/themes/base/_sections/verts/unit/section.js +3 -4
  14. package/dist/assets/themes/base/_theme.js +4 -3
  15. package/dist/assets/themes/bootstrap/_theme.js +2 -2
  16. package/dist/assets/themes/bootstrap/overrides/_links.scss +1 -1
  17. package/dist/assets/themes/classy/_theme.js +8 -6
  18. package/dist/assets/themes/classy/css/marketing/_sections.scss +1 -2
  19. package/dist/assets/themes/classy/js/hero-demo-form.js +3 -2
  20. package/dist/assets/themes/neobrutalism/_theme.js +5 -5
  21. package/dist/assets/themes/neobrutalism/js/pages/test/libraries/layers/index.js +1 -1
  22. package/dist/assets/themes/newsflash/_theme.js +6 -6
  23. package/dist/assets/themes/newsflash/js/pages/test/libraries/layers/index.js +1 -1
  24. package/dist/build.js +87 -111
  25. package/dist/cli.js +1 -1
  26. package/dist/commands/build.js +2 -2
  27. package/dist/commands/cdp/capture.js +2 -2
  28. package/dist/commands/cdp/quit.js +2 -2
  29. package/dist/commands/cdp/relaunch.js +2 -2
  30. package/dist/commands/cdp/theme.js +1 -1
  31. package/dist/commands/clean.js +2 -2
  32. package/dist/commands/deploy.js +4 -4
  33. package/dist/commands/finalize-release.js +4 -4
  34. package/dist/commands/install.js +3 -3
  35. package/dist/commands/launch.js +2 -2
  36. package/dist/commands/lib/deploy-precheck.js +3 -3
  37. package/dist/commands/lib/ensure-target.js +6 -6
  38. package/dist/commands/package.js +2 -2
  39. package/dist/commands/publish.js +2 -2
  40. package/dist/commands/release.js +3 -3
  41. package/dist/commands/runner.js +11 -11
  42. package/dist/commands/sign-windows.js +5 -5
  43. package/dist/commands/test.js +4 -4
  44. package/dist/commands/update.js +2 -2
  45. package/dist/commands/validate-certs.js +4 -4
  46. package/dist/commands/version.js +3 -3
  47. package/dist/defaults/AGENTS.md +17 -8
  48. package/dist/defaults/config/omega.json5 +7 -7
  49. package/dist/defaults/hooks/build/post.js +1 -1
  50. package/dist/defaults/hooks/build/pre.js +1 -1
  51. package/dist/defaults/hooks/deploy/pre.js +1 -1
  52. package/dist/defaults/hooks/release/post.js +1 -1
  53. package/dist/defaults/hooks/release/pre.js +1 -1
  54. package/dist/defaults/src/assets/js/components/about/index.js +3 -5
  55. package/dist/defaults/src/assets/js/components/main/index.js +3 -5
  56. package/dist/defaults/src/assets/js/components/settings/index.js +3 -5
  57. package/dist/defaults/src/integrations/context-menu/index.js +2 -2
  58. package/dist/defaults/src/integrations/menu/index.js +3 -3
  59. package/dist/defaults/src/integrations/tray/index.js +3 -3
  60. package/dist/defaults/src/main.js +3 -5
  61. package/dist/defaults/src/preload.js +3 -5
  62. package/dist/defaults/test/README.md +2 -2
  63. package/dist/gulp/main.js +9 -10
  64. package/dist/gulp/tasks/audit.js +7 -7
  65. package/dist/gulp/tasks/build-config.js +8 -8
  66. package/dist/gulp/tasks/bundle.js +16 -16
  67. package/dist/gulp/tasks/defaults.js +3 -3
  68. package/dist/gulp/tasks/distribute.js +2 -2
  69. package/dist/gulp/tasks/html.js +9 -9
  70. package/dist/gulp/tasks/package-quick.js +3 -3
  71. package/dist/gulp/tasks/package.js +3 -3
  72. package/dist/gulp/tasks/release.js +3 -3
  73. package/dist/gulp/tasks/sass.js +6 -6
  74. package/dist/gulp/tasks/serve.js +4 -4
  75. package/dist/hooks/notarize-artifacts.js +1 -1
  76. package/dist/hooks/notarize.js +1 -1
  77. package/dist/index.js +5 -8
  78. package/dist/lib/_environment-mixin.js +50 -0
  79. package/dist/lib/_lifecycle-mixin.js +45 -0
  80. package/dist/lib/analytics.js +33 -35
  81. package/dist/lib/app-state.js +14 -14
  82. package/dist/lib/auth-flow.js +18 -18
  83. package/dist/lib/auth-persistence.js +12 -12
  84. package/dist/lib/auth.js +421 -0
  85. package/dist/lib/auto-updater.js +49 -49
  86. package/dist/lib/context-menu.js +13 -13
  87. package/dist/lib/context.js +19 -19
  88. package/dist/lib/deep-link.js +34 -34
  89. package/dist/lib/fontawesome.js +5 -5
  90. package/dist/lib/ipc.js +4 -4
  91. package/dist/lib/menu.js +25 -25
  92. package/dist/lib/protocol.js +5 -5
  93. package/dist/lib/remote-config.js +22 -22
  94. package/dist/lib/remote-scripts.js +21 -21
  95. package/dist/lib/restart-manager/index.js +28 -28
  96. package/dist/lib/sign-helpers/exec-with-limit.js +1 -1
  97. package/dist/lib/sign-helpers/sign-events.js +1 -1
  98. package/dist/lib/startup.js +18 -13
  99. package/dist/lib/storage.js +10 -10
  100. package/dist/lib/templating.js +16 -16
  101. package/dist/lib/theme.js +10 -10
  102. package/dist/lib/tray.js +27 -27
  103. package/dist/lib/usage.js +11 -11
  104. package/dist/lib/window-manager.js +26 -26
  105. package/dist/main.js +398 -483
  106. package/dist/preload.js +236 -176
  107. package/dist/renderer.js +417 -391
  108. package/dist/test/fixtures/consumer-app/config/omega.json5 +1 -1
  109. package/dist/test/fixtures/consumer-app/src/assets/js/components/main/index.js +8 -11
  110. package/dist/test/fixtures/consumer-app/src/main.js +5 -7
  111. package/dist/test/fixtures/consumer-app/src/preload.js +2 -2
  112. package/dist/test/harness/boot-entry.js +22 -20
  113. package/dist/test/harness/main-entry.js +31 -30
  114. package/dist/test/harness/renderer-entry.js +5 -5
  115. package/dist/test/harness/renderer-preload.js +137 -141
  116. package/dist/test/index.js +10 -10
  117. package/dist/test/runner.js +2 -2
  118. package/dist/test/runners/boot.js +7 -6
  119. package/dist/test/runners/electron.js +3 -2
  120. package/dist/test/runners/render-event.js +2 -2
  121. package/dist/test/suites/boot/consumer-app-boots.test.js +57 -26
  122. package/dist/test/suites/boot/restart-manager.test.js +2 -2
  123. package/dist/test/suites/boot/storage-bundled.test.js +5 -5
  124. package/dist/test/suites/boot/theme.test.js +13 -13
  125. package/dist/test/suites/build/audit.test.js +1 -1
  126. package/dist/test/suites/build/auth-persistence-resolve.test.js +6 -6
  127. package/dist/test/suites/build/boot-fixture.test.js +2 -2
  128. package/dist/test/suites/build/boot-runner-timeout.test.js +6 -5
  129. package/dist/test/suites/build/brand-scss.test.js +1 -1
  130. package/dist/test/suites/build/build-json-bake.test.js +1 -1
  131. package/dist/test/suites/build/cli.test.js +2 -2
  132. package/dist/test/suites/build/config-schema.test.js +5 -5
  133. package/dist/test/suites/build/defaults-scaffold.test.js +2 -2
  134. package/dist/test/suites/build/deploy-hook.test.js +3 -3
  135. package/dist/test/suites/build/ensure-target.test.js +2 -2
  136. package/dist/test/suites/build/env-delivery.test.js +2 -2
  137. package/dist/test/suites/build/esm-only-dependency.test.js +2 -2
  138. package/dist/test/suites/build/exports.test.js +9 -8
  139. package/dist/test/suites/build/get-config.test.js +4 -4
  140. package/dist/test/suites/build/manifest-deps.test.js +1 -1
  141. package/dist/test/suites/build/merge-line-files.test.js +1 -1
  142. package/dist/test/suites/build/omega-shell.test.js +34 -2
  143. package/dist/test/suites/build/omega.test.js +350 -0
  144. package/dist/test/suites/build/renderer-auth-bridge.test.js +211 -78
  145. package/dist/test/suites/build/runner.test.js +2 -2
  146. package/dist/test/suites/build/sentry.test.js +2 -2
  147. package/dist/test/suites/build/sign-windows-e2e.test.js +2 -2
  148. package/dist/test/suites/build/templating.test.js +3 -3
  149. package/dist/test/suites/build/test-stealth.test.js +7 -9
  150. package/dist/test/suites/build/url-helpers.test.js +55 -56
  151. package/dist/test/suites/build/validate-config.test.js +2 -2
  152. package/dist/test/suites/build/wave5-pins.test.js +2 -2
  153. package/dist/test/suites/main/analytics.test.js +59 -59
  154. package/dist/test/suites/main/app-state.test.js +66 -66
  155. package/dist/test/suites/main/auth-flow.test.js +41 -41
  156. package/dist/test/suites/main/auth-persistence.test.js +54 -43
  157. package/dist/test/suites/main/{client-bridge.integration.test.js → auth.integration.test.js} +10 -9
  158. package/dist/test/suites/main/auth.test.js +336 -0
  159. package/dist/test/suites/main/auto-updater.test.js +134 -134
  160. package/dist/test/suites/main/boot-sequence.test.js +37 -49
  161. package/dist/test/suites/main/context-menu.test.js +51 -50
  162. package/dist/test/suites/main/context.test.js +25 -25
  163. package/dist/test/suites/main/deep-link.test.js +74 -74
  164. package/dist/test/suites/main/fontawesome.test.js +27 -27
  165. package/dist/test/suites/main/ipc.test.js +35 -35
  166. package/dist/test/suites/main/menu.test.js +101 -100
  167. package/dist/test/suites/main/protocol.test.js +19 -19
  168. package/dist/test/suites/main/remote-config.test.js +63 -63
  169. package/dist/test/suites/main/remote-scripts.test.js +103 -103
  170. package/dist/test/suites/main/request.test.js +71 -0
  171. package/dist/test/suites/main/restart-manager.test.js +33 -33
  172. package/dist/test/suites/main/startup-paths-and-ua.test.js +4 -4
  173. package/dist/test/suites/main/startup.test.js +26 -26
  174. package/dist/test/suites/main/stealth-window.test.js +1 -1
  175. package/dist/test/suites/main/storage.test.js +25 -25
  176. package/dist/test/suites/main/theme.test.js +34 -34
  177. package/dist/test/suites/main/tray.test.js +79 -79
  178. package/dist/test/suites/main/url-helpers.test.js +133 -133
  179. package/dist/test/suites/main/usage.test.js +25 -25
  180. package/dist/test/suites/main/window-bounds.test.js +27 -27
  181. package/dist/test/suites/main/window-manager.test.js +44 -44
  182. package/dist/test/suites/renderer/analytics-bridge.test.js +5 -5
  183. package/dist/test/suites/renderer/cross-context-helpers.test.js +37 -31
  184. package/dist/test/suites/renderer/round-trip.test.js +3 -3
  185. package/dist/test/suites/renderer/tooltips.test.js +15 -15
  186. package/dist/test/suites/renderer/{window-em-surface.test.js → window-desktop-surface.test.js} +27 -7
  187. package/dist/test/utils/extended-mode-warning.js +1 -1
  188. package/dist/utils/boot-harness.js +56 -0
  189. package/dist/utils/mode-helpers.js +2 -15
  190. package/dist/utils/ship-keys.js +3 -3
  191. package/dist/utils/signing-status.js +51 -0
  192. package/dist/utils/test-events.js +7 -0
  193. package/dist/utils/test-stealth.js +6 -6
  194. package/dist/utils/url-helpers.js +52 -42
  195. package/dist/utils/user-agent.js +44 -0
  196. package/dist/vendor/account/engine.js +3 -3
  197. package/dist/vendor/account/index.js +14 -45
  198. package/dist/vendor/account/resolve-account.js +44 -0
  199. package/dist/vendor/account/schema.js +1 -1
  200. package/dist/vendor/account/user.js +99 -0
  201. package/dist/vendor/config/client-config.js +1 -1
  202. package/dist/vendor/config/environment.js +11 -30
  203. package/dist/vendor/config/index.js +8 -11
  204. package/dist/vendor/config/load.js +1 -2
  205. package/dist/vendor/config/platforms.js +1 -1
  206. package/dist/vendor/config/retired-keys.js +2 -2
  207. package/dist/vendor/config/schema.js +5 -8
  208. package/dist/vendor/config/site-global.js +2 -3
  209. package/dist/vendor/config/validate.js +1 -2
  210. package/dist/vendor/config/winback.js +1 -1
  211. package/dist/vendor/devkit/actions-secrets.js +1 -1
  212. package/dist/vendor/devkit/attach-log-file.js +1 -1
  213. package/dist/vendor/devkit/build-json.js +1 -1
  214. package/dist/vendor/devkit/cli-router.js +3 -4
  215. package/dist/vendor/devkit/defaults-engine.js +9 -11
  216. package/dist/vendor/devkit/local.js +2 -0
  217. package/dist/vendor/devkit/merge-line-files.js +2 -3
  218. package/dist/vendor/devkit/test/runner-core.js +6 -6
  219. package/dist/vendor/monitoring/env.js +2 -2
  220. package/dist/vendor/monitoring/index.js +1 -1
  221. package/dist/vendor/monitoring/main.js +1 -1
  222. package/dist/vendor/monitoring/preload.js +1 -1
  223. package/dist/vendor/monitoring/renderer.js +1 -1
  224. package/docs/analytics.md +8 -8
  225. package/docs/app-state.md +19 -19
  226. package/docs/audit.md +4 -4
  227. package/docs/{client-bridge.md → auth.md} +88 -73
  228. package/docs/auto-updater.md +8 -8
  229. package/docs/boot-sequence.md +11 -6
  230. package/docs/build-system.md +1 -1
  231. package/docs/cdp-debugging.md +1 -1
  232. package/docs/common-mistakes.md +7 -7
  233. package/docs/config-schema.md +1 -1
  234. package/docs/context-menu.md +13 -13
  235. package/docs/context.md +11 -11
  236. package/docs/css.md +3 -9
  237. package/docs/deep-link.md +25 -25
  238. package/docs/environment-detection.md +16 -16
  239. package/docs/fontawesome.md +7 -5
  240. package/docs/hooks.md +8 -8
  241. package/docs/index.md +38 -27
  242. package/docs/ipc.md +10 -10
  243. package/docs/lib-modules.md +9 -9
  244. package/docs/logging.md +12 -14
  245. package/docs/menu.md +18 -18
  246. package/docs/remote-config.md +9 -9
  247. package/docs/remote-scripts.md +12 -12
  248. package/docs/restart-manager.md +8 -8
  249. package/docs/sentry.md +4 -4
  250. package/docs/shared/analytics.md +1 -1
  251. package/docs/shared/breaking-changes.md +79 -13
  252. package/docs/shared/config.md +17 -21
  253. package/docs/shared/logging.md +1 -1
  254. package/docs/shared/monitoring.md +5 -5
  255. package/docs/shared/testing.md +2 -2
  256. package/docs/shared/theming.md +1 -1
  257. package/docs/shared/translation.md +19 -10
  258. package/docs/startup.md +16 -16
  259. package/docs/storage.md +11 -11
  260. package/docs/templating.md +3 -3
  261. package/docs/test-boot-layer.md +12 -12
  262. package/docs/test-framework.md +17 -17
  263. package/docs/themes.md +7 -7
  264. package/docs/tooltips.md +2 -2
  265. package/docs/tray.md +31 -31
  266. package/docs/usage.md +7 -7
  267. package/docs/verts.md +1 -1
  268. package/docs/windows.md +22 -22
  269. package/package.json +3 -4
  270. package/dist/lib/client-bridge.js +0 -374
  271. package/dist/lib/logger.js +0 -4
  272. package/dist/test/suites/build/manager.test.js +0 -213
  273. package/dist/test/suites/main/client-bridge.test.js +0 -262
package/docs/windows.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Windows
2
2
 
3
- **Lazy named-window registry.** @omega.js/desktop does NOT auto-create any window. Your `main.js` calls `manager.windows.create('main', { show: !startup.isLaunchHidden() })` from inside `manager.initialize().then(() => { ... })`. Always create `main` — its presence in the registry is what lets @omega.js/desktop's `app.on('activate')` (macOS dock click) and `app.on('second-instance')` (win/linux re-launch) handlers surface UI when the user double-clicks the running app. In hidden launches, pass `show: false` to keep the window invisible until something explicitly calls `windows.show('main')`. Use the registry for the common case (named, persistent, integrated windows). For one-off windows (a toast, a print preview), use `new BrowserWindow()` directly — `window-manager` doesn't get in the way.
3
+ **Lazy named-window registry.** @omega.js/desktop does NOT auto-create any window. Your `main.js` calls `omega.windows.create('main', { show: !startup.isLaunchHidden() })` from inside `omega.initialize().then(() => { ... })`. Always create `main`: its presence in the registry is what lets @omega.js/desktop's `app.on('activate')` (macOS dock click) and `app.on('second-instance')` (win/linux re-launch) handlers surface UI when the user double-clicks the running app. In hidden launches, pass `show: false` to keep the window invisible until something explicitly calls `windows.show('main')`. Use the registry for the common case (named, persistent, integrated windows). For one-off windows (a toast, a print preview), use `new BrowserWindow()` directly, `window-manager` doesn't get in the way.
4
4
 
5
5
  ## Re-surface on user re-launch
6
6
 
@@ -14,20 +14,20 @@ Both handlers are no-ops if `main` isn't in the registry. So consumers who genui
14
14
  ## API
15
15
 
16
16
  ```js
17
- await manager.windows.create('main', overrides?) // canonical entry point
18
- await manager.windows.createNamed('main', mgr, opts?) // identical; .create() is sugar
19
- manager.windows.get('main') // BrowserWindow | null
20
- manager.windows.show('main') // show + focus + auto app.dock.show()
21
- manager.windows.hide('main')
22
- manager.windows.close('main') // force-close (bypasses hideOnClose)
23
- manager.windows.list() // string[] of currently-open names
17
+ await omega.windows.create('main', overrides?) // canonical entry point
18
+ await omega.windows.createNamed('main', mgr, opts?) // identical; .create() is sugar
19
+ omega.windows.get('main') // BrowserWindow | null
20
+ omega.windows.show('main') // show + focus + auto app.dock.show()
21
+ omega.windows.hide('main')
22
+ omega.windows.close('main') // force-close (bypasses hideOnClose)
23
+ omega.windows.list() // string[] of currently-open names
24
24
  ```
25
25
 
26
26
  `create()` is single-instance: a second call with the same name returns the existing window and focuses it (no double-create).
27
27
 
28
28
  ## Defaults
29
29
 
30
- No JSON config required. @omega.js/desktop bakes in sensible defaults so `manager.windows.create('main')` "just works":
30
+ No JSON config required. @omega.js/desktop bakes in sensible defaults so `omega.windows.create('main')` "just works":
31
31
 
32
32
  | Window | Defaults |
33
33
  |---|---|
@@ -37,8 +37,8 @@ No JSON config required. @omega.js/desktop bakes in sensible defaults so `manage
37
37
  Override at the call site:
38
38
 
39
39
  ```js
40
- manager.windows.create('main', { width: 1280, height: 800 });
41
- manager.windows.create('settings', { width: 600, height: 480 });
40
+ omega.windows.create('main', { width: 1280, height: 800 });
41
+ omega.windows.create('settings', { width: 600, height: 480 });
42
42
  ```
43
43
 
44
44
  ## Config (optional)
@@ -60,7 +60,7 @@ Per-window keys:
60
60
  | `view` | `main` | `<name>` | Folder under `src/views/`. Loads `dist/views/<view>/index.html`. |
61
61
  | `width` / `height` | 1024 / 720 | 800 / 600 | Initial size (overridden by saved bounds if `persistBounds: true`). |
62
62
  | `minWidth` / `minHeight` | 400 / 300 | 400 / 300 | |
63
- | `show` | `true` | `true` | Auto-show on `ready-to-show`. `false` keeps the window hidden until `manager.windows.show()`. |
63
+ | `show` | `true` | `true` | Auto-show on `ready-to-show`. `false` keeps the window hidden until `omega.windows.show()`. |
64
64
  | `hideOnClose` | `true` | `false` | Discord-style: X click hides instead of closes. See "Hide-on-close" below. |
65
65
  | `title` | `app.productName` | `app.productName` | Window title. |
66
66
  | `backgroundColor` | `#ffffff` | `#ffffff` | Background color before the page loads. |
@@ -96,15 +96,15 @@ The `main` window's X button **hides instead of closes** by default. Real quit o
96
96
  - Menu Quit (`main/quit` on mac, `file/quit` on win/linux)
97
97
  - Tray Quit (`quit` item)
98
98
  - Auto-updater install (`autoUpdater.installNow()`)
99
- - Programmatic `manager.quit({ force: true })`
99
+ - Programmatic `omega.quit({ force: true })`
100
100
 
101
101
  The window-manager close handler checks three flags before deciding to swallow vs let through:
102
102
 
103
103
  | Flag | Set by | Means |
104
104
  |---|---|---|
105
- | `manager._allowQuit` | `manager.quit({ force: true })`, `autoUpdater.installNow()` | Programmatic force — let the close go through. |
106
- | `manager._isQuitting` | `app.on('before-quit')` (every quit path Electron knows about) | App is quitting — let close events flow naturally. |
107
- | `win._emForceClose` | `manager.windows.close(name)` | Per-window override. |
105
+ | `omega._allowQuit` | `omega.quit({ force: true })`, `autoUpdater.installNow()` | Programmatic force: let the close go through. |
106
+ | `omega._isQuitting` | `app.on('before-quit')` (every quit path Electron knows about) | App is quitting: let close events flow naturally. |
107
+ | `win._emForceClose` | `omega.windows.close(name)` | Per-window override. |
108
108
 
109
109
  Other named windows default to `hideOnClose: false` (X actually closes). Override per window via config or call-site overrides.
110
110
 
@@ -121,27 +121,27 @@ Every named window's position and size persist to storage on resize / move / max
121
121
 
122
122
  ## macOS dock auto-show
123
123
 
124
- When `LSUIElement: true` is baked at build time (`startup.mode: 'hidden'`), the app launches with **no dock icon, no Cmd+Tab, no taskbar**. The first time `manager.windows.create()` or `manager.windows.show()` runs, @omega.js/desktop calls `app.dock.show()` automatically — the dock icon appears alongside the window.
124
+ When `LSUIElement: true` is baked at build time (`startup.mode: 'hidden'`), the app launches with **no dock icon, no Cmd+Tab, no taskbar**. The first time `omega.windows.create()` or `omega.windows.show()` runs, @omega.js/desktop calls `app.dock.show()` automatically: the dock icon appears alongside the window.
125
125
 
126
- **And the inverse**: for `startup.mode: 'hidden'` apps, when the LAST visible named window hides (hide-on-close X, `windows.hide()`, any consumer `win.hide()`), @omega.js/desktop calls `app.dock.hide()` again — the app returns to its fully-invisible posture, dock and UI appearing and vanishing together. Normal-mode apps keep their dock icon for life; quit paths are untouched (`manager._isQuitting` guard).
126
+ **And the inverse**: for `startup.mode: 'hidden'` apps, when the LAST visible named window hides (hide-on-close X, `windows.hide()`, any consumer `win.hide()`), @omega.js/desktop calls `app.dock.hide()` again: the app returns to its fully-invisible posture, dock and UI appearing and vanishing together. Normal-mode apps keep their dock icon for life; quit paths are untouched (`omega._isQuitting` guard).
127
127
 
128
128
  This means agent / menubar apps can stay completely invisible until the user explicitly asks for UI:
129
129
 
130
130
  ```js
131
- manager.initialize().then(() => {
131
+ omega.initialize().then(() => {
132
132
  // Don't call windows.create() here — app stays invisible.
133
133
  // Surface UI later when something warrants it:
134
- manager.tray.update('open', { click: () => manager.windows.create('main') });
134
+ omega.tray.update('open', { click: () => omega.windows.create('main') });
135
135
  });
136
136
  ```
137
137
 
138
138
  ## Auto-attach context-menu
139
139
 
140
- Every window created via `manager.windows.create()` is automatically wired up with the consumer's `src/integrations/context-menu/index.js` (see [context-menu.md](context-menu.md)). Idempotent per webContents (uses a WeakSet). For windows you create directly with `new BrowserWindow()`, call `manager.contextMenu.attach(win.webContents)` manually.
140
+ Every window created via `omega.windows.create()` is automatically wired up with the consumer's `src/integrations/context-menu/index.js` (see [context-menu.md](context-menu.md)). Idempotent per webContents (uses a WeakSet). For windows you create directly with `new BrowserWindow()`, call `omega.contextMenu.attach(win.webContents)` manually.
141
141
 
142
142
  ## Testing mode: stealth surfacing
143
143
 
144
- When `manager.isTesting()` (i.e. under `npx omega test`), every surfacing path in this lib — `ready-to-show`, `windows.show()`, the create-dedup focus — goes **stealth** instead of `win.show()`: `_surface()` applies the shared recipe from `src/utils/stealth-window.js` and surfaces via `showInactive()`, with no `win.focus()`/dock surfacing. **The full stealth story lives in [test-framework.md](test-framework.md) (SSOT)** — the recipe and why it's not `hide()`/`minimize()`, the raw-window `browser-window-created` hook, `webContents.focus()` suppression, macOS app-level activation suppression, and the **`OMEGA_TEST_SHOW=1`** opt-out. Covered by `suites/main/stealth-window.test.js`.
144
+ When `omega.isTesting()` (i.e. under `npx omega test`), every surfacing path in this lib (`ready-to-show`, `windows.show()`, the create-dedup focus) goes **stealth** instead of `win.show()`: `_surface()` applies the shared recipe from `src/utils/stealth-window.js` and surfaces via `showInactive()`, with no `win.focus()`/dock surfacing. **The full stealth story lives in [test-framework.md](test-framework.md) (SSOT)**: the recipe and why it's not `hide()`/`minimize()`, the raw-window `browser-window-created` hook, `webContents.focus()` suppression, macOS app-level activation suppression, and the **`OMEGA_TEST_SHOW=1`** opt-out. Covered by `suites/main/stealth-window.test.js`.
145
145
 
146
146
  ## Platform behavior
147
147
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omega.js/desktop",
3
- "version": "0.52.0",
3
+ "version": "0.53.0",
4
4
  "description": "OMEGA desktop framework — build, test, and package Electron apps for macOS, Windows, and Linux",
5
5
  "private": false,
6
6
  "publishConfig": {
@@ -23,7 +23,6 @@
23
23
  "./lib/context-menu": "./dist/lib/context-menu.js",
24
24
  "./lib/deep-link": "./dist/lib/deep-link.js",
25
25
  "./lib/ipc": "./dist/lib/ipc.js",
26
- "./lib/logger": "./dist/lib/logger.js",
27
26
  "./lib/logger-lite": "./dist/lib/logger-lite.js",
28
27
  "./lib/menu": "./dist/lib/menu.js",
29
28
  "./lib/protocol": "./dist/lib/protocol.js",
@@ -33,7 +32,7 @@
33
32
  "./lib/storage": "./dist/lib/storage.js",
34
33
  "./lib/templating": "./dist/lib/templating.js",
35
34
  "./lib/tray": "./dist/lib/tray.js",
36
- "./lib/client-bridge": "./dist/lib/client-bridge.js",
35
+ "./lib/auth": "./dist/lib/auth.js",
37
36
  "./lib/window-manager": "./dist/lib/window-manager.js",
38
37
  "./cli": "./dist/cli-run.js",
39
38
  "./ensure-target": "./dist/commands/lib/ensure-target.js",
@@ -155,7 +154,7 @@
155
154
  "@fortawesome/fontawesome-free": "^7.3.0",
156
155
  "@inquirer/prompts": "^8.5.2",
157
156
  "@octokit/rest": "^22.0.1",
158
- "@omega.js/client": "0.52.0",
157
+ "@omega.js/client": "0.53.0",
159
158
  "@popperjs/core": "^2.11.8",
160
159
  "@sentry/electron": "^7.13.0",
161
160
  "chalk": "^5.6.2",
@@ -1,374 +0,0 @@
1
- // Client bridge — main-process Firebase Auth, source of truth for renderers.
2
- //
3
- // This is @omega.js/desktop's analogue of BXM's background-service-worker auth role. The pattern:
4
- //
5
- // 1. Main runs its own Firebase Auth instance and is the source of truth.
6
- // 2. When a deep-link auth/token arrives, main calls signInWithCustomToken with that token,
7
- // then BROADCASTS the token to all renderer windows so their @omega.js/client Firebase
8
- // instances can sign in with the SAME token.
9
- // 3. On every renderer load, the renderer asks main "I'm at UID X (or null)" via the
10
- // desktop:auth:sync-request IPC. Main compares with its own UID and either does nothing,
11
- // tells the renderer to sign out, or fetches a fresh custom token from /omega
12
- // and sends it to the renderer.
13
- // 4. Sign-out: any renderer can request sign-out via desktop:auth:sign-out. Main signs out
14
- // its own Firebase + broadcasts desktop:auth:sign-out to all renderers.
15
- //
16
- // Firebase is BUNDLED by esbuild from @omega.js/desktop's module context (@omega.js/client owns it in @omega.js/desktop's
17
- // tree). If loading fails, the bridge stays in no-op mode and logs the reason.
18
-
19
- const LoggerLite = require('./logger-lite.js');
20
- const authPersistence = require('./auth-persistence.js');
21
- const { requiredPort } = require('../utils/url-helpers.js');
22
-
23
- const logger = new LoggerLite('client-bridge');
24
-
25
- const FIREBASE_APP_NAME = 'omega-auth';
26
-
27
- const bridge = {
28
- _initialized: false,
29
- _manager: null,
30
- _firebase: null, // firebase/app
31
- _firebaseAuth: null, // auth instance
32
- _firebaseModule: null, // firebase/auth namespace (signInWithCustomToken, signOut, ...)
33
- _stateSubs: new Set(), // main-side subscribers fn(user)
34
- _ipcRegistered: false,
35
- _resolvedPlan: null, // renderer-resolved subscription { plan, active, trialing, cancelling }
36
- _resolvedRoles: null, // renderer-resolved account roles { admin, betaTester, ... }
37
-
38
- async initialize(manager) {
39
- if (bridge._initialized) {
40
- return;
41
- }
42
-
43
- bridge._manager = manager;
44
-
45
- // Try to load firebase. If it's not installed, run in no-op mode.
46
- const ok = await bridge._tryLoadFirebase();
47
- if (!ok) {
48
- logger.warn('firebase not installed — client-bridge running in no-op mode. `npm i firebase` to enable auth.');
49
- }
50
-
51
- bridge._registerIpc();
52
-
53
- if (ok) {
54
- // Boot Firebase Auth. With a persistence strategy active (lib/auth-persistence.js,
55
- // default safeStorage), the session survives restarts — the restore lands async
56
- // via onAuthStateChanged shortly after boot.
57
- try {
58
- const strategy = await authPersistence.resolve(manager);
59
- bridge._firebaseAuth = bridge._getFirebaseAuth(strategy);
60
- // onAuthStateChanged is the source of truth listener.
61
- bridge._firebaseModule.onAuthStateChanged(bridge._firebaseAuth, (user) => {
62
- bridge._handleAuthStateChange(user);
63
- });
64
- logger.log(`initialize — firebase loaded (persistence: ${strategy ? strategy.name : 'in-memory'})`);
65
- } catch (e) {
66
- logger.error('firebase init failed:', e.message);
67
- }
68
- }
69
-
70
- bridge._initialized = true;
71
- },
72
-
73
- // Load firebase. Returns true on success. BUNDLED by esbuild from @omega.js/desktop's module context
74
- // (@omega.js/client owns firebase in @omega.js/desktop's tree) — same treatment as json5 in main.js. In the
75
- // webpack era it was a webpackIgnore'd runtime import(), which resolves relative to the CONSUMER's
76
- // main.bundle.js: that walk never reaches @omega.js/desktop's node_modules when @omega.js/desktop is symlinked
77
- // (`mgr install dev`) and depends on npm hoisting when installed — every dev app silently
78
- // ran the bridge in no-op mode. Interop guards handle both namespace shapes (json5 precedent).
79
- async _tryLoadFirebase() {
80
- try {
81
- const appMod = require('firebase/app');
82
- const authMod = require('firebase/auth');
83
-
84
- bridge._firebase = appMod.initializeApp ? appMod : (appMod.default || appMod);
85
- bridge._firebaseModule = authMod.getAuth ? authMod : (authMod.default || authMod);
86
- return true;
87
- } catch (e) {
88
- logger.warn('firebase load failed:', e.message);
89
- return false;
90
- }
91
- },
92
-
93
- _getFirebaseAuth(strategy) {
94
- if (bridge._firebaseAuth) return bridge._firebaseAuth;
95
-
96
- const firebaseConfig = bridge._manager.config.cloud?.config;
97
- if (!firebaseConfig || !Object.keys(firebaseConfig).length) {
98
- throw new Error('cloud.config is empty — cannot initialize auth.');
99
- }
100
-
101
- const { initializeApp, getApp } = bridge._firebase;
102
- const { getAuth, initializeAuth } = bridge._firebaseModule;
103
-
104
- let app;
105
- try { app = getApp(FIREBASE_APP_NAME); }
106
- catch (e) { app = initializeApp(firebaseConfig, FIREBASE_APP_NAME); }
107
-
108
- // With a storage strategy, boot auth on OUR persistence (session survives
109
- // restarts); without one, firebase's Node default (in-memory) applies.
110
- if (strategy && initializeAuth) {
111
- bridge._firebaseAuth = initializeAuth(app, {
112
- persistence: [authPersistence.buildFirebasePersistence(strategy)],
113
- });
114
- } else {
115
- bridge._firebaseAuth = getAuth(app);
116
- }
117
-
118
- // A TESTING run talks to the LOCAL stack, never real auth — the same move
119
- // getApiUrl() makes when it maps testing to localhost, and the same one
120
- // @omega.js/extension's background SW makes for its emulator runs. Only an
121
- // OMEGA_ENVIRONMENT=testing run reaches here; dev and production are untouched.
122
- if (bridge._manager.isTesting()) {
123
- const { connectAuthEmulator } = bridge._firebaseModule;
124
- const port = bridge._authEmulatorPort();
125
- logger.log(`testing run — connecting auth to the emulator on :${port}`);
126
- connectAuthEmulator(bridge._firebaseAuth, `http://localhost:${port}`, { disableWarnings: true });
127
- }
128
-
129
- return bridge._firebaseAuth;
130
- },
131
-
132
- // The auth emulator's port, on the same chain getApiUrl() walks: the
133
- // resolved-port env channel (N7), then the `dev.ports` map the bundle baked
134
- // into OMEGA_BUILD_JSON
135
- // ([#745](https://github.com/Omega-JS-Stack/omega/issues/745)). The classic
136
- // 9099 used to sit under those two as a last resort; it is gone (#834),
137
- // because nothing identity-checks what answers on it and a neighbouring
138
- // project's emulator reads as an auth mystery rather than a port problem.
139
- _authEmulatorPort() {
140
- return requiredPort(bridge._manager, 'OMEGA_AUTH_PORT', 'auth');
141
- },
142
-
143
- _registerIpc() {
144
- if (bridge._ipcRegistered) return;
145
- const ipc = bridge._manager.ipc;
146
-
147
- // Renderer asks main: "I'm at UID X (or null). Are we in sync?"
148
- ipc.handle('desktop:auth:sync-request', async ({ contextUid }) => {
149
- return bridge._handleSyncRequest(contextUid);
150
- });
151
-
152
- // Renderer asks main to sign out.
153
- ipc.handle('desktop:auth:sign-out', async () => {
154
- return bridge._handleSignOut();
155
- });
156
-
157
- // Renderer asks main for the current user (uid + email + displayName, no token).
158
- ipc.handle('desktop:auth:get-user', () => {
159
- const u = bridge._firebaseAuth?.currentUser;
160
- return u ? bridge._snapshotUser(u) : null;
161
- });
162
-
163
- // Renderer pushes its @omega.js/client account resolution (auth().listen() settled:
164
- // Firestore account fetched, subscription resolved). Main can't run Firestore,
165
- // so this is how main-side plan gates learn the REAL plan — BXM's "browser
166
- // contexts resolve, authority caches" split. UID-guarded: a stale push from a
167
- // renderer that hasn't synced yet is dropped.
168
- ipc.handle('desktop:auth:account-resolved', ({ uid, resolved, roles } = {}) => {
169
- const mainUid = bridge._firebaseAuth?.currentUser?.uid || null;
170
- if (!uid || uid !== mainUid) {
171
- return { accepted: false };
172
- }
173
-
174
- const changed = JSON.stringify(resolved) !== JSON.stringify(bridge._resolvedPlan)
175
- || JSON.stringify(roles || null) !== JSON.stringify(bridge._resolvedRoles);
176
- bridge._resolvedPlan = resolved || null;
177
- bridge._resolvedRoles = roles || null;
178
-
179
- if (changed) {
180
- logger.log(`account resolved — plan=${resolved?.plan || '(none)'} active=${!!resolved?.active}`);
181
- bridge._manager.ipc.broadcast('desktop:auth:plan-changed', { resolved: bridge._resolvedPlan, roles: bridge._resolvedRoles });
182
- // Main-side subscribers see the same user snapshot again with plan now known.
183
- const u = bridge._firebaseAuth?.currentUser;
184
- bridge._stateSubs.forEach((fn) => {
185
- try { fn(u ? bridge._snapshotUser(u) : null); } catch (e) { logger.error('onAuthChange subscriber threw:', e); }
186
- });
187
- }
188
- return { accepted: true };
189
- });
190
-
191
- bridge._ipcRegistered = true;
192
- },
193
-
194
- _handleAuthStateChange(user) {
195
- logger.log(`auth state → ${user ? user.email : 'signed out'}`);
196
-
197
- // Sign-out (or a different user signing in) invalidates the cached resolution —
198
- // renderers re-resolve and push fresh via desktop:auth:account-resolved.
199
- if (!user) {
200
- bridge._resolvedPlan = null;
201
- bridge._resolvedRoles = null;
202
- }
203
-
204
- // Notify any main-side subscribers (consumer code that called manager.omega.onAuthChange).
205
- const snap = user ? bridge._snapshotUser(user) : null;
206
- bridge._stateSubs.forEach((fn) => {
207
- try { fn(snap); } catch (e) { logger.error('onAuthChange subscriber threw:', e); }
208
- });
209
-
210
- // Tell renderers about the change so they can update UI / refresh menu items.
211
- bridge._manager.ipc.broadcast('desktop:auth:state-changed', snap);
212
-
213
- // Attribute Sentry events to the signed-in user (or clear on sign-out). When
214
- // sentry is disabled (no DSN, dev mode, etc.) setUser is a documented no-op.
215
- bridge._manager.sentry.setUser(snap);
216
- },
217
-
218
- // BXM's syncAuth pattern, ported to Electron.
219
- async _handleSyncRequest(contextUid) {
220
- if (!bridge._firebaseAuth) {
221
- return { needsSync: false, reason: 'firebase-not-loaded' };
222
- }
223
-
224
- const bgUser = bridge._firebaseAuth.currentUser;
225
- const bgUid = bgUser?.uid || null;
226
-
227
- // Already in sync.
228
- if (contextUid === bgUid) {
229
- return { needsSync: false };
230
- }
231
-
232
- // Renderer signed in but main signed out → tell renderer to sign out.
233
- if (!bgUser && contextUid) {
234
- return { needsSync: true, signOut: true };
235
- }
236
-
237
- // Main signed in, renderer not (or different user) → fetch a fresh custom token + send.
238
- try {
239
- const token = await bridge._fetchCustomToken(bgUser);
240
- return {
241
- needsSync: true,
242
- customToken: token,
243
- user: bridge._snapshotUser(bgUser),
244
- };
245
- } catch (e) {
246
- logger.error('sync-request: failed to fetch custom token:', e.message);
247
- return { needsSync: false, error: e.message };
248
- }
249
- },
250
-
251
- async _handleSignOut() {
252
- try {
253
- if (bridge._firebaseAuth?.currentUser) {
254
- await bridge._firebaseModule.signOut(bridge._firebaseAuth);
255
- }
256
- bridge._manager.ipc.broadcast('desktop:auth:sign-out', {});
257
- return { success: true };
258
- } catch (e) {
259
- logger.error('sign-out failed:', e.message);
260
- return { success: false, error: e.message };
261
- }
262
- },
263
-
264
- // Public API ──────────────────────────────────────────────────────────────────
265
-
266
- // Called by lib/deep-link's auth/token built-in.
267
- async handleAuthToken(token) {
268
- if (!bridge._firebaseAuth) {
269
- logger.warn('handleAuthToken called but firebase not loaded — ignoring.');
270
- return { success: false, reason: 'firebase-not-loaded' };
271
- }
272
- if (!token) {
273
- logger.warn('handleAuthToken called with no token — ignoring.');
274
- return { success: false, reason: 'no-token' };
275
- }
276
-
277
- try {
278
- const cred = await bridge._firebaseModule.signInWithCustomToken(bridge._firebaseAuth, token);
279
- logger.log(`signed in: ${cred.user.email || cred.user.uid}`);
280
-
281
- // Broadcast the token to renderers so they can sign in with the SAME token.
282
- // Tokens expire in 1 hour and aren't stored.
283
- bridge._manager.ipc.broadcast('desktop:auth:sign-in-with-token', { token });
284
-
285
- return { success: true, user: bridge._snapshotUser(cred.user) };
286
- } catch (e) {
287
- logger.error('signInWithCustomToken failed:', e.message);
288
- return { success: false, error: e.message };
289
- }
290
- },
291
-
292
- // Subscribe to auth state changes on the main side (e.g. for menu/tray refresh).
293
- onAuthChange(fn) {
294
- bridge._stateSubs.add(fn);
295
- return () => bridge._stateSubs.delete(fn);
296
- },
297
-
298
- // Snapshot of the current user (sync), or null.
299
- getCurrentUser() {
300
- const u = bridge._firebaseAuth?.currentUser;
301
- return u ? bridge._snapshotUser(u) : null;
302
- },
303
-
304
- // The renderer-resolved subscription ({ plan, active, trialing, cancelling }) or null
305
- // while no renderer has resolved yet. THE main-side plan source — consumer plan gates
306
- // read this (@omega.js/client's resolveSubscription output, pushed via desktop:auth:account-resolved).
307
- getResolvedPlan() {
308
- return bridge._resolvedPlan;
309
- },
310
-
311
- // The renderer-resolved account roles ({ admin, betaTester, ... }) or null.
312
- getResolvedRoles() {
313
- return bridge._resolvedRoles;
314
- },
315
-
316
- // Fresh Firebase ID token for the signed-in user (async), or null when
317
- // signed out / Firebase absent. For consumer main code calling
318
- // authenticated backend routes (`Authorization: Bearer <token>`).
319
- async getIdToken() {
320
- const u = bridge._firebaseAuth?.currentUser;
321
- return u ? u.getIdToken() : null;
322
- },
323
-
324
- // Force sign-out from main code.
325
- async signOut() {
326
- return bridge._handleSignOut();
327
- },
328
-
329
- // ─── Internals ────────────────────────────────────────────────────────────
330
-
331
- _snapshotUser(user) {
332
- return {
333
- uid: user.uid,
334
- email: user.email,
335
- displayName: user.displayName,
336
- photoURL: user.photoURL,
337
- emailVerified: user.emailVerified,
338
- };
339
- },
340
-
341
- // Fetch a fresh custom token for the currently-signed-in user via the shared
342
- // @omega.js/client request layer. Mirrors BXM: POST <apiUrl>/omega/user/token —
343
- // the route defaults uid to the authenticated caller and responds { token }
344
- // at the top level.
345
- async _fetchCustomToken(user) {
346
- const { createRequest } = require('@omega.js/client/modules/request.js');
347
-
348
- const request = createRequest({
349
- getApiUrl: () => bridge._manager.getApiUrl(),
350
- getIdToken: (force) => user.getIdToken(force),
351
- });
352
-
353
- const data = await request('/omega/user/token', { method: 'POST', body: {} });
354
-
355
- const token = data?.token;
356
- if (!token) {
357
- throw new Error('@omega.js/backend response missing token.');
358
- }
359
- return token;
360
- },
361
-
362
- // Tear-down used by tests — wipes state but keeps registered IPC handlers
363
- // (they're idempotent via `ipc.handle` duplicate-detection).
364
- async _resetForTests() {
365
- try {
366
- if (bridge._firebaseAuth?.currentUser && bridge._firebaseModule?.signOut) {
367
- await bridge._firebaseModule.signOut(bridge._firebaseAuth);
368
- }
369
- } catch (e) { /* ignore */ }
370
- bridge._stateSubs.clear();
371
- },
372
- };
373
-
374
- module.exports = bridge;
@@ -1,4 +0,0 @@
1
- // Shared devkit module — vendored into dist/vendor/devkit at prepare time by the
2
- // preparePackage `after` hook. This shim keeps the framework's internal require
3
- // paths stable while the implementation lives in @omega.js/devkit.
4
- module.exports = require('../vendor/devkit/logger.js');