@omega.js/desktop 0.50.0 → 0.51.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 (267) hide show
  1. package/README.md +1 -1
  2. package/dist/assets/css/tokens/_index.scss +1 -1
  3. package/dist/assets/themes/base/_includes/frontend/sections/account-section-header.html +4 -1
  4. package/dist/assets/themes/base/_includes/frontend/sections/footer.html +13 -6
  5. package/dist/assets/themes/base/_includes/frontend/sections/nav.html +10 -8
  6. package/dist/assets/themes/base/_includes/global/sections/account.html +3 -1
  7. package/dist/assets/themes/base/_includes/global/sections/app-sidebar.html +12 -8
  8. package/dist/assets/themes/base/_includes/global/sections/app-topbar.html +10 -6
  9. package/dist/assets/themes/base/_includes/global/sections/page-header.html +8 -4
  10. package/dist/assets/themes/base/_layouts/backend/pages/dashboard/index.html +24 -24
  11. package/dist/assets/themes/base/_layouts/frontend/pages/about.html +10 -10
  12. package/dist/assets/themes/base/_layouts/frontend/pages/account/index.html +35 -35
  13. package/dist/assets/themes/base/_layouts/frontend/pages/alternatives/index.html +4 -4
  14. package/dist/assets/themes/base/_layouts/frontend/pages/auth/signin.html +1 -1
  15. package/dist/assets/themes/base/_layouts/frontend/pages/auth/signup.html +4 -4
  16. package/dist/assets/themes/base/_layouts/frontend/pages/contact.html +3 -3
  17. package/dist/assets/themes/base/_layouts/frontend/pages/download.html +34 -33
  18. package/dist/assets/themes/base/_layouts/frontend/pages/extension/index.html +11 -11
  19. package/dist/assets/themes/base/_layouts/frontend/pages/legal/document.html +1 -1
  20. package/dist/assets/themes/base/_layouts/frontend/pages/status.html +1 -1
  21. package/dist/assets/themes/base/_layouts/frontend/pages/team/index.html +9 -7
  22. package/dist/assets/themes/base/_layouts/frontend/pages/team/member.html +5 -3
  23. package/dist/assets/themes/base/_sections/about/letter/section.html +1 -1
  24. package/dist/assets/themes/base/_sections/about/letter/section.json5 +4 -4
  25. package/dist/assets/themes/base/_sections/marketing/bento/section.html +1 -1
  26. package/dist/assets/themes/base/_sections/marketing/bento/section.json5 +15 -15
  27. package/dist/assets/themes/base/_sections/marketing/cta/section.json5 +1 -1
  28. package/dist/assets/themes/base/_sections/marketing/hero/section.html +6 -6
  29. package/dist/assets/themes/base/_sections/marketing/hero/section.json5 +10 -10
  30. package/dist/assets/themes/base/_sections/marketing/product-demo/section.html +1 -1
  31. package/dist/assets/themes/base/_sections/marketing/product-demo/section.json5 +2 -2
  32. package/dist/assets/themes/base/_sections/marketing/stats/section.html +1 -1
  33. package/dist/assets/themes/base/_sections/marketing/stats/section.json5 +5 -5
  34. package/dist/assets/themes/base/_sections/marketing/trusted-by/section.html +1 -1
  35. package/dist/assets/themes/base/_sections/marketing/trusted-by/section.json5 +2 -2
  36. package/dist/assets/themes/neobrutalism/_layouts/frontend/pages/index.html +10 -10
  37. package/dist/assets/themes/newsflash/_layouts/frontend/pages/index.html +10 -10
  38. package/dist/assets/themes/newsflash/_sections/marketing/desks/section.html +2 -2
  39. package/dist/assets/themes/newsflash/_sections/marketing/desks/section.json5 +4 -4
  40. package/dist/build.js +69 -28
  41. package/dist/cli-run.js +20 -13
  42. package/dist/cli.js +12 -7
  43. package/dist/commands/build.js +0 -2
  44. package/dist/commands/deploy.js +116 -44
  45. package/dist/commands/finalize-release.js +5 -4
  46. package/dist/commands/launch.js +12 -7
  47. package/dist/commands/lib/deploy-precheck.js +66 -34
  48. package/dist/commands/lib/ensure-target.js +71 -11
  49. package/dist/commands/package.js +0 -1
  50. package/dist/commands/publish.js +11 -4
  51. package/dist/commands/release.js +99 -252
  52. package/dist/commands/runner.js +42 -5
  53. package/dist/commands/sign-windows.js +46 -15
  54. package/dist/commands/test.js +4 -3
  55. package/dist/commands/validate-certs.js +291 -115
  56. package/dist/config/page-template.html +4 -0
  57. package/dist/defaults/.github/workflows/build.yml +144 -64
  58. package/dist/defaults/AGENTS.md +3 -2
  59. package/dist/defaults/_.gitignore +4 -2
  60. package/dist/defaults/config/certs/README.md +25 -45
  61. package/dist/defaults/config/omega.json5 +66 -32
  62. package/dist/defaults/hooks/deploy/pre.js +10 -0
  63. package/dist/defaults/src/assets/scss/main.scss +12 -2
  64. package/dist/defaults/src/integrations/tray/index.js +1 -1
  65. package/dist/gulp/main.js +13 -25
  66. package/dist/gulp/tasks/audit.js +18 -6
  67. package/dist/gulp/tasks/build-config.js +181 -60
  68. package/dist/gulp/tasks/bundle.js +108 -36
  69. package/dist/gulp/tasks/release.js +86 -4
  70. package/dist/gulp/tasks/sass.js +11 -0
  71. package/dist/hooks/lib/notarize-tools.js +137 -0
  72. package/dist/hooks/notarize-artifacts.js +57 -0
  73. package/dist/hooks/notarize.js +59 -8
  74. package/dist/lib/auth-persistence.js +25 -0
  75. package/dist/lib/client-bridge.js +11 -8
  76. package/dist/lib/deep-link.js +15 -8
  77. package/dist/lib/protocol.js +7 -1
  78. package/dist/lib/restart-manager/install.js +6 -3
  79. package/dist/lib/sign-helpers/auto-unlock.js +65 -27
  80. package/dist/lib/sign-helpers/console-lock.js +34 -0
  81. package/dist/lib/sign-helpers/exec-with-limit.js +68 -0
  82. package/dist/lib/sign-helpers/resolve-icons.js +6 -4
  83. package/dist/lib/tray.js +7 -6
  84. package/dist/main.js +16 -0
  85. package/dist/preload.js +8 -0
  86. package/dist/renderer.js +16 -4
  87. package/dist/runner/job-started.js +104 -0
  88. package/dist/test/fixtures/consumer-app/config/omega.json5 +7 -0
  89. package/dist/test/fixtures/consumer-app/package.json +1 -1
  90. package/dist/test/fixtures/consumer-app/src/assets/js/components/main/index.js +7 -1
  91. package/dist/test/harness/main-entry.js +2 -1
  92. package/dist/test/harness/renderer-preload.js +12 -3
  93. package/dist/test/runners/boot.js +101 -11
  94. package/dist/test/runners/electron.js +1 -1
  95. package/dist/test/suites/boot/consumer-app-boots.test.js +54 -0
  96. package/dist/test/suites/build/audit.test.js +45 -6
  97. package/dist/test/suites/build/auth-persistence-resolve.test.js +119 -0
  98. package/dist/test/suites/build/auto-unlock.test.js +105 -0
  99. package/dist/test/suites/build/boot-runner-timeout.test.js +253 -0
  100. package/dist/test/suites/build/brand-scss.test.js +106 -0
  101. package/dist/test/suites/build/build-config.test.js +146 -36
  102. package/dist/test/suites/build/build-json-bake.test.js +245 -0
  103. package/dist/test/suites/build/build-verbs.test.js +48 -15
  104. package/dist/test/suites/build/build-workflow-jobs.test.js +190 -0
  105. package/dist/test/suites/build/cli.test.js +32 -10
  106. package/dist/test/suites/build/config-schema.test.js +4 -4
  107. package/dist/test/suites/build/console-lock.test.js +51 -0
  108. package/dist/test/suites/build/defaults-scaffold.test.js +71 -2
  109. package/dist/test/suites/build/deploy-direct.test.js +241 -0
  110. package/dist/test/suites/build/deploy-dispatch.test.js +287 -0
  111. package/dist/test/suites/build/deploy-hook.test.js +169 -0
  112. package/dist/test/suites/build/ensure-target.test.js +14 -2
  113. package/dist/test/suites/build/env-delivery.test.js +19 -10
  114. package/dist/test/suites/build/env-watch.test.js +18 -7
  115. package/dist/test/suites/build/esm-only-dependency.test.js +127 -0
  116. package/dist/test/suites/build/exec-with-limit.test.js +53 -0
  117. package/dist/test/suites/build/finalize-release.test.js +2 -2
  118. package/dist/test/suites/build/get-config.test.js +120 -8
  119. package/dist/test/suites/build/github-utils.test.js +12 -6
  120. package/dist/test/suites/build/license-stamp.test.js +6 -4
  121. package/dist/test/suites/build/manager.test.js +88 -68
  122. package/dist/test/suites/build/manifest-deps.test.js +116 -0
  123. package/dist/test/suites/build/merge-line-files.test.js +27 -4
  124. package/dist/test/suites/build/notarize-artifacts.test.js +135 -0
  125. package/dist/test/suites/build/notarize-tools.test.js +38 -0
  126. package/dist/test/suites/build/notarize.test.js +207 -0
  127. package/dist/test/suites/build/release-pipeline.test.js +38 -60
  128. package/dist/test/suites/build/release-skipped-upload.test.js +107 -0
  129. package/dist/test/suites/build/resolve-icons.test.js +35 -35
  130. package/dist/test/suites/build/runner-job-guard.test.js +182 -0
  131. package/dist/test/suites/build/runner.test.js +25 -2
  132. package/dist/test/suites/build/sentry.test.js +8 -3
  133. package/dist/test/suites/build/setup-scripts.test.js +3 -0
  134. package/dist/test/suites/build/sign-windows.test.js +92 -8
  135. package/dist/test/suites/build/test-stealth.test.js +17 -11
  136. package/dist/test/suites/build/url-helpers.test.js +37 -17
  137. package/dist/test/suites/build/validate-certs.test.js +428 -55
  138. package/dist/test/suites/build/validate-config.test.js +3 -3
  139. package/dist/test/suites/main/auth-flow.test.js +12 -0
  140. package/dist/test/suites/main/auth-persistence.test.js +14 -17
  141. package/dist/test/suites/main/auto-updater.test.js +2 -2
  142. package/dist/test/suites/main/boot-sequence.test.js +1 -1
  143. package/dist/test/suites/main/client-bridge.integration.test.js +5 -82
  144. package/dist/test/suites/main/client-bridge.test.js +19 -6
  145. package/dist/test/suites/main/deep-link.test.js +54 -0
  146. package/dist/test/suites/main/startup-paths-and-ua.test.js +1 -1
  147. package/dist/test/suites/main/url-helpers.test.js +81 -72
  148. package/dist/test/suites/renderer/cross-context-helpers.test.js +19 -16
  149. package/dist/utils/build-pipeline.js +10 -10
  150. package/dist/utils/github.js +12 -51
  151. package/dist/utils/load-env.js +66 -0
  152. package/dist/utils/mode-helpers.js +43 -111
  153. package/dist/utils/platform.js +37 -0
  154. package/dist/utils/runner-env.js +2 -1
  155. package/dist/utils/runner-job-guard.js +149 -0
  156. package/dist/utils/ship-keys.js +52 -0
  157. package/dist/utils/test-stealth.js +5 -3
  158. package/dist/utils/url-helpers.js +33 -17
  159. package/dist/vendor/config/bundle-id.js +53 -0
  160. package/dist/vendor/config/client-config.js +141 -0
  161. package/dist/vendor/config/company.js +334 -15
  162. package/dist/vendor/config/dev-facts.js +48 -0
  163. package/dist/vendor/config/env-delivery.js +231 -9
  164. package/dist/vendor/config/env-retired.js +137 -0
  165. package/dist/vendor/config/env-rules.js +22 -3
  166. package/dist/vendor/config/env-schema.js +234 -117
  167. package/dist/vendor/config/env.js +55 -26
  168. package/dist/vendor/config/environment.js +189 -0
  169. package/dist/vendor/config/hooks.js +13 -11
  170. package/dist/vendor/config/index.js +121 -44
  171. package/dist/vendor/config/load.js +366 -115
  172. package/dist/vendor/config/merge.js +2 -2
  173. package/dist/vendor/config/order.js +3 -3
  174. package/dist/vendor/config/platforms.js +276 -0
  175. package/dist/vendor/config/repo.js +226 -104
  176. package/dist/vendor/config/retired-keys.js +232 -27
  177. package/dist/vendor/config/schema.js +525 -99
  178. package/dist/vendor/config/site-global.js +63 -53
  179. package/dist/vendor/config/targets.js +187 -0
  180. package/dist/vendor/config/validate.js +119 -59
  181. package/dist/vendor/devkit/argv.js +118 -0
  182. package/dist/vendor/devkit/attach-log-file.js +21 -13
  183. package/dist/vendor/devkit/brand-tokens.js +278 -0
  184. package/dist/vendor/devkit/brand-version.js +264 -0
  185. package/dist/vendor/devkit/build-json.js +91 -0
  186. package/dist/vendor/devkit/bundle.js +48 -0
  187. package/dist/vendor/devkit/certificate-expiry.js +108 -0
  188. package/dist/vendor/devkit/certs.js +16 -194
  189. package/dist/vendor/devkit/ci-workflows.js +124 -8
  190. package/dist/vendor/devkit/cli-router.js +3 -3
  191. package/dist/vendor/devkit/defaults-engine.js +69 -7
  192. package/dist/vendor/devkit/deploy-follow.js +297 -0
  193. package/dist/vendor/devkit/deploy-precheck.js +23 -8
  194. package/dist/vendor/devkit/deploy-record.js +11 -27
  195. package/dist/vendor/devkit/deploy-snapshot.js +661 -0
  196. package/dist/vendor/devkit/deploy.js +445 -75
  197. package/dist/vendor/devkit/git-auth.js +73 -0
  198. package/dist/vendor/devkit/git-remote.js +95 -0
  199. package/dist/vendor/devkit/github-repo.js +290 -0
  200. package/dist/vendor/devkit/local.js +47 -0
  201. package/dist/vendor/devkit/merge-line-files.js +23 -16
  202. package/dist/vendor/devkit/omega-bin.js +18 -3
  203. package/dist/vendor/devkit/pack-local.js +391 -0
  204. package/dist/vendor/devkit/preludes/index.js +120 -0
  205. package/dist/vendor/devkit/preludes/origin-heal.js +156 -0
  206. package/dist/vendor/devkit/service-account.js +43 -0
  207. package/dist/vendor/devkit/ship-plan.js +112 -0
  208. package/dist/vendor/devkit/signing-env.js +180 -0
  209. package/dist/vendor/devkit/signing-tree.js +92 -0
  210. package/dist/vendor/devkit/target-seams.js +142 -0
  211. package/dist/vendor/devkit/target-secrets.js +235 -50
  212. package/dist/vendor/devkit/test/esm-only-fixture.js +48 -0
  213. package/dist/vendor/devkit/test/fixtures/esm-only-package/browser.js +19 -0
  214. package/dist/vendor/devkit/test/fixtures/esm-only-package/index.js +20 -0
  215. package/dist/vendor/devkit/test/fixtures/esm-only-package/package.json +13 -0
  216. package/dist/vendor/monitoring/env.js +20 -10
  217. package/dist/vendor/monitoring/main.js +1 -1
  218. package/dist/vendor/monitoring/preload.js +1 -1
  219. package/dist/vendor/monitoring/renderer.js +1 -1
  220. package/docs/analytics.md +1 -1
  221. package/docs/auto-updater.md +5 -5
  222. package/docs/boot-sequence.md +1 -1
  223. package/docs/build-system.md +15 -7
  224. package/docs/client-bridge.md +9 -7
  225. package/docs/config-schema.md +4 -4
  226. package/docs/css.md +8 -2
  227. package/docs/deep-link.md +12 -4
  228. package/docs/environment-detection.md +32 -24
  229. package/docs/hooks.md +3 -1
  230. package/docs/icons.md +7 -7
  231. package/docs/index.md +61 -23
  232. package/docs/installer-options.md +24 -21
  233. package/docs/logging.md +5 -5
  234. package/docs/releasing.md +25 -17
  235. package/docs/runner.md +40 -5
  236. package/docs/shared/brands.md +12 -6
  237. package/docs/shared/breaking-changes.md +375 -21
  238. package/docs/shared/config.md +757 -199
  239. package/docs/shared/deploys.md +194 -91
  240. package/docs/shared/icons.md +18 -0
  241. package/docs/shared/local-dev.md +24 -6
  242. package/docs/shared/logging.md +9 -6
  243. package/docs/shared/monitoring.md +27 -13
  244. package/docs/shared/publishing.md +3 -3
  245. package/docs/shared/rulings.md +2 -2
  246. package/docs/shared/testing.md +1 -1
  247. package/docs/shared/theming.md +26 -1
  248. package/docs/shared/translation.md +49 -7
  249. package/docs/shared/updates.md +1 -1
  250. package/docs/signing.md +59 -33
  251. package/docs/test-framework.md +10 -5
  252. package/docs/themes.md +15 -1
  253. package/package.json +18 -12
  254. package/bin/omega-desktop +0 -2
  255. package/dist/commands/push-secrets.js +0 -141
  256. package/dist/test/suites/build/deliver-certs.test.js +0 -95
  257. package/dist/test/suites/build/derive-signing-env.test.js +0 -122
  258. package/dist/test/suites/build/push-secrets.test.js +0 -226
  259. package/dist/test/suites/build/resolve-signing-cert.test.js +0 -342
  260. package/dist/utils/deliver-certs.js +0 -69
  261. package/dist/utils/derive-signing-env.js +0 -56
  262. package/dist/utils/resolve-signing-cert.js +0 -175
  263. package/dist/vendor/config/desktop-artifacts.js +0 -110
  264. package/dist/vendor/config/instances.js +0 -208
  265. /package/dist/defaults/config/icons/{macos → mac}/dmg.png +0 -0
  266. /package/dist/defaults/config/icons/{macos → mac}/icon.png +0 -0
  267. /package/dist/defaults/config/icons/{macos → mac}/tray.png +0 -0
@@ -0,0 +1,52 @@
1
+ // The ship-credential check every publish runs FIRST
2
+ // ([#867](https://github.com/Omega-JS-Stack/omega/issues/867)).
3
+ //
4
+ // A format this brand DECLARES is one it means to ship, so a credential that
5
+ // format cannot publish without is a release that dies on a runner: the snap
6
+ // leg with no Snap Store login, the Windows leg with no credential for the
7
+ // signing strategy it configured. Both used to surface minutes into a build,
8
+ // or (worse) as a silently skipped target.
9
+ //
10
+ // What is owed comes from @omega.js/config's format table, narrowed to this
11
+ // brand by the env schema's own `requiredWhen` gates, so a brand that never
12
+ // mentions the snap owes nothing and a brand that writes
13
+ // `platforms.linux.formats.snap` owes the login. The refusal carries the same
14
+ // line shape push-secrets prints
15
+ // ([#891](https://github.com/Omega-JS-Stack/omega/issues/891)) and names the
16
+ // ONE walk that collects it, exactly as the extension publish does.
17
+ //
18
+ // A BUILD is not a publish: it keeps its clean skip (gulp/build-config drops
19
+ // the snap target when the login is absent), because nothing is being put in
20
+ // front of users.
21
+
22
+ const { shipPlan, missingShipKeys, shipKeyRefusal } = require('../vendor/devkit/ship-plan.js');
23
+
24
+ const Manager = new (require('../build.js'));
25
+ const logger = Manager.logger('ship-keys');
26
+
27
+ /**
28
+ * Refuse a publish whose declared formats have no credential to ship with.
29
+ *
30
+ * @param {object} [options]
31
+ * @param {object} [options.config] - Resolved config (default: the consumer's).
32
+ * @param {object} [options.env] - Env map (default: process.env).
33
+ * @returns {Array<object>} The ship plan (what this brand ships).
34
+ * @throws {Error} Naming every missing key, its declaration, and the walk.
35
+ */
36
+ function assertShipKeys(options) {
37
+ options = options || {};
38
+
39
+ const config = options.config || Manager.getConfig();
40
+ const plan = shipPlan(config, 'desktop');
41
+ const missing = missingShipKeys(plan, options.env || process.env);
42
+
43
+ if (missing.length > 0) {
44
+ throw new Error(shipKeyRefusal(missing));
45
+ }
46
+
47
+ logger.log(`Shipping ${plan.map((entry) => `${entry.platform}/${entry.format}`).join(', ')}`);
48
+
49
+ return plan;
50
+ }
51
+
52
+ module.exports = { assertShipKeys };
@@ -11,9 +11,11 @@
11
11
  // which initializes its Manager only after app ready (too late to prevent
12
12
  // the launch activation)
13
13
  //
14
- // Pass the Manager when you have one — its isTesting() is authoritative (honors
15
- // config.em.environment overrides). Without one (the harness, pre-Manager code),
16
- // falls back to the standalone mode-helpers check (OMEGA_TEST_MODE=true).
14
+ // Pass the Manager when you have one: its isTesting() reads the environment the
15
+ // Manager's own config was baked with. Without one (the harness, pre-Manager
16
+ // code) the standalone check reads the process's OMEGA_ENVIRONMENT, the one
17
+ // input every lane sets ([#817](https://github.com/Omega-JS-Stack/omega/issues/817));
18
+ // the test runners spawn their children naming `testing`.
17
19
  //
18
20
  // Usage:
19
21
  // const isTestStealth = require('./utils/test-stealth.js');
@@ -11,6 +11,10 @@
11
11
  // callers normally pass NO argument. An explicit `environment` arg is an override
12
12
  // (used mainly by tests to pin a specific environment's mapping).
13
13
 
14
+ // The missing-fact error is the ONE shared one (#834). Its module requires
15
+ // nothing, so it rides the renderer bundle exactly as this file does.
16
+ const { devFactMissing } = require('../vendor/config/dev-facts.js');
17
+
14
18
  // Guarded the way @omega.js/extension's twin is: the renderer bundle is a
15
19
  // browser context, and only the Node-side contexts (main, preload, build, the
16
20
  // test harness) carry the resolved-port env channel.
@@ -20,14 +24,9 @@ function envPort(name) {
20
24
  : undefined;
21
25
  }
22
26
 
23
- // The classic dev WEBSITE ORIGIN: a lockstep copy of @omega.js/config's
24
- // CLASSIC_DEV_ORIGIN, which is the SSOT. This module is attached in the RENDERER
25
- // too (src/renderer.js), so it rides the browser bundle and cannot require that
26
- // Node-only package (fs, net, json5); @omega.js/client mirrors the same constant
27
- // for the same reason. The build suite pins this copy to the config package's
28
- // value, so a drift fails a test instead of a user
29
- // ([#747](https://github.com/Omega-JS-Stack/omega/issues/747)).
30
- const CLASSIC_DEV_ORIGIN = 'https://localhost:4000';
27
+ // The step that writes the resolved dev map into this surface's artifact, named
28
+ // in every missing-fact error below (#834).
29
+ const DEV_FACT_WRITER = "@omega.js/desktop's bundle task, from the live stack's ports file plus the OMEGA_*_PORT env channel";
31
30
 
32
31
  // One local port, from whichever channel this context has: the env var first
33
32
  // (the CLI that booted the stack publishes it, N7), then the `dev.ports` map
@@ -35,10 +34,26 @@ const CLASSIC_DEV_ORIGIN = 'https://localhost:4000';
35
34
  // parent env, so a bumped emulator port reaches it only that way
36
35
  // ([#745](https://github.com/Omega-JS-Stack/omega/issues/745)). Same helper,
37
36
  // same order as @omega.js/extension's src/utils/url-helpers.js.
37
+ //
38
+ // There is no third step any more (#834): the classic numbers used to be
39
+ // hand-typed here as a last resort for "a build made with no stack up", which
40
+ // is a guess about a port nothing identity-checks. A miss is loud instead.
38
41
  function localPort(context, envName, name) {
39
42
  return envPort(envName) || context?.config?.dev?.ports?.[name];
40
43
  }
41
44
 
45
+ // The same read, REQUIRED: the one every URL getter below uses, because every
46
+ // one of them is answering with a real local address or not at all.
47
+ function requiredPort(context, envName, name) {
48
+ const port = localPort(context, envName, name);
49
+
50
+ if (!port) {
51
+ throw devFactMissing(`dev port for \`${name}\``, DEV_FACT_WRITER);
52
+ }
53
+
54
+ return port;
55
+ }
56
+
42
57
  function getFunctionsUrl(environment) {
43
58
  const env = environment || this.getEnvironment();
44
59
  const projectId = this?.config?.cloud?.config?.projectId;
@@ -51,7 +66,7 @@ function getFunctionsUrl(environment) {
51
66
  // same three-step chain getApiUrl() walks: the OMEGA_*_PORT env channel (N7),
52
67
  // then the baked map, then the classic default.
53
68
  if (env === 'development' || env === 'testing') {
54
- const port = localPort(this, 'OMEGA_FUNCTIONS_PORT', 'functions') || 5001;
69
+ const port = requiredPort(this, 'OMEGA_FUNCTIONS_PORT', 'functions');
55
70
  return `http://localhost:${port}/${projectId}/us-central1`;
56
71
  }
57
72
 
@@ -64,12 +79,13 @@ function getApiUrl(environment) {
64
79
  // Local for development OR testing; production otherwise. Mirrors
65
80
  // @omega.js/backend's getApiUrl (N7): a resolved OMEGA_HTTPS_PORT means
66
81
  // `mgr serve`'s mkcert proxy is up (https); otherwise plain http to the
67
- // hosting emulator (env port, baked port, or classic 5002).
82
+ // hosting emulator (env port, then baked port). Neither resolved is a broken
83
+ // build, not a case to assume the classic 5002 through (#834).
68
84
  if (env === 'development' || env === 'testing') {
69
85
  const httpsPort = localPort(this, 'OMEGA_HTTPS_PORT', 'https');
70
86
  return httpsPort
71
87
  ? `https://localhost:${httpsPort}`
72
- : `http://localhost:${localPort(this, 'OMEGA_HOSTING_PORT', 'hosting') || 5002}`;
88
+ : `http://localhost:${requiredPort(this, 'OMEGA_HOSTING_PORT', 'hosting')}`;
73
89
  }
74
90
 
75
91
  // Prod: api.<brand host>. Mirrors @omega.js/client.getApiUrl. Never derive
@@ -89,9 +105,10 @@ function getApiUrl(environment) {
89
105
  // ([#262](https://github.com/Omega-JS-Stack/omega/issues/262)): the baked
90
106
  // `dev.origin` the live website published (scheme, host AND port, the complete
91
107
  // fact) wins, else a port from the usual chain (OMEGA_WEBSITE_PORT, then the baked
92
- // `dev.ports.website`) composed over https, else the classic dev origin. The scheme
93
- // is https because `omega dev` fronts its public port with the mkcert proxy by
94
- // default, so a port alone can never say it
108
+ // `dev.ports.website`) composed over https. There is no classic-origin fallback
109
+ // any more (#834): a wrong dev origin fails as a silent connection refusal. The
110
+ // scheme is https because `omega dev` fronts its public port with the mkcert
111
+ // proxy by default, so a port alone can never say it
95
112
  // ([#747](https://github.com/Omega-JS-Stack/omega/issues/747)).
96
113
  // Prod → `config.brand.url`. Use this whenever app code wants to link out to "the
97
114
  // website" (Help → Website tray/menu items, "Open in browser," billing portal
@@ -106,8 +123,7 @@ function getWebsiteUrl(environment) {
106
123
  return origin;
107
124
  }
108
125
 
109
- const port = localPort(this, 'OMEGA_WEBSITE_PORT', 'website');
110
- return port ? `https://localhost:${port}` : CLASSIC_DEV_ORIGIN;
126
+ return `https://localhost:${requiredPort(this, 'OMEGA_WEBSITE_PORT', 'website')}`;
111
127
  }
112
128
 
113
129
  const url = this?.config?.brand?.url;
@@ -160,8 +176,8 @@ function attachTo(Manager) {
160
176
 
161
177
  module.exports = {
162
178
  attachTo,
163
- CLASSIC_DEV_ORIGIN,
164
179
  localPort,
180
+ requiredPort,
165
181
  getFunctionsUrl,
166
182
  getApiUrl,
167
183
  getWebsiteUrl,
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Bundle-identifier policy — reverse-DNS ids derived from the brand's web
3
+ * identity (Ian 2026-07-14: the prefix is the reverse domain, autoset at
4
+ * onboarding from the parent company when one exists, else the brand's own
5
+ * url).
6
+ *
7
+ * deriveBundleIdPrefix('https://itwcreativeworks.com') → 'com.itwcreativeworks'
8
+ * deriveBundleIdPrefix('https://playground.omegajs.dev') → 'dev.omegajs.playground'
9
+ * composeBundleId('com.itwcreativeworks', 'daily-build')
10
+ * → 'com.itwcreativeworks.daily.build'
11
+ *
12
+ * Sanitization keeps ids legal on BOTH stores: Apple allows hyphens but
13
+ * Android package segments don't, so domain labels drop non-alphanumerics
14
+ * and brand-id hyphens become dots (each id word is its own segment).
15
+ */
16
+
17
+ /**
18
+ * Reverse-DNS prefix from a company/brand url, or null when the url does
19
+ * not parse. `www.` is dropped; every other hostname label survives (a
20
+ * subdomain brand derives a subdomain-scoped prefix).
21
+ *
22
+ * @param {string} url - http(s) url (brand.url shape)
23
+ * @returns {string|null}
24
+ */
25
+ function deriveBundleIdPrefix(url) {
26
+ let hostname;
27
+ try {
28
+ hostname = new URL(url).hostname;
29
+ } catch {
30
+ return null;
31
+ }
32
+
33
+ const labels = hostname
34
+ .replace(/^www\./, '')
35
+ .split('.')
36
+ .map((label) => label.toLowerCase().replace(/[^a-z0-9]/g, ''))
37
+ .filter(Boolean);
38
+
39
+ return labels.length > 0 ? labels.reverse().join('.') : null;
40
+ }
41
+
42
+ /**
43
+ * Full bundle identifier: prefix + the brand id with hyphens as dots.
44
+ *
45
+ * @param {string} prefix - reverse-DNS prefix (config or derived)
46
+ * @param {string} brandId - brand.id ('daily-build')
47
+ * @returns {string}
48
+ */
49
+ function composeBundleId(prefix, brandId) {
50
+ return `${prefix}.${String(brandId).toLowerCase().replace(/-+/g, '.')}`;
51
+ }
52
+
53
+ module.exports = { deriveBundleIdPrefix, composeBundleId };
@@ -0,0 +1,141 @@
1
+ /**
2
+ * client-config.js: the ONE browser-safe subset of a resolved config
3
+ * ([#894](https://github.com/Omega-JS-Stack/omega/issues/894)).
4
+ *
5
+ * Three surfaces bake a config into an artifact a browser can read: web's page
6
+ * chrome, desktop's renderer bundle, and every extension bundle. Each one used
7
+ * to decide what may go public on its own, three different ways (desktop
8
+ * shipped the WHOLE resolved config, the extension kept a hand-written allow
9
+ * list, web wired a subset together in its engine and its foot include), so a
10
+ * new public value had to be added in three places and every one of them could
11
+ * be the place it leaked.
12
+ *
13
+ * This is that decision, once. The schema's `CLIENT_SECTIONS` says which
14
+ * sections a browser may see (and how much of each), the list below says which
15
+ * BUILD FACTS ride beside them, and nothing else leaves the build. All three
16
+ * bakes call this and wrap the answer identically:
17
+ *
18
+ * OMEGA_BUILD_JSON = { config: clientConfig(resolved), package, mode, license, builtAt }
19
+ *
20
+ * Secrets are a loud failure, not a filter: a secret-shaped key inside a
21
+ * section that is about to ship means the resolved config carried one at all,
22
+ * which loadConfig() and validateConfig() already refuse. A value a build is
23
+ * SANCTIONED to bake (`publicAtRest`, docs/shared/config.md) comes from .env
24
+ * and is added by that build after this gate, never carried through it.
25
+ */
26
+
27
+ const { CLIENT_SECTIONS } = require('./schema.js');
28
+ const { findSecretKeys } = require('./secrets.js');
29
+ const { resolveWinbackOffer } = require('./winback.js');
30
+
31
+ // The per-build facts a surface composes ON TOP of the resolved config before
32
+ // baking it. They are not config sections (nothing authors them in
33
+ // omega.json5): each is a fact about the artifact, spelled the same on every
34
+ // surface so one read answers everywhere.
35
+ //
36
+ // runtime 'web' | 'browser-extension' | 'electron'
37
+ // environment the build's verdict ('development' | 'testing' | 'production')
38
+ // version the target's own package version, the release tag every error
39
+ // report carries as `<brand.id>@<version>` (#380)
40
+ // buildTime the build stamp (ms)
41
+ // target WHICH target this artifact IS, by name (#887)
42
+ // url this target's own resolved public url (#588)
43
+ // dev the local stack's resolved map: ports + the sibling website's
44
+ // origin, development builds only (#300/#262). The extension's
45
+ // live-reload port rides here too (#896), the one home of a
46
+ // local-stack number.
47
+ const CLIENT_FACT_KEYS = ['runtime', 'environment', 'version', 'buildTime', 'target', 'url', 'dev'];
48
+
49
+ /**
50
+ * Copy exactly `paths` out of a section (dot-paths, deep).
51
+ * @param {*} section - the resolved section value.
52
+ * @param {string[]} paths - dot-paths inside it.
53
+ * @returns {object|undefined} the picked sub-tree, or undefined when it is empty.
54
+ */
55
+ function pickPaths(section, paths) {
56
+ if (!section || typeof section !== 'object') {
57
+ return undefined;
58
+ }
59
+
60
+ const picked = {};
61
+ let found = false;
62
+
63
+ for (const path of paths) {
64
+ const keys = path.split('.');
65
+ let source = section;
66
+ let missing = false;
67
+
68
+ for (const key of keys) {
69
+ if (!source || typeof source !== 'object' || !(key in source)) {
70
+ missing = true;
71
+ break;
72
+ }
73
+ source = source[key];
74
+ }
75
+ if (missing) continue;
76
+
77
+ let cursor = picked;
78
+ for (const key of keys.slice(0, -1)) {
79
+ cursor[key] = cursor[key] || {};
80
+ cursor = cursor[key];
81
+ }
82
+ cursor[keys[keys.length - 1]] = source;
83
+ found = true;
84
+ }
85
+
86
+ return found ? picked : undefined;
87
+ }
88
+
89
+ /**
90
+ * The subset of a resolved config a browser may see.
91
+ * @param {object} [resolved] - a resolved config (Manager.getConfig() output),
92
+ * with this build's facts composed onto it.
93
+ * @returns {object} the blob every browser surface bakes as OMEGA_BUILD_JSON.config.
94
+ * @throws {Error} when a section that is about to ship carries a secret-shaped key.
95
+ */
96
+ function clientConfig(resolved) {
97
+ const source = resolved || {};
98
+ const client = {};
99
+
100
+ for (const key of CLIENT_FACT_KEYS) {
101
+ if (key in source) client[key] = source[key];
102
+ }
103
+
104
+ for (const [section, rule] of Object.entries(CLIENT_SECTIONS)) {
105
+ if (!(section in source)) continue;
106
+
107
+ const value = rule === true ? source[section] : pickPaths(source[section], rule);
108
+ if (value === undefined) continue;
109
+
110
+ client[section] = value;
111
+ }
112
+
113
+ // A secret in here is not a value to drop: it is a config that should never
114
+ // have loaded, so it fails the build at the one place that would have
115
+ // published it.
116
+ const secrets = findSecretKeys(client);
117
+ if (secrets.length) {
118
+ throw new Error(
119
+ `[@omega.js/config] refusing to bake a browser config carrying secret-shaped keys: ${secrets.join(', ')}. `
120
+ + 'Secrets live in .env, never in config/omega.json5 (docs/shared/config.md).',
121
+ );
122
+ }
123
+
124
+ // The one home of the cancel-flow save offer's default (#268) is
125
+ // @omega.js/config, so the browser is handed the RESOLVED offer: the dialog a
126
+ // customer reads and the coupon the backend creates can never name different
127
+ // numbers.
128
+ if (client.payment) {
129
+ client.payment = { ...client.payment, winback: resolveWinbackOffer(client.payment) };
130
+ }
131
+
132
+ // Never an alias of the resolved config: what a bake stamps into an artifact
133
+ // cannot be something a later build step can still mutate. A JSON round trip
134
+ // and not structuredClone, because JSON is what a bake CAN carry: a page's
135
+ // resolved config arrives through a template data cascade that hangs
136
+ // functions and accessors off it, and those drop out here rather than
137
+ // crashing the build.
138
+ return JSON.parse(JSON.stringify(client));
139
+ }
140
+
141
+ module.exports = { clientConfig, CLIENT_FACT_KEYS };