@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,662 @@
|
|
|
1
|
+
const moment = require('moment');
|
|
2
|
+
const _ = require('lodash');
|
|
3
|
+
const { inferContact } = require('../../../libraries/infer-contact.js');
|
|
4
|
+
const { trackSignup } = require('../../../libraries/analytics/signup.js');
|
|
5
|
+
const { validate: validateEmail, isDisposable, ALL_CHECKS } = require('../../../libraries/email/validation.js');
|
|
6
|
+
const prepare = require('../../../libraries/email/prepare.js');
|
|
7
|
+
|
|
8
|
+
const MAX_POLL_TIME_MS = 30000;
|
|
9
|
+
const POLL_INTERVAL_MS = 500;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* POST /user/signup - Complete user signup
|
|
13
|
+
*
|
|
14
|
+
* Called by client after account creation to:
|
|
15
|
+
* 1. Poll for user doc to exist (waits for onCreate to complete)
|
|
16
|
+
* 2. Validate (reject only if flags.signupProcessed is already true)
|
|
17
|
+
* 3. Gather all data (client details, inferred contact)
|
|
18
|
+
* 4. Write everything to user doc in one merge
|
|
19
|
+
* 5. Fire the server half of sign_up (the one post-auth request that has the match data)
|
|
20
|
+
* 6. Process affiliate referral (writes to referrer's doc)
|
|
21
|
+
* 7. Send welcome emails + add to marketing lists (non-blocking)
|
|
22
|
+
*/
|
|
23
|
+
module.exports = async ({ ctx, user, settings, libraries }) => {
|
|
24
|
+
const { admin } = libraries;
|
|
25
|
+
|
|
26
|
+
// Require authentication
|
|
27
|
+
if (!user.authenticated) {
|
|
28
|
+
return ctx.respond('Authentication required', { code: 401 });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Get target UID
|
|
32
|
+
const uid = settings.uid;
|
|
33
|
+
|
|
34
|
+
// Require admin to signup other users
|
|
35
|
+
if (uid !== user.auth.uid && !user.roles.admin) {
|
|
36
|
+
return ctx.respond('Admin required', { code: 403 });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
ctx.log(`signup(): Starting for ${uid}, settings keys=${keyNames(settings)}`);
|
|
40
|
+
|
|
41
|
+
// 1. Poll for user doc to exist (wait for onCreate to complete)
|
|
42
|
+
const userDoc = await pollForUserDoc(ctx, uid);
|
|
43
|
+
|
|
44
|
+
if (!userDoc) {
|
|
45
|
+
return ctx.respond('User document not found after waiting. Please try again.', { code: 500 });
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
ctx.log(`signup(): User doc found for ${uid}`);
|
|
49
|
+
|
|
50
|
+
// 2. Check if signup has already been processed
|
|
51
|
+
if (userDoc.flags?.signupProcessed) {
|
|
52
|
+
return ctx.respond('Signup has already been processed', { code: 400 });
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// 3. Fetch the Auth user — needed for the canonical creationTime used to stamp
|
|
56
|
+
// metadata.created and consent timestamps. flags.signupProcessed (checked above) is
|
|
57
|
+
// the sole idempotency gate; there is intentionally no account-age window, so a
|
|
58
|
+
// legitimately-unprocessed account can complete signup whenever it retries.
|
|
59
|
+
const authUser = await admin.auth().getUser(uid).catch((e) => e);
|
|
60
|
+
|
|
61
|
+
if (authUser instanceof Error) {
|
|
62
|
+
return ctx.respond(`Failed to get auth user: ${authUser.message}`, { code: 500 });
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// 4. Gather all data, then write once
|
|
66
|
+
const email = user.auth.email;
|
|
67
|
+
const inferred = await inferUserContact(ctx, email);
|
|
68
|
+
ctx.log(`signup(): inferUserContact returned for ${email}:`, inferred);
|
|
69
|
+
const userRecord = buildUserRecord(ctx, {
|
|
70
|
+
settings,
|
|
71
|
+
inferred,
|
|
72
|
+
uid,
|
|
73
|
+
email,
|
|
74
|
+
creationTime: authUser.metadata.creationTime,
|
|
75
|
+
existingDoc: userDoc,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
ctx.log(`signup(): Writing user record for ${uid}, keys=${keyNames(userRecord)}`);
|
|
79
|
+
|
|
80
|
+
await admin.firestore().doc(`users/${uid}`)
|
|
81
|
+
.set(userRecord, { merge: true });
|
|
82
|
+
|
|
83
|
+
// 5. The SERVER half of `sign_up` (non-blocking)
|
|
84
|
+
//
|
|
85
|
+
// THIS is the request that half was waiting for
|
|
86
|
+
// ([#577](https://github.com/Omega-JS-Stack/omega/issues/577)): the auth
|
|
87
|
+
// trigger it used to fire from has no HTTP request behind it, so it reached
|
|
88
|
+
// Meta and TikTok with no IP, no user agent, no `_fbc`/`_fbp`/`_ttp` and an
|
|
89
|
+
// attribution the browser had not posted yet. Everything it lacked is here —
|
|
90
|
+
// the request's own pair, and the cookies the client sent inside
|
|
91
|
+
// `attribution` (the checkout intent's shape). `flags.signupProcessed`, which
|
|
92
|
+
// this route already refuses a second time, is the once-per-account gate, and
|
|
93
|
+
// the doc it reads was written a line ago, so there is nothing to race.
|
|
94
|
+
trackSignup({
|
|
95
|
+
Manager: ctx.Manager,
|
|
96
|
+
ctx: ctx,
|
|
97
|
+
authUser: authUser,
|
|
98
|
+
userRecord: userRecord,
|
|
99
|
+
request: {
|
|
100
|
+
ip: ctx.request.geolocation?.ip || null,
|
|
101
|
+
userAgent: ctx.request.client?.userAgent || null,
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// 6. Process affiliate referral (writes to referrer's doc, not this user's)
|
|
106
|
+
await processAffiliate(ctx, uid, email, settings);
|
|
107
|
+
|
|
108
|
+
// 7. Send emails + marketing (awaited so the function stays alive)
|
|
109
|
+
// They send INLINE in this request, and that costs the user nothing: NOTHING
|
|
110
|
+
// client-side awaits this route — the web auth listener fires the POST and
|
|
111
|
+
// moves straight on to the redirect
|
|
112
|
+
// ([#700](https://github.com/Omega-JS-Stack/omega/issues/700)).
|
|
113
|
+
// Gate marketing sync on explicit consent — never add a user to marketing lists without it
|
|
114
|
+
if (userRecord.consent?.marketing?.status === 'granted') {
|
|
115
|
+
await syncMarketingContact(ctx, uid, email);
|
|
116
|
+
} else {
|
|
117
|
+
ctx.log(`signup(): Skipping marketing sync — consent.marketing.status is "${userRecord.consent?.marketing?.status}"`);
|
|
118
|
+
}
|
|
119
|
+
await sendWelcomeEmails(ctx, uid, inferred?.firstName);
|
|
120
|
+
|
|
121
|
+
return ctx.respond({ signedUp: true });
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Poll for user doc to exist (wait for onCreate to complete)
|
|
126
|
+
*/
|
|
127
|
+
async function pollForUserDoc(ctx, uid) {
|
|
128
|
+
const { admin } = ctx.Manager.libraries;
|
|
129
|
+
const startTime = Date.now();
|
|
130
|
+
|
|
131
|
+
while (Date.now() - startTime < MAX_POLL_TIME_MS) {
|
|
132
|
+
const doc = await admin.firestore().doc(`users/${uid}`)
|
|
133
|
+
.get()
|
|
134
|
+
.catch((e) => {
|
|
135
|
+
ctx.error(`pollForUserDoc(): Error fetching doc:`, e);
|
|
136
|
+
return null;
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
if (doc && doc.exists && doc.data()?.auth?.uid) {
|
|
140
|
+
return doc.data();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
ctx.log(`pollForUserDoc(): Waiting for user doc ${uid}...`);
|
|
144
|
+
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
ctx.error(`pollForUserDoc(): Timeout waiting for user doc ${uid}`);
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* The top-level key names of a payload, for a log line that must not carry the payload.
|
|
153
|
+
*
|
|
154
|
+
* The incoming settings and the record signup writes are both made of the fields a user
|
|
155
|
+
* document is made of — the api keys buildUserRecord() deliberately preserves, the consent
|
|
156
|
+
* records, the request IP, the attribution — and a backend line lands in Cloud Logging for
|
|
157
|
+
* the whole retention window ([#632](https://github.com/Omega-JS-Stack/omega/issues/632)).
|
|
158
|
+
* The SHAPE is what a log reader needs; the values belong at `ctx.debug` or nowhere.
|
|
159
|
+
*
|
|
160
|
+
* @param {*} value - Any payload (a non-object reads as nothing)
|
|
161
|
+
* @returns {string} Comma-joined top-level key names, or '(none)'
|
|
162
|
+
*/
|
|
163
|
+
function keyNames(value) {
|
|
164
|
+
const keys = value && typeof value === 'object' ? Object.keys(value) : [];
|
|
165
|
+
|
|
166
|
+
return keys.length ? keys.join(', ') : '(none)';
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Build the complete user record to write at signup completion.
|
|
171
|
+
*
|
|
172
|
+
* Returns the WHOLE merged document (written without {merge}), layered deepest-first:
|
|
173
|
+
* 1. Manager.User() full schema shape — guarantees every leaf exists (so a doc created by a
|
|
174
|
+
* partial path, e.g. onCreate never firing, still ends up schema-complete).
|
|
175
|
+
* 2. the existing doc — real values win over the schema defaults, so we never clobber the
|
|
176
|
+
* user's api keys, subscription, roles, affiliate.code, or any custom/non-standard fields.
|
|
177
|
+
* 3. the signup data — attribution / trackingConsent / activity / consent / flags / personal
|
|
178
|
+
* we own at signup land on top.
|
|
179
|
+
*
|
|
180
|
+
* Why a full deep-merge instead of `.set(partial, {merge:true})`: Firestore's merge REPLACES a
|
|
181
|
+
* map field rather than deep-merging it, so writing a partial `attribution` flattened onCreate's
|
|
182
|
+
* full attribution object and the OMEGA migration had to re-add every leaf on every signup.
|
|
183
|
+
* Deep-merging in JS and writing the whole doc avoids that entirely.
|
|
184
|
+
*/
|
|
185
|
+
function buildUserRecord(ctx, { settings, inferred, uid, email, creationTime, existingDoc }) {
|
|
186
|
+
const Manager = ctx.Manager;
|
|
187
|
+
|
|
188
|
+
// The resolved geolocation: the request's own headers over whatever the client sent.
|
|
189
|
+
const geolocation = {
|
|
190
|
+
...(settings.context?.geolocation || {}),
|
|
191
|
+
...ctx.request.geolocation,
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
// Inferred name/company (from AI/regex on the email) — only set when present.
|
|
195
|
+
const personal = {};
|
|
196
|
+
if (inferred?.firstName || inferred?.lastName) {
|
|
197
|
+
personal.name = {
|
|
198
|
+
...(inferred.firstName ? { first: inferred.firstName } : {}),
|
|
199
|
+
...(inferred.lastName ? { last: inferred.lastName } : {}),
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
if (inferred?.company) {
|
|
203
|
+
personal.company = { name: inferred.company };
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Location from the request geolocation — only the fields nobody has filled in yet.
|
|
207
|
+
const location = locationFromGeolocation(geolocation, {
|
|
208
|
+
...(existingDoc?.personal?.location || {}),
|
|
209
|
+
...(settings.personal?.location || {}),
|
|
210
|
+
});
|
|
211
|
+
if (Object.keys(location).length) {
|
|
212
|
+
personal.location = location;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Layer 1: full schema shape (every leaf present with defaults).
|
|
216
|
+
const schemaShape = Manager.User({ auth: { uid, email } }).properties;
|
|
217
|
+
|
|
218
|
+
// Layer 3: the data signup owns.
|
|
219
|
+
const signupData = {
|
|
220
|
+
auth: { uid, email },
|
|
221
|
+
flags: { signupProcessed: true },
|
|
222
|
+
activity: {
|
|
223
|
+
...settings.context,
|
|
224
|
+
geolocation: geolocation,
|
|
225
|
+
client: {
|
|
226
|
+
...ctx.request.client,
|
|
227
|
+
...(settings.context?.client || {}),
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
attribution: settings.attribution || {},
|
|
231
|
+
trackingConsent: settings.trackingConsent || null,
|
|
232
|
+
consent: buildConsentRecord(ctx, settings.consent, creationTime, existingDoc?.consent),
|
|
233
|
+
metadata: Manager.Metadata().set({ tag: 'user/signup' }),
|
|
234
|
+
...(Object.keys(personal).length ? { personal } : {}),
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
// metadata.created from Auth's creationTime (canonical), matching onCreate + the migration SSOT.
|
|
238
|
+
if (creationTime) {
|
|
239
|
+
const createdDate = new Date(creationTime);
|
|
240
|
+
signupData.metadata.created = {
|
|
241
|
+
timestamp: createdDate.toISOString(),
|
|
242
|
+
timestampUNIX: Math.round(createdDate.getTime() / 1000),
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Deep-merge: schema (base) ← existing doc (real values win) ← signup data (owned fields win).
|
|
247
|
+
// _.merge mutates its first arg, so start from a fresh object.
|
|
248
|
+
return _.merge({}, schemaShape, existingDoc || {}, signupData);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* The `personal.location` fields the request's geolocation can fill.
|
|
253
|
+
*
|
|
254
|
+
* The header-derived country/region/city landed in `activity.geolocation` and nowhere else, so
|
|
255
|
+
* `personal.location` — the address half of the ad-platform match keys
|
|
256
|
+
* (`libraries/analytics/match-data.js`) and the email merge fields — stayed empty on every
|
|
257
|
+
* account ([#638](https://github.com/Omega-JS-Stack/omega/issues/638)).
|
|
258
|
+
*
|
|
259
|
+
* The fill is per-field and never overwrites: an IP guess loses to anything the user set, in the
|
|
260
|
+
* incoming settings or in the existing doc. Nothing to fill returns an EMPTY object, so the
|
|
261
|
+
* write carries no `personal.location` key at all rather than a map of nulls.
|
|
262
|
+
*/
|
|
263
|
+
function locationFromGeolocation(geolocation, existingLocation) {
|
|
264
|
+
const location = {};
|
|
265
|
+
|
|
266
|
+
for (const field of ['country', 'region', 'city']) {
|
|
267
|
+
if (existingLocation?.[field] || !geolocation?.[field]) {
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
location[field] = geolocation[field];
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return location;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Translate the client's lightweight consent payload into the canonical user-doc shape.
|
|
279
|
+
*
|
|
280
|
+
* Client sends: { legal: { granted, text }, marketing: { granted, text } }
|
|
281
|
+
* Server writes: { legal: { status, grantedAt: {...} }, marketing: { status, grantedAt: {...}, revokedAt: {...} } }
|
|
282
|
+
*
|
|
283
|
+
* Server-derived time (not client-supplied) is authoritative — defends against clock
|
|
284
|
+
* manipulation. Uses Auth's creationTime so consent timestamps match metadata.created.
|
|
285
|
+
* IP is captured from request geolocation.
|
|
286
|
+
*
|
|
287
|
+
* Legal is REQUIRED — the client must send legal.granted=true. If missing/false we still
|
|
288
|
+
* record what the client sent, but the route will not have reached this point in practice
|
|
289
|
+
* (the signup-form HTML5-requires the legal checkbox).
|
|
290
|
+
*/
|
|
291
|
+
function buildConsentRecord(ctx, clientConsent, creationTime, existingConsent) {
|
|
292
|
+
const consent = clientConsent || {};
|
|
293
|
+
const ip = ctx.request.geolocation?.ip || null;
|
|
294
|
+
|
|
295
|
+
// Stamp grantedAt/revokedAt from Auth's creationTime so consent timestamps match
|
|
296
|
+
// metadata.created (the OMEGA migration treats metadata.created as the SSOT and reconciles
|
|
297
|
+
// consent.grantedAt against it). Fall back to request start time if creationTime is absent.
|
|
298
|
+
const createdDate = creationTime ? new Date(creationTime) : null;
|
|
299
|
+
const timestamp = createdDate ? createdDate.toISOString() : ctx.meta.startTime.timestamp;
|
|
300
|
+
const timestampUNIX = createdDate ? Math.round(createdDate.getTime() / 1000) : ctx.meta.startTime.timestampUNIX;
|
|
301
|
+
|
|
302
|
+
// Build empty leaf shape — used wherever grantedAt or revokedAt is "not set"
|
|
303
|
+
const emptyMeta = { timestamp: null, timestampUNIX: null, source: null, ip: null, text: null };
|
|
304
|
+
|
|
305
|
+
// --- Legal ---
|
|
306
|
+
const legalGranted = consent.legal?.granted === true;
|
|
307
|
+
const legalText = typeof consent.legal?.text === 'string' ? consent.legal.text : null;
|
|
308
|
+
|
|
309
|
+
let legal = legalGranted
|
|
310
|
+
? {
|
|
311
|
+
status: 'granted',
|
|
312
|
+
grantedAt: { timestamp, timestampUNIX, source: 'signup', ip, text: legalText },
|
|
313
|
+
}
|
|
314
|
+
: {
|
|
315
|
+
status: 'revoked',
|
|
316
|
+
grantedAt: { ...emptyMeta },
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
// --- Marketing ---
|
|
320
|
+
const marketingGranted = consent.marketing?.granted === true;
|
|
321
|
+
const marketingText = typeof consent.marketing?.text === 'string' ? consent.marketing.text : null;
|
|
322
|
+
|
|
323
|
+
let marketing = marketingGranted
|
|
324
|
+
? {
|
|
325
|
+
status: 'granted',
|
|
326
|
+
grantedAt: { timestamp, timestampUNIX, source: 'signup', ip, text: marketingText },
|
|
327
|
+
revokedAt: { ...emptyMeta },
|
|
328
|
+
}
|
|
329
|
+
: {
|
|
330
|
+
status: 'revoked',
|
|
331
|
+
grantedAt: { ...emptyMeta },
|
|
332
|
+
// Record the decline with source=signup-form-declined. text=null (no message shown).
|
|
333
|
+
revokedAt: { timestamp, timestampUNIX, source: 'signup', ip, text: null },
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
// Never DOWNGRADE an existing granted consent. A legacy account (signed up before this
|
|
337
|
+
// flow, flags.signupProcessed never set) re-fires /user/signup on page load with empty
|
|
338
|
+
// consent — which would compute status 'revoked' above and, on a {merge:true} write, wipe
|
|
339
|
+
// out the consent they actually granted months ago. If the existing doc already has a
|
|
340
|
+
// consent granted and the incoming payload doesn't explicitly re-grant it, preserve the
|
|
341
|
+
// existing record. A genuine new grant or an at-signup decline (no prior grant) still applies.
|
|
342
|
+
if (existingConsent?.legal?.status === 'granted' && legal.status !== 'granted') {
|
|
343
|
+
legal = existingConsent.legal;
|
|
344
|
+
}
|
|
345
|
+
if (existingConsent?.marketing?.status === 'granted' && marketing.status !== 'granted') {
|
|
346
|
+
marketing = existingConsent.marketing;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
ctx.log(`buildConsentRecord: legal=${legal.status}, marketing=${marketing.status} (raw input legal.granted=${consent.legal?.granted}, marketing.granted=${consent.marketing?.granted})`);
|
|
350
|
+
|
|
351
|
+
return { legal, marketing };
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Infer name/company from email using AI (or regex fallback)
|
|
356
|
+
* Returns the inferred contact info, or null on failure
|
|
357
|
+
*/
|
|
358
|
+
async function inferUserContact(ctx, email) {
|
|
359
|
+
try {
|
|
360
|
+
const inferred = await inferContact(email, ctx);
|
|
361
|
+
|
|
362
|
+
if (!inferred?.firstName && !inferred?.lastName && !inferred?.company) {
|
|
363
|
+
ctx.log(`signup(): inferUserContact returned empty result for ${email} (method=${inferred?.method || 'unknown'})`);
|
|
364
|
+
return null;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
ctx.log(`signup(): Inferred contact: ${inferred.firstName || ''} ${inferred.lastName || ''}, company=${inferred.company || ''} (method=${inferred.method})`);
|
|
368
|
+
|
|
369
|
+
return inferred;
|
|
370
|
+
} catch (e) {
|
|
371
|
+
ctx.error('signup(): Name inference failed:', e);
|
|
372
|
+
return null;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Process affiliate referral if affiliate code provided
|
|
378
|
+
* Writes to the referrer's doc (not the current user's)
|
|
379
|
+
*/
|
|
380
|
+
async function processAffiliate(ctx, uid, email, settings) {
|
|
381
|
+
const { admin } = ctx.Manager.libraries;
|
|
382
|
+
const affiliateCode = settings.attribution?.affiliate?.code || null;
|
|
383
|
+
|
|
384
|
+
if (!affiliateCode) {
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// Skip referral credit for disposable email signups (affiliate fraud prevention)
|
|
389
|
+
if (isDisposable(email)) {
|
|
390
|
+
ctx.log(`processAffiliate(): Skipping referral — disposable email ${email}`);
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
ctx.log(`processAffiliate(): Looking for referrer with code ${affiliateCode}`);
|
|
395
|
+
|
|
396
|
+
const snapshot = await admin.firestore().collection('users')
|
|
397
|
+
.where('affiliate.code', '==', affiliateCode)
|
|
398
|
+
.get()
|
|
399
|
+
.catch((e) => {
|
|
400
|
+
ctx.error(`processAffiliate(): Failed to find referrer:`, e);
|
|
401
|
+
throw e;
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
if (snapshot.empty) {
|
|
405
|
+
ctx.log(`processAffiliate(): No referrer found with code ${affiliateCode}`);
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Update the first matching referrer
|
|
410
|
+
const referrerDoc = snapshot.docs[0];
|
|
411
|
+
const referrerData = referrerDoc.data() || {};
|
|
412
|
+
|
|
413
|
+
let referrals = referrerData?.affiliate?.referrals || [];
|
|
414
|
+
referrals = Array.isArray(referrals) ? referrals : [];
|
|
415
|
+
|
|
416
|
+
referrals.push({
|
|
417
|
+
uid: uid,
|
|
418
|
+
timestamp: ctx.meta.startTime.timestamp,
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
ctx.log(`processAffiliate(): Appending referral to ${referrerDoc.id}`, referrals);
|
|
422
|
+
|
|
423
|
+
await admin.firestore().doc(`users/${referrerDoc.id}`)
|
|
424
|
+
.set({
|
|
425
|
+
affiliate: {
|
|
426
|
+
referrals: referrals,
|
|
427
|
+
},
|
|
428
|
+
}, { merge: true })
|
|
429
|
+
.then(() => {
|
|
430
|
+
ctx.log(`processAffiliate(): Success`);
|
|
431
|
+
})
|
|
432
|
+
.catch((e) => {
|
|
433
|
+
ctx.error(`processAffiliate(): Failed to update referrer:`, e);
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Sync marketing contact — validates email (including mailbox verification) before syncing to providers
|
|
439
|
+
*/
|
|
440
|
+
async function syncMarketingContact(ctx, uid, email) {
|
|
441
|
+
const Manager = ctx.Manager;
|
|
442
|
+
const shouldSend = !ctx.isTesting() || process.env.TEST_EXTENDED_MODE;
|
|
443
|
+
|
|
444
|
+
if (!shouldSend) {
|
|
445
|
+
ctx.log(`signup(): Skipping marketing sync (OMEGA_TEST_MODE=true, TEST_EXTENDED_MODE not set)`);
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// Validate email before adding to marketing lists (includes mailbox verification via NeverBounce/ZeroBounce)
|
|
450
|
+
const validation = await validateEmail(email, { checks: ALL_CHECKS });
|
|
451
|
+
|
|
452
|
+
if (!validation.valid) {
|
|
453
|
+
ctx.log(`signup(): Skipping marketing sync — email validation failed:`, validation.checks);
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
const mailer = Manager.Email(ctx);
|
|
458
|
+
|
|
459
|
+
try {
|
|
460
|
+
const result = await mailer.sync(uid);
|
|
461
|
+
ctx.log('signup(): Marketing sync:', result);
|
|
462
|
+
} catch (e) {
|
|
463
|
+
ctx.error('signup(): Marketing sync failed:', e);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Send welcome, checkup, and feedback emails
|
|
469
|
+
*
|
|
470
|
+
* No testing-mode gate here: the mailer's own seam captures a testing send instead
|
|
471
|
+
* of delivering it, so these run for real in every environment and a broken one
|
|
472
|
+
* fails a test ([#774](https://github.com/Omega-JS-Stack/omega/issues/774)).
|
|
473
|
+
*/
|
|
474
|
+
async function sendWelcomeEmails(ctx, uid, firstName) {
|
|
475
|
+
await Promise.all([
|
|
476
|
+
sendWelcomeEmail(ctx, uid, firstName).catch(e => ctx.error('signup(): sendWelcomeEmail failed:', e)),
|
|
477
|
+
sendDiscountNudgeEmail(ctx, uid, firstName).catch(e => ctx.error('signup(): sendDiscountNudgeEmail failed:', e)),
|
|
478
|
+
sendCheckupEmail(ctx, uid, firstName).catch(e => ctx.error('signup(): sendCheckupEmail failed:', e)),
|
|
479
|
+
sendFeedbackEmail(ctx, uid, firstName).catch(e => ctx.error('signup(): sendFeedbackEmail failed:', e)),
|
|
480
|
+
]);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Send welcome email (immediate)
|
|
485
|
+
*/
|
|
486
|
+
async function sendWelcomeEmail(ctx, uid, firstName) {
|
|
487
|
+
const Manager = ctx.Manager;
|
|
488
|
+
const mailer = Manager.Email(ctx);
|
|
489
|
+
const greeting = firstName ? `Hey ${firstName}, welcome` : 'Welcome';
|
|
490
|
+
// Throws when brand.contact.person is unconfigured — the send is individually
|
|
491
|
+
// caught + logged by sendWelcomeEmails(), so signup still succeeds.
|
|
492
|
+
const person = prepare.resolvePerson(Manager.config.brand);
|
|
493
|
+
|
|
494
|
+
return mailer.send({
|
|
495
|
+
to: uid,
|
|
496
|
+
sender: 'hello',
|
|
497
|
+
categories: ['account/welcome'],
|
|
498
|
+
subject: `Welcome to ${Manager.config.brand.name}!`,
|
|
499
|
+
template: 'card',
|
|
500
|
+
copy: false,
|
|
501
|
+
data: {
|
|
502
|
+
email: {
|
|
503
|
+
preview: `Welcome aboard! I'm ${person.firstName}, the CEO and founder of ${Manager.config.brand.name}. I'm here to ensure your journey with us gets off to a great start.`,
|
|
504
|
+
},
|
|
505
|
+
content: {
|
|
506
|
+
title: `Welcome to ${Manager.config.brand.name}!`,
|
|
507
|
+
message: `${greeting} aboard!
|
|
508
|
+
|
|
509
|
+
I'm ${person.firstName}, the founder and CEO of **${Manager.config.brand.name}**, and I'm thrilled to have you with us. Your journey begins today, and we are committed to supporting you every step of the way.
|
|
510
|
+
|
|
511
|
+
We are dedicated to ensuring your experience is exceptional. Feel free to reply directly to this email with any questions you may have.
|
|
512
|
+
|
|
513
|
+
Thank you for choosing **${Manager.config.brand.name}**. Here's to new beginnings!`,
|
|
514
|
+
},
|
|
515
|
+
// Identity comes from brand.contact.person via prepare.resolveSignoff().
|
|
516
|
+
signoff: {
|
|
517
|
+
type: 'personal',
|
|
518
|
+
},
|
|
519
|
+
},
|
|
520
|
+
})
|
|
521
|
+
.then((result) => {
|
|
522
|
+
ctx.log('sendWelcomeEmail(): Success', result.status);
|
|
523
|
+
return result;
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Send discount-nudge email (24 hours after signup)
|
|
529
|
+
*
|
|
530
|
+
* A warm, personal check-in that offers a discount code in exchange for a reply.
|
|
531
|
+
* Scheduled fire-and-forget via sendAt (same pattern as checkup/feedback) — there is
|
|
532
|
+
* intentionally no premium check at send time, so a user who upgrades within 24h may
|
|
533
|
+
* still receive it. The copy is deliberately worded as a friendly thank-you (not "you
|
|
534
|
+
* haven't upgraded") so it reads fine regardless of the recipient's current plan.
|
|
535
|
+
*
|
|
536
|
+
* The reply itself is the goal: replies are a strong positive sender-reputation signal,
|
|
537
|
+
* and a real human check-in lands in the Primary tab rather than Promotions. Inbound
|
|
538
|
+
* reply handling (auto-issuing the code) is out of scope here — replies are handled
|
|
539
|
+
* separately.
|
|
540
|
+
*
|
|
541
|
+
* Subject is personalized with the recipient's first name when available, and uses
|
|
542
|
+
* intrigue framing ("something for you 🎁") rather than spam-trigger words ("free",
|
|
543
|
+
* "claim", "bonus") to protect deliverability.
|
|
544
|
+
*/
|
|
545
|
+
async function sendDiscountNudgeEmail(ctx, uid, firstName) {
|
|
546
|
+
const Manager = ctx.Manager;
|
|
547
|
+
const mailer = Manager.Email(ctx);
|
|
548
|
+
const greeting = firstName ? `Hey ${firstName}` : 'Hey there';
|
|
549
|
+
const subject = firstName
|
|
550
|
+
? `${firstName}, I've got something for you 🎁`
|
|
551
|
+
: `I've got something for you 🎁`;
|
|
552
|
+
// Throws when brand.contact.person is unconfigured — caught per-send by the caller.
|
|
553
|
+
const person = prepare.resolvePerson(Manager.config.brand);
|
|
554
|
+
|
|
555
|
+
return mailer.send({
|
|
556
|
+
to: uid,
|
|
557
|
+
sender: 'hello',
|
|
558
|
+
categories: ['engagement/discount-nudge'],
|
|
559
|
+
subject: subject,
|
|
560
|
+
template: 'card',
|
|
561
|
+
copy: false,
|
|
562
|
+
sendAt: moment().add(24, 'hours').unix(),
|
|
563
|
+
data: {
|
|
564
|
+
email: {
|
|
565
|
+
preview: `Just checking in from ${Manager.config.brand.name} — and I've got a little thank-you for you.`,
|
|
566
|
+
},
|
|
567
|
+
content: {
|
|
568
|
+
title: `How's it going?`,
|
|
569
|
+
message: `${greeting},
|
|
570
|
+
|
|
571
|
+
It's ${person.firstName}, the founder of **${Manager.config.brand.name}**.
|
|
572
|
+
|
|
573
|
+
As a thank-you for giving us a try, I'd love to send you a code for a **premium upgrade**.
|
|
574
|
+
|
|
575
|
+
**Just reply to this email** and I'll get one over to you.
|
|
576
|
+
|
|
577
|
+
I read every reply and I'm looking forward to hearing from you!`,
|
|
578
|
+
},
|
|
579
|
+
// Identity comes from brand.contact.person via prepare.resolveSignoff().
|
|
580
|
+
signoff: {
|
|
581
|
+
type: 'personal',
|
|
582
|
+
},
|
|
583
|
+
},
|
|
584
|
+
})
|
|
585
|
+
.then((result) => {
|
|
586
|
+
ctx.log('sendDiscountNudgeEmail(): Success', result.status);
|
|
587
|
+
return result;
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* Send checkup email (7 days after signup)
|
|
593
|
+
*/
|
|
594
|
+
async function sendCheckupEmail(ctx, uid, firstName) {
|
|
595
|
+
const Manager = ctx.Manager;
|
|
596
|
+
const mailer = Manager.Email(ctx);
|
|
597
|
+
const greeting = firstName ? `Hey ${firstName}` : 'Hi there';
|
|
598
|
+
// Throws when brand.contact.person is unconfigured — caught per-send by the caller.
|
|
599
|
+
const person = prepare.resolvePerson(Manager.config.brand);
|
|
600
|
+
|
|
601
|
+
return mailer.send({
|
|
602
|
+
to: uid,
|
|
603
|
+
sender: 'hello',
|
|
604
|
+
categories: ['account/checkup'],
|
|
605
|
+
subject: `How is your experience with ${Manager.config.brand.name}?`,
|
|
606
|
+
template: 'card',
|
|
607
|
+
copy: false,
|
|
608
|
+
sendAt: moment().add(5, 'days').unix(),
|
|
609
|
+
data: {
|
|
610
|
+
email: {
|
|
611
|
+
preview: `Checking in from ${Manager.config.brand.name} to see how things are going. Let us know if you have any questions or feedback!`,
|
|
612
|
+
},
|
|
613
|
+
content: {
|
|
614
|
+
title: `How's everything going?`,
|
|
615
|
+
message: `${greeting},
|
|
616
|
+
|
|
617
|
+
It's ${person.firstName} again from **${Manager.config.brand.name}**. Just checking in to see how things are going for you.
|
|
618
|
+
|
|
619
|
+
Have you had a chance to explore all our features? Any questions or feedback for us?
|
|
620
|
+
|
|
621
|
+
We're always here to help, so don't hesitate to reach out. Just reply to this email and we'll get back to you as soon as possible.
|
|
622
|
+
|
|
623
|
+
Thank you for choosing **${Manager.config.brand.name}**. Here's to new beginnings!`,
|
|
624
|
+
},
|
|
625
|
+
// Identity comes from brand.contact.person via prepare.resolveSignoff().
|
|
626
|
+
signoff: {
|
|
627
|
+
type: 'personal',
|
|
628
|
+
},
|
|
629
|
+
},
|
|
630
|
+
})
|
|
631
|
+
.then((result) => {
|
|
632
|
+
ctx.log('sendCheckupEmail(): Success', result.status);
|
|
633
|
+
return result;
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
/**
|
|
638
|
+
* Send feedback email (10 days after signup)
|
|
639
|
+
*/
|
|
640
|
+
function sendFeedbackEmail(ctx, uid, firstName) {
|
|
641
|
+
const Manager = ctx.Manager;
|
|
642
|
+
const mailer = Manager.Email(ctx);
|
|
643
|
+
const first = firstName || 'You';
|
|
644
|
+
|
|
645
|
+
return mailer.send({
|
|
646
|
+
to: uid,
|
|
647
|
+
sender: 'hello',
|
|
648
|
+
categories: ['engagement/feedback'],
|
|
649
|
+
subject: `${first} + feedback = Amazon gift card 🎁`,
|
|
650
|
+
template: 'feedback',
|
|
651
|
+
copy: false,
|
|
652
|
+
sendAt: moment().add(10, 'days').unix(),
|
|
653
|
+
})
|
|
654
|
+
.then((result) => {
|
|
655
|
+
ctx.log('sendFeedbackEmail(): Success', result.status);
|
|
656
|
+
return result;
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
// Exported for unit tests (pure, no I/O).
|
|
661
|
+
module.exports.locationFromGeolocation = locationFromGeolocation;
|
|
662
|
+
module.exports.keyNames = keyNames;
|