@omega.js/desktop 0.1.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 (818) hide show
  1. package/LICENSE +98 -0
  2. package/README.md +166 -0
  3. package/bin/omega +2 -0
  4. package/bin/omega-desktop +2 -0
  5. package/bin/omg +2 -0
  6. package/dist/assets/css/core/_fontawesome.scss +146 -0
  7. package/dist/assets/css/core/_initialize.scss +38 -0
  8. package/dist/assets/css/omega-desktop.scss +39 -0
  9. package/dist/assets/css/shell/_index.scss +207 -0
  10. package/dist/assets/css/tokens/_index.scss +166 -0
  11. package/dist/assets/js/bootstrap.bundle.js +1 -0
  12. package/dist/assets/js/core/app-shell.js +136 -0
  13. package/dist/assets/themes/_template/README.md +115 -0
  14. package/dist/assets/themes/_template/_config.scss +60 -0
  15. package/dist/assets/themes/_template/_theme.js +14 -0
  16. package/dist/assets/themes/_template/_theme.scss +28 -0
  17. package/dist/assets/themes/_template/css/base/_root.scss +19 -0
  18. package/dist/assets/themes/_template/css/components/_components.scss +23 -0
  19. package/dist/assets/themes/base/README.md +5 -0
  20. package/dist/assets/themes/base/_components/data/org-chart/component.html +33 -0
  21. package/dist/assets/themes/base/_components/data/org-chart/component.json5 +33 -0
  22. package/dist/assets/themes/base/_components/data/org-chart/component.scss +183 -0
  23. package/dist/assets/themes/base/_components/heading/masthead/component.html +22 -0
  24. package/dist/assets/themes/base/_components/heading/masthead/component.json5 +30 -0
  25. package/dist/assets/themes/base/_components/heading/section-head/component.html +18 -0
  26. package/dist/assets/themes/base/_components/heading/section-head/component.json5 +27 -0
  27. package/dist/assets/themes/base/_components/pricing/features/component.html +29 -0
  28. package/dist/assets/themes/base/_components/pricing/features/component.json5 +30 -0
  29. package/dist/assets/themes/base/_hero/orbit/index.html +30 -0
  30. package/dist/assets/themes/base/_hero/orbit/script.js +33 -0
  31. package/dist/assets/themes/base/_hero/orbit/style.scss +133 -0
  32. package/dist/assets/themes/base/_includes/admin/sections/sidebar.html +3 -0
  33. package/dist/assets/themes/base/_includes/admin/sections/topbar.html +3 -0
  34. package/dist/assets/themes/base/_includes/backend/sections/sidebar.html +3 -0
  35. package/dist/assets/themes/base/_includes/backend/sections/topbar.html +3 -0
  36. package/dist/assets/themes/base/_includes/frontend/components/connection-card.html +49 -0
  37. package/dist/assets/themes/base/_includes/frontend/components/post-card.html +54 -0
  38. package/dist/assets/themes/base/_includes/frontend/sections/account-connections.html +42 -0
  39. package/dist/assets/themes/base/_includes/frontend/sections/account-section-header.html +11 -0
  40. package/dist/assets/themes/base/_includes/frontend/sections/footer.html +154 -0
  41. package/dist/assets/themes/base/_includes/frontend/sections/nav.html +276 -0
  42. package/dist/assets/themes/base/_includes/global/sections/account.html +72 -0
  43. package/dist/assets/themes/base/_includes/global/sections/app-sidebar.html +248 -0
  44. package/dist/assets/themes/base/_includes/global/sections/app-topbar.html +228 -0
  45. package/dist/assets/themes/base/_includes/global/sections/page-header.html +99 -0
  46. package/dist/assets/themes/base/_layouts/admin/core/minimal-viewport-locked.html +22 -0
  47. package/dist/assets/themes/base/_layouts/admin/core/minimal.html +23 -0
  48. package/dist/assets/themes/base/_layouts/backend/core/base.html +57 -0
  49. package/dist/assets/themes/base/_layouts/backend/core/minimal-viewport-locked.html +32 -0
  50. package/dist/assets/themes/base/_layouts/backend/core/minimal.html +32 -0
  51. package/dist/assets/themes/base/_layouts/backend/pages/dashboard/index.html +230 -0
  52. package/dist/assets/themes/base/_layouts/frontend/core/base.html +31 -0
  53. package/dist/assets/themes/base/_layouts/frontend/core/cover.html +40 -0
  54. package/dist/assets/themes/base/_layouts/frontend/core/minimal.html +47 -0
  55. package/dist/assets/themes/base/_layouts/frontend/pages/404.html +54 -0
  56. package/dist/assets/themes/base/_layouts/frontend/pages/about.html +144 -0
  57. package/dist/assets/themes/base/_layouts/frontend/pages/account/index.html +2104 -0
  58. package/dist/assets/themes/base/_layouts/frontend/pages/alternatives/alternative.html +319 -0
  59. package/dist/assets/themes/base/_layouts/frontend/pages/alternatives/index.html +186 -0
  60. package/dist/assets/themes/base/_layouts/frontend/pages/app.html +142 -0
  61. package/dist/assets/themes/base/_layouts/frontend/pages/auth/reset.html +53 -0
  62. package/dist/assets/themes/base/_layouts/frontend/pages/auth/signin.html +90 -0
  63. package/dist/assets/themes/base/_layouts/frontend/pages/auth/signup.html +141 -0
  64. package/dist/assets/themes/base/_layouts/frontend/pages/auth/token.html +44 -0
  65. package/dist/assets/themes/base/_layouts/frontend/pages/blog/categories/category.html +88 -0
  66. package/dist/assets/themes/base/_layouts/frontend/pages/blog/categories/index.html +88 -0
  67. package/dist/assets/themes/base/_layouts/frontend/pages/blog/index.html +255 -0
  68. package/dist/assets/themes/base/_layouts/frontend/pages/blog/post.html +261 -0
  69. package/dist/assets/themes/base/_layouts/frontend/pages/blog/tags/index.html +84 -0
  70. package/dist/assets/themes/base/_layouts/frontend/pages/blog/tags/tag.html +88 -0
  71. package/dist/assets/themes/base/_layouts/frontend/pages/collection/category.html +72 -0
  72. package/dist/assets/themes/base/_layouts/frontend/pages/collection/document.html +54 -0
  73. package/dist/assets/themes/base/_layouts/frontend/pages/collection/index.html +84 -0
  74. package/dist/assets/themes/base/_layouts/frontend/pages/connections/callback.html +55 -0
  75. package/dist/assets/themes/base/_layouts/frontend/pages/contact.html +214 -0
  76. package/dist/assets/themes/base/_layouts/frontend/pages/download.html +793 -0
  77. package/dist/assets/themes/base/_layouts/frontend/pages/extension/index.html +253 -0
  78. package/dist/assets/themes/base/_layouts/frontend/pages/extension/installed.html +198 -0
  79. package/dist/assets/themes/base/_layouts/frontend/pages/feedback.html +171 -0
  80. package/dist/assets/themes/base/_layouts/frontend/pages/index.html +66 -0
  81. package/dist/assets/themes/base/_layouts/frontend/pages/legal/document.html +78 -0
  82. package/dist/assets/themes/base/_layouts/frontend/pages/payment/checkout.html +459 -0
  83. package/dist/assets/themes/base/_layouts/frontend/pages/payment/confirmation.html +131 -0
  84. package/dist/assets/themes/base/_layouts/frontend/pages/portal/email-preferences.html +104 -0
  85. package/dist/assets/themes/base/_layouts/frontend/pages/pricing.html +596 -0
  86. package/dist/assets/themes/base/_layouts/frontend/pages/status.html +361 -0
  87. package/dist/assets/themes/base/_layouts/frontend/pages/team/index.html +210 -0
  88. package/dist/assets/themes/base/_layouts/frontend/pages/team/member.html +125 -0
  89. package/dist/assets/themes/base/_layouts/frontend/pages/updates/index.html +119 -0
  90. package/dist/assets/themes/base/_layouts/frontend/pages/updates/update.html +118 -0
  91. package/dist/assets/themes/base/_sections/about/hero/section.html +68 -0
  92. package/dist/assets/themes/base/_sections/about/hero/section.json5 +58 -0
  93. package/dist/assets/themes/base/_sections/about/letter/section.html +60 -0
  94. package/dist/assets/themes/base/_sections/about/letter/section.json5 +56 -0
  95. package/dist/assets/themes/base/_sections/about/photo-band/section.html +24 -0
  96. package/dist/assets/themes/base/_sections/about/photo-band/section.json5 +23 -0
  97. package/dist/assets/themes/base/_sections/about/principles/section.html +17 -0
  98. package/dist/assets/themes/base/_sections/about/principles/section.json5 +34 -0
  99. package/dist/assets/themes/base/_sections/about/timeline/section.html +20 -0
  100. package/dist/assets/themes/base/_sections/about/timeline/section.json5 +34 -0
  101. package/dist/assets/themes/base/_sections/marketing/bento/section.html +91 -0
  102. package/dist/assets/themes/base/_sections/marketing/bento/section.json5 +152 -0
  103. package/dist/assets/themes/base/_sections/marketing/cta/section.html +44 -0
  104. package/dist/assets/themes/base/_sections/marketing/cta/section.json5 +47 -0
  105. package/dist/assets/themes/base/_sections/marketing/faq/section.html +52 -0
  106. package/dist/assets/themes/base/_sections/marketing/faq/section.json5 +68 -0
  107. package/dist/assets/themes/base/_sections/marketing/hero/section.html +349 -0
  108. package/dist/assets/themes/base/_sections/marketing/hero/section.json5 +335 -0
  109. package/dist/assets/themes/base/_sections/marketing/newsletter-cta/section.html +38 -0
  110. package/dist/assets/themes/base/_sections/marketing/newsletter-cta/section.js +86 -0
  111. package/dist/assets/themes/base/_sections/marketing/newsletter-cta/section.json5 +29 -0
  112. package/dist/assets/themes/base/_sections/marketing/pricing-cards/section.html +115 -0
  113. package/dist/assets/themes/base/_sections/marketing/pricing-cards/section.json5 +72 -0
  114. package/dist/assets/themes/base/_sections/marketing/product-demo/section.html +62 -0
  115. package/dist/assets/themes/base/_sections/marketing/product-demo/section.js +44 -0
  116. package/dist/assets/themes/base/_sections/marketing/product-demo/section.json5 +52 -0
  117. package/dist/assets/themes/base/_sections/marketing/prose/section.html +22 -0
  118. package/dist/assets/themes/base/_sections/marketing/prose/section.json5 +41 -0
  119. package/dist/assets/themes/base/_sections/marketing/showcase/section.html +35 -0
  120. package/dist/assets/themes/base/_sections/marketing/showcase/section.json5 +52 -0
  121. package/dist/assets/themes/base/_sections/marketing/stats/section.html +32 -0
  122. package/dist/assets/themes/base/_sections/marketing/stats/section.json5 +44 -0
  123. package/dist/assets/themes/base/_sections/marketing/testimonials/section.html +54 -0
  124. package/dist/assets/themes/base/_sections/marketing/testimonials/section.json5 +70 -0
  125. package/dist/assets/themes/base/_sections/marketing/trusted-by/section.html +30 -0
  126. package/dist/assets/themes/base/_sections/marketing/trusted-by/section.json5 +38 -0
  127. package/dist/assets/themes/base/_sections/verts/unit/section.html +7 -0
  128. package/dist/assets/themes/base/_sections/verts/unit/section.js +22 -0
  129. package/dist/assets/themes/base/_sections/verts/unit/section.json5 +24 -0
  130. package/dist/assets/themes/base/_sections/verts/unit/section.scss +24 -0
  131. package/dist/assets/themes/base/_theme.js +13 -0
  132. package/dist/assets/themes/base/_theme.scss +4 -0
  133. package/dist/assets/themes/bootstrap/_theme.js +16 -0
  134. package/dist/assets/themes/bootstrap/_theme.scss +11 -0
  135. package/dist/assets/themes/bootstrap/js/index.esm.js +19 -0
  136. package/dist/assets/themes/bootstrap/js/index.umd.js +34 -0
  137. package/dist/assets/themes/bootstrap/js/src/alert.js +87 -0
  138. package/dist/assets/themes/bootstrap/js/src/base-component.js +86 -0
  139. package/dist/assets/themes/bootstrap/js/src/button.js +72 -0
  140. package/dist/assets/themes/bootstrap/js/src/carousel.js +474 -0
  141. package/dist/assets/themes/bootstrap/js/src/collapse.js +297 -0
  142. package/dist/assets/themes/bootstrap/js/src/dom/data.js +55 -0
  143. package/dist/assets/themes/bootstrap/js/src/dom/event-handler.js +317 -0
  144. package/dist/assets/themes/bootstrap/js/src/dom/manipulator.js +71 -0
  145. package/dist/assets/themes/bootstrap/js/src/dom/selector-engine.js +126 -0
  146. package/dist/assets/themes/bootstrap/js/src/dropdown.js +458 -0
  147. package/dist/assets/themes/bootstrap/js/src/modal.js +378 -0
  148. package/dist/assets/themes/bootstrap/js/src/offcanvas.js +282 -0
  149. package/dist/assets/themes/bootstrap/js/src/popover.js +97 -0
  150. package/dist/assets/themes/bootstrap/js/src/scrollspy.js +296 -0
  151. package/dist/assets/themes/bootstrap/js/src/tab.js +315 -0
  152. package/dist/assets/themes/bootstrap/js/src/toast.js +224 -0
  153. package/dist/assets/themes/bootstrap/js/src/tooltip.js +632 -0
  154. package/dist/assets/themes/bootstrap/js/src/util/backdrop.js +151 -0
  155. package/dist/assets/themes/bootstrap/js/src/util/component-functions.js +35 -0
  156. package/dist/assets/themes/bootstrap/js/src/util/config.js +65 -0
  157. package/dist/assets/themes/bootstrap/js/src/util/focustrap.js +115 -0
  158. package/dist/assets/themes/bootstrap/js/src/util/index.js +306 -0
  159. package/dist/assets/themes/bootstrap/js/src/util/sanitizer.js +117 -0
  160. package/dist/assets/themes/bootstrap/js/src/util/scrollbar.js +114 -0
  161. package/dist/assets/themes/bootstrap/js/src/util/swipe.js +146 -0
  162. package/dist/assets/themes/bootstrap/js/src/util/template-factory.js +160 -0
  163. package/dist/assets/themes/bootstrap/overrides/_accordion.scss +21 -0
  164. package/dist/assets/themes/bootstrap/overrides/_avatars.scss +51 -0
  165. package/dist/assets/themes/bootstrap/overrides/_blog-images.scss +45 -0
  166. package/dist/assets/themes/bootstrap/overrides/_buttons-adaptive.scss +141 -0
  167. package/dist/assets/themes/bootstrap/overrides/_color-shades.scss +25 -0
  168. package/dist/assets/themes/bootstrap/overrides/_cursor-utilities.scss +13 -0
  169. package/dist/assets/themes/bootstrap/overrides/_index.scss +30 -0
  170. package/dist/assets/themes/bootstrap/overrides/_links.scss +26 -0
  171. package/dist/assets/themes/bootstrap/overrides/_soft-colors.scss +99 -0
  172. package/dist/assets/themes/bootstrap/overrides/_spacing-extended.scss +74 -0
  173. package/dist/assets/themes/bootstrap/overrides/_spinners.scss +22 -0
  174. package/dist/assets/themes/bootstrap/overrides/_typography-utilities.scss +40 -0
  175. package/dist/assets/themes/bootstrap/scss/_accordion.scss +153 -0
  176. package/dist/assets/themes/bootstrap/scss/_alert.scss +68 -0
  177. package/dist/assets/themes/bootstrap/scss/_badge.scss +38 -0
  178. package/dist/assets/themes/bootstrap/scss/_breadcrumb.scss +40 -0
  179. package/dist/assets/themes/bootstrap/scss/_button-group.scss +147 -0
  180. package/dist/assets/themes/bootstrap/scss/_buttons.scss +216 -0
  181. package/dist/assets/themes/bootstrap/scss/_card.scss +238 -0
  182. package/dist/assets/themes/bootstrap/scss/_carousel.scss +226 -0
  183. package/dist/assets/themes/bootstrap/scss/_close.scss +66 -0
  184. package/dist/assets/themes/bootstrap/scss/_containers.scss +41 -0
  185. package/dist/assets/themes/bootstrap/scss/_dropdown.scss +250 -0
  186. package/dist/assets/themes/bootstrap/scss/_forms.scss +9 -0
  187. package/dist/assets/themes/bootstrap/scss/_functions.scss +302 -0
  188. package/dist/assets/themes/bootstrap/scss/_grid.scss +39 -0
  189. package/dist/assets/themes/bootstrap/scss/_helpers.scss +12 -0
  190. package/dist/assets/themes/bootstrap/scss/_images.scss +42 -0
  191. package/dist/assets/themes/bootstrap/scss/_list-group.scss +199 -0
  192. package/dist/assets/themes/bootstrap/scss/_maps.scss +174 -0
  193. package/dist/assets/themes/bootstrap/scss/_mixins.scss +42 -0
  194. package/dist/assets/themes/bootstrap/scss/_modal.scss +240 -0
  195. package/dist/assets/themes/bootstrap/scss/_nav.scss +197 -0
  196. package/dist/assets/themes/bootstrap/scss/_navbar.scss +289 -0
  197. package/dist/assets/themes/bootstrap/scss/_offcanvas.scss +147 -0
  198. package/dist/assets/themes/bootstrap/scss/_pagination.scss +109 -0
  199. package/dist/assets/themes/bootstrap/scss/_placeholders.scss +51 -0
  200. package/dist/assets/themes/bootstrap/scss/_popover.scss +196 -0
  201. package/dist/assets/themes/bootstrap/scss/_progress.scss +68 -0
  202. package/dist/assets/themes/bootstrap/scss/_reboot.scss +611 -0
  203. package/dist/assets/themes/bootstrap/scss/_root.scss +187 -0
  204. package/dist/assets/themes/bootstrap/scss/_spinners.scss +85 -0
  205. package/dist/assets/themes/bootstrap/scss/_tables.scss +171 -0
  206. package/dist/assets/themes/bootstrap/scss/_toasts.scss +73 -0
  207. package/dist/assets/themes/bootstrap/scss/_tooltip.scss +119 -0
  208. package/dist/assets/themes/bootstrap/scss/_transitions.scss +27 -0
  209. package/dist/assets/themes/bootstrap/scss/_type.scss +106 -0
  210. package/dist/assets/themes/bootstrap/scss/_utilities.scss +806 -0
  211. package/dist/assets/themes/bootstrap/scss/_variables-dark.scss +102 -0
  212. package/dist/assets/themes/bootstrap/scss/_variables.scss +1753 -0
  213. package/dist/assets/themes/bootstrap/scss/bootstrap-grid.scss +62 -0
  214. package/dist/assets/themes/bootstrap/scss/bootstrap-reboot.scss +10 -0
  215. package/dist/assets/themes/bootstrap/scss/bootstrap-utilities.scss +19 -0
  216. package/dist/assets/themes/bootstrap/scss/bootstrap.scss +52 -0
  217. package/dist/assets/themes/bootstrap/scss/forms/_floating-labels.scss +97 -0
  218. package/dist/assets/themes/bootstrap/scss/forms/_form-check.scss +189 -0
  219. package/dist/assets/themes/bootstrap/scss/forms/_form-control.scss +214 -0
  220. package/dist/assets/themes/bootstrap/scss/forms/_form-range.scss +91 -0
  221. package/dist/assets/themes/bootstrap/scss/forms/_form-select.scss +80 -0
  222. package/dist/assets/themes/bootstrap/scss/forms/_form-text.scss +11 -0
  223. package/dist/assets/themes/bootstrap/scss/forms/_input-group.scss +132 -0
  224. package/dist/assets/themes/bootstrap/scss/forms/_labels.scss +36 -0
  225. package/dist/assets/themes/bootstrap/scss/forms/_validation.scss +12 -0
  226. package/dist/assets/themes/bootstrap/scss/helpers/_clearfix.scss +3 -0
  227. package/dist/assets/themes/bootstrap/scss/helpers/_color-bg.scss +7 -0
  228. package/dist/assets/themes/bootstrap/scss/helpers/_colored-links.scss +30 -0
  229. package/dist/assets/themes/bootstrap/scss/helpers/_focus-ring.scss +5 -0
  230. package/dist/assets/themes/bootstrap/scss/helpers/_icon-link.scss +25 -0
  231. package/dist/assets/themes/bootstrap/scss/helpers/_position.scss +36 -0
  232. package/dist/assets/themes/bootstrap/scss/helpers/_ratio.scss +26 -0
  233. package/dist/assets/themes/bootstrap/scss/helpers/_stacks.scss +15 -0
  234. package/dist/assets/themes/bootstrap/scss/helpers/_stretched-link.scss +15 -0
  235. package/dist/assets/themes/bootstrap/scss/helpers/_text-truncation.scss +7 -0
  236. package/dist/assets/themes/bootstrap/scss/helpers/_visually-hidden.scss +8 -0
  237. package/dist/assets/themes/bootstrap/scss/helpers/_vr.scss +8 -0
  238. package/dist/assets/themes/bootstrap/scss/mixins/_alert.scss +18 -0
  239. package/dist/assets/themes/bootstrap/scss/mixins/_backdrop.scss +14 -0
  240. package/dist/assets/themes/bootstrap/scss/mixins/_banner.scss +7 -0
  241. package/dist/assets/themes/bootstrap/scss/mixins/_border-radius.scss +78 -0
  242. package/dist/assets/themes/bootstrap/scss/mixins/_box-shadow.scss +18 -0
  243. package/dist/assets/themes/bootstrap/scss/mixins/_breakpoints.scss +127 -0
  244. package/dist/assets/themes/bootstrap/scss/mixins/_buttons.scss +70 -0
  245. package/dist/assets/themes/bootstrap/scss/mixins/_caret.scss +69 -0
  246. package/dist/assets/themes/bootstrap/scss/mixins/_clearfix.scss +9 -0
  247. package/dist/assets/themes/bootstrap/scss/mixins/_color-mode.scss +21 -0
  248. package/dist/assets/themes/bootstrap/scss/mixins/_color-scheme.scss +7 -0
  249. package/dist/assets/themes/bootstrap/scss/mixins/_container.scss +11 -0
  250. package/dist/assets/themes/bootstrap/scss/mixins/_deprecate.scss +10 -0
  251. package/dist/assets/themes/bootstrap/scss/mixins/_forms.scss +163 -0
  252. package/dist/assets/themes/bootstrap/scss/mixins/_gradients.scss +47 -0
  253. package/dist/assets/themes/bootstrap/scss/mixins/_grid.scss +151 -0
  254. package/dist/assets/themes/bootstrap/scss/mixins/_image.scss +16 -0
  255. package/dist/assets/themes/bootstrap/scss/mixins/_list-group.scss +26 -0
  256. package/dist/assets/themes/bootstrap/scss/mixins/_lists.scss +7 -0
  257. package/dist/assets/themes/bootstrap/scss/mixins/_pagination.scss +10 -0
  258. package/dist/assets/themes/bootstrap/scss/mixins/_reset-text.scss +17 -0
  259. package/dist/assets/themes/bootstrap/scss/mixins/_resize.scss +6 -0
  260. package/dist/assets/themes/bootstrap/scss/mixins/_table-variants.scss +24 -0
  261. package/dist/assets/themes/bootstrap/scss/mixins/_text-truncate.scss +8 -0
  262. package/dist/assets/themes/bootstrap/scss/mixins/_transition.scss +26 -0
  263. package/dist/assets/themes/bootstrap/scss/mixins/_utilities.scss +97 -0
  264. package/dist/assets/themes/bootstrap/scss/mixins/_visually-hidden.scss +38 -0
  265. package/dist/assets/themes/bootstrap/scss/tests/jasmine.js +16 -0
  266. package/dist/assets/themes/bootstrap/scss/tests/mixins/_auto-import-of-variables-dark.test.scss +7 -0
  267. package/dist/assets/themes/bootstrap/scss/tests/mixins/_color-modes.test.scss +69 -0
  268. package/dist/assets/themes/bootstrap/scss/tests/mixins/_media-query-color-mode-full.test.scss +8 -0
  269. package/dist/assets/themes/bootstrap/scss/tests/mixins/_utilities.test.scss +393 -0
  270. package/dist/assets/themes/bootstrap/scss/tests/sass-true/register.js +14 -0
  271. package/dist/assets/themes/bootstrap/scss/tests/sass-true/runner.js +17 -0
  272. package/dist/assets/themes/bootstrap/scss/tests/utilities/_api.test.scss +75 -0
  273. package/dist/assets/themes/bootstrap/scss/utilities/_api.scss +47 -0
  274. package/dist/assets/themes/bootstrap/scss/vendor/_rfs.scss +348 -0
  275. package/dist/assets/themes/classy/README.md +89 -0
  276. package/dist/assets/themes/classy/_config.scss +245 -0
  277. package/dist/assets/themes/classy/_theme.js +31 -0
  278. package/dist/assets/themes/classy/_theme.scss +57 -0
  279. package/dist/assets/themes/classy/css/app/_panels.scss +183 -0
  280. package/dist/assets/themes/classy/css/base/_fonts.scss +79 -0
  281. package/dist/assets/themes/classy/css/base/_no-raise.scss +49 -0
  282. package/dist/assets/themes/classy/css/base/_root.scss +113 -0
  283. package/dist/assets/themes/classy/css/base/_typography.scss +154 -0
  284. package/dist/assets/themes/classy/css/base/_utilities.scss +284 -0
  285. package/dist/assets/themes/classy/css/components/_alerts.scss +51 -0
  286. package/dist/assets/themes/classy/css/components/_badges.scss +46 -0
  287. package/dist/assets/themes/classy/css/components/_buttons.scss +175 -0
  288. package/dist/assets/themes/classy/css/components/_cards.scss +41 -0
  289. package/dist/assets/themes/classy/css/components/_dropdowns.scss +48 -0
  290. package/dist/assets/themes/classy/css/components/_forms.scss +171 -0
  291. package/dist/assets/themes/classy/css/components/_receipt.scss +45 -0
  292. package/dist/assets/themes/classy/css/layout/_footer.scss +298 -0
  293. package/dist/assets/themes/classy/css/layout/_general.scss +209 -0
  294. package/dist/assets/themes/classy/css/layout/_nav.scss +154 -0
  295. package/dist/assets/themes/classy/css/layout/_shell.scss +458 -0
  296. package/dist/assets/themes/classy/css/marketing/_bento.scss +238 -0
  297. package/dist/assets/themes/classy/css/marketing/_content.scss +1328 -0
  298. package/dist/assets/themes/classy/css/marketing/_downloads.scss +246 -0
  299. package/dist/assets/themes/classy/css/marketing/_hero.scss +311 -0
  300. package/dist/assets/themes/classy/css/marketing/_sections.scss +649 -0
  301. package/dist/assets/themes/classy/css/pages/404/index.scss +67 -0
  302. package/dist/assets/themes/classy/css/pages/_auth.scss +235 -0
  303. package/dist/assets/themes/classy/css/pages/admin/firebase/index.scss +22 -0
  304. package/dist/assets/themes/classy/css/pages/blog/[slug].scss +56 -0
  305. package/dist/assets/themes/classy/css/pages/cookies.scss +2 -0
  306. package/dist/assets/themes/classy/css/pages/dashboard/account/index.scss +31 -0
  307. package/dist/assets/themes/classy/css/pages/feedback/index.scss +39 -0
  308. package/dist/assets/themes/classy/css/pages/legal/_document.scss +192 -0
  309. package/dist/assets/themes/classy/css/pages/payment/checkout/index.scss +585 -0
  310. package/dist/assets/themes/classy/css/pages/payment/confirmation/index.scss +9 -0
  311. package/dist/assets/themes/classy/css/pages/privacy.scss +2 -0
  312. package/dist/assets/themes/classy/css/pages/status/index.scss +164 -0
  313. package/dist/assets/themes/classy/css/pages/terms.scss +2 -0
  314. package/dist/assets/themes/classy/css/pages/updates/index.scss +128 -0
  315. package/dist/assets/themes/classy/fonts/inter-italic-latin-ext.woff2 +0 -0
  316. package/dist/assets/themes/classy/fonts/inter-italic-latin.woff2 +0 -0
  317. package/dist/assets/themes/classy/fonts/inter-normal-latin-ext.woff2 +0 -0
  318. package/dist/assets/themes/classy/fonts/inter-normal-latin.woff2 +0 -0
  319. package/dist/assets/themes/classy/fonts/newsreader-italic-latin-ext.woff2 +0 -0
  320. package/dist/assets/themes/classy/fonts/newsreader-italic-latin.woff2 +0 -0
  321. package/dist/assets/themes/classy/fonts/newsreader-normal-latin-ext.woff2 +0 -0
  322. package/dist/assets/themes/classy/fonts/newsreader-normal-latin.woff2 +0 -0
  323. package/dist/assets/themes/classy/js/hero-demo-form.js +56 -0
  324. package/dist/assets/themes/neobrutalism/README.md +118 -0
  325. package/dist/assets/themes/neobrutalism/_config.scss +139 -0
  326. package/dist/assets/themes/neobrutalism/_layouts/frontend/core/base.html +30 -0
  327. package/dist/assets/themes/neobrutalism/_layouts/frontend/pages/index.html +275 -0
  328. package/dist/assets/themes/neobrutalism/_theme.js +24 -0
  329. package/dist/assets/themes/neobrutalism/_theme.scss +61 -0
  330. package/dist/assets/themes/neobrutalism/css/base/_mixins.scss +46 -0
  331. package/dist/assets/themes/neobrutalism/css/base/_root.scss +169 -0
  332. package/dist/assets/themes/neobrutalism/css/base/_typography.scss +61 -0
  333. package/dist/assets/themes/neobrutalism/css/base/_utilities.scss +89 -0
  334. package/dist/assets/themes/neobrutalism/css/components/_billing-toggle.scss +74 -0
  335. package/dist/assets/themes/neobrutalism/css/components/_buttons.scss +148 -0
  336. package/dist/assets/themes/neobrutalism/css/components/_cards.scss +47 -0
  337. package/dist/assets/themes/neobrutalism/css/components/_forms.scss +88 -0
  338. package/dist/assets/themes/neobrutalism/css/components/_marketing.scss +244 -0
  339. package/dist/assets/themes/neobrutalism/css/layout/_general.scss +213 -0
  340. package/dist/assets/themes/neobrutalism/css/layout/_navigation.scss +171 -0
  341. package/dist/assets/themes/neobrutalism/css/pages/index.scss +203 -0
  342. package/dist/assets/themes/neobrutalism/css/pages/pricing/index.scss +58 -0
  343. package/dist/assets/themes/neobrutalism/css/pages/test/libraries/layers/index.scss +7 -0
  344. package/dist/assets/themes/neobrutalism/js/pages/test/libraries/layers/index.js +9 -0
  345. package/dist/assets/themes/newsflash/README.md +117 -0
  346. package/dist/assets/themes/newsflash/_components/heading/lede/component.html +14 -0
  347. package/dist/assets/themes/newsflash/_components/heading/lede/component.json5 +23 -0
  348. package/dist/assets/themes/newsflash/_components/heading/rule-head/component.html +17 -0
  349. package/dist/assets/themes/newsflash/_components/heading/rule-head/component.json5 +23 -0
  350. package/dist/assets/themes/newsflash/_components/news/byline/component.html +12 -0
  351. package/dist/assets/themes/newsflash/_components/news/byline/component.json5 +18 -0
  352. package/dist/assets/themes/newsflash/_components/news/story-card/component.html +25 -0
  353. package/dist/assets/themes/newsflash/_components/news/story-card/component.json5 +21 -0
  354. package/dist/assets/themes/newsflash/_config.scss +147 -0
  355. package/dist/assets/themes/newsflash/_layouts/frontend/core/base.html +51 -0
  356. package/dist/assets/themes/newsflash/_layouts/frontend/pages/blog/index.html +299 -0
  357. package/dist/assets/themes/newsflash/_layouts/frontend/pages/blog/post.html +235 -0
  358. package/dist/assets/themes/newsflash/_layouts/frontend/pages/index.html +328 -0
  359. package/dist/assets/themes/newsflash/_sections/marketing/desks/section.html +39 -0
  360. package/dist/assets/themes/newsflash/_sections/marketing/desks/section.json5 +49 -0
  361. package/dist/assets/themes/newsflash/_sections/marketing/newsletter-cta/section.html +103 -0
  362. package/dist/assets/themes/newsflash/_sections/marketing/newsletter-cta/section.json5 +50 -0
  363. package/dist/assets/themes/newsflash/_sections/marketing/rundown/section.html +28 -0
  364. package/dist/assets/themes/newsflash/_sections/marketing/rundown/section.json5 +40 -0
  365. package/dist/assets/themes/newsflash/_theme.js +24 -0
  366. package/dist/assets/themes/newsflash/_theme.scss +70 -0
  367. package/dist/assets/themes/newsflash/css/base/_fonts.scss +80 -0
  368. package/dist/assets/themes/newsflash/css/base/_mixins.scss +51 -0
  369. package/dist/assets/themes/newsflash/css/base/_root.scss +257 -0
  370. package/dist/assets/themes/newsflash/css/base/_typography.scss +49 -0
  371. package/dist/assets/themes/newsflash/css/base/_utilities.scss +122 -0
  372. package/dist/assets/themes/newsflash/css/components/_badges.scss +65 -0
  373. package/dist/assets/themes/newsflash/css/components/_billing-toggle.scss +61 -0
  374. package/dist/assets/themes/newsflash/css/components/_buttons.scss +139 -0
  375. package/dist/assets/themes/newsflash/css/components/_cards.scss +47 -0
  376. package/dist/assets/themes/newsflash/css/components/_editorial.scss +173 -0
  377. package/dist/assets/themes/newsflash/css/components/_forms.scss +75 -0
  378. package/dist/assets/themes/newsflash/css/components/_infinite-scroll.scss +102 -0
  379. package/dist/assets/themes/newsflash/css/components/_panels.scss +156 -0
  380. package/dist/assets/themes/newsflash/css/components/_ticker.scss +67 -0
  381. package/dist/assets/themes/newsflash/css/layout/_general.scss +330 -0
  382. package/dist/assets/themes/newsflash/css/layout/_navigation.scss +168 -0
  383. package/dist/assets/themes/newsflash/css/pages/404/index.scss +79 -0
  384. package/dist/assets/themes/newsflash/css/pages/blog/[slug].scss +186 -0
  385. package/dist/assets/themes/newsflash/css/pages/blog/index.scss +17 -0
  386. package/dist/assets/themes/newsflash/css/pages/index.scss +157 -0
  387. package/dist/assets/themes/newsflash/css/pages/pricing/index.scss +55 -0
  388. package/dist/assets/themes/newsflash/css/pages/test/libraries/layers/index.scss +7 -0
  389. package/dist/assets/themes/newsflash/fonts/fraunces-italic-latin-ext.woff2 +0 -0
  390. package/dist/assets/themes/newsflash/fonts/fraunces-italic-latin.woff2 +0 -0
  391. package/dist/assets/themes/newsflash/fonts/fraunces-normal-latin-ext.woff2 +0 -0
  392. package/dist/assets/themes/newsflash/fonts/fraunces-normal-latin.woff2 +0 -0
  393. package/dist/assets/themes/newsflash/fonts/schibsted-grotesk-italic-latin-ext.woff2 +0 -0
  394. package/dist/assets/themes/newsflash/fonts/schibsted-grotesk-italic-latin.woff2 +0 -0
  395. package/dist/assets/themes/newsflash/fonts/schibsted-grotesk-normal-latin-ext.woff2 +0 -0
  396. package/dist/assets/themes/newsflash/fonts/schibsted-grotesk-normal-latin.woff2 +0 -0
  397. package/dist/assets/themes/newsflash/js/pages/blog/[slug].js +29 -0
  398. package/dist/assets/themes/newsflash/js/pages/test/libraries/layers/index.js +9 -0
  399. package/dist/build.js +249 -0
  400. package/dist/cli-run.js +34 -0
  401. package/dist/cli.js +49 -0
  402. package/dist/commands/build.js +32 -0
  403. package/dist/commands/cdp/capture.js +91 -0
  404. package/dist/commands/cdp/client.js +140 -0
  405. package/dist/commands/cdp/eval.js +20 -0
  406. package/dist/commands/cdp/quit.js +84 -0
  407. package/dist/commands/cdp/relaunch.js +62 -0
  408. package/dist/commands/cdp/shot.js +20 -0
  409. package/dist/commands/cdp/status.js +38 -0
  410. package/dist/commands/cdp/theme.js +21 -0
  411. package/dist/commands/cdp.js +53 -0
  412. package/dist/commands/clean.js +34 -0
  413. package/dist/commands/deploy.js +75 -0
  414. package/dist/commands/finalize-release.js +238 -0
  415. package/dist/commands/install.js +37 -0
  416. package/dist/commands/launch.js +150 -0
  417. package/dist/commands/lib/dependencies.js +35 -0
  418. package/dist/commands/lib/deploy-precheck.js +117 -0
  419. package/dist/commands/lib/ensure-target.js +300 -0
  420. package/dist/commands/logs.js +185 -0
  421. package/dist/commands/package.js +31 -0
  422. package/dist/commands/publish.js +34 -0
  423. package/dist/commands/push-secrets.js +141 -0
  424. package/dist/commands/release.js +286 -0
  425. package/dist/commands/runner.js +1874 -0
  426. package/dist/commands/sign-windows.js +563 -0
  427. package/dist/commands/test.js +121 -0
  428. package/dist/commands/update.js +21 -0
  429. package/dist/commands/validate-certs.js +238 -0
  430. package/dist/commands/version.js +9 -0
  431. package/dist/config/page-template.html +35 -0
  432. package/dist/defaults/.github/workflows/build.yml +276 -0
  433. package/dist/defaults/AGENTS.md +109 -0
  434. package/dist/defaults/CHANGELOG.md +15 -0
  435. package/dist/defaults/CLAUDE.md +1 -0
  436. package/dist/defaults/_.gitignore +24 -0
  437. package/dist/defaults/_mas/README.md +49 -0
  438. package/dist/defaults/_mas/entitlements.mas.inherit.plist +12 -0
  439. package/dist/defaults/_mas/entitlements.mas.loginhelper.plist +8 -0
  440. package/dist/defaults/_mas/entitlements.mas.plist +33 -0
  441. package/dist/defaults/config/certs/.gitkeep +0 -0
  442. package/dist/defaults/config/certs/README.md +60 -0
  443. package/dist/defaults/config/icons/macos/dmg.png +0 -0
  444. package/dist/defaults/config/icons/macos/icon.png +0 -0
  445. package/dist/defaults/config/icons/macos/tray.png +0 -0
  446. package/dist/defaults/config/icons/windows/icon.png +0 -0
  447. package/dist/defaults/config/omega.json5 +279 -0
  448. package/dist/defaults/docs/README.md +17 -0
  449. package/dist/defaults/gulpfile.js +6 -0
  450. package/dist/defaults/hooks/build/post.js +9 -0
  451. package/dist/defaults/hooks/build/pre.js +9 -0
  452. package/dist/defaults/hooks/notarize/post.js +16 -0
  453. package/dist/defaults/hooks/release/post.js +9 -0
  454. package/dist/defaults/hooks/release/pre.js +9 -0
  455. package/dist/defaults/src/assets/js/components/about/index.js +10 -0
  456. package/dist/defaults/src/assets/js/components/main/index.js +14 -0
  457. package/dist/defaults/src/assets/js/components/settings/index.js +10 -0
  458. package/dist/defaults/src/assets/scss/main.scss +34 -0
  459. package/dist/defaults/src/assets/scss/pages/about.scss +3 -0
  460. package/dist/defaults/src/assets/scss/pages/main.scss +4 -0
  461. package/dist/defaults/src/assets/scss/pages/settings.scss +3 -0
  462. package/dist/defaults/src/integrations/context-menu/index.js +60 -0
  463. package/dist/defaults/src/integrations/menu/index.js +58 -0
  464. package/dist/defaults/src/integrations/tray/index.js +60 -0
  465. package/dist/defaults/src/main.js +78 -0
  466. package/dist/defaults/src/preload.js +15 -0
  467. package/dist/defaults/src/views/about/index.html +3 -0
  468. package/dist/defaults/src/views/main/index.html +8 -0
  469. package/dist/defaults/src/views/settings/index.html +3 -0
  470. package/dist/defaults/test/README.md +41 -0
  471. package/dist/defaults/test/_init.js +10 -0
  472. package/dist/gulp/main.js +144 -0
  473. package/dist/gulp/tasks/audit.js +82 -0
  474. package/dist/gulp/tasks/build-config.js +445 -0
  475. package/dist/gulp/tasks/bundle.js +451 -0
  476. package/dist/gulp/tasks/defaults.js +20 -0
  477. package/dist/gulp/tasks/distribute.js +8 -0
  478. package/dist/gulp/tasks/html.js +95 -0
  479. package/dist/gulp/tasks/package-quick.js +102 -0
  480. package/dist/gulp/tasks/package.js +53 -0
  481. package/dist/gulp/tasks/release.js +57 -0
  482. package/dist/gulp/tasks/sass.js +82 -0
  483. package/dist/gulp/tasks/serve.js +191 -0
  484. package/dist/hooks/notarize.js +70 -0
  485. package/dist/index.js +14 -0
  486. package/dist/lib/_menu-mixin.js +199 -0
  487. package/dist/lib/analytics.js +360 -0
  488. package/dist/lib/app-state.js +220 -0
  489. package/dist/lib/auth-flow.js +160 -0
  490. package/dist/lib/auth-persistence.js +207 -0
  491. package/dist/lib/auto-updater.js +752 -0
  492. package/dist/lib/client-bridge.js +371 -0
  493. package/dist/lib/context-menu.js +302 -0
  494. package/dist/lib/context.js +162 -0
  495. package/dist/lib/deep-link.js +370 -0
  496. package/dist/lib/fontawesome.js +203 -0
  497. package/dist/lib/ipc.js +169 -0
  498. package/dist/lib/logger-lite.js +259 -0
  499. package/dist/lib/logger.js +4 -0
  500. package/dist/lib/menu.js +521 -0
  501. package/dist/lib/protocol.js +85 -0
  502. package/dist/lib/remote-config.js +224 -0
  503. package/dist/lib/remote-scripts.js +158 -0
  504. package/dist/lib/restart-manager/index.js +550 -0
  505. package/dist/lib/restart-manager/install.js +353 -0
  506. package/dist/lib/restart-manager/protocol.js +274 -0
  507. package/dist/lib/sign-helpers/auto-unlock.js +95 -0
  508. package/dist/lib/sign-helpers/entitlements.js +98 -0
  509. package/dist/lib/sign-helpers/resolve-icons.js +171 -0
  510. package/dist/lib/sign-helpers/sign-events.js +75 -0
  511. package/dist/lib/sign-helpers/update-info.js +186 -0
  512. package/dist/lib/startup.js +214 -0
  513. package/dist/lib/storage.js +193 -0
  514. package/dist/lib/templating.js +85 -0
  515. package/dist/lib/theme.js +173 -0
  516. package/dist/lib/tray.js +414 -0
  517. package/dist/lib/usage.js +141 -0
  518. package/dist/lib/window-manager.js +509 -0
  519. package/dist/main.js +542 -0
  520. package/dist/omega-bin.js +6 -0
  521. package/dist/preload.js +241 -0
  522. package/dist/renderer.js +456 -0
  523. package/dist/test/assert.js +4 -0
  524. package/dist/test/fixtures/consumer-app/config/omega.json5 +39 -0
  525. package/dist/test/fixtures/consumer-app/package.json +13 -0
  526. package/dist/test/fixtures/consumer-app/src/assets/js/components/main/index.js +11 -0
  527. package/dist/test/fixtures/consumer-app/src/assets/scss/main.scss +3 -0
  528. package/dist/test/fixtures/consumer-app/src/main.js +17 -0
  529. package/dist/test/fixtures/consumer-app/src/preload.js +4 -0
  530. package/dist/test/fixtures/consumer-app/src/views/main/index.html +4 -0
  531. package/dist/test/harness/boot-entry.js +237 -0
  532. package/dist/test/harness/main-entry.js +494 -0
  533. package/dist/test/harness/package.json +6 -0
  534. package/dist/test/harness/renderer-entry.js +173 -0
  535. package/dist/test/harness/renderer-preload.js +310 -0
  536. package/dist/test/harness/renderer.html +12 -0
  537. package/dist/test/index.js +65 -0
  538. package/dist/test/runner.js +99 -0
  539. package/dist/test/runners/boot.js +414 -0
  540. package/dist/test/runners/electron.js +92 -0
  541. package/dist/test/runners/render-event.js +46 -0
  542. package/dist/test/suites/boot/build-isolation.test.js +54 -0
  543. package/dist/test/suites/boot/consumer-app-boots.test.js +99 -0
  544. package/dist/test/suites/boot/restart-manager.test.js +38 -0
  545. package/dist/test/suites/boot/storage-bundled.test.js +59 -0
  546. package/dist/test/suites/boot/theme.test.js +68 -0
  547. package/dist/test/suites/boot/view-suite.test.js +47 -0
  548. package/dist/test/suites/build/attach-log-file.test.js +99 -0
  549. package/dist/test/suites/build/audit.test.js +271 -0
  550. package/dist/test/suites/build/boot-fixture.test.js +48 -0
  551. package/dist/test/suites/build/build-config.test.js +457 -0
  552. package/dist/test/suites/build/build-verbs.test.js +156 -0
  553. package/dist/test/suites/build/build-workflow-jobs.test.js +55 -0
  554. package/dist/test/suites/build/cdp.test.js +89 -0
  555. package/dist/test/suites/build/cli.test.js +135 -0
  556. package/dist/test/suites/build/config-schema.test.js +133 -0
  557. package/dist/test/suites/build/defaults-scaffold.test.js +192 -0
  558. package/dist/test/suites/build/deliver-certs.test.js +95 -0
  559. package/dist/test/suites/build/derive-signing-env.test.js +122 -0
  560. package/dist/test/suites/build/electron-node-version.test.js +49 -0
  561. package/dist/test/suites/build/electron-targets.test.js +92 -0
  562. package/dist/test/suites/build/ensure-target.test.js +131 -0
  563. package/dist/test/suites/build/entitlements.test.js +107 -0
  564. package/dist/test/suites/build/env-delivery.test.js +166 -0
  565. package/dist/test/suites/build/env-watch.test.js +86 -0
  566. package/dist/test/suites/build/exports.test.js +63 -0
  567. package/dist/test/suites/build/finalize-release.test.js +152 -0
  568. package/dist/test/suites/build/format-fetch-error.test.js +85 -0
  569. package/dist/test/suites/build/framework-deps.test.js +173 -0
  570. package/dist/test/suites/build/get-config.test.js +137 -0
  571. package/dist/test/suites/build/github-utils.test.js +54 -0
  572. package/dist/test/suites/build/icon-sheet.test.js +62 -0
  573. package/dist/test/suites/build/license-stamp.test.js +63 -0
  574. package/dist/test/suites/build/logger-lite.test.js +148 -0
  575. package/dist/test/suites/build/logs-command.test.js +226 -0
  576. package/dist/test/suites/build/manager.test.js +193 -0
  577. package/dist/test/suites/build/menu-mixin.test.js +130 -0
  578. package/dist/test/suites/build/merge-line-files.test.js +230 -0
  579. package/dist/test/suites/build/omega-shell.test.js +94 -0
  580. package/dist/test/suites/build/omega-tokens.test.js +64 -0
  581. package/dist/test/suites/build/project-scripts-deps.test.js +90 -0
  582. package/dist/test/suites/build/push-secrets.test.js +226 -0
  583. package/dist/test/suites/build/release-pipeline.test.js +365 -0
  584. package/dist/test/suites/build/renderer-auth-bridge.test.js +158 -0
  585. package/dist/test/suites/build/renderer-entry-key.test.js +37 -0
  586. package/dist/test/suites/build/renderer-node-shims.test.js +125 -0
  587. package/dist/test/suites/build/resolve-icons.test.js +296 -0
  588. package/dist/test/suites/build/resolve-signing-cert.test.js +342 -0
  589. package/dist/test/suites/build/restart-manager-install.test.js +178 -0
  590. package/dist/test/suites/build/restart-manager-protocol.test.js +158 -0
  591. package/dist/test/suites/build/run-consumer-hook.test.js +137 -0
  592. package/dist/test/suites/build/runner.test.js +2599 -0
  593. package/dist/test/suites/build/sanitize-signing-env.test.js +98 -0
  594. package/dist/test/suites/build/sanitize-url.test.js +78 -0
  595. package/dist/test/suites/build/secure-remote-url.test.js +42 -0
  596. package/dist/test/suites/build/sentry.test.js +89 -0
  597. package/dist/test/suites/build/serve-resolve-electron.test.js +58 -0
  598. package/dist/test/suites/build/setup-scripts.test.js +172 -0
  599. package/dist/test/suites/build/sign-events.test.js +90 -0
  600. package/dist/test/suites/build/sign-windows-e2e.test.js +302 -0
  601. package/dist/test/suites/build/sign-windows.test.js +313 -0
  602. package/dist/test/suites/build/strip-dev-blocks.test.js +116 -0
  603. package/dist/test/suites/build/templating.test.js +95 -0
  604. package/dist/test/suites/build/test-discovery.test.js +64 -0
  605. package/dist/test/suites/build/test-stealth.test.js +83 -0
  606. package/dist/test/suites/build/test-target-no-match.test.js +119 -0
  607. package/dist/test/suites/build/update-info.test.js +224 -0
  608. package/dist/test/suites/build/url-helpers.test.js +289 -0
  609. package/dist/test/suites/build/validate-certs.test.js +103 -0
  610. package/dist/test/suites/build/validate-config.test.js +90 -0
  611. package/dist/test/suites/build/verb-logs.test.js +43 -0
  612. package/dist/test/suites/build/wave5-pins.test.js +35 -0
  613. package/dist/test/suites/main/analytics.test.js +364 -0
  614. package/dist/test/suites/main/app-state.test.js +197 -0
  615. package/dist/test/suites/main/auth-flow.test.js +196 -0
  616. package/dist/test/suites/main/auth-persistence.test.js +167 -0
  617. package/dist/test/suites/main/auto-updater.test.js +1138 -0
  618. package/dist/test/suites/main/boot-sequence.test.js +77 -0
  619. package/dist/test/suites/main/client-bridge.integration.test.js +134 -0
  620. package/dist/test/suites/main/client-bridge.test.js +249 -0
  621. package/dist/test/suites/main/context-menu.test.js +365 -0
  622. package/dist/test/suites/main/context.test.js +155 -0
  623. package/dist/test/suites/main/deep-link.test.js +288 -0
  624. package/dist/test/suites/main/epipe-handler.test.js +40 -0
  625. package/dist/test/suites/main/fontawesome.test.js +147 -0
  626. package/dist/test/suites/main/harness-cdp.test.js +49 -0
  627. package/dist/test/suites/main/ipc.test.js +165 -0
  628. package/dist/test/suites/main/menu.test.js +419 -0
  629. package/dist/test/suites/main/protocol.test.js +77 -0
  630. package/dist/test/suites/main/remote-config.test.js +189 -0
  631. package/dist/test/suites/main/remote-scripts.test.js +276 -0
  632. package/dist/test/suites/main/restart-manager.test.js +328 -0
  633. package/dist/test/suites/main/startup-paths-and-ua.test.js +99 -0
  634. package/dist/test/suites/main/startup.test.js +129 -0
  635. package/dist/test/suites/main/stealth-window.test.js +74 -0
  636. package/dist/test/suites/main/storage.test.js +89 -0
  637. package/dist/test/suites/main/theme.test.js +137 -0
  638. package/dist/test/suites/main/tray.test.js +322 -0
  639. package/dist/test/suites/main/truncate-on-boot.test.js +22 -0
  640. package/dist/test/suites/main/url-helpers.test.js +429 -0
  641. package/dist/test/suites/main/usage.test.js +127 -0
  642. package/dist/test/suites/main/window-bounds.test.js +169 -0
  643. package/dist/test/suites/main/window-manager.test.js +233 -0
  644. package/dist/test/suites/renderer/analytics-bridge.test.js +128 -0
  645. package/dist/test/suites/renderer/cross-context-helpers.test.js +131 -0
  646. package/dist/test/suites/renderer/fontawesome.test.js +190 -0
  647. package/dist/test/suites/renderer/round-trip.test.js +107 -0
  648. package/dist/test/suites/renderer/theme.test.js +118 -0
  649. package/dist/test/suites/renderer/tooltips.test.js +166 -0
  650. package/dist/test/suites/renderer/verts.test.js +100 -0
  651. package/dist/test/suites/renderer/window-em-surface.test.js +107 -0
  652. package/dist/test/utils/dist-snapshot.js +36 -0
  653. package/dist/test/utils/extended-mode-warning.js +13 -0
  654. package/dist/utils/app-root.js +28 -0
  655. package/dist/utils/attach-log-file.js +4 -0
  656. package/dist/utils/build-pipeline.js +58 -0
  657. package/dist/utils/deliver-certs.js +69 -0
  658. package/dist/utils/derive-signing-env.js +56 -0
  659. package/dist/utils/dist-root.js +21 -0
  660. package/dist/utils/electron-node-version.js +48 -0
  661. package/dist/utils/electron-targets.js +72 -0
  662. package/dist/utils/format-fetch-error.js +38 -0
  663. package/dist/utils/github.js +73 -0
  664. package/dist/utils/load-consumer-file.js +26 -0
  665. package/dist/utils/merge-line-files.js +5 -0
  666. package/dist/utils/mode-helpers.js +147 -0
  667. package/dist/utils/resolve-signing-cert.js +175 -0
  668. package/dist/utils/run-consumer-hook.js +38 -0
  669. package/dist/utils/runner-env.js +687 -0
  670. package/dist/utils/safe-install.js +4 -0
  671. package/dist/utils/sanitize-signing-env.js +45 -0
  672. package/dist/utils/sanitize-url.js +28 -0
  673. package/dist/utils/secure-remote-url.js +25 -0
  674. package/dist/utils/stealth-window.js +37 -0
  675. package/dist/utils/test-stealth.js +29 -0
  676. package/dist/utils/url-helpers.js +169 -0
  677. package/dist/vendor/account/engine.js +182 -0
  678. package/dist/vendor/account/features.js +220 -0
  679. package/dist/vendor/account/index.js +53 -0
  680. package/dist/vendor/account/schema.js +272 -0
  681. package/dist/vendor/account/subscription.js +38 -0
  682. package/dist/vendor/analytics/adapters/ga4.js +26 -0
  683. package/dist/vendor/analytics/adapters/meta.js +26 -0
  684. package/dist/vendor/analytics/adapters/resolve.js +130 -0
  685. package/dist/vendor/analytics/adapters/tiktok.js +27 -0
  686. package/dist/vendor/analytics/catalog.js +908 -0
  687. package/dist/vendor/analytics/consent.js +49 -0
  688. package/dist/vendor/analytics/core.js +141 -0
  689. package/dist/vendor/analytics/identity.js +136 -0
  690. package/dist/vendor/analytics/index.js +170 -0
  691. package/dist/vendor/analytics/logger.js +40 -0
  692. package/dist/vendor/analytics/transports/browser.js +110 -0
  693. package/dist/vendor/config/company.js +31 -0
  694. package/dist/vendor/config/defaults.js +173 -0
  695. package/dist/vendor/config/demo.js +18 -0
  696. package/dist/vendor/config/desktop-artifacts.js +110 -0
  697. package/dist/vendor/config/edit.js +769 -0
  698. package/dist/vendor/config/env-delivery.js +145 -0
  699. package/dist/vendor/config/env-rules.js +93 -0
  700. package/dist/vendor/config/env-schema.js +1078 -0
  701. package/dist/vendor/config/env.js +445 -0
  702. package/dist/vendor/config/hooks.js +97 -0
  703. package/dist/vendor/config/index.js +237 -0
  704. package/dist/vendor/config/instances.js +208 -0
  705. package/dist/vendor/config/load.js +490 -0
  706. package/dist/vendor/config/merge.js +68 -0
  707. package/dist/vendor/config/order.js +139 -0
  708. package/dist/vendor/config/ports.js +374 -0
  709. package/dist/vendor/config/providers.js +32 -0
  710. package/dist/vendor/config/repo.js +142 -0
  711. package/dist/vendor/config/retired-keys.js +430 -0
  712. package/dist/vendor/config/schema.js +1610 -0
  713. package/dist/vendor/config/secrets.js +50 -0
  714. package/dist/vendor/config/seed.js +34 -0
  715. package/dist/vendor/config/site-global.js +205 -0
  716. package/dist/vendor/config/validate.js +554 -0
  717. package/dist/vendor/config/winback.js +61 -0
  718. package/dist/vendor/devkit/actions-secrets.js +145 -0
  719. package/dist/vendor/devkit/attach-log-file.js +262 -0
  720. package/dist/vendor/devkit/bundle.js +292 -0
  721. package/dist/vendor/devkit/certs.js +199 -0
  722. package/dist/vendor/devkit/ci-workflows.js +520 -0
  723. package/dist/vendor/devkit/clean-dirs.js +28 -0
  724. package/dist/vendor/devkit/cli-router.js +156 -0
  725. package/dist/vendor/devkit/defaults-engine.js +372 -0
  726. package/dist/vendor/devkit/deploy-precheck.js +54 -0
  727. package/dist/vendor/devkit/deploy-record.js +180 -0
  728. package/dist/vendor/devkit/deploy.js +286 -0
  729. package/dist/vendor/devkit/empty-modules-plugin.js +64 -0
  730. package/dist/vendor/devkit/env-watch.js +132 -0
  731. package/dist/vendor/devkit/framework-deps.js +46 -0
  732. package/dist/vendor/devkit/license.js +155 -0
  733. package/dist/vendor/devkit/local.js +1905 -0
  734. package/dist/vendor/devkit/logger.js +128 -0
  735. package/dist/vendor/devkit/merge-line-files.js +296 -0
  736. package/dist/vendor/devkit/npm-registry.js +52 -0
  737. package/dist/vendor/devkit/omega-bin.js +345 -0
  738. package/dist/vendor/devkit/prompt.js +187 -0
  739. package/dist/vendor/devkit/safe-install.js +18 -0
  740. package/dist/vendor/devkit/scaffold-guard.js +96 -0
  741. package/dist/vendor/devkit/stop-signals.js +28 -0
  742. package/dist/vendor/devkit/strip-dev-blocks-plugin.js +29 -0
  743. package/dist/vendor/devkit/strip-dev-blocks.js +32 -0
  744. package/dist/vendor/devkit/target-dependencies.js +137 -0
  745. package/dist/vendor/devkit/target-secrets.js +165 -0
  746. package/dist/vendor/devkit/test/assert.js +120 -0
  747. package/dist/vendor/devkit/test/define-cases.js +104 -0
  748. package/dist/vendor/devkit/test/extended-mode-warning.js +22 -0
  749. package/dist/vendor/devkit/test/runner-core.js +554 -0
  750. package/dist/vendor/devkit/test/scope.js +162 -0
  751. package/dist/vendor/devkit/update.js +569 -0
  752. package/dist/vendor/monitoring/core.js +180 -0
  753. package/dist/vendor/monitoring/env.js +49 -0
  754. package/dist/vendor/monitoring/index.js +28 -0
  755. package/dist/vendor/monitoring/logger.js +39 -0
  756. package/dist/vendor/monitoring/main.js +99 -0
  757. package/dist/vendor/monitoring/preload.js +45 -0
  758. package/dist/vendor/monitoring/renderer.js +87 -0
  759. package/docs/analytics.md +140 -0
  760. package/docs/app-state.md +92 -0
  761. package/docs/audit.md +69 -0
  762. package/docs/auto-updater.md +243 -0
  763. package/docs/boot-sequence.md +39 -0
  764. package/docs/build-system.md +161 -0
  765. package/docs/cdp-debugging.md +169 -0
  766. package/docs/client-bridge.md +267 -0
  767. package/docs/common-mistakes.md +21 -0
  768. package/docs/config-schema.md +120 -0
  769. package/docs/context-menu.md +112 -0
  770. package/docs/context.md +81 -0
  771. package/docs/css.md +84 -0
  772. package/docs/deep-link.md +178 -0
  773. package/docs/environment-detection.md +104 -0
  774. package/docs/fontawesome.md +107 -0
  775. package/docs/hooks.md +87 -0
  776. package/docs/icons.md +79 -0
  777. package/docs/index.md +279 -0
  778. package/docs/installer-options.md +162 -0
  779. package/docs/ipc.md +61 -0
  780. package/docs/lib-modules.md +53 -0
  781. package/docs/logging.md +229 -0
  782. package/docs/menu.md +160 -0
  783. package/docs/releasing.md +229 -0
  784. package/docs/remote-config.md +118 -0
  785. package/docs/remote-scripts.md +144 -0
  786. package/docs/restart-manager.md +144 -0
  787. package/docs/runner.md +253 -0
  788. package/docs/sentry.md +97 -0
  789. package/docs/shared/agent-docs.md +89 -0
  790. package/docs/shared/analytics.md +612 -0
  791. package/docs/shared/brands.md +51 -0
  792. package/docs/shared/breaking-changes.md +497 -0
  793. package/docs/shared/config.md +1387 -0
  794. package/docs/shared/deploys.md +215 -0
  795. package/docs/shared/icons.md +201 -0
  796. package/docs/shared/local-dev.md +147 -0
  797. package/docs/shared/logging.md +202 -0
  798. package/docs/shared/monitoring.md +153 -0
  799. package/docs/shared/publishing.md +183 -0
  800. package/docs/shared/rulings.md +34 -0
  801. package/docs/shared/testing.md +147 -0
  802. package/docs/shared/theming.md +604 -0
  803. package/docs/shared/translation.md +291 -0
  804. package/docs/shared/updates.md +61 -0
  805. package/docs/signing.md +267 -0
  806. package/docs/startup.md +142 -0
  807. package/docs/storage.md +59 -0
  808. package/docs/templating.md +101 -0
  809. package/docs/test-boot-layer.md +157 -0
  810. package/docs/test-framework.md +357 -0
  811. package/docs/themes.md +135 -0
  812. package/docs/tooltips.md +99 -0
  813. package/docs/tray.md +164 -0
  814. package/docs/usage.md +58 -0
  815. package/docs/verts.md +62 -0
  816. package/docs/windows.md +149 -0
  817. package/package.json +184 -0
  818. package/scripts/sync-nvmrc.js +100 -0
@@ -0,0 +1,1138 @@
1
+ // Main-process tests for lib/auto-updater.js — state machine, dev simulation,
2
+ // 30-day pending-update gate (first-download-wins).
3
+ //
4
+ // Each test resets the updater + clears storage so we get clean state.
5
+
6
+ const defineCases = require('../../../vendor/devkit/test/define-cases.js');
7
+
8
+ const STORAGE_KEY = 'autoUpdater';
9
+
10
+ async function reinit(ctx, env) {
11
+ const updater = ctx.manager.autoUpdater;
12
+ updater.shutdown();
13
+ ctx.manager.storage.set(STORAGE_KEY, null);
14
+
15
+ // Snapshot + override env for this run.
16
+ const saved = { OMEGA_DEV_UPDATE: process.env.OMEGA_DEV_UPDATE };
17
+ for (const [k, v] of Object.entries(env || {})) {
18
+ if (v == null) delete process.env[k];
19
+ else process.env[k] = v;
20
+ }
21
+ await updater.initialize(ctx.manager);
22
+ return async () => {
23
+ updater.shutdown();
24
+ for (const [k, v] of Object.entries(saved)) {
25
+ if (v == null) delete process.env[k];
26
+ else process.env[k] = v;
27
+ }
28
+ // Re-init with the original env so subsequent tests have a working updater + IPC handlers.
29
+ await updater.initialize(ctx.manager);
30
+ };
31
+ }
32
+
33
+ function waitFor(predicate, { timeout = 3000, step = 50 } = {}) {
34
+ return new Promise((resolve, reject) => {
35
+ const start = Date.now();
36
+ const tick = () => {
37
+ try {
38
+ if (predicate()) return resolve();
39
+ } catch (e) { /* keep polling */ }
40
+ if (Date.now() - start > timeout) return reject(new Error('waitFor timeout'));
41
+ setTimeout(tick, step);
42
+ };
43
+ tick();
44
+ });
45
+ }
46
+
47
+ module.exports = defineCases({
48
+ type: 'suite',
49
+ layer: 'main',
50
+ description: 'auto-updater (main)',
51
+ cleanup: async (ctx) => {
52
+ ctx.manager.autoUpdater.shutdown();
53
+ ctx.manager.storage.set(STORAGE_KEY, null);
54
+ delete process.env.OMEGA_DEV_UPDATE;
55
+ await ctx.manager.autoUpdater.initialize(ctx.manager);
56
+ },
57
+ tests: [
58
+ {
59
+ name: 'initialize ran during boot — initial status is idle',
60
+ run: (ctx) => {
61
+ const status = ctx.manager.autoUpdater.getStatus();
62
+ ctx.expect(status.code).toBe('idle');
63
+ ctx.expect(status.percent).toBe(0);
64
+ ctx.expect(status.error).toBe(null);
65
+ },
66
+ },
67
+ {
68
+ name: 'dev simulation: available scenario walks state through downloaded',
69
+ run: async (ctx) => {
70
+ const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: 'available' });
71
+ try {
72
+ await ctx.manager.autoUpdater.checkNow({ userInitiated: true });
73
+ await waitFor(() => ctx.manager.autoUpdater.getStatus().code === 'downloaded', { timeout: 5000 });
74
+
75
+ const s = ctx.manager.autoUpdater.getStatus();
76
+ ctx.expect(s.code).toBe('downloaded');
77
+ ctx.expect(s.version).toBe('999.0.0');
78
+ ctx.expect(s.percent).toBe(100);
79
+ ctx.expect(typeof s.downloadedAt).toBe('number');
80
+ } finally { await restore(); }
81
+ },
82
+ },
83
+ {
84
+ name: 'dev simulation: unavailable scenario lands in not-available',
85
+ run: async (ctx) => {
86
+ const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: 'unavailable' });
87
+ try {
88
+ await ctx.manager.autoUpdater.checkNow({ userInitiated: false });
89
+ await waitFor(() => ctx.manager.autoUpdater.getStatus().code === 'not-available');
90
+
91
+ const s = ctx.manager.autoUpdater.getStatus();
92
+ ctx.expect(s.code).toBe('not-available');
93
+ ctx.expect(s.error).toBe(null);
94
+ } finally { await restore(); }
95
+ },
96
+ },
97
+ {
98
+ name: 'dev simulation: error scenario lands in error',
99
+ run: async (ctx) => {
100
+ const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: 'error' });
101
+ try {
102
+ await ctx.manager.autoUpdater.checkNow({ userInitiated: false });
103
+ await waitFor(() => ctx.manager.autoUpdater.getStatus().code === 'error');
104
+
105
+ const s = ctx.manager.autoUpdater.getStatus();
106
+ ctx.expect(s.code).toBe('error');
107
+ ctx.expect(s.error).toBeDefined();
108
+ ctx.expect(s.error.message).toMatch(/Simulated/);
109
+ } finally { await restore(); }
110
+ },
111
+ },
112
+ {
113
+ name: 'simulate(): available scenario walks state through downloaded with no env var set',
114
+ run: async (ctx) => {
115
+ const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: null });
116
+ try {
117
+ await ctx.manager.autoUpdater.simulate('available');
118
+ await waitFor(() => ctx.manager.autoUpdater.getStatus().code === 'downloaded', { timeout: 5000 });
119
+
120
+ const s = ctx.manager.autoUpdater.getStatus();
121
+ ctx.expect(s.code).toBe('downloaded');
122
+ ctx.expect(s.version).toBe('999.0.0');
123
+ ctx.expect(s.percent).toBe(100);
124
+ } finally { await restore(); }
125
+ },
126
+ },
127
+ {
128
+ name: 'simulate(): unavailable scenario lands in not-available',
129
+ run: async (ctx) => {
130
+ const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: null });
131
+ try {
132
+ await ctx.manager.autoUpdater.simulate('unavailable');
133
+ await waitFor(() => ctx.manager.autoUpdater.getStatus().code === 'not-available');
134
+
135
+ ctx.expect(ctx.manager.autoUpdater.getStatus().error).toBe(null);
136
+ } finally { await restore(); }
137
+ },
138
+ },
139
+ {
140
+ name: 'simulate(): error scenario lands in error',
141
+ run: async (ctx) => {
142
+ const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: null });
143
+ try {
144
+ await ctx.manager.autoUpdater.simulate('error');
145
+ await waitFor(() => ctx.manager.autoUpdater.getStatus().code === 'error');
146
+
147
+ ctx.expect(ctx.manager.autoUpdater.getStatus().error.message).toMatch(/Simulated/);
148
+ } finally { await restore(); }
149
+ },
150
+ },
151
+ {
152
+ name: 'simulate(): unknown scenario throws',
153
+ run: async (ctx) => {
154
+ const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: null });
155
+ try {
156
+ await ctx.expect(() => ctx.manager.autoUpdater.simulate('sideways')).toThrow(/unknown scenario/i);
157
+ await ctx.expect(() => ctx.manager.autoUpdater.simulate()).toThrow(/scenario required/i);
158
+ } finally { await restore(); }
159
+ },
160
+ },
161
+ {
162
+ name: 'simulate(): refuses in a production build unless OMEGA_DEV_UPDATE is set',
163
+ run: async (ctx) => {
164
+ const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: null });
165
+ const origIsProduction = ctx.manager.isProduction;
166
+ ctx.manager.isProduction = () => true;
167
+ try {
168
+ await ctx.expect(() => ctx.manager.autoUpdater.simulate('available')).toThrow(/production/i);
169
+ ctx.expect(ctx.manager.autoUpdater.getStatus().code).toBe('idle');
170
+
171
+ // ...but a packaged QA build launched with the env var set may still simulate.
172
+ process.env.OMEGA_DEV_UPDATE = 'available';
173
+ await ctx.manager.autoUpdater.simulate('unavailable');
174
+ await waitFor(() => ctx.manager.autoUpdater.getStatus().code === 'not-available');
175
+ } finally {
176
+ delete process.env.OMEGA_DEV_UPDATE;
177
+ ctx.manager.isProduction = origIsProduction;
178
+ await restore();
179
+ }
180
+ },
181
+ },
182
+ {
183
+ // The whole point of the session flag: simulate() leaves the synthetic library
184
+ // wired, so every LATER trigger (the hourly feed tick) drives the simulator too.
185
+ // Without _isSimulating() latching, that fake 'downloaded' walks into the real
186
+ // install path and flips the manager's quit latch.
187
+ name: 'simulate(): latches the session so a later feed tick cannot reach the real install path',
188
+ run: async (ctx) => {
189
+ const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: null });
190
+ const u = ctx.manager.autoUpdater;
191
+ const origPrompt = u._promptToInstall;
192
+ let promptCalls = 0;
193
+ u._promptToInstall = async () => { promptCalls++; };
194
+ try {
195
+ await u.simulate('unavailable');
196
+ await waitFor(() => u.getStatus().code === 'not-available');
197
+ ctx.expect(u._isSimulating()).toBe(true);
198
+
199
+ // The feed tick calls checkForUpdates() with no scenario: the simulator falls
200
+ // back to 'available' and lands on a fake downloaded v999.0.0.
201
+ await u._feedCheckTick();
202
+ await waitFor(() => u.getStatus().code === 'downloaded', { timeout: 5000 });
203
+
204
+ // User idle well past the threshold, background check: only the simulation
205
+ // guard stands between this and a real install.
206
+ u._userInitiated = false;
207
+ u._lastActivityAt = Date.now() - (10 * 60 * 1000);
208
+ u._evaluateIdleInstall();
209
+
210
+ ctx.expect(ctx.manager._allowQuit).toBe(false);
211
+ ctx.expect(promptCalls).toBe(0);
212
+ } finally {
213
+ u._promptToInstall = origPrompt;
214
+ ctx.manager._allowQuit = false;
215
+ await restore();
216
+ }
217
+ },
218
+ },
219
+ {
220
+ name: 'simulate(): shutdown clears the session flag',
221
+ run: async (ctx) => {
222
+ const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: null });
223
+ const u = ctx.manager.autoUpdater;
224
+ try {
225
+ await u.simulate('unavailable');
226
+ ctx.expect(u._isSimulating()).toBe(true);
227
+
228
+ u.shutdown();
229
+ ctx.expect(u._isSimulating()).toBe(false);
230
+ } finally { await restore(); }
231
+ },
232
+ },
233
+ {
234
+ name: 'simulate(): refuses while a cascade is already running',
235
+ run: async (ctx) => {
236
+ const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: null });
237
+ const u = ctx.manager.autoUpdater;
238
+ try {
239
+ await u.simulate('available');
240
+ ctx.expect(u._devSimulating).toBe(true);
241
+
242
+ await ctx.expect(() => u.simulate('error')).toThrow(/already running/i);
243
+ // The refused call must not have reset the running cascade's state.
244
+ ctx.expect(u.getStatus().code).not.toBe('idle');
245
+ await waitFor(() => u.getStatus().code === 'downloaded', { timeout: 5000 });
246
+ } finally { await restore(); }
247
+ },
248
+ },
249
+ {
250
+ name: 'dev simulation: the env-var cascade never writes the pendingUpdate storage key',
251
+ run: async (ctx) => {
252
+ const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: 'available' });
253
+ try {
254
+ await ctx.manager.autoUpdater.checkNow({ userInitiated: false });
255
+ await waitFor(() => ctx.manager.autoUpdater.getStatus().code === 'downloaded', { timeout: 5000 });
256
+
257
+ // The fake download stamps memory (the in-session UI flow reads the
258
+ // same as a real one) but must leave the production record alone.
259
+ ctx.expect(typeof ctx.manager.autoUpdater.getStatus().downloadedAt).toBe('number');
260
+ ctx.expect(ctx.manager.storage.get(`${STORAGE_KEY}.pendingUpdate`)).toBeUndefined();
261
+ } finally { await restore(); }
262
+ },
263
+ },
264
+ {
265
+ name: 'simulate(): the cascade never writes the pendingUpdate storage key',
266
+ run: async (ctx) => {
267
+ const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: null });
268
+ try {
269
+ await ctx.manager.autoUpdater.simulate('available');
270
+ await waitFor(() => ctx.manager.autoUpdater.getStatus().code === 'downloaded', { timeout: 5000 });
271
+
272
+ ctx.expect(typeof ctx.manager.autoUpdater.getStatus().downloadedAt).toBe('number');
273
+ ctx.expect(ctx.manager.storage.get(`${STORAGE_KEY}.pendingUpdate`)).toBeUndefined();
274
+ } finally { await restore(); }
275
+ },
276
+ },
277
+ {
278
+ // Heals profiles an older build already polluted: a simulated record
279
+ // whose version can never be applied would otherwise seed the 30-day
280
+ // gate and force-install the first REAL download on the spot.
281
+ name: 'a stale simulated pendingUpdate cannot seed a real download (30-day gate does not force install)',
282
+ run: async (ctx) => {
283
+ const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: null });
284
+ const updater = ctx.manager.autoUpdater;
285
+ let quitCalled = false;
286
+ let origQuit;
287
+ try {
288
+ // A 40-day-old simulator record, then a boot so the reconciler sees it.
289
+ const staleTs = Date.now() - (40 * 24 * 60 * 60 * 1000);
290
+ ctx.manager.storage.set(`${STORAGE_KEY}.pendingUpdate`, { version: '999.0.0', downloadedAt: staleTs });
291
+ updater.shutdown();
292
+ await updater.initialize(ctx.manager);
293
+
294
+ ctx.expect(ctx.manager.storage.get(`${STORAGE_KEY}.pendingUpdate`)).toBe(null);
295
+ ctx.expect(updater._state.downloadedAt).toBe(null);
296
+
297
+ // A real update downloads: it starts its OWN 30-day clock.
298
+ const before = Date.now();
299
+ origQuit = updater._library.quitAndInstall;
300
+ updater._library.quitAndInstall = () => { quitCalled = true; };
301
+ updater._library.emit('update-downloaded', { version: '2.0.0' });
302
+
303
+ const stored = ctx.manager.storage.get(`${STORAGE_KEY}.pendingUpdate`);
304
+ ctx.expect(stored.version).toBe('2.0.0');
305
+ ctx.expect(stored.downloadedAt).not.toBeLessThan(before);
306
+ ctx.expect(updater._state.downloadedAt).toBe(stored.downloadedAt);
307
+ ctx.expect(quitCalled).toBe(false);
308
+ } finally {
309
+ // The spy sat on the shared electron-updater singleton.
310
+ if (origQuit) updater._library.quitAndInstall = origQuit;
311
+ ctx.manager._allowQuit = false;
312
+ await restore();
313
+ }
314
+ },
315
+ },
316
+ {
317
+ // A REAL download, not the simulator: the simulator deliberately never
318
+ // writes this key (see the two cases above).
319
+ name: 'first download persists pendingUpdate to storage',
320
+ run: async (ctx) => {
321
+ const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: null });
322
+ const updater = ctx.manager.autoUpdater;
323
+ try {
324
+ updater._library.emit('update-downloaded', { version: '2.0.0' });
325
+ ctx.expect(updater.getStatus().code).toBe('downloaded');
326
+
327
+ const stored = ctx.manager.storage.get(`${STORAGE_KEY}.pendingUpdate`);
328
+ ctx.expect(stored).toBeDefined();
329
+ ctx.expect(stored.version).toBe('2.0.0');
330
+ ctx.expect(typeof stored.downloadedAt).toBe('number');
331
+ } finally { await restore(); }
332
+ },
333
+ },
334
+ {
335
+ name: 'subsequent download keeps downloadedAt but tracks the NEW version (first timer wins, clear-on-apply matches)',
336
+ run: async (ctx) => {
337
+ const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: null });
338
+ try {
339
+ // Plant an older pending-update record manually.
340
+ const oldTs = Date.now() - (10 * 24 * 60 * 60 * 1000); // 10 days ago
341
+ ctx.manager.storage.set(`${STORAGE_KEY}.pendingUpdate`, { version: '888.0.0', downloadedAt: oldTs });
342
+
343
+ // Call _recordDownloadedAt for a "new" download.
344
+ ctx.manager.autoUpdater._recordDownloadedAt('2.0.0');
345
+
346
+ const stored = ctx.manager.storage.get(`${STORAGE_KEY}.pendingUpdate`);
347
+ // Timer unchanged (first-download-wins) — but the version must track
348
+ // the newest download: after installing 2.0.0, the clear-on-apply
349
+ // check (pending.version === getVersion()) has to match, or the stale
350
+ // flag would force-install every future download instantly forever.
351
+ ctx.expect(stored.downloadedAt).toBe(oldTs);
352
+ ctx.expect(stored.version).toBe('2.0.0');
353
+ } finally { await restore(); }
354
+ },
355
+ },
356
+ {
357
+ name: '30-day gate: pending update older than maxAgeMs forces installNow',
358
+ run: async (ctx) => {
359
+ const restore = await reinit(ctx, {});
360
+ let installCalled = false;
361
+ try {
362
+ // Override installNow to capture the trigger without actually quitting.
363
+ const origInstall = ctx.manager.autoUpdater.installNow;
364
+ ctx.manager.autoUpdater.installNow = async () => { installCalled = true; return true; };
365
+
366
+ // Plant an old pending-update.
367
+ const oldTs = Date.now() - (31 * 24 * 60 * 60 * 1000);
368
+ ctx.manager.autoUpdater._state.downloadedAt = oldTs;
369
+ ctx.manager.autoUpdater._options.maxAgeMs = 30 * 24 * 60 * 60 * 1000;
370
+
371
+ const triggered = ctx.manager.autoUpdater._enforceMaxAgeGate();
372
+ ctx.expect(triggered).toBe(true);
373
+ ctx.expect(installCalled).toBe(true);
374
+
375
+ ctx.manager.autoUpdater.installNow = origInstall;
376
+ } finally { await restore(); }
377
+ },
378
+ },
379
+ {
380
+ // wave-5 F3: at reconcile the gate provably CANNOT install (state is
381
+ // 'idle' and installNow requires 'downloaded') — the install must fire
382
+ // when the startup re-download lands, via the update-downloaded handler.
383
+ name: '30-day gate: carried-over stale pending installs when update-downloaded fires (real installNow state guard)',
384
+ run: async (ctx) => {
385
+ const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: null });
386
+ const updater = ctx.manager.autoUpdater;
387
+ let quitCalled = false;
388
+ let origQuit;
389
+ try {
390
+ // Plant a >30-day pending update, then reconcile like a fresh boot.
391
+ const oldTs = Date.now() - (31 * 24 * 60 * 60 * 1000);
392
+ ctx.manager.storage.set(`${STORAGE_KEY}.pendingUpdate`, { version: '777.0.0', downloadedAt: oldTs });
393
+ updater._options.maxAgeMs = 30 * 24 * 60 * 60 * 1000;
394
+ updater._reconcilePendingUpdate();
395
+
396
+ // Reconcile alone cannot install — the REAL installNow refuses on 'idle'.
397
+ ctx.expect(updater._state.code).toBe('idle');
398
+ ctx.expect(await updater.installNow()).toBe(false);
399
+
400
+ // The startup re-download lands: the wired update-downloaded handler
401
+ // must enforce the gate against the restored ORIGINAL downloadedAt.
402
+ origQuit = updater._library.quitAndInstall;
403
+ updater._library.quitAndInstall = () => { quitCalled = true; };
404
+ updater._library.emit('update-downloaded', { version: '777.0.0' });
405
+ ctx.expect(updater._state.code).toBe('downloaded');
406
+ ctx.expect(updater._state.downloadedAt).toBe(oldTs); // first-download-wins
407
+ ctx.expect(quitCalled).toBe(true);
408
+ } finally {
409
+ // The spy sat on the shared electron-updater singleton; the install
410
+ // path also flipped the manager's quit latch — undo both.
411
+ if (origQuit) updater._library.quitAndInstall = origQuit;
412
+ ctx.manager._allowQuit = false;
413
+ await restore();
414
+ }
415
+ },
416
+ },
417
+ {
418
+ name: '30-day gate: fresh pending update does NOT force install',
419
+ run: async (ctx) => {
420
+ const restore = await reinit(ctx, {});
421
+ try {
422
+ ctx.manager.autoUpdater._state.downloadedAt = Date.now() - (5 * 24 * 60 * 60 * 1000);
423
+ ctx.manager.autoUpdater._options.maxAgeMs = 30 * 24 * 60 * 60 * 1000;
424
+
425
+ const triggered = ctx.manager.autoUpdater._enforceMaxAgeGate();
426
+ ctx.expect(triggered).toBe(false);
427
+ } finally { await restore(); }
428
+ },
429
+ },
430
+ {
431
+ name: 'pendingUpdate cleared when current version matches (update was applied)',
432
+ run: async (ctx) => {
433
+ const restore = await reinit(ctx, {});
434
+ try {
435
+ // Stash a pending-update entry whose version matches our current package version.
436
+ const currentVersion = ctx.manager.getVersion();
437
+ ctx.expect(typeof currentVersion).toBe('string');
438
+
439
+ ctx.manager.storage.set(`${STORAGE_KEY}.pendingUpdate`, {
440
+ version: currentVersion,
441
+ downloadedAt: Date.now() - (5 * 24 * 60 * 60 * 1000),
442
+ });
443
+
444
+ // Re-init triggers the reconciler.
445
+ ctx.manager.autoUpdater.shutdown();
446
+ await ctx.manager.autoUpdater.initialize(ctx.manager);
447
+
448
+ const stillThere = ctx.manager.storage.get(`${STORAGE_KEY}.pendingUpdate`);
449
+ ctx.expect(stillThere).toBe(null);
450
+ } finally { await restore(); }
451
+ },
452
+ },
453
+ {
454
+ name: 'checkNow returns current status object',
455
+ run: async (ctx) => {
456
+ const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: 'unavailable' });
457
+ try {
458
+ const result = await ctx.manager.autoUpdater.checkNow();
459
+ ctx.expect(typeof result.code).toBe('string');
460
+ ctx.expect('percent' in result).toBe(true);
461
+ } finally { await restore(); }
462
+ },
463
+ },
464
+ {
465
+ name: 'IPC handler registered: desktop:auto-updater:status returns status',
466
+ run: async (ctx) => {
467
+ const result = await ctx.manager.ipc.invoke('desktop:auto-updater:status');
468
+ ctx.expect(typeof result.code).toBe('string');
469
+ },
470
+ },
471
+ {
472
+ name: 'enabled=false: skip wiring, no library, no interval',
473
+ run: async (ctx) => {
474
+ ctx.manager.autoUpdater.shutdown();
475
+ const cfg = ctx.manager.config;
476
+ const orig = cfg.autoUpdate;
477
+ cfg.autoUpdate = { enabled: false };
478
+ try {
479
+ await ctx.manager.autoUpdater.initialize(ctx.manager);
480
+ ctx.expect(ctx.manager.autoUpdater._feedCheckIntervalId).toBe(null);
481
+ ctx.expect(ctx.manager.autoUpdater._idleEvalIntervalId).toBe(null);
482
+ ctx.expect(ctx.manager.autoUpdater._library).toBe(null);
483
+ } finally {
484
+ cfg.autoUpdate = orig;
485
+ ctx.manager.autoUpdater.shutdown();
486
+ await ctx.manager.autoUpdater.initialize(ctx.manager);
487
+ }
488
+ },
489
+ },
490
+
491
+ // ─── Idle-aware install (1.2.33 + 1.2.38 refactor) ───────────────────────────
492
+ //
493
+ // The post-download install path is now centralized in the periodic tick. Tests
494
+ // exercise _evaluateIdleInstall() directly with stubbed installNow + _promptToInstall
495
+ // so we don't need a real BrowserWindow/dialog and don't actually quit the test.
496
+
497
+ {
498
+ name: 'markActive() bumps _lastActivityAt to current time',
499
+ run: async (ctx) => {
500
+ const u = ctx.manager.autoUpdater;
501
+ u._lastActivityAt = 0;
502
+ const before = Date.now();
503
+ u.markActive();
504
+ const after = Date.now();
505
+ ctx.expect(u._lastActivityAt >= before).toBe(true);
506
+ ctx.expect(u._lastActivityAt <= after).toBe(true);
507
+ },
508
+ },
509
+ {
510
+ name: '_onActivityIpc routes through to markActive',
511
+ run: async (ctx) => {
512
+ const u = ctx.manager.autoUpdater;
513
+ u._lastActivityAt = 0;
514
+ u._onActivityIpc();
515
+ ctx.expect(u._lastActivityAt > 0).toBe(true);
516
+ },
517
+ },
518
+ {
519
+ name: 'IPC channel desktop:auto-updater:activity is registered + bumps activity timestamp',
520
+ run: async (ctx) => {
521
+ const u = ctx.manager.autoUpdater;
522
+ // initialize() registered the listener — verify presence + simulate an inbound
523
+ // renderer message by invoking each registered listener directly. In-process
524
+ // this matches what `ipcMain.on(channel, ...)` does on a real `ipcRenderer.send`.
525
+ const listeners = ctx.manager.ipc._listeners['desktop:auto-updater:activity'];
526
+ ctx.expect(listeners).toBeDefined();
527
+ ctx.expect(listeners.size > 0).toBe(true);
528
+ u._lastActivityAt = 0;
529
+ listeners.forEach((fn) => fn());
530
+ ctx.expect(u._lastActivityAt > 0).toBe(true);
531
+ },
532
+ },
533
+ {
534
+ name: 'idle-install: when idle ≥ threshold + state=downloaded, installNow fires',
535
+ run: async (ctx) => {
536
+ const u = ctx.manager.autoUpdater;
537
+ const origInstall = u.installNow;
538
+ let installCalled = false;
539
+ u.installNow = async () => { installCalled = true; return true; };
540
+ try {
541
+ // Force state into "downloaded", non-user-initiated, with idle past threshold.
542
+ u._state = { ...u._state, code: 'downloaded', version: '1.2.3' };
543
+ u._userInitiated = false;
544
+ u._promptedForVersion = null;
545
+ // Simulate 16 minutes of idle.
546
+ u._lastActivityAt = Date.now() - (16 * 60 * 1000);
547
+ // Bypass dev-mode bail so we exercise the real path.
548
+ const origIsSimulating = u._isSimulating;
549
+ u._isSimulating = () => false;
550
+ try {
551
+ u._evaluateIdleInstall();
552
+ } finally { u._isSimulating = origIsSimulating; }
553
+ ctx.expect(installCalled).toBe(true);
554
+ } finally { u.installNow = origInstall; }
555
+ },
556
+ },
557
+ {
558
+ name: 'idle-install: when active, prompt fires once per version (no install)',
559
+ run: async (ctx) => {
560
+ const u = ctx.manager.autoUpdater;
561
+ const origInstall = u.installNow;
562
+ const origPrompt = u._promptToInstall;
563
+ let installCalled = false;
564
+ let promptCalls = 0;
565
+ u.installNow = async () => { installCalled = true; return true; };
566
+ u._promptToInstall = async (v) => { promptCalls++; };
567
+ try {
568
+ u._state = { ...u._state, code: 'downloaded', version: '1.2.3' };
569
+ u._userInitiated = false;
570
+ u._promptedForVersion = null;
571
+ u._lastActivityAt = Date.now(); // active right now
572
+ const origIsSimulating = u._isSimulating;
573
+ u._isSimulating = () => false;
574
+ try {
575
+ u._evaluateIdleInstall();
576
+ u._evaluateIdleInstall();
577
+ u._evaluateIdleInstall();
578
+ } finally { u._isSimulating = origIsSimulating; }
579
+ ctx.expect(installCalled).toBe(false); // user is active — no surprise install
580
+ ctx.expect(promptCalls).toBe(1); // dedup'd by _promptedForVersion
581
+ ctx.expect(u._promptedForVersion).toBe('1.2.3');
582
+ } finally {
583
+ u.installNow = origInstall;
584
+ u._promptToInstall = origPrompt;
585
+ }
586
+ },
587
+ },
588
+ {
589
+ name: 'idle-install: a NEW downloaded version re-arms the prompt',
590
+ run: async (ctx) => {
591
+ const u = ctx.manager.autoUpdater;
592
+ const origPrompt = u._promptToInstall;
593
+ let promptCalls = 0;
594
+ u._promptToInstall = async (v) => { promptCalls++; };
595
+ try {
596
+ u._userInitiated = false;
597
+ u._promptedForVersion = null;
598
+ u._lastActivityAt = Date.now();
599
+ const origIsSimulating = u._isSimulating;
600
+ u._isSimulating = () => false;
601
+ try {
602
+ u._state = { ...u._state, code: 'downloaded', version: '1.2.3' };
603
+ u._evaluateIdleInstall();
604
+ u._evaluateIdleInstall();
605
+ ctx.expect(promptCalls).toBe(1);
606
+
607
+ // Newer download — version flips. _promptedForVersion still 1.2.3, so 1.2.4 should re-prompt.
608
+ u._state = { ...u._state, code: 'downloaded', version: '1.2.4' };
609
+ u._evaluateIdleInstall();
610
+ ctx.expect(promptCalls).toBe(2);
611
+ ctx.expect(u._promptedForVersion).toBe('1.2.4');
612
+ } finally { u._isSimulating = origIsSimulating; }
613
+ } finally { u._promptToInstall = origPrompt; }
614
+ },
615
+ },
616
+ {
617
+ name: 'idle-install: skipped entirely when state !== downloaded',
618
+ run: async (ctx) => {
619
+ const u = ctx.manager.autoUpdater;
620
+ const origInstall = u.installNow;
621
+ const origPrompt = u._promptToInstall;
622
+ let installCalled = false;
623
+ let promptCalls = 0;
624
+ u.installNow = async () => { installCalled = true; return true; };
625
+ u._promptToInstall = async () => { promptCalls++; };
626
+ try {
627
+ for (const code of ['idle', 'checking', 'available', 'downloading', 'not-available', 'error']) {
628
+ u._state = { ...u._state, code, version: '1.2.3' };
629
+ u._userInitiated = false;
630
+ u._lastActivityAt = Date.now() - (16 * 60 * 1000); // idle, but state isn't downloaded
631
+ const origIsSimulating = u._isSimulating;
632
+ u._isSimulating = () => false;
633
+ try { u._evaluateIdleInstall(); } finally { u._isSimulating = origIsSimulating; }
634
+ }
635
+ ctx.expect(installCalled).toBe(false);
636
+ ctx.expect(promptCalls).toBe(0);
637
+ } finally {
638
+ u.installNow = origInstall;
639
+ u._promptToInstall = origPrompt;
640
+ }
641
+ },
642
+ },
643
+ {
644
+ name: 'idle-install: skipped when _userInitiated=true (consumer UI owns the path)',
645
+ run: async (ctx) => {
646
+ const u = ctx.manager.autoUpdater;
647
+ const origInstall = u.installNow;
648
+ const origPrompt = u._promptToInstall;
649
+ let installCalled = false;
650
+ let promptCalls = 0;
651
+ u.installNow = async () => { installCalled = true; return true; };
652
+ u._promptToInstall = async () => { promptCalls++; };
653
+ try {
654
+ u._state = { ...u._state, code: 'downloaded', version: '1.2.3' };
655
+ u._userInitiated = true;
656
+ u._promptedForVersion = null;
657
+ u._lastActivityAt = Date.now() - (16 * 60 * 1000); // idle
658
+ const origIsSimulating = u._isSimulating;
659
+ u._isSimulating = () => false;
660
+ try { u._evaluateIdleInstall(); } finally { u._isSimulating = origIsSimulating; }
661
+ ctx.expect(installCalled).toBe(false);
662
+ ctx.expect(promptCalls).toBe(0);
663
+ } finally {
664
+ u.installNow = origInstall;
665
+ u._promptToInstall = origPrompt;
666
+ }
667
+ },
668
+ },
669
+ {
670
+ name: 'idle-install: dev mode bails out (no install, no prompt)',
671
+ run: async (ctx) => {
672
+ const u = ctx.manager.autoUpdater;
673
+ const origInstall = u.installNow;
674
+ const origPrompt = u._promptToInstall;
675
+ let installCalled = false;
676
+ let promptCalls = 0;
677
+ u.installNow = async () => { installCalled = true; return true; };
678
+ u._promptToInstall = async () => { promptCalls++; };
679
+ try {
680
+ u._state = { ...u._state, code: 'downloaded', version: '1.2.3' };
681
+ u._userInitiated = false;
682
+ u._lastActivityAt = Date.now() - (16 * 60 * 1000);
683
+ const origIsSimulating = u._isSimulating;
684
+ u._isSimulating = () => true; // simulate OMEGA_DEV_UPDATE set
685
+ try { u._evaluateIdleInstall(); } finally { u._isSimulating = origIsSimulating; }
686
+ ctx.expect(installCalled).toBe(false);
687
+ ctx.expect(promptCalls).toBe(0);
688
+ } finally {
689
+ u.installNow = origInstall;
690
+ u._promptToInstall = origPrompt;
691
+ }
692
+ },
693
+ },
694
+
695
+ // ─── checkNow() concurrency / dedup ──────────────────────────────────────────
696
+
697
+ {
698
+ name: 'checkNow: dedupes — second call while state=checking returns without re-checking',
699
+ run: async (ctx) => {
700
+ const u = ctx.manager.autoUpdater;
701
+ // Stub the underlying library so we can count calls.
702
+ const origLib = u._library;
703
+ let checkCalls = 0;
704
+ u._library = {
705
+ checkForUpdates: async () => { checkCalls++; },
706
+ quitAndInstall: () => {},
707
+ autoDownload: true,
708
+ on: () => {},
709
+ };
710
+ try {
711
+ // Force state to "checking" — _readyToCheck() should refuse new checks.
712
+ u._state = { ...u._state, code: 'checking' };
713
+ await u.checkNow({ userInitiated: false });
714
+ await u.checkNow({ userInitiated: false });
715
+ await u.checkNow({ userInitiated: false });
716
+ ctx.expect(checkCalls).toBe(0);
717
+ } finally { u._library = origLib; }
718
+ },
719
+ },
720
+ {
721
+ name: 'checkNow: dedupes from each non-ready state (downloading, available, downloaded)',
722
+ run: async (ctx) => {
723
+ const u = ctx.manager.autoUpdater;
724
+ const origLib = u._library;
725
+ let checkCalls = 0;
726
+ u._library = {
727
+ checkForUpdates: async () => { checkCalls++; },
728
+ quitAndInstall: () => {},
729
+ autoDownload: true,
730
+ on: () => {},
731
+ };
732
+ try {
733
+ for (const code of ['available', 'downloading', 'downloaded']) {
734
+ u._state = { ...u._state, code };
735
+ await u.checkNow({ userInitiated: false });
736
+ }
737
+ ctx.expect(checkCalls).toBe(0);
738
+ } finally { u._library = origLib; }
739
+ },
740
+ },
741
+ {
742
+ name: 'checkNow: _userInitiated does NOT leak when state is non-ready (1.2.39 fix)',
743
+ run: async (ctx) => {
744
+ // Repro: background periodic check sets _userInitiated=false and starts downloading.
745
+ // User clicks "Check for Updates" — IPC invokes checkNow({userInitiated: true}).
746
+ // Pre-fix: that flipped _userInitiated to true even though the second check was
747
+ // skipped, breaking idle-install when the download completes.
748
+ const u = ctx.manager.autoUpdater;
749
+ u._state = { ...u._state, code: 'downloading' }; // mid-flight
750
+ u._userInitiated = false; // initial periodic check
751
+
752
+ await u.checkNow({ userInitiated: true }); // user clicks — should be ignored
753
+
754
+ ctx.expect(u._userInitiated).toBe(false); // still false — leak prevented
755
+ },
756
+ },
757
+ {
758
+ name: 'checkNow: _userInitiated DOES flip when the call actually performs a check',
759
+ run: async (ctx) => {
760
+ const u = ctx.manager.autoUpdater;
761
+ const origLib = u._library;
762
+ u._library = {
763
+ checkForUpdates: async () => {}, // no-op so the test runs synchronously
764
+ quitAndInstall: () => {},
765
+ autoDownload: true,
766
+ on: () => {},
767
+ };
768
+ try {
769
+ u._state = { ...u._state, code: 'idle' };
770
+ u._userInitiated = false;
771
+ await u.checkNow({ userInitiated: true });
772
+ ctx.expect(u._userInitiated).toBe(true);
773
+ } finally { u._library = origLib; }
774
+ },
775
+ },
776
+ {
777
+ name: 'checkNow: lastCheckedAt updates even when call is deduped',
778
+ run: async (ctx) => {
779
+ // Behavior contract: the timestamp tracks "we considered checking at this moment,"
780
+ // not "we successfully checked." A user click that hits the dedup guard still updates
781
+ // the timestamp so UI surfaces show fresh "checked just now" feedback.
782
+ const u = ctx.manager.autoUpdater;
783
+ u._state = { ...u._state, code: 'checking', lastCheckedAt: 0 };
784
+ const before = Date.now();
785
+ await u.checkNow({ userInitiated: true });
786
+ const after = Date.now();
787
+ ctx.expect(u._state.lastCheckedAt >= before).toBe(true);
788
+ ctx.expect(u._state.lastCheckedAt <= after).toBe(true);
789
+ },
790
+ },
791
+
792
+ // ─── Two-timer separation ────────────────────────────────────────────────────
793
+ //
794
+ // Feed-check (HTTP, expensive, hourly) and idle-eval (in-process, cheap, every
795
+ // minute) MUST stay separate timers — running the feed-check at idle-eval cadence
796
+ // hammers the GitHub release feed at 60× the necessary rate.
797
+
798
+ {
799
+ name: 'feed-check tick: hits the library + runs 30-day gate (no idle eval)',
800
+ run: async (ctx) => {
801
+ const u = ctx.manager.autoUpdater;
802
+ const origLib = u._library;
803
+ const origGate = u._enforceMaxAgeGate;
804
+ const origIdle = u._evaluateIdleInstall;
805
+ const calls = [];
806
+ u._library = {
807
+ checkForUpdates: async () => { calls.push('check'); },
808
+ quitAndInstall: () => {},
809
+ autoDownload: true,
810
+ on: () => {},
811
+ };
812
+ u._enforceMaxAgeGate = () => { calls.push('gate'); return false; };
813
+ u._evaluateIdleInstall = () => { calls.push('idle'); };
814
+ try {
815
+ u._state = { ...u._state, code: 'idle' };
816
+ await u._feedCheckTick();
817
+ ctx.expect(calls).toEqual(['check', 'gate']);
818
+ } finally {
819
+ u._library = origLib;
820
+ u._enforceMaxAgeGate = origGate;
821
+ u._evaluateIdleInstall = origIdle;
822
+ }
823
+ },
824
+ },
825
+ {
826
+ name: 'idle-eval tick: only runs idle eval (no HTTP, no gate)',
827
+ run: async (ctx) => {
828
+ const u = ctx.manager.autoUpdater;
829
+ const origLib = u._library;
830
+ const origGate = u._enforceMaxAgeGate;
831
+ const origIdle = u._evaluateIdleInstall;
832
+ const calls = [];
833
+ u._library = {
834
+ checkForUpdates: async () => { calls.push('check'); },
835
+ quitAndInstall: () => {},
836
+ autoDownload: true,
837
+ on: () => {},
838
+ };
839
+ u._enforceMaxAgeGate = () => { calls.push('gate'); return false; };
840
+ u._evaluateIdleInstall = () => { calls.push('idle'); };
841
+ try {
842
+ u._idleEvalTick();
843
+ ctx.expect(calls).toEqual(['idle']);
844
+ } finally {
845
+ u._library = origLib;
846
+ u._enforceMaxAgeGate = origGate;
847
+ u._evaluateIdleInstall = origIdle;
848
+ }
849
+ },
850
+ },
851
+ {
852
+ name: 'feed-check default cadence is 1h (production), not 1m',
853
+ run: (ctx) => {
854
+ const u = ctx.manager.autoUpdater;
855
+ // Sanity-guard: if someone re-merges the timers or drops the cadence to
856
+ // 60s again, this test fails. Production builds must not hammer the feed.
857
+ ctx.expect(u._options.feedCheckIntervalMs).toBe(60 * 60 * 1000);
858
+ ctx.expect(u._options.idleEvalIntervalMs).toBe(1 * 60 * 1000);
859
+ },
860
+ },
861
+ {
862
+ name: 'full update sequence: download → idle wait → auto-install fires',
863
+ run: async (ctx) => {
864
+ // The whole point of the new system: drive a real update through the dev simulator,
865
+ // then exercise the post-download decision path with stubbed installNow.
866
+ const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: 'available' });
867
+ const u = ctx.manager.autoUpdater;
868
+ const origInstall = u.installNow;
869
+ const origIsSimulating = u._isSimulating;
870
+ let installCalled = false;
871
+ u.installNow = async () => { installCalled = true; return true; };
872
+ try {
873
+ await u.checkNow({ userInitiated: false });
874
+ await waitFor(() => u.getStatus().code === 'downloaded', { timeout: 5000 });
875
+
876
+ // Now at "downloaded". Force user-idle past threshold + take dev-mode out of the picture.
877
+ u._lastActivityAt = Date.now() - (16 * 60 * 1000);
878
+ u._isSimulating = () => false;
879
+ // Run the same evaluation the periodic tick would.
880
+ u._evaluateIdleInstall();
881
+ ctx.expect(installCalled).toBe(true);
882
+ } finally {
883
+ u.installNow = origInstall;
884
+ u._isSimulating = origIsSimulating;
885
+ await restore();
886
+ }
887
+ },
888
+ },
889
+ {
890
+ name: 'full update sequence: download → user-active → prompt fires (no install)',
891
+ run: async (ctx) => {
892
+ const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: 'available' });
893
+ const u = ctx.manager.autoUpdater;
894
+ const origInstall = u.installNow;
895
+ const origPrompt = u._promptToInstall;
896
+ const origIsSimulating = u._isSimulating;
897
+ let installCalled = false;
898
+ let promptedVersion = null;
899
+ u.installNow = async () => { installCalled = true; return true; };
900
+ u._promptToInstall = async (v) => { promptedVersion = v; };
901
+ try {
902
+ await u.checkNow({ userInitiated: false });
903
+ await waitFor(() => u.getStatus().code === 'downloaded', { timeout: 5000 });
904
+
905
+ u._lastActivityAt = Date.now(); // active
906
+ u._isSimulating = () => false;
907
+ u._evaluateIdleInstall();
908
+
909
+ ctx.expect(installCalled).toBe(false);
910
+ ctx.expect(promptedVersion).toBe('999.0.0');
911
+ ctx.expect(u._promptedForVersion).toBe('999.0.0');
912
+ } finally {
913
+ u.installNow = origInstall;
914
+ u._promptToInstall = origPrompt;
915
+ u._isSimulating = origIsSimulating;
916
+ await restore();
917
+ }
918
+ },
919
+ },
920
+
921
+ // ─── Real-time integration test (drives the actual periodic tick) ───────────
922
+ //
923
+ // Where the unit tests above fake the clock + poke `_evaluateIdleInstall` directly,
924
+ // this one drives a REAL update through the dev simulator AND lets the actual
925
+ // periodic tick fire on its own. With `manager.isTesting() === true`, the tick
926
+ // cadence drops to IDLE_TICK_MS_TESTING (500ms) and the idle threshold drops to
927
+ // IDLE_INSTALL_THRESHOLD_MS_TESTING (3s) — so the full sequence completes inside
928
+ // ~5s instead of ~15min.
929
+ //
930
+ // We stub `installNow` so the test process doesn't actually quit, and lift the
931
+ // dev-mode bail in `_evaluateIdleInstall` so the simulated download exercises the
932
+ // real install decision path.
933
+
934
+ {
935
+ name: 'real-time end-to-end: download fires, threshold elapses, install triggers via periodic tick',
936
+ run: async (ctx) => {
937
+ const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: 'available' });
938
+ const u = ctx.manager.autoUpdater;
939
+ const origInstall = u.installNow;
940
+ const origIsSimulating = u._isSimulating;
941
+ let installCalled = false;
942
+ u.installNow = async () => { installCalled = true; return true; };
943
+ u._isSimulating = () => false; // pretend we're in production so the eval runs
944
+ try {
945
+ // Drive the dev simulator to "downloaded".
946
+ await u.checkNow({ userInitiated: false });
947
+ await waitFor(() => u.getStatus().code === 'downloaded', { timeout: 5000 });
948
+
949
+ // Mark idle — _lastActivityAt was bumped by checkNow paths, push it back
950
+ // past the testing threshold (3s + small margin).
951
+ u._lastActivityAt = Date.now() - 4000;
952
+
953
+ // Wait for the real periodic tick (500ms cadence in tests) to fire and
954
+ // call our stubbed installNow. Bound at 3s to keep the test fast.
955
+ await waitFor(() => installCalled, { timeout: 3000 });
956
+ ctx.expect(installCalled).toBe(true);
957
+ } finally {
958
+ u.installNow = origInstall;
959
+ u._isSimulating = origIsSimulating;
960
+ await restore();
961
+ }
962
+ },
963
+ },
964
+ {
965
+ name: 'real-time end-to-end: download fires while user-active → prompt fires, install does NOT',
966
+ run: async (ctx) => {
967
+ const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: 'available' });
968
+ const u = ctx.manager.autoUpdater;
969
+ const origInstall = u.installNow;
970
+ const origPrompt = u._promptToInstall;
971
+ const origIsSimulating = u._isSimulating;
972
+ let installCalled = false;
973
+ let promptedVersion = null;
974
+ u.installNow = async () => { installCalled = true; return true; };
975
+ u._promptToInstall = async (v) => { promptedVersion = v; };
976
+ u._isSimulating = () => false;
977
+ // Keep "the user" active throughout — the dev simulator takes ~2s to download
978
+ // (4 × 400ms ticks), and we want the activity timer to stay <3s old at all times.
979
+ // Bump every 500ms; the 3s threshold can never elapse if we keep doing this.
980
+ const keepActive = setInterval(() => u.markActive(), 500);
981
+ try {
982
+ await u.checkNow({ userInitiated: false });
983
+ await waitFor(() => u.getStatus().code === 'downloaded', { timeout: 5000 });
984
+
985
+ // Bump once explicitly the moment download lands — guarantees fresh activity
986
+ // when the next periodic tick (500ms) evaluates.
987
+ u.markActive();
988
+
989
+ // Wait for the prompt to fire. The first periodic tick after download lands
990
+ // sees "active + state=downloaded" → calls _promptToInstall.
991
+ await waitFor(() => promptedVersion !== null, { timeout: 2500 });
992
+
993
+ ctx.expect(promptedVersion).toBe('999.0.0');
994
+ ctx.expect(installCalled).toBe(false);
995
+ ctx.expect(u._promptedForVersion).toBe('999.0.0');
996
+ } finally {
997
+ clearInterval(keepActive);
998
+ u.installNow = origInstall;
999
+ u._promptToInstall = origPrompt;
1000
+ u._isSimulating = origIsSimulating;
1001
+ await restore();
1002
+ }
1003
+ },
1004
+ },
1005
+
1006
+ // ─── Mode helpers (@omega.js/backend-pattern: isDevelopment / isProduction / isTesting) ────
1007
+
1008
+ {
1009
+ name: 'manager.isTesting() returns true under OMEGA_TEST_MODE (set by test runner)',
1010
+ run: (ctx) => {
1011
+ ctx.expect(typeof ctx.manager.isTesting).toBe('function');
1012
+ ctx.expect(ctx.manager.isTesting()).toBe(true);
1013
+ },
1014
+ },
1015
+ {
1016
+ name: 'manager.isDevelopment() is false during tests (testing takes precedence)',
1017
+ run: (ctx) => {
1018
+ // The test runs unpackaged, but OMEGA_TEST_MODE=true → testing wins, so this is a
1019
+ // TEST environment, not development. isDevelopment() is therefore false.
1020
+ ctx.expect(typeof ctx.manager.isDevelopment).toBe('function');
1021
+ ctx.expect(ctx.manager.isDevelopment()).toBe(false);
1022
+ ctx.expect(ctx.manager.isTesting()).toBe(true);
1023
+ },
1024
+ },
1025
+ {
1026
+ name: 'manager environments are mutually exclusive (exactly one true)',
1027
+ run: (ctx) => {
1028
+ const flags = [ctx.manager.isDevelopment(), ctx.manager.isTesting(), ctx.manager.isProduction()];
1029
+ ctx.expect(flags.filter(Boolean).length).toBe(1);
1030
+ ctx.expect(ctx.manager.isProduction()).toBe(false);
1031
+ },
1032
+ },
1033
+ {
1034
+ name: 'auto-updater._idleThresholdMs() returns 3s in tests, 15min in prod',
1035
+ run: (ctx) => {
1036
+ const u = ctx.manager.autoUpdater;
1037
+ // Test mode currently active (manager.isTesting() === true).
1038
+ ctx.expect(u._idleThresholdMs()).toBe(3000);
1039
+
1040
+ // Stub manager.isTesting() to simulate prod and verify the threshold flips.
1041
+ const origIsTesting = ctx.manager.isTesting;
1042
+ ctx.manager.isTesting = () => false;
1043
+ try {
1044
+ ctx.expect(u._idleThresholdMs()).toBe(15 * 60 * 1000);
1045
+ } finally { ctx.manager.isTesting = origIsTesting; }
1046
+ },
1047
+ },
1048
+
1049
+ // ─── Misc internals ──────────────────────────────────────────────────────────
1050
+
1051
+ {
1052
+ name: 'shutdown clears pending timers + interval + idle-install state',
1053
+ run: async (ctx) => {
1054
+ const restore = await reinit(ctx, {});
1055
+ const u = ctx.manager.autoUpdater;
1056
+ try {
1057
+ u._pendingTimers.push(setTimeout(() => {}, 1000000));
1058
+ u._promptedForVersion = '1.2.3';
1059
+ u.shutdown();
1060
+ ctx.expect(u._feedCheckIntervalId).toBe(null);
1061
+ ctx.expect(u._idleEvalIntervalId).toBe(null);
1062
+ ctx.expect(u._pendingTimers).toEqual([]);
1063
+ ctx.expect(u._initialized).toBe(false);
1064
+ ctx.expect(u._library).toBe(null);
1065
+ ctx.expect(u._userInitiated).toBe(false);
1066
+ ctx.expect(u._promptedForVersion).toBe(null);
1067
+ ctx.expect(u._activityHooksWired).toBe(false);
1068
+ } finally { await restore(); }
1069
+ },
1070
+ },
1071
+ {
1072
+ name: '_menuItemFieldsForState produces correct label per state',
1073
+ run: (ctx) => {
1074
+ const u = ctx.manager.autoUpdater;
1075
+ const cases = [
1076
+ ['idle', 'Check for Updates...', true],
1077
+ ['error', 'Check for Updates...', true],
1078
+ ['not-available', "You're up to date", true],
1079
+ ['checking', 'Checking for Updates...', false],
1080
+ ];
1081
+ for (const [code, label, enabled] of cases) {
1082
+ u._state = { ...u._state, code, version: null, percent: 0 };
1083
+ const r = u._menuItemFieldsForState();
1084
+ ctx.expect(r.label).toBe(label);
1085
+ ctx.expect(r.enabled).toBe(enabled);
1086
+ }
1087
+ // version-bearing states
1088
+ u._state = { ...u._state, code: 'available', version: '2.0.0' };
1089
+ ctx.expect(u._menuItemFieldsForState().label).toBe('Downloading Update v2.0.0...');
1090
+ ctx.expect(u._menuItemFieldsForState().enabled).toBe(false);
1091
+
1092
+ u._state = { ...u._state, code: 'downloaded', version: '2.0.0' };
1093
+ ctx.expect(u._menuItemFieldsForState().label).toBe('Restart to Update v2.0.0');
1094
+ ctx.expect(u._menuItemFieldsForState().enabled).toBe(true);
1095
+
1096
+ u._state = { ...u._state, code: 'downloading', percent: 42 };
1097
+ ctx.expect(u._menuItemFieldsForState().label).toBe('Downloading Update (42%)');
1098
+ ctx.expect(u._menuItemFieldsForState().enabled).toBe(false);
1099
+ },
1100
+ },
1101
+ {
1102
+ name: 'IPC: desktop:auto-updater:check-now invokes checkNow with userInitiated=true',
1103
+ run: async (ctx) => {
1104
+ const u = ctx.manager.autoUpdater;
1105
+ const origCheck = u.checkNow;
1106
+ let receivedOpts = null;
1107
+ u.checkNow = async (opts) => { receivedOpts = opts; return u.getStatus(); };
1108
+ try {
1109
+ await ctx.manager.ipc.invoke('desktop:auto-updater:check-now');
1110
+ ctx.expect(receivedOpts).toBeDefined();
1111
+ ctx.expect(receivedOpts.userInitiated).toBe(true);
1112
+ } finally { u.checkNow = origCheck; }
1113
+ },
1114
+ },
1115
+ {
1116
+ name: 'IPC: desktop:auto-updater:install-now invokes installNow',
1117
+ run: async (ctx) => {
1118
+ const u = ctx.manager.autoUpdater;
1119
+ const origInstall = u.installNow;
1120
+ let installCalled = false;
1121
+ u.installNow = async () => { installCalled = true; return false; };
1122
+ try {
1123
+ await ctx.manager.ipc.invoke('desktop:auto-updater:install-now');
1124
+ ctx.expect(installCalled).toBe(true);
1125
+ } finally { u.installNow = origInstall; }
1126
+ },
1127
+ },
1128
+ {
1129
+ name: 'installNow: returns false when state is not "downloaded"',
1130
+ run: async (ctx) => {
1131
+ const u = ctx.manager.autoUpdater;
1132
+ u._state = { ...u._state, code: 'idle' };
1133
+ const r = await u.installNow();
1134
+ ctx.expect(r).toBe(false);
1135
+ },
1136
+ },
1137
+ ],
1138
+ });