@omega.js/extension 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 (684) hide show
  1. package/LICENSE +98 -0
  2. package/README.md +194 -0
  3. package/bin/omega +2 -0
  4. package/bin/omega-extension +2 -0
  5. package/bin/omg +2 -0
  6. package/dist/assets/css/bundles/.gitkeep +0 -0
  7. package/dist/assets/css/components/content/index.scss +5 -0
  8. package/dist/assets/css/components/options/index.scss +9 -0
  9. package/dist/assets/css/components/pages/index.scss +5 -0
  10. package/dist/assets/css/components/popup/index.scss +9 -0
  11. package/dist/assets/css/core/_animations.scss +64 -0
  12. package/dist/assets/css/core/_fontawesome.scss +146 -0
  13. package/dist/assets/css/core/_initialize.scss +23 -0
  14. package/dist/assets/css/core/_utilities.scss +80 -0
  15. package/dist/assets/css/omega-extension.scss +43 -0
  16. package/dist/assets/css/shell/_index.scss +207 -0
  17. package/dist/assets/css/tokens/_index.scss +166 -0
  18. package/dist/assets/js/core/app-shell.js +136 -0
  19. package/dist/assets/js/libs/initialize-tooltips.js +31 -0
  20. package/dist/assets/js/libs/recaptcha.js +94 -0
  21. package/dist/assets/js/main.js +4 -0
  22. package/dist/assets/themes/_template/README.md +115 -0
  23. package/dist/assets/themes/_template/_config.scss +60 -0
  24. package/dist/assets/themes/_template/_theme.js +14 -0
  25. package/dist/assets/themes/_template/_theme.scss +28 -0
  26. package/dist/assets/themes/_template/css/base/_root.scss +19 -0
  27. package/dist/assets/themes/_template/css/components/_components.scss +23 -0
  28. package/dist/assets/themes/base/README.md +5 -0
  29. package/dist/assets/themes/base/_components/data/org-chart/component.html +33 -0
  30. package/dist/assets/themes/base/_components/data/org-chart/component.json5 +33 -0
  31. package/dist/assets/themes/base/_components/data/org-chart/component.scss +183 -0
  32. package/dist/assets/themes/base/_components/heading/masthead/component.html +22 -0
  33. package/dist/assets/themes/base/_components/heading/masthead/component.json5 +30 -0
  34. package/dist/assets/themes/base/_components/heading/section-head/component.html +18 -0
  35. package/dist/assets/themes/base/_components/heading/section-head/component.json5 +27 -0
  36. package/dist/assets/themes/base/_components/pricing/features/component.html +29 -0
  37. package/dist/assets/themes/base/_components/pricing/features/component.json5 +30 -0
  38. package/dist/assets/themes/base/_hero/orbit/index.html +30 -0
  39. package/dist/assets/themes/base/_hero/orbit/script.js +33 -0
  40. package/dist/assets/themes/base/_hero/orbit/style.scss +133 -0
  41. package/dist/assets/themes/base/_includes/admin/sections/sidebar.html +3 -0
  42. package/dist/assets/themes/base/_includes/admin/sections/topbar.html +3 -0
  43. package/dist/assets/themes/base/_includes/backend/sections/sidebar.html +3 -0
  44. package/dist/assets/themes/base/_includes/backend/sections/topbar.html +3 -0
  45. package/dist/assets/themes/base/_includes/frontend/components/connection-card.html +49 -0
  46. package/dist/assets/themes/base/_includes/frontend/components/post-card.html +54 -0
  47. package/dist/assets/themes/base/_includes/frontend/sections/account-connections.html +42 -0
  48. package/dist/assets/themes/base/_includes/frontend/sections/account-section-header.html +11 -0
  49. package/dist/assets/themes/base/_includes/frontend/sections/footer.html +154 -0
  50. package/dist/assets/themes/base/_includes/frontend/sections/nav.html +276 -0
  51. package/dist/assets/themes/base/_includes/global/sections/account.html +72 -0
  52. package/dist/assets/themes/base/_includes/global/sections/app-sidebar.html +248 -0
  53. package/dist/assets/themes/base/_includes/global/sections/app-topbar.html +228 -0
  54. package/dist/assets/themes/base/_includes/global/sections/page-header.html +99 -0
  55. package/dist/assets/themes/base/_layouts/admin/core/minimal-viewport-locked.html +22 -0
  56. package/dist/assets/themes/base/_layouts/admin/core/minimal.html +23 -0
  57. package/dist/assets/themes/base/_layouts/backend/core/base.html +57 -0
  58. package/dist/assets/themes/base/_layouts/backend/core/minimal-viewport-locked.html +32 -0
  59. package/dist/assets/themes/base/_layouts/backend/core/minimal.html +32 -0
  60. package/dist/assets/themes/base/_layouts/backend/pages/dashboard/index.html +230 -0
  61. package/dist/assets/themes/base/_layouts/frontend/core/base.html +31 -0
  62. package/dist/assets/themes/base/_layouts/frontend/core/cover.html +40 -0
  63. package/dist/assets/themes/base/_layouts/frontend/core/minimal.html +47 -0
  64. package/dist/assets/themes/base/_layouts/frontend/pages/404.html +54 -0
  65. package/dist/assets/themes/base/_layouts/frontend/pages/about.html +144 -0
  66. package/dist/assets/themes/base/_layouts/frontend/pages/account/index.html +2104 -0
  67. package/dist/assets/themes/base/_layouts/frontend/pages/alternatives/alternative.html +319 -0
  68. package/dist/assets/themes/base/_layouts/frontend/pages/alternatives/index.html +186 -0
  69. package/dist/assets/themes/base/_layouts/frontend/pages/app.html +142 -0
  70. package/dist/assets/themes/base/_layouts/frontend/pages/auth/reset.html +53 -0
  71. package/dist/assets/themes/base/_layouts/frontend/pages/auth/signin.html +90 -0
  72. package/dist/assets/themes/base/_layouts/frontend/pages/auth/signup.html +141 -0
  73. package/dist/assets/themes/base/_layouts/frontend/pages/auth/token.html +44 -0
  74. package/dist/assets/themes/base/_layouts/frontend/pages/blog/categories/category.html +88 -0
  75. package/dist/assets/themes/base/_layouts/frontend/pages/blog/categories/index.html +88 -0
  76. package/dist/assets/themes/base/_layouts/frontend/pages/blog/index.html +255 -0
  77. package/dist/assets/themes/base/_layouts/frontend/pages/blog/post.html +261 -0
  78. package/dist/assets/themes/base/_layouts/frontend/pages/blog/tags/index.html +84 -0
  79. package/dist/assets/themes/base/_layouts/frontend/pages/blog/tags/tag.html +88 -0
  80. package/dist/assets/themes/base/_layouts/frontend/pages/collection/category.html +72 -0
  81. package/dist/assets/themes/base/_layouts/frontend/pages/collection/document.html +54 -0
  82. package/dist/assets/themes/base/_layouts/frontend/pages/collection/index.html +84 -0
  83. package/dist/assets/themes/base/_layouts/frontend/pages/connections/callback.html +55 -0
  84. package/dist/assets/themes/base/_layouts/frontend/pages/contact.html +214 -0
  85. package/dist/assets/themes/base/_layouts/frontend/pages/download.html +793 -0
  86. package/dist/assets/themes/base/_layouts/frontend/pages/extension/index.html +253 -0
  87. package/dist/assets/themes/base/_layouts/frontend/pages/extension/installed.html +198 -0
  88. package/dist/assets/themes/base/_layouts/frontend/pages/feedback.html +171 -0
  89. package/dist/assets/themes/base/_layouts/frontend/pages/index.html +66 -0
  90. package/dist/assets/themes/base/_layouts/frontend/pages/legal/document.html +78 -0
  91. package/dist/assets/themes/base/_layouts/frontend/pages/payment/checkout.html +459 -0
  92. package/dist/assets/themes/base/_layouts/frontend/pages/payment/confirmation.html +131 -0
  93. package/dist/assets/themes/base/_layouts/frontend/pages/portal/email-preferences.html +104 -0
  94. package/dist/assets/themes/base/_layouts/frontend/pages/pricing.html +596 -0
  95. package/dist/assets/themes/base/_layouts/frontend/pages/status.html +361 -0
  96. package/dist/assets/themes/base/_layouts/frontend/pages/team/index.html +210 -0
  97. package/dist/assets/themes/base/_layouts/frontend/pages/team/member.html +125 -0
  98. package/dist/assets/themes/base/_layouts/frontend/pages/updates/index.html +119 -0
  99. package/dist/assets/themes/base/_layouts/frontend/pages/updates/update.html +118 -0
  100. package/dist/assets/themes/base/_sections/about/hero/section.html +68 -0
  101. package/dist/assets/themes/base/_sections/about/hero/section.json5 +58 -0
  102. package/dist/assets/themes/base/_sections/about/letter/section.html +60 -0
  103. package/dist/assets/themes/base/_sections/about/letter/section.json5 +56 -0
  104. package/dist/assets/themes/base/_sections/about/photo-band/section.html +24 -0
  105. package/dist/assets/themes/base/_sections/about/photo-band/section.json5 +23 -0
  106. package/dist/assets/themes/base/_sections/about/principles/section.html +17 -0
  107. package/dist/assets/themes/base/_sections/about/principles/section.json5 +34 -0
  108. package/dist/assets/themes/base/_sections/about/timeline/section.html +20 -0
  109. package/dist/assets/themes/base/_sections/about/timeline/section.json5 +34 -0
  110. package/dist/assets/themes/base/_sections/marketing/bento/section.html +91 -0
  111. package/dist/assets/themes/base/_sections/marketing/bento/section.json5 +152 -0
  112. package/dist/assets/themes/base/_sections/marketing/cta/section.html +44 -0
  113. package/dist/assets/themes/base/_sections/marketing/cta/section.json5 +47 -0
  114. package/dist/assets/themes/base/_sections/marketing/faq/section.html +52 -0
  115. package/dist/assets/themes/base/_sections/marketing/faq/section.json5 +68 -0
  116. package/dist/assets/themes/base/_sections/marketing/hero/section.html +349 -0
  117. package/dist/assets/themes/base/_sections/marketing/hero/section.json5 +335 -0
  118. package/dist/assets/themes/base/_sections/marketing/newsletter-cta/section.html +38 -0
  119. package/dist/assets/themes/base/_sections/marketing/newsletter-cta/section.js +86 -0
  120. package/dist/assets/themes/base/_sections/marketing/newsletter-cta/section.json5 +29 -0
  121. package/dist/assets/themes/base/_sections/marketing/pricing-cards/section.html +115 -0
  122. package/dist/assets/themes/base/_sections/marketing/pricing-cards/section.json5 +72 -0
  123. package/dist/assets/themes/base/_sections/marketing/product-demo/section.html +62 -0
  124. package/dist/assets/themes/base/_sections/marketing/product-demo/section.js +44 -0
  125. package/dist/assets/themes/base/_sections/marketing/product-demo/section.json5 +52 -0
  126. package/dist/assets/themes/base/_sections/marketing/prose/section.html +22 -0
  127. package/dist/assets/themes/base/_sections/marketing/prose/section.json5 +41 -0
  128. package/dist/assets/themes/base/_sections/marketing/showcase/section.html +35 -0
  129. package/dist/assets/themes/base/_sections/marketing/showcase/section.json5 +52 -0
  130. package/dist/assets/themes/base/_sections/marketing/stats/section.html +32 -0
  131. package/dist/assets/themes/base/_sections/marketing/stats/section.json5 +44 -0
  132. package/dist/assets/themes/base/_sections/marketing/testimonials/section.html +54 -0
  133. package/dist/assets/themes/base/_sections/marketing/testimonials/section.json5 +70 -0
  134. package/dist/assets/themes/base/_sections/marketing/trusted-by/section.html +30 -0
  135. package/dist/assets/themes/base/_sections/marketing/trusted-by/section.json5 +38 -0
  136. package/dist/assets/themes/base/_sections/verts/unit/section.html +7 -0
  137. package/dist/assets/themes/base/_sections/verts/unit/section.js +22 -0
  138. package/dist/assets/themes/base/_sections/verts/unit/section.json5 +24 -0
  139. package/dist/assets/themes/base/_sections/verts/unit/section.scss +24 -0
  140. package/dist/assets/themes/base/_theme.js +13 -0
  141. package/dist/assets/themes/base/_theme.scss +4 -0
  142. package/dist/assets/themes/bootstrap/_theme.js +16 -0
  143. package/dist/assets/themes/bootstrap/_theme.scss +11 -0
  144. package/dist/assets/themes/bootstrap/js/index.esm.js +19 -0
  145. package/dist/assets/themes/bootstrap/js/index.umd.js +34 -0
  146. package/dist/assets/themes/bootstrap/js/src/alert.js +87 -0
  147. package/dist/assets/themes/bootstrap/js/src/base-component.js +86 -0
  148. package/dist/assets/themes/bootstrap/js/src/button.js +72 -0
  149. package/dist/assets/themes/bootstrap/js/src/carousel.js +474 -0
  150. package/dist/assets/themes/bootstrap/js/src/collapse.js +297 -0
  151. package/dist/assets/themes/bootstrap/js/src/dom/data.js +55 -0
  152. package/dist/assets/themes/bootstrap/js/src/dom/event-handler.js +317 -0
  153. package/dist/assets/themes/bootstrap/js/src/dom/manipulator.js +71 -0
  154. package/dist/assets/themes/bootstrap/js/src/dom/selector-engine.js +126 -0
  155. package/dist/assets/themes/bootstrap/js/src/dropdown.js +458 -0
  156. package/dist/assets/themes/bootstrap/js/src/modal.js +378 -0
  157. package/dist/assets/themes/bootstrap/js/src/offcanvas.js +282 -0
  158. package/dist/assets/themes/bootstrap/js/src/popover.js +97 -0
  159. package/dist/assets/themes/bootstrap/js/src/scrollspy.js +296 -0
  160. package/dist/assets/themes/bootstrap/js/src/tab.js +315 -0
  161. package/dist/assets/themes/bootstrap/js/src/toast.js +224 -0
  162. package/dist/assets/themes/bootstrap/js/src/tooltip.js +632 -0
  163. package/dist/assets/themes/bootstrap/js/src/util/backdrop.js +151 -0
  164. package/dist/assets/themes/bootstrap/js/src/util/component-functions.js +35 -0
  165. package/dist/assets/themes/bootstrap/js/src/util/config.js +65 -0
  166. package/dist/assets/themes/bootstrap/js/src/util/focustrap.js +115 -0
  167. package/dist/assets/themes/bootstrap/js/src/util/index.js +306 -0
  168. package/dist/assets/themes/bootstrap/js/src/util/sanitizer.js +117 -0
  169. package/dist/assets/themes/bootstrap/js/src/util/scrollbar.js +114 -0
  170. package/dist/assets/themes/bootstrap/js/src/util/swipe.js +146 -0
  171. package/dist/assets/themes/bootstrap/js/src/util/template-factory.js +160 -0
  172. package/dist/assets/themes/bootstrap/overrides/_accordion.scss +21 -0
  173. package/dist/assets/themes/bootstrap/overrides/_avatars.scss +51 -0
  174. package/dist/assets/themes/bootstrap/overrides/_blog-images.scss +45 -0
  175. package/dist/assets/themes/bootstrap/overrides/_buttons-adaptive.scss +141 -0
  176. package/dist/assets/themes/bootstrap/overrides/_color-shades.scss +25 -0
  177. package/dist/assets/themes/bootstrap/overrides/_cursor-utilities.scss +13 -0
  178. package/dist/assets/themes/bootstrap/overrides/_index.scss +30 -0
  179. package/dist/assets/themes/bootstrap/overrides/_links.scss +26 -0
  180. package/dist/assets/themes/bootstrap/overrides/_soft-colors.scss +99 -0
  181. package/dist/assets/themes/bootstrap/overrides/_spacing-extended.scss +74 -0
  182. package/dist/assets/themes/bootstrap/overrides/_spinners.scss +22 -0
  183. package/dist/assets/themes/bootstrap/overrides/_typography-utilities.scss +40 -0
  184. package/dist/assets/themes/bootstrap/scss/_accordion.scss +153 -0
  185. package/dist/assets/themes/bootstrap/scss/_alert.scss +68 -0
  186. package/dist/assets/themes/bootstrap/scss/_badge.scss +38 -0
  187. package/dist/assets/themes/bootstrap/scss/_breadcrumb.scss +40 -0
  188. package/dist/assets/themes/bootstrap/scss/_button-group.scss +147 -0
  189. package/dist/assets/themes/bootstrap/scss/_buttons.scss +216 -0
  190. package/dist/assets/themes/bootstrap/scss/_card.scss +238 -0
  191. package/dist/assets/themes/bootstrap/scss/_carousel.scss +226 -0
  192. package/dist/assets/themes/bootstrap/scss/_close.scss +66 -0
  193. package/dist/assets/themes/bootstrap/scss/_containers.scss +41 -0
  194. package/dist/assets/themes/bootstrap/scss/_dropdown.scss +250 -0
  195. package/dist/assets/themes/bootstrap/scss/_forms.scss +9 -0
  196. package/dist/assets/themes/bootstrap/scss/_functions.scss +302 -0
  197. package/dist/assets/themes/bootstrap/scss/_grid.scss +39 -0
  198. package/dist/assets/themes/bootstrap/scss/_helpers.scss +12 -0
  199. package/dist/assets/themes/bootstrap/scss/_images.scss +42 -0
  200. package/dist/assets/themes/bootstrap/scss/_list-group.scss +199 -0
  201. package/dist/assets/themes/bootstrap/scss/_maps.scss +174 -0
  202. package/dist/assets/themes/bootstrap/scss/_mixins.scss +42 -0
  203. package/dist/assets/themes/bootstrap/scss/_modal.scss +240 -0
  204. package/dist/assets/themes/bootstrap/scss/_nav.scss +197 -0
  205. package/dist/assets/themes/bootstrap/scss/_navbar.scss +289 -0
  206. package/dist/assets/themes/bootstrap/scss/_offcanvas.scss +147 -0
  207. package/dist/assets/themes/bootstrap/scss/_pagination.scss +109 -0
  208. package/dist/assets/themes/bootstrap/scss/_placeholders.scss +51 -0
  209. package/dist/assets/themes/bootstrap/scss/_popover.scss +196 -0
  210. package/dist/assets/themes/bootstrap/scss/_progress.scss +68 -0
  211. package/dist/assets/themes/bootstrap/scss/_reboot.scss +611 -0
  212. package/dist/assets/themes/bootstrap/scss/_root.scss +187 -0
  213. package/dist/assets/themes/bootstrap/scss/_spinners.scss +85 -0
  214. package/dist/assets/themes/bootstrap/scss/_tables.scss +171 -0
  215. package/dist/assets/themes/bootstrap/scss/_toasts.scss +73 -0
  216. package/dist/assets/themes/bootstrap/scss/_tooltip.scss +119 -0
  217. package/dist/assets/themes/bootstrap/scss/_transitions.scss +27 -0
  218. package/dist/assets/themes/bootstrap/scss/_type.scss +106 -0
  219. package/dist/assets/themes/bootstrap/scss/_utilities.scss +806 -0
  220. package/dist/assets/themes/bootstrap/scss/_variables-dark.scss +102 -0
  221. package/dist/assets/themes/bootstrap/scss/_variables.scss +1753 -0
  222. package/dist/assets/themes/bootstrap/scss/bootstrap-grid.scss +62 -0
  223. package/dist/assets/themes/bootstrap/scss/bootstrap-reboot.scss +10 -0
  224. package/dist/assets/themes/bootstrap/scss/bootstrap-utilities.scss +19 -0
  225. package/dist/assets/themes/bootstrap/scss/bootstrap.scss +52 -0
  226. package/dist/assets/themes/bootstrap/scss/forms/_floating-labels.scss +97 -0
  227. package/dist/assets/themes/bootstrap/scss/forms/_form-check.scss +189 -0
  228. package/dist/assets/themes/bootstrap/scss/forms/_form-control.scss +214 -0
  229. package/dist/assets/themes/bootstrap/scss/forms/_form-range.scss +91 -0
  230. package/dist/assets/themes/bootstrap/scss/forms/_form-select.scss +80 -0
  231. package/dist/assets/themes/bootstrap/scss/forms/_form-text.scss +11 -0
  232. package/dist/assets/themes/bootstrap/scss/forms/_input-group.scss +132 -0
  233. package/dist/assets/themes/bootstrap/scss/forms/_labels.scss +36 -0
  234. package/dist/assets/themes/bootstrap/scss/forms/_validation.scss +12 -0
  235. package/dist/assets/themes/bootstrap/scss/helpers/_clearfix.scss +3 -0
  236. package/dist/assets/themes/bootstrap/scss/helpers/_color-bg.scss +7 -0
  237. package/dist/assets/themes/bootstrap/scss/helpers/_colored-links.scss +30 -0
  238. package/dist/assets/themes/bootstrap/scss/helpers/_focus-ring.scss +5 -0
  239. package/dist/assets/themes/bootstrap/scss/helpers/_icon-link.scss +25 -0
  240. package/dist/assets/themes/bootstrap/scss/helpers/_position.scss +36 -0
  241. package/dist/assets/themes/bootstrap/scss/helpers/_ratio.scss +26 -0
  242. package/dist/assets/themes/bootstrap/scss/helpers/_stacks.scss +15 -0
  243. package/dist/assets/themes/bootstrap/scss/helpers/_stretched-link.scss +15 -0
  244. package/dist/assets/themes/bootstrap/scss/helpers/_text-truncation.scss +7 -0
  245. package/dist/assets/themes/bootstrap/scss/helpers/_visually-hidden.scss +8 -0
  246. package/dist/assets/themes/bootstrap/scss/helpers/_vr.scss +8 -0
  247. package/dist/assets/themes/bootstrap/scss/mixins/_alert.scss +18 -0
  248. package/dist/assets/themes/bootstrap/scss/mixins/_backdrop.scss +14 -0
  249. package/dist/assets/themes/bootstrap/scss/mixins/_banner.scss +7 -0
  250. package/dist/assets/themes/bootstrap/scss/mixins/_border-radius.scss +78 -0
  251. package/dist/assets/themes/bootstrap/scss/mixins/_box-shadow.scss +18 -0
  252. package/dist/assets/themes/bootstrap/scss/mixins/_breakpoints.scss +127 -0
  253. package/dist/assets/themes/bootstrap/scss/mixins/_buttons.scss +70 -0
  254. package/dist/assets/themes/bootstrap/scss/mixins/_caret.scss +69 -0
  255. package/dist/assets/themes/bootstrap/scss/mixins/_clearfix.scss +9 -0
  256. package/dist/assets/themes/bootstrap/scss/mixins/_color-mode.scss +21 -0
  257. package/dist/assets/themes/bootstrap/scss/mixins/_color-scheme.scss +7 -0
  258. package/dist/assets/themes/bootstrap/scss/mixins/_container.scss +11 -0
  259. package/dist/assets/themes/bootstrap/scss/mixins/_deprecate.scss +10 -0
  260. package/dist/assets/themes/bootstrap/scss/mixins/_forms.scss +163 -0
  261. package/dist/assets/themes/bootstrap/scss/mixins/_gradients.scss +47 -0
  262. package/dist/assets/themes/bootstrap/scss/mixins/_grid.scss +151 -0
  263. package/dist/assets/themes/bootstrap/scss/mixins/_image.scss +16 -0
  264. package/dist/assets/themes/bootstrap/scss/mixins/_list-group.scss +26 -0
  265. package/dist/assets/themes/bootstrap/scss/mixins/_lists.scss +7 -0
  266. package/dist/assets/themes/bootstrap/scss/mixins/_pagination.scss +10 -0
  267. package/dist/assets/themes/bootstrap/scss/mixins/_reset-text.scss +17 -0
  268. package/dist/assets/themes/bootstrap/scss/mixins/_resize.scss +6 -0
  269. package/dist/assets/themes/bootstrap/scss/mixins/_table-variants.scss +24 -0
  270. package/dist/assets/themes/bootstrap/scss/mixins/_text-truncate.scss +8 -0
  271. package/dist/assets/themes/bootstrap/scss/mixins/_transition.scss +26 -0
  272. package/dist/assets/themes/bootstrap/scss/mixins/_utilities.scss +97 -0
  273. package/dist/assets/themes/bootstrap/scss/mixins/_visually-hidden.scss +38 -0
  274. package/dist/assets/themes/bootstrap/scss/tests/jasmine.js +16 -0
  275. package/dist/assets/themes/bootstrap/scss/tests/mixins/_auto-import-of-variables-dark.test.scss +7 -0
  276. package/dist/assets/themes/bootstrap/scss/tests/mixins/_color-modes.test.scss +69 -0
  277. package/dist/assets/themes/bootstrap/scss/tests/mixins/_media-query-color-mode-full.test.scss +8 -0
  278. package/dist/assets/themes/bootstrap/scss/tests/mixins/_utilities.test.scss +393 -0
  279. package/dist/assets/themes/bootstrap/scss/tests/sass-true/register.js +14 -0
  280. package/dist/assets/themes/bootstrap/scss/tests/sass-true/runner.js +17 -0
  281. package/dist/assets/themes/bootstrap/scss/tests/utilities/_api.test.scss +75 -0
  282. package/dist/assets/themes/bootstrap/scss/utilities/_api.scss +47 -0
  283. package/dist/assets/themes/bootstrap/scss/vendor/_rfs.scss +348 -0
  284. package/dist/assets/themes/classy/README.md +89 -0
  285. package/dist/assets/themes/classy/_config.scss +245 -0
  286. package/dist/assets/themes/classy/_theme.js +31 -0
  287. package/dist/assets/themes/classy/_theme.scss +57 -0
  288. package/dist/assets/themes/classy/css/app/_panels.scss +183 -0
  289. package/dist/assets/themes/classy/css/base/_fonts.scss +79 -0
  290. package/dist/assets/themes/classy/css/base/_no-raise.scss +49 -0
  291. package/dist/assets/themes/classy/css/base/_root.scss +113 -0
  292. package/dist/assets/themes/classy/css/base/_typography.scss +154 -0
  293. package/dist/assets/themes/classy/css/base/_utilities.scss +284 -0
  294. package/dist/assets/themes/classy/css/components/_alerts.scss +51 -0
  295. package/dist/assets/themes/classy/css/components/_badges.scss +46 -0
  296. package/dist/assets/themes/classy/css/components/_buttons.scss +175 -0
  297. package/dist/assets/themes/classy/css/components/_cards.scss +41 -0
  298. package/dist/assets/themes/classy/css/components/_dropdowns.scss +48 -0
  299. package/dist/assets/themes/classy/css/components/_forms.scss +171 -0
  300. package/dist/assets/themes/classy/css/components/_receipt.scss +45 -0
  301. package/dist/assets/themes/classy/css/layout/_footer.scss +298 -0
  302. package/dist/assets/themes/classy/css/layout/_general.scss +209 -0
  303. package/dist/assets/themes/classy/css/layout/_nav.scss +154 -0
  304. package/dist/assets/themes/classy/css/layout/_shell.scss +458 -0
  305. package/dist/assets/themes/classy/css/marketing/_bento.scss +238 -0
  306. package/dist/assets/themes/classy/css/marketing/_content.scss +1328 -0
  307. package/dist/assets/themes/classy/css/marketing/_downloads.scss +246 -0
  308. package/dist/assets/themes/classy/css/marketing/_hero.scss +311 -0
  309. package/dist/assets/themes/classy/css/marketing/_sections.scss +649 -0
  310. package/dist/assets/themes/classy/css/pages/404/index.scss +67 -0
  311. package/dist/assets/themes/classy/css/pages/_auth.scss +235 -0
  312. package/dist/assets/themes/classy/css/pages/admin/firebase/index.scss +22 -0
  313. package/dist/assets/themes/classy/css/pages/blog/[slug].scss +56 -0
  314. package/dist/assets/themes/classy/css/pages/cookies.scss +2 -0
  315. package/dist/assets/themes/classy/css/pages/dashboard/account/index.scss +31 -0
  316. package/dist/assets/themes/classy/css/pages/feedback/index.scss +39 -0
  317. package/dist/assets/themes/classy/css/pages/legal/_document.scss +192 -0
  318. package/dist/assets/themes/classy/css/pages/payment/checkout/index.scss +585 -0
  319. package/dist/assets/themes/classy/css/pages/payment/confirmation/index.scss +9 -0
  320. package/dist/assets/themes/classy/css/pages/privacy.scss +2 -0
  321. package/dist/assets/themes/classy/css/pages/status/index.scss +164 -0
  322. package/dist/assets/themes/classy/css/pages/terms.scss +2 -0
  323. package/dist/assets/themes/classy/css/pages/updates/index.scss +128 -0
  324. package/dist/assets/themes/classy/fonts/inter-italic-latin-ext.woff2 +0 -0
  325. package/dist/assets/themes/classy/fonts/inter-italic-latin.woff2 +0 -0
  326. package/dist/assets/themes/classy/fonts/inter-normal-latin-ext.woff2 +0 -0
  327. package/dist/assets/themes/classy/fonts/inter-normal-latin.woff2 +0 -0
  328. package/dist/assets/themes/classy/fonts/newsreader-italic-latin-ext.woff2 +0 -0
  329. package/dist/assets/themes/classy/fonts/newsreader-italic-latin.woff2 +0 -0
  330. package/dist/assets/themes/classy/fonts/newsreader-normal-latin-ext.woff2 +0 -0
  331. package/dist/assets/themes/classy/fonts/newsreader-normal-latin.woff2 +0 -0
  332. package/dist/assets/themes/classy/js/hero-demo-form.js +56 -0
  333. package/dist/assets/themes/neobrutalism/README.md +118 -0
  334. package/dist/assets/themes/neobrutalism/_config.scss +139 -0
  335. package/dist/assets/themes/neobrutalism/_layouts/frontend/core/base.html +30 -0
  336. package/dist/assets/themes/neobrutalism/_layouts/frontend/pages/index.html +275 -0
  337. package/dist/assets/themes/neobrutalism/_theme.js +24 -0
  338. package/dist/assets/themes/neobrutalism/_theme.scss +61 -0
  339. package/dist/assets/themes/neobrutalism/css/base/_mixins.scss +46 -0
  340. package/dist/assets/themes/neobrutalism/css/base/_root.scss +169 -0
  341. package/dist/assets/themes/neobrutalism/css/base/_typography.scss +61 -0
  342. package/dist/assets/themes/neobrutalism/css/base/_utilities.scss +89 -0
  343. package/dist/assets/themes/neobrutalism/css/components/_billing-toggle.scss +74 -0
  344. package/dist/assets/themes/neobrutalism/css/components/_buttons.scss +148 -0
  345. package/dist/assets/themes/neobrutalism/css/components/_cards.scss +47 -0
  346. package/dist/assets/themes/neobrutalism/css/components/_forms.scss +88 -0
  347. package/dist/assets/themes/neobrutalism/css/components/_marketing.scss +244 -0
  348. package/dist/assets/themes/neobrutalism/css/layout/_general.scss +213 -0
  349. package/dist/assets/themes/neobrutalism/css/layout/_navigation.scss +171 -0
  350. package/dist/assets/themes/neobrutalism/css/pages/index.scss +203 -0
  351. package/dist/assets/themes/neobrutalism/css/pages/pricing/index.scss +58 -0
  352. package/dist/assets/themes/neobrutalism/css/pages/test/libraries/layers/index.scss +7 -0
  353. package/dist/assets/themes/neobrutalism/js/pages/test/libraries/layers/index.js +9 -0
  354. package/dist/assets/themes/newsflash/README.md +117 -0
  355. package/dist/assets/themes/newsflash/_components/heading/lede/component.html +14 -0
  356. package/dist/assets/themes/newsflash/_components/heading/lede/component.json5 +23 -0
  357. package/dist/assets/themes/newsflash/_components/heading/rule-head/component.html +17 -0
  358. package/dist/assets/themes/newsflash/_components/heading/rule-head/component.json5 +23 -0
  359. package/dist/assets/themes/newsflash/_components/news/byline/component.html +12 -0
  360. package/dist/assets/themes/newsflash/_components/news/byline/component.json5 +18 -0
  361. package/dist/assets/themes/newsflash/_components/news/story-card/component.html +25 -0
  362. package/dist/assets/themes/newsflash/_components/news/story-card/component.json5 +21 -0
  363. package/dist/assets/themes/newsflash/_config.scss +147 -0
  364. package/dist/assets/themes/newsflash/_layouts/frontend/core/base.html +51 -0
  365. package/dist/assets/themes/newsflash/_layouts/frontend/pages/blog/index.html +299 -0
  366. package/dist/assets/themes/newsflash/_layouts/frontend/pages/blog/post.html +235 -0
  367. package/dist/assets/themes/newsflash/_layouts/frontend/pages/index.html +328 -0
  368. package/dist/assets/themes/newsflash/_sections/marketing/desks/section.html +39 -0
  369. package/dist/assets/themes/newsflash/_sections/marketing/desks/section.json5 +49 -0
  370. package/dist/assets/themes/newsflash/_sections/marketing/newsletter-cta/section.html +103 -0
  371. package/dist/assets/themes/newsflash/_sections/marketing/newsletter-cta/section.json5 +50 -0
  372. package/dist/assets/themes/newsflash/_sections/marketing/rundown/section.html +28 -0
  373. package/dist/assets/themes/newsflash/_sections/marketing/rundown/section.json5 +40 -0
  374. package/dist/assets/themes/newsflash/_theme.js +24 -0
  375. package/dist/assets/themes/newsflash/_theme.scss +70 -0
  376. package/dist/assets/themes/newsflash/css/base/_fonts.scss +80 -0
  377. package/dist/assets/themes/newsflash/css/base/_mixins.scss +51 -0
  378. package/dist/assets/themes/newsflash/css/base/_root.scss +257 -0
  379. package/dist/assets/themes/newsflash/css/base/_typography.scss +49 -0
  380. package/dist/assets/themes/newsflash/css/base/_utilities.scss +122 -0
  381. package/dist/assets/themes/newsflash/css/components/_badges.scss +65 -0
  382. package/dist/assets/themes/newsflash/css/components/_billing-toggle.scss +61 -0
  383. package/dist/assets/themes/newsflash/css/components/_buttons.scss +139 -0
  384. package/dist/assets/themes/newsflash/css/components/_cards.scss +47 -0
  385. package/dist/assets/themes/newsflash/css/components/_editorial.scss +173 -0
  386. package/dist/assets/themes/newsflash/css/components/_forms.scss +75 -0
  387. package/dist/assets/themes/newsflash/css/components/_infinite-scroll.scss +102 -0
  388. package/dist/assets/themes/newsflash/css/components/_panels.scss +156 -0
  389. package/dist/assets/themes/newsflash/css/components/_ticker.scss +67 -0
  390. package/dist/assets/themes/newsflash/css/layout/_general.scss +330 -0
  391. package/dist/assets/themes/newsflash/css/layout/_navigation.scss +168 -0
  392. package/dist/assets/themes/newsflash/css/pages/404/index.scss +79 -0
  393. package/dist/assets/themes/newsflash/css/pages/blog/[slug].scss +186 -0
  394. package/dist/assets/themes/newsflash/css/pages/blog/index.scss +17 -0
  395. package/dist/assets/themes/newsflash/css/pages/index.scss +157 -0
  396. package/dist/assets/themes/newsflash/css/pages/pricing/index.scss +55 -0
  397. package/dist/assets/themes/newsflash/css/pages/test/libraries/layers/index.scss +7 -0
  398. package/dist/assets/themes/newsflash/fonts/fraunces-italic-latin-ext.woff2 +0 -0
  399. package/dist/assets/themes/newsflash/fonts/fraunces-italic-latin.woff2 +0 -0
  400. package/dist/assets/themes/newsflash/fonts/fraunces-normal-latin-ext.woff2 +0 -0
  401. package/dist/assets/themes/newsflash/fonts/fraunces-normal-latin.woff2 +0 -0
  402. package/dist/assets/themes/newsflash/fonts/schibsted-grotesk-italic-latin-ext.woff2 +0 -0
  403. package/dist/assets/themes/newsflash/fonts/schibsted-grotesk-italic-latin.woff2 +0 -0
  404. package/dist/assets/themes/newsflash/fonts/schibsted-grotesk-normal-latin-ext.woff2 +0 -0
  405. package/dist/assets/themes/newsflash/fonts/schibsted-grotesk-normal-latin.woff2 +0 -0
  406. package/dist/assets/themes/newsflash/js/pages/blog/[slug].js +29 -0
  407. package/dist/assets/themes/newsflash/js/pages/test/libraries/layers/index.js +9 -0
  408. package/dist/background.js +684 -0
  409. package/dist/build.js +268 -0
  410. package/dist/cli-run.js +25 -0
  411. package/dist/cli.js +30 -0
  412. package/dist/commands/build.js +67 -0
  413. package/dist/commands/clean.js +29 -0
  414. package/dist/commands/deploy.js +97 -0
  415. package/dist/commands/install.js +58 -0
  416. package/dist/commands/lib/dependencies.js +33 -0
  417. package/dist/commands/lib/deploy-precheck.js +57 -0
  418. package/dist/commands/lib/ensure-target.js +151 -0
  419. package/dist/commands/lib/push-secrets.js +60 -0
  420. package/dist/commands/migrate.js +77 -0
  421. package/dist/commands/test.js +110 -0
  422. package/dist/commands/update.js +21 -0
  423. package/dist/commands/version.js +12 -0
  424. package/dist/config/manifest.json +117 -0
  425. package/dist/config/page-template.html +21 -0
  426. package/dist/content.js +37 -0
  427. package/dist/defaults/.github/workflows/publish.yml +86 -0
  428. package/dist/defaults/.nvmrc +1 -0
  429. package/dist/defaults/.vscode/settings.json +8 -0
  430. package/dist/defaults/AGENTS.md +113 -0
  431. package/dist/defaults/CHANGELOG.md +15 -0
  432. package/dist/defaults/CLAUDE.md +1 -0
  433. package/dist/defaults/_.gitignore +75 -0
  434. package/dist/defaults/config/description.md +46 -0
  435. package/dist/defaults/config/messages.json +20 -0
  436. package/dist/defaults/config/omega.json5 +64 -0
  437. package/dist/defaults/docs/README.md +17 -0
  438. package/dist/defaults/gulpfile.js +6 -0
  439. package/dist/defaults/hooks/build/post.js +8 -0
  440. package/dist/defaults/hooks/build/pre.js +8 -0
  441. package/dist/defaults/src/assets/css/bundles/.gitkeep +0 -0
  442. package/dist/defaults/src/assets/css/components/content/index.scss +11 -0
  443. package/dist/defaults/src/assets/css/components/offscreen/index.scss +4 -0
  444. package/dist/defaults/src/assets/css/components/options/index.scss +14 -0
  445. package/dist/defaults/src/assets/css/components/pages/index.scss +10 -0
  446. package/dist/defaults/src/assets/css/components/popup/index.scss +10 -0
  447. package/dist/defaults/src/assets/css/components/sidepanel/index.scss +6 -0
  448. package/dist/defaults/src/assets/css/main.scss +32 -0
  449. package/dist/defaults/src/assets/images/.gitkeep +0 -0
  450. package/dist/defaults/src/assets/js/components/background/index.js +25 -0
  451. package/dist/defaults/src/assets/js/components/content/index.js +22 -0
  452. package/dist/defaults/src/assets/js/components/offscreen/index.js +25 -0
  453. package/dist/defaults/src/assets/js/components/options/index.js +22 -0
  454. package/dist/defaults/src/assets/js/components/pages/index.js +22 -0
  455. package/dist/defaults/src/assets/js/components/popup/index.js +22 -0
  456. package/dist/defaults/src/assets/js/components/sidepanel/index.js +20 -0
  457. package/dist/defaults/src/assets/vendor/.gitkeep +0 -0
  458. package/dist/defaults/src/manifest.json +131 -0
  459. package/dist/defaults/src/views/offscreen/index.html +3 -0
  460. package/dist/defaults/src/views/options/index.html +8 -0
  461. package/dist/defaults/src/views/pages/index.html +10 -0
  462. package/dist/defaults/src/views/popup/index.html +4 -0
  463. package/dist/defaults/src/views/sidepanel/index.html +4 -0
  464. package/dist/defaults/test/README.md +41 -0
  465. package/dist/defaults/test/_init.js +10 -0
  466. package/dist/defaults/test/boot/externally-connectable.test.js +28 -0
  467. package/dist/gulp/config/locales.js +12 -0
  468. package/dist/gulp/main.js +78 -0
  469. package/dist/gulp/tasks/_.js +18 -0
  470. package/dist/gulp/tasks/audit.js +222 -0
  471. package/dist/gulp/tasks/bundle.js +687 -0
  472. package/dist/gulp/tasks/defaults.js +281 -0
  473. package/dist/gulp/tasks/distribute.js +172 -0
  474. package/dist/gulp/tasks/fontawesome.js +25 -0
  475. package/dist/gulp/tasks/html.js +163 -0
  476. package/dist/gulp/tasks/icons.js +126 -0
  477. package/dist/gulp/tasks/package.js +742 -0
  478. package/dist/gulp/tasks/publish.js +417 -0
  479. package/dist/gulp/tasks/sass.js +252 -0
  480. package/dist/gulp/tasks/serve.js +103 -0
  481. package/dist/gulp/tasks/translate.js +252 -0
  482. package/dist/gulp/tasks/utils/build-json.js +43 -0
  483. package/dist/gulp/tasks/utils/template-transform.js +50 -0
  484. package/dist/gulp/templates/BUILD_INSTRUCTIONS.md +69 -0
  485. package/dist/index.js +46 -0
  486. package/dist/lib/affiliatizer.js +224 -0
  487. package/dist/lib/analytics.js +26 -0
  488. package/dist/lib/auth-helpers.js +213 -0
  489. package/dist/lib/extension.js +116 -0
  490. package/dist/lib/icons.js +36 -0
  491. package/dist/lib/logger-lite.js +35 -0
  492. package/dist/lib/logger.js +4 -0
  493. package/dist/lib/messaging.js +48 -0
  494. package/dist/lib/safe-install.js +4 -0
  495. package/dist/lib/theme.js +73 -0
  496. package/dist/lib/verts.js +68 -0
  497. package/dist/offscreen.js +31 -0
  498. package/dist/omega-bin.js +6 -0
  499. package/dist/options.js +80 -0
  500. package/dist/page.js +80 -0
  501. package/dist/popup.js +80 -0
  502. package/dist/sidepanel.js +80 -0
  503. package/dist/test/assert.js +4 -0
  504. package/dist/test/fixtures/consumer-extension/dist/background.js +23 -0
  505. package/dist/test/fixtures/consumer-extension/dist/manifest.json +20 -0
  506. package/dist/test/fixtures/consumer-extension/dist/options.html +10 -0
  507. package/dist/test/fixtures/consumer-extension/dist/popup.bundle.js +5 -0
  508. package/dist/test/fixtures/consumer-extension/dist/popup.html +12 -0
  509. package/dist/test/harness/extension/background.js +26 -0
  510. package/dist/test/harness/extension/manifest.json +27 -0
  511. package/dist/test/harness/extension/options.html +12 -0
  512. package/dist/test/harness/extension/popup.html +12 -0
  513. package/dist/test/harness/extension/sidepanel.html +12 -0
  514. package/dist/test/index.js +63 -0
  515. package/dist/test/runner.js +99 -0
  516. package/dist/test/runners/boot.js +221 -0
  517. package/dist/test/runners/chromium.js +391 -0
  518. package/dist/test/runners/helpers.js +24 -0
  519. package/dist/test/suites/background/messaging.test.js +44 -0
  520. package/dist/test/suites/background/storage.test.js +46 -0
  521. package/dist/test/suites/background/sw-context.test.js +49 -0
  522. package/dist/test/suites/boot/build-json-bake.test.js +80 -0
  523. package/dist/test/suites/boot/extension-loads.test.js +58 -0
  524. package/dist/test/suites/build/affiliatizer.test.js +64 -0
  525. package/dist/test/suites/build/app-launch.test.js +65 -0
  526. package/dist/test/suites/build/attach-log-file.test.js +99 -0
  527. package/dist/test/suites/build/auth-emulator-gate.test.js +118 -0
  528. package/dist/test/suites/build/auth-triggers.test.js +71 -0
  529. package/dist/test/suites/build/boot-discovery.test.js +134 -0
  530. package/dist/test/suites/build/boot-timeout.test.js +92 -0
  531. package/dist/test/suites/build/build-error-notification.test.js +122 -0
  532. package/dist/test/suites/build/build-json-bake.test.js +302 -0
  533. package/dist/test/suites/build/build-verb.test.js +138 -0
  534. package/dist/test/suites/build/bundle-targets.test.js +66 -0
  535. package/dist/test/suites/build/cache-warming.test.js +44 -0
  536. package/dist/test/suites/build/cli.test.js +150 -0
  537. package/dist/test/suites/build/default-boot-test.test.js +100 -0
  538. package/dist/test/suites/build/defaults-scaffold.test.js +287 -0
  539. package/dist/test/suites/build/deploy-dispatch.test.js +76 -0
  540. package/dist/test/suites/build/distribute-task.test.js +111 -0
  541. package/dist/test/suites/build/ensure-target.test.js +141 -0
  542. package/dist/test/suites/build/env-delivery.test.js +239 -0
  543. package/dist/test/suites/build/env-watch.test.js +86 -0
  544. package/dist/test/suites/build/expect.test.js +48 -0
  545. package/dist/test/suites/build/exports.test.js +53 -0
  546. package/dist/test/suites/build/extension-fallback.test.js +82 -0
  547. package/dist/test/suites/build/framework-deps.test.js +176 -0
  548. package/dist/test/suites/build/global-handlers.test.js +70 -0
  549. package/dist/test/suites/build/icon-sheet.test.js +74 -0
  550. package/dist/test/suites/build/logger-lite.test.js +61 -0
  551. package/dist/test/suites/build/manager.test.js +249 -0
  552. package/dist/test/suites/build/mode-helpers.test.js +136 -0
  553. package/dist/test/suites/build/omega-shell.test.js +103 -0
  554. package/dist/test/suites/build/omega-tokens.test.js +76 -0
  555. package/dist/test/suites/build/package-task.test.js +831 -0
  556. package/dist/test/suites/build/setup-scripts.test.js +172 -0
  557. package/dist/test/suites/build/strip-dev-blocks.test.js +122 -0
  558. package/dist/test/suites/build/template-replace.test.js +116 -0
  559. package/dist/test/suites/build/test-discovery.test.js +64 -0
  560. package/dist/test/suites/build/test-target-no-match.test.js +119 -0
  561. package/dist/test/suites/build/theme-resolve.test.js +115 -0
  562. package/dist/test/suites/build/translate.test.js +85 -0
  563. package/dist/test/suites/build/url-helpers.test.js +119 -0
  564. package/dist/test/suites/build/verb-logs.test.js +43 -0
  565. package/dist/test/suites/build/verts-binding.test.js +67 -0
  566. package/dist/test/suites/build/wakeup-ping.test.js +90 -0
  567. package/dist/test/suites/build/wave5-messenger.test.js +80 -0
  568. package/dist/test/suites/view/options-and-sidepanel.test.js +16 -0
  569. package/dist/test/suites/view/popup-context.test.js +53 -0
  570. package/dist/test/suites/view/sidepanel.test.js +16 -0
  571. package/dist/test/utils/extended-mode-warning.js +13 -0
  572. package/dist/utils/attach-log-file.js +4 -0
  573. package/dist/utils/mode-helpers.js +117 -0
  574. package/dist/utils/url-helpers.js +69 -0
  575. package/dist/vendor/account/engine.js +182 -0
  576. package/dist/vendor/account/features.js +220 -0
  577. package/dist/vendor/account/index.js +53 -0
  578. package/dist/vendor/account/schema.js +272 -0
  579. package/dist/vendor/account/subscription.js +38 -0
  580. package/dist/vendor/config/company.js +31 -0
  581. package/dist/vendor/config/defaults.js +173 -0
  582. package/dist/vendor/config/demo.js +18 -0
  583. package/dist/vendor/config/desktop-artifacts.js +110 -0
  584. package/dist/vendor/config/edit.js +769 -0
  585. package/dist/vendor/config/env-delivery.js +145 -0
  586. package/dist/vendor/config/env-rules.js +93 -0
  587. package/dist/vendor/config/env-schema.js +1078 -0
  588. package/dist/vendor/config/env.js +445 -0
  589. package/dist/vendor/config/hooks.js +97 -0
  590. package/dist/vendor/config/index.js +237 -0
  591. package/dist/vendor/config/instances.js +208 -0
  592. package/dist/vendor/config/load.js +490 -0
  593. package/dist/vendor/config/merge.js +68 -0
  594. package/dist/vendor/config/order.js +139 -0
  595. package/dist/vendor/config/ports.js +374 -0
  596. package/dist/vendor/config/providers.js +32 -0
  597. package/dist/vendor/config/repo.js +142 -0
  598. package/dist/vendor/config/retired-keys.js +430 -0
  599. package/dist/vendor/config/schema.js +1610 -0
  600. package/dist/vendor/config/secrets.js +50 -0
  601. package/dist/vendor/config/seed.js +34 -0
  602. package/dist/vendor/config/site-global.js +205 -0
  603. package/dist/vendor/config/validate.js +554 -0
  604. package/dist/vendor/config/winback.js +61 -0
  605. package/dist/vendor/devkit/actions-secrets.js +145 -0
  606. package/dist/vendor/devkit/attach-log-file.js +262 -0
  607. package/dist/vendor/devkit/bundle.js +292 -0
  608. package/dist/vendor/devkit/ci-workflows.js +520 -0
  609. package/dist/vendor/devkit/clean-dirs.js +28 -0
  610. package/dist/vendor/devkit/cli-router.js +156 -0
  611. package/dist/vendor/devkit/defaults-engine.js +372 -0
  612. package/dist/vendor/devkit/deploy-precheck.js +54 -0
  613. package/dist/vendor/devkit/deploy-record.js +180 -0
  614. package/dist/vendor/devkit/deploy.js +286 -0
  615. package/dist/vendor/devkit/empty-modules-plugin.js +64 -0
  616. package/dist/vendor/devkit/env-watch.js +132 -0
  617. package/dist/vendor/devkit/framework-deps.js +46 -0
  618. package/dist/vendor/devkit/icons.js +178 -0
  619. package/dist/vendor/devkit/license.js +155 -0
  620. package/dist/vendor/devkit/local.js +1905 -0
  621. package/dist/vendor/devkit/logger.js +128 -0
  622. package/dist/vendor/devkit/merge-line-files.js +296 -0
  623. package/dist/vendor/devkit/npm-registry.js +52 -0
  624. package/dist/vendor/devkit/omega-bin.js +345 -0
  625. package/dist/vendor/devkit/safe-install.js +18 -0
  626. package/dist/vendor/devkit/scaffold-guard.js +96 -0
  627. package/dist/vendor/devkit/strip-dev-blocks-plugin.js +29 -0
  628. package/dist/vendor/devkit/strip-dev-blocks.js +32 -0
  629. package/dist/vendor/devkit/target-dependencies.js +137 -0
  630. package/dist/vendor/devkit/target-secrets.js +165 -0
  631. package/dist/vendor/devkit/test/assert.js +120 -0
  632. package/dist/vendor/devkit/test/define-cases.js +104 -0
  633. package/dist/vendor/devkit/test/extended-mode-warning.js +22 -0
  634. package/dist/vendor/devkit/test/runner-core.js +554 -0
  635. package/dist/vendor/devkit/test/scope.js +162 -0
  636. package/dist/vendor/devkit/translate/cache.js +84 -0
  637. package/dist/vendor/devkit/translate/engine.js +243 -0
  638. package/dist/vendor/devkit/translate/index.js +49 -0
  639. package/dist/vendor/devkit/translate/languages.js +134 -0
  640. package/dist/vendor/devkit/translate/providers.js +188 -0
  641. package/dist/vendor/devkit/update.js +569 -0
  642. package/docs/affiliatizer.md +81 -0
  643. package/docs/audit.md +67 -0
  644. package/docs/auth.md +149 -0
  645. package/docs/build-system.md +176 -0
  646. package/docs/cdp-debugging.md +42 -0
  647. package/docs/cli.md +73 -0
  648. package/docs/common-mistakes.md +10 -0
  649. package/docs/components.md +87 -0
  650. package/docs/css.md +133 -0
  651. package/docs/defaults.md +72 -0
  652. package/docs/environment-detection.md +79 -0
  653. package/docs/extension.md +93 -0
  654. package/docs/hooks.md +111 -0
  655. package/docs/icons.md +47 -0
  656. package/docs/index.md +291 -0
  657. package/docs/logging.md +33 -0
  658. package/docs/managers.md +100 -0
  659. package/docs/offscreen.md +57 -0
  660. package/docs/publishing.md +182 -0
  661. package/docs/shared/agent-docs.md +89 -0
  662. package/docs/shared/analytics.md +612 -0
  663. package/docs/shared/brands.md +51 -0
  664. package/docs/shared/breaking-changes.md +497 -0
  665. package/docs/shared/config.md +1387 -0
  666. package/docs/shared/deploys.md +215 -0
  667. package/docs/shared/icons.md +201 -0
  668. package/docs/shared/local-dev.md +147 -0
  669. package/docs/shared/logging.md +202 -0
  670. package/docs/shared/monitoring.md +153 -0
  671. package/docs/shared/publishing.md +183 -0
  672. package/docs/shared/rulings.md +34 -0
  673. package/docs/shared/testing.md +147 -0
  674. package/docs/shared/theming.md +604 -0
  675. package/docs/shared/translation.md +291 -0
  676. package/docs/shared/updates.md +61 -0
  677. package/docs/templating.md +66 -0
  678. package/docs/test-boot-layer.md +143 -0
  679. package/docs/test-framework.md +411 -0
  680. package/docs/themes.md +89 -0
  681. package/docs/translations.md +73 -0
  682. package/docs/verts.md +67 -0
  683. package/docs/xss-prevention.md +95 -0
  684. package/package.json +159 -0
@@ -0,0 +1,1610 @@
1
+ /**
2
+ * Canonical schema for config/omega.json5 — pure data, no logic.
3
+ *
4
+ * Entry format is electron-manager's proven src/config/schema.js shape:
5
+ *
6
+ * {
7
+ * path: 'brand.id', // dot-path into the RESOLVED config
8
+ * type: 'string' | 'boolean' | 'number' | 'integer' | 'array' | 'object',
9
+ * required: true | false | (config) => bool,
10
+ * min: Number, // only checked when value present + number
11
+ * max: Number, // only checked when value present + number
12
+ * match: RegExp, // only checked when value present + string
13
+ * enum: [...], // only checked when value present
14
+ * default: <value>, // the framework answer — see below
15
+ * description: 'What the field drives.',
16
+ * }
17
+ *
18
+ * configSections() below derives the per-target list of top-level sections
19
+ * this schema declares ([#607](https://github.com/Omega-JS-Stack/omega/issues/607)):
20
+ * a page's frontmatter overrides config keys under a `config:` parent, and
21
+ * that list is BOTH halves of the rule — a section restated bare is a build
22
+ * error, and a key under `config:` that is not a section is one too. Page
23
+ * machinery (`meta`, `schema`, `layout`, `permalink`) is not config and has
24
+ * no home here: `meta` in particular lives in page frontmatter alone, its
25
+ * site-wide default being brand.name / brand.description (the head's
26
+ * fallback), so the config section it briefly had is registered RETIRED.
27
+ *
28
+ * `default:` is the ONE home of every config default
29
+ * ([#478](https://github.com/Omega-JS-Stack/omega/issues/478)): defaults.js
30
+ * builds the merge chain's LOWEST layer from these entries, and the manage
31
+ * walk materializes any missing block into the brand's omega.json5 with this
32
+ * description as its guiding comment. A key with no sane framework answer —
33
+ * owner decisions, tri-states that mean "ask", ids the services provision,
34
+ * anything secret-shaped — carries NO `default:` and is never materialized.
35
+ *
36
+ * `materialize: false` is the one exception to that second sentence
37
+ * ([#793](https://github.com/Omega-JS-Stack/omega/issues/793)): the rule's
38
+ * `default:` still resolves (every reader sees it) but is never WRITTEN into a
39
+ * brand file. It is for framework facts a brand may override and rarely does —
40
+ * presentation the framework owns, like the packaged connection cards — where a
41
+ * copy in every brand config would drift from the framework it came from.
42
+ *
43
+ * The other standing exclusion is the PRESENCE GATE (#425), which means the
44
+ * sections a service reads presence from: `monitoring.providers.sentry` is the
45
+ * brand's pick of Sentry, so those SDK knobs carry NO default and keep their
46
+ * home in @omega.js/monitoring, and `advertising` is the brand's pick of ads
47
+ * (#527 — the adsense `client` id is the ONE switch, and a materialized
48
+ * advertising block would switch the web build's automatic vert placements on
49
+ * for a brand that configured none). Blocks whose services gate on `enabled`
50
+ * instead — github, cloudflare, searchConsole, slapform, chatsy, replyify —
51
+ * DO carry the default that states the polarity (`enabled: true`, read
52
+ * `!== false`), because presence there picks nothing. Role-level switches
53
+ * beside any providers block (`monitoring.enabled`,
54
+ * `marketing.campaigns.enabled`) always may. The gating doctrine those three
55
+ * cases come from lives in docs/shared/config.md.
56
+ *
57
+ * Rules run against the RESOLVED config for a target — target-section keys
58
+ * are overlaid at the top level by loadConfig() (targets.desktop.platforms
59
+ * validates as `platforms`), which is also how any shared key inside a
60
+ * target entry overrides the shared value.
61
+ *
62
+ * SHARED_SCHEMA always applies; TARGET_SCHEMAS[target] adds that target's
63
+ * refinements. Sections grow as each framework adopts dual-read — seed
64
+ * entries come from EM's schema (desktop) and the sandbox brand's real @omega.js/backend
65
+ * config (backend), never from guesses.
66
+ */
67
+
68
+ // The durations a winback coupon can be built for on every provider that
69
+ // supports the offer (#268). Stripe's third option ('repeating') needs a
70
+ // duration_in_months beside it, which is provider surface nothing here asks
71
+ // for. It lives HERE, with the rule that enumerates it, so winback.js can
72
+ // derive its offer defaults from this schema without a require cycle;
73
+ // winback.js re-exports it, so every existing import keeps working.
74
+ const WINBACK_DURATIONS = ['once', 'forever'];
75
+
76
+ // Canonical target names — the FRAMEWORK keys allowed under `targets`.
77
+ // Key presence in a config's `targets` object = "this brand enables this
78
+ // target" (absorbs the legacy brand-config targets ARRAY).
79
+ const TARGETS = ['web', 'backend', 'desktop', 'extension', 'mobile'];
80
+
81
+ // A brand also runs targets no framework owns — a Render API, a worker, a
82
+ // script (#603). Those are declared under any OTHER key, and the entry must
83
+ // say so: `targets.api: { type: 'custom' }`. The type is what separates a
84
+ // deliberate custom target from a typo'd framework name, which stays an error.
85
+ // Their verbs come entirely from the target's own package.json scripts; the
86
+ // manager is the one that runs them (docs/manager/index.md).
87
+ const CUSTOM_TARGET_TYPE = 'custom';
88
+
89
+ /**
90
+ * Whether a `targets.<key>` entry declares itself custom. The array
91
+ * (multi-instance) form is custom only when EVERY instance says so — a mixed
92
+ * array has no honest reading, so the validator fails it.
93
+ *
94
+ * @param {object|Array|null|undefined} entry - The raw targets.<key> value.
95
+ * @returns {boolean} True when the entry is a custom target.
96
+ */
97
+ function isCustomTargetEntry(entry) {
98
+ if (Array.isArray(entry)) {
99
+ return entry.length > 0 && entry.every((instance) => instance?.type === CUSTOM_TARGET_TYPE);
100
+ }
101
+ return entry?.type === CUSTOM_TARGET_TYPE;
102
+ }
103
+
104
+ // What SHAPE the backend target deploys as (#584). Not to be confused with the
105
+ // custom TARGET above: that is a target no framework owns; this is the
106
+ // @omega.js/backend target itself, running its Express app on `PORT` for a
107
+ // container host (Render & co) instead of exporting Cloud Functions. Same
108
+ // framework, same auth middleware, same helpers — a different artifact, and
109
+ // with it no Functions deploy and no emulator lane.
110
+ const BACKEND_PROJECT_TYPES = ['firebase', 'custom'];
111
+
112
+ /**
113
+ * The backend target's project type. Reads a `targets.backend` entry or a
114
+ * RESOLVED backend config alike — a target entry's keys land at the top level
115
+ * of the resolved view, so `projectType` is in the same place either way.
116
+ *
117
+ * Anything that isn't the literal 'custom' reads as 'firebase': the validator
118
+ * is what makes a nonsense value loud, and a READER must never take a brand
119
+ * off Cloud Functions on a typo.
120
+ *
121
+ * @param {object|null|undefined} entry - targets.backend, or a resolved backend config.
122
+ * @returns {'firebase'|'custom'}
123
+ */
124
+ function backendProjectType(entry) {
125
+ return entry?.projectType === 'custom' ? 'custom' : 'firebase';
126
+ }
127
+
128
+ // The machine-owned top-level sections identical in every omega.json5 —
129
+ // omega-manager's disperse enumerates THIS list instead of hardcoding
130
+ // per-target mapping blocks. (`targets` itself is the scoping key, not a
131
+ // shared section; `theme` is project-owned but shared-shaped.)
132
+ const SHARED_SECTIONS = ['brand', 'cloud', 'repo', 'edge', 'captcha', 'search', 'forms', 'inbound', 'analytics', 'advertising', 'features', 'payment', 'monitoring', 'connections', 'theme', 'translation'];
133
+
134
+ // brand.id's slug rule — the ONE home for the pattern. Anything that gates a
135
+ // caller-supplied brand id (the backend verts routes' normalizeBrandId) imports
136
+ // this instead of copying the regex.
137
+ const BRAND_ID_PATTERN = /^[a-z][a-z0-9+\-.]*$/;
138
+
139
+ const SHARED_SCHEMA = [
140
+ // ── the instance's own url ───────────────────────────────────────────────
141
+ {
142
+ path: 'url',
143
+ type: 'string',
144
+ required: false,
145
+ match: /^https?:\/\//,
146
+ description: "This instance's own public URL, and the one key a target entry may set at the top level. A multi-instance target derives it from the instance id (`{ id: 'admin' }` → https://admin.<host of brand.url>, #588; docs/shared/config.md → Multi-instance targets); an entry's own `url` overrides it for a custom host. `brand.url` stays the BRAND's url, which is what brand-level facts (authDomain, the persona domain) read.",
147
+ },
148
+
149
+ // ── brand ────────────────────────────────────────────────────────────────
150
+ {
151
+ path: 'brand.id',
152
+ type: 'string',
153
+ required: true,
154
+ match: BRAND_ID_PATTERN,
155
+ description: 'URL-scheme-safe slug. Drives deep-link schemes, default appIds, repo names. Lowercase, starts with a letter, alnum/+/-/.',
156
+ },
157
+ {
158
+ path: 'brand.name',
159
+ type: 'string',
160
+ required: true,
161
+ description: 'Human-readable brand name. Default productName/site title everywhere.',
162
+ },
163
+ {
164
+ path: 'brand.url',
165
+ type: 'string',
166
+ required: false,
167
+ match: /^https?:\/\//,
168
+ description: 'Marketing site URL. Derives remote-config URLs, "Open Website" menu items, hosting boilerplate links.',
169
+ },
170
+ {
171
+ path: 'brand.description',
172
+ type: 'string',
173
+ required: false,
174
+ description: 'One-sentence brand description. Meta descriptions, store listings.',
175
+ },
176
+ {
177
+ path: 'brand.tagline',
178
+ type: 'string',
179
+ required: false,
180
+ description: 'Short marketing tagline.',
181
+ },
182
+ {
183
+ path: 'brand.company',
184
+ type: 'string',
185
+ required: false,
186
+ description: 'Parent/legal entity name ("Acme Inc"). Legal documents, receipts; falls back to brand.name.',
187
+ },
188
+ {
189
+ path: 'brand.type',
190
+ type: 'string',
191
+ required: false,
192
+ description: "The brand's schema.org type ('Organization', 'Corporation', 'LocalBusiness'). @omega.js/web stamps it on the JSON-LD brand node and every @id that cross-references it; unset renders the framework default 'Organization' (#271).",
193
+ },
194
+ {
195
+ path: 'brand.font',
196
+ type: 'string',
197
+ required: false,
198
+ description: "Display font NAME (no extension, 'CromaSans-ExtraBold') the assets service renders the wordmark + combomark from, resolved against the brand's assets/fonts/ then the system font dirs. Unset = those two logo variants are skipped, not generated with a substitute face.",
199
+ },
200
+ {
201
+ path: 'brand.contact.email',
202
+ type: 'string',
203
+ required: false,
204
+ match: /@/,
205
+ description: 'Support email surfaced in About / Help / legal pages.',
206
+ },
207
+ {
208
+ path: 'brand.contact.phone',
209
+ type: 'string',
210
+ required: false,
211
+ description: "Support phone number as it should read. @omega.js/web publishes it as the JSON-LD brand node's `telephone`; unset renders empty.",
212
+ },
213
+ {
214
+ path: 'brand.contact.person.name',
215
+ type: 'string',
216
+ required: false,
217
+ description: 'The human who signs "personal" emails, as it should read ("Jane Doe, CEO"). Required only when something sends a personal-signoff email — that path fails loudly rather than substituting a framework identity.',
218
+ },
219
+ {
220
+ path: 'brand.contact.person.firstName',
221
+ type: 'string',
222
+ required: false,
223
+ description: 'Conversational short name for personal email copy ("Jane" in "I\'m Jane, the founder"). Defaults to the first word of brand.contact.person.name.',
224
+ },
225
+ {
226
+ path: 'brand.contact.person.image',
227
+ type: 'string',
228
+ required: false,
229
+ description: 'Headshot URL for the personal email signoff. Omitted from the signoff when unset.',
230
+ },
231
+ {
232
+ path: 'brand.contact.person.url',
233
+ type: 'string',
234
+ required: false,
235
+ match: /^https?:\/\//,
236
+ description: 'Personal URL linked from the email signoff. Omitted when unset.',
237
+ },
238
+ {
239
+ path: 'brand.contact.person.urlText',
240
+ type: 'string',
241
+ required: false,
242
+ description: 'Link text for brand.contact.person.url (e.g. a social handle). Falls back to the URL itself.',
243
+ },
244
+ {
245
+ path: 'brand.contact.carbonCopy',
246
+ type: 'array',
247
+ required: false,
248
+ description: 'Audit BCC recipients ([{ email, name }]) added to transactional sends with copy: true. Unset = no audit copies; never a framework default.',
249
+ },
250
+ {
251
+ path: 'brand.address',
252
+ type: 'object',
253
+ required: false,
254
+ description: 'Postal address (line1, line2, city, region, postalCode, country). Legal pages + email footers.',
255
+ },
256
+ {
257
+ path: 'brand.color',
258
+ type: 'string',
259
+ required: false,
260
+ match: /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/,
261
+ description: 'Brand accent color as a hex string (#RGB or #RRGGBB). @omega.js/web composes the --omega-accent ramps (light + dark) from it; unset leaves the theme\'s neutral placeholder standing.',
262
+ },
263
+ {
264
+ path: 'brand.images',
265
+ type: 'object',
266
+ required: false,
267
+ description: 'Brand image URLs/paths (wordmark, brandmark, combomark, icon).',
268
+ },
269
+ {
270
+ path: 'brand.images.companyWordmark',
271
+ type: 'string',
272
+ required: false,
273
+ description: 'Parent/legal-entity wordmark (brand.company) rendered in the transactional email footer. Omitted from the footer when unset.',
274
+ },
275
+
276
+ // ── socials ──────────────────────────────────────────────────────────────
277
+ {
278
+ path: 'socials',
279
+ type: 'object',
280
+ required: false,
281
+ description: "Platform → handle ({ twitter: 'somiibo' }). The handle derives the profile URL every surface reads (JSON-LD sameAs, the footer row, omega_social), and @omega.js/web emits a redirect shortlink page at /<platform> for each entry (#429). An entry that redirects somewhere OTHER than its profile URL takes the object form { handle, redirect } — the redirect target wins for the shortlink, the handle still names the profile.",
282
+ },
283
+
284
+ // ── cloud (role: app/cloud platform; D12 provider-discriminated) ─────────
285
+ {
286
+ path: 'cloud.provider',
287
+ type: 'string',
288
+ required: false,
289
+ enum: ['firebase'],
290
+ description: "App/cloud platform provider. Only 'firebase' today — the discriminator exists so a second provider slots in without a key rename.",
291
+ },
292
+ {
293
+ path: 'cloud.config',
294
+ type: 'object',
295
+ required: false,
296
+ description: 'Provider app config. For firebase: the web-app config verbatim from the console. Public by design — the web API key is not a secret.',
297
+ },
298
+ {
299
+ path: 'cloud.config.projectId',
300
+ type: 'string',
301
+ required: false,
302
+ description: 'Drives auth, emulator project selection, analytics uuidv5 namespace, remote-config URL fallbacks.',
303
+ },
304
+ {
305
+ path: 'cloud.messaging.vapidKey',
306
+ type: 'string',
307
+ required: false,
308
+ description: 'Web-push VAPID public key (Firebase console → Cloud Messaging → Web Push certificates). Public by design — it ships to every browser; the private half stays in the console.',
309
+ },
310
+ {
311
+ path: 'cloud.shared',
312
+ type: 'boolean',
313
+ required: false,
314
+ default: false,
315
+ description: 'true = the cloud project is shared with other brands; only per-brand operations run (service-account, sdk-config).',
316
+ },
317
+ {
318
+ path: 'cloud.supportEmail',
319
+ type: 'string',
320
+ required: false,
321
+ match: /@/,
322
+ description: "OAuth consent screen support email. Defaults to the AUTHORIZING user's email — the provider rejects any address the caller doesn't own; set only for an owned Google Group.",
323
+ },
324
+ {
325
+ path: 'cloud.oauthRedirectsConfigured',
326
+ type: 'boolean',
327
+ required: false,
328
+ description: "true = the Google OAuth client's authorized origins + redirect URIs are configured by hand. The ONE reconcile flag config keeps (#434) — that surface has no API to re-check, so the cloud service asks once, writes the answer back here, and every later run trusts it. Never set it without doing the work: the confirm is the only proof there is.",
329
+ },
330
+ {
331
+ path: 'cloud.consentAudience',
332
+ type: 'boolean',
333
+ required: false,
334
+ description: "false = stop asking about the OAuth consent screen's audience. Tri-state opt-out (#667): Google gives NO API write for the audience, so an Internal screen (Error 403: org_internal for every non-org account) is a manage-time stopper that opens the console page and polls — `false` records the deliberate choice to live with an org-only sign-in, and only the cloud service's audience step goes quiet.",
335
+ },
336
+ {
337
+ path: 'cloud.apiSubdomain',
338
+ type: 'boolean',
339
+ required: false,
340
+ default: true,
341
+ description: 'false = skip the api.{domain} hosting custom domain.',
342
+ },
343
+ {
344
+ path: 'cloud.organizationId',
345
+ type: 'string|boolean',
346
+ required: false,
347
+ description: 'Cloud organization ID the project is created inside — tri-state: unset = ask at project create, false = no org (standalone), value = create inside it.',
348
+ },
349
+ {
350
+ path: 'cloud.billingAccount',
351
+ type: 'string|boolean',
352
+ required: false,
353
+ description: "Billing account ('billingAccounts/XXXXXX-XXXXXX-XXXXXX') — tri-state: unset = ask, false = stay on the free tier, value = auto-upgrade.",
354
+ },
355
+
356
+ // ── analytics ────────────────────────────────────────────────────────────
357
+ {
358
+ path: 'analytics.providers.google.id',
359
+ type: 'string',
360
+ required: false,
361
+ match: /^G-[A-Z0-9]+$/,
362
+ description: 'GA4 Measurement ID. Presence-driven: set G-XXXXXXXXXX to enable. Secret lives in process.env.GOOGLE_ANALYTICS_SECRET.',
363
+ },
364
+ {
365
+ path: 'analytics.providers.google.propertyId',
366
+ type: 'string',
367
+ required: false,
368
+ description: 'GA4 property id (digits, as the Admin API returns it) — the manager\'s analytics service reconciles the brand\'s data streams and the Firebase link against it, and it gates the whole google half: no propertyId, no google operations. Interactive setup selects/creates the property and writes it back here.',
369
+ },
370
+ {
371
+ path: 'analytics.providers.google.accountId',
372
+ type: 'string',
373
+ required: false,
374
+ description: "GA account id the property lives under (digits) — the analytics property picker and the console bookmarks read it. A company-managed brand inherits the COMPANY layer's value through the merge chain; a brand-level value always wins.",
375
+ },
376
+ {
377
+ path: 'analytics.providers.meta.id',
378
+ type: 'string',
379
+ required: false,
380
+ description: 'Meta (Facebook) Pixel ID. Presence-driven.',
381
+ },
382
+ {
383
+ path: 'analytics.providers.meta.accountId',
384
+ type: 'string',
385
+ required: false,
386
+ description: "Meta AD ACCOUNT id ('act_…' or the bare digits) the pixel is created on and found by name against. A non-secret platform id, so it lives here beside google.accountId — the token that reaches the account stays in .env (META_ACCESS_TOKEN).",
387
+ },
388
+ {
389
+ path: 'analytics.providers.tiktok.id',
390
+ type: 'string',
391
+ required: false,
392
+ description: 'TikTok Pixel ID. Presence-driven.',
393
+ },
394
+ {
395
+ path: 'analytics.providers.tiktok.accountId',
396
+ type: 'string',
397
+ required: false,
398
+ description: 'TikTok ADVERTISER id the pixel is created on. Same rule as meta.accountId: the id lives here, TIKTOK_ACCESS_TOKEN stays in .env.',
399
+ },
400
+ {
401
+ path: 'analytics.providers.tiktok.appId',
402
+ type: 'string',
403
+ required: false,
404
+ description: "TikTok DEVELOPER APP id the access-token mint authorizes through (#448) — public config, so the manager can walk the portal without asking twice; setup writes it back here (#635). The app secret is pasted once and never saved, and the token lands in .env (TIKTOK_ACCESS_TOKEN).",
405
+ },
406
+
407
+ // ── advertising ──────────────────────────────────────────────────────────
408
+ {
409
+ path: 'advertising.providers.adsense.client',
410
+ type: 'string',
411
+ required: false,
412
+ description: 'AdSense publisher client id (ca-pub-…). The ONE adsense switch (#527), presence-driven: set it and the manager service manages the account, the site renders units from it and ads.txt carries the record; leave it out and none of that happens. No second gate — a managed-but-ad-free brand is deliberately inexpressible.',
413
+ },
414
+ {
415
+ path: 'advertising.providers.adsense.displaySlot',
416
+ type: 'string',
417
+ required: false,
418
+ description: 'Default AdSense slot id for display units (per-include override wins).',
419
+ },
420
+ {
421
+ path: 'advertising.providers.adsense.inArticleSlot',
422
+ type: 'string',
423
+ required: false,
424
+ description: 'Default AdSense slot id for in-article units.',
425
+ },
426
+ {
427
+ path: 'advertising.providers.adsense.inFeedSlot',
428
+ type: 'string',
429
+ required: false,
430
+ description: 'Default AdSense slot id for in-feed units.',
431
+ },
432
+ {
433
+ path: 'advertising.providers.adsense.multiplexSlot',
434
+ type: 'string',
435
+ required: false,
436
+ description: 'Default AdSense slot id for multiplex units.',
437
+ },
438
+ {
439
+ path: 'advertising.providers.inhouse.source',
440
+ type: 'string',
441
+ required: false,
442
+ description: "In-house ads source: 'self' (this brand's backend serves its own `ads` inventory), 'company' (the parent company's api — resolved via company.url), or a full base URL used verbatim. Presence-driven — unset disables house units.",
443
+ },
444
+ {
445
+ path: 'advertising.fallback',
446
+ type: 'string|boolean',
447
+ required: false,
448
+ description: "The role a provider miss falls through to — 'inhouse' renders the house unit, false (or absent) ends the ladder at the built-in promo. Role-level, beside `providers` (docs/web/ads-system.md).",
449
+ },
450
+ {
451
+ path: 'advertising.tags',
452
+ type: 'array',
453
+ required: false,
454
+ description: "This brand's contextual tags (['music', 'audio-tools']) — the targeting match input a house unit sends with its serve request unless the include passes its own. No user tracking.",
455
+ },
456
+
457
+ // ── company ──────────────────────────────────────────────────────────────
458
+ {
459
+ path: 'company.url',
460
+ type: 'string',
461
+ required: false,
462
+ match: /^https?:\/\//,
463
+ description: "The parent company's canonical URL. Sub-brand surfaces derive the company api from it (e.g. advertising inhouse source 'company' → api.<company host>).",
464
+ },
465
+
466
+ // ── features (the catalog every product prices) ──────────────────────────
467
+ {
468
+ path: 'features',
469
+ type: 'object',
470
+ required: false,
471
+ description: "The feature catalog (#647): every feature DEFINED once, keyed by id, in the order every surface renders it. An entry carries name, icon, definition — and, when the feature is METERED, a `usage` block ({ pace: 'daily' | false, mirror: ['<doc kind>'] }); an entry without one is a perk. Products name only the VALUE (payment.products[].features). Key order is row order on the pricing page and the account's usage bars.",
472
+ },
473
+
474
+ // ── payment (role: billing; provider-discriminated) ──────────────────────
475
+ {
476
+ path: 'payment.providers.stripe.publishableKey',
477
+ type: 'string',
478
+ required: false,
479
+ match: /^pk_(test|live)_/,
480
+ description: 'Stripe publishable key. Lives in config (not secret); the secret key stays in env.',
481
+ },
482
+ {
483
+ path: 'payment.providers.paypal.clientId',
484
+ type: 'string',
485
+ required: false,
486
+ description: 'PayPal client ID. Lives in config; secret stays in env.',
487
+ },
488
+ {
489
+ path: 'payment.providers.chargebee.site',
490
+ type: 'string',
491
+ required: false,
492
+ description: 'Chargebee site slug.',
493
+ },
494
+ {
495
+ path: 'payment.providers.coinbase.enabled',
496
+ type: 'boolean',
497
+ required: false,
498
+ default: false,
499
+ description: "Coinbase Commerce (crypto) checkout, one-time purchases only. Its ONLY credential is a secret (COINBASE_COMMERCE_API_KEY), so there is no public datum to gate on — this is the switch, default OFF, because an accidental ON shows a Crypto button no key can complete.",
500
+ },
501
+ {
502
+ path: 'payment.products',
503
+ type: 'array',
504
+ required: false,
505
+ description: 'Product catalog (@omega.js/backend-shaped: id, name, type, prices, per-provider IDs) — referenceable from every target. The ONLY pricing-page source (C2); optional presentation fields: tagline, popular, enterprise (the talk-to-us tier: its own full-width row, never a card), hidden (still created on every provider and purchasable by id — QA tiers, grandfathered plans — but never rendered on the pricing page), url, features { <catalog id>: value } — a number on a counted feature (the MONTHLY limit, -1 unlimited), true/false/a string on a perk (#647).',
506
+ },
507
+ {
508
+ path: 'payment.winback.enabled',
509
+ type: 'boolean',
510
+ required: false,
511
+ default: true,
512
+ description: 'The cancel-flow save offer (#268), shown before the cancellation questionnaire. Defaults ON — false is the whole off switch, and the cancel flow goes straight to the questionnaire.',
513
+ },
514
+ {
515
+ path: 'payment.winback.percent',
516
+ type: 'integer',
517
+ required: false,
518
+ min: 1,
519
+ max: 100,
520
+ default: 50,
521
+ description: 'Whole percentage off the next cycle the save offer pitches. Defaults to 50. Mutually exclusive with payment.winback.amount.',
522
+ },
523
+ {
524
+ path: 'payment.winback.amount',
525
+ type: 'number',
526
+ required: false,
527
+ min: 0.01,
528
+ description: "Flat amount off the next cycle (payment.currency's major unit, e.g. 10 = $10), instead of a percentage. Mutually exclusive with payment.winback.percent.",
529
+ },
530
+ {
531
+ path: 'payment.winback.duration',
532
+ type: 'string',
533
+ required: false,
534
+ enum: WINBACK_DURATIONS,
535
+ default: 'once',
536
+ description: "How long the accepted offer lasts: 'once' (the next cycle only, the default) or 'forever' (a permanent price cut).",
537
+ },
538
+
539
+ // ── monitoring (role: error monitoring; provider-keyed, #425) ────────────
540
+ {
541
+ path: 'monitoring.enabled',
542
+ type: 'boolean',
543
+ required: false,
544
+ default: true,
545
+ description: 'Role-level switch (default true). false skips the monitoring service outright, whatever the providers block says.',
546
+ },
547
+ {
548
+ path: 'monitoring.providers.sentry.org',
549
+ type: 'string',
550
+ required: false,
551
+ description: 'Sentry organization slug (public). Auto-detected when the auth token sees exactly one org — the monitoring service writes it back here.',
552
+ },
553
+ {
554
+ path: 'monitoring.providers.sentry.dsn',
555
+ type: 'string',
556
+ required: false,
557
+ match: /^https?:\/\//,
558
+ description: 'Sentry DSN (public by design). Per-surface DSNs go in targets.<type>.monitoring.providers.sentry.dsn overrides.',
559
+ },
560
+ {
561
+ path: 'monitoring.providers.sentry.environment',
562
+ type: 'string',
563
+ required: false,
564
+ description: "Environment tag every event carries. Unset (or null) lets the host's own gate name it: 'production' on a production run, 'development' otherwise.",
565
+ },
566
+ {
567
+ path: 'monitoring.providers.sentry.sampleRate',
568
+ type: 'number',
569
+ required: false,
570
+ min: 0,
571
+ max: 1,
572
+ description: 'Fraction of ERROR events kept, 0..1. Defaults to 1 (keep everything).',
573
+ },
574
+ {
575
+ path: 'monitoring.providers.sentry.tracesSampleRate',
576
+ type: 'number',
577
+ required: false,
578
+ min: 0,
579
+ max: 1,
580
+ description: 'Fraction of performance traces kept, 0..1. Defaults to 0.1.',
581
+ },
582
+ {
583
+ path: 'monitoring.providers.sentry.replaysSessionSampleRate',
584
+ type: 'number',
585
+ required: false,
586
+ min: 0,
587
+ max: 1,
588
+ description: 'Browser only: fraction of SESSIONS recorded as a replay, 0..1. Defaults to 0 — replay costs bandwidth and captures the DOM, so it is strictly opt-in; any value above 0 loads the replay integration.',
589
+ },
590
+ {
591
+ path: 'monitoring.providers.sentry.replaysOnErrorSampleRate',
592
+ type: 'number',
593
+ required: false,
594
+ min: 0,
595
+ max: 1,
596
+ description: 'Browser only: fraction of ERRORING sessions recorded as a replay, 0..1. Defaults to 0 (same opt-in gate as replaysSessionSampleRate).',
597
+ },
598
+ {
599
+ path: 'monitoring.providers.sentry.scrubEmail',
600
+ type: 'boolean',
601
+ required: false,
602
+ description: 'PII guard on the user attached to an event: the uid always rides, the email only when this is explicitly false. Defaults to true (scrubbed).',
603
+ },
604
+ {
605
+ path: 'monitoring.providers.sentry.attachScreenshot',
606
+ type: 'boolean',
607
+ required: false,
608
+ description: '@omega.js/desktop only: attach a screenshot of the window to a captured event. Defaults to false.',
609
+ },
610
+ {
611
+ path: 'monitoring.providers.sentry.bundlePatterns',
612
+ type: 'array',
613
+ required: false,
614
+ description: "Browser only: the URL fragments that identify OUR bundles — a browser event reports only when a stack frame matches one. Defaults to ['/assets/js/'], where both @omega.js/web and @omega.js/extension serve every framework bundle.",
615
+ },
616
+
617
+ // ── connections ──────────────────────────────────────────────────────────
618
+ {
619
+ path: 'connections',
620
+ type: 'object',
621
+ required: false,
622
+ // RESOLUTION-ONLY ([#793](https://github.com/Omega-JS-Stack/omega/issues/793)):
623
+ // this default is framework PRESENTATION, never an owner decision, so it
624
+ // resolves into every read and is never written into a brand's omega.json5 —
625
+ // five copied provider blocks in every brand config would drift from the
626
+ // framework they came from. A brand overrides any key it wants; the rest
627
+ // keeps tracking the framework.
628
+ materialize: false,
629
+ // The five providers @omega.js/backend ships a module for, each with what
630
+ // the account page's card DRAWS ([#793](https://github.com/Omega-JS-Stack/omega/issues/793)):
631
+ // the framework owns the presentation so a brand's entry is one line, and
632
+ // the ordinary merge chain lets a brand overwrite any of it. `enabled` is
633
+ // false here because a packaged provider still needs its
634
+ // CONNECTIONS_<PROVIDER>_CLIENT_ID pair before a card could connect
635
+ // anything — turning one on is the brand's act.
636
+ default: {
637
+ google: {
638
+ enabled: false,
639
+ name: 'Google',
640
+ logo: 'google',
641
+ description: 'Enable single sign-on with your Google account',
642
+ },
643
+ discord: {
644
+ enabled: false,
645
+ name: 'Discord',
646
+ logo: 'discord',
647
+ description: 'Connect to access Discord community features',
648
+ },
649
+ spotify: {
650
+ enabled: false,
651
+ name: 'Spotify',
652
+ logo: 'spotify',
653
+ description: 'Connect your Spotify listening account',
654
+ },
655
+ twitch: {
656
+ enabled: false,
657
+ name: 'Twitch',
658
+ logo: 'twitch',
659
+ description: 'Connect your Twitch channel',
660
+ },
661
+ kick: {
662
+ enabled: false,
663
+ name: 'Kick',
664
+ logo: 'kick',
665
+ description: 'Connect your Kick channel',
666
+ },
667
+ },
668
+ description: "Per-provider user-connection settings, keyed by provider name (`connections: { twitch: {…} }`) — public values only, never client secrets (those are the CONNECTIONS_<PROVIDER>_CLIENT_ID/_SECRET env pair). The set of providers is OPEN (a brand ships its own as `src/connections/<name>.js`), so the section stays free-form; the keys every entry may carry are `enabled` (the packaged providers default to false — set it true to offer one; a brand's own provider is on unless this is false), `scope` (an array that wins over the provider module's default), `name` and `logo` (what the account page's card draws — the CONFIG is the only card list, #793: `logo` is the name of a mark @omega.js/web ships in core/logos/brandmarks/original, rendered inline, or a full URL rendered as an img), and `description` (the line under the card's title). The five packaged providers carry all three by default, so enabling one is one line. packages/backend/docs/connections.md",
669
+ },
670
+
671
+ // ── repo (role: source hosting; provider-discriminated) ─────────────────
672
+ {
673
+ path: 'repo.providers.github.enabled',
674
+ type: 'boolean',
675
+ required: false,
676
+ default: true,
677
+ description: 'false = the repo service skips entirely — no org profile, no repo settings, no GitHub Pages reconciliation; the brand hosts its source somewhere the manager does not touch.',
678
+ },
679
+ {
680
+ path: 'repo.providers.github.org',
681
+ type: 'string',
682
+ required: false,
683
+ description: "GitHub org/user the brand monorepo lives in. No org → the repo service skips and the site's desktop release URLs stay underived.",
684
+ },
685
+ {
686
+ path: 'repo.providers.github.repo',
687
+ type: 'string',
688
+ required: false,
689
+ description: 'Brand repo name, or an "owner/name" slug when the repo lives under a different org than repo.providers.github.org. Defaults to <brand.id>-omega (the <brand.id>-<role> repo rule).',
690
+ },
691
+ {
692
+ path: 'repo.providers.github.shared',
693
+ type: 'boolean',
694
+ required: false,
695
+ default: false,
696
+ description: 'true = the org is shared with other brands; org-level reconciliation is skipped.',
697
+ },
698
+ {
699
+ path: 'repo.providers.github.private',
700
+ type: 'boolean',
701
+ required: false,
702
+ default: true,
703
+ description: 'Brand repo visibility (default true).',
704
+ },
705
+
706
+ // ── domain (two roles: registrar + mailbox; provider-keyed, #425) ────────
707
+ {
708
+ path: 'domain.providers',
709
+ type: 'object',
710
+ required: false,
711
+ description: "The brand's REGISTRAR, named as the one KEY under it ({ namecheap: {} }) — presence picks it, and no entry means nothing chosen, so the domain service skips. The provider set is open; the entry may be empty.",
712
+ },
713
+ {
714
+ path: 'domain.providers.namecheap',
715
+ type: 'object',
716
+ required: false,
717
+ description: 'Namecheap as the registrar — the one provider whose nameservers the domain service points at the Cloudflare zone via API (NAMECHEAP_USERNAME + NAMECHEAP_API_KEY in .env). An empty object is the whole declaration; every other registrar gets manual instructions.',
718
+ },
719
+ {
720
+ path: 'domain.email.providers',
721
+ type: 'object',
722
+ required: false,
723
+ description: "The brand's MAILBOX provider, named as the one KEY under it ({ cloudflare: {} } | { squarespace: {} } | { privateemail: {} }) — presence picks it; no entry leaves the edge service's email-routing operations off.",
724
+ },
725
+ {
726
+ path: 'domain.email.forwarding',
727
+ type: 'array',
728
+ required: false,
729
+ description: "Address forwarding rules ([{ from: 'support' | '*', to: 'inbox@example.com' }]) the mailbox provider reconciles. Role-level and provider-agnostic.",
730
+ },
731
+
732
+ // ── edge (role: CDN/DNS edge; provider-discriminated) ────────────────────
733
+ {
734
+ path: 'edge.providers.cloudflare.enabled',
735
+ type: 'boolean',
736
+ required: false,
737
+ default: true,
738
+ description: 'false = the edge service skips entirely — no zone, DNS, settings, rules, speed-test or worker reconciliation for this brand.',
739
+ },
740
+ {
741
+ path: 'edge.providers.cloudflare',
742
+ type: 'object',
743
+ required: false,
744
+ description: 'Cloudflare zone reconciliation (@omega.js/manager cloudflare service): dns, settings, rules, cacheRules, speedTest, workers. Tokens live in .env.',
745
+ },
746
+ {
747
+ path: 'edge.providers.cloudflare.zone',
748
+ type: 'string',
749
+ required: false,
750
+ description: "Cloudflare zone id. The web build's cache purge (`omega purge`) targets it; unset = purge skips.",
751
+ },
752
+ {
753
+ path: 'edge.providers.cloudflare.rules.redirect',
754
+ type: 'array',
755
+ required: false,
756
+ description: "The zone's dynamic redirect rules, ORDERED — the ONE home for a TEMPLATED redirect, i.e. one whose destination is computed from the request path (#466): [{ name, expression, statusCode, preserveQueryString, targetUrl, enabled }]. `expression` and `targetUrl` ({ value } for a fixed URL, { expression } for a computed one) are Cloudflare's own filter language, because only the edge can answer a URL the build cannot enumerate — DashQR's printed `/c/<id>` codes redirect to `/code?id=<id>` with `targetUrl.expression: concat(\"https://\", http.host, \"/code?id=\", substring(http.request.uri.path, 3))`. The @omega.js/manager edge service reconciles them by `name`. A redirect whose URLs CAN be enumerated is a redirect PAGE instead (docs/web/index.md), never config.",
757
+ },
758
+
759
+ // ── captcha (role: bot defense; provider-discriminated) ──────────────────
760
+ {
761
+ path: 'captcha.providers.recaptcha.project',
762
+ type: 'string',
763
+ required: false,
764
+ description: "The brand's OWN GCP project hosting the classic reCAPTCHA key — used only for the console deep-link in guidance. Site/secret keys stay in .env.",
765
+ },
766
+ {
767
+ path: 'captcha.providers.recaptcha.siteKey',
768
+ type: 'string',
769
+ required: false,
770
+ description: 'Classic reCAPTCHA site key rendered client-side. Public by design — the secret half stays in .env (RECAPTCHA_SECRET_KEY).',
771
+ },
772
+ {
773
+ path: 'captcha.providers.recaptcha.domainsConfirmed',
774
+ type: 'array',
775
+ required: false,
776
+ description: "Domains the owner has confirmed on the classic reCAPTCHA key (the API cannot read the key's domain list, so the confirmation is the record). Machine-written: the captcha service stamps a domain here once, and finding it is what keeps later runs from asking again.",
777
+ },
778
+
779
+ // ── forms (role: form handling; provider-discriminated) ──────────────────
780
+ {
781
+ path: 'forms.providers.slapform.enabled',
782
+ type: 'boolean',
783
+ required: false,
784
+ default: true,
785
+ description: 'false = the slapform service skips and the contact page has no form endpoint.',
786
+ },
787
+ {
788
+ path: 'forms.providers.slapform.formId',
789
+ type: 'string',
790
+ required: false,
791
+ description: 'Slapform form id — the contact page posts to https://api.slapform.com/{formId}. Interactive manager runs land it here.',
792
+ },
793
+ {
794
+ path: 'forms.providers.slapform.templateFormId',
795
+ type: 'string',
796
+ required: false,
797
+ description: 'Existing form the new form is cloned from when the service creates one.',
798
+ },
799
+ {
800
+ path: 'forms.providers.slapform.updateFormInfo',
801
+ type: 'boolean',
802
+ required: false,
803
+ default: true,
804
+ description: 'false = the form is shared and managed by another brand; its name/settings are left alone.',
805
+ },
806
+ {
807
+ path: 'forms.providers.slapform.plan',
808
+ type: 'object',
809
+ required: false,
810
+ default: { id: 'grandmaster', name: 'Grandmaster' },
811
+ description: 'Tier granted to the form-owner account ({ id, name }) — Slapform-operator only.',
812
+ },
813
+
814
+ // ── inbound (role: inbound conversations; per-channel providers) ─────────
815
+ {
816
+ path: 'inbound.chat.providers.chatsy.enabled',
817
+ type: 'boolean',
818
+ required: false,
819
+ default: true,
820
+ description: 'false = no chat widget boots and the chatsy service skips.',
821
+ },
822
+ {
823
+ path: 'inbound.chat.providers.chatsy.agentId',
824
+ type: 'string',
825
+ required: false,
826
+ description: 'Chatsy agent id. The manager writes it back here; @omega.js/client boots the widget from it.',
827
+ },
828
+ {
829
+ path: 'inbound.chat.providers.chatsy.accountId',
830
+ type: 'string',
831
+ required: false,
832
+ description: "Chatsy owner-account uid the agent belongs to — the account whose subscription the chat service reconciles to `plan`. The service resolves it from the agent itself; set it only when the agent's owner is provisioned elsewhere.",
833
+ },
834
+ {
835
+ path: 'inbound.chat.providers.chatsy.templateAgentId',
836
+ type: 'string',
837
+ required: false,
838
+ description: 'Existing agent the new agent is cloned from when the service creates one.',
839
+ },
840
+ {
841
+ path: 'inbound.chat.providers.chatsy.updateAgentInfo',
842
+ type: 'boolean',
843
+ required: false,
844
+ default: true,
845
+ description: 'false = the agent is shared and managed by another brand; its settings/knowledge are left alone.',
846
+ },
847
+ {
848
+ path: 'inbound.chat.providers.chatsy.plan',
849
+ type: 'object',
850
+ required: false,
851
+ default: { id: 'max', name: 'Max' },
852
+ description: 'Tier granted to the agent-owner account ({ id, name }) — Chatsy-operator only.',
853
+ },
854
+ {
855
+ path: 'inbound.chat.providers.chatsy.sponsorshipsUrl',
856
+ type: 'string',
857
+ required: false,
858
+ description: "Sponsorship page the baseline knowledge points at. Unset → {website}/contact.",
859
+ },
860
+ {
861
+ path: 'inbound.chat.providers.chatsy.settings',
862
+ type: 'object',
863
+ required: false,
864
+ description: 'Widget presentation settings passed verbatim to the Chatsy SDK by @omega.js/client.',
865
+ },
866
+ {
867
+ path: 'inbound.email.providers.replyify.enabled',
868
+ type: 'boolean',
869
+ required: false,
870
+ default: true,
871
+ description: 'false = the replyify service skips.',
872
+ },
873
+ {
874
+ path: 'inbound.email.providers.replyify.agentId',
875
+ type: 'string',
876
+ required: false,
877
+ description: 'Replyify agent id. Interactive manager runs land it here.',
878
+ },
879
+ {
880
+ path: 'inbound.email.providers.replyify.templateAgentId',
881
+ type: 'string',
882
+ required: false,
883
+ description: 'Existing agent the new agent is cloned from when the service creates one.',
884
+ },
885
+ {
886
+ path: 'inbound.email.providers.replyify.updateAgentInfo',
887
+ type: 'boolean',
888
+ required: false,
889
+ default: true,
890
+ description: 'false = the agent is shared and managed by another brand; its filter/knowledge are left alone.',
891
+ },
892
+ {
893
+ path: 'inbound.email.providers.replyify.plan',
894
+ type: 'object',
895
+ required: false,
896
+ default: { id: 'max', name: 'Max' },
897
+ description: 'Tier granted to the agent-owner account ({ id, name }) — Replyify-operator only.',
898
+ },
899
+ {
900
+ path: 'inbound.email.providers.replyify.discount',
901
+ type: 'object',
902
+ required: false,
903
+ description: 'Discount the baseline knowledge offers ({ code, label }). Unset = no discount section.',
904
+ },
905
+
906
+ // ── search (role: search-engine surfaces; provider-discriminated) ────────
907
+ {
908
+ path: 'search.providers.searchConsole.enabled',
909
+ type: 'boolean',
910
+ required: false,
911
+ default: true,
912
+ description: 'false = the search service skips entirely — the property is never verified and no sitemap is submitted (submitSitemap only turns the sitemap half off).',
913
+ },
914
+ {
915
+ path: 'search.providers.searchConsole.submitSitemap',
916
+ type: 'boolean',
917
+ required: false,
918
+ default: true,
919
+ description: 'false = skip sitemap submission to Google Search Console.',
920
+ },
921
+ {
922
+ path: 'search.providers.searchConsole.sitemapPaths',
923
+ type: 'array',
924
+ required: false,
925
+ default: ['/sitemap.xml'],
926
+ description: "Sitemap paths submitted as https://{domain}{path} (default ['/sitemap.xml']).",
927
+ },
928
+ {
929
+ path: 'search.providers.searchConsole.gaLinked',
930
+ type: 'boolean',
931
+ required: false,
932
+ description: 'true = the owner has associated this Search Console property with the brand\'s GA property (no API exists for the link, so the confirmation is the record). Machine-written: the search service stamps it after the interactive association, and finding it is what keeps later runs from asking again.',
933
+ },
934
+
935
+ // ── certificates (role: code signing; provider-discriminated) ────────────
936
+ {
937
+ path: 'certificates.enabled',
938
+ type: 'boolean',
939
+ required: false,
940
+ description: 'false = the certificates service skips entirely — no bundle ids, no signing certificates, no provisioning profiles for this brand. Absent reads as ON for brands with a desktop/mobile target (the service still skips one with neither).',
941
+ },
942
+ {
943
+ path: 'certificates.providers.apple.bundleIdPrefix',
944
+ type: 'string',
945
+ required: false,
946
+ description: "Reverse-DNS prefix the brand's bundle id is composed from ('com.mycompany' + brand.id → com.mycompany.my.brand). The brand's own answer — the onboard wizard seeds it and setup asks for it (#635); unset, the Apple operations have no id to reconcile.",
947
+ },
948
+ {
949
+ path: 'certificates.providers.apple.capabilities',
950
+ type: 'array',
951
+ required: false,
952
+ description: "App Store Connect capabilities enabled on the bundle id (['APPLE_ID_AUTH']). Unset uses the framework set.",
953
+ },
954
+ {
955
+ path: 'certificates.providers.apple.profiles',
956
+ type: 'array',
957
+ required: false,
958
+ description: 'Certificate types that get a provisioning profile per applicable platform (IOS_DISTRIBUTION, MAC_APP_DISTRIBUTION, DEVELOPER_ID_APPLICATION_G2). Unset uses the framework set.',
959
+ },
960
+ {
961
+ path: 'certificates.providers.apple.certificates',
962
+ type: 'array',
963
+ required: false,
964
+ description: "Certificate types managed for the Apple Developer account ([{ type, manual }]) — `manual: true` marks the ones Apple's API cannot create, which the Account Holder downloads from the portal. Unset uses the framework set.",
965
+ },
966
+
967
+ // ── devlog (role: commit-digest publishing; provider-discriminated) ──────
968
+ {
969
+ path: 'devlog',
970
+ type: 'object',
971
+ required: false,
972
+ description: 'Commit-digest devlog pipeline (@omega.js/manager devlog): enabled + providers.ghostii (orgs, lookbackDays, publish settings).',
973
+ },
974
+ {
975
+ path: 'devlog.enabled',
976
+ type: 'boolean',
977
+ required: false,
978
+ description: 'true PUBLISHES AI-written commit-digest posts to the brand\'s live website. Case 3 (docs/shared/config.md): the literal true is the only ON, absence is off, and no default is materialized — publishing is never implicit.',
979
+ },
980
+
981
+ // ── seo ──────────────────────────────────────────────────────────────────
982
+ {
983
+ path: 'seo',
984
+ type: 'object',
985
+ required: false,
986
+ description: 'Parasite-SEO content: seo.github.content is the @omega.js/manager seo service\'s repos, and big content blocks may live in the config/seo.json5 sidecar. The indexing switch is NOT here: it is `targets.web.meta.index`, the same name a page writes (#564).',
987
+ },
988
+
989
+ // ── account ──────────────────────────────────────────────────────────────
990
+ {
991
+ path: 'account',
992
+ type: 'object',
993
+ required: false,
994
+ description: "Managed Firebase Auth accounts (@omega.js/manager account service): enabled + admins ([{ email, account, marketing }]; '{domain}' templates to the brand domain). Owner-defined — typically set once in the COMPANY omega.json5 (arrays replace, so the company list wins whole). Passwords NEVER live here: per-account OMEGA_ACCOUNT_PASSWORD__* env vars, the config/hooks/account/password.js hook, or the ACCOUNT_PASSWORD_SEED derivation.",
995
+ },
996
+
997
+ // ── parent ───────────────────────────────────────────────────────────────
998
+ // parent … dataRequest are brand-level sections the manager reads UNFOLDED
999
+ // (#277): a website-only brand has no targets.backend to hold them, and
1000
+ // adding one purely as a config home would falsely enable the target. A
1001
+ // targets.backend block still overrides any of them: that comes free from
1002
+ // the merge chain.
1003
+ {
1004
+ path: 'parent',
1005
+ type: 'string|boolean',
1006
+ required: false,
1007
+ description: "Webhook parent topology: 'self' when this brand IS the parent, a parent URL otherwise, or false to deliberately opt out (shared webhook account owned elsewhere).",
1008
+ },
1009
+
1010
+ // ── github ───────────────────────────────────────────────────────────────
1011
+ {
1012
+ path: 'github',
1013
+ type: 'object',
1014
+ required: false,
1015
+ description: 'GitHub identity for the brand (user, website repo URL).',
1016
+ },
1017
+
1018
+ // ── reviews ──────────────────────────────────────────────────────────────
1019
+ {
1020
+ path: 'reviews',
1021
+ type: 'object',
1022
+ required: false,
1023
+ description: 'Review-collection settings (enabled, sites).',
1024
+ },
1025
+
1026
+ // ── marketing (two roles: campaigns + newsletter, provider-keyed, #425) ──
1027
+ // Enumerated field by field (#425): the section used to validate as one
1028
+ // opaque object, so a typo in a list id or a publication id read as nothing
1029
+ // and the sync silently landed contacts in the wrong place. The section entry
1030
+ // stays too — it is one of the shared keys #277 types at brand level.
1031
+ {
1032
+ path: 'marketing',
1033
+ type: 'object',
1034
+ required: false,
1035
+ description: 'Marketing automation: the campaigns + newsletter roles (each provider-keyed) and the prune switch.',
1036
+ },
1037
+ {
1038
+ path: 'marketing.campaigns.enabled',
1039
+ type: 'boolean',
1040
+ required: false,
1041
+ default: true,
1042
+ description: 'Role-level switch for email marketing (default true). false skips the campaigns service and the backend contact sync.',
1043
+ },
1044
+ {
1045
+ path: 'marketing.campaigns.providers.sendgrid.listId',
1046
+ type: 'string',
1047
+ required: false,
1048
+ description: "SendGrid Marketing list UUID the brand's contacts land in. Written back by the campaigns service; unset means contacts reach All Contacts only.",
1049
+ },
1050
+ {
1051
+ path: 'marketing.campaigns.providers.sendgrid.groups.orders',
1052
+ type: 'integer',
1053
+ required: false,
1054
+ description: "SendGrid unsubscribe (ASM) group id for order and billing email (receipts, renewals, refunds, plan changes). Written back by the campaigns service, which provisions the group by name; ASM ids are per SendGrid ACCOUNT, so this is config and never code. Unset makes @omega.js/backend fail the send loudly.",
1055
+ },
1056
+ {
1057
+ path: 'marketing.campaigns.providers.sendgrid.groups.hello',
1058
+ type: 'integer',
1059
+ required: false,
1060
+ description: "SendGrid unsubscribe (ASM) group id for onboarding email (welcome, checkup, feedback request). Written back by the campaigns service, which provisions the group by name; ASM ids are per SendGrid ACCOUNT, so this is config and never code. Unset makes @omega.js/backend fail the send loudly.",
1061
+ },
1062
+ {
1063
+ path: 'marketing.campaigns.providers.sendgrid.groups.account',
1064
+ type: 'integer',
1065
+ required: false,
1066
+ description: "SendGrid unsubscribe (ASM) group id for account-action email (deletion, data requests) — also the fallback for any send naming no group. Written back by the campaigns service, which provisions the group by name; ASM ids are per SendGrid ACCOUNT, so this is config and never code. Unset makes @omega.js/backend fail the send loudly.",
1067
+ },
1068
+ {
1069
+ path: 'marketing.campaigns.providers.sendgrid.groups.marketing',
1070
+ type: 'integer',
1071
+ required: false,
1072
+ description: "SendGrid unsubscribe (ASM) group id for promotional email (offers, win-back, abandoned cart) and every marketing Single Send. Written back by the campaigns service, which provisions the group by name; ASM ids are per SendGrid ACCOUNT, so this is config and never code. Unset makes @omega.js/backend fail the send loudly.",
1073
+ },
1074
+ {
1075
+ path: 'marketing.campaigns.providers.sendgrid.groups.security',
1076
+ type: 'integer',
1077
+ required: false,
1078
+ description: "SendGrid unsubscribe (ASM) group id for security email (password reset, 2FA, sign-in alerts). Written back by the campaigns service, which provisions the group by name; ASM ids are per SendGrid ACCOUNT, so this is config and never code. Unset makes @omega.js/backend fail the send loudly.",
1079
+ },
1080
+ {
1081
+ path: 'marketing.campaigns.providers.sendgrid.groups.newsletter',
1082
+ type: 'integer',
1083
+ required: false,
1084
+ description: "SendGrid unsubscribe (ASM) group id for newsletter email (announcements, industry news). Written back by the campaigns service, which provisions the group by name; ASM ids are per SendGrid ACCOUNT, so this is config and never code. Unset makes @omega.js/backend fail the send loudly.",
1085
+ },
1086
+ {
1087
+ path: 'marketing.campaigns.providers.sendgrid.groups.internal',
1088
+ type: 'integer',
1089
+ required: false,
1090
+ description: "SendGrid unsubscribe (ASM) group id for internal alert email (dispute alerts, system notifications to the brand contact). Written back by the campaigns service, which provisions the group by name; ASM ids are per SendGrid ACCOUNT, so this is config and never code. Unset makes @omega.js/backend fail the send loudly.",
1091
+ },
1092
+ {
1093
+ path: 'marketing.newsletter.enabled',
1094
+ type: 'boolean',
1095
+ required: false,
1096
+ default: true,
1097
+ description: 'Role-level switch for the newsletter (default true). false skips the newsletter service and the backend subscriber sync.',
1098
+ },
1099
+ {
1100
+ path: 'marketing.newsletter.providers.beehiiv.publicationId',
1101
+ type: 'string',
1102
+ required: false,
1103
+ description: "Beehiiv publication id (e.g. 'pub_xxxxx') the brand's subscribers land in, and the id every inbound Beehiiv webhook event is matched against. Written back by the newsletter service.",
1104
+ },
1105
+ {
1106
+ path: 'marketing.newsletter.content',
1107
+ type: 'object|array',
1108
+ required: false,
1109
+ description: 'Newsletter-PIPELINE config (sources, categories, tone, template, theme, sponsorships) — a single object or an array whose first entry the generator uses. Role-level on purpose: it configures @omega.js/backend\'s generator, not Beehiiv.',
1110
+ },
1111
+ {
1112
+ path: 'marketing.prune.enabled',
1113
+ type: 'boolean',
1114
+ required: false,
1115
+ default: true,
1116
+ description: 'Monthly cold-contact prune across both providers. ON by default (Ian 2026-08-22) and materialized into every brand config — false is the per-brand off switch.',
1117
+ },
1118
+
1119
+ // ── blog ─────────────────────────────────────────────────────────────────
1120
+ {
1121
+ path: 'blog',
1122
+ type: 'object',
1123
+ required: false,
1124
+ description: 'AI blog-content settings (Ghostii pipeline).',
1125
+ },
1126
+
1127
+ // ── dataRequest ──────────────────────────────────────────────────────────
1128
+ {
1129
+ path: 'dataRequest',
1130
+ type: 'object',
1131
+ required: false,
1132
+ description: 'GDPR/CCPA data-request query definitions.',
1133
+ },
1134
+
1135
+ // ── directory ────────────────────────────────────────────────────────────
1136
+ // The same brand-level family (#277): the manager's directory service reads
1137
+ // these unfolded. PUBLIC FACTS ONLY — the entry is pushed into the parent
1138
+ // project's world-readable `brands` collection, so the validator's
1139
+ // secret-shape guard is the hard floor here (#246).
1140
+ {
1141
+ path: 'directory',
1142
+ type: 'object',
1143
+ required: false,
1144
+ description: "Directory participation (@omega.js/manager directory service): { enabled } — opt in to push this brand's entry into the parent project's `brands` collection. Absent or false never pushes. Needs `parent` to name the relationship and DIRECTORY_SERVICE_ACCOUNT in the brand .env.",
1145
+ },
1146
+ {
1147
+ path: 'directory.enabled',
1148
+ type: 'boolean',
1149
+ required: false,
1150
+ default: false,
1151
+ description: 'true opts the brand into the directory push. Default false — participation is never implicit.',
1152
+ },
1153
+
1154
+ // ── sponsorships ─────────────────────────────────────────────────────────
1155
+ {
1156
+ path: 'sponsorships',
1157
+ type: 'object',
1158
+ required: false,
1159
+ description: "The brand's sponsorship terms — the first directory BLOCK (#246), and the section the server service also publishes. Public terms only: { acceptable, unacceptable, prices }.",
1160
+ },
1161
+ {
1162
+ path: 'sponsorships.acceptable',
1163
+ type: 'array',
1164
+ required: false,
1165
+ description: 'Topics the brand accepts sponsored content about (strings).',
1166
+ },
1167
+ {
1168
+ path: 'sponsorships.unacceptable',
1169
+ type: 'array',
1170
+ required: false,
1171
+ description: 'Topics the brand refuses sponsored content about (strings).',
1172
+ },
1173
+ {
1174
+ path: 'sponsorships.prices',
1175
+ type: 'object',
1176
+ required: false,
1177
+ description: "Price in USD per placement type — placement key to number ({ 'guest-post': 70, 'link-insertion': 50 }). A placement with no price is not for sale.",
1178
+ },
1179
+
1180
+ // ── ports (dev-only) ─────────────────────────────────────────────────────
1181
+ {
1182
+ path: 'ports',
1183
+ type: 'object',
1184
+ required: false,
1185
+ description: 'Explicit dev-port pins (N7). Unset ports auto-allocate (classic defaults, bump-if-taken); a pinned port never bumps — busy pin is a hard error. Keys: auth, functions, firestore, database, hosting, storage, pubsub, ui, website, livereload, cdp. Dev/emulator only — production never reads this.',
1186
+ },
1187
+
1188
+ // ── theme ────────────────────────────────────────────────────────────────
1189
+ {
1190
+ path: 'theme.id',
1191
+ type: 'string',
1192
+ required: false,
1193
+ description: "Theme id (seeded 'classy' at onboarding). Project-owned — the manager never overwrites it.",
1194
+ },
1195
+ {
1196
+ path: 'theme.appearance',
1197
+ type: 'string',
1198
+ required: false,
1199
+ enum: ['system', 'light', 'dark'],
1200
+ description: "Default appearance. 'system' follows the OS; a user's runtime choice persists in storage and wins.",
1201
+ },
1202
+
1203
+ // ── ai ───────────────────────────────────────────────────────────────────
1204
+ {
1205
+ path: 'ai.enabled',
1206
+ type: 'boolean',
1207
+ required: false,
1208
+ description: "The manager's AI-provider setup gate ([#639](https://github.com/Omega-JS-Stack/omega/issues/639)): the `ai` service asks once for OPENAI_API_KEY / ANTHROPIC_API_KEY. `false` is the permanent opt-out the gate's Disable lands; absence means ask. No default is materialized — a brand that never answers is never re-shaped.",
1209
+ },
1210
+
1211
+ // ── translation ──────────────────────────────────────────────────────────
1212
+ {
1213
+ path: 'translation.enabled',
1214
+ type: 'boolean',
1215
+ required: false,
1216
+ default: true,
1217
+ description: 'Master switch (default true). Translation only runs when languages is non-empty.',
1218
+ },
1219
+ {
1220
+ path: 'translation.default',
1221
+ type: 'string',
1222
+ required: false,
1223
+ default: 'en',
1224
+ description: "Source language code (default 'en'). Drives <html lang>, og:locale, and the default hreflang.",
1225
+ },
1226
+ {
1227
+ path: 'translation.languages',
1228
+ type: 'array',
1229
+ required: false,
1230
+ description: "Target language codes (e.g. ['es', 'fr']). Empty/absent = translation off. Validated against @omega.js/devkit/translate's language SSOT.",
1231
+ },
1232
+ {
1233
+ path: 'translation.providers',
1234
+ type: 'object',
1235
+ required: false,
1236
+ description: "AI translation provider, keyed by name — presence picks the engine ({ claude: {} } or { chatgpt: {} }). 'claude' (the default when the block is absent) rides the local Claude Code install — no API key; 'chatgpt' uses the OpenAI API via OPENAI_API_KEY in env.",
1237
+ },
1238
+ {
1239
+ path: 'translation.model',
1240
+ type: 'string',
1241
+ required: false,
1242
+ description: "Model override for the chosen provider (defaults: claude → 'sonnet' alias, chatgpt → 'gpt-5.4-nano').",
1243
+ },
1244
+ {
1245
+ path: 'translation.exclude',
1246
+ type: 'array',
1247
+ required: false,
1248
+ description: 'Web only: extra page paths/folders to skip (system pages like checkout/legal/auth are always skipped).',
1249
+ },
1250
+
1251
+ // ── client (the @omega.js/client runtime blob) ───────────────────────────
1252
+ // The blob is a settings bag the client normalizes against its own defaults,
1253
+ // so it is deliberately NOT enumerated key by key here. `consent` is the
1254
+ // exception (#383): it decides whether a visitor is tracked at all, which is
1255
+ // a legal surface, not a preference — a typo that silently disabled the
1256
+ // banner would ship a site with no consent gate and no error.
1257
+ //
1258
+ // The three below are the second exception (#650 — found adopting omega in a
1259
+ // consumer): keys a BRAND authors. They work (they reach the client payload
1260
+ // and change what the site does), so every validate run told the brand that
1261
+ // turned one off it might be a typo. The rest of the blob stays the client's.
1262
+ {
1263
+ path: 'client.consent.enabled',
1264
+ type: 'boolean',
1265
+ required: false,
1266
+ default: true,
1267
+ description: 'The consent banner + the provider-script gate (default true). false ships NO banner — legal only for a site that loads no analytics/marketing provider at all.',
1268
+ },
1269
+ {
1270
+ path: 'client.consent.config.position',
1271
+ type: 'string',
1272
+ required: false,
1273
+ enum: ['bottom-left', 'bottom-right', 'bottom'],
1274
+ description: "Where the panel sits. 'bottom' is the centered full-width form.",
1275
+ },
1276
+ {
1277
+ path: 'client.consent.config.content',
1278
+ type: 'object',
1279
+ required: false,
1280
+ description: "Banner copy: message, panelIntro, accept, customize, acceptAll, acceptNone (a literal `{terms}`/`{cookies}` links the terms/cookie-policy page). Category labels are framework copy — a brand renames the buttons, not the categories.",
1281
+ },
1282
+ {
1283
+ path: 'client.auth.config.policy',
1284
+ type: 'string',
1285
+ required: false,
1286
+ enum: ['authenticated', 'unauthenticated', 'disabled'],
1287
+ description: "Who a page is FOR: 'authenticated' redirects a signed-out visitor to the signin route, 'unauthenticated' redirects a signed-in one away, 'disabled' skips the auth module entirely (a vert iframe). Absent = no policy, which is the site-wide answer — the auth/admin layouts set theirs in page frontmatter, so a brand only sets this to blanket a whole site.",
1288
+ },
1289
+ {
1290
+ path: 'client.exitPopup.enabled',
1291
+ type: 'boolean',
1292
+ required: false,
1293
+ default: true,
1294
+ description: 'The exit-intent offer popup (default true). false ships no popup at all; its copy, timeout and avatars live under client.exitPopup.config.',
1295
+ },
1296
+ {
1297
+ path: 'client.serviceWorker.enabled',
1298
+ type: 'boolean',
1299
+ required: false,
1300
+ default: true,
1301
+ description: 'Registers the site service worker (default true) — the offline/refresh lane and the push-notification registration ride it. false unregisters any worker the visitor already has.',
1302
+ },
1303
+
1304
+ // ── targets ──────────────────────────────────────────────────────────────
1305
+ {
1306
+ path: 'targets',
1307
+ type: 'object',
1308
+ required: false,
1309
+ description: "Key presence = target enabled; values = target-scoped config (any shared key inside overrides it) — an object, or an array of id'd instances (multi-instance targets). Framework keys are web/backend/desktop/extension/mobile; any other key must declare `type: 'custom'` (#603), a target the manager drives entirely through its own package.json scripts. Anything else is an error.",
1310
+ },
1311
+ ];
1312
+
1313
+ // Per-target refinements — validated against the RESOLVED config (the target
1314
+ // section's keys land at the top level).
1315
+ const TARGET_SCHEMAS = {
1316
+ // Grows with @omega.js/web's design: distribute, purgecss safelist,
1317
+ // workflows land as their features do.
1318
+ web: [
1319
+ {
1320
+ path: 'meta',
1321
+ type: 'object',
1322
+ required: false,
1323
+ description: "Site-wide defaults for page-meta values: the SAME names a page's `meta:` frontmatter carries, and the page (or its layout) wins (docs/web/frontmatter.md). `index` is the only key defined today, because everything else already falls back to the brand block.",
1324
+ },
1325
+ {
1326
+ path: 'meta.index',
1327
+ type: 'boolean',
1328
+ required: false,
1329
+ default: true,
1330
+ description: "false takes the WHOLE site out of search: every page emits noindex and sitemap.xml, llms.txt and pages.json list nothing. It is the site-wide DEFAULT of the one flag every signal reads (#564), so a page's own `meta.index: true` still exempts it. Case 2 (docs/shared/config.md): default ON, and the literal false is the only OFF.",
1331
+ },
1332
+ {
1333
+ path: 'imagemin',
1334
+ type: 'object',
1335
+ required: false,
1336
+ description: 'Responsive image matrix (build-time 320/640/1024 + webp, quality 80). `enabled: false` ships images verbatim.',
1337
+ },
1338
+ {
1339
+ path: 'dev.limitCollections',
1340
+ type: 'object',
1341
+ required: false,
1342
+ description: "Dev-only collection sampling (#190): collection name → max documents ({ posts: 50 }), plus `randomize: true` for a random sample instead of the first N. Collection names are @omega.js/web's (posts, alternatives, team, updates) plus the brand's own `collections`, and the engine hard-fails an unknown one. Development builds only — production never samples.",
1343
+ },
1344
+ {
1345
+ path: 'collections',
1346
+ type: 'object',
1347
+ required: false,
1348
+ description: "The brand's own content collections (#207): collection name → { field, size, title, description, permalink }. Documents live in `_<name>/`, and @omega.js/web generates the paginated listing page plus one page per category of `field` (the dotted frontmatter path the categories group on, e.g. 'doc.category'). A built-in collection name (posts, alternatives, team, updates) is an error.",
1349
+ },
1350
+ {
1351
+ path: 'purgecss',
1352
+ type: 'object',
1353
+ required: false,
1354
+ description: 'PurgeCSS post-pass settings for the production css bundle.',
1355
+ },
1356
+ {
1357
+ path: 'purgecss.safelist',
1358
+ type: 'object|array',
1359
+ required: false,
1360
+ description: "Selectors the content scan cannot see, merged OVER the framework's own safelist (#250). The object form takes PurgeCSS's lanes (standard, deep, greedy, keyframes); a bare array is PurgeCSS's shorthand for `standard`.",
1361
+ },
1362
+ {
1363
+ path: 'purgecss.safelist.standard',
1364
+ type: 'array',
1365
+ required: false,
1366
+ description: 'Exact class names to keep (strings; PurgeCSS also accepts a regex here).',
1367
+ },
1368
+ {
1369
+ path: 'purgecss.safelist.deep',
1370
+ type: 'array',
1371
+ required: false,
1372
+ description: 'Patterns (strings, compiled to RegExp) whose matching selectors keep their descendants too.',
1373
+ },
1374
+ {
1375
+ path: 'purgecss.safelist.greedy',
1376
+ type: 'array',
1377
+ required: false,
1378
+ description: 'Patterns (strings, compiled to RegExp) keeping every selector that contains a match — the lane for a runtime-stamped namespace.',
1379
+ },
1380
+ {
1381
+ path: 'purgecss.safelist.keyframes',
1382
+ type: 'array',
1383
+ required: false,
1384
+ description: 'Patterns (strings, compiled to RegExp) naming @keyframes to keep.',
1385
+ },
1386
+ ],
1387
+
1388
+ // Seeded from the sandbox brand's real @omega.js/backend config (backend-manager-config.json).
1389
+ backend: [
1390
+ // parent, github, reviews, marketing, blog, dataRequest moved to
1391
+ // SHARED_SCHEMA (#277); a targets.backend block still overrides them
1392
+ // through the merge chain.
1393
+ {
1394
+ path: 'projectType',
1395
+ type: 'string',
1396
+ required: false,
1397
+ enum: BACKEND_PROJECT_TYPES,
1398
+ description: "How this backend RUNS and deploys (#584). 'firebase' (default) exports Cloud Functions; 'custom' runs the same app as an Express server on `PORT` for a container host (Render & co) — no Functions deploy, no emulator lane, and the target's own `deploy` script is the publish lane. Everything else — the auth middleware, the routes, the schemas, every helper — is identical in both modes.",
1399
+ },
1400
+ {
1401
+ path: 'auth.signup.maxPerIpPerDay',
1402
+ type: 'integer',
1403
+ required: false,
1404
+ min: 1,
1405
+ description: 'Signups allowed per client IP per day (beforeUserCreated blocks the rest). Default 2. Raise it when the audience shares egress (NAT/CGNAT/VPN, offices, campuses).',
1406
+ },
1407
+ ],
1408
+
1409
+ // Seeded from EM's proven src/config/schema.js. EM's per-OS `targets` key
1410
+ // is renamed `platforms` here (avoids targets.desktop.targets).
1411
+ desktop: [
1412
+ {
1413
+ path: 'app.category',
1414
+ type: 'string',
1415
+ required: false,
1416
+ enum: ['productivity', 'developer-tools', 'utilities', 'media', 'social', 'network'],
1417
+ description: 'Generic high-level category. Maps to per-platform UTI + freedesktop strings.',
1418
+ },
1419
+ {
1420
+ path: 'platforms.win.signing.strategy',
1421
+ type: 'string',
1422
+ required: false,
1423
+ enum: ['self-hosted', 'cloud', 'local'],
1424
+ description: 'Windows code-signing path. self-hosted = EV USB token on a runner; cloud = provider CLI; local = developer signs manually.',
1425
+ },
1426
+ {
1427
+ path: 'startup.mode',
1428
+ type: 'string',
1429
+ required: false,
1430
+ enum: ['normal', 'hidden'],
1431
+ description: 'normal = main window appears at launch; hidden = bakes LSUIElement=true on macOS (no dock, no Cmd+Tab).',
1432
+ },
1433
+ {
1434
+ path: 'cdp.readySignal',
1435
+ type: 'string',
1436
+ required: false,
1437
+ description: 'Boot-complete signal for `mgr cdp relaunch` — a URL substring matched against CDP page targets.',
1438
+ },
1439
+ {
1440
+ path: 'releases.enabled',
1441
+ type: 'boolean',
1442
+ required: false,
1443
+ description: 'One switch for the release surface: false suppresses the site\'s derived download links AND desktop publishing (electron-builder publish). Site derivation defaults true only when the releases block exists; desktop publishing defaults true regardless.',
1444
+ },
1445
+ {
1446
+ path: 'releases.owner',
1447
+ type: 'string',
1448
+ required: false,
1449
+ description: 'GitHub owner of the releases repo; defaults to the brand repo owner.',
1450
+ },
1451
+ {
1452
+ path: 'releases.repo',
1453
+ type: 'string',
1454
+ required: false,
1455
+ description: 'GitHub repo where built artifacts + the auto-update feed live. Defaults to `<brand.id>-releases`.',
1456
+ },
1457
+ {
1458
+ path: 'restartManager.enabled',
1459
+ type: 'boolean',
1460
+ required: false,
1461
+ description: 'External guardian app that relaunches this app on crash. Default true; false disables entirely.',
1462
+ },
1463
+ {
1464
+ path: 'restartManager.feed.owner',
1465
+ type: 'string',
1466
+ required: false,
1467
+ match: /^[A-Za-z0-9-]+$/,
1468
+ description: 'GitHub owner of the RM release feed. Override for forks/mirrors.',
1469
+ },
1470
+ {
1471
+ path: 'restartManager.feed.repo',
1472
+ type: 'string',
1473
+ required: false,
1474
+ match: /^[\w.-]+$/,
1475
+ description: 'GitHub repo of the RM release feed.',
1476
+ },
1477
+ {
1478
+ path: 'remoteScripts.enabled',
1479
+ type: 'boolean',
1480
+ required: false,
1481
+ description: 'Emergency remote code execution in the main process — OPT-IN: only `true` arms the lane, anything else leaves it inert.',
1482
+ },
1483
+ {
1484
+ path: 'remoteScripts.url',
1485
+ type: 'string',
1486
+ required: false,
1487
+ match: /^https?:\/\//,
1488
+ description: 'Remote-script source override (default `${brand.url}/data/scripts/main.js`). https-only at runtime — a cleartext URL is refused (localhost excepted for dev).',
1489
+ },
1490
+ {
1491
+ path: 'restartManager.feed.url',
1492
+ type: 'string',
1493
+ required: false,
1494
+ match: /^https?:\/\//,
1495
+ description: 'Full base-URL override for the RM feed + artifacts (air-gapped mirrors). Wins over feed.owner/repo.',
1496
+ },
1497
+ ],
1498
+
1499
+ // Store listings ARE the extension page's declaration and the curated
1500
+ // site.targets.extension view (#85, #610 — no second copy in web config).
1501
+ extension: [
1502
+ {
1503
+ path: 'listings.chrome.url',
1504
+ type: 'string',
1505
+ required: false,
1506
+ match: /^https?:\/\//,
1507
+ description: "Chrome Web Store listing URL. Feeds site.targets.extension.listings.chrome — the /extension page's button and its /extension/chrome shortlink.",
1508
+ },
1509
+ {
1510
+ path: 'listings.chrome.state',
1511
+ type: 'string',
1512
+ required: false,
1513
+ description: 'Listing state note (e.g. live, pending review). Display-safe, informational only.',
1514
+ },
1515
+ {
1516
+ path: 'listings.firefox.url',
1517
+ type: 'string',
1518
+ required: false,
1519
+ match: /^https?:\/\//,
1520
+ description: 'Firefox Add-ons (AMO) listing URL. Feeds site.targets.extension.listings.firefox.',
1521
+ },
1522
+ {
1523
+ path: 'listings.firefox.state',
1524
+ type: 'string',
1525
+ required: false,
1526
+ description: 'Listing state note (e.g. live, pending review). Display-safe, informational only.',
1527
+ },
1528
+ {
1529
+ path: 'listings.edge.url',
1530
+ type: 'string',
1531
+ required: false,
1532
+ match: /^https?:\/\//,
1533
+ description: 'Microsoft Edge Add-ons listing URL. Feeds site.targets.extension.listings.edge.',
1534
+ },
1535
+ {
1536
+ path: 'listings.edge.state',
1537
+ type: 'string',
1538
+ required: false,
1539
+ description: 'Listing state note (e.g. live, pending review). Display-safe, informational only.',
1540
+ },
1541
+ {
1542
+ path: 'listings.opera.url',
1543
+ type: 'string',
1544
+ required: false,
1545
+ match: /^https?:\/\//,
1546
+ description: 'Opera Add-ons listing URL. Feeds site.targets.extension.listings.opera.',
1547
+ },
1548
+ {
1549
+ path: 'listings.opera.state',
1550
+ type: 'string',
1551
+ required: false,
1552
+ description: 'Listing state note (e.g. live, pending review). Display-safe, informational only.',
1553
+ },
1554
+ {
1555
+ path: 'listings.safari.url',
1556
+ type: 'string',
1557
+ required: false,
1558
+ match: /^https?:\/\//,
1559
+ description: 'App Store (Safari extension) listing URL. Feeds site.targets.extension.listings.safari.',
1560
+ },
1561
+ {
1562
+ path: 'listings.safari.state',
1563
+ type: 'string',
1564
+ required: false,
1565
+ description: 'Listing state note (e.g. live, pending review). Display-safe, informational only.',
1566
+ },
1567
+ {
1568
+ path: 'listings.brave.url',
1569
+ type: 'string',
1570
+ required: false,
1571
+ match: /^https?:\/\//,
1572
+ description: 'Brave listing URL (Chrome Web Store serves Brave). Feeds site.targets.extension.listings.brave.',
1573
+ },
1574
+ {
1575
+ path: 'listings.brave.state',
1576
+ type: 'string',
1577
+ required: false,
1578
+ description: 'Listing state note (e.g. live, pending review). Display-safe, informational only.',
1579
+ },
1580
+ ],
1581
+
1582
+ // RESERVED — MAM is parked for a separate overhaul; schema slot only.
1583
+ mobile: [],
1584
+ };
1585
+
1586
+ // Declared top-level keys that are NOT config sections: `targets` is scoping
1587
+ // machinery, and `url` is this instance's RESOLVED public url (#588): a value
1588
+ // the loader derives, carried to templates as the `site.url` build fact, never
1589
+ // a namespace a page's `config:` block overrides.
1590
+ const NON_SECTION_KEYS = ['targets', 'url'];
1591
+
1592
+ /** The top-level sections a rule array declares (machinery keys excluded). */
1593
+ const sectionsOf = (rules) => [...new Set(rules.map((rule) => rule.path.split('.')[0]))].filter((section) => !NON_SECTION_KEYS.includes(section));
1594
+
1595
+ /**
1596
+ * The config sections a target's RESOLVED config can hold (#607) — the
1597
+ * namespace a page's `config:` block overrides, in BOTH directions: a section
1598
+ * restated bare in frontmatter is a build error, and a key under `config:`
1599
+ * that is not one of these is a build error too. Per-target, because a
1600
+ * target's refinements land at the top level: `app` is a desktop section, and
1601
+ * a web layout's own `app:` block (the deep-link interstitial) is page data,
1602
+ * not config.
1603
+ * @param {string} [target] - target name; omitted = the shared sections only
1604
+ * @returns {string[]} section names, sorted
1605
+ */
1606
+ function configSections(target) {
1607
+ return [...new Set([...sectionsOf(SHARED_SCHEMA), ...sectionsOf(TARGET_SCHEMAS[target] || [])])].sort();
1608
+ }
1609
+
1610
+ module.exports = { TARGETS, CUSTOM_TARGET_TYPE, isCustomTargetEntry, BACKEND_PROJECT_TYPES, backendProjectType, SHARED_SECTIONS, SHARED_SCHEMA, TARGET_SCHEMAS, BRAND_ID_PATTERN, WINBACK_DURATIONS, configSections };