@omega.js/web 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 (1162) hide show
  1. package/LICENSE +98 -0
  2. package/README.md +640 -0
  3. package/bin/omega +2 -0
  4. package/bin/omega-web +2 -0
  5. package/bin/omg +2 -0
  6. package/core/_includes/admin/sections/sidebar.json +100 -0
  7. package/core/_includes/admin/sections/topbar.json +11 -0
  8. package/core/_includes/backend/sections/sidebar.json +39 -0
  9. package/core/_includes/backend/sections/topbar.json +16 -0
  10. package/core/_includes/core/body.html +102 -0
  11. package/core/_includes/core/foot.html +536 -0
  12. package/core/_includes/core/head.html +423 -0
  13. package/core/_includes/core/pricing/price-per-unit.html +33 -0
  14. package/core/_includes/core/showcase/entry-body.html +94 -0
  15. package/core/_includes/core/showcase/index-body.html +43 -0
  16. package/core/_includes/core/showcase/nav.html +60 -0
  17. package/core/_includes/frontend/sections/footer.json +74 -0
  18. package/core/_includes/frontend/sections/nav.json +70 -0
  19. package/core/_includes/global/sections/account.json +31 -0
  20. package/core/_includes/modules/engagement/giscus.html +26 -0
  21. package/core/_layouts/blueprint/404.html +10 -0
  22. package/core/_layouts/blueprint/about.html +10 -0
  23. package/core/_layouts/blueprint/account/index.html +11 -0
  24. package/core/_layouts/blueprint/admin/calendar/index.html +590 -0
  25. package/core/_layouts/blueprint/admin/dashboard/index.html +301 -0
  26. package/core/_layouts/blueprint/admin/firebase/index.html +214 -0
  27. package/core/_layouts/blueprint/admin/posts/editor.html +157 -0
  28. package/core/_layouts/blueprint/admin/posts/index.html +126 -0
  29. package/core/_layouts/blueprint/admin/stackblitz/index.html +30 -0
  30. package/core/_layouts/blueprint/admin/studio/index.html +85 -0
  31. package/core/_layouts/blueprint/admin/users/index.html +272 -0
  32. package/core/_layouts/blueprint/admin/users/new.html +82 -0
  33. package/core/_layouts/blueprint/admin/verts/index.html +204 -0
  34. package/core/_layouts/blueprint/alternatives/alternative.html +15 -0
  35. package/core/_layouts/blueprint/alternatives/index.html +10 -0
  36. package/core/_layouts/blueprint/app.html +10 -0
  37. package/core/_layouts/blueprint/auth/reset.html +19 -0
  38. package/core/_layouts/blueprint/auth/signin.html +19 -0
  39. package/core/_layouts/blueprint/auth/signup.html +19 -0
  40. package/core/_layouts/blueprint/auth/token.html +19 -0
  41. package/core/_layouts/blueprint/blog/categories/category.html +8 -0
  42. package/core/_layouts/blueprint/blog/categories/index.html +11 -0
  43. package/core/_layouts/blueprint/blog/index.html +10 -0
  44. package/core/_layouts/blueprint/blog/post.html +5 -0
  45. package/core/_layouts/blueprint/blog/tags/index.html +11 -0
  46. package/core/_layouts/blueprint/blog/tags/tag.html +8 -0
  47. package/core/_layouts/blueprint/careers.md +13 -0
  48. package/core/_layouts/blueprint/collection/category.html +9 -0
  49. package/core/_layouts/blueprint/collection/document.html +9 -0
  50. package/core/_layouts/blueprint/collection/index.html +9 -0
  51. package/core/_layouts/blueprint/connections/callback.html +11 -0
  52. package/core/_layouts/blueprint/contact.html +14 -0
  53. package/core/_layouts/blueprint/dashboard/index.html +10 -0
  54. package/core/_layouts/blueprint/download.html +20 -0
  55. package/core/_layouts/blueprint/extension/index.html +14 -0
  56. package/core/_layouts/blueprint/extension/installed.html +11 -0
  57. package/core/_layouts/blueprint/feedback.html +10 -0
  58. package/core/_layouts/blueprint/index.html +16 -0
  59. package/core/_layouts/blueprint/legal/cookies.md +57 -0
  60. package/core/_layouts/blueprint/legal/privacy.md +109 -0
  61. package/core/_layouts/blueprint/legal/terms.md +155 -0
  62. package/core/_layouts/blueprint/payment/checkout.html +15 -0
  63. package/core/_layouts/blueprint/payment/confirmation.html +14 -0
  64. package/core/_layouts/blueprint/portal/email-preferences.html +15 -0
  65. package/core/_layouts/blueprint/pricing.html +20 -0
  66. package/core/_layouts/blueprint/status.html +10 -0
  67. package/core/_layouts/blueprint/team/index.html +10 -0
  68. package/core/_layouts/blueprint/team/member.html +5 -0
  69. package/core/_layouts/blueprint/updates/index.html +10 -0
  70. package/core/_layouts/blueprint/updates/update.html +10 -0
  71. package/core/_layouts/core/root.html +45 -0
  72. package/core/_layouts/modules/utilities/redirect.html +129 -0
  73. package/core/css/_studio.scss +237 -0
  74. package/core/css/components/_index.scss +146 -0
  75. package/core/css/core/_.scss +0 -0
  76. package/core/css/core/_alert.scss +87 -0
  77. package/core/css/core/_animations.scss +343 -0
  78. package/core/css/core/_bindings.scss +116 -0
  79. package/core/css/core/_blog-search.scss +57 -0
  80. package/core/css/core/_chatsy.scss +61 -0
  81. package/core/css/core/_consent.scss +424 -0
  82. package/core/css/core/_custom-font-awesome.scss +146 -0
  83. package/core/css/core/_exit-popup.scss +42 -0
  84. package/core/css/core/_generated-badge.scss +33 -0
  85. package/core/css/core/_initialize.scss +29 -0
  86. package/core/css/core/_lazy-loading.scss +104 -0
  87. package/core/css/core/_social-sharing.scss +41 -0
  88. package/core/css/core/_tones.scss +32 -0
  89. package/core/css/core/_utilities.scss +157 -0
  90. package/core/css/main.scss +54 -0
  91. package/core/css/motion/_index.scss +368 -0
  92. package/core/css/pages/404/index.scss +0 -0
  93. package/core/css/pages/admin/calendar/index.scss +692 -0
  94. package/core/css/pages/admin/firebase/index.scss +33 -0
  95. package/core/css/pages/admin/index.scss +9 -0
  96. package/core/css/pages/admin/users/index.scss +9 -0
  97. package/core/css/pages/admin/verts/index.scss +9 -0
  98. package/core/css/pages/alternatives/[alternative]/index.scss +0 -0
  99. package/core/css/pages/blog/[slug].scss +55 -0
  100. package/core/css/pages/dashboard/account/index.scss +242 -0
  101. package/core/css/pages/download/index.scss +480 -0
  102. package/core/css/pages/extension/installed/index.scss +317 -0
  103. package/core/css/pages/feedback/index.scss +31 -0
  104. package/core/css/pages/payment/confirmation/index.scss +14 -0
  105. package/core/css/pages/pricing/index.scss +0 -0
  106. package/core/css/pages/status/index.scss +162 -0
  107. package/core/css/pages/test/libraries/layers/index.scss +28 -0
  108. package/core/css/pages/updates/[update].scss +46 -0
  109. package/core/css/pages/updates/index.scss +13 -0
  110. package/core/css/shell/_index.scss +207 -0
  111. package/core/css/tokens/_index.scss +166 -0
  112. package/core/icons/flags/ar.svg +1 -0
  113. package/core/icons/flags/bd.svg +1 -0
  114. package/core/icons/flags/ca.svg +1 -0
  115. package/core/icons/flags/ch.svg +1 -0
  116. package/core/icons/flags/cn.svg +1 -0
  117. package/core/icons/flags/co.svg +1 -0
  118. package/core/icons/flags/cw.svg +1 -0
  119. package/core/icons/flags/cz.svg +1 -0
  120. package/core/icons/flags/de.svg +1 -0
  121. package/core/icons/flags/dk.svg +1 -0
  122. package/core/icons/flags/eg.svg +1 -0
  123. package/core/icons/flags/es.svg +1 -0
  124. package/core/icons/flags/fi.svg +1 -0
  125. package/core/icons/flags/fr.svg +1 -0
  126. package/core/icons/flags/gb.svg +1 -0
  127. package/core/icons/flags/gq.svg +1 -0
  128. package/core/icons/flags/gr.svg +1 -0
  129. package/core/icons/flags/gt.svg +1 -0
  130. package/core/icons/flags/hn.svg +1 -0
  131. package/core/icons/flags/id.svg +1 -0
  132. package/core/icons/flags/ie.svg +1 -0
  133. package/core/icons/flags/in.svg +1 -0
  134. package/core/icons/flags/ir.svg +1 -0
  135. package/core/icons/flags/it.svg +1 -0
  136. package/core/icons/flags/jp.svg +1 -0
  137. package/core/icons/flags/kp.svg +1 -0
  138. package/core/icons/flags/kr.svg +1 -0
  139. package/core/icons/flags/ly.svg +1 -0
  140. package/core/icons/flags/ma.svg +1 -0
  141. package/core/icons/flags/mu.svg +1 -0
  142. package/core/icons/flags/mx.svg +1 -0
  143. package/core/icons/flags/ng.svg +1 -0
  144. package/core/icons/flags/pa.svg +1 -0
  145. package/core/icons/flags/ph.svg +1 -0
  146. package/core/icons/flags/pk.svg +1 -0
  147. package/core/icons/flags/pt.svg +1 -0
  148. package/core/icons/flags/ru.svg +1 -0
  149. package/core/icons/flags/sa.svg +1 -0
  150. package/core/icons/flags/sd.svg +1 -0
  151. package/core/icons/flags/se.svg +1 -0
  152. package/core/icons/flags/th.svg +1 -0
  153. package/core/icons/flags/tm.svg +1 -0
  154. package/core/icons/flags/tr.svg +1 -0
  155. package/core/icons/flags/us.svg +1 -0
  156. package/core/icons/flags/uy.svg +1 -0
  157. package/core/icons/flags/vn.svg +1 -0
  158. package/core/icons/flags/za.svg +1 -0
  159. package/core/images/exit-popup/subscriber-1.jpg +0 -0
  160. package/core/images/exit-popup/subscriber-2.jpg +0 -0
  161. package/core/images/exit-popup/subscriber-3.jpg +0 -0
  162. package/core/images/exit-popup/subscriber-4.jpg +0 -0
  163. package/core/images/placeholder/photo-1.jpg +0 -0
  164. package/core/images/placeholder/photo-2.jpg +0 -0
  165. package/core/images/placeholder/photo-3.jpg +0 -0
  166. package/core/js/core/analytics-loader.js +253 -0
  167. package/core/js/core/app-shell.js +136 -0
  168. package/core/js/core/appearance.js +195 -0
  169. package/core/js/core/auth.js +457 -0
  170. package/core/js/core/complete.js +72 -0
  171. package/core/js/core/consent.js +384 -0
  172. package/core/js/core/dev-icon-audit.js +63 -0
  173. package/core/js/core/dev-palette.js +670 -0
  174. package/core/js/core/dev-sections.js +53 -0
  175. package/core/js/core/exit-popup.js +276 -0
  176. package/core/js/core/initialize.js +8 -0
  177. package/core/js/core/language-switcher.js +167 -0
  178. package/core/js/core/lazy-loading.js +479 -0
  179. package/core/js/core/motion.js +28 -0
  180. package/core/js/core/query-strings.js +146 -0
  181. package/core/js/core/service-worker.js +28 -0
  182. package/core/js/core/social-sharing.js +293 -0
  183. package/core/js/first-paint.js +35 -0
  184. package/core/js/layouts/modules/utilities/redirect.js +122 -0
  185. package/core/js/libs/admin-helpers.js +62 -0
  186. package/core/js/libs/alert-dismiss.js +35 -0
  187. package/core/js/libs/analytics.js +368 -0
  188. package/core/js/libs/auth/email.js +144 -0
  189. package/core/js/libs/auth/errors.js +119 -0
  190. package/core/js/libs/auth/forms.js +206 -0
  191. package/core/js/libs/auth/index.js +181 -0
  192. package/core/js/libs/auth/oauth.js +394 -0
  193. package/core/js/libs/auth/orphan.js +110 -0
  194. package/core/js/libs/auth/password-toggle.js +44 -0
  195. package/core/js/libs/auth/session-params.js +266 -0
  196. package/core/js/libs/auth/tracking.js +41 -0
  197. package/core/js/libs/charts.js +516 -0
  198. package/core/js/libs/consent-region.js +127 -0
  199. package/core/js/libs/dev.js +150 -0
  200. package/core/js/libs/graph.js +201 -0
  201. package/core/js/libs/initialize-tooltips.js +31 -0
  202. package/core/js/libs/logger.js +38 -0
  203. package/core/js/libs/omega-copy.js +89 -0
  204. package/core/js/libs/path-prefix.js +44 -0
  205. package/core/js/libs/payment-config.js +76 -0
  206. package/core/js/libs/recaptcha.js +94 -0
  207. package/core/js/libs/sale-name.js +295 -0
  208. package/core/js/libs/showcase-frames.js +37 -0
  209. package/core/js/libs/srcset.js +69 -0
  210. package/core/js/libs/tracking-consent.js +161 -0
  211. package/core/js/main.js +144 -0
  212. package/core/js/pages/404/index.js +57 -0
  213. package/core/js/pages/admin/calendar/calendar-core.js +701 -0
  214. package/core/js/pages/admin/calendar/calendar-events.js +853 -0
  215. package/core/js/pages/admin/calendar/calendar-renderer.js +807 -0
  216. package/core/js/pages/admin/calendar/campaign-preview.js +105 -0
  217. package/core/js/pages/admin/calendar/index.js +40 -0
  218. package/core/js/pages/admin/firebase/index.js +727 -0
  219. package/core/js/pages/admin/index.js +531 -0
  220. package/core/js/pages/admin/posts/editor/index.js +230 -0
  221. package/core/js/pages/admin/posts/index.js +237 -0
  222. package/core/js/pages/admin/studio/index.js +538 -0
  223. package/core/js/pages/admin/users/index.js +669 -0
  224. package/core/js/pages/admin/users/new/index.js +65 -0
  225. package/core/js/pages/admin/verts/index.js +407 -0
  226. package/core/js/pages/alternatives/[alternative]/index.js +17 -0
  227. package/core/js/pages/app/index.js +135 -0
  228. package/core/js/pages/blog/[slug].js +132 -0
  229. package/core/js/pages/blog/_search.mjs +108 -0
  230. package/core/js/pages/blog/index.js +193 -0
  231. package/core/js/pages/blog/page/[num]/index.js +7 -0
  232. package/core/js/pages/connections/callback/index.js +148 -0
  233. package/core/js/pages/contact/index.js +170 -0
  234. package/core/js/pages/cookies.js +2 -0
  235. package/core/js/pages/dashboard/account/index.js +366 -0
  236. package/core/js/pages/dashboard/account/modules/orders.js +82 -0
  237. package/core/js/pages/dashboard/account/sections/api-keys.js +149 -0
  238. package/core/js/pages/dashboard/account/sections/billing.js +1667 -0
  239. package/core/js/pages/dashboard/account/sections/connections.js +333 -0
  240. package/core/js/pages/dashboard/account/sections/data-request.js +225 -0
  241. package/core/js/pages/dashboard/account/sections/delete.js +80 -0
  242. package/core/js/pages/dashboard/account/sections/notifications.js +167 -0
  243. package/core/js/pages/dashboard/account/sections/orders.js +151 -0
  244. package/core/js/pages/dashboard/account/sections/profile.js +200 -0
  245. package/core/js/pages/dashboard/account/sections/referrals.js +217 -0
  246. package/core/js/pages/dashboard/account/sections/refund.js +253 -0
  247. package/core/js/pages/dashboard/account/sections/security.js +726 -0
  248. package/core/js/pages/dashboard/account/sections/team.js +247 -0
  249. package/core/js/pages/download/index.js +418 -0
  250. package/core/js/pages/extension/index.js +164 -0
  251. package/core/js/pages/extension/installed/index.js +135 -0
  252. package/core/js/pages/feedback/index.js +193 -0
  253. package/core/js/pages/legal/_document.js +91 -0
  254. package/core/js/pages/payment/checkout/index.js +420 -0
  255. package/core/js/pages/payment/checkout/modules/api.js +139 -0
  256. package/core/js/pages/payment/checkout/modules/dev-section.js +175 -0
  257. package/core/js/pages/payment/checkout/modules/discount.js +69 -0
  258. package/core/js/pages/payment/checkout/modules/pricing.js +73 -0
  259. package/core/js/pages/payment/checkout/modules/state.js +339 -0
  260. package/core/js/pages/payment/checkout/modules/tracking.js +54 -0
  261. package/core/js/pages/payment/confirmation/index.js +91 -0
  262. package/core/js/pages/payment/confirmation/modules/celebration.js +45 -0
  263. package/core/js/pages/payment/confirmation/modules/state.js +112 -0
  264. package/core/js/pages/payment/confirmation/modules/tracking.js +133 -0
  265. package/core/js/pages/payment/confirmation/modules/verify.js +142 -0
  266. package/core/js/pages/portal/email-preferences/index.js +183 -0
  267. package/core/js/pages/pricing/index.js +425 -0
  268. package/core/js/pages/privacy.js +2 -0
  269. package/core/js/pages/reset/index.js +12 -0
  270. package/core/js/pages/signin/index.js +22 -0
  271. package/core/js/pages/signup/index.js +23 -0
  272. package/core/js/pages/status/index.js +687 -0
  273. package/core/js/pages/terms.js +2 -0
  274. package/core/js/pages/test/components/[category]/[name]/index.js +26 -0
  275. package/core/js/pages/test/libraries/appearance/index.js +220 -0
  276. package/core/js/pages/test/libraries/form-manager/index.js +328 -0
  277. package/core/js/pages/test/libraries/layers/index.js +16 -0
  278. package/core/js/pages/test/libraries/verts/index.js +68 -0
  279. package/core/js/pages/test/sections/[category]/[name]/index.js +28 -0
  280. package/core/js/pages/test/styleguide/index.js +35 -0
  281. package/core/js/pages/token/index.js +203 -0
  282. package/core/logos/brandmarks/original/apple-app-store.svg +1 -0
  283. package/core/logos/brandmarks/original/apple.svg +6 -0
  284. package/core/logos/brandmarks/original/bitbucket.svg +14 -0
  285. package/core/logos/brandmarks/original/bitcoin.svg +1 -0
  286. package/core/logos/brandmarks/original/bluesky.svg +7 -0
  287. package/core/logos/brandmarks/original/bootstrap.svg +43 -0
  288. package/core/logos/brandmarks/original/brave.svg +1 -0
  289. package/core/logos/brandmarks/original/chrome.svg +1 -0
  290. package/core/logos/brandmarks/original/discord.svg +1 -0
  291. package/core/logos/brandmarks/original/dropbox.svg +10 -0
  292. package/core/logos/brandmarks/original/edge.svg +1 -0
  293. package/core/logos/brandmarks/original/facebook-f.svg +1 -0
  294. package/core/logos/brandmarks/original/facebook.svg +8 -0
  295. package/core/logos/brandmarks/original/figma.svg +10 -0
  296. package/core/logos/brandmarks/original/firefox.svg +1 -0
  297. package/core/logos/brandmarks/original/github.svg +6 -0
  298. package/core/logos/brandmarks/original/gitlab.svg +17 -0
  299. package/core/logos/brandmarks/original/google-a-s.svg +1 -0
  300. package/core/logos/brandmarks/original/google-chrome-webstore.svg +49 -0
  301. package/core/logos/brandmarks/original/google-play-store.svg +1 -0
  302. package/core/logos/brandmarks/original/google-web-search.svg +1 -0
  303. package/core/logos/brandmarks/original/google.svg +1 -0
  304. package/core/logos/brandmarks/original/gulp.svg +4 -0
  305. package/core/logos/brandmarks/original/html-5.svg +1 -0
  306. package/core/logos/brandmarks/original/instagram.svg +1 -0
  307. package/core/logos/brandmarks/original/javascript.svg +1 -0
  308. package/core/logos/brandmarks/original/jira.svg +18 -0
  309. package/core/logos/brandmarks/original/kick.svg +7 -0
  310. package/core/logos/brandmarks/original/linkedin.svg +1 -0
  311. package/core/logos/brandmarks/original/linux.svg +28 -0
  312. package/core/logos/brandmarks/original/messenger.svg +16 -0
  313. package/core/logos/brandmarks/original/microsoft.svg +1 -0
  314. package/core/logos/brandmarks/original/npm.svg +8 -0
  315. package/core/logos/brandmarks/original/opera.svg +1 -0
  316. package/core/logos/brandmarks/original/patreon.svg +6 -0
  317. package/core/logos/brandmarks/original/pinterest.svg +6 -0
  318. package/core/logos/brandmarks/original/reddit.svg +1 -0
  319. package/core/logos/brandmarks/original/safari.svg +31 -0
  320. package/core/logos/brandmarks/original/sass.svg +3 -0
  321. package/core/logos/brandmarks/original/signal.svg +6 -0
  322. package/core/logos/brandmarks/original/skype.svg +47 -0
  323. package/core/logos/brandmarks/original/slack.svg +33 -0
  324. package/core/logos/brandmarks/original/soundcloud.svg +1 -0
  325. package/core/logos/brandmarks/original/spotify.svg +5 -0
  326. package/core/logos/brandmarks/original/steam.svg +6 -0
  327. package/core/logos/brandmarks/original/telegram.svg +14 -0
  328. package/core/logos/brandmarks/original/tiktok.svg +1 -0
  329. package/core/logos/brandmarks/original/trello.svg +16 -0
  330. package/core/logos/brandmarks/original/tumblr.svg +1 -0
  331. package/core/logos/brandmarks/original/twitch.svg +6 -0
  332. package/core/logos/brandmarks/original/twitter.svg +6 -0
  333. package/core/logos/brandmarks/original/unity.svg +6 -0
  334. package/core/logos/brandmarks/original/x.svg +7 -0
  335. package/core/logos/brandmarks/original/yahoo-y.svg +1 -0
  336. package/core/logos/brandmarks/original/youtube-square.svg +1 -0
  337. package/core/logos/brandmarks/original/youtube.svg +1 -0
  338. package/core/logos/combomarks/dark/airbnb.svg +30 -0
  339. package/core/logos/combomarks/dark/slack.svg +38 -0
  340. package/core/logos/combomarks/dark/spotify.svg +35 -0
  341. package/core/logos/combomarks/dark/twilio.svg +26 -0
  342. package/core/logos/combomarks/dark/weebly.svg +35 -0
  343. package/core/logos/combomarks/gray/airbnb.svg +33 -0
  344. package/core/logos/combomarks/gray/alphabet.svg +23 -0
  345. package/core/logos/combomarks/gray/amazon.svg +38 -0
  346. package/core/logos/combomarks/gray/boring-company.svg +91 -0
  347. package/core/logos/combomarks/gray/burberry.svg +239 -0
  348. package/core/logos/combomarks/gray/embark.svg +21 -0
  349. package/core/logos/combomarks/gray/fitbit.svg +61 -0
  350. package/core/logos/combomarks/gray/fred-perry.svg +94 -0
  351. package/core/logos/combomarks/gray/google.svg +27 -0
  352. package/core/logos/combomarks/gray/hollister.svg +34 -0
  353. package/core/logos/combomarks/gray/hubspot.svg +27 -0
  354. package/core/logos/combomarks/gray/kaplan.svg +33 -0
  355. package/core/logos/combomarks/gray/lenovo.svg +27 -0
  356. package/core/logos/combomarks/gray/levis.svg +25 -0
  357. package/core/logos/combomarks/gray/mapbox.svg +37 -0
  358. package/core/logos/combomarks/gray/netflix.svg +15 -0
  359. package/core/logos/combomarks/gray/new-balance.svg +32 -0
  360. package/core/logos/combomarks/gray/nike.svg +17 -0
  361. package/core/logos/combomarks/gray/paypal.svg +41 -0
  362. package/core/logos/combomarks/gray/puma.svg +82 -0
  363. package/core/logos/combomarks/gray/samsung.svg +26 -0
  364. package/core/logos/combomarks/gray/shopify.svg +36 -0
  365. package/core/logos/combomarks/gray/slack.svg +42 -0
  366. package/core/logos/combomarks/gray/spotify.svg +38 -0
  367. package/core/logos/combomarks/gray/stripe.svg +24 -0
  368. package/core/logos/combomarks/gray/tnf.svg +30 -0
  369. package/core/logos/combomarks/gray/twilio.svg +29 -0
  370. package/core/logos/combomarks/gray/uber.svg +24 -0
  371. package/core/logos/combomarks/gray/weebly.svg +38 -0
  372. package/core/logos/combomarks/original/airbnb.svg +33 -0
  373. package/core/logos/combomarks/original/alphabet.svg +23 -0
  374. package/core/logos/combomarks/original/amazon.svg +38 -0
  375. package/core/logos/combomarks/original/asana.svg +8 -0
  376. package/core/logos/combomarks/original/boring-company.svg +91 -0
  377. package/core/logos/combomarks/original/discord.svg +6 -0
  378. package/core/logos/combomarks/original/embark.svg +24 -0
  379. package/core/logos/combomarks/original/fitbit.svg +60 -0
  380. package/core/logos/combomarks/original/github.svg +6 -0
  381. package/core/logos/combomarks/original/gitlab.svg +20 -0
  382. package/core/logos/combomarks/original/google.svg +28 -0
  383. package/core/logos/combomarks/original/hubspot.svg +29 -0
  384. package/core/logos/combomarks/original/instagram.svg +7 -0
  385. package/core/logos/combomarks/original/kickstarter.svg +6 -0
  386. package/core/logos/combomarks/original/lenovo.svg +24 -0
  387. package/core/logos/combomarks/original/linkedin.svg +6 -0
  388. package/core/logos/combomarks/original/mapbox.svg +39 -0
  389. package/core/logos/combomarks/original/mastodon.svg +7 -0
  390. package/core/logos/combomarks/original/medium.svg +6 -0
  391. package/core/logos/combomarks/original/meta.svg +22 -0
  392. package/core/logos/combomarks/original/microsoft.svg +13 -0
  393. package/core/logos/combomarks/original/monday.svg +12 -0
  394. package/core/logos/combomarks/original/netflix.svg +15 -0
  395. package/core/logos/combomarks/original/notion.svg +9 -0
  396. package/core/logos/combomarks/original/paypal.svg +41 -0
  397. package/core/logos/combomarks/original/reddit.svg +13 -0
  398. package/core/logos/combomarks/original/samsung.svg +25 -0
  399. package/core/logos/combomarks/original/shopify.svg +35 -0
  400. package/core/logos/combomarks/original/slack.svg +47 -0
  401. package/core/logos/combomarks/original/spotify.svg +37 -0
  402. package/core/logos/combomarks/original/stackoverflow.svg +28 -0
  403. package/core/logos/combomarks/original/stripe.svg +24 -0
  404. package/core/logos/combomarks/original/tiktok.svg +12 -0
  405. package/core/logos/combomarks/original/tumblr.svg +6 -0
  406. package/core/logos/combomarks/original/uber.svg +24 -0
  407. package/core/logos/combomarks/original/weebly.svg +34 -0
  408. package/core/logos/combomarks/original/whatsapp.svg +20 -0
  409. package/core/logos/combomarks/original/youtube.svg +10 -0
  410. package/core/logos/combomarks/original/zoom.svg +7 -0
  411. package/core/logos/combomarks/white/airbnb.svg +33 -0
  412. package/core/logos/combomarks/white/alphabet.svg +23 -0
  413. package/core/logos/combomarks/white/amazon.svg +38 -0
  414. package/core/logos/combomarks/white/boring-company.svg +91 -0
  415. package/core/logos/combomarks/white/embark.svg +21 -0
  416. package/core/logos/combomarks/white/fitbit.svg +61 -0
  417. package/core/logos/combomarks/white/google.svg +27 -0
  418. package/core/logos/combomarks/white/hubspot.svg +27 -0
  419. package/core/logos/combomarks/white/kaplan.svg +33 -0
  420. package/core/logos/combomarks/white/lenovo.svg +27 -0
  421. package/core/logos/combomarks/white/mapbox.svg +37 -0
  422. package/core/logos/combomarks/white/netflix.svg +15 -0
  423. package/core/logos/combomarks/white/paypal.svg +40 -0
  424. package/core/logos/combomarks/white/samsung.svg +26 -0
  425. package/core/logos/combomarks/white/shopify.svg +36 -0
  426. package/core/logos/combomarks/white/slack.svg +42 -0
  427. package/core/logos/combomarks/white/spotify.svg +38 -0
  428. package/core/logos/combomarks/white/stripe.svg +24 -0
  429. package/core/logos/combomarks/white/twilio.svg +29 -0
  430. package/core/logos/combomarks/white/uber.svg +24 -0
  431. package/core/logos/combomarks/white/weebly.svg +38 -0
  432. package/defaults/pages/404.md +4 -0
  433. package/defaults/pages/about.md +4 -0
  434. package/defaults/pages/admin/calendar/index.html +4 -0
  435. package/defaults/pages/admin/firebase/index.html +4 -0
  436. package/defaults/pages/admin/index.md +4 -0
  437. package/defaults/pages/admin/posts/editor.html +4 -0
  438. package/defaults/pages/admin/posts/index.html +4 -0
  439. package/defaults/pages/admin/stackblitz/index.html +4 -0
  440. package/defaults/pages/admin/studio/index.html +4 -0
  441. package/defaults/pages/admin/users/index.html +4 -0
  442. package/defaults/pages/admin/users/new.html +4 -0
  443. package/defaults/pages/admin/verts/index.html +4 -0
  444. package/defaults/pages/ads-txt.html +11 -0
  445. package/defaults/pages/alternatives/index.md +4 -0
  446. package/defaults/pages/app.md +4 -0
  447. package/defaults/pages/auth/reset.md +4 -0
  448. package/defaults/pages/auth/signin.md +4 -0
  449. package/defaults/pages/auth/signup.md +4 -0
  450. package/defaults/pages/auth/token.md +4 -0
  451. package/defaults/pages/blog/categories/category.html +21 -0
  452. package/defaults/pages/blog/categories.md +4 -0
  453. package/defaults/pages/blog/index-json.html +27 -0
  454. package/defaults/pages/blog/tags/tag.html +21 -0
  455. package/defaults/pages/blog/tags.md +4 -0
  456. package/defaults/pages/blog.md +22 -0
  457. package/defaults/pages/careers.md +4 -0
  458. package/defaults/pages/connections/callback.md +4 -0
  459. package/defaults/pages/contact.md +4 -0
  460. package/defaults/pages/dashboard/account.md +4 -0
  461. package/defaults/pages/dashboard/index.md +12 -0
  462. package/defaults/pages/download.md +4 -0
  463. package/defaults/pages/extension/index.html +4 -0
  464. package/defaults/pages/extension/installed.html +4 -0
  465. package/defaults/pages/feedback.md +4 -0
  466. package/defaults/pages/feeds/posts-json.html +40 -0
  467. package/defaults/pages/feeds/posts-xml.html +38 -0
  468. package/defaults/pages/humans.html +25 -0
  469. package/defaults/pages/index.md +4 -0
  470. package/defaults/pages/legal/cookies.md +4 -0
  471. package/defaults/pages/legal/privacy.md +4 -0
  472. package/defaults/pages/legal/terms.md +4 -0
  473. package/defaults/pages/llms-txt.html +46 -0
  474. package/defaults/pages/opensearch.html +16 -0
  475. package/defaults/pages/pages-json.html +42 -0
  476. package/defaults/pages/payment/checkout.md +4 -0
  477. package/defaults/pages/payment/confirmation.md +4 -0
  478. package/defaults/pages/portal/email-preferences.md +4 -0
  479. package/defaults/pages/pricing.md +4 -0
  480. package/defaults/pages/redirects/account.md +12 -0
  481. package/defaults/pages/redirects/admin/dashboard.md +11 -0
  482. package/defaults/pages/redirects/auth/change-password.md +9 -0
  483. package/defaults/pages/redirects/auth/forgot.md +9 -0
  484. package/defaults/pages/redirects/auth/join.md +9 -0
  485. package/defaults/pages/redirects/auth/login.md +9 -0
  486. package/defaults/pages/redirects/auth/recover.md +9 -0
  487. package/defaults/pages/redirects/auth/register.md +9 -0
  488. package/defaults/pages/redirects/auth/reset-password.md +9 -0
  489. package/defaults/pages/redirects/billing/cancel.md +9 -0
  490. package/defaults/pages/redirects/billing/refund.md +9 -0
  491. package/defaults/pages/robots.html +36 -0
  492. package/defaults/pages/security-txt.html +11 -0
  493. package/defaults/pages/sitemap.html +37 -0
  494. package/defaults/pages/status.md +4 -0
  495. package/defaults/pages/team/index.md +4 -0
  496. package/defaults/pages/test/account/dashboard.html +16 -0
  497. package/defaults/pages/test/index.md +12 -0
  498. package/defaults/pages/test/libraries/appearance.html +268 -0
  499. package/defaults/pages/test/libraries/cover.html +50 -0
  500. package/defaults/pages/test/libraries/error.html +30 -0
  501. package/defaults/pages/test/libraries/firestore.html +202 -0
  502. package/defaults/pages/test/libraries/form-manager.html +374 -0
  503. package/defaults/pages/test/libraries/layers.html +52 -0
  504. package/defaults/pages/test/libraries/lazy-loading.html +450 -0
  505. package/defaults/pages/test/libraries/verts.html +149 -0
  506. package/defaults/pages/test/redirect/external.md +13 -0
  507. package/defaults/pages/test/redirect/internal.md +13 -0
  508. package/defaults/pages/test/styleguide/index.html +588 -0
  509. package/defaults/pages/test/translation/index.md +50 -0
  510. package/defaults/pages/updates/index.md +4 -0
  511. package/defaults/sample-posts/2026-01-12-welcome-to-the-blog.md +26 -0
  512. package/defaults/sample-posts/2026-02-03-a-field-guide-to-good-defaults.md +50 -0
  513. package/defaults/sample-posts/2026-02-24-shipping-week-notes.md +37 -0
  514. package/defaults/sample-posts/2026-03-17-the-case-for-one-config.md +36 -0
  515. package/defaults/sample-posts/2026-04-09-write-release-notes-people-read.md +34 -0
  516. package/defaults/sample-posts/2026-05-21-dark-mode-done-properly.md +38 -0
  517. package/defaults/sample-posts/2026-06-02-the-boring-deploy-is-the-good-deploy.md +30 -0
  518. package/defaults/sample-posts/2026-06-11-naming-things-a-survival-guide.md +30 -0
  519. package/defaults/sample-posts/2026-06-19-your-pricing-page-is-a-product.md +28 -0
  520. package/defaults/sample-posts/2026-06-30-email-is-an-api-with-feelings.md +28 -0
  521. package/defaults/sample-posts/2026-07-08-ship-the-docs-with-the-diff.md +30 -0
  522. package/defaults/sample-team/avery-quinn.md +22 -0
  523. package/defaults/sample-team/jordan-reyes.md +25 -0
  524. package/defaults/sample-team/riley-nakamura.md +22 -0
  525. package/defaults/sample-team/sam-okafor.md +22 -0
  526. package/defaults/sample-updates/v1.0.0.md +28 -0
  527. package/defaults/sample-updates/v1.1.0.md +27 -0
  528. package/defaults/sample-updates/v1.2.0.md +27 -0
  529. package/defaults/sample-updates/v1.3.0.md +28 -0
  530. package/defaults/showcase/components.html +62 -0
  531. package/defaults/showcase/entry.html +76 -0
  532. package/defaults/showcase/frame.html +62 -0
  533. package/defaults/showcase/index.html +59 -0
  534. package/dist/assets.js +1349 -0
  535. package/dist/brand-tokens.js +195 -0
  536. package/dist/build.js +192 -0
  537. package/dist/cachebreak-html.js +71 -0
  538. package/dist/cli-run.js +25 -0
  539. package/dist/cli.js +47 -0
  540. package/dist/collections.js +183 -0
  541. package/dist/commands/audit.js +470 -0
  542. package/dist/commands/build.js +197 -0
  543. package/dist/commands/clean.js +18 -0
  544. package/dist/commands/customize.js +125 -0
  545. package/dist/commands/deploy.js +441 -0
  546. package/dist/commands/dev.js +1067 -0
  547. package/dist/commands/install.js +31 -0
  548. package/dist/commands/lib/deploy-precheck.js +43 -0
  549. package/dist/commands/lib/ensure-target.js +115 -0
  550. package/dist/commands/migrate.js +86 -0
  551. package/dist/commands/purge.js +31 -0
  552. package/dist/commands/test.js +237 -0
  553. package/dist/commands/translate.js +57 -0
  554. package/dist/commands/update.js +22 -0
  555. package/dist/commands/version.js +11 -0
  556. package/dist/config-sections.js +245 -0
  557. package/dist/consumer-scan.js +118 -0
  558. package/dist/consumer.js +49 -0
  559. package/dist/customize.js +328 -0
  560. package/dist/decisions.js +153 -0
  561. package/dist/dist-audit.js +417 -0
  562. package/dist/dynamic-pages.js +357 -0
  563. package/dist/engine.js +1402 -0
  564. package/dist/firebase-auth-helpers.js +198 -0
  565. package/dist/font-metrics.js +420 -0
  566. package/dist/frontmatter-liquid.js +253 -0
  567. package/dist/github-secrets.js +80 -0
  568. package/dist/hero-animations.js +170 -0
  569. package/dist/imagemin.js +261 -0
  570. package/dist/index.js +41 -0
  571. package/dist/inline-icons.js +65 -0
  572. package/dist/language-flags.js +55 -0
  573. package/dist/layers.js +80 -0
  574. package/dist/layouts.js +44 -0
  575. package/dist/limit-collections.js +199 -0
  576. package/dist/link-resolver.js +303 -0
  577. package/dist/markdown-images.js +85 -0
  578. package/dist/merge.js +20 -0
  579. package/dist/migrate/codemod.js +172 -0
  580. package/dist/migrate/config-convert.js +448 -0
  581. package/dist/migrate/consumer-assets.js +130 -0
  582. package/dist/migrate/index.js +223 -0
  583. package/dist/migrate/lint.js +108 -0
  584. package/dist/migrate/rules.js +1929 -0
  585. package/dist/minify-html.js +148 -0
  586. package/dist/mode-helpers.js +96 -0
  587. package/dist/omega-bin.js +7 -0
  588. package/dist/overrides.js +190 -0
  589. package/dist/path-prefix.js +167 -0
  590. package/dist/paths.js +37 -0
  591. package/dist/pricing.js +301 -0
  592. package/dist/purge.js +95 -0
  593. package/dist/redirect-page.js +45 -0
  594. package/dist/sample-content.js +210 -0
  595. package/dist/scaffold.js +123 -0
  596. package/dist/sections.js +810 -0
  597. package/dist/service-worker.js +200 -0
  598. package/dist/social-pages.js +142 -0
  599. package/dist/srcset.js +67 -0
  600. package/dist/static-assets.js +154 -0
  601. package/dist/target-shortlinks.js +123 -0
  602. package/dist/theme-vocabulary.js +76 -0
  603. package/dist/translate/collect-text-nodes.js +93 -0
  604. package/dist/translate/default-routes.js +152 -0
  605. package/dist/translate/generate-defaults.js +297 -0
  606. package/dist/translate/index.js +568 -0
  607. package/dist/translate/packaged-defaults.js +127 -0
  608. package/dist/translate/sitemap.js +108 -0
  609. package/dist/vendor/account/engine.js +182 -0
  610. package/dist/vendor/account/features.js +220 -0
  611. package/dist/vendor/account/index.js +53 -0
  612. package/dist/vendor/account/schema.js +272 -0
  613. package/dist/vendor/account/subscription.js +38 -0
  614. package/dist/vendor/config/company.js +31 -0
  615. package/dist/vendor/config/defaults.js +173 -0
  616. package/dist/vendor/config/demo.js +18 -0
  617. package/dist/vendor/config/desktop-artifacts.js +110 -0
  618. package/dist/vendor/config/edit.js +769 -0
  619. package/dist/vendor/config/env-delivery.js +145 -0
  620. package/dist/vendor/config/env-rules.js +93 -0
  621. package/dist/vendor/config/env-schema.js +1078 -0
  622. package/dist/vendor/config/env.js +445 -0
  623. package/dist/vendor/config/hooks.js +97 -0
  624. package/dist/vendor/config/index.js +237 -0
  625. package/dist/vendor/config/instances.js +208 -0
  626. package/dist/vendor/config/load.js +490 -0
  627. package/dist/vendor/config/merge.js +68 -0
  628. package/dist/vendor/config/order.js +139 -0
  629. package/dist/vendor/config/ports.js +374 -0
  630. package/dist/vendor/config/providers.js +32 -0
  631. package/dist/vendor/config/repo.js +142 -0
  632. package/dist/vendor/config/retired-keys.js +430 -0
  633. package/dist/vendor/config/schema.js +1610 -0
  634. package/dist/vendor/config/secrets.js +50 -0
  635. package/dist/vendor/config/seed.js +34 -0
  636. package/dist/vendor/config/site-global.js +205 -0
  637. package/dist/vendor/config/validate.js +554 -0
  638. package/dist/vendor/config/winback.js +61 -0
  639. package/dist/vendor/devkit/actions-secrets.js +145 -0
  640. package/dist/vendor/devkit/attach-log-file.js +262 -0
  641. package/dist/vendor/devkit/bare-requires.js +153 -0
  642. package/dist/vendor/devkit/bundle.js +292 -0
  643. package/dist/vendor/devkit/ci-workflows.js +520 -0
  644. package/dist/vendor/devkit/cli-router.js +156 -0
  645. package/dist/vendor/devkit/command-path.js +46 -0
  646. package/dist/vendor/devkit/defaults-engine.js +372 -0
  647. package/dist/vendor/devkit/deploy-precheck.js +54 -0
  648. package/dist/vendor/devkit/deploy-record.js +180 -0
  649. package/dist/vendor/devkit/deploy.js +286 -0
  650. package/dist/vendor/devkit/env-watch.js +132 -0
  651. package/dist/vendor/devkit/framework-deps.js +46 -0
  652. package/dist/vendor/devkit/icons.js +178 -0
  653. package/dist/vendor/devkit/license.js +155 -0
  654. package/dist/vendor/devkit/local-https.js +360 -0
  655. package/dist/vendor/devkit/local.js +1905 -0
  656. package/dist/vendor/devkit/logger.js +128 -0
  657. package/dist/vendor/devkit/merge-line-files.js +296 -0
  658. package/dist/vendor/devkit/omega-bin.js +345 -0
  659. package/dist/vendor/devkit/reads.js +274 -0
  660. package/dist/vendor/devkit/safe-install.js +18 -0
  661. package/dist/vendor/devkit/scaffold-guard.js +96 -0
  662. package/dist/vendor/devkit/stop-signals.js +28 -0
  663. package/dist/vendor/devkit/strip-dev-blocks-plugin.js +29 -0
  664. package/dist/vendor/devkit/strip-dev-blocks.js +32 -0
  665. package/dist/vendor/devkit/target-secrets.js +165 -0
  666. package/dist/vendor/devkit/test/scope.js +162 -0
  667. package/dist/vendor/devkit/translate/cache.js +84 -0
  668. package/dist/vendor/devkit/translate/engine.js +243 -0
  669. package/dist/vendor/devkit/translate/index.js +49 -0
  670. package/dist/vendor/devkit/translate/languages.js +134 -0
  671. package/dist/vendor/devkit/translate/providers.js +188 -0
  672. package/dist/vendor/devkit/update.js +569 -0
  673. package/dist/vendor/template-kit/data/language-flags.js +54 -0
  674. package/dist/vendor/template-kit/data/languages.js +195 -0
  675. package/dist/vendor/template-kit/data/social-urls.js +26 -0
  676. package/dist/vendor/template-kit/filters.js +227 -0
  677. package/dist/vendor/template-kit/index.js +28 -0
  678. package/dist/vendor/template-kit/jekyll-compat.js +268 -0
  679. package/dist/vendor/template-kit/register-liquid.js +0 -0
  680. package/dist/vendor/template-kit/tags/collections.js +191 -0
  681. package/dist/vendor/template-kit/tags/conditionals.js +101 -0
  682. package/dist/vendor/template-kit/tags/content.js +175 -0
  683. package/dist/vendor/template-kit/tags/index.js +33 -0
  684. package/dist/vendor/template-kit/tags/media.js +376 -0
  685. package/dist/vendor/template-kit/variable-resolver.js +163 -0
  686. package/docs/ads-system.md +109 -0
  687. package/docs/classy-v2/DIRECTION.md +92 -0
  688. package/docs/classy-v2/draft-1.html +1932 -0
  689. package/docs/classy-v2/draft-2.html +1956 -0
  690. package/docs/frontmatter.md +153 -0
  691. package/docs/index.md +254 -0
  692. package/docs/libs.md +298 -0
  693. package/docs/omega-sections-spec.md +159 -0
  694. package/docs/page-contract.md +112 -0
  695. package/docs/sections.md +845 -0
  696. package/docs/shared/agent-docs.md +89 -0
  697. package/docs/shared/analytics.md +612 -0
  698. package/docs/shared/brands.md +51 -0
  699. package/docs/shared/breaking-changes.md +497 -0
  700. package/docs/shared/config.md +1387 -0
  701. package/docs/shared/deploys.md +215 -0
  702. package/docs/shared/icons.md +201 -0
  703. package/docs/shared/local-dev.md +147 -0
  704. package/docs/shared/logging.md +202 -0
  705. package/docs/shared/monitoring.md +153 -0
  706. package/docs/shared/publishing.md +183 -0
  707. package/docs/shared/rulings.md +34 -0
  708. package/docs/shared/testing.md +147 -0
  709. package/docs/shared/theming.md +604 -0
  710. package/docs/shared/translation.md +291 -0
  711. package/docs/shared/updates.md +61 -0
  712. package/docs/template-kit.md +128 -0
  713. package/docs/test-framework.md +163 -0
  714. package/package.json +122 -0
  715. package/runtime/boot.js +149 -0
  716. package/runtime/icons.js +77 -0
  717. package/runtime/manager.js +36 -0
  718. package/scaffold/.github/workflows/build.yml +139 -0
  719. package/scaffold/.nvmrc +1 -0
  720. package/scaffold/AGENTS.md +55 -0
  721. package/scaffold/CLAUDE.md +1 -0
  722. package/scaffold/_.gitattributes +4 -0
  723. package/scaffold/_.gitignore +27 -0
  724. package/scaffold/config/omega.json5 +20 -0
  725. package/scaffold/src/assets/css/pages/.gitkeep +0 -0
  726. package/scaffold/src/assets/images/.gitkeep +0 -0
  727. package/scaffold/src/assets/js/pages/.gitkeep +0 -0
  728. package/scaffold/src/pages/.gitkeep +0 -0
  729. package/scaffold/src/pages/example.md.txt +94 -0
  730. package/scaffold/src/service-worker.js +16 -0
  731. package/sw/entry.js +19 -0
  732. package/sw/manager.js +281 -0
  733. package/themes/_template/README.md +115 -0
  734. package/themes/_template/_config.scss +60 -0
  735. package/themes/_template/_theme.js +14 -0
  736. package/themes/_template/_theme.scss +28 -0
  737. package/themes/_template/css/base/_root.scss +19 -0
  738. package/themes/_template/css/components/_components.scss +23 -0
  739. package/themes/base/README.md +5 -0
  740. package/themes/base/_components/data/org-chart/component.html +33 -0
  741. package/themes/base/_components/data/org-chart/component.json5 +33 -0
  742. package/themes/base/_components/data/org-chart/component.scss +183 -0
  743. package/themes/base/_components/heading/masthead/component.html +22 -0
  744. package/themes/base/_components/heading/masthead/component.json5 +30 -0
  745. package/themes/base/_components/heading/section-head/component.html +18 -0
  746. package/themes/base/_components/heading/section-head/component.json5 +27 -0
  747. package/themes/base/_components/pricing/features/component.html +29 -0
  748. package/themes/base/_components/pricing/features/component.json5 +30 -0
  749. package/themes/base/_hero/orbit/index.html +30 -0
  750. package/themes/base/_hero/orbit/script.js +33 -0
  751. package/themes/base/_hero/orbit/style.scss +133 -0
  752. package/themes/base/_includes/admin/sections/sidebar.html +3 -0
  753. package/themes/base/_includes/admin/sections/topbar.html +3 -0
  754. package/themes/base/_includes/backend/sections/sidebar.html +3 -0
  755. package/themes/base/_includes/backend/sections/topbar.html +3 -0
  756. package/themes/base/_includes/frontend/components/connection-card.html +49 -0
  757. package/themes/base/_includes/frontend/components/post-card.html +54 -0
  758. package/themes/base/_includes/frontend/sections/account-connections.html +42 -0
  759. package/themes/base/_includes/frontend/sections/account-section-header.html +11 -0
  760. package/themes/base/_includes/frontend/sections/footer.html +154 -0
  761. package/themes/base/_includes/frontend/sections/nav.html +276 -0
  762. package/themes/base/_includes/global/sections/account.html +72 -0
  763. package/themes/base/_includes/global/sections/app-sidebar.html +248 -0
  764. package/themes/base/_includes/global/sections/app-topbar.html +228 -0
  765. package/themes/base/_includes/global/sections/page-header.html +99 -0
  766. package/themes/base/_layouts/admin/core/minimal-viewport-locked.html +22 -0
  767. package/themes/base/_layouts/admin/core/minimal.html +23 -0
  768. package/themes/base/_layouts/backend/core/base.html +57 -0
  769. package/themes/base/_layouts/backend/core/minimal-viewport-locked.html +32 -0
  770. package/themes/base/_layouts/backend/core/minimal.html +32 -0
  771. package/themes/base/_layouts/backend/pages/dashboard/index.html +230 -0
  772. package/themes/base/_layouts/frontend/core/base.html +31 -0
  773. package/themes/base/_layouts/frontend/core/cover.html +40 -0
  774. package/themes/base/_layouts/frontend/core/minimal.html +47 -0
  775. package/themes/base/_layouts/frontend/pages/404.html +54 -0
  776. package/themes/base/_layouts/frontend/pages/about.html +144 -0
  777. package/themes/base/_layouts/frontend/pages/account/index.html +2104 -0
  778. package/themes/base/_layouts/frontend/pages/alternatives/alternative.html +319 -0
  779. package/themes/base/_layouts/frontend/pages/alternatives/index.html +186 -0
  780. package/themes/base/_layouts/frontend/pages/app.html +142 -0
  781. package/themes/base/_layouts/frontend/pages/auth/reset.html +53 -0
  782. package/themes/base/_layouts/frontend/pages/auth/signin.html +90 -0
  783. package/themes/base/_layouts/frontend/pages/auth/signup.html +141 -0
  784. package/themes/base/_layouts/frontend/pages/auth/token.html +44 -0
  785. package/themes/base/_layouts/frontend/pages/blog/categories/category.html +88 -0
  786. package/themes/base/_layouts/frontend/pages/blog/categories/index.html +88 -0
  787. package/themes/base/_layouts/frontend/pages/blog/index.html +255 -0
  788. package/themes/base/_layouts/frontend/pages/blog/post.html +261 -0
  789. package/themes/base/_layouts/frontend/pages/blog/tags/index.html +84 -0
  790. package/themes/base/_layouts/frontend/pages/blog/tags/tag.html +88 -0
  791. package/themes/base/_layouts/frontend/pages/collection/category.html +72 -0
  792. package/themes/base/_layouts/frontend/pages/collection/document.html +54 -0
  793. package/themes/base/_layouts/frontend/pages/collection/index.html +84 -0
  794. package/themes/base/_layouts/frontend/pages/connections/callback.html +55 -0
  795. package/themes/base/_layouts/frontend/pages/contact.html +214 -0
  796. package/themes/base/_layouts/frontend/pages/download.html +793 -0
  797. package/themes/base/_layouts/frontend/pages/extension/index.html +253 -0
  798. package/themes/base/_layouts/frontend/pages/extension/installed.html +198 -0
  799. package/themes/base/_layouts/frontend/pages/feedback.html +171 -0
  800. package/themes/base/_layouts/frontend/pages/index.html +66 -0
  801. package/themes/base/_layouts/frontend/pages/legal/document.html +78 -0
  802. package/themes/base/_layouts/frontend/pages/payment/checkout.html +459 -0
  803. package/themes/base/_layouts/frontend/pages/payment/confirmation.html +131 -0
  804. package/themes/base/_layouts/frontend/pages/portal/email-preferences.html +104 -0
  805. package/themes/base/_layouts/frontend/pages/pricing.html +596 -0
  806. package/themes/base/_layouts/frontend/pages/status.html +361 -0
  807. package/themes/base/_layouts/frontend/pages/team/index.html +210 -0
  808. package/themes/base/_layouts/frontend/pages/team/member.html +125 -0
  809. package/themes/base/_layouts/frontend/pages/updates/index.html +119 -0
  810. package/themes/base/_layouts/frontend/pages/updates/update.html +118 -0
  811. package/themes/base/_sections/about/hero/section.html +68 -0
  812. package/themes/base/_sections/about/hero/section.json5 +58 -0
  813. package/themes/base/_sections/about/letter/section.html +60 -0
  814. package/themes/base/_sections/about/letter/section.json5 +56 -0
  815. package/themes/base/_sections/about/photo-band/section.html +24 -0
  816. package/themes/base/_sections/about/photo-band/section.json5 +23 -0
  817. package/themes/base/_sections/about/principles/section.html +17 -0
  818. package/themes/base/_sections/about/principles/section.json5 +34 -0
  819. package/themes/base/_sections/about/timeline/section.html +20 -0
  820. package/themes/base/_sections/about/timeline/section.json5 +34 -0
  821. package/themes/base/_sections/marketing/bento/section.html +91 -0
  822. package/themes/base/_sections/marketing/bento/section.json5 +152 -0
  823. package/themes/base/_sections/marketing/cta/section.html +44 -0
  824. package/themes/base/_sections/marketing/cta/section.json5 +47 -0
  825. package/themes/base/_sections/marketing/faq/section.html +52 -0
  826. package/themes/base/_sections/marketing/faq/section.json5 +68 -0
  827. package/themes/base/_sections/marketing/hero/section.html +349 -0
  828. package/themes/base/_sections/marketing/hero/section.json5 +335 -0
  829. package/themes/base/_sections/marketing/newsletter-cta/section.html +38 -0
  830. package/themes/base/_sections/marketing/newsletter-cta/section.js +86 -0
  831. package/themes/base/_sections/marketing/newsletter-cta/section.json5 +29 -0
  832. package/themes/base/_sections/marketing/pricing-cards/section.html +115 -0
  833. package/themes/base/_sections/marketing/pricing-cards/section.json5 +72 -0
  834. package/themes/base/_sections/marketing/product-demo/section.html +62 -0
  835. package/themes/base/_sections/marketing/product-demo/section.js +44 -0
  836. package/themes/base/_sections/marketing/product-demo/section.json5 +52 -0
  837. package/themes/base/_sections/marketing/prose/section.html +22 -0
  838. package/themes/base/_sections/marketing/prose/section.json5 +41 -0
  839. package/themes/base/_sections/marketing/showcase/section.html +35 -0
  840. package/themes/base/_sections/marketing/showcase/section.json5 +52 -0
  841. package/themes/base/_sections/marketing/stats/section.html +32 -0
  842. package/themes/base/_sections/marketing/stats/section.json5 +44 -0
  843. package/themes/base/_sections/marketing/testimonials/section.html +54 -0
  844. package/themes/base/_sections/marketing/testimonials/section.json5 +70 -0
  845. package/themes/base/_sections/marketing/trusted-by/section.html +30 -0
  846. package/themes/base/_sections/marketing/trusted-by/section.json5 +38 -0
  847. package/themes/base/_sections/verts/unit/section.html +7 -0
  848. package/themes/base/_sections/verts/unit/section.js +22 -0
  849. package/themes/base/_sections/verts/unit/section.json5 +24 -0
  850. package/themes/base/_sections/verts/unit/section.scss +24 -0
  851. package/themes/base/_theme.js +13 -0
  852. package/themes/base/_theme.scss +4 -0
  853. package/themes/bootstrap/_theme.js +16 -0
  854. package/themes/bootstrap/_theme.scss +11 -0
  855. package/themes/bootstrap/js/index.esm.js +19 -0
  856. package/themes/bootstrap/js/index.umd.js +34 -0
  857. package/themes/bootstrap/js/src/alert.js +87 -0
  858. package/themes/bootstrap/js/src/base-component.js +86 -0
  859. package/themes/bootstrap/js/src/button.js +72 -0
  860. package/themes/bootstrap/js/src/carousel.js +474 -0
  861. package/themes/bootstrap/js/src/collapse.js +297 -0
  862. package/themes/bootstrap/js/src/dom/data.js +55 -0
  863. package/themes/bootstrap/js/src/dom/event-handler.js +317 -0
  864. package/themes/bootstrap/js/src/dom/manipulator.js +71 -0
  865. package/themes/bootstrap/js/src/dom/selector-engine.js +126 -0
  866. package/themes/bootstrap/js/src/dropdown.js +458 -0
  867. package/themes/bootstrap/js/src/modal.js +378 -0
  868. package/themes/bootstrap/js/src/offcanvas.js +282 -0
  869. package/themes/bootstrap/js/src/popover.js +97 -0
  870. package/themes/bootstrap/js/src/scrollspy.js +296 -0
  871. package/themes/bootstrap/js/src/tab.js +315 -0
  872. package/themes/bootstrap/js/src/toast.js +224 -0
  873. package/themes/bootstrap/js/src/tooltip.js +632 -0
  874. package/themes/bootstrap/js/src/util/backdrop.js +151 -0
  875. package/themes/bootstrap/js/src/util/component-functions.js +35 -0
  876. package/themes/bootstrap/js/src/util/config.js +65 -0
  877. package/themes/bootstrap/js/src/util/focustrap.js +115 -0
  878. package/themes/bootstrap/js/src/util/index.js +306 -0
  879. package/themes/bootstrap/js/src/util/sanitizer.js +117 -0
  880. package/themes/bootstrap/js/src/util/scrollbar.js +114 -0
  881. package/themes/bootstrap/js/src/util/swipe.js +146 -0
  882. package/themes/bootstrap/js/src/util/template-factory.js +160 -0
  883. package/themes/bootstrap/overrides/_accordion.scss +21 -0
  884. package/themes/bootstrap/overrides/_avatars.scss +51 -0
  885. package/themes/bootstrap/overrides/_blog-images.scss +45 -0
  886. package/themes/bootstrap/overrides/_buttons-adaptive.scss +141 -0
  887. package/themes/bootstrap/overrides/_color-shades.scss +25 -0
  888. package/themes/bootstrap/overrides/_cursor-utilities.scss +13 -0
  889. package/themes/bootstrap/overrides/_index.scss +30 -0
  890. package/themes/bootstrap/overrides/_links.scss +26 -0
  891. package/themes/bootstrap/overrides/_soft-colors.scss +99 -0
  892. package/themes/bootstrap/overrides/_spacing-extended.scss +74 -0
  893. package/themes/bootstrap/overrides/_spinners.scss +22 -0
  894. package/themes/bootstrap/overrides/_typography-utilities.scss +40 -0
  895. package/themes/bootstrap/scss/_accordion.scss +153 -0
  896. package/themes/bootstrap/scss/_alert.scss +68 -0
  897. package/themes/bootstrap/scss/_badge.scss +38 -0
  898. package/themes/bootstrap/scss/_breadcrumb.scss +40 -0
  899. package/themes/bootstrap/scss/_button-group.scss +147 -0
  900. package/themes/bootstrap/scss/_buttons.scss +216 -0
  901. package/themes/bootstrap/scss/_card.scss +238 -0
  902. package/themes/bootstrap/scss/_carousel.scss +226 -0
  903. package/themes/bootstrap/scss/_close.scss +66 -0
  904. package/themes/bootstrap/scss/_containers.scss +41 -0
  905. package/themes/bootstrap/scss/_dropdown.scss +250 -0
  906. package/themes/bootstrap/scss/_forms.scss +9 -0
  907. package/themes/bootstrap/scss/_functions.scss +302 -0
  908. package/themes/bootstrap/scss/_grid.scss +39 -0
  909. package/themes/bootstrap/scss/_helpers.scss +12 -0
  910. package/themes/bootstrap/scss/_images.scss +42 -0
  911. package/themes/bootstrap/scss/_list-group.scss +199 -0
  912. package/themes/bootstrap/scss/_maps.scss +174 -0
  913. package/themes/bootstrap/scss/_mixins.scss +42 -0
  914. package/themes/bootstrap/scss/_modal.scss +240 -0
  915. package/themes/bootstrap/scss/_nav.scss +197 -0
  916. package/themes/bootstrap/scss/_navbar.scss +289 -0
  917. package/themes/bootstrap/scss/_offcanvas.scss +147 -0
  918. package/themes/bootstrap/scss/_pagination.scss +109 -0
  919. package/themes/bootstrap/scss/_placeholders.scss +51 -0
  920. package/themes/bootstrap/scss/_popover.scss +196 -0
  921. package/themes/bootstrap/scss/_progress.scss +68 -0
  922. package/themes/bootstrap/scss/_reboot.scss +611 -0
  923. package/themes/bootstrap/scss/_root.scss +187 -0
  924. package/themes/bootstrap/scss/_spinners.scss +85 -0
  925. package/themes/bootstrap/scss/_tables.scss +171 -0
  926. package/themes/bootstrap/scss/_toasts.scss +73 -0
  927. package/themes/bootstrap/scss/_tooltip.scss +119 -0
  928. package/themes/bootstrap/scss/_transitions.scss +27 -0
  929. package/themes/bootstrap/scss/_type.scss +106 -0
  930. package/themes/bootstrap/scss/_utilities.scss +806 -0
  931. package/themes/bootstrap/scss/_variables-dark.scss +102 -0
  932. package/themes/bootstrap/scss/_variables.scss +1753 -0
  933. package/themes/bootstrap/scss/bootstrap-grid.scss +62 -0
  934. package/themes/bootstrap/scss/bootstrap-reboot.scss +10 -0
  935. package/themes/bootstrap/scss/bootstrap-utilities.scss +19 -0
  936. package/themes/bootstrap/scss/bootstrap.scss +52 -0
  937. package/themes/bootstrap/scss/forms/_floating-labels.scss +97 -0
  938. package/themes/bootstrap/scss/forms/_form-check.scss +189 -0
  939. package/themes/bootstrap/scss/forms/_form-control.scss +214 -0
  940. package/themes/bootstrap/scss/forms/_form-range.scss +91 -0
  941. package/themes/bootstrap/scss/forms/_form-select.scss +80 -0
  942. package/themes/bootstrap/scss/forms/_form-text.scss +11 -0
  943. package/themes/bootstrap/scss/forms/_input-group.scss +132 -0
  944. package/themes/bootstrap/scss/forms/_labels.scss +36 -0
  945. package/themes/bootstrap/scss/forms/_validation.scss +12 -0
  946. package/themes/bootstrap/scss/helpers/_clearfix.scss +3 -0
  947. package/themes/bootstrap/scss/helpers/_color-bg.scss +7 -0
  948. package/themes/bootstrap/scss/helpers/_colored-links.scss +30 -0
  949. package/themes/bootstrap/scss/helpers/_focus-ring.scss +5 -0
  950. package/themes/bootstrap/scss/helpers/_icon-link.scss +25 -0
  951. package/themes/bootstrap/scss/helpers/_position.scss +36 -0
  952. package/themes/bootstrap/scss/helpers/_ratio.scss +26 -0
  953. package/themes/bootstrap/scss/helpers/_stacks.scss +15 -0
  954. package/themes/bootstrap/scss/helpers/_stretched-link.scss +15 -0
  955. package/themes/bootstrap/scss/helpers/_text-truncation.scss +7 -0
  956. package/themes/bootstrap/scss/helpers/_visually-hidden.scss +8 -0
  957. package/themes/bootstrap/scss/helpers/_vr.scss +8 -0
  958. package/themes/bootstrap/scss/mixins/_alert.scss +18 -0
  959. package/themes/bootstrap/scss/mixins/_backdrop.scss +14 -0
  960. package/themes/bootstrap/scss/mixins/_banner.scss +7 -0
  961. package/themes/bootstrap/scss/mixins/_border-radius.scss +78 -0
  962. package/themes/bootstrap/scss/mixins/_box-shadow.scss +18 -0
  963. package/themes/bootstrap/scss/mixins/_breakpoints.scss +127 -0
  964. package/themes/bootstrap/scss/mixins/_buttons.scss +70 -0
  965. package/themes/bootstrap/scss/mixins/_caret.scss +69 -0
  966. package/themes/bootstrap/scss/mixins/_clearfix.scss +9 -0
  967. package/themes/bootstrap/scss/mixins/_color-mode.scss +21 -0
  968. package/themes/bootstrap/scss/mixins/_color-scheme.scss +7 -0
  969. package/themes/bootstrap/scss/mixins/_container.scss +11 -0
  970. package/themes/bootstrap/scss/mixins/_deprecate.scss +10 -0
  971. package/themes/bootstrap/scss/mixins/_forms.scss +163 -0
  972. package/themes/bootstrap/scss/mixins/_gradients.scss +47 -0
  973. package/themes/bootstrap/scss/mixins/_grid.scss +151 -0
  974. package/themes/bootstrap/scss/mixins/_image.scss +16 -0
  975. package/themes/bootstrap/scss/mixins/_list-group.scss +26 -0
  976. package/themes/bootstrap/scss/mixins/_lists.scss +7 -0
  977. package/themes/bootstrap/scss/mixins/_pagination.scss +10 -0
  978. package/themes/bootstrap/scss/mixins/_reset-text.scss +17 -0
  979. package/themes/bootstrap/scss/mixins/_resize.scss +6 -0
  980. package/themes/bootstrap/scss/mixins/_table-variants.scss +24 -0
  981. package/themes/bootstrap/scss/mixins/_text-truncate.scss +8 -0
  982. package/themes/bootstrap/scss/mixins/_transition.scss +26 -0
  983. package/themes/bootstrap/scss/mixins/_utilities.scss +97 -0
  984. package/themes/bootstrap/scss/mixins/_visually-hidden.scss +38 -0
  985. package/themes/bootstrap/scss/tests/jasmine.js +16 -0
  986. package/themes/bootstrap/scss/tests/mixins/_auto-import-of-variables-dark.test.scss +7 -0
  987. package/themes/bootstrap/scss/tests/mixins/_color-modes.test.scss +69 -0
  988. package/themes/bootstrap/scss/tests/mixins/_media-query-color-mode-full.test.scss +8 -0
  989. package/themes/bootstrap/scss/tests/mixins/_utilities.test.scss +393 -0
  990. package/themes/bootstrap/scss/tests/sass-true/register.js +14 -0
  991. package/themes/bootstrap/scss/tests/sass-true/runner.js +17 -0
  992. package/themes/bootstrap/scss/tests/utilities/_api.test.scss +75 -0
  993. package/themes/bootstrap/scss/utilities/_api.scss +47 -0
  994. package/themes/bootstrap/scss/vendor/_rfs.scss +348 -0
  995. package/themes/classy/README.md +89 -0
  996. package/themes/classy/_config.scss +245 -0
  997. package/themes/classy/_theme.js +31 -0
  998. package/themes/classy/_theme.scss +57 -0
  999. package/themes/classy/css/app/_panels.scss +183 -0
  1000. package/themes/classy/css/base/_fonts.scss +79 -0
  1001. package/themes/classy/css/base/_no-raise.scss +49 -0
  1002. package/themes/classy/css/base/_root.scss +113 -0
  1003. package/themes/classy/css/base/_typography.scss +154 -0
  1004. package/themes/classy/css/base/_utilities.scss +284 -0
  1005. package/themes/classy/css/components/_alerts.scss +51 -0
  1006. package/themes/classy/css/components/_badges.scss +46 -0
  1007. package/themes/classy/css/components/_buttons.scss +175 -0
  1008. package/themes/classy/css/components/_cards.scss +41 -0
  1009. package/themes/classy/css/components/_dropdowns.scss +48 -0
  1010. package/themes/classy/css/components/_forms.scss +171 -0
  1011. package/themes/classy/css/components/_receipt.scss +45 -0
  1012. package/themes/classy/css/layout/_footer.scss +298 -0
  1013. package/themes/classy/css/layout/_general.scss +209 -0
  1014. package/themes/classy/css/layout/_nav.scss +154 -0
  1015. package/themes/classy/css/layout/_shell.scss +458 -0
  1016. package/themes/classy/css/marketing/_bento.scss +238 -0
  1017. package/themes/classy/css/marketing/_content.scss +1328 -0
  1018. package/themes/classy/css/marketing/_downloads.scss +246 -0
  1019. package/themes/classy/css/marketing/_hero.scss +311 -0
  1020. package/themes/classy/css/marketing/_sections.scss +649 -0
  1021. package/themes/classy/css/pages/404/index.scss +67 -0
  1022. package/themes/classy/css/pages/_auth.scss +235 -0
  1023. package/themes/classy/css/pages/admin/firebase/index.scss +22 -0
  1024. package/themes/classy/css/pages/blog/[slug].scss +56 -0
  1025. package/themes/classy/css/pages/cookies.scss +2 -0
  1026. package/themes/classy/css/pages/dashboard/account/index.scss +31 -0
  1027. package/themes/classy/css/pages/feedback/index.scss +39 -0
  1028. package/themes/classy/css/pages/legal/_document.scss +192 -0
  1029. package/themes/classy/css/pages/payment/checkout/index.scss +585 -0
  1030. package/themes/classy/css/pages/payment/confirmation/index.scss +9 -0
  1031. package/themes/classy/css/pages/privacy.scss +2 -0
  1032. package/themes/classy/css/pages/status/index.scss +164 -0
  1033. package/themes/classy/css/pages/terms.scss +2 -0
  1034. package/themes/classy/css/pages/updates/index.scss +128 -0
  1035. package/themes/classy/fonts/inter-italic-latin-ext.woff2 +0 -0
  1036. package/themes/classy/fonts/inter-italic-latin.woff2 +0 -0
  1037. package/themes/classy/fonts/inter-normal-latin-ext.woff2 +0 -0
  1038. package/themes/classy/fonts/inter-normal-latin.woff2 +0 -0
  1039. package/themes/classy/fonts/newsreader-italic-latin-ext.woff2 +0 -0
  1040. package/themes/classy/fonts/newsreader-italic-latin.woff2 +0 -0
  1041. package/themes/classy/fonts/newsreader-normal-latin-ext.woff2 +0 -0
  1042. package/themes/classy/fonts/newsreader-normal-latin.woff2 +0 -0
  1043. package/themes/classy/js/hero-demo-form.js +56 -0
  1044. package/themes/neobrutalism/README.md +118 -0
  1045. package/themes/neobrutalism/_config.scss +139 -0
  1046. package/themes/neobrutalism/_layouts/frontend/core/base.html +30 -0
  1047. package/themes/neobrutalism/_layouts/frontend/pages/index.html +275 -0
  1048. package/themes/neobrutalism/_theme.js +24 -0
  1049. package/themes/neobrutalism/_theme.scss +61 -0
  1050. package/themes/neobrutalism/css/base/_mixins.scss +46 -0
  1051. package/themes/neobrutalism/css/base/_root.scss +169 -0
  1052. package/themes/neobrutalism/css/base/_typography.scss +61 -0
  1053. package/themes/neobrutalism/css/base/_utilities.scss +89 -0
  1054. package/themes/neobrutalism/css/components/_billing-toggle.scss +74 -0
  1055. package/themes/neobrutalism/css/components/_buttons.scss +148 -0
  1056. package/themes/neobrutalism/css/components/_cards.scss +47 -0
  1057. package/themes/neobrutalism/css/components/_forms.scss +88 -0
  1058. package/themes/neobrutalism/css/components/_marketing.scss +244 -0
  1059. package/themes/neobrutalism/css/layout/_general.scss +213 -0
  1060. package/themes/neobrutalism/css/layout/_navigation.scss +171 -0
  1061. package/themes/neobrutalism/css/pages/index.scss +203 -0
  1062. package/themes/neobrutalism/css/pages/pricing/index.scss +58 -0
  1063. package/themes/neobrutalism/css/pages/test/libraries/layers/index.scss +7 -0
  1064. package/themes/neobrutalism/js/pages/test/libraries/layers/index.js +9 -0
  1065. package/themes/newsflash/README.md +117 -0
  1066. package/themes/newsflash/_components/heading/lede/component.html +14 -0
  1067. package/themes/newsflash/_components/heading/lede/component.json5 +23 -0
  1068. package/themes/newsflash/_components/heading/rule-head/component.html +17 -0
  1069. package/themes/newsflash/_components/heading/rule-head/component.json5 +23 -0
  1070. package/themes/newsflash/_components/news/byline/component.html +12 -0
  1071. package/themes/newsflash/_components/news/byline/component.json5 +18 -0
  1072. package/themes/newsflash/_components/news/story-card/component.html +25 -0
  1073. package/themes/newsflash/_components/news/story-card/component.json5 +21 -0
  1074. package/themes/newsflash/_config.scss +147 -0
  1075. package/themes/newsflash/_layouts/frontend/core/base.html +51 -0
  1076. package/themes/newsflash/_layouts/frontend/pages/blog/index.html +299 -0
  1077. package/themes/newsflash/_layouts/frontend/pages/blog/post.html +235 -0
  1078. package/themes/newsflash/_layouts/frontend/pages/index.html +328 -0
  1079. package/themes/newsflash/_sections/marketing/desks/section.html +39 -0
  1080. package/themes/newsflash/_sections/marketing/desks/section.json5 +49 -0
  1081. package/themes/newsflash/_sections/marketing/newsletter-cta/section.html +103 -0
  1082. package/themes/newsflash/_sections/marketing/newsletter-cta/section.json5 +50 -0
  1083. package/themes/newsflash/_sections/marketing/rundown/section.html +28 -0
  1084. package/themes/newsflash/_sections/marketing/rundown/section.json5 +40 -0
  1085. package/themes/newsflash/_theme.js +24 -0
  1086. package/themes/newsflash/_theme.scss +70 -0
  1087. package/themes/newsflash/css/base/_fonts.scss +80 -0
  1088. package/themes/newsflash/css/base/_mixins.scss +51 -0
  1089. package/themes/newsflash/css/base/_root.scss +257 -0
  1090. package/themes/newsflash/css/base/_typography.scss +49 -0
  1091. package/themes/newsflash/css/base/_utilities.scss +122 -0
  1092. package/themes/newsflash/css/components/_badges.scss +65 -0
  1093. package/themes/newsflash/css/components/_billing-toggle.scss +61 -0
  1094. package/themes/newsflash/css/components/_buttons.scss +139 -0
  1095. package/themes/newsflash/css/components/_cards.scss +47 -0
  1096. package/themes/newsflash/css/components/_editorial.scss +173 -0
  1097. package/themes/newsflash/css/components/_forms.scss +75 -0
  1098. package/themes/newsflash/css/components/_infinite-scroll.scss +102 -0
  1099. package/themes/newsflash/css/components/_panels.scss +156 -0
  1100. package/themes/newsflash/css/components/_ticker.scss +67 -0
  1101. package/themes/newsflash/css/layout/_general.scss +330 -0
  1102. package/themes/newsflash/css/layout/_navigation.scss +168 -0
  1103. package/themes/newsflash/css/pages/404/index.scss +79 -0
  1104. package/themes/newsflash/css/pages/blog/[slug].scss +186 -0
  1105. package/themes/newsflash/css/pages/blog/index.scss +17 -0
  1106. package/themes/newsflash/css/pages/index.scss +157 -0
  1107. package/themes/newsflash/css/pages/pricing/index.scss +55 -0
  1108. package/themes/newsflash/css/pages/test/libraries/layers/index.scss +7 -0
  1109. package/themes/newsflash/fonts/fraunces-italic-latin-ext.woff2 +0 -0
  1110. package/themes/newsflash/fonts/fraunces-italic-latin.woff2 +0 -0
  1111. package/themes/newsflash/fonts/fraunces-normal-latin-ext.woff2 +0 -0
  1112. package/themes/newsflash/fonts/fraunces-normal-latin.woff2 +0 -0
  1113. package/themes/newsflash/fonts/schibsted-grotesk-italic-latin-ext.woff2 +0 -0
  1114. package/themes/newsflash/fonts/schibsted-grotesk-italic-latin.woff2 +0 -0
  1115. package/themes/newsflash/fonts/schibsted-grotesk-normal-latin-ext.woff2 +0 -0
  1116. package/themes/newsflash/fonts/schibsted-grotesk-normal-latin.woff2 +0 -0
  1117. package/themes/newsflash/js/pages/blog/[slug].js +29 -0
  1118. package/themes/newsflash/js/pages/test/libraries/layers/index.js +9 -0
  1119. package/translations/es/pages/404.json +86 -0
  1120. package/translations/es/pages/account.json +36 -0
  1121. package/translations/es/pages/app.json +44 -0
  1122. package/translations/es/pages/cancel.json +36 -0
  1123. package/translations/es/pages/change-password.json +36 -0
  1124. package/translations/es/pages/connections/callback.json +41 -0
  1125. package/translations/es/pages/dashboard/account.json +475 -0
  1126. package/translations/es/pages/dashboard.json +36 -0
  1127. package/translations/es/pages/forgot.json +36 -0
  1128. package/translations/es/pages/join.json +36 -0
  1129. package/translations/es/pages/login.json +36 -0
  1130. package/translations/es/pages/payment/checkout.json +83 -0
  1131. package/translations/es/pages/payment/confirmation.json +62 -0
  1132. package/translations/es/pages/portal/email-preferences.json +46 -0
  1133. package/translations/es/pages/recover.json +36 -0
  1134. package/translations/es/pages/refund.json +36 -0
  1135. package/translations/es/pages/register.json +36 -0
  1136. package/translations/es/pages/reset-password.json +36 -0
  1137. package/translations/es/pages/reset.json +42 -0
  1138. package/translations/es/pages/signin.json +45 -0
  1139. package/translations/es/pages/signup.json +60 -0
  1140. package/translations/es/pages/token.json +39 -0
  1141. package/translations/fa/pages/404.json +86 -0
  1142. package/translations/fa/pages/account.json +36 -0
  1143. package/translations/fa/pages/app.json +44 -0
  1144. package/translations/fa/pages/cancel.json +36 -0
  1145. package/translations/fa/pages/change-password.json +36 -0
  1146. package/translations/fa/pages/connections/callback.json +41 -0
  1147. package/translations/fa/pages/dashboard/account.json +475 -0
  1148. package/translations/fa/pages/dashboard.json +36 -0
  1149. package/translations/fa/pages/forgot.json +36 -0
  1150. package/translations/fa/pages/join.json +36 -0
  1151. package/translations/fa/pages/login.json +36 -0
  1152. package/translations/fa/pages/payment/checkout.json +83 -0
  1153. package/translations/fa/pages/payment/confirmation.json +62 -0
  1154. package/translations/fa/pages/portal/email-preferences.json +46 -0
  1155. package/translations/fa/pages/recover.json +36 -0
  1156. package/translations/fa/pages/refund.json +36 -0
  1157. package/translations/fa/pages/register.json +36 -0
  1158. package/translations/fa/pages/reset-password.json +36 -0
  1159. package/translations/fa/pages/reset.json +42 -0
  1160. package/translations/fa/pages/signin.json +45 -0
  1161. package/translations/fa/pages/signup.json +60 -0
  1162. package/translations/fa/pages/token.json +39 -0
package/dist/engine.js ADDED
@@ -0,0 +1,1402 @@
1
+ /**
2
+ * The Eleventy engine core of @omega.js/web: layered themes (virtual templates
3
+ * / symlink farm), template-kit registration on Eleventy's own LiquidJS
4
+ * instance, frontmatter Liquid rendering, the `resolved` data alias
5
+ * (page.resolved equivalent — native data cascade), Jekyll conventions (dated
6
+ * post filenames, permalink normalization), blog collections + taxonomy, and
7
+ * default pages as virtual templates suppressed by same-URL consumer files.
8
+ * Promoted from the winning bake-off spike (_attic/spikes/bakeoff-shared/DECISION.md).
9
+ */
10
+ const fs = require('node:fs');
11
+ const path = require('node:path');
12
+ const JSON5 = require('json5');
13
+ const yaml = require('js-yaml');
14
+ const markdownIt = require('markdown-it');
15
+ const { registerLiquid } = require('./vendor/template-kit/register-liquid.js');
16
+ const { CACHE_TIMESTAMP } = require('./vendor/template-kit/filters.js');
17
+ const { slugify } = require('./vendor/template-kit/jekyll-compat.js');
18
+ const { toSiteGlobal } = require('./vendor/config/site-global.js');
19
+ const { resolveWinbackOffer } = require('./vendor/config/winback.js');
20
+ const Logger = require('./vendor/devkit/logger.js');
21
+ const reads = require('./vendor/devkit/reads.js');
22
+ const { KEYLESS_STAMP } = require('./vendor/devkit/license.js');
23
+ const { createFrontmatterResolver } = require('./frontmatter-liquid.js');
24
+ const { collectLayered, resolveThemeLayers } = require('./layers.js');
25
+ const { applyMarkdownImages } = require('./markdown-images.js');
26
+ const { permalinkOf } = require('./consumer-scan.js');
27
+ const { createDecisions } = require('./decisions.js');
28
+ const { registerVirtualLayouts, composeSymlinkFarm } = require('./layouts.js');
29
+ const { registerSectionTags, buildSectionLibrary } = require('./sections.js');
30
+ const { watchHeroAnimations, HERO_DIR } = require('./hero-animations.js');
31
+ const { registerCollections, BUILT_IN_COLLECTIONS } = require('./collections.js');
32
+ const { applyCollectionLimits } = require('./limit-collections.js');
33
+ const { readCollections, collectionPages, applyDocumentData } = require('./dynamic-pages.js');
34
+ const { readSocials, socialPages } = require('./social-pages.js');
35
+ const { readTargetShortlinks, targetShortlinkPages } = require('./target-shortlinks.js');
36
+ const { resolvePageAsset } = require('./assets.js');
37
+ const { resolvePathPrefix, prefixHtml } = require('./path-prefix.js');
38
+ const { SAMPLE_SETS, resolveAnchor, generateSampleSet } = require('./sample-content.js');
39
+ const { composePricing } = require('./pricing.js');
40
+ const { composeBrandTokens } = require('./brand-tokens.js');
41
+ const { resolveFontAwesomeRoots, createIconLoader } = require('./vendor/devkit/icons.js');
42
+ const { inlineIcons } = require('./inline-icons.js');
43
+ const { getEnvironment } = require('./mode-helpers.js');
44
+ const { ogLocale } = require('./vendor/devkit/translate/index.js');
45
+ const { PATHS } = require('./paths.js');
46
+ const {
47
+ CONFIG_SECTIONS, DEAD_SITE_SECTIONS, SITE_FACT_KEYS, RANDOM_ID_ASSIGN_IDIOM,
48
+ templateReads, randomIdReads, assignsRandomId,
49
+ } = require('./config-sections.js');
50
+
51
+ const logger = new Logger('engine');
52
+
53
+ // Data-cascade keys that are engine machinery, not page/layout data — everything
54
+ // else IS the resolved page data (the cascade already deep-merged layout
55
+ // defaults under page frontmatter, which is exactly what inject-properties.rb
56
+ // computed as page.resolved).
57
+ const RESOLVED_OMIT = new Set([
58
+ 'collections', 'content', 'page', 'eleventy', 'pkg', 'eleventyComputed',
59
+ 'resolved', 'permalink', 'layout', 'tags', 'pagination', 'site', 'assetManifest',
60
+ 'paginator', 'pageAssets', 'layoutAssets', 'jekyll', 'sectionLibrary',
61
+ ]);
62
+
63
+ // Build-fact keys that do NOT seed `resolved` (bulk/runtime values templates
64
+ // read via site.* directly — mirrors inject-properties.rb's config exclusions;
65
+ // seeding the site collection arrays would make every page's resolved walk all
66
+ // 1,030 post docs). Every SITE COLLECTION joins them per build — the built-ins
67
+ // and the brand's own alike (SITE_COLLECTIONS below, #593).
68
+ const RESOLVED_SITE_EXCLUDE = new Set(['data', 'omega', 'time']);
69
+
70
+ // Consumer PAGE frontmatter is meta-only (Ian's rule, 2026-07-19: content
71
+ // lives in {% section %} calls — and nothing may even TRY to consume it from
72
+ // frontmatter). Softened same day (Ian: no build-fail): content keys in a
73
+ // page's own frontmatter are STRIPPED from the data cascade with a warning
74
+ // before resolution — sections/components can never see them, and the build
75
+ // proceeds. Plumbing keys (layout, permalink,
76
+ // tags, pagination — the RESOLVED_OMIT set) are filtered before this check;
77
+ // collections (_posts/_team/…) are content ENTRIES whose frontmatter IS the
78
+ // document, and layouts are theme voice that never renders standalone —
79
+ // neither passes through the guard.
80
+ // `config` is the page's omega.json5 override block (#607) — every config
81
+ // section a page restates lives under it, and nothing else may: what is in
82
+ // the config file goes under `config:`, what is not may not. Every other key
83
+ // here is PAGE machinery, which is why it has no config home: `meta` (the
84
+ // head walk, the only meta there is) and `schema` (JSON-LD SEO) are the
85
+ // page's own SEO surface, and `redirect` (the modules/utilities/redirect
86
+ // layout's target — docs/web/index.md) is layout MACHINERY a page configures
87
+ // the same way: shipped contracts that worked only from defaults/ and
88
+ // _layouts/ until #247 — a consumer page lost them silently.
89
+ // `sitemap` is NOT here: #564 folded `sitemap.include` into `meta.index`, so
90
+ // there is no longer a way to be indexable and out of the sitemap.
91
+ const PAGE_FRONTMATTER_ALLOW = new Set([
92
+ 'config', 'meta',
93
+ 'schema', 'redirect', 'templateEngineOverride', 'eleventyExcludeFromCollections',
94
+ ]);
95
+
96
+ // Deep merge shared with the section tag's defaults ← data ← args chain —
97
+ // one semantics for both override lanes (see src/merge.js).
98
+ const { deepMerge } = require('./merge.js');
99
+
100
+ // Dev surfaces never ship (#554, Ian 2026-08-24): every page the framework
101
+ // publishes under /test — the index, the styleguide, the library harnesses,
102
+ // the translation page — is a DEVELOPMENT surface, and so is a consumer page
103
+ // that claims one of those URLs. A production build emits none of them, the
104
+ // same omission the showcase gallery already rides. The boundary is a path
105
+ // SEGMENT, not a bare prefix: /testimonials is brand content.
106
+ const DEV_ONLY_URL_RE = /^\/test(\/|\.|$)/;
107
+
108
+ /**
109
+ * The first dead config read in a CONFIG VALUE
110
+ * ([#671](https://github.com/Omega-JS-Stack/omega/issues/671)). The per-template
111
+ * census cannot see this one: `targets.web.tagline: "Agency - {{ site.brand.name }}"`
112
+ * lives in omega.json5, renders through the same Liquid, and has rendered the
113
+ * brand name EMPTY since #611 on every page that falls through to the default.
114
+ * Same census, same section list — the address is a config key path instead of
115
+ * a file and line.
116
+ * @param {*} node - a config value (walked recursively)
117
+ * @param {string[]} [trail] - the key path to this node
118
+ * @returns {{ path: string, read: object }|null}
119
+ */
120
+ function deadConfigValueRead(node, trail = []) {
121
+ if (typeof node === 'string') {
122
+ const read = templateReads(node).find((entry) => entry.root === 'site' && DEAD_SITE_SECTIONS.has(entry.key));
123
+ return read ? { path: trail.join('.'), read } : null;
124
+ }
125
+ if (!node || typeof node !== 'object') return null;
126
+ for (const [key, value] of Object.entries(node)) {
127
+ const found = deadConfigValueRead(value, [...trail, key]);
128
+ if (found) return found;
129
+ }
130
+ return null;
131
+ }
132
+
133
+ /**
134
+ * Configure an Eleventy instance as an OMEGA web engine.
135
+ * @param {object} eleventyConfig
136
+ * @param {object} options
137
+ * @param {string} options.consumerDir - the consumer site — Eleventy input dir
138
+ * @param {object} options.siteData - raw site data (site-data.json shape / resolved omega config)
139
+ * @param {string} [options.themesDir] - directory containing theme layer dirs (default: packaged themes)
140
+ * @param {string} [options.coreDir] - the framework core layer (default: packaged core)
141
+ * @param {string} [options.defaultsDir] - framework default pages dir (default: packaged defaults)
142
+ * @param {string} [options.activeTheme] - theme id (default: siteData.theme.id)
143
+ * @param {string} [options.layoutMode] - 'virtual' (default) or 'farm'
144
+ * @param {string} [options.farmDir] - symlink-farm target (farm mode)
145
+ * @param {object} [options.assetManifest] - js/css manifest from the asset build
146
+ * @param {string} [options.version] - the website target's own package version (site.omega.version → the Configuration block)
147
+ * @param {string} [options.pathPrefix] - the base path the built site is served under (#355) — default the domain root
148
+ * @param {string} [options.sampleAnchor] - YYYY-MM-DD rolling-date anchor for sample content (default: OMEGA_SAMPLE_ANCHOR env, then today)
149
+ * @param {object} [options.license] - the deploy-time license stamp (#320) → site.license (default: the keyless stamp)
150
+ * @returns {object} internals exposed for tests ({ site, layers, frontmatter })
151
+ */
152
+ function configureOmega(eleventyConfig, options) {
153
+ // The capture scope (#200): every config-time read below goes through
154
+ // @omega.js/devkit/reads, and each one records the dir it touched. `omega dev` arms a
155
+ // handler before this runs and registers the recorded union as its
156
+ // config-reset watch targets when the scope closes on the way out — so a
157
+ // capture added here can never drift out of the watch set. Other callers
158
+ // (`omega build`, tests) open a scope nobody consumes: recording is cheap.
159
+ reads.openScope({ consumerDir: options.consumerDir });
160
+ try {
161
+ return buildConfig(eleventyConfig, options);
162
+ } finally {
163
+ // Config-time reading is over: hand the recorded union to whoever armed a
164
+ // handler (the dev loop's watch registration — src/commands/dev.js). In a
165
+ // FINALLY because a config build that throws (a bad section.json5, a
166
+ // broken omega.json5) must still register what it read: those very dirs
167
+ // are where the fix lands, and an unregistered union means the fix needs a
168
+ // restart to be seen.
169
+ reads.closeScope();
170
+ }
171
+ }
172
+
173
+ /**
174
+ * The config build itself — everything between opening and closing the capture
175
+ * scope (configureOmega above owns that lifecycle).
176
+ * @param {object} eleventyConfig
177
+ * @param {object} options - configureOmega's options
178
+ * @returns {object} internals exposed for tests ({ site, layers, frontmatter })
179
+ */
180
+ function buildConfig(eleventyConfig, options) {
181
+ // The environment (#717): read ONCE, from the one surface every OMEGA
182
+ // framework answers with. `options.environment` is still the deliberate
183
+ // override the verbs thread (`omega build` → production) — it just arrives
184
+ // through getEnvironment() now instead of being compared as a loose string.
185
+ const environment = getEnvironment.call(options);
186
+ const themesDir = options.themesDir || PATHS.themes;
187
+ const coreDir = options.coreDir || PATHS.core;
188
+ const defaultsDir = options.defaultsDir || PATHS.defaults;
189
+ // The packaged defaults tree is a config-time input WHOLE (#136): default
190
+ // pages, the showcase and the sample corpora are all read out of it below,
191
+ // and probing the root records the tree as ONE dependency — nothing under it
192
+ // rides the incremental path, so a defaults reader added later is watched
193
+ // whether or not it reads through a dir this build touched.
194
+ reads.dirExists(defaultsDir);
195
+ // The two namespaces (#607): `config` is the brand's omega.json5 for this
196
+ // target, which every template reads through `resolved.config.*` (with the
197
+ // page's own `config:` block merged on top); `site` is BUILD FACTS — what
198
+ // the build knows and the config cannot say. The curated targets view is
199
+ // the one thing toSiteGlobal derives that IS a build fact, so it moves
200
+ // across; src/config-sections.js names both sets.
201
+ const config = toSiteGlobal(options.siteData);
202
+ const site = { targets: config.targets };
203
+ delete config.targets;
204
+
205
+ // The config-VALUE half of the #611 read guard (#671), before anything reads
206
+ // one: a dead read here is ONE key in omega.json5 and it silently drains the
207
+ // brand name out of every page's <title>. The curated targets view is gone
208
+ // by now — it is a build fact, not a config value.
209
+ const deadValue = deadConfigValueRead(config);
210
+ if (deadValue) {
211
+ throw new Error(
212
+ `[@omega.js/web:engine] config \`${deadValue.path}\` reads \`${deadValue.read.expression}\` — the brand config left `
213
+ + `the \`site\` global, which is BUILD FACTS only (${SITE_FACT_KEYS.join(', ')}, plus the collections `
214
+ + `targets.web.collections declares). Spell it `
215
+ + `\`${deadValue.read.expression.replace(/^site\./, 'resolved.config.')}\` in omega.json5. `
216
+ + 'Run `omega migrate` to rewrite it (docs/web/index.md).',
217
+ );
218
+ }
219
+
220
+ const activeTheme = options.activeTheme || (config.theme && config.theme.id) || 'classy';
221
+ const layoutMode = options.layoutMode || 'virtual';
222
+
223
+ // Reflect the active theme into the config templates render against
224
+ config.theme = { ...(config.theme || {}), id: activeTheme };
225
+
226
+ // ---- The brand's own collections (#207): validated HERE, before anything
227
+ // reads them, so a bad entry fails the config build instead of quietly
228
+ // generating nothing. Built-ins plus these are the whole collection roster —
229
+ // directory tagging, permalinks, dev sampling and the generated pages below
230
+ // all read this one list.
231
+ const dynamicCollections = readCollections(config.collections);
232
+ const allCollections = [...BUILT_IN_COLLECTIONS, ...dynamicCollections];
233
+
234
+ // ---- Runtime composition: omega.json5 keeps ONE home per shared section
235
+ // (cloud, payment at the top level); the chrome + @omega.js/client contract
236
+ // reads them through resolved.config.client — the client settings blob (#1:
237
+ // renamed from the legacy `web_manager`; WebManager is not an OMEGA concept).
238
+ // Pricing loops over config.client.payment.products and the Configuration
239
+ // spread feeds the client. Compose here — same bridge pattern as extension's
240
+ // package.js mapping analytics.providers → the client's flat shape.
241
+ config.client = config.client || {};
242
+ if (config.cloud && config.cloud.config) {
243
+ config.client.firebase = config.client.firebase || {};
244
+ config.client.firebase.app = config.client.firebase.app || {};
245
+ config.client.firebase.app.config = config.cloud.config;
246
+ }
247
+ if (config.cloud && config.cloud.messaging && config.cloud.messaging.vapidKey) {
248
+ config.client.firebase = config.client.firebase || {};
249
+ config.client.firebase.messaging = config.client.firebase.messaging || {};
250
+ config.client.firebase.messaging.config = config.client.firebase.messaging.config || {};
251
+ config.client.firebase.messaging.config.vapidKey = config.cloud.messaging.vapidKey;
252
+ }
253
+ // The cancel-flow save offer (#268) is RESOLVED here, not in the browser: the
254
+ // framework default (50% off the next cycle) has ONE home in @omega.js/config,
255
+ // and the backend's apply route resolves the same section through the same
256
+ // function — so the dialog the customer reads and the coupon the provider
257
+ // creates can never name different numbers. The spread leaves config.payment
258
+ // itself alone; templates and the pricing composer read the brand's section.
259
+ if (config.payment) config.client.payment = { ...config.payment, winback: resolveWinbackOffer(config.payment) };
260
+
261
+ // The FEATURES CATALOG rides the same bridge (#647): the account page's usage
262
+ // bars and plan bullets read names, icons and definitions from it, and a
263
+ // feature is DEFINED in exactly one place for the server and the browser both.
264
+ if (config.features) config.client.features = config.features;
265
+
266
+ // Pricing view-model (C2): payment.products is the ONLY plan source — the
267
+ // seed surfaces as resolved.pricing (cascade still lets consumer frontmatter
268
+ // override presentation). A composed VIEW of the config, not config itself,
269
+ // so it rides the build-fact global. null = honest empty state.
270
+ site.pricing = composePricing(config.payment, config.features);
271
+
272
+ // Brand accent ramp (C3/D6): brand.color → the --omega-accent-* family,
273
+ // emitted by head.html after the CSS bundles. null (no/invalid color) =
274
+ // the token sheet's neutral placeholder stands.
275
+ site.brandTokens = composeBrandTokens(config.brand?.color);
276
+
277
+ // The license verdict ([#320](https://github.com/Omega-JS-Stack/omega/issues/320)):
278
+ // a BUILD fact — the production build's one deploy-time check, whose
279
+ // `attribution` decides the footer's "Powered by omegajs.dev" block. Nothing
280
+ // handed in (dev, a test, any lane that runs no check) is the keyless stamp,
281
+ // which is the state every build ships in today.
282
+ site.license = options.license || KEYLESS_STAMP;
283
+
284
+ // ---- Theme layer chain: active theme → base → core
285
+ // (consumer-local themes/<id> beats the packaged theme — C3 tier 2)
286
+ const themeLayers = resolveThemeLayers({ activeTheme, consumerDir: options.consumerDir, themesDir });
287
+ const layers = [...themeLayers, coreDir];
288
+
289
+ // ---- LiquidJS: Jekyll include syntax + layered include roots.
290
+ // timezoneOffset 0: filename dates are UTC midnights; rendering them in UTC
291
+ // matches CI-built Jekyll output (Actions runners are UTC).
292
+ // Consumer _includes first (somiibo's index includes
293
+ // frontend/components/hero-demo.html from its own _includes), then layers.
294
+ // EXISTING dirs only — LiquidJS probes every root per include lookup, and
295
+ // a nonexistent root costs ~1s over the corpus (measured: 3.60→4.65s).
296
+ const includeRoots = [path.join(options.consumerDir, '_includes'), ...layers.map((layer) => path.join(layer, '_includes'))]
297
+ .filter((dir) => reads.dirExists(dir));
298
+ eleventyConfig.setLiquidOptions({
299
+ jekyllInclude: true,
300
+ root: includeRoots,
301
+ timezoneOffset: 0,
302
+ // Parsed-template cache for includes: without it LiquidJS re-reads and
303
+ // re-parses every {% include %} on every render — the real core chrome
304
+ // (head/body/foot ≈ 885 lines) made that the corpus bottleneck
305
+ // (40.6s → measured again with cache below). Keyed by resolved file
306
+ // path, so theme switches (different winning path) stay correct.
307
+ cache: true,
308
+ });
309
+
310
+ // ---- Jekyll site-data emulation.
311
+ // site.data._includes.<path> mirrors UJM's json-in-_includes data system
312
+ // (admin sidebar/topbar read site.data._includes.admin.sections.sidebar):
313
+ // every .json under a layered include root lands at its path, higher
314
+ // layers win.
315
+ const dataIncludes = {};
316
+ for (const root of [...includeRoots].reverse()) {
317
+ for (const entry of reads.readdir(root, { recursive: true, withFileTypes: true })) {
318
+ if (!entry.isFile() || !entry.name.endsWith('.json')) continue;
319
+ const rel = path.relative(root, path.join(entry.parentPath, entry.name));
320
+ const segments = rel.replace(/\.json$/, '').split(path.sep);
321
+ let node = dataIncludes;
322
+ for (const segment of segments.slice(0, -1)) node = node[segment] = node[segment] || {};
323
+ try {
324
+ // JSON5 — the packaged section files (nav/footer/sidebar) use
325
+ // unquoted keys, comments, and trailing commas
326
+ node[segments[segments.length - 1]] = JSON5.parse(reads.read(path.join(root, rel)));
327
+ } catch { /* malformed data file — leave the slot empty */ }
328
+ }
329
+ }
330
+ site.data = { ...(config.data || {}), _includes: dataIncludes };
331
+
332
+
333
+ // ---- template-kit on Eleventy's own Liquid instance
334
+ // This instance backs template-kit's `markdown` filter; Eleventy keeps its
335
+ // own for .md templates. BOTH get the optimized image renderer (#193) so a
336
+ // markdown image is the same markup wherever the markdown is rendered, and
337
+ // BOTH run the typographer (#547): Kramdown smartened quotes, apostrophes,
338
+ // dashes and ellipses at build time, so a brand converted off Jekyll drifts
339
+ // into straight punctuation on its very next build without it (HARD RULE 5 —
340
+ // preserve semantics). A hard default, no consumer knob.
341
+ const md = markdownIt({ html: true, typographer: true });
342
+ applyMarkdownImages(md);
343
+ eleventyConfig.amendLibrary('md', (mdLib) => {
344
+ mdLib.set({ typographer: true });
345
+ applyMarkdownImages(mdLib);
346
+ });
347
+ const collectionsHolder = new Map();
348
+
349
+ // site.posts / site.team / site.updates / site.alternatives are Jekyll's
350
+ // site collections, flattened to Jekyll doc shape (post.url, post.date,
351
+ // post.post.title). Real arrays SYNCED when the holder fills — mutated in
352
+ // place, never reassigned: the site object is captured into Eleventy's
353
+ // data cascade at data-init (before collections compute), so replacing the
354
+ // array (or lazy getters) after that is invisible to templates.
355
+ //
356
+ // A BRAND's own collection (#207) publishes the same way (#593): a UJM
357
+ // consumer's `{% for product in site.products %}` is how a declared
358
+ // collection is read, and reaching templates only as `collections.products`
359
+ // (frontmatter under `.data`) rendered that loop empty on a green build —
360
+ // no warning, no output (HARD RULE 5, preserve semantics).
361
+ const SITE_COLLECTIONS = allCollections.map((collection) => collection.name);
362
+ for (const name of SITE_COLLECTIONS) site[name] = [];
363
+ // A collection array is a build fact templates read off `site` directly,
364
+ // never a `resolved` key — the brand's own on exactly the built-ins' terms.
365
+ const resolvedSiteExclude = new Set([...RESOLVED_SITE_EXCLUDE, ...SITE_COLLECTIONS]);
366
+ const holderSet = collectionsHolder.set.bind(collectionsHolder);
367
+ collectionsHolder.set = (name, docs) => {
368
+ if (SITE_COLLECTIONS.includes(name)) {
369
+ site[name].length = 0;
370
+ // ONE flattener for both lanes (#711): `jekyllDoc` gives a site doc the
371
+ // same lazy `content` the paginator's docs carry, so a layout reading
372
+ // `post.content` off site.posts counts the post's real body instead of
373
+ // an empty string (the newsflash homepage printed "1 min read" for every
374
+ // cover story and tile). id rides along inside it (Jekyll doc parity —
375
+ // layouts pass member.id/post.id to the omega_member/omega_post tags).
376
+ site[name].push(...docs.map((doc) => jekyllDoc(doc)));
377
+ }
378
+ return holderSet(name, docs);
379
+ };
380
+
381
+ // Icon roots (C4 cp108/cp111): the curated core set wins, then the
382
+ // brand's own Font Awesome set when one is supplied (Pro npm install or
383
+ // OMEGA_FONTAWESOME_ROOT download dir), with the free npm set (resolved,
384
+ // never vendored) as the always-present floor; the richest metadata
385
+ // resolves legacy aliases (search → magnifying-glass).
386
+ const fa = resolveFontAwesomeRoots();
387
+
388
+ // The resolved library with meta — the showcase/docs data source (spec §9).
389
+ // Registered unconditionally (deterministic data model, ~20 json5 reads);
390
+ // only the PAGES consuming it are development-gated below.
391
+ eleventyConfig.addGlobalData('sectionLibrary', buildSectionLibrary({
392
+ baseDirs: [options.consumerDir, ...themeLayers],
393
+ consumerDir: options.consumerDir,
394
+ }));
395
+
396
+ // The hero animation folders (#441): probed through the captured-read helper
397
+ // so a `_hero/` dir appearing mid-session resets the config, exactly like the
398
+ // section walk's own probes. The tag registered below reads them at render
399
+ // time, which is why the probe has to be its own line here.
400
+ watchHeroAnimations([options.consumerDir, ...themeLayers]);
401
+
402
+ eleventyConfig.amendLibrary('liquid', (engine) => {
403
+ // The section/component library tags resolve through the same precedence
404
+ // as every other layer: consumer-local _sections/_components → active
405
+ // theme → base (docs/web/omega-sections-spec.md).
406
+ // …and with them `{% hero_animation %}` (#441): the hero's custom demo
407
+ // branch calls it, and section markup is context-free by contract, so a TAG
408
+ // is how the folder's markup reaches the section at all.
409
+ registerSectionTags(engine, { baseDirs: [options.consumerDir, ...themeLayers] });
410
+ registerLiquid(engine, {
411
+ // template-kit's `site` slot is the CONFIG it reads (url, baseurl,
412
+ // icons.style, translation) — `relative_url`/`absolute_url` and every
413
+ // tag's ctx.site.config. The build-fact global is not its business.
414
+ site: config,
415
+ getCollection: (name) => collectionsHolder.get(name) || [],
416
+ getCollectionNames: () => [...collectionsHolder.keys()],
417
+ fileExists: (file) => fs.existsSync(path.join(options.consumerDir, file)),
418
+ markdown: (content) => md.render(content),
419
+ logos: { dir: path.join(coreDir, 'logos') },
420
+ });
421
+ });
422
+
423
+ // ONE slugifier (#488): Eleventy ships a universal `slugify` filter of its
424
+ // own, and it OUTRANKS the template-kit one amendLibrary registers (the
425
+ // Liquid engine applies universal filters after the amendments) — so a term
426
+ // containing `&` was linked at `a-and-r` while the taxonomy page it pointed
427
+ // at was generated at `a-r` (src/collections.js slugifies with template-kit).
428
+ // Every term with an `&` shipped a guaranteed dead link. template-kit's is
429
+ // the authority — Jekyll-parity, so a migrating brand's taxonomy URLs never
430
+ // move — and a PLUGIN is where it has to be claimed: this config callback
431
+ // runs BEFORE Eleventy's own defaults, plugins run after.
432
+ eleventyConfig.addPlugin((config) => config.addFilter('slugify', slugify));
433
+
434
+ // ---- Layered layouts (zero copying): virtual templates or symlink farm.
435
+ // Layer order: consumer-local _layouts (sweet-saucy ships src/_layouts/
436
+ // recipe.html) → active theme → base → core (blueprint/root/modules —
437
+ // the theme-agnostic framework layouts live in the core layer).
438
+ // The farm must live OUTSIDE the input dir — inside it, Eleventy processes
439
+ // the symlinked layouts as content templates (and `../`-relative inputs
440
+ // defeat ignore globs).
441
+ const layoutMap = collectLayered([
442
+ path.join(options.consumerDir, '_layouts'),
443
+ ...themeLayers.map((layer) => path.join(layer, '_layouts')),
444
+ path.join(coreDir, '_layouts'),
445
+ ]);
446
+ // Consumer _layouts live INSIDE the input dir — without an ignore, Eleventy
447
+ // would process them as content templates (each writing to /index.html).
448
+ // Eleventy matches ignores against CWD-RELATIVE paths (`../corpus/...` for
449
+ // `../`-relative input dirs — the known farm gotcha), so the glob must be
450
+ // built the same way; the bare `**/` form covers cwd-contained inputs.
451
+ eleventyConfig.ignores.add('**/_layouts/**');
452
+ eleventyConfig.ignores.add(path.join(path.relative(process.cwd(), options.consumerDir), '_layouts', '**'));
453
+ // Consumer-local section/component/hero-animation folders are template
454
+ // machinery too — without the ignore, Eleventy would emit every section.html
455
+ // (and every `_hero/<name>/index.html`, #441) as content.
456
+ for (const machineryDir of ['_sections', '_components', HERO_DIR]) {
457
+ eleventyConfig.ignores.add(`**/${machineryDir}/**`);
458
+ eleventyConfig.ignores.add(path.join(path.relative(process.cwd(), options.consumerDir), machineryDir, '**'));
459
+ }
460
+ // A tier-2 theme lives INSIDE the input dir (`<src>/themes/<id>/`), and
461
+ // everything under it is LAYER SOURCE — never content. The families above
462
+ // covered a consumer's own machinery dirs but not a theme's copies of them,
463
+ // so a consumer-local theme shipping an include override failed the build on
464
+ // a duplicate permalink, and an INACTIVE local theme did it without even
465
+ // being in the chain ([#773](https://github.com/Omega-JS-Stack/omega/issues/773)).
466
+ // The whole namespace is the rule, because that is what it means: pages live
467
+ // in `pages/`, and `resolveThemeLayers` probes exactly this directory.
468
+ eleventyConfig.ignores.add(path.join(path.relative(process.cwd(), options.consumerDir), 'themes', '**'));
469
+ // The cwd-relative twin, kept to the machinery families rather than the whole
470
+ // namespace: unanchored, `**/themes/**` would also swallow a brand's own page
471
+ // at `pages/themes/…`, and no page is ever called `_includes`.
472
+ for (const machineryDir of ['_includes', '_layouts', '_sections', '_components', HERO_DIR]) {
473
+ eleventyConfig.ignores.add(`**/themes/*/${machineryDir}/**`);
474
+ }
475
+ if (layoutMode === 'farm') {
476
+ composeSymlinkFarm(layoutMap, options.farmDir);
477
+ eleventyConfig.setIncludesDirectory(path.relative(options.consumerDir, options.farmDir));
478
+ } else {
479
+ registerVirtualLayouts(eleventyConfig, layoutMap);
480
+ }
481
+
482
+ // The layout CHAIN behind a page, for the layout-keyed asset lane (#624).
483
+ // Eleventy merges a layout's frontmatter into the cascade but `data.layout`
484
+ // stays the page's OWN declaration, so the chain is walked here from the same
485
+ // layered map the render resolves through: `blueprint/careers` →
486
+ // `modules/utilities/redirect` → `frontend/core/cover`, and every layout in
487
+ // it contributes its assets.
488
+ // The one frontmatter fence, read by both hand-parsers below (the layout
489
+ // chain here, the page's own frontmatter further down).
490
+ const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---/;
491
+ const layoutFiles = new Map();
492
+ for (const [rel, abs] of layoutMap) layoutFiles.set(rel.replace(/\.[^.]+$/, '').split(path.sep).join('/'), abs);
493
+ const layoutParent = new Map();
494
+ const parentOf = (name) => {
495
+ if (layoutParent.has(name)) return layoutParent.get(name);
496
+ let parent = null;
497
+ const file = layoutFiles.get(name);
498
+ if (file) {
499
+ const match = (reads.read(file) || '').match(FRONTMATTER_RE);
500
+ try {
501
+ const own = match ? yaml.load(match[1]) : null;
502
+ if (own && typeof own.layout === 'string') parent = own.layout;
503
+ } catch { /* exotic frontmatter — the chain simply ends here */ }
504
+ }
505
+ layoutParent.set(name, parent);
506
+ return parent;
507
+ };
508
+ // Outermost FIRST (the root layout, then each layout that renders inside it),
509
+ // so assets load general → specific exactly as the layer chain does.
510
+ const layoutChain = (layout) => {
511
+ const chain = [];
512
+ let name = typeof layout === 'string' ? layout : null;
513
+ while (name && !chain.includes(name)) {
514
+ chain.unshift(name);
515
+ name = parentOf(name);
516
+ }
517
+ return chain;
518
+ };
519
+
520
+ // ---- Frontmatter Liquid + collection tagging
521
+ const frontmatter = createFrontmatterResolver({ site });
522
+ // The page's pagination ALIAS (#544): `pagination.alias` is a declaration in
523
+ // the very cascade being resolved, so a frontmatter value naming the alias is
524
+ // per-page BY DEFINITION — it defers to the `resolved` pass exactly like a
525
+ // `resolved.`/`page.` ref, where the alias binding exists. Without this the
526
+ // value rendered empty at cache time and cached it, and every generated page
527
+ // shipped the same nameless title (280 of them, found porting trusteroo).
528
+ const aliasRoots = (data) => {
529
+ const alias = data.pagination && data.pagination.alias;
530
+ return alias ? new Set([alias]) : undefined;
531
+ };
532
+ // The alias BINDING, for the two passes that render with a per-page scope.
533
+ const aliasScope = (data) => {
534
+ const alias = data.pagination && data.pagination.alias;
535
+ return alias ? { [alias]: data[alias] } : undefined;
536
+ };
537
+ // The template's OWN frontmatter, re-parsed from the source file — no
538
+ // Eleventy hook sees template data apart from the cascade (preprocessors
539
+ // already get the merged tree). Two consumers: the meta-only PAGE guard
540
+ // below, and the collections parity repair in `resolved` — Eleventy's
541
+ // cascade merge breaks the inject-properties.rb contract for content
542
+ // ENTRIES (it CONCATS a doc array onto a layout-default array, and a
543
+ // layout-default object beats a doc scalar), so `resolved` re-applies a
544
+ // doc's own keys with OUR deepMerge. Values are raw (un-liquified) —
545
+ // resolved's renderData pass liquifies them with the same resolver.
546
+ // Virtual templates (blueprints, sample content) have no file to parse
547
+ // and keep pure cascade behavior; ANY read/parse failure degrades the
548
+ // same way.
549
+ const pageOwnData = new Map();
550
+ const pageOwnSource = new Map();
551
+ const readOwnSource = (inputPath) => {
552
+ if (pageOwnSource.has(inputPath)) return pageOwnSource.get(inputPath);
553
+ let source = null;
554
+ try {
555
+ source = fs.readFileSync(path.resolve(inputPath), 'utf8');
556
+ } catch { /* virtual template — there is no file behind it */ }
557
+ pageOwnSource.set(inputPath, source);
558
+ return source;
559
+ };
560
+ const readOwnFrontmatter = (inputPath) => {
561
+ if (pageOwnData.has(inputPath)) return pageOwnData.get(inputPath);
562
+ let own = null;
563
+ try {
564
+ const match = (readOwnSource(inputPath) || '').match(FRONTMATTER_RE);
565
+ const parsed = match ? yaml.load(match[1]) : null;
566
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
567
+ own = {};
568
+ for (const key of Object.keys(parsed)) {
569
+ if (!RESOLVED_OMIT.has(key)) own[key] = parsed[key];
570
+ }
571
+ }
572
+ } catch { /* virtual template or exotic frontmatter — cascade behavior stands */ }
573
+ pageOwnData.set(inputPath, own);
574
+ return own;
575
+ };
576
+
577
+ // The template's own SIDECAR data file (`<template>.11tydata.*`), re-read
578
+ // for the same reason (#269): page frontmatter is meta-only, so the sidecar
579
+ // is the page-level lane for a SHELL layout's band data — and Eleventy's
580
+ // cascade CONCATS a sidecar array onto the layout default, so a page could
581
+ // only append to a band, never replace it. `resolved` re-applies the sidecar
582
+ // with OUR deepMerge, the arrays-replace rule every other override lane
583
+ // obeys; object/string keys land exactly where the cascade already put them.
584
+ // EVERY sidecar form Eleventy reads synchronously rides the lane (#543):
585
+ // .js/.cjs first, .json last, the order Eleventy itself merges them in, so
586
+ // one contract holds no matter which extension a page reached for (found
587
+ // porting trusteroo: the identical data as .11tydata.js still concatenated a
588
+ // 6-item faqs.items onto the layout default). `.mjs` is async-only and stays
589
+ // on pure cascade behavior, as a missing file — the common case — does.
590
+ const SIDECAR_EXTENSIONS = ['.js', '.cjs', '.json'];
591
+ const pageSidecarData = new Map();
592
+ const sidecarPath = (inputPath, extension) => {
593
+ const file = path.resolve(inputPath);
594
+ return path.join(path.dirname(file), `${path.basename(file, path.extname(file))}.11tydata${extension}`);
595
+ };
596
+ // A module sidecar is `require`d, so its exports outlive a config reset —
597
+ // busting the entry keeps a dev edit from serving the boot-time value.
598
+ const readSidecarFile = (file) => {
599
+ if (!file.endsWith('.json')) {
600
+ delete require.cache[require.resolve(file)];
601
+ const loaded = require(file);
602
+ return typeof loaded === 'function' ? loaded() : loaded;
603
+ }
604
+ return JSON.parse(fs.readFileSync(file, 'utf8'));
605
+ };
606
+ const readSidecarData = (inputPath) => {
607
+ if (pageSidecarData.has(inputPath)) return pageSidecarData.get(inputPath);
608
+ let sidecar = null;
609
+ for (const extension of SIDECAR_EXTENSIONS) {
610
+ try {
611
+ const parsed = readSidecarFile(sidecarPath(inputPath, extension));
612
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
613
+ sidecar = sidecar || {};
614
+ for (const key of Object.keys(parsed)) {
615
+ if (!RESOLVED_OMIT.has(key)) sidecar[key] = deepMerge(sidecar[key], parsed[key]);
616
+ }
617
+ }
618
+ } catch { /* no sidecar at this extension, or a malformed one — cascade behavior stands */ }
619
+ }
620
+ pageSidecarData.set(inputPath, sidecar);
621
+ return sidecar;
622
+ };
623
+
624
+ // Is a frontmatter key a CONFIG section (#607)? Every section the schema
625
+ // declares for a web build, plus whatever this brand's own omega.json5
626
+ // carries — a brand key with no schema rule yet is still config, and
627
+ // restating it bare would reach nothing at all. The SAME question decides
628
+ // both directions of the namespace rule (Ian 2026-08-26): a section spelled
629
+ // bare is an error, and a key under `config:` that is not one is an error.
630
+ const isConfigSection = (key) => CONFIG_SECTIONS.has(key) || Object.hasOwn(config, key);
631
+
632
+ // The READ half of the same seam (#611). A config key restated bare in
633
+ // frontmatter throws above; a config key READ off the `site` global does not
634
+ // fail at all — it renders an empty string, silently, on every page carrying
635
+ // one. So the page's own source is censused and the first sighting stops the
636
+ // build with the file and the expression. The census runs over the WHOLE
637
+ // file: `{{ site.brand.name }}` inside a frontmatter value is the same dead
638
+ // read as one in the body, and the playground's own pages hid theirs there.
639
+ //
640
+ // The section list is `CONFIG_SECTIONS` EXACTLY — never `isConfigSection`'s
641
+ // `hasOwn(config, key)` widening, which is right for the frontmatter lane and
642
+ // wrong here (the blind-verifier walk, 2026-08-25). `toSiteGlobal` always
643
+ // derives a top-level `url`, and a brand may override `omega`/`characters` in
644
+ // its own omega.json5; under the widening every one of those became "config",
645
+ // so `{{ site.url }}` — the canonical idiom `omega migrate` rule 7 itself
646
+ // writes — failed the build with no way to comply. A read guard's false
647
+ // positive is a build a brand cannot fix, so it answers to the schema alone.
648
+ //
649
+ // What COUNTS as a read (Liquid context, no fenced code, no `{% raw %}`) is
650
+ // config-sections.js's `templateReads` — the same census `omega migrate`'s
651
+ // rule 24 rewrites from, so the guard can never fail on something the verb
652
+ // would not have fixed.
653
+ // `meta` IS flagged here even though it is not config at all: a page spells
654
+ // `meta:` bare, but `site.meta` is as dead as any config read (the walk
655
+ // lives at `resolved.meta`), and rule 24 rewrites it — so the guard may
656
+ // demand it. DEAD_SITE_SECTIONS is that list, config sections plus `meta`.
657
+ const configReadsIn = (source) => templateReads(source)
658
+ .filter((read) => read.root === 'site' && DEAD_SITE_SECTIONS.has(read.key));
659
+
660
+ eleventyConfig.addPreprocessor('omega-frontmatter', 'md,html,liquid', (data) => {
661
+ const inputPath = data.page.inputPath;
662
+ const ownSource = readOwnSource(inputPath) || '';
663
+
664
+ // The OTHER dead read the census owns (#595): UJM's per-render `random_id`
665
+ // global, which OMEGA does not have — the read renders empty and every id
666
+ // it scopes collides. A WARNING, not a throw, unlike the config reads
667
+ // above: `random_id` is an ordinary variable name, so a layout or an
668
+ // include may legitimately assign it for this page, and a read guard's
669
+ // false positive is a build a brand cannot fix.
670
+ if (!assignsRandomId(ownSource)) {
671
+ const leftover = randomIdReads(ownSource);
672
+ if (leftover.length) {
673
+ logger.warn(
674
+ `${inputPath}:${leftover[0].line}: reads a bare \`random_id\` — UJM's per-render global is gone, so it renders `
675
+ + `EMPTY and every id built from it collides. Assign it first: \`${RANDOM_ID_ASSIGN_IDIOM}\`. `
676
+ + 'Run `omega migrate` to write it (docs/web/index.md).',
677
+ );
678
+ }
679
+ }
680
+
681
+ // The dead-read guard (#611), over every template with a FILE behind it —
682
+ // a page, a collection document, a consumer's own anything. Virtual
683
+ // templates (blueprints, the showcase, sample content) have no source to
684
+ // read and the static lane already covers those surfaces.
685
+ const deadReads = configReadsIn(ownSource);
686
+ if (deadReads.length) {
687
+ const first = deadReads[0];
688
+ throw new Error(
689
+ `[@omega.js/web:engine] ${inputPath}:${first.line}: reads \`${first.expression}\` — the brand config left the `
690
+ + `\`site\` global, which is BUILD FACTS only (${SITE_FACT_KEYS.join(', ')}, plus the collections `
691
+ + `targets.web.collections declares). Spell it `
692
+ + `\`${first.expression.replace(/^site\./, 'resolved.config.')}\``
693
+ + `${deadReads.length > 1 ? ` (and ${deadReads.length - 1} more in this file)` : ''}. `
694
+ + 'Run `omega migrate` to rewrite them (docs/web/index.md).',
695
+ );
696
+ }
697
+
698
+ // Directory → collection tag, the Jekyll convention: a document under
699
+ // `_<collection>/` belongs to that collection, the brand's own included.
700
+ const collection = allCollections.find((entry) => inputPath.includes(`/${entry.dir}/`));
701
+ if (collection) data.tags = [collection.name];
702
+
703
+ // A BRAND collection's documents (#317): the built-ins ship their own
704
+ // blueprint layouts and meta, a declared collection's documents get theirs
705
+ // here — the collection block their layout renders from, and per-document
706
+ // meta. A document that falls back to the site's title and description is
707
+ // the indexable duplication #312 fixed one layer up.
708
+ if (collection && dynamicCollections.includes(collection)) {
709
+ applyDocumentData(collection, data, config.brand && config.brand.name);
710
+ }
711
+
712
+ // The OTHER half of the namespace rule (#607, Ian 2026-08-26): what is in
713
+ // the config file goes under `config:`, and what is NOT in the config file
714
+ // may not. A key under `config:` that no omega.json5 section answers to
715
+ // merges into `resolved.config` and is read by nothing — a typo (`them:`),
716
+ // or page data written into the config lane, both silent. It fails here,
717
+ // for every template that carries frontmatter: a collection document's
718
+ // `config:` block is the same block a page's is.
719
+ const ownFrontmatter = readOwnFrontmatter(inputPath) || {};
720
+ if ('config' in ownFrontmatter) {
721
+ const ownConfig = ownFrontmatter.config;
722
+
723
+ // Before the membership check: a `config:` that is not a MAP of sections
724
+ // at all. `resolved` deep-merges the page block OVER the seeded config,
725
+ // and a null (the empty key `config:` with nothing under it), an array or
726
+ // a scalar REPLACES it — so resolved.config stops being the brand config
727
+ // for that page and every read under it renders empty, silently. The
728
+ // shape is the brand's own typo, so it fails here naming what arrived.
729
+ if (!ownConfig || typeof ownConfig !== 'object' || Array.isArray(ownConfig)) {
730
+ const shape = ownConfig === null || ownConfig === undefined
731
+ ? 'an EMPTY key (null)'
732
+ : (Array.isArray(ownConfig) ? 'an array' : `a ${typeof ownConfig}`);
733
+ throw new Error(
734
+ `[@omega.js/web:engine] ${inputPath}: \`config:\` is ${shape} — it must be a MAP of omega.json5 `
735
+ + 'sections (config:\n theme:\n …). Any other shape REPLACES the whole merged config for this page, '
736
+ + 'so every `resolved.config.*` read on it renders empty. Delete the key, or put the sections under it '
737
+ + '(docs/web/frontmatter.md).',
738
+ );
739
+ }
740
+
741
+ const strays = Object.keys(ownConfig).filter((key) => !isConfigSection(key));
742
+ if (strays.length) {
743
+ throw new Error(
744
+ `[@omega.js/web:engine] ${inputPath}: \`config:\` carries `
745
+ + `${strays.length > 1 ? 'keys' : 'a key'} ${strays.map((key) => `\`${key}\``).join(', ')} that omega.json5 has no `
746
+ + `${strays.length > 1 ? 'sections' : 'section'} for, so nothing reads `
747
+ + `${strays.length > 1 ? 'them' : 'it'}. \`config:\` holds omega.json5 sections ONLY `
748
+ + `(${[...CONFIG_SECTIONS].join(', ')}); page machinery (meta, schema, redirect) stays bare, and page `
749
+ + 'CONTENT lives in {% section %} calls in the page body (docs/web/frontmatter.md).',
750
+ );
751
+ }
752
+ }
753
+
754
+ // The meta-only guard: real files under pages/ may carry ONLY meta keys
755
+ // in frontmatter. readOwnFrontmatter already filters the plumbing set
756
+ // (layout, permalink, tags, pagination…) and returns null for virtual
757
+ // templates (blueprints, showcase) — so anything left outside the allow
758
+ // set is content in frontmatter, a lane that doesn't exist: the keys are
759
+ // deleted from the cascade before resolution (sections/components never
760
+ // see them) and the build warns. The WHOLE key drops for this page — a
761
+ // directory-data value merged under the same key falls back to section
762
+ // defaults too; deleting the frontmatter key (what the warning says)
763
+ // restores it.
764
+ if (/\/pages\//.test(inputPath)) {
765
+ const own = readOwnFrontmatter(inputPath);
766
+ const ownKeys = own ? Object.keys(own).filter((key) => !PAGE_FRONTMATTER_ALLOW.has(key)) : [];
767
+
768
+ // A config section restated BARE is a build ERROR, not smuggled content
769
+ // (#607): the two namespaces are separate, so a bare `theme:` would look
770
+ // like it overrode omega.json5 and reach nothing. No backwards
771
+ // compatibility — `omega migrate` rewrites the page.
772
+ const bareConfig = ownKeys.filter((key) => isConfigSection(key));
773
+ if (bareConfig.length) {
774
+ throw new Error(
775
+ `[@omega.js/web:engine] ${inputPath}: frontmatter restates the config `
776
+ + `${bareConfig.length > 1 ? 'sections' : 'section'} ${bareConfig.map((key) => `\`${key}\``).join(', ')} bare. `
777
+ + 'A page overrides omega.json5 under a `config:` parent '
778
+ + `(config:\n ${bareConfig[0]}:\n …); no config section keeps a bare spelling. `
779
+ + 'Run `omega migrate` to move it (docs/web/frontmatter.md).',
780
+ );
781
+ }
782
+
783
+ const contentKeys = ownKeys.filter((key) => !isConfigSection(key));
784
+ if (contentKeys.length) {
785
+ for (const key of contentKeys) delete data[key];
786
+ logger.warn(
787
+ `${inputPath}: ignoring frontmatter content keys (${contentKeys.join(', ')}) — `
788
+ + `consumer page frontmatter is meta-only (layout, permalink, meta, schema, config); `
789
+ + `content lives in {% section %} calls in the page body (docs/web/sections.md).`,
790
+ );
791
+ }
792
+ }
793
+
794
+ // The cascade IS the frontmatter render scope (#542): a value reaching for
795
+ // one of the consumer's own `_data` globals (`{{ brands.size }}` beside a
796
+ // `_data/brands.json`) reads the same globals a template render reads,
797
+ // instead of rendering empty against a site-only scope. The page's own
798
+ // pagination alias defers with the per-page refs (#544).
799
+ frontmatter.resolveData(data, {
800
+ globals: data,
801
+ defer: aliasRoots(data),
802
+ file: inputPath,
803
+ });
804
+ });
805
+
806
+ // The meta-file lane's reader (#141): a page's cascade data may still hold
807
+ // RAW Liquid contributed by a LAYOUT (blueprint/updates/update sets
808
+ // `meta.title: "Version {{ page.update.version }} - {{ site.brand.name }}"`)
809
+ // — per-page refs defer at frontmatter time and render in that page's own
810
+ // `resolved` computed. sitemap.xml/pages.json/llms.txt read OTHER pages'
811
+ // data, so they read it through this filter: the value renders against THAT
812
+ // item's scope, exactly as the page's own head rendered it. Reading
813
+ // `item.data.resolved` directly would work too but change the fallbacks —
814
+ // resolved seeds the site sections, so every entry without its own title
815
+ // would inherit the SITE's meta.title instead of the template's default.
816
+ const dataAt = (data, dottedPath) => String(dottedPath).split('.')
817
+ .reduce((node, key) => (node == null ? node : node[key]), data);
818
+ eleventyConfig.addFilter('omega_rendered', (item, dottedPath) => {
819
+ const value = dataAt(item && item.data, dottedPath);
820
+ if (typeof value !== 'string' || !(value.includes('{{') || value.includes('{%'))) return value;
821
+ const resolved = item.data.resolved;
822
+ if (!resolved) return value; // no computed data (never in a real build) — raw beats crashing
823
+ // THAT item's scope, whole: its pagination alias binding (#544) and the
824
+ // `_data` globals its own cascade carries (#542) — a generated page's
825
+ // per-document title has to read the same here as in its own head.
826
+ return frontmatter.render(value, {
827
+ resolved,
828
+ page: { ...resolved, resolved, url: item.url, slug: item.data.page.fileSlug, fileSlug: item.data.page.fileSlug },
829
+ ...aliasScope(item.data),
830
+ }, { globals: item.data, file: item.data.page.inputPath });
831
+ });
832
+
833
+ // A body that composes sections is Liquid-HTML, not prose: rendered
834
+ // section markup must never pass through the markdown transform — blank
835
+ // lines inside emitted HTML blocks become empty <p> elements that land
836
+ // as REAL grid children (the 2026-07-19 live find: both brand homepages'
837
+ // bento/stats scattered by phantom <p>s). `omega customize` obeys the
838
+ // same rule by materializing .html copies; hand-authored .md
839
+ // compositions get it automatically here.
840
+ eleventyConfig.addPreprocessor('omega-composition-liquid', 'md', (data, content) => {
841
+ if (data.templateEngineOverride === undefined && /^\s*{%-?\s*(section|composition)\b/m.test(content)) {
842
+ data.templateEngineOverride = 'liquid';
843
+ }
844
+ });
845
+
846
+ // ---- Jekyll conventions + page.resolved equivalent
847
+ // Every template's permalink resolves through THIS — the global computed
848
+ // below and every gated virtual template (renderGate) — so the production
849
+ // /test omission (#554) covers a consumer's own page at a /test URL exactly
850
+ // as it covers the framework default that page took over.
851
+ const resolvePermalink = (data) => {
852
+ const permalink = jekyllPermalink(data, allCollections);
853
+ if (environment === 'production' && typeof permalink === 'string' && DEV_ONLY_URL_RE.test(permalink)) {
854
+ return false;
855
+ }
856
+ return permalink;
857
+ };
858
+ eleventyConfig.addGlobalData('eleventyComputed', {
859
+ permalink: resolvePermalink,
860
+ // Jekyll paginator compat: layouts iterate `paginator.posts` with Jekyll
861
+ // post shapes (post.url, post.post.title), so items are flattened
862
+ // ({ url, date, ...data }) — references, not copies.
863
+ paginator: (data) => {
864
+ const p = data.pagination;
865
+ if (!p || !p.items) return undefined;
866
+ const totalPages = (p.pages || []).length;
867
+ return {
868
+ posts: p.items.map(jekyllDoc),
869
+ page: p.pageNumber + 1,
870
+ per_page: p.items.length,
871
+ total_pages: totalPages,
872
+ previous_page: p.pageNumber > 0 ? p.pageNumber : null,
873
+ previous_page_path: (p.href && p.href.previous) || null,
874
+ next_page: p.pageNumber + 2 <= totalPages ? p.pageNumber + 2 : null,
875
+ next_page_path: (p.href && p.href.next) || null,
876
+ };
877
+ },
878
+ // Per-page asset lookups against the content-hash manifest, keyed by URL
879
+ // alone (spec §7 — the asset_path frontmatter override is dead): `<key>`
880
+ // for flat entries (js/pages/pricing.js), `<key>/index` for per-page dirs
881
+ // (js/pages/pricing/index.js), and [name] wildcard segments for generated
882
+ // page families (js/pages/blog/[slug].js serves every /blog/<slug> post).
883
+ // Each answer is a LIST — every layer that ships a file for this URL, in
884
+ // load order (#624), because page assets never replace one another.
885
+ pageAssets: (data) => {
886
+ const manifest = data.assetManifest || {};
887
+ const trimmed = (data.page.url || '/').replace(/^\/|\/$/g, '');
888
+ const base = trimmed === '' ? 'index' : trimmed;
889
+ return {
890
+ js: resolvePageAsset(manifest.js && manifest.js.pages, base) || [],
891
+ css: resolvePageAsset(manifest.css && manifest.css.pages, base) || [],
892
+ };
893
+ },
894
+ // The same lookup, keyed by LAYOUT instead of URL (#624): a page gets every
895
+ // asset of every layout in its chain, outermost first — the chain, not just
896
+ // `data.layout`, because a blueprint that sits on the redirect layout
897
+ // (blueprint/careers) must still get the redirect layout's script.
898
+ layoutAssets: (data) => {
899
+ const manifest = data.assetManifest || {};
900
+ const chain = layoutChain(data.layout);
901
+ return {
902
+ js: chain.flatMap((name) => resolvePageAsset(manifest.js && manifest.js.layouts, name) || []),
903
+ css: chain.flatMap((name) => resolvePageAsset(manifest.css && manifest.css.layouts, name) || []),
904
+ };
905
+ },
906
+ resolved: (data) => {
907
+ // inject-properties.rb parity: resolved = build facts ← layout chain ←
908
+ // page data, PLUS the config namespace (#607).
909
+ //
910
+ // `resolved.config` is the WHOLE brand config with this page's own
911
+ // `config:` block merged on top — the seed below puts the entire config
912
+ // under the cascade (never a filtered subset: a brand section with no
913
+ // schema rule is config too), and the generic merge that follows lets a
914
+ // layout's or page's `config:` win key by key. `resolved.meta` carries
915
+ // ONE seed and nothing more: meta is page machinery (Ian 2026-08-26), so
916
+ // the walk is the site-wide index floor → page `meta:` → layout `meta`,
917
+ // with head.html falling back to brand.name / brand.description.
918
+ const out = {};
919
+ for (const key of Object.keys(site)) {
920
+ if (!resolvedSiteExclude.has(key)) out[key] = site[key];
921
+ }
922
+ out.config = config;
923
+
924
+ // The site-wide index default (#564, Ian 2026-09-09, one name at both
925
+ // levels): `targets.web.meta.index` is the SAME key a page writes, so
926
+ // the site default and the page override never drift apart. It is a
927
+ // SEED, not a second emission path: every signal reads the one resolved
928
+ // `meta.index` below, so the robots meta, sitemap.xml, llms.txt and
929
+ // pages.json cannot disagree. The merge that follows puts page and
930
+ // layout `meta:` over it, which is how a page's own `meta.index: true`
931
+ // exempts itself from a noindexed site. Consequential-feature doctrine
932
+ // (#527): only the LITERAL false takes a whole site off search — absent
933
+ // or true is today's behavior.
934
+ if (config.meta && config.meta.index === false) out.meta = { index: false };
935
+
936
+ for (const key of Object.keys(data)) {
937
+ if (!RESOLVED_OMIT.has(key)) out[key] = deepMerge(out[key], data[key]);
938
+ }
939
+
940
+ // Parity repair — sidecar lane (#269): the page's own data file is the
941
+ // sanctioned page-level lane for shell-layout band data, so its keys are
942
+ // re-applied here with the arrays-replace rule. It sits BELOW the
943
+ // template's own frontmatter, exactly where the cascade puts it.
944
+ const sidecar = readSidecarData(data.page.inputPath);
945
+ if (sidecar) {
946
+ for (const key of Object.keys(sidecar)) {
947
+ out[key] = deepMerge(out[key], sidecar[key]);
948
+ }
949
+ }
950
+
951
+ // Parity repair — collections lane: Eleventy's cascade merge concats
952
+ // arrays and lets a layout-default object beat a doc scalar — re-apply
953
+ // the template's OWN frontmatter (re-parsed from source) with OUR
954
+ // semantics, so a content entry (_posts/_team/_alternatives docs)
955
+ // always wins its own keys outright: arrays REPLACE. Pages are
956
+ // meta-only (guard above stripped content keys from the cascade) — this
957
+ // lane filters to the allow set so nothing stripped re-enters here.
958
+ const own = readOwnFrontmatter(data.page.inputPath);
959
+ if (own) {
960
+ const isPage = /\/pages\//.test(data.page.inputPath);
961
+ for (const key of Object.keys(own)) {
962
+ if (isPage && !PAGE_FRONTMATTER_ALLOW.has(key)) continue;
963
+ out[key] = deepMerge(out[key], own[key]);
964
+ }
965
+ }
966
+
967
+ // The index posture's AUTOMATIC exclusions (#564). Everything above is
968
+ // authored: the site default, the layout's `meta:`, the page's own.
969
+ // These are the framework's own, applied last because they are not
970
+ // overridable: a draft is unpublished, a dev surface never ships, an
971
+ // admin screen is private, a redirect stub is a forwarding URL, and
972
+ // pages 2..N of a LISTING are duplicates of the canonical page 1. Every
973
+ // output reads the ONE value they land on, so no template keeps a rule
974
+ // list of its own and the signals cannot disagree.
975
+ //
976
+ // Alias pagination is the other shape the machinery serves (one page per
977
+ // taxonomy TERM, `size: 1` + `alias`), where a page number is an index
978
+ // into terms and says nothing about duplication; those pages carry their
979
+ // own `meta.index` on their layout. Eleventy's computed-data dependency
980
+ // pass probes with proxies, so the number is only a number on a real
981
+ // render.
982
+ const pageNumber = data.pagination && !data.pagination.alias && data.pagination.pageNumber;
983
+ const url = typeof data.page.url === 'string' ? data.page.url : '';
984
+ const layoutName = typeof out.layout === 'string' ? out.layout : '';
985
+
986
+ if (out.draft
987
+ || DEV_ONLY_URL_RE.test(url)
988
+ || url.startsWith('/admin/')
989
+ || layoutName.includes('redirect')
990
+ || (typeof pageNumber === 'number' && pageNumber > 0)) {
991
+ out.meta = { ...out.meta, index: false };
992
+ }
993
+
994
+ // Layout-frontmatter Liquid: the preprocessor only sees PAGE frontmatter,
995
+ // so cascade data contributed by layouts (the real base contact layout
996
+ // carries `{{ site.brand.name }}`, real sweet-saucy recipe carries
997
+ // `{{ page.recipe.title }}` in meta values) still holds raw refs here.
998
+ // `resolved` is in context too — layout defaults template on the merged
999
+ // data (base alternative: tagline "The #1 {{ resolved.alternative.
1000
+ // competitor.name }} alternative"); `page.resolved` covers the legacy
1001
+ // spelling in not-yet-migrated consumer frontmatter.
1002
+ // The page's pagination alias is bound here too (#544) — this IS the
1003
+ // render-time pass its refs deferred to — and the cascade rides along as
1004
+ // the globals a `_data` ref resolves against (#542): the sidecar re-apply
1005
+ // above put RAW values back, so this is where they render.
1006
+ // Copy-on-write: shared cascade sub-objects are never mutated, and
1007
+ // pages with no remaining refs return `out` untouched.
1008
+ return frontmatter.renderData(out, {
1009
+ resolved: out,
1010
+ page: { ...out, resolved: out, url: data.page.url, slug: data.page.fileSlug, fileSlug: data.page.fileSlug },
1011
+ ...aliasScope(data),
1012
+ }, { globals: data, file: data.page.inputPath });
1013
+ },
1014
+ });
1015
+
1016
+ // page.url parity with Jekyll (legacy UJM): the flat `about.html` output
1017
+ // still reads as '/about' everywhere templates look (canonical, hreflang,
1018
+ // data-page-path, nav active-detection, collection doc.url). index.html
1019
+ // outputs are collapsed to directory URLs by Eleventy before transforms run.
1020
+ eleventyConfig.addUrlTransform(({ url }) => {
1021
+ if (typeof url === 'string' && url.endsWith('.html')) {
1022
+ return url.slice(0, -'.html'.length);
1023
+ }
1024
+ return undefined;
1025
+ });
1026
+
1027
+ // ---- Collections: posts, alternatives, team, blog taxonomy, and the
1028
+ // brand's own collections plus their taxonomies (#207)
1029
+ registerCollections(eleventyConfig, collectionsHolder, dynamicCollections);
1030
+
1031
+ // ---- Dev-mode collection limiting (#190): a brand with thousands of posts
1032
+ // samples them locally so the dev build stays fast — the sampled-out
1033
+ // documents never enter the build at all. The config is validated in EVERY
1034
+ // environment (a typo must fail `omega build` too), but only a development
1035
+ // build samples: a shipped site is always the whole site.
1036
+ applyCollectionLimits(eleventyConfig, {
1037
+ consumerDir: options.consumerDir,
1038
+ limits: config.dev && config.dev.limitCollections,
1039
+ collections: dynamicCollections,
1040
+ environment,
1041
+ });
1042
+
1043
+ // ---- The live decisions (#200 Lane B): which URLs the consumer's own pages
1044
+ // claim, and which collections the brand has real content in. Both are
1045
+ // CONTENT facts, so both are rescan captures — a config-time value would
1046
+ // only refresh through a config reset, and a config reset on every page edit
1047
+ // is exactly what the incremental contract forbids. Everything below asks
1048
+ // this object at render time.
1049
+ const decisions = createDecisions({
1050
+ consumerDir: options.consumerDir,
1051
+ collectionDirs: SAMPLE_SETS.map((set) => set.collectionDir),
1052
+ environment,
1053
+ });
1054
+ // Re-scan before every REBUILD, so a render can never read a decision older
1055
+ // than its own build — whichever watcher saw the file event first. The dev
1056
+ // loop's rescan watcher is the prompt lane (it updates and reports the
1057
+ // moment a file lands, before any rebuild finishes); this is the ordering
1058
+ // guarantee. A production build has exactly one build and one scan.
1059
+ if (environment !== 'production') {
1060
+ eleventyConfig.on('eleventy.before', () => decisions.refresh());
1061
+ }
1062
+
1063
+ // ---- Default pages: virtual templates the consumer can take over by
1064
+ // claiming the same permalink. The showcase (development only) rides the
1065
+ // same lane: auto-generated pages over the resolved library — /test/sections
1066
+ // + one page per entry (spec §9). Production builds omit the showcase
1067
+ // entirely: a page rendering EVERY section would keep every section's CSS
1068
+ // alive through the PurgeCSS content scan and quietly defeat §7
1069
+ // self-trimming.
1070
+ // The framework's own /test pages ride that same omission (#554): their
1071
+ // permalinks are config-time facts, so a production build never registers
1072
+ // them at all. A consumer page at a /test URL is a render-time fact and is
1073
+ // shut by resolvePermalink above.
1074
+ const frameworkPages = [
1075
+ ...[...collectLayered([path.join(defaultsDir, 'pages')])]
1076
+ .map(([rel, abs]) => ({ virtual: `omega-defaults/${rel}`, label: `defaults/pages/${rel}`, abs })),
1077
+ ...(environment === 'production' ? [] : [...collectLayered([path.join(defaultsDir, 'showcase')])]
1078
+ .map(([rel, abs]) => ({ virtual: `omega-defaults/showcase/${rel}`, label: `defaults/showcase/${rel}`, abs }))),
1079
+ ].map((page) => {
1080
+ const raw = reads.read(page.abs);
1081
+ return { ...page, raw, url: permalinkOf(raw) };
1082
+ }).filter((page) => !(environment === 'production' && DEV_ONLY_URL_RE.test(page.url || '')));
1083
+
1084
+ for (const page of frameworkPages) {
1085
+ // Registered UNCONDITIONALLY, gated at render time: suppression is a live
1086
+ // answer now, and a template skipped at config time could only come back
1087
+ // through a config reset.
1088
+ eleventyConfig.addTemplate(page.virtual, page.raw, renderGate(() => !decisions.suppresses(page.url), resolvePermalink));
1089
+ }
1090
+
1091
+ // ---- Social shortlinks (#429): a redirect page per entry of the socials
1092
+ // block, on the same lane and the same gate as the default pages above — the
1093
+ // permalink is a config-time fact (`/<platform>`), so a consumer page at one
1094
+ // of them takes just that URL over.
1095
+ // ---- Target shortlinks (#561): the same lane again, for the download
1096
+ // platform/artifact URLs (/download/mac, /download/linux/snap) and the
1097
+ // extension store URLs (/extension/chrome) legacy UJM shipped as hand-made
1098
+ // default pages. The declaration is the SAME map /download and /extension
1099
+ // already render from.
1100
+ const shortlinkPages = [
1101
+ ...socialPages(readSocials(config.socials)),
1102
+ ...targetShortlinkPages(readTargetShortlinks(site)),
1103
+ ];
1104
+ for (const page of shortlinkPages) {
1105
+ eleventyConfig.addTemplate(page.virtual, page.raw, renderGate(() => !decisions.suppresses(page.url), resolvePermalink));
1106
+ }
1107
+
1108
+ // ---- Dynamic pages (#207): the listing and category pages of every
1109
+ // collection the brand declared, generated as virtual templates on the SAME
1110
+ // lane as the default pages above. Their permalink is only known per RENDER
1111
+ // (the listing's page number, the category's slug), so the gate asks the
1112
+ // live decisions about that page's own URL — a consumer page at any one of
1113
+ // them takes just that URL over.
1114
+ const dynamicPages = dynamicCollections.flatMap(collectionPages);
1115
+ for (const page of dynamicPages) {
1116
+ eleventyConfig.addTemplate(page.virtual, page.raw, {
1117
+ eleventyComputed: {
1118
+ permalink: (data) => {
1119
+ const url = page.urlOf(data);
1120
+ return url && !decisions.suppresses(url) ? `${url}.html` : false;
1121
+ },
1122
+ },
1123
+ });
1124
+ }
1125
+
1126
+ // The collision picture: the default pages plus every generated page whose
1127
+ // URL is a config-time fact (a listing's own page 1). A category page's URL
1128
+ // is a CONTENT fact — it exists because a document names that term — so it
1129
+ // can only be answered by the render-time gate above.
1130
+ decisions.framework([...frameworkPages, ...shortlinkPages, ...dynamicPages].filter((page) => page.url));
1131
+
1132
+ // ---- Sample content (development only): a content-less brand still gets
1133
+ // living pages locally. Injected as virtual templates under the matching
1134
+ // collection segment so they ride the exact same lane as real content
1135
+ // (tags, permalinks, taxonomy). Dates ROLL — the corpus rhythm re-anchors
1136
+ // to the build day (or the OMEGA_SAMPLE_ANCHOR / sampleAnchor pin), spec
1137
+ // §8. The FIRST consumer file in a collection — or a production build —
1138
+ // removes that collection's samples entirely, and in dev that happens on the
1139
+ // very next render: the gate is the live own-content answer.
1140
+ if (environment !== 'production') {
1141
+ const sampleAnchorMs = resolveAnchor(options.sampleAnchor);
1142
+ for (const set of SAMPLE_SETS) {
1143
+ for (const { name, content } of generateSampleSet(defaultsDir, set, sampleAnchorMs)) {
1144
+ eleventyConfig.addTemplate(
1145
+ `omega-defaults/${set.collectionDir}/${name}`,
1146
+ content,
1147
+ renderGate(() => !decisions.hasOwn(set.collectionDir), resolvePermalink),
1148
+ );
1149
+ }
1150
+ }
1151
+ }
1152
+
1153
+ // ---- Globals. ONE instant per build, so every stamp below names the same
1154
+ // moment instead of drifting a millisecond apart.
1155
+ const buildTime = new Date();
1156
+ // site.time: Jekyll's build timestamp, kept as a real build fact (#613) —
1157
+ // `article:modified_time` in the head and the BlogPosting `dateModified` in
1158
+ // the foot both read it, and with nothing setting it both shipped EMPTY on
1159
+ // every post. `site.omega.date.iso` is the same instant by construction.
1160
+ site.time = buildTime.toISOString();
1161
+ // site.omega carries UJM-runtime site values the core includes read
1162
+ // (cache_breaker in the @omega.js/client Configuration, date.year in the
1163
+ // copyright meta, date.iso as the sitemap/feed build stamp,
1164
+ // placeholder.src in lazy-loaded imgs).
1165
+ site.omega = {
1166
+ // The build stamp the runtime lazy-loader appends (cb=) — same value as
1167
+ // omega_cachebreak and the omega-cachebreak-img transform. Was 0 (inert)
1168
+ // until the central cache-breaker landed.
1169
+ cache_breaker: CACHE_TIMESTAMP,
1170
+ // The WEBSITE TARGET's own package version, read by the caller off the target
1171
+ // root's package.json (the engine only ever sees the src dir). It rides the
1172
+ // Configuration block as `version`, which is the release tag every error
1173
+ // report carries — `brand.id@version` (#380). null (a caller that hands
1174
+ // none) leaves @omega.js/client on its build-stamp fallback.
1175
+ version: options.version || null,
1176
+ date: { year: buildTime.getFullYear(), iso: site.time },
1177
+ placeholder: { src: 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==' },
1178
+ ...(config.omega || {}),
1179
+ };
1180
+ // site.characters: the literal-character set templates interpolate rather
1181
+ // than type — head.html's copyright meta reads characters.copyright, and
1182
+ // with nothing defining the set every page fleet-wide shipped a leading
1183
+ // blank. The legacy _config_default.yml set carried over verbatim (#286);
1184
+ // a brand key wins, like site.omega's.
1185
+ site.characters = {
1186
+ asterisk: '*',
1187
+ 'brace-left': '{',
1188
+ 'brace-right': '}',
1189
+ 'brace-both': '{}',
1190
+ 'bracket-left': '[',
1191
+ 'bracket-right': ']',
1192
+ 'bracket-both': '[]',
1193
+ copyright: '©',
1194
+ underscore: '_',
1195
+ ...(config.characters || {}),
1196
+ };
1197
+ eleventyConfig.addGlobalData('site', site);
1198
+ // og:locale wants Open Graph's language_TERRITORY form (en → en_US), and the
1199
+ // code → locale map is the devkit language SSOT — the head include cannot
1200
+ // derive it in Liquid, so it arrives as a computed global.
1201
+ eleventyConfig.addGlobalData('ogLocale', ogLocale(config.translation?.default || 'en'));
1202
+ // `dev` rides the jekyll global into the Configuration chrome (N7): `omega
1203
+ // dev` passes { ports } with the resolved map; production builds pass
1204
+ // nothing → null, and @omega.js/client falls back to the classic ports.
1205
+ //
1206
+ // A FUNCTION `dev` is a LIVE map, re-read on every render — the sibling
1207
+ // backend's ports file is pid-stamped and deleted on shutdown, so the map a
1208
+ // boot-time read produced is wrong the moment the emulator boots late or
1209
+ // restarts on bumped numbers, and the browser has no other channel
1210
+ // ([#300](https://github.com/Omega-JS-Stack/omega/issues/300)). Global data
1211
+ // registered as a function is evaluated per build, so every re-render bakes
1212
+ // what is running RIGHT NOW.
1213
+ eleventyConfig.addGlobalData('jekyll', () => ({
1214
+ environment,
1215
+ dev: (typeof options.dev === 'function' ? options.dev() : options.dev) || null,
1216
+ }));
1217
+ // The manifest is a FUNCTION for the same reason (#765): an object handed to
1218
+ // addGlobalData is snapshotted into the data cascade at registration, so a
1219
+ // dev rebuild that folds a new font-preload list into the live manifest was
1220
+ // invisible to every later render — measured: the object form still served
1221
+ // the boot list after a rebuild AND after a template edit, while the
1222
+ // callback form served the new one. The dev lane mutates that object in
1223
+ // place, so the callback re-reads what the last asset build produced.
1224
+ const emptyManifest = { js: { pages: {}, layouts: {} }, css: { pages: {}, layouts: {} } };
1225
+ eleventyConfig.addGlobalData('assetManifest', () => options.assetManifest || emptyManifest);
1226
+
1227
+ // ---- Icon inlining (#619, dev AND prod): every empty `<i>` whose classes
1228
+ // name an icon gets that icon's SVG inlined, so static chrome costs zero
1229
+ // runtime fetches and never flashes. What the pass emits is exactly what the
1230
+ // runtime watcher would have produced (runtime/icons.js), and the stamp it
1231
+ // leaves is what tells the watcher to leave the element alone.
1232
+ const loadIcon = createIconLoader({
1233
+ svgsDirs: [path.join(coreDir, 'icons'), ...fa.svgsDirs],
1234
+ aliasFile: fa.aliasFile,
1235
+ });
1236
+ // Warn ONCE per name per build: a missing icon in packaged chrome is a
1237
+ // framework bug a consumer cannot fix (#86), and the marker the pass leaves
1238
+ // on the element is what the dev audit reports in the browser.
1239
+ const warnedIcons = new Set();
1240
+ eleventyConfig.addTransform('omega-inline-icons', function (content) {
1241
+ if (this.page.outputPath && this.page.outputPath.endsWith('.html')) {
1242
+ return inlineIcons(content, loadIcon, (key) => {
1243
+ if (warnedIcons.has(key)) return;
1244
+ warnedIcons.add(key);
1245
+ logger.warn(`no SVG in the icon set for "${key}" — rendering an empty icon`);
1246
+ });
1247
+ }
1248
+ return content;
1249
+ });
1250
+
1251
+ // ---- Image cache-breaker (dev AND prod): every local <img>/<source> URL
1252
+ // in a .html output carries ?cb=<build stamp> so image edits show up on
1253
+ // rebuilds. Registered before the minifier (transforms run in order) and
1254
+ // shares the omega_cachebreak filter's stamp — one value per build process.
1255
+ const { cachebreakHtml } = require('./cachebreak-html.js');
1256
+ eleventyConfig.addTransform('omega-cachebreak-img', function (content) {
1257
+ if (this.page.outputPath && this.page.outputPath.endsWith('.html')) {
1258
+ return cachebreakHtml(content, CACHE_TIMESTAMP);
1259
+ }
1260
+ return content;
1261
+ });
1262
+
1263
+ // ---- Base path (#355): a site mounted under a URL path (a GitHub Pages
1264
+ // project site) gets every root-relative URL it emits moved under that path,
1265
+ // plus the stamp the browser half reads (src/path-prefix.js). Registered ONLY
1266
+ // when a prefix is set — a build at the domain root runs no pass at all, so
1267
+ // its output is what it has always been, byte for byte. The dev server serves
1268
+ // at the root and never passes one.
1269
+ const pathPrefix = resolvePathPrefix(options.pathPrefix);
1270
+ if (pathPrefix) {
1271
+ eleventyConfig.addTransform('omega-path-prefix', function (content) {
1272
+ if (this.page.outputPath && this.page.outputPath.endsWith('.html')) {
1273
+ return prefixHtml(content, pathPrefix);
1274
+ }
1275
+ return content;
1276
+ });
1277
+ }
1278
+
1279
+ // ---- Production HTML minification (the UJM minifyHtml successor). Only
1280
+ // .html outputs — the meta-files (sitemap.xml, feeds, robots.txt, …) ship
1281
+ // exactly as their templates render them.
1282
+ if (environment === 'production') {
1283
+ const { minifyHtml } = require('./minify-html.js');
1284
+ eleventyConfig.addTransform('omega-minify-html', function (content) {
1285
+ if (this.page.outputPath && this.page.outputPath.endsWith('.html')) {
1286
+ return minifyHtml(content);
1287
+ }
1288
+ return content;
1289
+ });
1290
+ }
1291
+
1292
+ return { site, config, layers, layoutMap, frontmatter, suppressed: decisions.suppressedUrls(), decisions, collectionsHolder };
1293
+ }
1294
+
1295
+ /**
1296
+ * One Eleventy collection item as a JEKYLL document: the flat shape layouts
1297
+ * iterate (`post.url`, `post.date`, `post.post.title`) plus Jekyll's
1298
+ * `post.content` — a reference to the item, never a copy.
1299
+ *
1300
+ * `content` is LAZY and non-enumerable (#598). Eleventy renders collection
1301
+ * content AFTER computed data resolves, so reading it here would throw its
1302
+ * premature-use error, and an enumerable getter would throw the same error in
1303
+ * every data walk that copies the doc (the `resolved` merge, the frontmatter
1304
+ * render pass). Read at RENDER time it is the post's real body — which is
1305
+ * what `{% omega_readtime %}` counts: the blog hub's featured card printed
1306
+ * "1 min read" for a twelve-minute post while the post's own page printed 12.
1307
+ *
1308
+ * BOTH doc lanes flatten through here (#711): `paginator.posts` passes raw
1309
+ * Eleventy items, the `site.<name>` arrays pass the collection holder's docs
1310
+ * (collections.js `toDoc`, which forwards `templateContent` the same lazy
1311
+ * way). `id` is the holder doc's — an item has none — and sits before the data
1312
+ * spread so a document's explicit frontmatter id still wins.
1313
+ * @param {object} item - an Eleventy collection item, or a holder doc
1314
+ * @returns {object} the Jekyll doc shape
1315
+ */
1316
+ function jekyllDoc(item) {
1317
+ const identity = item.id === undefined ? {} : { id: item.id };
1318
+ const doc = { ...identity, url: item.url, date: item.date, ...item.data };
1319
+ Object.defineProperty(doc, 'content', {
1320
+ get: () => item.templateContent,
1321
+ enumerable: false,
1322
+ configurable: true,
1323
+ });
1324
+ return doc;
1325
+ }
1326
+
1327
+ /**
1328
+ * The permalink every template computes — the ONE Jekyll-convention rule, so a
1329
+ * gated virtual template (renderGate below) resolves its URL exactly like an
1330
+ * ungated one.
1331
+ * @param {object} data - the template's data cascade
1332
+ * @param {object[]} collections - the declared collections (#207): `{ dir: '_<name>', base }` — a document under `dir` publishes at `<base>/<slug>`
1333
+ * @returns {string|object|undefined} the permalink Eleventy writes to
1334
+ */
1335
+ function jekyllPermalink(data, collections) {
1336
+ const inputPath = data.page.inputPath;
1337
+ let permalink = data.permalink;
1338
+ // Collection URLs mirror UJM's Jekyll defaults (permalink: "/<coll>/
1339
+ // :title", with Eleventy's fileSlug stripping the dated-filename part) —
1340
+ // but an EXPLICIT permalink in the doc's frontmatter wins, like Jekyll.
1341
+ // '' counts as absent: Eleventy's computed dependency pass probes with
1342
+ // an empty-string proxy, and that probe value persists into the data.
1343
+ if (permalink === undefined || permalink === '') {
1344
+ const collection = collections.find((entry) => inputPath.includes(`/${entry.dir}/`));
1345
+ if (collection) permalink = `${collection.base}/${data.page.fileSlug}`;
1346
+ }
1347
+ // Jekyll flat URLs (legacy UJM parity): `/about` writes `about.html`,
1348
+ // NOT `about/index.html` — site URLs carry no trailing slash. page.url
1349
+ // stays extensionless ('/about') via configureOmega's .html-stripping
1350
+ // urlTransform, exactly like Jekyll's page.url for extensionless permalinks.
1351
+ // A whitelist of real output extensions, NOT path.extname — dotted slugs
1352
+ // (`/updates/v1.0.0`) must still get their .html. Liquid-carrying
1353
+ // permalinks (pagination/taxonomy) can't be shape-tested as raw strings —
1354
+ // they spell their full shape explicitly (blog.md ends in `.html`) and
1355
+ // pass through untouched.
1356
+ const KNOWN_EXT = /\.(html|xml|txt|json|js|css|webmanifest|svg|ics|pdf)$/i;
1357
+ if (typeof permalink === 'string' && !/[{}]/.test(permalink)
1358
+ && !KNOWN_EXT.test(permalink) && !permalink.endsWith('/')) {
1359
+ return `${permalink}.html`;
1360
+ }
1361
+ return permalink;
1362
+ }
1363
+
1364
+ /**
1365
+ * The render-time gate of a virtual template (#200 Lane B). The template is
1366
+ * registered unconditionally and decides per BUILD whether it SHIPS:
1367
+ * `permalink: false` writes no file and `eleventyExcludeFromCollections` keeps
1368
+ * it out of every collection, so nothing downstream can see it. Eleventy
1369
+ * computes both per build, so a decision that flips between watch rebuilds
1370
+ * lands on the very next render with no config reset.
1371
+ *
1372
+ * What a shut gate does NOT do is skip the render: Eleventy still renders a
1373
+ * `permalink: false` template and throws away the output. The cost is real —
1374
+ * a template error inside a suppressed default page is still a fatal build
1375
+ * error — and it is the price of the lane: a template skipped at config time
1376
+ * could only come back through a config reset.
1377
+ *
1378
+ * The gate is also invisible to PAGINATED templates: Eleventy reads
1379
+ * `eleventyExcludeFromCollections` off the raw frontmatter when it expands
1380
+ * pagination, before any computed value exists. Every paginated default
1381
+ * therefore spells the key in its own frontmatter (pinned by
1382
+ * test/collections-gate.test.js).
1383
+ * @param {function} isActive - () => boolean, asked at data time
1384
+ * @param {function} resolvePermalink - the build's shared permalink resolver (jekyllPermalink plus the production /test omission)
1385
+ * @returns {object} addTemplate data
1386
+ */
1387
+ function renderGate(isActive, resolvePermalink) {
1388
+ return {
1389
+ eleventyComputed: {
1390
+ // Open gate: the same permalink the global computed would have produced
1391
+ // (a per-template `eleventyComputed.permalink` replaces that key).
1392
+ permalink: (data) => (isActive() ? resolvePermalink(data) : false),
1393
+ // Open gate: the page's OWN frontmatter answer stands VERBATIM — `true`,
1394
+ // the list-of-collections form, or undefined for the pages that never
1395
+ // set it. Narrowing it to a boolean here would quietly rewrite a
1396
+ // template's own answer into one this gate never asked about.
1397
+ eleventyExcludeFromCollections: (data) => (isActive() ? data.eleventyExcludeFromCollections : true),
1398
+ },
1399
+ };
1400
+ }
1401
+
1402
+ module.exports = { configureOmega };