@omega.js/backend 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 (993) hide show
  1. package/LICENSE +98 -0
  2. package/README.md +1053 -0
  3. package/bin/omega +2 -0
  4. package/bin/omega-backend +2 -0
  5. package/bin/omg +2 -0
  6. package/cli.js +3 -0
  7. package/dist/cli/command-table.js +268 -0
  8. package/dist/cli/commands/auth.js +259 -0
  9. package/dist/cli/commands/base-command.js +451 -0
  10. package/dist/cli/commands/build.js +27 -0
  11. package/dist/cli/commands/clean.js +13 -0
  12. package/dist/cli/commands/clear.js +11 -0
  13. package/dist/cli/commands/cwd.js +9 -0
  14. package/dist/cli/commands/deploy.js +205 -0
  15. package/dist/cli/commands/emulator-orphans.js +440 -0
  16. package/dist/cli/commands/emulator.js +1829 -0
  17. package/dist/cli/commands/firebase-init.js +101 -0
  18. package/dist/cli/commands/firestore.js +261 -0
  19. package/dist/cli/commands/indexes.js +51 -0
  20. package/dist/cli/commands/install.js +151 -0
  21. package/dist/cli/commands/logs.js +358 -0
  22. package/dist/cli/commands/mcp.js +41 -0
  23. package/dist/cli/commands/migrate-markers.js +176 -0
  24. package/dist/cli/commands/migrate-rules.js +90 -0
  25. package/dist/cli/commands/migrate.js +59 -0
  26. package/dist/cli/commands/serve.js +208 -0
  27. package/dist/cli/commands/setup-tests/base-test.js +174 -0
  28. package/dist/cli/commands/setup-tests/emulator-config.js +109 -0
  29. package/dist/cli/commands/setup-tests/env-runtime-config-deprecated.js +47 -0
  30. package/dist/cli/commands/setup-tests/firebase-admin.js +49 -0
  31. package/dist/cli/commands/setup-tests/firebase-auth.js +26 -0
  32. package/dist/cli/commands/setup-tests/firebase-cli.js +26 -0
  33. package/dist/cli/commands/setup-tests/firebase-functions.js +49 -0
  34. package/dist/cli/commands/setup-tests/firestore-indexes-file.js +72 -0
  35. package/dist/cli/commands/setup-tests/firestore-indexes-in-json.js +20 -0
  36. package/dist/cli/commands/setup-tests/firestore-indexes-required.js +108 -0
  37. package/dist/cli/commands/setup-tests/firestore-indexes-synced.js +236 -0
  38. package/dist/cli/commands/setup-tests/firestore-rules-file.js +115 -0
  39. package/dist/cli/commands/setup-tests/firestore-rules-in-json.js +49 -0
  40. package/dist/cli/commands/setup-tests/functions-package.js +44 -0
  41. package/dist/cli/commands/setup-tests/gcloud-cli.js +26 -0
  42. package/dist/cli/commands/setup-tests/gitignore.js +122 -0
  43. package/dist/cli/commands/setup-tests/helpers/merge-line-files.js +28 -0
  44. package/dist/cli/commands/setup-tests/helpers/required-indexes.js +109 -0
  45. package/dist/cli/commands/setup-tests/helpers/seed-campaigns.js +246 -0
  46. package/dist/cli/commands/setup-tests/helpers.js +72 -0
  47. package/dist/cli/commands/setup-tests/hosting-folder.js +23 -0
  48. package/dist/cli/commands/setup-tests/hosting-rewrites.js +52 -0
  49. package/dist/cli/commands/setup-tests/index.js +111 -0
  50. package/dist/cli/commands/setup-tests/is-firebase-project.js +21 -0
  51. package/dist/cli/commands/setup-tests/java-installed.js +27 -0
  52. package/dist/cli/commands/setup-tests/legacy-tests-cleanup.js +43 -0
  53. package/dist/cli/commands/setup-tests/marketing-campaigns-seeded.js +230 -0
  54. package/dist/cli/commands/setup-tests/node-version.js +60 -0
  55. package/dist/cli/commands/setup-tests/npm-project-scripts.js +42 -0
  56. package/dist/cli/commands/setup-tests/nvmrc-version.js +36 -0
  57. package/dist/cli/commands/setup-tests/omega-backend.js +42 -0
  58. package/dist/cli/commands/setup-tests/omega-config.js +84 -0
  59. package/dist/cli/commands/setup-tests/project-directories.js +34 -0
  60. package/dist/cli/commands/setup-tests/project-id-consistency.js +201 -0
  61. package/dist/cli/commands/setup-tests/public-html-files.js +22 -0
  62. package/dist/cli/commands/setup-tests/realtime-rules-file.js +69 -0
  63. package/dist/cli/commands/setup-tests/realtime-rules-in-json.js +20 -0
  64. package/dist/cli/commands/setup-tests/remoteconfig-template-file.js +32 -0
  65. package/dist/cli/commands/setup-tests/remoteconfig-template-in-json.js +31 -0
  66. package/dist/cli/commands/setup-tests/service-account.js +86 -0
  67. package/dist/cli/commands/setup-tests/storage-lifecycle-policy.js +81 -0
  68. package/dist/cli/commands/setup-tests/storage-rules-file.js +32 -0
  69. package/dist/cli/commands/setup-tests/storage-rules-in-json.js +20 -0
  70. package/dist/cli/commands/stripe.js +14 -0
  71. package/dist/cli/commands/test-lanes/stripe-live.js +440 -0
  72. package/dist/cli/commands/test.js +976 -0
  73. package/dist/cli/commands/update.js +27 -0
  74. package/dist/cli/commands/version.js +10 -0
  75. package/dist/cli/commands/watch.js +196 -0
  76. package/dist/cli/flags.js +13 -0
  77. package/dist/cli/index.js +173 -0
  78. package/dist/cli/run.js +33 -0
  79. package/dist/cli/utils/attach-log-file.js +4 -0
  80. package/dist/cli/utils/compile-rules.js +1155 -0
  81. package/dist/cli/utils/ensure-target.js +314 -0
  82. package/dist/cli/utils/project-type.js +140 -0
  83. package/dist/cli/utils/public-files.js +49 -0
  84. package/dist/cli/utils/safe-install.js +4 -0
  85. package/dist/cli/utils/spawn-shell.js +94 -0
  86. package/dist/cli/utils/stage-functions.js +338 -0
  87. package/dist/cli/utils/stage-local-packages.js +210 -0
  88. package/dist/cli/utils/target-checks.js +161 -0
  89. package/dist/cli/utils/target.js +24 -0
  90. package/dist/cli/utils/ui.js +292 -0
  91. package/dist/defaults/AGENTS.md +125 -0
  92. package/dist/defaults/CHANGELOG.md +15 -0
  93. package/dist/defaults/CLAUDE.md +1 -0
  94. package/dist/defaults/_.gitignore +65 -0
  95. package/dist/defaults/docs/README.md +17 -0
  96. package/dist/defaults/test/README.md +65 -0
  97. package/dist/defaults/test/_init.js +14 -0
  98. package/dist/defaults/test/helpers/connect-trap.js +72 -0
  99. package/dist/defaults/test/unit/registration.test.js +143 -0
  100. package/dist/defaults/test/unit/rules-posture.test.js +101 -0
  101. package/dist/defaults/test/unit/socket-free.test.js +43 -0
  102. package/dist/manager/events/auth/before-create.js +89 -0
  103. package/dist/manager/events/auth/before-signin.js +76 -0
  104. package/dist/manager/events/auth/on-create.js +112 -0
  105. package/dist/manager/events/auth/on-delete.js +141 -0
  106. package/dist/manager/events/auth/utils.js +93 -0
  107. package/dist/manager/events/cron/daily/blog-auto-publisher.js +287 -0
  108. package/dist/manager/events/cron/daily/data-requests.js +57 -0
  109. package/dist/manager/events/cron/daily/expire-paypal-cancellations.js +191 -0
  110. package/dist/manager/events/cron/daily/marketing-prune.js +486 -0
  111. package/dist/manager/events/cron/daily/reset-usage.js +188 -0
  112. package/dist/manager/events/cron/daily/trial-lapse-sweep.js +355 -0
  113. package/dist/manager/events/cron/daily.js +3 -0
  114. package/dist/manager/events/cron/frequent/abandoned-carts.js +204 -0
  115. package/dist/manager/events/cron/frequent/email-queue.js +71 -0
  116. package/dist/manager/events/cron/frequent/marketing-campaigns.js +463 -0
  117. package/dist/manager/events/cron/frequent/retry-failed-webhooks.js +70 -0
  118. package/dist/manager/events/cron/frequent.js +3 -0
  119. package/dist/manager/events/cron/runner.js +64 -0
  120. package/dist/manager/events/firestore/notifications/on-write.js +123 -0
  121. package/dist/manager/events/firestore/payments-disputes/on-write.js +256 -0
  122. package/dist/manager/events/firestore/payments-disputes/providers/stripe.js +218 -0
  123. package/dist/manager/events/firestore/payments-disputes/providers/test.js +231 -0
  124. package/dist/manager/events/firestore/payments-webhooks/analytics.js +687 -0
  125. package/dist/manager/events/firestore/payments-webhooks/on-write.js +1224 -0
  126. package/dist/manager/events/firestore/payments-webhooks/transitions/index.js +395 -0
  127. package/dist/manager/events/firestore/payments-webhooks/transitions/one-time/purchase-completed.js +58 -0
  128. package/dist/manager/events/firestore/payments-webhooks/transitions/one-time/purchase-failed.js +28 -0
  129. package/dist/manager/events/firestore/payments-webhooks/transitions/one-time/purchase-refunded.js +30 -0
  130. package/dist/manager/events/firestore/payments-webhooks/transitions/send-email.js +74 -0
  131. package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/cancellation-removed.js +14 -0
  132. package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/cancellation-requested.js +28 -0
  133. package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/checkout-declined.js +18 -0
  134. package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/new-subscription.js +67 -0
  135. package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/payment-failed.js +25 -0
  136. package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/payment-recovered.js +26 -0
  137. package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/payment-refunded.js +34 -0
  138. package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/plan-changed.js +42 -0
  139. package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/subscription-cancelled.js +33 -0
  140. package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/subscription-winback.js +17 -0
  141. package/dist/manager/functions/_legacy/actions/create-post-handler.js +188 -0
  142. package/dist/manager/functions/_legacy/actions/generate-uuid.js +63 -0
  143. package/dist/manager/functions/_legacy/actions/sign-up-handler.js +205 -0
  144. package/dist/manager/functions/_legacy/admin/create-post.js +207 -0
  145. package/dist/manager/functions/_legacy/admin/firestore-write.js +72 -0
  146. package/dist/manager/functions/_legacy/admin/get-stats.js +218 -0
  147. package/dist/manager/functions/_legacy/admin/query.js +198 -0
  148. package/dist/manager/functions/_legacy/admin/send-notification.js +206 -0
  149. package/dist/manager/functions/_legacy/template.js +33 -0
  150. package/dist/manager/functions/_legacy/test/authenticate.js +40 -0
  151. package/dist/manager/functions/_legacy/test/webhook.js +37 -0
  152. package/dist/manager/functions/wrappers/mailchimp/addToList.js +25 -0
  153. package/dist/manager/helpers/analytics.js +498 -0
  154. package/dist/manager/helpers/api-manager.js +313 -0
  155. package/dist/manager/helpers/backend-router.js +37 -0
  156. package/dist/manager/helpers/context/authenticate.js +215 -0
  157. package/dist/manager/helpers/context/client-info.js +91 -0
  158. package/dist/manager/helpers/context/index.js +228 -0
  159. package/dist/manager/helpers/context/logging.js +150 -0
  160. package/dist/manager/helpers/context/parse.js +160 -0
  161. package/dist/manager/helpers/context/respond.js +247 -0
  162. package/dist/manager/helpers/event-middleware.js +109 -0
  163. package/dist/manager/helpers/metadata.js +32 -0
  164. package/dist/manager/helpers/middleware.js +541 -0
  165. package/dist/manager/helpers/redact-secret.js +25 -0
  166. package/dist/manager/helpers/resolved-config.js +34 -0
  167. package/dist/manager/helpers/roles.js +69 -0
  168. package/dist/manager/helpers/safe-compare.js +31 -0
  169. package/dist/manager/helpers/schema-engine.js +314 -0
  170. package/dist/manager/helpers/schema-zod.js +203 -0
  171. package/dist/manager/helpers/settings.js +215 -0
  172. package/dist/manager/helpers/usage.js +541 -0
  173. package/dist/manager/helpers/user.js +57 -0
  174. package/dist/manager/helpers/utilities.js +565 -0
  175. package/dist/manager/index.js +1404 -0
  176. package/dist/manager/libraries/abandoned-cart-config.js +12 -0
  177. package/dist/manager/libraries/ai/index.js +321 -0
  178. package/dist/manager/libraries/ai/prompt.js +85 -0
  179. package/dist/manager/libraries/ai/providers/anthropic-format.js +297 -0
  180. package/dist/manager/libraries/ai/providers/anthropic.js +169 -0
  181. package/dist/manager/libraries/ai/providers/claude-code.js +183 -0
  182. package/dist/manager/libraries/ai/providers/openai.js +1272 -0
  183. package/dist/manager/libraries/ai/providers/test.js +257 -0
  184. package/dist/manager/libraries/ai/tokens.js +54 -0
  185. package/dist/manager/libraries/analytics/conversions.js +478 -0
  186. package/dist/manager/libraries/analytics/match-data.js +573 -0
  187. package/dist/manager/libraries/analytics/signup.js +120 -0
  188. package/dist/manager/libraries/auth-user.js +46 -0
  189. package/dist/manager/libraries/content/feed-parser.js +198 -0
  190. package/dist/manager/libraries/content/ghostii.js +191 -0
  191. package/dist/manager/libraries/content/source-resolver.js +629 -0
  192. package/dist/manager/libraries/email/constants.js +532 -0
  193. package/dist/manager/libraries/email/data/blocked-local-parts.json +55 -0
  194. package/dist/manager/libraries/email/data/blocked-local-patterns.js +14 -0
  195. package/dist/manager/libraries/email/data/corporate-domains.json +23 -0
  196. package/dist/manager/libraries/email/data/custom-disposable-domains.json +58 -0
  197. package/dist/manager/libraries/email/data/disposable-domains.json +8184 -0
  198. package/dist/manager/libraries/email/data/typo-domains.js +83 -0
  199. package/dist/manager/libraries/email/disposable-domains.js +92 -0
  200. package/dist/manager/libraries/email/generators/lib/filter.js +179 -0
  201. package/dist/manager/libraries/email/generators/lib/image-host.js +344 -0
  202. package/dist/manager/libraries/email/generators/lib/image-illustrator.js +154 -0
  203. package/dist/manager/libraries/email/generators/lib/markdown-renderer.js +294 -0
  204. package/dist/manager/libraries/email/generators/lib/mjml-template.js +120 -0
  205. package/dist/manager/libraries/email/generators/lib/structure.js +299 -0
  206. package/dist/manager/libraries/email/generators/lib/svg-illustrator.js +197 -0
  207. package/dist/manager/libraries/email/generators/lib/templates/base.js +229 -0
  208. package/dist/manager/libraries/email/generators/lib/templates/card.js +33 -0
  209. package/dist/manager/libraries/email/generators/lib/templates/classic-schema.js +59 -0
  210. package/dist/manager/libraries/email/generators/lib/templates/clean.js +82 -0
  211. package/dist/manager/libraries/email/generators/lib/templates/editorial/helpers.js +100 -0
  212. package/dist/manager/libraries/email/generators/lib/templates/editorial/index.js +318 -0
  213. package/dist/manager/libraries/email/generators/lib/templates/feedback.js +101 -0
  214. package/dist/manager/libraries/email/generators/lib/templates/field-report/helpers.js +138 -0
  215. package/dist/manager/libraries/email/generators/lib/templates/field-report/index.js +494 -0
  216. package/dist/manager/libraries/email/generators/lib/templates/index.js +47 -0
  217. package/dist/manager/libraries/email/generators/lib/templates/newsletter-shared.js +610 -0
  218. package/dist/manager/libraries/email/generators/lib/templates/order.js +461 -0
  219. package/dist/manager/libraries/email/generators/lib/templates/plain.js +83 -0
  220. package/dist/manager/libraries/email/generators/lib/templates/shared-campaign.js +64 -0
  221. package/dist/manager/libraries/email/generators/newsletter.js +899 -0
  222. package/dist/manager/libraries/email/index.js +146 -0
  223. package/dist/manager/libraries/email/marketing/index.js +754 -0
  224. package/dist/manager/libraries/email/prepare.js +365 -0
  225. package/dist/manager/libraries/email/providers/beehiiv.js +589 -0
  226. package/dist/manager/libraries/email/providers/sendgrid.js +845 -0
  227. package/dist/manager/libraries/email/transactional/index.js +510 -0
  228. package/dist/manager/libraries/email/utm.js +86 -0
  229. package/dist/manager/libraries/email/validation-provider-neverbounce.js +70 -0
  230. package/dist/manager/libraries/email/validation-provider-zerobounce.js +42 -0
  231. package/dist/manager/libraries/email/validation.js +255 -0
  232. package/dist/manager/libraries/env.js +198 -0
  233. package/dist/manager/libraries/infer-contact.js +101 -0
  234. package/dist/manager/libraries/load-provider.js +65 -0
  235. package/dist/manager/libraries/notification.js +256 -0
  236. package/dist/manager/libraries/openai.js +13 -0
  237. package/dist/manager/libraries/payment/discount-codes.js +204 -0
  238. package/dist/manager/libraries/payment/fetch-failure.js +56 -0
  239. package/dist/manager/libraries/payment/license.js +44 -0
  240. package/dist/manager/libraries/payment/order-id.js +57 -0
  241. package/dist/manager/libraries/payment/provider-errors.js +71 -0
  242. package/dist/manager/libraries/payment/providers/chargebee.js +817 -0
  243. package/dist/manager/libraries/payment/providers/coinbase.js +330 -0
  244. package/dist/manager/libraries/payment/providers/paypal.js +987 -0
  245. package/dist/manager/libraries/payment/providers/stripe.js +755 -0
  246. package/dist/manager/libraries/payment/providers/test.js +250 -0
  247. package/dist/manager/libraries/payment/refund-linkage.js +78 -0
  248. package/dist/manager/libraries/payment/refund-policy.js +113 -0
  249. package/dist/manager/libraries/payment/winback.js +55 -0
  250. package/dist/manager/libraries/prompts/infer-contact.md +78 -0
  251. package/dist/manager/libraries/rate-limits.js +17 -0
  252. package/dist/manager/libraries/recaptcha.js +58 -0
  253. package/dist/manager/libraries/user-doc.js +234 -0
  254. package/dist/manager/routes/admin/backup/post.js +106 -0
  255. package/dist/manager/routes/admin/cron/post.js +37 -0
  256. package/dist/manager/routes/admin/database/get.js +35 -0
  257. package/dist/manager/routes/admin/database/post.js +34 -0
  258. package/dist/manager/routes/admin/email/post.js +74 -0
  259. package/dist/manager/routes/admin/firestore/get.js +35 -0
  260. package/dist/manager/routes/admin/firestore/post.js +56 -0
  261. package/dist/manager/routes/admin/firestore/query/post.js +128 -0
  262. package/dist/manager/routes/admin/hook/post.js +104 -0
  263. package/dist/manager/routes/admin/infer-contact/post.js +35 -0
  264. package/dist/manager/routes/admin/notification/post.js +37 -0
  265. package/dist/manager/routes/admin/payment/post.js +56 -0
  266. package/dist/manager/routes/admin/post/deduplicate-image-alts.js +52 -0
  267. package/dist/manager/routes/admin/post/dispatch-deploy.js +36 -0
  268. package/dist/manager/routes/admin/post/post.js +487 -0
  269. package/dist/manager/routes/admin/post/put.js +146 -0
  270. package/dist/manager/routes/admin/post/templates/post.html +14 -0
  271. package/dist/manager/routes/admin/repo/content/post.js +106 -0
  272. package/dist/manager/routes/admin/stats/get.js +222 -0
  273. package/dist/manager/routes/admin/users/disable/post.js +50 -0
  274. package/dist/manager/routes/admin/users/list/get.js +110 -0
  275. package/dist/manager/routes/admin/users/sync/post.js +119 -0
  276. package/dist/manager/routes/brand/get.js +26 -0
  277. package/dist/manager/routes/content/post/get.js +110 -0
  278. package/dist/manager/routes/general/email/post.js +89 -0
  279. package/dist/manager/routes/general/email/templates/general/download-app-link.js +42 -0
  280. package/dist/manager/routes/general/uuid/post.js +31 -0
  281. package/dist/manager/routes/handler/post/post.js +142 -0
  282. package/dist/manager/routes/health/get.js +43 -0
  283. package/dist/manager/routes/index.js +11 -0
  284. package/dist/manager/routes/marketing/campaign/delete.js +45 -0
  285. package/dist/manager/routes/marketing/campaign/get.js +69 -0
  286. package/dist/manager/routes/marketing/campaign/post.js +124 -0
  287. package/dist/manager/routes/marketing/campaign/put.js +86 -0
  288. package/dist/manager/routes/marketing/campaign/utils.js +59 -0
  289. package/dist/manager/routes/marketing/contact/delete.js +100 -0
  290. package/dist/manager/routes/marketing/contact/post.js +149 -0
  291. package/dist/manager/routes/marketing/contact/put.js +37 -0
  292. package/dist/manager/routes/marketing/email-preferences/post.js +275 -0
  293. package/dist/manager/routes/marketing/webhook/forward/post.js +170 -0
  294. package/dist/manager/routes/marketing/webhook/post.js +123 -0
  295. package/dist/manager/routes/marketing/webhook/providers/beehiiv.js +197 -0
  296. package/dist/manager/routes/marketing/webhook/providers/sendgrid.js +194 -0
  297. package/dist/manager/routes/payments/cancel/_is-trialing.js +40 -0
  298. package/dist/manager/routes/payments/cancel/post.js +149 -0
  299. package/dist/manager/routes/payments/cancel/providers/chargebee.js +41 -0
  300. package/dist/manager/routes/payments/cancel/providers/paypal.js +56 -0
  301. package/dist/manager/routes/payments/cancel/providers/stripe.js +33 -0
  302. package/dist/manager/routes/payments/cancel/providers/test.js +118 -0
  303. package/dist/manager/routes/payments/discount/get.js +25 -0
  304. package/dist/manager/routes/payments/dispute-alert/post.js +117 -0
  305. package/dist/manager/routes/payments/dispute-alert/providers/chargeblast.js +55 -0
  306. package/dist/manager/routes/payments/intent/post.js +341 -0
  307. package/dist/manager/routes/payments/intent/providers/chargebee.js +197 -0
  308. package/dist/manager/routes/payments/intent/providers/coinbase.js +94 -0
  309. package/dist/manager/routes/payments/intent/providers/paypal.js +244 -0
  310. package/dist/manager/routes/payments/intent/providers/stripe.js +141 -0
  311. package/dist/manager/routes/payments/intent/providers/test.js +341 -0
  312. package/dist/manager/routes/payments/plan/post.js +147 -0
  313. package/dist/manager/routes/payments/plan/providers/chargebee.js +62 -0
  314. package/dist/manager/routes/payments/plan/providers/paypal.js +62 -0
  315. package/dist/manager/routes/payments/plan/providers/stripe.js +59 -0
  316. package/dist/manager/routes/payments/plan/providers/test.js +113 -0
  317. package/dist/manager/routes/payments/portal/post.js +113 -0
  318. package/dist/manager/routes/payments/portal/providers/chargebee.js +62 -0
  319. package/dist/manager/routes/payments/portal/providers/paypal.js +25 -0
  320. package/dist/manager/routes/payments/portal/providers/stripe.js +62 -0
  321. package/dist/manager/routes/payments/portal/providers/test.js +18 -0
  322. package/dist/manager/routes/payments/refund/post.js +207 -0
  323. package/dist/manager/routes/payments/refund/providers/chargebee.js +147 -0
  324. package/dist/manager/routes/payments/refund/providers/coinbase.js +40 -0
  325. package/dist/manager/routes/payments/refund/providers/paypal.js +239 -0
  326. package/dist/manager/routes/payments/refund/providers/stripe.js +160 -0
  327. package/dist/manager/routes/payments/refund/providers/test.js +204 -0
  328. package/dist/manager/routes/payments/trial-eligibility/get.js +29 -0
  329. package/dist/manager/routes/payments/uncancel/post.js +106 -0
  330. package/dist/manager/routes/payments/uncancel/providers/chargebee.js +30 -0
  331. package/dist/manager/routes/payments/uncancel/providers/paypal.js +25 -0
  332. package/dist/manager/routes/payments/uncancel/providers/stripe.js +27 -0
  333. package/dist/manager/routes/payments/uncancel/providers/test.js +114 -0
  334. package/dist/manager/routes/payments/webhook/post.js +157 -0
  335. package/dist/manager/routes/payments/webhook/providers/chargebee.js +218 -0
  336. package/dist/manager/routes/payments/webhook/providers/coinbase.js +91 -0
  337. package/dist/manager/routes/payments/webhook/providers/paypal.js +214 -0
  338. package/dist/manager/routes/payments/webhook/providers/stripe.js +182 -0
  339. package/dist/manager/routes/payments/webhook/providers/test.js +18 -0
  340. package/dist/manager/routes/payments/winback/post.js +276 -0
  341. package/dist/manager/routes/payments/winback/providers/chargebee.js +24 -0
  342. package/dist/manager/routes/payments/winback/providers/paypal.js +22 -0
  343. package/dist/manager/routes/payments/winback/providers/stripe.js +41 -0
  344. package/dist/manager/routes/payments/winback/providers/test.js +42 -0
  345. package/dist/manager/routes/restart/index.js +37 -0
  346. package/dist/manager/routes/special/electron-client/post.js +59 -0
  347. package/dist/manager/routes/test/authenticate/get.js +11 -0
  348. package/dist/manager/routes/test/health/get.js +8 -0
  349. package/dist/manager/routes/test/index.js +16 -0
  350. package/dist/manager/routes/test/lab/post.js +17 -0
  351. package/dist/manager/routes/test/redirect/get.js +27 -0
  352. package/dist/manager/routes/test/reset-account/post.js +79 -0
  353. package/dist/manager/routes/test/roster/get.js +40 -0
  354. package/dist/manager/routes/test/schema/post.js +20 -0
  355. package/dist/manager/routes/test/usage/post.js +44 -0
  356. package/dist/manager/routes/test/webhook/post.js +21 -0
  357. package/dist/manager/routes/user/api-keys/post.js +51 -0
  358. package/dist/manager/routes/user/connections/_context.js +194 -0
  359. package/dist/manager/routes/user/connections/_grant.js +247 -0
  360. package/dist/manager/routes/user/connections/_lease.js +184 -0
  361. package/dist/manager/routes/user/connections/_providers.js +191 -0
  362. package/dist/manager/routes/user/connections/_state.js +117 -0
  363. package/dist/manager/routes/user/connections/delete.js +55 -0
  364. package/dist/manager/routes/user/connections/get.js +167 -0
  365. package/dist/manager/routes/user/connections/post.js +370 -0
  366. package/dist/manager/routes/user/connections/providers/discord.js +41 -0
  367. package/dist/manager/routes/user/connections/providers/google.js +55 -0
  368. package/dist/manager/routes/user/connections/providers/kick.js +68 -0
  369. package/dist/manager/routes/user/connections/providers/spotify.js +40 -0
  370. package/dist/manager/routes/user/connections/providers/twitch.js +52 -0
  371. package/dist/manager/routes/user/data-request/delete.js +84 -0
  372. package/dist/manager/routes/user/data-request/get.js +229 -0
  373. package/dist/manager/routes/user/data-request/post.js +136 -0
  374. package/dist/manager/routes/user/delete.js +149 -0
  375. package/dist/manager/routes/user/feedback/post.js +81 -0
  376. package/dist/manager/routes/user/get.js +14 -0
  377. package/dist/manager/routes/user/orders/get.js +95 -0
  378. package/dist/manager/routes/user/sessions/delete.js +95 -0
  379. package/dist/manager/routes/user/sessions/get.js +40 -0
  380. package/dist/manager/routes/user/settings/validate/post.js +86 -0
  381. package/dist/manager/routes/user/signup/post.js +662 -0
  382. package/dist/manager/routes/user/subscription/get.js +77 -0
  383. package/dist/manager/routes/user/token/post.js +30 -0
  384. package/dist/manager/routes/verts/delete.js +43 -0
  385. package/dist/manager/routes/verts/get.js +49 -0
  386. package/dist/manager/routes/verts/post.js +61 -0
  387. package/dist/manager/routes/verts/put.js +75 -0
  388. package/dist/manager/routes/verts/redirect/get.js +36 -0
  389. package/dist/manager/routes/verts/serve/get.js +67 -0
  390. package/dist/manager/routes/verts/utils.js +436 -0
  391. package/dist/manager/schemas/admin/backup/post.js +8 -0
  392. package/dist/manager/schemas/admin/cron/post.js +8 -0
  393. package/dist/manager/schemas/admin/database/get.js +5 -0
  394. package/dist/manager/schemas/admin/database/post.js +6 -0
  395. package/dist/manager/schemas/admin/email/post.js +31 -0
  396. package/dist/manager/schemas/admin/firestore/get.js +5 -0
  397. package/dist/manager/schemas/admin/firestore/post.js +8 -0
  398. package/dist/manager/schemas/admin/firestore/query/post.js +5 -0
  399. package/dist/manager/schemas/admin/hook/post.js +8 -0
  400. package/dist/manager/schemas/admin/infer-contact/post.js +6 -0
  401. package/dist/manager/schemas/admin/notification/post.js +23 -0
  402. package/dist/manager/schemas/admin/payment/post.js +8 -0
  403. package/dist/manager/schemas/admin/post/post.js +23 -0
  404. package/dist/manager/schemas/admin/post/put.js +13 -0
  405. package/dist/manager/schemas/admin/repo/content/post.js +10 -0
  406. package/dist/manager/schemas/admin/stats/get.js +5 -0
  407. package/dist/manager/schemas/admin/users/disable/post.js +9 -0
  408. package/dist/manager/schemas/admin/users/list/get.js +10 -0
  409. package/dist/manager/schemas/admin/users/sync/post.js +7 -0
  410. package/dist/manager/schemas/brand/get.js +3 -0
  411. package/dist/manager/schemas/content/post/get.js +8 -0
  412. package/dist/manager/schemas/general/email/post.js +10 -0
  413. package/dist/manager/schemas/general/uuid/post.js +9 -0
  414. package/dist/manager/schemas/handler/post/post.js +13 -0
  415. package/dist/manager/schemas/health/get.js +3 -0
  416. package/dist/manager/schemas/marketing/campaign/delete.js +8 -0
  417. package/dist/manager/schemas/marketing/campaign/get.js +13 -0
  418. package/dist/manager/schemas/marketing/campaign/post.js +43 -0
  419. package/dist/manager/schemas/marketing/campaign/put.js +37 -0
  420. package/dist/manager/schemas/marketing/contact/delete.js +8 -0
  421. package/dist/manager/schemas/marketing/contact/post.js +14 -0
  422. package/dist/manager/schemas/marketing/contact/put.js +8 -0
  423. package/dist/manager/schemas/marketing/email-preferences/post.js +15 -0
  424. package/dist/manager/schemas/marketing/webhook/forward/post.js +8 -0
  425. package/dist/manager/schemas/marketing/webhook/post.js +7 -0
  426. package/dist/manager/schemas/payments/cancel/post.js +15 -0
  427. package/dist/manager/schemas/payments/discount/get.js +8 -0
  428. package/dist/manager/schemas/payments/dispute-alert/post.js +6 -0
  429. package/dist/manager/schemas/payments/intent/post.js +28 -0
  430. package/dist/manager/schemas/payments/plan/post.js +11 -0
  431. package/dist/manager/schemas/payments/portal/post.js +12 -0
  432. package/dist/manager/schemas/payments/refund/post.js +14 -0
  433. package/dist/manager/schemas/payments/trial-eligibility/get.js +7 -0
  434. package/dist/manager/schemas/payments/uncancel/post.js +9 -0
  435. package/dist/manager/schemas/payments/webhook/post.js +6 -0
  436. package/dist/manager/schemas/payments/winback/post.js +12 -0
  437. package/dist/manager/schemas/restart/index.js +5 -0
  438. package/dist/manager/schemas/special/electron-client/post.js +11 -0
  439. package/dist/manager/schemas/test/authenticate/get.js +3 -0
  440. package/dist/manager/schemas/test/health/get.js +3 -0
  441. package/dist/manager/schemas/test/index.js +5 -0
  442. package/dist/manager/schemas/test/lab/post.js +3 -0
  443. package/dist/manager/schemas/test/redirect/get.js +7 -0
  444. package/dist/manager/schemas/test/reset-account/post.js +3 -0
  445. package/dist/manager/schemas/test/roster/get.js +3 -0
  446. package/dist/manager/schemas/test/schema/post.js +70 -0
  447. package/dist/manager/schemas/test/usage/post.js +8 -0
  448. package/dist/manager/schemas/test/webhook/post.js +7 -0
  449. package/dist/manager/schemas/user/api-keys/post.js +6 -0
  450. package/dist/manager/schemas/user/connections/delete.js +8 -0
  451. package/dist/manager/schemas/user/connections/get.js +21 -0
  452. package/dist/manager/schemas/user/connections/post.js +12 -0
  453. package/dist/manager/schemas/user/data-request/delete.js +3 -0
  454. package/dist/manager/schemas/user/data-request/get.js +5 -0
  455. package/dist/manager/schemas/user/data-request/post.js +6 -0
  456. package/dist/manager/schemas/user/delete.js +6 -0
  457. package/dist/manager/schemas/user/feedback/post.js +8 -0
  458. package/dist/manager/schemas/user/get.js +3 -0
  459. package/dist/manager/schemas/user/orders/get.js +6 -0
  460. package/dist/manager/schemas/user/sessions/delete.js +6 -0
  461. package/dist/manager/schemas/user/sessions/get.js +6 -0
  462. package/dist/manager/schemas/user/settings/validate/post.js +8 -0
  463. package/dist/manager/schemas/user/signup/post.js +27 -0
  464. package/dist/manager/schemas/user/subscription/get.js +5 -0
  465. package/dist/manager/schemas/user/token/post.js +5 -0
  466. package/dist/manager/schemas/verts/delete.js +8 -0
  467. package/dist/manager/schemas/verts/get.js +9 -0
  468. package/dist/manager/schemas/verts/post.js +23 -0
  469. package/dist/manager/schemas/verts/put.js +20 -0
  470. package/dist/manager/schemas/verts/redirect/get.js +10 -0
  471. package/dist/manager/schemas/verts/serve/get.js +14 -0
  472. package/dist/manager/server-manager.js +69 -0
  473. package/dist/mcp/client.js +98 -0
  474. package/dist/mcp/handler.js +521 -0
  475. package/dist/mcp/index.js +157 -0
  476. package/dist/mcp/tools.js +555 -0
  477. package/dist/mcp/utils.js +108 -0
  478. package/dist/omega-bin.js +6 -0
  479. package/dist/require.js +3 -0
  480. package/dist/test/fixtures/firebase-project/.firebaserc +5 -0
  481. package/dist/test/fixtures/firebase-project/config/omega.json5 +131 -0
  482. package/dist/test/fixtures/firebase-project/database.rules.json +6 -0
  483. package/dist/test/fixtures/firebase-project/firebase.json +49 -0
  484. package/dist/test/fixtures/firebase-project/firestore.indexes.json +4 -0
  485. package/dist/test/fixtures/firebase-project/package.json +14 -0
  486. package/dist/test/fixtures/firebase-project/src/index.js +11 -0
  487. package/dist/test/fixtures/firebase-project/storage.rules +8 -0
  488. package/dist/test/parse-audit.js +25 -0
  489. package/dist/test/run-tests.js +90 -0
  490. package/dist/test/runner.js +982 -0
  491. package/dist/test/seed.js +242 -0
  492. package/dist/test/test-accounts.js +2660 -0
  493. package/dist/test/utils/assertions.js +189 -0
  494. package/dist/test/utils/email-capture.js +252 -0
  495. package/dist/test/utils/extended-mode-warning.js +11 -0
  496. package/dist/test/utils/firestore-rules-client.js +277 -0
  497. package/dist/test/utils/http-client.js +228 -0
  498. package/dist/test/utils/test-mode-file.js +192 -0
  499. package/dist/utils/merge-line-files.js +6 -0
  500. package/dist/utils/scaffold-defaults.js +71 -0
  501. package/dist/utils/test-lanes.js +62 -0
  502. package/dist/vendor/account/engine.js +182 -0
  503. package/dist/vendor/account/features.js +220 -0
  504. package/dist/vendor/account/index.js +53 -0
  505. package/dist/vendor/account/schema.js +272 -0
  506. package/dist/vendor/account/subscription.js +38 -0
  507. package/dist/vendor/analytics/adapters/ga4.js +26 -0
  508. package/dist/vendor/analytics/adapters/meta.js +26 -0
  509. package/dist/vendor/analytics/adapters/resolve.js +130 -0
  510. package/dist/vendor/analytics/adapters/tiktok.js +27 -0
  511. package/dist/vendor/analytics/catalog.js +908 -0
  512. package/dist/vendor/analytics/identity.js +136 -0
  513. package/dist/vendor/config/company.js +31 -0
  514. package/dist/vendor/config/defaults.js +173 -0
  515. package/dist/vendor/config/demo.js +18 -0
  516. package/dist/vendor/config/desktop-artifacts.js +110 -0
  517. package/dist/vendor/config/edit.js +769 -0
  518. package/dist/vendor/config/env-delivery.js +145 -0
  519. package/dist/vendor/config/env-rules.js +93 -0
  520. package/dist/vendor/config/env-schema.js +1078 -0
  521. package/dist/vendor/config/env.js +445 -0
  522. package/dist/vendor/config/hooks.js +97 -0
  523. package/dist/vendor/config/index.js +237 -0
  524. package/dist/vendor/config/instances.js +208 -0
  525. package/dist/vendor/config/load.js +490 -0
  526. package/dist/vendor/config/merge.js +68 -0
  527. package/dist/vendor/config/order.js +139 -0
  528. package/dist/vendor/config/ports.js +374 -0
  529. package/dist/vendor/config/providers.js +32 -0
  530. package/dist/vendor/config/repo.js +142 -0
  531. package/dist/vendor/config/retired-keys.js +430 -0
  532. package/dist/vendor/config/schema.js +1610 -0
  533. package/dist/vendor/config/secrets.js +50 -0
  534. package/dist/vendor/config/seed.js +34 -0
  535. package/dist/vendor/config/site-global.js +205 -0
  536. package/dist/vendor/config/validate.js +554 -0
  537. package/dist/vendor/config/winback.js +61 -0
  538. package/dist/vendor/devkit/attach-log-file.js +262 -0
  539. package/dist/vendor/devkit/bare-requires.js +153 -0
  540. package/dist/vendor/devkit/command-path.js +46 -0
  541. package/dist/vendor/devkit/defaults-engine.js +372 -0
  542. package/dist/vendor/devkit/deploy-record.js +180 -0
  543. package/dist/vendor/devkit/deploy.js +286 -0
  544. package/dist/vendor/devkit/license.js +155 -0
  545. package/dist/vendor/devkit/local-https.js +360 -0
  546. package/dist/vendor/devkit/local.js +1905 -0
  547. package/dist/vendor/devkit/logger.js +128 -0
  548. package/dist/vendor/devkit/merge-line-files.js +296 -0
  549. package/dist/vendor/devkit/npm-registry.js +52 -0
  550. package/dist/vendor/devkit/omega-bin.js +345 -0
  551. package/dist/vendor/devkit/parse-audit.js +74 -0
  552. package/dist/vendor/devkit/safe-install.js +18 -0
  553. package/dist/vendor/devkit/scaffold-guard.js +96 -0
  554. package/dist/vendor/devkit/stop-signals.js +28 -0
  555. package/dist/vendor/devkit/test/assert.js +120 -0
  556. package/dist/vendor/devkit/test/define-cases.js +104 -0
  557. package/dist/vendor/devkit/test/runner-core.js +554 -0
  558. package/dist/vendor/devkit/test/scope.js +162 -0
  559. package/dist/vendor/devkit/update.js +569 -0
  560. package/dist/vendor/monitoring/core.js +180 -0
  561. package/dist/vendor/monitoring/env.js +49 -0
  562. package/dist/vendor/monitoring/logger.js +39 -0
  563. package/dist/vendor/monitoring/node.js +72 -0
  564. package/docs/admin-post-route.md +57 -0
  565. package/docs/ai-library.md +171 -0
  566. package/docs/architecture.md +62 -0
  567. package/docs/audit.md +70 -0
  568. package/docs/auth-hooks.md +74 -0
  569. package/docs/build-system.md +65 -0
  570. package/docs/cdp-debugging.md +29 -0
  571. package/docs/cli-firestore-auth.md +85 -0
  572. package/docs/cli-logs.md +67 -0
  573. package/docs/cli-output.md +146 -0
  574. package/docs/code-patterns.md +77 -0
  575. package/docs/common-mistakes.md +12 -0
  576. package/docs/common-operations.md +60 -0
  577. package/docs/connections.md +212 -0
  578. package/docs/consent.md +362 -0
  579. package/docs/directory-structure.md +150 -0
  580. package/docs/email-system.md +459 -0
  581. package/docs/environment-detection.md +93 -0
  582. package/docs/file-naming.md +10 -0
  583. package/docs/firestore.md +134 -0
  584. package/docs/ghostii.md +240 -0
  585. package/docs/index.md +299 -0
  586. package/docs/key-files.md +37 -0
  587. package/docs/logging.md +60 -0
  588. package/docs/marketing-campaigns.md +407 -0
  589. package/docs/marketing-fields.md +25 -0
  590. package/docs/mcp.md +222 -0
  591. package/docs/migration.md +129 -0
  592. package/docs/payment-system.md +856 -0
  593. package/docs/paypal-sandbox-qa.md +105 -0
  594. package/docs/response-headers.md +9 -0
  595. package/docs/routes.md +211 -0
  596. package/docs/sanitization.md +71 -0
  597. package/docs/schemas.md +163 -0
  598. package/docs/shared/agent-docs.md +89 -0
  599. package/docs/shared/analytics.md +612 -0
  600. package/docs/shared/brands.md +51 -0
  601. package/docs/shared/breaking-changes.md +497 -0
  602. package/docs/shared/config.md +1387 -0
  603. package/docs/shared/deploys.md +215 -0
  604. package/docs/shared/icons.md +201 -0
  605. package/docs/shared/local-dev.md +147 -0
  606. package/docs/shared/logging.md +202 -0
  607. package/docs/shared/monitoring.md +153 -0
  608. package/docs/shared/publishing.md +183 -0
  609. package/docs/shared/rulings.md +34 -0
  610. package/docs/shared/testing.md +147 -0
  611. package/docs/shared/theming.md +604 -0
  612. package/docs/shared/translation.md +291 -0
  613. package/docs/shared/updates.md +61 -0
  614. package/docs/stripe-webhook-forwarding.md +20 -0
  615. package/docs/test-boot-layer.md +67 -0
  616. package/docs/test-framework.md +583 -0
  617. package/docs/usage-rate-limiting.md +121 -0
  618. package/docs/verts.md +29 -0
  619. package/package.json +143 -0
  620. package/templates/config/omega.json5 +322 -0
  621. package/templates/database.rules.json +82 -0
  622. package/templates/firebase.json +67 -0
  623. package/templates/firestore.framework.rules +191 -0
  624. package/templates/firestore.indexes.json +4 -0
  625. package/templates/firestore.rules +59 -0
  626. package/templates/index.js +12 -0
  627. package/templates/public/404.html +26 -0
  628. package/templates/public/index.html +24 -0
  629. package/templates/remoteconfig.template.json +1 -0
  630. package/templates/storage-lifecycle-config-1-day.json +9 -0
  631. package/templates/storage-lifecycle-config-30-days.json +9 -0
  632. package/templates/storage.rules +11 -0
  633. package/test/_init/accounts-validation.js +58 -0
  634. package/test/ai/tools-live.test.js +171 -0
  635. package/test/analytics/conversion-delivery.test.js +1013 -0
  636. package/test/analytics/match-normalization.test.js +238 -0
  637. package/test/analytics/signup-conversion.test.js +135 -0
  638. package/test/boot/cli-dispatch.test.js +142 -0
  639. package/test/boot/defaults-scaffold.test.js +248 -0
  640. package/test/boot/deploy-staging.test.js +446 -0
  641. package/test/boot/disposable-domains.test.js +154 -0
  642. package/test/boot/emulator-boots.test.js +39 -0
  643. package/test/boot/emulator-port-preflight.test.js +126 -0
  644. package/test/boot/emulator-ready-timeout.test.js +46 -0
  645. package/test/boot/project-id-refresh.test.js +73 -0
  646. package/test/boot/suite-portability.test.js +278 -0
  647. package/test/boot/update-command.test.js +63 -0
  648. package/test/cli/cross-platform.test.js +168 -0
  649. package/test/cli/custom-project-type.test.js +426 -0
  650. package/test/cli/deploy-license-env.test.js +100 -0
  651. package/test/cli/emulator-adoption.test.js +254 -0
  652. package/test/cli/emulator-orphans.test.js +658 -0
  653. package/test/cli/emulator-ownership.test.js +177 -0
  654. package/test/cli/emulator-port-retry.test.js +395 -0
  655. package/test/cli/emulator-shutdown.test.js +788 -0
  656. package/test/cli/emulator-stale-reap.test.js +574 -0
  657. package/test/cli/ensure-target.test.js +325 -0
  658. package/test/cli/flags.test.js +64 -0
  659. package/test/cli/https-trust.test.js +38 -0
  660. package/test/cli/install-update-dispatch.test.js +47 -0
  661. package/test/cli/lane-environments.test.js +159 -0
  662. package/test/cli/marketing-campaigns-seeded.test.js +194 -0
  663. package/test/cli/migrate-bare-requires.test.js +128 -0
  664. package/test/cli/migrate-markers.test.js +530 -0
  665. package/test/cli/required-indexes.test.js +64 -0
  666. package/test/cli/rules-compile.test.js +622 -0
  667. package/test/cli/rules-migration-deferral.test.js +259 -0
  668. package/test/cli/rules-version.test.js +104 -0
  669. package/test/cli/setup-load-files.test.js +66 -0
  670. package/test/cli/setup-offline-mode.test.js +276 -0
  671. package/test/cli/setup-retired.test.js +64 -0
  672. package/test/cli/setup-shared-project-indexes.test.js +262 -0
  673. package/test/cli/setup-tests-requires.test.js +45 -0
  674. package/test/cli/stage-env-compose.test.js +316 -0
  675. package/test/cli/stripe-live-lane.test.js +303 -0
  676. package/test/cli/target.test.js +182 -0
  677. package/test/cli/templates.test.js +66 -0
  678. package/test/cli/test-runner-env.test.js +115 -0
  679. package/test/cli/test-stack-shutdown.test.js +275 -0
  680. package/test/cli/test-target-no-match.test.js +128 -0
  681. package/test/cli/verb-logs.test.js +118 -0
  682. package/test/cli/version-dispatch.test.js +39 -0
  683. package/test/content/blog-generate.test.js +164 -0
  684. package/test/email/campaign-config-fault.test.js +376 -0
  685. package/test/email/campaign-cron-pipeline.test.js +536 -0
  686. package/test/email/campaign-send.test.js +47 -0
  687. package/test/email/consent-lifecycle.test.js +258 -0
  688. package/test/email/content-html-policy.test.js +270 -0
  689. package/test/email/feedback-and-plain-send.test.js +54 -0
  690. package/test/email/fixtures/clean.json +30 -0
  691. package/test/email/fixtures/editorial.json +30 -0
  692. package/test/email/fixtures/field-report.json +53 -0
  693. package/test/email/identity.test.js +321 -0
  694. package/test/email/marketing/consent-gate.test.js +265 -0
  695. package/test/email/marketing/custom-fields-catalog.test.js +259 -0
  696. package/test/email/marketing/prune-per-provider.test.js +762 -0
  697. package/test/email/marketing/remove-log-privacy.test.js +225 -0
  698. package/test/email/marketing-lifecycle.test.js +139 -0
  699. package/test/email/newsletter-generate.test.js +853 -0
  700. package/test/email/newsletter-svg-tokens.test.js +51 -0
  701. package/test/email/newsletter-templates.test.js +492 -0
  702. package/test/email/order-one-time-cta.test.js +97 -0
  703. package/test/email/render-content.test.js +248 -0
  704. package/test/email/safe-url.test.js +0 -0
  705. package/test/email/sanitize-images.test.js +66 -0
  706. package/test/email/send-log-privacy.test.js +107 -0
  707. package/test/email/templates.test.js +288 -0
  708. package/test/email/testing-capture.test.js +260 -0
  709. package/test/email/transactional-send.test.js +36 -0
  710. package/test/email/transactional.test.js +562 -0
  711. package/test/email/unsubscribe-groups.test.js +155 -0
  712. package/test/email/unsubscribe-key.test.js +91 -0
  713. package/test/email/validation-cases.test.js +152 -0
  714. package/test/email/validation.test.js +746 -0
  715. package/test/events/auth-delete-conversion.test.js +138 -0
  716. package/test/events/auth-delete-race.test.js +211 -0
  717. package/test/events/auth-on-create-log.test.js +164 -0
  718. package/test/events/auth-signup-conversion.test.js +141 -0
  719. package/test/events/auth-signup-limit.test.js +142 -0
  720. package/test/events/auth-trigger-log-privacy.test.js +256 -0
  721. package/test/events/cron-job-doc-shape.test.js +101 -0
  722. package/test/events/cron-reset-usage.test.js +116 -0
  723. package/test/events/notification-conversion.test.js +182 -0
  724. package/test/events/payments/_webhook-harness.js +307 -0
  725. package/test/events/payments/abandoned-cart-activity.test.js +135 -0
  726. package/test/events/payments/analytics-payment-events.test.js +945 -0
  727. package/test/events/payments/dispute-email-status.test.js +55 -0
  728. package/test/events/payments/journey-payments-abandoned.test.js +126 -0
  729. package/test/events/payments/journey-payments-cancel-endpoint.test.js +102 -0
  730. package/test/events/payments/journey-payments-cancel-no-order.test.js +76 -0
  731. package/test/events/payments/journey-payments-cancel.test.js +184 -0
  732. package/test/events/payments/journey-payments-decline.test.js +225 -0
  733. package/test/events/payments/journey-payments-discount.test.js +91 -0
  734. package/test/events/payments/journey-payments-dispute.test.js +190 -0
  735. package/test/events/payments/journey-payments-failure.test.js +148 -0
  736. package/test/events/payments/journey-payments-legacy-product.test.js +151 -0
  737. package/test/events/payments/journey-payments-one-time-decline.test.js +127 -0
  738. package/test/events/payments/journey-payments-one-time-failure.test.js +113 -0
  739. package/test/events/payments/journey-payments-one-time-refund.test.js +186 -0
  740. package/test/events/payments/journey-payments-one-time.test.js +175 -0
  741. package/test/events/payments/journey-payments-plan-change.test.js +146 -0
  742. package/test/events/payments/journey-payments-plan-switch-trial.test.js +161 -0
  743. package/test/events/payments/journey-payments-plan-switch.test.js +182 -0
  744. package/test/events/payments/journey-payments-refund-no-order.test.js +76 -0
  745. package/test/events/payments/journey-payments-refund-webhook.test.js +225 -0
  746. package/test/events/payments/journey-payments-suspend.test.js +183 -0
  747. package/test/events/payments/journey-payments-trial-cancel.test.js +132 -0
  748. package/test/events/payments/journey-payments-trial.test.js +173 -0
  749. package/test/events/payments/journey-payments-uid-resolution.test.js +134 -0
  750. package/test/events/payments/journey-payments-uncancel.test.js +157 -0
  751. package/test/events/payments/journey-payments-upgrade.test.js +137 -0
  752. package/test/events/payments/journey-payments-winback-decline.test.js +171 -0
  753. package/test/events/payments/journey-payments-winback.test.js +195 -0
  754. package/test/events/payments/paypal-expiry-cron.test.js +166 -0
  755. package/test/events/payments/purchase-failed-handler.test.js +80 -0
  756. package/test/events/payments/purchase-refunded-handler.test.js +108 -0
  757. package/test/events/payments/test-processor-doc-shape.test.js +162 -0
  758. package/test/events/payments/transition-order-emails.test.js +185 -0
  759. package/test/events/payments/transition-promo-lines.test.js +192 -0
  760. package/test/events/payments/transitions-detect.test.js +767 -0
  761. package/test/events/payments/trial-lapse-sweep-staleness.test.js +173 -0
  762. package/test/events/payments/trial-lapse-sweep.test.js +359 -0
  763. package/test/events/payments/webhook-atomic-writes.test.js +69 -0
  764. package/test/events/payments/webhook-chargebee-unreachable.test.js +106 -0
  765. package/test/events/payments/webhook-discount-clear.test.js +189 -0
  766. package/test/events/payments/webhook-failure-intent.test.js +93 -0
  767. package/test/events/payments/webhook-hosted-page-uid-trust.test.js +212 -0
  768. package/test/events/payments/webhook-ordering.test.js +326 -0
  769. package/test/events/payments/webhook-provider-lookup-trust.test.js +206 -0
  770. package/test/events/payments/webhook-refund-amount-trust.test.js +223 -0
  771. package/test/events/payments/webhook-refund-envelope-shape.test.js +148 -0
  772. package/test/events/payments/webhook-refund-linkage-trust.test.js +404 -0
  773. package/test/events/payments/webhook-refund-transaction-fallback.test.js +184 -0
  774. package/test/events/payments/webhook-refund-without-order.test.js +170 -0
  775. package/test/events/payments/webhook-refusal-reporting.test.js +194 -0
  776. package/test/events/payments/webhook-refused-intent.test.js +142 -0
  777. package/test/events/payments/webhook-retry-sweep.test.js +252 -0
  778. package/test/events/payments/webhook-transition-claim.test.js +389 -0
  779. package/test/events/payments/webhook-uid-trust.test.js +203 -0
  780. package/test/events/payments/webhook-user-without-auth.test.js +161 -0
  781. package/test/fixtures/chargebee/invoice-one-time.json +27 -0
  782. package/test/fixtures/chargebee/subscription-active.json +44 -0
  783. package/test/fixtures/chargebee/subscription-cancelled.json +42 -0
  784. package/test/fixtures/chargebee/subscription-in-trial.json +41 -0
  785. package/test/fixtures/chargebee/subscription-legacy-plan.json +41 -0
  786. package/test/fixtures/chargebee/subscription-non-renewing.json +41 -0
  787. package/test/fixtures/chargebee/subscription-paused.json +42 -0
  788. package/test/fixtures/chargebee/webhook-payment-failed.json +51 -0
  789. package/test/fixtures/chargebee/webhook-subscription-created.json +47 -0
  790. package/test/fixtures/coinbase/charge-confirmed.json +50 -0
  791. package/test/fixtures/coinbase/charge-failed.json +34 -0
  792. package/test/fixtures/coinbase/charge-pending.json +34 -0
  793. package/test/fixtures/migrate/ported-route.js.txt +11 -0
  794. package/test/fixtures/paypal/capture-completed.json +51 -0
  795. package/test/fixtures/paypal/capture-refunded.json +43 -0
  796. package/test/fixtures/paypal/order-approved.json +62 -0
  797. package/test/fixtures/paypal/order-completed.json +110 -0
  798. package/test/fixtures/paypal/sale-refunded.json +38 -0
  799. package/test/fixtures/paypal/subscription-active.json +76 -0
  800. package/test/fixtures/paypal/subscription-cancelled.json +50 -0
  801. package/test/fixtures/paypal/subscription-suspended.json +65 -0
  802. package/test/fixtures/stripe/checkout-session-completed.json +130 -0
  803. package/test/fixtures/stripe/invoice-payment-failed.json +148 -0
  804. package/test/fixtures/stripe/invoice-subscription-payment-failed.json +28 -0
  805. package/test/fixtures/stripe/invoice-subscription-payment-succeeded.json +28 -0
  806. package/test/fixtures/stripe/subscription-active.json +161 -0
  807. package/test/fixtures/stripe/subscription-canceled.json +161 -0
  808. package/test/fixtures/stripe/subscription-trialing.json +161 -0
  809. package/test/helpers/_shared-config.js +15 -0
  810. package/test/helpers/ai-request-payload.test.js +619 -0
  811. package/test/helpers/ai-schema-resolve.test.js +125 -0
  812. package/test/helpers/ai-test-provider.test.js +227 -0
  813. package/test/helpers/ai-token-accounting.test.js +150 -0
  814. package/test/helpers/ai-tools-format.test.js +384 -0
  815. package/test/helpers/analytics-no-id-notice.test.js +146 -0
  816. package/test/helpers/analytics-user-data.test.js +194 -0
  817. package/test/helpers/api-manager.test.js +311 -0
  818. package/test/helpers/backend-router.test.js +136 -0
  819. package/test/helpers/content/blog-auto-publisher.test.js +482 -0
  820. package/test/helpers/content/feed-parser.test.js +529 -0
  821. package/test/helpers/content/ghostii-blocks.test.js +135 -0
  822. package/test/helpers/content/ghostii-feed-integration.test.js +405 -0
  823. package/test/helpers/content/ghostii-write-article.test.js +244 -0
  824. package/test/helpers/dev-only-routes.test.js +225 -0
  825. package/test/helpers/env-reader.test.js +362 -0
  826. package/test/helpers/environment.test.js +263 -0
  827. package/test/helpers/event-middleware.test.js +407 -0
  828. package/test/helpers/infer-contact.test.js +157 -0
  829. package/test/helpers/lane-url.test.js +90 -0
  830. package/test/helpers/merge-line-files.test.js +272 -0
  831. package/test/helpers/metadata.test.js +122 -0
  832. package/test/helpers/middleware-request-log.test.js +134 -0
  833. package/test/helpers/middleware-user-log.test.js +124 -0
  834. package/test/helpers/payment/chargebee/parse-webhook.test.js +490 -0
  835. package/test/helpers/payment/chargebee/refund-details.test.js +198 -0
  836. package/test/helpers/payment/chargebee/to-unified-one-time.test.js +148 -0
  837. package/test/helpers/payment/chargebee/to-unified-subscription.test.js +649 -0
  838. package/test/helpers/payment/coinbase/create-intent.test.js +247 -0
  839. package/test/helpers/payment/coinbase/parse-webhook.test.js +195 -0
  840. package/test/helpers/payment/coinbase/refund-unsupported.test.js +141 -0
  841. package/test/helpers/payment/coinbase/to-unified-one-time.test.js +201 -0
  842. package/test/helpers/payment/discount-codes.test.js +141 -0
  843. package/test/helpers/payment/extract-resource.test.js +113 -0
  844. package/test/helpers/payment/fetch-failure.test.js +215 -0
  845. package/test/helpers/payment/license-gate.test.js +108 -0
  846. package/test/helpers/payment/order-id.test.js +99 -0
  847. package/test/helpers/payment/paypal/create-intent.test.js +382 -0
  848. package/test/helpers/payment/paypal/fetch-capture.test.js +123 -0
  849. package/test/helpers/payment/paypal/fetch-sale.test.js +183 -0
  850. package/test/helpers/payment/paypal/parse-webhook.test.js +678 -0
  851. package/test/helpers/payment/paypal/refund-details.test.js +202 -0
  852. package/test/helpers/payment/paypal/resolve-plan-id.test.js +108 -0
  853. package/test/helpers/payment/paypal/switch-plan.test.js +92 -0
  854. package/test/helpers/payment/paypal/to-unified-one-time.test.js +383 -0
  855. package/test/helpers/payment/paypal/to-unified-subscription.test.js +884 -0
  856. package/test/helpers/payment/stripe/fetch-charge.test.js +147 -0
  857. package/test/helpers/payment/stripe/parse-webhook.test.js +448 -0
  858. package/test/helpers/payment/stripe/refund-details.test.js +147 -0
  859. package/test/helpers/payment/stripe/to-unified-one-time.test.js +307 -0
  860. package/test/helpers/payment/stripe/to-unified-subscription.test.js +709 -0
  861. package/test/helpers/persona-domain.test.js +87 -0
  862. package/test/helpers/recaptcha.test.js +166 -0
  863. package/test/helpers/resolved-config.test.js +106 -0
  864. package/test/helpers/response-log-redaction.test.js +262 -0
  865. package/test/helpers/roles.test.js +155 -0
  866. package/test/helpers/route-context-debug-gate.test.js +118 -0
  867. package/test/helpers/route-context-logging.test.js +214 -0
  868. package/test/helpers/route-context.test.js +129 -0
  869. package/test/helpers/safe-compare.test.js +114 -0
  870. package/test/helpers/sanitize.test.js +223 -0
  871. package/test/helpers/schema-engine.test.js +537 -0
  872. package/test/helpers/schema-zod.test.js +554 -0
  873. package/test/helpers/seed-accounts-load.test.js +163 -0
  874. package/test/helpers/seed-google-personas.test.js +114 -0
  875. package/test/helpers/seeded-personas.test.js +823 -0
  876. package/test/helpers/settings.test.js +292 -0
  877. package/test/helpers/setup-engines-pin.test.js +68 -0
  878. package/test/helpers/setup-manifest-sync.test.js +116 -0
  879. package/test/helpers/slugify.test.js +395 -0
  880. package/test/helpers/storage.test.js +195 -0
  881. package/test/helpers/test-banner-latch.test.js +139 -0
  882. package/test/helpers/usage-consume.test.js +460 -0
  883. package/test/helpers/usage-log-privacy.test.js +121 -0
  884. package/test/helpers/user-doc-heal.test.js +540 -0
  885. package/test/helpers/user.test.js +771 -0
  886. package/test/helpers/webhook-forward.test.js +419 -0
  887. package/test/helpers/wipe-auth-project.test.js +143 -0
  888. package/test/mcp/discovery.test.js +52 -0
  889. package/test/mcp/oauth.test.js +160 -0
  890. package/test/mcp/protocol.test.js +278 -0
  891. package/test/mcp/roles.test.js +203 -0
  892. package/test/mcp/utils.test.js +245 -0
  893. package/test/notification/identity.test.js +155 -0
  894. package/test/routes/admin/create-post.test.js +362 -0
  895. package/test/routes/admin/database.test.js +133 -0
  896. package/test/routes/admin/deduplicate-image-alts.test.js +191 -0
  897. package/test/routes/admin/email-content-html.test.js +58 -0
  898. package/test/routes/admin/email-request-log.test.js +86 -0
  899. package/test/routes/admin/email.test.js +116 -0
  900. package/test/routes/admin/firestore-query.test.js +206 -0
  901. package/test/routes/admin/firestore.test.js +129 -0
  902. package/test/routes/admin/infer-contact.test.js +220 -0
  903. package/test/routes/admin/notification.test.js +199 -0
  904. package/test/routes/admin/post-convert-image.test.js +159 -0
  905. package/test/routes/admin/post-deploy-flag.test.js +81 -0
  906. package/test/routes/admin/post-download-error.test.js +90 -0
  907. package/test/routes/admin/post-resize-image.test.js +185 -0
  908. package/test/routes/admin/post.test.js +369 -0
  909. package/test/routes/admin/repo-content.test.js +223 -0
  910. package/test/routes/admin/stats.test.js +114 -0
  911. package/test/routes/admin/users-disable.test.js +63 -0
  912. package/test/routes/admin/users-list.test.js +71 -0
  913. package/test/routes/content/post.test.js +60 -0
  914. package/test/routes/general/uuid.test.js +133 -0
  915. package/test/routes/health.test.js +110 -0
  916. package/test/routes/marketing/campaign.test.js +184 -0
  917. package/test/routes/marketing/contact.test.js +416 -0
  918. package/test/routes/marketing/email-preferences.test.js +293 -0
  919. package/test/routes/marketing/push-send.test.js +34 -0
  920. package/test/routes/marketing/webhook-forward.test.js +63 -0
  921. package/test/routes/marketing/webhook.test.js +641 -0
  922. package/test/routes/payments/_route-harness.js +145 -0
  923. package/test/routes/payments/cancel-provider-errors.test.js +227 -0
  924. package/test/routes/payments/cancel-skip-guards.test.js +126 -0
  925. package/test/routes/payments/cancel-trialing.test.js +428 -0
  926. package/test/routes/payments/cancel.test.js +165 -0
  927. package/test/routes/payments/dedup-race.test.js +186 -0
  928. package/test/routes/payments/discount.test.js +82 -0
  929. package/test/routes/payments/dispute-alert.test.js +324 -0
  930. package/test/routes/payments/intent-discount-amounts.test.js +550 -0
  931. package/test/routes/payments/intent-discount-coupons.test.js +293 -0
  932. package/test/routes/payments/intent-one-time-metadata.test.js +122 -0
  933. package/test/routes/payments/intent-purchaser-guard.test.js +139 -0
  934. package/test/routes/payments/intent-zero-total.test.js +260 -0
  935. package/test/routes/payments/intent.test.js +413 -0
  936. package/test/routes/payments/plan.test.js +367 -0
  937. package/test/routes/payments/portal-return-url.test.js +107 -0
  938. package/test/routes/payments/portal.test.js +94 -0
  939. package/test/routes/payments/refund-one-time.test.js +254 -0
  940. package/test/routes/payments/refund-paypal-proration.test.js +190 -0
  941. package/test/routes/payments/refund.test.js +181 -0
  942. package/test/routes/payments/trial-eligibility.test.js +73 -0
  943. package/test/routes/payments/uncancel.test.js +270 -0
  944. package/test/routes/payments/webhook-stripe-invoice.test.js +129 -0
  945. package/test/routes/payments/webhook-stripe-refund-one-time.test.js +130 -0
  946. package/test/routes/payments/webhook-test-provider.test.js +156 -0
  947. package/test/routes/payments/webhook.test.js +115 -0
  948. package/test/routes/payments/winback-stripe-coupon.test.js +126 -0
  949. package/test/routes/payments/winback.test.js +597 -0
  950. package/test/routes/test/authenticate.test.js +79 -0
  951. package/test/routes/test/redirect.test.js +66 -0
  952. package/test/routes/test/reset-account.test.js +126 -0
  953. package/test/routes/test/roster.test.js +101 -0
  954. package/test/routes/test/schema.test.js +556 -0
  955. package/test/routes/test/usage.test.js +374 -0
  956. package/test/routes/user/api-keys.test.js +158 -0
  957. package/test/routes/user/connections-grant.test.js +759 -0
  958. package/test/routes/user/connections-identity.test.js +260 -0
  959. package/test/routes/user/connections-log-privacy.test.js +206 -0
  960. package/test/routes/user/connections-refresh-lease.test.js +696 -0
  961. package/test/routes/user/connections-return.test.js +261 -0
  962. package/test/routes/user/connections-uid.test.js +516 -0
  963. package/test/routes/user/delete.test.js +138 -0
  964. package/test/routes/user/feedback.test.js +106 -0
  965. package/test/routes/user/orders.test.js +233 -0
  966. package/test/routes/user/sessions.test.js +231 -0
  967. package/test/routes/user/settings-validate.test.js +84 -0
  968. package/test/routes/user/signup-emails.test.js +113 -0
  969. package/test/routes/user/signup-location.test.js +84 -0
  970. package/test/routes/user/signup-log-privacy.test.js +112 -0
  971. package/test/routes/user/signup.test.js +579 -0
  972. package/test/routes/user/subscription.test.js +101 -0
  973. package/test/routes/user/token.test.js +111 -0
  974. package/test/routes/user/user.test.js +158 -0
  975. package/test/routes/verts/cache.test.js +78 -0
  976. package/test/routes/verts/click-destination.test.js +76 -0
  977. package/test/routes/verts/crud.test.js +194 -0
  978. package/test/routes/verts/redirect.test.js +147 -0
  979. package/test/routes/verts/selection.test.js +237 -0
  980. package/test/routes/verts/serve.test.js +185 -0
  981. package/test/routes/verts/unit-document.test.js +117 -0
  982. package/test/rules/_environment.js +66 -0
  983. package/test/rules/brand-merge.test.js +195 -0
  984. package/test/rules/field-helpers.test.js +309 -0
  985. package/test/rules/notifications.test.js +577 -0
  986. package/test/rules/payments-carts.test.js +406 -0
  987. package/test/rules/sessions.test.js +170 -0
  988. package/test/rules/user.test.js +517 -0
  989. package/test/rules/verts.test.js +101 -0
  990. package/test/security/fetch-log-secrets.test.js +124 -0
  991. package/test/security/primitives.test.js +105 -0
  992. package/test/security/repo-pinning.test.js +77 -0
  993. package/test/stripe-live/subscription-lifecycle.test.js +141 -0
@@ -0,0 +1,604 @@
1
+ # Theming — the OMEGA design system contract
2
+
3
+ > classy v2 (the flagship skin) + the shared machinery every theme and, at C4,
4
+ > every target rides. Visual spec: [docs/web/classy-v2/DIRECTION.md](../web/classy-v2/DIRECTION.md).
5
+
6
+ The one-hex/one-token half of this contract is checkable per edit: the plugin's `omega:brandcheck` skill,
7
+ [agent-plugins/claude/skills/brandcheck/SKILL.md](../../agent-plugins/claude/skills/brandcheck/SKILL.md), and
8
+ its quality hook fires on every stylesheet edit (contrast, focus, and reduced-motion checks ride `omega:accessibility`).
9
+
10
+ ## The three layers
11
+
12
+ 1. **Tokens** — `packages/web/core/css/tokens/_index.scss` emits the
13
+ `--omega-*` custom-property contract: neutrals (`ground`, `surface`,
14
+ `surface-2`, `ink`, `ink-muted`, `ink-faint`, `line`, `line-strong`), the
15
+ accent family (`accent`, `-hover`, `-active`, `-subtle`, `-ink`, `-ring`),
16
+ status (`ok`/`warn`/`danger`, each with an `-rgb` channel twin — see
17
+ "One status hue site-wide" below), the categorical ramp (`chart-1`…`chart-6` —
18
+ series-1 rides the accent, the rest are CVD-validated muted hues, cool half
19
+ before warm), shadows
20
+ (`shadow-1`/`shadow-2`), shape (`radius-xs/s/m/l/xl`), motion (`speed`,
21
+ `speed-slow`, `speed-first-paint`, `ease`), and the type slots (`font-ui`, `font-serif`,
22
+ `font-mono`, plus the pairing slots `font-marketing` and `font-display`).
23
+ Light + dark values ship together: OS preference carries via
24
+ `@media (prefers-color-scheme: dark)`, and the `data-bs-theme` stamp
25
+ (core/js/core/appearance.js) beats it in both directions. **Names are the
26
+ stable API; values are the skin.**
27
+ 2. **Mechanics** — `core/css/shell/_index.scss` (the `.omega-shell` app
28
+ chrome: 264px sidebar / 68px rail / 60px topbar, drawer under 1200px; the
29
+ rail clips nothing — its nav scrolls inside the `__sidebar-scroll` region
30
+ so rail popovers can escape, [#319](https://github.com/Omega-JS-Stack/omega/issues/319);
31
+ main claims the `1fr` row explicitly, so a page that turns the topbar off
32
+ still scrolls inside main instead of handing the scroll back to the
33
+ document, [#741](https://github.com/Omega-JS-Stack/omega/issues/741)),
34
+ `core/css/motion/_index.scss` (below), and `core/css/components/_index.scss`
35
+ (the shared component vocabulary: `omega-chip` and its `--accent`/`--ink`
36
+ modifiers, plus the app-chrome pair the base topbar/sidebar renders —
37
+ `.btn-icon`, the square ghost icon button, and `.omega-search`, the ⌘K field
38
+ with its input and its `kbd` shortcut badge). All three paint exclusively through tokens so a skin restyles
39
+ them without touching structure, and all three emit BEFORE the theme so a
40
+ skin's own rules win. **Promotion rule
41
+ ([#242](https://github.com/Omega-JS-Stack/omega/issues/242))**: a component
42
+ the BASE layer renders belongs in the component sheet, never in one theme's
43
+ partials — the icon-CSS ruling (presentation SSOT in the shared sheet).
44
+ Theme-only components (classy's marketing vocabulary) stay in their theme.
45
+ 3. **Theme** — `themes/<id>/`, a skin over `themes/base` (the structural
46
+ `omega-*` markup layer every theme chain terminates at; classy is the
47
+ flagship and the default skin). classy v2's `css/base/_root.scss` bridges Bootstrap's CSS
48
+ variables onto the tokens, so every Bootstrap component follows the theme,
49
+ the brand ramp, and consumer overrides with zero recompilation.
50
+
51
+ ## The three surface tiers (and which way elevation runs)
52
+
53
+ `--omega-ground` is the page, `--omega-surface` is a card lifted off it, and
54
+ `--omega-surface-2` is the second tier: table heads, chips, code blocks, hover
55
+ fills, receipt panels, segmented tracks. The tiers move in OPPOSITE directions
56
+ per mode, and a component may not assume one of them: dark elevates by getting
57
+ LIGHTER (`#0d0d0e` ground → `#151516` surface → `#1d1d1f` surface-2), light
58
+ elevates with white plus a shadow and recesses by getting DARKER (`#f5f5f4`
59
+ ground → `#ffffff` surface → `#ececeb` surface-2, the well).
60
+
61
+ Light surface-2 shipped at `#f6f6f5`, one point off the ground, so every
62
+ component that painted it directly on the page was invisible in light mode and
63
+ correct in dark ([#152](https://github.com/Omega-JS-Stack/omega/issues/152)).
64
+ The value is now a real well: readable on the ground AND inside a white card,
65
+ still lighter than `--omega-line`. Its Bootstrap twins move with it:
66
+ `$classy-surface-2-light` (compile-time `$body-secondary-bg`) and
67
+ `--bs-secondary-bg-rgb` in classy's root bridge.
68
+
69
+ Two readings of a segmented control are both sanctioned, and both work in both
70
+ modes: a RAISED track (surface + `--omega-line-strong` + `shadow-1`, selected
71
+ segment recessed to surface-2, the billing toggle) or a RECESSED track
72
+ (surface-2 straight on the ground, selected segment raised to white, the
73
+ platform rail).
74
+
75
+ ## brand.color → the accent ramps
76
+
77
+ `omega.json5 → brand.color` is an optional hex string (`#RGB` / `#RRGGBB`,
78
+ schema-validated since [#272](https://github.com/Omega-JS-Stack/omega/issues/272);
79
+ unset leaves the token sheet's neutral placeholder standing) and it drives
80
+ `composeBrandTokens()` (`packages/web/src/brand-tokens.js`): a light ramp plus a **dark-mode variant**
81
+ (darker brands lift into a legible lightness band for the charcoal ground;
82
+ already-light brands pass through). `core/_includes/core/head.html` emits both
83
+ as inline `:root` blocks AFTER the css bundles — same three-stamp plumbing as
84
+ the token sheet — so the ramp wins the cascade everywhere, including
85
+ `.btn-primary`, links, focus rings, `.form-check-input:checked`,
86
+ `.progress-bar`, and `.text-primary`/`.bg-primary` (classy re-points those at
87
+ the tokens).
88
+
89
+ ## Consumer customization (tier 1 — main.scss)
90
+
91
+ - **Discovery**: `omega customize --list` prints the layered override map —
92
+ every shadowable file (sections, includes, css, pages), its owning layer
93
+ (framework / `theme:<id>` / consumer), and whether you already shadow it.
94
+ `omega customize <path>` materializes ONE of them at that path with a
95
+ provenance header; reading the theme source tree to guess a path is over.
96
+ The css lane lists only what sass actually layers by — `main.scss` and the
97
+ page sheets — because a bare relative `@use`/`@import` inside a theme sheet
98
+ resolves against the importing file, so a consumer copy of a partial would
99
+ never load.
100
+ - **Recolor**: set `brand.color` in omega.json5. No CSS.
101
+ - **Sass knobs**: every variable in `themes/classy/_config.scss` is `!default`
102
+ — `@use 'omega:main' with ($primary: …, $font-family-sans-serif: …,
103
+ $border-radius: …)` from the consumer main.scss.
104
+ - **Token overrides**: redefine any `--omega-*` property in `:root` /
105
+ `[data-bs-theme='dark']` for surgical re-vibing (grays, radii, speeds,
106
+ shadows, type).
107
+ - **Type presets**: stamp `data-omega-type="sans|serif|mono"` on `<html>`
108
+ (via `theme.html.attributes`) or re-point `--omega-font-marketing`
109
+ / `--omega-font-display` directly. Default is the mix pairing — serif
110
+ marketing display over sans UI.
111
+ - **Fonts (D5, shipped)**: classy vendors **Inter** (UI grotesk) and
112
+ **Newsreader** (marketing serif); newsflash vendors **Schibsted Grotesk**
113
+ and **Fraunces** the same way (cp187 — its Google Fonts CDN link is dead) —
114
+ variable woff2, latin + latin-ext, OFL —
115
+ in `themes/<theme>/fonts/`; the asset pipeline copies every layer's `fonts/`
116
+ dir to `/assets/fonts` (first layer wins, and a consumer-local theme is
117
+ followed by the packaged theme it SHADOWS — inheriting that skin's sheet
118
+ through the hatch inherits the files its `@font-face` rules point at,
119
+ [#773](https://github.com/Omega-JS-Stack/omega/issues/773)), `css/base/_fonts.scss` carries
120
+ the `@font-face` blocks (`font-display: swap`), and `css/base/_root.scss`
121
+ re-points `--omega-font-ui` / `--omega-font-serif` at them. The core token
122
+ sheet keeps system stacks as the framework default AND the fallback tail —
123
+ swapping faces stays a values-only change (two custom properties + the
124
+ files). Ordering note: core main.scss MUST load the token sheet before the
125
+ theme `@forward` (Sass emits a module's CSS at its first load), or theme
126
+ token overrides lose the cascade. The head PRELOADS the first-paint faces,
127
+ and the list is read off those `@font-face` rules in the compiled sheet
128
+ ([#765](https://github.com/Omega-JS-Stack/omega/issues/765)) — a face with no
129
+ `unicode-range`, or one whose range reaches basic latin (U+0000-00FF), is a
130
+ first-paint face; the `-latin-ext` subsets stay out. So a theme naming its
131
+ files its own way, or vendoring ONE variable face, preloads correctly with
132
+ nothing to declare: the declaration IS the `@font-face`
133
+ ([docs/web/index.md](../web/index.md)).
134
+ And the face that paints BEFORE it lands is metric-matched
135
+ ([#768](https://github.com/Omega-JS-Stack/omega/issues/768)): the css lane
136
+ measures each vendored family's own font file, measures the system family the
137
+ theme's stack already names next, and generates
138
+ `@font-face { font-family: '<Family> Fallback'; src: local('<system family>'), ...;
139
+ size-adjust; ascent-override; descent-override; line-gap-override }` (one
140
+ `local()` per measured family the stack names, so the face resolves on macOS
141
+ and Windows alike; the overrides follow the first), then
142
+ names that face right after the web family in the `--omega-font-*` stack. The
143
+ system font occupies the same lines the webfont will, so `font-display: swap`
144
+ moves nothing (classy: Inter over `Helvetica Neue` at size-adjust 105.508%,
145
+ Newsreader over `georgia` at 91.224%). A theme gets this for FREE: the
146
+ transform reads the compiled sheet, so a theme's own faces and its own stacks
147
+ are all it has to declare, and no scss file names a fallback family. What a
148
+ theme owes is the tail: a stack that reaches none of the measured system
149
+ families (Arial, Helvetica, Helvetica Neue, Georgia, Times New Roman,
150
+ Verdana) gets no fallback face and one build warning naming the family.
151
+
152
+ Tier 2 stays: fork `themes/_template` for a full theme; `themes/base` remains
153
+ the fall-through layer for anything the theme doesn't cover, and base's
154
+ `_theme.scss` forwards classy's theme as the default skin css. The tier is
155
+ PROVEN in a real build by the brand-shape corpus's `shape-theme-override` cell
156
+ ([#773](https://github.com/Omega-JS-Stack/omega/issues/773)): a consumer-local
157
+ theme shadows the packaged one of the same id, and every file kind the cascade
158
+ resolves — layout, include, the scss entry, a section's html, its own section
159
+ js, and a lane left to the chain with `inherit: ['js']` — is read back out of
160
+ `dist/` ([testing.md](testing.md)).
161
+
162
+ ### CSS fall-through — the two lanes (cp190, closes the audit's asymmetry)
163
+
164
+ Layouts/includes fall through per-file, but a theme's `_theme.scss` never
165
+ did — a non-classy theme rendered the shared `omega-*` vocabulary
166
+ unstyled on fall-through pages. Two blessed lanes now cover it, both
167
+ proven:
168
+
169
+ - **Partial/consumer themes** (no Bootstrap of their own): put
170
+ `@forward 'omega:theme';` at the top of the theme's `_theme.scss` — the
171
+ importer resolves through the layer roots and SELF-SKIPS the requesting
172
+ file, so the forward lands on base's `_theme.scss` (a bridge forwarding
173
+ classy's theme as the default skin css) and emits classy's whole
174
+ chain (Bootstrap included, configured through the forward); the theme's
175
+ own rules land after and win the cascade. Pinned in
176
+ `test/themes.test.js` ("inheritance hatch").
177
+ - **Full sibling themes** (own Bootstrap config — newsflash): do NOT
178
+ inherit wholesale (two Bootstraps); import classy's app/auth partials
179
+ directly as the vocabulary FLOOR — they are deliberately TOKEN-PURE
180
+ (zero Sass config coupling), so they paint through the importing theme's
181
+ token re-values. The set: `layout/shell` (+ `.page-header`),
182
+ `layout/footer` (the shared footer include speaks `omega-footer`
183
+ vocabulary on every page — the floor supplies structure, the theme
184
+ re-inks it), `app/panels` (table/statgrid/iconbtn/count), `pages/auth`,
185
+ `components/receipt`, `components/badges` (dot-status; the chip left for the
186
+ core component sheet in #242, so every theme gets it with no floor at all). Import
187
+ EARLY (the floor sits UNDER the theme's voice, so later theme rules win
188
+ collisions like classy's `.badge` base). Live models:
189
+ `themes/newsflash/_theme.scss`, `themes/neobrutalism/_theme.scss`.
190
+ NOT in the floor and never needed there: `components/buttons` and
191
+ `components/forms` are Sass-config coupled, so the two app-chrome pieces they
192
+ used to hide — `.btn-icon` and `.omega-search` — moved to the core component
193
+ sheet in [#303](https://github.com/Omega-JS-Stack/omega/issues/303). A sibling
194
+ theme that skipped them rendered a native UA button box beside the breadcrumb
195
+ and a native search field with reboot's inverted `kbd` (a solid cream slab on
196
+ a dark skin). Anything else a base surface renders takes the same route: the
197
+ core sheet, never a widened floor.
198
+
199
+ **The guard ([#98](https://github.com/Omega-JS-Stack/omega/issues/98))**: the web
200
+ asset lane reads the COMPILED main bundle for three sentinels the two lanes both
201
+ guarantee — `.omega-auth`, `.omega-statgrid`, `.omega-footer`, one per
202
+ fall-through surface (the other floor partials ride along unsentineled) — and a
203
+ non-classy theme missing ANY of them gets one loud build WARNING (never a failure)
204
+ naming the theme, the missing piece (hatch vs floor — decided by whether the
205
+ bundle defines `--bs-body-bg`, i.e. whether the theme ships its own Bootstrap),
206
+ and the exact line to add. `packages/web/src/theme-vocabulary.js`; every bundled
207
+ theme is silent, pinned in `test/themes.test.js` ("fall-through guard").
208
+
209
+ Rule for classy authors: app/auth vocabulary partials MUST stay token-pure
210
+ — a Sass config dependency there breaks every sibling theme's floor.
211
+
212
+ ## Content-page vocabulary (default pages + blueprints)
213
+
214
+ Every classy frontend default page composes from one shared set
215
+ (`themes/classy/css/marketing/_content.scss` + the existing section/bento
216
+ vocab): `omega-page-hero` (+ `omega-display--page`) opener, `omega-prose`
217
+ long-form (blog posts, legal md, bios), `omega-timeline`, `omega-post-card`
218
+ (the ONE blog card — `_includes/frontend/components/post-card.html`, shared
219
+ by index/related/category/tag grids), `omega-person`, `omega-facts`
220
+ (hairline-divided columns; `__value--num` serif numerals, `__sub` footnote),
221
+ `omega-chip-cloud`, `omega-blog-search`, and token-driven `.pagination`.
222
+ Consumer frontmatter keys are unchanged — pages re-rendered, data contracts
223
+ kept.
224
+
225
+ **Compositional set (cp147 — pages are COMPOSED, not centered)**:
226
+ `omega-hero-split` (asymmetric opener: statement + side rail),
227
+ `omega-duo` (label/head column + body column, sticky aside; column split
228
+ overridable via `--omega-duo-cols`), `omega-statement` (editorial letter
229
+ text — big serif with italic `<em>`), `omega-numbered` (principles list
230
+ with serif italic indices), `omega-channel` (contact/support rows),
231
+ `omega-band` (one wide hairline row — enterprise, platform strips),
232
+ `omega-form-panel` (the hairline container every long form sits in), and
233
+ the `.omega-quiet` fine-print voice.
234
+
235
+ **Post media + author contracts** (post-card AND the post page honor them):
236
+ `post.image: false` → designed no-media panel (serif italic category
237
+ monogram on dotgrid), never a 404 `<img>`; `post.image: "<path>"` → that
238
+ image lazy-loaded; absent → the legacy `/assets/images/blog/post-<id>/`
239
+ convention. An author that resolves to no team member renders a neutral
240
+ pen-nib mark + the brand name (no broken avatar rows).
241
+
242
+ **Footer pattern** (`frontend/sections/footer.html` + `css/layout/_footer.scss`,
243
+ draft-2): brand block (lockup + one-liner + social icon row) beside auto-fit
244
+ link columns; ONE hairline base row where copyright, legal links, the
245
+ language dropup pill, and the segmented appearance control all share the
246
+ same 1.75rem scale. The appearance segments are plain `data-appearance-set`
247
+ buttons — core appearance.js stamps `.active`/`aria-pressed`. The brand
248
+ lockup class (`.omega-nav__brand`) is root-scoped and shared by nav +
249
+ footer.
250
+
251
+ **Per-page theme css slot**: `themes/<theme>/css/pages/<page>/index.scss`
252
+ compiles to its own bundle and loads after main css AND core's page sheet — one
253
+ rule, every layer's sheet in layer order ([#624](https://github.com/Omega-JS-Stack/omega/issues/624)). That's where a theme outranks core page
254
+ rules; classy's `status` and `feedback` entries repaint the JS-toggled `bg-*`
255
+ state classes (a class contract — never rename them) into the hairline
256
+ language there.
257
+
258
+ ## App panels (App DNA — dashboard/admin content)
259
+
260
+ `themes/classy/css/app/_panels.scss`: `omega-statgrid` (the DIRECTION
261
+ merged stat card — ONE card, hairline column dividers, micro-label +
262
+ tabular value + delta chip per cell; it sizes off ITS OWN width, never the
263
+ viewport — `--omega-statgrid-cols` sets the column CEILING and cells reflow
264
+ below a 10rem floor, so a statgrid nested in a half-width card wraps instead
265
+ of overflowing), `omega-panel-title` (the 14/650 card-title voice), and
266
+ `omega-activity` (hairline-divided feed rows with neutral icon chips).
267
+ The backend dashboard layout and the admin dashboard/users blueprints
268
+ render on these; every JS-populated id (`stat-*`, tables, charts) is a
269
+ contract and stays.
270
+
271
+ ## Categorical tones + the interactive affordance (#72)
272
+
273
+ Two shared utility sets in `core/css` — core, not classy, so every theme
274
+ inherits them:
275
+
276
+ - **Tones** (`core/css/core/_tones.scss`): `.omega-tone-1`…`.omega-tone-6` set
277
+ `--omega-tone` to the matching `--omega-chart-*` slot, and
278
+ `.omega-badge-tone` paints a chip with it (`color-mix` tint, no
279
+ uppercasing — a tone label is an identifier, not a word). ONE ramp for two
280
+ surfaces: a chart series and the badge naming the same thing are the same
281
+ color. Which name falls in which slot is the page's business. The sheet
282
+ loads after the theme forward, so it outranks a theme's own chip rules.
283
+ - **`.omega-interactive`** (`core/css/motion/_index.scss`): the whole-surface
284
+ click affordance — the surface warms on hover AND `:focus-visible`, an
285
+ accent ring on focus, an accent-subtle tint on press. `--lift` adds the
286
+ card lift (2px up, shadow-2) that presses back down; a row takes the base
287
+ class alone. `prefers-reduced-motion` drops the transition and the lift, and
288
+ classy drops the lift outright (see "classy never raises on hover" below).
289
+
290
+ Charts read the same ramp: `core/js/libs/charts.js` (`chartColors()`) resolves
291
+ `--omega-chart-1…6` off `:root` and hands them to the chart definition as its
292
+ theme palette, so a chart follows the brand ramp and dark mode without knowing
293
+ either exists. A surface that means a STATUS passes the
294
+ status tokens explicitly instead (`colors: ['var(--omega-ok)', …]` — the
295
+ helper's `resolveColor` reads them off the live sheet); the admin dashboard's
296
+ plan doughnut is the reference case ([#74](https://github.com/Omega-JS-Stack/omega/issues/74)).
297
+
298
+ Graphs read the same ramp again: `core/js/libs/graph.js` (`graphTheme()`) maps
299
+ `--omega-chart-1…6` onto mermaid's per-item slots (`cScale0…5` and `pie1…6`),
300
+ and the rest of a diagram off `--omega-ink`, `--omega-surface`/`--omega-surface-2`,
301
+ `--omega-line` and `--omega-accent` — the accent is the node outline, the one
302
+ place the brand color lands in a diagram
303
+ ([#169](https://github.com/Omega-JS-Stack/omega/issues/169)). Mermaid paints
304
+ with literal colors, so every token is resolved before it is handed over, and
305
+ the read happens at draw time: like charts, a light/dark flip lands on the next
306
+ redraw and nothing listens for it.
307
+
308
+ ## One status hue site-wide
309
+
310
+ `--omega-ok` / `--omega-warn` / `--omega-danger` are the ONLY greens, ambers
311
+ and reds on a site. Each ships with an `-rgb` channel twin (`--omega-ok-rgb:
312
+ 18, 146, 92`) because Bootstrap's translucency utilities paint from
313
+ `rgba(var(--bs-success-rgb), …)`, which no `var()` can feed a hex to. Every
314
+ theme's root bridge points `--bs-success`/`-warning`/`-danger` and their `-rgb`
315
+ companions at the tokens, AFTER Bootstrap compiles, so `bg-success`,
316
+ `.text-success` and a token-painted glyph are the same color in both modes —
317
+ the /status page's "all systems operational" and the uptime bars beneath it
318
+ were two different greens in dark mode until this landed
319
+ ([#13](https://github.com/Omega-JS-Stack/omega/issues/13)).
320
+
321
+ Affirmation ticks ride that same bridge: every "you get this" check (plan
322
+ features, benefit lists, hero meta, comparison "yes" cells, signup benefits)
323
+ wears Bootstrap's `.text-success` on the icon or its wrapper, and nothing else.
324
+ There is no framework class for it: the `.omega-check`/`--omega-check` seam
325
+ was deleted (Ian's ruling 2026-07-31, #44) because it computed exactly
326
+ `.text-success`: a concept Bootstrap already names is expressed through
327
+ Bootstrap's hook, and `omega-*` is only for concepts Bootstrap has no name for.
328
+ A call site NEVER paints a check its own color (#11's call-site rule stands).
329
+
330
+ **A theme that re-values a status hue MUST re-value its `-rgb` twin** (only
331
+ newsflash does today) or the two drift apart again. `test/tokens.test.js` pins
332
+ both halves: the twins exist in every stamp, and the bridge is the LAST
333
+ `--bs-success` in every theme's compiled css.
334
+
335
+ ## Motion library
336
+
337
+ CSS: `core/css/motion/_index.scss`. Engine: `@omega.js/client/modules/motion.js`
338
+ (`createMotion()`), started by `core/js/first-paint.js` and registered on the
339
+ omega library by `core/js/core/motion.js` — shared with desktop/extension at C4
340
+ exactly like icon-renderer.
341
+
342
+ | Surface | Use |
343
+ |---|---|
344
+ | `data-omega-reveal="up\|fade\|left\|right\|scale"` | reveal once on scroll-in |
345
+ | `data-omega-reveal-stagger="60"` (parent) | staggers child reveals (ms step) |
346
+ | `data-omega-first-paint` (band) | the band IS the first viewport: its reveals are started at PARSE time by head.html's inline starter (no fetch), and a rotator inside it opens on its first word (css) |
347
+ | `data-omega-countup` | counts to the number already in the markup |
348
+ | `data-omega-rotate="2600"` | children cycle (hero word rotator, quotes) |
349
+ | `data-omega-marquee` + `.omega-marquee__track/__item` | seamless loop — the set is cloned until half the track covers the container (never runs dry), constant px/s (attr value overrides); clones are `aria-hidden` with focusables detabbed (`tabindex=-1`, still mouse-clickable) so interactive sets (newsflash ticker headlines) stay accessible |
350
+ | `data-omega-scroll-watch="24"` | stamps `data-omega-scrolled` (glassy nav) |
351
+ | `data-omega-segmented` | gliding-thumb segmented control: engine injects `.omega-segmented__thumb` and tracks the checked/`.active` segment (billing toggle, platform rails, footer appearance) |
352
+ | `data-omega-dotfield="22"` | canvas dot grid (value = px spacing): slow traveling wave, dots tint along ONE drifting rainbow gradient, pointer glow (tracked window-level so the fixed nav can't blind it); the loop starts only once first paint has settled and repaints at 30fps (below), reduced motion gets ONE still grid, and static CSS dots remain for no-JS |
353
+ | `.omega-hover-lift/-raise/-dim`, `.omega-pressable`, `.omega-hover-nudge .omega-nudge` | pure-CSS hover/press effects |
354
+ | `.omega-interactive` (+ `--lift`) | whole-surface click affordance: warm on hover/focus-visible, ring on focus, tint on press |
355
+ | `.omega-float`, `.omega-caret` | ambient float, terminal caret |
356
+
357
+ Resilience rules (load-bearing):
358
+
359
+ - Reveal styles only hide content under the inline `html[data-omega-motion]`
360
+ stamp (head.html) — **no JS means a fully visible page**.
361
+ - **A reveal never waits for the big bundle**
362
+ ([#585](https://github.com/Omega-JS-Stack/omega/issues/585)). The stamp lands
363
+ before first paint, so a hidden reveal that waited on the engine's
364
+ IntersectionObserver was blank text for the whole JS download on a cold cache
365
+ — LCP measured on an empty band. The cause was never the animation but WHERE
366
+ the starter lived: `core/js/main.js`, behind firebase/auth/analytics init. The
367
+ fix is `core/js/first-paint.js` — its own asset entry, loaded from `head.html`
368
+ as a deferred `type="module"` script, so it runs the moment the DOM is parsed:
369
+ ahead of the main bundle's module in the foot, and independent of images,
370
+ fonts and firebase. Same engine, same CSS transition, same feel; it just
371
+ starts when the DOM is ready instead of when the app is.
372
+ - **The band in the FIRST viewport starts its reveals at PARSE time**
373
+ ([#763](https://github.com/Omega-JS-Stack/omega/issues/763), Ian's ruling
374
+ 2026-09-02). Booting the engine early made the gate cheap, not free: the
375
+ reveal lane is a SCROLL lane, and the opening band is never scrolled to.
376
+ Holding it at opacity 0 until the observer fires cost the playground homepage
377
+ ~2.0s of a 2,588ms mobile LCP, measured on `p.omega-hero__sub`
378
+ ([#749](https://github.com/Omega-JS-Stack/omega/issues/749)). #749 answered
379
+ that by taking the hero copy out of the lane entirely; the above-the-fold fade
380
+ is part of the design, so the attributes came back and the ATTRIBUTE changed
381
+ meaning instead. A band that declares `data-omega-first-paint` is run by an
382
+ inline starter in `head.html`, right after the motion stamp: a plain script,
383
+ no module and no fetch. A MutationObserver on `document.documentElement`
384
+ collects the reveals in each batch of added nodes, and **the guard is per
385
+ ELEMENT, never per band** — the parser hands a band over at its OPEN tag, when
386
+ it holds no copy yet, so a band-level "handled" flag stamps nothing and leaves
387
+ the work to `DOMContentLoaded`, which waits for the deferred bundles: the very
388
+ wait this removes. Each batch coalesces TWO animation frames — the first
389
+ paints what was collected at opacity 0, which is the transition's starting
390
+ state, the second stamps it — then re-applies the stagger across the whole
391
+ container (`--omega-reveal-delay` per child, same 60ms default; parse order is
392
+ document order, so an index never changes and re-setting a value restarts
393
+ nothing) and sets `data-omega-inview`. `DOMContentLoaded` disconnects the
394
+ observer after one last pass, as a backstop. The engine's `observeReveal`
395
+ returns early on a stamped element, so its later boot changes nothing inside
396
+ the band, and the starter needs no reduced-motion guard of its own: the reveal
397
+ lane hides only under `(prefers-reduced-motion: no-preference)`, so a visitor
398
+ who asked for less motion gets the final state either way, JS or no JS.
399
+ **The inline critical block has to carry the reveal lane** for any of that to
400
+ be visible: every reveal rule is scoped `html[data-omega-motion] …`, a token
401
+ no markup scan can see, so PurgeCSS purged the lane out of the block and a
402
+ built page painted its copy VISIBLE, stamped it, and met the deferred sheet
403
+ with the element already resolved — no fade at all. The critical extractor
404
+ safelists that one lane (`src/assets.js`, ~1.1 KB), pinned by
405
+ `test/critical-css.test.js`. So the marketing
406
+ hero's copy stack (badge, headline, sub, CTAs, meta, frame) is back on the
407
+ lane behind its `data-omega-reveal-stagger="40"` container, and so is any
408
+ markup the template never sees (an authored `demo_html` slot, a custom hero
409
+ animation folder). The headline's word rotator is the one above-the-fold gate
410
+ the starter does NOT resolve — the engine owns the cycle — so its css rule
411
+ stays: inside a first-paint band the FIRST word paints with the document and
412
+ yields the moment the engine stamps `data-omega-active`, which it does on that
413
+ same child. Below-the-fold bands keep the one lane, untouched.
414
+
415
+ **A first-paint band fades on its own, shorter token.** Chrome reports the
416
+ largest element painted only when its fade ENDS, so the first viewport's
417
+ transition and the stagger ahead of it land on LCP in full; the proof on the
418
+ playground home (mobile, slow 4G) put the 550ms reveal behind a 90ms stagger
419
+ 750ms past first paint. `motion/_index.scss` therefore times
420
+ `[data-omega-first-paint] [data-omega-reveal]` with `--omega-speed-first-paint`
421
+ (300ms in core) and the hero's stagger is 40ms a step: the same fade, the same
422
+ lane, 400ms past first paint. Below the fold nothing changed, because no
423
+ metric watches a scroll-in reveal. A theme re-times the first viewport by
424
+ setting the token, exactly as it sets `--omega-speed-slow` for the rest.
425
+
426
+ **Every first-viewport band declares the attribute, not just the hero**
427
+ ([#467](https://github.com/Omega-JS-Stack/omega/issues/467) Phase 2). The
428
+ five the #749 worker found still gated took the identical treatment:
429
+ `about/hero` (both its layouts, photo-lead and split), the document masthead
430
+ in `_layouts/frontend/core/minimal.html`, and the opening bands of
431
+ `download.html`, `contact.html` and `pricing.html`. Four of them compose
432
+ their copy through the shared `heading/masthead` component, which emits the
433
+ reveal attributes itself, so the component takes a `first_paint: true`
434
+ switch rather than losing them. The follow-up pass then walked the REST of
435
+ that component's callers, and every one of them proved to be its page's
436
+ opening band too: `status.html`, `feedback.html`, `blog/index.html` and the
437
+ four blog taxonomy layouts, `team/index.html`, `legal/document.html` (whose
438
+ band wears `omega-legal__head`, not a dotgrid), `alternatives/index.html`
439
+ and `alternatives/alternative.html`, the three `collection/` layouts,
440
+ `extension/index.html` and `updates/index.html`. The switch stays on the
441
+ component because a MID-page caller (a consumer's own composition, the
442
+ component gallery) still belongs on the reveal lane. Those masthead clusters
443
+ therefore carry no reveal attributes at all, which #763 left exactly as it
444
+ found them: the starter animates whatever a first-paint band carries, and
445
+ these bands carry nothing. Below the fold on every one of these pages,
446
+ nothing changed. Pinned by
447
+ `packages/web/test/first-paint-bands.test.js`.
448
+ - **The first-paint script is the one early seam, and it stays small.** Anything
449
+ that must beat the big bundle belongs there (brand custom hero animations,
450
+ [#441](https://github.com/Omega-JS-Stack/omega/issues/441), ride the same
451
+ engine and get the same early start) — weighed against that budget. It must
452
+ never import `@omega.js/client`: the singleton drags the whole runtime into
453
+ the bundle and rebuilds the very problem it exists to solve. The motion
454
+ module's subpath is standalone by design, and `core/js/core/motion.js` adopts
455
+ the running instance rather than starting a second one.
456
+ - **One lane, not two** (Ian's ruling 2026-08-26). An earlier round answered
457
+ #585 with a second, pure-CSS entrance lane (paint-time keyframes, a 120ms
458
+ lead-in, an `:nth-child` stagger cap, a 1s auto-resolve net, `will-change`
459
+ staging). It put the text on screen but at a different rhythm and a different
460
+ start moment than the reveal everywhere else — a visibly worse animation. It
461
+ is deleted: no `omega-reveal-in` keyframe, no `--omega-reveal-wait`, no
462
+ `html[data-omega-motion-ready]` gate, no `data-omega-reveal-lead` hook. A
463
+ band's stagger is again the engine's job alone, from the one number its author
464
+ writes on the cluster (`data-omega-reveal-stagger="70"` → `--omega-reveal-delay`
465
+ per child).
466
+ - **Ambient motion waits for first paint; a mutation never reads geometry
467
+ behind itself** ([#752](https://github.com/Omega-JS-Stack/omega/issues/752)).
468
+ The homepage hero carries `data-omega-dotfield`, and the engine used to start
469
+ its loop the moment it scanned: a full canvas grid repainted on every one of
470
+ the display's frames, on the page's own thread, through the whole LCP window.
471
+ The rules below answer it, and they are the pattern for anything ambient
472
+ this engine grows next:
473
+ - **The settle signal.** No measure, no style read and no paint until the
474
+ first `requestIdleCallback` AFTER the load event (a 2s timeout, so a
475
+ permanently busy page still gets its field), or the load event itself where
476
+ idle callbacks do not exist. The `whenSettled(doc, fn)` helper in
477
+ `motion.js` is the one implementation.
478
+ - **The handoff stamp rides the first painted grid.** classy fades its CSS
479
+ fallback dots out on `.omega-dotgrid[data-omega-dotfield-ready]::before`,
480
+ so `data-omega-dotfield-ready` lands with the grid that replaces them and
481
+ never at scan: stamped early it left the hero backdrop blank for the whole
482
+ wait. The engine keeps its own re-entry set, so a rescan before the stamp
483
+ exists still installs exactly one field.
484
+ - **A capped cadence.** The field then repaints at 30fps, not at whatever the
485
+ display offers: the wave crawls (~900px crest, a 20s rainbow cycle), so 30
486
+ reads exactly like 60 and costs half the main thread, and a 120Hz display
487
+ pays the same 30, not double. The pointer trail still eases on every frame
488
+ (cheap), so its feel is unchanged. Under `prefers-reduced-motion` the field
489
+ paints ONE grid, held at the start of the wave, with no loop and no pointer
490
+ tracking; it repaints only on a resize or a `data-bs-theme` flip, and
491
+ re-reads `--omega-line-strong` there, because the once-a-second color poll
492
+ lives inside the loop that path does not have.
493
+ - **The marquee measures in a later frame.** `build()` replaces the track's
494
+ children and reads the set width one `requestAnimationFrame` later, never
495
+ in the tick that just mutated it: that read was a forced synchronous
496
+ reflow on every build, and a build runs on resize, on `fonts.ready` and on
497
+ every image load in the set. A burst of those collapses to ONE measure, so
498
+ the second never sizes the loop from a track the first just cloned.
499
+ - `prefers-reduced-motion` renders final states: reveals resolve instantly,
500
+ count-ups show their target, rotators hold the first word, marquees park.
501
+ EVERY continuous loop in `core/css` and in the theme sheets this package
502
+ authors parks with them (vendored Bootstrap keeps upstream's own behavior):
503
+ the `.animation-*` utilities (spin, pulse, pulse-right, bounce, wiggle, flex,
504
+ shimmer), the motion library's float, marquee, and caret blink, the lazy-load
505
+ and binding shimmers, the exit-popup wave, the studio record pulse, the
506
+ download/extension walkthrough pointers, and the theme loops (the typing
507
+ dots, the org-chart flow lines, the ticker pulse, the CTA rings, the
508
+ infinite-scroll track). The one-shot fades, slides, and popups already end on
509
+ their final state. `test/animations.test.js` derives its roster from every
510
+ core and theme sheet that declares `infinite`, so a new unparked loop fails
511
+ the suite.
512
+ - **Autoplaying video parks in JS, because CSS cannot pause one**
513
+ ([#499](https://github.com/Omega-JS-Stack/omega/issues/499)). The motion
514
+ engine's scan is the ONE lane: under `prefers-reduced-motion` it strips
515
+ `autoplay` from every `video[autoplay]`, pauses it, and turns its controls
516
+ on, so the poster holds and the visitor decides. It applies to every
517
+ autoplaying band at once (the hero's video demo, `marketing/product-demo`) —
518
+ a section never forks its own pause. Once parked, a rescan leaves a video
519
+ the visitor started alone.
520
+ - The PurgeCSS safelist keeps every `omega-`-namespaced selector plus
521
+ Bootstrap's own JS-toggled transition classes (`collapse`/`collapsing`/
522
+ `show`/`showing`/`fade` — `src/assets.js`) because that state is
523
+ runtime-stamped and never visible to the content scan. **New
524
+ runtime-stamped classes must live in the `omega-` namespace** (or join the
525
+ safelist explicitly).
526
+
527
+ **Classy never raises on hover** (Ian 2026-08-29,
528
+ [#686](https://github.com/Omega-JS-Stack/omega/issues/686)). Nothing travels
529
+ upward under the pointer in classy — not a button, not a card, not a tile.
530
+ Everything else a hover does stays: the warm, the ring, the shadow, the line,
531
+ the press, and the hero frame's tilt. The raise itself is SHARED css (core's
532
+ motion library serves every skin, and newsflash/neobrutalism import classy's
533
+ floor partials), so classy OPTS OUT in `themes/classy/css/base/_no-raise.scss`
534
+ — loaded last by its `_theme.scss` — and every other theme keeps its lifts.
535
+ Raises classy owns alone are gone from their own files instead. The legacy
536
+ `.hover-up` utility needs one specificity step (`html .hover-up:hover`) because
537
+ `core/css/core/_animations.scss` lands AFTER every theme, and a re-declared
538
+ hover transform carries its `prefers-reduced-motion` park with it, since a
539
+ media query adds no specificity. `test/hover-raise.test.js` compiles both the
540
+ classy and a sibling bundle, so a new raise reaching classy fails the suite.
541
+
542
+ **Every loading state carries a visible animation** (Ian 2026-08-15). Any
543
+ surface that WAITS — a poll, a fetch, a build, a webhook that has not landed —
544
+ shows an animated waiting indicator: the Bootstrap `spinner-border` idiom
545
+ (`role="status"` plus a `visually-hidden` label; `spinner-border-lg` for a
546
+ full-panel wait) for a spinner, the binding skeleton's shimmer for
547
+ placeholder content. Bare text alone is never a waiting state: with nothing
548
+ moving, a page that is working looks broken. Under `prefers-reduced-motion`
549
+ the indicator swaps to a STATIC state that still says what is happening —
550
+ park the loop (`animation: none`) and keep visible copy naming the wait
551
+ beside it, because vendored Bootstrap only slows its own spinner, so the page
552
+ rendering one owns the park (the confirmation page's
553
+ `core/css/pages/payment/confirmation/index.scss` is the reference).
554
+
555
+ ## Copy register
556
+
557
+ Product copy — every string a visitor reads: layout and section copy,
558
+ section-defaults `json5`, and the JS string literals that render into the
559
+ page — **never uses em dashes**. Use a comma, a semicolon, a period, or
560
+ parentheses instead, whichever the sentence actually wants (Ian 2026-08-15).
561
+ The rule is about COPY, not about code: comments, `docs/`, frontmatter
562
+ headers, and `logger.*` messages are not rendered and are untouched, and a
563
+ bare `—` standing in as a placeholder glyph or a range separator is
564
+ typography rather than a sentence.
565
+
566
+ ## classy v2 (the flagship skin)
567
+
568
+ Warm-paper light / de-blued charcoal dark; zero gradients (the only permitted
569
+ fades are alpha masks and monotone chart fills); ink primary buttons
570
+ (`.btn-adaptive` — built from `$dark`/`$light` by the shared overrides layer);
571
+ accent reserved for links, active states, focus, meters, chart series-1, and
572
+ small signals. Marketing display voice is the serif `--omega-font-marketing`
573
+ (`.omega-display`); app surfaces stay on the grotesk. The app chrome rides
574
+ `.omega-shell` with the content area drawn as one big rounded surface card
575
+ (matching margins/radii on topbar + main — the markup contract is untouched).
576
+
577
+ Structure: `_config.scss` (consumer knobs + Bootstrap forward) → `css/base`
578
+ (root bridge, typography, utilities) → `css/components`
579
+ (buttons/cards/forms/badges/dropdowns) → `css/layout`
580
+ (general/nav/footer/shell) → `css/marketing` (hero/bento/sections) →
581
+ `css/pages` (auth) → shared `../bootstrap/overrides`.
582
+
583
+ Section chrome is data-driven: `_includes/frontend/sections/{nav,footer}.html`
584
+ and the shared app chrome `_includes/global/sections/{app-sidebar,app-topbar,
585
+ page-header}.html` render JSON section data (`core/_includes/**.json`,
586
+ consumer-overridable per file). The sidebar supports an optional project
587
+ selector module (`selector:`), nested collapsible groups, badges, an ad slot
588
+ (`bottom.ad.enabled`), and the auth-bound user row; the topbar carries the
589
+ drawer/rail toggles, the breadcrumb trail (from `theme.header.breadcrumbs`),
590
+ the ⌘K search pill (`search:`), custom actions, and the account dropdown. Each
591
+ region rides its own page switch: `theme.sidebar.enabled: false` drops the rail
592
+ AND the two topbar toggles that drive it (dead buttons otherwise, naming an id
593
+ the page no longer carries,
594
+ [#740](https://github.com/Omega-JS-Stack/omega/issues/740)), and
595
+ `theme.topbar.enabled: false` drops the topbar.
596
+
597
+ ## Stable-API line (don't churn once consumers exist)
598
+
599
+ Token NAMES · `_config.scss` variable names · `.omega-shell` markup contract
600
+ (which since [#319](https://github.com/Omega-JS-Stack/omega/issues/319) includes
601
+ the `__sidebar-scroll` region — hand-rolled shell markup without it gets a rail
602
+ that no longer scrolls its nav) · `_includes` section names + their JSON data
603
+ shapes · the motion attribute contract. Everything behind that line — values, partial internals, page
604
+ markup — iterates freely with the skin arc.