@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,823 @@
1
+ const { TEST_ACCOUNTS, getAccountDefinitions, getFirstPaidProduct, buildOrderFixture, buildSessionFixtures } = require('../../dist/test/test-accounts.js');
2
+ const isTrialing = require('../../dist/manager/routes/payments/cancel/_is-trialing.js');
3
+ const defineCases = require('../../dist/vendor/devkit/test/define-cases.js');
4
+
5
+ /**
6
+ * Test: the seeded personas' billing data ([#263](https://github.com/Omega-JS-Stack/omega/issues/263))
7
+ *
8
+ * A persona that HOLDS a paid plan is a persona that BOUGHT one, and a purchase
9
+ * leaves three marks: a price and a cadence the brand actually sells, a term that
10
+ * ends when the billing cycle does, and an order record behind it. A hollow seed
11
+ * (no price, no cadence, an expiry a decade out, an orderId naming a document that
12
+ * was never written) renders as an empty billing panel and answers a cancel with a
13
+ * plan nobody sells.
14
+ *
15
+ * And every persona is a FULL account ([#327](https://github.com/Omega-JS-Stack/omega/issues/327)),
16
+ * journey ones included: identical in shape AND in substance to a real user, so QA
17
+ * signed in as one sees exactly what a customer sees. The completeness cases below
18
+ * are what stops a persona added tomorrow from arriving half-built — the referrals
19
+ * and the active sessions an established account carries included
20
+ * ([#343](https://github.com/Omega-JS-Stack/omega/issues/343)), because those two
21
+ * lists were the last ones a persona could not fill.
22
+ *
23
+ * PURE: reads the seeder's own definitions against the brand catalog — no emulator,
24
+ * no Firestore. What it pins is the SEED, which is what every payment suite and the
25
+ * dev palette start from.
26
+ *
27
+ * Run: npx omega test helpers/seeded-personas
28
+ */
29
+ module.exports = defineCases({
30
+ description: 'Seeded personas carry legitimate billing data',
31
+ type: 'group',
32
+ auth: 'none',
33
+
34
+ tests: [
35
+ // A persona holding a paid plan — or carrying a purchase record for one it
36
+ // has since lapsed from — quotes the catalog's own price and cadence.
37
+ {
38
+ name: 'purchased-personas-quote-the-catalog',
39
+ async run({ assert, config, skip }) {
40
+ if (!paidPlan(config)) {
41
+ skip('No paid subscription product configured in this brand');
42
+ }
43
+
44
+ const definitions = getAccountDefinitions('example.com', config);
45
+ const purchased = Object.entries(definitions).filter(([, definition]) => isPurchase(definition.properties?.subscription));
46
+
47
+ assert.ok(purchased.length > 0, 'The seeder must define personas that bought a subscription');
48
+
49
+ for (const [key, definition] of purchased) {
50
+ const subscription = definition.properties.subscription;
51
+ const payment = subscription.payment || {};
52
+ const plan = planBought(subscription, config);
53
+
54
+ assert.ok(plan, `Persona '${key}' names a plan the catalog does not carry`);
55
+ assert.ok(
56
+ payment.frequency && plan.prices[payment.frequency] !== undefined,
57
+ `Persona '${key}' must be billed at a cadence the catalog sells ${plan.id} at (got ${payment.frequency})`,
58
+ );
59
+ assert.equal(
60
+ payment.price,
61
+ plan.prices[payment.frequency],
62
+ `Persona '${key}' must quote the catalog price for ${plan.id}/${payment.frequency}`,
63
+ );
64
+ assert.ok(payment.price > 0, `Persona '${key}' bought a paid plan, so its price cannot be 0`);
65
+ }
66
+ },
67
+ },
68
+
69
+ // A live term runs to the end of the cycle it is being billed on — not to a
70
+ // date a decade away no real provider would ever record.
71
+ {
72
+ name: 'live-terms-end-with-the-billing-cycle',
73
+ async run({ assert, config }) {
74
+ const definitions = getAccountDefinitions('example.com', config);
75
+ const nowUNIX = Math.floor(Date.now() / 1000);
76
+
77
+ for (const [key, definition] of Object.entries(definitions)) {
78
+ const subscription = definition.properties?.subscription;
79
+ const expiresUNIX = subscription?.expires?.timestampUNIX;
80
+
81
+ // Lapsed and never-subscribed personas are out: a past expiry (or none
82
+ // at all) is exactly what they are seeded to represent.
83
+ if (!expiresUNIX || expiresUNIX <= nowUNIX) {
84
+ continue;
85
+ }
86
+
87
+ const cadence = subscription.payment?.frequency || 'monthly';
88
+ const cycleDays = { daily: 1, weekly: 7, monthly: 30, annually: 365 }[cadence];
89
+ const remaining = (expiresUNIX - nowUNIX) / 86400;
90
+
91
+ assert.ok(cycleDays, `Persona '${key}' is billed at an unknown cadence (${cadence})`);
92
+ assert.ok(
93
+ remaining <= cycleDays + 1,
94
+ `Persona '${key}' expires ${Math.round(remaining)} days out — a ${cadence} term ends within ${cycleDays}`,
95
+ );
96
+ }
97
+ },
98
+ },
99
+
100
+ // The orderId on a seeded subscription names a purchase record the backend
101
+ // reads back (the test cancel provider's plan lookup, the test webhook
102
+ // library's resource rebuild, the per-owner trial-eligibility query). Every
103
+ // one of them must have a fixture behind it.
104
+ {
105
+ name: 'every-seeded-orderid-has-an-order-fixture',
106
+ async run({ assert, config, skip }) {
107
+ if (!paidPlan(config)) {
108
+ skip('No paid subscription product configured in this brand');
109
+ }
110
+
111
+ const paidProduct = getFirstPaidProduct(config);
112
+ const definitions = getAccountDefinitions('example.com', config);
113
+ const seeded = Object.entries(TEST_ACCOUNTS).filter(([, account]) => account.properties?.subscription?.payment?.orderId);
114
+
115
+ assert.ok(seeded.length > 0, 'The seeder must define personas whose subscription names an order');
116
+
117
+ for (const [key, account] of seeded) {
118
+ const orderId = account.properties.subscription.payment.orderId;
119
+ const held = definitions[key].properties.subscription.product;
120
+ // The plan the order records is the persona's own — the catalog's first
121
+ // paid plan only stands in for one that lapsed back to basic.
122
+ const bought = held.id !== 'basic' ? held.id : paidProduct.id;
123
+ const fixture = buildOrderFixture(key, config);
124
+
125
+ assert.ok(fixture, `Persona '${key}' names order ${orderId} but the seeder builds no fixture for it`);
126
+ assert.equal(fixture.orderId, orderId, `The fixture for '${key}' must be the order its subscription names`);
127
+ assert.equal(fixture.doc.id, orderId, 'The order document is keyed by its own id');
128
+ assert.equal(fixture.doc.owner, account.uid, `The order for '${key}' belongs to that persona`);
129
+ assert.equal(fixture.doc.type, 'subscription', `The order for '${key}' records a subscription purchase`);
130
+ assert.equal(fixture.doc.productId, bought, `The order for '${key}' names the plan that was bought`);
131
+ assert.equal(fixture.doc.unified.product.id, bought, `The order's unified product for '${key}' is the plan that was bought`);
132
+ assert.equal(fixture.doc.unified.status, account.properties.subscription.status, `The order for '${key}' mirrors the subscription's state`);
133
+ assert.ok(fixture.doc.unified.payment.price > 0, `The order for '${key}' records what was paid`);
134
+ assert.ok(fixture.doc.unified.payment.frequency, `The order for '${key}' records the cadence it was bought on`);
135
+
136
+ // The blocks a real order carries: the cancel/refund requests the routes
137
+ // write onto it, and the stamps of what last touched it.
138
+ assert.equal(fixture.doc.requests.cancellation, null, `Nothing has requested a cancellation of '${key}' order`);
139
+ assert.equal(fixture.doc.requests.refund, null, `Nothing has requested a refund of '${key}' order`);
140
+ assert.ok(fixture.doc.metadata.created.timestampUNIX > 0, `The order for '${key}' records when it was created`);
141
+ assert.ok(fixture.doc.metadata.updated.timestampUNIX > 0, `The order for '${key}' records when it was last written`);
142
+ assert.equal(fixture.doc.metadata.updatedBy.event.name, 'seed', `The order for '${key}' names the seed as its writer, not a webhook`);
143
+ }
144
+ },
145
+ },
146
+
147
+ // A project's own personas (`test/_init.js` accounts) are seeded by the same
148
+ // seeder, so a purchase in a project persona's name is as real as a
149
+ // framework one's — and the order names the plan THAT persona holds.
150
+ {
151
+ name: 'project-personas-get-their-own-order-fixtures',
152
+ async run({ assert, config, skip }) {
153
+ const plan = otherPaidPlan(config) || paidPlan(config);
154
+
155
+ if (!plan) {
156
+ skip('No paid subscription product configured in this brand');
157
+ }
158
+
159
+ const extraAccounts = {
160
+ 'project-subscriber': {
161
+ id: 'project-subscriber',
162
+ uid: '_test-project-subscriber',
163
+ email: '_test.project-subscriber@{domain}',
164
+ properties: {
165
+ roles: {},
166
+ subscription: {
167
+ product: { id: plan.id, name: plan.name },
168
+ status: 'active',
169
+ payment: { provider: 'test', resourceId: 'sub_test_project', orderId: '_test-order-project-subscriber' },
170
+ },
171
+ },
172
+ },
173
+ };
174
+
175
+ const fixture = buildOrderFixture('project-subscriber', config, extraAccounts);
176
+
177
+ assert.ok(fixture, 'A project persona naming an order must get a fixture, not a dangling id');
178
+ assert.equal(fixture.orderId, '_test-order-project-subscriber', 'The fixture is the order the project persona names');
179
+ assert.equal(fixture.doc.owner, '_test-project-subscriber', 'The order belongs to the project persona');
180
+ assert.equal(fixture.doc.productId, plan.id, 'The order names the plan the persona actually holds, not the catalog default');
181
+ assert.equal(fixture.doc.unified.payment.price, plan.prices[Object.keys(plan.prices)[0]], 'The order records the catalog price of that plan');
182
+ },
183
+ },
184
+
185
+ // A plan the catalog does not sell has no price to quote, so the seeder
186
+ // quotes none: a persona on one keeps its seed exactly as written rather
187
+ // than gaining null billing data that reads like an answer.
188
+ {
189
+ name: 'unpriced-plans-invent-no-billing-data',
190
+ async run({ assert, config }) {
191
+ const extraAccounts = {
192
+ 'project-unpriced': {
193
+ id: 'project-unpriced',
194
+ uid: '_test-project-unpriced',
195
+ email: '_test.project-unpriced@{domain}',
196
+ properties: {
197
+ roles: {},
198
+ subscription: {
199
+ product: { id: '_test-unsold-plan', name: 'Unsold' },
200
+ status: 'active',
201
+ payment: { provider: 'test', resourceId: 'sub_test_unpriced' },
202
+ },
203
+ },
204
+ },
205
+ };
206
+
207
+ const definition = getAccountDefinitions('example.com', config, extraAccounts)['project-unpriced'];
208
+ const payment = definition.properties.subscription.payment;
209
+
210
+ assert.equal(payment.provider, 'test', 'The seeded payment record is left as written');
211
+ assert.equal(payment.frequency, undefined, 'A plan with no catalog price gets no cadence written onto it');
212
+ assert.equal(payment.price, undefined, 'A plan with no catalog price gets no price written onto it');
213
+ },
214
+ },
215
+
216
+ // The counterpart guard: a persona that never bought anything must stay
217
+ // pristine. `journey-flows-trial` in particular — any subscription order in
218
+ // its name disqualifies it from the trial its journey exists to prove.
219
+ {
220
+ name: 'never-subscribed-personas-stay-hollow',
221
+ async run({ assert, config }) {
222
+ const definitions = getAccountDefinitions('example.com', config);
223
+
224
+ for (const key of ['basic', 'journey-flows-trial', 'consent-granted']) {
225
+ const subscription = definitions[key].properties.subscription;
226
+
227
+ assert.equal(subscription.product.id, 'basic', `Persona '${key}' is a free account`);
228
+ assert.equal(subscription.payment, undefined, `Persona '${key}' never bought anything, so it carries no payment record`);
229
+ assert.equal(buildOrderFixture(key, config), null, `Persona '${key}' must have no order behind it`);
230
+ }
231
+ },
232
+ },
233
+
234
+ // The suite-exclusive purchasers ([#406](https://github.com/Omega-JS-Stack/omega/issues/406)):
235
+ // the discount suites' cases each BUY a subscription, which is why they get
236
+ // one account apiece. Every one of them has to ARRIVE owning nothing — the
237
+ // checkout guard refuses a caller already holding a paid plan, and the trial
238
+ // cases read eligibility off a per-owner order query that any seeded
239
+ // purchase record would answer for them.
240
+ {
241
+ name: 'the-discount-suites-purchasers-arrive-owning-nothing',
242
+ async run({ assert, config }) {
243
+ const definitions = getAccountDefinitions('example.com', config);
244
+ const purchasers = Object.keys(TEST_ACCOUNTS).filter((key) => key.startsWith('intent-discount-'));
245
+
246
+ assert.ok(purchasers.length > 0, 'The seeder must declare the discount suites their own purchasers');
247
+
248
+ for (const key of purchasers) {
249
+ const subscription = definitions[key].properties.subscription;
250
+
251
+ assert.equal(subscription.product.id, 'basic', `Persona '${key}' must arrive on the free tier`);
252
+ assert.equal(subscription.payment, undefined, `Persona '${key}' has bought nothing yet, so it carries no payment record`);
253
+ assert.equal(buildOrderFixture(key, config), null, `Persona '${key}' must have no order behind it`);
254
+ }
255
+ },
256
+ },
257
+
258
+ // The steady-state mid-trial persona ([#301](https://github.com/Omega-JS-Stack/omega/issues/301)):
259
+ // a subscriber inside the trial the catalog offers, which only reads as one
260
+ // if its term ends exactly when the trial does — the equality
261
+ // routes/payments/cancel/_is-trialing.js tells a running trial from a
262
+ // converted one by.
263
+ {
264
+ name: 'the-trialing-persona-is-inside-its-trial',
265
+ async run({ assert, config, skip }) {
266
+ if (!paidPlan(config)) {
267
+ skip('No paid subscription product configured in this brand');
268
+ }
269
+
270
+ const plan = getFirstPaidProduct(config);
271
+ const definition = getAccountDefinitions('example.com', config)['premium-trialing'];
272
+
273
+ assert.ok(definition, 'The seeder must define a mid-trial persona');
274
+
275
+ const subscription = definition.properties.subscription;
276
+
277
+ assert.equal(subscription.product.id, plan.id, 'The trialing persona holds the catalog\'s paid plan');
278
+ assert.equal(subscription.status, 'active', 'A live trial IS an active subscription — the providers\' trialing status maps to active');
279
+ assert.equal(subscription.trial.claimed, true, 'The trial has been claimed');
280
+ assert.equal(subscription.trial.outcome, null, 'A trial that is still running has not ended in anything yet');
281
+ assert.equal(
282
+ subscription.expires.timestampUNIX,
283
+ subscription.trial.expires.timestampUNIX,
284
+ 'The term ends exactly when the trial does — the equality that makes it read as trialing',
285
+ );
286
+ assert.ok(isTrialing(subscription), 'The cancel flow must read this persona as trialing');
287
+
288
+ // The trial runs for as long as the BRAND offers it on that plan, not a
289
+ // hand-typed length the catalog never promised.
290
+ const catalogDays = (config.payment?.products || []).find((product) => product.id === plan.id)?.trial?.days || 14;
291
+ const remaining = (subscription.trial.expires.timestampUNIX - Math.floor(Date.now() / 1000)) / 86400;
292
+
293
+ assert.ok(
294
+ Math.abs(remaining - catalogDays) < 1,
295
+ `The trial must run the catalog's ${catalogDays} days (got ${Math.round(remaining)})`,
296
+ );
297
+
298
+ // And the purchase record behind it carries the same trial (the order's
299
+ // `unified` IS the subscription).
300
+ const fixture = buildOrderFixture('premium-trialing', config);
301
+
302
+ assert.ok(fixture, 'The trialing persona names an order, so it must have one');
303
+ assert.equal(fixture.doc.unified.trial.claimed, true, 'The purchase record shows the subscription was bought on a trial');
304
+ assert.equal(
305
+ fixture.doc.unified.expires.timestampUNIX,
306
+ subscription.expires.timestampUNIX,
307
+ 'The order records the same term the persona holds',
308
+ );
309
+ },
310
+ },
311
+
312
+ // Every persona is a FULL account ([#327](https://github.com/Omega-JS-Stack/omega/issues/327)):
313
+ // account creation writes the schema's SHAPE and nothing else, so a persona
314
+ // that stopped there is a nameless account from nowhere on every surface that
315
+ // shows a person. This is the guard that a persona added tomorrow cannot
316
+ // regress to that.
317
+ {
318
+ name: 'every-persona-is-a-full-account',
319
+ async run({ assert, config }) {
320
+ const definitions = getAccountDefinitions('example.com', config);
321
+
322
+ for (const [key, definition] of Object.entries(definitions)) {
323
+ const { personal, activity } = definition.properties;
324
+
325
+ for (const path of REAL_ACCOUNT_LEAVES) {
326
+ const value = leafAt({ personal, activity }, path);
327
+
328
+ assert.ok(
329
+ value !== undefined && value !== null && value !== '',
330
+ `Persona '${key}' must carry ${path} — a real user has one`,
331
+ );
332
+ }
333
+
334
+ // The numeric leaves default to 0, which is the schema saying "unset".
335
+ assert.ok(personal.telephone.national > 0, `Persona '${key}' must carry a telephone number`);
336
+ assert.ok(personal.birthday.timestampUNIX > 0, `Persona '${key}' must carry a birthday`);
337
+ assert.notEqual(activity.geolocation.latitude, 0, `Persona '${key}' must be somewhere (latitude)`);
338
+ assert.notEqual(activity.geolocation.longitude, 0, `Persona '${key}' must be somewhere (longitude)`);
339
+ assert.equal(typeof activity.client.mobile, 'boolean', `Persona '${key}' signed up on a known form factor`);
340
+
341
+ // The location is ONE fact: where the request came from is where the
342
+ // person says they are. A persona split across two places is a bug in
343
+ // the seeder, and it renders as one on the account page.
344
+ assert.equal(personal.location.country, activity.geolocation.country, `Persona '${key}' lives where it signed up from (country)`);
345
+ assert.equal(personal.location.city, activity.geolocation.city, `Persona '${key}' lives where it signed up from (city)`);
346
+ }
347
+ },
348
+ },
349
+
350
+ // The dev palette's roster ([#400](https://github.com/Omega-JS-Stack/omega/issues/400)).
351
+ // The palette kept its own hand-curated copy of this list, so a persona
352
+ // seeded without somebody remembering to add it was invisible in the
353
+ // switcher for weeks (the referral pair, #363). The `palette` label makes
354
+ // the SEED the one owner: a labeled persona is human-facing, and what
355
+ // routes/test/roster hands the palette is exactly the list below, in
356
+ // exactly this order.
357
+ {
358
+ name: 'the-palette-roster-is-the-seeds-own',
359
+ async run({ assert, config }) {
360
+ const labelled = Object.entries(TEST_ACCOUNTS).filter(([, account]) => account.palette);
361
+
362
+ assert.deepEqual(
363
+ labelled.map(([, account]) => [account.email.split('@')[0], account.palette]),
364
+ [
365
+ ['_test.admin', 'Admin'],
366
+ ['_test.basic', 'Basic'],
367
+ ['_test.premium-active', 'Premium'],
368
+ ['_test.premium-trialing', 'Trialing'],
369
+ ['_test.premium-expired', 'Expired'],
370
+ ['_test.premium-suspended', 'Suspended'],
371
+ ['_test.premium-cancelling', 'Cancelling'],
372
+ ['_test.refunded', 'Refunded'],
373
+ ['_test.usage-spread', 'Usage'],
374
+ ['_test.referrer', 'Referrer'],
375
+ ['_test.referred', 'Referred'],
376
+ ['_test.journey-flows-upgrade', 'Journey: Upgrade'],
377
+ ['_test.journey-flows-cancel', 'Journey: Cancel'],
378
+ ['_test.journey-flows-failure', 'Journey: Failure'],
379
+ ['_test.journey-flows-trial', 'Journey: Trial'],
380
+ ],
381
+ 'The palette-facing personas are these, in the order the seeder declares them',
382
+ );
383
+
384
+ // The machinery an automated suite drives is never offered to somebody
385
+ // mid-suite: a deletion fixture, a signup the suite has to perform
386
+ // itself, a consent state a lifecycle test establishes, and — since
387
+ // [#406](https://github.com/Omega-JS-Stack/omega/issues/406) — the
388
+ // suite-exclusive accounts that used to be minted mid-test.
389
+ for (const key of [
390
+ 'delete', 'delete-by-admin', 'signup-merge', 'consent-granted',
391
+ 'journey-payments-winback', 'journey-payments-winback-decline',
392
+ 'journey-payments-plan-switch', 'journey-payments-plan-switch-trial',
393
+ 'journey-payments-uncancel',
394
+ 'webhook-chargebee-unreachable', 'webhook-retry-sweep',
395
+ 'intent-discount-percent-url', 'intent-discount-amount-trial',
396
+ ]) {
397
+ assert.equal(TEST_ACCOUNTS[key].palette, undefined, `Persona '${key}' is machinery, so the palette must not offer it`);
398
+ }
399
+
400
+ // A persona a human signs in as is a persona a human LOOKS at, so every
401
+ // labeled one is a full account (#327): the same leaves the sweep
402
+ // above requires of the whole table, asserted here as the roster's own
403
+ // entry requirement.
404
+ const definitions = getAccountDefinitions('example.com', config);
405
+
406
+ for (const [key] of labelled) {
407
+ const definition = definitions[key];
408
+
409
+ assert.ok(definition, `Palette persona '${key}' must be a seeded account`);
410
+
411
+ for (const path of REAL_ACCOUNT_LEAVES) {
412
+ const value = leafAt(definition.properties, path);
413
+
414
+ assert.ok(
415
+ value !== undefined && value !== null && value !== '',
416
+ `Palette persona '${key}' must carry ${path}: QA signs in as it and sees exactly that`,
417
+ );
418
+ }
419
+ }
420
+ },
421
+ },
422
+
423
+ // The profile is DERIVED from the persona key, never rolled — so the account
424
+ // QA screenshotted last week is the account in front of them today, and a
425
+ // reseed never shuffles who anybody is.
426
+ {
427
+ name: 'a-persona-profile-is-the-same-on-every-boot',
428
+ async run({ assert, config }) {
429
+ const first = getAccountDefinitions('example.com', config);
430
+ const second = getAccountDefinitions('example.com', config);
431
+
432
+ for (const key of Object.keys(first)) {
433
+ assert.deepEqual(
434
+ second[key].properties.personal,
435
+ first[key].properties.personal,
436
+ `Persona '${key}' must draw the same identity on every seed`,
437
+ );
438
+ assert.deepEqual(
439
+ second[key].properties.activity,
440
+ first[key].properties.activity,
441
+ `Persona '${key}' must draw the same signup context on every seed`,
442
+ );
443
+ }
444
+ },
445
+ },
446
+
447
+ // A persona bought through the TEST provider carries what that provider
448
+ // writes. The negative-path fixtures are excluded by construction, not by an
449
+ // exemption list: they name a null or unknown provider, which is the whole
450
+ // point of them.
451
+ {
452
+ name: 'test-provider-personas-carry-the-record-it-writes',
453
+ async run({ assert, config, skip }) {
454
+ if (!paidPlan(config)) {
455
+ skip('No paid subscription product configured in this brand');
456
+ }
457
+
458
+ const definitions = getAccountDefinitions('example.com', config);
459
+ const bought = Object.entries(definitions).filter(([, definition]) => definition.properties.subscription?.payment?.provider === 'test');
460
+
461
+ assert.ok(bought.length > 0, 'The seeder must define personas bought through the test provider');
462
+
463
+ for (const [key, definition] of bought) {
464
+ const payment = definition.properties.subscription.payment;
465
+
466
+ assert.ok(payment.resourceId, `Persona '${key}' must name the resource the provider holds its subscription under`);
467
+ assert.ok(payment.startDate?.timestampUNIX > 0, `Persona '${key}' must record when its subscription began`);
468
+ assert.ok(payment.updatedBy?.event?.name, `Persona '${key}' must name the event that last wrote its subscription`);
469
+ assert.ok(payment.updatedBy?.date?.timestampUNIX > 0, `Persona '${key}' must record when that event landed`);
470
+
471
+ // The write's DATE follows the term: a term that already ended cannot
472
+ // have been written to today (the email layer prints this stamp as the
473
+ // last payment date). One day of slack absorbs boundary rounding.
474
+ const expires = definition.properties.subscription.expires;
475
+ if (expires?.timestampUNIX && expires.timestampUNIX <= Math.floor(Date.now() / 1000)) {
476
+ assert.ok(
477
+ payment.updatedBy.date.timestampUNIX <= expires.timestampUNIX + 86400,
478
+ `Persona '${key}' ended ${expires.timestamp} but its last write claims ${payment.updatedBy.date.timestamp}`
479
+ );
480
+ }
481
+ }
482
+ },
483
+ },
484
+
485
+ // The reported break (#327): the dev palette's Premium persona had a resolved
486
+ // plan and no provider record, so every payment-gated surface skipped it —
487
+ // the billing panel offers the winback discount only where it can actually be
488
+ // applied (a provider and its resource: core/js/pages/dashboard/account/
489
+ // sections/billing.js), and QA read the missing pitch as a product bug.
490
+ {
491
+ name: 'the-premium-persona-is-a-real-subscriber',
492
+ async run({ assert, config, skip }) {
493
+ if (!paidPlan(config)) {
494
+ skip('No paid subscription product configured in this brand');
495
+ }
496
+
497
+ const subscription = getAccountDefinitions('example.com', config)['premium-active'].properties.subscription;
498
+ const payment = subscription.payment;
499
+
500
+ assert.equal(subscription.status, 'active', 'The Premium persona holds a live subscription');
501
+ assert.equal(subscription.product.id, getFirstPaidProduct(config).id, 'The Premium persona holds the catalog\'s paid plan');
502
+ assert.equal(subscription.cancellation.pending, false, 'Nothing has been cancelled — this is the steady-state subscriber');
503
+
504
+ // The winback pitch's own gate, asserted as the gate reads it.
505
+ assert.ok(payment.provider && payment.resourceId, 'A payment-gated surface must be able to reach this persona at its provider');
506
+ assert.equal(payment.provider, 'test', 'The persona is held at the TEST provider — demo-safe, never a live one');
507
+
508
+ assert.ok(payment.startDate.timestampUNIX < Math.floor(Date.now() / 1000), 'The persona subscribed in the past, not this instant');
509
+ assert.ok(payment.price > 0, 'The persona pays what the catalog charges');
510
+
511
+ // And the purchase behind it exists, like any real subscriber's.
512
+ const fixture = buildOrderFixture('premium-active', config);
513
+
514
+ assert.ok(fixture, 'The Premium persona names an order, so it must have one');
515
+ assert.equal(fixture.doc.owner, '_test-premium-active', 'The order belongs to the Premium persona');
516
+ assert.equal(fixture.doc.unified.status, 'active', 'The order records the live subscription it bought');
517
+ },
518
+ },
519
+
520
+ // The referrals an established account has earned (#343). They were the last
521
+ // list on the account page still coming from client-side fixtures, which is
522
+ // exactly why they were wrong: the shape those fixtures invented is not the
523
+ // shape a signup writes, and nobody could tell while no persona carried one.
524
+ {
525
+ name: 'referrers-carry-the-referrals-a-signup-writes',
526
+ async run({ assert, config }) {
527
+ const definitions = getAccountDefinitions('example.com', config);
528
+ const byUid = new Map(Object.values(definitions).map((definition) => [definition.uid, definition]));
529
+ const referrers = Object.entries(definitions).filter(([, definition]) => (definition.properties.affiliate?.referrals || []).length > 0);
530
+ const nowUNIX = Math.floor(Date.now() / 1000);
531
+
532
+ assert.ok(referrers.length > 0, 'The seeder must define personas that referred somebody');
533
+
534
+ for (const [key, definition] of referrers) {
535
+ const referrals = definition.properties.affiliate.referrals;
536
+ const converted = [];
537
+ const pending = [];
538
+
539
+ assert.ok(referrals.length > 1, `Persona '${key}' should carry a few referrals, not one`);
540
+
541
+ for (const referral of referrals) {
542
+ // The record processAffiliate appends (routes/user/signup/post.js):
543
+ // a uid and an ISO timestamp, and nothing else. A seed with extra
544
+ // fields would let a reader lean on data no real referral has.
545
+ assert.deepEqual(
546
+ Object.keys(referral).sort(),
547
+ ['timestamp', 'uid'],
548
+ `Persona '${key}' must record referrals exactly as a signup writes them`,
549
+ );
550
+
551
+ const at = Math.floor(new Date(referral.timestamp).getTime() / 1000);
552
+
553
+ assert.ok(at > 0, `Persona '${key}' has a referral with an unreadable timestamp (${referral.timestamp})`);
554
+ assert.ok(at < nowUNIX, `Persona '${key}' has a referral dated in the future`);
555
+
556
+ // The referred account exists: the panel prints the uid, and a
557
+ // referral pointing at nobody is a uid the emulator cannot resolve.
558
+ const referred = byUid.get(referral.uid);
559
+
560
+ assert.ok(referred, `Persona '${key}' referred ${referral.uid}, which is no seeded persona`);
561
+
562
+ const isConverted = referred.properties.subscription?.product?.id !== 'basic'
563
+ || Boolean(referred.properties.subscription?.payment?.orderId);
564
+
565
+ (isConverted ? converted : pending).push(referral.uid);
566
+ }
567
+
568
+ // A real referral list is mixed: some of the people who signed up went
569
+ // on to subscribe and some never did. Conversion is a fact about the
570
+ // REFERRED account (the referral record itself carries no status), so
571
+ // the mix only exists when the referrals name real personas.
572
+ assert.ok(converted.length > 0, `Persona '${key}' referred nobody who ever subscribed`);
573
+ assert.ok(pending.length > 0, `Persona '${key}' referred nobody who is still on the free tier`);
574
+ }
575
+ },
576
+ },
577
+
578
+ // And they are the DEDICATED referrer's alone (Ian 2026-08-18,
579
+ // [#363](https://github.com/Omega-JS-Stack/omega/issues/363)). A persona
580
+ // demonstrates exactly its own scenario: premium-active is the steady-state
581
+ // subscriber QA signs in as, and an affiliate list on it is a second story
582
+ // told by an account that exists to tell the billing one.
583
+ {
584
+ name: 'only-the-referrer-persona-carries-referrals',
585
+ async run({ assert, config }) {
586
+ const definitions = getAccountDefinitions('example.com', config);
587
+ const carriers = Object.entries(definitions)
588
+ .filter(([, definition]) => (definition.properties.affiliate?.referrals || []).length > 0)
589
+ .map(([key]) => key);
590
+
591
+ assert.deepEqual(carriers, ['referrer'], 'The referrer persona is the only account seeded with referrals');
592
+ assert.equal(
593
+ (definitions['premium-active'].properties.affiliate?.referrals || []).length,
594
+ 0,
595
+ 'The Premium persona demonstrates a subscription, not an affiliate link',
596
+ );
597
+
598
+ // The inbound half obeys the same rule (#369): being referred is the
599
+ // referred persona's ONE state, so it is the only account seeded with an
600
+ // affiliate attribution — a second one would be a persona telling the
601
+ // referral story on the side of the story it was built for.
602
+ const attributed = Object.entries(definitions)
603
+ .filter(([, definition]) => definition.properties.attribution?.affiliate?.code)
604
+ .map(([key]) => key);
605
+
606
+ assert.deepEqual(attributed, ['referred'], 'The referred persona is the only account seeded as having come in through a referral');
607
+
608
+ // The one that DOES carry them stays a full realistic account (Ian
609
+ // 2026-08-17, #327), and its dates stay the shape a signup writes: the
610
+ // account page reads the timestamp as an ISO STRING, and the epoch
611
+ // millis the old client-side fixtures invented rendered every row as
612
+ // `NaN years ago` (#343).
613
+ const referrals = definitions.referrer.properties.affiliate.referrals;
614
+
615
+ assert.ok(referrals.length >= 3, `The referrer persona must carry a real list of referrals (got ${referrals.length})`);
616
+
617
+ for (const referral of referrals) {
618
+ assert.equal(
619
+ typeof referral.timestamp,
620
+ 'string',
621
+ `Referral ${referral.uid} must be dated with the ISO string a signup writes, never epoch millis`,
622
+ );
623
+ assert.match(referral.timestamp, /^\d{4}-\d{2}-\d{2}T/, `Referral ${referral.uid} is dated '${referral.timestamp}', which no signup would write`);
624
+ }
625
+ },
626
+ },
627
+
628
+ // The OTHER half of the referral pair (Ian 2026-08-19,
629
+ // [#369](https://github.com/Omega-JS-Stack/omega/issues/369)). A referral is
630
+ // two facts, written to two docs: the record on the referrer's list, and the
631
+ // attribution on the account that arrived through the link. While only the
632
+ // outbound half was seeded, every surface that reads the INBOUND one — "you
633
+ // were referred by" — had no persona to render, and no test could tell the
634
+ // two halves had drifted apart.
635
+ {
636
+ name: 'the-referred-persona-is-the-inbound-half-of-the-pair',
637
+ async run({ assert, config }) {
638
+ const definitions = getAccountDefinitions('example.com', config);
639
+ const referred = definitions.referred;
640
+
641
+ assert.ok(referred, 'The seeder must define the persona that came in through a referral');
642
+
643
+ const affiliate = referred.properties.attribution?.affiliate;
644
+
645
+ // The block routes/user/signup writes from the request, exactly as the
646
+ // account schema defines it (packages/account/src/schema.js): a code, a
647
+ // click stamp, and where it was clicked. Extra fields would let a reader
648
+ // lean on data no signup has ever written.
649
+ assert.deepEqual(
650
+ Object.keys(affiliate || {}).sort(),
651
+ ['code', 'page', 'timestamp', 'url'],
652
+ 'The referred persona must carry attribution.affiliate exactly as a signup writes it',
653
+ );
654
+ assert.equal(
655
+ affiliate.code,
656
+ definitions.referrer.properties.affiliate.code,
657
+ 'The referred persona came in through the REFERRER persona\'s own code',
658
+ );
659
+ assert.match(affiliate.url, new RegExp(`ref=${affiliate.code}$`), `The link it arrived on carries the code (got ${affiliate.url})`);
660
+ assert.ok(affiliate.page, 'The referred persona records the page it landed on');
661
+
662
+ // The two halves are ONE event: the referrer's record for this account
663
+ // and the account's own attribution are dated the same instant, which is
664
+ // the only way a pair could have come from a single signup.
665
+ const outbound = definitions.referrer.properties.affiliate.referrals.find((referral) => referral.uid === referred.uid);
666
+
667
+ assert.ok(outbound, 'The referrer\'s outbound list must include the account it referred');
668
+ assert.equal(outbound.timestamp, affiliate.timestamp, 'Both halves of the referral are dated the same signup');
669
+ assert.match(affiliate.timestamp, /^\d{4}-\d{2}-\d{2}T/, `The click is dated with the ISO string a signup writes (got ${affiliate.timestamp})`);
670
+
671
+ // ONE distinguishing state: it was referred. It is a full realistic
672
+ // account around that ([#327](https://github.com/Omega-JS-Stack/omega/issues/327)) —
673
+ // the exhaustive leaf sweep is 'every-persona-is-a-full-account' above —
674
+ // and it tells no second story: no billing history, no referrals of its own.
675
+ assert.ok(referred.properties.personal.name.first, 'The referred persona is a real person, not a bare account');
676
+ assert.ok(referred.properties.activity.geolocation.city, 'The referred persona signed up from somewhere');
677
+ assert.equal(referred.properties.subscription.product.id, 'basic', 'The referred persona demonstrates a referral, not a purchase');
678
+ assert.equal(buildOrderFixture('referred', config), null, 'The referred persona bought nothing, so it has no order behind it');
679
+ assert.equal((referred.properties.affiliate?.referrals || []).length, 0, 'The referred persona is the referred one — it has referred nobody');
680
+ },
681
+ },
682
+
683
+ // The devices an account is signed in on (#343). Sessions are the one part
684
+ // of an account that lives OUTSIDE the user doc — a Realtime Database record
685
+ // an app writes while it is signed in — so account creation alone leaves the
686
+ // security panel showing the browser in front of you and nothing else.
687
+ {
688
+ name: 'personas-are-signed-in-on-their-other-devices',
689
+ async run({ assert }) {
690
+ const withSessions = Object.keys(TEST_ACCOUNTS)
691
+ .map((key) => [key, buildSessionFixtures(key, TEST_ACCOUNTS)])
692
+ .filter(([, sessions]) => sessions);
693
+ const nowUNIX = Math.floor(Date.now() / 1000);
694
+
695
+ assert.ok(withSessions.length > 0, 'The seeder must define personas that are signed in somewhere');
696
+
697
+ for (const [key, sessions] of withSessions) {
698
+ const ids = Object.keys(sessions);
699
+ const platforms = new Set();
700
+
701
+ assert.ok(ids.length >= 2 && ids.length <= 3, `Persona '${key}' should carry 2-3 devices (got ${ids.length})`);
702
+
703
+ for (const id of ids) {
704
+ const session = sessions[id];
705
+
706
+ // The route finds a user's sessions with `orderByChild('uid')`
707
+ // (routes/user/sessions/get.js), so a record whose uid is not the
708
+ // persona's is a session nothing will ever return.
709
+ assert.equal(session.uid, TEST_ACCOUNTS[key].uid, `Session ${id} must belong to persona '${key}'`);
710
+
711
+ assert.ok(session.platform, `Session ${id} must name the platform it runs on — the panel renders the device from it`);
712
+ assert.ok(session.ip, `Session ${id} must record where it connected from`);
713
+ assert.ok(session.timestampUNIX > 0, `Session ${id} must record when it last checked in`);
714
+ assert.ok(session.timestampUNIX < nowUNIX, `Session ${id} checked in from the future`);
715
+
716
+ // ACTIVE sessions: a device that last checked in a month ago is not
717
+ // somebody who is signed in right now.
718
+ assert.ok(session.timestampUNIX > nowUNIX - (7 * 86400), `Session ${id} is too stale to read as an active session`);
719
+
720
+ platforms.add(session.platform);
721
+ }
722
+
723
+ assert.equal(platforms.size, ids.length, `Persona '${key}' should be signed in on ${ids.length} DIFFERENT devices`);
724
+
725
+ // The ids are derived from the persona, so a re-seed replaces the same
726
+ // records instead of piling a second set on top of them.
727
+ assert.deepEqual(
728
+ Object.keys(buildSessionFixtures(key, TEST_ACCOUNTS)),
729
+ ids,
730
+ `Persona '${key}' must seed the same session ids on every boot`,
731
+ );
732
+ }
733
+ },
734
+ },
735
+ ],
736
+ });
737
+
738
+ /**
739
+ * The leaves a REAL user doc carries a value in — the sections `routes/user/signup`
740
+ * fills from the request (`activity`) and the person fills in about themselves
741
+ * (`personal`). Every one of them is a field some surface renders.
742
+ */
743
+ const REAL_ACCOUNT_LEAVES = [
744
+ 'personal.name.first',
745
+ 'personal.name.last',
746
+ 'personal.gender',
747
+ 'personal.location.country',
748
+ 'personal.location.region',
749
+ 'personal.location.city',
750
+ 'personal.location.postalCode',
751
+ 'personal.location.street',
752
+ 'personal.company.name',
753
+ 'personal.company.position',
754
+ 'personal.telephone.countryCode',
755
+ 'activity.geolocation.ip',
756
+ 'activity.geolocation.continent',
757
+ 'activity.geolocation.country',
758
+ 'activity.geolocation.region',
759
+ 'activity.geolocation.city',
760
+ 'activity.client.language',
761
+ 'activity.client.device',
762
+ 'activity.client.platform',
763
+ 'activity.client.browser',
764
+ 'activity.client.runtime',
765
+ 'activity.client.userAgent',
766
+ 'activity.client.url',
767
+ ];
768
+
769
+ /**
770
+ * Read a dotted path off an object, undefined for anything missing on the way.
771
+ */
772
+ function leafAt(source, path) {
773
+ return path.split('.').reduce((value, segment) => (value === undefined || value === null ? value : value[segment]), source);
774
+ }
775
+
776
+ /**
777
+ * The catalog's first plan that is actually for sale. A brand that sells nothing
778
+ * has no billing data for its personas to carry — the suite skips rather than
779
+ * failing a brand for a feature it does not use.
780
+ */
781
+ function paidPlan(config) {
782
+ return (config.payment?.products || []).find((product) => product.id !== 'basic' && product.prices) || null;
783
+ }
784
+
785
+ /**
786
+ * A second plan for sale, so a persona can be stood on one the catalog default
787
+ * would never resolve to. A brand selling a single plan has none.
788
+ */
789
+ function otherPaidPlan(config) {
790
+ const first = paidPlan(config);
791
+
792
+ return (config.payment?.products || []).find((product) => {
793
+ return product.type === 'subscription' && product.prices && product.id !== first?.id;
794
+ }) || null;
795
+ }
796
+
797
+ /**
798
+ * A seeded subscription represents a purchase when it holds a paid plan, or when
799
+ * it carries the record of one it has since lapsed from (a provider, a resource,
800
+ * an order).
801
+ */
802
+ function isPurchase(subscription) {
803
+ if (!subscription) {
804
+ return false;
805
+ }
806
+
807
+ const payment = subscription.payment || {};
808
+
809
+ return subscription.product?.id !== 'basic'
810
+ || Boolean(payment.provider || payment.resourceId || payment.orderId);
811
+ }
812
+
813
+ /**
814
+ * The catalog entry for the plan a persona bought — the plan it holds, or the
815
+ * paid plan it lapsed from back to basic.
816
+ */
817
+ function planBought(subscription, config) {
818
+ const products = config.payment?.products || [];
819
+ const paidProduct = getFirstPaidProduct(config);
820
+ const id = subscription.product?.id !== 'basic' ? subscription.product?.id : paidProduct.id;
821
+
822
+ return products.find((product) => product.id === id && product.prices) || null;
823
+ }