@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,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test: Marketing Provider Lifecycle (end-to-end against live SendGrid + Beehiiv)
|
|
3
|
+
*
|
|
4
|
+
* Walks two long-lived test accounts through their full lifecycle and verifies
|
|
5
|
+
* provider state at every transition:
|
|
6
|
+
*
|
|
7
|
+
* 1. Pre-check — both accounts should be absent from SendGrid + Beehiiv
|
|
8
|
+
* 2. Sync — flip consent.marketing.status and call Marketing.sync()
|
|
9
|
+
* 3. Verify — granted account present in both, declined account absent from both
|
|
10
|
+
* 4. Unsubscribe — hit the email-preferences endpoint for the granted account
|
|
11
|
+
* 5. Verify — granted account now absent from both
|
|
12
|
+
*
|
|
13
|
+
* The two accounts use the `_test.allow_*` prefix so they bypass the
|
|
14
|
+
* blocked-local-patterns gate (which blocks plain `_test.*` from reaching
|
|
15
|
+
* providers). They are the only accounts intentionally allowed to round-trip
|
|
16
|
+
* through SendGrid + Beehiiv.
|
|
17
|
+
*
|
|
18
|
+
* Run with TEST_EXTENDED_MODE=true (no-op otherwise). Requires SENDGRID_API_KEY
|
|
19
|
+
* and BEEHIIV_API_KEY in env. Total runtime is ~60-90s — most of it spent waiting
|
|
20
|
+
* for SendGrid's async upsert/delete background jobs to surface.
|
|
21
|
+
*/
|
|
22
|
+
const sendgridProvider = require('../../dist/manager/libraries/email/providers/sendgrid.js');
|
|
23
|
+
const beehiivProvider = require('../../dist/manager/libraries/email/providers/beehiiv.js');
|
|
24
|
+
const defineCases = require('../../dist/vendor/devkit/test/define-cases.js');
|
|
25
|
+
|
|
26
|
+
const SETTLE_MS = 5000; // Beehiiv settles in 1-2s; SendGrid's background-job upsert can take 10-20s+
|
|
27
|
+
const POLL_INTERVAL_MS = 2000;
|
|
28
|
+
const POLL_MAX_MS = 90000; // SendGrid's delete-contact job can take 30-60s+ to surface
|
|
29
|
+
|
|
30
|
+
function sleep(ms) {
|
|
31
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Poll a provider lookup until it matches the expected state (present/absent).
|
|
36
|
+
* Returns the resolved value (contact object or null) once condition is met, or
|
|
37
|
+
* the last value seen after timing out.
|
|
38
|
+
*
|
|
39
|
+
* @param {Function} fetchFn - async function that returns contact or null
|
|
40
|
+
* @param {boolean} expectPresent - true = wait until present, false = wait until absent
|
|
41
|
+
*/
|
|
42
|
+
async function pollProvider(fetchFn, expectPresent) {
|
|
43
|
+
const start = Date.now();
|
|
44
|
+
let lastValue = null;
|
|
45
|
+
|
|
46
|
+
while (Date.now() - start < POLL_MAX_MS) {
|
|
47
|
+
lastValue = await fetchFn();
|
|
48
|
+
const present = !!lastValue;
|
|
49
|
+
if (present === expectPresent) {
|
|
50
|
+
return lastValue;
|
|
51
|
+
}
|
|
52
|
+
await sleep(POLL_INTERVAL_MS);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return lastValue;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
module.exports = defineCases({
|
|
59
|
+
description: 'Marketing provider lifecycle (live SendGrid + Beehiiv round-trip)',
|
|
60
|
+
type: 'group',
|
|
61
|
+
skip: !process.env.TEST_EXTENDED_MODE
|
|
62
|
+
? 'TEST_EXTENDED_MODE not set (this test hits live SendGrid + Beehiiv APIs)'
|
|
63
|
+
: false,
|
|
64
|
+
tests: [
|
|
65
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
66
|
+
// Phase 1 — Pre-check: both accounts should be absent from providers
|
|
67
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
68
|
+
{
|
|
69
|
+
name: 'phase-1-pre-check-both-accounts-absent',
|
|
70
|
+
auth: 'admin',
|
|
71
|
+
timeout: 180000,
|
|
72
|
+
|
|
73
|
+
async run({ accounts, assert }) {
|
|
74
|
+
const granted = accounts['consent-granted'];
|
|
75
|
+
const declined = accounts['consent-declined'];
|
|
76
|
+
|
|
77
|
+
// Force-clean any leftovers from a prior run (e.g. the previous phase-2a
|
|
78
|
+
// left a contact in SendGrid that THIS run's phase-1 needs to start
|
|
79
|
+
// without). SendGrid's delete is an async job, so wait for absence.
|
|
80
|
+
await sendgridProvider.removeContact(granted.email);
|
|
81
|
+
await sendgridProvider.removeContact(declined.email);
|
|
82
|
+
await beehiivProvider.removeContact(granted.email);
|
|
83
|
+
await beehiivProvider.removeContact(declined.email);
|
|
84
|
+
|
|
85
|
+
const grantedSg = await pollProvider(() => sendgridProvider.findContact(granted.email), false);
|
|
86
|
+
const declinedSg = await pollProvider(() => sendgridProvider.findContact(declined.email), false);
|
|
87
|
+
const grantedBh = await pollProvider(() => beehiivProvider.findContact(granted.email), false);
|
|
88
|
+
const declinedBh = await pollProvider(() => beehiivProvider.findContact(declined.email), false);
|
|
89
|
+
|
|
90
|
+
assert.equal(grantedSg, null, 'granted account should be absent from SendGrid');
|
|
91
|
+
assert.equal(declinedSg, null, 'declined account should be absent from SendGrid');
|
|
92
|
+
assert.equal(grantedBh, null, 'granted account should be absent from Beehiiv');
|
|
93
|
+
assert.equal(declinedBh, null, 'declined account should be absent from Beehiiv');
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
98
|
+
// Phase 2 — Sync granted account → both providers
|
|
99
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
100
|
+
{
|
|
101
|
+
name: 'phase-2a-granted-account-syncs-to-both-providers',
|
|
102
|
+
auth: 'admin',
|
|
103
|
+
timeout: 180000,
|
|
104
|
+
|
|
105
|
+
async run({ accounts, assert, Manager, ctx, config }) {
|
|
106
|
+
const granted = accounts['consent-granted'];
|
|
107
|
+
const admin = Manager.libraries.admin;
|
|
108
|
+
|
|
109
|
+
// Set consent.marketing.status = 'granted' on the user doc
|
|
110
|
+
await admin.firestore().doc(`users/${granted.uid}`).set({
|
|
111
|
+
consent: {
|
|
112
|
+
marketing: {
|
|
113
|
+
status: 'granted',
|
|
114
|
+
grantedAt: {
|
|
115
|
+
timestamp: new Date().toISOString(),
|
|
116
|
+
timestampUNIX: Math.floor(Date.now() / 1000),
|
|
117
|
+
source: 'test',
|
|
118
|
+
ip: null,
|
|
119
|
+
text: 'consent-lifecycle test',
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
legal: {
|
|
123
|
+
status: 'granted',
|
|
124
|
+
grantedAt: {
|
|
125
|
+
timestamp: new Date().toISOString(),
|
|
126
|
+
timestampUNIX: Math.floor(Date.now() / 1000),
|
|
127
|
+
source: 'test',
|
|
128
|
+
ip: null,
|
|
129
|
+
text: 'consent-lifecycle test',
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
}, { merge: true });
|
|
134
|
+
|
|
135
|
+
// Trigger marketing sync via the Email() surface
|
|
136
|
+
const result = await Manager.Email(ctx).sync(granted.uid);
|
|
137
|
+
assert.ok(result, 'sync should return a result');
|
|
138
|
+
assert.notEqual(result.blocked, 'validation', 'sync should not be blocked by validation (uses _test.allow_*)');
|
|
139
|
+
|
|
140
|
+
// Poll providers until they reflect the upsert. SendGrid's upsert is
|
|
141
|
+
// an async background job (returns a job_id, not the inserted contact)
|
|
142
|
+
// so a single check 5s later isn't enough — it can take 10-20s to
|
|
143
|
+
// surface. Beehiiv is usually instant but uses the same poll for symmetry.
|
|
144
|
+
const sgContact = await pollProvider(() => sendgridProvider.findContact(granted.email), true);
|
|
145
|
+
const bhContact = await pollProvider(() => beehiivProvider.findContact(granted.email), true);
|
|
146
|
+
|
|
147
|
+
assert.ok(sgContact, 'granted account should now exist in SendGrid');
|
|
148
|
+
if (config.marketing?.newsletter?.providers?.beehiiv?.publicationId) {
|
|
149
|
+
assert.ok(bhContact, 'granted account should now exist in Beehiiv');
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
155
|
+
// Phase 2b — Declined account: verify it stays out of providers when we
|
|
156
|
+
// DON'T call sync (which is what the signup route does for declined
|
|
157
|
+
// marketing consent).
|
|
158
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
159
|
+
{
|
|
160
|
+
name: 'phase-2b-declined-account-stays-out-of-providers',
|
|
161
|
+
auth: 'admin',
|
|
162
|
+
timeout: 180000,
|
|
163
|
+
|
|
164
|
+
async run({ accounts, assert, Manager, ctx }) {
|
|
165
|
+
const declined = accounts['consent-declined'];
|
|
166
|
+
const admin = Manager.libraries.admin;
|
|
167
|
+
|
|
168
|
+
// Set consent.marketing.status = 'revoked' on the user doc.
|
|
169
|
+
// We deliberately do NOT call sync — the production signup route gates
|
|
170
|
+
// on consent.marketing.status === 'granted' before calling sync, so a
|
|
171
|
+
// declined user simply never gets a sync call. We verify that contract
|
|
172
|
+
// by NOT calling sync and asserting the user stays absent.
|
|
173
|
+
await admin.firestore().doc(`users/${declined.uid}`).set({
|
|
174
|
+
consent: {
|
|
175
|
+
marketing: {
|
|
176
|
+
status: 'revoked',
|
|
177
|
+
grantedAt: { timestamp: null, timestampUNIX: null, source: null, ip: null, text: null },
|
|
178
|
+
revokedAt: {
|
|
179
|
+
timestamp: new Date().toISOString(),
|
|
180
|
+
timestampUNIX: Math.floor(Date.now() / 1000),
|
|
181
|
+
source: 'test',
|
|
182
|
+
ip: null,
|
|
183
|
+
text: null,
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
legal: {
|
|
187
|
+
status: 'granted',
|
|
188
|
+
grantedAt: {
|
|
189
|
+
timestamp: new Date().toISOString(),
|
|
190
|
+
timestampUNIX: Math.floor(Date.now() / 1000),
|
|
191
|
+
source: 'test',
|
|
192
|
+
ip: null,
|
|
193
|
+
text: 'consent-lifecycle test',
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
}, { merge: true });
|
|
198
|
+
|
|
199
|
+
await sleep(SETTLE_MS);
|
|
200
|
+
|
|
201
|
+
const sgContact = await sendgridProvider.findContact(declined.email);
|
|
202
|
+
const bhContact = await beehiivProvider.findContact(declined.email);
|
|
203
|
+
|
|
204
|
+
assert.equal(sgContact, null, 'declined account should remain absent from SendGrid');
|
|
205
|
+
assert.equal(bhContact, null, 'declined account should remain absent from Beehiiv');
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
|
|
209
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
210
|
+
// Phase 3 — Unsubscribe: granted account is removed via Manager.Email().remove
|
|
211
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
212
|
+
{
|
|
213
|
+
name: 'phase-3-granted-account-unsubscribe-removes-from-both',
|
|
214
|
+
auth: 'admin',
|
|
215
|
+
timeout: 180000,
|
|
216
|
+
|
|
217
|
+
async run({ accounts, assert, Manager, ctx }) {
|
|
218
|
+
const granted = accounts['consent-granted'];
|
|
219
|
+
|
|
220
|
+
// Trigger removal — simulates the email-preferences opt-out flow
|
|
221
|
+
const result = await Manager.Email(ctx).remove(granted.email);
|
|
222
|
+
assert.ok(result, 'remove should return a result');
|
|
223
|
+
|
|
224
|
+
// Poll for absence — SendGrid's contact delete is also an async job.
|
|
225
|
+
const sgContact = await pollProvider(() => sendgridProvider.findContact(granted.email), false);
|
|
226
|
+
const bhContact = await pollProvider(() => beehiivProvider.findContact(granted.email), false);
|
|
227
|
+
|
|
228
|
+
assert.equal(sgContact, null, 'granted account should now be absent from SendGrid');
|
|
229
|
+
assert.equal(bhContact, null, 'granted account should now be absent from Beehiiv');
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
|
|
233
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
234
|
+
// Phase 4 — Validation gate: _test.* (non-allow) is blocked by validate()
|
|
235
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
236
|
+
{
|
|
237
|
+
name: 'phase-4-non-allow-test-email-blocked-by-validation',
|
|
238
|
+
auth: 'admin',
|
|
239
|
+
timeout: 30000,
|
|
240
|
+
|
|
241
|
+
async run({ assert, Manager, ctx }) {
|
|
242
|
+
// _test.never-reaches-providers@... is NOT _test.allow_* → should be blocked
|
|
243
|
+
const blockedEmail = '_test.never-reaches-providers@somiibo.com';
|
|
244
|
+
|
|
245
|
+
const result = await Manager.Email(ctx).add({ email: blockedEmail });
|
|
246
|
+
|
|
247
|
+
assert.equal(result.blocked, 'validation', 'non-allow _test.* email should be blocked by validation');
|
|
248
|
+
|
|
249
|
+
// And the provider lookup should show nothing
|
|
250
|
+
const sgContact = await sendgridProvider.findContact(blockedEmail);
|
|
251
|
+
const bhContact = await beehiivProvider.findContact(blockedEmail);
|
|
252
|
+
|
|
253
|
+
assert.equal(sgContact, null, 'blocked email should never appear in SendGrid');
|
|
254
|
+
assert.equal(bhContact, null, 'blocked email should never appear in Beehiiv');
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
],
|
|
258
|
+
});
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal-only send-field policy test — four fields hand the renderer raw HTML (or
|
|
3
|
+
* the trust to render it), and none of them may arrive over the API.
|
|
4
|
+
*
|
|
5
|
+
* - `data.content.html` skips markdown entirely (the transactional/campaign body).
|
|
6
|
+
* - `html` is the top-level override that replaces the rendered MJML document outright
|
|
7
|
+
* — a documented MCP `send_email` parameter until Ian's #125 ruling closed it.
|
|
8
|
+
* - `contentHtml` is the pre-rendered campaign HTML the newsletter generator hands to
|
|
9
|
+
* `sendCampaign()` — read AHEAD of the escaped renderer, and it persists into the
|
|
10
|
+
* stored campaign doc that cron sends later.
|
|
11
|
+
* - `trustedContent: true` flips the body renderer to `html: true`.
|
|
12
|
+
*
|
|
13
|
+
* First-party callers depend on all three, but a caller arriving over the API —
|
|
14
|
+
* including an admin-authenticated AI on the MCP `send_email` / `create_campaign` /
|
|
15
|
+
* `update_campaign` tools — must be turned away with a coded-400 permanent fault, or
|
|
16
|
+
* the escaped lane is one field away from being bypassed (#90).
|
|
17
|
+
*
|
|
18
|
+
* The external lanes assert the boundary through `prepare.internalOnlyFieldFault()`; the
|
|
19
|
+
* internal lane never calls it, so its passthrough must still work end to end.
|
|
20
|
+
*
|
|
21
|
+
* Plain-node unit test (no emulator, no network). The route round-trips that prove the
|
|
22
|
+
* boundary is WIRED live in test/routes/admin/email-content-html.test.js and
|
|
23
|
+
* test/routes/marketing/campaign.test.js.
|
|
24
|
+
*/
|
|
25
|
+
const assert = require('node:assert');
|
|
26
|
+
const prepare = require('../../dist/manager/libraries/email/prepare.js');
|
|
27
|
+
const { buildCampaignDoc } = require('../../dist/manager/routes/marketing/campaign/utils.js');
|
|
28
|
+
const defineCases = require('../../dist/vendor/devkit/test/define-cases.js');
|
|
29
|
+
|
|
30
|
+
// The internal lane's build runs for real so the passthrough is proven all the way to
|
|
31
|
+
// the SendGrid payload, not just at renderContent().
|
|
32
|
+
function transactionalBuild(settings) {
|
|
33
|
+
process.env.UNSUBSCRIBE_HMAC_KEY = process.env.UNSUBSCRIBE_HMAC_KEY || 'test-key';
|
|
34
|
+
|
|
35
|
+
const Transactional = require('../../dist/manager/libraries/email/transactional/index.js');
|
|
36
|
+
const Manager = {
|
|
37
|
+
config: {
|
|
38
|
+
brand: { id: 'testbrand', name: 'Test Brand', url: 'https://test.dev', contact: { email: 'hello@test.dev' }, images: {} },
|
|
39
|
+
// The account's unsubscribe group ids (#649) — every send resolves one from config
|
|
40
|
+
marketing: { campaigns: { providers: { sendgrid: { groups: { orders: 900001, hello: 900002, account: 900003, marketing: 900004, security: 900005, newsletter: 900006, internal: 900007 } } } } },
|
|
41
|
+
},
|
|
42
|
+
project: { websiteUrl: 'https://test.dev' },
|
|
43
|
+
libraries: { admin: {} },
|
|
44
|
+
User: () => ({ properties: {} }),
|
|
45
|
+
};
|
|
46
|
+
const ctx = { Manager, log: () => {}, error: () => {} };
|
|
47
|
+
|
|
48
|
+
return new Transactional(ctx).build({
|
|
49
|
+
to: 'user@test.dev',
|
|
50
|
+
template: 'card',
|
|
51
|
+
...settings,
|
|
52
|
+
}).then((email) => email.content[0].value);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
module.exports = defineCases({
|
|
56
|
+
description: 'Email internal-only send fields (content.html, html, contentHtml, trustedContent)',
|
|
57
|
+
type: 'group',
|
|
58
|
+
tests: [
|
|
59
|
+
// ---------- The external lanes reject it ----------
|
|
60
|
+
|
|
61
|
+
{
|
|
62
|
+
name: 'external lane rejects data.content.html with a coded 400',
|
|
63
|
+
|
|
64
|
+
run() {
|
|
65
|
+
const fault = prepare.internalOnlyFieldFault({
|
|
66
|
+
to: 'user@test.dev',
|
|
67
|
+
subject: 'Probe',
|
|
68
|
+
data: { content: { html: '<img src=x onerror="alert(1)">' } },
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
assert.ok(fault instanceof Error, 'no fault returned for data.content.html');
|
|
72
|
+
assert.equal(fault.code, 400, `fault must be a permanent 400, got ${fault.code}`);
|
|
73
|
+
assert.ok(fault.message.includes('data.content.html'), `message must name the field: ${fault.message}`);
|
|
74
|
+
assert.ok(/internal-caller only/.test(fault.message), `message must state the rule: ${fault.message}`);
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
{
|
|
79
|
+
name: 'external lane rejects the field even when it is empty',
|
|
80
|
+
|
|
81
|
+
run() {
|
|
82
|
+
// An external caller has no legitimate reason to send the key at all — the
|
|
83
|
+
// presence of it is the fault, not its contents.
|
|
84
|
+
assert.ok(prepare.internalOnlyFieldFault({ data: { content: { html: '' } } }) instanceof Error, 'empty html passed');
|
|
85
|
+
assert.ok(prepare.internalOnlyFieldFault({ data: { content: { html: null } } }) instanceof Error, 'null html passed');
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
{
|
|
90
|
+
name: 'external lane passes ordinary markdown settings through clean',
|
|
91
|
+
|
|
92
|
+
run() {
|
|
93
|
+
assert.equal(prepare.internalOnlyFieldFault({ data: { content: { message: '**hi**' } } }), null);
|
|
94
|
+
assert.equal(prepare.internalOnlyFieldFault({ data: {} }), null);
|
|
95
|
+
assert.equal(prepare.internalOnlyFieldFault({}), null);
|
|
96
|
+
assert.equal(prepare.internalOnlyFieldFault(undefined), null);
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
{
|
|
101
|
+
name: 'external lane rejects the top-level html override with a coded 400',
|
|
102
|
+
|
|
103
|
+
run() {
|
|
104
|
+
// `settings.html` replaces the rendered MJML body outright (transactional
|
|
105
|
+
// build()) — the same bypass as data.content.html, one level up. Ian's #125
|
|
106
|
+
// ruling put it on the internal-only list: the admin/MCP lane now matches the
|
|
107
|
+
// API lane exactly, and the MCP send_email tool no longer advertises it.
|
|
108
|
+
const fault = prepare.internalOnlyFieldFault({
|
|
109
|
+
to: 'user@test.dev',
|
|
110
|
+
subject: 'Probe',
|
|
111
|
+
html: '<img src=x onerror="alert(1)">',
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
assert.ok(fault instanceof Error, 'no fault returned for top-level html');
|
|
115
|
+
assert.equal(fault.code, 400, `fault must be a permanent 400, got ${fault.code}`);
|
|
116
|
+
assert.ok(fault.message.includes('html'), `message must name the field: ${fault.message}`);
|
|
117
|
+
assert.ok(/internal-caller only/.test(fault.message), `message must state the rule: ${fault.message}`);
|
|
118
|
+
|
|
119
|
+
// Presence is the fault, same as every other field on the list.
|
|
120
|
+
assert.ok(prepare.internalOnlyFieldFault({ html: '' }) instanceof Error, 'empty html passed');
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
{
|
|
125
|
+
name: 'the MCP send_email tool no longer advertises an html parameter',
|
|
126
|
+
|
|
127
|
+
run() {
|
|
128
|
+
// The tool schema is what an admin-authenticated AI reads. #125 removed the
|
|
129
|
+
// 'Raw HTML body (alternative to template)' parameter; if it ever comes back,
|
|
130
|
+
// the lane is advertising the bypass again.
|
|
131
|
+
const sendEmail = require('../../dist/mcp/tools.js').find((tool) => tool.name === 'send_email');
|
|
132
|
+
|
|
133
|
+
assert.ok(sendEmail, 'send_email tool not found — update this test');
|
|
134
|
+
assert.equal(sendEmail.inputSchema.properties.html, undefined, 'send_email re-advertises the html parameter');
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
{
|
|
139
|
+
name: 'external lane rejects top-level contentHtml with a coded 400',
|
|
140
|
+
|
|
141
|
+
run() {
|
|
142
|
+
// The campaign lane reads settings.contentHtml AHEAD of the escaped renderer
|
|
143
|
+
// (marketing/index.js) — a caller-supplied one is the same bypass.
|
|
144
|
+
const fault = prepare.internalOnlyFieldFault({
|
|
145
|
+
name: 'Probe campaign',
|
|
146
|
+
subject: 'Probe',
|
|
147
|
+
contentHtml: '<img src=x onerror="alert(1)">',
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
assert.ok(fault instanceof Error, 'no fault returned for contentHtml');
|
|
151
|
+
assert.equal(fault.code, 400, `fault must be a permanent 400, got ${fault.code}`);
|
|
152
|
+
assert.ok(fault.message.includes('contentHtml'), `message must name the field: ${fault.message}`);
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
{
|
|
157
|
+
name: 'external lane rejects caller-supplied trustedContent with a coded 400',
|
|
158
|
+
|
|
159
|
+
run() {
|
|
160
|
+
// trustedContent: true flips the body renderer to html: true — raw markup and
|
|
161
|
+
// javascript: hrefs would both survive.
|
|
162
|
+
const fault = prepare.internalOnlyFieldFault({
|
|
163
|
+
subject: 'Probe',
|
|
164
|
+
trustedContent: true,
|
|
165
|
+
data: { content: { message: '<a href="javascript:alert(1)">x</a>' } },
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
assert.ok(fault instanceof Error, 'no fault returned for trustedContent');
|
|
169
|
+
assert.equal(fault.code, 400, `fault must be a permanent 400, got ${fault.code}`);
|
|
170
|
+
assert.ok(fault.message.includes('trustedContent'), `message must name the field: ${fault.message}`);
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
|
|
174
|
+
{
|
|
175
|
+
name: 'external lane rejects trustedContent: false too — presence is the fault',
|
|
176
|
+
|
|
177
|
+
run() {
|
|
178
|
+
assert.ok(prepare.internalOnlyFieldFault({ trustedContent: false }) instanceof Error, 'trustedContent: false passed');
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
// ---------- The stored campaign doc can no longer carry it ----------
|
|
183
|
+
|
|
184
|
+
{
|
|
185
|
+
name: 'a rejected campaign never reaches buildCampaignDoc, so the doc cannot carry contentHtml',
|
|
186
|
+
|
|
187
|
+
run() {
|
|
188
|
+
// buildCampaignDoc() blacklists doc-level fields rather than allowlisting, so
|
|
189
|
+
// anything the caller sends persists into doc.settings — which is why the route
|
|
190
|
+
// rejects BEFORE calling it. This asserts both halves: the persistence is real,
|
|
191
|
+
// and the guard fires on that exact payload.
|
|
192
|
+
const hostile = { name: 'Probe', subject: 'Probe', contentHtml: '<img src=x onerror=alert(1)>' };
|
|
193
|
+
|
|
194
|
+
const { campaignSettings } = buildCampaignDoc(hostile);
|
|
195
|
+
|
|
196
|
+
assert.ok('contentHtml' in campaignSettings, 'buildCampaignDoc no longer persists contentHtml — update this test');
|
|
197
|
+
assert.ok(prepare.internalOnlyFieldFault(hostile) instanceof Error, 'the payload that persists was not rejected');
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
// ---------- Belt: the route schemas do not admit the two top-level fields ----------
|
|
202
|
+
|
|
203
|
+
{
|
|
204
|
+
name: 'the admin/email + campaign schemas strip html, contentHtml and trustedContent',
|
|
205
|
+
|
|
206
|
+
run() {
|
|
207
|
+
// These three never reach the handler over HTTP today, because a zod object
|
|
208
|
+
// strips unknown keys and none of them is declared (#125 removed the last one,
|
|
209
|
+
// `html`, from the admin/email schema). That strip is the BELT;
|
|
210
|
+
// internalOnlyFieldFault() in the handler is the BRACES — the moment either
|
|
211
|
+
// field is declared on a schema (or a consumer route forwards raw settings),
|
|
212
|
+
// the guard is what stops it. If this test ever fails, the guard is live.
|
|
213
|
+
const emailSchema = require('../../dist/manager/schemas/admin/email/post.js')();
|
|
214
|
+
const email = emailSchema.parse({
|
|
215
|
+
to: 'a@b.co',
|
|
216
|
+
subject: 's',
|
|
217
|
+
html: '<img src=x onerror=alert(1)>',
|
|
218
|
+
contentHtml: '<img src=x>',
|
|
219
|
+
trustedContent: true,
|
|
220
|
+
data: { content: { html: '<img src=x onerror=alert(1)>' } },
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
assert.equal(email.html, undefined, 'admin/email schema admitted the top-level html override');
|
|
224
|
+
assert.equal(email.contentHtml, undefined, 'admin/email schema admitted contentHtml');
|
|
225
|
+
assert.equal(email.trustedContent, undefined, 'admin/email schema admitted trustedContent');
|
|
226
|
+
|
|
227
|
+
// ...but data is a passthrough, which is exactly why data.content.html needs
|
|
228
|
+
// the handler guard rather than a schema strip.
|
|
229
|
+
assert.equal(email.data.content.html, '<img src=x onerror=alert(1)>', 'data.content.html no longer passes the schema — update this test');
|
|
230
|
+
|
|
231
|
+
const campaignSchema = require('../../dist/manager/schemas/marketing/campaign/post.js')();
|
|
232
|
+
const campaign = campaignSchema.parse({ name: 'n', subject: 's', contentHtml: '<img src=x>', trustedContent: true });
|
|
233
|
+
|
|
234
|
+
assert.equal(campaign.contentHtml, undefined, 'campaign schema admitted contentHtml');
|
|
235
|
+
assert.equal(campaign.trustedContent, undefined, 'campaign schema admitted trustedContent');
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
|
|
239
|
+
// ---------- The internal lane keeps working ----------
|
|
240
|
+
|
|
241
|
+
{
|
|
242
|
+
name: 'internal caller still gets its pre-rendered html into the payload',
|
|
243
|
+
|
|
244
|
+
async run() {
|
|
245
|
+
const html = await transactionalBuild({
|
|
246
|
+
subject: 'Internal',
|
|
247
|
+
data: { content: { html: '<p id="prerendered">first-party <b>html</b></p>' } },
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
assert.ok(html.includes('id="prerendered"'), `pre-rendered html did not reach the payload: ${html}`);
|
|
251
|
+
assert.ok(/<b>html<\/b>/.test(html), 'pre-rendered markup was escaped for an internal caller');
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
|
|
255
|
+
{
|
|
256
|
+
name: 'internal caller still gets the top-level html override into the payload',
|
|
257
|
+
|
|
258
|
+
async run() {
|
|
259
|
+
// #125 closed the override to the API lane only — build() still honors it for
|
|
260
|
+
// a first-party caller that hands over a complete document.
|
|
261
|
+
const html = await transactionalBuild({
|
|
262
|
+
subject: 'Internal',
|
|
263
|
+
html: '<html><body id="whole-document">first-party</body></html>',
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
assert.equal(html, '<html><body id="whole-document">first-party</body></html>', `the override did not replace the body: ${html}`);
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
],
|
|
270
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test: Send feedback + plain template emails
|
|
3
|
+
* Quick visual test — sends one of each to verify rendering.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const defineCases = require('../../dist/vendor/devkit/test/define-cases.js');
|
|
7
|
+
module.exports = defineCases({
|
|
8
|
+
description: 'Feedback + plain template send',
|
|
9
|
+
type: 'group',
|
|
10
|
+
skip: !process.env.TEST_EXTENDED_MODE ? 'TEST_EXTENDED_MODE not set' : false,
|
|
11
|
+
tests: [
|
|
12
|
+
{
|
|
13
|
+
name: 'feedback-template',
|
|
14
|
+
auth: 'admin',
|
|
15
|
+
timeout: 30000,
|
|
16
|
+
async run({ http, assert, config }) {
|
|
17
|
+
const response = await http.post('backend-manager/admin/email', {
|
|
18
|
+
subject: '[TEST] Feedback template',
|
|
19
|
+
to: `_test-email-send@${config.domain}`,
|
|
20
|
+
template: 'feedback',
|
|
21
|
+
sender: 'hello',
|
|
22
|
+
copy: false,
|
|
23
|
+
data: { signoff: { type: 'personal' } },
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
assert.isSuccess(response, 'Should send feedback email');
|
|
27
|
+
assert.equal(response.data.status, 'sent', 'Status should be sent');
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: 'plain-template-with-markdown',
|
|
32
|
+
auth: 'admin',
|
|
33
|
+
timeout: 30000,
|
|
34
|
+
async run({ http, assert, config }) {
|
|
35
|
+
const response = await http.post('backend-manager/admin/email', {
|
|
36
|
+
subject: '[TEST] Plain template with markdown',
|
|
37
|
+
to: `_test-email-send@${config.domain}`,
|
|
38
|
+
template: 'plain',
|
|
39
|
+
sender: 'hello',
|
|
40
|
+
copy: false,
|
|
41
|
+
data: {
|
|
42
|
+
content: {
|
|
43
|
+
message: '# Quick Update\n\nHey Ian,\n\nJust wanted to let you know that your **account settings** have been updated successfully.\n\nHere is what changed:\n\n- Email notifications: **enabled**\n- Two-factor auth: **enabled**\n- API key: [regenerated](https://example.com/account)\n\nIf you didn\'t make these changes, please [contact support](https://example.com/support) immediately.\n\nThanks!',
|
|
44
|
+
},
|
|
45
|
+
signoff: { type: 'personal' },
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
assert.isSuccess(response, 'Should send plain email');
|
|
50
|
+
assert.equal(response.data.status, 'sent', 'Status should be sent');
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "Predefined fixture for the `clean` template. Loaded via NEWSLETTER_FIXTURE=clean. Edit freely — no AI involved. Classic content shape: intro + sections[{title, body, cta, image_prompt}].",
|
|
3
|
+
"subject": "Identity is the new growth lever",
|
|
4
|
+
"preheader": "Three platform shifts to lock in this week.",
|
|
5
|
+
"summary": "LinkedIn, YouTube, and the broader creator stack are all tightening identity controls this week. The operators with documented attribution histories will outrun the rest. Documentation is no longer overhead — it's the moat.",
|
|
6
|
+
"tags": ["linkedin", "youtube", "platform-policy", "creator-economy", "operator-tools"],
|
|
7
|
+
"intro": "Identity matters because trust is the new growth lever. Teams that handle this well will spend less time cleaning up later.",
|
|
8
|
+
"sections": [
|
|
9
|
+
{
|
|
10
|
+
"title": "LinkedIn ships verified-profile gates for business accounts",
|
|
11
|
+
"body": "LinkedIn now requires verified identity attestations on any business profile claiming more than 500 followers. Accounts with documented attribution histories sail through verification in under twelve hours. The practical implication is to lock down your attribution log this week, not next.",
|
|
12
|
+
"image_prompt": "Abstract geometric illustration of a verification checkmark inside a layered profile card."
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"title": "Documentation is the new operator moat",
|
|
16
|
+
"body": "Operator playbooks are now the difference between an account that recovers from a flag and one that gets permanently restricted. Teams running documented processes recover in days. The ones without burn weeks negotiating with support queues.",
|
|
17
|
+
"image_prompt": "Stack of geometric document layers casting clean shadows on a flat surface."
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"title": "YouTube tests creator-attribution metadata on uploads",
|
|
21
|
+
"body": "A subset of channels saw a new upload checkbox this week asking whether content was assisted by automation. The checkbox is optional today. Reading the room: it will not stay optional. Creators with clear internal workflows will adapt in an afternoon.",
|
|
22
|
+
"image_prompt": "Minimalist play button morphing into a label tag, flat geometric style."
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"signoff": "Best,\nThe Somiibo Team",
|
|
26
|
+
"citations": [
|
|
27
|
+
{ "note": "LinkedIn flagged 12,000 impersonation attempts in the verified-profile beta.", "source": "Reported by LinkedIn product team, May 2026" },
|
|
28
|
+
{ "note": "Operator playbook adoption up 38% week-over-week in Q2.", "source": "Per platform analytics, May 2026" }
|
|
29
|
+
]
|
|
30
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "Predefined fixture for the `editorial` template. Loaded via NEWSLETTER_FIXTURE=editorial. Same classic content shape as clean.json — both templates render the same data.",
|
|
3
|
+
"subject": "Identity is the new growth lever",
|
|
4
|
+
"preheader": "Three platform shifts to lock in this week.",
|
|
5
|
+
"summary": "LinkedIn, YouTube, and the broader creator stack are all tightening identity controls this week. The operators with documented attribution histories will outrun the rest. Documentation is no longer overhead — it's the moat.",
|
|
6
|
+
"tags": ["linkedin", "youtube", "platform-policy", "creator-economy", "operator-tools"],
|
|
7
|
+
"intro": "Identity matters because trust is the new growth lever. Teams that handle this well will spend less time cleaning up later, and the patterns emerging this week tell you exactly where to focus.",
|
|
8
|
+
"sections": [
|
|
9
|
+
{
|
|
10
|
+
"title": "LinkedIn ships verified-profile gates for business accounts",
|
|
11
|
+
"body": "LinkedIn now requires verified identity attestations on any business profile claiming more than 500 followers. The rollout is fast and uneven — some accounts hit the gate inside two days of profile activity, others sit unchallenged for weeks. The pattern that matters is this: accounts with documented attribution histories sail through verification in under twelve hours. Accounts without get queued. The practical implication is to lock down your attribution log this week, not next.",
|
|
12
|
+
"image_prompt": "Abstract geometric illustration of a verification checkmark inside a layered profile card."
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"title": "Documentation is the new operator moat",
|
|
16
|
+
"body": "Operator playbooks — once dismissed as overhead — are now the difference between an account that recovers from a flag and one that gets permanently restricted. The teams already running on documented processes recover in days. The ones without burn weeks negotiating with support queues. Treat your playbook as a compliance artifact, not a knowledge-management nice-to-have.",
|
|
17
|
+
"image_prompt": "Stack of geometric document layers casting clean shadows on a flat surface."
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"title": "YouTube tests creator-attribution metadata on uploads",
|
|
21
|
+
"body": "A subset of channels saw a new upload checkbox this week asking whether content was assisted by automation. The checkbox is optional today. Reading the room: it will not stay optional. Creators with clear internal workflows already labeled will adapt in an afternoon. Everyone else will spend a quarter retrofitting.",
|
|
22
|
+
"image_prompt": "Minimalist play button morphing into a label tag, flat geometric style."
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"signoff": "Best,\nThe Somiibo Team",
|
|
26
|
+
"citations": [
|
|
27
|
+
{ "note": "LinkedIn flagged 12,000 impersonation attempts in the verified-profile beta.", "source": "Reported by LinkedIn product team, May 2026" },
|
|
28
|
+
{ "note": "Operator playbook adoption up 38% week-over-week in Q2.", "source": "Per platform analytics, May 2026" }
|
|
29
|
+
]
|
|
30
|
+
}
|