@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,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test: the trial-lapse sweep never writes over a newer webhook
|
|
3
|
+
*
|
|
4
|
+
* The sweep is a BACKSTOP for a trial-end webhook that never landed — so the one
|
|
5
|
+
* thing it must never do is undo a webhook that DID. Its guard re-reads the
|
|
6
|
+
* subscription and compares the write stamp against the second the run took its
|
|
7
|
+
* snapshot, and the Stage 1 review found two holes in that
|
|
8
|
+
* ([#212](https://github.com/Omega-JS-Stack/omega/issues/212)):
|
|
9
|
+
*
|
|
10
|
+
* 1. The stamp is a whole SECOND. A webhook that wrote inside the same second as
|
|
11
|
+
* the sweep's read carries an EQUAL number, and a strict `>` read equal as
|
|
12
|
+
* older — so the exact write the guard exists to protect was the one it let
|
|
13
|
+
* through.
|
|
14
|
+
* 2. A bare read followed by a separate `set` leaves a window between them. A
|
|
15
|
+
* webhook landing IN that window was clobbered anyway: the guard proved
|
|
16
|
+
* freshness at a moment that had already passed.
|
|
17
|
+
*
|
|
18
|
+
* Plain-node control-flow test (no emulator, no network), for the same reason the
|
|
19
|
+
* atomic-writes suite is one: the emulator cannot land a contending write inside
|
|
20
|
+
* another client's transaction on demand. The shared in-memory stand-in models
|
|
21
|
+
* Firestore's real transaction contract — versioned reads, staged writes, and a
|
|
22
|
+
* re-run of the whole block when a read document changed underneath — so the retry
|
|
23
|
+
* is exercised rather than assumed. The emulator suite (trial-lapse-sweep.test.js)
|
|
24
|
+
* stays the integration surface for the sweep's happy paths.
|
|
25
|
+
*
|
|
26
|
+
* Run: npx omega test backend:events/payments/trial-lapse-sweep-staleness
|
|
27
|
+
*/
|
|
28
|
+
const assert = require('node:assert');
|
|
29
|
+
const sweep = require('../../../dist/manager/events/cron/daily/trial-lapse-sweep.js');
|
|
30
|
+
const { buildAdmin, CONFIG } = require('./_webhook-harness.js');
|
|
31
|
+
const defineCases = require('../../../dist/vendor/devkit/test/define-cases.js');
|
|
32
|
+
|
|
33
|
+
const DAY = 24 * 60 * 60;
|
|
34
|
+
|
|
35
|
+
const UID = '_test-sweep-staleness-uid';
|
|
36
|
+
const RESOURCE_ID = '_test-sweep-staleness-sub';
|
|
37
|
+
|
|
38
|
+
/** A `$timestamp` pair from a UNIX second */
|
|
39
|
+
function stamp(unix) {
|
|
40
|
+
return { timestamp: new Date(unix * 1000).toISOString(), timestampUNIX: unix };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* A candidate the sweep will select and decide to LAPSE: its trial ended 5 days ago
|
|
45
|
+
* (inside the window, past the grace), it is still active on a paid product, and the
|
|
46
|
+
* provider has no record of the subscription (no payments-orders doc to answer from).
|
|
47
|
+
*/
|
|
48
|
+
function candidate(extraSubscription = {}) {
|
|
49
|
+
const expiredUNIX = Math.floor(Date.now() / 1000) - 5 * DAY;
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
subscription: {
|
|
53
|
+
product: { id: 'premium', name: 'Premium' },
|
|
54
|
+
status: 'active',
|
|
55
|
+
expires: stamp(expiredUNIX),
|
|
56
|
+
trial: { claimed: true, expires: stamp(expiredUNIX) },
|
|
57
|
+
cancellation: { pending: false },
|
|
58
|
+
payment: { provider: 'test', orderId: null, resourceId: RESOURCE_ID, frequency: 'monthly', price: 4.99 },
|
|
59
|
+
...extraSubscription,
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Run the real sweep against the in-memory stand-in
|
|
66
|
+
*
|
|
67
|
+
* @param {object} options
|
|
68
|
+
* @param {object} options.user - The seeded users/{uid} document
|
|
69
|
+
* @param {function|null} options.onRead - The contention seam
|
|
70
|
+
* @returns {Promise<{ store: Map, logs: string[] }>}
|
|
71
|
+
*/
|
|
72
|
+
async function runSweep({ user, onRead = null } = {}) {
|
|
73
|
+
const { admin, store } = buildAdmin({
|
|
74
|
+
seed: { [`users/${UID}`]: user },
|
|
75
|
+
authUids: [UID],
|
|
76
|
+
onRead: onRead,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const logs = [];
|
|
80
|
+
const Manager = { config: CONFIG, libraries: { admin } };
|
|
81
|
+
const ctx = {
|
|
82
|
+
Manager,
|
|
83
|
+
isTesting: () => true,
|
|
84
|
+
log: (...args) => logs.push(args.join(' ')),
|
|
85
|
+
warn: (...args) => logs.push(args.join(' ')),
|
|
86
|
+
error: (...args) => logs.push(args.join(' ')),
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
await sweep({ Manager, ctx, context: {}, libraries: { admin } });
|
|
90
|
+
|
|
91
|
+
return { store, logs };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
module.exports = defineCases({
|
|
95
|
+
description: 'Trial-lapse sweep: the staleness guard and its write are one transaction',
|
|
96
|
+
type: 'group',
|
|
97
|
+
timeout: 30000,
|
|
98
|
+
|
|
99
|
+
tests: [
|
|
100
|
+
{
|
|
101
|
+
name: 'a-write-in-the-same-second-as-the-read-counts-as-newer',
|
|
102
|
+
|
|
103
|
+
async run() {
|
|
104
|
+
// The guard's whole predicate, at the resolution the stamps actually carry.
|
|
105
|
+
// Under the strict `>` this replaced, the equal case answered false — the
|
|
106
|
+
// sweep called a webhook from its own second "older" and wrote over it.
|
|
107
|
+
assert.equal(sweep.isNewerThanSweep(1000, 1000), true, 'a write inside the sweep read\'s own second is NEWER, not older');
|
|
108
|
+
assert.equal(sweep.isNewerThanSweep(1001, 1000), true, 'a later write is newer');
|
|
109
|
+
assert.equal(sweep.isNewerThanSweep(999, 1000), false, 'a write from before the read is genuinely older');
|
|
110
|
+
assert.equal(sweep.isNewerThanSweep(0, 1000), false, 'a subscription nobody has stamped is not newer');
|
|
111
|
+
assert.equal(sweep.isNewerThanSweep(undefined, 1000), false, 'an unstamped subscription reads as 0, not NaN');
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
{
|
|
116
|
+
name: 'an-uncontended-candidate-still-lapses',
|
|
117
|
+
|
|
118
|
+
async run() {
|
|
119
|
+
const { store } = await runSweep({ user: candidate() });
|
|
120
|
+
const swept = store.get(`users/${UID}`);
|
|
121
|
+
|
|
122
|
+
assert.equal(swept.subscription.trial.outcome, 'lapsed', 'the provider has no such subscription — the trial lapsed');
|
|
123
|
+
assert.equal(swept.subscription.status, 'cancelled', 'a lapsed trial ends cancelled');
|
|
124
|
+
assert.equal(swept.subscription.product.id, 'basic', 'a lapsed trial ends on basic');
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
{
|
|
129
|
+
name: 'a-webhook-landing-between-the-guard-and-the-write-is-never-clobbered',
|
|
130
|
+
|
|
131
|
+
async run() {
|
|
132
|
+
// The window the transaction closes: the sweep re-reads a candidate that is
|
|
133
|
+
// still a candidate, and the provider's own trial-end webhook commits before
|
|
134
|
+
// the sweep's write goes out. Written THROUGH the stand-in, so the document
|
|
135
|
+
// version moves and the transaction is genuinely contended.
|
|
136
|
+
let contended = false;
|
|
137
|
+
|
|
138
|
+
const { store, logs } = await runSweep({
|
|
139
|
+
user: candidate(),
|
|
140
|
+
onRead: (path, { write }) => {
|
|
141
|
+
if (contended || path !== `users/${UID}`) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
contended = true;
|
|
146
|
+
|
|
147
|
+
// What the webhook pipeline writes when it converts the trial itself:
|
|
148
|
+
// the term moved out past the trial end, and the write is stamped.
|
|
149
|
+
write(path, {
|
|
150
|
+
subscription: {
|
|
151
|
+
status: 'active',
|
|
152
|
+
product: { id: 'premium', name: 'Premium' },
|
|
153
|
+
expires: stamp(Math.floor(Date.now() / 1000) + 25 * DAY),
|
|
154
|
+
payment: { updatedBy: { date: stamp(Math.floor(Date.now() / 1000) + 5) } },
|
|
155
|
+
},
|
|
156
|
+
}, { merge: true });
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
const swept = store.get(`users/${UID}`);
|
|
161
|
+
|
|
162
|
+
assert.ok(contended, 'the contending write must actually have run — otherwise this proves nothing');
|
|
163
|
+
assert.equal(swept.subscription.status, 'active', 'the webhook\'s state survives: the subscriber is still active');
|
|
164
|
+
assert.equal(swept.subscription.product.id, 'premium', 'the webhook\'s product survives — the sweep did not reset it to basic');
|
|
165
|
+
assert.equal(swept.subscription.trial.outcome, undefined, 'and no lapse was stamped over it');
|
|
166
|
+
assert.ok(
|
|
167
|
+
logs.some((line) => line.includes('a newer subscription write landed')),
|
|
168
|
+
`the re-run must SAY it skipped on staleness, got: ${logs.join(' | ')}`,
|
|
169
|
+
);
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
],
|
|
173
|
+
});
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test: the daily trial-lapse sweep
|
|
3
|
+
*
|
|
4
|
+
* A trial that ends without converting should leave the user on basic. The providers
|
|
5
|
+
* say so with a webhook, and when that webhook is missed the user keeps a paid product
|
|
6
|
+
* they never paid for — invisibly, because `trial.claimed` means "this subscription HAD
|
|
7
|
+
* a trial", never "it converted" ([#212]).
|
|
8
|
+
*
|
|
9
|
+
* The sweep is a BACKSTOP, so what it must NOT do matters as much as what it does: it
|
|
10
|
+
* never infers a lapse from dates alone (every candidate is confirmed against live
|
|
11
|
+
* provider state), it leaves the 24h grace window and everything older than 30 days
|
|
12
|
+
* alone, and a second run changes nothing.
|
|
13
|
+
*
|
|
14
|
+
* The test provider answers `fetchResource` from `payments-orders`, so "the provider
|
|
15
|
+
* says X" is seeded as an order doc — or, for "the subscription is gone", as no order
|
|
16
|
+
* doc at all.
|
|
17
|
+
*/
|
|
18
|
+
const sweep = require('../../../dist/manager/events/cron/daily/trial-lapse-sweep.js');
|
|
19
|
+
const analytics = require('../../../dist/manager/events/firestore/payments-webhooks/analytics.js');
|
|
20
|
+
const defineCases = require('../../../dist/vendor/devkit/test/define-cases.js');
|
|
21
|
+
|
|
22
|
+
const DAY = 24 * 60 * 60;
|
|
23
|
+
|
|
24
|
+
const GONE_UID = '_test-trial-lapse-gone';
|
|
25
|
+
const CANCELLED_UID = '_test-trial-lapse-cancelled';
|
|
26
|
+
const CONVERTED_UID = '_test-trial-lapse-converted';
|
|
27
|
+
const GRACE_UID = '_test-trial-lapse-grace';
|
|
28
|
+
const AGED_UID = '_test-trial-lapse-aged';
|
|
29
|
+
|
|
30
|
+
const CANCELLED_ORDER = '_test-order-trial-lapse-cancelled';
|
|
31
|
+
const CONVERTED_ORDER = '_test-order-trial-lapse-converted';
|
|
32
|
+
|
|
33
|
+
module.exports = defineCases({
|
|
34
|
+
description: 'Trial-lapse sweep: confirms with the provider, then corrects state',
|
|
35
|
+
type: 'suite',
|
|
36
|
+
timeout: 180000,
|
|
37
|
+
|
|
38
|
+
tests: [
|
|
39
|
+
{
|
|
40
|
+
// The sweep is the ONLY path that ever sees a PayPal trial's outcome: PayPal
|
|
41
|
+
// fires no trial-end event, so nothing else in the pipeline is told. Before
|
|
42
|
+
// [#407](https://github.com/Omega-JS-Stack/omega/issues/407) the state was
|
|
43
|
+
// corrected here and the analytics signal was never sent at all.
|
|
44
|
+
name: 'the-sweep-reports-a-lapse-no-webhook-will-ever-announce',
|
|
45
|
+
async run({ assert }) {
|
|
46
|
+
const nowUNIX = Math.floor(Date.now() / 1000);
|
|
47
|
+
const trialEndUNIX = nowUNIX - 5 * DAY;
|
|
48
|
+
|
|
49
|
+
// PayPal's in-trial shape: `expires` is next_billing_time, which during a
|
|
50
|
+
// trial IS the trial's end.
|
|
51
|
+
const paypalTrial = {
|
|
52
|
+
product: { id: 'premium', name: 'Premium' },
|
|
53
|
+
status: 'active',
|
|
54
|
+
expires: stamp(trialEndUNIX),
|
|
55
|
+
trial: { claimed: true, expires: stamp(trialEndUNIX) },
|
|
56
|
+
payment: { provider: 'paypal', resourceId: 'I-PAYPAL-TRIAL', frequency: 'monthly', price: 9.99 },
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const conversion = sweep.resolveTrialOutcomeConversion(paypalTrial, 'lapsed', 'USD');
|
|
60
|
+
|
|
61
|
+
assert.ok(conversion, 'a lapse the sweep decided should be reported');
|
|
62
|
+
assert.equal(conversion.event, 'trial_lapse');
|
|
63
|
+
assert.equal(conversion.params.value, 9.99, 'the value is the subscription that never started paying');
|
|
64
|
+
assert.equal(conversion.params.is_trial, true);
|
|
65
|
+
assert.equal(conversion.params.is_recurring, false, 'nothing was ever charged');
|
|
66
|
+
assert.equal(
|
|
67
|
+
conversion.eventId,
|
|
68
|
+
'trial_lapse.I-PAYPAL-TRIAL',
|
|
69
|
+
'the dedupe id is keyed to the SUBSCRIPTION, which is the one thing this sweep and the payment webhook both know',
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
const converted = sweep.resolveTrialOutcomeConversion({ ...paypalTrial }, 'converted', 'USD');
|
|
73
|
+
|
|
74
|
+
assert.equal(converted.event, 'trial_convert');
|
|
75
|
+
assert.equal(converted.eventId, 'trial_convert.I-PAYPAL-TRIAL');
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
{
|
|
80
|
+
// A trial whose outcome a payment webhook already resolved was already
|
|
81
|
+
// reported at the moment it happened. The sweep still stamps the outcome —
|
|
82
|
+
// that is state correction — but reporting it again would be a second
|
|
83
|
+
// conversion: GA4 deduplicates a `purchase` on `transaction_id`, and this
|
|
84
|
+
// path has no invoice to name, so the two reports would arrive under two
|
|
85
|
+
// different ids ([#656]).
|
|
86
|
+
name: 'the-sweep-never-reports-an-outcome-a-webhook-already-resolved',
|
|
87
|
+
async run({ assert }) {
|
|
88
|
+
const nowUNIX = Math.floor(Date.now() / 1000);
|
|
89
|
+
const trialEndUNIX = nowUNIX - 5 * DAY;
|
|
90
|
+
|
|
91
|
+
// The conversion charge moved `expires` out to the end of the first paid
|
|
92
|
+
// period — the webhook saw it, and fired trial_convert then.
|
|
93
|
+
const alreadyReported = {
|
|
94
|
+
product: { id: 'premium', name: 'Premium' },
|
|
95
|
+
status: 'active',
|
|
96
|
+
expires: stamp(nowUNIX + 25 * DAY),
|
|
97
|
+
trial: { claimed: true, expires: stamp(trialEndUNIX) },
|
|
98
|
+
payment: { provider: 'stripe', resourceId: 'sub_stripe_converted', frequency: 'monthly', price: 9.99 },
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
assert.equal(
|
|
102
|
+
sweep.resolveTrialOutcomeConversion(alreadyReported, 'converted', 'USD'),
|
|
103
|
+
null,
|
|
104
|
+
'the term has already moved past the trial, so a payment webhook reported this conversion',
|
|
105
|
+
);
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
{
|
|
110
|
+
// The two paths that can report a trial outcome derive their dedupe id in
|
|
111
|
+
// two different files, and the whole no-double-count guarantee rests on the
|
|
112
|
+
// two strings being IDENTICAL. Nothing else would notice them drifting, so
|
|
113
|
+
// this compares the real output of both derivations for one subscription
|
|
114
|
+
// ([#407](https://github.com/Omega-JS-Stack/omega/issues/407)).
|
|
115
|
+
name: 'the-sweep-and-the-webhook-derive-the-same-trial-dedupe-id',
|
|
116
|
+
async run({ assert }) {
|
|
117
|
+
const nowUNIX = Math.floor(Date.now() / 1000);
|
|
118
|
+
const trialEndUNIX = nowUNIX - 5 * DAY;
|
|
119
|
+
const RESOURCE_ID = 'sub_shared_trial';
|
|
120
|
+
|
|
121
|
+
const inTrial = {
|
|
122
|
+
product: { id: 'premium', name: 'Premium' },
|
|
123
|
+
status: 'active',
|
|
124
|
+
expires: stamp(trialEndUNIX),
|
|
125
|
+
trial: { claimed: true, expires: stamp(trialEndUNIX) },
|
|
126
|
+
payment: { provider: 'stripe', resourceId: RESOURCE_ID, frequency: 'monthly', price: 9.99 },
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// The same subscription, one charge later — what the webhook resolves.
|
|
130
|
+
const converted = { ...inTrial, expires: stamp(nowUNIX + 25 * DAY) };
|
|
131
|
+
|
|
132
|
+
for (const [outcome, transition, eventType, after] of [
|
|
133
|
+
['converted', null, 'invoice.payment_succeeded', converted],
|
|
134
|
+
['lapsed', 'payment-failed', 'invoice.payment_failed', { ...inTrial, status: 'suspended' }],
|
|
135
|
+
]) {
|
|
136
|
+
const resolved = analytics.resolvePaymentEvent('subscription', transition, eventType, after, {}, null, inTrial);
|
|
137
|
+
const webhookId = analytics.resolveEventId(resolved, { id: '_test-order', metadata: { updatedBy: { event: { id: 'evt_whatever' } } } });
|
|
138
|
+
const sweepId = sweep.resolveTrialOutcomeConversion(inTrial, outcome, 'USD').eventId;
|
|
139
|
+
|
|
140
|
+
assert.equal(webhookId, sweepId, `both paths must call a ${outcome} trial the same conversion`);
|
|
141
|
+
assert.equal(webhookId.endsWith(`.${RESOURCE_ID}`), true, 'and both must key it on the subscription');
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
|
|
146
|
+
{
|
|
147
|
+
name: 'seed-expired-trials',
|
|
148
|
+
async run({ firestore, assert, state, config, skip }) {
|
|
149
|
+
const paidProduct = config.payment.products.find(p => p.id !== 'basic' && p.type === 'subscription' && p.prices);
|
|
150
|
+
|
|
151
|
+
if (!paidProduct) {
|
|
152
|
+
skip('No paid subscription product configured in this brand');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
state.paidProductId = paidProduct.id;
|
|
156
|
+
|
|
157
|
+
// Five candidates, one per branch the sweep has to get right
|
|
158
|
+
state.seeds = {
|
|
159
|
+
// Trial ended 5 days ago; NO order doc → the provider has no such subscription
|
|
160
|
+
[GONE_UID]: { expiredAgo: 5 * DAY, resourceId: 'sub_test_trial_gone', orderId: null },
|
|
161
|
+
// Trial ended 5 days ago; the order says the provider cancelled it
|
|
162
|
+
[CANCELLED_UID]: { expiredAgo: 5 * DAY, resourceId: 'sub_test_trial_cancelled', orderId: CANCELLED_ORDER },
|
|
163
|
+
// Trial ended 5 days ago; the order says the subscription is live — it converted
|
|
164
|
+
[CONVERTED_UID]: { expiredAgo: 5 * DAY, resourceId: 'sub_test_trial_converted', orderId: CONVERTED_ORDER },
|
|
165
|
+
// Trial ended 2 hours ago — inside the grace window that absorbs webhook lag
|
|
166
|
+
[GRACE_UID]: { expiredAgo: 2 * 60 * 60, resourceId: 'sub_test_trial_grace', orderId: null },
|
|
167
|
+
// Trial ended 40 days ago — aged out below the sweep's floor
|
|
168
|
+
[AGED_UID]: { expiredAgo: 40 * DAY, resourceId: 'sub_test_trial_aged', orderId: null },
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const nowUNIX = Math.floor(Date.now() / 1000);
|
|
172
|
+
|
|
173
|
+
for (const [uid, seed] of Object.entries(state.seeds)) {
|
|
174
|
+
const expiredUNIX = nowUNIX - seed.expiredAgo;
|
|
175
|
+
|
|
176
|
+
await firestore.delete(`users/${uid}`);
|
|
177
|
+
await firestore.set(`users/${uid}`, {
|
|
178
|
+
subscription: {
|
|
179
|
+
product: { id: paidProduct.id, name: paidProduct.name || paidProduct.id },
|
|
180
|
+
status: 'active',
|
|
181
|
+
expires: stamp(expiredUNIX),
|
|
182
|
+
trial: { claimed: true, expires: stamp(expiredUNIX) },
|
|
183
|
+
cancellation: { pending: false, date: stamp(0) },
|
|
184
|
+
payment: { provider: 'test', orderId: seed.orderId, resourceId: seed.resourceId, frequency: 'monthly' },
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// The provider's answers for the two candidates that have a subscription:
|
|
190
|
+
// an order whose `unified` the test library reconstructs a resource from
|
|
191
|
+
await firestore.delete(`payments-orders/${CANCELLED_ORDER}`);
|
|
192
|
+
await firestore.set(`payments-orders/${CANCELLED_ORDER}`, order({
|
|
193
|
+
id: CANCELLED_ORDER,
|
|
194
|
+
owner: CANCELLED_UID,
|
|
195
|
+
productId: paidProduct.id,
|
|
196
|
+
resourceId: 'sub_test_trial_cancelled',
|
|
197
|
+
status: 'cancelled',
|
|
198
|
+
}));
|
|
199
|
+
|
|
200
|
+
await firestore.delete(`payments-orders/${CONVERTED_ORDER}`);
|
|
201
|
+
await firestore.set(`payments-orders/${CONVERTED_ORDER}`, order({
|
|
202
|
+
id: CONVERTED_ORDER,
|
|
203
|
+
owner: CONVERTED_UID,
|
|
204
|
+
productId: paidProduct.id,
|
|
205
|
+
resourceId: 'sub_test_trial_converted',
|
|
206
|
+
status: 'active',
|
|
207
|
+
}));
|
|
208
|
+
|
|
209
|
+
const seeded = await firestore.get(`users/${GONE_UID}`);
|
|
210
|
+
assert.equal(seeded.subscription.status, 'active', 'Every candidate starts active on a paid product');
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
|
|
214
|
+
{
|
|
215
|
+
name: 'the-sweep-lapses-what-the-provider-no-longer-has',
|
|
216
|
+
timeout: 150000,
|
|
217
|
+
async run({ firestore, assert, waitFor, pubsub, state }) {
|
|
218
|
+
await pubsub.trigger('omega_cronDaily');
|
|
219
|
+
|
|
220
|
+
await waitFor(async () => {
|
|
221
|
+
const doc = await firestore.get(`users/${GONE_UID}`);
|
|
222
|
+
return !!doc?.subscription?.trial?.outcome;
|
|
223
|
+
}, 120000, 1000);
|
|
224
|
+
|
|
225
|
+
const gone = await firestore.get(`users/${GONE_UID}`);
|
|
226
|
+
|
|
227
|
+
assert.equal(gone.subscription.trial.outcome, 'lapsed', 'The provider has no such subscription — the trial lapsed');
|
|
228
|
+
assert.equal(gone.subscription.status, 'cancelled', 'A lapsed trial ends cancelled');
|
|
229
|
+
assert.equal(gone.subscription.product.id, 'basic', 'A lapsed trial ends on basic');
|
|
230
|
+
assert.equal(gone.subscription.cancellation.pending, false, 'Nothing is left pending');
|
|
231
|
+
assert.equal(gone.subscription.trial.claimed, true, 'claimed still means "a trial happened" — the sweep never repurposes it');
|
|
232
|
+
|
|
233
|
+
state.lapsedCancelledAtUNIX = gone.subscription.cancellation.date.timestampUNIX;
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
|
|
237
|
+
{
|
|
238
|
+
name: 'a-cancelled-subscription-lapses-the-same-way',
|
|
239
|
+
timeout: 60000,
|
|
240
|
+
async run({ firestore, assert, waitFor }) {
|
|
241
|
+
await waitFor(async () => {
|
|
242
|
+
const doc = await firestore.get(`users/${CANCELLED_UID}`);
|
|
243
|
+
return !!doc?.subscription?.trial?.outcome;
|
|
244
|
+
}, 30000, 500);
|
|
245
|
+
|
|
246
|
+
const cancelled = await firestore.get(`users/${CANCELLED_UID}`);
|
|
247
|
+
|
|
248
|
+
assert.equal(cancelled.subscription.trial.outcome, 'lapsed', 'The provider says cancelled — the trial lapsed');
|
|
249
|
+
assert.equal(cancelled.subscription.status, 'cancelled', 'A lapsed trial ends cancelled');
|
|
250
|
+
assert.equal(cancelled.subscription.product.id, 'basic', 'A lapsed trial ends on basic');
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
|
|
254
|
+
{
|
|
255
|
+
name: 'a-converted-trial-is-stamped-and-otherwise-untouched',
|
|
256
|
+
timeout: 60000,
|
|
257
|
+
async run({ firestore, assert, waitFor, state }) {
|
|
258
|
+
await waitFor(async () => {
|
|
259
|
+
const doc = await firestore.get(`users/${CONVERTED_UID}`);
|
|
260
|
+
return !!doc?.subscription?.trial?.outcome;
|
|
261
|
+
}, 30000, 500);
|
|
262
|
+
|
|
263
|
+
const converted = await firestore.get(`users/${CONVERTED_UID}`);
|
|
264
|
+
|
|
265
|
+
assert.equal(converted.subscription.trial.outcome, 'converted', 'The provider says active — the trial converted');
|
|
266
|
+
assert.equal(converted.subscription.status, 'active', 'A converted trial keeps its active subscription');
|
|
267
|
+
assert.equal(converted.subscription.product.id, state.paidProductId, 'A converted trial keeps its paid product');
|
|
268
|
+
assert.equal(converted.subscription.cancellation.pending, false, 'Nothing was cancelled');
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
|
|
272
|
+
{
|
|
273
|
+
name: 'the-window-edges-are-left-alone',
|
|
274
|
+
async run({ firestore, assert, state }) {
|
|
275
|
+
const grace = await firestore.get(`users/${GRACE_UID}`);
|
|
276
|
+
const aged = await firestore.get(`users/${AGED_UID}`);
|
|
277
|
+
|
|
278
|
+
assert.equal(grace.subscription.trial.outcome, undefined, 'A trial that ended 2h ago is inside the grace window — webhook lag gets its chance first');
|
|
279
|
+
assert.equal(grace.subscription.status, 'active', 'The grace candidate is untouched');
|
|
280
|
+
assert.equal(grace.subscription.product.id, state.paidProductId, 'The grace candidate keeps its product');
|
|
281
|
+
|
|
282
|
+
assert.equal(aged.subscription.trial.outcome, undefined, 'A trial that ended 40 days ago is below the floor — history is not the sweep\'s job');
|
|
283
|
+
assert.equal(aged.subscription.status, 'active', 'The aged candidate is untouched');
|
|
284
|
+
},
|
|
285
|
+
},
|
|
286
|
+
|
|
287
|
+
{
|
|
288
|
+
name: 'a-second-run-changes-nothing',
|
|
289
|
+
timeout: 150000,
|
|
290
|
+
async run({ firestore, assert, waitFor, pubsub, state }) {
|
|
291
|
+
// A run that skips every stamped candidate produces no signal of its own, so
|
|
292
|
+
// the grace candidate becomes the marker: aged past the grace window, the
|
|
293
|
+
// SECOND run must lapse it — and its stamp is proof the sweep ran to the end.
|
|
294
|
+
const expiredUNIX = Math.floor(Date.now() / 1000) - 5 * DAY;
|
|
295
|
+
await firestore.set(`users/${GRACE_UID}`, {
|
|
296
|
+
subscription: { trial: { expires: stamp(expiredUNIX) } },
|
|
297
|
+
}, { merge: true });
|
|
298
|
+
|
|
299
|
+
await pubsub.trigger('omega_cronDaily');
|
|
300
|
+
|
|
301
|
+
await waitFor(async () => {
|
|
302
|
+
const doc = await firestore.get(`users/${GRACE_UID}`);
|
|
303
|
+
return !!doc?.subscription?.trial?.outcome;
|
|
304
|
+
}, 120000, 1000);
|
|
305
|
+
|
|
306
|
+
const grace = await firestore.get(`users/${GRACE_UID}`);
|
|
307
|
+
const gone = await firestore.get(`users/${GONE_UID}`);
|
|
308
|
+
const converted = await firestore.get(`users/${CONVERTED_UID}`);
|
|
309
|
+
|
|
310
|
+
assert.equal(grace.subscription.trial.outcome, 'lapsed', 'The second run reached the sweep and lapsed the newly-eligible candidate');
|
|
311
|
+
assert.equal(gone.subscription.trial.outcome, 'lapsed', 'The already-lapsed candidate is still lapsed');
|
|
312
|
+
assert.equal(
|
|
313
|
+
gone.subscription.cancellation.date.timestampUNIX,
|
|
314
|
+
state.lapsedCancelledAtUNIX,
|
|
315
|
+
'The cancellation date is the FIRST run\'s — the second run skipped it instead of writing again',
|
|
316
|
+
);
|
|
317
|
+
assert.equal(converted.subscription.trial.outcome, 'converted', 'The converted candidate is still converted');
|
|
318
|
+
assert.equal(converted.subscription.status, 'active', 'The converted candidate is still active');
|
|
319
|
+
},
|
|
320
|
+
},
|
|
321
|
+
|
|
322
|
+
],
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* A `$timestamp` pair from a UNIX second
|
|
327
|
+
*/
|
|
328
|
+
function stamp(unix) {
|
|
329
|
+
return { timestamp: new Date(unix * 1000).toISOString(), timestampUNIX: unix };
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* The purchase record the test provider answers `fetchResource` from — its `unified`
|
|
334
|
+
* status IS what the provider reports for the subscription
|
|
335
|
+
*/
|
|
336
|
+
function order({ id, owner, productId, resourceId, status }) {
|
|
337
|
+
const nowUNIX = Math.floor(Date.now() / 1000);
|
|
338
|
+
|
|
339
|
+
return {
|
|
340
|
+
id: id,
|
|
341
|
+
type: 'subscription',
|
|
342
|
+
owner: owner,
|
|
343
|
+
productId: productId,
|
|
344
|
+
provider: 'test',
|
|
345
|
+
resourceId: resourceId,
|
|
346
|
+
unified: {
|
|
347
|
+
product: { id: productId },
|
|
348
|
+
status: status,
|
|
349
|
+
expires: stamp(nowUNIX + 30 * DAY),
|
|
350
|
+
trial: { claimed: true, expires: stamp(nowUNIX - 5 * DAY) },
|
|
351
|
+
cancellation: { pending: false },
|
|
352
|
+
payment: { provider: 'test', orderId: id, resourceId: resourceId, frequency: 'monthly' },
|
|
353
|
+
},
|
|
354
|
+
metadata: {
|
|
355
|
+
created: stamp(nowUNIX - 20 * DAY),
|
|
356
|
+
updated: stamp(nowUNIX - 20 * DAY),
|
|
357
|
+
},
|
|
358
|
+
};
|
|
359
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test: the webhook pipeline's three writes land together or not at all
|
|
3
|
+
*
|
|
4
|
+
* Processing a subscription event writes users/{uid}.subscription, then
|
|
5
|
+
* payments-orders/{orderId}, then payments-intents/{orderId}. As three separate
|
|
6
|
+
* awaits, a throw between them left the user PAID with no order behind it — the
|
|
7
|
+
* order page, the cancel endpoint and the refund lane all read the order, so the
|
|
8
|
+
* split state is invisible until a support ticket ([#219]).
|
|
9
|
+
*
|
|
10
|
+
* Plain-node control-flow test (no emulator, no network): the real trigger module
|
|
11
|
+
* runs against the shared in-memory Firestore stand-in (_webhook-harness.js) whose
|
|
12
|
+
* payments-orders write rejects. The emulator cannot produce this fault at all —
|
|
13
|
+
* the admin SDK bypasses rules, and all three writes are legal — so the split is
|
|
14
|
+
* only reachable by making one write fail, which is exactly what the stand-in
|
|
15
|
+
* does. The emulator suites stay the integration surface for the happy paths.
|
|
16
|
+
*/
|
|
17
|
+
const assert = require('node:assert');
|
|
18
|
+
const { runTrigger } = require('./_webhook-harness.js');
|
|
19
|
+
const defineCases = require('../../../dist/vendor/devkit/test/define-cases.js');
|
|
20
|
+
|
|
21
|
+
const UID = '_test-atomic-uid';
|
|
22
|
+
const ORDER_ID = '4242-4242-4242';
|
|
23
|
+
const RESOURCE_ID = '_test-atomic-sub';
|
|
24
|
+
const EVENT_ID = '_test-atomic-evt';
|
|
25
|
+
|
|
26
|
+
/** The event this suite is about, with only the failing write left to choose */
|
|
27
|
+
function runEvent({ failPath } = {}) {
|
|
28
|
+
return runTrigger({
|
|
29
|
+
uid: UID,
|
|
30
|
+
orderId: ORDER_ID,
|
|
31
|
+
resourceId: RESOURCE_ID,
|
|
32
|
+
eventId: EVENT_ID,
|
|
33
|
+
failPath: failPath,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
module.exports = defineCases({
|
|
38
|
+
description: 'Webhook pipeline writes are atomic (no split payment state)',
|
|
39
|
+
type: 'group',
|
|
40
|
+
timeout: 30000,
|
|
41
|
+
|
|
42
|
+
tests: [
|
|
43
|
+
{
|
|
44
|
+
name: 'a healthy event writes the subscription, the order and the intent',
|
|
45
|
+
|
|
46
|
+
async run() {
|
|
47
|
+
const { store } = await runEvent();
|
|
48
|
+
|
|
49
|
+
assert.equal(store.get(`users/${UID}`)?.subscription?.status, 'active', 'the subscription lands');
|
|
50
|
+
assert.equal(store.get(`payments-orders/${ORDER_ID}`)?.owner, UID, 'the order lands');
|
|
51
|
+
assert.equal(store.get(`payments-intents/${ORDER_ID}`)?.status, 'completed', 'the intent is closed out');
|
|
52
|
+
assert.equal(store.get(`payments-webhooks/${EVENT_ID}`)?.status, 'completed', 'the webhook completes');
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
{
|
|
57
|
+
name: 'a failed order write leaves NO subscription behind it',
|
|
58
|
+
|
|
59
|
+
async run() {
|
|
60
|
+
const { store } = await runEvent({ failPath: `payments-orders/${ORDER_ID}` });
|
|
61
|
+
|
|
62
|
+
assert.ok(!store.get(`users/${UID}`)?.subscription, 'the user must not be left paid with no order');
|
|
63
|
+
assert.ok(!store.get(`payments-orders/${ORDER_ID}`), 'the order write is the one that failed');
|
|
64
|
+
assert.equal(store.get(`payments-intents/${ORDER_ID}`)?.status, 'failed', 'the failure path still closes the intent');
|
|
65
|
+
assert.equal(store.get(`payments-webhooks/${EVENT_ID}`)?.status, 'failed', 'the webhook is marked failed');
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
});
|