@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.
- package/LICENSE +98 -0
- package/README.md +1053 -0
- package/bin/omega +2 -0
- package/bin/omega-backend +2 -0
- package/bin/omg +2 -0
- package/cli.js +3 -0
- package/dist/cli/command-table.js +268 -0
- package/dist/cli/commands/auth.js +259 -0
- package/dist/cli/commands/base-command.js +451 -0
- package/dist/cli/commands/build.js +27 -0
- package/dist/cli/commands/clean.js +13 -0
- package/dist/cli/commands/clear.js +11 -0
- package/dist/cli/commands/cwd.js +9 -0
- package/dist/cli/commands/deploy.js +205 -0
- package/dist/cli/commands/emulator-orphans.js +440 -0
- package/dist/cli/commands/emulator.js +1829 -0
- package/dist/cli/commands/firebase-init.js +101 -0
- package/dist/cli/commands/firestore.js +261 -0
- package/dist/cli/commands/indexes.js +51 -0
- package/dist/cli/commands/install.js +151 -0
- package/dist/cli/commands/logs.js +358 -0
- package/dist/cli/commands/mcp.js +41 -0
- package/dist/cli/commands/migrate-markers.js +176 -0
- package/dist/cli/commands/migrate-rules.js +90 -0
- package/dist/cli/commands/migrate.js +59 -0
- package/dist/cli/commands/serve.js +208 -0
- package/dist/cli/commands/setup-tests/base-test.js +174 -0
- package/dist/cli/commands/setup-tests/emulator-config.js +109 -0
- package/dist/cli/commands/setup-tests/env-runtime-config-deprecated.js +47 -0
- package/dist/cli/commands/setup-tests/firebase-admin.js +49 -0
- package/dist/cli/commands/setup-tests/firebase-auth.js +26 -0
- package/dist/cli/commands/setup-tests/firebase-cli.js +26 -0
- package/dist/cli/commands/setup-tests/firebase-functions.js +49 -0
- package/dist/cli/commands/setup-tests/firestore-indexes-file.js +72 -0
- package/dist/cli/commands/setup-tests/firestore-indexes-in-json.js +20 -0
- package/dist/cli/commands/setup-tests/firestore-indexes-required.js +108 -0
- package/dist/cli/commands/setup-tests/firestore-indexes-synced.js +236 -0
- package/dist/cli/commands/setup-tests/firestore-rules-file.js +115 -0
- package/dist/cli/commands/setup-tests/firestore-rules-in-json.js +49 -0
- package/dist/cli/commands/setup-tests/functions-package.js +44 -0
- package/dist/cli/commands/setup-tests/gcloud-cli.js +26 -0
- package/dist/cli/commands/setup-tests/gitignore.js +122 -0
- package/dist/cli/commands/setup-tests/helpers/merge-line-files.js +28 -0
- package/dist/cli/commands/setup-tests/helpers/required-indexes.js +109 -0
- package/dist/cli/commands/setup-tests/helpers/seed-campaigns.js +246 -0
- package/dist/cli/commands/setup-tests/helpers.js +72 -0
- package/dist/cli/commands/setup-tests/hosting-folder.js +23 -0
- package/dist/cli/commands/setup-tests/hosting-rewrites.js +52 -0
- package/dist/cli/commands/setup-tests/index.js +111 -0
- package/dist/cli/commands/setup-tests/is-firebase-project.js +21 -0
- package/dist/cli/commands/setup-tests/java-installed.js +27 -0
- package/dist/cli/commands/setup-tests/legacy-tests-cleanup.js +43 -0
- package/dist/cli/commands/setup-tests/marketing-campaigns-seeded.js +230 -0
- package/dist/cli/commands/setup-tests/node-version.js +60 -0
- package/dist/cli/commands/setup-tests/npm-project-scripts.js +42 -0
- package/dist/cli/commands/setup-tests/nvmrc-version.js +36 -0
- package/dist/cli/commands/setup-tests/omega-backend.js +42 -0
- package/dist/cli/commands/setup-tests/omega-config.js +84 -0
- package/dist/cli/commands/setup-tests/project-directories.js +34 -0
- package/dist/cli/commands/setup-tests/project-id-consistency.js +201 -0
- package/dist/cli/commands/setup-tests/public-html-files.js +22 -0
- package/dist/cli/commands/setup-tests/realtime-rules-file.js +69 -0
- package/dist/cli/commands/setup-tests/realtime-rules-in-json.js +20 -0
- package/dist/cli/commands/setup-tests/remoteconfig-template-file.js +32 -0
- package/dist/cli/commands/setup-tests/remoteconfig-template-in-json.js +31 -0
- package/dist/cli/commands/setup-tests/service-account.js +86 -0
- package/dist/cli/commands/setup-tests/storage-lifecycle-policy.js +81 -0
- package/dist/cli/commands/setup-tests/storage-rules-file.js +32 -0
- package/dist/cli/commands/setup-tests/storage-rules-in-json.js +20 -0
- package/dist/cli/commands/stripe.js +14 -0
- package/dist/cli/commands/test-lanes/stripe-live.js +440 -0
- package/dist/cli/commands/test.js +976 -0
- package/dist/cli/commands/update.js +27 -0
- package/dist/cli/commands/version.js +10 -0
- package/dist/cli/commands/watch.js +196 -0
- package/dist/cli/flags.js +13 -0
- package/dist/cli/index.js +173 -0
- package/dist/cli/run.js +33 -0
- package/dist/cli/utils/attach-log-file.js +4 -0
- package/dist/cli/utils/compile-rules.js +1155 -0
- package/dist/cli/utils/ensure-target.js +314 -0
- package/dist/cli/utils/project-type.js +140 -0
- package/dist/cli/utils/public-files.js +49 -0
- package/dist/cli/utils/safe-install.js +4 -0
- package/dist/cli/utils/spawn-shell.js +94 -0
- package/dist/cli/utils/stage-functions.js +338 -0
- package/dist/cli/utils/stage-local-packages.js +210 -0
- package/dist/cli/utils/target-checks.js +161 -0
- package/dist/cli/utils/target.js +24 -0
- package/dist/cli/utils/ui.js +292 -0
- package/dist/defaults/AGENTS.md +125 -0
- package/dist/defaults/CHANGELOG.md +15 -0
- package/dist/defaults/CLAUDE.md +1 -0
- package/dist/defaults/_.gitignore +65 -0
- package/dist/defaults/docs/README.md +17 -0
- package/dist/defaults/test/README.md +65 -0
- package/dist/defaults/test/_init.js +14 -0
- package/dist/defaults/test/helpers/connect-trap.js +72 -0
- package/dist/defaults/test/unit/registration.test.js +143 -0
- package/dist/defaults/test/unit/rules-posture.test.js +101 -0
- package/dist/defaults/test/unit/socket-free.test.js +43 -0
- package/dist/manager/events/auth/before-create.js +89 -0
- package/dist/manager/events/auth/before-signin.js +76 -0
- package/dist/manager/events/auth/on-create.js +112 -0
- package/dist/manager/events/auth/on-delete.js +141 -0
- package/dist/manager/events/auth/utils.js +93 -0
- package/dist/manager/events/cron/daily/blog-auto-publisher.js +287 -0
- package/dist/manager/events/cron/daily/data-requests.js +57 -0
- package/dist/manager/events/cron/daily/expire-paypal-cancellations.js +191 -0
- package/dist/manager/events/cron/daily/marketing-prune.js +486 -0
- package/dist/manager/events/cron/daily/reset-usage.js +188 -0
- package/dist/manager/events/cron/daily/trial-lapse-sweep.js +355 -0
- package/dist/manager/events/cron/daily.js +3 -0
- package/dist/manager/events/cron/frequent/abandoned-carts.js +204 -0
- package/dist/manager/events/cron/frequent/email-queue.js +71 -0
- package/dist/manager/events/cron/frequent/marketing-campaigns.js +463 -0
- package/dist/manager/events/cron/frequent/retry-failed-webhooks.js +70 -0
- package/dist/manager/events/cron/frequent.js +3 -0
- package/dist/manager/events/cron/runner.js +64 -0
- package/dist/manager/events/firestore/notifications/on-write.js +123 -0
- package/dist/manager/events/firestore/payments-disputes/on-write.js +256 -0
- package/dist/manager/events/firestore/payments-disputes/providers/stripe.js +218 -0
- package/dist/manager/events/firestore/payments-disputes/providers/test.js +231 -0
- package/dist/manager/events/firestore/payments-webhooks/analytics.js +687 -0
- package/dist/manager/events/firestore/payments-webhooks/on-write.js +1224 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/index.js +395 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/one-time/purchase-completed.js +58 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/one-time/purchase-failed.js +28 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/one-time/purchase-refunded.js +30 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/send-email.js +74 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/cancellation-removed.js +14 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/cancellation-requested.js +28 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/checkout-declined.js +18 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/new-subscription.js +67 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/payment-failed.js +25 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/payment-recovered.js +26 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/payment-refunded.js +34 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/plan-changed.js +42 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/subscription-cancelled.js +33 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/subscription-winback.js +17 -0
- package/dist/manager/functions/_legacy/actions/create-post-handler.js +188 -0
- package/dist/manager/functions/_legacy/actions/generate-uuid.js +63 -0
- package/dist/manager/functions/_legacy/actions/sign-up-handler.js +205 -0
- package/dist/manager/functions/_legacy/admin/create-post.js +207 -0
- package/dist/manager/functions/_legacy/admin/firestore-write.js +72 -0
- package/dist/manager/functions/_legacy/admin/get-stats.js +218 -0
- package/dist/manager/functions/_legacy/admin/query.js +198 -0
- package/dist/manager/functions/_legacy/admin/send-notification.js +206 -0
- package/dist/manager/functions/_legacy/template.js +33 -0
- package/dist/manager/functions/_legacy/test/authenticate.js +40 -0
- package/dist/manager/functions/_legacy/test/webhook.js +37 -0
- package/dist/manager/functions/wrappers/mailchimp/addToList.js +25 -0
- package/dist/manager/helpers/analytics.js +498 -0
- package/dist/manager/helpers/api-manager.js +313 -0
- package/dist/manager/helpers/backend-router.js +37 -0
- package/dist/manager/helpers/context/authenticate.js +215 -0
- package/dist/manager/helpers/context/client-info.js +91 -0
- package/dist/manager/helpers/context/index.js +228 -0
- package/dist/manager/helpers/context/logging.js +150 -0
- package/dist/manager/helpers/context/parse.js +160 -0
- package/dist/manager/helpers/context/respond.js +247 -0
- package/dist/manager/helpers/event-middleware.js +109 -0
- package/dist/manager/helpers/metadata.js +32 -0
- package/dist/manager/helpers/middleware.js +541 -0
- package/dist/manager/helpers/redact-secret.js +25 -0
- package/dist/manager/helpers/resolved-config.js +34 -0
- package/dist/manager/helpers/roles.js +69 -0
- package/dist/manager/helpers/safe-compare.js +31 -0
- package/dist/manager/helpers/schema-engine.js +314 -0
- package/dist/manager/helpers/schema-zod.js +203 -0
- package/dist/manager/helpers/settings.js +215 -0
- package/dist/manager/helpers/usage.js +541 -0
- package/dist/manager/helpers/user.js +57 -0
- package/dist/manager/helpers/utilities.js +565 -0
- package/dist/manager/index.js +1404 -0
- package/dist/manager/libraries/abandoned-cart-config.js +12 -0
- package/dist/manager/libraries/ai/index.js +321 -0
- package/dist/manager/libraries/ai/prompt.js +85 -0
- package/dist/manager/libraries/ai/providers/anthropic-format.js +297 -0
- package/dist/manager/libraries/ai/providers/anthropic.js +169 -0
- package/dist/manager/libraries/ai/providers/claude-code.js +183 -0
- package/dist/manager/libraries/ai/providers/openai.js +1272 -0
- package/dist/manager/libraries/ai/providers/test.js +257 -0
- package/dist/manager/libraries/ai/tokens.js +54 -0
- package/dist/manager/libraries/analytics/conversions.js +478 -0
- package/dist/manager/libraries/analytics/match-data.js +573 -0
- package/dist/manager/libraries/analytics/signup.js +120 -0
- package/dist/manager/libraries/auth-user.js +46 -0
- package/dist/manager/libraries/content/feed-parser.js +198 -0
- package/dist/manager/libraries/content/ghostii.js +191 -0
- package/dist/manager/libraries/content/source-resolver.js +629 -0
- package/dist/manager/libraries/email/constants.js +532 -0
- package/dist/manager/libraries/email/data/blocked-local-parts.json +55 -0
- package/dist/manager/libraries/email/data/blocked-local-patterns.js +14 -0
- package/dist/manager/libraries/email/data/corporate-domains.json +23 -0
- package/dist/manager/libraries/email/data/custom-disposable-domains.json +58 -0
- package/dist/manager/libraries/email/data/disposable-domains.json +8184 -0
- package/dist/manager/libraries/email/data/typo-domains.js +83 -0
- package/dist/manager/libraries/email/disposable-domains.js +92 -0
- package/dist/manager/libraries/email/generators/lib/filter.js +179 -0
- package/dist/manager/libraries/email/generators/lib/image-host.js +344 -0
- package/dist/manager/libraries/email/generators/lib/image-illustrator.js +154 -0
- package/dist/manager/libraries/email/generators/lib/markdown-renderer.js +294 -0
- package/dist/manager/libraries/email/generators/lib/mjml-template.js +120 -0
- package/dist/manager/libraries/email/generators/lib/structure.js +299 -0
- package/dist/manager/libraries/email/generators/lib/svg-illustrator.js +197 -0
- package/dist/manager/libraries/email/generators/lib/templates/base.js +229 -0
- package/dist/manager/libraries/email/generators/lib/templates/card.js +33 -0
- package/dist/manager/libraries/email/generators/lib/templates/classic-schema.js +59 -0
- package/dist/manager/libraries/email/generators/lib/templates/clean.js +82 -0
- package/dist/manager/libraries/email/generators/lib/templates/editorial/helpers.js +100 -0
- package/dist/manager/libraries/email/generators/lib/templates/editorial/index.js +318 -0
- package/dist/manager/libraries/email/generators/lib/templates/feedback.js +101 -0
- package/dist/manager/libraries/email/generators/lib/templates/field-report/helpers.js +138 -0
- package/dist/manager/libraries/email/generators/lib/templates/field-report/index.js +494 -0
- package/dist/manager/libraries/email/generators/lib/templates/index.js +47 -0
- package/dist/manager/libraries/email/generators/lib/templates/newsletter-shared.js +610 -0
- package/dist/manager/libraries/email/generators/lib/templates/order.js +461 -0
- package/dist/manager/libraries/email/generators/lib/templates/plain.js +83 -0
- package/dist/manager/libraries/email/generators/lib/templates/shared-campaign.js +64 -0
- package/dist/manager/libraries/email/generators/newsletter.js +899 -0
- package/dist/manager/libraries/email/index.js +146 -0
- package/dist/manager/libraries/email/marketing/index.js +754 -0
- package/dist/manager/libraries/email/prepare.js +365 -0
- package/dist/manager/libraries/email/providers/beehiiv.js +589 -0
- package/dist/manager/libraries/email/providers/sendgrid.js +845 -0
- package/dist/manager/libraries/email/transactional/index.js +510 -0
- package/dist/manager/libraries/email/utm.js +86 -0
- package/dist/manager/libraries/email/validation-provider-neverbounce.js +70 -0
- package/dist/manager/libraries/email/validation-provider-zerobounce.js +42 -0
- package/dist/manager/libraries/email/validation.js +255 -0
- package/dist/manager/libraries/env.js +198 -0
- package/dist/manager/libraries/infer-contact.js +101 -0
- package/dist/manager/libraries/load-provider.js +65 -0
- package/dist/manager/libraries/notification.js +256 -0
- package/dist/manager/libraries/openai.js +13 -0
- package/dist/manager/libraries/payment/discount-codes.js +204 -0
- package/dist/manager/libraries/payment/fetch-failure.js +56 -0
- package/dist/manager/libraries/payment/license.js +44 -0
- package/dist/manager/libraries/payment/order-id.js +57 -0
- package/dist/manager/libraries/payment/provider-errors.js +71 -0
- package/dist/manager/libraries/payment/providers/chargebee.js +817 -0
- package/dist/manager/libraries/payment/providers/coinbase.js +330 -0
- package/dist/manager/libraries/payment/providers/paypal.js +987 -0
- package/dist/manager/libraries/payment/providers/stripe.js +755 -0
- package/dist/manager/libraries/payment/providers/test.js +250 -0
- package/dist/manager/libraries/payment/refund-linkage.js +78 -0
- package/dist/manager/libraries/payment/refund-policy.js +113 -0
- package/dist/manager/libraries/payment/winback.js +55 -0
- package/dist/manager/libraries/prompts/infer-contact.md +78 -0
- package/dist/manager/libraries/rate-limits.js +17 -0
- package/dist/manager/libraries/recaptcha.js +58 -0
- package/dist/manager/libraries/user-doc.js +234 -0
- package/dist/manager/routes/admin/backup/post.js +106 -0
- package/dist/manager/routes/admin/cron/post.js +37 -0
- package/dist/manager/routes/admin/database/get.js +35 -0
- package/dist/manager/routes/admin/database/post.js +34 -0
- package/dist/manager/routes/admin/email/post.js +74 -0
- package/dist/manager/routes/admin/firestore/get.js +35 -0
- package/dist/manager/routes/admin/firestore/post.js +56 -0
- package/dist/manager/routes/admin/firestore/query/post.js +128 -0
- package/dist/manager/routes/admin/hook/post.js +104 -0
- package/dist/manager/routes/admin/infer-contact/post.js +35 -0
- package/dist/manager/routes/admin/notification/post.js +37 -0
- package/dist/manager/routes/admin/payment/post.js +56 -0
- package/dist/manager/routes/admin/post/deduplicate-image-alts.js +52 -0
- package/dist/manager/routes/admin/post/dispatch-deploy.js +36 -0
- package/dist/manager/routes/admin/post/post.js +487 -0
- package/dist/manager/routes/admin/post/put.js +146 -0
- package/dist/manager/routes/admin/post/templates/post.html +14 -0
- package/dist/manager/routes/admin/repo/content/post.js +106 -0
- package/dist/manager/routes/admin/stats/get.js +222 -0
- package/dist/manager/routes/admin/users/disable/post.js +50 -0
- package/dist/manager/routes/admin/users/list/get.js +110 -0
- package/dist/manager/routes/admin/users/sync/post.js +119 -0
- package/dist/manager/routes/brand/get.js +26 -0
- package/dist/manager/routes/content/post/get.js +110 -0
- package/dist/manager/routes/general/email/post.js +89 -0
- package/dist/manager/routes/general/email/templates/general/download-app-link.js +42 -0
- package/dist/manager/routes/general/uuid/post.js +31 -0
- package/dist/manager/routes/handler/post/post.js +142 -0
- package/dist/manager/routes/health/get.js +43 -0
- package/dist/manager/routes/index.js +11 -0
- package/dist/manager/routes/marketing/campaign/delete.js +45 -0
- package/dist/manager/routes/marketing/campaign/get.js +69 -0
- package/dist/manager/routes/marketing/campaign/post.js +124 -0
- package/dist/manager/routes/marketing/campaign/put.js +86 -0
- package/dist/manager/routes/marketing/campaign/utils.js +59 -0
- package/dist/manager/routes/marketing/contact/delete.js +100 -0
- package/dist/manager/routes/marketing/contact/post.js +149 -0
- package/dist/manager/routes/marketing/contact/put.js +37 -0
- package/dist/manager/routes/marketing/email-preferences/post.js +275 -0
- package/dist/manager/routes/marketing/webhook/forward/post.js +170 -0
- package/dist/manager/routes/marketing/webhook/post.js +123 -0
- package/dist/manager/routes/marketing/webhook/providers/beehiiv.js +197 -0
- package/dist/manager/routes/marketing/webhook/providers/sendgrid.js +194 -0
- package/dist/manager/routes/payments/cancel/_is-trialing.js +40 -0
- package/dist/manager/routes/payments/cancel/post.js +149 -0
- package/dist/manager/routes/payments/cancel/providers/chargebee.js +41 -0
- package/dist/manager/routes/payments/cancel/providers/paypal.js +56 -0
- package/dist/manager/routes/payments/cancel/providers/stripe.js +33 -0
- package/dist/manager/routes/payments/cancel/providers/test.js +118 -0
- package/dist/manager/routes/payments/discount/get.js +25 -0
- package/dist/manager/routes/payments/dispute-alert/post.js +117 -0
- package/dist/manager/routes/payments/dispute-alert/providers/chargeblast.js +55 -0
- package/dist/manager/routes/payments/intent/post.js +341 -0
- package/dist/manager/routes/payments/intent/providers/chargebee.js +197 -0
- package/dist/manager/routes/payments/intent/providers/coinbase.js +94 -0
- package/dist/manager/routes/payments/intent/providers/paypal.js +244 -0
- package/dist/manager/routes/payments/intent/providers/stripe.js +141 -0
- package/dist/manager/routes/payments/intent/providers/test.js +341 -0
- package/dist/manager/routes/payments/plan/post.js +147 -0
- package/dist/manager/routes/payments/plan/providers/chargebee.js +62 -0
- package/dist/manager/routes/payments/plan/providers/paypal.js +62 -0
- package/dist/manager/routes/payments/plan/providers/stripe.js +59 -0
- package/dist/manager/routes/payments/plan/providers/test.js +113 -0
- package/dist/manager/routes/payments/portal/post.js +113 -0
- package/dist/manager/routes/payments/portal/providers/chargebee.js +62 -0
- package/dist/manager/routes/payments/portal/providers/paypal.js +25 -0
- package/dist/manager/routes/payments/portal/providers/stripe.js +62 -0
- package/dist/manager/routes/payments/portal/providers/test.js +18 -0
- package/dist/manager/routes/payments/refund/post.js +207 -0
- package/dist/manager/routes/payments/refund/providers/chargebee.js +147 -0
- package/dist/manager/routes/payments/refund/providers/coinbase.js +40 -0
- package/dist/manager/routes/payments/refund/providers/paypal.js +239 -0
- package/dist/manager/routes/payments/refund/providers/stripe.js +160 -0
- package/dist/manager/routes/payments/refund/providers/test.js +204 -0
- package/dist/manager/routes/payments/trial-eligibility/get.js +29 -0
- package/dist/manager/routes/payments/uncancel/post.js +106 -0
- package/dist/manager/routes/payments/uncancel/providers/chargebee.js +30 -0
- package/dist/manager/routes/payments/uncancel/providers/paypal.js +25 -0
- package/dist/manager/routes/payments/uncancel/providers/stripe.js +27 -0
- package/dist/manager/routes/payments/uncancel/providers/test.js +114 -0
- package/dist/manager/routes/payments/webhook/post.js +157 -0
- package/dist/manager/routes/payments/webhook/providers/chargebee.js +218 -0
- package/dist/manager/routes/payments/webhook/providers/coinbase.js +91 -0
- package/dist/manager/routes/payments/webhook/providers/paypal.js +214 -0
- package/dist/manager/routes/payments/webhook/providers/stripe.js +182 -0
- package/dist/manager/routes/payments/webhook/providers/test.js +18 -0
- package/dist/manager/routes/payments/winback/post.js +276 -0
- package/dist/manager/routes/payments/winback/providers/chargebee.js +24 -0
- package/dist/manager/routes/payments/winback/providers/paypal.js +22 -0
- package/dist/manager/routes/payments/winback/providers/stripe.js +41 -0
- package/dist/manager/routes/payments/winback/providers/test.js +42 -0
- package/dist/manager/routes/restart/index.js +37 -0
- package/dist/manager/routes/special/electron-client/post.js +59 -0
- package/dist/manager/routes/test/authenticate/get.js +11 -0
- package/dist/manager/routes/test/health/get.js +8 -0
- package/dist/manager/routes/test/index.js +16 -0
- package/dist/manager/routes/test/lab/post.js +17 -0
- package/dist/manager/routes/test/redirect/get.js +27 -0
- package/dist/manager/routes/test/reset-account/post.js +79 -0
- package/dist/manager/routes/test/roster/get.js +40 -0
- package/dist/manager/routes/test/schema/post.js +20 -0
- package/dist/manager/routes/test/usage/post.js +44 -0
- package/dist/manager/routes/test/webhook/post.js +21 -0
- package/dist/manager/routes/user/api-keys/post.js +51 -0
- package/dist/manager/routes/user/connections/_context.js +194 -0
- package/dist/manager/routes/user/connections/_grant.js +247 -0
- package/dist/manager/routes/user/connections/_lease.js +184 -0
- package/dist/manager/routes/user/connections/_providers.js +191 -0
- package/dist/manager/routes/user/connections/_state.js +117 -0
- package/dist/manager/routes/user/connections/delete.js +55 -0
- package/dist/manager/routes/user/connections/get.js +167 -0
- package/dist/manager/routes/user/connections/post.js +370 -0
- package/dist/manager/routes/user/connections/providers/discord.js +41 -0
- package/dist/manager/routes/user/connections/providers/google.js +55 -0
- package/dist/manager/routes/user/connections/providers/kick.js +68 -0
- package/dist/manager/routes/user/connections/providers/spotify.js +40 -0
- package/dist/manager/routes/user/connections/providers/twitch.js +52 -0
- package/dist/manager/routes/user/data-request/delete.js +84 -0
- package/dist/manager/routes/user/data-request/get.js +229 -0
- package/dist/manager/routes/user/data-request/post.js +136 -0
- package/dist/manager/routes/user/delete.js +149 -0
- package/dist/manager/routes/user/feedback/post.js +81 -0
- package/dist/manager/routes/user/get.js +14 -0
- package/dist/manager/routes/user/orders/get.js +95 -0
- package/dist/manager/routes/user/sessions/delete.js +95 -0
- package/dist/manager/routes/user/sessions/get.js +40 -0
- package/dist/manager/routes/user/settings/validate/post.js +86 -0
- package/dist/manager/routes/user/signup/post.js +662 -0
- package/dist/manager/routes/user/subscription/get.js +77 -0
- package/dist/manager/routes/user/token/post.js +30 -0
- package/dist/manager/routes/verts/delete.js +43 -0
- package/dist/manager/routes/verts/get.js +49 -0
- package/dist/manager/routes/verts/post.js +61 -0
- package/dist/manager/routes/verts/put.js +75 -0
- package/dist/manager/routes/verts/redirect/get.js +36 -0
- package/dist/manager/routes/verts/serve/get.js +67 -0
- package/dist/manager/routes/verts/utils.js +436 -0
- package/dist/manager/schemas/admin/backup/post.js +8 -0
- package/dist/manager/schemas/admin/cron/post.js +8 -0
- package/dist/manager/schemas/admin/database/get.js +5 -0
- package/dist/manager/schemas/admin/database/post.js +6 -0
- package/dist/manager/schemas/admin/email/post.js +31 -0
- package/dist/manager/schemas/admin/firestore/get.js +5 -0
- package/dist/manager/schemas/admin/firestore/post.js +8 -0
- package/dist/manager/schemas/admin/firestore/query/post.js +5 -0
- package/dist/manager/schemas/admin/hook/post.js +8 -0
- package/dist/manager/schemas/admin/infer-contact/post.js +6 -0
- package/dist/manager/schemas/admin/notification/post.js +23 -0
- package/dist/manager/schemas/admin/payment/post.js +8 -0
- package/dist/manager/schemas/admin/post/post.js +23 -0
- package/dist/manager/schemas/admin/post/put.js +13 -0
- package/dist/manager/schemas/admin/repo/content/post.js +10 -0
- package/dist/manager/schemas/admin/stats/get.js +5 -0
- package/dist/manager/schemas/admin/users/disable/post.js +9 -0
- package/dist/manager/schemas/admin/users/list/get.js +10 -0
- package/dist/manager/schemas/admin/users/sync/post.js +7 -0
- package/dist/manager/schemas/brand/get.js +3 -0
- package/dist/manager/schemas/content/post/get.js +8 -0
- package/dist/manager/schemas/general/email/post.js +10 -0
- package/dist/manager/schemas/general/uuid/post.js +9 -0
- package/dist/manager/schemas/handler/post/post.js +13 -0
- package/dist/manager/schemas/health/get.js +3 -0
- package/dist/manager/schemas/marketing/campaign/delete.js +8 -0
- package/dist/manager/schemas/marketing/campaign/get.js +13 -0
- package/dist/manager/schemas/marketing/campaign/post.js +43 -0
- package/dist/manager/schemas/marketing/campaign/put.js +37 -0
- package/dist/manager/schemas/marketing/contact/delete.js +8 -0
- package/dist/manager/schemas/marketing/contact/post.js +14 -0
- package/dist/manager/schemas/marketing/contact/put.js +8 -0
- package/dist/manager/schemas/marketing/email-preferences/post.js +15 -0
- package/dist/manager/schemas/marketing/webhook/forward/post.js +8 -0
- package/dist/manager/schemas/marketing/webhook/post.js +7 -0
- package/dist/manager/schemas/payments/cancel/post.js +15 -0
- package/dist/manager/schemas/payments/discount/get.js +8 -0
- package/dist/manager/schemas/payments/dispute-alert/post.js +6 -0
- package/dist/manager/schemas/payments/intent/post.js +28 -0
- package/dist/manager/schemas/payments/plan/post.js +11 -0
- package/dist/manager/schemas/payments/portal/post.js +12 -0
- package/dist/manager/schemas/payments/refund/post.js +14 -0
- package/dist/manager/schemas/payments/trial-eligibility/get.js +7 -0
- package/dist/manager/schemas/payments/uncancel/post.js +9 -0
- package/dist/manager/schemas/payments/webhook/post.js +6 -0
- package/dist/manager/schemas/payments/winback/post.js +12 -0
- package/dist/manager/schemas/restart/index.js +5 -0
- package/dist/manager/schemas/special/electron-client/post.js +11 -0
- package/dist/manager/schemas/test/authenticate/get.js +3 -0
- package/dist/manager/schemas/test/health/get.js +3 -0
- package/dist/manager/schemas/test/index.js +5 -0
- package/dist/manager/schemas/test/lab/post.js +3 -0
- package/dist/manager/schemas/test/redirect/get.js +7 -0
- package/dist/manager/schemas/test/reset-account/post.js +3 -0
- package/dist/manager/schemas/test/roster/get.js +3 -0
- package/dist/manager/schemas/test/schema/post.js +70 -0
- package/dist/manager/schemas/test/usage/post.js +8 -0
- package/dist/manager/schemas/test/webhook/post.js +7 -0
- package/dist/manager/schemas/user/api-keys/post.js +6 -0
- package/dist/manager/schemas/user/connections/delete.js +8 -0
- package/dist/manager/schemas/user/connections/get.js +21 -0
- package/dist/manager/schemas/user/connections/post.js +12 -0
- package/dist/manager/schemas/user/data-request/delete.js +3 -0
- package/dist/manager/schemas/user/data-request/get.js +5 -0
- package/dist/manager/schemas/user/data-request/post.js +6 -0
- package/dist/manager/schemas/user/delete.js +6 -0
- package/dist/manager/schemas/user/feedback/post.js +8 -0
- package/dist/manager/schemas/user/get.js +3 -0
- package/dist/manager/schemas/user/orders/get.js +6 -0
- package/dist/manager/schemas/user/sessions/delete.js +6 -0
- package/dist/manager/schemas/user/sessions/get.js +6 -0
- package/dist/manager/schemas/user/settings/validate/post.js +8 -0
- package/dist/manager/schemas/user/signup/post.js +27 -0
- package/dist/manager/schemas/user/subscription/get.js +5 -0
- package/dist/manager/schemas/user/token/post.js +5 -0
- package/dist/manager/schemas/verts/delete.js +8 -0
- package/dist/manager/schemas/verts/get.js +9 -0
- package/dist/manager/schemas/verts/post.js +23 -0
- package/dist/manager/schemas/verts/put.js +20 -0
- package/dist/manager/schemas/verts/redirect/get.js +10 -0
- package/dist/manager/schemas/verts/serve/get.js +14 -0
- package/dist/manager/server-manager.js +69 -0
- package/dist/mcp/client.js +98 -0
- package/dist/mcp/handler.js +521 -0
- package/dist/mcp/index.js +157 -0
- package/dist/mcp/tools.js +555 -0
- package/dist/mcp/utils.js +108 -0
- package/dist/omega-bin.js +6 -0
- package/dist/require.js +3 -0
- package/dist/test/fixtures/firebase-project/.firebaserc +5 -0
- package/dist/test/fixtures/firebase-project/config/omega.json5 +131 -0
- package/dist/test/fixtures/firebase-project/database.rules.json +6 -0
- package/dist/test/fixtures/firebase-project/firebase.json +49 -0
- package/dist/test/fixtures/firebase-project/firestore.indexes.json +4 -0
- package/dist/test/fixtures/firebase-project/package.json +14 -0
- package/dist/test/fixtures/firebase-project/src/index.js +11 -0
- package/dist/test/fixtures/firebase-project/storage.rules +8 -0
- package/dist/test/parse-audit.js +25 -0
- package/dist/test/run-tests.js +90 -0
- package/dist/test/runner.js +982 -0
- package/dist/test/seed.js +242 -0
- package/dist/test/test-accounts.js +2660 -0
- package/dist/test/utils/assertions.js +189 -0
- package/dist/test/utils/email-capture.js +252 -0
- package/dist/test/utils/extended-mode-warning.js +11 -0
- package/dist/test/utils/firestore-rules-client.js +277 -0
- package/dist/test/utils/http-client.js +228 -0
- package/dist/test/utils/test-mode-file.js +192 -0
- package/dist/utils/merge-line-files.js +6 -0
- package/dist/utils/scaffold-defaults.js +71 -0
- package/dist/utils/test-lanes.js +62 -0
- package/dist/vendor/account/engine.js +182 -0
- package/dist/vendor/account/features.js +220 -0
- package/dist/vendor/account/index.js +53 -0
- package/dist/vendor/account/schema.js +272 -0
- package/dist/vendor/account/subscription.js +38 -0
- package/dist/vendor/analytics/adapters/ga4.js +26 -0
- package/dist/vendor/analytics/adapters/meta.js +26 -0
- package/dist/vendor/analytics/adapters/resolve.js +130 -0
- package/dist/vendor/analytics/adapters/tiktok.js +27 -0
- package/dist/vendor/analytics/catalog.js +908 -0
- package/dist/vendor/analytics/identity.js +136 -0
- package/dist/vendor/config/company.js +31 -0
- package/dist/vendor/config/defaults.js +173 -0
- package/dist/vendor/config/demo.js +18 -0
- package/dist/vendor/config/desktop-artifacts.js +110 -0
- package/dist/vendor/config/edit.js +769 -0
- package/dist/vendor/config/env-delivery.js +145 -0
- package/dist/vendor/config/env-rules.js +93 -0
- package/dist/vendor/config/env-schema.js +1078 -0
- package/dist/vendor/config/env.js +445 -0
- package/dist/vendor/config/hooks.js +97 -0
- package/dist/vendor/config/index.js +237 -0
- package/dist/vendor/config/instances.js +208 -0
- package/dist/vendor/config/load.js +490 -0
- package/dist/vendor/config/merge.js +68 -0
- package/dist/vendor/config/order.js +139 -0
- package/dist/vendor/config/ports.js +374 -0
- package/dist/vendor/config/providers.js +32 -0
- package/dist/vendor/config/repo.js +142 -0
- package/dist/vendor/config/retired-keys.js +430 -0
- package/dist/vendor/config/schema.js +1610 -0
- package/dist/vendor/config/secrets.js +50 -0
- package/dist/vendor/config/seed.js +34 -0
- package/dist/vendor/config/site-global.js +205 -0
- package/dist/vendor/config/validate.js +554 -0
- package/dist/vendor/config/winback.js +61 -0
- package/dist/vendor/devkit/attach-log-file.js +262 -0
- package/dist/vendor/devkit/bare-requires.js +153 -0
- package/dist/vendor/devkit/command-path.js +46 -0
- package/dist/vendor/devkit/defaults-engine.js +372 -0
- package/dist/vendor/devkit/deploy-record.js +180 -0
- package/dist/vendor/devkit/deploy.js +286 -0
- package/dist/vendor/devkit/license.js +155 -0
- package/dist/vendor/devkit/local-https.js +360 -0
- package/dist/vendor/devkit/local.js +1905 -0
- package/dist/vendor/devkit/logger.js +128 -0
- package/dist/vendor/devkit/merge-line-files.js +296 -0
- package/dist/vendor/devkit/npm-registry.js +52 -0
- package/dist/vendor/devkit/omega-bin.js +345 -0
- package/dist/vendor/devkit/parse-audit.js +74 -0
- package/dist/vendor/devkit/safe-install.js +18 -0
- package/dist/vendor/devkit/scaffold-guard.js +96 -0
- package/dist/vendor/devkit/stop-signals.js +28 -0
- package/dist/vendor/devkit/test/assert.js +120 -0
- package/dist/vendor/devkit/test/define-cases.js +104 -0
- package/dist/vendor/devkit/test/runner-core.js +554 -0
- package/dist/vendor/devkit/test/scope.js +162 -0
- package/dist/vendor/devkit/update.js +569 -0
- package/dist/vendor/monitoring/core.js +180 -0
- package/dist/vendor/monitoring/env.js +49 -0
- package/dist/vendor/monitoring/logger.js +39 -0
- package/dist/vendor/monitoring/node.js +72 -0
- package/docs/admin-post-route.md +57 -0
- package/docs/ai-library.md +171 -0
- package/docs/architecture.md +62 -0
- package/docs/audit.md +70 -0
- package/docs/auth-hooks.md +74 -0
- package/docs/build-system.md +65 -0
- package/docs/cdp-debugging.md +29 -0
- package/docs/cli-firestore-auth.md +85 -0
- package/docs/cli-logs.md +67 -0
- package/docs/cli-output.md +146 -0
- package/docs/code-patterns.md +77 -0
- package/docs/common-mistakes.md +12 -0
- package/docs/common-operations.md +60 -0
- package/docs/connections.md +212 -0
- package/docs/consent.md +362 -0
- package/docs/directory-structure.md +150 -0
- package/docs/email-system.md +459 -0
- package/docs/environment-detection.md +93 -0
- package/docs/file-naming.md +10 -0
- package/docs/firestore.md +134 -0
- package/docs/ghostii.md +240 -0
- package/docs/index.md +299 -0
- package/docs/key-files.md +37 -0
- package/docs/logging.md +60 -0
- package/docs/marketing-campaigns.md +407 -0
- package/docs/marketing-fields.md +25 -0
- package/docs/mcp.md +222 -0
- package/docs/migration.md +129 -0
- package/docs/payment-system.md +856 -0
- package/docs/paypal-sandbox-qa.md +105 -0
- package/docs/response-headers.md +9 -0
- package/docs/routes.md +211 -0
- package/docs/sanitization.md +71 -0
- package/docs/schemas.md +163 -0
- package/docs/shared/agent-docs.md +89 -0
- package/docs/shared/analytics.md +612 -0
- package/docs/shared/brands.md +51 -0
- package/docs/shared/breaking-changes.md +497 -0
- package/docs/shared/config.md +1387 -0
- package/docs/shared/deploys.md +215 -0
- package/docs/shared/icons.md +201 -0
- package/docs/shared/local-dev.md +147 -0
- package/docs/shared/logging.md +202 -0
- package/docs/shared/monitoring.md +153 -0
- package/docs/shared/publishing.md +183 -0
- package/docs/shared/rulings.md +34 -0
- package/docs/shared/testing.md +147 -0
- package/docs/shared/theming.md +604 -0
- package/docs/shared/translation.md +291 -0
- package/docs/shared/updates.md +61 -0
- package/docs/stripe-webhook-forwarding.md +20 -0
- package/docs/test-boot-layer.md +67 -0
- package/docs/test-framework.md +583 -0
- package/docs/usage-rate-limiting.md +121 -0
- package/docs/verts.md +29 -0
- package/package.json +143 -0
- package/templates/config/omega.json5 +322 -0
- package/templates/database.rules.json +82 -0
- package/templates/firebase.json +67 -0
- package/templates/firestore.framework.rules +191 -0
- package/templates/firestore.indexes.json +4 -0
- package/templates/firestore.rules +59 -0
- package/templates/index.js +12 -0
- package/templates/public/404.html +26 -0
- package/templates/public/index.html +24 -0
- package/templates/remoteconfig.template.json +1 -0
- package/templates/storage-lifecycle-config-1-day.json +9 -0
- package/templates/storage-lifecycle-config-30-days.json +9 -0
- package/templates/storage.rules +11 -0
- package/test/_init/accounts-validation.js +58 -0
- package/test/ai/tools-live.test.js +171 -0
- package/test/analytics/conversion-delivery.test.js +1013 -0
- package/test/analytics/match-normalization.test.js +238 -0
- package/test/analytics/signup-conversion.test.js +135 -0
- package/test/boot/cli-dispatch.test.js +142 -0
- package/test/boot/defaults-scaffold.test.js +248 -0
- package/test/boot/deploy-staging.test.js +446 -0
- package/test/boot/disposable-domains.test.js +154 -0
- package/test/boot/emulator-boots.test.js +39 -0
- package/test/boot/emulator-port-preflight.test.js +126 -0
- package/test/boot/emulator-ready-timeout.test.js +46 -0
- package/test/boot/project-id-refresh.test.js +73 -0
- package/test/boot/suite-portability.test.js +278 -0
- package/test/boot/update-command.test.js +63 -0
- package/test/cli/cross-platform.test.js +168 -0
- package/test/cli/custom-project-type.test.js +426 -0
- package/test/cli/deploy-license-env.test.js +100 -0
- package/test/cli/emulator-adoption.test.js +254 -0
- package/test/cli/emulator-orphans.test.js +658 -0
- package/test/cli/emulator-ownership.test.js +177 -0
- package/test/cli/emulator-port-retry.test.js +395 -0
- package/test/cli/emulator-shutdown.test.js +788 -0
- package/test/cli/emulator-stale-reap.test.js +574 -0
- package/test/cli/ensure-target.test.js +325 -0
- package/test/cli/flags.test.js +64 -0
- package/test/cli/https-trust.test.js +38 -0
- package/test/cli/install-update-dispatch.test.js +47 -0
- package/test/cli/lane-environments.test.js +159 -0
- package/test/cli/marketing-campaigns-seeded.test.js +194 -0
- package/test/cli/migrate-bare-requires.test.js +128 -0
- package/test/cli/migrate-markers.test.js +530 -0
- package/test/cli/required-indexes.test.js +64 -0
- package/test/cli/rules-compile.test.js +622 -0
- package/test/cli/rules-migration-deferral.test.js +259 -0
- package/test/cli/rules-version.test.js +104 -0
- package/test/cli/setup-load-files.test.js +66 -0
- package/test/cli/setup-offline-mode.test.js +276 -0
- package/test/cli/setup-retired.test.js +64 -0
- package/test/cli/setup-shared-project-indexes.test.js +262 -0
- package/test/cli/setup-tests-requires.test.js +45 -0
- package/test/cli/stage-env-compose.test.js +316 -0
- package/test/cli/stripe-live-lane.test.js +303 -0
- package/test/cli/target.test.js +182 -0
- package/test/cli/templates.test.js +66 -0
- package/test/cli/test-runner-env.test.js +115 -0
- package/test/cli/test-stack-shutdown.test.js +275 -0
- package/test/cli/test-target-no-match.test.js +128 -0
- package/test/cli/verb-logs.test.js +118 -0
- package/test/cli/version-dispatch.test.js +39 -0
- package/test/content/blog-generate.test.js +164 -0
- package/test/email/campaign-config-fault.test.js +376 -0
- package/test/email/campaign-cron-pipeline.test.js +536 -0
- package/test/email/campaign-send.test.js +47 -0
- package/test/email/consent-lifecycle.test.js +258 -0
- package/test/email/content-html-policy.test.js +270 -0
- package/test/email/feedback-and-plain-send.test.js +54 -0
- package/test/email/fixtures/clean.json +30 -0
- package/test/email/fixtures/editorial.json +30 -0
- package/test/email/fixtures/field-report.json +53 -0
- package/test/email/identity.test.js +321 -0
- package/test/email/marketing/consent-gate.test.js +265 -0
- package/test/email/marketing/custom-fields-catalog.test.js +259 -0
- package/test/email/marketing/prune-per-provider.test.js +762 -0
- package/test/email/marketing/remove-log-privacy.test.js +225 -0
- package/test/email/marketing-lifecycle.test.js +139 -0
- package/test/email/newsletter-generate.test.js +853 -0
- package/test/email/newsletter-svg-tokens.test.js +51 -0
- package/test/email/newsletter-templates.test.js +492 -0
- package/test/email/order-one-time-cta.test.js +97 -0
- package/test/email/render-content.test.js +248 -0
- package/test/email/safe-url.test.js +0 -0
- package/test/email/sanitize-images.test.js +66 -0
- package/test/email/send-log-privacy.test.js +107 -0
- package/test/email/templates.test.js +288 -0
- package/test/email/testing-capture.test.js +260 -0
- package/test/email/transactional-send.test.js +36 -0
- package/test/email/transactional.test.js +562 -0
- package/test/email/unsubscribe-groups.test.js +155 -0
- package/test/email/unsubscribe-key.test.js +91 -0
- package/test/email/validation-cases.test.js +152 -0
- package/test/email/validation.test.js +746 -0
- package/test/events/auth-delete-conversion.test.js +138 -0
- package/test/events/auth-delete-race.test.js +211 -0
- package/test/events/auth-on-create-log.test.js +164 -0
- package/test/events/auth-signup-conversion.test.js +141 -0
- package/test/events/auth-signup-limit.test.js +142 -0
- package/test/events/auth-trigger-log-privacy.test.js +256 -0
- package/test/events/cron-job-doc-shape.test.js +101 -0
- package/test/events/cron-reset-usage.test.js +116 -0
- package/test/events/notification-conversion.test.js +182 -0
- package/test/events/payments/_webhook-harness.js +307 -0
- package/test/events/payments/abandoned-cart-activity.test.js +135 -0
- package/test/events/payments/analytics-payment-events.test.js +945 -0
- package/test/events/payments/dispute-email-status.test.js +55 -0
- package/test/events/payments/journey-payments-abandoned.test.js +126 -0
- package/test/events/payments/journey-payments-cancel-endpoint.test.js +102 -0
- package/test/events/payments/journey-payments-cancel-no-order.test.js +76 -0
- package/test/events/payments/journey-payments-cancel.test.js +184 -0
- package/test/events/payments/journey-payments-decline.test.js +225 -0
- package/test/events/payments/journey-payments-discount.test.js +91 -0
- package/test/events/payments/journey-payments-dispute.test.js +190 -0
- package/test/events/payments/journey-payments-failure.test.js +148 -0
- package/test/events/payments/journey-payments-legacy-product.test.js +151 -0
- package/test/events/payments/journey-payments-one-time-decline.test.js +127 -0
- package/test/events/payments/journey-payments-one-time-failure.test.js +113 -0
- package/test/events/payments/journey-payments-one-time-refund.test.js +186 -0
- package/test/events/payments/journey-payments-one-time.test.js +175 -0
- package/test/events/payments/journey-payments-plan-change.test.js +146 -0
- package/test/events/payments/journey-payments-plan-switch-trial.test.js +161 -0
- package/test/events/payments/journey-payments-plan-switch.test.js +182 -0
- package/test/events/payments/journey-payments-refund-no-order.test.js +76 -0
- package/test/events/payments/journey-payments-refund-webhook.test.js +225 -0
- package/test/events/payments/journey-payments-suspend.test.js +183 -0
- package/test/events/payments/journey-payments-trial-cancel.test.js +132 -0
- package/test/events/payments/journey-payments-trial.test.js +173 -0
- package/test/events/payments/journey-payments-uid-resolution.test.js +134 -0
- package/test/events/payments/journey-payments-uncancel.test.js +157 -0
- package/test/events/payments/journey-payments-upgrade.test.js +137 -0
- package/test/events/payments/journey-payments-winback-decline.test.js +171 -0
- package/test/events/payments/journey-payments-winback.test.js +195 -0
- package/test/events/payments/paypal-expiry-cron.test.js +166 -0
- package/test/events/payments/purchase-failed-handler.test.js +80 -0
- package/test/events/payments/purchase-refunded-handler.test.js +108 -0
- package/test/events/payments/test-processor-doc-shape.test.js +162 -0
- package/test/events/payments/transition-order-emails.test.js +185 -0
- package/test/events/payments/transition-promo-lines.test.js +192 -0
- package/test/events/payments/transitions-detect.test.js +767 -0
- package/test/events/payments/trial-lapse-sweep-staleness.test.js +173 -0
- package/test/events/payments/trial-lapse-sweep.test.js +359 -0
- package/test/events/payments/webhook-atomic-writes.test.js +69 -0
- package/test/events/payments/webhook-chargebee-unreachable.test.js +106 -0
- package/test/events/payments/webhook-discount-clear.test.js +189 -0
- package/test/events/payments/webhook-failure-intent.test.js +93 -0
- package/test/events/payments/webhook-hosted-page-uid-trust.test.js +212 -0
- package/test/events/payments/webhook-ordering.test.js +326 -0
- package/test/events/payments/webhook-provider-lookup-trust.test.js +206 -0
- package/test/events/payments/webhook-refund-amount-trust.test.js +223 -0
- package/test/events/payments/webhook-refund-envelope-shape.test.js +148 -0
- package/test/events/payments/webhook-refund-linkage-trust.test.js +404 -0
- package/test/events/payments/webhook-refund-transaction-fallback.test.js +184 -0
- package/test/events/payments/webhook-refund-without-order.test.js +170 -0
- package/test/events/payments/webhook-refusal-reporting.test.js +194 -0
- package/test/events/payments/webhook-refused-intent.test.js +142 -0
- package/test/events/payments/webhook-retry-sweep.test.js +252 -0
- package/test/events/payments/webhook-transition-claim.test.js +389 -0
- package/test/events/payments/webhook-uid-trust.test.js +203 -0
- package/test/events/payments/webhook-user-without-auth.test.js +161 -0
- package/test/fixtures/chargebee/invoice-one-time.json +27 -0
- package/test/fixtures/chargebee/subscription-active.json +44 -0
- package/test/fixtures/chargebee/subscription-cancelled.json +42 -0
- package/test/fixtures/chargebee/subscription-in-trial.json +41 -0
- package/test/fixtures/chargebee/subscription-legacy-plan.json +41 -0
- package/test/fixtures/chargebee/subscription-non-renewing.json +41 -0
- package/test/fixtures/chargebee/subscription-paused.json +42 -0
- package/test/fixtures/chargebee/webhook-payment-failed.json +51 -0
- package/test/fixtures/chargebee/webhook-subscription-created.json +47 -0
- package/test/fixtures/coinbase/charge-confirmed.json +50 -0
- package/test/fixtures/coinbase/charge-failed.json +34 -0
- package/test/fixtures/coinbase/charge-pending.json +34 -0
- package/test/fixtures/migrate/ported-route.js.txt +11 -0
- package/test/fixtures/paypal/capture-completed.json +51 -0
- package/test/fixtures/paypal/capture-refunded.json +43 -0
- package/test/fixtures/paypal/order-approved.json +62 -0
- package/test/fixtures/paypal/order-completed.json +110 -0
- package/test/fixtures/paypal/sale-refunded.json +38 -0
- package/test/fixtures/paypal/subscription-active.json +76 -0
- package/test/fixtures/paypal/subscription-cancelled.json +50 -0
- package/test/fixtures/paypal/subscription-suspended.json +65 -0
- package/test/fixtures/stripe/checkout-session-completed.json +130 -0
- package/test/fixtures/stripe/invoice-payment-failed.json +148 -0
- package/test/fixtures/stripe/invoice-subscription-payment-failed.json +28 -0
- package/test/fixtures/stripe/invoice-subscription-payment-succeeded.json +28 -0
- package/test/fixtures/stripe/subscription-active.json +161 -0
- package/test/fixtures/stripe/subscription-canceled.json +161 -0
- package/test/fixtures/stripe/subscription-trialing.json +161 -0
- package/test/helpers/_shared-config.js +15 -0
- package/test/helpers/ai-request-payload.test.js +619 -0
- package/test/helpers/ai-schema-resolve.test.js +125 -0
- package/test/helpers/ai-test-provider.test.js +227 -0
- package/test/helpers/ai-token-accounting.test.js +150 -0
- package/test/helpers/ai-tools-format.test.js +384 -0
- package/test/helpers/analytics-no-id-notice.test.js +146 -0
- package/test/helpers/analytics-user-data.test.js +194 -0
- package/test/helpers/api-manager.test.js +311 -0
- package/test/helpers/backend-router.test.js +136 -0
- package/test/helpers/content/blog-auto-publisher.test.js +482 -0
- package/test/helpers/content/feed-parser.test.js +529 -0
- package/test/helpers/content/ghostii-blocks.test.js +135 -0
- package/test/helpers/content/ghostii-feed-integration.test.js +405 -0
- package/test/helpers/content/ghostii-write-article.test.js +244 -0
- package/test/helpers/dev-only-routes.test.js +225 -0
- package/test/helpers/env-reader.test.js +362 -0
- package/test/helpers/environment.test.js +263 -0
- package/test/helpers/event-middleware.test.js +407 -0
- package/test/helpers/infer-contact.test.js +157 -0
- package/test/helpers/lane-url.test.js +90 -0
- package/test/helpers/merge-line-files.test.js +272 -0
- package/test/helpers/metadata.test.js +122 -0
- package/test/helpers/middleware-request-log.test.js +134 -0
- package/test/helpers/middleware-user-log.test.js +124 -0
- package/test/helpers/payment/chargebee/parse-webhook.test.js +490 -0
- package/test/helpers/payment/chargebee/refund-details.test.js +198 -0
- package/test/helpers/payment/chargebee/to-unified-one-time.test.js +148 -0
- package/test/helpers/payment/chargebee/to-unified-subscription.test.js +649 -0
- package/test/helpers/payment/coinbase/create-intent.test.js +247 -0
- package/test/helpers/payment/coinbase/parse-webhook.test.js +195 -0
- package/test/helpers/payment/coinbase/refund-unsupported.test.js +141 -0
- package/test/helpers/payment/coinbase/to-unified-one-time.test.js +201 -0
- package/test/helpers/payment/discount-codes.test.js +141 -0
- package/test/helpers/payment/extract-resource.test.js +113 -0
- package/test/helpers/payment/fetch-failure.test.js +215 -0
- package/test/helpers/payment/license-gate.test.js +108 -0
- package/test/helpers/payment/order-id.test.js +99 -0
- package/test/helpers/payment/paypal/create-intent.test.js +382 -0
- package/test/helpers/payment/paypal/fetch-capture.test.js +123 -0
- package/test/helpers/payment/paypal/fetch-sale.test.js +183 -0
- package/test/helpers/payment/paypal/parse-webhook.test.js +678 -0
- package/test/helpers/payment/paypal/refund-details.test.js +202 -0
- package/test/helpers/payment/paypal/resolve-plan-id.test.js +108 -0
- package/test/helpers/payment/paypal/switch-plan.test.js +92 -0
- package/test/helpers/payment/paypal/to-unified-one-time.test.js +383 -0
- package/test/helpers/payment/paypal/to-unified-subscription.test.js +884 -0
- package/test/helpers/payment/stripe/fetch-charge.test.js +147 -0
- package/test/helpers/payment/stripe/parse-webhook.test.js +448 -0
- package/test/helpers/payment/stripe/refund-details.test.js +147 -0
- package/test/helpers/payment/stripe/to-unified-one-time.test.js +307 -0
- package/test/helpers/payment/stripe/to-unified-subscription.test.js +709 -0
- package/test/helpers/persona-domain.test.js +87 -0
- package/test/helpers/recaptcha.test.js +166 -0
- package/test/helpers/resolved-config.test.js +106 -0
- package/test/helpers/response-log-redaction.test.js +262 -0
- package/test/helpers/roles.test.js +155 -0
- package/test/helpers/route-context-debug-gate.test.js +118 -0
- package/test/helpers/route-context-logging.test.js +214 -0
- package/test/helpers/route-context.test.js +129 -0
- package/test/helpers/safe-compare.test.js +114 -0
- package/test/helpers/sanitize.test.js +223 -0
- package/test/helpers/schema-engine.test.js +537 -0
- package/test/helpers/schema-zod.test.js +554 -0
- package/test/helpers/seed-accounts-load.test.js +163 -0
- package/test/helpers/seed-google-personas.test.js +114 -0
- package/test/helpers/seeded-personas.test.js +823 -0
- package/test/helpers/settings.test.js +292 -0
- package/test/helpers/setup-engines-pin.test.js +68 -0
- package/test/helpers/setup-manifest-sync.test.js +116 -0
- package/test/helpers/slugify.test.js +395 -0
- package/test/helpers/storage.test.js +195 -0
- package/test/helpers/test-banner-latch.test.js +139 -0
- package/test/helpers/usage-consume.test.js +460 -0
- package/test/helpers/usage-log-privacy.test.js +121 -0
- package/test/helpers/user-doc-heal.test.js +540 -0
- package/test/helpers/user.test.js +771 -0
- package/test/helpers/webhook-forward.test.js +419 -0
- package/test/helpers/wipe-auth-project.test.js +143 -0
- package/test/mcp/discovery.test.js +52 -0
- package/test/mcp/oauth.test.js +160 -0
- package/test/mcp/protocol.test.js +278 -0
- package/test/mcp/roles.test.js +203 -0
- package/test/mcp/utils.test.js +245 -0
- package/test/notification/identity.test.js +155 -0
- package/test/routes/admin/create-post.test.js +362 -0
- package/test/routes/admin/database.test.js +133 -0
- package/test/routes/admin/deduplicate-image-alts.test.js +191 -0
- package/test/routes/admin/email-content-html.test.js +58 -0
- package/test/routes/admin/email-request-log.test.js +86 -0
- package/test/routes/admin/email.test.js +116 -0
- package/test/routes/admin/firestore-query.test.js +206 -0
- package/test/routes/admin/firestore.test.js +129 -0
- package/test/routes/admin/infer-contact.test.js +220 -0
- package/test/routes/admin/notification.test.js +199 -0
- package/test/routes/admin/post-convert-image.test.js +159 -0
- package/test/routes/admin/post-deploy-flag.test.js +81 -0
- package/test/routes/admin/post-download-error.test.js +90 -0
- package/test/routes/admin/post-resize-image.test.js +185 -0
- package/test/routes/admin/post.test.js +369 -0
- package/test/routes/admin/repo-content.test.js +223 -0
- package/test/routes/admin/stats.test.js +114 -0
- package/test/routes/admin/users-disable.test.js +63 -0
- package/test/routes/admin/users-list.test.js +71 -0
- package/test/routes/content/post.test.js +60 -0
- package/test/routes/general/uuid.test.js +133 -0
- package/test/routes/health.test.js +110 -0
- package/test/routes/marketing/campaign.test.js +184 -0
- package/test/routes/marketing/contact.test.js +416 -0
- package/test/routes/marketing/email-preferences.test.js +293 -0
- package/test/routes/marketing/push-send.test.js +34 -0
- package/test/routes/marketing/webhook-forward.test.js +63 -0
- package/test/routes/marketing/webhook.test.js +641 -0
- package/test/routes/payments/_route-harness.js +145 -0
- package/test/routes/payments/cancel-provider-errors.test.js +227 -0
- package/test/routes/payments/cancel-skip-guards.test.js +126 -0
- package/test/routes/payments/cancel-trialing.test.js +428 -0
- package/test/routes/payments/cancel.test.js +165 -0
- package/test/routes/payments/dedup-race.test.js +186 -0
- package/test/routes/payments/discount.test.js +82 -0
- package/test/routes/payments/dispute-alert.test.js +324 -0
- package/test/routes/payments/intent-discount-amounts.test.js +550 -0
- package/test/routes/payments/intent-discount-coupons.test.js +293 -0
- package/test/routes/payments/intent-one-time-metadata.test.js +122 -0
- package/test/routes/payments/intent-purchaser-guard.test.js +139 -0
- package/test/routes/payments/intent-zero-total.test.js +260 -0
- package/test/routes/payments/intent.test.js +413 -0
- package/test/routes/payments/plan.test.js +367 -0
- package/test/routes/payments/portal-return-url.test.js +107 -0
- package/test/routes/payments/portal.test.js +94 -0
- package/test/routes/payments/refund-one-time.test.js +254 -0
- package/test/routes/payments/refund-paypal-proration.test.js +190 -0
- package/test/routes/payments/refund.test.js +181 -0
- package/test/routes/payments/trial-eligibility.test.js +73 -0
- package/test/routes/payments/uncancel.test.js +270 -0
- package/test/routes/payments/webhook-stripe-invoice.test.js +129 -0
- package/test/routes/payments/webhook-stripe-refund-one-time.test.js +130 -0
- package/test/routes/payments/webhook-test-provider.test.js +156 -0
- package/test/routes/payments/webhook.test.js +115 -0
- package/test/routes/payments/winback-stripe-coupon.test.js +126 -0
- package/test/routes/payments/winback.test.js +597 -0
- package/test/routes/test/authenticate.test.js +79 -0
- package/test/routes/test/redirect.test.js +66 -0
- package/test/routes/test/reset-account.test.js +126 -0
- package/test/routes/test/roster.test.js +101 -0
- package/test/routes/test/schema.test.js +556 -0
- package/test/routes/test/usage.test.js +374 -0
- package/test/routes/user/api-keys.test.js +158 -0
- package/test/routes/user/connections-grant.test.js +759 -0
- package/test/routes/user/connections-identity.test.js +260 -0
- package/test/routes/user/connections-log-privacy.test.js +206 -0
- package/test/routes/user/connections-refresh-lease.test.js +696 -0
- package/test/routes/user/connections-return.test.js +261 -0
- package/test/routes/user/connections-uid.test.js +516 -0
- package/test/routes/user/delete.test.js +138 -0
- package/test/routes/user/feedback.test.js +106 -0
- package/test/routes/user/orders.test.js +233 -0
- package/test/routes/user/sessions.test.js +231 -0
- package/test/routes/user/settings-validate.test.js +84 -0
- package/test/routes/user/signup-emails.test.js +113 -0
- package/test/routes/user/signup-location.test.js +84 -0
- package/test/routes/user/signup-log-privacy.test.js +112 -0
- package/test/routes/user/signup.test.js +579 -0
- package/test/routes/user/subscription.test.js +101 -0
- package/test/routes/user/token.test.js +111 -0
- package/test/routes/user/user.test.js +158 -0
- package/test/routes/verts/cache.test.js +78 -0
- package/test/routes/verts/click-destination.test.js +76 -0
- package/test/routes/verts/crud.test.js +194 -0
- package/test/routes/verts/redirect.test.js +147 -0
- package/test/routes/verts/selection.test.js +237 -0
- package/test/routes/verts/serve.test.js +185 -0
- package/test/routes/verts/unit-document.test.js +117 -0
- package/test/rules/_environment.js +66 -0
- package/test/rules/brand-merge.test.js +195 -0
- package/test/rules/field-helpers.test.js +309 -0
- package/test/rules/notifications.test.js +577 -0
- package/test/rules/payments-carts.test.js +406 -0
- package/test/rules/sessions.test.js +170 -0
- package/test/rules/user.test.js +517 -0
- package/test/rules/verts.test.js +101 -0
- package/test/security/fetch-log-secrets.test.js +124 -0
- package/test/security/primitives.test.js +105 -0
- package/test/security/repo-pinning.test.js +77 -0
- 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
|
+
}
|