@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,554 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema-driven validation for resolved omega.json5 configs.
|
|
3
|
+
*
|
|
4
|
+
* The rule walker (runSchema) is electron-manager's proven validate-config
|
|
5
|
+
* engine ported verbatim: required/type/match/enum semantics, where match +
|
|
6
|
+
* enum only run on PRESENT values and a conditional `required` function
|
|
7
|
+
* receives the full config. See schema.js for the rule format.
|
|
8
|
+
*
|
|
9
|
+
* validateConfig() layers on top of the walker:
|
|
10
|
+
* - SHARED_SCHEMA always runs; TARGET_SCHEMAS[options.target] adds that
|
|
11
|
+
* target's refinements against the same resolved top-level namespace
|
|
12
|
+
* - `targets` sanity: unknown target names are errors (typo protection —
|
|
13
|
+
* targets.website is a mistake, the canonical name is web) and each
|
|
14
|
+
* present entry must be an object ({} = enabled with defaults) OR an
|
|
15
|
+
* array of id'd instance entries (multi-instance targets: ids required,
|
|
16
|
+
* dir-safe, unique per type; >1 backend instance is a WARNING)
|
|
17
|
+
* - `cloud.config.authDomain`, when set, must be the brand's OWN host (the
|
|
18
|
+
* resolved instance url, else brand.url): a firebaseapp.com value or a
|
|
19
|
+
* mismatch is an error, and demo-* (emulator-only) projects are exempt
|
|
20
|
+
* - retired keys are always errors (see retired-keys.js) — a name that was
|
|
21
|
+
* renamed outright reads as nothing at all, so it fails loudly instead of
|
|
22
|
+
* losing its settings silently
|
|
23
|
+
* - secret-shaped keys are always errors (see secrets.js) — loadConfig
|
|
24
|
+
* additionally hard-fails on them before any merge happens
|
|
25
|
+
* - keys the schema does not declare are WARNINGS (#636): the walker only
|
|
26
|
+
* ever visits declared paths, so a typo — or a live path nobody declared —
|
|
27
|
+
* used to pass in silence
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
const { TARGETS, isCustomTargetEntry, SHARED_SCHEMA, TARGET_SCHEMAS } = require('./schema.js');
|
|
31
|
+
const { findSecretKeys } = require('./secrets.js');
|
|
32
|
+
const { findRetiredKeys } = require('./retired-keys.js');
|
|
33
|
+
const { isPlainObject } = require('./merge.js');
|
|
34
|
+
const { INSTANCE_ID_PATTERN } = require('./instances.js');
|
|
35
|
+
const { isDemoProject } = require('./demo.js');
|
|
36
|
+
const { isCountedFeature } = require('../account/features.js');
|
|
37
|
+
|
|
38
|
+
// Firebase's own default authDomain shape: a third-party host by definition
|
|
39
|
+
const FIREBASE_AUTH_DOMAIN = /\.firebaseapp\.com$/;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Read a dotted path out of a config object — the path resolver the schema
|
|
43
|
+
* walk and the env presence checker (env-rules.js) share.
|
|
44
|
+
*
|
|
45
|
+
* @param {object} obj - The (resolved) config object.
|
|
46
|
+
* @param {string} dottedPath - e.g. 'analytics.providers.google.id'.
|
|
47
|
+
* @returns {*} The value, or undefined when any segment is missing.
|
|
48
|
+
*/
|
|
49
|
+
function getPath(obj, dottedPath) {
|
|
50
|
+
if (!obj) return undefined;
|
|
51
|
+
const parts = String(dottedPath).split('.');
|
|
52
|
+
let cur = obj;
|
|
53
|
+
for (const p of parts) {
|
|
54
|
+
if (cur == null) return undefined;
|
|
55
|
+
cur = cur[p];
|
|
56
|
+
}
|
|
57
|
+
return cur;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function isPresent(v) {
|
|
61
|
+
if (v == null) return false;
|
|
62
|
+
if (typeof v === 'string' && v.length === 0) return false;
|
|
63
|
+
if (Array.isArray(v) && v.length === 0) return false;
|
|
64
|
+
if (typeof v === 'object' && !Array.isArray(v) && Object.keys(v).length === 0) return false;
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Walk a config object against a schema array and collect human-readable errors.
|
|
70
|
+
* @param {object} config - The (resolved) config object.
|
|
71
|
+
* @param {object[]} schema - Rule array in the schema.js entry format.
|
|
72
|
+
* @returns {string[]} Errors; empty when the config passes.
|
|
73
|
+
*/
|
|
74
|
+
function runSchema(config, schema) {
|
|
75
|
+
const errors = [];
|
|
76
|
+
|
|
77
|
+
for (const rule of schema) {
|
|
78
|
+
const value = getPath(config, rule.path);
|
|
79
|
+
const present = isPresent(value);
|
|
80
|
+
|
|
81
|
+
// ─── Required check ─────────────────────────────────────────────────────
|
|
82
|
+
let isRequired = false;
|
|
83
|
+
if (typeof rule.required === 'function') {
|
|
84
|
+
// A throwing required() is a broken first-party schema rule — report
|
|
85
|
+
// it loud instead of silently treating the field as optional
|
|
86
|
+
try { isRequired = !!rule.required(config); }
|
|
87
|
+
catch (e) {
|
|
88
|
+
errors.push(`config.${rule.path} required() threw: ${e.message}`);
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
} else {
|
|
92
|
+
isRequired = !!rule.required;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (!present) {
|
|
96
|
+
if (isRequired) {
|
|
97
|
+
const why = rule.description ? ` — ${rule.description}` : '';
|
|
98
|
+
errors.push(`config.${rule.path} is required${why}`);
|
|
99
|
+
}
|
|
100
|
+
// Absent + not required → nothing else to check.
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ─── Type check ('|' unions allowed: 'string|boolean') ──────────────────
|
|
105
|
+
if (rule.type) {
|
|
106
|
+
const matches = (type) => {
|
|
107
|
+
switch (type) {
|
|
108
|
+
case 'string': return typeof value === 'string';
|
|
109
|
+
case 'boolean': return typeof value === 'boolean';
|
|
110
|
+
case 'number': return typeof value === 'number' && Number.isFinite(value);
|
|
111
|
+
case 'integer': return Number.isInteger(value);
|
|
112
|
+
case 'array': return Array.isArray(value);
|
|
113
|
+
case 'object': return isPlainObject(value);
|
|
114
|
+
default: return true;
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
if (!rule.type.split('|').some(matches)) {
|
|
118
|
+
errors.push(`config.${rule.path} has wrong type — got ${Array.isArray(value) ? 'array' : typeof value}, expected ${rule.type}`);
|
|
119
|
+
continue; // skip secondary checks if the type is wrong
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// ─── Min check (numbers) ────────────────────────────────────────────────
|
|
124
|
+
if (typeof rule.min === 'number' && typeof value === 'number' && value < rule.min) {
|
|
125
|
+
const why = rule.description ? ` — ${rule.description}` : '';
|
|
126
|
+
errors.push(`config.${rule.path} ${value} is below the minimum ${rule.min}${why}`);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// ─── Max check (numbers) ────────────────────────────────────────────────
|
|
130
|
+
if (typeof rule.max === 'number' && typeof value === 'number' && value > rule.max) {
|
|
131
|
+
const why = rule.description ? ` — ${rule.description}` : '';
|
|
132
|
+
errors.push(`config.${rule.path} ${value} is above the maximum ${rule.max}${why}`);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ─── Match check (strings) ──────────────────────────────────────────────
|
|
136
|
+
if (rule.match && typeof value === 'string' && !rule.match.test(value)) {
|
|
137
|
+
const why = rule.description ? ` — ${rule.description}` : '';
|
|
138
|
+
errors.push(`config.${rule.path} "${value}" does not match expected pattern ${rule.match}${why}`);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// ─── Enum check ─────────────────────────────────────────────────────────
|
|
142
|
+
if (rule.enum && !rule.enum.includes(value)) {
|
|
143
|
+
errors.push(`config.${rule.path} "${value}" is not allowed — must be one of [${rule.enum.join(', ')}]`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return errors;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* The host this (resolved) config's BRAND lives on, which is a brand-level
|
|
152
|
+
* fact and never an instance one (#588, Ian 2026-09-01): one Firebase project,
|
|
153
|
+
* one backend, one persona domain, shared by every instance a brand runs. So
|
|
154
|
+
* `brand.url` answers first and the top-level `url` (the instance's own public
|
|
155
|
+
* url, derived from its id or declared on its entry) is only the fallback for
|
|
156
|
+
* a config that carries no brand.url at all. Reading them the other way round
|
|
157
|
+
* made a `targets/website-admin` load fail its own brand's authDomain check
|
|
158
|
+
* and seeded personas at a different domain than the backend's.
|
|
159
|
+
* @param {object} config - The resolved config object.
|
|
160
|
+
* @returns {string} The lowercase hostname, or '' when no URL is known.
|
|
161
|
+
*/
|
|
162
|
+
function resolvedBrandHost(config) {
|
|
163
|
+
const url = getPath(config, 'brand.url') || getPath(config, 'url');
|
|
164
|
+
|
|
165
|
+
if (typeof url !== 'string' || !url) {
|
|
166
|
+
return '';
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
try {
|
|
170
|
+
return new URL(url.includes('://') ? url : `https://${url}`).hostname.toLowerCase();
|
|
171
|
+
} catch (e) {
|
|
172
|
+
return '';
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* authDomain must be the brand's OWN host (cp268). Firebase's default
|
|
178
|
+
* `<project>.firebaseapp.com` is a third-party host: under browser storage
|
|
179
|
+
* partitioning its redirect sign-in loses the session, which is why the web
|
|
180
|
+
* build self-hosts Firebase's `/__/auth/*` helper files on the brand domain.
|
|
181
|
+
*
|
|
182
|
+
* demo-* projects are exempt because they are emulator-only (no real GCP project
|
|
183
|
+
* exists), so no redirect sign-in ever leaves the emulator.
|
|
184
|
+
* @param {object} config - The resolved config object.
|
|
185
|
+
* @returns {string[]} Errors; empty when the config passes.
|
|
186
|
+
*/
|
|
187
|
+
function validateAuthDomain(config) {
|
|
188
|
+
const authDomain = getPath(config, 'cloud.config.authDomain');
|
|
189
|
+
|
|
190
|
+
if (typeof authDomain !== 'string' || !authDomain.trim()) {
|
|
191
|
+
return [];
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (isDemoProject(getPath(config, 'cloud.config.projectId'))) {
|
|
195
|
+
return [];
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const value = authDomain.trim().toLowerCase().replace(/^https?:\/\//, '').replace(/\/.*$/, '');
|
|
199
|
+
|
|
200
|
+
if (FIREBASE_AUTH_DOMAIN.test(value)) {
|
|
201
|
+
return [
|
|
202
|
+
`config.cloud.config.authDomain "${authDomain}" is a firebaseapp.com domain, but it must be this brand's own host, `
|
|
203
|
+
+ `which self-hosts /__/auth/* so redirect sign-in survives browser storage partitioning `
|
|
204
|
+
+ `(docs/shared/config.md → "authDomain is the brand's own host")`,
|
|
205
|
+
];
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const brandHost = resolvedBrandHost(config);
|
|
209
|
+
|
|
210
|
+
// No URL anywhere: nothing to compare against, and brand.url is optional
|
|
211
|
+
if (!brandHost || value === brandHost) {
|
|
212
|
+
return [];
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return [
|
|
216
|
+
`config.cloud.config.authDomain "${authDomain}" is not this brand's host "${brandHost}": `
|
|
217
|
+
+ `authDomain must be the host the site is served from, which self-hosts /__/auth/* `
|
|
218
|
+
+ `(docs/shared/config.md → "authDomain is the brand's own host")`,
|
|
219
|
+
];
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* The features catalog and the values each product names against it (#647).
|
|
224
|
+
*
|
|
225
|
+
* A feature is defined ONCE, at the top level, and a product names only its
|
|
226
|
+
* VALUE — which means the two halves have to agree or the promise a page
|
|
227
|
+
* renders is fiction. Three ways they can disagree, each an error:
|
|
228
|
+
* - a value on an id the catalog does not define reads as NOTHING (the same
|
|
229
|
+
* silence a retired key used to buy), so the row simply vanishes;
|
|
230
|
+
* - a NUMBER on a perk claims a meter that will never count, and the account
|
|
231
|
+
* page would draw a usage bar against a limit no gate enforces;
|
|
232
|
+
* - a PERK value (true / a string) on a counted feature leaves the gate with
|
|
233
|
+
* no number, which reads as zero — the plan advertises the feature and
|
|
234
|
+
* every call refuses it.
|
|
235
|
+
* Plus the catalog's own shape: an entry needs the `name` every surface
|
|
236
|
+
* prints, and a `usage` block only pages by day or not at all.
|
|
237
|
+
*
|
|
238
|
+
* `false` is legal on ANY feature: the tier does not include it, counted or
|
|
239
|
+
* not, which is what an absent value means too.
|
|
240
|
+
*
|
|
241
|
+
* @param {object} config - The resolved config object.
|
|
242
|
+
* @returns {string[]} Errors; empty when the config passes.
|
|
243
|
+
*/
|
|
244
|
+
function validateFeatures(config) {
|
|
245
|
+
const errors = [];
|
|
246
|
+
const catalog = config ? getPath(config, 'features') : undefined;
|
|
247
|
+
|
|
248
|
+
if (isPresent(catalog) && !isPlainObject(catalog)) {
|
|
249
|
+
return [`config.features must be a map of feature id → definition — got ${Array.isArray(catalog) ? 'array' : typeof catalog}`];
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const entries = isPlainObject(catalog) ? catalog : {};
|
|
253
|
+
|
|
254
|
+
Object.entries(entries).forEach(([id, entry]) => {
|
|
255
|
+
if (!isPlainObject(entry)) {
|
|
256
|
+
errors.push(`config.features.${id} must be an object — { name, icon, definition, usage? }`);
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (typeof entry.name !== 'string' || !entry.name.trim()) {
|
|
261
|
+
errors.push(`config.features.${id}.name is required — it is the label every surface prints (pricing rows, comparison matrix, account usage bars)`);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (entry.usage === undefined) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (!isPlainObject(entry.usage)) {
|
|
269
|
+
errors.push(`config.features.${id}.usage must be an object — { pace: 'daily' | false, mirror: ['<doc kind>'] }; omit it entirely to make ${id} a perk`);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (entry.usage.pace !== undefined && entry.usage.pace !== 'daily' && entry.usage.pace !== false) {
|
|
274
|
+
errors.push(
|
|
275
|
+
`config.features.${id}.usage.pace must be 'daily' (the default) or false — a month limit is either spread over the days of the month or spent whenever the user likes`,
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (entry.usage.mirror !== undefined
|
|
280
|
+
&& (!Array.isArray(entry.usage.mirror) || entry.usage.mirror.some((kind) => typeof kind !== 'string'))) {
|
|
281
|
+
errors.push(`config.features.${id}.usage.mirror must be an array of document kinds — e.g. ['teams']`);
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
const products = config ? getPath(config, 'payment.products') : undefined;
|
|
286
|
+
|
|
287
|
+
if (!Array.isArray(products)) {
|
|
288
|
+
return errors;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
products.forEach((product, index) => {
|
|
292
|
+
const values = isPlainObject(product) ? product.features : undefined;
|
|
293
|
+
const label = `config.payment.products[${index}] (${(product && product.id) || 'unnamed'})`;
|
|
294
|
+
|
|
295
|
+
if (Array.isArray(values)) {
|
|
296
|
+
errors.push(
|
|
297
|
+
`${label} features must be a map of values, not a list (#647) — `
|
|
298
|
+
+ `write \`features: { saves: 100, support: true }\` and define each feature ONCE in the top-level \`features\` catalog`,
|
|
299
|
+
);
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (values === undefined || values === null) {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (!isPlainObject(values)) {
|
|
308
|
+
errors.push(`${label} features must be a map of feature id → value — got ${typeof values}`);
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
Object.entries(values).forEach(([id, value]) => {
|
|
313
|
+
const entry = entries[id];
|
|
314
|
+
|
|
315
|
+
if (!entry) {
|
|
316
|
+
errors.push(
|
|
317
|
+
`${label} names the feature "${id}", which the top-level features catalog does not define — `
|
|
318
|
+
+ `add it to \`features\` (nothing reads a value with no definition behind it)`,
|
|
319
|
+
);
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Not included is not a claim: `false` says so on any kind of feature.
|
|
324
|
+
if (value === false) {
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
if (isCountedFeature(entry)) {
|
|
329
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
330
|
+
errors.push(
|
|
331
|
+
`${label} gives "${id}" the value ${JSON.stringify(value)}, but ${id} is a counted feature `
|
|
332
|
+
+ `(it carries a \`usage\` block) — its value is the MONTHLY limit as a number, or -1 for unlimited`,
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (typeof value === 'number') {
|
|
339
|
+
errors.push(
|
|
340
|
+
`${label} gives "${id}" the number ${value}, but ${id} is not a counted feature `
|
|
341
|
+
+ `(no \`usage\` block in the catalog) — give it true or a string, or add \`usage: {}\` to \`features.${id}\` to meter it`,
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
return errors;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* The LEAF paths of a resolved config the schema does not declare (#636).
|
|
352
|
+
*
|
|
353
|
+
* A rule declares its own path AND everything beneath it — an `object`/`array`
|
|
354
|
+
* rule is a declared subtree (brand.address's postal fields, an open provider
|
|
355
|
+
* map), which is what keeps a brand's own data out of this list. An empty
|
|
356
|
+
* object/array is itself a leaf, declared when the schema declares anything
|
|
357
|
+
* below it (`certificates: {}` against certificates.enabled).
|
|
358
|
+
*
|
|
359
|
+
* The `targets` subtree is skipped whole: those keys belong to a framework or,
|
|
360
|
+
* for a custom target (#603), to the brand — the targets-sanity check above is
|
|
361
|
+
* what polices that namespace.
|
|
362
|
+
*
|
|
363
|
+
* @param {object} config - The resolved config object.
|
|
364
|
+
* @param {object[]} schema - Rule array in the schema.js entry format.
|
|
365
|
+
* @returns {string[]} Undeclared leaf paths, in config order.
|
|
366
|
+
*/
|
|
367
|
+
function findUndeclaredPaths(config, schema) {
|
|
368
|
+
const declared = schema.map((rule) => rule.path);
|
|
369
|
+
const found = [];
|
|
370
|
+
|
|
371
|
+
const walk = (value, path) => {
|
|
372
|
+
if (isPlainObject(value) && Object.keys(value).length > 0) {
|
|
373
|
+
Object.keys(value).forEach((key) => walk(value[key], `${path}.${key}`));
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
if (!declared.some((rule) => rule === path || path.startsWith(`${rule}.`) || rule.startsWith(`${path}.`))) {
|
|
377
|
+
found.push(path);
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
Object.keys(isPlainObject(config) ? config : {})
|
|
382
|
+
.filter((key) => key !== 'targets')
|
|
383
|
+
.forEach((key) => walk(config[key], key));
|
|
384
|
+
|
|
385
|
+
return found;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Validate a resolved config: shared schema + optional target refinements +
|
|
390
|
+
* targets-key sanity + secret-shaped-key detection.
|
|
391
|
+
* @param {object} config - The resolved config object.
|
|
392
|
+
* @param {object} [options]
|
|
393
|
+
* @param {string} [options.target] - Canonical target name; adds TARGET_SCHEMAS[target].
|
|
394
|
+
* @returns {{ errors: string[], warnings: string[] }}
|
|
395
|
+
*/
|
|
396
|
+
function validateConfig(config, options) {
|
|
397
|
+
options = options || {};
|
|
398
|
+
|
|
399
|
+
if (options.target && !TARGETS.includes(options.target)) {
|
|
400
|
+
throw new Error(`Unknown target "${options.target}" — must be one of [${TARGETS.join(', ')}]`);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const schema = options.target
|
|
404
|
+
? [...SHARED_SCHEMA, ...TARGET_SCHEMAS[options.target]]
|
|
405
|
+
: SHARED_SCHEMA;
|
|
406
|
+
|
|
407
|
+
const errors = runSchema(config, schema);
|
|
408
|
+
const warnings = [];
|
|
409
|
+
|
|
410
|
+
// ─── targets sanity ────────────────────────────────────────────────────
|
|
411
|
+
const targets = config ? config.targets : undefined;
|
|
412
|
+
if (isPlainObject(targets)) {
|
|
413
|
+
Object.keys(targets).forEach((key) => {
|
|
414
|
+
const entry = targets[key];
|
|
415
|
+
const framework = TARGETS.includes(key);
|
|
416
|
+
|
|
417
|
+
// A key no framework owns is legal ONLY as a declared custom target
|
|
418
|
+
// (#603) — that declaration is what separates a deliberate Render API or
|
|
419
|
+
// worker from a typo'd framework name. The framework keys are the
|
|
420
|
+
// inverse: they may never claim the custom type, since their verbs come
|
|
421
|
+
// from their framework, not from package.json scripts.
|
|
422
|
+
if (!framework && !isCustomTargetEntry(entry)) {
|
|
423
|
+
errors.push(
|
|
424
|
+
`config.targets.${key} is not a known target — must be one of [${TARGETS.join(', ')}], `
|
|
425
|
+
+ `or declare \`type: 'custom'\` (every instance of the array form) to run through its own package.json scripts`,
|
|
426
|
+
);
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
if (framework && isCustomTargetEntry(entry)) {
|
|
430
|
+
errors.push(`config.targets.${key} is a framework target — it cannot declare \`type: 'custom'\``);
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// Multi-instance array form: every entry MUST carry a unique dir-safe
|
|
435
|
+
// id (it names the targets/<canonical>-<id> dir). Backend stays single-
|
|
436
|
+
// instance in practice — >1 is a warning, not an error (spec v1).
|
|
437
|
+
if (Array.isArray(entry)) {
|
|
438
|
+
if (entry.length === 0) {
|
|
439
|
+
errors.push(`config.targets.${key} instance array must not be empty — use {} for a single default instance`);
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
const seen = new Set();
|
|
444
|
+
entry.forEach((instance, index) => {
|
|
445
|
+
if (!isPlainObject(instance)) {
|
|
446
|
+
errors.push(`config.targets.${key}[${index}] must be an instance object — got ${Array.isArray(instance) ? 'array' : typeof instance}`);
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
if (typeof instance.id !== 'string' || !INSTANCE_ID_PATTERN.test(instance.id)) {
|
|
450
|
+
errors.push(`config.targets.${key}[${index}] must carry a dir-safe id (lowercase, starts with a letter, alnum/-) — it names targets/<dir>-<id>`);
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
if (seen.has(instance.id)) {
|
|
454
|
+
errors.push(`config.targets.${key} instance id "${instance.id}" is not unique — ids must be unique per target type`);
|
|
455
|
+
}
|
|
456
|
+
seen.add(instance.id);
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
if (key === 'backend' && entry.length > 1) {
|
|
460
|
+
warnings.push(`config.targets.backend has ${entry.length} instances — multi-instance backend is unsupported for now (Cloud Functions = one project surface per brand)`);
|
|
461
|
+
}
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
if (!isPlainObject(entry)) {
|
|
466
|
+
errors.push(`config.targets.${key} must be an object ({} = enabled with defaults) or an array of id'd instances — got ${typeof entry}`);
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// ─── authDomain is the brand's own host (cp268) ────────────────────────
|
|
472
|
+
validateAuthDomain(config).forEach((error) => errors.push(error));
|
|
473
|
+
|
|
474
|
+
// ─── the winback offer is ONE shape (#268) ─────────────────────────────
|
|
475
|
+
// A coupon is percent-based or amount-based everywhere in the payment stack,
|
|
476
|
+
// and the schema walker checks fields one at a time, so the pair rule lives
|
|
477
|
+
// here. Two shapes on one offer has no honest reading — a provider would
|
|
478
|
+
// have to pick — so it fails the config instead.
|
|
479
|
+
const winback = config ? getPath(config, 'payment.winback') : undefined;
|
|
480
|
+
if (isPlainObject(winback) && typeof winback.percent === 'number' && typeof winback.amount === 'number') {
|
|
481
|
+
errors.push('config.payment.winback sets both percent and amount — a save offer is one shape or the other, never both');
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// ─── a product price has ONE spelling (#674) ───────────────────────────
|
|
485
|
+
// Every reader takes the bare number: the intent route's confirmation URL,
|
|
486
|
+
// all three provider libraries' resolvePrice(), the checkout page's own
|
|
487
|
+
// summary math. The checkout's resolvers ALSO unwrapped `{ amount: N }`, so
|
|
488
|
+
// an object-shaped price rendered a real total on the page and put
|
|
489
|
+
// `[object Object]` in the confirmation URL's `amount` — the two sides
|
|
490
|
+
// disagreeing about the same catalog entry. There is no shared resolver to
|
|
491
|
+
// put the shape in (the browser bundle cannot reach a build-time package, and
|
|
492
|
+
// the deployed backend does not carry one either), so the shape is settled
|
|
493
|
+
// HERE, in the one place both sides' catalog comes from.
|
|
494
|
+
const products = config ? getPath(config, 'payment.products') : undefined;
|
|
495
|
+
if (Array.isArray(products)) {
|
|
496
|
+
products.forEach((product, index) => {
|
|
497
|
+
const prices = isPlainObject(product) ? product.prices : undefined;
|
|
498
|
+
|
|
499
|
+
if (!isPlainObject(prices)) {
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
Object.entries(prices).forEach(([key, value]) => {
|
|
504
|
+
if (typeof value !== 'number') {
|
|
505
|
+
errors.push(
|
|
506
|
+
`config.payment.products[${index}] (${product.id || 'unnamed'}) price "${key}" must be a number `
|
|
507
|
+
+ `— write \`${key}: 9.99\`, never \`${key}: { amount: 9.99 }\` (the two sides do not read the object shape the same)`,
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
});
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// ─── the features catalog and the values products name (#647) ──────────
|
|
515
|
+
validateFeatures(config).forEach((error) => errors.push(error));
|
|
516
|
+
|
|
517
|
+
// ─── undeclared paths (#636) ───────────────────────────────────────────
|
|
518
|
+
// A warning, never an error: a brand config outliving one framework version
|
|
519
|
+
// must still build, and the finding is what closes the gap — either the key
|
|
520
|
+
// is dead, or the schema owes it a rule.
|
|
521
|
+
const undeclared = findUndeclaredPaths(config, schema);
|
|
522
|
+
if (undeclared.length > 0) {
|
|
523
|
+
warnings.push(
|
|
524
|
+
'config carries keys the schema does not declare — nothing reads them, so a typo looks exactly like a '
|
|
525
|
+
+ `feature. Remove them, or give each one a rule in @omega.js/config's schema.js `
|
|
526
|
+
+ `(docs/shared/config.md → Validation): ${undeclared.join(', ')}`,
|
|
527
|
+
);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// ─── retired keys (#142) ───────────────────────────────────────────────
|
|
531
|
+
findRetiredKeys(config).forEach(({ path, replacement, why }) => {
|
|
532
|
+
errors.push(
|
|
533
|
+
`config.${path} is retired — the key is now "${replacement}" (${why}). `
|
|
534
|
+
+ `Rename it; there is no dual-read, so the old name is silently ignored `
|
|
535
|
+
+ `(docs/shared/config.md → "Migration — legacy configs")`,
|
|
536
|
+
);
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
// ─── secrets ───────────────────────────────────────────────────────────
|
|
540
|
+
findSecretKeys(config).forEach((keyPath) => {
|
|
541
|
+
errors.push(`config.${keyPath} looks like a secret — secrets live in .env, never in omega.json5`);
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
return { errors, warnings };
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// Render a numbered, human-readable error block. Used by callers that want to
|
|
548
|
+
// throw a single Error with all problems collected.
|
|
549
|
+
function formatErrors(errors) {
|
|
550
|
+
if (!errors.length) return '';
|
|
551
|
+
return errors.map((e, i) => ` ${i + 1}. ${e}`).join('\n');
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
module.exports = { validateConfig, runSchema, formatErrors, getPath, resolvedBrandHost };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* winback.js — the cancel-flow save offer, resolved once
|
|
3
|
+
* ([#268](https://github.com/Omega-JS-Stack/omega/issues/268)).
|
|
4
|
+
*
|
|
5
|
+
* When a customer starts cancelling, the billing card pitches a discount on the
|
|
6
|
+
* next cycle before it asks them why they are leaving. What that offer IS is the
|
|
7
|
+
* brand's call (Ian, 2026-08-15: "the offer must be CONFIGURABLE in omega.json5,
|
|
8
|
+
* offer content/numbers per brand, not hardcoded"), and a brand that writes
|
|
9
|
+
* nothing at all still makes one: 50% off the next cycle, one cycle only.
|
|
10
|
+
*
|
|
11
|
+
* That default lives HERE and nowhere else. The backend's apply route resolves
|
|
12
|
+
* `Manager.config.payment` through this function, and the web build bakes the
|
|
13
|
+
* SAME call into the client blob it emits — so the dialog the customer reads and
|
|
14
|
+
* the coupon the provider creates can never disagree about the number.
|
|
15
|
+
*
|
|
16
|
+
* The offer's shape mirrors a discount code exactly (see the backend's
|
|
17
|
+
* libraries/payment/discount-codes.js): percent-based OR amount-based, never
|
|
18
|
+
* both, and the absent shape is OMITTED rather than set to undefined, because a
|
|
19
|
+
* resolved offer is written into Firestore on the order doc and firebase-admin
|
|
20
|
+
* refuses a document carrying an undefined value.
|
|
21
|
+
*
|
|
22
|
+
* `enabled` is the whole off switch and it defaults ON: the offer exists for
|
|
23
|
+
* every brand until one turns it off (`payment.winback.enabled: false`). A
|
|
24
|
+
* disabled offer still RESOLVES its numbers — one shape for every caller, one
|
|
25
|
+
* flag to read — it is simply never made.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
const { WINBACK_DURATIONS } = require('./schema.js');
|
|
29
|
+
const { schemaDefaults } = require('./defaults.js');
|
|
30
|
+
|
|
31
|
+
// What a brand that configures nothing gets — DERIVED from the schema's own
|
|
32
|
+
// `payment.winback` defaults (#478: one home for every default, and the block
|
|
33
|
+
// materializes into the brand file on the next manage run). `duration: 'once'`
|
|
34
|
+
// is the next cycle only; 'forever' is the brand asking for a permanent price
|
|
35
|
+
// cut.
|
|
36
|
+
const WINBACK_OFFER_DEFAULTS = schemaDefaults().payment.winback;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The save offer a `payment` section makes, with the framework default applied.
|
|
40
|
+
*
|
|
41
|
+
* @param {object} [payment] - The resolved config's `payment` section.
|
|
42
|
+
* @returns {{ enabled: boolean, percent?: number, amount?: number, duration: string }}
|
|
43
|
+
*/
|
|
44
|
+
function resolveWinbackOffer(payment) {
|
|
45
|
+
const configured = (payment && payment.winback) || {};
|
|
46
|
+
|
|
47
|
+
// A brand naming an amount is making an amount offer, and it carries no
|
|
48
|
+
// percent at all — not even the default one, which would otherwise ride along
|
|
49
|
+
// and give a provider two shapes to choose between.
|
|
50
|
+
const shape = typeof configured.amount === 'number'
|
|
51
|
+
? { amount: configured.amount }
|
|
52
|
+
: { percent: typeof configured.percent === 'number' ? configured.percent : WINBACK_OFFER_DEFAULTS.percent };
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
enabled: configured.enabled !== false,
|
|
56
|
+
...shape,
|
|
57
|
+
duration: configured.duration || WINBACK_OFFER_DEFAULTS.duration,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
module.exports = { resolveWinbackOffer, WINBACK_OFFER_DEFAULTS, WINBACK_DURATIONS };
|