@omega.js/desktop 0.51.0 → 0.53.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (289) hide show
  1. package/README.md +18 -13
  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 +145 -48
  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/.github/workflows/build.yml +2 -0
  48. package/dist/defaults/AGENTS.md +17 -8
  49. package/dist/defaults/config/omega.json5 +7 -7
  50. package/dist/defaults/hooks/build/post.js +1 -1
  51. package/dist/defaults/hooks/build/pre.js +1 -1
  52. package/dist/defaults/hooks/deploy/pre.js +1 -1
  53. package/dist/defaults/hooks/release/post.js +1 -1
  54. package/dist/defaults/hooks/release/pre.js +1 -1
  55. package/dist/defaults/src/assets/js/components/about/index.js +3 -5
  56. package/dist/defaults/src/assets/js/components/main/index.js +3 -5
  57. package/dist/defaults/src/assets/js/components/settings/index.js +3 -5
  58. package/dist/defaults/src/integrations/context-menu/index.js +2 -2
  59. package/dist/defaults/src/integrations/menu/index.js +3 -3
  60. package/dist/defaults/src/integrations/tray/index.js +3 -3
  61. package/dist/defaults/src/main.js +3 -5
  62. package/dist/defaults/src/preload.js +3 -5
  63. package/dist/defaults/test/README.md +2 -2
  64. package/dist/gulp/main.js +9 -10
  65. package/dist/gulp/tasks/audit.js +7 -7
  66. package/dist/gulp/tasks/build-config.js +8 -8
  67. package/dist/gulp/tasks/bundle.js +16 -16
  68. package/dist/gulp/tasks/defaults.js +3 -3
  69. package/dist/gulp/tasks/distribute.js +2 -2
  70. package/dist/gulp/tasks/html.js +9 -9
  71. package/dist/gulp/tasks/package-quick.js +3 -3
  72. package/dist/gulp/tasks/package.js +3 -3
  73. package/dist/gulp/tasks/release.js +3 -3
  74. package/dist/gulp/tasks/sass.js +6 -6
  75. package/dist/gulp/tasks/serve.js +4 -4
  76. package/dist/hooks/notarize-artifacts.js +1 -1
  77. package/dist/hooks/notarize.js +1 -1
  78. package/dist/index.js +5 -8
  79. package/dist/lib/_environment-mixin.js +50 -0
  80. package/dist/lib/_lifecycle-mixin.js +45 -0
  81. package/dist/lib/analytics.js +33 -35
  82. package/dist/lib/app-state.js +14 -14
  83. package/dist/lib/auth-flow.js +18 -18
  84. package/dist/lib/auth-persistence.js +12 -12
  85. package/dist/lib/auth.js +421 -0
  86. package/dist/lib/auto-updater.js +49 -49
  87. package/dist/lib/context-menu.js +13 -13
  88. package/dist/lib/context.js +19 -19
  89. package/dist/lib/deep-link.js +34 -34
  90. package/dist/lib/fontawesome.js +5 -5
  91. package/dist/lib/ipc.js +4 -4
  92. package/dist/lib/menu.js +25 -25
  93. package/dist/lib/protocol.js +5 -5
  94. package/dist/lib/remote-config.js +22 -22
  95. package/dist/lib/remote-scripts.js +21 -21
  96. package/dist/lib/restart-manager/index.js +28 -28
  97. package/dist/lib/sign-helpers/exec-with-limit.js +1 -1
  98. package/dist/lib/sign-helpers/sign-events.js +1 -1
  99. package/dist/lib/startup.js +18 -13
  100. package/dist/lib/storage.js +10 -10
  101. package/dist/lib/templating.js +16 -16
  102. package/dist/lib/theme.js +10 -10
  103. package/dist/lib/tray.js +27 -27
  104. package/dist/lib/usage.js +11 -11
  105. package/dist/lib/window-manager.js +26 -26
  106. package/dist/main.js +398 -483
  107. package/dist/preload.js +236 -176
  108. package/dist/renderer.js +417 -391
  109. package/dist/runner/job-started.js +1 -1
  110. package/dist/test/fixtures/consumer-app/config/omega.json5 +1 -1
  111. package/dist/test/fixtures/consumer-app/src/assets/js/components/main/index.js +8 -11
  112. package/dist/test/fixtures/consumer-app/src/main.js +5 -7
  113. package/dist/test/fixtures/consumer-app/src/preload.js +2 -2
  114. package/dist/test/harness/boot-entry.js +22 -20
  115. package/dist/test/harness/main-entry.js +31 -30
  116. package/dist/test/harness/renderer-entry.js +5 -5
  117. package/dist/test/harness/renderer-preload.js +137 -141
  118. package/dist/test/index.js +10 -10
  119. package/dist/test/runner.js +2 -2
  120. package/dist/test/runners/boot.js +7 -6
  121. package/dist/test/runners/electron.js +3 -2
  122. package/dist/test/runners/render-event.js +2 -2
  123. package/dist/test/suites/boot/consumer-app-boots.test.js +57 -26
  124. package/dist/test/suites/boot/restart-manager.test.js +2 -2
  125. package/dist/test/suites/boot/storage-bundled.test.js +5 -5
  126. package/dist/test/suites/boot/theme.test.js +13 -13
  127. package/dist/test/suites/build/audit.test.js +1 -1
  128. package/dist/test/suites/build/auth-persistence-resolve.test.js +6 -6
  129. package/dist/test/suites/build/boot-fixture.test.js +2 -2
  130. package/dist/test/suites/build/boot-runner-timeout.test.js +6 -5
  131. package/dist/test/suites/build/brand-scss.test.js +1 -1
  132. package/dist/test/suites/build/build-json-bake.test.js +1 -1
  133. package/dist/test/suites/build/cli.test.js +2 -2
  134. package/dist/test/suites/build/config-schema.test.js +5 -5
  135. package/dist/test/suites/build/defaults-scaffold.test.js +2 -2
  136. package/dist/test/suites/build/deploy-hook.test.js +3 -3
  137. package/dist/test/suites/build/ensure-target.test.js +2 -2
  138. package/dist/test/suites/build/env-delivery.test.js +2 -2
  139. package/dist/test/suites/build/esm-only-dependency.test.js +2 -2
  140. package/dist/test/suites/build/exports.test.js +9 -8
  141. package/dist/test/suites/build/get-config.test.js +4 -4
  142. package/dist/test/suites/build/manifest-deps.test.js +1 -1
  143. package/dist/test/suites/build/merge-line-files.test.js +1 -1
  144. package/dist/test/suites/build/omega-shell.test.js +34 -2
  145. package/dist/test/suites/build/omega.test.js +350 -0
  146. package/dist/test/suites/build/renderer-auth-bridge.test.js +211 -78
  147. package/dist/test/suites/build/runner.test.js +612 -5
  148. package/dist/test/suites/build/sentry.test.js +2 -2
  149. package/dist/test/suites/build/sign-windows-e2e.test.js +2 -2
  150. package/dist/test/suites/build/templating.test.js +3 -3
  151. package/dist/test/suites/build/test-stealth.test.js +7 -9
  152. package/dist/test/suites/build/url-helpers.test.js +55 -56
  153. package/dist/test/suites/build/validate-config.test.js +2 -2
  154. package/dist/test/suites/build/wave5-pins.test.js +2 -2
  155. package/dist/test/suites/main/analytics.test.js +59 -59
  156. package/dist/test/suites/main/app-state.test.js +66 -66
  157. package/dist/test/suites/main/auth-flow.test.js +41 -41
  158. package/dist/test/suites/main/auth-persistence.test.js +54 -43
  159. package/dist/test/suites/main/{client-bridge.integration.test.js → auth.integration.test.js} +10 -9
  160. package/dist/test/suites/main/auth.test.js +336 -0
  161. package/dist/test/suites/main/auto-updater.test.js +134 -134
  162. package/dist/test/suites/main/boot-sequence.test.js +37 -49
  163. package/dist/test/suites/main/context-menu.test.js +51 -50
  164. package/dist/test/suites/main/context.test.js +25 -25
  165. package/dist/test/suites/main/deep-link.test.js +74 -74
  166. package/dist/test/suites/main/fontawesome.test.js +27 -27
  167. package/dist/test/suites/main/ipc.test.js +35 -35
  168. package/dist/test/suites/main/menu.test.js +101 -100
  169. package/dist/test/suites/main/protocol.test.js +19 -19
  170. package/dist/test/suites/main/remote-config.test.js +63 -63
  171. package/dist/test/suites/main/remote-scripts.test.js +103 -103
  172. package/dist/test/suites/main/request.test.js +71 -0
  173. package/dist/test/suites/main/restart-manager.test.js +33 -33
  174. package/dist/test/suites/main/startup-paths-and-ua.test.js +4 -4
  175. package/dist/test/suites/main/startup.test.js +26 -26
  176. package/dist/test/suites/main/stealth-window.test.js +1 -1
  177. package/dist/test/suites/main/storage.test.js +25 -25
  178. package/dist/test/suites/main/theme.test.js +34 -34
  179. package/dist/test/suites/main/tray.test.js +79 -79
  180. package/dist/test/suites/main/url-helpers.test.js +133 -133
  181. package/dist/test/suites/main/usage.test.js +25 -25
  182. package/dist/test/suites/main/window-bounds.test.js +27 -27
  183. package/dist/test/suites/main/window-manager.test.js +44 -44
  184. package/dist/test/suites/renderer/analytics-bridge.test.js +5 -5
  185. package/dist/test/suites/renderer/cross-context-helpers.test.js +37 -31
  186. package/dist/test/suites/renderer/round-trip.test.js +3 -3
  187. package/dist/test/suites/renderer/tooltips.test.js +15 -15
  188. package/dist/test/suites/renderer/{window-em-surface.test.js → window-desktop-surface.test.js} +27 -7
  189. package/dist/test/utils/extended-mode-warning.js +1 -1
  190. package/dist/utils/boot-harness.js +56 -0
  191. package/dist/utils/mode-helpers.js +2 -15
  192. package/dist/utils/ship-keys.js +3 -3
  193. package/dist/utils/signing-status.js +51 -0
  194. package/dist/utils/test-events.js +7 -0
  195. package/dist/utils/test-stealth.js +6 -6
  196. package/dist/utils/url-helpers.js +52 -42
  197. package/dist/utils/user-agent.js +44 -0
  198. package/dist/vendor/account/engine.js +3 -3
  199. package/dist/vendor/account/index.js +14 -45
  200. package/dist/vendor/account/resolve-account.js +44 -0
  201. package/dist/vendor/account/schema.js +1 -1
  202. package/dist/vendor/account/user.js +99 -0
  203. package/dist/vendor/config/client-config.js +1 -1
  204. package/dist/vendor/config/environment.js +11 -30
  205. package/dist/vendor/config/index.js +13 -12
  206. package/dist/vendor/config/load.js +1 -2
  207. package/dist/vendor/config/platforms.js +1 -1
  208. package/dist/vendor/config/repo.js +24 -0
  209. package/dist/vendor/config/retired-keys.js +2 -2
  210. package/dist/vendor/config/schema.js +5 -8
  211. package/dist/vendor/config/site-global.js +2 -3
  212. package/dist/vendor/config/validate.js +1 -2
  213. package/dist/vendor/config/winback.js +1 -1
  214. package/dist/vendor/devkit/actions-secrets.js +1 -1
  215. package/dist/vendor/devkit/attach-log-file.js +1 -1
  216. package/dist/vendor/devkit/brand-version.js +162 -1
  217. package/dist/vendor/devkit/build-json.js +1 -1
  218. package/dist/vendor/devkit/cli-router.js +3 -4
  219. package/dist/vendor/devkit/defaults-engine.js +9 -11
  220. package/dist/vendor/devkit/deploy.js +64 -4
  221. package/dist/vendor/devkit/git-remote.js +78 -1
  222. package/dist/vendor/devkit/local.js +53 -3
  223. package/dist/vendor/devkit/lockfile.js +127 -0
  224. package/dist/vendor/devkit/merge-line-files.js +2 -3
  225. package/dist/vendor/devkit/pack-local.js +4 -7
  226. package/dist/vendor/devkit/preludes/origin-heal.js +35 -49
  227. package/dist/vendor/devkit/target-secrets.js +34 -45
  228. package/dist/vendor/devkit/test/runner-core.js +6 -6
  229. package/dist/vendor/monitoring/env.js +2 -2
  230. package/dist/vendor/monitoring/index.js +1 -1
  231. package/dist/vendor/monitoring/main.js +1 -1
  232. package/dist/vendor/monitoring/preload.js +1 -1
  233. package/dist/vendor/monitoring/renderer.js +1 -1
  234. package/docs/analytics.md +8 -8
  235. package/docs/app-state.md +19 -19
  236. package/docs/audit.md +4 -4
  237. package/docs/{client-bridge.md → auth.md} +88 -73
  238. package/docs/auto-updater.md +8 -8
  239. package/docs/boot-sequence.md +11 -6
  240. package/docs/build-system.md +1 -1
  241. package/docs/cdp-debugging.md +1 -1
  242. package/docs/common-mistakes.md +7 -7
  243. package/docs/config-schema.md +1 -1
  244. package/docs/context-menu.md +13 -13
  245. package/docs/context.md +11 -11
  246. package/docs/css.md +3 -9
  247. package/docs/deep-link.md +25 -25
  248. package/docs/environment-detection.md +16 -16
  249. package/docs/fontawesome.md +7 -5
  250. package/docs/hooks.md +8 -8
  251. package/docs/index.md +39 -28
  252. package/docs/ipc.md +10 -10
  253. package/docs/lib-modules.md +9 -9
  254. package/docs/logging.md +12 -14
  255. package/docs/menu.md +18 -18
  256. package/docs/releasing.md +4 -2
  257. package/docs/remote-config.md +9 -9
  258. package/docs/remote-scripts.md +12 -12
  259. package/docs/restart-manager.md +8 -8
  260. package/docs/runner.md +12 -10
  261. package/docs/sentry.md +4 -4
  262. package/docs/shared/analytics.md +1 -1
  263. package/docs/shared/brands.md +1 -1
  264. package/docs/shared/breaking-changes.md +79 -13
  265. package/docs/shared/config.md +24 -21
  266. package/docs/shared/deploys.md +30 -7
  267. package/docs/shared/local-dev.md +5 -3
  268. package/docs/shared/logging.md +1 -1
  269. package/docs/shared/monitoring.md +5 -5
  270. package/docs/shared/testing.md +2 -2
  271. package/docs/shared/theming.md +1 -1
  272. package/docs/shared/translation.md +19 -10
  273. package/docs/signing.md +1 -1
  274. package/docs/startup.md +16 -16
  275. package/docs/storage.md +11 -11
  276. package/docs/templating.md +3 -3
  277. package/docs/test-boot-layer.md +12 -12
  278. package/docs/test-framework.md +17 -17
  279. package/docs/themes.md +7 -7
  280. package/docs/tooltips.md +2 -2
  281. package/docs/tray.md +31 -31
  282. package/docs/usage.md +7 -7
  283. package/docs/verts.md +1 -1
  284. package/docs/windows.md +22 -22
  285. package/package.json +3 -4
  286. package/dist/lib/client-bridge.js +0 -374
  287. package/dist/lib/logger.js +0 -4
  288. package/dist/test/suites/build/manager.test.js +0 -213
  289. package/dist/test/suites/main/client-bridge.test.js +0 -262
@@ -1,374 +0,0 @@
1
- // Client bridge — main-process Firebase Auth, source of truth for renderers.
2
- //
3
- // This is @omega.js/desktop's analogue of BXM's background-service-worker auth role. The pattern:
4
- //
5
- // 1. Main runs its own Firebase Auth instance and is the source of truth.
6
- // 2. When a deep-link auth/token arrives, main calls signInWithCustomToken with that token,
7
- // then BROADCASTS the token to all renderer windows so their @omega.js/client Firebase
8
- // instances can sign in with the SAME token.
9
- // 3. On every renderer load, the renderer asks main "I'm at UID X (or null)" via the
10
- // desktop:auth:sync-request IPC. Main compares with its own UID and either does nothing,
11
- // tells the renderer to sign out, or fetches a fresh custom token from /omega
12
- // and sends it to the renderer.
13
- // 4. Sign-out: any renderer can request sign-out via desktop:auth:sign-out. Main signs out
14
- // its own Firebase + broadcasts desktop:auth:sign-out to all renderers.
15
- //
16
- // Firebase is BUNDLED by esbuild from @omega.js/desktop's module context (@omega.js/client owns it in @omega.js/desktop's
17
- // tree). If loading fails, the bridge stays in no-op mode and logs the reason.
18
-
19
- const LoggerLite = require('./logger-lite.js');
20
- const authPersistence = require('./auth-persistence.js');
21
- const { requiredPort } = require('../utils/url-helpers.js');
22
-
23
- const logger = new LoggerLite('client-bridge');
24
-
25
- const FIREBASE_APP_NAME = 'omega-auth';
26
-
27
- const bridge = {
28
- _initialized: false,
29
- _manager: null,
30
- _firebase: null, // firebase/app
31
- _firebaseAuth: null, // auth instance
32
- _firebaseModule: null, // firebase/auth namespace (signInWithCustomToken, signOut, ...)
33
- _stateSubs: new Set(), // main-side subscribers fn(user)
34
- _ipcRegistered: false,
35
- _resolvedPlan: null, // renderer-resolved subscription { plan, active, trialing, cancelling }
36
- _resolvedRoles: null, // renderer-resolved account roles { admin, betaTester, ... }
37
-
38
- async initialize(manager) {
39
- if (bridge._initialized) {
40
- return;
41
- }
42
-
43
- bridge._manager = manager;
44
-
45
- // Try to load firebase. If it's not installed, run in no-op mode.
46
- const ok = await bridge._tryLoadFirebase();
47
- if (!ok) {
48
- logger.warn('firebase not installed — client-bridge running in no-op mode. `npm i firebase` to enable auth.');
49
- }
50
-
51
- bridge._registerIpc();
52
-
53
- if (ok) {
54
- // Boot Firebase Auth. With a persistence strategy active (lib/auth-persistence.js,
55
- // default safeStorage), the session survives restarts — the restore lands async
56
- // via onAuthStateChanged shortly after boot.
57
- try {
58
- const strategy = await authPersistence.resolve(manager);
59
- bridge._firebaseAuth = bridge._getFirebaseAuth(strategy);
60
- // onAuthStateChanged is the source of truth listener.
61
- bridge._firebaseModule.onAuthStateChanged(bridge._firebaseAuth, (user) => {
62
- bridge._handleAuthStateChange(user);
63
- });
64
- logger.log(`initialize — firebase loaded (persistence: ${strategy ? strategy.name : 'in-memory'})`);
65
- } catch (e) {
66
- logger.error('firebase init failed:', e.message);
67
- }
68
- }
69
-
70
- bridge._initialized = true;
71
- },
72
-
73
- // Load firebase. Returns true on success. BUNDLED by esbuild from @omega.js/desktop's module context
74
- // (@omega.js/client owns firebase in @omega.js/desktop's tree) — same treatment as json5 in main.js. In the
75
- // webpack era it was a webpackIgnore'd runtime import(), which resolves relative to the CONSUMER's
76
- // main.bundle.js: that walk never reaches @omega.js/desktop's node_modules when @omega.js/desktop is symlinked
77
- // (`mgr install dev`) and depends on npm hoisting when installed — every dev app silently
78
- // ran the bridge in no-op mode. Interop guards handle both namespace shapes (json5 precedent).
79
- async _tryLoadFirebase() {
80
- try {
81
- const appMod = require('firebase/app');
82
- const authMod = require('firebase/auth');
83
-
84
- bridge._firebase = appMod.initializeApp ? appMod : (appMod.default || appMod);
85
- bridge._firebaseModule = authMod.getAuth ? authMod : (authMod.default || authMod);
86
- return true;
87
- } catch (e) {
88
- logger.warn('firebase load failed:', e.message);
89
- return false;
90
- }
91
- },
92
-
93
- _getFirebaseAuth(strategy) {
94
- if (bridge._firebaseAuth) return bridge._firebaseAuth;
95
-
96
- const firebaseConfig = bridge._manager.config.cloud?.config;
97
- if (!firebaseConfig || !Object.keys(firebaseConfig).length) {
98
- throw new Error('cloud.config is empty — cannot initialize auth.');
99
- }
100
-
101
- const { initializeApp, getApp } = bridge._firebase;
102
- const { getAuth, initializeAuth } = bridge._firebaseModule;
103
-
104
- let app;
105
- try { app = getApp(FIREBASE_APP_NAME); }
106
- catch (e) { app = initializeApp(firebaseConfig, FIREBASE_APP_NAME); }
107
-
108
- // With a storage strategy, boot auth on OUR persistence (session survives
109
- // restarts); without one, firebase's Node default (in-memory) applies.
110
- if (strategy && initializeAuth) {
111
- bridge._firebaseAuth = initializeAuth(app, {
112
- persistence: [authPersistence.buildFirebasePersistence(strategy)],
113
- });
114
- } else {
115
- bridge._firebaseAuth = getAuth(app);
116
- }
117
-
118
- // A TESTING run talks to the LOCAL stack, never real auth — the same move
119
- // getApiUrl() makes when it maps testing to localhost, and the same one
120
- // @omega.js/extension's background SW makes for its emulator runs. Only an
121
- // OMEGA_ENVIRONMENT=testing run reaches here; dev and production are untouched.
122
- if (bridge._manager.isTesting()) {
123
- const { connectAuthEmulator } = bridge._firebaseModule;
124
- const port = bridge._authEmulatorPort();
125
- logger.log(`testing run — connecting auth to the emulator on :${port}`);
126
- connectAuthEmulator(bridge._firebaseAuth, `http://localhost:${port}`, { disableWarnings: true });
127
- }
128
-
129
- return bridge._firebaseAuth;
130
- },
131
-
132
- // The auth emulator's port, on the same chain getApiUrl() walks: the
133
- // resolved-port env channel (N7), then the `dev.ports` map the bundle baked
134
- // into OMEGA_BUILD_JSON
135
- // ([#745](https://github.com/Omega-JS-Stack/omega/issues/745)). The classic
136
- // 9099 used to sit under those two as a last resort; it is gone (#834),
137
- // because nothing identity-checks what answers on it and a neighbouring
138
- // project's emulator reads as an auth mystery rather than a port problem.
139
- _authEmulatorPort() {
140
- return requiredPort(bridge._manager, 'OMEGA_AUTH_PORT', 'auth');
141
- },
142
-
143
- _registerIpc() {
144
- if (bridge._ipcRegistered) return;
145
- const ipc = bridge._manager.ipc;
146
-
147
- // Renderer asks main: "I'm at UID X (or null). Are we in sync?"
148
- ipc.handle('desktop:auth:sync-request', async ({ contextUid }) => {
149
- return bridge._handleSyncRequest(contextUid);
150
- });
151
-
152
- // Renderer asks main to sign out.
153
- ipc.handle('desktop:auth:sign-out', async () => {
154
- return bridge._handleSignOut();
155
- });
156
-
157
- // Renderer asks main for the current user (uid + email + displayName, no token).
158
- ipc.handle('desktop:auth:get-user', () => {
159
- const u = bridge._firebaseAuth?.currentUser;
160
- return u ? bridge._snapshotUser(u) : null;
161
- });
162
-
163
- // Renderer pushes its @omega.js/client account resolution (auth().listen() settled:
164
- // Firestore account fetched, subscription resolved). Main can't run Firestore,
165
- // so this is how main-side plan gates learn the REAL plan — BXM's "browser
166
- // contexts resolve, authority caches" split. UID-guarded: a stale push from a
167
- // renderer that hasn't synced yet is dropped.
168
- ipc.handle('desktop:auth:account-resolved', ({ uid, resolved, roles } = {}) => {
169
- const mainUid = bridge._firebaseAuth?.currentUser?.uid || null;
170
- if (!uid || uid !== mainUid) {
171
- return { accepted: false };
172
- }
173
-
174
- const changed = JSON.stringify(resolved) !== JSON.stringify(bridge._resolvedPlan)
175
- || JSON.stringify(roles || null) !== JSON.stringify(bridge._resolvedRoles);
176
- bridge._resolvedPlan = resolved || null;
177
- bridge._resolvedRoles = roles || null;
178
-
179
- if (changed) {
180
- logger.log(`account resolved — plan=${resolved?.plan || '(none)'} active=${!!resolved?.active}`);
181
- bridge._manager.ipc.broadcast('desktop:auth:plan-changed', { resolved: bridge._resolvedPlan, roles: bridge._resolvedRoles });
182
- // Main-side subscribers see the same user snapshot again with plan now known.
183
- const u = bridge._firebaseAuth?.currentUser;
184
- bridge._stateSubs.forEach((fn) => {
185
- try { fn(u ? bridge._snapshotUser(u) : null); } catch (e) { logger.error('onAuthChange subscriber threw:', e); }
186
- });
187
- }
188
- return { accepted: true };
189
- });
190
-
191
- bridge._ipcRegistered = true;
192
- },
193
-
194
- _handleAuthStateChange(user) {
195
- logger.log(`auth state → ${user ? user.email : 'signed out'}`);
196
-
197
- // Sign-out (or a different user signing in) invalidates the cached resolution —
198
- // renderers re-resolve and push fresh via desktop:auth:account-resolved.
199
- if (!user) {
200
- bridge._resolvedPlan = null;
201
- bridge._resolvedRoles = null;
202
- }
203
-
204
- // Notify any main-side subscribers (consumer code that called manager.omega.onAuthChange).
205
- const snap = user ? bridge._snapshotUser(user) : null;
206
- bridge._stateSubs.forEach((fn) => {
207
- try { fn(snap); } catch (e) { logger.error('onAuthChange subscriber threw:', e); }
208
- });
209
-
210
- // Tell renderers about the change so they can update UI / refresh menu items.
211
- bridge._manager.ipc.broadcast('desktop:auth:state-changed', snap);
212
-
213
- // Attribute Sentry events to the signed-in user (or clear on sign-out). When
214
- // sentry is disabled (no DSN, dev mode, etc.) setUser is a documented no-op.
215
- bridge._manager.sentry.setUser(snap);
216
- },
217
-
218
- // BXM's syncAuth pattern, ported to Electron.
219
- async _handleSyncRequest(contextUid) {
220
- if (!bridge._firebaseAuth) {
221
- return { needsSync: false, reason: 'firebase-not-loaded' };
222
- }
223
-
224
- const bgUser = bridge._firebaseAuth.currentUser;
225
- const bgUid = bgUser?.uid || null;
226
-
227
- // Already in sync.
228
- if (contextUid === bgUid) {
229
- return { needsSync: false };
230
- }
231
-
232
- // Renderer signed in but main signed out → tell renderer to sign out.
233
- if (!bgUser && contextUid) {
234
- return { needsSync: true, signOut: true };
235
- }
236
-
237
- // Main signed in, renderer not (or different user) → fetch a fresh custom token + send.
238
- try {
239
- const token = await bridge._fetchCustomToken(bgUser);
240
- return {
241
- needsSync: true,
242
- customToken: token,
243
- user: bridge._snapshotUser(bgUser),
244
- };
245
- } catch (e) {
246
- logger.error('sync-request: failed to fetch custom token:', e.message);
247
- return { needsSync: false, error: e.message };
248
- }
249
- },
250
-
251
- async _handleSignOut() {
252
- try {
253
- if (bridge._firebaseAuth?.currentUser) {
254
- await bridge._firebaseModule.signOut(bridge._firebaseAuth);
255
- }
256
- bridge._manager.ipc.broadcast('desktop:auth:sign-out', {});
257
- return { success: true };
258
- } catch (e) {
259
- logger.error('sign-out failed:', e.message);
260
- return { success: false, error: e.message };
261
- }
262
- },
263
-
264
- // Public API ──────────────────────────────────────────────────────────────────
265
-
266
- // Called by lib/deep-link's auth/token built-in.
267
- async handleAuthToken(token) {
268
- if (!bridge._firebaseAuth) {
269
- logger.warn('handleAuthToken called but firebase not loaded — ignoring.');
270
- return { success: false, reason: 'firebase-not-loaded' };
271
- }
272
- if (!token) {
273
- logger.warn('handleAuthToken called with no token — ignoring.');
274
- return { success: false, reason: 'no-token' };
275
- }
276
-
277
- try {
278
- const cred = await bridge._firebaseModule.signInWithCustomToken(bridge._firebaseAuth, token);
279
- logger.log(`signed in: ${cred.user.email || cred.user.uid}`);
280
-
281
- // Broadcast the token to renderers so they can sign in with the SAME token.
282
- // Tokens expire in 1 hour and aren't stored.
283
- bridge._manager.ipc.broadcast('desktop:auth:sign-in-with-token', { token });
284
-
285
- return { success: true, user: bridge._snapshotUser(cred.user) };
286
- } catch (e) {
287
- logger.error('signInWithCustomToken failed:', e.message);
288
- return { success: false, error: e.message };
289
- }
290
- },
291
-
292
- // Subscribe to auth state changes on the main side (e.g. for menu/tray refresh).
293
- onAuthChange(fn) {
294
- bridge._stateSubs.add(fn);
295
- return () => bridge._stateSubs.delete(fn);
296
- },
297
-
298
- // Snapshot of the current user (sync), or null.
299
- getCurrentUser() {
300
- const u = bridge._firebaseAuth?.currentUser;
301
- return u ? bridge._snapshotUser(u) : null;
302
- },
303
-
304
- // The renderer-resolved subscription ({ plan, active, trialing, cancelling }) or null
305
- // while no renderer has resolved yet. THE main-side plan source — consumer plan gates
306
- // read this (@omega.js/client's resolveSubscription output, pushed via desktop:auth:account-resolved).
307
- getResolvedPlan() {
308
- return bridge._resolvedPlan;
309
- },
310
-
311
- // The renderer-resolved account roles ({ admin, betaTester, ... }) or null.
312
- getResolvedRoles() {
313
- return bridge._resolvedRoles;
314
- },
315
-
316
- // Fresh Firebase ID token for the signed-in user (async), or null when
317
- // signed out / Firebase absent. For consumer main code calling
318
- // authenticated backend routes (`Authorization: Bearer <token>`).
319
- async getIdToken() {
320
- const u = bridge._firebaseAuth?.currentUser;
321
- return u ? u.getIdToken() : null;
322
- },
323
-
324
- // Force sign-out from main code.
325
- async signOut() {
326
- return bridge._handleSignOut();
327
- },
328
-
329
- // ─── Internals ────────────────────────────────────────────────────────────
330
-
331
- _snapshotUser(user) {
332
- return {
333
- uid: user.uid,
334
- email: user.email,
335
- displayName: user.displayName,
336
- photoURL: user.photoURL,
337
- emailVerified: user.emailVerified,
338
- };
339
- },
340
-
341
- // Fetch a fresh custom token for the currently-signed-in user via the shared
342
- // @omega.js/client request layer. Mirrors BXM: POST <apiUrl>/omega/user/token —
343
- // the route defaults uid to the authenticated caller and responds { token }
344
- // at the top level.
345
- async _fetchCustomToken(user) {
346
- const { createRequest } = require('@omega.js/client/modules/request.js');
347
-
348
- const request = createRequest({
349
- getApiUrl: () => bridge._manager.getApiUrl(),
350
- getIdToken: (force) => user.getIdToken(force),
351
- });
352
-
353
- const data = await request('/omega/user/token', { method: 'POST', body: {} });
354
-
355
- const token = data?.token;
356
- if (!token) {
357
- throw new Error('@omega.js/backend response missing token.');
358
- }
359
- return token;
360
- },
361
-
362
- // Tear-down used by tests — wipes state but keeps registered IPC handlers
363
- // (they're idempotent via `ipc.handle` duplicate-detection).
364
- async _resetForTests() {
365
- try {
366
- if (bridge._firebaseAuth?.currentUser && bridge._firebaseModule?.signOut) {
367
- await bridge._firebaseModule.signOut(bridge._firebaseAuth);
368
- }
369
- } catch (e) { /* ignore */ }
370
- bridge._stateSubs.clear();
371
- },
372
- };
373
-
374
- module.exports = bridge;
@@ -1,4 +0,0 @@
1
- // Shared devkit module — vendored into dist/vendor/devkit at prepare time by the
2
- // preparePackage `after` hook. This shim keeps the framework's internal require
3
- // paths stable while the implementation lives in @omega.js/devkit.
4
- module.exports = require('../vendor/devkit/logger.js');
@@ -1,213 +0,0 @@
1
- // Build-time tests for the Manager class (build.js).
2
-
3
- const Manager = require('../../../build.js');
4
- const defineCases = require('../../../vendor/devkit/test/define-cases.js');
5
-
6
- // The one input and the two lane flags that decide it, saved and restored
7
- // around every case that touches them (#817).
8
- const LANE_KEYS = ['OMEGA_ENVIRONMENT', 'OMEGA_BUILD_MODE', 'OMEGA_TEST_MODE', 'NODE_ENV'];
9
-
10
- /**
11
- * Run `fn` with exactly `vars` set on the lane keys (null clears them all).
12
- * @param {object|null} vars - the lane env for this case.
13
- * @param {function} fn - the body.
14
- * @returns {*} whatever `fn` returns.
15
- */
16
- function withEnvironment(vars, fn) {
17
- const saved = {};
18
- for (const key of LANE_KEYS) saved[key] = process.env[key];
19
- try {
20
- for (const key of LANE_KEYS) delete process.env[key];
21
- Object.assign(process.env, vars || {});
22
- return fn();
23
- } finally {
24
- for (const key of LANE_KEYS) {
25
- if (saved[key] === undefined) delete process.env[key];
26
- else process.env[key] = saved[key];
27
- }
28
- }
29
- }
30
-
31
- /**
32
- * What src/build.js resolves at LOAD for a given lane. build.js sets the input
33
- * once, at require time, so this loads a fresh copy of it per lane.
34
- * @param {object} vars - the lane env.
35
- * @returns {string} the resolved environment.
36
- */
37
- function laneEnvironment(vars) {
38
- const buildPath = require.resolve('../../../build.js');
39
- return withEnvironment(vars, () => {
40
- const saved = require.cache[buildPath];
41
- delete require.cache[buildPath];
42
- try {
43
- return require(buildPath).getEnvironment();
44
- } finally {
45
- if (saved) require.cache[buildPath] = saved;
46
- else delete require.cache[buildPath];
47
- }
48
- });
49
- }
50
-
51
- module.exports = defineCases({
52
- type: 'group', // independent tests; run all even on failure
53
- layer: 'build',
54
- description: 'Manager (build.js)',
55
- tests: [
56
- {
57
- name: 'class is exported and instantiable',
58
- run: (ctx) => {
59
- ctx.expect(typeof Manager).toBe('function');
60
- ctx.expect(new Manager()).toBeInstanceOf(Manager);
61
- },
62
- },
63
- {
64
- name: 'getMode returns the mode shape',
65
- run: (ctx) => {
66
- const mode = Manager.getMode();
67
- ctx.expect(mode).toHaveProperty('build');
68
- ctx.expect(mode).toHaveProperty('publish');
69
- ctx.expect(mode).toHaveProperty('server');
70
- ctx.expect(mode).toHaveProperty('environment');
71
- ctx.expect(['development', 'testing', 'production']).toContain(mode.environment);
72
- },
73
- },
74
- {
75
- // The environment reads ONE input, `OMEGA_ENVIRONMENT`
76
- // ([#817](https://github.com/Omega-JS-Stack/omega/issues/817)), and the
77
- // lane that boots or builds this app sets it (src/build.js does, at load).
78
- // Nothing sniffs NODE_ENV, app.isPackaged or OMEGA_TEST_MODE any more.
79
- name: 'getEnvironment answers the one input, for each of the three names',
80
- run: (ctx) => {
81
- withEnvironment(null, () => {
82
- for (const name of ['development', 'testing', 'production']) {
83
- process.env.OMEGA_ENVIRONMENT = name;
84
- ctx.expect(Manager.getEnvironment()).toBe(name);
85
- }
86
- });
87
- },
88
- },
89
- {
90
- // The bug this replaced: with no signal desktop's own copy answered
91
- // 'production', so a plain `npm start` bundled itself as a production
92
- // artifact, while @omega.js/extension's copy of the same function
93
- // answered 'development' from the same inputs (#817).
94
- name: 'the build lane resolves production, a dev boot resolves development',
95
- run: (ctx) => {
96
- const buildLane = laneEnvironment({ OMEGA_BUILD_MODE: 'true' });
97
- ctx.expect(buildLane).toBe('production');
98
-
99
- const devBoot = laneEnvironment({});
100
- ctx.expect(devBoot).toBe('development');
101
-
102
- // A lane that already named one keeps it, EXCEPT a build lane: a
103
- // production build spawned from a test run still bakes production.
104
- ctx.expect(laneEnvironment({ OMEGA_ENVIRONMENT: 'testing' })).toBe('testing');
105
- ctx.expect(laneEnvironment({ OMEGA_ENVIRONMENT: 'testing', OMEGA_BUILD_MODE: 'true' })).toBe('production');
106
- },
107
- },
108
- {
109
- name: 'no input at all is a loud error naming OMEGA_ENVIRONMENT, never a default',
110
- run: (ctx) => {
111
- withEnvironment(null, () => {
112
- delete process.env.OMEGA_ENVIRONMENT;
113
- ctx.expect(() => Manager.getEnvironment()).toThrow(/OMEGA_ENVIRONMENT/);
114
- process.env.OMEGA_ENVIRONMENT = 'staging';
115
- ctx.expect(() => Manager.getEnvironment()).toThrow(/OMEGA_ENVIRONMENT/);
116
- });
117
- },
118
- },
119
- {
120
- // The core invariant: is*() DERIVE from getEnvironment(), so they can
121
- // NEVER disagree with it, and exactly one is always true.
122
- name: 'invariant: is*() exactly matches getEnvironment() + mutually exclusive (every scenario)',
123
- run: (ctx) => {
124
- withEnvironment(null, () => {
125
- for (const name of ['development', 'testing', 'production']) {
126
- process.env.OMEGA_ENVIRONMENT = name;
127
- const e = Manager.getEnvironment();
128
- ctx.expect(e).toBe(name);
129
- ctx.expect(Manager.isDevelopment()).toBe(e === 'development');
130
- ctx.expect(Manager.isTesting()).toBe(e === 'testing');
131
- ctx.expect(Manager.isProduction()).toBe(e === 'production');
132
- const trueCount = [Manager.isDevelopment(), Manager.isTesting(), Manager.isProduction()].filter(Boolean).length;
133
- ctx.expect(trueCount).toBe(1);
134
- }
135
- });
136
- },
137
- },
138
- {
139
- name: 'getPackage("main") resolves to @omega.js/desktop package.json',
140
- run: (ctx) => {
141
- const pkg = Manager.getPackage('main');
142
- ctx.expect(pkg.name).toBe('@omega.js/desktop');
143
- ctx.expect(pkg).toHaveProperty('version');
144
- ctx.expect(pkg).toHaveProperty('exports');
145
- },
146
- },
147
- {
148
- name: 'getRootPath("main") returns a non-empty string',
149
- run: (ctx) => {
150
- const root = Manager.getRootPath('main');
151
- ctx.expect(typeof root).toBe('string');
152
- ctx.expect(root.length).toBeGreaterThan(0);
153
- },
154
- },
155
- {
156
- name: 'getLiveReloadPort defaults to 35729',
157
- run: (ctx) => {
158
- const prev = process.env.OMEGA_LIVERELOAD_PORT;
159
- delete process.env.OMEGA_LIVERELOAD_PORT;
160
- try {
161
- ctx.expect(Manager.getLiveReloadPort()).toBe(35729);
162
- } finally {
163
- if (prev !== undefined) process.env.OMEGA_LIVERELOAD_PORT = prev;
164
- }
165
- },
166
- },
167
- {
168
- name: 'getWindowsSignStrategy defaults to self-hosted when no config',
169
- run: (ctx) => {
170
- // No EM_WIN_SIGN_STRATEGY env-var support anymore — config is the only source.
171
- // Run from a cwd with no omega.json5 to confirm the default.
172
- const fs = require('fs'); const os = require('os'); const path = require('path');
173
- const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'desktop-strategy-'));
174
- const orig = process.cwd();
175
- try {
176
- process.chdir(tmp);
177
- ctx.expect(Manager.getWindowsSignStrategy()).toBe('self-hosted');
178
- } finally {
179
- process.chdir(orig);
180
- fs.rmSync(tmp, { recursive: true, force: true });
181
- }
182
- },
183
- },
184
- {
185
- name: 'getWindowsSignStrategy reads config platforms.windows.signing.strategy',
186
- run: (ctx) => {
187
- const fs = require('fs'); const os = require('os'); const path = require('path');
188
- const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'desktop-strategy-'));
189
- fs.mkdirSync(path.join(tmp, 'config'), { recursive: true });
190
- fs.writeFileSync(path.join(tmp, 'config', 'omega.json5'),
191
- `{ targets: { desktop: { type: 'desktop', platforms: { windows: { signing: { strategy: 'cloud' } } } } } }`);
192
- const orig = process.cwd();
193
- try {
194
- process.chdir(tmp);
195
- ctx.expect(Manager.getWindowsSignStrategy()).toBe('cloud');
196
- } finally {
197
- process.chdir(orig);
198
- fs.rmSync(tmp, { recursive: true, force: true });
199
- }
200
- },
201
- },
202
- {
203
- name: 'logger returns a named logger',
204
- run: (ctx) => {
205
- const m = new Manager();
206
- const logger = m.logger('test-name');
207
- ctx.expect(logger.name).toBe('test-name');
208
- ctx.expect(typeof logger.log).toBe('function');
209
- ctx.expect(typeof logger.error).toBe('function');
210
- },
211
- },
212
- ],
213
- });