@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
@@ -70,6 +70,49 @@ module.exports = defineCases({
70
70
  }
71
71
  },
72
72
  },
73
+ {
74
+ name: 'baseConfig: files never pack the target\'s scratch and state dirs (#866)',
75
+ run: (ctx) => {
76
+ const { baseConfig } = require(path.join(__dirname, '..', '..', '..', 'gulp', 'tasks', 'build-config.js'));
77
+ const files = baseConfig({}).files;
78
+ // The boot runner stages .omega/test-app with symlinks into the target and the
79
+ // packager followed them (dangling after a folder rename); none of these dirs
80
+ // is app content anyway.
81
+ for (const dir of ['.omega', '.claude', '.temp', '.cache', '.gh-runners', 'test']) {
82
+ ctx.expect(files).toContain(`!${dir}/**`);
83
+ }
84
+ // src/ stays: the runtime reads src/integrations/* from the app root.
85
+ ctx.expect(files.includes('!src/**')).toBe(false);
86
+ },
87
+ },
88
+ {
89
+ name: 'baseConfig: the DMG is signed with the Developer ID identity (#891)',
90
+ run: (ctx) => {
91
+ const { baseConfig } = require(path.join(__dirname, '..', '..', '..', 'gulp', 'tasks', 'build-config.js'));
92
+ const out = baseConfig({ app: { productName: 'Deployment Playground' } });
93
+
94
+ // Gatekeeper assesses a downloaded image by its OWN signature
95
+ // (`--context context:primary-signature`), and electron-builder leaves
96
+ // the image unsigned unless told: run 34735658588 notarized and stapled
97
+ // the DMG, then spctl refused it for the missing signature.
98
+ ctx.expect(out.dmg.sign).toBe(true);
99
+ },
100
+ },
101
+ {
102
+ name: 'notarizeHooks: the DMG is proved per artifact, BEFORE its upload (#891)',
103
+ run: (ctx) => {
104
+ const { notarizeHooks } = require(path.join(__dirname, '..', '..', '..', 'gulp', 'tasks', 'build-config.js'));
105
+ const hooks = notarizeHooks();
106
+
107
+ // electron-builder awaits `artifactBuildCompleted` before it emits the
108
+ // `artifactCreated` the publisher uploads on; `afterAllArtifactBuild`
109
+ // runs after every upload is queued. Run 34738410986 published a signed
110
+ // and notarized DMG with no staple because the proof ran there.
111
+ ctx.expect(hooks.afterSign).toContain(path.join('hooks', 'notarize.js'));
112
+ ctx.expect(hooks.artifactBuildCompleted).toContain(path.join('hooks', 'notarize-artifacts.js'));
113
+ ctx.expect(hooks.afterAllArtifactBuild).toBeUndefined();
114
+ },
115
+ },
73
116
  {
74
117
  name: 'baseConfig: artifact names carry NO version, from @omega.js/config\'s ONE rule (#620)',
75
118
  run: (ctx) => {
@@ -82,19 +125,22 @@ module.exports = defineCases({
82
125
  // github.com/<org>/<repo>/releases/latest/download/<asset> a stable
83
126
  // direct-download URL — the site derives its buttons from THESE names,
84
127
  // so a second spelling here is a dead download button.
85
- ctx.expect(out.mac.artifactName).toBe('Deployment-Playground-mac-universal.${ext}');
86
- ctx.expect(out.nsis.artifactName).toBe('Deployment-Playground-windows-universal.${ext}');
87
- ctx.expect(out.deb.artifactName).toBe(names.linux.debian);
128
+ // The segment is the FORMAT (#867), the same word the site's
129
+ // /download/<platform>/<format> link carries.
130
+ ctx.expect(out.dmg.artifactName).toBe(names.mac.dmg);
131
+ ctx.expect(out.nsis.artifactName).toBe(names.windows.nsis);
132
+ ctx.expect(out.deb.artifactName).toBe(names.linux.deb);
88
133
  ctx.expect(out.appImage.artifactName).toBe(names.linux.appimage);
134
+ ctx.expect(out.dmg.artifactName).toBe('Deployment-Playground-mac-dmg.dmg');
135
+ ctx.expect(out.nsis.artifactName).toBe('Deployment-Playground-windows-nsis.exe');
89
136
 
90
- // The mac fallback covers the dmg AND the auto-update zip, so the dmg
91
- // needs no override of its own.
92
- ctx.expect(out.mac.artifactName.replace('${ext}', 'dmg')).toBe(names.mac.universal);
93
- ctx.expect(out.nsis.artifactName.replace('${ext}', 'exe')).toBe(names.windows.universal);
94
- ctx.expect(out.dmg.artifactName).toBe(undefined);
137
+ // The mac FALLBACK is the auto-update zip's, the one mac artifact that
138
+ // is not a declared format: nobody links it, the feed names it.
139
+ ctx.expect(out.mac.artifactName).toBe('Deployment-Playground-mac.${ext}');
140
+ ctx.expect(out.mac.artifactName.replace('${ext}', 'zip')).toBe('Deployment-Playground-mac.zip');
95
141
 
96
142
  // Nothing keeps a ${version} token — every name is stable across releases.
97
- for (const template of [out.mac.artifactName, out.nsis.artifactName, out.deb.artifactName, out.appImage.artifactName, out.linux.artifactName]) {
143
+ for (const template of [out.mac.artifactName, out.dmg.artifactName, out.nsis.artifactName, out.deb.artifactName, out.appImage.artifactName, out.linux.artifactName]) {
98
144
  ctx.expect(template.includes('${version}')).toBe(false);
99
145
  }
100
146
  },
@@ -159,7 +205,7 @@ module.exports = defineCases({
159
205
 
160
206
  // NSIS merges every arch into ONE installer, so windows is genuinely
161
207
  // safe — its multi-arch default is never refused.
162
- ctx.expect(baseConfig({ platforms: { win: { arch: ['x64', 'ia32', 'arm64'] } } }, build).win.target[0].arch)
208
+ ctx.expect(baseConfig({ platforms: { windows: { arch: ['x64', 'ia32', 'arm64'] } } }, build).win.target[0].arch)
163
209
  .toEqual(['x64', 'ia32', 'arm64']);
164
210
 
165
211
  // One linux arch names one deb: an arm64-only brand ships fine.
@@ -280,28 +326,28 @@ module.exports = defineCases({
280
326
  },
281
327
  },
282
328
  {
283
- name: 'baseConfig: platforms.win.oneClick: false produces wizard installer',
329
+ name: 'baseConfig: platforms.windows.oneClick: false produces wizard installer',
284
330
  run: (ctx) => {
285
331
  const { baseConfig } = require(path.join(__dirname, '..', '..', '..', 'gulp', 'tasks', 'build-config.js'));
286
- const out = baseConfig({ platforms: { win: { oneClick: false } } });
332
+ const out = baseConfig({ platforms: { windows: { oneClick: false } } });
287
333
  ctx.expect(out.nsis.oneClick).toBe(false);
288
334
  ctx.expect(out.nsis.allowToChangeInstallationDirectory).toBe(true);
289
335
  },
290
336
  },
291
337
  {
292
- name: 'baseConfig: snap missing from config — no snap target (default OFF)',
338
+ name: 'baseConfig: a config that declares no platforms ships every format, snap included (#867)',
293
339
  run: (ctx) => {
294
- // Behavior contract: callers who never set platforms.linux.snap.enabled should
295
- // never get a snap target emitted. The scaffold ships `enabled: true` so new
296
- // projects opt in by virtue of the scaffold; this case is for older projects
297
- // or programmatic callers that don't supply the field.
340
+ // Presence is the switch and every format defaults ON, so a caller that
341
+ // declares nothing gets the framework's whole shipping set. The snap's
342
+ // second gate is its credentials, which is what keeps a brand that has
343
+ // not run the Snap ask yet from failing CI.
298
344
  const { baseConfig } = require(path.join(__dirname, '..', '..', '..', 'gulp', 'tasks', 'build-config.js'));
299
345
  const saved = process.env.SNAPCRAFT_STORE_CREDENTIALS;
300
- process.env.SNAPCRAFT_STORE_CREDENTIALS = 'fake-creds-blob'; // creds present, but config doesn't enable
346
+ process.env.SNAPCRAFT_STORE_CREDENTIALS = 'fake-creds-blob';
301
347
  try {
302
348
  const out = baseConfig({});
303
- ctx.expect(out.linux.target.find((t) => t.target === 'snap')).toBeUndefined();
304
- ctx.expect(out.snap).toBeUndefined();
349
+ ctx.expect(out.linux.target.map((t) => t.target)).toEqual(['deb', 'AppImage', 'snap']);
350
+ ctx.expect(out.snap).toBeDefined();
305
351
  } finally {
306
352
  if (saved === undefined) delete process.env.SNAPCRAFT_STORE_CREDENTIALS;
307
353
  else process.env.SNAPCRAFT_STORE_CREDENTIALS = saved;
@@ -309,13 +355,13 @@ module.exports = defineCases({
309
355
  },
310
356
  },
311
357
  {
312
- name: 'baseConfig: snap enabled in config but no creds — auto-skipped',
358
+ name: 'baseConfig: snap declared but no creds, auto-skipped',
313
359
  run: (ctx) => {
314
360
  const { baseConfig } = require(path.join(__dirname, '..', '..', '..', 'gulp', 'tasks', 'build-config.js'));
315
361
  const saved = process.env.SNAPCRAFT_STORE_CREDENTIALS;
316
362
  delete process.env.SNAPCRAFT_STORE_CREDENTIALS;
317
363
  try {
318
- const out = baseConfig({ platforms: { linux: { snap: { enabled: true } } } });
364
+ const out = baseConfig({ platforms: { linux: { formats: { snap: {} } } } });
319
365
  ctx.expect(out.linux.target.find((t) => t.target === 'snap')).toBeUndefined();
320
366
  ctx.expect(out.snap).toBeUndefined();
321
367
  } finally {
@@ -324,13 +370,13 @@ module.exports = defineCases({
324
370
  },
325
371
  },
326
372
  {
327
- name: 'baseConfig: snap explicitly disabled — no snap target regardless of creds',
373
+ name: 'baseConfig: snap dropped with `false`, no snap target regardless of creds',
328
374
  run: (ctx) => {
329
375
  const { baseConfig } = require(path.join(__dirname, '..', '..', '..', 'gulp', 'tasks', 'build-config.js'));
330
376
  const saved = process.env.SNAPCRAFT_STORE_CREDENTIALS;
331
377
  process.env.SNAPCRAFT_STORE_CREDENTIALS = 'fake-creds-blob';
332
378
  try {
333
- const out = baseConfig({ platforms: { linux: { snap: { enabled: false } } } });
379
+ const out = baseConfig({ platforms: { linux: { formats: { snap: false } } } });
334
380
  ctx.expect(out.linux.target.find((t) => t.target === 'snap')).toBeUndefined();
335
381
  ctx.expect(out.snap).toBeUndefined();
336
382
  } finally {
@@ -340,18 +386,18 @@ module.exports = defineCases({
340
386
  },
341
387
  },
342
388
  {
343
- name: 'baseConfig: snap enabled + creds present emits snap target + snap publish block',
389
+ name: 'baseConfig: snap declared + creds present emits snap target + snap publish block',
344
390
  run: (ctx) => {
345
391
  const { baseConfig } = require(path.join(__dirname, '..', '..', '..', 'gulp', 'tasks', 'build-config.js'));
346
392
  const saved = process.env.SNAPCRAFT_STORE_CREDENTIALS;
347
393
  process.env.SNAPCRAFT_STORE_CREDENTIALS = 'fake-creds-blob';
348
394
  try {
349
- const out = baseConfig({ platforms: { linux: { snap: { enabled: true } } } });
395
+ const out = baseConfig({ platforms: { linux: { formats: { snap: { channels: ['edge'] } } } } });
350
396
  ctx.expect(out.linux.target.find((t) => t.target === 'snap')).toBeDefined();
351
397
  ctx.expect(out.snap).toBeDefined();
352
398
  ctx.expect(out.snap.confinement).toBe('strict');
353
399
  ctx.expect(out.snap.publish.provider).toBe('snapStore');
354
- ctx.expect(out.snap.publish.channels).toEqual(['stable']);
400
+ ctx.expect(out.snap.publish.channels).toEqual(['edge'], 'the format\'s own settings ride inside it');
355
401
  } finally {
356
402
  if (saved === undefined) delete process.env.SNAPCRAFT_STORE_CREDENTIALS;
357
403
  else process.env.SNAPCRAFT_STORE_CREDENTIALS = saved;
@@ -364,9 +410,9 @@ module.exports = defineCases({
364
410
  const { baseConfig } = require(path.join(__dirname, '..', '..', '..', 'gulp', 'tasks', 'build-config.js'));
365
411
  const out = baseConfig({
366
412
  platforms: {
367
- mac: { arch: ['arm64'] },
368
- win: { arch: ['x64'] },
369
- linux: { arch: ['arm64'] },
413
+ mac: { arch: ['arm64'] },
414
+ windows: { arch: ['x64'] },
415
+ linux: { arch: ['arm64'] },
370
416
  },
371
417
  });
372
418
  ctx.expect(out.mac.target[0].arch).toEqual(['arm64']);
@@ -412,7 +458,7 @@ module.exports = defineCases({
412
458
  // framework monorepo, and a feed baked from it would 404 forever.
413
459
  ctx.expect(publishConfig({
414
460
  brand: { id: 'omega-playground' },
415
- repo: { providers: { github: { org: 'Omega-JS-Stack' } } },
461
+ repo: { provider: 'github', org: 'Omega-JS-Stack' },
416
462
  releases: {},
417
463
  })).toEqual({
418
464
  provider: 'github',
@@ -421,12 +467,19 @@ module.exports = defineCases({
421
467
  releaseType: 'release',
422
468
  });
423
469
 
424
- // An explicit block still names its own repo.
470
+ // #883: `releases` is a presence switch and nothing more. The owner
471
+ // and repo overrides are retired, so the feed can only ever be the
472
+ // brand's one derived releases repo.
425
473
  ctx.expect(publishConfig({
426
474
  brand: { id: 'acme' },
427
- repo: { providers: { github: { org: 'Acme-Org' } } },
428
- releases: { owner: 'Acme-Binaries', repo: 'acme-bins' },
429
- }).repo).toBe('acme-bins');
475
+ repo: { provider: 'github', org: 'Acme-Org' },
476
+ releases: {},
477
+ })).toEqual({
478
+ provider: 'github',
479
+ owner: 'Acme-Org',
480
+ repo: 'acme-releases',
481
+ releaseType: 'release',
482
+ });
430
483
  },
431
484
  },
432
485
  {
@@ -436,7 +489,7 @@ module.exports = defineCases({
436
489
 
437
490
  ctx.expect(publishConfig({
438
491
  brand: { id: 'acme' },
439
- repo: { providers: { github: { org: 'Acme-Org' } } },
492
+ repo: { provider: 'github', org: 'Acme-Org' },
440
493
  releases: { enabled: false },
441
494
  })).toBe(null);
442
495
 
@@ -453,5 +506,62 @@ module.exports = defineCases({
453
506
  ctx.expect(out.protocols).toBeUndefined();
454
507
  },
455
508
  },
509
+ // The .deb target's two metadata facts (#872). electron-builder reads the
510
+ // homepage off the app package.json (`extraMetadata` is what it merges in) and
511
+ // the maintainer off `linux.maintainer`; without them the deb build dies at
512
+ // PACKAGE time, after the AppImage of the same run has already uploaded.
513
+ {
514
+ name: 'baseConfig: the brand url and support email become the deb metadata (#872)',
515
+ run: (ctx) => {
516
+ const { baseConfig } = require(path.join(__dirname, '..', '..', '..', 'gulp', 'tasks', 'build-config.js'));
517
+ const yaml = require('js-yaml');
518
+
519
+ const out = baseConfig({
520
+ brand: {
521
+ name: 'OMEGA Playground',
522
+ url: 'https://playground.omegajs.dev',
523
+ contact: { email: 'support@playground.omegajs.dev' },
524
+ },
525
+ });
526
+
527
+ ctx.expect(out.extraMetadata.homepage).toBe('https://playground.omegajs.dev');
528
+ ctx.expect(out.linux.maintainer).toBe('OMEGA Playground <support@playground.omegajs.dev>');
529
+
530
+ // And they survive into the file that is actually written.
531
+ const written = yaml.load(yaml.dump(out, { lineWidth: -1, noRefs: true }));
532
+ ctx.expect(written.extraMetadata.homepage).toBe('https://playground.omegajs.dev');
533
+ ctx.expect(written.linux.maintainer).toBe('OMEGA Playground <support@playground.omegajs.dev>');
534
+ },
535
+ },
536
+ {
537
+ name: 'assertLinuxPackageMetadata: an absent value names its config key and never writes an empty one',
538
+ run: async (ctx) => {
539
+ const { baseConfig, assertLinuxPackageMetadata } = require(path.join(__dirname, '..', '..', '..', 'gulp', 'tasks', 'build-config.js'));
540
+
541
+ const bare = baseConfig({});
542
+ // Nothing to derive, so nothing is emitted: no `maintainer: ''` in the yml.
543
+ ctx.expect('extraMetadata' in bare).toBe(false);
544
+ ctx.expect('maintainer' in bare.linux).toBe(false);
545
+
546
+ await ctx.expect(() => assertLinuxPackageMetadata(bare)).toThrow(/brand\.url/);
547
+ await ctx.expect(() => assertLinuxPackageMetadata(bare)).toThrow(/brand\.contact\.email/);
548
+
549
+ // Only the missing half is named.
550
+ const noEmail = baseConfig({ brand: { name: 'App', url: 'https://app.example.com' } });
551
+ await ctx.expect(() => assertLinuxPackageMetadata(noEmail)).toThrow(/brand\.contact\.email/);
552
+ ctx.expect(noEmail.extraMetadata.homepage).toBe('https://app.example.com');
553
+
554
+ // A consumer override of `linux.maintainer` satisfies it, which is why the
555
+ // assert runs after the electronBuilder merge and not inside baseConfig.
556
+ const overridden = baseConfig({ brand: { name: 'App', url: 'https://app.example.com' } });
557
+ overridden.linux.maintainer = 'App Team <team@app.example.com>';
558
+ assertLinuxPackageMetadata(overridden);
559
+
560
+ // And a build with no deb target has nothing to check.
561
+ const noDeb = baseConfig({});
562
+ noDeb.linux.target = [{ target: 'AppImage', arch: ['x64'] }];
563
+ assertLinuxPackageMetadata(noDeb);
564
+ },
565
+ },
456
566
  ],
457
567
  });
@@ -0,0 +1,245 @@
1
+ // Build-layer tests for what the build bakes as OMEGA_BUILD_JSON
2
+ // ([#894](https://github.com/Omega-JS-Stack/omega/issues/894),
3
+ // [#743](https://github.com/Omega-JS-Stack/omega/issues/743)).
4
+ //
5
+ // One wrapper and one name on every surface: `{ config, package, mode, license,
6
+ // builtAt }`. What differs is WHO reads it, and how it is delivered:
7
+ //
8
+ // main / preload the RESOLVED config, whole, as an esbuild `define` plus the
9
+ // banner. The main process boots from it (main.js reads
10
+ // OMEGA_BUILD_JSON.config in a packaged app, where
11
+ // config/omega.json5 is inside the asar), so autoUpdate,
12
+ // platforms, startup and the rest have to be there. Both are
13
+ // Node, neither is a public surface.
14
+ // renderer the browser-safe subset (@omega.js/config's clientConfig),
15
+ // delivered as the ONE `dist/build.js` every view's shell
16
+ // loads ahead of its bundle: the same file, the same shape and
17
+ // the same load order web and the extension use. A renderer is
18
+ // a public surface, its bundle is readable from DevTools, so
19
+ // the GCP account facts and the brand's provisioning sections
20
+ // must not be in it.
21
+
22
+ const path = require('path');
23
+ const fs = require('fs');
24
+ const os = require('os');
25
+ const vm = require('vm');
26
+ const defineCases = require('../../../vendor/devkit/test/define-cases.js');
27
+
28
+ const FRAMEWORK_ROOT = path.join(__dirname, '..', '..', '..', '..');
29
+ const SRC = path.join(FRAMEWORK_ROOT, 'src');
30
+ const TASK = path.join(SRC, 'gulp', 'tasks', 'bundle.js');
31
+ const PAGE_TEMPLATE = path.join(SRC, 'config', 'page-template.html');
32
+
33
+ // A resolved desktop config with one section from each side of the gate.
34
+ const CONFIG = () => ({
35
+ brand: { id: 'paperloom', name: 'Paperloom' },
36
+ theme: { id: 'classy' },
37
+ cloud: {
38
+ provider: 'firebase',
39
+ config: { apiKey: 'public-web-key', projectId: 'paperloom-prod' },
40
+ billingAccount: '01ABCD-234567-89EFGH',
41
+ },
42
+ app: { productName: 'Paperloom' },
43
+ platforms: { windows: { signing: 'cloud' } },
44
+ certificates: { providers: { apple: { teamId: 'TEAM' } } },
45
+ account: { admins: [{ email: 'root@paperloom.com' }] },
46
+ repo: { provider: 'github', org: 'Paperloom' },
47
+ });
48
+
49
+ const MODE = { environment: 'production', build: true, publish: false, server: false };
50
+
51
+ const INPUT = () => {
52
+ const { buildFacts } = require(TASK);
53
+ const pkg = { name: 'paperloom', version: '3.1.4' };
54
+
55
+ return {
56
+ config: CONFIG(),
57
+ facts: buildFacts({ pkg, mode: MODE, dev: { ports: { backend: 5002 } } }),
58
+ pkg,
59
+ mode: MODE,
60
+ license: { status: 'licensed', payments: 'live', attribution: 'removed' },
61
+ };
62
+ };
63
+
64
+ // Run a `build.js` the way a browser does: a scope whose only global is `self`.
65
+ function loadBuildJs(file) {
66
+ const scope = { self: {} };
67
+ vm.runInNewContext(fs.readFileSync(file, 'utf8'), scope);
68
+ return scope.self.OMEGA_BUILD_JSON;
69
+ }
70
+
71
+ // Stage a minimal desktop project and run the REAL bundle task in it: the task
72
+ // reads its project (package.json / config / dist) from cwd at require time.
73
+ async function inStagedProject(fn) {
74
+ const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'desktop-build-json-'));
75
+ fs.writeFileSync(path.join(tmp, 'package.json'), '{ "name": "staged-desktop", "version": "3.1.4" }');
76
+ fs.mkdirSync(path.join(tmp, 'config'), { recursive: true });
77
+ fs.writeFileSync(path.join(tmp, 'config', 'omega.json5'), `{
78
+ brand: { id: 'staged', name: 'Staged' },
79
+ cloud: { config: { apiKey: 'AIza-staged', projectId: 'demo-staged' }, billingAccount: '01ABCD-234567-89EFGH' },
80
+ targets: { desktop: { type: 'desktop' } },
81
+ }`);
82
+
83
+ const write = (relative, contents) => {
84
+ const full = path.join(tmp, relative);
85
+ fs.mkdirSync(path.dirname(full), { recursive: true });
86
+ fs.writeFileSync(full, contents);
87
+ };
88
+ write('src/main.js', 'globalThis.__main = OMEGA_BUILD_JSON.config.brand.id;\n');
89
+ write('src/preload.js', 'globalThis.__preload = OMEGA_BUILD_JSON.config.brand.id;\n');
90
+ write('src/assets/js/components/main/index.js', [
91
+ 'try { globalThis.__fromWindow = window.OMEGA_BUILD_JSON.config.brand.id; } catch (e) { globalThis.__fromWindow = null; }',
92
+ '',
93
+ ].join('\n'));
94
+
95
+ const oldCwd = process.cwd();
96
+ const flush = () => {
97
+ for (const key of Object.keys(require.cache)) {
98
+ if (key.startsWith(SRC + path.sep)) delete require.cache[key];
99
+ }
100
+ };
101
+
102
+ flush();
103
+ try {
104
+ process.chdir(tmp);
105
+ const task = require(TASK);
106
+ await new Promise((resolve, reject) => task((error) => (error ? reject(error) : resolve())));
107
+ return await fn(tmp);
108
+ } finally {
109
+ process.chdir(oldCwd);
110
+ flush();
111
+ fs.rmSync(tmp, { recursive: true, force: true });
112
+ }
113
+ }
114
+
115
+ module.exports = defineCases({
116
+ type: 'suite',
117
+ layer: 'build',
118
+ description: 'build json bake: one wrapper, one dist/build.js for the renderer (#894, #743)',
119
+ timeout: 180000,
120
+ tests: [
121
+ {
122
+ name: 'the wrapper is { config, package, mode, license, builtAt } on both halves',
123
+ run: (ctx) => {
124
+ const { composeBuildJson, composeClientBuildJson } = require(TASK);
125
+
126
+ const input = INPUT();
127
+ const buildJson = composeBuildJson(input);
128
+ const clientJson = composeClientBuildJson(input);
129
+
130
+ for (const blob of [buildJson, clientJson]) {
131
+ ctx.expect(Object.keys(blob).sort()).toEqual(['builtAt', 'config', 'license', 'mode', 'package']);
132
+ ctx.expect(blob.package.version).toBe('3.1.4');
133
+ ctx.expect(blob.license.status).toBe('licensed');
134
+ ctx.expect(typeof blob.builtAt).toBe('string');
135
+ // The mode is the same THREE keys everywhere (#894): desktop's own
136
+ // `server` verdict stays inside the Manager.
137
+ ctx.expect(Object.keys(blob.mode).sort()).toEqual(['build', 'environment', 'publish']);
138
+ ctx.expect(blob.mode.environment).toBe('production');
139
+ ctx.expect(blob.mode.build).toBe(true);
140
+ }
141
+ },
142
+ },
143
+ {
144
+ name: 'main and preload keep the WHOLE resolved config: the main process boots from it',
145
+ run: (ctx) => {
146
+ const { composeBuildJson } = require(TASK);
147
+
148
+ const buildJson = composeBuildJson(INPUT());
149
+
150
+ ctx.expect(buildJson.config.platforms.windows.signing).toBe('cloud');
151
+ ctx.expect(buildJson.config.certificates.providers.apple.teamId).toBe('TEAM');
152
+ ctx.expect(buildJson.config.cloud.billingAccount).toBe('01ABCD-234567-89EFGH');
153
+ // Plus the build facts main reads off it
154
+ ctx.expect(buildJson.config.version).toBe('3.1.4');
155
+ ctx.expect(buildJson.config.dev.ports.backend).toBe(5002);
156
+ },
157
+ },
158
+ {
159
+ name: 'the RENDERER snapshot carries the client subset, and no credential section at all',
160
+ run: (ctx) => {
161
+ const { composeClientBuildJson } = require(TASK);
162
+
163
+ const clientJson = composeClientBuildJson(INPUT());
164
+
165
+ // What the renderer's @omega.js/client actually boots from
166
+ ctx.expect(clientJson.config.brand.id).toBe('paperloom');
167
+ ctx.expect(clientJson.config.theme.id).toBe('classy');
168
+ ctx.expect(clientJson.config.cloud.config.apiKey).toBe('public-web-key');
169
+ ctx.expect(clientJson.config.app.productName).toBe('Paperloom');
170
+ ctx.expect(clientJson.config.version).toBe('3.1.4');
171
+ ctx.expect(clientJson.config.dev.ports.backend).toBe(5002);
172
+ // The runtime is BAKED (#896): the client's own sniff has no Electron
173
+ // signal to find in a renderer and would answer 'web'.
174
+ ctx.expect(clientJson.config.runtime).toBe('electron');
175
+
176
+ // …and nothing a public surface has no business carrying
177
+ ctx.expect(clientJson.config.platforms).toBeUndefined();
178
+ ctx.expect(clientJson.config.certificates).toBeUndefined();
179
+ ctx.expect(clientJson.config.account).toBeUndefined();
180
+ ctx.expect(clientJson.config.repo).toBeUndefined();
181
+ ctx.expect(clientJson.config.cloud.billingAccount).toBeUndefined();
182
+ },
183
+ },
184
+ {
185
+ // The load order the renderer depends on: the shell's FIRST script is the
186
+ // snapshot, ahead of the view's own bundle. `../../build.js` from
187
+ // dist/views/<view>/ is dist/build.js, and a packaged app resolves it
188
+ // inside the asar exactly as the bundle tag beside it does.
189
+ name: 'the page template loads build.js as its first script, ahead of the view bundle (#743)',
190
+ run: (ctx) => {
191
+ const template = fs.readFileSync(PAGE_TEMPLATE, 'utf8');
192
+ const loader = template.indexOf('<script src="../../build.js');
193
+ const bundle = template.indexOf('<script src="../../assets/js/components/');
194
+
195
+ ctx.expect(loader > -1).toBe(true);
196
+ ctx.expect(bundle > loader).toBe(true);
197
+ // The FIRST script tag in the shell, full stop.
198
+ ctx.expect(template.indexOf('<script')).toBe(loader);
199
+ },
200
+ },
201
+ {
202
+ name: 'a real build writes ONE dist/build.js, and the renderer bundle carries no copy (#743)',
203
+ run: async (ctx) => {
204
+ await inStagedProject(async (tmp) => {
205
+ const loader = path.join(tmp, 'dist', 'build.js');
206
+ ctx.expect(fs.existsSync(loader)).toBe(true);
207
+
208
+ const buildJson = loadBuildJs(loader);
209
+ ctx.expect(buildJson.config.brand.id).toBe('staged');
210
+ ctx.expect(buildJson.config.runtime).toBe('electron');
211
+ ctx.expect(buildJson.config.cloud.config.apiKey).toBe('AIza-staged');
212
+ // The subset gate ran: a provisioning fact never reaches the file a
213
+ // renderer (and anyone with DevTools) reads.
214
+ ctx.expect(buildJson.config.cloud.billingAccount).toBeUndefined();
215
+
216
+ // The renderer bundle carries neither the banner nor the define: with
217
+ // no build.js loaded there is no snapshot in that scope at all.
218
+ const rendererBundle = path.join(tmp, 'dist', 'assets', 'js', 'components', 'main.bundle.js');
219
+ const source = fs.readFileSync(rendererBundle, 'utf8');
220
+ ctx.expect(source.includes('OMEGA_BUILD_JSON={')).toBe(false);
221
+ ctx.expect(source.includes('OMEGA_BUILD_JSON = {')).toBe(false);
222
+
223
+ const scope = { self: {} };
224
+ scope.window = scope.self;
225
+ vm.runInNewContext(source, scope);
226
+ ctx.expect(scope.__fromWindow).toBe(null);
227
+
228
+ // Loaded the way a view loads it, build.js first, it answers.
229
+ const loaded = { self: {} };
230
+ loaded.window = loaded.self;
231
+ vm.runInNewContext(fs.readFileSync(loader, 'utf8'), loaded);
232
+ vm.runInNewContext(source, loaded);
233
+ ctx.expect(loaded.__fromWindow).toBe('staged');
234
+
235
+ // main and preload still carry the WHOLE config as a define: they are
236
+ // Node, and a packaged main boots from it inside the asar.
237
+ const mainBundle = fs.readFileSync(path.join(tmp, 'dist', 'main.bundle.js'), 'utf8');
238
+ ctx.expect(mainBundle.includes('OMEGA_BUILD_JSON')).toBe(true);
239
+ ctx.expect(mainBundle.includes('01ABCD-234567-89EFGH')).toBe(true);
240
+ ctx.expect(fs.existsSync(path.join(tmp, 'dist', 'preload.bundle.js'))).toBe(true);
241
+ });
242
+ },
243
+ },
244
+ ],
245
+ });