@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
@@ -1,89 +1,128 @@
1
- // Build-layer tests for renderer.js's auth bridge (_wireAuthBridge).
1
+ // Build-layer tests for the renderer's auth bridge (`_wireAuthBridge`) against
2
+ // main's REAL lib/auth.js handlers.
2
3
  //
3
- // The bridge is pure JS — ipc in, ipc out, no DOM — so it runs in plain Node here
4
- // against the REAL @omega.js/client Auth class (a renderer-layer suite can't: those
5
- // bodies run inside a BrowserWindow with no require).
4
+ // The bridge is pure JS (ipc in, ipc out, no DOM), so it runs in plain Node here on
5
+ // a REAL renderer instance, the @omega.js/client base class included (a renderer-
6
+ // layer suite can't: those bodies run inside a BrowserWindow with no require).
6
7
  //
7
- // What's pinned: the sync-request payload carries the signed-in UID. The renderer
8
- // reads it from the client's auth handle, whose accessor is `getUser()` — an
9
- // accessor-name drift here is silent (optional chain → undefined → contextUid null),
10
- // which is exactly how #103 shipped: every renderer boot claimed "signed out" and
11
- // forced main into a full custom-token round-trip it could have skipped.
8
+ // What's pinned:
9
+ // - the sync-request payload carries the signed-in UID, read off the client's
10
+ // Firebase session. A drift here is silent (null → "signed out"), which is
11
+ // exactly how #103 shipped: every renderer boot claimed "signed out" and forced
12
+ // main into a full custom-token round-trip it could have skipped.
13
+ // - every signed-in state pushes `{ uid, document, identity }` to main, and main
14
+ // builds its `auth.user` from that WHOLE document, so its plan reads from it.
15
+ // - the `omega-signin` / `omega-account` click triggers the extension's pages
16
+ // carry route to main's open-flow / open-account channels. Pinned here, not in
17
+ // a renderer-layer page: a real click would open the user's browser.
18
+ // - the client's `omega-signout` trigger routes to main's sign-out channel, and
19
+ // the window signs itself out only on main's broadcast.
20
+ //
21
+ // Two seams, both named: the backend (`omega.request`, whose wakeup would reach the
22
+ // network) and the client's auth state source (`omega.auth.listen`), because a
23
+ // real signed-in renderer needs the auth emulator, which the e2e-desktop lane drives.
12
24
 
13
25
  const path = require('path');
14
26
 
27
+ const { User } = require('../../../vendor/account/index.js');
15
28
  const { WAKEUP_ROUTE } = require('@omega.js/client/modules/request.js');
16
29
  const defineCases = require('../../../vendor/devkit/test/define-cases.js');
17
30
 
18
- const RENDERER_PATH = path.join(__dirname, '..', '..', '..', 'renderer.js');
19
- const CLIENT_BRIDGE_PATH = path.join(__dirname, '..', '..', '..', 'lib', 'client-bridge.js');
20
-
21
- // The real client Auth, wired to a manager stub whose firebaseAuth reports `user`.
22
- // `_resolveFirebaseConfig` returns an apiKey-less blob so listen() settles immediately
23
- // with a null user instead of hanging on a Firebase that isn't there.
24
- async function makeClientAuth(user) {
25
- const { default: Auth } = await import('@omega.js/client/modules/auth.js');
26
- return new Auth({
27
- firebaseAuth: { currentUser: user },
28
- _resolveFirebaseConfig: () => ({}),
29
- });
30
- }
31
+ const ROOT = path.join(__dirname, '..', '..', '..');
32
+ const mainAuth = require(path.join(ROOT, 'lib', 'auth.js'));
33
+
34
+ // A signed-in renderer's account: the stored document with an active paid plan
35
+ const IDENTITY = { uid: 'uid-abc', email: 'a@b.co', displayName: 'Abc', photoURL: 'https://example.com/a.png', emailVerified: true };
36
+ const DOCUMENT = { subscription: { product: { id: 'premium' }, status: 'active' } };
31
37
 
32
- // A renderer Manager with the client already booted and an ipc stub whose invoke
33
- // answers are supplied by `handle`. Returns the manager, the recorded invokes,
34
- // the recorded backend requests, and the `timeline` of both in the order the
35
- // bridge produced them — the wakeup ping's whole point is that it is FIRST.
36
- function makeRenderer(auth, handle) {
37
- const Manager = require(RENDERER_PATH);
38
- const manager = new Manager();
38
+ /**
39
+ * A fresh renderer instance on a recording bridge. `handle(channel, payload)`
40
+ * answers each ipc.invoke. Returns the instance, the recorded invokes and
41
+ * backend requests, the `timeline` of both in the order the bridge produced
42
+ * them (the wakeup ping's whole point is that it is FIRST), and `deliver`, the
43
+ * client's auth listener the bridge registered.
44
+ * @param {{ uid: string }|null} firebaseUser - the client's Firebase session user.
45
+ * @param {Function} handle - the ipc.invoke answerer.
46
+ * @returns {object} the renderer and its recordings.
47
+ */
48
+ function makeRenderer(firebaseUser, handle) {
39
49
  const invokes = [];
40
50
  const requests = [];
41
51
  const timeline = [];
52
+ const listeners = [];
42
53
 
43
- manager.ipc = {
44
- on: () => {},
45
- invoke: async (channel, payload) => {
46
- invokes.push({ channel, payload });
47
- timeline.push(channel);
48
- return handle(channel, payload);
54
+ const desktop = {
55
+ ipc: {
56
+ on: () => () => {},
57
+ invoke: async (channel, payload) => {
58
+ invokes.push({ channel, payload });
59
+ timeline.push(channel);
60
+ return handle(channel, payload);
61
+ },
49
62
  },
50
63
  };
51
- manager.omega = {
52
- auth: () => auth,
53
- request: async (url, options = {}) => {
54
- requests.push({ url, options });
55
- timeline.push(options.wakeup ? 'wakeup' : url);
56
- },
64
+
65
+ const saved = globalThis.window;
66
+ globalThis.window = { desktop };
67
+ let omega;
68
+ try {
69
+ const { Omega } = require(path.join(ROOT, 'renderer.js'));
70
+ omega = new Omega();
71
+ } finally {
72
+ if (saved === undefined) delete globalThis.window;
73
+ else globalThis.window = saved;
74
+ }
75
+
76
+ // The client's Firebase session, as its `firebaseAuth` getter reads it
77
+ omega._firebaseAuth = { currentUser: firebaseUser };
78
+ omega.request = async (url, options = {}) => {
79
+ requests.push({ url, options });
80
+ timeline.push(options.wakeup ? 'wakeup' : url);
57
81
  };
82
+ omega.auth.listen = (callback) => {
83
+ listeners.push(callback);
84
+ return () => {};
85
+ };
86
+
87
+ const deliver = (state) => listeners.forEach((callback) => callback(state));
58
88
 
59
- return { manager, invokes, requests, timeline };
89
+ return { omega, invokes, requests, timeline, deliver };
90
+ }
91
+
92
+ /**
93
+ * A click inside an element carrying `className`, handed to the registry's one
94
+ * delegated listener. closest() answers only when the registry's selector names
95
+ * that class, so the class is the registered one.
96
+ * @param {object} omega - the renderer instance.
97
+ * @param {string} className - the trigger class clicked.
98
+ */
99
+ function clickTrigger(omega, className) {
100
+ omega.triggers._handleClick({
101
+ target: {
102
+ closest: (selector) => (selector.split(',').includes(`.${className}`)
103
+ ? { classList: { contains: (name) => name === className } }
104
+ : null),
105
+ },
106
+ preventDefault: () => {},
107
+ stopPropagation: () => {},
108
+ });
60
109
  }
61
110
 
62
111
  module.exports = defineCases({
63
112
  type: 'suite',
64
113
  layer: 'build',
65
- description: 'renderer auth bridge — sync-request context UID',
114
+ description: 'renderer auth bridge: sync-request context UID + the account push main builds its user from',
66
115
  tests: [
67
- {
68
- name: 'the client auth handle exposes getUser() (and no user() accessor)',
69
- run: async (ctx) => {
70
- const auth = await makeClientAuth({ uid: 'uid-abc', email: 'a@b.co' });
71
- ctx.expect(typeof auth.getUser).toBe('function');
72
- ctx.expect(auth.getUser().uid).toBe('uid-abc');
73
- ctx.expect(auth.user).toBeUndefined();
74
- },
75
- },
76
116
  {
77
117
  // #644: main answers a sync-request that needs a token by POSTing
78
- // /omega/user/token — the app's first backend call, on a function that is
118
+ // /omega/user/token, the app's first backend call, on a function that is
79
119
  // cold at every launch. The ping goes out before the bridge waits on
80
120
  // anything, so the cold start burns down while auth settles.
81
121
  name: 'the bridge warms the backend first, then syncs',
82
122
  run: async (ctx) => {
83
- const auth = await makeClientAuth({ uid: 'uid-abc', email: 'a@b.co' });
84
- const { manager, requests, timeline } = makeRenderer(auth, () => ({ needsSync: false }));
123
+ const { omega, requests, timeline } = makeRenderer({ uid: 'uid-abc' }, () => ({ needsSync: false }));
85
124
 
86
- await manager._wireAuthBridge();
125
+ await omega._wireAuthBridge();
87
126
 
88
127
  ctx.expect(requests.length).toBe(1);
89
128
  ctx.expect(requests[0].url).toBe(WAKEUP_ROUTE);
@@ -95,10 +134,9 @@ module.exports = defineCases({
95
134
  {
96
135
  name: 'sync-request carries the signed-in UID as contextUid',
97
136
  run: async (ctx) => {
98
- const auth = await makeClientAuth({ uid: 'uid-abc', email: 'a@b.co' });
99
- const { manager, invokes } = makeRenderer(auth, () => ({ needsSync: false }));
137
+ const { omega, invokes } = makeRenderer({ uid: 'uid-abc' }, () => ({ needsSync: false }));
100
138
 
101
- await manager._wireAuthBridge();
139
+ await omega._wireAuthBridge();
102
140
 
103
141
  const sync = invokes.find((i) => i.channel === 'desktop:auth:sync-request');
104
142
  ctx.expect(sync).toBeDefined();
@@ -108,49 +146,144 @@ module.exports = defineCases({
108
146
  {
109
147
  name: 'sync-request carries null when nobody is signed in',
110
148
  run: async (ctx) => {
111
- const auth = await makeClientAuth(null);
112
- const { manager, invokes } = makeRenderer(auth, () => ({ needsSync: false }));
149
+ const { omega, invokes } = makeRenderer(null, () => ({ needsSync: false }));
113
150
 
114
- await manager._wireAuthBridge();
151
+ await omega._wireAuthBridge();
115
152
 
116
153
  const sync = invokes.find((i) => i.channel === 'desktop:auth:sync-request');
117
154
  ctx.expect(sync.payload.contextUid).toBeNull();
118
155
  },
119
156
  },
120
157
  {
121
- name: 'matching UIDs short-circuit against the real main-side handler — no token round-trip',
158
+ name: 'matching UIDs short-circuit against the real main-side handler: no token round-trip',
122
159
  run: async (ctx) => {
123
- // Main's side is the REAL client-bridge handler with its auth stubbed to the
160
+ // Main's side is the REAL lib/auth.js handler with its session stubbed to the
124
161
  // same user; the only thing under test is whether the renderer's claim matches.
125
- const bridge = require(CLIENT_BRIDGE_PATH);
126
- const origAuth = bridge._firebaseAuth;
127
- bridge._firebaseAuth = { currentUser: { uid: 'uid-abc' } };
162
+ const origAuth = mainAuth._firebaseAuth;
163
+ mainAuth._firebaseAuth = { currentUser: { uid: 'uid-abc' } };
128
164
 
129
165
  try {
130
- const auth = await makeClientAuth({ uid: 'uid-abc', email: 'a@b.co' });
131
- let signIns = 0;
132
- let signOuts = 0;
133
- auth.signInWithCustomToken = async () => { signIns++; };
134
- auth.signOut = async () => { signOuts++; };
135
-
136
166
  let result = null;
137
- const { manager } = makeRenderer(auth, async (channel, payload) => {
167
+ const { omega } = makeRenderer({ uid: 'uid-abc' }, async (channel, payload) => {
138
168
  if (channel !== 'desktop:auth:sync-request') return null;
139
- result = await bridge._handleSyncRequest(payload.contextUid);
169
+ result = await mainAuth._handleSyncRequest(payload.contextUid);
140
170
  return result;
141
171
  });
172
+ let signIns = 0;
173
+ let signOuts = 0;
174
+ omega.auth.signInWithCustomToken = async () => { signIns++; };
175
+ omega.auth.signOut = async () => { signOuts++; };
142
176
 
143
- await manager._wireAuthBridge();
177
+ await omega._wireAuthBridge();
144
178
 
145
179
  ctx.expect(result.needsSync).toBe(false);
146
180
  // The handler's catch ALSO returns needsSync:false (with an error field), so a
147
- // null contextUid falling through to the token fetch would pass the line above —
181
+ // null contextUid falling through to the token fetch would pass the line above;
148
182
  // the absent error is what proves the uid match short-circuited.
149
183
  ctx.expect(result.error).toBeUndefined();
150
184
  ctx.expect(signIns).toBe(0);
151
185
  ctx.expect(signOuts).toBe(0);
152
186
  } finally {
153
- bridge._firebaseAuth = origAuth;
187
+ mainAuth._firebaseAuth = origAuth;
188
+ }
189
+ },
190
+ },
191
+ {
192
+ name: 'the signin and account triggers route a click to main\'s open-flow and open-account channels',
193
+ run: async (ctx) => {
194
+ const { omega, invokes } = makeRenderer(null, () => ({ needsSync: false }));
195
+
196
+ await omega._wireAuthBridge();
197
+
198
+ clickTrigger(omega, 'omega-signin');
199
+ clickTrigger(omega, 'omega-account');
200
+
201
+ const opens = invokes.map((i) => i.channel).filter((channel) => channel.startsWith('desktop:auth:open-'));
202
+ ctx.expect(opens).toEqual(['desktop:auth:open-flow', 'desktop:auth:open-account']);
203
+ },
204
+ },
205
+ {
206
+ // The client registers `omega-signout` (the one registration, at
207
+ // initialize); on desktop it signs MAIN out, and this window's own
208
+ // sign-out is the broadcast listener's, so nothing signs out twice.
209
+ // `confirm` is the one seam: Node has no dialog.
210
+ name: 'the client\'s signout trigger invokes main\'s sign-out channel, and the window does not sign itself out',
211
+ run: async (ctx) => {
212
+ const { omega, invokes } = makeRenderer({ uid: 'uid-abc' }, (channel) => (channel === 'desktop:auth:sign-out' ? { success: true } : { needsSync: false }));
213
+ let localSignOuts = 0;
214
+ const notices = [];
215
+ omega.auth.signOut = async () => { localSignOuts++; };
216
+ omega.utilities.showNotification = (message, type) => notices.push([message, type]);
217
+
218
+ await omega._wireAuthBridge();
219
+ omega.auth.setupEventListeners();
220
+
221
+ globalThis.confirm = () => true;
222
+ try {
223
+ clickTrigger(omega, 'omega-signout');
224
+ await new Promise((resolve) => setImmediate(resolve));
225
+ } finally {
226
+ delete globalThis.confirm;
227
+ }
228
+
229
+ ctx.expect(invokes.filter((i) => i.channel === 'desktop:auth:sign-out').length).toBe(1);
230
+ ctx.expect(localSignOuts).toBe(0);
231
+ ctx.expect(notices).toEqual([['Successfully signed out.', 'success']]);
232
+ },
233
+ },
234
+ {
235
+ name: 'a signed-out state pushes nothing',
236
+ run: async (ctx) => {
237
+ const { omega, invokes, deliver } = makeRenderer(null, () => ({ needsSync: false }));
238
+
239
+ await omega._wireAuthBridge();
240
+ deliver({ user: new User(), denied: false });
241
+ await Promise.resolve();
242
+
243
+ ctx.expect(invokes.some((i) => i.channel === 'desktop:auth:account-resolved')).toBe(false);
244
+ },
245
+ },
246
+ {
247
+ name: 'a signed-in state pushes { uid, document, identity }, and main\'s auth.user is built from it',
248
+ run: async (ctx) => {
249
+ // Main's side is the REAL lib/auth.js intake, on a session holding the same uid
250
+ const saved = { firebaseAuth: mainAuth._firebaseAuth, omega: mainAuth._omega, user: mainAuth.user, state: mainAuth.state };
251
+ const broadcasts = [];
252
+ mainAuth._firebaseAuth = { currentUser: { uid: IDENTITY.uid } };
253
+ mainAuth._omega = { ipc: { broadcast: (channel, payload) => broadcasts.push({ channel, payload }) } };
254
+ mainAuth._land(new User());
255
+
256
+ try {
257
+ let pushed = null;
258
+ const { omega, deliver } = makeRenderer({ uid: IDENTITY.uid }, (channel, payload) => {
259
+ if (channel === 'desktop:auth:sync-request') return { needsSync: false };
260
+ if (channel === 'desktop:auth:account-resolved') {
261
+ pushed = payload;
262
+ return mainAuth._handleAccountResolved(payload);
263
+ }
264
+ return null;
265
+ });
266
+
267
+ await omega._wireAuthBridge();
268
+ ctx.expect(mainAuth.user.authenticated).toBe(false);
269
+ ctx.expect(mainAuth.user.plan).toBe('basic');
270
+
271
+ const rendererUser = new User(DOCUMENT, IDENTITY);
272
+ deliver({ user: rendererUser, denied: false });
273
+ await Promise.resolve();
274
+
275
+ ctx.expect(pushed).toEqual({ uid: IDENTITY.uid, document: rendererUser.toJSON(), identity: IDENTITY });
276
+ ctx.expect(mainAuth.user.uid).toBe(IDENTITY.uid);
277
+ ctx.expect(mainAuth.user.plan).toBe('premium');
278
+ ctx.expect(mainAuth.user.active).toBe(true);
279
+ ctx.expect(mainAuth.user.profile.displayName).toBe('Abc');
280
+ ctx.expect(mainAuth.user.toJSON()).toEqual(rendererUser.toJSON());
281
+ ctx.expect(broadcasts).toEqual([{ channel: 'desktop:auth:plan-changed', payload: { document: rendererUser.toJSON() } }]);
282
+ } finally {
283
+ mainAuth._firebaseAuth = saved.firebaseAuth;
284
+ mainAuth._omega = saved.omega;
285
+ mainAuth.user = saved.user;
286
+ mainAuth.state = saved.state;
154
287
  }
155
288
  },
156
289
  },
@@ -565,7 +565,7 @@ module.exports = defineCases({
565
565
  const src = fs.readFileSync(path.join(__dirname, '..', '..', '..', 'commands', 'runner.js'), 'utf8');
566
566
  ctx.expect(src).toContain('Existing runner installation detected');
567
567
  ctx.expect(src).toContain('uninstalling first for a clean re-install');
568
- // ...and the electron-manager era's install counts as an existing one.
568
+ // ...and the legacy em-runner install counts as an existing one.
569
569
  ctx.expect(src).toMatch(/jetpack\.exists\(home\)\s*\|\|\s*listLegacyRunnerHomes\(\{ home \}\)\.length > 0/);
570
570
  },
571
571
  },
@@ -1013,7 +1013,7 @@ module.exports = defineCases({
1013
1013
  },
1014
1014
  },
1015
1015
  {
1016
- name: 'the electron-manager era homes are found from the environment, never the current home',
1016
+ name: 'the legacy em-runner homes are found from the environment, never the current home',
1017
1017
  run: (ctx) => {
1018
1018
  // An upgraded box still runs `em runner`'s install out of
1019
1019
  // %LOCALAPPDATA%\em-runner (C:\actions-runners before that). Those are
@@ -3,7 +3,7 @@
3
3
  // The policy itself — config gating, the dev/kill switches, user normalization,
4
4
  // release tagging — is @omega.js/monitoring's, proven by its own suite
5
5
  // (packages/monitoring/test/). What belongs here is the desktop half: the
6
- // package entry resolves in a main-process context, the manager's `sentry`
6
+ // package entry resolves in a main-process context, the omega instance's `sentry`
7
7
  // surface is the one every lib calls, and a brand with no DSN gets a silent
8
8
  // no-op instead of a crash.
9
9
 
@@ -47,7 +47,7 @@ module.exports = defineCases({
47
47
  try {
48
48
  main.captureException(new Error('test'));
49
49
  main.captureMessage('test');
50
- // client-bridge calls this on every auth change — a documented no-op when off.
50
+ // lib/auth.js calls this on every auth change, a documented no-op when off.
51
51
  main.setUser({ uid: 'abc', email: 'user@example.com' });
52
52
  } catch (e) {
53
53
  threw = e;
@@ -27,7 +27,7 @@ const defineCases = require('../../../vendor/devkit/test/define-cases.js');
27
27
  const signerPath = path.join(__dirname, '..', '..', '..', 'commands', 'sign-windows.js');
28
28
  const eventsPath = path.join(__dirname, '..', '..', '..', 'lib', 'sign-helpers', 'sign-events.js');
29
29
  const signWindows = require(signerPath);
30
- const Manager = new (require(path.join(__dirname, '..', '..', '..', 'build.js')))();
30
+ const build = require(path.join(__dirname, '..', '..', '..', 'build.js'));
31
31
 
32
32
  const NOT_WINDOWS = 'Windows-only: signtool + the EV token live on the signing box';
33
33
  const NO_SIGNTOOL = 'signtool is not reachable — install the Windows SDK / VS Build Tools, or set SIGNTOOL_PATH';
@@ -76,7 +76,7 @@ function currentGate() {
76
76
  // The strategy comes from the project's own config — the same call the signer
77
77
  // makes — and is only worth resolving where the rest of the gate has passed.
78
78
  if (process.platform !== 'win32') return gateReason(process.platform, process.env, false, null);
79
- return gateReason(process.platform, process.env, signtoolOnPath(process.env), Manager.getWindowsSignStrategy());
79
+ return gateReason(process.platform, process.env, signtoolOnPath(process.env), build.getWindowsSignStrategy());
80
80
  }
81
81
 
82
82
  // The signer, loaded with its event log pointed inside this suite's scratch
@@ -52,11 +52,11 @@ module.exports = defineCases({
52
52
  },
53
53
  },
54
54
  {
55
- name: 'buildPageVars produces brand/app/page/theme/cacheBust from a manager-like object',
55
+ name: 'buildPageVars produces brand/app/page/theme/cacheBust from an omega-like object',
56
56
  run: (ctx) => {
57
57
  const t = require(path.join(__dirname, '..', '..', '..', 'lib', 'templating.js'));
58
- const fakeManager = { config: { brand: { name: 'MyApp', id: 'myapp' }, app: { productName: 'MyApp' } } };
59
- const vars = t.buildPageVars('settings', { cacheBust: '12345' }, fakeManager);
58
+ const fakeOmega = { config: { brand: { name: 'MyApp', id: 'myapp' }, app: { productName: 'MyApp' } } };
59
+ const vars = t.buildPageVars('settings', { cacheBust: '12345' }, fakeOmega);
60
60
  ctx.expect(vars.page.name).toBe('settings');
61
61
  ctx.expect(vars.page.title).toBe('MyApp');
62
62
  ctx.expect(vars.brand.id).toBe('myapp');
@@ -68,19 +68,17 @@ module.exports = defineCases({
68
68
  },
69
69
  },
70
70
  {
71
- name: 'a provided manager answers from the config IT was baked with',
71
+ name: 'a provided instance answers from the config IT was baked with',
72
72
  run: (ctx) => {
73
- // The build-time Manager carries the same mixin as the runtime Managers,
74
- // and since #817 they all read the ONE input: the process variable, or
75
- // the baked `config.environment` when the process has none.
76
- const Manager = require('../../../build.js');
73
+ // An instance carrying the shared isTesting() answers like the runtime
74
+ // `omega` instances: since #817 they all read the ONE input, the process
75
+ // variable, or the baked `config.environment` when the process has none.
76
+ const { isTesting } = require('../../../utils/mode-helpers.js');
77
77
  withEnv({ OMEGA_ENVIRONMENT: undefined, OMEGA_TEST_SHOW: undefined }, () => {
78
- const testing = new Manager();
79
- testing.config = { environment: 'testing' };
78
+ const testing = { isTesting, config: { environment: 'testing' } };
80
79
  ctx.expect(isTestStealth(testing)).toBe(true);
81
80
 
82
- const production = new Manager();
83
- production.config = { environment: 'production' };
81
+ const production = { isTesting, config: { environment: 'production' } };
84
82
  ctx.expect(isTestStealth(production)).toBe(false);
85
83
  });
86
84
  },