@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/startup.md CHANGED
@@ -24,7 +24,7 @@ The default behavior — `mode: 'normal'` + `openAtLogin: { enabled: true, mode:
24
24
 
25
25
  ### `normal` (default)
26
26
 
27
- Standard app behavior. Your `main.js` calls `windows.create('main', { show: !startup.isLaunchHidden() })` from inside `manager.initialize().then(...)`. In `normal` mode, `show` is `true` so the window appears immediately. Dock visible (macOS), taskbar entry (win/linux).
27
+ Standard app behavior. Your `main.js` calls `windows.create('main', { show: !startup.isLaunchHidden() })` from inside `omega.initialize().then(...)`. In `normal` mode, `show` is `true` so the window appears immediately. Dock visible (macOS), taskbar entry (win/linux).
28
28
 
29
29
  ### `hidden`
30
30
 
@@ -38,31 +38,31 @@ Use this for: menubar apps, agent apps (clipboard managers, time trackers, syste
38
38
 
39
39
  > **Note:** the deprecated `'tray-only'` mode is no longer valid — its behavior was always identical to `'hidden'`, so they've been folded into one. Old `tray-only` configs fall back to `'normal'` per `getMode()` validation.
40
40
 
41
- ## Public API on `manager.startup`
41
+ ## Public API on `omega.startup`
42
42
 
43
43
  ```js
44
- manager.startup.getMode() // user-launch mode: 'normal' | 'hidden'
45
- manager.startup.isLaunchHidden() // true if THIS launch is hidden — combines
44
+ omega.startup.getMode() // user-launch mode: 'normal' | 'hidden'
45
+ omega.startup.isLaunchHidden() // true if THIS launch is hidden: combines
46
46
  // user-launch mode + login-launch detection.
47
47
  // Use this in main.js to gate windows.create().
48
- manager.startup.wasLaunchedAtLogin() // true if the OS auto-launched us at login
49
- manager.startup.applyEarly() // calls app.dock.hide() if needed (called by main.js boot)
48
+ omega.startup.wasLaunchedAtLogin() // true if the OS auto-launched us at login
49
+ omega.startup.applyEarly() // calls app.dock.hide() if needed (called by main.js boot)
50
50
 
51
- manager.startup.setOpenAtLogin(true) // back-compat boolean form
52
- manager.startup.setOpenAtLogin({ enabled: true, mode: 'hidden' }) // object form
53
- manager.startup.isOpenAtLogin() // read live OS state
51
+ omega.startup.setOpenAtLogin(true) // back-compat boolean form
52
+ omega.startup.setOpenAtLogin({ enabled: true, mode: 'hidden' }) // object form
53
+ omega.startup.isOpenAtLogin() // read live OS state
54
54
  ```
55
55
 
56
56
  ## Typical main.js pattern
57
57
 
58
58
  ```js
59
- manager.initialize().then(() => {
59
+ omega.initialize().then(() => {
60
60
  // Always create the main window. In hidden launches, `show: false` keeps it
61
61
  // invisible until something explicitly calls windows.show('main') — but it's
62
62
  // in the registry, so @omega.js/desktop's activate/second-instance handlers can find and
63
63
  // surface it when the user double-clicks the running app.
64
- manager.windows.create('main', {
65
- show: !manager.startup.isLaunchHidden(),
64
+ omega.windows.create('main', {
65
+ show: !omega.startup.isLaunchHidden(),
66
66
  });
67
67
  });
68
68
  ```
@@ -71,9 +71,9 @@ Don't conditionally skip `create()` for hidden launches — without `main` in th
71
71
 
72
72
  ## Boot order
73
73
 
74
- `startup.applyEarly()` is the **first** call in `Manager.initialize()` — before `whenReady`, before any other lib. The goal: spend as little time as possible in the dock-bounce window.
74
+ `startup.applyEarly()` is the **first** call in `omega.initialize()`: before `whenReady`, before any other lib. The goal: spend as little time as possible in the dock-bounce window.
75
75
 
76
- Sequence: applyEarly → before-quit hook → ipc → storage → sentry → protocol → deep-link → app-state → whenReady → updater → tray/menu/contextMenu → startup.initialize → @omega.js/client → windows.initialize. **@omega.js/desktop no longer auto-creates the main window** — your `main.js` does that inside the `.then()` callback after `initialize()` resolves.
76
+ Sequence: applyEarly → before-quit hook → ipc → storage → theme → fontawesome → sentry → protocol → deep-link → auth-flow → app-state → context → usage → whenReady → updater → tray/menu/contextMenu → startup.initialize → auth → remote-config → remote-scripts → analytics → restart-manager → windows.initialize (full list: [boot-sequence.md](boot-sequence.md)). **@omega.js/desktop does not auto-create the main window**: your `main.js` does that inside the `.then()` callback after `initialize()` resolves.
77
77
 
78
78
  ## How zero-bounce works on macOS
79
79
 
@@ -86,7 +86,7 @@ Sequence: applyEarly → before-quit hook → ipc → storage → sentry → pro
86
86
 
87
87
  With the key baked, a MANUAL launch also starts dockless — the dock icon appears the moment the main window surfaces (every surface path runs `_ensureDockVisible()` → `app.dock.show()`), so the visible difference is only that the bounce animation is replaced by the icon appearing when the window is ready.
88
88
 
89
- At runtime, when the consumer first calls `manager.windows.show()` (or the `windows.create()` call resolves with `show: true`), @omega.js/desktop calls `app.dock.show()` so the dock icon appears alongside the window. Reverses cleanly via `app.dock.hide()` if you want to go back to invisible.
89
+ At runtime, when the consumer first calls `omega.windows.show()` (or the `windows.create()` call resolves with `show: true`), @omega.js/desktop calls `app.dock.show()` so the dock icon appears alongside the window. Reverses cleanly via `app.dock.hide()` if you want to go back to invisible.
90
90
 
91
91
  The injection is YAML-text-level (preserves comments, idempotent, merges with existing `extendInfo`). See `src/gulp/tasks/build-config.js`.
92
92
 
@@ -136,7 +136,7 @@ Hidden / agent apps usually want:
136
136
  And in `src/integrations/tray/index.js`:
137
137
 
138
138
  ```js
139
- tray.update('open', { click: () => manager.windows.show('main') });
139
+ tray.update('open', { click: () => omega.windows.show('main') });
140
140
  ```
141
141
 
142
142
  The window is created at boot but invisible. When the user clicks the tray's "Open" item (or double-clicks the app icon), `windows.show('main')` runs, @omega.js/desktop calls `app.dock.show()`, and the user sees both the dock icon and the window appear together.
package/docs/storage.md CHANGED
@@ -13,13 +13,13 @@ Linux: ~/.config/<productName>/omega-storage.json
13
13
  ## Main-process API (sync, direct disk-backed)
14
14
 
15
15
  ```js
16
- manager.storage.get(key, defaultValue) // any
17
- manager.storage.set(key, value)
18
- manager.storage.delete(key)
19
- manager.storage.has(key) // boolean
20
- manager.storage.clear()
21
- manager.storage.onChange(key, fn) // returns unsubscribe fn
22
- manager.storage.getPath() // absolute path to omega-storage.json
16
+ omega.storage.get(key, defaultValue) // any
17
+ omega.storage.set(key, value)
18
+ omega.storage.delete(key)
19
+ omega.storage.has(key) // boolean
20
+ omega.storage.clear()
21
+ omega.storage.onChange(key, fn) // returns unsubscribe fn
22
+ omega.storage.getPath() // absolute path to omega-storage.json
23
23
  ```
24
24
 
25
25
  ## Renderer-process API (async, proxied through preload + IPC)
@@ -41,19 +41,19 @@ off();
41
41
  Keys support dot-notation for nested objects natively:
42
42
 
43
43
  ```js
44
- manager.storage.set('window.main.bounds', { x: 10, y: 20, w: 800, h: 600 });
45
- manager.storage.get('window.main.bounds.w'); // → 800
44
+ omega.storage.set('window.main.bounds', { x: 10, y: 20, w: 800, h: 600 });
45
+ omega.storage.get('window.main.bounds.w'); // → 800
46
46
  ```
47
47
 
48
48
  ## Change broadcasts
49
49
 
50
50
  Every `set` / `delete` / `clear` in main broadcasts an `desktop:storage:change` IPC event to all renderer windows. The renderer's `window.desktop.storage.onChange` filters by key locally.
51
51
 
52
- In main, `manager.storage.onChange(key, fn)` registers a callback fired with `(value, previous)`.
52
+ In main, `omega.storage.onChange(key, fn)` registers a callback fired with `(value, previous)`.
53
53
 
54
54
  ## Implementation notes
55
55
 
56
- - Storage initialization is async — `Manager.initialize()` `await`s it before any other lib boots, since features like `app-state` and `windows` rely on it.
56
+ - Storage initialization is async: `omega.initialize()` `await`s it before any other lib boots, since features like `app-state` and `windows` rely on it.
57
57
  - IPC handlers (`desktop:storage:get` etc.) are registered on the @omega.js/desktop `ipc` bus, not directly on `ipcMain`. See [ipc.md](ipc.md).
58
58
  - The store uses `name: 'omega-storage'` (filename `omega-storage.json`). Don't reuse this name in a separate `electron-store` instance.
59
59
  - `electron-store@11` is ESM-only. The bundler inlines it INTO `main.bundle.js` (the static-specifier `import()` in `lib/storage.js`) — consumers install NOTHING; packaged apps carry it inside the bundle with no runtime resolution. (It used to be a runtime import the bundler was told to ignore, which silently no-op'd storage in packaged consumers — @omega.js/desktop is a devDependency and never ships in the asar.)
@@ -77,13 +77,13 @@ Drop your own `config/page-template.html` in your project root. @omega.js/deskto
77
77
 
78
78
  ## Runtime API
79
79
 
80
- `manager.templating` is also available at runtime if you need to template a string yourself (e.g. dynamic deep-link routes):
80
+ `omega.templating` is also available at runtime if you need to template a string yourself (e.g. dynamic deep-link routes):
81
81
 
82
82
  ```js
83
- manager.templating.render('Hello {{ user.name }}', { user: { name: 'Ian' } });
83
+ omega.templating.render('Hello {{ user.name }}', { user: { name: 'Ian' } });
84
84
  // → 'Hello Ian'
85
85
 
86
- manager.templating.render('Custom [name]', { name: 'X' }, { brackets: ['[', ']'] });
86
+ omega.templating.render('Custom [name]', { name: 'X' }, { brackets: ['[', ']'] });
87
87
  // → 'Custom X'
88
88
  ```
89
89
 
@@ -11,12 +11,12 @@ The build and the boot happen in a **staged app root of their own**, `<project>/
11
11
  | `build` | Plain Node — config parsing, util fns, schema validation. | Fast (ms) |
12
12
  | `main` | @omega.js/desktop lib code in isolation (storage, ipc, tray, etc.) inside Electron. | Fast (~50ms each) |
13
13
  | `renderer` | Inside a hidden BrowserWindow. | Fast |
14
- | `boot` | The **whole boot integration** — consumer's main.js → manager.initialize → live state | ~1s startup, then fast |
14
+ | `boot` | The **whole boot integration**: consumer's main.js → omega.initialize → live state | ~1s startup, then fast |
15
15
 
16
16
  Use `boot` for tests that need to verify **integration** rather than unit behavior:
17
17
  - "Does the consumer's `src/main.js` actually wire up correctly?"
18
18
  - "Did all 13 boot steps complete without throwing?"
19
- - "Did config flow from JSON5 → manager.config → tray titles?"
19
+ - "Did config flow from JSON5 → omega.config → tray titles?"
20
20
  - "Did `src/integrations/{tray,menu,context-menu}/index.js` load?"
21
21
  - "Is the menu rendered with the expected default ids?"
22
22
 
@@ -31,17 +31,17 @@ module.exports = {
31
31
  timeout: 20000,
32
32
  tests: [
33
33
  {
34
- description: 'manager initialized end-to-end',
35
- inspect: async ({ manager, expect, projectRoot }) => {
36
- expect(manager._initialized).toBe(true);
37
- expect(manager.config).toBeTruthy();
34
+ description: 'omega initialized end-to-end',
35
+ inspect: async ({ omega, expect, projectRoot }) => {
36
+ expect(omega._initialized).toBe(true);
37
+ expect(omega.config).toBeTruthy();
38
38
  },
39
39
  },
40
40
  {
41
41
  description: 'tray + menu rendered',
42
- inspect: async ({ manager, expect }) => {
43
- expect(manager.tray.has('open')).toBe(true);
44
- expect(manager.menu.isRendered()).toBe(true);
42
+ inspect: async ({ omega, expect }) => {
43
+ expect(omega.tray.has('open')).toBe(true);
44
+ expect(omega.menu.isRendered()).toBe(true);
45
45
  },
46
46
  },
47
47
  ],
@@ -51,7 +51,7 @@ module.exports = {
51
51
  The `inspect` function receives:
52
52
  | Arg | Description |
53
53
  |---|---|
54
- | `manager` | The fully-initialized live Manager instance — same one your consumer code uses. |
54
+ | `omega` | The fully-initialized live main-process instance, the same one your consumer code uses. |
55
55
  | `expect` | @omega.js/desktop's [Jest-compatible assertion library](../src/test/assert.js). |
56
56
  | `projectRoot` | Absolute path to the consumer project root (its `src/`, `config/` — and the `dist/` a boot run must never write). |
57
57
  | `appRoot` | Absolute path to the staged app root Electron booted — `<projectRoot>/.omega/test-app`. Assert on built artifacts here (`<appRoot>/dist/main.bundle.js`), not under `projectRoot`. |
@@ -67,7 +67,7 @@ The `inspect` function receives:
67
67
  - `OMEGA_TEST_BOOT=1` — gate
68
68
  - `OMEGA_TEST_BOOT_HARNESS=<absolute path to dist/test/harness/boot-entry.js>`
69
69
  - `OMEGA_TEST_BOOT_SPEC=<temp file with test definitions>`
70
- 5. @omega.js/desktop's `main.js` boots normally; after `manager.initialize()` resolves, detects `OMEGA_TEST_BOOT=1`, reconstitutes each `inspect` from its serialized body string, runs them sequentially, and emits `__EM_TEST__` JSON lines on stdout.
70
+ 5. @omega.js/desktop's `main.js` boots normally; after `omega.initialize()` resolves, detects `OMEGA_TEST_BOOT=1`, reconstitutes each `inspect` from its serialized body string, runs them sequentially, and emits `__OMEGA_TEST__` JSON lines on stdout (the ONE prefix, `TEST_EVENT_PREFIX` in [src/utils/test-events.js](../src/utils/test-events.js)).
71
71
  6. Test runner parses results, calls `app.exit()`. **No sleep, no kill.**
72
72
 
73
73
  ## View suites in this lane
@@ -152,6 +152,6 @@ The `build`/`main`/`renderer` layers cover @omega.js/desktop's lib code fast and
152
152
 
153
153
  ## Limitations
154
154
 
155
- - Tests run sequentially in a single Electron process to amortize startup cost (~1s). State doesn't carry across tests — they all share one `manager` instance.
155
+ - Tests run sequentially in a single Electron process to amortize startup cost (~1s). State doesn't carry across tests: they all share one `omega` instance.
156
156
  - `inspect` function bodies are serialized via `Function.prototype.toString` and reconstituted with `new Function(...)`. Closures over the test file's outer scope **don't survive** — only the `inspect` argument bag is available inside.
157
157
  - We can't simulate user input (clicking the tray, right-clicking, typing). For that, you'd need `nut-js` or similar — out of scope.
@@ -4,16 +4,16 @@ Built-in test framework for both @omega.js/desktop itself and consumer projects.
4
4
 
5
5
  ## 🚫 NEVER mock — test against the real harness (HARD RULE)
6
6
 
7
- **Do NOT hand-roll fake/stub/mock objects** — no `mockManager`, fake `ipc`/`storage`/`window`/`tray`, stubbed `app`/`BrowserWindow`, or fake IPC channels. Every test gets the **real** framework context:
7
+ **Do NOT hand-roll fake/stub/mock objects**: no mock `omega`, fake `ipc`/`storage`/`window`/`tray`, stubbed `app`/`BrowserWindow`, or fake IPC channels. Every test gets the **real** framework context:
8
8
 
9
9
  - `build` runs real @omega.js/desktop helper code in plain Node.
10
- - `main` / `renderer` / `boot` run inside a **real spawned Electron process**, where `ctx.manager` (and boot's `inspect({ manager })`) is the **real booted Manager** — real `manager.storage`, `manager.ipc`, `manager.tray`, `manager.windows`, etc. Use them; exercise the code the way production does.
10
+ - `main` / `renderer` / `boot` run inside a **real spawned Electron process**, where `ctx.omega` (and boot's `inspect({ omega })`) is the **real booted main-process instance**: real `omega.storage`, `omega.ipc`, `omega.tray`, `omega.windows`, etc. Use them; exercise the code the way production does.
11
11
 
12
12
  **Pure functions are the ONLY exception.** A function with zero I/O (config-defaults merge, icon-path resolver, schema validator, CLI alias resolver, a string/number transform) can be `require()`d and called directly with plain inputs — that's not mocking, there's nothing to mock. The moment a function touches `app.*` / `BrowserWindow` / `ipcMain` / `Tray` / the real bundle / an external service, it MUST run against the real harness in the appropriate layer (`main` / `renderer` / `boot`), not a stub.
13
13
 
14
14
  **Real external APIs are gated behind extended mode (`TEST_EXTENDED_MODE`), NOT mocked** (see [Extended vs normal mode](#extended-vs-normal-mode) below). Normal mode skips them *in the source*; extended mode runs them for real. The test never fakes them. **Anything an extended test creates in a real external system MUST be cleaned up** by the test (via the suite's `cleanup(ctx)` hook) — external systems are not reset between runs.
15
15
 
16
- If you find yourself writing `const mockX = {...}` to satisfy code under test, STOP — pass the real `ctx.manager` (or its real sub-object), or, if the function is genuinely pure, call it directly with plain data.
16
+ If you find yourself writing `const mockX = {...}` to satisfy code under test, STOP: pass the real `ctx.omega` (or its real sub-object), or, if the function is genuinely pure, call it directly with plain data.
17
17
 
18
18
  ### The ONLY two exceptions where a narrow stub is allowed
19
19
 
@@ -30,7 +30,7 @@ A feature is not done when it works — it's done when every surface it exposes
30
30
 
31
31
  | Coverage | Layer | Proves |
32
32
  |---|---|---|
33
- | **Logic** | `build` / `main` | The feature's functions do the right thing when called directly (real Manager, real storage, real IPC) |
33
+ | **Logic** | `build` / `main` | The feature's functions do the right thing when called directly (the real booted `omega`, real storage, real IPC) |
34
34
  | **UI** | `renderer` | The feature's interface is WIRED — a real event on the real DOM triggers the behavior and the visible result appears |
35
35
  | **End-to-end** | `boot` | The feature survives in the consumer's actual built bundle (extend the boot suite's `inspect` assertions) |
36
36
 
@@ -85,7 +85,7 @@ TEST_EXTENDED_MODE=true npx omega test build/config
85
85
 
86
86
  The target matches against the test file path. The source prefix scopes selection to framework-only or project-only tests — a prefixed target excludes the other source entirely:
87
87
 
88
- - `mgr:` — the **universal cross-framework alias** for "the manager's own tests" (framework-only). Works identically in @omega.js/desktop, BXM, UJM, and @omega.js/backend.
88
+ - `mgr:`: the **universal cross-framework alias** for "the framework's own tests" (framework-only). Works identically in @omega.js/desktop, @omega.js/extension, @omega.js/web, and @omega.js/backend.
89
89
  - `desktop:` / `framework:` — desktop-specific aliases for framework-only tests, equivalent to `mgr:`.
90
90
  - `project:` — consumer project tests only.
91
91
 
@@ -111,20 +111,20 @@ Anything an extended suite creates externally must be torn down in its `cleanup(
111
111
 
112
112
  ### `OMEGA_ENVIRONMENT=testing`, the one input a test run names
113
113
 
114
- Both @omega.js/desktop test runners (`runners/electron.js`, `runners/boot.js`) spawn their child with `OMEGA_ENVIRONMENT=testing`, the ONE environment input ([#817](https://github.com/Omega-JS-Stack/omega/issues/817)), and nothing writes over an explicit one: the word a build baked into the artifact is the fallback for a packaged app, never an override ([#925](https://github.com/Omega-JS-Stack/omega/issues/925)). That powers `manager.isTesting()` (and the `Manager.isTesting()` static), the cross-context helper everything in @omega.js/desktop checks when it needs to behave differently in tests:
114
+ Both @omega.js/desktop test runners (`runners/electron.js`, `runners/boot.js`) spawn their child with `OMEGA_ENVIRONMENT=testing`, the ONE environment input ([#817](https://github.com/Omega-JS-Stack/omega/issues/817)), and nothing writes over an explicit one: the word a build baked into the artifact is the fallback for a packaged app, never an override ([#925](https://github.com/Omega-JS-Stack/omega/issues/925)). That powers `omega.isTesting()` (and the build module's `isTesting()`), the cross-context helper everything in @omega.js/desktop checks when it needs to behave differently in tests:
115
115
 
116
116
  - `auto-updater` flips its idle threshold from 15min → 3s and its periodic tick from 60s → 500ms, AND short-circuits the native install-prompt dialog (so tests don't pop modal windows).
117
117
  - **Every BrowserWindow surfaces stealth** — named windows via `window-manager._surface()` AND raw `new BrowserWindow()` ones (e.g. a consumer's automation popup) via a global `browser-window-created` hook registered in `main.js` step 1a-ii. The shared recipe lives in `src/utils/stealth-window.js`: shown INACTIVE (keyboard focus never leaves your editor), opacity 0, click-through (`setIgnoreMouseEvents`), and raw windows get `show()` rerouted to `showInactive()` + `focus()` no-op'd — a test run never interrupts you, and a stray real click physically can't land in the app, while synthetic test input (`executeJavaScript`, `sendInputEvent`, CDP) is unaffected. **`webContents.focus()` is suppressed separately** (a global `web-contents-created` hook in the same main.js block): it bypasses the window-level patches — it's a different object whose `focus()` reaches the native window directly and makes the invisible window KEY, grabbing the keyboard mid-typing even under the accessory policy (which only prevents *launch* activation, not key-window steals). Consumers call it legitimately (e.g. address-bar focus on tab select), so it's no-op'd per-contents under the same predicate. Set **`OMEGA_TEST_SHOW=1`** to surface windows normally and watch a run live (the predicate is evaluated per window, so flipping it mid-run works). Deliberately NOT `hide()`/`minimize()`: occluded windows get throttled by Chromium (`requestAnimationFrame` pauses, `document.visibilityState` flips to `hidden`) — tests would exercise a DIFFERENT runtime, whereas an opacity-0 shown-inactive window renders and behaves identically to a visible one. See [windows.md](windows.md).
118
118
  - **App-level activation is suppressed too (macOS).** Launching a regular-policy app activates it — the menu bar and keyboard focus switch to the test process at launch even though every window surfaces inactive. Under the same stealth predicate (`src/utils/test-stealth.js` — Testing mode + `OMEGA_TEST_SHOW` unset), `main.js` (step 1a, before app ready) and the spawned test harness flip the app to the **accessory activation policy** (`app.dock.hide()` — the same switch `LSUIElement` bakes for packaged hidden-mode apps): the process never activates, never shows a dock icon, and never steals focus, while windows still render identically. `OMEGA_TEST_SHOW=1` restores normal activation along with visible windows. Verified by frontmost-app sampling across a full run: zero focus changes (previously four steals per run).
119
119
  - `main.js#initialize` isolates userData per environment: testing runs get `<userData> (Testing)` — **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). Dev runs get ` (Development)`; production is untouched. See [boot-sequence.md](boot-sequence.md).
120
120
  - **The main-layer harness exposes a CDP endpoint.** `test/harness/main-entry.js` appends `--remote-debugging-port=0` at require time (loopback-only, OS-assigned port) and publishes the resolved port as **`process.env.OMEGA_CDP_PORT`** before suites run (read from Chromium's `DevToolsActivePort` file; any value inherited from your shell is overwritten — that one points at your dev app, not the harness). Consumer suites can drive real browser automation against the harness Electron itself, e.g. `playwright-core`'s `connectOverCDP('http://127.0.0.1:' + process.env.OMEGA_CDP_PORT)`. Covered by `suites/main/harness-cdp.test.js`.
121
- - Other lib code can branch on `manager.isTesting()` to suppress dock bounce, login-item changes, OS protocol-handler registration, etc.
121
+ - Other lib code can branch on `omega.isTesting()` to suppress dock bounce, login-item changes, OS protocol-handler registration, etc.
122
122
 
123
123
  Consumers writing their own tests name the same input in their own runner, for example in `package.json`:
124
124
  ```json
125
125
  "test": "OMEGA_ENVIRONMENT=testing vitest"
126
126
  ```
127
- Then in your code, gate test-only behavior on `manager.isTesting()` instead of inventing yet another env var.
127
+ Then in your code, gate test-only behavior on `omega.isTesting()` instead of inventing yet another env var.
128
128
 
129
129
  ## Test discovery
130
130
 
@@ -170,14 +170,14 @@ module.exports = {
170
170
  layer: 'main', // 'build' | 'main' | 'renderer'
171
171
  description: 'storage (main)',
172
172
  cleanup: async (ctx) => { // runs after the last test
173
- ctx.manager.storage.clear();
173
+ ctx.omega.storage.clear();
174
174
  },
175
175
  tests: [
176
176
  {
177
177
  name: 'set + get round-trip',
178
178
  run: (ctx) => {
179
- ctx.manager.storage.set('hello', 'world');
180
- ctx.expect(ctx.manager.storage.get('hello')).toBe('world');
179
+ ctx.omega.storage.set('hello', 'world');
180
+ ctx.expect(ctx.omega.storage.get('hello')).toBe('world');
181
181
  },
182
182
  },
183
183
  {
@@ -232,7 +232,7 @@ module.exports = [
232
232
  | Layer | Where it runs | Use for |
233
233
  |---|---|---|
234
234
  | `build` | Plain Node | CLI, package.json, config schema, gulp tasks |
235
- | `main` | Spawned Electron main process | Manager init, lib modules, IPC, windows |
235
+ | `main` | Spawned Electron main process | `omega.initialize()`, lib modules, IPC, windows |
236
236
  | `renderer` | Hidden BrowserWindow: the framework's harness page by default, one of the project's own `src/views/` when the suite declares `view: '<name>'` | `window.desktop.*`, preload bridge, UI logic, your own views |
237
237
 
238
238
  The runner partitions test files by layer at discovery time. The build layer runs inline; the main layer spawns Electron once with all main suites and parses JSON-line stdout.
@@ -246,7 +246,7 @@ ctx.expect(actual) // Jest-compatible expect()
246
246
  ctx.state // shared object across tests in a suite/group
247
247
  ctx.layer // 'build' | 'main' | 'renderer'
248
248
  ctx.skip(reason) // skip from inside the test
249
- ctx.manager // (main layer only) the booted @omega.js/desktop Manager
249
+ ctx.omega // (main layer only) the booted @omega.js/desktop main-process instance
250
250
  ```
251
251
 
252
252
  ## expect() matchers
@@ -290,15 +290,15 @@ All test output is also teed (ANSI-stripped) to `<projectRoot>/logs/test.log`, t
290
290
  ## Test harness internals (main layer)
291
291
 
292
292
  - `runners/electron.js` spawns Electron with `harness/main-entry.js` as the app.
293
- - `harness/main-entry.js` boots Manager with `skipWindowCreation: true`, runs the suites, emits results via `__EM_TEST__{json}\n` lines on stdout.
293
+ - `harness/main-entry.js` boots `omega` with `skipWindowCreation: true`, runs the suites, emits results via `__OMEGA_TEST__{json}\n` lines on stdout (`TEST_EVENT_PREFIX`, [src/utils/test-events.js](../src/utils/test-events.js), the one prefix every harness writes and every runner reads).
294
294
  - The runner parses those lines and renders @omega.js/backend-style output.
295
295
  - stderr is always drained (otherwise the pipe fills and the harness blocks); printed only when `OMEGA_TEST_DEBUG=1`.
296
296
  - `ELECTRON_RUN_AS_NODE` is stripped from the spawn env (would otherwise make Electron behave as Node and break the harness).
297
- - **Consumer-scheme containment.** The consumer's `main.js` — where the real `protocol.handle('<brand.id>', …)` lives — never runs in this harness, so `brand://` URLs loaded by main-layer suites would be UNHANDLED. Chromium treats a load on an unhandled scheme as an *external protocol* and hands it to the OS — and if an installed copy of the app owns the scheme (e.g. `/Applications/Brand.app` on macOS, registered via its `Info.plist`), Launch Services launches the installed production app mid-test-run. The harness contains this two ways after Manager boots: (1) it registers a stub handler for the consumer's brand scheme (read from `<cwd>/config/omega.json5`) on the default session, so `brand://` loads commit in-process as a blank page; (2) it denies the `openExternal` permission on the default session and every session created after it (partitions included), so no navigation can hand ANY scheme to the OS during a test run. Suites that need real page content for `brand://` URLs belong in the boot layer, where the real app (and its real protocol handler) runs. A main-layer suite that genuinely needs its OWN handler for the brand scheme must call `protocol.unhandle(brand.id)` first — the harness stub holds the scheme on the default session.
297
+ - **Consumer-scheme containment.** The consumer's `main.js` (where the real `protocol.handle('<brand.id>', …)` lives) never runs in this harness, so `brand://` URLs loaded by main-layer suites would be UNHANDLED. Chromium treats a load on an unhandled scheme as an *external protocol* and hands it to the OS: and if an installed copy of the app owns the scheme (e.g. `/Applications/Brand.app` on macOS, registered via its `Info.plist`), Launch Services launches the installed production app mid-test-run. The harness contains this two ways after `omega` boots: (1) it registers a stub handler for the consumer's brand scheme (read from `<cwd>/config/omega.json5`) on the default session, so `brand://` loads commit in-process as a blank page; (2) it denies the `openExternal` permission on the default session and every session created after it (partitions included), so no navigation can hand ANY scheme to the OS during a test run. Suites that need real page content for `brand://` URLs belong in the boot layer, where the real app (and its real protocol handler) runs. A main-layer suite that genuinely needs its OWN handler for the brand scheme must call `protocol.unhandle(brand.id)` first: the harness stub holds the scheme on the default session.
298
298
 
299
299
  ## A test run never touches the OS keychain, and a blocked boot reports
300
300
 
301
- The harness resolves auth persistence to the **`none`** strategy on every layer, main and boot alike, before any config is read: a brand that declares `omega.authPersistence: 'safeStorage'` still runs its tests with Firebase auth in memory, because the lanes never sign a real user in. That is a hard rule, not a nicety. The boot lane spawns the raw, unsigned `Electron` binary, which carries no keychain ACL, so one `safeStorage` read parks the entire run behind a macOS SecurityAgent prompt ([#907](https://github.com/Omega-JS-Stack/omega/issues/907)). ONE signal answers "is this a test run" on both layers, `manager.isTesting()`: the boot lane boots the consumer's PRODUCTION artifact, and the word baked into it no longer writes over the `OMEGA_ENVIRONMENT=testing` the runner spawned it with ([#925](https://github.com/Omega-JS-Stack/omega/issues/925)), so the booted app reports the lane it is running in. And since a boot can block long before `main.js` requires the harness (where the per-test timeout lives), the boot runner carries its own budget, measured as SILENCE: every harness line rearms it, so a suite that legitimately runs longer keeps going, and a boot that produces no harness output for 60s (`OMEGA_TEST_BOOT_TIMEOUT_MS` overrides) counts its tests failed, prints `✗ boot: no harness output after 60s; last runtime.log line: "<line>"` from the booted app's `logs/runtime.log`, and ends the child instead of hanging `npx omega test`.
301
+ The harness resolves auth persistence to the **`none`** strategy on every layer, main and boot alike, before any config is read: a brand that declares `omega.authPersistence: 'safeStorage'` still runs its tests with Firebase auth in memory, because the lanes never sign a real user in. That is a hard rule, not a nicety. The boot lane spawns the raw, unsigned `Electron` binary, which carries no keychain ACL, so one `safeStorage` read parks the entire run behind a macOS SecurityAgent prompt ([#907](https://github.com/Omega-JS-Stack/omega/issues/907)). ONE signal answers "is this a test run" on both layers, `omega.isTesting()`: the boot lane boots the consumer's PRODUCTION artifact, and the word baked into it no longer writes over the `OMEGA_ENVIRONMENT=testing` the runner spawned it with ([#925](https://github.com/Omega-JS-Stack/omega/issues/925)), so the booted app reports the lane it is running in. And since a boot can block long before `main.js` requires the harness (where the per-test timeout lives), the boot runner carries its own budget, measured as SILENCE: every harness line rearms it, so a suite that legitimately runs longer keeps going, and a boot that produces no harness output for 60s (`OMEGA_TEST_BOOT_TIMEOUT_MS` overrides) counts its tests failed, prints `✗ boot: no harness output after 60s; last runtime.log line: "<line>"` from the booted app's `logs/runtime.log`, and ends the child instead of hanging `npx omega test`.
302
302
 
303
303
  ## Writing consumer tests
304
304
 
@@ -314,7 +314,7 @@ module.exports = {
314
314
  {
315
315
  name: 'storage starts empty',
316
316
  run: (ctx) => {
317
- ctx.expect(ctx.manager.storage.get('user')).toBeUndefined();
317
+ ctx.expect(ctx.omega.storage.get('user')).toBeUndefined();
318
318
  },
319
319
  },
320
320
  ],
package/docs/themes.md CHANGED
@@ -87,13 +87,13 @@ theme: {
87
87
  }
88
88
  ```
89
89
 
90
- ## Appearance (light / dark / system) — `manager.theme`
90
+ ## Appearance (light / dark / system): `omega.theme`
91
91
 
92
92
  @omega.js/desktop owns appearance at runtime. `config.theme.appearance` is only the **app default**; the resolved appearance is applied and kept live by the theme lib:
93
93
 
94
94
  - **`'system'` (default)** follows the OS preference **live** — when the OS flips, every page updates without a reload or restart.
95
95
  - **`'light'` / `'dark'`** are explicit overrides.
96
- - A user's runtime choice (`manager.theme.set(...)`) is **persisted in `manager.storage`** (`theme.appearance`) and wins over the config default on every boot.
96
+ - A user's runtime choice (`omega.theme.set(...)`) is **persisted in `omega.storage`** (`theme.appearance`) and wins over the config default on every boot.
97
97
 
98
98
  ### How it propagates
99
99
 
@@ -105,10 +105,10 @@ The applier is **opt-in by presence**: it only manages pages whose `<html>` alre
105
105
 
106
106
  ```js
107
107
  // Main
108
- manager.theme.get(); // 'system' | 'light' | 'dark' (the chosen source)
109
- manager.theme.resolved(); // 'light' | 'dark' (what's showing)
110
- manager.theme.set('dark'); // apply + persist (throws on invalid values)
111
- const unsub = manager.theme.onChange(({ source, resolved }) => { ... });
108
+ omega.theme.get(); // 'system' | 'light' | 'dark' (the chosen source)
109
+ omega.theme.resolved(); // 'light' | 'dark' (what's showing)
110
+ omega.theme.set('dark'); // apply + persist (throws on invalid values)
111
+ const unsub = omega.theme.onChange(({ source, resolved }) => { ... });
112
112
 
113
113
  // Renderer (any page with the @omega.js/desktop preload)
114
114
  await window.desktop.theme.get(); // { source, resolved }
@@ -120,7 +120,7 @@ Main also broadcasts `desktop:theme:changed { source, resolved }` to BrowserWind
120
120
 
121
121
  ### Declarative controls
122
122
 
123
- Any element with `data-omega-theme-set` becomes a theme switch (wired by the renderer Manager's initialize — event-delegated, so late-rendered controls work):
123
+ Any element with `data-omega-theme-set` becomes a theme switch (wired by the renderer's `omega.initialize()`, event-delegated, so late-rendered controls work):
124
124
 
125
125
  ```html
126
126
  <button data-omega-theme-set="light">Day</button>
package/docs/tooltips.md CHANGED
@@ -52,7 +52,7 @@ control and put the tooltip on the wrapper:
52
52
  ## The rest of Bootstrap's JS
53
53
 
54
54
  The full namespace is exposed at **`window.bootstrap`** (and
55
- `manager.bootstrap`) — `Tooltip`, `Popover`, `Collapse`, `Dropdown`, `Modal`,
55
+ `omega.bootstrap`): `Tooltip`, `Popover`, `Collapse`, `Dropdown`, `Modal`,
56
56
  `Offcanvas`, `Tab`, `Toast`, `Alert`, `Button`, `Carousel`, `ScrollSpy`. Only
57
57
  tooltips are auto-initialized; the other components' standard **data-api**
58
58
  works out of the box on plain Bootstrap markup (e.g.
@@ -94,6 +94,6 @@ runs from a preload, e.g. the test harness).
94
94
 
95
95
  - `src/test/suites/renderer/tooltips.test.js` — bundle loads, auto-init on
96
96
  insertion, live retitle, dispose-on-removal, tip cleanup. (The harness wires
97
- the renderer Manager in the preload world — see the suite header for the
97
+ the renderer instance in the preload world: see the suite header for the
98
98
  world-split notes; single-world hover behavior is covered by consumer boot
99
99
  suites.)
package/docs/tray.md CHANGED
@@ -4,19 +4,19 @@ File-based tray/menubar. @omega.js/desktop looks for `src/integrations/tray/inde
4
4
 
5
5
  ## Config
6
6
 
7
- No config block. Path is conventional: `src/integrations/tray/index.js`. To opt out, call `manager.tray.disable()` from your main entry — idempotent, tears down any existing Tray.
7
+ No config block. Path is conventional: `src/integrations/tray/index.js`. To opt out, call `omega.tray.disable()` from your main entry: idempotent, tears down any existing Tray.
8
8
 
9
9
  ## Definition file
10
10
 
11
11
  ```js
12
12
  // src/integrations/tray/index.js
13
- module.exports = ({ manager, tray }) => {
13
+ module.exports = ({ omega, tray }) => {
14
14
  // @omega.js/desktop auto-resolves the tray icon from config/icons/<platform>/tray.png at build
15
15
  // time, so explicit tray.icon() is OPTIONAL. Call it only to override.
16
16
  // Note: on macOS, if you pass your own path, the filename MUST end in
17
17
  // `Template.png` for the OS to auto-invert it in dark mode.
18
18
  // tray.icon('src/assets/icons/my-trayTemplate.png');
19
- tray.tooltip(manager.config?.app?.productName);
19
+ tray.tooltip(omega.config?.app?.productName);
20
20
 
21
21
  // Easiest: start from @omega.js/desktop's default template.
22
22
  tray.useDefaults();
@@ -25,7 +25,7 @@ module.exports = ({ manager, tray }) => {
25
25
  tray.insertAfter('open', {
26
26
  id: 'dashboard',
27
27
  label: 'Open Dashboard',
28
- click: () => manager.windows.show('dashboard'),
28
+ click: () => omega.windows.show('dashboard'),
29
29
  });
30
30
  tray.update('open', { label: 'Show Window' });
31
31
  tray.remove('website');
@@ -47,7 +47,7 @@ tray.clear() // start over
47
47
 
48
48
  ## Id-path API
49
49
 
50
- Same shape across menu / tray / context-menu. Available **during definition** (on the `tray` builder arg) AND **at runtime** on `manager.tray`:
50
+ Same shape across menu / tray / context-menu. Available **during definition** (on the `tray` builder arg) AND **at runtime** on `omega.tray`:
51
51
 
52
52
  ```js
53
53
  .find(idPath) // live descriptor or null
@@ -69,8 +69,8 @@ Tray ids are **flat** — no `tray/` prefix (the lib namespace is implicit). For
69
69
  | ID | Item |
70
70
  |---|---|
71
71
  | `title` | Disabled label showing the app name |
72
- | `open` | "Open `<app>`" — calls `manager.windows.show('main')` |
73
- | `check-for-updates` | Wired to `manager.autoUpdater` (label updates dynamically) |
72
+ | `open` | "Open `<app>`": calls `omega.windows.show('main')` |
73
+ | `check-for-updates` | Wired to `omega.autoUpdater` (label updates dynamically) |
74
74
  | `website` | Visit `brand.url` (only present if configured) |
75
75
  | `quit` | Quit the app |
76
76
 
@@ -84,9 +84,9 @@ tray.item({ id: 'account', label: 'Account', submenu: [
84
84
  { id: 'sign-out', label: 'Sign out', click: () => {} },
85
85
  ]});
86
86
 
87
- manager.tray.find('account/sign-out');
88
- manager.tray.update('account/sign-out', { enabled: false });
89
- manager.tray.appendTo('account', { id: 'profile', label: 'Profile' });
87
+ omega.tray.find('account/sign-out');
88
+ omega.tray.update('account/sign-out', { enabled: false });
89
+ omega.tray.appendTo('account', { id: 'profile', label: 'Profile' });
90
90
  ```
91
91
 
92
92
  ## Item descriptors
@@ -103,30 +103,30 @@ Mirror Electron's [`MenuItemConstructorOptions`](https://www.electronjs.org/docs
103
103
  | `click` | function | Wrapped to swallow errors so a bad handler can't kill the menu |
104
104
  | `submenu` | array | Recursively resolved with the same conveniences |
105
105
 
106
- ## Runtime API on `manager.tray`
106
+ ## Runtime API on `omega.tray`
107
107
 
108
108
  ```js
109
- manager.tray.refresh() // re-evaluate dynamic state and re-render
110
- manager.tray.define(fn) // replace the whole definition at runtime
111
- manager.tray.disable() // tear down + stop responding (idempotent)
112
- manager.tray.setIcon(path)
113
- manager.tray.setTooltip(text)
114
- manager.tray.addItem(descriptor) // append (preserves existing items)
115
- manager.tray.clearItems()
116
- manager.tray.destroy() // tear down (mostly for tests)
109
+ omega.tray.refresh() // re-evaluate dynamic state and re-render
110
+ omega.tray.define(fn) // replace the whole definition at runtime
111
+ omega.tray.disable() // tear down + stop responding (idempotent)
112
+ omega.tray.setIcon(path)
113
+ omega.tray.setTooltip(text)
114
+ omega.tray.addItem(descriptor) // append (preserves existing items)
115
+ omega.tray.clearItems()
116
+ omega.tray.destroy() // tear down (mostly for tests)
117
117
 
118
118
  // Id-path API — same as listed above.
119
- manager.tray.find('quit')
120
- manager.tray.update('quit', { label: 'Goodbye' })
121
- manager.tray.remove('website')
122
- manager.tray.insertAfter('open', { id: 'preferences', label: 'Preferences...', click: ... })
123
- manager.tray.hide('check-for-updates')
119
+ omega.tray.find('quit')
120
+ omega.tray.update('quit', { label: 'Goodbye' })
121
+ omega.tray.remove('website')
122
+ omega.tray.insertAfter('open', { id: 'preferences', label: 'Preferences...', click: ... })
123
+ omega.tray.hide('check-for-updates')
124
124
 
125
125
  // Inspection
126
- manager.tray.getItems() // shallow copy of raw descriptors
127
- manager.tray.getIcon()
128
- manager.tray.getTooltip()
129
- manager.tray.isRendered()
126
+ omega.tray.getItems() // shallow copy of raw descriptors
127
+ omega.tray.getIcon()
128
+ omega.tray.getTooltip()
129
+ omega.tray.isRendered()
130
130
  ```
131
131
 
132
132
  ## Common patterns
@@ -135,8 +135,8 @@ manager.tray.isRendered()
135
135
 
136
136
  ```js
137
137
  // in your renderer/main code, after sign-in:
138
- manager.storage.set('user', { ... });
139
- manager.tray.refresh(); // dynamic-label functions re-evaluate
138
+ omega.storage.set('user', { ... });
139
+ omega.tray.refresh(); // dynamic-label functions re-evaluate
140
140
  ```
141
141
 
142
142
  ### Hide updater item if you ship without auto-update
@@ -153,7 +153,7 @@ module.exports = ({ tray }) => {
153
153
  ### Replace the entire tray at runtime
154
154
 
155
155
  ```js
156
- manager.tray.define(({ manager, tray }) => {
156
+ omega.tray.define(({ omega, tray }) => {
157
157
  tray.icon('icons/dark-mode.png');
158
158
  tray.item({ id: 'x', label: 'New layout', click: ... });
159
159
  });
package/docs/usage.md CHANGED
@@ -1,15 +1,15 @@
1
1
  # Usage
2
2
 
3
- Tracks app-launch + hours-of-use stats. Sister of legacy @omega.js/desktop's Usage library, but uses `manager.storage` instead of a separate electron-store.
3
+ Tracks app-launch + hours-of-use stats. Sister of legacy @omega.js/desktop's Usage library, but uses `omega.storage` instead of a separate electron-store.
4
4
 
5
5
  ## What's tracked
6
6
 
7
7
  ```js
8
- manager.usage.opens() // total app launches
9
- manager.usage.hoursTotal() // cumulative hours-of-use across clean exits
10
- manager.usage.hoursThisSession() // live, computed from session start
11
- manager.usage.installedAt() // ISO timestamp of first launch
12
- manager.usage.toJSON() // all of the above as a structured-cloneable object
8
+ omega.usage.opens() // total app launches
9
+ omega.usage.hoursTotal() // cumulative hours-of-use across clean exits
10
+ omega.usage.hoursThisSession() // live, computed from session start
11
+ omega.usage.installedAt() // ISO timestamp of first launch
12
+ omega.usage.toJSON() // all of the above as a structured-cloneable object
13
13
  ```
14
14
 
15
15
  ## How it accumulates
@@ -51,7 +51,7 @@ const snap = await window.desktop.usage.get();
51
51
 
52
52
  ## Why not just use app-state?
53
53
 
54
- `app-state.js` already tracks `launchCount` (= opens). We could fold these in. But `app-state` is concerned with first-launch / crash-sentinel / version-change semantics — `usage` is concerned with telemetry. Keeping them separate keeps each module focused. Both write to disjoint keys in `manager.storage`.
54
+ `app-state.js` already tracks `launchCount` (= opens). We could fold these in. But `app-state` is concerned with first-launch / crash-sentinel / version-change semantics: `usage` is concerned with telemetry. Keeping them separate keeps each module focused. Both write to disjoint keys in `omega.storage`.
55
55
 
56
56
  ## Tests
57
57
 
package/docs/verts.md CHANGED
@@ -12,7 +12,7 @@ zero consumer JS, same element vocabulary as the web `verts/unit` section and
12
12
 
13
13
  ## What the wiring does
14
14
 
15
- `renderer.js _wireAds` (runs inside `manager.initialize()`, same liveness
15
+ `renderer.js _wireAds` (runs inside `omega.initialize()`, same liveness
16
16
  model as the FontAwesome/tooltip wiring) binds every `[data-omega-vert]`
17
17
  element present at init AND inserted later (MutationObserver), marking bound
18
18
  hosts `data-omega-vert-bound="house"`. Everything after the bind lives in the