@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,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POST /marketing/email-preferences - Update marketing email consent
|
|
3
|
+
*
|
|
4
|
+
* Two supported modes:
|
|
5
|
+
*
|
|
6
|
+
* 1) Authenticated (account-page toggle):
|
|
7
|
+
* - User must be logged in.
|
|
8
|
+
* - settings.action = 'opt-in' | 'opt-out'.
|
|
9
|
+
* - Writes canonical consent.marketing to user doc.
|
|
10
|
+
* - Hits BOTH SendGrid + Beehiiv via the email library (one source of truth).
|
|
11
|
+
*
|
|
12
|
+
* 2) Anonymous (HMAC unsubscribe link from email footer):
|
|
13
|
+
* - No login. settings.email + settings.asmId + settings.sig + settings.action.
|
|
14
|
+
* - HMAC validates the link was generated by us.
|
|
15
|
+
* - Hits SendGrid ASM directly (legacy behavior — preserves existing one-click unsub).
|
|
16
|
+
* - Also writes canonical consent.marketing to user doc when email maps to a user.
|
|
17
|
+
* - Then cross-syncs via the email library (best-effort): unsubscribe removes the
|
|
18
|
+
* contact from ALL providers (Beehiiv too — compliance); subscribe re-adds the
|
|
19
|
+
* contact (sync when the email maps to a user, add when it doesn't).
|
|
20
|
+
*/
|
|
21
|
+
const fetch = require('wonderful-fetch');
|
|
22
|
+
const crypto = require('crypto');
|
|
23
|
+
const env = require('../../../libraries/env.js');
|
|
24
|
+
const { MARKETING_RATE_LIMIT } = require('../../../libraries/rate-limits.js');
|
|
25
|
+
|
|
26
|
+
module.exports = async ({ ctx, Manager, user, settings, analytics }) => {
|
|
27
|
+
|
|
28
|
+
// --- AUTHENTICATED MODE ---
|
|
29
|
+
if (user.authenticated) {
|
|
30
|
+
return handleAuthenticated({ ctx, Manager, user, settings, analytics });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// --- ANONYMOUS HMAC MODE ---
|
|
34
|
+
return handleAnonymous({ ctx, Manager, settings, analytics });
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Authenticated user toggling marketing on/off from the account page.
|
|
39
|
+
*/
|
|
40
|
+
async function handleAuthenticated({ ctx, Manager, user, settings, analytics }) {
|
|
41
|
+
const { admin } = Manager.libraries;
|
|
42
|
+
const action = settings.action;
|
|
43
|
+
|
|
44
|
+
if (action !== 'subscribe' && action !== 'unsubscribe') {
|
|
45
|
+
return ctx.respond('Invalid action — must be "subscribe" or "unsubscribe"', { code: 400 });
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Rate-limit per-user (defense against accidental toggling spam). consume()
|
|
49
|
+
// is check, count and write in ONE call and throws the 429 itself
|
|
50
|
+
// ([#647](https://github.com/Omega-JS-Stack/omega/issues/647)); the limit is
|
|
51
|
+
// EXPLICIT because this is a framework anti-abuse gate, not a plan feature.
|
|
52
|
+
try {
|
|
53
|
+
await ctx.usage.consume('email-preferences', 1, { limit: MARKETING_RATE_LIMIT });
|
|
54
|
+
} catch (e) {
|
|
55
|
+
// Only a rate limit is a rate limit — a 500 from consume() must not come
|
|
56
|
+
// back as one
|
|
57
|
+
if (e.code !== 429) {
|
|
58
|
+
throw e;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return ctx.respond('Rate limit exceeded', { code: 429 });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const uid = user.auth.uid;
|
|
65
|
+
const email = user.auth.email;
|
|
66
|
+
const ip = ctx.request.geolocation?.ip || null;
|
|
67
|
+
const timestamp = ctx.meta.startTime.timestamp;
|
|
68
|
+
const timestampUNIX = ctx.meta.startTime.timestampUNIX;
|
|
69
|
+
|
|
70
|
+
// Build the consent.marketing mutation
|
|
71
|
+
const marketingPatch = action === 'subscribe'
|
|
72
|
+
? {
|
|
73
|
+
status: 'granted',
|
|
74
|
+
grantedAt: { timestamp, timestampUNIX, source: 'account', ip, text: null },
|
|
75
|
+
// Leave revokedAt untouched — informational record of most recent revoke
|
|
76
|
+
}
|
|
77
|
+
: {
|
|
78
|
+
status: 'revoked',
|
|
79
|
+
revokedAt: { timestamp, timestampUNIX, source: 'account', ip, text: null },
|
|
80
|
+
// Leave grantedAt untouched — informational record of original grant
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
await admin.firestore().doc(`users/${uid}`).set({
|
|
84
|
+
consent: { marketing: marketingPatch },
|
|
85
|
+
metadata: Manager.Metadata().set({ tag: 'marketing/email-preferences' }),
|
|
86
|
+
}, { merge: true });
|
|
87
|
+
|
|
88
|
+
ctx.log(`email-preferences (auth): ${uid} → ${action}`);
|
|
89
|
+
|
|
90
|
+
// Skip provider calls in test mode unless extended mode is on
|
|
91
|
+
const shouldCallExternalAPIs = !ctx.isTesting() || process.env.TEST_EXTENDED_MODE;
|
|
92
|
+
|
|
93
|
+
if (shouldCallExternalAPIs) {
|
|
94
|
+
const mailer = Manager.Email(ctx);
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
if (action === 'unsubscribe') {
|
|
98
|
+
await mailer.remove(email);
|
|
99
|
+
} else {
|
|
100
|
+
await mailer.sync(uid);
|
|
101
|
+
}
|
|
102
|
+
} catch (e) {
|
|
103
|
+
ctx.error(`email-preferences (auth) provider sync failed:`, e);
|
|
104
|
+
// Doc is already updated — provider sync is best-effort. Don't fail the request.
|
|
105
|
+
}
|
|
106
|
+
} else {
|
|
107
|
+
ctx.log('email-preferences (auth): Skipping provider calls (OMEGA_TEST_MODE=true)');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
analytics.event('marketing/email-preferences', { action, mode: 'authenticated' });
|
|
111
|
+
|
|
112
|
+
return ctx.respond({ success: true, action });
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Anonymous HMAC unsubscribe link (preserves existing email-footer one-click flow).
|
|
117
|
+
*/
|
|
118
|
+
async function handleAnonymous({ ctx, Manager, settings, analytics }) {
|
|
119
|
+
const { admin } = Manager.libraries;
|
|
120
|
+
|
|
121
|
+
const email = (settings.email || '').trim().toLowerCase();
|
|
122
|
+
const asmId = parseInt(settings.asmId, 10);
|
|
123
|
+
const action = settings.action || 'unsubscribe';
|
|
124
|
+
|
|
125
|
+
// Validate inputs
|
|
126
|
+
if (!email) {
|
|
127
|
+
return ctx.respond('Email is required', { code: 400 });
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
131
|
+
if (!emailRegex.test(email)) {
|
|
132
|
+
return ctx.respond('Invalid email format', { code: 400 });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (!asmId || isNaN(asmId)) {
|
|
136
|
+
return ctx.respond('ASM group ID is required', { code: 400 });
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (action !== 'subscribe' && action !== 'unsubscribe') {
|
|
140
|
+
return ctx.respond('Invalid action', { code: 400 });
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// HMAC validation (proves we generated this link)
|
|
144
|
+
const expectedSig = crypto.createHmac('sha256', env.require('UNSUBSCRIBE_HMAC_KEY')).update(email).digest('hex');
|
|
145
|
+
if (settings.sig !== expectedSig) {
|
|
146
|
+
return ctx.respond('Invalid signature', { code: 403 });
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// IP rate limiting (anonymous flow) — an EXPLICIT keyed counter, which is
|
|
150
|
+
// the only way an anonymous caller is counted now
|
|
151
|
+
// ([#647](https://github.com/Omega-JS-Stack/omega/issues/647))
|
|
152
|
+
try {
|
|
153
|
+
await ctx.usage.forKey(ctx.request.geolocation.ip).consume('email-preferences', 1, { limit: MARKETING_RATE_LIMIT });
|
|
154
|
+
} catch (e) {
|
|
155
|
+
// Only a rate limit is a rate limit — a 500 from consume() must not come
|
|
156
|
+
// back as one
|
|
157
|
+
if (e.code !== 429) {
|
|
158
|
+
throw e;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return ctx.respond('Rate limit exceeded', { code: 429 });
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Mirror to the user doc if this email maps to a user (best-effort, silent on miss).
|
|
165
|
+
// Runs BEFORE the provider calls below — on subscribe it writes consent granted first,
|
|
166
|
+
// so the email library's consent gate passes when we re-sync.
|
|
167
|
+
const uid = await mirrorAnonymousToUserDoc({ ctx, Manager, email, action });
|
|
168
|
+
|
|
169
|
+
// Call SendGrid ASM (legacy behavior)
|
|
170
|
+
const shouldCallExternalAPIs = !ctx.isTesting() || process.env.TEST_EXTENDED_MODE;
|
|
171
|
+
|
|
172
|
+
if (!shouldCallExternalAPIs) {
|
|
173
|
+
ctx.log('email-preferences (anon): Skipping SendGrid (OMEGA_TEST_MODE=true)');
|
|
174
|
+
return ctx.respond({ success: true });
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
try {
|
|
178
|
+
if (action === 'unsubscribe') {
|
|
179
|
+
// POST returns JSON ({recipient_emails: [...]}) on success
|
|
180
|
+
await fetch(`https://api.sendgrid.com/v3/asm/groups/${asmId}/suppressions`, {
|
|
181
|
+
method: 'POST',
|
|
182
|
+
response: 'json',
|
|
183
|
+
headers: { 'Authorization': `Bearer ${env.get('SENDGRID_API_KEY')}` },
|
|
184
|
+
timeout: 60000,
|
|
185
|
+
body: { recipient_emails: [email] },
|
|
186
|
+
});
|
|
187
|
+
} else {
|
|
188
|
+
// DELETE returns 204 with empty body — must NOT request response: 'json'
|
|
189
|
+
// or wonderful-fetch throws SyntaxError on the empty body.
|
|
190
|
+
await fetch(`https://api.sendgrid.com/v3/asm/groups/${asmId}/suppressions/${encodeURIComponent(email)}`, {
|
|
191
|
+
method: 'DELETE',
|
|
192
|
+
response: 'text',
|
|
193
|
+
headers: { 'Authorization': `Bearer ${env.get('SENDGRID_API_KEY')}` },
|
|
194
|
+
timeout: 60000,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
} catch (e) {
|
|
198
|
+
ctx.log(`SendGrid ASM ${action} error:`, e);
|
|
199
|
+
return ctx.respond('Failed to process your request', { code: 500 });
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Cross-provider sync via the email library (best-effort — ASM suppression above
|
|
203
|
+
// already succeeded). Unsubscribe must remove the contact from Beehiiv too
|
|
204
|
+
// (compliance); subscribe must actually re-add the contact: sync the matched user
|
|
205
|
+
// (the mirror above already wrote consent granted, so the library consent gate
|
|
206
|
+
// passes) or add a bare newsletter contact when no user matched.
|
|
207
|
+
const mailer = Manager.Email(ctx);
|
|
208
|
+
|
|
209
|
+
try {
|
|
210
|
+
if (action === 'unsubscribe') {
|
|
211
|
+
await mailer.remove(email);
|
|
212
|
+
} else if (uid) {
|
|
213
|
+
await mailer.sync(uid);
|
|
214
|
+
} else {
|
|
215
|
+
await mailer.add({ email, source: 'resubscribe' });
|
|
216
|
+
}
|
|
217
|
+
} catch (e) {
|
|
218
|
+
ctx.error(`email-preferences (anon) provider sync failed:`, e);
|
|
219
|
+
// Doc + ASM are already updated — provider sync is best-effort. Don't fail the request.
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
ctx.log('email-preferences (anon) result:', { email, asmId, action });
|
|
223
|
+
analytics.event('marketing/email-preferences', { action, mode: 'anonymous' });
|
|
224
|
+
|
|
225
|
+
return ctx.respond({ success: true });
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Anonymous HMAC unsub also writes to the user doc (if found) so consent stays in sync.
|
|
230
|
+
* Silent if the email doesn't map to a user. Source is recorded as 'sendgrid' since the
|
|
231
|
+
* HMAC link only fires from SendGrid email footers.
|
|
232
|
+
*
|
|
233
|
+
* @returns {Promise<string|null>} The matched user's uid, or null when no user matches.
|
|
234
|
+
*/
|
|
235
|
+
async function mirrorAnonymousToUserDoc({ ctx, Manager, email, action }) {
|
|
236
|
+
const { admin } = Manager.libraries;
|
|
237
|
+
|
|
238
|
+
const snapshot = await admin.firestore().collection('users')
|
|
239
|
+
.where('auth.email', '==', email)
|
|
240
|
+
.limit(1)
|
|
241
|
+
.get()
|
|
242
|
+
.catch((e) => {
|
|
243
|
+
ctx.error('email-preferences (anon): Failed to look up user by email:', e);
|
|
244
|
+
return null;
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
if (!snapshot || snapshot.empty) {
|
|
248
|
+
return null; // Silent — email may not map to a current user
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const userDoc = snapshot.docs[0];
|
|
252
|
+
const uid = userDoc.id;
|
|
253
|
+
const timestamp = ctx.meta.startTime.timestamp;
|
|
254
|
+
const timestampUNIX = ctx.meta.startTime.timestampUNIX;
|
|
255
|
+
|
|
256
|
+
const marketingPatch = action === 'unsubscribe'
|
|
257
|
+
? {
|
|
258
|
+
status: 'revoked',
|
|
259
|
+
revokedAt: { timestamp, timestampUNIX, source: 'sendgrid', ip: null, text: null },
|
|
260
|
+
}
|
|
261
|
+
: {
|
|
262
|
+
status: 'granted',
|
|
263
|
+
grantedAt: { timestamp, timestampUNIX, source: 'sendgrid', ip: null, text: null },
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
await admin.firestore().doc(`users/${uid}`).set({
|
|
267
|
+
consent: { marketing: marketingPatch },
|
|
268
|
+
metadata: Manager.Metadata().set({ tag: 'marketing/email-preferences' }),
|
|
269
|
+
}, { merge: true })
|
|
270
|
+
.catch((e) => {
|
|
271
|
+
ctx.error(`email-preferences (anon): Failed to mirror to user doc ${uid}:`, e);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
return uid;
|
|
275
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POST /marketing/webhook/forward?provider=sendgrid|beehiiv&key=<OMEGA_WEBHOOK_KEY>
|
|
3
|
+
*
|
|
4
|
+
* Parent-only forwarder. SendGrid and Beehiiv send webhooks to this single URL
|
|
5
|
+
* on the parent @omega.js/backend. The parent reads its `brands` collection and re-POSTs the
|
|
6
|
+
* raw body to every child's /marketing/webhook?provider=X. Each child then
|
|
7
|
+
* processes the event against its own Firestore and providers.
|
|
8
|
+
*
|
|
9
|
+
* Gating:
|
|
10
|
+
* - Only enabled when Manager.config.parent === 'self'. Any other value (a URL
|
|
11
|
+
* pointing TO the parent, the typical setup for child BEMs) returns 404.
|
|
12
|
+
* - Same OMEGA_WEBHOOK_KEY is shared across all brands, so the
|
|
13
|
+
* parent forwards the key it received (already validated) when calling
|
|
14
|
+
* each child.
|
|
15
|
+
*
|
|
16
|
+
* Brand URL derivation:
|
|
17
|
+
* - Each brand doc in the `brands` collection has `brand.url` (e.g. 'https://somiibo.com').
|
|
18
|
+
* - API URL is derived by inserting 'api.' subdomain: 'https://api.somiibo.com'.
|
|
19
|
+
* - Child receivers live at `/omega/marketing/webhook` on that host.
|
|
20
|
+
*
|
|
21
|
+
* Self-inclusion:
|
|
22
|
+
* - The parent's own brand IS included in the fan-out. The parent @omega.js/backend has
|
|
23
|
+
* its own user base (e.g. itwcreativeworks.com users) and needs the same
|
|
24
|
+
* consent updates as any other brand. Self-fan-out goes via HTTP like
|
|
25
|
+
* every other child — no special inline path.
|
|
26
|
+
*
|
|
27
|
+
* Failure isolation:
|
|
28
|
+
* - Each child POST is awaited via Promise.allSettled so one slow/down child
|
|
29
|
+
* doesn't block the others. Failures are logged but the overall request
|
|
30
|
+
* still returns 200 so the provider doesn't retry the parent indefinitely.
|
|
31
|
+
* Children themselves track idempotency, so provider retries are safe.
|
|
32
|
+
*/
|
|
33
|
+
const fetch = require('wonderful-fetch');
|
|
34
|
+
const safeCompare = require('../../../../helpers/safe-compare.js');
|
|
35
|
+
const env = require('../../../../libraries/env.js');
|
|
36
|
+
|
|
37
|
+
const CHILD_TIMEOUT_MS = 10000;
|
|
38
|
+
|
|
39
|
+
module.exports = async ({ ctx, Manager, libraries }) => {
|
|
40
|
+
const { admin } = libraries;
|
|
41
|
+
const query = ctx.request.query;
|
|
42
|
+
|
|
43
|
+
// Gate: only the parent @omega.js/backend exposes this route. Any brand whose config.parent
|
|
44
|
+
// points to a URL (the normal case) returns 404 — pretend the route doesn't exist.
|
|
45
|
+
if (!Manager.isParent()) {
|
|
46
|
+
return ctx.respond('Not found', { code: 404 });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const provider = query.provider;
|
|
50
|
+
const key = query.key;
|
|
51
|
+
|
|
52
|
+
if (!provider) {
|
|
53
|
+
return ctx.respond('Missing provider parameter', { code: 400 });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Same key used for the receiver — parent validates incoming, then re-uses
|
|
57
|
+
// it for outbound calls to children (all brands share this env value).
|
|
58
|
+
if (!safeCompare(key, env.get('OMEGA_WEBHOOK_KEY'))) {
|
|
59
|
+
return ctx.respond('Invalid key', { code: 401 });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Read the brands collection. This lives in the PARENT's Firestore.
|
|
63
|
+
const snapshot = await admin.firestore().collection('brands').get()
|
|
64
|
+
.catch((e) => {
|
|
65
|
+
ctx.error('marketing webhook forward: failed to read brands collection:', e);
|
|
66
|
+
return null;
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
if (!snapshot) {
|
|
70
|
+
return ctx.respond('Failed to load brands', { code: 500 });
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Collect brand URLs from the docs
|
|
74
|
+
const brands = [];
|
|
75
|
+
snapshot.forEach((doc) => {
|
|
76
|
+
const data = doc.data() || {};
|
|
77
|
+
const brandUrl = data.brand?.url || null;
|
|
78
|
+
const brandId = data.brand?.id || doc.id;
|
|
79
|
+
|
|
80
|
+
if (!brandUrl) {
|
|
81
|
+
ctx.log(`marketing webhook forward: brand ${brandId} has no brand.url, skipping`);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
brands.push({ brandId, brandUrl });
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
if (brands.length === 0) {
|
|
89
|
+
ctx.log('marketing webhook forward: no brands to forward to');
|
|
90
|
+
return ctx.respond({ received: true, forwarded: 0 });
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
ctx.log(`marketing webhook forward: fanning out ${provider} event to ${brands.length} brand(s)`);
|
|
94
|
+
|
|
95
|
+
// Forward the raw body to every child. ctx.ref.req.body holds the body
|
|
96
|
+
// as we received it from the provider. We re-POST it without modification.
|
|
97
|
+
const rawBody = ctx.ref.req?.body;
|
|
98
|
+
|
|
99
|
+
const results = await Promise.allSettled(
|
|
100
|
+
brands.map(({ brandId, brandUrl }) => forwardToChild({
|
|
101
|
+
ctx,
|
|
102
|
+
brandId,
|
|
103
|
+
brandUrl,
|
|
104
|
+
provider,
|
|
105
|
+
key,
|
|
106
|
+
body: rawBody,
|
|
107
|
+
}))
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
let succeeded = 0;
|
|
111
|
+
let failed = 0;
|
|
112
|
+
const failures = [];
|
|
113
|
+
for (let i = 0; i < results.length; i += 1) {
|
|
114
|
+
const r = results[i];
|
|
115
|
+
const brand = brands[i];
|
|
116
|
+
if (r.status === 'fulfilled' && r.value?.ok) {
|
|
117
|
+
succeeded += 1;
|
|
118
|
+
} else {
|
|
119
|
+
failed += 1;
|
|
120
|
+
const reason = r.status === 'rejected' ? r.reason?.message : (r.value?.error || 'unknown');
|
|
121
|
+
failures.push({ brandId: brand.brandId, reason });
|
|
122
|
+
ctx.error(`marketing webhook forward: ${brand.brandId} failed:`, reason);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
ctx.log(`marketing webhook forward: ${provider} complete — succeeded=${succeeded}, failed=${failed}`);
|
|
127
|
+
|
|
128
|
+
// Always return 200 — child failures shouldn't make the provider retry the
|
|
129
|
+
// parent. Each child tracks its own idempotency so safe to re-fan on retry.
|
|
130
|
+
return ctx.respond({
|
|
131
|
+
received: true,
|
|
132
|
+
forwarded: brands.length,
|
|
133
|
+
succeeded,
|
|
134
|
+
failed,
|
|
135
|
+
failures: failures.length > 0 ? failures : undefined,
|
|
136
|
+
});
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* POST the raw body to one child @omega.js/backend's /marketing/webhook receiver.
|
|
141
|
+
* Returns { ok: true } on success, { ok: false, error } on failure.
|
|
142
|
+
*/
|
|
143
|
+
async function forwardToChild({ ctx, brandId, brandUrl, provider, key, body }) {
|
|
144
|
+
// Derive API URL: brandUrl 'https://somiibo.com' → 'https://api.somiibo.com'.
|
|
145
|
+
// Use URL parsing so we tolerate trailing slashes and unusual hosts.
|
|
146
|
+
let apiUrl;
|
|
147
|
+
try {
|
|
148
|
+
const url = new URL(brandUrl);
|
|
149
|
+
url.hostname = `api.${url.hostname}`;
|
|
150
|
+
url.pathname = '/omega/marketing/webhook';
|
|
151
|
+
url.search = `?provider=${encodeURIComponent(provider)}&key=${encodeURIComponent(key)}`;
|
|
152
|
+
apiUrl = url.toString();
|
|
153
|
+
} catch (e) {
|
|
154
|
+
return { ok: false, error: `Invalid brand URL "${brandUrl}": ${e.message}` };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
try {
|
|
158
|
+
const result = await fetch(apiUrl, {
|
|
159
|
+
method: 'POST',
|
|
160
|
+
response: 'json',
|
|
161
|
+
timeout: CHILD_TIMEOUT_MS,
|
|
162
|
+
headers: { 'Content-Type': 'application/json' },
|
|
163
|
+
body,
|
|
164
|
+
});
|
|
165
|
+
ctx.log(`marketing webhook forward: ${brandId} OK — ${JSON.stringify(result)}`);
|
|
166
|
+
return { ok: true };
|
|
167
|
+
} catch (e) {
|
|
168
|
+
return { ok: false, error: e?.message || String(e) };
|
|
169
|
+
}
|
|
170
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POST /marketing/webhook?provider=sendgrid|beehiiv&key=<OMEGA_WEBHOOK_KEY>
|
|
3
|
+
*
|
|
4
|
+
* Receives cross-provider unsubscribe webhooks (SendGrid + Beehiiv) and:
|
|
5
|
+
* 1. Authenticates via ?key= query param (OMEGA_WEBHOOK_KEY env)
|
|
6
|
+
* 2. Optionally rejects mismatched brand via ?brand= filter
|
|
7
|
+
* 3. Loads the matching provider module from ./providers/{provider}.js
|
|
8
|
+
* 4. Parses the webhook payload into one or more normalized events
|
|
9
|
+
* 5. For each supported event: dispatch to the provider's handler
|
|
10
|
+
* 6. Returns 200 immediately so the provider doesn't retry
|
|
11
|
+
*
|
|
12
|
+
* Each provider module defines:
|
|
13
|
+
* - parseWebhook(req) — returns Array<{ eventId, eventType, email, timestamp, raw, ... }>
|
|
14
|
+
* - isSupported(parsed) — returns true if this parsed event should be processed
|
|
15
|
+
* - handleEvent(ctx) — does the work for one event (user doc + cross-provider sync)
|
|
16
|
+
*
|
|
17
|
+
* No idempotency ledger: every supported handler (revoke consent + cross-provider
|
|
18
|
+
* remove) is naturally idempotent — re-processing a provider retry produces the same
|
|
19
|
+
* end state with no extra side effects, so duplicate suppression buys nothing.
|
|
20
|
+
*/
|
|
21
|
+
const path = require('path');
|
|
22
|
+
const loadProvider = require('../../../libraries/load-provider.js');
|
|
23
|
+
const safeCompare = require('../../../helpers/safe-compare.js');
|
|
24
|
+
const env = require('../../../libraries/env.js');
|
|
25
|
+
|
|
26
|
+
module.exports = async ({ ctx, Manager }) => {
|
|
27
|
+
const query = ctx.request.query;
|
|
28
|
+
|
|
29
|
+
const provider = query.provider;
|
|
30
|
+
const key = query.key;
|
|
31
|
+
|
|
32
|
+
// Validate provider
|
|
33
|
+
if (!provider) {
|
|
34
|
+
return ctx.respond('Missing provider parameter', { code: 400 });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Validate key against OMEGA_WEBHOOK_KEY (separate from OMEGA_ADMIN_KEY
|
|
38
|
+
// so it can be rotated independently and scoped narrowly)
|
|
39
|
+
if (!safeCompare(key, env.get('OMEGA_WEBHOOK_KEY'))) {
|
|
40
|
+
return ctx.respond('Invalid key', { code: 401 });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Brand filter (defensive — mirror payments webhook pattern). If a brand is
|
|
44
|
+
// specified and doesn't match ours, silently ignore. This lets one webhook
|
|
45
|
+
// URL be shared across brands while each brand only processes its own events.
|
|
46
|
+
const brand = query.brand;
|
|
47
|
+
const ourBrand = Manager.config.brand?.id;
|
|
48
|
+
if (brand && ourBrand && brand !== ourBrand) {
|
|
49
|
+
ctx.log(`marketing webhook: brand mismatch (received=${brand}, expected=${ourBrand}), ignoring`);
|
|
50
|
+
return ctx.respond({ received: true, ignored: true });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Load the provider module
|
|
54
|
+
let providerModule;
|
|
55
|
+
try {
|
|
56
|
+
providerModule = loadProvider(path.join(__dirname, 'providers'), provider);
|
|
57
|
+
} catch (e) {
|
|
58
|
+
ctx.error(`marketing webhook: failed to load provider "${provider}":`, e);
|
|
59
|
+
return ctx.respond(`Unknown provider: ${provider}`, { code: 400 });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Parse the webhook body into events
|
|
63
|
+
let events;
|
|
64
|
+
try {
|
|
65
|
+
events = providerModule.parseWebhook(ctx.ref.req);
|
|
66
|
+
} catch (e) {
|
|
67
|
+
ctx.error(`marketing webhook: parse failed for ${provider}:`, e);
|
|
68
|
+
return ctx.respond(`Failed to parse webhook: ${e.message}`, { code: 400 });
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (!Array.isArray(events) || events.length === 0) {
|
|
72
|
+
ctx.log(`marketing webhook: ${provider} returned no events`);
|
|
73
|
+
return ctx.respond({ received: true, processed: 0 });
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
ctx.log(`marketing webhook: ${provider} delivered ${events.length} event(s)`);
|
|
77
|
+
|
|
78
|
+
// Process each event independently — one failure shouldn't block the others.
|
|
79
|
+
// Use Promise.allSettled so we return success only after all events have been
|
|
80
|
+
// attempted.
|
|
81
|
+
const results = await Promise.allSettled(
|
|
82
|
+
events.map((event) => processOneEvent({ Manager, ctx, provider, event, providerModule }))
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
let processed = 0;
|
|
86
|
+
let skipped = 0;
|
|
87
|
+
let failed = 0;
|
|
88
|
+
for (const r of results) {
|
|
89
|
+
if (r.status === 'fulfilled') {
|
|
90
|
+
if (r.value?.processed) processed++;
|
|
91
|
+
else skipped++;
|
|
92
|
+
} else {
|
|
93
|
+
failed++;
|
|
94
|
+
ctx.error('marketing webhook: event processing rejected:', r.reason);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
ctx.log(`marketing webhook: ${provider} complete — processed=${processed}, skipped=${skipped}, failed=${failed}`);
|
|
99
|
+
|
|
100
|
+
return ctx.respond({ received: true, processed, skipped, failed });
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Process a single event: support check, then dispatch to the provider's handler.
|
|
105
|
+
* Handlers are idempotent, so provider retries re-run safely with no dedup ledger.
|
|
106
|
+
* Returns { processed: bool, skipped?: string, error?: any }.
|
|
107
|
+
*/
|
|
108
|
+
async function processOneEvent({ Manager, ctx, provider, event, providerModule }) {
|
|
109
|
+
const { eventType } = event;
|
|
110
|
+
|
|
111
|
+
// Filter by supported event types
|
|
112
|
+
if (providerModule.isSupported && !providerModule.isSupported(event)) {
|
|
113
|
+
return { processed: false, skipped: 'unsupported-event-type' };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
await providerModule.handleEvent({ Manager, ctx, parsed: event });
|
|
118
|
+
return { processed: true };
|
|
119
|
+
} catch (e) {
|
|
120
|
+
ctx.error(`marketing webhook: handler failed for ${provider} event ${event.eventId} (${eventType}):`, e);
|
|
121
|
+
return { processed: false, error: e };
|
|
122
|
+
}
|
|
123
|
+
}
|