@omega.js/desktop 0.52.0 → 0.54.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 (345) hide show
  1. package/README.md +53 -48
  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-run.js +4 -1
  26. package/dist/cli.js +3 -3
  27. package/dist/commands/build.js +2 -2
  28. package/dist/commands/cdp/capture.js +2 -2
  29. package/dist/commands/cdp/client.js +1 -1
  30. package/dist/commands/cdp/quit.js +2 -2
  31. package/dist/commands/cdp/relaunch.js +2 -2
  32. package/dist/commands/cdp/theme.js +1 -1
  33. package/dist/commands/cdp.js +1 -1
  34. package/dist/commands/clean.js +4 -5
  35. package/dist/commands/deploy.js +4 -4
  36. package/dist/commands/dev.js +25 -0
  37. package/dist/commands/finalize-release.js +4 -4
  38. package/dist/commands/launch.js +2 -2
  39. package/dist/commands/lib/deploy-precheck.js +3 -3
  40. package/dist/commands/lib/ensure-target.js +18 -23
  41. package/dist/commands/lib/migrate.js +17 -0
  42. package/dist/commands/logs.js +1 -1
  43. package/dist/commands/package.js +2 -2
  44. package/dist/commands/publish.js +2 -2
  45. package/dist/commands/release.js +4 -4
  46. package/dist/commands/runner.js +11 -11
  47. package/dist/commands/sign-windows.js +5 -5
  48. package/dist/commands/test.js +8 -8
  49. package/dist/commands/update.js +7 -6
  50. package/dist/commands/validate-certs.js +4 -4
  51. package/dist/commands/version.js +3 -3
  52. package/dist/defaults/.github/workflows/build.yml +18 -18
  53. package/dist/defaults/_.gitignore +0 -2
  54. package/dist/defaults/_mas/README.md +3 -3
  55. package/dist/defaults/config/certs/README.md +1 -1
  56. package/dist/defaults/config/omega.json5 +43 -43
  57. package/dist/defaults/docs/README.md +3 -3
  58. package/dist/defaults/gulpfile.js +1 -1
  59. package/dist/defaults/hooks/build/post.js +2 -2
  60. package/dist/defaults/hooks/build/pre.js +2 -2
  61. package/dist/defaults/hooks/deploy/pre.js +1 -1
  62. package/dist/defaults/hooks/notarize/post.js +2 -2
  63. package/dist/defaults/hooks/release/post.js +2 -2
  64. package/dist/defaults/hooks/release/pre.js +2 -2
  65. package/dist/defaults/src/assets/js/components/about/index.js +3 -5
  66. package/dist/defaults/src/assets/js/components/main/index.js +3 -5
  67. package/dist/defaults/src/assets/js/components/settings/index.js +3 -5
  68. package/dist/defaults/src/assets/scss/pages/about.scss +1 -1
  69. package/dist/defaults/src/assets/scss/pages/main.scss +1 -1
  70. package/dist/defaults/src/assets/scss/pages/settings.scss +1 -1
  71. package/dist/defaults/src/integrations/context-menu/index.js +13 -13
  72. package/dist/defaults/src/integrations/menu/index.js +8 -8
  73. package/dist/defaults/src/integrations/tray/index.js +12 -12
  74. package/dist/defaults/src/main.js +5 -7
  75. package/dist/defaults/src/preload.js +4 -6
  76. package/dist/defaults/test/README.md +5 -5
  77. package/dist/defaults/test/_init.js +1 -1
  78. package/dist/gulp/main.js +9 -10
  79. package/dist/gulp/tasks/audit.js +11 -14
  80. package/dist/gulp/tasks/build-config.js +8 -8
  81. package/dist/gulp/tasks/bundle.js +16 -16
  82. package/dist/gulp/tasks/defaults.js +3 -3
  83. package/dist/gulp/tasks/distribute.js +2 -2
  84. package/dist/gulp/tasks/html.js +9 -9
  85. package/dist/gulp/tasks/package-quick.js +3 -3
  86. package/dist/gulp/tasks/package.js +3 -3
  87. package/dist/gulp/tasks/release.js +3 -3
  88. package/dist/gulp/tasks/sass.js +6 -6
  89. package/dist/gulp/tasks/serve.js +4 -4
  90. package/dist/hooks/notarize-artifacts.js +1 -1
  91. package/dist/hooks/notarize.js +1 -1
  92. package/dist/index.js +5 -8
  93. package/dist/lib/_environment-mixin.js +50 -0
  94. package/dist/lib/_lifecycle-mixin.js +45 -0
  95. package/dist/lib/analytics.js +33 -35
  96. package/dist/lib/app-state.js +14 -14
  97. package/dist/lib/auth-flow.js +18 -18
  98. package/dist/lib/auth-persistence.js +12 -12
  99. package/dist/lib/auth.js +421 -0
  100. package/dist/lib/auto-updater.js +49 -49
  101. package/dist/lib/context-menu.js +13 -13
  102. package/dist/lib/context.js +19 -19
  103. package/dist/lib/deep-link.js +34 -34
  104. package/dist/lib/fontawesome.js +5 -5
  105. package/dist/lib/ipc.js +4 -4
  106. package/dist/lib/menu.js +25 -25
  107. package/dist/lib/protocol.js +5 -5
  108. package/dist/lib/remote-config.js +22 -22
  109. package/dist/lib/remote-scripts.js +21 -21
  110. package/dist/lib/restart-manager/index.js +29 -29
  111. package/dist/lib/restart-manager/install.js +1 -1
  112. package/dist/lib/restart-manager/protocol.js +1 -1
  113. package/dist/lib/sign-helpers/exec-with-limit.js +1 -1
  114. package/dist/lib/sign-helpers/sign-events.js +1 -1
  115. package/dist/lib/startup.js +18 -13
  116. package/dist/lib/storage.js +10 -10
  117. package/dist/lib/templating.js +16 -16
  118. package/dist/lib/theme.js +10 -10
  119. package/dist/lib/tray.js +27 -27
  120. package/dist/lib/usage.js +11 -11
  121. package/dist/lib/window-manager.js +26 -26
  122. package/dist/main.js +399 -483
  123. package/dist/preload.js +236 -176
  124. package/dist/renderer.js +417 -391
  125. package/dist/test/fixtures/consumer-app/config/omega.json5 +1 -1
  126. package/dist/test/fixtures/consumer-app/src/assets/js/components/main/index.js +8 -11
  127. package/dist/test/fixtures/consumer-app/src/main.js +5 -7
  128. package/dist/test/fixtures/consumer-app/src/preload.js +2 -2
  129. package/dist/test/harness/boot-entry.js +22 -20
  130. package/dist/test/harness/main-entry.js +31 -30
  131. package/dist/test/harness/renderer-entry.js +5 -5
  132. package/dist/test/harness/renderer-preload.js +137 -141
  133. package/dist/test/index.js +10 -10
  134. package/dist/test/runner.js +2 -2
  135. package/dist/test/runners/boot.js +7 -6
  136. package/dist/test/runners/electron.js +3 -2
  137. package/dist/test/runners/render-event.js +2 -2
  138. package/dist/test/suites/boot/consumer-app-boots.test.js +57 -26
  139. package/dist/test/suites/boot/restart-manager.test.js +2 -2
  140. package/dist/test/suites/boot/storage-bundled.test.js +5 -5
  141. package/dist/test/suites/boot/theme.test.js +13 -13
  142. package/dist/test/suites/build/audit.test.js +21 -8
  143. package/dist/test/suites/build/auth-persistence-resolve.test.js +6 -6
  144. package/dist/test/suites/build/boot-fixture.test.js +2 -2
  145. package/dist/test/suites/build/boot-runner-timeout.test.js +6 -5
  146. package/dist/test/suites/build/brand-scss.test.js +1 -1
  147. package/dist/test/suites/build/build-json-bake.test.js +1 -1
  148. package/dist/test/suites/build/build-workflow-jobs.test.js +2 -2
  149. package/dist/test/suites/build/cli.test.js +30 -2
  150. package/dist/test/suites/build/config-schema.test.js +5 -5
  151. package/dist/test/suites/build/defaults-em-dash.test.js +22 -0
  152. package/dist/test/suites/build/defaults-scaffold.test.js +21 -7
  153. package/dist/test/suites/build/deploy-direct.test.js +7 -5
  154. package/dist/test/suites/build/deploy-dispatch.test.js +2 -1
  155. package/dist/test/suites/build/deploy-hook.test.js +7 -5
  156. package/dist/test/suites/build/dev-verb.test.js +67 -0
  157. package/dist/test/suites/build/ensure-target.test.js +13 -5
  158. package/dist/test/suites/build/env-delivery.test.js +2 -2
  159. package/dist/test/suites/build/esm-only-dependency.test.js +2 -2
  160. package/dist/test/suites/build/exports.test.js +9 -8
  161. package/dist/test/suites/build/get-config.test.js +4 -4
  162. package/dist/test/suites/build/manifest-deps.test.js +1 -1
  163. package/dist/test/suites/build/merge-line-files.test.js +7 -7
  164. package/dist/test/suites/build/migrate.test.js +29 -0
  165. package/dist/test/suites/build/omega-shell.test.js +34 -2
  166. package/dist/test/suites/build/omega.test.js +350 -0
  167. package/dist/test/suites/build/project-scripts-deps.test.js +6 -10
  168. package/dist/test/suites/build/renderer-auth-bridge.test.js +211 -78
  169. package/dist/test/suites/build/runner-env-write.test.js +73 -0
  170. package/dist/test/suites/build/runner.test.js +11 -10
  171. package/dist/test/suites/build/sentry.test.js +2 -2
  172. package/dist/test/suites/build/setup-scripts.test.js +27 -0
  173. package/dist/test/suites/build/sign-windows-e2e.test.js +2 -2
  174. package/dist/test/suites/build/templating.test.js +3 -3
  175. package/dist/test/suites/build/test-stealth.test.js +7 -9
  176. package/dist/test/suites/build/url-helpers.test.js +55 -56
  177. package/dist/test/suites/build/validate-config.test.js +15 -4
  178. package/dist/test/suites/build/verb-logs.test.js +20 -0
  179. package/dist/test/suites/build/wave5-pins.test.js +2 -2
  180. package/dist/test/suites/main/analytics.test.js +59 -59
  181. package/dist/test/suites/main/app-state.test.js +66 -66
  182. package/dist/test/suites/main/auth-flow.test.js +41 -41
  183. package/dist/test/suites/main/auth-persistence.test.js +54 -43
  184. package/dist/test/suites/main/{client-bridge.integration.test.js → auth.integration.test.js} +10 -9
  185. package/dist/test/suites/main/auth.test.js +336 -0
  186. package/dist/test/suites/main/auto-updater.test.js +134 -134
  187. package/dist/test/suites/main/boot-sequence.test.js +37 -49
  188. package/dist/test/suites/main/context-menu.test.js +51 -50
  189. package/dist/test/suites/main/context.test.js +25 -25
  190. package/dist/test/suites/main/deep-link.test.js +74 -74
  191. package/dist/test/suites/main/fontawesome.test.js +27 -27
  192. package/dist/test/suites/main/ipc.test.js +35 -35
  193. package/dist/test/suites/main/menu.test.js +101 -100
  194. package/dist/test/suites/main/protocol.test.js +19 -19
  195. package/dist/test/suites/main/remote-config.test.js +63 -63
  196. package/dist/test/suites/main/remote-scripts.test.js +103 -103
  197. package/dist/test/suites/main/request.test.js +71 -0
  198. package/dist/test/suites/main/restart-manager.test.js +33 -33
  199. package/dist/test/suites/main/startup-paths-and-ua.test.js +4 -4
  200. package/dist/test/suites/main/startup.test.js +26 -26
  201. package/dist/test/suites/main/stealth-window.test.js +1 -1
  202. package/dist/test/suites/main/storage.test.js +25 -25
  203. package/dist/test/suites/main/theme.test.js +34 -34
  204. package/dist/test/suites/main/tray.test.js +79 -79
  205. package/dist/test/suites/main/url-helpers.test.js +133 -133
  206. package/dist/test/suites/main/usage.test.js +25 -25
  207. package/dist/test/suites/main/window-bounds.test.js +27 -27
  208. package/dist/test/suites/main/window-manager.test.js +44 -44
  209. package/dist/test/suites/renderer/analytics-bridge.test.js +5 -5
  210. package/dist/test/suites/renderer/cross-context-helpers.test.js +37 -31
  211. package/dist/test/suites/renderer/round-trip.test.js +3 -3
  212. package/dist/test/suites/renderer/tooltips.test.js +15 -15
  213. package/dist/test/suites/renderer/{window-em-surface.test.js → window-desktop-surface.test.js} +28 -8
  214. package/dist/test/utils/extended-mode-warning.js +1 -1
  215. package/dist/utils/boot-harness.js +56 -0
  216. package/dist/utils/build-pipeline.js +4 -4
  217. package/dist/utils/mode-helpers.js +2 -15
  218. package/dist/utils/runner-env.js +13 -28
  219. package/dist/utils/ship-keys.js +3 -3
  220. package/dist/utils/signing-status.js +51 -0
  221. package/dist/utils/test-events.js +7 -0
  222. package/dist/utils/test-stealth.js +6 -6
  223. package/dist/utils/url-helpers.js +52 -42
  224. package/dist/utils/user-agent.js +44 -0
  225. package/dist/vendor/account/engine.js +3 -3
  226. package/dist/vendor/account/index.js +14 -45
  227. package/dist/vendor/account/resolve-account.js +44 -0
  228. package/dist/vendor/account/schema.js +1 -1
  229. package/dist/vendor/account/user.js +99 -0
  230. package/dist/vendor/config/client-config.js +1 -1
  231. package/dist/vendor/config/company.js +46 -14
  232. package/dist/vendor/config/defaults.js +30 -7
  233. package/dist/vendor/config/edit.js +25 -3
  234. package/dist/vendor/config/env-delivery.js +1 -1
  235. package/dist/vendor/config/env-schema.js +3 -6
  236. package/dist/vendor/config/env.js +34 -22
  237. package/dist/vendor/config/environment.js +11 -30
  238. package/dist/vendor/config/index.js +21 -28
  239. package/dist/vendor/config/load.js +16 -9
  240. package/dist/vendor/config/platforms.js +1 -1
  241. package/dist/vendor/config/repo.js +10 -27
  242. package/dist/vendor/config/schema-client.js +64 -0
  243. package/dist/vendor/config/schema-cloud.js +38 -0
  244. package/dist/vendor/config/schema-manager.js +118 -0
  245. package/dist/vendor/config/schema-overrides.js +68 -0
  246. package/dist/vendor/config/schema.js +104 -160
  247. package/dist/vendor/config/site-global.js +2 -3
  248. package/dist/vendor/config/validate.js +97 -78
  249. package/dist/vendor/config/winback.js +1 -1
  250. package/dist/vendor/devkit/actions-secrets.js +1 -1
  251. package/dist/vendor/devkit/agents-md.js +233 -0
  252. package/dist/vendor/devkit/attach-log-file.js +16 -2
  253. package/dist/vendor/devkit/build-json.js +1 -1
  254. package/dist/vendor/devkit/ci-workflows.js +30 -30
  255. package/dist/vendor/devkit/cli-router.js +16 -11
  256. package/dist/vendor/devkit/defaults-engine.js +15 -51
  257. package/dist/vendor/devkit/deploy-snapshot.js +44 -9
  258. package/dist/vendor/devkit/env-lines.js +183 -0
  259. package/dist/vendor/devkit/local.js +64 -10
  260. package/dist/vendor/devkit/lockfile.js +32 -13
  261. package/dist/vendor/devkit/logger.js +7 -2
  262. package/dist/vendor/devkit/merge-line-files.js +219 -177
  263. package/dist/vendor/devkit/omega-bin.js +208 -111
  264. package/dist/vendor/devkit/preludes/docs-sync.js +52 -0
  265. package/dist/vendor/devkit/preludes/index.js +1 -0
  266. package/dist/vendor/devkit/target-picker.js +45 -0
  267. package/dist/vendor/devkit/test/dashed-files.js +37 -0
  268. package/dist/vendor/devkit/test/run-verb-under-tee.js +71 -0
  269. package/dist/vendor/devkit/test/runner-core.js +6 -6
  270. package/dist/vendor/devkit/update.js +15 -15
  271. package/dist/vendor/devkit/verb-scripts.js +40 -0
  272. package/dist/vendor/devkit/verbs.js +170 -0
  273. package/dist/vendor/monitoring/env.js +2 -2
  274. package/dist/vendor/monitoring/index.js +1 -1
  275. package/dist/vendor/monitoring/main.js +1 -1
  276. package/dist/vendor/monitoring/preload.js +1 -1
  277. package/dist/vendor/monitoring/renderer.js +1 -1
  278. package/package.json +19 -26
  279. package/dist/commands/install.js +0 -37
  280. package/dist/defaults/AGENTS.md +0 -110
  281. package/dist/defaults/CLAUDE.md +0 -1
  282. package/dist/lib/client-bridge.js +0 -374
  283. package/dist/lib/logger.js +0 -4
  284. package/dist/test/suites/build/manager.test.js +0 -213
  285. package/dist/test/suites/main/client-bridge.test.js +0 -262
  286. package/dist/vendor/config/env-retired.js +0 -137
  287. package/dist/vendor/config/retired-keys.js +0 -635
  288. package/docs/analytics.md +0 -140
  289. package/docs/app-state.md +0 -92
  290. package/docs/audit.md +0 -69
  291. package/docs/auto-updater.md +0 -243
  292. package/docs/boot-sequence.md +0 -39
  293. package/docs/build-system.md +0 -169
  294. package/docs/cdp-debugging.md +0 -169
  295. package/docs/client-bridge.md +0 -269
  296. package/docs/common-mistakes.md +0 -21
  297. package/docs/config-schema.md +0 -120
  298. package/docs/context-menu.md +0 -112
  299. package/docs/context.md +0 -81
  300. package/docs/css.md +0 -90
  301. package/docs/deep-link.md +0 -186
  302. package/docs/environment-detection.md +0 -112
  303. package/docs/fontawesome.md +0 -107
  304. package/docs/hooks.md +0 -89
  305. package/docs/icons.md +0 -79
  306. package/docs/index.md +0 -317
  307. package/docs/installer-options.md +0 -165
  308. package/docs/ipc.md +0 -61
  309. package/docs/lib-modules.md +0 -53
  310. package/docs/logging.md +0 -229
  311. package/docs/menu.md +0 -160
  312. package/docs/releasing.md +0 -239
  313. package/docs/remote-config.md +0 -118
  314. package/docs/remote-scripts.md +0 -144
  315. package/docs/restart-manager.md +0 -144
  316. package/docs/runner.md +0 -290
  317. package/docs/sentry.md +0 -97
  318. package/docs/shared/agent-docs.md +0 -89
  319. package/docs/shared/analytics.md +0 -612
  320. package/docs/shared/brands.md +0 -57
  321. package/docs/shared/breaking-changes.md +0 -851
  322. package/docs/shared/config.md +0 -1952
  323. package/docs/shared/deploys.md +0 -341
  324. package/docs/shared/icons.md +0 -219
  325. package/docs/shared/local-dev.md +0 -167
  326. package/docs/shared/logging.md +0 -205
  327. package/docs/shared/monitoring.md +0 -167
  328. package/docs/shared/publishing.md +0 -187
  329. package/docs/shared/rulings.md +0 -34
  330. package/docs/shared/testing.md +0 -147
  331. package/docs/shared/theming.md +0 -629
  332. package/docs/shared/translation.md +0 -333
  333. package/docs/shared/updates.md +0 -61
  334. package/docs/signing.md +0 -293
  335. package/docs/startup.md +0 -142
  336. package/docs/storage.md +0 -59
  337. package/docs/templating.md +0 -101
  338. package/docs/test-boot-layer.md +0 -157
  339. package/docs/test-framework.md +0 -362
  340. package/docs/themes.md +0 -149
  341. package/docs/tooltips.md +0 -99
  342. package/docs/tray.md +0 -164
  343. package/docs/usage.md +0 -58
  344. package/docs/verts.md +0 -62
  345. package/docs/windows.md +0 -149
@@ -1,120 +0,0 @@
1
- # Config schema
2
-
3
- @omega.js/desktop validates `config/omega.json5` against the canonical OMEGA schema in **`@omega.js/config`** (vendored into `dist/vendor/config/` at prepare time; also exposed to consumers as `require('@omega.js/desktop/config')`). The shared schema covers the cross-framework sections (brand, cloud, analytics, payment, monitoring, connections, theme, targets); the desktop-specific refinements (app.category, the `platforms` shipping declaration, platforms.windows.signing.strategy, startup.mode, restartManager.*, …) live in the same package's `TARGET_SCHEMAS.desktop` and apply when validating with `{ target: 'desktop' }`. Validation always runs against the RESOLVED config: `targets.desktop` contents land at the top level (see the monorepo's `docs/shared/config.md` for the format).
4
-
5
- Validation runs in two places:
6
-
7
- 1. **`Manager.initialize()` (boot)** — hard-fails the app at boot if any required field is missing or any present field is invalid. So a misconfigured app never reaches the "white window of confusion" phase — it tells you exactly which field is broken.
8
- 2. **`gulp audit` (build)**: same schema, plus build-pipeline-specific extras (file-existence for icons, an addressable releases repo in publish mode, etc.).
9
-
10
- ## Schema entry shape
11
-
12
- ```js
13
- {
14
- path: 'brand.id', // dot-path into the config
15
- type: 'string' | 'boolean' | 'number' | 'array' | 'object',
16
- required: true | false | (config) => bool,
17
- match: /^[a-z][a-z0-9+\-.]*$/, // string-value regex
18
- enum: ['normal', 'hidden'], // value-must-be-in-this-list
19
- description: 'Used for the deep-link scheme + default appId.',
20
- }
21
- ```
22
-
23
- ## The `required` flag
24
-
25
- @omega.js/desktop keeps validation simple: **`required` is either `true`, `false`, or a function**.
26
-
27
- ```js
28
- required: true // hard-fail if missing
29
- required: false // OK to omit (but if present, match/enum/type still run)
30
- required: (cfg) => bool // conditional — predicate gets the full config
31
- ```
32
-
33
- The function form is for "this field is mandatory only when another part of config is set." Illustrative shape (no current entry uses it — every present-day field is `true` or `false`):
34
-
35
- ```js
36
- {
37
- path: 'analytics.providers.google.id',
38
- required: (cfg) => Boolean(cfg?.analytics?.providers?.google?.secret), // id mandatory only when a secret is configured
39
- match: /^G-[A-Z0-9]+$/,
40
- }
41
- ```
42
-
43
- This is identical strictness in dev and production. There's no separate `'publish-only'` tier — if a field truly matters only for builds, validate it inside `gulp/audit.js` (next to `fileMustExist` calls for icons, etc.) rather than the schema.
44
-
45
- ## How `match` / `enum` / `type` interact with absence
46
-
47
- They **only run when the value is present**. A missing field with `required: false` is silent. A missing field with `required: true` fires the "missing" error and nothing else — so consumers don't see a confusing flood of "missing AND wrong type AND doesn't match" for the same field.
48
-
49
- ## Presence-driven feature flags (@omega.js/backend convention)
50
-
51
- A non-empty credential value enables a feature — there is no separate `enabled: true/false` flag for credential-gated features:
52
-
53
- | Feature | Enable signal | Disable signal |
54
- |---|---|---|
55
- | Sentry | `monitoring.providers.sentry.dsn = 'https://...'` | `monitoring.providers.sentry.dsn = ''` |
56
- | GA4 analytics | `analytics.providers.google.id = 'G-XXXXX'` | `analytics.providers.google.id = ''` |
57
- | Firebase Auth (renderer) | `cloud.config.projectId = '...'` (etc.) | empty `cloud.config` |
58
-
59
- **Exceptions where an explicit `enabled` flag exists:** `remoteConfig.enabled`, `autoUpdate.enabled`, `releases.enabled`, `restartManager.enabled`, `startup.openAtLogin.enabled`. (`platforms.linux.snap.enabled` was one until [#867](https://github.com/Omega-JS-Stack/omega/issues/867): the snap is a declared FORMAT now, so its presence is the switch and `platforms.linux.formats.snap: false` is the off.) These toggle BEHAVIOR, not credentials: a fork can keep the brand's `repo.org` and still want releases off, for example.
60
-
61
- ## Adding a new field
62
-
63
- When you add a new config knob anywhere in @omega.js/desktop:
64
-
65
- 1. Add an entry to `TARGET_SCHEMAS.desktop` in `@omega.js/config` (`packages/config/src/schema.js` in the Omega monorepo) — or to `SHARED_SCHEMA` if the field is genuinely cross-framework.
66
- 2. If it has a default, set it in [`src/defaults/config/omega.json5`](../src/defaults/config/omega.json5) (under `targets.desktop` for desktop-scoped fields).
67
- 3. That's it. No separate validation logic to add elsewhere — the schema entry is the validation.
68
-
69
- ## What's NOT in the schema
70
-
71
- These checks live in [`gulp/tasks/audit.js`](../src/gulp/tasks/audit.js) instead, because they depend on build-pipeline state rather than the config shape:
72
-
73
- - **`src/main.js` / `src/preload.js` existence** — the bundle task skips them with a warning but the schema doesn't know about consumer entry points.
74
- - **`brand.images.icon` file existence** — only enforced when packaging (`isBuildMode()` / `isPublishMode()`); dev runs with the default Electron icon.
75
- - **An addressable releases repo** (`repo.org` + `brand.id`), only enforced in publish mode.
76
-
77
- These are kept in `audit.js` so the schema stays a pure description of the config shape, callable from any context without dragging in build state.
78
-
79
- ## Examples
80
-
81
- Required field missing:
82
-
83
- ```
84
- @omega.js/desktop: config validation failed — fix the following in config/omega.json5:
85
- 1. config.brand.id is required — URL-scheme-safe slug. Used as deep-link scheme + default appId. Must be lowercase, start with a letter, alnum/+/-/.
86
- ```
87
-
88
- Field present but invalid:
89
-
90
- ```
91
- 1. config.startup.mode "tray-only" is not allowed — must be one of [normal, hidden]
92
- 2. config.brand.id "My App!" does not match expected pattern /^[a-z][a-z0-9+\-.]*$/ — URL-scheme-safe slug. Used as deep-link scheme + default appId. Must be lowercase, start with a letter, alnum/+/-/.
93
- ```
94
-
95
- Errors are numbered so you can fix everything in one pass instead of fix-rebuild-fix-rebuild.
96
-
97
- ## Adding payment fields (@omega.js/backend-shaped)
98
-
99
- @omega.js/desktop's schema mirrors [@omega.js/backend's `manager-config.example.json`](https://github.com/itw-creative-works/backend-manager) shape for payment so the same product catalog reads identically on backend, web, and desktop:
100
-
101
- ```js
102
- {
103
- payment: {
104
- providers: {
105
- stripe: { publishableKey: 'pk_live_...' }, // schema: match /^pk_(test|live)_/
106
- paypal: { clientId: '...' },
107
- },
108
- products: [
109
- { id: 'basic', name: 'Basic', type: 'subscription', limits: { credits: 100 } },
110
- ],
111
- },
112
- }
113
- ```
114
-
115
- The schema only enforces shape for the few well-defined publishable keys — the product catalog itself is freeform so @omega.js/backend can extend it without @omega.js/desktop caring.
116
-
117
- ## Source
118
-
119
- - Schema definitions + validator engine: `@omega.js/config` (`packages/config/src/{schema,validate}.js` in the Omega monorepo; vendored copy at `dist/vendor/config/`)
120
- - @omega.js/desktop integration tests: [`src/test/suites/build/validate-config.test.js`](../src/test/suites/build/validate-config.test.js)
@@ -1,112 +0,0 @@
1
- # Context Menu (Right-Click)
2
-
3
- File-based context menu. Unlike tray and application menu (called once at boot), the context-menu definition is called **every time the user right-clicks** — so it gets fresh `params` each time and can vary the menu by selection.
4
-
5
- ## Config
6
-
7
- No config block. Path is conventional: `src/integrations/context-menu/index.js`. To opt out, call `manager.contextMenu.disable()` from your main entry — after that, right-click events are silently swallowed.
8
-
9
- ## Definition file
10
-
11
- ```js
12
- // src/integrations/context-menu/index.js
13
- module.exports = ({ manager, menu, params, webContents }) => {
14
- // Easiest: start from @omega.js/desktop's defaults, then customize per event.
15
- menu.useDefaults();
16
-
17
- // Add a "Search Google" entry when text is selected:
18
- if (params.selectionText) {
19
- menu.insertAfter('copy', {
20
- id: 'search-google',
21
- label: `Search "${params.selectionText.slice(0, 20)}"`,
22
- click: () => require('electron').shell.openExternal(
23
- `https://google.com/search?q=${encodeURIComponent(params.selectionText)}`,
24
- ),
25
- });
26
- }
27
-
28
- // Hide the dev-tools entries even in development:
29
- menu.remove('toggle-devtools');
30
- };
31
- ```
32
-
33
- Calling no `menu.*` methods (or `menu.clear()` after `useDefaults()` with nothing added) **suppresses the popup** entirely.
34
-
35
- ## Builder API (per event)
36
-
37
- ```js
38
- menu.item(descriptor)
39
- menu.separator()
40
- menu.submenu(label, items)
41
- menu.useDefaults() // populate with @omega.js/desktop's defaults based on params
42
- menu.clear() // wipe items added so far this event
43
- ```
44
-
45
- ## Id-path API (per event)
46
-
47
- Same shape across menu / tray / context-menu. Available **inside the definition fn** on the `menu` builder. Operates on the items being built for the current right-click event:
48
-
49
- ```js
50
- .find(idPath)
51
- .has(idPath)
52
- .update(idPath, patch)
53
- .remove(idPath)
54
- .enable(idPath, bool = true)
55
- .show(idPath, bool = true)
56
- .hide(idPath)
57
- .insertBefore(idPath, item)
58
- .insertAfter(idPath, item)
59
- .appendTo(idPath, item)
60
- ```
61
-
62
- Context-menu ids are **flat** — no `context/` prefix needed (the lib namespace is implicit). Submenus you build with `menu.submenu(...)` are addressable as `parent/child` paths via the resolver.
63
-
64
- (Runtime-on-`manager.contextMenu` mutators don't apply here — items are rebuilt every event. Mutate inside the definition fn instead.)
65
-
66
- ## Default template ids
67
-
68
- @omega.js/desktop's `useDefaults()` populates items based on `params`. Every default item carries an id you can target:
69
-
70
- | ID | When it appears |
71
- |---|---|
72
- | `undo`, `redo` | `params.editFlags.canUndo` / `canRedo` |
73
- | `cut`, `copy`, `paste`, `paste-and-match-style`, `select-all` | `params.isEditable` |
74
- | `copy` | `params.selectionText` (read-only) |
75
- | `open-link`, `copy-link` | `params.linkURL` |
76
- | `reload` | always |
77
- | `inspect`, `toggle-devtools` | `manager.isDevelopment()` only |
78
-
79
- ## Definition fn arguments
80
-
81
- | Arg | Description |
82
- |---|---|
83
- | `manager` | The running @omega.js/desktop Manager |
84
- | `menu` | Per-event builder + id-path API |
85
- | `params` | Electron's [`ContextMenuParams`](https://www.electronjs.org/docs/latest/api/web-contents#event-context-menu) — `selectionText`, `isEditable`, `linkURL`, `srcURL`, `mediaType`, `editFlags`, `x`, `y`, etc. |
86
- | `webContents` | The `webContents` that fired the event |
87
-
88
- ## Auto-attach
89
-
90
- Every window created via `manager.windows.createNamed()` is automatically wired up with the context-menu listener. Idempotent per `webContents` (uses a `WeakSet`). For windows you create directly with `new BrowserWindow()`, call:
91
-
92
- ```js
93
- manager.contextMenu.attach(win.webContents);
94
- ```
95
-
96
- ## Runtime API on `manager.contextMenu`
97
-
98
- ```js
99
- manager.contextMenu.define(fn) // replace the definition at runtime
100
- manager.contextMenu.disable() // ignore future right-click events (idempotent)
101
- manager.contextMenu.attach(webContents) // manual attach
102
- manager.contextMenu.buildItems(params, wc) // run the definition without popping a menu (useful for tests)
103
- manager.contextMenu.hasCustomDefinition() // false → using the built-in default fn
104
- ```
105
-
106
- ## Default fn
107
-
108
- Without a consumer file, @omega.js/desktop uses a built-in fallback that just calls `useDefaults()` — sensible undo/redo/cut/copy/paste/link/reload/inspect baseline. Same behavior as the default scaffold.
109
-
110
- ## Default scaffold
111
-
112
- The scaffold every verb runs ships `src/integrations/context-menu/index.js` calling `menu.useDefaults()` plus commented-out examples covering insertAfter, remove, hide, enable, and building from scratch.
package/docs/context.md DELETED
@@ -1,81 +0,0 @@
1
- # Context
2
-
3
- Runtime info block. Mirrors @omega.js/backend's `assistant.request.{geolocation,client}` shape so @omega.js/desktop apps + sister projects (@omega.js/backend, UJM, @omega.js/client) all reference the same property paths when reading user info.
4
-
5
- Populated asynchronously during `manager.initialize()`.
6
-
7
- ## Shape
8
-
9
- ```js
10
- manager.context.geolocation = {
11
- ip: '203.0.113.42', // async-fetched via ipify
12
- country: null, // future enhancement
13
- region: null,
14
- city: null,
15
- };
16
-
17
- manager.context.client = {
18
- userAgent: 'Mozilla/5.0 ...', // app.userAgentFallback
19
- locale: 'en-US', // app.getLocale()
20
- platform: 'darwin', // os.platform()
21
- arch: 'arm64', // os.arch()
22
- mobile: false, // always false on @omega.js/desktop (desktop framework)
23
- };
24
-
25
- manager.context.session = {
26
- id: '<uuid>', // fresh per launch (crypto.randomUUID)
27
- startTime: '2026-05-08T...', // ISO at boot
28
- deviceId: '<uuid or MAC>', // stable per-machine
29
- };
30
-
31
- manager.context.app = {
32
- version: '1.2.3', // manager.getVersion()
33
- environment: 'production', // manager.getEnvironment()
34
- isPackaged: true, // app.isPackaged
35
- };
36
- ```
37
-
38
- ## Device ID resolution
39
-
40
- The walk itself is the shared one — `@omega.js/analytics`' `deriveDeviceId({ get, set, seed })`, the same call `@omega.js/client` makes on a page ([#396](https://github.com/Omega-JS-Stack/omega/issues/396)). What this module supplies is desktop's own world: electron-store, and the MAC as the seed. Order:
41
-
42
- 1. **Storage** — already persisted from a prior boot. Wins so we're stable across NIC swaps / VPN changes.
43
- 2. **First non-internal MAC** from `os.networkInterfaces()`, the injected seed. Stable on a stable rig, and it hands a reinstalled app the id it had before its storage was wiped.
44
- 3. **A generated UUID** — the shared derivation's floor. Persisted on first launch.
45
-
46
- Once resolved on first launch it never changes. This is the input to `analytics._clientId = uuidv5(deviceId, projectIdNamespace)`, and it is desktop's alone: a browser on the same machine derives its own id from its own localStorage.
47
-
48
- ## Geolocation
49
-
50
- `geolocation.ip` is fetched in the background via `https://api.ipify.org?format=json`. Cached to `storage.context.geolocation` so the next launch has last-known-good values even if offline. The `country/region/city` fields are reserved for a future enrichment provider.
51
-
52
- Failure mode: a failed ipify fetch leaves the previous cached value untouched. The app keeps working with last-known-good.
53
-
54
- ## API
55
-
56
- ```js
57
- manager.context.geolocation.ip // direct read
58
- manager.context.session.deviceId // direct read
59
- const snap = manager.context.toJSON(); // structured-cloneable snapshot
60
- ```
61
-
62
- Renderer:
63
-
64
- ```js
65
- const snap = await window.desktop.context.get();
66
- console.log(snap.session.deviceId);
67
- ```
68
-
69
- ## Why the @omega.js/backend shape
70
-
71
- Sister projects (@omega.js/backend, @omega.js/client, UJM) all reference paths like `assistant.request.geolocation.country` and `assistant.request.client.userAgent`. @omega.js/desktop matches the leaf names so consumer code can write logic that works across all four runtimes:
72
-
73
- ```js
74
- const country = manager.context.geolocation.country
75
- || assistant.request.geolocation.country // @omega.js/backend
76
- || omega.context.geolocation.country;
77
- ```
78
-
79
- ## Tests
80
-
81
- - `src/test/suites/main/context.test.js` — session shape, deviceId stability across re-init, the injected seed + persistence of the shared derivation, client info, IPC handler, JSON-roundtrippability.
package/docs/css.md DELETED
@@ -1,90 +0,0 @@
1
- # CSS Architecture
2
-
3
- @omega.js/desktop styles are SCSS, compiled by the pipeline's `sass` task into per-window bundles on top of a shared base. Bootstrap 5 (via @omega.js/desktop's classy theme) is the foundation — consumers restyle Bootstrap, they don't replace it.
4
-
5
- ## Main entry
6
-
7
- `<consumer>/src/assets/scss/main.scss` — loaded by EVERY window. It configures the theme via `@use ... with (...)`:
8
-
9
- ```scss
10
- // Generated from `brand.color` by the sass task (#912).
11
- @use 'brand';
12
-
13
- @use 'omega-desktop' as * with (
14
- $primary: brand.$primary,
15
- $dark: #1a1a2e,
16
- $classy-bg-dark: #0f0f1a,
17
- $classy-bg-dark-secondary: #161628,
18
- $classy-bg-dark-tertiary: #1e1e38,
19
- );
20
-
21
- // The runtime --omega-accent ramp, after the framework import.
22
- @include brand.ramp;
23
-
24
- // Custom global styles below
25
- ```
26
-
27
- Compiles to `dist/assets/css/main.bundle.css` (Bootstrap + classy theme + your globals).
28
-
29
- ## Per-window styles
30
-
31
- `src/assets/scss/pages/<window>.scss` → `dist/assets/css/components/<window>.bundle.css`, loaded ONLY on that window's page. One file per window (`main.scss`, `settings.scss`, …) — page-specific chrome lives here, shared styles live in the main entry.
32
-
33
- ## Theme integration
34
-
35
- The `@use 'omega-desktop'` entry pulls in Bootstrap 5 + @omega.js/desktop's classy theme. Appearance (`system`/`light`/`dark`) defaults from `config.theme.appearance` and is applied + kept live on `<html data-bs-theme>` by `manager.theme` (OS-following, runtime-switchable, persisted override, see [themes.md](themes.md)). Theme variables (`$primary`, `$dark`, `$classy-bg-*`, typography, borders) are overridable via the `with (...)` block, and `$primary` arrives from `brand.color` through the generated `dist/assets/scss/_brand.scss` unless a literal replaces `brand.$primary` ([#912](https://github.com/Omega-JS-Stack/omega/issues/912)). See [themes.md](themes.md) for the full variable reference.
36
-
37
- ## Icon presentation
38
-
39
- Icon CSS is ONE sheet for every omega target, vendored from @omega.js/web at prepare (package.json `omega.vendorAssets` → `dist/assets/css/core/_fontawesome.scss`) and loaded by the `omega-desktop` entry. It ships the square glyph-centered box every rendered `<i>` gets, the `fa-2xs`…`fa-6xl` size scale, and the `fa-spin` / `fa-bounce` / `fa-beat` utilities (each parked under `prefers-reduced-motion`). Nothing to import and nothing to hand-fix. See [shared/icons.md](shared/icons.md).
40
-
41
- ## App shell
42
-
43
- Dashboard/admin windows use the `.omega-shell` layout — a sidebar + topbar + main grid with a collapsible desktop rail and a mobile drawer. Two layers ship, both vendored from @omega.js/web at prepare: the MECHANICS (`dist/assets/css/shell/_index.scss` — the grid, region geometry, states, and the `--omega-shell-*` tokens, loaded by the `omega-desktop` entry before the theme) and the theme's SKIN (`dist/assets/themes/<theme-id>/css/layout/_shell.scss`, layered over it). Nothing to import — `@use 'omega-desktop'` gets both.
44
-
45
- Emit this markup in the window's HTML:
46
-
47
- ```html
48
- <div class="omega-shell" data-omega-shell>
49
- <aside class="omega-shell__sidebar" id="app-sidebar">
50
- <!-- pinned head (brand, selector) sits here, outside the scroll region -->
51
- <div class="omega-shell__sidebar-scroll">
52
- <!-- nav scrolls HERE (the rail itself clips nothing, so popovers can
53
- escape); text that should hide in the collapsed rail wears
54
- .omega-shell__label -->
55
- </div>
56
- </aside>
57
-
58
- <header class="omega-shell__topbar">
59
- <div class="omega-shell__topbar-start">
60
- <button data-shell-toggle="drawer" aria-expanded="false" aria-controls="app-sidebar">☰</button>
61
- <button data-shell-toggle="collapse" aria-expanded="true" aria-controls="app-sidebar">⇤</button>
62
- </div>
63
- <div class="omega-shell__topbar-end"><!-- account menu, actions --></div>
64
- </header>
65
-
66
- <main class="omega-shell__main"><!-- page content --></main>
67
-
68
- <div class="omega-shell__scrim" data-shell-dismiss></div>
69
- </div>
70
- ```
71
-
72
- Wire the behavior from a renderer component (`src/assets/js/components/<window>/index.js`) — `__main_assets__` is the build alias for @omega.js/desktop's vendored core assets:
73
-
74
- ```js
75
- import appShell from '__main_assets__/js/core/app-shell.js';
76
-
77
- appShell();
78
- ```
79
-
80
- The module is delegated and declarative: `[data-shell-toggle="collapse"]` toggles the rail, `[data-shell-toggle="drawer"]` toggles the mobile drawer, `[data-shell-dismiss]` (and Escape) closes it. It stamps the state on the container — `data-shell-collapsed="true"` (persisted under the `shell.collapsed` storage key) and `data-shell-open="true"` — which is what the CSS keys off; the API is also registered at `omega._library.appShell`. Add `.omega-shell--locked` when `main` should never scroll (the page manages its own interior scroll).
81
-
82
- ## Bootstrap-first convention
83
-
84
- NEVER create custom classes for things Bootstrap already provides — use `btn`, `card`, `form-*`, `d-flex`, `gap-*`, `rounded-*`, `bg-body-*`, `text-*` natively, and use `bg-body` variants (not `bg-light`/`bg-dark`) so dark mode adapts. Theme SCSS overrides how Bootstrap components LOOK; custom CSS is only for genuinely novel components with no Bootstrap equivalent. Same rule in BXM and UJM.
85
-
86
- ## See also
87
-
88
- - [themes.md](themes.md) — theme variables, appearance modes
89
- - [build-system.md](build-system.md) — where the `sass` task runs in the pipeline
90
- - [templating.md](templating.md) — the page template that loads the bundles
package/docs/deep-link.md DELETED
@@ -1,186 +0,0 @@
1
- # Deep Links
2
-
3
- Cross-platform deep-link handling that's simple to use and hard to get wrong. @omega.js/desktop owns all the OS plumbing (single-instance lock, scheme registration, argv parsing, second-instance routing, focus-on-warm-start) and gives you one unified event API regardless of how the link arrived.
4
-
5
- ## Config
6
-
7
- ```jsonc
8
- "deepLinks": {
9
- "schemes": ["myapp"] // urls like myapp://...
10
- }
11
- ```
12
-
13
- @omega.js/desktop registers each scheme with the OS via `app.setAsDefaultProtocolClient` so the system routes matching URLs to your app. Registration is **production-only** — dev/test runs never claim OS-wide protocol handlers for unpackaged Electron binaries. In dev, exercise your handlers with `manager.deepLink.dispatch(url)` instead.
14
-
15
- ## How it works (so you don't have to think about it)
16
-
17
- | Platform | Cold-start (app not running) | Warm-start (app already running) |
18
- |---|---|---|
19
- | **macOS** | `app.on('open-url')` — queued before `whenReady`, drained after | `app.on('open-url')` |
20
- | **Windows** | URL appended to `process.argv`; @omega.js/desktop extracts it | OS forwards argv to the existing instance via `app.on('second-instance')`; @omega.js/desktop reads the duplicate's real argv from that event's `additionalData` |
21
- | **Linux** | Same as Windows | Same as Windows |
22
-
23
- @omega.js/desktop handles all of these and dispatches them through the same `manager.deepLink.on()` event registry. Your code looks identical regardless of platform or cold/warm start. Single-instance lock is acquired automatically (via `lib/protocol.js`); duplicate launches exit cleanly and forward their argv to the original instance.
24
-
25
- ## Public API
26
-
27
- ```js
28
- manager.deepLink.on(pattern, handler) // register a handler. Returns unsubscribe fn.
29
- manager.deepLink.off(pattern, handler)
30
- manager.deepLink.dispatch(url) // manually fire (testing, custom triggers)
31
- manager.deepLink.getColdStartUrl() // the URL the app was launched with, or null
32
- ```
33
-
34
- ## Patterns
35
-
36
- ```
37
- 'auth/token' // exact match
38
- 'user/profile/:id' // named param → ctx.params.id
39
- 'org/:slug/repo/:repo' // multiple params
40
- '*' // wildcard catch-all (only fires when no concrete handler matched)
41
- ```
42
-
43
- ## Handler signature
44
-
45
- ```js
46
- manager.deepLink.on('user/profile/:id', (ctx) => {
47
- ctx.url // 'myapp://user/profile/42?ref=tray'
48
- ctx.scheme // 'myapp'
49
- ctx.route // 'user/profile/42'
50
- ctx.pattern // 'user/profile/:id'
51
- ctx.params // { id: '42' }
52
- ctx.query // { ref: 'tray' }
53
- ctx.source // 'cold-start' | 'warm-start' | 'manual'
54
- ctx.argv // process.argv (cold) or the duplicate's real argv (warm, from additionalData)
55
- ctx.cwd // working directory (the duplicate's on warm-start)
56
- ctx.handled // mutable: set true to suppress remaining handlers (including built-ins)
57
- });
58
- ```
59
-
60
- ## Built-in routes
61
-
62
- @omega.js/desktop ships with handlers for common patterns. They run AFTER consumer handlers, so you can shadow any of them by registering your own handler at the same pattern.
63
-
64
- | Route | Default behavior |
65
- |---|---|
66
- | `auth/token` | Calls `manager.omega.handleAuthToken(query.authToken)` — the receiving end of the `manager.openAuthFlow()` sign-in round-trip. MODERN shape only (`?authToken=`, what the website's token page sends); legacy-app formats (`?token=`, `?payload=`) are UJM's concern and are ignored. In production the URL arrives via the OS scheme; in dev/test `lib/auth-flow.js`'s loopback listener dispatches the same URL manually (the scheme isn't OS-registered in dev — protocol.js registers only in production, and macOS can't runtime-register unlisted schemes at all) |
67
- | `app/show` | `manager.windows.show(query.window || 'main')` |
68
- | `app/quit` | `app.quit()` |
69
-
70
- ### Overriding a built-in
71
-
72
- ```js
73
- // Replace the built-in app/show with custom logic.
74
- manager.deepLink.on('app/show', (ctx) => {
75
- if (ctx.query.window === 'admin' && !manager.appState.isAdminUser()) {
76
- showError('not authorized');
77
- ctx.handled = true; // suppress built-in
78
- return;
79
- }
80
- // Otherwise let the built-in run normally.
81
- });
82
- ```
83
-
84
- ## Resolution order
85
-
86
- For each incoming URL, @omega.js/desktop walks handlers in this order:
87
-
88
- 1. **Consumer concrete handlers** (any non-wildcard pattern you registered with `.on()`)
89
- 2. **Built-in concrete handlers** (`auth/token`, `app/show`, `app/quit`)
90
- 3. **Wildcard handlers** (`'*'`) — only if NO concrete handler matched
91
-
92
- Setting `ctx.handled = true` in any handler stops the cascade. Within a single tier, handlers fire in registration order. Errors in a handler are caught and logged — they don't stop subsequent handlers.
93
-
94
- ## Common patterns
95
-
96
- ### Route to a window + send IPC
97
-
98
- ```js
99
- manager.deepLink.on('user/profile/:id', (ctx) => {
100
- manager.windows.show('main');
101
- manager.windows.get('main').webContents.send('navigate', {
102
- to: `/profile/${ctx.params.id}`,
103
- });
104
- });
105
- ```
106
-
107
- ### Catch-all logger
108
-
109
- ```js
110
- manager.deepLink.on('*', (ctx) => {
111
- manager.logger.warn(`Unrouted deep link: ${ctx.url}`);
112
- });
113
- ```
114
-
115
- ### Cold-start branching
116
-
117
- ```js
118
- const coldUrl = manager.deepLink.getColdStartUrl();
119
- if (coldUrl) {
120
- manager.logger.log(`Launched from deep link: ${coldUrl}`);
121
- // appState.launchedFromDeepLink() is also set automatically
122
- }
123
- ```
124
-
125
- ### Manually dispatching (e.g. from a tray click)
126
-
127
- ```js
128
- tray.item({
129
- label: 'Open Profile',
130
- click: () => manager.deepLink.dispatch('myapp://user/profile/me'),
131
- });
132
- ```
133
-
134
- ## Boot queueing
135
-
136
- Every dispatch is held until `manager.initialize()` completes (main.js calls `deepLink.markManagerReady()` as its last step). A cold-start `auth/token` link — the OS launching the app from the sign-in round trip — therefore never fires before client-bridge has Firebase up; it queues and drains the moment the manager is ready. Warm-start dispatches on a running app pass straight through.
137
-
138
- ## Single-instance behavior
139
-
140
- @omega.js/desktop acquires the OS-level single-instance lock during `protocol.initialize()` (boot step 5, before deep-link inits). If another copy of the app is already running:
141
-
142
- 1. The new instance loses the lock.
143
- 2. The OS forwards its argv to the original instance.
144
- 3. The new instance's `Manager.initialize()` returns early (after `protocol.hasSingleInstanceLock() === false`).
145
- 4. The original instance's `app.on('second-instance')` fires with the Chromium-processed argv as its second argument AND the duplicate's real argv as its fourth, `additionalData` (@omega.js/desktop passes `{ argv, cwd }` to `app.requestSingleInstanceLock()` for you).
146
- 5. @omega.js/desktop extracts the deep-link URL from that argv and dispatches normally — but as `source: 'warm-start'`.
147
- 6. @omega.js/desktop also focuses the existing main window automatically (consumer can override by registering a route handler that does its own thing).
148
-
149
- Reading the duplicate's own flags (a CLI-shaped app, a `--open <file>` handler) means reading that fourth argument:
150
-
151
- ```js
152
- app.on('second-instance', (event, argv, cwd, additionalData) => additionalData.argv);
153
- ```
154
-
155
- Never parse the event's own `argv` for flags: Chromium re-serializes it (switches first, Chromium's own switches spliced in, the values detached at the end), so a `--message two` launch arrives with the value detached from the flag.
156
-
157
- ## Linking with `appState`
158
-
159
- When a deep link is detected at cold-start, @omega.js/desktop calls `manager.appState.setLaunchedFromDeepLink(true)`. This means:
160
-
161
- ```js
162
- if (manager.appState.launchedFromDeepLink()) {
163
- // user clicked a link to launch the app — handle differently than a tray click or login launch
164
- }
165
- ```
166
-
167
- Combine with `appState.isFirstLaunch()` to detect "first launch via deep link" (e.g. from an onboarding flow on your website).
168
-
169
- ## Testing
170
-
171
- The dispatch pipeline is unit-testable without actually triggering an OS event:
172
-
173
- ```js
174
- manager.deepLink.dispatch('myapp://auth/token?token=test');
175
- // Fires source='manual'. Handlers run synchronously.
176
- ```
177
-
178
- See `src/test/suites/main/deep-link.test.js` for the full coverage.
179
-
180
- ## Implementation notes
181
-
182
- - `lib/protocol.js` owns the single-instance lock + scheme registration; `lib/deep-link.js` owns the dispatch pipeline. They're separate modules but tightly coupled.
183
- - OS scheme registration is gated on `manager.isProduction()` (in `lib/protocol.js`) — unpackaged dev/test binaries are never registered as system protocol handlers (unconditional registration also intermittently triggered macOS Launch Services `-600` dialogs during test runs).
184
- - On Windows/Linux, scheme registration uses `app.setAsDefaultProtocolClient(scheme, process.execPath, [process.cwd()])` so `app.exe scheme://...` style invocations route argv correctly.
185
- - macOS open-url events that arrive before `whenReady` are queued internally and drained on `deepLink.initialize()`.
186
- - Argv extraction walks backward from the end of argv (where the URL typically sits) and matches against registered schemes.