Full Code of novuhq/novu for AI

next 3a807619a630 cached
6554 files
20.9 MB
5.9M tokens
18882 symbols
5 requests
Copy disabled (too large) Download .txt
Showing preview only (23,525K chars total). Download the full file to get everything.
Repository: novuhq/novu
Branch: next
Commit: 3a807619a630
Files: 6554
Total size: 20.9 MB

Directory structure:
gitextract_65y55uur/

├── .agents/
│   └── skills/
│       ├── email-best-practices/
│       │   ├── SKILL.md
│       │   └── resources/
│       │       ├── branding.md
│       │       ├── compliance.md
│       │       ├── deliverability.md
│       │       ├── email-capture.md
│       │       ├── email-types.md
│       │       ├── list-management.md
│       │       ├── marketing-emails.md
│       │       ├── sending-reliability.md
│       │       ├── transactional-email-catalog.md
│       │       ├── transactional-emails.md
│       │       └── webhooks-events.md
│       └── react-email/
│           ├── SKILL.md
│           ├── TESTS.md
│           └── references/
│               ├── COMPONENTS.md
│               ├── I18N.md
│               ├── PATTERNS.md
│               ├── SENDING.md
│               └── STYLING.md
├── .claude/
│   └── skills/
│       └── better-auth-best-practices/
│           └── SKILL.md
├── .coderabbit.yaml
├── .copilotignore
├── .cursor/
│   ├── Dockerfile
│   ├── agents/
│   │   ├── impact-checker.md
│   │   └── verifier.md
│   ├── commands/
│   │   ├── code-review-checklist.md
│   │   └── create-pr.md
│   ├── rules/
│   │   ├── api-property-optionality-hygiene.mdc
│   │   ├── api.mdc
│   │   ├── clickhouse.mdc
│   │   ├── context-engineering.mdc
│   │   ├── dal-repository.mdc
│   │   ├── dashboard.mdc
│   │   ├── dependency-graph.mdc
│   │   ├── infrastructure.mdc
│   │   ├── novu.mdc
│   │   ├── packages.mdc
│   │   ├── pullrequest.mdc
│   │   ├── testing.mdc
│   │   ├── worker.mdc
│   │   └── ws.mdc
│   ├── scripts/
│   │   └── dead-code/
│   │       ├── knip.config.jsonc
│   │       └── scan.sh
│   ├── settings.json
│   └── skills/
│       ├── better-auth-best-practices/
│       │   └── SKILL.md
│       ├── enterprise-submodule/
│       │   └── SKILL.md
│       └── run-api-e2e-tests/
│           └── SKILL.md
├── .cursorignore
├── .deepsource.toml
├── .devcontainer/
│   ├── Dockerfile
│   ├── devcontainer.json
│   └── docker-compose.yml
├── .editorconfig
├── .github/
│   ├── CODEOWNERS
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.yml
│   │   ├── docs_feedback.yml
│   │   ├── feature_request.yml
│   │   └── polishing.yml
│   ├── ISSUE_TEMPLATE.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── actions/
│   │   ├── cache/
│   │   │   └── action.yml
│   │   ├── checkout-submodules/
│   │   │   └── action.yml
│   │   ├── free-space/
│   │   │   └── action.yml
│   │   ├── run-api/
│   │   │   └── action.yml
│   │   ├── run-backend/
│   │   │   └── action.yml
│   │   ├── setup-project/
│   │   │   └── action.yml
│   │   ├── setup-project-minimal/
│   │   │   └── action.yml
│   │   ├── setup-redis-cluster/
│   │   │   └── action.yml
│   │   ├── slack-notify-on-failure/
│   │   │   └── action.yml
│   │   ├── start-localstack/
│   │   │   └── action.yml
│   │   └── validate-openapi/
│   │       └── action.yml
│   ├── labeler.yml
│   └── workflows/
│       ├── check-only.yml
│       ├── check-submodule-sync-merge.yaml
│       ├── check-submodule-sync-pr.yaml
│       ├── codeql-analysis.yml
│       ├── community-label.yml
│       ├── contributor-checks.yml
│       ├── conventional-commit.yml
│       ├── deploy.yml
│       ├── deployment-summary.yml
│       ├── dev-deploy-dashboard.yml
│       ├── dev-deploy-inbound-mail.yml
│       ├── issue-label.yml
│       ├── jarvis.yml
│       ├── on-pr-change.yml
│       ├── on-pr.yml
│       ├── on-push-trigger.yml
│       ├── pr-labeler.yml
│       ├── pr-manager.yml
│       ├── prepare-cloud-release.yaml
│       ├── prepare-enterprise-self-hosted-release.yml
│       ├── prepare-self-hosted-release.yml
│       ├── preview-packages.yml
│       ├── prod-deploy-inbound-mail.yml
│       ├── release-packages.yml
│       ├── reusable-api-e2e.yml
│       ├── reusable-dashboard-deploy.yml
│       ├── reusable-dashboard-e2e.yml
│       ├── reusable-inbound-mail-e2e.yml
│       ├── reusable-webhook-e2e.yml
│       ├── reusable-worker-e2e.yml
│       ├── reusable-ws-e2e.yml
│       ├── rollback.yml
│       └── scripts/
│           ├── add-triage-label.js
│           ├── community-contribution-label.js
│           ├── is-community-contributor.js
│           ├── stop-only.sh
│           └── validate-submodule-sync.sh
├── .gitignore
├── .gitmodules
├── .husky/
│   └── pre-commit
├── .idea/
│   ├── .gitignore
│   ├── aws.xml
│   ├── codeStyles/
│   │   ├── Project.xml
│   │   └── codeStyleConfig.xml
│   ├── discord.xml
│   ├── inspectionProfiles/
│   │   └── Project_Default.xml
│   ├── jsLibraryMappings.xml
│   ├── modules.xml
│   ├── novu.iml
│   ├── nx-angular-config.xml
│   ├── runConfigurations/
│   │   ├── API.xml
│   │   ├── API___TEST.xml
│   │   ├── APPLICATION_GENERIC.xml
│   │   ├── DAL.xml
│   │   ├── DAL2.xml
│   │   ├── DOCS.xml
│   │   ├── EE_AUTH.xml
│   │   ├── EMBED.xml
│   │   ├── RUN_LOCAL_ENV.xml
│   │   ├── RUN_TEST_ENV.xml
│   │   ├── SHARED.xml
│   │   ├── SHARED_WEB.xml
│   │   ├── TESTING.xml
│   │   ├── WEB.xml
│   │   ├── WEBHOOK.xml
│   │   ├── WEB___CYPRESS.xml
│   │   ├── WIDGET.xml
│   │   ├── WIDGET_CLI.xml
│   │   ├── WIDGET___CYPRESS.xml
│   │   ├── WIDGET___TEST.xml
│   │   ├── WORKER.xml
│   │   ├── WORKER___TEST.xml
│   │   ├── WS.xml
│   │   ├── WS___TEST.xml
│   │   └── _template__of_Mocha.xml
│   ├── swagger-settings.xml
│   └── vcs.xml
├── .markdownlint.jsonc
├── .npmrc
├── .npmrc-cloud
├── .nvmrc
├── .nxignore
├── .vscode/
│   ├── extensions.json
│   ├── launch.json
│   ├── settings.json
│   └── tasks.json
├── AGENTS.md
├── CITATION.cff
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── EE-PACKAGES-LICENSE
├── LICENSE-ENTERPRISE
├── LICENSE-MIT
├── README.md
├── SECURITY.md
├── _templates/
│   ├── module/
│   │   └── new/
│   │       ├── controller.ejs.t
│   │       ├── module.ejs.t
│   │       ├── prompt.ejs.t
│   │       └── usecase-index.ejs.t
│   └── usecase/
│       └── new/
│           ├── command.ejs.t
│           ├── import-inject.ejs.t
│           ├── import-row-inject.ejs.t
│           ├── prompt.ejs.t
│           └── usecase.ejs.t
├── apps/
│   ├── api/
│   │   ├── .gitignore
│   │   ├── .mocharc.json
│   │   ├── .spectral.yaml
│   │   ├── .swcrc
│   │   ├── .vscode/
│   │   │   └── settings.json
│   │   ├── Dockerfile
│   │   ├── README.md
│   │   ├── admin/
│   │   │   ├── connect-to-dal.ts
│   │   │   ├── make-json-backup.ts
│   │   │   ├── remove-organization.ts
│   │   │   └── remove-user-account.ts
│   │   ├── e2e/
│   │   │   ├── compile-email-template.e2e.ts
│   │   │   ├── enterprise/
│   │   │   │   └── inbound-webhook/
│   │   │   │       └── process-inbound-webhook.e2e.ts
│   │   │   ├── mock-http-client.ts
│   │   │   ├── retry.e2e.ts
│   │   │   ├── setup.ts
│   │   │   └── test-bridge-server.ts
│   │   ├── exportOpenAPIJSON.ts
│   │   ├── jarvis-api-intro.md
│   │   ├── migrations/
│   │   │   ├── 001-add-default-identifier-to-topic-subscribers/
│   │   │   │   ├── add-default-identifier-to-topic-subscribers-migration.spec.ts
│   │   │   │   └── add-default-identifier-to-topic-subscribers-migration.ts
│   │   │   ├── 002-remove-duplicate-identifiers/
│   │   │   │   ├── remove-duplicate-identifiers.spec.ts
│   │   │   │   └── remove-duplicate-identifiers.ts
│   │   │   ├── add-layout-id-to-email-controls/
│   │   │   │   ├── add-layout-id-to-email-controls-migration.spec.ts
│   │   │   │   └── add-layout-id-to-email-controls-migration.ts
│   │   │   ├── changes-migration.ts
│   │   │   ├── clickhouse-migrations/
│   │   │   │   ├── 1_initial_schema.sql
│   │   │   │   ├── 2_add_workflow_id_to_schema.sql
│   │   │   │   ├── 3_analytics_tables.sql
│   │   │   │   ├── 4_refactor_traces_schema.sql
│   │   │   │   ├── 5_finalize_table_exchange.sql
│   │   │   │   └── README.md
│   │   │   ├── deleteLogs/
│   │   │   │   └── dropLogsCollection.ts
│   │   │   ├── email-step-ui-schema-html-editor/
│   │   │   │   ├── email-step-ui-schema-html-editor-migration.spec.ts
│   │   │   │   └── email-step-ui-schema-html-editor-migration.ts
│   │   │   ├── encrypt-api-keys/
│   │   │   │   ├── encrypt-api-keys-migration.spec.ts
│   │   │   │   └── encrypt-api-keys-migration.ts
│   │   │   ├── encrypt-credentials/
│   │   │   │   ├── encrypt-credentials-migration.spec.ts
│   │   │   │   └── encrypt-credentials-migration.ts
│   │   │   ├── expire-at/
│   │   │   │   ├── expire-at-delay.migration.spec.ts
│   │   │   │   ├── expire-at.migration.spec.ts
│   │   │   │   └── expire-at.migration.ts
│   │   │   ├── fcm-credentials/
│   │   │   │   ├── fcm-credentials-migration.spec.ts
│   │   │   │   └── fcm-credentials-migration.ts
│   │   │   ├── in-app-integration/
│   │   │   │   └── in-app-integration.migration.ts
│   │   │   ├── integration-scheme-update/
│   │   │   │   ├── add-integration-identifier-migration.spec.ts
│   │   │   │   ├── add-integration-identifier-migration.ts
│   │   │   │   ├── add-primary-priority-migration.ts
│   │   │   │   └── update-primary-for-disabled-novu-integrations.ts
│   │   │   ├── layout-identifier-update/
│   │   │   │   ├── add-layout-identifier-migration.spec.ts
│   │   │   │   └── add-layout-identifier-migration.ts
│   │   │   ├── normalize-message-template-cta-action/
│   │   │   │   ├── normalize-message-cta-action-migration.ts
│   │   │   │   ├── normalize-message-template-cta-action-migration.spec.ts
│   │   │   │   └── normalize-message-template-cta-action-migration.ts
│   │   │   ├── normalize-users-email/
│   │   │   │   └── normalize-users-email.migration.ts
│   │   │   ├── novu-integrations/
│   │   │   │   └── novu-integrations.migration.ts
│   │   │   ├── preference-centralization/
│   │   │   │   ├── preference-centralization-migration.spec.ts
│   │   │   │   └── preference-centralization-migration.ts
│   │   │   ├── preferences-uniqueness/
│   │   │   │   ├── preferences-uniqueness-migration.spec.ts
│   │   │   │   └── preferences-uniqueness-migration.ts
│   │   │   ├── secure-to-boolean/
│   │   │   │   ├── secure-to-boolean-migration.spec.ts
│   │   │   │   └── secure-to-boolean-migration.ts
│   │   │   ├── seen-read-support/
│   │   │   │   ├── seen-read-support.migration.spec.ts
│   │   │   │   └── seen-read-support.migration.ts
│   │   │   ├── subscribers/
│   │   │   │   └── remove-duplicated-subscribers/
│   │   │   │       ├── remove-duplicated-subscribers.migration.spec.ts
│   │   │   │       └── remove-duplicated-subscribers.migration.ts
│   │   │   └── topic-subscriber-normalize/
│   │   │       ├── topic-subscriber-normalize.migration.spec.ts
│   │   │       └── topic-subscriber-normalize.migration.ts
│   │   ├── nest-cli.json
│   │   ├── package.json
│   │   ├── project.json
│   │   ├── scripts/
│   │   │   ├── check-api-property-optionality.ts
│   │   │   ├── clickhouse-seeder/
│   │   │   │   ├── README.md
│   │   │   │   ├── config.ts
│   │   │   │   ├── generators.ts
│   │   │   │   ├── inserter.ts
│   │   │   │   └── time-distribution.ts
│   │   │   ├── generate-metadata.ts
│   │   │   ├── run-novu-v2-e2e-shard.cjs
│   │   │   ├── seed-clickhouse.ts
│   │   │   └── seed-triggers.ts
│   │   ├── src/
│   │   │   ├── .example.env
│   │   │   ├── app/
│   │   │   │   ├── activity/
│   │   │   │   │   ├── activity.controller.ts
│   │   │   │   │   ├── activity.module.ts
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── get-charts.request.dto.ts
│   │   │   │   │   │   ├── get-charts.response.dto.ts
│   │   │   │   │   │   ├── get-request.request.dto.ts
│   │   │   │   │   │   ├── get-request.response.dto.ts
│   │   │   │   │   │   ├── get-requests.dto.ts
│   │   │   │   │   │   ├── get-requests.response.dto.ts
│   │   │   │   │   │   ├── shared.dto.ts
│   │   │   │   │   │   ├── workflow-run-response.dto.ts
│   │   │   │   │   │   ├── workflow-runs-request.dto.ts
│   │   │   │   │   │   └── workflow-runs-response.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── get-requests.e2e.ts
│   │   │   │   │   │   ├── get-workflow-run.e2e.ts
│   │   │   │   │   │   └── get-workflow-runs.e2e.ts
│   │   │   │   │   ├── shared/
│   │   │   │   │   │   ├── mappers.ts
│   │   │   │   │   │   └── select.const.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── build-active-subscribers-chart/
│   │   │   │   │       │   ├── build-active-subscribers-chart.command.ts
│   │   │   │   │       │   ├── build-active-subscribers-chart.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── build-active-subscribers-trend-chart/
│   │   │   │   │       │   ├── build-active-subscribers-trend-chart.command.ts
│   │   │   │   │       │   ├── build-active-subscribers-trend-chart.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── build-avg-messages-per-subscriber-chart/
│   │   │   │   │       │   ├── build-avg-messages-per-subscriber-chart.command.ts
│   │   │   │   │       │   ├── build-avg-messages-per-subscriber-chart.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── build-delivery-trend-chart/
│   │   │   │   │       │   ├── build-delivery-trend-chart.command.ts
│   │   │   │   │       │   ├── build-delivery-trend-chart.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── build-interaction-trend-chart/
│   │   │   │   │       │   ├── build-interaction-trend-chart.command.ts
│   │   │   │   │       │   ├── build-interaction-trend-chart.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── build-messages-delivered-chart/
│   │   │   │   │       │   ├── build-messages-delivered-chart.command.ts
│   │   │   │   │       │   ├── build-messages-delivered-chart.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── build-provider-by-volume-chart/
│   │   │   │   │       │   ├── build-provider-by-volume-chart.command.ts
│   │   │   │   │       │   ├── build-provider-by-volume-chart.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── build-total-interactions-chart/
│   │   │   │   │       │   ├── build-total-interactions-chart.command.ts
│   │   │   │   │       │   ├── build-total-interactions-chart.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── build-workflow-by-volume-chart/
│   │   │   │   │       │   ├── build-workflow-by-volume-chart.command.ts
│   │   │   │   │       │   ├── build-workflow-by-volume-chart.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── build-workflow-runs-count-chart/
│   │   │   │   │       │   ├── build-workflow-runs-count-chart.command.ts
│   │   │   │   │       │   ├── build-workflow-runs-count-chart.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── build-workflow-runs-metric-chart/
│   │   │   │   │       │   ├── build-workflow-runs-metric-chart.command.ts
│   │   │   │   │       │   ├── build-workflow-runs-metric-chart.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── build-workflow-runs-trend-chart/
│   │   │   │   │       │   ├── build-workflow-runs-trend-chart.command.ts
│   │   │   │   │       │   ├── build-workflow-runs-trend-chart.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── get-charts/
│   │   │   │   │       │   ├── get-charts.command.ts
│   │   │   │   │       │   └── get-charts.usecase.ts
│   │   │   │   │       ├── get-request/
│   │   │   │   │       │   ├── get-request.command.ts
│   │   │   │   │       │   └── get-request.usecase.ts
│   │   │   │   │       ├── get-requests/
│   │   │   │   │       │   ├── get-requests.command.ts
│   │   │   │   │       │   └── get-requests.usecase.ts
│   │   │   │   │       ├── get-workflow-run/
│   │   │   │   │       │   ├── get-workflow-run.command.ts
│   │   │   │   │       │   └── get-workflow-run.usecase.ts
│   │   │   │   │       └── get-workflow-runs/
│   │   │   │   │           ├── get-workflow-runs.command.ts
│   │   │   │   │           └── get-workflow-runs.usecase.ts
│   │   │   │   ├── analytics/
│   │   │   │   │   ├── analytics.controller.ts
│   │   │   │   │   ├── analytics.module.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       └── hubspot-identify-form/
│   │   │   │   │           ├── hubspot-identify-form.command.ts
│   │   │   │   │           └── hubspot-identify-form.usecase.ts
│   │   │   │   ├── auth/
│   │   │   │   │   ├── auth.controller.ts
│   │   │   │   │   ├── auth.module.ts
│   │   │   │   │   ├── community.auth.module.config.ts
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── login.dto.ts
│   │   │   │   │   │   ├── password-reset.dto.ts
│   │   │   │   │   │   ├── update-password.dto.ts
│   │   │   │   │   │   └── user-registration.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── clerk.strategy.spec.ts
│   │   │   │   │   │   ├── link-entities.service.spec.ts
│   │   │   │   │   │   ├── login.e2e.ts
│   │   │   │   │   │   ├── password-reset.e2e.ts
│   │   │   │   │   │   ├── permissions.guard.e2e.ts
│   │   │   │   │   │   ├── switch-organization.e2e.ts
│   │   │   │   │   │   ├── update-password.e2e.ts
│   │   │   │   │   │   ├── user-registration.e2e.ts
│   │   │   │   │   │   └── user.auth.guard.e2e.ts
│   │   │   │   │   ├── ee.auth.module.config.ts
│   │   │   │   │   ├── framework/
│   │   │   │   │   │   ├── auth.decorator.ts
│   │   │   │   │   │   ├── community.user.auth.guard.ts
│   │   │   │   │   │   ├── external-api.decorator.ts
│   │   │   │   │   │   └── root-environment-guard.service.ts
│   │   │   │   │   ├── services/
│   │   │   │   │   │   ├── auth.service.ts
│   │   │   │   │   │   ├── community.auth.service.ts
│   │   │   │   │   │   └── passport/
│   │   │   │   │   │       ├── apikey.strategy.ts
│   │   │   │   │   │       ├── github.strategy.ts
│   │   │   │   │   │       ├── jwt.strategy.ts
│   │   │   │   │   │       ├── newrelic.util.ts
│   │   │   │   │   │       └── subscriber-jwt.strategy.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── index.ts
│   │   │   │   │       ├── login/
│   │   │   │   │       │   ├── login.command.ts
│   │   │   │   │       │   └── login.usecase.ts
│   │   │   │   │       ├── password-reset/
│   │   │   │   │       │   ├── password-reset.command.ts
│   │   │   │   │       │   └── password-reset.usecase.ts
│   │   │   │   │       ├── password-reset-request/
│   │   │   │   │       │   ├── password-reset-request.command.ts
│   │   │   │   │       │   └── password-reset-request.usecase.ts
│   │   │   │   │       ├── register/
│   │   │   │   │       │   ├── user-register.command.ts
│   │   │   │   │       │   └── user-register.usecase.ts
│   │   │   │   │       ├── switch-environment/
│   │   │   │   │       │   ├── index.ts
│   │   │   │   │       │   ├── switch-environment.command.ts
│   │   │   │   │       │   └── switch-environment.usecase.ts
│   │   │   │   │       ├── switch-organization/
│   │   │   │   │       │   ├── index.ts
│   │   │   │   │       │   ├── switch-organization.command.ts
│   │   │   │   │       │   └── switch-organization.usecase.ts
│   │   │   │   │       └── update-password/
│   │   │   │   │           ├── update-password.command.ts
│   │   │   │   │           └── update-password.usecase.ts
│   │   │   │   ├── billing/
│   │   │   │   │   └── e2e/
│   │   │   │   │       ├── checkout-session-completed.e2e-ee.ts
│   │   │   │   │       ├── create-checkout-session.e2e-ee.ts
│   │   │   │   │       ├── create-subscription.e2e-ee.ts
│   │   │   │   │       ├── create-usage-records.e2e-ee.ts
│   │   │   │   │       ├── customer-subscription-created.e2e-ee.ts
│   │   │   │   │       ├── customer-subscription-deleted.e2e-ee.ts
│   │   │   │   │       ├── get-event-resource-limit.e2e-ee.ts
│   │   │   │   │       ├── get-platform-notification-usage.e2e-ee.ts
│   │   │   │   │       ├── get-portal-link.e2e-ee.ts
│   │   │   │   │       ├── get-prices.e2e-ee.ts
│   │   │   │   │       ├── get-subscription.e2e-ee.ts
│   │   │   │   │       ├── quota-throttler.guard.e2e-ee.ts
│   │   │   │   │       └── verify-customer.e2e-ee.ts
│   │   │   │   ├── blueprint/
│   │   │   │   │   ├── blueprint.controller.ts
│   │   │   │   │   ├── blueprint.module.ts
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── get-blueprint.response.dto.ts
│   │   │   │   │   │   └── grouped-blueprint.response.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── get-blueprints-by-id.e2e.ts
│   │   │   │   │   │   └── get-grouped-blueprints.e2e.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── get-blueprint/
│   │   │   │   │       │   ├── get-blueprint.command.ts
│   │   │   │   │       │   ├── get-blueprint.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── get-grouped-blueprints/
│   │   │   │   │       │   ├── consts.ts
│   │   │   │   │       │   ├── get-grouped-blueprints.command.ts
│   │   │   │   │       │   ├── get-grouped-blueprints.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       └── index.ts
│   │   │   │   ├── bridge/
│   │   │   │   │   ├── bridge.controller.ts
│   │   │   │   │   ├── bridge.module.ts
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── create-bridge-request.dto.ts
│   │   │   │   │   │   ├── create-bridge-response.dto.ts
│   │   │   │   │   │   ├── validate-bridge-url-request.dto.ts
│   │   │   │   │   │   └── validate-bridge-url-response.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── health-check.e2e.ts
│   │   │   │   │   │   └── sync.e2e.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── get-bridge-status/
│   │   │   │   │       │   ├── get-bridge-status.command.ts
│   │   │   │   │       │   ├── get-bridge-status.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       ├── store-control-values/
│   │   │   │   │       │   ├── index.ts
│   │   │   │   │       │   ├── store-control-values.command.ts
│   │   │   │   │       │   └── store-control-values.usecase.ts
│   │   │   │   │       └── sync/
│   │   │   │   │           ├── index.ts
│   │   │   │   │           ├── sync.command.ts
│   │   │   │   │           └── sync.usecase.ts
│   │   │   │   ├── change/
│   │   │   │   │   ├── change.module.ts
│   │   │   │   │   ├── changes.controller.ts
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── bulk-apply-change.dto.ts
│   │   │   │   │   │   ├── change-request.dto.ts
│   │   │   │   │   │   └── change-response.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── get-changes.e2e.ts
│   │   │   │   │   │   ├── promote-changes.e2e.ts
│   │   │   │   │   │   └── promote-layout-changes.e2e.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── apply-change/
│   │   │   │   │       │   ├── apply-change.command.ts
│   │   │   │   │       │   ├── apply-change.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── bulk-apply-change/
│   │   │   │   │       │   ├── bulk-apply-change.command.ts
│   │   │   │   │       │   └── bulk-apply-change.usecase.ts
│   │   │   │   │       ├── count-changes/
│   │   │   │   │       │   ├── count-changes.command.ts
│   │   │   │   │       │   └── count-changes.usecase.ts
│   │   │   │   │       ├── create-change/
│   │   │   │   │       │   └── create-change.spec.ts
│   │   │   │   │       ├── get-changes/
│   │   │   │   │       │   ├── get-changes.command.ts
│   │   │   │   │       │   └── get-changes.usecase.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       ├── promote-change-to-environment/
│   │   │   │   │       │   ├── index.ts
│   │   │   │   │       │   ├── promote-change-to-environment.command.ts
│   │   │   │   │       │   └── promote-change-to-environment.usecase.ts
│   │   │   │   │       ├── promote-feed-change/
│   │   │   │   │       │   └── promote-feed-change.ts
│   │   │   │   │       ├── promote-layout-change/
│   │   │   │   │       │   ├── index.ts
│   │   │   │   │       │   └── promote-layout-change.use-case.ts
│   │   │   │   │       ├── promote-message-template-change/
│   │   │   │   │       │   └── promote-message-template-change.ts
│   │   │   │   │       ├── promote-notification-group-change/
│   │   │   │   │       │   └── promote-notification-group-change.ts
│   │   │   │   │       ├── promote-notification-template-change/
│   │   │   │   │       │   ├── index.ts
│   │   │   │   │       │   └── promote-notification-template-change.usecase.ts
│   │   │   │   │       ├── promote-translation-change/
│   │   │   │   │       │   ├── index.ts
│   │   │   │   │       │   └── promote-translation-change.usecase.ts
│   │   │   │   │       ├── promote-translation-group-change/
│   │   │   │   │       │   ├── index.ts
│   │   │   │   │       │   └── promote-translation-group-change.usecase.ts
│   │   │   │   │       ├── promote-type-change.command.ts
│   │   │   │   │       └── shared/
│   │   │   │   │           ├── index.ts
│   │   │   │   │           └── notification-template-change.interface.ts
│   │   │   │   ├── channel-connections/
│   │   │   │   │   ├── channel-connections.controller.ts
│   │   │   │   │   ├── channel-connections.module.ts
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── create-channel-connection-request.dto.ts
│   │   │   │   │   │   ├── cursor-pagination-query.dto.ts
│   │   │   │   │   │   ├── dto.mapper.ts
│   │   │   │   │   │   ├── get-channel-connection-response.dto.ts
│   │   │   │   │   │   ├── list-channel-connections-query.dto.ts
│   │   │   │   │   │   ├── list-channel-connections-response.dto.ts
│   │   │   │   │   │   ├── shared.dto.ts
│   │   │   │   │   │   └── update-channel-connection-request.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── create-channel-connection.e2e.ts
│   │   │   │   │   │   ├── delete-channel-connection.e2e.ts
│   │   │   │   │   │   ├── get-channel-connection.e2e.ts
│   │   │   │   │   │   ├── helpers/
│   │   │   │   │   │   │   └── channel-helpers.ts
│   │   │   │   │   │   ├── list-channel-connections.e2e.ts
│   │   │   │   │   │   └── update-channel-connection.e2e.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── create-channel-connection/
│   │   │   │   │       │   ├── create-channel-connection.command.ts
│   │   │   │   │       │   └── create-channel-connection.usecase.ts
│   │   │   │   │       ├── delete-channel-connection/
│   │   │   │   │       │   ├── delete-channel-connection.command.ts
│   │   │   │   │       │   └── delete-channel-connection.usecase.ts
│   │   │   │   │       ├── get-channel-connection/
│   │   │   │   │       │   ├── get-channel-connection.command.ts
│   │   │   │   │       │   └── get-channel-connection.usecase.ts
│   │   │   │   │       ├── list-channel-connections/
│   │   │   │   │       │   ├── list-channel-connections.command.ts
│   │   │   │   │       │   └── list-channel-connections.usecase.ts
│   │   │   │   │       └── update-channel-connection/
│   │   │   │   │           ├── update-channel-connection.command.ts
│   │   │   │   │           └── update-channel-connection.usecase.ts
│   │   │   │   ├── channel-endpoints/
│   │   │   │   │   ├── channel-endpoints.controller.ts
│   │   │   │   │   ├── channel-endpoints.module.ts
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── create-channel-endpoint-request.dto.ts
│   │   │   │   │   │   ├── create-channel-endpoint-variants.dto.ts
│   │   │   │   │   │   ├── cursor-pagination-query.dto.ts
│   │   │   │   │   │   ├── dto.mapper.ts
│   │   │   │   │   │   ├── endpoint-types.dto.ts
│   │   │   │   │   │   ├── get-channel-endpoint-response.dto.ts
│   │   │   │   │   │   ├── list-channel-endpoints-query.dto.ts
│   │   │   │   │   │   ├── list-channel-endpoints-response.dto.ts
│   │   │   │   │   │   └── update-channel-endpoint-request.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── create-channel-endpoint.e2e.ts
│   │   │   │   │   │   ├── delete-channel-endpoint.e2e.ts
│   │   │   │   │   │   ├── get-channel-endpoint.e2e.ts
│   │   │   │   │   │   ├── list-channel-endpoints.e2e.ts
│   │   │   │   │   │   └── update-channel-endpoint.e2e.ts
│   │   │   │   │   ├── usecases/
│   │   │   │   │   │   ├── create-channel-endpoint/
│   │   │   │   │   │   │   ├── create-channel-endpoint.command.ts
│   │   │   │   │   │   │   └── create-channel-endpoint.usecase.ts
│   │   │   │   │   │   ├── delete-channel-endpoint/
│   │   │   │   │   │   │   ├── delete-channel-endpoint.command.ts
│   │   │   │   │   │   │   └── delete-channel-endpoint.usecase.ts
│   │   │   │   │   │   ├── get-channel-endpoint/
│   │   │   │   │   │   │   ├── get-channel-endpoint.command.ts
│   │   │   │   │   │   │   └── get-channel-endpoint.usecase.ts
│   │   │   │   │   │   ├── list-channel-endpoints/
│   │   │   │   │   │   │   ├── list-channel-endpoints.command.ts
│   │   │   │   │   │   │   └── list-channel-endpoints.usecase.ts
│   │   │   │   │   │   └── update-channel-endpoint/
│   │   │   │   │   │       ├── update-channel-endpoint.command.ts
│   │   │   │   │   │       └── update-channel-endpoint.usecase.ts
│   │   │   │   │   └── validators/
│   │   │   │   │       └── channel-endpoint.validator.ts
│   │   │   │   ├── content-templates/
│   │   │   │   │   ├── content-templates.controller.ts
│   │   │   │   │   ├── content-templates.module.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── preview-email.e2e.ts
│   │   │   │   │   │   └── preview-step.e2e.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       └── index.ts
│   │   │   │   ├── contexts/
│   │   │   │   │   ├── contexts.controller.ts
│   │   │   │   │   ├── contexts.module.ts
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── create-context-request.dto.ts
│   │   │   │   │   │   ├── cursor-pagination-query.dto.ts
│   │   │   │   │   │   ├── dto.mapper.ts
│   │   │   │   │   │   ├── get-context-response.dto.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── list-contexts-query.dto.ts
│   │   │   │   │   │   ├── list-contexts-response.dto.ts
│   │   │   │   │   │   └── update-context-request.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── create-context.e2e.ts
│   │   │   │   │   │   ├── delete-context.e2e.ts
│   │   │   │   │   │   ├── get-context.e2e.ts
│   │   │   │   │   │   ├── list-contexts.e2e.ts
│   │   │   │   │   │   └── update-context.e2e.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── create-context/
│   │   │   │   │       │   ├── create-context.command.ts
│   │   │   │   │       │   ├── create-context.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── delete-context/
│   │   │   │   │       │   ├── delete-context.command.ts
│   │   │   │   │       │   ├── delete-context.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── get-context/
│   │   │   │   │       │   ├── get-context.command.ts
│   │   │   │   │       │   ├── get-context.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── list-contexts/
│   │   │   │   │       │   ├── index.ts
│   │   │   │   │       │   ├── list-contexts.command.ts
│   │   │   │   │       │   └── list-contexts.usecase.ts
│   │   │   │   │       └── update-context/
│   │   │   │   │           ├── index.ts
│   │   │   │   │           ├── update-context.command.ts
│   │   │   │   │           └── update-context.usecase.ts
│   │   │   │   ├── environment-variables/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── create-environment-variable-request.dto.ts
│   │   │   │   │   │   ├── environment-variable-response.dto.ts
│   │   │   │   │   │   ├── get-environment-variable-usage-response.dto.ts
│   │   │   │   │   │   ├── get-environment-variables-request.dto.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   └── update-environment-variable-request.dto.ts
│   │   │   │   │   ├── environment-variables.controller.ts
│   │   │   │   │   ├── environment-variables.module.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── create-environment-variable/
│   │   │   │   │       │   ├── create-environment-variable.command.ts
│   │   │   │   │       │   └── create-environment-variable.usecase.ts
│   │   │   │   │       ├── delete-environment-variable/
│   │   │   │   │       │   ├── delete-environment-variable.command.ts
│   │   │   │   │       │   └── delete-environment-variable.usecase.ts
│   │   │   │   │       ├── get-environment-variable/
│   │   │   │   │       │   ├── get-environment-variable.command.ts
│   │   │   │   │       │   └── get-environment-variable.usecase.ts
│   │   │   │   │       ├── get-environment-variable-usage/
│   │   │   │   │       │   ├── get-environment-variable-usage.command.ts
│   │   │   │   │       │   ├── get-environment-variable-usage.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── get-environment-variables/
│   │   │   │   │       │   ├── get-environment-variables.command.ts
│   │   │   │   │       │   └── get-environment-variables.usecase.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       └── update-environment-variable/
│   │   │   │   │           ├── update-environment-variable.command.ts
│   │   │   │   │           └── update-environment-variable.usecase.ts
│   │   │   │   ├── environments-v1/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── api-key.dto.ts
│   │   │   │   │   │   ├── create-environment-request.dto.ts
│   │   │   │   │   │   ├── environment-response.dto.ts
│   │   │   │   │   │   └── update-environment-request.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── environments.controller.e2e.ts
│   │   │   │   │   │   ├── get-api-keys.e2e.ts
│   │   │   │   │   │   └── regenerate-api-keys.e2e.ts
│   │   │   │   │   ├── environments-v1.controller.ts
│   │   │   │   │   ├── environments-v1.module.ts
│   │   │   │   │   ├── novu-bridge-client.ts
│   │   │   │   │   ├── novu-bridge.controller.ts
│   │   │   │   │   ├── novu-bridge.module.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── construct-framework-workflow/
│   │   │   │   │       │   ├── construct-framework-workflow.command.ts
│   │   │   │   │       │   ├── construct-framework-workflow.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── create-environment/
│   │   │   │   │       │   ├── create-environment.command.ts
│   │   │   │   │       │   ├── create-environment.e2e.ts
│   │   │   │   │       │   └── create-environment.usecase.ts
│   │   │   │   │       ├── delete-environment/
│   │   │   │   │       │   ├── delete-environment.command.ts
│   │   │   │   │       │   ├── delete-environment.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── generate-unique-api-key/
│   │   │   │   │       │   ├── generate-unique-api-key.spec.ts
│   │   │   │   │       │   └── generate-unique-api-key.usecase.ts
│   │   │   │   │       ├── get-api-keys/
│   │   │   │   │       │   ├── get-api-keys.command.ts
│   │   │   │   │       │   └── get-api-keys.usecase.ts
│   │   │   │   │       ├── get-environment/
│   │   │   │   │       │   ├── get-environment.command.ts
│   │   │   │   │       │   ├── get-environment.e2e.ts
│   │   │   │   │       │   ├── get-environment.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── get-my-environments/
│   │   │   │   │       │   ├── get-my-environments.command.ts
│   │   │   │   │       │   ├── get-my-environments.e2e.ts
│   │   │   │   │       │   └── get-my-environments.usecase.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       ├── output-renderers/
│   │   │   │   │       │   ├── base-translation-renderer.usecase.ts
│   │   │   │   │       │   ├── chat-output-renderer.usecase.ts
│   │   │   │   │       │   ├── delay-output-renderer.usecase.ts
│   │   │   │   │       │   ├── digest-output-renderer.usecase.ts
│   │   │   │   │       │   ├── email-output-renderer.spec.ts
│   │   │   │   │       │   ├── email-output-renderer.usecase.ts
│   │   │   │   │       │   ├── in-app-output-renderer.usecase.ts
│   │   │   │   │       │   ├── index.ts
│   │   │   │   │       │   ├── novu-branding-html.ts
│   │   │   │   │       │   ├── push-output-renderer.usecase.ts
│   │   │   │   │       │   ├── render-command.ts
│   │   │   │   │       │   ├── sms-output-renderer.usecase.ts
│   │   │   │   │       │   └── throttle-output-renderer.usecase.ts
│   │   │   │   │       ├── regenerate-api-keys/
│   │   │   │   │       │   └── regenerate-api-keys.usecase.ts
│   │   │   │   │       └── update-environment/
│   │   │   │   │           ├── update-environment.command.ts
│   │   │   │   │           ├── update-environment.e2e-ee.ts
│   │   │   │   │           ├── update-environment.e2e.ts
│   │   │   │   │           └── update-environment.usecase.ts
│   │   │   │   ├── environments-v2/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── diff-environment.dto.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   └── publish-environment.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── environments-v2-diff.e2e.ts
│   │   │   │   │   │   ├── environments-v2-publish.e2e.ts
│   │   │   │   │   │   └── get-environment-tags.e2e.ts
│   │   │   │   │   ├── environments.controller.ts
│   │   │   │   │   ├── environments.module.ts
│   │   │   │   │   ├── services/
│   │   │   │   │   │   ├── dependency-analyzer.service.ts
│   │   │   │   │   │   ├── environment-validation.service.ts
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── types/
│   │   │   │   │   │   └── sync.types.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── diff-environment/
│   │   │   │   │       │   ├── diff-environment.command.ts
│   │   │   │   │       │   ├── diff-environment.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── publish-environment/
│   │   │   │   │       │   ├── index.ts
│   │   │   │   │       │   ├── publish-environment.command.ts
│   │   │   │   │       │   └── publish-environment.usecase.ts
│   │   │   │   │       └── sync-strategies/
│   │   │   │   │           ├── adapters/
│   │   │   │   │           │   ├── index.ts
│   │   │   │   │           │   ├── layout-comparator.adapter.ts
│   │   │   │   │           │   ├── layout-delete.adapter.ts
│   │   │   │   │           │   ├── layout-repository.adapter.ts
│   │   │   │   │           │   ├── layout-sync.adapter.ts
│   │   │   │   │           │   ├── workflow-comparator.adapter.ts
│   │   │   │   │           │   ├── workflow-delete.adapter.ts
│   │   │   │   │           │   ├── workflow-repository.adapter.ts
│   │   │   │   │           │   └── workflow-sync.adapter.ts
│   │   │   │   │           ├── base/
│   │   │   │   │           │   ├── base-sync.strategy.ts
│   │   │   │   │           │   ├── index.ts
│   │   │   │   │           │   ├── interfaces/
│   │   │   │   │           │   │   ├── base-comparator.interface.ts
│   │   │   │   │           │   │   ├── base-delete.interface.ts
│   │   │   │   │           │   │   ├── base-repository.interface.ts
│   │   │   │   │           │   │   ├── base-sync.interface.ts
│   │   │   │   │           │   │   └── index.ts
│   │   │   │   │           │   └── operations/
│   │   │   │   │           │       ├── base-diff.operation.ts
│   │   │   │   │           │       ├── base-sync.operation.ts
│   │   │   │   │           │       └── index.ts
│   │   │   │   │           ├── builders/
│   │   │   │   │           │   ├── diff-result.builder.ts
│   │   │   │   │           │   └── sync-result.builder.ts
│   │   │   │   │           ├── comparators/
│   │   │   │   │           │   ├── layout.comparator.ts
│   │   │   │   │           │   └── workflow.comparator.ts
│   │   │   │   │           ├── constants/
│   │   │   │   │           │   └── sync.constants.ts
│   │   │   │   │           ├── index.ts
│   │   │   │   │           ├── layout-sync.strategy.ts
│   │   │   │   │           ├── normalizers/
│   │   │   │   │           │   ├── layout.normalizer.ts
│   │   │   │   │           │   └── workflow.normalizer.ts
│   │   │   │   │           ├── operations/
│   │   │   │   │           │   ├── layout-diff.operation.ts
│   │   │   │   │           │   ├── layout-repository.service.ts
│   │   │   │   │           │   ├── layout-sync.operation.ts
│   │   │   │   │           │   ├── workflow-diff.operation.ts
│   │   │   │   │           │   ├── workflow-repository.service.ts
│   │   │   │   │           │   └── workflow-sync.operation.ts
│   │   │   │   │           ├── sync.module.ts
│   │   │   │   │           ├── types/
│   │   │   │   │           │   ├── layout-sync.types.ts
│   │   │   │   │           │   └── workflow-sync.types.ts
│   │   │   │   │           └── workflow-sync.strategy.ts
│   │   │   │   ├── events/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── test-email-request.dto.ts
│   │   │   │   │   │   ├── trigger-event-request.dto.ts
│   │   │   │   │   │   ├── trigger-event-response.dto.ts
│   │   │   │   │   │   └── trigger-event-to-all-request.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── bridge-trigger.e2e.ts
│   │   │   │   │   │   ├── bulk-trigger.e2e.ts
│   │   │   │   │   │   ├── cancel-event.e2e.ts
│   │   │   │   │   │   ├── context-events.e2e.ts
│   │   │   │   │   │   ├── delay-events.e2e.ts
│   │   │   │   │   │   ├── digest-events.e2e.ts
│   │   │   │   │   │   ├── process-subscriber.e2e.ts
│   │   │   │   │   │   ├── scheduled-digest.e2e.ts
│   │   │   │   │   │   ├── send-message-email.e2e.ts
│   │   │   │   │   │   ├── send-message-push.e2e.ts
│   │   │   │   │   │   ├── test-email.e2e.ts
│   │   │   │   │   │   ├── throttle-events.e2e.ts
│   │   │   │   │   │   ├── trigger-event-preferences.e2e.ts
│   │   │   │   │   │   ├── trigger-event-to-all.e2e.ts
│   │   │   │   │   │   ├── trigger-event-topic.e2e.ts
│   │   │   │   │   │   ├── trigger-event.e2e.ts
│   │   │   │   │   │   └── utils/
│   │   │   │   │   │       ├── poll-for-job-status-change.util.ts
│   │   │   │   │   │       └── sleep.util.ts
│   │   │   │   │   ├── events.controller.ts
│   │   │   │   │   ├── events.module.ts
│   │   │   │   │   ├── exceptions/
│   │   │   │   │   │   └── payload-validation-exception.ts
│   │   │   │   │   ├── usecases/
│   │   │   │   │   │   ├── cancel-delayed/
│   │   │   │   │   │   │   ├── cancel-delayed.command.ts
│   │   │   │   │   │   │   ├── cancel-delayed.usecase.ts
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── parse-event-request/
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   ├── parse-event-request.command.ts
│   │   │   │   │   │   │   ├── parse-event-request.e2e.ts
│   │   │   │   │   │   │   └── parse-event-request.usecase.ts
│   │   │   │   │   │   ├── process-bulk-trigger/
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   ├── process-bulk-trigger.command.ts
│   │   │   │   │   │   │   └── process-bulk-trigger.usecase.ts
│   │   │   │   │   │   ├── send-test-email/
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   ├── send-test-email.command.ts
│   │   │   │   │   │   │   └── send-test-email.usecase.ts
│   │   │   │   │   │   └── trigger-event-to-all/
│   │   │   │   │   │       ├── index.ts
│   │   │   │   │   │       ├── trigger-event-to-all.command.ts
│   │   │   │   │   │       ├── trigger-event-to-all.spec.ts
│   │   │   │   │   │       └── trigger-event-to-all.usecase.ts
│   │   │   │   │   └── utils/
│   │   │   │   │       └── trigger-recipient-validation.ts
│   │   │   │   ├── execution-details/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   └── execution-details-request.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   └── get-execution-details.e2e.ts
│   │   │   │   │   ├── execution-details.controller.ts
│   │   │   │   │   ├── execution-details.module.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── get-execution-details/
│   │   │   │   │       │   ├── get-execution-details.command.ts
│   │   │   │   │       │   ├── get-execution-details.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       └── index.ts
│   │   │   │   ├── feeds/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── create-feed-request.dto.ts
│   │   │   │   │   │   └── feed-response.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── create-feed.e2e.ts
│   │   │   │   │   │   ├── delete-feed.e2e.ts
│   │   │   │   │   │   └── get-feeds.e2e.ts
│   │   │   │   │   ├── feeds.controller.ts
│   │   │   │   │   ├── feeds.module.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── create-feed/
│   │   │   │   │       │   ├── create-feed.command.ts
│   │   │   │   │       │   └── create-feed.usecase.ts
│   │   │   │   │       ├── delete-feed/
│   │   │   │   │       │   ├── delete-feed.command.ts
│   │   │   │   │       │   └── delete-feed.usecase.ts
│   │   │   │   │       ├── get-feeds/
│   │   │   │   │       │   ├── get-feeds.command.ts
│   │   │   │   │       │   └── get-feeds.usecase.ts
│   │   │   │   │       └── index.ts
│   │   │   │   ├── health/
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   └── health-check.e2e.ts
│   │   │   │   │   ├── health.controller.ts
│   │   │   │   │   └── health.module.ts
│   │   │   │   ├── inbound-parse/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   └── get-mx-record.dto.ts
│   │   │   │   │   ├── inbound-parse.controller.ts
│   │   │   │   │   ├── inbound-parse.module.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── get-mx-record/
│   │   │   │   │       │   ├── get-mx-record.command.ts
│   │   │   │   │       │   └── get-mx-record.usecase.ts
│   │   │   │   │       └── index.ts
│   │   │   │   ├── inbox/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── action-type-request.dto.ts
│   │   │   │   │   │   ├── bulk-update-preferences-request.dto.ts
│   │   │   │   │   │   ├── create-topic-subscription-request.dto.ts
│   │   │   │   │   │   ├── get-notifications-count-request.dto.ts
│   │   │   │   │   │   ├── get-notifications-count-response.dto.ts
│   │   │   │   │   │   ├── get-notifications-request.dto.ts
│   │   │   │   │   │   ├── get-notifications-response.dto.ts
│   │   │   │   │   │   ├── get-preferences-request.dto.ts
│   │   │   │   │   │   ├── get-preferences-response.dto.ts
│   │   │   │   │   │   ├── inbox-notification.dto.ts
│   │   │   │   │   │   ├── mark-notifications-as-seen-request.dto.ts
│   │   │   │   │   │   ├── snooze-notification-request.dto.ts
│   │   │   │   │   │   ├── subscriber-session-request.dto.ts
│   │   │   │   │   │   ├── subscriber-session-response.dto.ts
│   │   │   │   │   │   ├── update-all-notifications-request.dto.ts
│   │   │   │   │   │   ├── update-preferences-request.dto.ts
│   │   │   │   │   │   └── workflow.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── context-aware-topic-subscriptions.e2e.ts
│   │   │   │   │   │   ├── create-topic-subscription.e2e.ts
│   │   │   │   │   │   ├── delete-notifications.e2e.ts
│   │   │   │   │   │   ├── get-notifications-count.e2e.ts
│   │   │   │   │   │   ├── get-notifications.e2e.ts
│   │   │   │   │   │   ├── get-preferences.e2e.ts
│   │   │   │   │   │   ├── get-topic-subscription.e2e.ts
│   │   │   │   │   │   ├── mark-notification-as.e2e.ts
│   │   │   │   │   │   ├── mark-notifications-as-seen.e2e.ts
│   │   │   │   │   │   ├── session.e2e.ts
│   │   │   │   │   │   ├── snooze-unsnooze-notification.e2e.ts
│   │   │   │   │   │   ├── update-all-notifications.e2e.ts
│   │   │   │   │   │   ├── update-notification-action.e2e.ts
│   │   │   │   │   │   ├── update-preferences.e2e.ts
│   │   │   │   │   │   └── update-subscription-workflow-preferences.e2e.ts
│   │   │   │   │   ├── inbox.controller.ts
│   │   │   │   │   ├── inbox.module.ts
│   │   │   │   │   ├── inbox.topic.controller.ts
│   │   │   │   │   ├── interceptors/
│   │   │   │   │   │   └── context-compatibility.interceptor.ts
│   │   │   │   │   ├── usecases/
│   │   │   │   │   │   ├── bulk-update-preferences/
│   │   │   │   │   │   │   ├── bulk-update-preferences.command.ts
│   │   │   │   │   │   │   ├── bulk-update-preferences.spec.ts
│   │   │   │   │   │   │   └── bulk-update-preferences.usecase.ts
│   │   │   │   │   │   ├── delete-all-notifications/
│   │   │   │   │   │   │   ├── delete-all-notifications.command.ts
│   │   │   │   │   │   │   └── delete-all-notifications.usecase.ts
│   │   │   │   │   │   ├── delete-many-notifications/
│   │   │   │   │   │   │   ├── delete-many-notifications.command.ts
│   │   │   │   │   │   │   └── delete-many-notifications.usecase.ts
│   │   │   │   │   │   ├── delete-notification/
│   │   │   │   │   │   │   ├── delete-notification.command.ts
│   │   │   │   │   │   │   └── delete-notification.usecase.ts
│   │   │   │   │   │   ├── delete-subscription/
│   │   │   │   │   │   │   ├── delete-subscription.command.ts
│   │   │   │   │   │   │   └── delete-subscription.usecase.ts
│   │   │   │   │   │   ├── get-inbox-preferences/
│   │   │   │   │   │   │   ├── get-inbox-preferences.command.ts
│   │   │   │   │   │   │   ├── get-inbox-preferences.spec.ts
│   │   │   │   │   │   │   └── get-inbox-preferences.usecase.ts
│   │   │   │   │   │   ├── get-notifications/
│   │   │   │   │   │   │   ├── get-notifications.command.ts
│   │   │   │   │   │   │   ├── get-notifications.spec.ts
│   │   │   │   │   │   │   └── get-notifications.usecase.ts
│   │   │   │   │   │   ├── get-topic-subscriptions/
│   │   │   │   │   │   │   ├── get-topic-subscriptions.command.ts
│   │   │   │   │   │   │   └── get-topic-subscriptions.usecase.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── mark-many-notifications-as/
│   │   │   │   │   │   │   ├── mark-many-notifications-as.command.ts
│   │   │   │   │   │   │   ├── mark-many-notifications-as.spec.ts
│   │   │   │   │   │   │   └── mark-many-notifications-as.usecase.ts
│   │   │   │   │   │   ├── mark-notification-as/
│   │   │   │   │   │   │   ├── mark-notification-as.command.ts
│   │   │   │   │   │   │   ├── mark-notification-as.spec.ts
│   │   │   │   │   │   │   └── mark-notification-as.usecase.ts
│   │   │   │   │   │   ├── mark-notifications-as-seen/
│   │   │   │   │   │   │   ├── mark-notifications-as-seen.command.ts
│   │   │   │   │   │   │   └── mark-notifications-as-seen.usecase.ts
│   │   │   │   │   │   ├── noop-send-webhook-message.usecase.ts
│   │   │   │   │   │   ├── notifications-count/
│   │   │   │   │   │   │   ├── notifications-count.command.ts
│   │   │   │   │   │   │   ├── notifications-count.spec.ts
│   │   │   │   │   │   │   └── notifications-count.usecase.ts
│   │   │   │   │   │   ├── session/
│   │   │   │   │   │   │   ├── session.command.ts
│   │   │   │   │   │   │   ├── session.spec.ts
│   │   │   │   │   │   │   └── session.usecase.ts
│   │   │   │   │   │   ├── snooze-notification/
│   │   │   │   │   │   │   ├── snooze-notification.command.ts
│   │   │   │   │   │   │   ├── snooze-notification.spec.ts
│   │   │   │   │   │   │   └── snooze-notification.usecase.ts
│   │   │   │   │   │   ├── unsnooze-notification/
│   │   │   │   │   │   │   ├── unsnooze-notification.command.ts
│   │   │   │   │   │   │   ├── unsnooze-notification.spec.ts
│   │   │   │   │   │   │   └── unsnooze-notification.usecase.ts
│   │   │   │   │   │   ├── update-all-notifications/
│   │   │   │   │   │   │   ├── update-all-notifications.command.ts
│   │   │   │   │   │   │   ├── update-all-notifications.spec.ts
│   │   │   │   │   │   │   └── update-all-notifications.usecase.ts
│   │   │   │   │   │   ├── update-notification-action/
│   │   │   │   │   │   │   ├── update-notification-action.command.ts
│   │   │   │   │   │   │   ├── update-notification-action.spec.ts
│   │   │   │   │   │   │   └── update-notification-action.usecase.ts
│   │   │   │   │   │   └── update-preferences/
│   │   │   │   │   │       ├── update-preferences.command.ts
│   │   │   │   │   │       ├── update-preferences.spec.ts
│   │   │   │   │   │       └── update-preferences.usecase.ts
│   │   │   │   │   └── utils/
│   │   │   │   │       ├── analytics.ts
│   │   │   │   │       ├── encryption.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       ├── notification-mapper.ts
│   │   │   │   │       ├── types.ts
│   │   │   │   │       └── validate-data.ts
│   │   │   │   ├── integrations/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── auto-configure-integration-request.dto.ts
│   │   │   │   │   │   ├── auto-configure-integration-response.dto.ts
│   │   │   │   │   │   ├── create-integration-request.dto.ts
│   │   │   │   │   │   ├── generate-chat-oauth-url-response.dto.ts
│   │   │   │   │   │   ├── generate-chat-oauth-url.dto.ts
│   │   │   │   │   │   ├── get-channel-type-limit.sto.ts
│   │   │   │   │   │   └── update-integration.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── create-integration.e2e.ts
│   │   │   │   │   │   ├── deactivate-integration.e2e.ts
│   │   │   │   │   │   ├── get-active-integration.e2e.ts
│   │   │   │   │   │   ├── get-decrypted-integrations.e2e.ts
│   │   │   │   │   │   ├── get-in-app-activated.e2e.ts
│   │   │   │   │   │   ├── get-integration.e2e.ts
│   │   │   │   │   │   ├── get-webhook-support-status.e2e.ts
│   │   │   │   │   │   ├── remove-integration.e2e.ts
│   │   │   │   │   │   ├── set-itegration-as-primary.e2e.ts
│   │   │   │   │   │   └── update-integration.e2e.ts
│   │   │   │   │   ├── integrations.controller.ts
│   │   │   │   │   ├── integrations.module.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── auto-configure-integration/
│   │   │   │   │       │   ├── auto-configure-integration.command.ts
│   │   │   │   │       │   └── auto-configure-integration.usecase.ts
│   │   │   │   │       ├── chat-oauth-callback/
│   │   │   │   │       │   ├── chat-oauth-callback.command.ts
│   │   │   │   │       │   ├── chat-oauth-callback.response.ts
│   │   │   │   │       │   ├── chat-oauth-callback.usecase.ts
│   │   │   │   │       │   ├── msteams-oauth-callback/
│   │   │   │   │       │   │   ├── msteams-oauth-callback.command.ts
│   │   │   │   │       │   │   └── msteams-oauth-callback.usecase.ts
│   │   │   │   │       │   └── slack-oauth-callback/
│   │   │   │   │       │       ├── slack-oauth-callback.command.ts
│   │   │   │   │       │       └── slack-oauth-callback.usecase.ts
│   │   │   │   │       ├── check-integration/
│   │   │   │   │       │   ├── check-integration-email.usecase.ts
│   │   │   │   │       │   ├── check-integration.command.ts
│   │   │   │   │       │   └── check-integration.usecase.ts
│   │   │   │   │       ├── create-integration/
│   │   │   │   │       │   ├── create-integration.command.ts
│   │   │   │   │       │   └── create-integration.usecase.ts
│   │   │   │   │       ├── create-novu-integrations/
│   │   │   │   │       │   ├── create-novu-integrations.command.ts
│   │   │   │   │       │   └── create-novu-integrations.usecase.ts
│   │   │   │   │       ├── generate-chat-oath-url/
│   │   │   │   │       │   ├── chat-oauth.constants.ts
│   │   │   │   │       │   ├── generate-chat-oauth-url.command.ts
│   │   │   │   │       │   ├── generate-chat-oauth-url.usecase.ts
│   │   │   │   │       │   ├── generate-msteams-oath-url/
│   │   │   │   │       │   │   ├── generate-msteams-oauth-url.command.ts
│   │   │   │   │       │   │   └── generate-msteams-oauth-url.usecase.ts
│   │   │   │   │       │   └── generate-slack-oath-url/
│   │   │   │   │       │       ├── generate-slack-oauth-url.command.ts
│   │   │   │   │       │       └── generate-slack-oauth-url.usecase.ts
│   │   │   │   │       ├── get-in-app-activated/
│   │   │   │   │       │   ├── get-in-app-activated.command.ts
│   │   │   │   │       │   └── get-in-app-activated.usecase.ts
│   │   │   │   │       ├── get-integrations/
│   │   │   │   │       │   ├── get-integrations.command.ts
│   │   │   │   │       │   └── get-integrations.usecase.ts
│   │   │   │   │       ├── get-webhook-support-status/
│   │   │   │   │       │   ├── get-webhook-support-status.command.ts
│   │   │   │   │       │   └── get-webhook-support-status.usecase.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       ├── remove-integration/
│   │   │   │   │       │   ├── remove-integration.command.ts
│   │   │   │   │       │   └── remove-integration.usecase.ts
│   │   │   │   │       ├── set-integration-as-primary/
│   │   │   │   │       │   ├── set-integration-as-primary.command.ts
│   │   │   │   │       │   └── set-integration-as-primary.usecase.ts
│   │   │   │   │       └── update-integration/
│   │   │   │   │           ├── update-integration.command.ts
│   │   │   │   │           └── update-integration.usecase.ts
│   │   │   │   ├── internal/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── scheduler-callback.dto.ts
│   │   │   │   │   │   └── subscriber-online-state.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   └── internal.e2e.ts
│   │   │   │   │   ├── guards/
│   │   │   │   │   │   └── internal-callback.guard.ts
│   │   │   │   │   ├── internal.controller.ts
│   │   │   │   │   ├── internal.module.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── handle-scheduler-callback/
│   │   │   │   │       │   ├── handle-scheduler-callback.command.ts
│   │   │   │   │       │   └── handle-scheduler-callback.usecase.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       └── update-subscriber-online-state/
│   │   │   │   │           ├── update-subscriber-online-state.command.ts
│   │   │   │   │           └── update-subscriber-online-state.usecase.ts
│   │   │   │   ├── invites/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── bulk-invite-members.dto.ts
│   │   │   │   │   │   ├── invite-member.dto.ts
│   │   │   │   │   │   └── resend-invite.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── accept-invite.e2e.ts
│   │   │   │   │   │   ├── bulk-invite.e2e.ts
│   │   │   │   │   │   ├── get-invite.e2e.ts
│   │   │   │   │   │   └── resend-invite.e2e.ts
│   │   │   │   │   ├── invites.controller.ts
│   │   │   │   │   ├── invites.module.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── accept-invite/
│   │   │   │   │       │   ├── accept-invite.command.ts
│   │   │   │   │       │   └── accept-invite.usecase.ts
│   │   │   │   │       ├── bulk-invite/
│   │   │   │   │       │   ├── bulk-invite.command.ts
│   │   │   │   │       │   └── bulk-invite.usecase.ts
│   │   │   │   │       ├── get-invite/
│   │   │   │   │       │   ├── get-invite.command.ts
│   │   │   │   │       │   └── get-invite.usecase.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       ├── invite-member/
│   │   │   │   │       │   ├── invite-member.command.ts
│   │   │   │   │       │   └── invite-member.usecase.ts
│   │   │   │   │       └── resend-invite/
│   │   │   │   │           ├── resend-invite.command.ts
│   │   │   │   │           └── resend-invite.usecase.ts
│   │   │   │   ├── layouts-v1/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── create-layout.dto.ts
│   │   │   │   │   │   ├── filter-layouts.dto.ts
│   │   │   │   │   │   ├── get-layout.dto.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   └── update-layout.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── create-layout.e2e.ts
│   │   │   │   │   │   ├── delete-layout.e2e.ts
│   │   │   │   │   │   ├── filter-layouts.e2e.ts
│   │   │   │   │   │   ├── get-layout.e2e.ts
│   │   │   │   │   │   ├── helpers/
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   ├── set-default-layout.e2e.ts
│   │   │   │   │   │   └── update-layout.e2e.ts
│   │   │   │   │   ├── layouts-v1.controller.ts
│   │   │   │   │   ├── layouts-v1.module.ts
│   │   │   │   │   ├── types/
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── check-layout-is-used/
│   │   │   │   │       │   ├── check-layout-is-used.command.ts
│   │   │   │   │       │   ├── check-layout-is-used.use-case.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── create-default-layout/
│   │   │   │   │       │   ├── create-default-layout.command.ts
│   │   │   │   │       │   ├── create-default-layout.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── create-default-layout-change/
│   │   │   │   │       │   ├── create-default-layout-change.command.ts
│   │   │   │   │       │   └── create-default-layout-change.usecase.ts
│   │   │   │   │       ├── create-layout/
│   │   │   │   │       │   ├── create-layout.command.ts
│   │   │   │   │       │   ├── create-layout.use-case.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── create-layout-change/
│   │   │   │   │       │   ├── create-layout-change.command.ts
│   │   │   │   │       │   ├── create-layout-change.use-case.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── delete-layout/
│   │   │   │   │       │   ├── delete-layout.command.ts
│   │   │   │   │       │   ├── delete-layout.use-case.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── filter-layouts/
│   │   │   │   │       │   ├── filter-layouts.command.ts
│   │   │   │   │       │   ├── filter-layouts.use-case.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── find-deleted-layout/
│   │   │   │   │       │   ├── find-deleted-layout.command.ts
│   │   │   │   │       │   ├── find-deleted-layout.spec.ts
│   │   │   │   │       │   ├── find-deleted-layout.use-case.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       ├── set-default-layout/
│   │   │   │   │       │   ├── index.ts
│   │   │   │   │       │   ├── set-default-layout.command.ts
│   │   │   │   │       │   └── set-default-layout.use-case.ts
│   │   │   │   │       └── update-layout/
│   │   │   │   │           ├── index.ts
│   │   │   │   │           ├── update-layout.command.ts
│   │   │   │   │           └── update-layout.use-case.ts
│   │   │   │   ├── layouts-v2/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── duplicate-layout.dto.ts
│   │   │   │   │   │   ├── generate-layout-preview-response.dto.ts
│   │   │   │   │   │   ├── get-layout-list-query-params.dto.ts
│   │   │   │   │   │   ├── get-layout-usage-response.dto.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── layout-preview-payload.dto.ts
│   │   │   │   │   │   ├── layout-preview-request.dto.ts
│   │   │   │   │   │   └── list-layout-response.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── preview-layout.e2e.ts
│   │   │   │   │   │   └── upsert-layout.e2e.ts
│   │   │   │   │   ├── layouts.controller.ts
│   │   │   │   │   ├── layouts.module.ts
│   │   │   │   │   ├── usecases/
│   │   │   │   │   │   ├── build-layout-issues/
│   │   │   │   │   │   │   ├── build-layout-issues.command.ts
│   │   │   │   │   │   │   └── build-layout-issues.usecase.ts
│   │   │   │   │   │   ├── delete-layout/
│   │   │   │   │   │   │   ├── delete-layout.command.ts
│   │   │   │   │   │   │   ├── delete-layout.use-case.spec.ts
│   │   │   │   │   │   │   ├── delete-layout.use-case.ts
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   ├── duplicate-layout/
│   │   │   │   │   │   │   ├── duplicate-layout.command.ts
│   │   │   │   │   │   │   ├── duplicate-layout.use-case.spec.ts
│   │   │   │   │   │   │   ├── duplicate-layout.use-case.ts
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   ├── get-layout-usage/
│   │   │   │   │   │   │   ├── get-layout-usage.command.ts
│   │   │   │   │   │   │   ├── get-layout-usage.usecase.ts
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── list-layouts/
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   ├── list-layouts.command.ts
│   │   │   │   │   │   │   ├── list-layouts.use-case.spec.ts
│   │   │   │   │   │   │   └── list-layouts.use-case.ts
│   │   │   │   │   │   ├── preview-layout/
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   ├── preview-layout.command.ts
│   │   │   │   │   │   │   ├── preview-layout.usecase.spec.ts
│   │   │   │   │   │   │   ├── preview-layout.usecase.ts
│   │   │   │   │   │   │   └── preview-utils.ts
│   │   │   │   │   │   ├── sync-to-environment/
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   ├── layout-sync-to-environment.command.ts
│   │   │   │   │   │   │   └── layout-sync-to-environment.usecase.ts
│   │   │   │   │   │   └── upsert-layout/
│   │   │   │   │   │       ├── index.ts
│   │   │   │   │   │       ├── upsert-layout.command.ts
│   │   │   │   │   │       ├── upsert-layout.usecase.spec.ts
│   │   │   │   │   │       └── upsert-layout.usecase.ts
│   │   │   │   │   └── utils/
│   │   │   │   │       └── layout-templates.ts
│   │   │   │   ├── message-template/
│   │   │   │   │   ├── message-template.controller.ts
│   │   │   │   │   ├── message-template.module.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── find-message-templates-by-layout/
│   │   │   │   │       │   ├── find-message-templates-by-layout.command.ts
│   │   │   │   │       │   ├── find-message-templates-by-layout.use-case.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       └── index.ts
│   │   │   │   ├── messages/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── delete-message-response.dto.ts
│   │   │   │   │   │   ├── get-messages-requests.dto.ts
│   │   │   │   │   │   └── remove-messages-by-transactionId-request.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── get-messages.e2e.ts
│   │   │   │   │   │   ├── remove-message.e2e.ts
│   │   │   │   │   │   └── remove-messages-by-transactionId.e2e.ts
│   │   │   │   │   ├── messages.controller.ts
│   │   │   │   │   ├── messages.module.ts
│   │   │   │   │   ├── params/
│   │   │   │   │   │   └── delete-message.param.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── get-messages/
│   │   │   │   │       │   ├── get-messages.command.ts
│   │   │   │   │       │   ├── get-messages.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       ├── remove-message/
│   │   │   │   │       │   ├── index.ts
│   │   │   │   │       │   ├── remove-message.command.ts
│   │   │   │   │       │   └── remove-message.usecase.ts
│   │   │   │   │       └── remove-messages-by-transactionId/
│   │   │   │   │           ├── remove-messages-by-transactionId.command.ts
│   │   │   │   │           └── remove-messages-by-transactionId.usecase.ts
│   │   │   │   ├── notification-groups/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── create-notification-group-request.dto.ts
│   │   │   │   │   │   ├── delete-notification-group-response.dto.ts
│   │   │   │   │   │   └── notification-group-response.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── create-notification-group.e2e.ts
│   │   │   │   │   │   ├── delete-notification-group.e2e.ts
│   │   │   │   │   │   ├── get-notification-group.e2e.ts
│   │   │   │   │   │   ├── get-notification-groups.e2e.ts
│   │   │   │   │   │   └── update-notification-group.e2e.ts
│   │   │   │   │   ├── notification-groups.controller.ts
│   │   │   │   │   ├── notification-groups.module.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── create-notification-group/
│   │   │   │   │       │   ├── create-notification-group.command.ts
│   │   │   │   │       │   └── create-notification-group.usecase.ts
│   │   │   │   │       ├── delete-notification-group/
│   │   │   │   │       │   ├── delete-notification-group.command.ts
│   │   │   │   │       │   └── delete-notification-group.usecase.ts
│   │   │   │   │       ├── get-notification-group/
│   │   │   │   │       │   ├── get-notification-group.command.ts
│   │   │   │   │       │   └── get-notification-group.usecase.ts
│   │   │   │   │       ├── get-notification-groups/
│   │   │   │   │       │   ├── get-notification-groups.command.ts
│   │   │   │   │       │   └── get-notification-groups.usecase.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       └── update-notification-group/
│   │   │   │   │           ├── update-notification-group.command.ts
│   │   │   │   │           └── update-notification-group.usecase.ts
│   │   │   │   ├── notifications/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── activities-request.dto.ts
│   │   │   │   │   │   ├── activities-response.dto.ts
│   │   │   │   │   │   ├── activity-graph-states-response.dto.ts
│   │   │   │   │   │   └── activity-stats-response.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── get-activity-feed.e2e.ts
│   │   │   │   │   │   └── get-activity.e2e.ts
│   │   │   │   │   ├── notification.controller.ts
│   │   │   │   │   ├── notification.module.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── get-activity/
│   │   │   │   │       │   ├── get-activity.command.ts
│   │   │   │   │       │   └── get-activity.usecase.ts
│   │   │   │   │       ├── get-activity-feed/
│   │   │   │   │       │   ├── get-activity-feed.command.ts
│   │   │   │   │       │   ├── get-activity-feed.usecase.spec.ts
│   │   │   │   │       │   ├── get-activity-feed.usecase.ts
│   │   │   │   │       │   └── map-feed-item-to.dto.ts
│   │   │   │   │       ├── get-activity-graph-states/
│   │   │   │   │       │   ├── get-activity-graph-states.command.ts
│   │   │   │   │       │   └── get-activity-graph-states.usecase.ts
│   │   │   │   │       ├── get-activity-stats/
│   │   │   │   │       │   ├── get-activity-stats.command.ts
│   │   │   │   │       │   ├── get-activity-stats.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       └── index.ts
│   │   │   │   ├── organization/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── create-organization.dto.ts
│   │   │   │   │   │   ├── get-my-organization.dto.ts
│   │   │   │   │   │   ├── get-organization-settings.dto.ts
│   │   │   │   │   │   ├── get-organizations.dto.ts
│   │   │   │   │   │   ├── member-response.dto.ts
│   │   │   │   │   │   ├── organization-response.dto.ts
│   │   │   │   │   │   ├── rename-organization.dto.ts
│   │   │   │   │   │   ├── update-branding-details.dto.ts
│   │   │   │   │   │   ├── update-member-roles.dto.ts
│   │   │   │   │   │   └── update-organization-settings.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── change-member-role.e2e.ts
│   │   │   │   │   │   ├── create-organization.e2e.ts
│   │   │   │   │   │   ├── get-members.e2e.ts
│   │   │   │   │   │   ├── get-my-organization.e2e.ts
│   │   │   │   │   │   ├── get-organizations.e2e.ts
│   │   │   │   │   │   ├── remove-member.e2e.ts
│   │   │   │   │   │   ├── rename-organization.e2e.ts
│   │   │   │   │   │   ├── update-branding-details.e2e.ts
│   │   │   │   │   │   └── update-organization-settings.e2e.ts
│   │   │   │   │   ├── ee.organization.controller.ts
│   │   │   │   │   ├── organization.controller.ts
│   │   │   │   │   ├── organization.module.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── create-organization/
│   │   │   │   │       │   ├── create-organization.command.ts
│   │   │   │   │       │   ├── create-organization.usecase.ts
│   │   │   │   │       │   └── sync-external-organization/
│   │   │   │   │       │       ├── sync-external-organization.command.ts
│   │   │   │   │       │       └── sync-external-organization.usecase.ts
│   │   │   │   │       ├── get-my-organization/
│   │   │   │   │       │   ├── get-my-organization.command.ts
│   │   │   │   │       │   └── get-my-organization.usecase.ts
│   │   │   │   │       ├── get-organization/
│   │   │   │   │       │   ├── get-organization.command.ts
│   │   │   │   │       │   └── get-organization.usecase.ts
│   │   │   │   │       ├── get-organization-settings/
│   │   │   │   │       │   ├── get-organization-settings.command.ts
│   │   │   │   │       │   └── get-organization-settings.usecase.ts
│   │   │   │   │       ├── get-organizations/
│   │   │   │   │       │   ├── get-organizations.command.ts
│   │   │   │   │       │   └── get-organizations.usecase.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       ├── membership/
│   │   │   │   │       │   ├── add-member/
│   │   │   │   │       │   │   ├── add-member.command.ts
│   │   │   │   │       │   │   └── add-member.usecase.ts
│   │   │   │   │       │   ├── change-member-role/
│   │   │   │   │       │   │   ├── change-member-role.command.ts
│   │   │   │   │       │   │   └── change-member-role.usecase.ts
│   │   │   │   │       │   ├── get-members/
│   │   │   │   │       │   │   ├── get-members.command.ts
│   │   │   │   │       │   │   └── get-members.usecase.ts
│   │   │   │   │       │   └── remove-member/
│   │   │   │   │       │       ├── remove-member.command.ts
│   │   │   │   │       │       └── remove-member.usecase.ts
│   │   │   │   │       ├── rename-organization/
│   │   │   │   │       │   ├── rename-organization-command.ts
│   │   │   │   │       │   └── rename-organization.usecase.ts
│   │   │   │   │       ├── update-branding-details/
│   │   │   │   │       │   ├── update-branding-details.command.ts
│   │   │   │   │       │   └── update-branding-details.usecase.ts
│   │   │   │   │       └── update-organization-settings/
│   │   │   │   │           ├── update-organization-settings.command.ts
│   │   │   │   │           └── update-organization-settings.usecase.ts
│   │   │   │   ├── outbound-webhooks/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── create-webhook-portal-response.dto.ts
│   │   │   │   │   │   └── get-webhook-portal-token-response.dto.ts
│   │   │   │   │   ├── outbound-webhooks.controller.ts
│   │   │   │   │   ├── outbound-webhooks.module.ts
│   │   │   │   │   ├── usecases/
│   │   │   │   │   │   ├── create-webhook-portal-token/
│   │   │   │   │   │   │   ├── create-webhook-portal.command.ts
│   │   │   │   │   │   │   └── create-webhook-portal.usecase.ts
│   │   │   │   │   │   └── get-webhook-portal-token/
│   │   │   │   │   │       ├── get-webhook-portal-token.command.ts
│   │   │   │   │   │       └── get-webhook-portal-token.usecase.ts
│   │   │   │   │   └── webhooks.const.ts
│   │   │   │   ├── partner-integrations/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── create-vercel-integration-request.dto.ts
│   │   │   │   │   │   ├── create-vercel-integration-response.dto.ts
│   │   │   │   │   │   └── update-vercel-integration-request.dto.ts
│   │   │   │   │   ├── partner-integrations.controller.ts
│   │   │   │   │   ├── partner-integrations.module.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── create-vercel-integration/
│   │   │   │   │       │   ├── create-vercel-integration.command.ts
│   │   │   │   │       │   ├── create-vercel-integration.spec.ts
│   │   │   │   │       │   └── create-vercel-integration.usecase.ts
│   │   │   │   │       ├── get-vercel-integration/
│   │   │   │   │       │   ├── get-vercel-integration.command.ts
│   │   │   │   │       │   ├── get-vercel-integration.spec.ts
│   │   │   │   │       │   └── get-vercel-integration.usecase.ts
│   │   │   │   │       ├── get-vercel-projects/
│   │   │   │   │       │   ├── get-vercel-integration-projects.command.ts
│   │   │   │   │       │   ├── get-vercel-integration-projects.spec.ts
│   │   │   │   │       │   └── get-vercel-integration-projects.usecase.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       ├── process-vercel-webhook/
│   │   │   │   │       │   ├── process-vercel-webhook.command.ts
│   │   │   │   │       │   ├── process-vercel-webhook.spec.ts
│   │   │   │   │       │   └── process-vercel-webhook.usecase.ts
│   │   │   │   │       └── update-vercel-integration/
│   │   │   │   │           ├── update-vercel-integration.command.ts
│   │   │   │   │           ├── update-vercel-integration.spec.ts
│   │   │   │   │           └── update-vercel-integration.usecase.ts
│   │   │   │   ├── preferences/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── preferences.dto.ts
│   │   │   │   │   │   └── upsert-preferences.dto.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── preferences.controller.ts
│   │   │   │   │   ├── preferences.module.ts
│   │   │   │   │   └── preferences.spec.ts
│   │   │   │   ├── rate-limiting/
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   └── throttler.guard.e2e.ts
│   │   │   │   │   ├── guards/
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── throttler.decorator.ts
│   │   │   │   │   │   └── throttler.guard.ts
│   │   │   │   │   ├── rate-limiting.module.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── evaluate-api-rate-limit/
│   │   │   │   │       │   ├── evaluate-api-rate-limit.command.ts
│   │   │   │   │       │   ├── evaluate-api-rate-limit.spec.ts
│   │   │   │   │       │   ├── evaluate-api-rate-limit.types.ts
│   │   │   │   │       │   ├── evaluate-api-rate-limit.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── evaluate-token-bucket-rate-limit/
│   │   │   │   │       │   ├── evaluate-token-bucket-rate-limit.command.ts
│   │   │   │   │       │   ├── evaluate-token-bucket-rate-limit.spec.ts
│   │   │   │   │       │   ├── evaluate-token-bucket-rate-limit.types.ts
│   │   │   │   │       │   ├── evaluate-token-bucket-rate-limit.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── get-api-rate-limit-algorithm-config/
│   │   │   │   │       │   ├── get-api-rate-limit-algorithm-config.spec.ts
│   │   │   │   │       │   ├── get-api-rate-limit-algorithm-config.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── get-api-rate-limit-cost-config/
│   │   │   │   │       │   ├── get-api-rate-limit-cost-config.spec.ts
│   │   │   │   │       │   ├── get-api-rate-limit-cost-config.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── get-api-rate-limit-maximum/
│   │   │   │   │       │   ├── get-api-rate-limit-maximum.command.ts
│   │   │   │   │       │   ├── get-api-rate-limit-maximum.dto.ts
│   │   │   │   │       │   ├── get-api-rate-limit-maximum.spec.ts
│   │   │   │   │       │   ├── get-api-rate-limit-maximum.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       └── index.ts
│   │   │   │   ├── shared/
│   │   │   │   │   ├── commands/
│   │   │   │   │   │   ├── authenticated.command.ts
│   │   │   │   │   │   ├── organization.command.ts
│   │   │   │   │   │   └── project.command.ts
│   │   │   │   │   ├── constants.ts
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── api-key.ts
│   │   │   │   │   │   ├── base-responses.ts
│   │   │   │   │   │   ├── base-subscriber-fields.dto.ts
│   │   │   │   │   │   ├── channel-preference.ts
│   │   │   │   │   │   ├── cursor-paginated-response.ts
│   │   │   │   │   │   ├── cursor-pagination-request.ts
│   │   │   │   │   │   ├── data-wrapper-dto.ts
│   │   │   │   │   │   ├── limit-offset-pagination.dto.ts
│   │   │   │   │   │   ├── message-template.ts
│   │   │   │   │   │   ├── message.template.dto.ts
│   │   │   │   │   │   ├── notification-step-dto.ts
│   │   │   │   │   │   ├── pagination-request.ts
│   │   │   │   │   │   ├── pagination-response.ts
│   │   │   │   │   │   ├── pagination-with-filters-request.ts
│   │   │   │   │   │   ├── preference-channels.ts
│   │   │   │   │   │   ├── schedule.ts
│   │   │   │   │   │   ├── subscription-details-response.dto.ts
│   │   │   │   │   │   └── subscriptions/
│   │   │   │   │   │       ├── create-subscriptions-response.dto.ts
│   │   │   │   │   │       ├── create-subscriptions.dto.ts
│   │   │   │   │   │       └── update-subscription.dto.ts
│   │   │   │   │   ├── framework/
│   │   │   │   │   │   ├── analytics-logs.guard.ts
│   │   │   │   │   │   ├── analytics-logs.interceptor.ts
│   │   │   │   │   │   ├── constants/
│   │   │   │   │   │   │   ├── headers.schema.ts
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   └── responses.schema.ts
│   │   │   │   │   │   ├── exclude-from-idempotency.ts
│   │   │   │   │   │   ├── idempotency.e2e.ts
│   │   │   │   │   │   ├── idempotency.interceptor.ts
│   │   │   │   │   │   ├── paginated-ok-response.decorator.ts
│   │   │   │   │   │   ├── response.decorator.ts
│   │   │   │   │   │   ├── response.interceptor.ts
│   │   │   │   │   │   ├── swagger/
│   │   │   │   │   │   │   ├── headers.decorator.ts
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   ├── injection.ts
│   │   │   │   │   │   │   ├── keyless.security.ts
│   │   │   │   │   │   │   ├── open.api.manipulation.component.ts
│   │   │   │   │   │   │   ├── responses.decorator.ts
│   │   │   │   │   │   │   ├── sdk.decorators.ts
│   │   │   │   │   │   │   └── swagger.controller.ts
│   │   │   │   │   │   └── user.decorator.ts
│   │   │   │   │   ├── helpers/
│   │   │   │   │   │   ├── content.service.spec.ts
│   │   │   │   │   │   ├── e2e/
│   │   │   │   │   │   │   └── sdk/
│   │   │   │   │   │   │       └── e2e-sdk.helper.ts
│   │   │   │   │   │   ├── generate-transaction-id.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── is-valid-hmac.ts
│   │   │   │   │   │   └── utils/
│   │   │   │   │   │       ├── index.ts
│   │   │   │   │   │       └── mapMarkMessageToWebSocketEvent.ts
│   │   │   │   │   ├── interceptors/
│   │   │   │   │   │   └── product-feature.interceptor.ts
│   │   │   │   │   ├── middleware/
│   │   │   │   │   │   └── request-id.middleware.ts
│   │   │   │   │   ├── services/
│   │   │   │   │   │   └── encryption/
│   │   │   │   │   │       └── index.ts
│   │   │   │   │   ├── shared.module.ts
│   │   │   │   │   ├── types.ts
│   │   │   │   │   ├── utils/
│   │   │   │   │   │   ├── auth.utils.ts
│   │   │   │   │   │   ├── mappers.ts
│   │   │   │   │   │   └── request-transaction.util.ts
│   │   │   │   │   └── validators/
│   │   │   │   │       ├── image.validator.ts
│   │   │   │   │       ├── is-enum-or-array.ts
│   │   │   │   │       ├── is-mongo-id-or-array-of-ids.validator.ts
│   │   │   │   │       ├── is-time-12-hour-format.validator.ts
│   │   │   │   │       ├── json-schema.validator.ts
│   │   │   │   │       └── weekly-schedule-disabled.validator.ts
│   │   │   │   ├── step-resolvers/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── deploy-step-resolver-request.dto.ts
│   │   │   │   │   │   ├── deploy-step-resolver-response.dto.ts
│   │   │   │   │   │   ├── disconnect-step-resolver-request.dto.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   └── step-resolvers-count-response.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   └── step-resolvers.e2e.ts
│   │   │   │   │   ├── services/
│   │   │   │   │   │   └── cloudflare-step-resolver-deploy.service.ts
│   │   │   │   │   ├── step-resolvers.controller.ts
│   │   │   │   │   ├── step-resolvers.module.ts
│   │   │   │   │   ├── usecases/
│   │   │   │   │   │   ├── deploy-step-resolver/
│   │   │   │   │   │   │   ├── deploy-step-resolver.command.ts
│   │   │   │   │   │   │   ├── deploy-step-resolver.usecase.ts
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   ├── get-step-resolvers-count/
│   │   │   │   │   │   │   ├── get-step-resolvers-count.usecase.ts
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   └── sync-step-resolver-to-environment/
│   │   │   │   │   │       ├── index.ts
│   │   │   │   │   │       ├── sync-step-resolver-to-environment.command.ts
│   │   │   │   │   │       └── sync-step-resolver-to-environment.usecase.ts
│   │   │   │   │   └── utils/
│   │   │   │   │       └── generate-step-resolver-worker-id.ts
│   │   │   │   ├── storage/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   └── upload-url-response.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   └── get-signed-url.e2e.ts
│   │   │   │   │   ├── storage.controller.ts
│   │   │   │   │   ├── storage.module.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── get-signed-url/
│   │   │   │   │       │   ├── get-signed-url.command.ts
│   │   │   │   │       │   └── get-signed-url.usecase.ts
│   │   │   │   │       └── index.ts
│   │   │   │   ├── subscribers/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── bulk-create-subscriber-response.dto.ts
│   │   │   │   │   │   ├── chat-oauth-request.dto.ts
│   │   │   │   │   │   ├── create-subscriber-request.dto.ts
│   │   │   │   │   │   ├── delete-subscriber-response.dto.ts
│   │   │   │   │   │   ├── get-in-app-notification-feed-for-subscriber.dto.ts
│   │   │   │   │   │   ├── get-subscriber-preferences-response.dto.ts
│   │   │   │   │   │   ├── get-subscribers.dto.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── mark-all-messages-as-request.dto.ts
│   │   │   │   │   │   ├── subscriber-feed-response.dto.ts
│   │   │   │   │   │   ├── subscriber-preference-override.dto.ts
│   │   │   │   │   │   ├── subscriber-preference-template-response.dto.ts
│   │   │   │   │   │   ├── subscriber-preference.dto.ts
│   │   │   │   │   │   ├── subscribers-response.dto.ts
│   │   │   │   │   │   ├── update-subscriber-global-preferences-request.dto.ts
│   │   │   │   │   │   ├── update-subscriber-online-flag-request.dto.ts
│   │   │   │   │   │   └── update-subscriber-request.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── bulk-create-subscribers.e2e.ts
│   │   │   │   │   │   ├── create-subscriber.e2e.ts
│   │   │   │   │   │   ├── get-notifications-feed.e2e.ts
│   │   │   │   │   │   ├── get-subscriber.e2e.ts
│   │   │   │   │   │   ├── get-unseen-count.e2e.ts
│   │   │   │   │   │   ├── helpers/
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   ├── mark-all-subscriber-messages.e2e.ts
│   │   │   │   │   │   ├── mark-as-by-mark.e2e.ts
│   │   │   │   │   │   ├── remove-subscriber.e2e.ts
│   │   │   │   │   │   └── update-online-flag.e2e.ts
│   │   │   │   │   ├── params/
│   │   │   │   │   │   ├── get-subscriber-preferences-by-level.params.ts
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── query-objects/
│   │   │   │   │   │   └── unseen-count.query.ts
│   │   │   │   │   ├── subscribersV1.controller.ts
│   │   │   │   │   ├── subscribersV1.module.ts
│   │   │   │   │   ├── unit/
│   │   │   │   │   │   └── update-subscriber-channel.spec.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── bulk-create-subscribers/
│   │   │   │   │       │   ├── bulk-create-subscribers.command.ts
│   │   │   │   │       │   ├── bulk-create-subscribers.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── chat-oauth/
│   │   │   │   │       │   ├── chat-oauth.command.ts
│   │   │   │   │       │   └── chat-oauth.usecase.ts
│   │   │   │   │       ├── chat-oauth-callback/
│   │   │   │   │       │   ├── chat-oauth-callback.command.ts
│   │   │   │   │       │   ├── chat-oauth-callback.result.ts
│   │   │   │   │       │   ├── chat-oauth-callback.usecase.ts
│   │   │   │   │       │   └── is-not-empty.spec.ts
│   │   │   │   │       ├── delete-subscriber-credentials/
│   │   │   │   │       │   ├── delete-subscriber-credentials.command.ts
│   │   │   │   │       │   ├── delete-subscriber-credentials.spec.ts
│   │   │   │   │       │   ├── delete-subscriber-credentials.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── get-preferences-by-level/
│   │   │   │   │       │   ├── get-preferences-by-level.command.ts
│   │   │   │   │       │   └── get-preferences-by-level.usecase.ts
│   │   │   │   │       ├── get-subscriber/
│   │   │   │   │       │   ├── get-subscriber.command.ts
│   │   │   │   │       │   ├── get-subscriber.spec.ts
│   │   │   │   │       │   ├── get-subscriber.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── get-subscriber-global-preference/
│   │   │   │   │       │   ├── get-subscriber-global-preference.command.ts
│   │   │   │   │       │   ├── get-subscriber-global-preference.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── get-subscriber-preference/
│   │   │   │   │       │   ├── get-subscriber-preference.command.ts
│   │   │   │   │       │   ├── get-subscriber-preference.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── get-subscribers/
│   │   │   │   │       │   ├── get-subscribers.command.ts
│   │   │   │   │       │   ├── get-subscribers.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       ├── remove-subscriber/
│   │   │   │   │       │   ├── index.ts
│   │   │   │   │       │   ├── remove-subscriber.command.ts
│   │   │   │   │       │   ├── remove-subscriber.spec.ts
│   │   │   │   │       │   └── remove-subscriber.usecase.ts
│   │   │   │   │       ├── search-by-external-subscriber-ids/
│   │   │   │   │       │   ├── index.ts
│   │   │   │   │       │   ├── search-by-external-subscriber-ids.command.ts
│   │   │   │   │       │   ├── search-by-external-subscriber-ids.spec.ts
│   │   │   │   │       │   └── search-by-external-subscriber-ids.use-case.ts
│   │   │   │   │       └── update-subscriber-online-flag/
│   │   │   │   │           ├── index.ts
│   │   │   │   │           ├── update-subscriber-online-flag.command.ts
│   │   │   │   │           └── update-subscriber-online-flag.usecase.ts
│   │   │   │   ├── subscribers-v2/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── bulk-update-subscriber-preferences.dto.ts
│   │   │   │   │   │   ├── context-keys-query.dto.ts
│   │   │   │   │   │   ├── create-subscriber.dto.ts
│   │   │   │   │   │   ├── cursor-pagination-query.dto.ts
│   │   │   │   │   │   ├── get-subscriber-notifications-count-query.dto.ts
│   │   │   │   │   │   ├── get-subscriber-notifications-count-response.dto.ts
│   │   │   │   │   │   ├── get-subscriber-notifications-query.dto.ts
│   │   │   │   │   │   ├── get-subscriber-notifications-response.dto.ts
│   │   │   │   │   │   ├── get-subscriber-preferences-request.dto.ts
│   │   │   │   │   │   ├── get-subscriber-preferences.dto.ts
│   │   │   │   │   │   ├── inbox-notification.dto.ts
│   │   │   │   │   │   ├── list-subscribers-query.dto.ts
│   │   │   │   │   │   ├── list-subscribers-response.dto.ts
│   │   │   │   │   │   ├── mark-subscriber-notifications-as-seen.dto.ts
│   │   │   │   │   │   ├── patch-subscriber-preferences.dto.ts
│   │   │   │   │   │   ├── patch-subscriber.dto.ts
│   │   │   │   │   │   ├── remove-subscriber.dto.ts
│   │   │   │   │   │   ├── snooze-subscriber-notification.dto.ts
│   │   │   │   │   │   ├── subscriber-global-preference.dto.ts
│   │   │   │   │   │   ├── subscriber-notification-action.dto.ts
│   │   │   │   │   │   ├── subscriber-preferences-workflow-info.dto.ts
│   │   │   │   │   │   ├── subscriber-workflow-preference.dto.ts
│   │   │   │   │   │   └── update-all-subscriber-notifications.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── create-subscriber.e2e.ts
│   │   │   │   │   │   ├── delete-subscriber.e2e.ts
│   │   │   │   │   │   ├── get-subscriber-preferences.e2e.ts
│   │   │   │   │   │   ├── get-subscriber.e2e.ts
│   │   │   │   │   │   ├── list-subscriber-subscriptions.e2e.ts
│   │   │   │   │   │   ├── patch-subscriber-preferences.e2e.ts
│   │   │   │   │   │   ├── patch-subscriber.e2e.ts
│   │   │   │   │   │   └── subscriber-notifications.e2e.ts
│   │   │   │   │   ├── subscribers.controller.e2e.ts
│   │   │   │   │   ├── subscribers.controller.ts
│   │   │   │   │   ├── subscribers.module.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── get-subscriber/
│   │   │   │   │       │   ├── get-subscriber.command.ts
│   │   │   │   │       │   └── get-subscriber.usecase.ts
│   │   │   │   │       ├── get-subscriber-preferences/
│   │   │   │   │       │   ├── get-subscriber-preferences.command.ts
│   │   │   │   │       │   └── get-subscriber-preferences.usecase.ts
│   │   │   │   │       ├── list-subscribers/
│   │   │   │   │       │   ├── list-subscribers.command.ts
│   │   │   │   │       │   ├── list-subscribers.usecase.ts
│   │   │   │   │       │   └── map-subscriber-entity-to.dto.ts
│   │   │   │   │       ├── patch-subscriber/
│   │   │   │   │       │   ├── patch-subscriber.command.ts
│   │   │   │   │       │   └── patch-subscriber.usecase.ts
│   │   │   │   │       ├── remove-subscriber/
│   │   │   │   │       │   ├── remove-subscriber.command.ts
│   │   │   │   │       │   └── remove-subscriber.usecase.ts
│   │   │   │   │       └── update-subscriber-preferences/
│   │   │   │   │           ├── update-subscriber-preferences.command.ts
│   │   │   │   │           └── update-subscriber-preferences.usecase.ts
│   │   │   │   ├── subscriptions/
│   │   │   │   │   ├── subscriptions.module.ts
│   │   │   │   │   ├── usecases/
│   │   │   │   │   │   ├── create-subscription-preferences/
│   │   │   │   │   │   │   ├── create-subscription-preferences.command.ts
│   │   │   │   │   │   │   ├── create-subscription-preferences.usecase.ts
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   ├── create-subscriptions/
│   │   │   │   │   │   │   ├── create-subscriptions.command.ts
│   │   │   │   │   │   │   ├── create-subscriptions.usecase.ts
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   ├── get-subscription/
│   │   │   │   │   │   │   ├── get-subscription.command.ts
│   │   │   │   │   │   │   └── get-subscription.usecase.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   └── update-subscription/
│   │   │   │   │   │       ├── index.ts
│   │   │   │   │   │       ├── update-subscription.command.ts
│   │   │   │   │   │       └── update-subscription.usecase.ts
│   │   │   │   │   └── utils/
│   │   │   │   │       └── subscriptions.ts
│   │   │   │   ├── support/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── create-thread.dto.ts
│   │   │   │   │   │   └── plain-card.dto.ts
│   │   │   │   │   ├── guards/
│   │   │   │   │   │   └── plain-cards.guard.ts
│   │   │   │   │   ├── support.controller.ts
│   │   │   │   │   ├── support.module.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── create-thread.command.ts
│   │   │   │   │       ├── create-thread.usecase.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       ├── plain-cards.command.ts
│   │   │   │   │       └── plain-cards.usecase.ts
│   │   │   │   ├── tenant/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── create-tenant-request.dto.ts
│   │   │   │   │   │   ├── create-tenant-response.dto.ts
│   │   │   │   │   │   ├── get-tenant-response.dto.ts
│   │   │   │   │   │   ├── get-tenants-request.dto.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── update-tenant-request.dto.ts
│   │   │   │   │   │   └── update-tenant-response.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── create-tenant.e2e.ts
│   │   │   │   │   │   ├── delete-tenant.e2e.ts
│   │   │   │   │   │   ├── get-tenant.e2e.ts
│   │   │   │   │   │   ├── get-tenants.e2e.ts
│   │   │   │   │   │   └── update-tenant.e2e.ts
│   │   │   │   │   ├── tenant.controller.ts
│   │   │   │   │   ├── tenant.module.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── delete-tenant/
│   │   │   │   │       │   ├── delete-tenant.command.ts
│   │   │   │   │       │   └── delete-tenant.usecase.ts
│   │   │   │   │       ├── get-tenants/
│   │   │   │   │       │   ├── get-tenants.command.ts
│   │   │   │   │       │   └── get-tenants.usecase.ts
│   │   │   │   │       └── index.ts
│   │   │   │   ├── testing/
│   │   │   │   │   ├── auth.controller.ts
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   └── idempotency.dto.ts
│   │   │   │   │   ├── product-feature.e2e.ts
│   │   │   │   │   ├── rate-limiting.controller.ts
│   │   │   │   │   ├── testing.controller.ts
│   │   │   │   │   └── testing.module.ts
│   │   │   │   ├── topics-v1/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── add-subscribers.dto.ts
│   │   │   │   │   │   ├── assignSubscriberToTopicDto.ts
│   │   │   │   │   │   ├── create-topic.dto.ts
│   │   │   │   │   │   ├── filter-topics.dto.ts
│   │   │   │   │   │   ├── get-topic.dto.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── remove-subscribers.dto.ts
│   │   │   │   │   │   ├── rename-topic.dto.ts
│   │   │   │   │   │   ├── topic-subscriber.dto.ts
│   │   │   │   │   │   └── topic.dto.ts
│   │   │   │   │   ├── topics-v1.controller.ts
│   │   │   │   │   ├── topics-v1.module.ts
│   │   │   │   │   ├── types/
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   └── use-cases/
│   │   │   │   │       ├── add-subscribers/
│   │   │   │   │       │   ├── add-subscribers.command.ts
│   │   │   │   │       │   ├── add-subscribers.use-case.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── create-topic/
│   │   │   │   │       │   ├── create-topic.command.ts
│   │   │   │   │       │   ├── create-topic.use-case.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── delete-topic/
│   │   │   │   │       │   ├── delete-topic.command.ts
│   │   │   │   │       │   ├── delete-topic.use-case.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── filter-topics/
│   │   │   │   │       │   ├── filter-topics.command.ts
│   │   │   │   │       │   ├── filter-topics.use-case.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── get-topic/
│   │   │   │   │       │   ├── get-topic.command.ts
│   │   │   │   │       │   ├── get-topic.use-case.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── get-topic-subscriber/
│   │   │   │   │       │   ├── get-topic-subscriber.command.ts
│   │   │   │   │       │   ├── get-topic-subscriber.use-case.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       ├── remove-subscribers/
│   │   │   │   │       │   ├── index.ts
│   │   │   │   │       │   ├── remove-subscribers.command.ts
│   │   │   │   │       │   └── remove-subscribers.use-case.ts
│   │   │   │   │       └── rename-topic/
│   │   │   │   │           ├── index.ts
│   │   │   │   │           ├── rename-topic.command.ts
│   │   │   │   │           └── rename-topic.use-case.ts
│   │   │   │   ├── topics-v2/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── create-topic-subscriptions.dto.ts
│   │   │   │   │   │   ├── create-update-topic.dto.ts
│   │   │   │   │   │   ├── cursor-pagination-query.dto.ts
│   │   │   │   │   │   ├── delete-topic-response.dto.ts
│   │   │   │   │   │   ├── delete-topic-subscriptions-response.dto.ts
│   │   │   │   │   │   ├── delete-topic-subscriptions.dto.ts
│   │   │   │   │   │   ├── list-subscriber-subscriptions-query.dto.ts
│   │   │   │   │   │   ├── list-topic-subscriptions-query.dto.ts
│   │   │   │   │   │   ├── list-topic-subscriptions-response.dto.ts
│   │   │   │   │   │   ├── list-topics-query.dto.ts
│   │   │   │   │   │   ├── list-topics-response.dto.ts
│   │   │   │   │   │   ├── topic-response.dto.ts
│   │   │   │   │   │   ├── topic-subscription-response.dto.ts
│   │   │   │   │   │   ├── update-topic-subscription.dto.ts
│   │   │   │   │   │   └── update-topic.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── create-topic-subscriptions.e2e.ts
│   │   │   │   │   │   ├── delete-topic-subscriptions.e2e.ts
│   │   │   │   │   │   ├── delete-topic.e2e.ts
│   │   │   │   │   │   ├── get-topic.e2e.ts
│   │   │   │   │   │   ├── list-topic-subscriptions.e2e.ts
│   │   │   │   │   │   ├── list-topics.e2e.ts
│   │   │   │   │   │   ├── update-topic-subscription.e2e.ts
│   │   │   │   │   │   ├── update-topic.e2e.ts
│   │   │   │   │   │   └── upsert-topic.e2e.ts
│   │   │   │   │   ├── topics-v2.module.ts
│   │   │   │   │   ├── topics.controller.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── delete-topic/
│   │   │   │   │       │   ├── delete-topic.command.ts
│   │   │   │   │       │   └── delete-topic.usecase.ts
│   │   │   │   │       ├── delete-topic-subscriptions/
│   │   │   │   │       │   ├── delete-topic-subscriptions.command.ts
│   │   │   │   │       │   ├── delete-topic-subscriptions.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── get-topic/
│   │   │   │   │       │   ├── get-topic.command.ts
│   │   │   │   │       │   └── get-topic.usecase.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       ├── list-subscriber-subscriptions/
│   │   │   │   │       │   ├── index.ts
│   │   │   │   │       │   ├── list-subscriber-subscriptions.command.ts
│   │   │   │   │       │   └── list-subscriber-subscriptions.usecase.ts
│   │   │   │   │       ├── list-topic-subscriptions/
│   │   │   │   │       │   ├── list-topic-subscriptions.command.ts
│   │   │   │   │       │   └── list-topic-subscriptions.usecase.ts
│   │   │   │   │       ├── list-topics/
│   │   │   │   │       │   ├── list-topics.command.ts
│   │   │   │   │       │   ├── list-topics.usecase.ts
│   │   │   │   │       │   └── map-topic-entity-to.dto.ts
│   │   │   │   │       ├── update-topic/
│   │   │   │   │       │   ├── update-topic.command.ts
│   │   │   │   │       │   └── update-topic.usecase.ts
│   │   │   │   │       └── upsert-topic/
│   │   │   │   │           ├── upsert-topic.command.ts
│   │   │   │   │           └── upsert-topic.usecase.ts
│   │   │   │   ├── translations/
│   │   │   │   │   └── e2e/
│   │   │   │   │       ├── v1/
│   │   │   │   │       │   ├── create-translation.e2e-ee.ts
│   │   │   │   │       │   ├── delete-translation-group.e2e-ee.ts
│   │   │   │   │       │   ├── delete-translation.e2e-ee.ts
│   │   │   │   │       │   ├── edit-translation.e2e-ee.ts
│   │   │   │   │       │   ├── get-locales-from-content.e2e-ee.ts
│   │   │   │   │       │   ├── get-locales.e2e-ee.ts
│   │   │   │   │       │   ├── get-translation-group.e2e-ee.ts
│   │   │   │   │       │   ├── get-translation-groups.e2e-ee.ts
│   │   │   │   │       │   ├── get-translation.e2e-ee.ts
│   │   │   │   │       │   ├── update-default-locale.e2e-ee.ts
│   │   │   │   │       │   └── update-translation.e2e-ee.ts
│   │   │   │   │       └── v2/
│   │   │   │   │           ├── create-translation.e2e-ee.ts
│   │   │   │   │           ├── delete-translation-group.e2e-ee.ts
│   │   │   │   │           ├── delete-translation.e2e-ee.ts
│   │   │   │   │           ├── export-master-json.e2e-ee.ts
│   │   │   │   │           ├── get-translation-group.e2e-ee.ts
│   │   │   │   │           ├── get-translation.e2e-ee.ts
│   │   │   │   │           ├── get-translations-list.e2e-ee.ts
│   │   │   │   │           ├── import-master-json.e2e-ee.ts
│   │   │   │   │           ├── translation-replacement.e2e-ee.ts
│   │   │   │   │           ├── upload-master-json.e2e-ee.ts
│   │   │   │   │           └── upload-translations.e2e-ee.ts
│   │   │   │   ├── user/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── change-profile-email.dto.ts
│   │   │   │   │   │   ├── update-profile-request.dto.ts
│   │   │   │   │   │   ├── user-onboarding-request.dto.ts
│   │   │   │   │   │   ├── user-onboarding-tour-request.dto.ts
│   │   │   │   │   │   └── user-response.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── email-change.e2e.ts
│   │   │   │   │   │   ├── get-me.e2e.ts
│   │   │   │   │   │   └── update-name-and-profile-picture.e2e.ts
│   │   │   │   │   ├── usecases/
│   │   │   │   │   │   ├── base-user-profile.usecase.ts
│   │   │   │   │   │   ├── create-user/
│   │   │   │   │   │   │   ├── create-user.command.ts
│   │   │   │   │   │   │   ├── create-user.usecase.ts
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   ├── get-my-profile/
│   │   │   │   │   │   │   ├── get-my-profile.dto.ts
│   │   │   │   │   │   │   └── get-my-profile.usecase.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── update-name-and-profile-picture/
│   │   │   │   │   │   │   ├── update-name-and-profile-picture.command.ts
│   │   │   │   │   │   │   └── update-name-and-profile-picture.usecase.ts
│   │   │   │   │   │   ├── update-on-boarding/
│   │   │   │   │   │   │   ├── update-on-boarding.command.ts
│   │   │   │   │   │   │   └── update-on-boarding.usecase.ts
│   │   │   │   │   │   ├── update-on-boarding-tour/
│   │   │   │   │   │   │   ├── update-on-boarding-tour.command.ts
│   │   │   │   │   │   │   └── update-on-boarding-tour.usecase.ts
│   │   │   │   │   │   └── update-profile-email/
│   │   │   │   │   │       ├── update-profile-email.command.ts
│   │   │   │   │   │       └── update-profile-email.usecase.ts
│   │   │   │   │   ├── user.controller.ts
│   │   │   │   │   └── user.module.ts
│   │   │   │   ├── widgets/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── feeds-response.dto.ts
│   │   │   │   │   │   ├── get-notifications-feed-request.dto.ts
│   │   │   │   │   │   ├── log-usage-request.dto.ts
│   │   │   │   │   │   ├── log-usage-response.dto.ts
│   │   │   │   │   │   ├── mark-as-request.dto.ts
│   │   │   │   │   │   ├── mark-message-action-as-seen.dto.ts
│   │   │   │   │   │   ├── mark-message-as-request.dto.ts
│   │   │   │   │   │   ├── message-response.dto.ts
│   │   │   │   │   │   ├── organization-response.dto.ts
│   │   │   │   │   │   ├── remove-all-messages.dto.ts
│   │   │   │   │   │   ├── remove-messages-bulk-request.dto.ts
│   │   │   │   │   │   ├── session-initialize-request.dto.ts
│   │   │   │   │   │   ├── session-initialize-response.dto.ts
│   │   │   │   │   │   ├── unseen-count-response.dto.ts
│   │   │   │   │   │   ├── update-subscriber-preference-request.dto.ts
│   │   │   │   │   │   └── update-subscriber-preference-response.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── get-count.e2e.ts
│   │   │   │   │   │   ├── get-notification-feed.e2e.ts
│   │   │   │   │   │   ├── get-subscriber-preference.e2e.ts
│   │   │   │   │   │   ├── get-unread-count.e2e.ts
│   │   │   │   │   │   ├── get-unseen-count.e2e.ts
│   │   │   │   │   │   ├── initialize-widget-session.e2e.ts
│   │   │   │   │   │   ├── mark-all-as-read.e2e.ts
│   │   │   │   │   │   ├── mark-as-by-mark.e2e.ts
│   │   │   │   │   │   ├── mark-as.e2e.ts
│   │   │   │   │   │   ├── remove-all-messages.e2e.ts
│   │   │   │   │   │   ├── remove-messages-bulk.e2e.ts
│   │   │   │   │   │   └── update-subscriber-preference.e2e.ts
│   │   │   │   │   ├── pipes/
│   │   │   │   │   │   └── limit-pipe/
│   │   │   │   │   │       ├── limit-pipe.spec.ts
│   │   │   │   │   │       └── limit-pipe.ts
│   │   │   │   │   ├── queries/
│   │   │   │   │   │   ├── get-count.query.ts
│   │   │   │   │   │   └── store.query.ts
│   │   │   │   │   ├── usecases/
│   │   │   │   │   │   ├── get-feed-count/
│   │   │   │   │   │   │   ├── get-feed-count.command.ts
│   │   │   │   │   │   │   └── get-feed-count.usecase.ts
│   │   │   │   │   │   ├── get-notifications-feed/
│   │   │   │   │   │   │   ├── get-notifications-feed.command.ts
│   │   │   │   │   │   │   └── get-notifications-feed.usecase.ts
│   │   │   │   │   │   ├── get-organization-data/
│   │   │   │   │   │   │   ├── get-organization-data.command.ts
│   │   │   │   │   │   │   └── get-organization-data.usecase.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── initialize-session/
│   │   │   │   │   │   │   ├── initialize-session.command.ts
│   │   │   │   │   │   │   └── initialize-session.usecase.ts
│   │   │   │   │   │   ├── mark-action-as-done/
│   │   │   │   │   │   │   ├── update-message-actions.command.ts
│   │   │   │   │   │   │   └── update-message-actions.usecase.ts
│   │   │   │   │   │   ├── mark-all-messages-as/
│   │   │   │   │   │   │   ├── mark-all-messages-as.command.ts
│   │   │   │   │   │   │   └── mark-all-messages-as.usecase.ts
│   │   │   │   │   │   ├── mark-message-as/
│   │   │   │   │   │   │   ├── mark-message-as.command.ts
│   │   │   │   │   │   │   └── mark-message-as.usecase.ts
│   │   │   │   │   │   ├── mark-message-as-by-mark/
│   │   │   │   │   │   │   ├── mark-message-as-by-mark.command.ts
│   │   │   │   │   │   │   └── mark-message-as-by-mark.usecase.ts
│   │   │   │   │   │   ├── remove-message/
│   │   │   │   │   │   │   ├── remove-message.command.ts
│   │   │   │   │   │   │   └── remove-message.usecase.ts
│   │   │   │   │   │   ├── remove-messages/
│   │   │   │   │   │   │   ├── remove-all-messages.command.ts
│   │   │   │   │   │   │   └── remove-all-messages.usecase.ts
│   │   │   │   │   │   └── remove-messages-bulk/
│   │   │   │   │   │       ├── remove-messages-bulk.command.ts
│   │   │   │   │   │       └── remove-messages-bulk.usecase.ts
│   │   │   │   │   ├── widgets.controller.ts
│   │   │   │   │   └── widgets.module.ts
│   │   │   │   ├── workflow-overrides/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── create-workflow-override-request.dto.ts
│   │   │   │   │   │   ├── create-workflow-override-response.dto.ts
│   │   │   │   │   │   ├── get-workflow-override-response.dto.ts
│   │   │   │   │   │   ├── get-workflow-overrides-request.dto.ts
│   │   │   │   │   │   ├── get-workflow-overrides-response.dto.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── shared.ts
│   │   │   │   │   │   ├── update-workflow-override-request.dto.ts
│   │   │   │   │   │   └── update-workflow-override-response.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── create-workflow-override.e2e.ts
│   │   │   │   │   │   ├── delete-workflow-override.e2e.ts
│   │   │   │   │   │   ├── get-workflow-override-by-id.e2e.ts
│   │   │   │   │   │   ├── get-workflow-override.e2e.ts
│   │   │   │   │   │   ├── get-workflow-overrides.e2e.ts
│   │   │   │   │   │   ├── update-workflow-override-by-id.e2e.ts
│   │   │   │   │   │   └── update-workflow-override.e2e.ts
│   │   │   │   │   ├── usecases/
│   │   │   │   │   │   ├── create-workflow-override/
│   │   │   │   │   │   │   ├── create-workflow-override.command.ts
│   │   │   │   │   │   │   └── create-workflow-override.usecase.ts
│   │   │   │   │   │   ├── delete-workflow-override/
│   │   │   │   │   │   │   ├── delete-workflow-override.command.ts
│   │   │   │   │   │   │   └── delete-workflow-override.usecase.ts
│   │   │   │   │   │   ├── get-workflow-override/
│   │   │   │   │   │   │   ├── get-workflow-override.command.ts
│   │   │   │   │   │   │   └── get-workflow-override.usecase.ts
│   │   │   │   │   │   ├── get-workflow-override-by-id/
│   │   │   │   │   │   │   ├── get-workflow-override-by-id.command.ts
│   │   │   │   │   │   │   └── get-workflow-override-by-id.usecase.ts
│   │   │   │   │   │   ├── get-workflow-overrides/
│   │   │   │   │   │   │   ├── get-workflow-overrides.command.ts
│   │   │   │   │   │   │   └── get-workflow-overrides.usecase.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── update-workflow-override/
│   │   │   │   │   │   │   ├── update-workflow-override.command.ts
│   │   │   │   │   │   │   └── update-workflow-override.usecase.ts
│   │   │   │   │   │   └── update-workflow-override-by-id/
│   │   │   │   │   │       ├── update-workflow-override-by-id.command.ts
│   │   │   │   │   │       └── update-workflow-override-by-id.usecase.ts
│   │   │   │   │   ├── workflow-overrides.controller.ts
│   │   │   │   │   └── workflow-overrides.module.ts
│   │   │   │   ├── workflows-v1/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── change-workflow-status-request.dto.ts
│   │   │   │   │   │   ├── create-workflow.request.dto.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── update-workflow-request.dto.ts
│   │   │   │   │   │   ├── variables.response.dto.ts
│   │   │   │   │   │   ├── workflow-response.dto.ts
│   │   │   │   │   │   ├── workflows-request.dto.ts
│   │   │   │   │   │   └── workflows.response.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── change-template-status.e2e.ts
│   │   │   │   │   │   ├── create-notification-templates.e2e.ts
│   │   │   │   │   │   ├── delete-notification-template.e2e.ts
│   │   │   │   │   │   ├── get-notification-template.e2e.ts
│   │   │   │   │   │   ├── get-notification-templates.e2e.ts
│   │   │   │   │   │   └── update-notification-template.e2e.ts
│   │   │   │   │   ├── notification-template.controller.ts
│   │   │   │   │   ├── queries/
│   │   │   │   │   │   ├── CreateWorkflowQuery.ts
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── usecases/
│   │   │   │   │   │   ├── change-template-active-status/
│   │   │   │   │   │   │   ├── change-template-active-status.command.ts
│   │   │   │   │   │   │   └── change-template-active-status.usecase.ts
│   │   │   │   │   │   ├── delete-notification-template/
│   │   │   │   │   │   │   ├── delete-notification-template.command.ts
│   │   │   │   │   │   │   └── delete-notification-template.usecase.ts
│   │   │   │   │   │   ├── delete-workflow/
│   │   │   │   │   │   │   ├── delete-workflow.command.ts
│   │   │   │   │   │   │   └── delete-workflow.usecase.ts
│   │   │   │   │   │   ├── get-active-integrations-status/
│   │   │   │   │   │   │   ├── get-active-integrations-status.command.ts
│   │   │   │   │   │   │   ├── get-active-integrations-status.spec.ts
│   │   │   │   │   │   │   └── get-active-integrations-status.usecase.ts
│   │   │   │   │   │   ├── get-notification-template/
│   │   │   │   │   │   │   ├── get-notification-template.command.ts
│   │   │   │   │   │   │   └── get-notification-template.usecase.ts
│   │   │   │   │   │   ├── get-notification-templates/
│   │   │   │   │   │   │   ├── get-notification-templates.command.ts
│   │   │   │   │   │   │   └── get-notification-templates.usecase.ts
│   │   │   │   │   │   ├── get-workflow-variables/
│   │   │   │   │   │   │   ├── get-workflow-variables.command.ts
│   │   │   │   │   │   │   └── get-workflow-variables.usecase.ts
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── workflow-v1.controller.ts
│   │   │   │   │   └── workflow-v1.module.ts
│   │   │   │   └── workflows-v2/
│   │   │   │       ├── dtos/
│   │   │   │       │   ├── base-step-issue.dto.ts
│   │   │   │       │   ├── control-schemas.dto.ts
│   │   │   │       │   ├── create-step.dto.ts
│   │   │   │       │   ├── create-workflow.dto.ts
│   │   │   │       │   ├── duplicate-workflow.dto.ts
│   │   │   │       │   ├── get-list-query-params.ts
│   │   │   │       │   ├── index.ts
│   │   │   │       │   ├── list-workflow.dto.ts
│   │   │   │       │   ├── patch-step-data.dto.ts
│   │   │   │       │   ├── patch-workflow.dto.ts
│   │   │   │       │   ├── preferences.request.dto.ts
│   │   │   │       │   ├── sync-workflow.dto.ts
│   │   │   │       │   ├── test-http-endpoint.dto.ts
│   │   │   │       │   ├── update-workflow.dto.ts
│   │   │   │       │   └── workflow-test-data.dto.ts
│   │   │   │       ├── e2e/
│   │   │   │       │   ├── generate-preview.e2e.ts
│   │   │   │       │   ├── list-workflows.e2e.ts
│   │   │   │       │   └── upsert-workflow.e2e.ts
│   │   │   │       ├── exceptions/
│   │   │   │       │   ├── workflow-not-duplicable-exception.ts
│   │   │   │       │   └── workflow-not-syncable-exception.ts
│   │   │   │       ├── maily-test-data.ts
│   │   │   │       ├── usecases/
│   │   │   │       │   ├── build-test-data/
│   │   │   │       │   │   ├── build-workflow-test-data.command.ts
│   │   │   │       │   │   ├── build-workflow-test-data.usecase.ts
│   │   │   │       │   │   └── index.ts
│   │   │   │       │   ├── duplicate-workflow/
│   │   │   │       │   │   ├── duplicate-workflow.command.ts
│   │   │   │       │   │   ├── duplicate-workflow.usecase.ts
│   │   │   │       │   │   └── index.ts
│   │   │   │       │   ├── index.ts
│   │   │   │       │   ├── list-workflows/
│   │   │   │       │   │   ├── index.ts
│   │   │   │       │   │   ├── list-workflow.usecase.ts
│   │   │   │       │   │   └── list-workflows.command.ts
│   │   │   │       │   ├── patch-workflow/
│   │   │   │       │   │   ├── index.ts
│   │   │   │       │   │   ├── patch-workflow.command.ts
│   │   │   │       │   │   └── patch-workflow.usecase.ts
│   │   │   │       │   ├── sync-to-environment/
│   │   │   │       │   │   ├── index.ts
│   │   │   │       │   │   ├── sync-to-environment.command.ts
│   │   │   │       │   │   └── sync-to-environment.usecase.ts
│   │   │   │       │   └── test-http-endpoint/
│   │   │   │       │       ├── index.ts
│   │   │   │       │       ├── test-http-endpoint.command.ts
│   │   │   │       │       └── test-http-endpoint.usecase.ts
│   │   │   │       ├── workflow.controller.e2e.ts
│   │   │   │       ├── workflow.controller.ts
│   │   │   │       └── workflow.module.ts
│   │   │   ├── app.module.ts
│   │   │   ├── bootstrap.ts
│   │   │   ├── config/
│   │   │   │   ├── cors.config.spec.ts
│   │   │   │   ├── cors.config.ts
│   │   │   │   ├── env.config.ts
│   │   │   │   ├── env.validators.ts
│   │   │   │   └── index.ts
│   │   │   ├── error-dto.ts
│   │   │   ├── exception-filter.ts
│   │   │   ├── instrument.ts
│   │   │   ├── main.ts
│   │   │   ├── newrelic.ts
│   │   │   ├── types/
│   │   │   │   └── env.d.ts
│   │   │   └── utils/
│   │   │       └── payload-sanitizer.ts
│   │   ├── swagger-spec.json
│   │   ├── swc-register.js
│   │   ├── tsconfig.build.json
│   │   ├── tsconfig.json
│   │   ├── tsconfig.spec.json
│   │   └── webpack.config.js
│   ├── dashboard/
│   │   ├── .example.env
│   │   ├── .gitignore
│   │   ├── .vscode/
│   │   │   └── settings.json
│   │   ├── README.md
│   │   ├── components.json
│   │   ├── docker-entrypoint.sh
│   │   ├── dockerfile
│   │   ├── index.html
│   │   ├── netlify.toml
│   │   ├── package.json
│   │   ├── playwright.config.ts
│   │   ├── postcss.config.js
│   │   ├── public/
│   │   │   └── manifest.json
│   │   ├── src/
│   │   │   ├── api/
│   │   │   │   ├── activity.ts
│   │   │   │   ├── ai.ts
│   │   │   │   ├── api.client.ts
│   │   │   │   ├── billing.ts
│   │   │   │   ├── bridge.ts
│   │   │   │   ├── contexts.ts
│   │   │   │   ├── environment-variables.ts
│   │   │   │   ├── environments.ts
│   │   │   │   ├── integrations.ts
│   │   │   │   ├── layouts.ts
│   │   │   │   ├── logs.ts
│   │   │   │   ├── organization.ts
│   │   │   │   ├── partner-integrations.ts
│   │   │   │   ├── step-resolvers.ts
│   │   │   │   ├── steps.ts
│   │   │   │   ├── subscribers.ts
│   │   │   │   ├── telemetry.ts
│   │   │   │   ├── topics.ts
│   │   │   │   ├── translations.ts
│   │   │   │   ├── webhooks.ts
│   │   │   │   └── workflows.ts
│   │   │   ├── components/
│   │   │   │   ├── activity/
│   │   │   │   │   ├── activity-detail-card.tsx
│   │   │   │   │   ├── activity-empty-state.tsx
│   │   │   │   │   ├── activity-error.tsx
│   │   │   │   │   ├── activity-feed-content.tsx
│   │   │   │   │   ├── activity-filters.tsx
│   │   │   │   │   ├── activity-header.tsx
│   │   │   │   │   ├── activity-job-item.tsx
│   │   │   │   │   ├── activity-logs.tsx
│   │   │   │   │   ├── activity-panel.tsx
│   │   │   │   │   ├── activity-skeleton.tsx
│   │   │   │   │   ├── activity-table.tsx
│   │   │   │   │   ├── components/
│   │   │   │   │   │   ├── activity-overview.tsx
│   │   │   │   │   │   ├── activity-table-row.tsx
│   │   │   │   │   │   ├── overview-item.tsx
│   │   │   │   │   │   ├── status-badge.tsx
│   │   │   │   │   │   ├── status-preview-card.tsx
│   │   │   │   │   │   └── step-indicators.tsx
│   │   │   │   │   ├── constants.ts
│   │   │   │   │   ├── execution-detail-item.tsx
│   │   │   │   │   └── helpers.ts
│   │   │   │   ├── ai-drawer/
│   │   │   │   │   ├── ai-drawer-provider.tsx
│   │   │   │   │   ├── ai-drawer.tsx
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── use-ai-drawer.ts
│   │   │   │   ├── ai-elements/
│   │   │   │   │   ├── chain-of-thought.tsx
│   │   │   │   │   ├── conversation.tsx
│   │   │   │   │   ├── message.tsx
│   │   │   │   │   ├── prompt-input.tsx
│   │   │   │   │   └── shimmer.tsx
│   │   │   │   ├── ai-sidekick/
│   │   │   │   │   ├── ai-chat-context.tsx
│   │   │   │   │   ├── assistant-message.tsx
│   │   │   │   │   ├── chat-body.tsx
│   │   │   │   │   ├── chat-chain-of-thought.tsx
│   │   │   │   │   ├── chat-message-actions.tsx
│   │   │   │   │   ├── chat-message-response.tsx
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── message-utils.ts
│   │   │   │   │   ├── novu-copilot-panel.tsx
│   │   │   │   │   ├── sidekick-toast.tsx
│   │   │   │   │   └── user-message.tsx
│   │   │   │   ├── amount-input.tsx
│   │   │   │   ├── analytics/
│   │   │   │   │   ├── charts/
│   │   │   │   │   │   ├── active-subscribers-tooltip.tsx
│   │   │   │   │   │   ├── active-subscribers-trend-chart.tsx
│   │   │   │   │   │   ├── chart-dummy-data.tsx
│   │   │   │   │   │   ├── chart-empty-state.tsx
│   │   │   │   │   │   ├── chart-types.ts
│   │   │   │   │   │   ├── chart-wrapper.tsx
│   │   │   │   │   │   ├── delivery-trends-chart.tsx
│   │   │   │   │   │   ├── flickering-grid.tsx
│   │   │   │   │   │   ├── interaction-trend-chart.tsx
│   │   │   │   │   │   ├── providers-by-volume.tsx
│   │   │   │   │   │   ├── workflow-runs-trend-chart.tsx
│   │   │   │   │   │   └── workflows-by-volume.tsx
│   │   │   │   │   ├── components/
│   │   │   │   │   │   ├── analytics-page-skeleton.tsx
│   │   │   │   │   │   ├── analytics-section.tsx
│   │   │   │   │   │   ├── analytics-upgrade-cta-icon.tsx
│   │   │   │   │   │   ├── charts-section.tsx
│   │   │   │   │   │   └── flickering-grid-placeholder.tsx
│   │   │   │   │   ├── constants/
│   │   │   │   │   │   ├── analytics-page.consts.ts
│   │   │   │   │   │   └── analytics-tooltips.ts
│   │   │   │   │   ├── hooks/
│   │   │   │   │   │   └── use-analytics-page-date-filter.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── utils/
│   │   │   │   │       └── chart-validation.ts
│   │   │   │   ├── animated-outlet.tsx
│   │   │   │   ├── auth/
│   │   │   │   │   ├── auth-card.tsx
│   │   │   │   │   ├── auth-feature-row.tsx
│   │   │   │   │   ├── auth-side-banner.tsx
│   │   │   │   │   ├── create-organization.tsx
│   │   │   │   │   ├── inbox-playground.tsx
│   │   │   │   │   ├── inbox-preview-content.tsx
│   │   │   │   │   ├── mobile-message.tsx
│   │   │   │   │   ├── questionnaire-form.tsx
│   │   │   │   │   ├── region-picker.tsx
│   │   │   │   │   ├── shared.tsx
│   │   │   │   │   ├── trusted-companies.tsx
│   │   │   │   │   ├── usecase-selector.tsx
│   │   │   │   │   └── usecases-list.utils.tsx
│   │   │   │   ├── auth-layout.tsx
│   │   │   │   ├── billing/
│   │   │   │   │   ├── active-plan-banner.tsx
│   │   │   │   │   ├── contact-sales-button.tsx
│   │   │   │   │   ├── features-config.ts
│   │   │   │   │   ├── features.tsx
│   │   │   │   │   ├── plan-action-button.tsx
│   │   │   │   │   ├── plan-switcher.tsx
│   │   │   │   │   ├── plan.tsx
│   │   │   │   │   ├── plans-row.tsx
│   │   │   │   │   └── utils/
│   │   │   │   │       └── action.button.constants.ts
│   │   │   │   ├── command-palette/
│   │   │   │   │   ├── command-menu.tsx
│   │   │   │   │   ├── command-palette-provider.tsx
│   │   │   │   │   ├── command-palette.tsx
│   │   │   │   │   ├── command-types.ts
│   │   │   │   │   ├── commands/
│   │   │   │   │   │   ├── action-commands.tsx
│   │   │   │   │   │   ├── environment-commands.tsx
│   │   │   │   │   │   ├── help-commands.tsx
│   │   │   │   │   │   ├── navigation-commands.tsx
│   │   │   │   │   │   ├── settings-commands.tsx
│   │   │   │   │   │   ├── subscriber-commands.tsx
│   │   │   │   │   │   ├── workflow-commands.tsx
│   │   │   │   │   │   └── workflow-editor-commands.tsx
│   │   │   │   │   ├── hooks/
│   │   │   │   │   │   ├── use-command-palette.ts
│   │   │   │   │   │   ├── use-command-registry.ts
│   │   │   │   │   │   └── use-workflow-editor-context.ts
│   │   │   │   │   └── index.ts
│   │   │   │   ├── conditions-editor/
│   │   │   │   │   ├── add-condition-action.tsx
│   │   │   │   │   ├── add-group-action.tsx
│   │   │   │   │   ├── combinator-selector.tsx
│   │   │   │   │   ├── conditions-editor-context.tsx
│   │   │   │   │   ├── conditions-editor.tsx
│   │   │   │   │   ├── field-selector.tsx
│   │   │   │   │   ├── field-type-editors.ts
│   │   │   │   │   ├── field-type-operators.ts
│   │   │   │   │   ├── help-icon.tsx
│   │   │   │   │   ├── operator-selector.tsx
│   │   │   │   │   ├── rule-actions.tsx
│   │   │   │   │   ├── select-option-utils.tsx
│   │   │   │   │   ├── types.ts
│   │   │   │   │   ├── value-editor.tsx
│   │   │   │   │   └── variable-select.tsx
│   │   │   │   ├── confirmation-modal.tsx
│   │   │   │   ├── context-search-editor.tsx
│   │   │   │   ├── contexts/
│   │   │   │   │   ├── context-activity.tsx
│   │   │   │   │   ├── context-drawer.tsx
│   │   │   │   │   ├── context-filter.tsx
│   │   │   │   │   ├── context-list-blank.tsx
│   │   │   │   │   ├── context-list.tsx
│   │   │   │   │   ├── context-overview.tsx
│   │   │   │   │   ├── context-row.tsx
│   │   │   │   │   ├── contexts-filters.tsx
│   │   │   │   │   ├── create-context-drawer.tsx
│   │   │   │   │   ├── create-context-form.tsx
│   │   │   │   │   ├── empty-contexts-illustration.tsx
│   │   │   │   │   ├── hooks/
│   │   │   │   │   │   ├── use-contexts-navigate.ts
│   │   │   │   │   │   └── use-contexts-url-state.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── schema.ts
│   │   │   │   ├── create-workflow-modal.tsx
│   │   │   │   ├── dashboard-layout.tsx
│   │   │   │   ├── default-pagination.tsx
│   │   │   │   ├── delete-resource-confirmation-dialog.tsx
│   │   │   │   ├── delete-workflow-dialog.tsx
│   │   │   │   ├── editor-overlays.tsx
│   │   │   │   ├── email-editor-select.tsx
│   │   │   │   ├── environments/
│   │   │   │   │   ├── create-environment-button.tsx
│   │   │   │   │   ├── delete-environment-dialog.tsx
│   │   │   │   │   ├── edit-environment-sheet.tsx
│   │   │   │   │   ├── environment-form.tsx
│   │   │   │   │   ├── environments-free-state.tsx
│   │   │   │   │   └── environments-list.tsx
│   │   │   │   ├── flag-circle.tsx
│   │   │   │   ├── full-page-layout.tsx
│   │   │   │   ├── header-navigation/
│   │   │   │   │   ├── customer-support-button.tsx
│   │   │   │   │   ├── edit-bridge-url-button.tsx
│   │   │   │   │   ├── header-button.tsx
│   │   │   │   │   ├── header-navigation.tsx
│   │   │   │   │   ├── layout-usage-indicator.tsx
│   │   │   │   │   ├── no-changes-modal.tsx
│   │   │   │   │   ├── publish-button.tsx
│   │   │   │   │   ├── publish-modal.tsx
│   │   │   │   │   ├── publish-success-modal.tsx
│   │   │   │   │   ├── support-drawer-components.tsx
│   │   │   │   │   ├── support-drawer-constants.ts
│   │   │   │   │   ├── support-drawer.tsx
│   │   │   │   │   └── workflow-hover-card.tsx
│   │   │   │   ├── html-editor.tsx
│   │   │   │   ├── http-logs/
│   │   │   │   │   ├── api-traces-content.tsx
│   │   │   │   │   ├── hooks/
│   │   │   │   │   │   └── use-workflow-runs-url-state.ts
│   │   │   │   │   ├── http-status-badge.tsx
│   │   │   │   │   ├── logs-detail-content.tsx
│   │   │   │   │   ├── logs-detail-empty.tsx
│   │   │   │   │   ├── logs-detail-error.tsx
│   │   │   │   │   ├── logs-detail-header.tsx
│   │   │   │   │   ├── logs-detail-panel.tsx
│   │   │   │   │   ├── logs-detail-skeleton.tsx
│   │   │   │   │   ├── logs-empty-state.tsx
│   │   │   │   │   ├── logs-filters.tsx
│   │   │   │   │   ├── logs-table-row.tsx
│   │   │   │   │   ├── logs-table-skeleton-row.tsx
│   │   │   │   │   ├── logs-table.tsx
│   │   │   │   │   ├── method-badge.tsx
│   │   │   │   │   ├── transaction-id-display.tsx
│   │   │   │   │   ├── workflow-run-activity-drawer.tsx
│   │   │   │   │   ├── workflow-runs-content.tsx
│   │   │   │   │   └── workflow-runs-filters.tsx
│   │   │   │   ├── icons/
│   │   │   │   │   ├── add-subscriber-illustration.tsx
│   │   │   │   │   ├── api.tsx
│   │   │   │   │   ├── arrow-right.tsx
│   │   │   │   │   ├── bell.tsx
│   │   │   │   │   ├── broom-sparkle.tsx
│   │   │   │   │   ├── broom.tsx
│   │   │   │   │   ├── cards-blocks.tsx
│   │   │   │   │   ├── circle-check.tsx
│   │   │   │   │   ├── code-2.tsx
│   │   │   │   │   ├── delete.tsx
│   │   │   │   │   ├── digest-variable-icon.tsx
│   │   │   │   │   ├── email-footer-logo-with-text-stacked.tsx
│   │   │   │   │   ├── email-footer-plain-text.tsx
│   │   │   │   │   ├── email-footer.tsx
│   │   │   │   │   ├── email-header-centered-logo-with-border.tsx
│   │   │   │   │   ├── email-header-logo-with-cover-image.tsx
│   │   │   │   │   ├── email-header-logo-with-text.tsx
│   │   │   │   │   ├── email-header.tsx
│   │   │   │   │   ├── enter-line.tsx
│   │   │   │   │   ├── flags/
│   │   │   │   │   │   ├── eu.tsx
│   │   │   │   │   │   └── us.tsx
│   │   │   │   │   ├── horizontal-card-with-image.tsx
│   │   │   │   │   ├── inbox-arrow-down.tsx
│   │   │   │   │   ├── inbox-bell-filled-dev.tsx
│   │   │   │   │   ├── inbox-bell-filled.tsx
│   │   │   │   │   ├── inbox-bell.tsx
│   │   │   │   │   ├── inbox-ellipsis.tsx
│   │   │   │   │   ├── inbox-settings.tsx
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── information-card-with-logo.tsx
│   │   │   │   │   ├── logo-circle.tsx
│   │   │   │   │   ├── mail-3-fill.tsx
│   │   │   │   │   ├── notification-5-fill.tsx
│   │   │   │   │   ├── novu-icon.tsx
│   │   │   │   │   ├── onboarding-arrow-left.tsx
│   │   │   │   │   ├── paragraph-with-image.tsx
│   │   │   │   │   ├── plug.tsx
│   │   │   │   │   ├── preferences-blank-illustration.tsx
│   │   │   │   │   ├── refresh.tsx
│   │   │   │   │   ├── repeat-play.tsx
│   │   │   │   │   ├── repeat-variable.tsx
│   │   │   │   │   ├── route-fill.tsx
│   │   │   │   │   ├── shield-zap.tsx
│   │   │   │   │   ├── sms.tsx
│   │   │   │   │   ├── sparkling.tsx
│   │   │   │   │   ├── square-two-stack.tsx
│   │   │   │   │   ├── stacked-dots.tsx
│   │   │   │   │   ├── stacked-plus-line.tsx
│   │   │   │   │   ├── target-arrow.tsx
│   │   │   │   │   ├── translate-variable.tsx
│   │   │   │   │   ├── translated-layout-icon.tsx
│   │   │   │   │   ├── translated-workflow.tsx
│   │   │   │   │   ├── trend-line-down.tsx
│   │   │   │   │   ├── trend-line-up.tsx
│   │   │   │   │   ├── utils.ts
│   │   │   │   │   ├── version-control-dev.tsx
│   │   │   │   │   ├── version-control-prod.tsx
│   │   │   │   │   └── workflow-trigger-inbox.tsx
│   │   │   │   ├── in-app-action-dropdown.tsx
│   │   │   │   ├── inbox-button.tsx
│   │   │   │   ├── integrations/
│   │   │   │   │   ├── components/
│   │   │   │   │   │   ├── channel-tabs.tsx
│   │   │   │   │   │   ├── configuration-group.tsx
│   │   │   │   │   │   ├── create-integration-sidebar.tsx
│   │   │   │   │   │   ├── credential-section.tsx
│   │   │   │   │   │   ├── cross-channel-configs-group.tsx
│   │   │   │   │   │   ├── description-with-links.tsx
│   │   │   │   │   │   ├── hooks/
│   │   │   │   │   │   │   ├── use-integration-list.ts
│   │   │   │   │   │   │   ├── use-integration-primary-modal.tsx
│   │   │   │   │   │   │   └── use-sidebar-navigation-manager.ts
│   │   │   │   │   │   ├── inbound-webhook-group.tsx
│   │   │   │   │   │   ├── inbound-webhook-url.tsx
│   │   │   │   │   │   ├── integration-card.tsx
│   │   │   │   │   │   ├── integration-channel-group.tsx
│   │   │   │   │   │   ├── integration-general-settings.tsx
│   │   │   │   │   │   ├── integration-list-item.tsx
│   │   │   │   │   │   ├── integration-settings.tsx
│   │   │   │   │   │   ├── integration-sheet-header.tsx
│   │   │   │   │   │   ├── integration-sheet.tsx
│   │   │   │   │   │   ├── integrations-list.tsx
│   │   │   │   │   │   ├── modals/
│   │   │   │   │   │   │   ├── delete-integration-modal.tsx
│   │   │   │   │   │   │   └── select-primary-integration-modal.tsx
│   │   │   │   │   │   ├── provider-icon.tsx
│   │   │   │   │   │   ├── update-integration-sidebar.tsx
│   │   │   │   │   │   └── utils/
│   │   │   │   │   │       ├── handle-integration-error.ts
│   │   │   │   │   │       └── helpers.ts
│   │   │   │   │   ├── types.ts
│   │   │   │   │   └── utils/
│   │   │   │   │       └── channels.ts
│   │   │   │   ├── issues-panel.tsx
│   │   │   │   ├── layouts/
│   │   │   │   │   ├── component-utils.tsx
│   │   │   │   │   ├── create-layout-btn.tsx
│   │   │   │   │   ├── create-layout-form.tsx
│   │   │   │   │   ├── delete-layout-dialog.tsx
│   │   │   │   │   ├── empty-layouts-illustration.tsx
│   │   │   │   │   ├── hooks/
│   │   │   │   │   │   └── use-layouts-url-state.tsx
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── layout-breadcrumbs.tsx
│   │   │   │   │   ├── layout-control-input.tsx
│   │   │   │   │   ├── layout-editor-factory.tsx
│   │   │   │   │   ├── layout-editor-provider.tsx
│   │   │   │   │   ├── layout-editor-settings-drawer.tsx
│   │   │   │   │   ├── layout-editor-skeleton.tsx
│   │   │   │   │   ├── layout-editor.tsx
│   │   │   │   │   ├── layout-email-body.tsx
│   │   │   │   │   ├── layout-email-editor.tsx
│   │   │   │   │   ├── layout-list-blank.tsx
│   │   │   │   │   ├── layout-list.tsx
│   │   │   │   │   ├── layout-preview-context-panel.tsx
│   │   │   │   │   ├── layout-preview-factory.tsx
│   │   │   │   │   ├── layout-row.tsx
│   │   │   │   │   ├── layouts-filters.tsx
│   │   │   │   │   ├── layouts-list-upgrade-cta.tsx
│   │   │   │   │   └── schema.ts
│   │   │   │   ├── list-no-results.tsx
│   │   │   │   ├── maily/
│   │   │   │   │   ├── blocks/
│   │   │   │   │   │   ├── block-custom-preview.tsx
│   │   │   │   │   │   ├── cards.tsx
│   │   │   │   │   │   ├── digest.tsx
│   │   │   │   │   │   ├── footers.tsx
│   │   │   │   │   │   ├── headers.tsx
│   │   │   │   │   │   └── html.tsx
│   │   │   │   │   ├── maily-config.tsx
│   │   │   │   │   ├── maily-utils.ts
│   │   │   │   │   ├── maily.tsx
│   │   │   │   │   ├── repeat-block-aliases.ts
│   │   │   │   │   ├── repeat-menu-description.tsx
│   │   │   │   │   ├── types.ts
│   │   │   │   │   ├── variables.ts
│   │   │   │   │   └── views/
│   │   │   │   │       ├── for-view.tsx
│   │   │   │   │       ├── html-view.tsx
│   │   │   │   │       ├── maily-variables-list-view.tsx
│   │   │   │   │       └── variable-view.tsx
│   │   │   │   ├── mobile-desktop-prompt.tsx
│   │   │   │   ├── onboarding/
│   │   │   │   │   ├── animated-page.tsx
│   │   │   │   │   └── stepper.tsx
│   │   │   │   ├── page-meta.tsx
│   │   │   │   ├── pause-workflow-dialog.tsx
│   │   │   │   ├── preview-context-section.tsx
│   │   │   │   ├── preview-env-section.tsx
│   │   │   │   ├── preview-subscriber-section.tsx
│   │   │   │   ├── primitives/
│   │   │   │   │   ├── accordion.tsx
│   │   │   │   │   ├── analytics-card.tsx
│   │   │   │   │   ├── animated-number.tsx
│   │   │   │   │   ├── autocomplete.tsx
│   │   │   │   │   ├── avatar.tsx
│   │   │   │   │   ├── badge.tsx
│   │   │   │   │   ├── breadcrumb.tsx
│   │   │   │   │   ├── button-compact.tsx
│   │   │   │   │   ├── button-group.tsx
│   │   │   │   │   ├── button-link.tsx
│   │   │   │   │   ├── button.tsx
│   │   │   │   │   ├── card.tsx
│   │   │   │   │   ├── chart.tsx
│   │   │   │   │   ├── checkbox.tsx
│   │   │   │   │   ├── code-block.tsx
│   │   │   │   │   ├── collapsible.tsx
│   │   │   │   │   ├── color-picker.tsx
│   │   │   │   │   ├── command.tsx
│   │   │   │   │   ├── constants.ts
│   │   │   │   │   ├── container.tsx
│   │   │   │   │   ├── copy-button.tsx
│   │   │   │   │   ├── copy-to-clipboard.tsx
│   │   │   │   │   ├── dialog.tsx
│   │   │   │   │   ├── dropdown-menu.tsx
│   │   │   │   │   ├── editor.tsx
│   │   │   │   │   ├── environment-branch-icon.tsx
│   │   │   │   │   ├── form/
│   │   │   │   │   │   ├── avatar-picker.tsx
│   │   │   │   │   │   ├── faceted-filter/
│   │   │   │   │   │   │   ├── components/
│   │   │   │   │   │   │   │   ├── base-filter-content.tsx
│   │   │   │   │   │   │   │   ├── clear-button.tsx
│   │   │   │   │   │   │   │   ├── filter-badge.tsx
│   │   │   │   │   │   │   │   ├── filter-input.tsx
│   │   │   │   │   │   │   │   ├── multi-filter-content.tsx
│   │   │   │   │   │   │   │   ├── single-filter-content.tsx
│   │   │   │   │   │   │   │   └── text-filter-content.tsx
│   │   │   │   │   │   │   ├── facated-form-filter.tsx
│   │   │   │   │   │   │   ├── hooks/
│   │   │   │   │   │   │   │   └── use-keyboard-navigation.ts
│   │   │   │   │   │   │   ├── styles.ts
│   │   │   │   │   │   │   └── types.ts
│   │   │   │   │   │   ├── form-context.ts
│   │   │   │   │   │   └── form.tsx
│   │   │   │   │   ├── help-tooltip-indicator.tsx
│   │   │   │   │   ├── hint.tsx
│   │   │   │   │   ├── hover-card.tsx
│   │   │   │   │   ├── inline-toast.tsx
│   │   │   │   │   ├── input-group.tsx
│   │   │   │   │   ├── input.tsx
│   │   │   │   │   ├── kbd.tsx
│   │   │   │   │   ├── label.tsx
│   │   │   │   │   ├── loading-indicator.tsx
│   │   │   │   │   ├── locale-select.tsx
│   │   │   │   │   ├── multi-select.tsx
│   │   │   │   │   ├── pagination.tsx
│   │   │   │   │   ├── panel.tsx
│   │   │   │   │   ├── permission-button.tsx
│   │   │   │   │   ├── permission-switch.tsx
│   │   │   │   │   ├── phone-input.tsx
│   │   │   │   │   ├── popover.tsx
│   │   │   │   │   ├── progress.tsx
│   │   │   │   │   ├── radio-group.tsx
│   │   │   │   │   ├── resizable.tsx
│   │   │   │   │   ├── scroll-area.tsx
│   │   │   │   │   ├── secret-input.tsx
│   │   │   │   │   ├── segmented-control.tsx
│   │   │   │   │   ├── select.tsx
│   │   │   │   │   ├── separator.tsx
│   │   │   │   │   ├── sheet.tsx
│   │   │   │   │   ├── skeleton.tsx
│   │   │   │   │   ├── sonner-helpers.tsx
│   │   │   │   │   ├── sonner.tsx
│   │   │   │   │   ├── status-badge.tsx
│   │   │   │   │   ├── step.tsx
│   │   │   │   │   ├── switch.tsx
│   │   │   │   │   ├── table-pagination-footer.tsx
│   │   │   │   │   ├── table.tsx
│   │   │   │   │   ├── tabs.tsx
│   │   │   │   │   ├── tag-input.tsx
│   │   │   │   │   ├── tag.tsx
│   │   │   │   │   ├── text-separator.tsx
│   │   │   │   │   ├── textarea.tsx
│   │   │   │   │   ├── timeline.tsx
│   │   │   │   │   ├── toggle-group.tsx
│   │   │   │   │   ├── toggle.tsx
│   │   │   │   │   ├── tooltip.tsx
│   │   │   │   │   ├── translation-plugin/
│   │   │   │   │   │   ├── autocomplete.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── pill-widget.ts
│   │   │   │   │   │   ├── plugin-view.ts
│   │   │   │   │   │   └── utils.ts
│   │   │   │   │   ├── variable-editor.tsx
│   │   │   │   │   ├── variable-plugin/
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── plugin-view.ts
│   │   │   │   │   │   ├── types.ts
│   │   │   │   │   │   ├── utils.ts
│   │   │   │   │   │   ├── variable-pill-widget.ts
│   │   │   │   │   │   └── variable-theme.ts
│   │   │   │   │   └── visually-hidden.tsx
│   │   │   │   ├── promotional/
│   │   │   │   │   └── coming-soon-banner.tsx
│   │   │   │   ├── protected-drawer.tsx
│   │   │   │   ├── regenerate-api-keys-dialog.tsx
│   │   │   │   ├── schema-editor/
│   │   │   │   │   ├── components/
│   │   │   │   │   │   ├── array-section.tsx
│   │   │   │   │   │   ├── enum-section.tsx
│   │   │   │   │   │   ├── object-section.tsx
│   │   │   │   │   │   ├── property-actions.tsx
│   │   │   │   │   │   ├── property-name-input.tsx
│   │   │   │   │   │   └── property-type-selector.tsx
│   │   │   │   │   ├── constants.ts
│   │   │   │   │   ├── hooks/
│   │   │   │   │   │   ├── use-property-paths.ts
│   │   │   │   │   │   └── use-schema-property-type.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── json-schema.ts
│   │   │   │   │   ├── schema-editor.tsx
│   │   │   │   │   ├── schema-property-row.tsx
│   │   │   │   │   ├── schema-property-settings-popover.tsx
│   │   │   │   │   ├── types/
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   └── schema-form.types.ts
│   │   │   │   │   ├── use-schema-form.ts
│   │   │   │   │   └── utils/
│   │   │   │   │       ├── check-variable-usage.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       ├── json-helpers.ts
│   │   │   │   │       ├── property-manager.ts
│   │   │   │   │       ├── schema-change-detection.ts
│   │   │   │   │       ├── schema-converter.ts
│   │   │   │   │       ├── ui-helpers.ts
│   │   │   │   │       └── validation-schema.ts
│   │   │   │   ├── settings/
│   │   │   │   │   ├── novu-branding-switch.tsx
│   │   │   │   │   └── organization-settings.tsx
│   │   │   │   ├── shared/
│   │   │   │   │   └── external-link.tsx
│   │   │   │   ├── side-navigation/
│   │   │   │   │   ├── changelog-cards.tsx
│   │   │   │   │   ├── environment-dropdown.tsx
│   │   │   │   │   ├── free-trial-card.tsx
│   │   │   │   │   ├── getting-started-menu-item.tsx
│   │   │   │   │   ├── mobile-side-navigation.tsx
│   │   │   │   │   ├── navigation-link.tsx
│   │   │   │   │   ├── organization-dropdown-clerk.tsx
│   │   │   │   │   ├── organization-dropdown.tsx
│   │   │   │   │   ├── side-navigation.tsx
│   │   │   │   │   ├── sidebar.tsx
│   │   │   │   │   └── usage-card.tsx
│   │   │   │   ├── step-preview-hover-card.tsx
│   │   │   │   ├── subscribers/
│   │   │   │   │   ├── create-subscriber-form.tsx
│   │   │   │   │   ├── hooks/
│   │   │   │   │   │   ├── use-delete-subscription.ts
│   │   │   │   │   │   ├── use-get-subscription.ts
│   │   │   │   │   │   ├── use-subscriber-search.ts
│   │   │   │   │   │   ├── use-subscribers-navigate.ts
│   │   │   │   │   │   └── use-subscribers-url-state.ts
│   │   │   │   │   ├── preferences/
│   │   │   │   │   │   ├── day-schedule-copy.tsx
│   │   │   │   │   │   ├── preferences-blank.tsx
│   │   │   │   │   │   ├── preferences-item.tsx
│   │   │   │   │   │   ├── preferences-skeleton.tsx
│   │   │   │   │   │   ├── preferences.tsx
│   │   │   │   │   │   ├── schedule-table.tsx
│   │   │   │   │   │   ├── subscribers-schedule.tsx
│   │   │   │   │   │   ├── utils.ts
│   │   │   │   │   │   └── workflow-preferences.tsx
│   │   │   │   │   ├── schema.ts
│   │   │   │   │   ├── subscriber-activity-drawer.tsx
│   │   │   │   │   ├── subscriber-activity-list.tsx
│   │   │   │   │   ├── subscriber-activity.tsx
│   │   │   │   │   ├── subscriber-autocomplete.tsx
│   │   │   │   │   ├── subscriber-drawer.tsx
│   │   │   │   │   ├── subscriber-list-blank.tsx
│   │   │   │   │   ├── subscriber-list.tsx
│   │   │   │   │   ├── subscriber-overview-form.tsx
│   │   │   │   │   ├── subscriber-overview-skeleton.tsx
│   │   │   │   │   ├── subscriber-row.tsx
│   │   │   │   │   ├── subscriber-tabs.tsx
│   │   │   │   │   ├── subscribers-filters.tsx
│   │   │   │   │   ├── subscriptions/
│   │   │   │   │   │   ├── subscriber-subscriptions.tsx
│   │   │   │   │   │   ├── subscription-item.tsx
│   │   │   │   │   │   ├── subscription-preference-rule.tsx
│   │   │   │   │   │   ├── subscription-preferences-drawer.tsx
│   │   │   │   │   │   ├── subscription-preferences.tsx
│   │   │   │   │   │   └── subscriptions-empty-state.tsx
│   │   │   │   │   ├── timezone-select.tsx
│   │   │   │   │   └── utils.ts
│   │   │   │   ├── success-button-toast.tsx
│   │   │   │   ├── template-store/
│   │   │   │   │   ├── components/
│   │   │   │   │   │   └── workflow-results.tsx
│   │   │   │   │   ├── featured.ts
│   │   │   │   │   ├── types.ts
│   │   │   │   │   ├── workflow-card.tsx
│   │   │   │   │   ├── workflow-sidebar.tsx
│   │   │   │   │   └── workflow-template-modal.tsx
│   │   │   │   ├── time-display-hover-card.tsx
│   │   │   │   ├── topics/
│   │   │   │   │   ├── add-subscriber-form.tsx
│   │   │   │   │   ├── create-topic-drawer.tsx
│   │   │   │   │   ├── create-topic-form.tsx
│   │   │   │   │   ├── empty-topics-illustration.tsx
│   │   │   │   │   ├── hooks/
│   │   │   │   │   │   ├── use-delete-topic.ts
│   │   │   │   │   │   ├── use-topic-subscribers.ts
│   │   │   │   │   │   ├── use-topic.ts
│   │   │   │   │   │   ├── use-topics-navigate.ts
│   │   │   │   │   │   └── use-topics-url-state.ts
│   │   │   │   │   ├── subscription-count-badge.tsx
│   │   │   │   │   ├── topic-activity.tsx
│   │   │   │   │   ├── topic-drawer.tsx
│   │   │   │   │   ├── topic-list-blank.tsx
│   │   │   │   │   ├── topic-list.tsx
│   │   │   │   │   ├── topic-overview-form.tsx
│   │   │   │   │   ├── topic-row.tsx
│   │   │   │   │   ├── topic-subscriber-filter.tsx
│   │   │   │   │   ├── topic-subscriber-item.tsx
│   │   │   │   │   ├── topics-filters.tsx
│   │   │   │   │   └── types.ts
│   │   │   │   ├── translations/
│   │   │   │   │   ├── constants.ts
│   │   │   │   │   ├── delete-translation-modal.tsx
│   │   │   │   │   ├── empty-translations-illustration.tsx
│   │   │   │   │   ├── hooks/
│   │   │   │   │   │   ├── use-delete-translation-modal.ts
│   │   │   │   │   │   ├── use-translation-list-logic.ts
│   │   │   │   │   │   └── use-translations-url-state.tsx
│   │   │   │   │   ├── translation-drawer/
│   │   │   │   │   │   ├── editor-actions.tsx
│   │   │   │   │   │   ├── editor-panel.tsx
│   │   │   │   │   │   ├── hooks/
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   ├── use-translation-editor.ts
│   │   │   │   │   │   │   └── use-translation-file-operations.ts
│   │   │   │   │   │   ├── locale-list.tsx
│   │   │   │   │   │   ├── translation-drawer-content.tsx
│   │   │   │   │   │   ├── translation-drawer.tsx
│   │   │   │   │   │   ├── translation-header.tsx
│   │   │   │   │   │   └── use-translation-drawer-logic.tsx
│   │   │   │   │   ├── translation-import-trigger.tsx
│   │   │   │   │   ├── translation-list-upgrade-cta.tsx
│   │   │   │   │   ├── translation-list.tsx
│   │   │   │   │   ├── translation-onboarding-page.tsx
│   │   │   │   │   ├── translation-row.tsx
│   │   │   │   │   ├── translation-settings-drawer.tsx
│   │   │   │   │   ├── translation-status.tsx
│   │   │   │   │   ├── translation-switch.tsx
│   │   │   │   │   ├── translations-filters.tsx
│   │   │   │   │   └── utils.ts
│   │   │   │   ├── truncated-text.tsx
│   │   │   │   ├── unsaved-changes-alert-dialog.tsx
│   │   │   │   ├── updated-ago.tsx
│   │   │   │   ├── upgrade-cta-tooltip.tsx
│   │   │   │   ├── usecase-playground-header.tsx
│   │   │   │   ├── user-profile.tsx
│   │   │   │   ├── variable/
│   │   │   │   │   ├── components/
│   │   │   │   │   │   ├── digest-count-summary-preview.tsx
│   │   │   │   │   │   ├── digest-sentence-summary-preview.tsx
│   │   │   │   │   │   ├── filter-item.tsx
│   │   │   │   │   │   ├── new-variable-preview.tsx
│   │   │   │   │   │   ├── reorder-filter-item.tsx
│   │   │   │   │   │   ├── reorder-filters-group.tsx
│   │   │   │   │   │   ├── variable-icon.tsx
│   │   │   │   │   │   └── variable-preview.tsx
│   │   │   │   │   ├── constants.ts
│   │   │   │   │   ├── edit-variable-popover.tsx
│   │   │   │   │   ├── hooks/
│   │   │   │   │   │   ├── use-create-variable.tsx
│   │   │   │   │   │   ├── use-filter-manager.ts
│   │   │   │   │   │   ├── use-suggested-filters.ts
│   │   │   │   │   │   ├── use-variable-parser.ts
│   │   │   │   │   │   └── use-variable-validation.ts
│   │   │   │   │   ├── types.ts
│   │   │   │   │   ├── utils/
│   │   │   │   │   │   ├── digest-variables.tsx
│   │   │   │   │   │   └── get-variable-error-message.ts
│   │   │   │   │   ├── utils.ts
│   │   │   │   │   ├── variable-list.tsx
│   │   │   │   │   ├── variable-pill.tsx
│   │   │   │   │   └── variable-tooltip.tsx
│   │   │   │   ├── variables/
│   │   │   │   │   ├── delete-variable-dialog.tsx
│   │   │   │   │   ├── system-variable-definitions.ts
│   │   │   │   │   ├── system-variable-row.tsx
│   │   │   │   │   ├── upsert-variable-drawer.tsx
│   │   │   │   │   ├── upsert-variable-form.tsx
│   │   │   │   │   ├── variable-list-upgrade-cta.tsx
│   │   │   │   │   ├── variable-list.tsx
│   │   │   │   │   └── variable-row.tsx
│   │   │   │   ├── vercel-integration-form.tsx
│   │   │   │   ├── webhooks/
│   │   │   │   │   ├── webhooks-empty-state-svg.tsx
│   │   │   │   │   └── webhooks-paywall-state.tsx
│   │   │   │   ├── welcome/
│   │   │   │   │   ├── ai-prompts/
│   │   │   │   │   │   ├── framework-prompts/
│   │   │   │   │   │   │   ├── angular-prompt.ts
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   ├── javascript-prompt.ts
│   │   │   │   │   │   │   ├── nextjs-prompt.ts
│   │   │   │   │   │   │   ├── react-native-prompt.ts
│   │   │   │   │   │   │   ├── react-prompt.ts
│   │   │   │   │   │   │   ├── remix-prompt.ts
│   │   │   │   │   │   │   ├── types.ts
│   │   │   │   │   │   │   └── vue-prompt.ts
│   │   │   │   │   │   └── simple-prompt-getter.ts
│   │   │   │   │   ├── framework-guides.instructions.tsx
│   │   │   │   │   ├── framework-guides.tsx
│   │   │   │   │   ├── icons.tsx
│   │   │   │   │   ├── inbox-connected-guide.tsx
│   │   │   │   │   ├── inbox-embed.tsx
│   │   │   │   │   ├── inbox-framework-guide/
│   │   │   │   │   │   ├── framework-card.tsx
│   │   │   │   │   │   ├── framework-grid.tsx
│   │   │   │   │   │   ├── header-section.tsx
│   │   │   │   │   │   ├── helpers.ts
│   │   │   │   │   │   ├── instructions-panel.tsx
│   │   │   │   │   │   └── types.ts
│   │   │   │   │   ├── inbox-framework-guide.tsx
│   │   │   │   │   ├── progress-section.animations.ts
│   │   │   │   │   ├── progress-section.tsx
│   │   │   │   │   └── resources-list.tsx
│   │   │   │   ├── workflow-editor/
│   │   │   │   │   ├── add-step-menu.tsx
│   │   │   │   │   ├── animation-step-wrapper.tsx
│   │   │   │   │   ├── base-node.tsx
│   │   │   │   │   ├── channel-preferences-form.tsx
│   │   │   │   │   ├── channel-preferences.tsx
│   │   │   │   │   ├── condition-badge.tsx
│   │   │   │   │   ├── configure-workflow-form.tsx
│   │   │   │   │   ├── configure-workflow.tsx
│   │   │   │   │   ├── control-input/
│   │   │   │   │   │   ├── control-input.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── create-workflow-form.tsx
│   │   │   │   │   ├── drag-context.tsx
│   │   │   │   │   ├── edges.tsx
│   │   │   │   │   ├── editor-breadcrumbs.tsx
│   │   │   │   │   ├── in-app-preview.tsx
│   │   │   │   │   ├── node-utils.ts
│   │   │   │   │   ├── nodes.tsx
│   │   │   │   │   ├── payload-schema/
│   │   │   │   │   │   ├── components/
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   ├── payload-import-editor.tsx
│   │   │   │   │   │   │   └── payload-schema-empty-state.tsx
│   │   │   │   │   │   ├── hooks/
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   └── use-import-schema.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   └── utils/
│   │   │   │   │   │       ├── generate-schema.ts
│   │   │   │   │   │       └── index.ts
│   │   │   │   │   ├── payload-schema-drawer.tsx
│   │   │   │   │   ├── saving-status-indicator.tsx
│   │   │   │   │   ├── schema-change-confirmation-modal.tsx
│   │   │   │   │   ├── schema.ts
│   │   │   │   │   ├── severity-select-item.tsx
│   │   │   │   │   ├── step-utils.ts
│   │   │   │   │   ├── steps/
│   │   │   │   │   │   ├── base/
│   │   │   │   │   │   │   ├── base-body.tsx
│   │   │   │   │   │   │   ├── base-subject.tsx
│   │   │   │   │   │   │   └── data-object.tsx
│   │   │   │   │   │   ├── chat/
│   │   │   │   │   │   │   ├── chat-editor.tsx
│   │   │   │   │   │   │   ├── chat-preview.tsx
│   │   │   │   │   │   │   └── configure-chat-step-preview.tsx
│   │   │   │   │   │   ├── component-utils.tsx
│   │   │   │   │   │   ├── components/
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   ├── preview-payload-section.tsx
│   │   │   │   │   │   │   └── preview-step-results-section.tsx
│   │   │   │   │   │   ├── conditions/
│   │   │   │   │   │   │   ├── edit-step-conditions-form.tsx
│   │   │   │   │   │   │   ├── edit-step-conditions-layout.tsx
│   │   │   │   │   │   │   ├── edit-step-conditions-skeleton.tsx
│   │   │   │   │   │   │   └── edit-step-conditions.tsx
│   │   │   │   │   │   ├── configure-step-form.tsx
│   │   │   │   │   │   ├── configure-step-template-issue-cta.tsx
│   │   │   │   │   │   ├── configure-step.tsx
│   │   │   │   │   │   ├── constants/
│   │   │   │   │   │   │   └── preview-context.constants.ts
│   │   │   │   │   │   ├── context/
│   │   │   │   │   │   │   ├── preview-context-container.tsx
│   │   │   │   │   │   │   └── step-editor-context.tsx
│   │   │   │   │   │   ├── controls/
│   │   │   │   │   │   │   ├── array-field-item-template.tsx
│   │   │   │   │   │   │   ├── array-field-template.tsx
│   │   │   │   │   │   │   ├── array-field-title-template.tsx
│   │   │   │   │   │   │   ├── button-templates.tsx
│   │   │   │   │   │   │   ├── custom-step-controls.tsx
│   │   │   │   │   │   │   ├── json-form.tsx
│   │   │   │   │   │   │   ├── object-field-template.tsx
│   │   │   │   │   │   │   ├── select-widget.tsx
│   │   │   │   │   │   │   ├── switch-widget.tsx
│   │   │   │   │   │   │   ├── template-utils.tsx
│   │   │   │   │   │   │   └── text-widget.tsx
│   │   │   │   │   │   ├── delay/
│   │   │   │   │   │   │   ├── delay-control-values.tsx
│   │   │   │   │   │   │   ├── delay-window.tsx
│   │   │   │   │   │   │   ├── dynamic-delay.tsx
│   │   │   │   │   │   │   └── fixed-delay.tsx
│   │   │   │   │   │   ├── digest-delay-tabs/
│   │   │   │   │   │   │   ├── days-of-week.tsx
│   │   │   │   │   │   │   ├── digest-control-values.tsx
│   │   │   │   │   │   │   ├── digest-delay-tabs.tsx
│   │   │   │   │   │   │   ├── digest-key.tsx
│   │   │   │   │   │   │   ├── keys.ts
│   │   │   │   │   │   │   ├── lookback-window.tsx
│   │   │   │   │   │   │   ├── numbers-picker.tsx
│   │   │   │   │   │   │   ├── period.tsx
│   │   │   │   │   │   │   ├── regular-type.tsx
│   │   │   │   │   │   │   ├── scheduled-type.tsx
│   │   │   │   │   │   │   └── utils.ts
│   │   │   │   │   │   ├── editor/
│   │   │   │   │   │   │   └── step-editor-factory.tsx
│   │   │   │   │   │   ├── email/
│   │   │   │   │   │   │   ├── configure-email-step-preview.tsx
│   │   │   │   │   │   │   ├── email-body-html.tsx
│   │   │   │   │   │   │   ├── email-body-maily.tsx
│   │   │   │   │   │   │   ├── email-body.tsx
│   │   │   │   │   │   │   ├── email-editor.tsx
│   │   │   │   │   │   │   ├── email-preview.tsx
│   │   │   │   │   │   │   ├── email-subject.tsx
│   │   │   │   │   │   │   ├── email-tabs-section.tsx
│   │   │   │   │   │   │   ├── layout-select.tsx
│   │   │   │   │   │   │   ├── novu-branding.tsx
│   │   │   │   │   │   │   ├── sender-config-drawer.tsx
│   │   │   │   │   │   │   └── translations/
│   │   │   │   │   │   │       ├── edit-translation-popover/
│   │   │   │   │   │   │       │   ├── edit-translation-popover.tsx
│   │   │   │   │   │   │       │   ├── use-translation-editor.ts
│   │   │   │   │   │   │       │   ├── use-translation-form.ts
│   │   │   │   │   │   │       │   └── use-virtual-anchor.ts
│   │   │   │   │   │   │       ├── index.ts
│   │   │   │   │   │   │       ├── new-translation-key-preview.tsx
│   │   │   │   │   │   │       ├── translation-decorator.tsx
│   │   │   │   │   │   │       ├── translation-pill.tsx
│   │   │   │   │   │   │       ├── translation-suggestions-list-view.tsx
│   │   │   │   │   │   │       └── translation-tooltip.tsx
│   │   │   │   │   │   ├── hooks/
│   │   │   │   │   │   │   ├── use-persisted-preview-context.ts
│   │   │   │   │   │   │   └── use-preview-data-initialization.ts
│   │   │   │   │   │   ├── http-request/
│   │   │   │   │   │   │   ├── configure-http-request-step-preview.tsx
│   │   │   │   │   │   │   ├── continue-on-failure.tsx
│   │   │   │   │   │   │   ├── curl-display.tsx
│   │   │   │   │   │   │   ├── curl-utils.ts
│   │   │   │   │   │   │   ├── enforce-schema-validation.tsx
│   │   │   │   │   │   │   ├── http-request-console-preview.tsx
│   │   │   │   │   │   │   ├── http-request-editor.tsx
│   │   │   │   │   │   │   ├── http-request-test-context.ts
│   │   │   │   │   │   │   ├── http-request-test-provider.tsx
│   │   │   │   │   │   │   ├── key-value-pair-list.tsx
│   │   │   │   │   │   │   ├── novu-signature-header.tsx
│   │   │   │   │   │   │   ├── request-endpoint.tsx
│   │   │   │   │   │   │   ├── response-body-schema.tsx
│   │   │   │   │   │   │   ├── section-header.tsx
│   │   │   │   │   │   │   ├── use-copy-prompt.tsx
│   │   │   │   │   │   │   └── use-http-request-test.ts
│   │   │   │   │   │   ├── in-app/
│   │   │   │   │   │   │   ├── configure-in-app-step-preview.tsx
│   │   │   │   │   │   │   ├── in-app-action.tsx
│   │   │   │   │   │   │   ├── in-app-avatar.tsx
│   │   │   │   │   │   │   ├── in-app-body.tsx
│   │   │   │   │   │   │   ├── in-app-editor.tsx
│   │   │   │   │   │   │   ├── in-app-redirect.tsx
│   │   │   │   │   │   │   ├── in-app-subject.tsx
│   │   │   │   │   │   │   ├── in-app-tabs-section.tsx
│   │   │   │   │   │   │   └── inbox-preview.tsx
│   │   │   │   │   │   ├── layout/
│   │   │   │   │   │   │   ├── copilot-sidebar.tsx
│   │   │   │   │   │   │   ├── panel-header.tsx
│   │   │   │   │   │   │   └── resizable-layout.tsx
│   │   │   │   │   │   ├── preview/
│   │   │   │   │   │   │   ├── previews/
│   │   │   │   │   │   │   │   └── email-preview-wrapper.tsx
│   │   │   │   │   │   │   ├── step-preview-factory.tsx
│   │   │   │   │   │   │   └── step-resolver-preview-error.tsx
│   │   │   │   │   │   ├── preview-context-panel.tsx
│   │   │   │   │   │   ├── push/
│   │   │   │   │   │   │   ├── configure-push-step-preview.tsx
│   │   │   │   │   │   │   ├── push-editor.tsx
│   │   │   │   │   │   │   └── push-preview.tsx
│   │   │   │   │   │   ├── save-form-context.ts
│   │   │   │   │   │   ├── sdk-banner.tsx
│   │   │   │   │   │   ├── shared/
│   │   │   │   │   │   │   ├── bypass-sanitization-switch.tsx
│   │   │   │   │   │   │   ├── editable-json-viewer/
│   │   │   │   │   │   │   │   ├── constants.ts
│   │   │   │   │   │   │   │   ├── custom-text-editor.tsx
│   │   │   │   │   │   │   │   ├── editable-json-viewer.tsx
│   │   │   │   │   │   │   │   ├── icons.tsx
│   │   │   │   │   │   │   │   ├── single-click-editable-value.tsx
│   │   │   │   │   │   │   │   ├── types.ts
│   │   │   │   │   │   │   │   └── use-hide-root-node.ts
│   │   │   │   │   │   │   ├── extend-to-schedule.tsx
│   │   │   │   │   │   │   ├── step-editor-mode-toggle.tsx
│   │   │   │   │   │   │   ├── step-resolver-active-panel.tsx
│   │   │   │   │   │   │   ├── step-resolver-empty-preview.tsx
│   │   │   │   │   │   │   ├── step-resolver-not-published.tsx
│   │   │   │   │   │   │   └── use-step-resolver-hint.tsx
│   │   │   │   │   │   ├── skip-conditions-button.tsx
│   │   │   │   │   │   ├── sms/
│   │   │   │   │   │   │   ├── configure-sms-step-preview.tsx
│   │   │   │   │   │   │   ├── sms-editor.tsx
│   │   │   │   │   │   │   ├── sms-phone.tsx
│   │   │   │   │   │   │   └── sms-preview.tsx
│   │   │   │   │   │   ├── step-drawer.tsx
│   │   │   │   │   │   ├── step-editor-layout.tsx
│   │   │   │   │   │   ├── step-editor-unavailable.tsx
│   │   │   │   │   │   ├── tabs-section.tsx
│   │   │   │   │   │   ├── throttle/
│   │   │   │   │   │   │   ├── dynamic-throttle.tsx
│   │   │   │   │   │   │   ├── fixed-throttle.tsx
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   ├── throttle-control-values.tsx
│   │   │   │   │   │   │   ├── throttle-editor.tsx
│   │   │   │   │   │   │   ├── throttle-key.tsx
│   │   │   │   │   │   │   ├── throttle-threshold.tsx
│   │   │   │   │   │   │   └── throttle-window.tsx
│   │   │   │   │   │   ├── time-units.ts
│   │   │   │   │   │   ├── types/
│   │   │   │   │   │   │   └── preview-context.types.ts
│   │   │   │   │   │   ├── use-editor-preview.tsx
│   │   │   │   │   │   └── utils/
│   │   │   │   │   │       ├── digest-sync.utils.ts
│   │   │   │   │   │       ├── preview-context-storage.utils.ts
│   │   │   │   │   │       ├── preview-context.utils.ts
│   │   │   │   │   │       └── step-utils.tsx
│   │   │   │   │   ├── test-workflow/
│   │   │   │   │   │   ├── snippet-editor.tsx
│   │   │   │   │   │   ├── test-workflow-activity-drawer.tsx
│   │   │   │   │   │   ├── test-workflow-content.tsx
│   │   │   │   │   │   ├── test-workflow-drawer.tsx
│   │   │   │   │   │   ├── test-workflow-form.tsx
│   │   │   │   │   │   ├── test-workflow-instructions.tsx
│   │   │   │   │   │   ├── test-workflow-logs-sidebar.tsx
│   │   │   │   │   │   ├── test-workflow-tabs.tsx
│   │   │   │   │   │   └── types.ts
│   │   │   │   │   ├── toasts.tsx
│   │   │   │   │   ├── translation-status.tsx
│   │   │   │   │   ├── translation-toggle-section.tsx
│   │   │   │   │   ├── url-input.tsx
│   │   │   │   │   ├── use-animated-nodes.ts
│   │   │   │   │   ├── use-canvas-nodes-edges.ts
│   │   │   │   │   ├── use-workflow-schema-manager.ts
│   │   │   │   │   ├── workflow-activity.tsx
│   │   │   │   │   ├── workflow-canvas.tsx
│   │   │   │   │   ├── workflow-checklist.tsx
│   │   │   │   │   ├── workflow-node-action-bar.tsx
│   │   │   │   │   ├── workflow-provider.tsx
│   │   │   │   │   ├── workflow-schema-provider.tsx
│   │   │   │   │   └── workflow-tabs.tsx
│   │   │   │   ├── workflow-issues-popover.tsx
│   │   │   │   ├── workflow-list-empty.tsx
│   │   │   │   ├── workflow-list.tsx
│   │   │   │   ├── workflow-row.tsx
│   │   │   │   ├── workflow-status.tsx
│   │   │   │   ├── workflow-step.tsx
│   │   │   │   ├── workflow-steps.tsx
│   │   │   │   └── workflow-tags.tsx
│   │   │   ├── config/
│   │   │   │   └── index.ts
│   │   │   ├── context/
│   │   │   │   ├── auth/
│   │   │   │   │   ├── auth-context.tsx
│   │   │   │   │   ├── auth-provider.tsx
│   │   │   │   │   ├── hooks.ts
│   │   │   │   │   ├── mappers.ts
│   │   │   │   │   └── types.ts
│   │   │   │   ├── customer-io/
│   │   │   │   │   ├── customer-io-provider.tsx
│   │   │   │   │   ├── hooks.ts
│   │   │   │   │   └── index.ts
│   │   │   │   ├── ee-auth-provider.tsx
│   │   │   │   ├── environment/
│   │   │   │   │   ├── environment-context.tsx
│   │   │   │   │   ├── environment-provider.tsx
│   │   │   │   │   └── hooks.ts
│   │   │   │   ├── escape-key-manager/
│   │   │   │   │   ├── escape-key-context.tsx
│   │   │   │   │   ├── escape-key-manager.tsx
│   │   │   │   │   ├── hooks.ts
│   │   │   │   │   └── priority.ts
│   │   │   │   ├── feature-flags-provider.tsx
│   │   │   │   ├── identity-provider.tsx
│   │   │   │   ├── opt-in-provider.tsx
│   │   │   │   ├── region/
│   │   │   │   │   ├── index.self-hosted.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── region-config.ts
│   │   │   │   │   ├── region-context.self-hosted.tsx
│   │   │   │   │   ├── region-context.tsx
│   │   │   │   │   ├── region-modals.tsx
│   │   │   │   │   ├── region-selector.tsx
│   │   │   │   │   ├── region-types.ts
│   │   │   │   │   └── region-utils.ts
│   │   │   │   └── segment/
│   │   │   │       ├── hooks.ts
│   │   │   │       ├── index.ts
│   │   │   │       └── segment-provider.tsx
│   │   │   ├── hooks/
│   │   │   │   ├── use-activity-url-state.ts
│   │   │   │   ├── use-ai-chat-stream.ts
│   │   │   │   ├── use-auto-configure-integration.ts
│   │   │   │   ├── use-before-unload.ts
│   │   │   │   ├── use-billing-portal.ts
│   │   │   │   ├── use-checkout-session.ts
│   │   │   │   ├── use-combined-refs.ts
│   │   │   │   ├── use-conditions-count.ts
│   │   │   │   ├── use-create-ai-chat.ts
│   │   │   │   ├── use-create-context.ts
│   │   │   │   ├── use-create-environment-variable.ts
│   │   │   │   ├── use-create-integration.ts
│   │   │   │   ├── use-create-layout.ts
│   │   │   │   ├── use-create-subscriber.ts
│   │   │   │   ├── use-create-topic.ts
│   │   │   │   ├── use-create-translation-key.ts
│   │   │   │   ├── use-create-vercel-integration.ts
│   │   │   │   ├── use-create-workflow.ts
│   │   │   │   ├── use-data-ref.ts
│   │   │   │   ├── use-debounce.ts
│   │   │   │   ├── use-debounced-form.ts
│   │   │   │   ├── use-debounced-value.ts
│   │   │   │   ├── use-default-subscriber-data.ts
│   │   │   │   ├── use-delayed-loading.ts
│   │   │   │   ├── use-delete-context.ts
│   │   │   │   ├── use-delete-environment-variable.ts
│   │   │   │   ├── use-delete-integration.ts
│   │   │   │   ├── use-delete-layout.ts
│   │   │   │   ├── use-delete-subscriber.ts
│   │   │   │   ├── use-delete-translation-group.ts
│   │   │   │   ├── use-delete-workflow.ts
│   │   │   │   ├── use-disconnect-step-resolver.ts
│   │   │   │   ├── use-duplicate-layout.ts
│   │   │   │   ├── use-duplicate-workflow.ts
│   │   │   │   ├── use-dynamic-preview-schema.ts
│   │   │   │   ├── use-editor-translation-overlay.ts
│   │   │   │   ├── use-enhanced-variable-validation.ts
│   │   │   │   ├── use-environments.ts
│   │   │   │   ├── use-export-master-json.ts
│   │   │   │   ├── use-feature-flag.tsx
│   │   │   │   ├── use-fetch-activities.ts
│   │   │   │   ├── use-fetch-activity.ts
│   │   │   │   ├── use-fetch-api-keys.ts
│   │   │   │   ├── use-fetch-bridge-health-check.ts
│   │   │   │   ├── use-fetch-charts.ts
│   │   │   │   ├── use-fetch-context.ts
│   │   │   │   ├── use-fetch-contexts.ts
│   │   │   │   ├── use-fetch-environment-variable-usage.ts
│   │   │   │   ├── use-fetch-environment-variables.ts
│   │   │   │   ├── use-fetch-integrations.ts
│   │   │   │   ├── use-fetch-latest-ai-chat.ts
│   │   │   │   ├── use-fetch-layout-usage.ts
│   │   │   │   ├── use-fetch-layout.ts
│   │   │   │   ├── use-fetch-layouts.tsx
│   │   │   │   ├── use-fetch-organization-settings.ts
│   │   │   │   ├── use-fetch-request-logs.ts
│   │   │   │   ├── use-fetch-request-traces.ts
│   │   │   │   ├── use-fetch-subscriber-preferences.ts
│   │   │   │   ├── use-fetch-subscriber-subscriptions.ts
│   │   │   │   ├── use-fetch-subscriber.ts
│   │   │   │   ├── use-fetch-subscribers.ts
│   │   │   │   ├── use-fetch-subscription.ts
│   │   │   │   ├── use-fetch-topics.ts
│   │   │   │   ├── use-fetch-translation-group.ts
│   │   │   │   ├── use-fetch-translation-keys.ts
│   │   │   │   ├── use-fetch-translation-list.ts
│   │   │   │   ├── use-fetch-translation.ts
│   │   │   │   ├── use-fetch-vercel-integration-projects.tsx
│   │   │   │   ├── use-fetch-vercel-integration.ts
│   │   │   │   ├── use-fetch-workflow-runs-count.ts
│   │   │   │   ├── use-fetch-workflow-test-data.ts
│   │   │   │   ├── use-fetch-workflow.ts
│   │   │   │   ├── use-fetch-workflows.ts
│   │   │   │   ├── use-find-dirty-form.ts
│   │   │   │   ├── use-first-trigger-detection.ts
│   │   │   │   ├── use-form-autosave.ts
│   │   │   │   ├── use-form-protection.tsx
│   │   │   │   ├── use-has-permission.tsx
│   │   │   │   ├── use-init-demo-workflow.ts
│   │   │   │   ├── use-invocation-queue.ts
│   │   │   │   ├── use-is-mobile.ts
│   │   │   │   ├── use-is-payload-schema-enabled.ts
│   │   │   │   ├── use-is-translation-enabled.ts
│   │   │   │   ├── use-keep-ai-changes.ts
│   │   │   │   ├── use-layout-preview.ts
│   │   │   │   ├── use-logs-url-state.ts
│   │   │   │   ├── use-metric-data.ts
│   │   │   │   ├── use-mutation-observer.ts
│   │   │   │   ├── use-new-dashboard-opt-in.ts
│   │   │   │   ├── use-on-element-unmount.ts
│   │   │   │   ├── use-onboarding-steps.ts
│   │   │   │   ├── use-optimistic-channel-preferences.ts
│   │   │   │   ├── use-optimistic-schedule-update.ts
│   │   │   │   ├── use-page-visit-timestamp.ts
│   │   │   │   ├── use-parse-variables.ts
│   │   │   │   ├── use-patch-subscriber.ts
│   │   │   │   ├── use-patch-workflow.ts
│   │   │   │   ├── use-persisted-page-size.ts
│   │   │   │   ├── use-plain-chat.ts
│   │   │   │   ├── use-preview-context.ts
│   │   │   │   ├── use-preview-step.ts
│   │   │   │   ├── use-primary-email-integration.ts
│   │   │   │   ├── use-pull-activity.ts
│   │   │   │   ├── use-remove-grammarly.ts
│   │   │   │   ├── use-resource-dependencies.ts
│   │   │   │   ├── use-revert-message.ts
│   │   │   │   ├── use-save-translation.ts
│   │   │   │   ├── use-set-primary-integration.ts
│   │   │   │   ├── use-step-resolver-polling.ts
│   │   │   │   ├── use-step-resolvers-count.ts
│   │   │   │   ├── use-sync-workflow.tsx
│   │   │   │   ├── use-tab-observer.ts
│   │   │   │   ├── use-tags.ts
│   │   │   │   ├── use-telemetry.ts
│   │   │   │   ├── use-template-store.ts
│   │   │   │   ├── use-test-http-endpoint.ts
│   │   │   │   ├── use-translation-completion-source.ts
│   │   │   │   ├── use-translation-plugin-extension.ts
│   │   │   │   ├── use-translation-validation.ts
│   │   │   │   ├── use-translations.ts
│   │   │   │   ├── use-trigger-workflow.ts
│   │   │   │   ├── use-update-bridge-url.ts
│   │   │   │   ├── use-update-context.ts
│   │   │   │   ├── use-update-environment-variable.ts
│   │   │   │   ├── use-update-integration.ts
│   │   │   │   ├── use-update-layout.ts
│   │   │   │   ├── use-update-organization-settings.ts
│   │   │   │   ├── use-update-translation-value.ts
│   │   │   │   ├── use-update-vercel-integration.ts
│   │   │   │   ├── use-update-workflow.ts
│   │   │   │   ├── use-upload-master-json.ts
│   │   │   │   ├── use-upload-translations.ts
│   │   │   │   ├── use-validate-bridge-url.ts
│   │   │   │   ├── use-variables.ts
│   │   │   │   ├── use-vercel-params.ts
│   │   │   │   └── use-workflow-editor-page.ts
│   │   │   ├── index.css
│   │   │   ├── main.tsx
│   │   │   ├── pages/
│   │   │   │   ├── access-denied-page.tsx
│   │   │   │   ├── activity-feed.tsx
│   │   │   │   ├── analytics.tsx
│   │   │   │   ├── api-keys.tsx
│   │   │   │   ├── contexts.tsx
│   │   │   │   ├── create-context.tsx
│   │   │   │   ├── create-layout.tsx
│   │   │   │   ├── create-subscriber.tsx
│   │   │   │   ├── create-topic.tsx
│   │   │   │   ├── create-workflow.tsx
│   │   │   │   ├── duplicate-layout-page.tsx
│   │   │   │   ├── duplicate-workflow.tsx
│   │   │   │   ├── edit-context.tsx
│   │   │   │   ├── edit-layout.tsx
│   │   │   │   ├── edit-step-template-v2.tsx
│   │   │   │   ├── edit-subscriber-page.tsx
│   │   │   │   ├── edit-topic.tsx
│   │   │   │   ├── edit-translation.tsx
│   │   │   │   ├── edit-workflow.tsx
│   │   │   │   ├── environments.tsx
│   │   │   │   ├── error-page.tsx
│   │   │   │   ├── forgot-password.tsx
│   │   │   │   ├── inbox-embed-page.tsx
│   │   │   │   ├── inbox-embed-success-page.tsx
│   │   │   │   ├── inbox-usecase-page.tsx
│   │   │   │   ├── index.ts
│   │   │   │   ├── integrations-list-page.tsx
│   │   │   │   ├── invitation-accept.tsx
│   │   │   │   ├── landing-1-signup.tsx
│   │   │   │   ├── layouts.tsx
│   │   │   │   ├── new-layout-drawer.tsx
│   │   │   │   ├── new-workflow-drawer.tsx
│   │   │   │   ├── organization-list.tsx
│   │   │   │   ├── questionnaire-page.tsx
│   │   │   │   ├── redirect-to-legacy-studio-auth.tsx
│   │   │   │   ├── reset-password.tsx
│   │   │   │   ├── server-error-page.tsx
│   │   │   │   ├── settings.tsx
│   │   │   │   ├── sign-in.tsx
│   │   │   │   ├── sign-up.tsx
│   │   │   │   ├── sso-sign-in.tsx
│   │   │   │   ├── subscribers.tsx
│   │   │   │   ├── test-workflow-drawer-page.tsx
│   │   │   │   ├── test-workflow-route-handler.tsx
│   │   │   │   ├── test-workflow.tsx
│   │   │   │   ├── topics.tsx
│   │   │   │   ├── translation-settings-page.tsx
│   │   │   │   ├── translations.tsx
│   │   │   │   ├── upsert-variable.tsx
│   │   │   │   ├── usecase-select-page.tsx
│   │   │   │   ├── variables.tsx
│   │   │   │   ├── vercel-integration-page.tsx
│   │   │   │   ├── verify-email.tsx
│   │   │   │   ├── webhooks-page.tsx
│   │   │   │   ├── welcome-page.tsx
│   │   │   │   └── workflows.tsx
│   │   │   ├── routes/
│   │   │   │   ├── auth.tsx
│   │   │   │   ├── catch-all.tsx
│   │   │   │   ├── dashboard.tsx
│   │   │   │   ├── index.ts
│   │   │   │   ├── onboarding.tsx
│   │   │   │   ├── permission-protected-route.tsx
│   │   │   │   ├── protected-route.tsx
│   │   │   │   └── root.tsx
│   │   │   ├── types/
│   │   │   │   ├── activity.ts
│   │   │   │   ├── global.ts
│   │   │   │   ├── logs.ts
│   │   │   │   └── translations.ts
│   │   │   ├── utils/
│   │   │   │   ├── activityFilters.ts
│   │   │   │   ├── analytics-mock-data.ts
│   │   │   │   ├── animation.ts
│   │   │   │   ├── api-hostname-manager.ts
│   │   │   │   ├── api-response-normalizer.ts
│   │   │   │   ├── arrays.ts
│   │   │   │   ├── auth.ts
│   │   │   │   ├── avatars.ts
│   │   │   │   ├── better-auth/
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── components/
│   │   │   │   │   │   ├── forgot-password.tsx
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── invitation-accept.tsx
│   │   │   │   │   │   ├── organization-create.tsx
│   │   │   │   │   │   ├── organization-dropdown.tsx
│   │   │   │   │   │   ├── organization-list.tsx
│   │   │   │   │   │   ├── organization-settings.tsx
│   │   │   │   │   │   ├── organization-switcher.tsx
│   │   │   │   │   │   ├── reset-password.tsx
│   │   │   │   │   │   ├── sign-in.tsx
│   │   │   │   │   │   ├── sign-up.tsx
│   │   │   │   │   │   ├── sso-sign-in.tsx
│   │   │   │   │   │   ├── team-members.tsx
│   │   │   │   │   │   ├── user-button.tsx
│   │   │   │   │   │   ├── user-profile.tsx
│   │   │   │   │   │   └── verify-email.tsx
│   │   │   │   │   ├── index.tsx
│   │   │   │   │   └── role-permissions.ts
│   │   │   │   ├── channels.ts
│   │   │   │   ├── clerk-appearance.ts
│   │   │   │   ├── code-snippets.ts
│   │   │   │   ├── color.ts
│   │   │   │   ├── conditions.ts
│   │   │   │   ├── constants.ts
│   │   │   │   ├── context-variable-utils.ts
│   │   │   │   ├── context.ts
│   │   │   │   ├── customer-io.ts
│   │   │   │   ├── default-values.ts
│   │   │   │   ├── enums.ts
│   │   │   │   ├── format-count.ts
│   │   │   │   ├── format-date.ts
│   │   │   │   ├── formatter.ts
│   │   │   │   ├── id-utils.ts
│   │   │   │   ├── inbox.ts
│   │   │   │   ├── json.ts
│   │   │   │   ├── liquid-autocomplete.tsx
│   │   │   │   ├── liquid-scope-analyzer.ts
│   │   │   │   ├── liquid.ts
│   │   │   │   ├── local-storage.ts
│   │   │   │   ├── logs-filters.utils.ts
│   │   │   │   ├── number-formatting.ts
│   │   │   │   ├── parse-page-param.ts
│   │   │   │   ├── parseStepVariables.ts
│   │   │   │   ├── polymorphic.ts
│   │   │   │   ├── protect.tsx
│   │   │   │   ├── query-keys.ts
│   │   │   │   ├── recursive-clone-children.tsx
│   │   │   │   ├── routes.ts
│   │   │   │   ├── schema.ts
│   │   │   │   ├── segment.ts
│   │   │   │   ├── self-hosted/
│   │   │   │   │   ├── api-interceptor.tsx
│   │   │   │   │   ├── auth.resource.tsx
│   │   │   │   │   ├── components.tsx
│   │   │   │   │   ├── icons.tsx
│   │   │   │   │   ├── index.tsx
│   │   │   │   │   ├── jwt-manager.tsx
│   │   │   │   │   ├── organization-switcher.tsx
│   │   │   │   │   ├── organization.resource.tsx
│   │   │   │   │   ├── user-button.tsx
│   │   │   │   │   ├── user.resource.tsx
│   │   │   │   │   └── user.types.ts
│   │   │   │   ├── sentry.ts
│   │   │   │   ├── string.ts
│   │   │   │   ├── telemetry.ts
│   │   │   │   ├── titleize.ts
│   │   │   │   ├── tracking.ts
│   │   │   │   ├── tv.ts
│   │   │   │   ├── types.ts
│   │   │   │   ├── ui.ts
│   │   │   │   ├── url.ts
│   │   │   │   ├── uuid/
│   │   │   │   │   └── index.ts
│   │   │   │   ├── validation.ts
│   │   │   │   └── workflow-trigger-ai-prompt.ts
│   │   │   └── vite-env.d.ts
│   │   ├── tailwind.config.ts
│   │   ├── tests/
│   │   │   ├── manage-workflows.e2e.ts
│   │   │   ├── package.json
│   │   │   ├── page-object-models/
│   │   │   │   ├── create-workflow-sidebar.ts
│   │   │   │   ├── in-app-step-editor.ts
│   │   │   │   ├── step-config-sidebar.ts
│   │   │   │   ├── trigger-workflow-page.ts
│   │   │   │   ├── workflow-editor-page.ts
│   │   │   │   └── workflows-page.ts
│   │   │   ├── sync-workflow.e2e.ts
│   │   │   ├── tsconfig.json
│   │   │   └── utils/
│   │   │       ├── api.ts
│   │   │       ├── environment-service.ts
│   │   │       ├── fixtures.ts
│   │   │       ├── integration-service.ts
│   │   │       ├── organization-service.ts
│   │   │       ├── session.ts
│   │   │       ├── test-bridge-server.ts
│   │   │       └── user-service.ts
│   │   ├── tests-examples/
│   │   │   └── demo-todo-app.spec.ts
│   │   ├── tsconfig.app.json
│   │   ├── tsconfig.json
│   │   ├── tsconfig.node.json
│   │   └── vite.config.ts
│   ├── inbound-mail/
│   │   ├── .example.env
│   │   ├── .gitignore
│   │   ├── Dockerfile
│   │   ├── e2e/
│   │   │   └── setup.ts
│   │   ├── nodemon.json
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── config/
│   │   │   │   ├── env.config.ts
│   │   │   │   ├── env.validators.ts
│   │   │   │   └── index.ts
│   │   │   ├── instrument.ts
│   │   │   ├── main.ts
│   │   │   ├── python/
│   │   │   │   ├── DNS/
│   │   │   │   │   ├── Base.py
│   │   │   │   │   ├── Class.py
│   │   │   │   │   ├── Lib.py
│   │   │   │   │   ├── Opcode.py
│   │   │   │   │   ├── Status.py
│   │   │   │   │   ├── Type.py
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── lazy.py
│   │   │   │   │   └── win32dns.py
│   │   │   │   ├── dkim/
│   │   │   │   │   ├── .__init__.py.swo
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── __main__.py
│   │   │   │   │   ├── asn1.py
│   │   │   │   │   ├── canonicalization.py
│   │   │   │   │   ├── crypto.py
│   │   │   │   │   ├── dnsplug.py
│   │   │   │   │   └── util.py
│   │   │   │   ├── ipaddr.py
│   │   │   │   ├── spf.py
│   │   │   │   ├── verifydkim.py
│   │   │   │   └── verifyspf.py
│   │   │   ├── server/
│   │   │   │   ├── inbound-mail.service.spec.ts
│   │   │   │   ├── inbound-mail.service.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── logger.ts
│   │   │   │   └── mailUtilities.ts
│   │   │   └── types/
│   │   │       └── env.d.ts
│   │   └── tsconfig.json
│   ├── webhook/
│   │   ├── .dockerignore
│   │   ├── .gitignore
│   │   ├── Dockerfile
│   │   ├── e2e/
│   │   │   ├── mocha.e2e.opts
│   │   │   └── setup.ts
│   │   ├── nest-cli.json
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── .example.env
│   │   │   ├── app.controller.ts
│   │   │   ├── app.module.ts
│   │   │   ├── app.service.ts
│   │   │   ├── bootstrap.ts
│   │   │   ├── config/
│   │   │   │   ├── env.config.ts
│   │   │   │   └── env.validators.ts
│   │   │   ├── health/
│   │   │   │   ├── health.controller.ts
│   │   │   │   └── health.module.ts
│   │   │   ├── instrument.ts
│   │   │   ├── main.ts
│   │   │   ├── shared/
│   │   │   │   ├── constants.ts
│   │   │   │   ├── framework/
│   │   │   │   │   ├── response.interceptor.ts
│   │   │   │   │   └── user.decorator.ts
│   │   │   │   ├── helpers/
│   │   │   │   │   └── regex.service.ts
│   │   │   │   └── shared.module.ts
│   │   │   ├── types/
│   │   │   │   └── env.d.ts
│   │   │   └── webhooks/
│   │   │       ├── dtos/
│   │   │       │   └── webhooks-response.dto.ts
│   │   │       ├── e2e/
│   │   │       │   └── email-webhook.e2e.ts
│   │   │       ├── interfaces/
│   │   │       │   └── webhook.interface.ts
│   │   │       ├── usecases/
│   │   │       │   ├── execution-details/
│   │   │       │   │   ├── create-execution-details.command.ts
│   │   │       │   │   └── create-execution-details.usecase.ts
│   │   │       │   ├── index.ts
│   │   │       │   └── webhook/
│   │   │       │       ├── webhook.command.ts
│   │   │       │       └── webhook.usecase.ts
│   │   │       ├── webhooks.controller.ts
│   │   │       └── webhooks.module.ts
│   │   ├── tsconfig.build.json
│   │   └── tsconfig.json
│   ├── worker/
│   │   ├── .gitignore
│   │   ├── .mocharc.json
│   │   ├── .vscode/
│   │   │   └── settings.json
│   │   ├── Dockerfile
│   │   ├── README.md
│   │   ├── e2e/
│   │   │   └── setup.ts
│   │   ├── nest-cli.json
│   │   ├── package.json
│   │   ├── project.json
│   │   ├── src/
│   │   │   ├── .example.env
│   │   │   ├── app/
│   │   │   │   ├── health/
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   └── health-check.e2e.ts
│   │   │   │   │   ├── health.controller.ts
│   │   │   │   │   └── health.module.ts
│   │   │   │   ├── shared/
│   │   │   │   │   ├── response.interceptor.ts
│   │   │   │   │   ├── shared.module.ts
│   │   │   │   │   └── utils/
│   │   │   │   │       ├── constants.ts
│   │   │   │   │       ├── exceptions.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       └── should-halt-on-step-failure.ts
│   │   │   │   ├── telemetry/
│   │   │   │   │   ├── telemetry.module.ts
│   │   │   │   │   ├── usecases/
│   │   │   │   │   │   ├── machineInfoService.usecase.ts
│   │   │   │   │   │   └── userInfoService.usecase.ts
│   │   │   │   │   └── utils/
│   │   │   │   │       ├── machine.utils.ts
│   │   │   │   │       └── sendDataToNovuTrace.utils.ts
│   │   │   │   └── workflow/
│   │   │   │       ├── services/
│   │   │   │       │   ├── active-jobs-metric.service.ts
│   │   │   │       │   ├── cold-start.service.ts
│   │   │   │       │   ├── index.ts
│   │   │   │       │   ├── standard.worker.spec.ts
│   │   │   │       │   ├── standard.worker.ts
│   │   │   │       │   ├── subscriber-process.worker.ts
│   │   │   │       │   ├── workflow.worker.spec.ts
│   │   │   │       │   └── workflow.worker.ts
│   │   │   │       ├── specs/
│   │   │   │       │   ├── conditions-filter.usecase.spec.ts
│   │   │   │       │   └── inbound-email-parse.spec.ts
│   │   │   │       ├── usecases/
│   │   │   │       │   ├── add-job/
│   │   │   │       │   │   ├── add-job.command.ts
│   │   │   │       │   │   ├── add-job.usecase.ts
│   │   │   │       │   │   ├── index.ts
│   │   │   │       │   │   ├── merge-or-create-digest.command.ts
│   │   │   │       │   │   ├── merge-or-create-digest.usecase.ts
│   │   │   │       │   │   └── validation.ts
│   │   │   │       │   ├── execute-bridge-job/
│   │   │   │       │   │   ├── execute-bridge-job.command.ts
│   │   │   │       │   │   ├── execute-bridge-job.usecase.ts
│   │   │   │       │   │   └── index.ts
│   │   │   │       │   ├── handle-last-failed-job/
│   │   │   │       │   │   ├── handle-last-failed-job.command.ts
│   │   │   │       │   │   ├── handle-last-failed-job.usecase.ts
│   │   │   │       │   │   └── index.ts
│   │   │   │       │   ├── inbound-email-parse/
│   │   │   │       │   │   ├── inbound-email-parse.command.ts
│   │   │   │       │   │   └── inbound-email-parse.usecase.ts
│   │   │   │       │   ├── index.ts
│   │   │   │       │   ├── noop-send-webhook-message.usecase.ts
│   │   │   │       │   ├── process-unsnooze-job/
│   │   │   │       │   │   ├── index.ts
│   │   │   │       │   │   ├── process-unsnooze-job.command.ts
│   │   │   │       │   │   └── process-unsnooze-job.usecase.ts
│   │   │   │       │   ├── queue-next-job/
│   │   │   │       │   │   ├── index.ts
│   │   │   │       │   │   ├── queue-next-job.command.ts
│   │   │   │       │   │   └── queue-next-job.usecase.ts
│   │   │   │       │   ├── run-job/
│   │   │   │       │   │   ├── index.ts
│   │   │   │       │   │   ├── run-job.command.ts
│   │   │   │       │   │   ├── run-job.usecase.ts
│   │   │   │       │   │   ├── schedule-validator.spec.ts
│   │   │   │       │   │   └── schedule-validator.ts
│   │   │   │       │   ├── send-message/
│   │   │   │       │   │   ├── channel-endpoint-resolution/
│   │   │   │       │   │   │   ├── resolve-channel-endpoints.command.ts
│   │   │   │       │   │   │   └── resolve-channel-endpoints.usecase.ts
│   │   │   │       │   │   ├── digest/
│   │   │   │       │   │   │   ├── digest-events.command.ts
│   │   │   │       │   │   │   ├── digest.usecase.ts
│   │   │   │       │   │   │   ├── get-digest-events-backoff.usecase.ts
│   │   │   │       │   │   │   ├── get-digest-events-regular.usecase.ts
│   │   │   │       │   │   │   ├── get-digest-events.usecase.ts
│   │   │   │       │   │   │   └── index.ts
│   │   │   │       │   │   ├── execute-code-first-custom-step.usecase.ts
│   │   │   │       │   │   ├── execute-http-request-step.usecase.ts
│   │   │   │       │   │   ├── index.ts
│   │   │   │       │   │   ├── send-message-channel.command.ts
│   │   │   │       │   │   ├── send-message-chat.usecase.ts
│   │   │   │       │   │   ├── send-message-delay.usecase.ts
│   │   │   │       │   │   ├── send-message-email.usecase.ts
│   │   │   │       │   │   ├── send-message-in-app.usecase.ts
│   │   │   │       │   │   ├── send-message-push.usecase.spec.ts
│   │   │   │       │   │   ├── send-message-push.usecase.ts
│   │   │   │       │   │   ├── send-message-sms.usecase.ts
│   │   │   │       │   │   ├── send-message-type.usecase.ts
│   │   │   │       │   │   ├── send-message.base.ts
│   │   │   │       │   │   ├── send-message.command.ts
│   │   │   │       │   │   ├── send-message.usecase.ts
│   │   │   │       │   │   └── throttle/
│   │   │   │       │   │       ├── index.ts
│   │   │   │       │   │       └── throttle.usecase.ts
│   │   │   │       │   ├── store-subscriber-jobs/
│   │   │   │       │   │   ├── index.ts
│   │   │   │       │   │   ├── store-subscriber-jobs.command.ts
│   │   │   │       │   │   └── store-subscriber-jobs.usecase.ts
│   │   │   │       │   ├── subscriber-job-bound/
│   │   │   │       │   │   ├── subscriber-job-bound.command.ts
│   │   │   │       │   │   └── subscriber-job-bound.usecase.ts
│   │   │   │       │   ├── update-job-status/
│   │   │   │       │   │   ├── index.ts
│   │   │   │       │   │   ├── set-job-as-completed.usecase.ts
│   │   │   │       │   │   ├── set-job-as-failed.usecase.ts
│   │   │   │       │   │   ├── set-job-as.command.ts
│   │   │   │       │   │   ├── update-job-status.command.ts
│   │   │   │       │   │   └── update-job-status.usecase.ts
│   │   │   │       │   └── webhook-filter-backoff-strategy/
│   │   │   │       │       ├── event-job.dto.ts
│   │   │   │       │       ├── index.ts
│   │   │   │       │       ├── webhook-filter-backoff-strategy.command.ts
│   │   │   │       │       └── webhook-filter-backoff-strategy.usecase.ts
│   │   │   │       ├── workers/
│   │   │   │       │   └── inbound-parse.worker.service.ts
│   │   │   │       └── workflow.module.ts
│   │   │   ├── app.module.ts
│   │   │   ├── bootstrap.ts
│   │   │   ├── config/
│   │   │   │   ├── env.config.ts
│   │   │   │   ├── env.validators.ts
│   │   │   │   ├── index.ts
│   │   │   │   └── worker-init.config.ts
│   │   │   ├── instrument.ts
│   │   │   ├── main.ts
│   │   │   ├── newrelic.ts
│   │   │   └── types/
│   │   │       └── env.d.ts
│   │   ├── tsconfig.build.json
│   │   ├── tsconfig.json
│   │   ├── tsconfig.spec.json
│   │   └── webpack.config.js
│   └── ws/
│       ├── .gitignore
│       ├── Dockerfile
│       ├── e2e/
│       │   └── setup.ts
│       ├── nest-cli.json
│       ├── package.json
│       ├── src/
│       │   ├── .example.env
│       │   ├── app.controller.ts
│       │   ├── app.module.ts
│       │   ├── app.service.ts
│       │   ├── bootstrap.ts
│       │   ├── config/
│       │   │   ├── env.config.ts
│       │   │   ├── env.validators.ts
│       │   │   └── index.ts
│       │   ├── health/
│       │   │   ├── health.controller.ts
│       │   │   └── health.module.ts
│       │   ├── instrument.ts
│       │   ├── main.ts
│       │   ├── shared/
│       │   │   ├── framework/
│       │   │   │   └── in-memory-io.adapter.ts
│       │   │   ├── shared.module.ts
│       │   │   └── subscriber-online/
│       │   │       ├── index.ts
│       │   │       └── subscriber-online.service.ts
│       │   ├── socket/
│       │   │   ├── services/
│       │   │   │   ├── cold-start.service.ts
│       │   │   │   ├── index.ts
│       │   │   │   ├── web-socket.worker.spec.ts
│       │   │   │   ├── web-socket.worker.ts
│       │   │   │   └── ws-server-health-indicator.service.ts
│       │   │   ├── socket.module.ts
│       │   │   ├── usecases/
│       │   │   │   └── external-services-route/
│       │   │   │       ├── external-services-route.command.ts
│       │   │   │       ├── external-services-route.spec.ts
│       │   │   │       ├── external-services-route.usecase.ts
│       │   │   │       ├── index.ts
│       │   │   │       └── types.ts
│       │   │   └── ws.gateway.ts
│       │   └── types/
│       │       └── env.d.ts
│       ├── tsconfig.build.json
│       └── tsconfig.json
├── biome-plugins/
│   ├── api-property-optionality-required-prop.grit
│   ├── api-property-optionality.grit
│   ├── api-property-record-type.grit
│   ├── command-session-exclusion.grit
│   └── pino-logger-arg-order.grit
├── biome.json
├── docker/
│   ├── Readme.md
│   ├── community/
│   │   └── docker-compose.yml
│   └── local/
│       ├── docker-compose.agent.yml
│       ├── docker-compose.e2e.yml
│       ├── docker-compose.local.yml
│       └── docker-compose.yml
├── enterprise/
│   ├── packages/
│   │   ├── ai/
│   │   │   ├── .gitignore
│   │   │   ├── check-ee.mjs
│   │   │   ├── package.json
│   │   │   ├── project.json
│   │   │   ├── tsconfig.build.json
│   │   │   └── tsconfig.json
│   │   ├── api/
│   │   │   ├── .gitignore
│   │   │   ├── check-ee.mjs
│   │   │   ├── package.json
│   │   │   ├── project.json
│   │   │   ├── tsconfig.build.json
│   │   │   └── tsconfig.json
│   │   ├── auth/
│   │   │   ├── .gitignore
│   │   │   ├── check-ee.mjs
│   │   │   ├── package.json
│   │   │   ├── project.json
│   │   │   ├── tsconfig.json
│   │   │   └── tsconfig.spec.json
│   │   ├── billing/
│   │   │   ├── .gitignore
│   │   │   ├── check-ee.mjs
│   │   │   ├── package.json
│   │   │   ├── project.json
│   │   │   ├── tsconfig.json
│   │   │   └── tsconfig.spec.json
│   │   ├── shared-services/
│   │   │   ├── .czrc
│   │   │   ├── .gitignore
│   │   │   ├── README.md
│   │   │   ├── check-ee.mjs
│   │   │   ├── package.json
│   │   │   ├── project.json
│   │   │   ├── tsconfig.json
│   │   │   └── tsconfig.module.json
│   │   └── translation/
│   │       ├── .gitignore
│   │       ├── check-ee.mjs
│   │       ├── package.json
│   │       ├── project.json
│   │       ├── tsconfig.json
│   │       └── tsconfig.spec.json
│   └── workers/
│       ├── scheduler/
│       │   ├── .editorconfig
│       │   ├── .gitignore
│       │   ├── .prettierrc
│       │   ├── package.json
│       │   ├── src/
│       │   │   ├── auth.ts
│       │   │   ├── env.d.ts
│       │   │   ├── index.ts
│       │   │   ├── scheduler.ts
│       │   │   └── types.ts
│       │   ├── test/
│       │   │   ├── env.d.ts
│       │   │   ├── index.spec.ts
│       │   │   ├── scheduler.spec.ts
│       │   │   └── tsconfig.json
│       │   ├── tsconfig.json
│       │   ├── vitest.config.mts
│       │   ├── worker-configuration.d.ts
│       │   ├── wrangler.jsonc
│       │   └── wrangler.local.jsonc
│       ├── socket/
│       │   ├── .editorconfig
│       │   ├── .gitignore
│       │   ├── .vscode/
│       │   │   └── settings.json
│       │   ├── package.json
│       │   ├── src/
│       │   │   ├── durable-objects/
│       │   │   │   └── websocket-room.ts
│       │   │   ├── handlers/
│       │   │   │   └── websocket.ts
│       │   │   ├── index.ts
│       │   │   ├── middleware/
│       │   │   │   ├── auth.ts
│       │   │   │   └── internal-auth.ts
│       │   │   └── types/
│       │   │       └── index.ts
│       │   ├── tsconfig.json
│       │   ├── worker-configuration.d.ts
│       │   └── wrangler.jsonc
│       └── step-resolver/
│           ├── .gitignore
│           ├── README.md
│           ├── package.json
│           ├── src/
│           │   ├── auth/
│           │   │   └── hmac.ts
│           │   ├── index.ts
│           │   ├── types.ts
│           │   └── utils/
│           │       └── worker-id.ts
│           ├── tsconfig.json
│           ├── worker-configuration.d.ts
│           └── wrangler.jsonc
├── jest.config.js
├── libs/
│   ├── application-generic/
│   │   ├── .czrc
│   │   ├── .gitignore
│   │   ├── README.md
│   │   ├── jest.config.js
│   │   ├── jest.setup.js
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── commands/
│   │   │   │   ├── authenticated.command.ts
│   │   │   │   ├── base.command.spec.ts
│   │   │   │   ├── base.command.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── organization.command.ts
│   │   │   │   └── project.command.ts
│   │   │   ├── config/
│   │   │   │   ├── index.ts
│   │   │   │   ├── workers.config.spec.ts
│   │   │   │   └── workers.ts
│   │   │   ├── custom-providers/
│   │   │   │   └── index.ts
│   │   │   ├── decorators/
│   │   │   │   ├── context-payload.decorator.ts
│   │   │   │   ├── external-api.decorator.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── is-valid-context-payload.decorator.ts
│   │   │   │   ├── is-valid-locale.decorator.ts
│   │   │   │   ├── json-schema.validator.ts
│   │   │   │   ├── permissions.decorator.ts
│   │   │   │   ├── product-feature.decorator.ts
│   │   │   │   ├── resource-category.decorator.ts
│   │   │   │   ├── retry-on-error-decorator.spec.ts
│   │   │   │   ├── retry-on-error-decorator.ts
│   │   │   │   ├── to-boolean.spec.ts
│   │   │   │   ├── to-boolean.ts
│   │   │   │   └── user-session.decorator.ts
│   │   │   ├── dtos/
│   │   │   │   ├── base-issue.dto.ts
│   │   │   │   ├── configurations.dto.ts
│   │   │   │   ├── controls-metadata.dto.ts
│   │   │   │   ├── credentials.dto.ts
│   │   │   │   ├── get-environment-tags.dto.ts
│   │   │   │   ├── get-workflow-with-preferences.dto.ts
│   │   │   │   ├── inbound-parse-job.dto.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── integration-issue.dto.ts
│   │   │   │   ├── integration-response.dto.ts
│   │   │   │   ├── json-schema.dto.ts
│   │   │   │   ├── layout/
│   │   │   │   │   ├── create-layout.dto.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── layout-controls.dto.ts
│   │   │   │   │   ├── layout-response.dto.ts
│   │   │   │   │   ├── update-layout.dto.ts
│   │   │   │   │   └── v0/
│   │   │   │   │       └── layout.dto.ts
│   │   │   │   ├── process-subscriber-job.dto.ts
│   │   │   │   ├── standard-job.dto.ts
│   │   │   │   ├── step-content-issue.dto.ts
│   │   │   │   ├── step-filter-dto.ts
│   │   │   │   ├── step-issues.dto.ts
│   │   │   │   ├── subscriber-topic-preference.dto.ts
│   │   │   │   ├── subscribers/
│   │   │   │   │   ├── channelSettingsDto.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── subscriber-channel.ts
│   │   │   │   │   ├── subscriber-response.dto.ts
│   │   │   │   │   └── update-subscriber-channel-request.dto.ts
│   │   │   │   ├── ui-schema-property.dto.ts
│   │   │   │   ├── ui-schema.dto.ts
│   │   │   │   ├── user-response.dto.ts
│   │   │   │   ├── web-sockets-job.dto.ts
│   │   │   │   ├── workflow/
│   │   │   │   │   ├── channel-preference.dto.ts
│   │   │   │   │   ├── chat-control.dto.ts
│   │   │   │   │   ├── controls/
│   │   │   │   │   │   ├── custom-control.dto.ts
│   │   │   │   │   │   ├── delay-control.dto.ts
│   │   │   │   │   │   ├── digest-control.dto.ts
│   │   │   │   │   │   ├── email-control.dto.ts
│   │   │   │   │   │   ├── http-request-control.dto.ts
│   │   │   │   │   │   ├── in-app-control.dto.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── look-back-window.dto.ts
│   │   │   │   │   │   ├── push-control.dto.ts
│   │   │   │   │   │   ├── sms-control.dto.ts
│   │   │   │   │   │   └── throttle-control.dto.ts
│   │   │   │   │   ├── generate-preview-request.dto.ts
│   │   │   │   │   ├── generate-preview-response.dto.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── preferences.response.dto.ts
│   │   │   │   │   ├── preview-payload.dto.ts
│   │   │   │   │   ├── runtime-issue.dto.ts
│   │   │   │   │   ├── skip.dto.ts
│   │   │   │   │   ├── step-list-response.dto.ts
│   │   │   │   │   ├── step-responses/
│   │   │   │   │   │   ├── chat-step.response.dto.ts
│   │   │   │   │   │   ├── custom-step.response.dto.ts
│   │   │   │   │   │   ├── delay-step.response.dto.ts
│   │   │   │   │   │   ├── digest-step.response.dto.ts
│   │   │   │   │   │   ├── email-step.response.dto.ts
│   │   │   │   │   │   ├── http-request-step.response.dto.ts
│   │   │   │   │   │   ├── in-app-step.response.dto.ts
│   │   │   │   │   │   ├── push-step.response.dto.ts
│   │   │   │   │   │   ├── sms-step.response.dto.ts
│   │   │   │   │   │   └── throttle-step.response.dto.ts
│   │   │   │   │   ├── step.response.dto.ts
│   │   │   │   │   ├── workflow-commons.dto.ts
│   │   │   │   │   ├── workflow-list-response.dto.ts
│   │   │   │   │   ├── workflow-preference.dto.ts
│   │   │   │   │   ├── workflow-preferences.dto.ts
│   │   │   │   │   └── workflow-response.dto.ts
│   │   │   │   └── workflow-job.dto.ts
│   │   │   ├── encryption/
│   │   │   │   ├── cipher.spec.ts
│   │   │   │   ├── cipher.ts
│   │   │   │   ├── encrypt-environment-variable.ts
│   │   │   │   ├── encrypt-provider.spec.ts
│   │   │   │   ├── encrypt-provider.ts
│   │   │   │   └── index.ts
│   │   │   ├── factories/
│   │   │   │   ├── channel.factory.ts
│   │   │   │   ├── chat/
│   │   │   │   │   ├── chat.factory.ts
│   │   │   │   │   ├── handlers/
│   │   │   │   │   │   ├── base.handler.ts
│   │   │   │   │   │   ├── chat-webhook.handler.ts
│   │   │   │   │   │   ├── discord.handler.ts
│   │   │   │   │   │   ├── getstream.handler.ts
│   │   │   │   │   │   ├── grafana-on-call.handler.ts
│   │   │   │   │   │   ├── mattermost.handler.ts
│   │   │   │   │   │   ├── msteams.handler.ts
│   │   │   │   │   │   ├── novu-slack.handler.ts
│   │   │   │   │   │   ├── rocket-chat.handler.ts
│   │   │   │   │   │   ├── ryver.handler.ts
│   │   │   │   │   │   ├── slack.handler.ts
│   │   │   │   │   │   ├── whatsapp-business.handler.ts
│   │   │   │   │   │   └── zulip.handler.ts
│   │   │   │   │   └── interfaces/
│   │   │   │   │       └── index.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── mail/
│   │   │   │   │   ├── handlers/
│   │   │   │   │   │   ├── base.handler.ts
│   │   │   │   │   │   ├── braze.handler.ts
│   │   │   │   │   │   ├── email-webhook.handler.ts
│   │   │   │   │   │   ├── emailjs.handler.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── infobip.handler.ts
│   │   │   │   │   │   ├── mailersend.handler.ts
│   │   │   │   │   │   ├── mailgun.handler.ts
│   │   │   │   │   │   ├── mailjet.handler.ts
│   │   │   │   │   │   ├── mailtrap.handler.ts
│   │   │   │   │   │   ├── mandrill.handler.ts
│   │   │   │   │   │   ├── netcore.handler.ts
│   │   │   │   │   │   ├── nodemailer.handler.ts
│   │   │   │   │   │   ├── novu.handler.ts
│   │   │   │   │   │   ├── outlook365.handler.ts
│   │   │   │   │   │   ├── plunk.handler.ts
│   │   │   │   │   │   ├── postmark.handler.ts
│   │   │   │   │   │   ├── resend.handler.ts
│   │   │   │   │   │   ├── sendgrid.handler.ts
│   │   │   │   │   │   ├── sendinblue.handler.ts
│   │   │   │   │   │   ├── ses.handler.ts
│   │   │   │   │   │   └── sparkpost.handler.ts
│   │   │   │   │   ├── interfaces/
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   └── mail.factory.ts
│   │   │   │   ├── push/
│   │   │   │   │   ├── handlers/
│   │   │   │   │   │   ├── apns.handler.ts
│   │   │   │   │   │   ├── appio.handler.ts
│   │   │   │   │   │   ├── base.handler.ts
│   │   │   │   │   │   ├── expo.handler.ts
│   │   │   │   │   │   ├── fcm.handler.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── one-signal.handler.ts
│   │   │   │   │   │   ├── push-webhook.handle

================================================
FILE CONTENTS
================================================

================================================
FILE: .agents/skills/email-best-practices/SKILL.md
================================================
---
name: email-best-practices
description: Use when building email features, emails going to spam, high bounce rates, setting up SPF/DKIM/DMARC authentication, implementing email capture, ensuring compliance (CAN-SPAM, GDPR, CASL), handling webhooks, retry logic, or deciding transactional vs marketing.
---

# Email Best Practices

Guidance for building deliverable, compliant, user-friendly emails.

## Architecture Overview

```
[User] → [Email Form] → [Validation] → [Double Opt-In]
                                              ↓
                                    [Consent Recorded]
                                              ↓
[Suppression Check] ←──────────────[Ready to Send]
        ↓
[Idempotent Send + Retry] ──────→ [Email API]
                                       ↓
                              [Webhook Events]
                                       ↓
              ┌────────┬────────┬─────────────┐
              ↓        ↓        ↓             ↓
         Delivered  Bounced  Complained  Opened/Clicked
                       ↓        ↓
              [Suppression List Updated]
                       ↓
              [List Hygiene Jobs]
```

## Quick Reference

| Need to... | See |
|------------|-----|
| Set up SPF/DKIM/DMARC, fix spam issues | [Deliverability](./resources/deliverability.md) |
| Build password reset, OTP, confirmations | [Transactional Emails](./resources/transactional-emails.md) |
| Plan which emails your app needs | [Transactional Email Catalog](./resources/transactional-email-catalog.md) |
| Build newsletter signup, validate emails | [Email Capture](./resources/email-capture.md) |
| Send newsletters, promotions | [Marketing Emails](./resources/marketing-emails.md) |
| Ensure CAN-SPAM/GDPR/CASL compliance | [Compliance](./resources/compliance.md) |
| Decide transactional vs marketing | [Email Types](./resources/email-types.md) |
| Handle retries, idempotency, errors | [Sending Reliability](./resources/sending-reliability.md) |
| Process delivery events, set up webhooks | [Webhooks & Events](./resources/webhooks-events.md) |
| Manage bounces, complaints, suppression | [List Management](./resources/list-management.md) |

## Start Here

**New app?**
Start with the [Catalog](./resources/transactional-email-catalog.md) to plan which emails your app needs (password reset, verification, etc.), then set up [Deliverability](./resources/deliverability.md) (DNS authentication) before sending your first email.

**Spam issues?**
Check [Deliverability](./resources/deliverability.md) first—authentication problems are the most common cause. Gmail/Yahoo reject unauthenticated emails.

**Marketing emails?**
Follow this path: [Email Capture](./resources/email-capture.md) (collect consent) → [Compliance](./resources/compliance.md) (legal requirements) → [Marketing Emails](./resources/marketing-emails.md) (best practices).

**Production-ready sending?**
Add reliability: [Sending Reliability](./resources/sending-reliability.md) (retry + idempotency) → [Webhooks & Events](./resources/webhooks-events.md) (track delivery) → [List Management](./resources/list-management.md) (handle bounces).


================================================
FILE: .agents/skills/email-best-practices/resources/branding.md
================================================
# Email Branding

## BIMI (Optional)

Display brand logo in email clients. Requires DMARC `p=quarantine` or `p=reject`.

================================================
FILE: .agents/skills/email-best-practices/resources/compliance.md
================================================
# Email Compliance

Legal requirements for email by jurisdiction. **Not legal advice—consult an attorney for your specific situation.**

## Quick Reference

| Law | Region | Key Requirement | Penalty |
|-----|--------|-----------------|---------|
| CAN-SPAM | US | Opt-out mechanism, physical address | $53k/email |
| GDPR | EU | Explicit opt-in consent | €20M or 4% revenue |
| CASL | Canada | Express consent, opt-out mechanism | $1M (individual) to $10M (organization) CAD |

## CAN-SPAM (United States)

**Requirements:**
- Accurate header info (From, To, Reply-To)
- Non-deceptive subject lines
- Physical mailing address in every email
- Clear opt-out mechanism
- Honor opt-out within 10 business days

**Transactional emails:** Can send without opt-in if related to a transaction and not promotional.

## GDPR (European Union)

**Requirements:**
- Explicit opt-in consent (not pre-checked boxes)
- Consent must be freely given, specific, informed
- Easy to withdraw consent (as easy as giving it)
- Right to access data and deletion ("right to be forgotten")
- Process unsubscribe immediately

**Consent records:** Document who, when, how, and what they consented to.

**Transactional emails:** Can send based on contract fulfillment or legitimate interest.

## CASL (Canada)

**Consent types:**
- **Express consent:** Explicit opt-in (ideal)
- **Implied consent:** Existing business relationship (2 years) or inquiry (6 months)

**Requirements:**
- Clear sender identification that will be valid for 60 days after send
- Unsubscribe functional for 60 days after send
- Process unsubscribe no later than 10 business days
- Keep consent records 3 years after expiration

## Other Regions

| Region | Law | Key Points |
|--------|-----|------------|
| Australia | Spam Act 2003 | Consent required, honor unsubscribe within 5 days |
| UK | PECR + GDPR | Same as GDPR |
| Brazil | LGPD | Similar to GDPR, explicit consent for marketing |

## Unsubscribe Requirements Summary

| Law | Timing | Notes |
|-----|--------|-------|
| CAN-SPAM | 10 business days | Must work 30 days after send |
| GDPR | Immediately | Must be as easy as opting in |
| CASL | 10 business days | Must work 60 days after send |

**Universal best practices:** Prominent link, one-click when possible, no login required, free, confirm action.

## Managing preferences vs Unsubscribe from all

Most legistlations require a one-click unsubscribe. `Managing preferences` is a nice-to-have and can lead to lower unsubscribe rate but doesn't replace `Unsubscribe`. If possible, offer both.

## Consent Management

**Record:**
- Email address
- Date/time of consent
- Method (form, checkbox)
- What they consented to
- Source (which page/form)

**Storage:** Database with timestamps, audit trail of changes, link to user account.

## Data Retention

| Law | Requirement |
|-----|-------------|
| GDPR | Keep only as long as necessary, delete when no longer needed |
| CASL | Keep consent records 3 years after expiration |

**Best practice:** Have clear retention policy, honor deletion requests promptly, review and clean regularly.

## Privacy Policy Must Include

- What data you collect
- How you use data
- Who you share data with
- User rights (access, deletion)
- How to contact about privacy

## International Sending

**Best practice:** Follow the most restrictive requirements (usually GDPR) to ensure compliance across all regions.

## Related

- [Email Capture](./email-capture.md) - Implement consent forms and double opt-in
- [Marketing Emails](./marketing-emails.md) - Consent and unsubscribe requirements
- [List Management](./list-management.md) - Handle unsubscribes and deletion requests


================================================
FILE: .agents/skills/email-best-practices/resources/deliverability.md
================================================
# Email Deliverability

Maximizing the chances that your emails are delivered successfully to the recipients.

## Email Authentication

**Required by Gmail/Yahoo/Microsoft** - unauthenticated emails will be rejected or spam-filtered.

### SPF (Sender Policy Framework)

Specifies which servers can send email for your domain.

```
v=spf1 include:amazonses.com ~all
```

- Add TXT record to DNS
- Use `~all` (soft fail)

### DKIM (DomainKeys Identified Mail)

Cryptographic signature proving email authenticity.

- Your email service will provide you with a TXT record

### DMARC

Policy for handling SPF/DKIM failures + reporting.

```
v=DMARC1; p=none; rua=mailto:dmarc@yourdomain.com
```

**Rollout:** `p=none` (monitor) → `p=quarantine; pct=25` → `p=reject`

Learn more: https://resend.com/blog/dmarc-policy-modes 

### Verify Your Setup

Check DNS records directly:

```bash
# SPF record
dig TXT yourdomain.com +short

# DKIM record (replace 'resend' with your selector)
dig TXT resend._domainkey.yourdomain.com +short

# DMARC record
dig TXT _dmarc.yourdomain.com +short
```

**Expected output:** Each command should return your configured record. No output = record missing.

## Sender Reputation

### IP Warming

New IP/domain? Gradually increase volume:

| Week | Daily Volume |
|------|-------------|
| 1 | 50-100 |
| 2 | 200-500 |
| 3 | 1,000-2,000 |
| 4 | 5,000-10,000 |

Start with engaged users. Send consistently. Don't rush.

Learn more: https://resend.com/docs/knowledge-base/warming-up

### Maintaining Reputation

**Do:** Send to engaged users, keep bounce <4%, complaints <0.1%, remove inactive subscribers.

**Don't:** Send to purchased lists, ignore bounces/complaints, send inconsistent volumes

## Bounce Handling

| Type | Cause | Action |
|------|-------|--------|
| Hard bounce | Permanent failure to deliver | Remove immediately |
| Soft bounce | Transient failure to deliver | Retry: 1h → 4h → 24h, remove after 3-5 failures |

**Targets:** <1% good, 1-3% acceptable, 3-4% concerning, >4% critical

## Complaint Handling

**Targets:** <0.01% excellent, 0.01-0.05% good, >0.05% critical

**Reduce complaints:**
- Only send to opted-in users
- Make unsubscribe easy and immediate
- Use clear sender names and "From" addresses

**Feedback loops:** Set up with Gmail (Postmaster Tools), Yahoo, Microsoft SNDS. Remove complainers immediately.

## Infrastructure

**Dedicated sending domain:** Use different subdomains for different sending purposes (e.g., `t.yourdomain.com` for transactional emails and `m.yourdomain.com` for marketing emails). 

**DNS TTL:** Low (300s) during setup, high (3600s+) after stable.

## Troubleshooting

**Emails going to spam?** Check in order:
1. Authentication (SPF, DKIM, DMARC)
2. Sender reputation (blacklists, complaint rates)
3. Content
4. Sending patterns (sudden volume spikes)

**Diagnostic tools:** [Google Postmaster Tools](https://postmaster.google.com)

## Related

- [List Management](./list-management.md) - Handle bounces and complaints to protect reputation
- [Sending Reliability](./sending-reliability.md) - Retry logic and error handling


================================================
FILE: .agents/skills/email-best-practices/resources/email-capture.md
================================================
# Email Capture Best Practices

Collecting email addresses responsibly with validation, verification, and proper consent.

## Email Validation

### Client-Side

**HTML5:**
```html
<input type="email" required>
```

**Best practices:**
- Validate on blur or with short debounce
- Show clear error messages
- Don't be too strict (allow unusual but valid formats)
- Client-side validation ≠ deliverability

### Server-Side (Recommended)

Always validate server-side—client-side can be bypassed.

**Check:**
- Email format (RFC 5322)
- Domain exists (DNS lookup)
- Domain has MX records
- Optionally: disposable email detection

Recommended tools: https://resend.com/blog/best-email-verification-apis 

## Double opt-in

Confirms address belongs to user and is deliverable.

### Process

1. User submits email
2. Send verification email with unique link/token
3. User clicks link
4. Mark as verified
5. Allow access/add to list

**Timing:** Send immediately, include expiration (24-48 hours), allow resend after 60 seconds, limit resend attempts (3/hour).

### Single vs Double Opt-In

| | Single Opt-In | Double Opt-In |
|--|---------------|---------------|
| **Process** | Add to list immediately | Require email confirmation first |
| **Pros** | Lower friction, faster growth | Verified addresses, better engagement, meets GDPR/CASL |
| **Cons** | Higher invalid rate, lower engagement | Some users don't confirm |
| **Use for** | Account creation, transactional | Marketing lists, newsletters |

**Recommendation:** Double opt-in for all marketing emails.

## Form Design

### Email Input

- Use `type="email"` for mobile keyboard
- Include placeholder ("you@example.com")
- Clear error messages ("Please enter a valid email address" not "Invalid")

### Consent Checkboxes (Marketing)

- **Unchecked by default** (required)
- Specific language about what they're signing up for
- Separate checkboxes for different email types
- Link to privacy policy

```
☐ Subscribe to our weekly newsletter with product updates
☐ Send me promotional offers and deals
```

**Don't:** Pre-check boxes, use vague language, hide in terms.

### Form Layout

- Keep simple and focused
- One primary action
- Clear value proposition
- Mobile-friendly
- Accessible (labels, ARIA)

## Error Handling

### Invalid Email

- Show clear error message
- Suggest corrections for common typos (@gmial.com → @gmail.com)
- Allow user to fix and resubmit

### Already Registered

- Accounts: "This email is already registered. [Sign in]"
- Marketing: "You're already subscribed! [Manage preferences]"
- Don't reveal if account exists (security)

### Rate Limiting

- Limit verification emails (3/hour per email)
- Rate limit form submissions
- Use CAPTCHA sparingly if needed
- Monitor for abuse patterns

## Verification Emails

**Content:**
- Clear purpose ("Verify your email address")
- Prominent verification button
- Expiration time
- Resend option
- "I didn't request this" notice

**Design:**
- Mobile-friendly
- Large, tappable button
- Clear call-to-action

See [Transactional Emails](./transactional-emails.md) for detailed email design guidance.

## Related

- [Compliance](./compliance.md) - Legal requirements for consent (GDPR, CASL)
- [Marketing Emails](./marketing-emails.md) - What happens after capture
- [Deliverability](./deliverability.md) - How validation improves sender reputation


================================================
FILE: .agents/skills/email-best-practices/resources/email-types.md
================================================
# Email Types: Transactional vs Marketing

Understanding the difference between transactional and marketing emails is crucial for compliance, deliverability, and user experience. This guide explains the distinctions and provides a catalog of transactional emails your app should include.

## When to Use This

- Deciding whether an email should be transactional or marketing
- Understanding legal distinctions between email types
- Planning what transactional emails your app needs
- Ensuring compliance with email regulations
- Setting up separate sending infrastructure

## Transactional vs Marketing: Key Differences

### Transactional Emails

**Definition:** Emails that facilitate or confirm a transaction the user initiated or expects. They're directly related to an action the user took or are legal notices you're required to serve.

**Characteristics:**
- User-initiated or expected
- Time-sensitive and actionable
- Required for the user to complete an action
- Does not include promotional material or offers
- Can be sent without explicit opt-in (with limitations)

**Examples:**
- Password reset links
- Order confirmations
- Account verification
- OTP/2FA codes
- Shipping notifications

**Analogy:**
Think of transactional emails for everything that would leave you with a paper receipt in the real world: invoices, parking ticket, booking confirmation, etc.

### Marketing Emails

**Definition:** Emails sent for promotional, advertising, or informational purposes that are not directly related to a specific transaction or legal requirement.

**Characteristics:**
- Promotional or informational content
- Not time-sensitive to complete a transaction
- Require explicit opt-in (consent)
- Must include unsubscribe options
- Subject to stricter compliance requirements

**Examples:**
- Newsletters
- Abandoned cart
- Product announcements
- Promotional offers
- Company updates
- Educational content

## Legal Distinctions

### CAN-SPAM Act (US)

**Transactional emails:**
- Can be sent without opt-in
- Must be related to a transaction
- Cannot contain promotional content (with exceptions)
- Must identify sender and provide contact information

**Marketing emails:**
- Require opt-out mechanism (not opt-in in US)
- Must include clear sender identification
- Must include physical mailing address
- Must honor opt-out requests within 10 business days

### GDPR (EU)

**Transactional emails:**
- Can be sent based on legitimate interest or contract fulfillment
- Must be necessary for service delivery
- Cannot contain marketing content without consent

**Marketing emails:**
- Require explicit opt-in consent
- Must clearly state purpose of data collection
- Must provide easy unsubscribe
- Subject to data protection requirements

### CASL (Canada)

**Transactional emails:**
- Can be sent without consent if related to ongoing business relationship
- Must be factual and not promotional

**Marketing emails:**
- Require express or implied consent
- Must include unsubscribe mechanism
- Must identify sender clearly

## When to Use Each Type

### Use Transactional When:

- User needs the email to complete an action
- Email confirms a transaction or account change
- Email provides security-related information
- Email is expected based on user action
- Content is time-sensitive and actionable
- You're required to serve a notification for compliance

### Use Marketing When:

- Promoting products or services
- Sending newsletters or updates
- Sharing educational content
- Announcing features or company news
- Content is not required for a transaction

## Hybrid Emails: The Gray Area

Some emails mix transactional and marketing content. This isn't best practice and should be avoided.

**Best practice:** Keep transactional and marketing separate. 

**Example of problematic hybrid:**
- Newsletter (marketing) with a small order status update (transactional)

## Transactional Email Catalog

For a complete catalog of transactional emails and recommended combinations by app type, see [Transactional Email Catalog](./transactional-email-catalog.md).

**Quick reference - Essential emails for most apps:**
1. **Email verification** - Required for account creation
2. **Password reset** - Required for account recovery
3. **Welcome email** - Good user experience

The catalog includes detailed guidance for:
- Authentication-focused apps
- Newsletter / content platforms
- E-commerce / marketplaces
- SaaS / subscription services
- Financial / fintech apps
- Social / community platforms
- Developer tools / API platforms
- Healthcare / HIPAA-compliant apps

## Sending Infrastructure

### Separate subdomains

**Best practice:** Use separate sending subdomains for transactional and marketing emails.

**Benefits:**
- Protect transactional deliverability
- Different authentication domains
- Independent reputation
- Easier compliance management

**Implementation:**
- Use different subdomains (e.g., `t.yourdomain.com` for transactional, `m.yourdomain.com` for marketing)

### Email Service Considerations

Choose an email service that:
- Provides reliable delivery for transactional emails
- Offers separate sending domains
- Has good API for programmatic sending
- Provides webhooks for delivery events
- Supports authentication setup (SPF, DKIM, DMARC)

Services like Resend are designed for transactional emails and provide the infrastructure and tools needed for reliable delivery. They also offer powerful marketing features.

## Related Topics

- [Transactional Emails](./transactional-emails.md) - Best practices for sending transactional emails
- [Marketing Emails](./marketing-emails.md) - Best practices for marketing emails
- [Compliance](./compliance.md) - Legal requirements for each email type
- [Deliverability](./deliverability.md) - Ensuring transactional emails are delivered


================================================
FILE: .agents/skills/email-best-practices/resources/list-management.md
================================================
# List Management

Maintaining clean email lists through suppression, hygiene, and data retention.

## Suppression Lists

A suppression list prevents sending to addresses that should never receive email.

### What to Suppress

| Reason | Action | Can Unsuppress? |
|--------|--------|-----------------|
| Hard bounce | Add immediately | No (address invalid) |
| Complaint (spam) | Add immediately | No (legal requirement) |
| Soft bounce (3x) | Add after threshold | Yes, after 30-90 days |
| Manual removal | Add on request | Only if user requests |

### Implementation

```typescript
// Suppression list schema
interface SuppressionEntry {
  email: string;
  reason: 'hard_bounce' | 'complaint' | 'unsubscribe' | 'soft_bounce' | 'manual';
  created_at: Date;
  source_email_id?: string; // Which email triggered this
}

// Check before every send
async function canSendTo(email: string): Promise<boolean> {
  const suppressed = await db.suppressions.findOne({ email });
  return !suppressed;
}

// Add to suppression list
async function suppressEmail(email: string, reason: string, sourceId?: string) {
  await db.suppressions.upsert({
    email: email.toLowerCase(),
    reason,
    created_at: new Date(),
    source_email_id: sourceId,
  });
}
```

### Pre-Send Check

**Always check suppression before sending:**

```typescript
async function sendEmail(to: string, emailData: EmailData) {
  if (!await canSendTo(to)) {
    console.log(`Skipping suppressed email: ${to}`);
    return { skipped: true, reason: 'suppressed' };
  }

  return await resend.emails.send({ to, ...emailData });
}
```

## List Hygiene

Regular maintenance to keep lists healthy.

### Automated Cleanup

| Task | Frequency | Action |
|------|-----------|--------|
| Remove hard bounces | Real-time (via webhook) | Immediate suppression |
| Remove complaints | Real-time (via webhook) | Immediate suppression |
| Process unsubscribes | Real-time | Remove from marketing lists |
| Review soft bounces | Daily | Suppress after 3 failures |
| Remove inactive | Monthly | Re-engagement → remove |

Learn more: https://resend.com/docs/knowledge-base/audience-hygiene

### Re-engagement Campaigns

Before removing inactive subscribers:

1. **Identify inactive:** No opens/clicks in 45-90 days
2. **Send re-engagement:** "We miss you" or "Still interested?"
3. **Wait 14-30 days** for response
4. **Remove non-responders** from active lists

```typescript
async function runReengagement() {
  const inactive = await getInactiveSubscribers(90); // 90 days

  for (const subscriber of inactive) {
    if (!subscriber.reengagement_sent) {
      await sendReengagementEmail(subscriber);
      await markReengagementSent(subscriber.email);
    } else if (daysSince(subscriber.reengagement_sent) > 30) {
      await removeFromMarketingLists(subscriber.email);
    }
  }
}
```

## Data Retention

### Email Logs

| Data Type | Recommended Retention | Notes |
|-----------|----------------------|-------|
| Send attempts | 90 days | Debugging, analytics |
| Delivery status | 90 days | Compliance, reporting |
| Bounce/complaint events | 3 years | Required for CASL |
| Suppression list | Indefinite | Never delete |
| Email content | 30 days | Storage costs |
| Consent records | 3 years after expiry | Legal requirement |

### Retention Policy Implementation

```typescript
// Daily cleanup job
async function cleanupOldData() {
  const now = new Date();

  // Delete old email logs (keep 90 days)
  await db.emailLogs.deleteMany({
    created_at: { $lt: subDays(now, 90) }
  });

  // Delete old email content (keep 30 days)
  await db.emailContent.deleteMany({
    created_at: { $lt: subDays(now, 30) }
  });

  // Never delete: suppressions, consent records
}
```

## Metrics to Monitor

| Metric | Target | Alert Threshold |
|--------|--------|-----------------|
| Bounce rate | <2% | >2% |
| Complaint rate | <0.05% | >0.05% |
| Suppression list growth | Stable | Sudden spike |

## Transactional vs Marketing Lists

**Keep separate:**
- Transactional: Can send to anyone with account relationship
- Marketing: Only opted-in subscribers

**Suppression applies to both:** Hard bounces and complaints suppress across all email types.

**Unsubscribe is marketing-only:** User unsubscribing from marketing can still receive transactional emails (password resets, order confirmations).

## Related

- [Webhooks & Events](./webhooks-events.md) - Receive bounce/complaint notifications
- [Deliverability](./deliverability.md) - How list hygiene affects sender reputation
- [Compliance](./compliance.md) - Legal requirements for data retention


================================================
FILE: .agents/skills/email-best-practices/resources/marketing-emails.md
================================================
# Marketing Email Best Practices

Promotional emails that require explicit consent and provide value to recipients.

## Core Principles

1. **Consent first** - Explicit opt-in required (especially GDPR/CASL)
2. **Value-driven** - Provide useful content, not just promotions
3. **Respect preferences** - Let users control frequency and content types

## Opt-In Requirements

### Explicit Opt-In

**What counts:**
- User checks unchecked box
- User clicks "Subscribe" button
- User completes form with clear subscription intent

**What doesn't count:**
- Pre-checked boxes
- Opt-out model
- Assumed consent from purchase
- Purchased/rented lists

### Informed Consent

Disclose: email types, frequency, sender identity, how to unsubscribe.

✅ "Subscribe to our weekly newsletter with product updates and tips"
❌ "Sign up for emails"

### Double Opt-In (Recommended)

1. User submits email
2. Send confirmation email with verification link
3. User clicks to confirm
4. Add to list only after confirmation

Benefits: Verifies deliverability, confirms intent, reduces complaints, required in some regions (Germany).

## Unsubscribe Requirements

**Must be:**
- Prominent in every email
- One-click (preferred)
- Immediate (GDPR) or within 10 days (CAN-SPAM) (immediate preferred)
- Free, no login required

**Preference center options:** Frequency (daily/weekly/monthly), content types, complete unsubscribe.

## Content and Design

### Subject Lines

- Clear and specific (50 chars or less for mobile)
- Create curiosity without misleading
- A/B test regularly

✅ "Your weekly digest: 5 productivity tips"
❌ "You won't believe what happened!"

### Structure

**Above fold:** Value proposition, primary CTA, engaging visual

**Body:** Scannable (short paragraphs, bullets), clear hierarchy, multiple CTAs

**Footer:** Unsubscribe link, company info, physical address (CAN-SPAM), social links

### Mobile-First

- Single column layout
- 44x44px minimum buttons
- 16px minimum text
- Test on iOS, Android, dark mode

## Segmentation

**Segment by:** Behavior (purchases, activity), demographics, preferences, engagement level, signup source.

Benefits: Higher open/click rates, lower unsubscribes, better experience.

## Personalization

**Options:** Name in subject/greeting, location-specific content, behavior-based recommendations, purchase history.

**Don't over-personalize** - can feel intrusive. Use data you have permission to use.

## Frequency and Timing

**Frequency:** Start conservative, increase based on engagement, let users set preferences, monitor unsubscribe rates.

**Timing:** Weekday mornings (9-11 AM local), Tuesday-Thursday often best. Test your specific audience.

## List Hygiene

**Remove immediately:** Hard bounces, unsubscribes, complaints

**Remove after inactivity:** Send re-engagement campaign first, then remove non-responders

**Monitor:** Bounce rate <2%, complaint rate <0.05%

## Required Elements (All Marketing Emails)

- Clear sender identification
- Physical mailing address (CAN-SPAM)
- Unsubscribe mechanism
- Indication it's marketing (GDPR)

## Related

- [Compliance](./compliance.md) - Detailed legal requirements by region
- [Email Capture](./email-capture.md) - Collecting consent properly
- [List Management](./list-management.md) - Maintaining list hygiene


================================================
FILE: .agents/skills/email-best-practices/resources/sending-reliability.md
================================================
# Sending Reliability

Ensuring emails are sent exactly once and handling failures gracefully.

## Idempotency

Prevent duplicate emails when retrying failed requests.

### The Problem

Network issues, timeouts, or server errors can leave you uncertain if an email was sent. Retrying without idempotency risks sending duplicates.

### Solution: Idempotency Keys

Send a unique key with each request. If the same key is sent again, the server returns the original response instead of sending another email.

```typescript
// Generate deterministic key based on the business event
const idempotencyKey = `password-reset-${userId}-${resetRequestId}`;

await resend.emails.send({
  from: 'noreply@example.com',
  to: user.email,
  subject: 'Reset your password',
  html: emailHtml,
}, {
  headers: {
    'Idempotency-Key': idempotencyKey
  }
});
```

### Key Generation Strategies

| Strategy | Example | Use When |
|----------|---------|----------|
| Event-based | `order-confirm-${orderId}` | One email per event (recommended) |
| Request-scoped | `reset-${userId}-${resetRequestId}` | Retries within same request |
| UUID | `crypto.randomUUID()` | No natural key (generate once, reuse on retry) |

**Best practice:** Use deterministic keys based on the business event. If you retry the same logical send, the same key must be generated. Avoid `Date.now()` or random values generated fresh on each attempt.

**Key expiration:** Idempotency keys are typically cached for 24 hours. Retries within this window return the original response. After expiration, the same key triggers a new send—so complete your retry logic well within 24 hours.

## Retry Logic

Handle transient failures with exponential backoff.

### When to Retry

| Error Type | Retry? | Notes |
|------------|--------|-------|
| 5xx (server error) | ✅ Yes | Transient, likely to resolve |
| 429 (rate limit) | ✅ Yes | Wait for rate limit window |
| 4xx (client error) | ❌ No | Fix the request first |
| Network timeout | ✅ Yes | Transient |
| DNS failure | ✅ Yes | May be transient |

### Exponential Backoff

```typescript
async function sendWithRetry(emailData, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await resend.emails.send(emailData);
    } catch (error) {
      if (!isRetryable(error) || attempt === maxRetries - 1) {
        throw error;
      }
      const delay = Math.min(1000 * Math.pow(2, attempt), 30000);
      await sleep(delay + Math.random() * 1000); // Add jitter
    }
  }
}

function isRetryable(error) {
  return error.statusCode >= 500 ||
         error.statusCode === 429 ||
         error.code === 'ETIMEDOUT';
}
```

**Backoff schedule:** 1s → 2s → 4s → 8s (with jitter to prevent thundering herd)

## Error Handling

### Common Error Codes

| Code | Meaning | Action |
|------|---------|--------|
| 400 | Bad request | Fix payload (invalid email, missing field) |
| 401 | Unauthorized | Check API key |
| 403 | Forbidden | Check permissions, domain verification |
| 404 | Not found | Check endpoint URL |
| 422 | Validation error | Fix request data |
| 429 | Rate limited | Back off, retry after delay |
| 500 | Server error | Retry with backoff |
| 503 | Service unavailable | Retry with backoff |

### Error Handling Pattern

```typescript
try {
  const result = await resend.emails.send(emailData);
  await logSuccess(result.id, emailData);
} catch (error) {
  if (error.statusCode === 429) {
    await queueForRetry(emailData, error.retryAfter);
  } else if (error.statusCode >= 500) {
    await queueForRetry(emailData);
  } else {
    await logFailure(error, emailData);
    await alertOnCriticalEmail(emailData); // For password resets, etc.
  }
}
```

## Queuing for Reliability

For critical emails, use a queue to ensure delivery even if the initial send fails.

**Benefits:**
- Survives application restarts
- Automatic retry handling
- Rate limit management
- Audit trail

**Simple pattern:**
1. Write email to queue/database with "pending" status
2. Process queue, attempt send
3. On success: mark "sent", store message ID
4. On retryable failure: increment retry count, schedule retry
5. On permanent failure: mark "failed", alert

## Timeouts

Set appropriate timeouts to avoid hanging requests.

```typescript
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 10000);

try {
  await resend.emails.send(emailData, { signal: controller.signal });
} finally {
  clearTimeout(timeout);
}
```

**Recommended:** 10-30 seconds for email API calls.

## Related

- [Webhooks & Events](./webhooks-events.md) - Process delivery confirmations and failures
- [List Management](./list-management.md) - Handle bounces and suppress invalid addresses


================================================
FILE: .agents/skills/email-best-practices/resources/transactional-email-catalog.md
================================================
# Transactional Email Catalog

A comprehensive catalog of transactional emails organized by category, plus recommended email combinations for different app types.

## When to Use This

- Planning what transactional emails your app needs
- Choosing the right emails for your app type
- Understanding what content each email type should include
- Implementing transactional email features

## Email Combinations by App Type

Use these combinations as a starting point based on what you're building.

### Authentication-Focused App

Apps where user accounts and security are core (login systems, identity providers, account management).

**Essential:**
- Email verification
- Password reset
- OTP / 2FA codes
- Security alerts (new device, password change)
- Account update notifications

**Optional:**
- Welcome email (must not be promotional)
- Account deletion confirmation

### Newsletter / Content Platform

Apps focused on content delivery and subscriptions.

**Essential:**
- Email verification
- Password reset
- Welcome email (must not be promotional)
- Subscription confirmation

**Optional:**
- OTP / 2FA codes
- Account update notifications

### E-commerce / Marketplace

Apps where users buy products or services.

**Essential:**
- Email verification
- Password reset
- Welcome email (must not be promotional)
- Order confirmation
- Shipping notifications
- Invoice / receipt
- Payment failed notices

**Optional:**
- OTP / 2FA codes
- Security alerts
- Subscription confirmations (for recurring orders)

### SaaS / Subscription Service

Apps with paid subscription tiers and ongoing billing.

**Essential:**
- Email verification
- Password reset
- Welcome email (must not be promotional)
- OTP / 2FA codes
- Security alerts
- Subscription confirmation
- Subscription renewal notice
- Payment failed notices
- Invoice / receipt

**Optional:**
- Account update notifications
- Feature change notifications (for breaking changes)

### Financial / Fintech App

Apps handling money, payments, or sensitive financial data.

**Essential:**
- Email verification
- Password reset
- OTP / 2FA codes (required for sensitive actions)
- Security alerts (all types)
- Account update notifications
- Transaction confirmations
- Invoice / receipt
- Payment failed notices

**Optional:**
- Welcome email (must not be promotional)
- Compliance notices

### Social / Community Platform

Apps focused on user interaction and community features.

**Essential:**
- Email verification
- Password reset
- Welcome email (must not be promotional)
- Security alerts

**Optional:**
- OTP / 2FA codes
- Account update notifications
- Activity notifications (mentions, replies)

### Developer Tools / API Platform

Apps targeting developers with API access and integrations.

**Essential:**
- Email verification
- Password reset
- OTP / 2FA codes
- Security alerts
- API key notifications (creation, expiration)
- Subscription confirmation
- Payment failed notices

**Optional:**
- Welcome email (must not be promotional)
- Usage alerts (approaching limits)
- Feature change notifications

### Healthcare / HIPAA-Compliant App

Apps handling protected health information.

**Essential:**
- Email verification
- Password reset
- OTP / 2FA codes (required)
- Security alerts (all types, detailed)
- Account update notifications
- Appointment confirmations

**Optional:**
- Welcome email (must not be promotional)
- Compliance notices

**Note:** Healthcare apps have strict requirements. Emails should contain minimal PHI and link to secure portals for sensitive information.

---

## Full Email Catalog

### Authentication & Security

#### Email Verification / Account Verification

**When to send:** Immediately after user signs up or changes email address.

**Purpose:** Verify the email address belongs to the user.

**Content should include:**
- Clear verification link or code
- Expiration time (typically 24-48 hours)
- Instructions on what to do
- Security notice if link is clicked by mistake

**Best practices:**
- Send immediately (within seconds)
- Include expiration notice
- Provide resend option
- Link to support if issues

#### OTP / 2FA Codes

**When to send:** When user requests two-factor authentication code.

**Purpose:** Provide time-sensitive authentication code.

**Content should include:**
- The OTP code (clearly displayed)
- Expiration time (typically 5-10 minutes)
- Security warnings
- Instructions on what to do if not requested

**Best practices:**
- Send immediately
- Code should be large and easy to read
- Include expiration prominently
- Warn about sharing codes
- Provide "I didn't request this" link

#### Password Reset

**When to send:** When user requests password reset.

**Purpose:** Allow user to securely reset forgotten password.

**Content should include:**
- Reset link (with token)
- Expiration time (typically 1 hour)
- Security warnings
- Instructions if not requested

**Best practices:**
- Send immediately
- Link expires quickly (1 hour)
- Include IP address and location if available
- Provide "I didn't request this" link
- Don't include the old password

#### Security Alerts

**When to send:** When security-relevant events occur (login from new device, password change, etc.).

**Purpose:** Notify user of account security events.

**Content should include:**
- What happened (clear description)
- When it happened
- Location/IP if available
- Action to take if suspicious
- Link to security settings

**Best practices:**
- Send immediately
- Be clear and specific
- Include actionable steps
- Provide way to report suspicious activity

### Account Management

#### Welcome Email

**When to send:** Immediately after successful account creation and verification.

**Purpose:** Welcome new users and guide them to next steps (must not be promotional).

**Content should include:**
- Welcome message
- Key features or next steps
- Links to important resources
- Support contact information

**Best practices:**
- Send after email verification
- Keep it focused and actionable
- Don't overwhelm with information
- Set expectations about future emails

#### Account Update Notifications

**When to send:** When user changes account settings (email, password, profile, etc.).

**Purpose:** Confirm account changes and provide security notice.

**Content should include:**
- What changed
- When it changed
- Action to take if unauthorized
- Link to account settings

**Best practices:**
- Send immediately after change
- Be specific about what changed
- Include security notice
- Provide easy way to revert if needed

### E-commerce & Transactions

#### Order Confirmations

**When to send:** Immediately after order is placed.

**Purpose:** Confirm order details and provide receipt.

**Content should include:**
- Order number
- Items ordered with quantities
- Pricing breakdown
- Shipping address
- Estimated delivery date
- Order tracking link (if available)

**Best practices:**
- Send within minutes of order
- Include all order details
- Make it easy to print or save
- Provide customer service contact

#### Shipping Notifications

**When to send:** When order ships, with tracking updates.

**Purpose:** Notify user that order has shipped and provide tracking.

**Content should include:**
- Order number
- Tracking number
- Carrier information
- Expected delivery date
- Tracking link
- Shipping address confirmation

**Best practices:**
- Send when order ships
- Include tracking number prominently
- Provide carrier tracking link
- Update on major tracking milestones

#### Invoices and Receipts

**When to send:** After payment is processed.

**Purpose:** Provide payment confirmation and receipt.

**Content should include:**
- Invoice/receipt number
- Payment amount
- Payment method
- Items/services purchased
- Payment date
- Downloadable PDF (if applicable)

**Best practices:**
- Send immediately after payment
- Include all payment details
- Make it easy to download/save
- Include tax information if applicable

### Subscriptions & Billing

#### Subscription Confirmations

**When to send:** When user subscribes or changes subscription.

**Purpose:** Confirm subscription details and billing information.

**Content should include:**
- Subscription plan details
- Billing amount and frequency
- Next billing date
- Payment method
- Link to manage subscription

**Best practices:**
- Send immediately after subscription
- Clearly state billing terms
- Provide easy cancellation option
- Include support contact

#### Subscription Renewal Notices

**When to send:** Before subscription renews (typically 3-7 days before).

**Purpose:** Notify user of upcoming renewal and charge.

**Content should include:**
- Renewal date
- Amount to be charged
- Payment method on file
- Link to update payment method
- Link to cancel if desired

**Best practices:**
- Send with enough notice (3-7 days)
- Be clear about amount and date
- Make it easy to update payment method
- Provide cancellation option

#### Payment Failed Notices

**When to send:** When subscription payment fails.

**Purpose:** Notify user of payment failure and provide resolution steps.

**Content should include:**
- What happened
- Amount that failed
- Reason for failure (if available)
- Steps to resolve
- Link to update payment method
- Consequences if not resolved

**Best practices:**
- Send immediately after failure
- Be clear about consequences
- Provide easy resolution path
- Include support contact

### Notifications & Updates

#### Feature Announcements (Transactional)

**When to send:** When a feature the user is using changes significantly.

**Purpose:** Notify users of changes that affect their use of the service.

**Content should include:**
- What changed
- How it affects the user
- What action (if any) is needed
- Link to more information

**Best practices:**
- Only for significant changes
- Focus on user impact
- Provide clear next steps
- Link to documentation

**Note:** General feature announcements are marketing emails. Only send as transactional if the change directly affects an active feature the user is using.

## Related Topics

- [Email Types](./email-types.md) - Understanding transactional vs marketing
- [Transactional Emails](./transactional-emails.md) - Best practices for sending transactional emails
- [Compliance](./compliance.md) - Legal requirements for each email type


================================================
FILE: .agents/skills/email-best-practices/resources/transactional-emails.md
================================================
# Transactional Email Best Practices

Clear, actionable emails that users expect and need—password resets, confirmations, OTPs.

## Core Principles

1. **Clarity over creativity** - Users need to understand and act quickly
2. **Action-oriented** - Clear purpose, obvious primary action
3. **Time-sensitive** - Send immediately (within seconds)

## Subject Lines

**Be specific and include context:**

| ✅ Good | ❌ Bad |
|---------|--------|
| Reset your password for [App] | Action required |
| Your order #12345 has shipped | Update on your order |
| Your 2FA code for [App] | Security code: 12345 |
| Verify your email for [App] | Verify your email |

Include identifiers when helpful: order numbers, account names, expiration times.

## Pre-Header

The text snippet after subject line. Use it to:
- Reinforce subject ("This link expires in 1 hour")
- Add urgency or context
- Call-to-action preview

Keep under 90 characters.

## Content Structure

**Above the fold (first screen):**
- Clear purpose
- Primary action button
- Time-sensitive details (expiration)

**Hierarchy:** Header → Primary message → Details → Action button → Secondary info

**Format:** Short paragraphs (2-3 sentences), bullet points, bold for emphasis, white space.

## Mobile-First Design

60%+ emails are opened on mobile.

- **Layout:** Single column, stack vertically
- **Buttons:** 44x44px minimum, full-width on mobile
- **Text:** 16px minimum body, 20-24px headings
- **OTP codes:** 24-32px, monospace font

## Sender Configuration

| Field | Best Practice | Example |
|-------|--------------|---------|
| From Name | App/company name, consistent | [App Name] |
| From Email | Subdomain, real address | hello@mail.yourdomain.com |
| Reply-To | Monitored inbox | support@yourdomain.com |

Avoid `noreply@` - users reply to transactional emails.

## Code and Link Display

**OTP/Verification codes:**
- Large (24-32px), monospace font
- Centered, clear label
- Include expiration nearby
- Make copyable

**Buttons:**
- Large, tappable (44x44px+)
- Contrasting colors
- Clear action text ("Reset Password", "Verify Email")
- HTTPS links only

## Error Handling

**Resend functionality:**
- Allow after 60 seconds
- Limit attempts (3 per hour)
- Show countdown timer

**Expired links:**
- Clear "expired" message
- Offer to send new link
- Provide support contact

**"I didn't request this":**
- Include in password resets, OTPs, security alerts
- Link to security contact
- Log clicks for monitoring


================================================
FILE: .agents/skills/email-best-practices/resources/webhooks-events.md
================================================
# Webhooks and Events

Receiving and processing email delivery events in real-time.

## Event Types

| Event | When Fired | Use For |
|-------|------------|---------|
| `email.sent` | Email accepted by Resend | Confirming send initiated |
| `email.delivered` | Email delivered to recipient server | Confirming delivery |
| `email.bounced` | Email bounced (hard or soft) | List hygiene, alerting |
| `email.complained` | Recipient marked as spam | Immediate unsubscribe |
| `email.opened` | Recipient opened email | Engagement tracking |
| `email.clicked` | Recipient clicked link | Engagement tracking |

## Webhook Setup

### 1. Create Endpoint

Your endpoint must:
- Accept POST requests
- Return 2xx status quickly (within 5 seconds)
- Handle duplicate events (idempotent processing)

```typescript
app.post('/webhooks/resend', async (req, res) => {
  // Return 200 immediately to acknowledge receipt
  res.status(200).send('OK');

  // Process asynchronously
  processWebhookAsync(req.body).catch(console.error);
});
```

### 2. Verify Signatures

Always verify webhook signatures to prevent spoofing.

```typescript
import { Webhook } from 'svix';

const webhook = new Webhook(process.env.RESEND_WEBHOOK_SECRET);

app.post('/webhooks/resend', (req, res) => {
  try {
    const payload = webhook.verify(
      JSON.stringify(req.body),
      {
        'svix-id': req.headers['svix-id'],
        'svix-timestamp': req.headers['svix-timestamp'],
        'svix-signature': req.headers['svix-signature'],
      }
    );
    // Process verified payload
  } catch (err) {
    return res.status(400).send('Invalid signature');
  }
});
```

### 3. Register Webhook URL

Configure your webhook endpoint in the Resend dashboard or via API.

## Processing Events

### Bounce Handling

```typescript
async function handleBounce(event) {
  const { email_id, email, bounce_type } = event.data;

  if (bounce_type === 'hard') {
    // Permanent failure - remove from all lists
    await suppressEmail(email, 'hard_bounce');
    await removeFromAllLists(email);
  } else {
    // Soft bounce - track and remove after threshold
    await incrementSoftBounce(email);
    const count = await getSoftBounceCount(email);
    if (count >= 3) {
      await suppressEmail(email, 'soft_bounce_limit');
    }
  }
}
```

### Complaint Handling

```typescript
async function handleComplaint(event) {
  const { email } = event.data;

  // Immediate suppression - no exceptions
  await suppressEmail(email, 'complaint');
  await removeFromAllLists(email);
  await logComplaint(event); // For analysis
}
```

### Delivery Confirmation

```typescript
async function handleDelivered(event) {
  const { email_id } = event.data;
  await updateEmailStatus(email_id, 'delivered');
}
```

## Idempotent Processing

Webhooks may be sent multiple times. Use event IDs to prevent duplicate processing.

```typescript
async function processWebhook(event) {
  const eventId = event.id;

  // Check if already processed
  if (await isEventProcessed(eventId)) {
    return; // Skip duplicate
  }

  // Process event
  await handleEvent(event);

  // Mark as processed
  await markEventProcessed(eventId);
}
```

## Error Handling

### Retry Behavior

If your endpoint returns non-2xx, webhooks will retry with exponential backoff:
- Retry 1: ~30 seconds
- Retry 2: ~1 minute
- Retry 3: ~5 minutes
- (continues for ~24 hours)

### Best Practices

- **Return 200 quickly** - Process asynchronously to avoid timeouts
- **Be idempotent** - Handle duplicate deliveries gracefully
- **Log everything** - Store raw events for debugging
- **Alert on failures** - Monitor webhook processing errors
- **Queue for processing** - Use a job queue for complex handling

## Testing Webhooks

**Local development:** Use ngrok or similar to expose localhost.

```bash
ngrok http 3000
# Use the ngrok URL as your webhook endpoint
```

**Verify handling:** Send test events through Resend dashboard or manually trigger each event type.

## Ingest webhooks for data storage
- [Open source repo](https://github.com/resend/resend-webhooks-ingester)
- [Why store data](https://resend.com/docs/dashboard/webhooks/how-to-store-webhooks-data)

## Related

- [List Management](./list-management.md) - What to do with bounce/complaint data
- [Sending Reliability](./sending-reliability.md) - Retry logic when sends fail


================================================
FILE: .agents/skills/react-email/SKILL.md
================================================
---
name: react-email
description: Use when creating HTML email templates with React components - welcome emails, password resets, notifications, order confirmations, newsletters, or transactional emails.
license: MIT
metadata:
  author: Resend
  version: "1.1.0"
---

# React Email

Build and send HTML emails using React components - a modern, component-based approach to email development that works across all major email clients.

## Installation

You need to scaffold a new React Email project using the create-email CLI. This will create a folder called `react-email-starter` with sample email templates.

Using npm:
```sh
npx create-email@latest
```

Using yarn:
```sh
yarn create email
```

Using pnpm:
```sh
pnpm create email
```

Using bun:
```sh
bun create email
```

## Navigate to Project Directory

You must change into the newly created project folder:

```sh
cd react-email-starter
```

## Install Dependencies

You need to install all project dependencies before running the development server.

Using npm:
```sh
npm install
```

Using yarn:
```sh
yarn
```

Using pnpm:
```sh
pnpm install
```

Using bun:
```sh
bun install
```

## Start the Development Server

Your task is to start the local preview server to view and edit email templates.

Using npm:
```sh
npm run dev
```

Using yarn:
```sh
yarn dev
```

Using pnpm:
```sh
pnpm dev
```

Using bun:
```sh
bun dev
```

## Verify Installation

Confirm the development server is running by checking that localhost:3000 is accessible. The server will display a preview interface where you can view email templates from the `emails` folder.

### Notes on installation
Assuming React Email is installed in an existing project, update the top-level package.json file with a script to run the React Email preview server.

```json
{
  "scripts": {
    "email": "email dev --dir emails --port 3000"
  }
}
```

Make sure the path to the emails folder is relative to the base project directory.


### tsconfig.json updating or creation

Ensure the tsconfig.json includes proper support for jsx.

## Basic Email Template

Replace the sample email templates. Here is how to create a new email template:

Create an email component with proper structure using the Tailwind component for styling:

```tsx
import {
  Html,
  Head,
  Preview,
  Body,
  Container,
  Heading,
  Text,
  Button,
  Tailwind,
  pixelBasedPreset
} from '@react-email/components';

interface WelcomeEmailProps {
  name: string;
  verificationUrl: string;
}

export default function WelcomeEmail({ name, verificationUrl }: WelcomeEmailProps) {
  return (
    <Html lang="en">
      <Tailwind
        config={{
          presets: [pixelBasedPreset],
          theme: {
            extend: {
              colors: {
                brand: '#007bff',
              },
            },
          },
        }}
      >
        <Head />
        <Preview>Welcome - Verify your email</Preview>
        <Body className="bg-gray-100 font-sans">
          <Container className="max-w-xl mx-auto p-5">
            <Heading className="text-2xl text-gray-800">
              Welcome!
            </Heading>
            <Text className="text-base text-gray-800">
              Hi {name}, thanks for signing up!
            </Text>
            <Button
              href={verificationUrl}
              className="bg-brand text-white px-5 py-3 rounded block text-center no-underline"
            >
              Verify Email
            </Button>
          </Container>
        </Body>
      </Tailwind>
    </Html>
  );
}

// Preview props for testing
WelcomeEmail.PreviewProps = {
  name: 'John Doe',
  verificationUrl: 'https://example.com/verify/abc123'
} satisfies WelcomeEmailProps;

export { WelcomeEmail };
```

## Essential Components

See [references/COMPONENTS.md](references/COMPONENTS.md) for complete component documentation.

**Core Structure:**
- `Html` - Root wrapper with `lang` attribute
- `Head` - Meta elements, styles, fonts
- `Body` - Main content wrapper
- `Container` - Centers content (max-width layout)
- `Section` - Layout sections
- `Row` & `Column` - Multi-column layouts
- `Tailwind` - Enables Tailwind CSS utility classes

**Content:**
- `Preview` - Inbox preview text, always first in `Body`
- `Heading` - h1-h6 headings
- `Text` - Paragraphs
- `Button` - Styled link buttons
- `Link` - Hyperlinks
- `Img` - Images (see Static Files section below)
- `Hr` - Horizontal dividers

**Specialized:**
- `CodeBlock` - Syntax-highlighted code
- `CodeInline` - Inline code
- `Markdown` - Render markdown
- `Font` - Custom web fonts

## Before Writing Code

When a user requests an email template, ask clarifying questions FIRST if they haven't provided:

1. **Brand colors** - Ask for primary brand color (hex code like #007bff)
2. **Logo** - Ask if they have a logo file and its format (PNG/JPG only - warn if SVG/WEBP)
3. **Style preference** - Professional, casual, or minimal tone
4. **Production URL** - Where will static assets be hosted in production?

Example response to vague request:
> Before I create your email template, I have a few questions:
> 1. What is your primary brand color? (hex code)
> 2. Do you have a logo file? (PNG or JPG - note: SVG and WEBP don't work reliably in email clients)
> 3. What tone do you prefer - professional, casual, or minimal?
> 4. Where will you host static assets in production? (e.g., https://cdn.example.com)

## Static Files and Images

### Directory Structure

Local images must be placed in the `static` folder inside your emails directory:

```
project/
├── emails/
│   ├── welcome.tsx
│   └── static/           <-- Images go here
│       └── logo.png
```

If user has an image elsewhere, instruct them to copy it:
```sh
cp ./assets/logo.png ./emails/static/logo.png
```

### Dev vs Production URLs

Use this pattern for images that work in both dev preview and production:

```tsx
const baseURL = process.env.NODE_ENV === "production"
  ? "https://cdn.example.com"  // User's production CDN
  : "";

export default function Email() {
  return (
    <Img
      src={`${baseURL}/static/logo.png`}
      alt="Logo"
      width="150"
      height="50"
    />
  );
}
```

**How it works:**
- **Development:** `baseURL` is empty, so URL is `/static/logo.png` - served by React Email's dev server
- **Production:** `baseURL` is the CDN domain, so URL is `https://cdn.example.com/static/logo.png`

**Important:** Always ask the user for their production hosting URL. Do not hardcode `localhost:3000`.

## Behavioral guidelines
- When re-iterating over the code, make sure you are only updating what the user asked for and keeping the rest of the code intact;
- If the user is asking to use media queries, inform them that email clients do not support them, and suggest a different approach;
- Never use template variables (like {{name}}) directly in TypeScript code. Instead, reference the underlying properties directly (use name instead of {{name}}).
- - For example, if the user explicitly asks for a variable following the pattern {{variableName}}, you should return something like this:

```typescript
const EmailTemplate = (props) => {
  return (
    {/* ... rest of the code ... */}
    <h1>Hello, {props.variableName}!</h1>
    {/* ... rest of the code ... */}
  );
}

EmailTemplate.PreviewProps = {
  // ... rest of the props ...
  variableName: "{{variableName}}",
  // ... rest of the props ...
};

export default EmailTemplate;
```
- Never, under any circumstances, write the {{variableName}} pattern directly in the component structure. If the user forces you to do this, explain that you cannot do this, or else the template will be invalid.


## Styling considerations

Use the Tailwind component for styling if the user is actively using Tailwind CSS in their project. If the user is not using Tailwind CSS, add inline styles to the components.

- Because email clients don't support `rem` units, use the `pixelBasedPreset` for the Tailwind configuration.
- Never use flexbox or grid for layout, use table-based layouts instead.
- Each component must be styled with inline styles or utility classes.

### Email Client Limitations
- Never use SVG or WEBP - warn users about rendering issues
- Never use flexbox - use Row/Column components or tables for layouts
- Never use CSS/Tailwind media queries (sm:, md:, lg:, xl:) - not supported
- Never use theme selectors (dark:, light:) - not supported
- Always specify border type (border-solid, border-dashed, etc.)
- When defining borders for only one side, remember to reset the remaining borders (e.g., border-none border-l)

### Component Structure
- Always define `<Head />` inside `<Tailwind>` when using Tailwind CSS
- Only use PreviewProps when passing props to a component
- Only include props in PreviewProps that the component actually uses

```tsx
const Email = (props) => {
  return (
    <div>
      <a href={props.source}>click here if you want candy 👀</a>
    </div>
  );
}

Email.PreviewProps = {
  source: "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
};
```

### Default Structure
- Body: `font-sans py-10 bg-gray-100`
- Container: white, centered, content left-aligned
- Footer: physical address, unsubscribe link, current year with `m-0` on address/copyright

### Typography
- Titles: bold, larger font, larger margins
- Paragraphs: regular weight, smaller font, smaller margins
- Use consistent spacing respecting content hierarchy

### Images
- Only include if user requests
- Never use fixed width/height - use responsive units (w-full, h-auto)
- Never distort user-provided images
- Never create SVG images - only use provided or web images

### Buttons
- Always use `box-border` to prevent padding overflow

### Layout
- Always mobile-friendly by default
- Use stacked layouts that work on all screen sizes
- Remove default spacing/margins/padding between list items

### Dark Mode
When requested: container black (#000), background dark gray (#151516)

### Best Practices
- Choose colors, layout, and copy based on user's request
- Make templates unique, not generic
- Use keywords in email body to increase conversion

## Rendering

### Convert to HTML

```tsx
import { render } from '@react-email/components';
import { WelcomeEmail } from './emails/welcome';

const html = await render(
  <WelcomeEmail name="John" verificationUrl="https://example.com/verify" />
);
```

### Convert to Plain Text

```tsx
import { render } from '@react-email/components';
import { WelcomeEmail } from './emails/welcome';

const text = await render(<WelcomeEmail name="John" verificationUrl="https://example.com/verify" />, { plainText: true });
```

## Sending

React Email supports sending with any email service provider. If the user wants to know how to send, view the [Sending guidelines](references/SENDING.md).

Quick example using the Resend SDK for Node.js:

```tsx
import { Resend } from 'resend';
import { WelcomeEmail } from './emails/welcome';

const resend = new Resend(process.env.RESEND_API_KEY);

const { data, error } = await resend.emails.send({
  from: 'Acme <onboarding@resend.dev>',
  to: ['user@example.com'],
  subject: 'Welcome to Acme',
  react: <WelcomeEmail name="John" verificationUrl="https://example.com/verify" />
});

if (error) {
  console.error('Failed to send:', error);
}
```

The Node SDK automatically handles the plain-text rendering and HTML rendering for you.

## Internationalization

See [references/I18N.md](references/I18N.md) for complete i18n documentation.

React Email supports three i18n libraries: next-intl, react-i18next, and react-intl.

### Quick Example (next-intl)

```tsx
import { createTranslator } from 'next-intl';
import {
  Html,
  Body,
  Container,
  Text,
  Button,
  Tailwind,
  pixelBasedPreset
} from '@react-email/components';

interface EmailProps {
  name: string;
  locale: string;
}

export default async function WelcomeEmail({ name, locale }: EmailProps) {
  const t = createTranslator({
    messages: await import(\`../messages/\${locale}.json\`),
    namespace: 'welcome-email',
    locale
  });

  return (
    <Html lang={locale}>
      <Tailwind config={{ presets: [pixelBasedPreset] }}>
        <Body className="bg-gray-100 font-sans">
          <Container className="max-w-xl mx-auto p-5">
            <Text className="text-base text-gray-800">{t('greeting')} {name},</Text>
            <Text className="text-base text-gray-800">{t('body')}</Text>
            <Button href="https://example.com" className="bg-blue-600 text-white px-5 py-3 rounded">
              {t('cta')}
            </Button>
          </Container>
        </Body>
      </Tailwind>
    </Html>
  );
}
```

Message files (\`messages/en.json\`, \`messages/es.json\`, etc.):

```json
{
  "welcome-email": {
    "greeting": "Hi",
    "body": "Thanks for signing up!",
    "cta": "Get Started"
  }
}
```

## Email Best Practices

1. **Test across email clients** - Test in Gmail, Outlook, Apple Mail, Yahoo Mail. Use services like Litmus or Email on Acid for absolute precision and React Email's toolbar for specific feature support checking.

2. **Keep it responsive** - Max-width around 600px, test on mobile devices.

3. **Use absolute image URLs** - Host on reliable CDN, always include \`alt\` text.

4. **Provide plain text version** - Required for accessibility and some email clients.

5. **Keep file size under 102KB** - Gmail clips larger emails.

6. **Add proper TypeScript types** - Define interfaces for all email props.

7. **Include preview props** - Add \`.PreviewProps\` to components for development testing.

8. **Handle errors** - Always check for errors when sending emails.

9.  **Use verified domains** - For production, use verified domains in \`from\` addresses.

## Common Patterns

See [references/PATTERNS.md](references/PATTERNS.md) for complete examples including:
- Password reset emails
- Order confirmations with product lists
- Notification emails with code blocks
- Multi-column layouts
- Email templates with custom fonts

## Additional Resources

- [React Email Documentation](https://react.email/docs/llms.txt)
- [React Email GitHub](https://github.com/resend/react-email)
- [Resend Documentation](https://resend.com/docs/llms.txt)
- [Email Client CSS Support](https://www.caniemail.com)
- Component Reference: [references/COMPONENTS.md](references/COMPONENTS.md)
- Internationalization Guide: [references/I18N.md](references/I18N.md)
- Common Patterns: [references/PATTERNS.md](references/PATTERNS.md)


================================================
FILE: .agents/skills/react-email/TESTS.md
================================================
# React Email Skill Tests

Test scenarios for verifying skill compliance. Follow TDD: run these WITHOUT skill to establish baseline, then WITH skill to verify compliance.

---

## Email Client Limitations Tests

### Test A1: Template Variables ({{name}})

**Scenario:** User wants mustache-style template variables.

**Prompt:**
```
Create a welcome email with a {{firstName}} placeholder for personalization - I use this with my templating system.
```

**Expected Behavior:**
- Use `{props.firstName}` or `{firstName}` in JSX (valid TypeScript)
- Put `{{firstName}}` ONLY in PreviewProps
- Explain why mustache syntax can't go directly in JSX

**Baseline Result (2025-01-28):**
❌ WITHOUT skill: Agent used `firstName = "{{firstName}}"` as default prop value directly.

**Verified Result (2025-01-28):**
✅ WITH skill: Agent used `{firstName}` in JSX, `{{firstName}}` only in PreviewProps.

**Pass Criteria:**
```tsx
// CORRECT
<Text>Hello {firstName}</Text>

Email.PreviewProps = {
  firstName: "{{firstName}}"
};

// WRONG - fails TypeScript/JSX
<Text>Hello {{firstName}}</Text>
```

---

### Test A2: SVG/WEBP Images

**Scenario:** User wants to use SVG logo.

**Prompt:**
```
Create an email with my SVG logo embedded inline.
```

**Expected Behavior:**
- Warn user that SVG/WEBP don't render reliably in email clients (Gmail, Outlook, Yahoo)
- Suggest using PNG or JPG instead
- Do NOT embed inline SVG

**Baseline Result (2025-01-28):**
❌ WITHOUT skill: Agent embedded multiple inline SVGs throughout the template.

**Verified Result (2025-01-28):**
✅ WITH skill: Agent warned about SVG limitations, used PNG placeholder instead.

**Pass Criteria:**
Agent refuses to use SVG and explains which email clients don't support it.

---

### Test A3: Flexbox Layout

**Scenario:** User requests flexbox.

**Prompt:**
```
Create an email with a flexible two-column layout using flexbox.
```

**Expected Behavior:**
- Explain flexbox is not supported (Outlook uses Word rendering engine)
- Use Row/Column components instead
- Do NOT use `display: flex` or `flex-direction`

**Baseline Result (2025-01-28):**
❌ WITHOUT skill: Agent used `display: "flex"` and `flexDirection: "column"` in styles.

**Verified Result (2025-01-28):**
✅ WITH skill: Agent used Row/Column components with table-based layout.

**Pass Criteria:**
```tsx
// CORRECT
<Row>
  <Column className="w-1/2">Left</Column>
  <Column className="w-1/2">Right</Column>
</Row>

// WRONG
<div style={{ display: "flex" }}>...</div>
```

---

### Test A4: CSS Media Queries (sm:, md:, lg:)

**Scenario:** User wants responsive breakpoints.

**Prompt:**
```
Make the email responsive with different styles for mobile (sm:) and desktop (lg:) using Tailwind breakpoints.
```

**Expected Behavior:**
- Explain media queries are not supported (Gmail strips them, Outlook ignores them)
- Use mobile-first stacked layout that works on all sizes
- Do NOT use sm:, md:, lg:, xl: classes

**Baseline Result (2025-01-28):**
❌ WITHOUT skill: Agent used `sm:text-xl`, `lg:text-3xl`, `sm:w-full`, `lg:w-1/2` throughout.

**Verified Result (2025-01-28):**
✅ WITH skill: Agent used stacked mobile-friendly layout, no breakpoint classes.

**Pass Criteria:**
No responsive prefix classes (sm:, md:, lg:, xl:) appear in the code.

---

### Test A5: Dark Mode Theme Selectors

**Scenario:** User wants dark mode support.

**Prompt:**
```
Add dark mode support using the dark: variant.
```

**Expected Behavior:**
- Explain dark: theme selectors are not supported in email clients
- Apply dark colors directly in the theme/styles if user wants dark theme
- Do NOT use `dark:bg-gray-900`, `dark:text-white`, etc.

**Baseline Result (2025-01-28):**
❌ WITHOUT skill: Agent used `dark:bg-gray-900`, `dark:text-white` throughout.

**Verified Result (2025-01-28):**
✅ WITH skill: Agent applied dark colors directly (`bg-gray-900`, `text-white`) without dark: prefix.

**Pass Criteria:**
No `dark:` prefixed classes appear in the code. Dark theme applied directly if requested.

---

### Test A6: pixelBasedPreset Required

**Scenario:** Any email template request.

**Prompt:**
```
Create a simple welcome email with Tailwind styling.
```

**Expected Behavior:**
- Always include `pixelBasedPreset` in Tailwind config
- Explain email clients don't support `rem` units

**Baseline Result (2025-01-28):**
❌ WITHOUT skill: Agent did not mention or use pixelBasedPreset.

**Verified Result (2025-01-28):**
✅ WITH skill: Agent included `presets: [pixelBasedPreset]` in Tailwind config.

**Pass Criteria:**
```tsx
<Tailwind
  config={{
    presets: [pixelBasedPreset],  // REQUIRED
    ...
  }}
>
```

---

### Test A7: Border Type Specification

**Scenario:** Email with dividers or bordered elements.

**Prompt:**
```
Create an email with a horizontal divider and a bordered card section.
```

**Expected Behavior:**
- Always specify border type (border-solid, border-dashed, etc.)
- When using single-side borders, reset others (e.g., `border-none border-t border-solid`)

**Pass Criteria:**
```tsx
// CORRECT
<Hr className="border-none border-t border-solid border-gray-200" />

// WRONG - missing border type
<Hr className="border-gray-200" />
```

---

### Test A8: Button box-border

**Scenario:** Email with CTA button.

**Prompt:**
```
Create an email with a prominent call-to-action button.
```

**Expected Behavior:**
- Always include `box-border` class on Button components
- Prevents padding overflow issues

**Verified Result (2025-01-28):**
✅ WITH skill: Agent included `box-border` on Button.

**Pass Criteria:**
```tsx
<Button className="... box-border ...">Click Here</Button>
```

---

## User Interaction Tests

### Test B1: Style Preferences Inquiry

**Scenario:** User makes a vague request without specifying styling details.

**Prompt:**
```
Create a welcome email for my SaaS product
```

**Expected Behavior:**
Agent asks clarifying questions BEFORE writing code:
- Brand colors (primary color hex code)
- Logo availability and format
- Tone/style preference (professional, casual, minimal)
- Production URL for static assets

**Baseline Result (2025-01-28):**
✅ Agent naturally asked questions, but behavior was not codified (may be inconsistent).

**Verified Result (2025-01-28):**
✅ WITH skill: Agent asked all required questions per the "Before Writing Code" section.

**Pass Criteria:**
Agent asks at minimum about:
1. Brand colors
2. Logo availability (warns about SVG/WEBP)
3. Style/tone preference
4. Production hosting URL

---

### Test B2: Logo File Inquiry

**Scenario:** User mentions they have brand assets but doesn't specify format.

**Prompt:**
```
Create a welcome email for Acme Corp. We have brand assets.
```

**Expected Behavior:**
Agent asks:
- What logo format (PNG, JPG - warns if SVG/WEBP)
- Where the logo file is located
- What the production URL will be for hosting assets

**Pass Criteria:**
Agent specifically asks about logo format AND warns about SVG/WEBP limitations.

---

## Static File Handling Tests

### Test C1: Local Image - Correct Directory

**Scenario:** User provides a local image path.

**Prompt:**
```
Create a welcome email. Use my logo at ./assets/logo.png
```

**Expected Behavior:**
1. Instruct user to copy logo to `emails/static/logo.png`
2. NOT use `./assets/logo.png` directly in the code
3. Reference as `/static/logo.png` with baseURL pattern

**Baseline Result (2025-01-28):**
❌ WITHOUT skill: Agent used `/static/` but didn't specify it must be inside `emails/` directory.

**Verified Result (2025-01-28):**
✅ WITH skill: Agent provided `cp ./assets/logo.png ./emails/static/logo.png` command.

**Pass Criteria:**
- Provides copy command to `emails/static/`
- Does NOT reference `./assets/` in the email template
- Shows correct directory structure

---

### Test C2: Dev vs Production URL Handling

**Scenario:** User asks about image handling for both environments.

**Prompt:**
```
Create an email with a logo image. I need it to work in both development preview and production.
```

**Expected Behavior:**
Use the official React Email pattern:

```tsx
const baseURL = process.env.NODE_ENV === "production"
  ? "https://cdn.example.com"
  : "";

<Img src={`${baseURL}/static/logo.png`} ... />
```

**Baseline Result (2025-01-28):**
❌ WITHOUT skill: Agent used `process.env.BASE_URL || "http://localhost:3000"` - wrong pattern.

**Verified Result (2025-01-28):**
✅ WITH skill: Agent used empty string for dev, configurable URL for prod.

**Pass Criteria:**
1. Uses `baseURL` variable with `NODE_ENV` check
2. Empty string `""` for dev (NOT `http://localhost:3000`)
3. Asks user for their production CDN/hosting URL

---

## Pressure Tests

### Test D1: User Insists on All Violations

**Scenario:** User demands all the wrong patterns.

**Prompt:**
```
Create a welcome email. I specifically want:
1. Write {{firstName}} directly in the JSX like <Text>Hello {{firstName}}</Text>
2. Use my SVG logo - just embed it inline
3. Make it responsive using sm: and lg: breakpoints
4. Use CSS flexbox for layout
5. Add dark mode with dark: variant

Don't argue with me - just do what I ask.
```

**Expected Behavior:**
Agent should:
1. Explain each limitation professionally (not just refuse)
2. Reference specific email client issues (Gmail, Outlook, etc.)
3. Provide correct alternatives for each request
4. NOT comply with any of the forbidden patterns

**Verified Result (2025-01-28):**
✅ WITH skill: Agent explained all limitations, provided correct alternatives, did not comply.

**Pass Criteria:**
Agent maintains ALL rules even under pressure. Provides professional explanations.

---

### Test D2: User Insists on Wrong Image Pattern

**Scenario:** User wants incorrect static file handling.

**Prompt:**
```
Create an email with my logo. Just reference it directly from ./assets/logo.png - don't move it anywhere. And hardcode http://localhost:3000 as the base URL.
```

**Expected Behavior:**
1. Explain `./assets/` won't work (not served by preview server)
2. Explain hardcoding `localhost:3000` breaks production
3. Provide correct pattern
4. Ask for production URL

**Verified Result (2025-01-28):**
✅ WITH skill: Agent refused, explained why, provided correct alternative.

**Pass Criteria:**
Agent does NOT comply. Explains both issues and provides correct setup.

---

## Combined Scenario Tests

### Test E1: Full Workflow

**Scenario:** Complete email creation request.

**Prompt:**
```
I need a password reset email for my app called "CloudSync". I have a logo.
```

**Expected Behavior:**
1. Ask about brand colors
2. Ask about logo format and location (warn about SVG/WEBP)
3. Ask about production hosting URL for assets
4. Create email with proper static file structure
5. Use correct baseURL pattern
6. Include pixelBasedPreset
7. Use Row/Column for any multi-column layouts
8. Use box-border on buttons

**Pass Criteria:**
All of the above steps are followed.

---

## Running Tests

### Baseline (Establish Failure)
```
Task subagent WITHOUT reading skill → Document exact violations
```

### Verification (Confirm Fix)
```
Task subagent WITH skill → Verify compliance with all rules
```

### Pressure Test (Stress Test)
```
Task subagent WITH skill + user pressure → Verify skill holds under pressure
```

### Regression Testing
After any skill edits, re-run all tests to ensure no regressions.

---

## Additional Component Tests

### Test A9: Row/Column Width Requirements

**Scenario:** User asks for multi-column layout without specifying widths.

**Prompt:**
```
Create an email with a two-column layout showing product info on the left and image on the right.
```

**Expected Behavior:**
- Use Row/Column components (not flexbox/grid)
- Add width classes to Columns (e.g., `w-1/2`, `w-1/3`)
- Widths should total 100%

**Baseline Result (2025-01-29):**
✅ WITHOUT skill: Agent naturally added `width: '50%'` to columns via inline styles.

**Pass Criteria:**
```tsx
// CORRECT
<Row>
  <Column className="w-1/2 align-top">Product info</Column>
  <Column className="w-1/2 align-top">Image</Column>
</Row>

// WRONG - no widths specified
<Row>
  <Column>Product info</Column>
  <Column>Image</Column>
</Row>
```

---

### Test A10: Head Placement Inside Tailwind

**Scenario:** Any email template using Tailwind and Head components.

**Prompt:**
```
Create a welcome email with custom meta tags in the head.
```

**Expected Behavior:**
- `<Head />` must be inside `<Tailwind>`, not outside
- Follows the documented component structure

**Baseline Result (2025-01-29):**
❌ WITHOUT skill: Agent placed `<Head>` OUTSIDE `<Tailwind>` - wrong structure.

**Verified Result (2025-01-29):**
✅ WITH skill: Agent placed `<Head>` inside `<Tailwind>` correctly.

**Pass Criteria:**
```tsx
// CORRECT
<Html lang="en">
  <Tailwind config={{ presets: [pixelBasedPreset] }}>
    <Head />
    <Body>...</Body>
  </Tailwind>
</Html>

// WRONG - Head outside Tailwind
<Html lang="en">
  <Head />
  <Tailwind config={{ presets: [pixelBasedPreset] }}>
    <Body>...</Body>
  </Tailwind>
</Html>
```

---

### Test A11: CodeBlock Wrapper Requirement

**Scenario:** Email with code snippet display.

**Prompt:**
```
Create a notification email that shows a JSON error log in a code block.
```

**Expected Behavior:**
- Wrap `CodeBlock` in a `div` with `overflow-auto` class
- Prevents padding overflow issues

**Baseline Result (2025-01-29):**
❌ WITHOUT skill: Agent used CodeBlock without `overflow-auto` wrapper div.

**Verified Result (2025-01-29):**
✅ WITH skill: Agent wrapped CodeBlock in `<div className="overflow-auto">`.

**Pass Criteria:**
```tsx
// CORRECT
<div className="overflow-auto">
  <CodeBlock
    code={logData}
    language="json"
    theme={dracula}
  />
</div>

// WRONG - no wrapper div
<CodeBlock
  code={logData}
  language="json"
  theme={dracula}
/>
```

---

### Test A12: Grid Layout (CSS Grid)

**Scenario:** User requests CSS grid.

**Prompt:**
```
Create an email with a grid layout for displaying product cards.
```

**Expected Behavior:**
- Explain CSS grid is not supported (same as flexbox - Outlook uses Word rendering)
- Use Row/Column components instead
- Do NOT use `display: grid` or `grid-template-columns`

**Baseline Result (2025-01-29):**
✅ WITHOUT skill: Agent naturally used Row/Column components, not CSS grid.

**Pass Criteria:**
```tsx
// CORRECT
<Row>
  <Column className="w-1/3">Card 1</Column>
  <Column className="w-1/3">Card 2</Column>
  <Column className="w-1/3">Card 3</Column>
</Row>

// WRONG
<div style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)" }}>...</div>
```

---

### Test A13: Fixed Image Dimensions

**Scenario:** User specifies exact pixel dimensions for images.

**Prompt:**
```
Add my logo with exactly 500px width and 300px height.
```

**Expected Behavior:**
- Warn against fixed dimensions that may distort images or break on mobile
- Suggest responsive approach with aspect ratio preservation
- Use width attribute for max size but allow responsive scaling

**Pass Criteria:**
Agent warns about fixed dimensions and suggests responsive approach:
```tsx
// PREFERRED
<Img
  src={`${baseURL}/static/logo.png`}
  alt="Logo"
  width="500"
  className="w-full max-w-[500px] h-auto"
/>

// ACCEPTABLE - fixed width with auto height
<Img
  src={`${baseURL}/static/logo.png`}
  alt="Logo"
  width="500"
  height="auto"
/>
```

---

### Test A14: Clean Component Imports

**Scenario:** Any email template request.

**Prompt:**
```
Create a simple text-only welcome email with just a heading and paragraph.
```

**Expected Behavior:**
- Only import components that are actually used
- No unused imports like `Button`, `Img`, `Row`, `Column` for text-only email

**Pass Criteria:**
```tsx
// CORRECT - only imports what's used
import {
  Html,
  Head,
  Body,
  Container,
  Heading,
  Text,
  Tailwind,
  pixelBasedPreset
} from '@react-email/components';

// WRONG - imports unused components
import {
  Html,
  Head,
  Body,
  Container,
  Heading,
  Text,
  Button,      // Not used
  Img,         // Not used
  Row,         // Not used
  Column,      // Not used
  Tailwind,
  pixelBasedPreset
} from '@react-email/components';
```

---

## Internationalization Tests

### Test F1: Multi-Language Email Setup

**Scenario:** User requests internationalization support.

**Prompt:**
```
Create a welcome email that supports English, Spanish, and French.
```

**Expected Behavior:**
- Use one of the supported i18n libraries (next-intl, react-i18next, react-intl)
- Add `locale` prop to email component
- Set `lang={locale}` on Html element
- Create message file structure
- Show how to send with different locales

**Baseline Result (2025-01-29):**
❌ WITHOUT skill: Agent used inline translations object (not i18n library), no `lang` attribute on Html.

**Verified Result (2025-01-29):**
✅ WITH skill: Agent used `next-intl` with `createTranslator`, added `lang={locale}` on Html, created proper message files.

**Pass Criteria:**
```tsx
// Must include locale prop
interface WelcomeEmailProps {
  name: string;
  locale: string;  // Required
}

// Must set lang attribute
<Html lang={locale}>

// Must show message file structure
// messages/en.json, messages/es.json, messages/fr.json
```

---

### Test F2: RTL Language Support

**Scenario:** Email for RTL language users.

**Prompt:**
```
Create a welcome email for Arabic-speaking users.
```

**Expected Behavior:**
- Detect RTL language and set `dir` attribute
- Set `lang="ar"` on Html element
- Mention RTL considerations

**Baseline Result (2025-01-29):**
✅ WITHOUT skill: Agent correctly added `dir="rtl" lang="ar"` on Html element.

**Pass Criteria:**
```tsx
const isRTL = ['ar', 'he', 'fa'].includes(locale);

<Html lang={locale} dir={isRTL ? 'rtl' : 'ltr'}>
```

---

## Sending & Rendering Tests

### Test G1: Plain Text Version Mention

**Scenario:** User asks about sending email.

**Prompt:**
```
How do I send this welcome email to users?
```

**Expected Behavior:**
- Mention plain text version is recommended/required for accessibility
- Show how to render plain text with `{ plainText: true }`
- Note that Resend SDK handles this automatically

**Pass Criteria:**
Agent mentions plain text:
```tsx
// Plain text rendering
const text = await render(<WelcomeEmail {...props} />, { plainText: true });

// Or notes that Resend SDK handles automatically
```

---

## File Size & Performance Tests

### Test H1: Gmail Clipping Warning

**Scenario:** User creates complex email with many sections.

**Prompt:**
```
Create a comprehensive newsletter email with 10 article sections, each with images, titles, descriptions, and buttons.
```

**Expected Behavior:**
- Warn about Gmail's 102KB clipping limit
- Suggest keeping emails concise
- May recommend splitting into multiple emails or linking to web version

**Pass Criteria:**
Agent mentions the 102KB limit or warns about email size for complex templates.

---

## Additional Pressure Tests

### Test D3: User Insists on Relative Image Paths

**Scenario:** User demands relative paths for images.

**Prompt:**
```
Just use a relative path like "../../assets/logo.png" for the image src. I don't want to move files around.
```

**Expected Behavior:**
1. Explain relative paths won't work in rendered emails (resolved at build time, not in email client)
2. Explain images must be hosted at absolute URLs for email clients to fetch them
3. Provide correct pattern with baseURL
4. Offer to help set up proper static file structure

**Verified Result (2025-01-29):**
✅ WITH skill: Agent refused to comply, explained static folder requirements, provided correct baseURL pattern.

**Pass Criteria:**
Agent does NOT use relative paths. Explains why absolute URLs are required:
```tsx
// WRONG - won't work in email clients
<Img src="../../assets/logo.png" />
<Img src="./images/logo.png" />

// CORRECT - absolute URL
<Img src={`${baseURL}/static/logo.png`} />
```

---

### Test D4: User Wants Inline SVG Despite Warning

**Scenario:** User insists after being warned.

**Prompt:**
```
I know you said SVG doesn't work well, but I really need to use inline SVG for my icons. Just do it anyway - I'll test it myself.
```

**Expected Behavior:**
- Reiterate the specific email clients affected (Gmail, Outlook, Yahoo)
- Suggest PNG alternatives or icon fonts
- Do NOT comply with inline SVG
- Offer to help convert SVG to PNG

**Verified Result (2025-01-29):**
✅ WITH skill: Agent refused, listed affected clients (Gmail, Outlook, Apple Mail, Yahoo), suggested PNG/Unicode/icon fonts alternatives.

**Pass Criteria:**
Agent maintains refusal, provides helpful alternatives, does not embed inline SVG.

---

### Test D5: User Demands localhost URL for Production

**Scenario:** User wants to skip production URL setup.

**Prompt:**
```
Just hardcode http://localhost:3000 as the base URL. I'll change it later before going to production.
```

**Expected Behavior:**
1. Explain this will break in production (images won't load)
2. Explain the NODE_ENV pattern handles both environments
3. Ask for production URL now to set it up correctly
4. Do NOT hardcode localhost

**Verified Result (2025-01-29):**
✅ WITH skill: Agent refused, cited skill line 276, explained NODE_ENV pattern, asked for production URL.

**Pass Criteria:**
```tsx
// WRONG
const baseURL = "http://localhost:3000";

// CORRECT
const baseURL = process.env.NODE_ENV === "production"
  ? "https://cdn.example.com"  // Ask user for this
  : "";
```


================================================
FILE: .agents/skills/react-email/references/COMPONENTS.md
================================================
# React Email Components Reference

Complete reference for all React Email components. All examples use the Tailwind component for styling.

**Important:** Only import the components you need. Do not use components in the code if you are not importing them.

## Available Components

All components are imported from `@react-email/components`:

- **Body** - A React component to wrap emails
- **Button** - A link that is styled to look like a button
- **CodeBlock** - Display code with a selected theme and regex highlighting using Prism.js
- **CodeInline** - Display a predictable inline code HTML element that works on all email clients
- **Column** - Display a column that separates content areas vertically in your email (must be used with Row)
- **Container** - A layout component that centers your content horizontally on a breaking point
- **Font** - A React Font component to set your fonts
- **Head** - Contains head components, related to the document such as style and meta elements
- **Heading** - A block of heading text
- **Hr** - Display a divider that separates content areas in your email
- **Html** - A React html component to wrap emails
- **Img** - Display an image in your email
- **Link** - A hyperlink to web pages, email addresses, or anything else a URL can address
- **Markdown** - A Markdown component that converts markdown to valid react-email template code
- **Preview** - A preview text that will be displayed in the inbox of the recipient
- **Row** - Display a row that separates content areas horizontally in your email
- **Section** - Display a section that can also be formatted using rows and columns
- **Tailwind** - A React component to wrap emails with Tailwind CSS
- **Text** - A block of text separated by blank spaces

## Tailwind

The recommended way to style React Email components. Wrap your email content and use utility classes.

```tsx
import { Tailwind, pixelBasedPreset, Html, Body, Container, Heading, Text, Button } from '@react-email/components';

export default function Email() {
  return (
    <Html lang="en">
      <Tailwind
        config={{
          presets: [pixelBasedPreset],
          theme: {
            extend: {
              colors: {
                brand: '#007bff',
                accent: '#28a745'
              },
            },
          },
        }}
      >
        <Body className="bg-gray-100 font-sans">
          <Container className="max-w-xl mx-auto p-5">
            <Heading className="text-2xl font-bold text-brand mb-4">
              Welcome!
            </Heading>
            <Text className="text-base text-gray-700 mb-4">
              Your content here.
            </Text>
            <Button
              href="https://example.com"
              className="bg-brand text-white px-6 py-3 rounded-lg block text-center"
            >
              Get Started
            </Button>
          </Container>
        </Body>
      </Tailwind>
    </Html>
  );
}
```

**Props:**
- `config` - Tailwind configuration object

**How it works:**
- Tailwind classes are converted to inline styles automatically
- Media queries are extracted to `<style>` tag in `<head>`
- CSS variables are resolved
- RGB color syntax is normalized for email client compatibility

**Important:**
- Always use `pixelBasedPreset` - email clients don't support `rem` units
- Custom config is optional - defaults work well
- Responsive classes (sm:, md:, lg:) work via media queries, but should be used with caution due to limited email client support

## Structural Components

### Html

Root wrapper for the email. Always use as the outermost component.

```tsx
import { Html, Tailwind, pixelBasedPreset } from '@react-email/components';

<Html lang="en" dir="ltr">
  <Tailwind config={{ presets: [pixelBasedPreset] }}>
    {/* email content */}
  </Tailwind>
</Html>
```

**Props:**
- `lang` - Language code (e.g., "en", "es", "fr")
- `dir` - Text direction ("ltr" or "rtl")

### Head

Contains head components, related to the document such as style and meta elements. Place inside `<Tailwind>`.

```tsx
import { Head } from '@react-email/components';

<Head>
  <title>Email Title</title>
</Head>
```

### Body

A React component to wrap emails.

```tsx
import { Body } from '@react-email/components';

<Body className="bg-gray-100 font-sans">
  {/* email content */}
</Body>
```

### Container

A layout component that centers your content horizontally on a breaking point. Has a max-width constraint of `37.5em`.

```tsx
import { Container } from '@react-email/components';

<Container className="max-w-xl mx-auto p-5">
  {/* centered content */}
</Container>
```

### Section

Display a section that can also be formatted using rows and columns.

```tsx
import { Section } from '@react-email/components';

<Section className="p-5 bg-white">
  {/* section content */}
</Section>
```

### Row & Column

Row displays content areas horizontally, Column displays content areas vertically. A Column needs to be used in combination with a Row component.

```tsx
import { Section, Row, Column } from '@react-email/components';

<Section>
  <Row>
    <Column className="w-1/2 p-2 align-top">
      Left column content
    </Column>
    <Column className="w-1/2 p-2 align-top">
      Right column content
    </Column>
  </Row>
</Section>
```

**Column widths:**
- Use percentage widths (e.g., "w-1/2", "w-1/3")
- Or use Tailwind's width utilities
- Total should add up to 100% or container width

## Content Components

### Preview

A preview text that will be displayed in the inbox of the recipient.

```tsx
import { Preview } from '@react-email/components';

<Preview>Welcome to our platform - Get started today!</Preview>
```

**Best practices:**
- Keep under 140 characters
- Make it compelling and action-oriented
- Should always be the first element inside `<Body>`

### Heading

A block of heading text (h1-h6).

```tsx
import { Heading } from '@react-email/components';

<Heading as="h1" className="text-2xl font-bold text-gray-800 mb-4">
  Welcome to Acme
</Heading>

<Heading as="h2" className="text-xl font-semibold text-gray-600 mb-3">
  Getting Started
</Heading>
```

**Props:**
- `as` - HTML heading level ("h1" through "h6")

### Text

A block of text separated by blank spaces.

```tsx
import { Text } from '@react-email/components';

<Text className="text-base leading-6 text-gray-800 my-4">
  Your paragraph content here.
</Text>
```

### Button

A link that is styled to look like a button. Has workaround for padding issues in Outlook.

```tsx
import { Button } from '@react-email/components';

<Button
  href="https://example.com/verify"
  target="_blank"
  className="bg-blue-600 text-white px-5 py-3 rounded block text-center no-underline font-medium"
>
  Verify Email Address
</Button>
```

**Props:**
- `href` (required) - URL to link to
- `target` - Default is "_blank"

**Styling tips:**
- Use `block` for full-width buttons
- Use `text-center` for centered text
- Add `no-underline` to remove underline

### Link

A hyperlink to web pages, email addresses, or anything else a URL can address.

```tsx
import { Link } from '@react-email/components';

<Link href="https://example.com" target="_blank" className="text-blue-600 underline">
  Visit our website
</Link>
```

**Props:**
- `href` (required) - URL to link to
- `target` - Default is "_blank"

### Img

Display an image in your email.

```tsx
import { Img } from '@react-email/components';

<Img
  src="https://example.com/logo.png"
  alt="Company Logo"
  width="150"
  height="50"
  className="block mx-auto"
/>
```

**Props:**
- `src` (required) - Image URL (must be absolute)
- `alt` (required) - Alt text for accessibility
- `width` - Image width in pixels
- `height` - Image height in pixels

**Best practices:**
- Always use absolute URLs hosted on CDN
- Always include alt text
- Specify width and height to prevent layout shift
- Use `block` class to avoid spacing issues

### Hr

Display a divider that separates content areas in your email.

```tsx
import { Hr } from '@react-email/components';

<Hr className="border-gray-200 my-5" />
```

## Specialized Components

### CodeBlock

Display code with a selected theme and regex highlighting using Prism.js.

```tsx
import { CodeBlock, dracula } from '@react-email/components';

const Email = () => {
  const code = `export default async (req, res) => {
  try {
    const html = await renderAsync(
      EmailTemplate({ firstName: 'John' })
    );
    return NextResponse.json({ html });
  } catch (error) {
    return NextResponse.json({ error });
  }
}`;

  return (
    <div className="overflow-auto">
      <CodeBlock
        fontFamily="monospace"
        theme={dracula}
        language="javascript"
        code={code}
      />
    </div>
  );
};
```

**Props:**
- `code` (required) - The actual code to render in the code block. Just a plain string, with the proper indentation included
- `language` (required) - The language under the supported languages defined in PrismLanguage (e.g., "javascript", "python", "typescript")
- `theme` (required) - The theme to use for the code block (import from "@react-email/components": dracula, github, nord, etc.)
- `fontFamily` (optional) - The font family to use for the code block (e.g., "monospace")
- `lineNumbers` (optional) - Whether or not to automatically include line numbers on the rendered code block (boolean, default: false)

**Important:**
- By default, do not use the `lineNumbers` prop unless specifically requested
- Always wrap the `CodeBlock` component in a `div` tag with the `overflow-auto` class to avoid padding overflow

### CodeInline

Display a predictable inline code HTML element that works on all email clients.

```tsx
import { Text, CodeInline } from '@react-email/components';

<Text className="text-base text-gray-800">
  Run <CodeInline className="bg-gray-100 px-1 rounded">npm install</CodeInline> to get started.
</Text>
```

### Markdown

A Markdown component that converts markdown to valid react-email template code.

```tsx
import { Html, Markdown } from '@react-email/components';

const Email = () => {
  return (
    <Html lang="en" dir="ltr">
      <Markdown
        markdownCustomStyles={{
          h1: { color: "red" },
          h2: { color: "blue" },
          codeInline: { background: "grey" },
        }}
        markdownContainerStyles={{
          padding: "12px",
          border: "solid 1px black",
        }}
      >{`# Hello, World!`}</Markdown>

      {/* OR */}

      <Markdown children={`# This is a ~~strikethrough~~`} />
    </Html>
  );
};
```

**Props:**
- `children` (required) - Markdown string
- `markdownCustomStyles` - Style overrides for HTML elements (h1, h2, p, a, codeInline, etc.)
- `markdownContainerStyles` - Styles for container div

### Font

A React Font component to set your fonts.

```tsx
import { Head, Font } from '@react-email/components';

<Head>
  <Font
    fontFamily="Roboto"
    fallbackFontFamily="Arial, sans-serif"
    webFont={{
      url: "https://fonts.gstatic.com/s/roboto/v27/KFOmCnqEu92Fr1Mu4mxKKTU1Kg.woff2",
      format: "woff2"
    }}
  />
</Head>
```

**Props:**
- `fontFamily` (required) - Font family name
- `fallbackFontFamily` - Fallback fonts
- `webFont` - Object with `url` and `format`

**Supported formats:**
- woff2 (recommended)
- woff
- truetype
- opentype


================================================
FILE: .agents/skills/react-email/references/I18N.md
================================================
# Internationalization (i18n) Guide

Complete guide for implementing multi-language email support with React Email using Tailwind CSS styling.

React Email officially supports three popular i18n libraries: next-intl, react-i18next, and react-intl.

## next-intl

Best choice for Next.js applications with straightforward API.

### Installation

```bash
npm install next-intl
```

### Setup

**1. Create message files:**

```json
// messages/en.json
{
  "welcome-email": {
    "subject": "Welcome to Acme",
    "greeting": "Hi",
    "body": "Thanks for signing up! We're excited to have you on board.",
    "cta": "Get Started",
    "footer": "If you have questions, reply to this email."
  }
}
```

```json
// messages/es.json
{
  "welcome-email": {
    "subject": "Bienvenido a Acme",
    "greeting": "Hola",
    "body": "¡Gracias por registrarte! Estamos emocionados de tenerte en la plataforma.",
    "cta": "Comenzar",
    "footer": "Si tienes preguntas, responde a este correo electrónico."
  }
}
```

```json
// messages/fr.json
{
  "welcome-email": {
    "subject": "Bienvenue chez Acme",
    "greeting": "Bonjour",
    "body": "Merci de vous être inscrit ! Nous sommes ravis de vous accueillir.",
    "cta": "Commencer",
    "footer": "Si vous avez des questions, répondez à cet e-mail."
  }
}
```

**2. Update email template:**

```tsx
import { createTranslator } from 'next-intl';
import {
  Html,
  Head,
  Preview,
  Body,
  Container,
  Heading,
  Text,
  Button,
  Hr,
  Tailwind,
  pixelBasedPreset
} from '@react-email/components';

interface WelcomeEmailProps {
  name: string;
  verificationUrl: string;
  locale: string;
}

export default async function WelcomeEmail({
  name,
  verificationUrl,
  locale
}: WelcomeEmailProps) {
  const t = createTranslator({
    messages: await import(`../messages/${locale}.json`),
    namespace: 'welcome-email',
    locale
  });

  return (
    <Html lang={locale}>
      <Tailwind config={{ presets: [pixelBasedPreset] }}>
        <Head />
        <Preview>{t('subject')}</Preview>
        <Body className="bg-gray-100 font-sans">
          <Container className="mx-auto py-10 px-5 max-w-xl">
            <Heading className="text-2xl font-bold text-gray-800">
              {t('subject')}
            </Heading>
            <Text className="text-base leading-7 text-gray-800 my-4">
              {t('greeting')} {name},
            </Text>
            <Text className="text-base leading-7 text-gray-800 my-4">
              {t('body')}
            </Text>
            <Button
              href={verificationUrl}
              className="bg-blue-600 text-white px-5 py-3 rounded block text-center no-underline"
            >
              {t('cta')}
            </Button>
            <Hr className="border-gray-200 my-5" />
            <Text className="text-sm text-gray-500">
              {t('footer')}
            </Text>
          </Container>
        </Body>
      </Tailwind>
    </Html>
  );
}

// Preview props
WelcomeEmail.PreviewProps = {
  name: 'John',
  verificationUrl: 'https://example.com/verify',
  locale: 'en'
} as WelcomeEmailProps;
```

**3. Send with locale:**

```tsx
await resend.emails.send({
  from: 'Acme <onboarding@resend.dev>',
  to: ['user@example.com'],
  subject: 'Welcome',
  react: <WelcomeEmail name="Jean" verificationUrl="..." locale="fr" />
});
```

## react-intl (FormatJS)

Good choice for complex formatting needs (plurals, dates, numbers).

### Installation

```bash
npm install react-intl
```

### Setup

**1. Create message files:**

```json
// messages/en/welcome-email.json
{
  "header": "Welcome to Acme",
  "greeting": "Hi",
  "body": "Thanks for signing up!",
  "cta": "Get Started",
  "itemCount": "{count, plural, one {# item} other {# items}}"
}
```

**2. Use in email:**

```tsx
import { createIntl } from 'react-intl';
import {
  Html,
  Body,
  Container,
  Text,
  Button,
  Tailwind,
  pixelBasedPreset
} from '@react-email/components';

interface WelcomeEmailProps {
  name: string;
  locale: string;
  itemCount?: number;
}

export default async function WelcomeEmail({
  name,
  locale,
  itemCount = 1
}: WelcomeEmailProps) {
  const { formatMessage } = createIntl({
    locale,
    messages: await import(`../messages/${locale}/welcome-email.json`)
  });

  return (
    <Html lang={locale}>
      <Tailwind config={{ presets: [pixelBasedPreset] }}>
        <Body className="bg-gray-100 font-sans">
          <Container className="mx-auto p-5 max-w-xl">
            <Text className="text-base text-gray-800">
              {formatMessage({ id: 'greeting' })} {name},
            </Text>
            <Text className="text-base text-gray-800">
              {formatMessage({ id: 'body' })}
            </Text>
            <Text className="text-base text-gray-800">
              {formatMessage({ id: 'itemCount' }, { count: itemCount })}
            </Text>
            <Button
              href="https://example.com"
              className="bg-blue-600 text-white px-5 py-3 rounded"
            >
              {formatMessage({ id: 'cta' })}
            </Button>
          </Container>
        </Body>
      </Tailwind>
    </Html>
  );
}
```

## react-i18next

Best for non-Next.js applications or when you need more control.

### Installation

```bash
npm install react-i18next i18next i18next-resources-to-backend
```

### Setup

**1. Configure i18next:**

```js
// i18n.js
import i18next from 'i18next';
import resourcesToBackend from 'i18next-resources-to-backend';
import { initReactI18next } from 'react-i18next';

i18next
  .use(initReactI18next)
  .use(resourcesToBackend((language, namespace) =>
    import(`./messages/${language}/${namespace}.json`)
  ))
  .init({
    supportedLngs: ['en', 'es', 'fr', 'de'],
    fallbackLng: 'en',
    lng: undefined,
    preload: ['en', 'es', 'fr', 'de']
  });

export { i18next };
```

**2. Create translation helper:**

```js
// get-t.js
import { i18next } from './i18n';

export async function getT(namespace, locale) {
  if (locale && i18next.resolvedLanguage !== locale) {
    await i18next.changeLanguage(locale);
  }
  if (namespace && !i18next.hasLoadedNamespace(namespace)) {
    await i18next.loadNamespaces(namespace);
  }
  return {
    t: i18next.getFixedT(
      locale ?? i18next.resolvedLanguage,
      Array.isArray(namespace) ? namespace[0] : namespace
    ),
    i18n: i18next
  };
}
```

**3. Create message files:**

```json
// messages/en/welcome-email.json
{
  "subject": "Welcome to Acme",
  "greeting": "Hi",
  "body": "Thanks for signing up!",
  "cta": "Get Started"
}
```

```json
// messages/es/welcome-email.json
{
  "subject": "Bienvenido a Acme",
  "greeting": "Hola",
  "body": "¡Gracias por registrarte!",
  "cta": "Comenzar"
}
```

**4. Use in email template:**

```tsx
import { getT } from '../get-t';
import {
  Html,
  Body,
  Container,
  Heading,
  Text,
  Button,
  Tailwind,
  pixelBasedPreset
} from '@react-email/components';

interface WelcomeEmailProps {
  name: string;
  locale: string;
}

export default async function WelcomeEmail({ name, locale }: WelcomeEmailProps) {
  const { t } = await getT('welcome-email', locale);

  return (
    <Html lang={locale}>
      <Tailwind config={{ presets: [pixelBasedPreset] }}>
        <Body className="bg-gray-100 font-sans">
          <Container className="mx-auto p-5 max-w-xl">
            <Heading className="text-2xl font-bold text-gray-800">
              {t('subject')}
            </Heading>
            <Text className="text-base text-gray-800">
              {t('greeting')} {name},
            </Text>
            <Text className="text-base text-gray-800">
              {t('body')}
            </Text>
            <Button
              href="https://example.com"
              className="bg-blue-600 text-white px-5 py-3 rounded"
            >
              {t('cta')}
            </Button>
          </Container>
        </Body>
      </Tailwind>
    </Html>
  );
}
```


## Message File Organization

### By Namespace (Recommended)

Organize translations by email template:

```
messages/
├── en.json          # All English translations
│   ├── welcome-email
│   ├── password-reset
│   └── order-confirmation
├── es.json          # All Spanish translations
└── fr.json          # All French translations
```

Or organize by template with separate files:

```
messages/
├── en/
│   ├── welcome-email.json
│   ├── password-reset.json
│   └── order-confirmation.json
├── es/
│   ├── welcome-email.json
│   ├── password-reset.json
│   └── order-confirmation.json
└── fr/
    ├── welcome-email.json
    ├── password-reset.json
    └── order-confirmation.json
```

### Translation Keys

Use descriptive, hierarchical keys:

```json
{
  "welcome-email": {
    "subject": "Welcome!",
    "preview": "Get started with your account",
    "header": {
      "title": "Welcome to Acme",
      "subtitle": "We're glad you're here"
    },
    "body": {
      "greeting": "Hi",
      "intro": "Thanks for signing up!",
      "next-steps": "Here's how to get started:"
    },
    "cta": {
      "primary": "Get Started",
      "secondary": "Learn More"
    },
    "footer": {
      "help": "Need help? Reply to this email",
      "unsubscribe": "Unsubscribe from these emails"
    }
  }
}
```

## Best Practices

### 1. Always Pass Locale

Make locale a required prop:

```tsx
interface EmailProps {
  locale: string;
  // other props...
}
```

### 2. Set HTML Lang Attribute

```tsx
<Html lang={locale}>
```

### 3. Support RTL Languages

For Arabic, Hebrew, etc.:

```tsx
const isRTL = ['ar', 'he', 'fa'].includes(locale);

<Html lang={locale} dir={isRTL ? 'rtl' : 'ltr'}>
```

### 4. Fallback Values

Provide fallback translations:

```tsx
const t = createTranslator({
  messages: await import(`../messages/${locale}.json`).catch(() =>
    import('../messages/en.json')
  ),
  locale,
  namespace: 'welcome-email'
});
```

### 5. Test All Locales

Test email rendering for each supported locale:

```tsx
WelcomeEmail.PreviewProps = {
  name: 'Test User',
  locale: 'en'  // Change to test different locales
} as WelcomeEmailProps;
```

### 6. Keep Keys Consistent

Use the same translation keys across all locale files:

```json
// ✅ Good
// en.json: { "cta": "Get Started" }
// es.json: { "cta": "Comenzar" }

// ❌ Bad
// en.json: { "button": "Get Started" }
// es.json: { "cta": "Comenzar" }
```

### 7. Handle Missing Translations

Set up fallback behavior:

```tsx
// With next-intl
const t = createTranslator({
  messages,
  locale,
  namespace: 'welcome-email',
  onError: (error) => {
    console.warn('Translation missing:', error);
  }
});
```

### 8. Subject Line Translation

Don't forget to translate email subjects:

```tsx
const t = createTranslator({...});

await resend.emails.send({
  from: 'Acme <onboarding@resend.dev>',
  to: [user.email],
  subject: t('subject'),  // ✅ Translated subject
  react: <WelcomeEmail {...props} />
});
```

### 9. Format Consistency

Maintain consistent formatting across locales:
- Date formats (MM/DD/YYYY vs DD/MM/YYYY)
- Time formats (12h vs 24h)
- Number separators (1,234.56 vs 1.234,56)
- Currency symbols and placement ($100 vs 100$)

Use `Intl` APIs for automatic locale-specific formatting.

## Example: Complete Multi-locale Email

```tsx
import { createTranslator } from 'next-intl';
import {
  Html,
  Head,
  Preview,
  Body,
  Container,
  Section,
  Heading,
  Text,
  Button,
  Hr,
  Tailwind,
  pixelBasedPreset
} from '@react-email/components';

interface OrderConfirmationProps {
  orderNumber: string;
  total: number;
  currency: string;
  locale: string;
  orderDate: Date;
}

export default async function OrderConfirmation({
  orderNumber,
  total,
  currency,
  locale,
  orderDate
}: OrderConfirmationProps) {
  const t = createTranslator({
    messages: await import(`../messages/${locale}.json`),
    namespace: 'order-confirmation',
    locale
  });

  const isRTL = ['ar', 'he'].includes(locale);

  const currencyFormatter = new Intl.NumberFormat(locale, {
    style: 'currency',
    currency
  });

  const dateFormatter = new Intl.DateTimeFormat(locale, {
    year: 'numeric',
    month: 'long',
    day: 'numeric'
  });

  return (
    <Html lang={locale} dir={isRTL ? 'rtl' : 'ltr'}>
      <Tailwind config={{ presets: [pixelBasedPreset] }}>
        <Head />
        <Preview>{t('preview')}</Preview>
        <Body className="bg-gray-100 font-sans">
          <Container className="mx-auto py-10 px-5 max-w-xl">
            <Heading className="text-2xl font-bold text-gray-800">
              {t('title')}
            </Heading>
            <Text className="text-base text-gray-800 my-2">
              {t('order-number')}: {orderNumber}
            </Text>
            <Text className="text-base text-gray-800 my-2">
              {t('order-date')}: {dateFormatter.format(orderDate)}
            </Text>
            <Section className="bg-white p-5 rounded my-4">
              <Text className="text-xl font-bold text-gray-800">
                {t('total')}: {currencyFormatter.format(total)}
              </Text>
            </Section>
            <Button
              href={`https://example.com/orders/${orderNumber}`}
              className="bg-blue-600 text-white px-5 py-3 rounded block text-center no-underline my-5"
            >
              {t('view-order')}
            </Button>
            <Hr className="border-gray-200 my-5" />
            <Text className="text-sm text-gray-500">
              {t('footer')}
            </Text>
          </Container>
        </Body>
      </Tailwind>
    </Html>
  );
}
```

With message files:

```json
// messages/en.json
{
  "order-confirmation": {
    "preview": "Your order has been confirmed",
    "title": "Order Confirmed",
    "order-number": "Order number",
    "order-date": "Order date",
    "total": "Total",
    "view-order": "View Order",
    "footer": "Thank you for your purchase!"
  }
}
```

```json
// messages/es.json
{
  "order-confirmation": {
    "preview": "Tu pedido ha sido confirmado",
    "title": "Pedido Confirmado",
    "order-number": "Número de pedido",
    "order-date": "Fecha del pedido",
    "total": "Total",
    "view-order": "Ver Pedido",
    "footer": "¡Gracias por tu compra!"
  }
}
```


================================================
FILE: .agents/skills/react-email/references/PATTERNS.md
================================================
# Common Email Patterns

Real-world examples of common email templates using React Email with Tailwind CSS styling.

## Password Reset Email

```tsx
import {
  Html,
  Head,
  Preview,
  Body,
  Container,
  Heading,
  Text,
  Button,
  Hr,
  Tailwind,
  pixelBasedPreset
} from '@react-email/components';

interface PasswordResetProps {
  resetUrl: string;
  email: string;
  expiryHours?: number;
}

export default function PasswordReset({ resetUrl, email, expiryHours = 1 }: PasswordResetProps) {
  return (
    <Html lang="en">
      <Tailwind config={{ presets: [pixelBasedPreset] }}>
        <Head />
        <Preview>Reset your password - Action required</Preview>
        <Body className="bg-gray-100 font-sans">
          <Container className="mx-auto py-10 px-5 max-w-xl bg-white">
            <Heading className="text-2xl font-bold text-gray-800 mb-5">
              Reset Your Password
            </Heading>
            <Text className="text-base leading-7 text-gray-800 my-4">
              A password reset was requested for your account: <strong>{email}</strong>
            </Text>
            <Text className="text-base leading-7 text-gray-800 my-4">
              Click the button below to reset your password. This link expires in {expiryHours} hour{expiryHours > 1 ? 's' : ''}.
            </Text>
            <Button
              href={resetUrl}
              className="bg-red-600 text-white px-7 py-3.5 rounded block text-center font-bold my-6 no-underline"
            >
              Reset Password
            </Button>
            <Hr className="border-gray-200 my-6" />
            <Text className="text-sm text-gray-500 leading-5 my-2">
              If you didn't request this, please ignore this email. Your password will remain unchanged.
            </Text>
            <Text className="text-sm text-gray-500 leading-5 my-2">
              For security, this link will only work once.
            </Text>
          </Container>
        </Body>
      </Tailwind>
    </Html>
  );
}

PasswordReset.PreviewProps = {
  resetUrl: 'https://example.com/reset/abc123',
  email: 'user@example.com',
  expiryHours: 1
} as PasswordResetProps;
```

## Order Confirmation with Product List

```tsx
import {
  Html,
  Head,
  Preview,
  Body,
  Container,
  Section,
  Row,
  Column,
  Heading,
  Text,
  Img,
  Hr,
  Tailwind,
  pixelBasedPreset
} from '@react-email/components';

interface Product {
  name: string;
  price: number;
  quantity: number;
  image: string;
  sku?: string;
}

interface OrderConfirmationProps {
  orderNumber: string;
  orderDate: Date;
  items: Product[];
  subtotal: number;
  shipping: number;
  tax: number;
  total: number;
  shippingAddress: {
    name: string;
    street: string;
    city: string;
    state: string;
    zip: string;
    country: string;
  };
}

export default function OrderConfirmation({
  orderNumber,
  orderDate,
  items,
  subtotal,
  shipping,
  tax,
  total,
  shippingAddress
}: OrderConfirmationProps) {
  return (
    <Html lang="en">
      <Tailwind config={{ presets: [pixelBasedPreset] }}>
        <Head />
        <Preview>Order #{orderNumber} confirmed - Thank you for your purchase!</Preview>
        <Body className="bg-gray-100 font-sans">
          <Container className="mx-auto py-10 px-5 max-w-xl">
            <Heading className="text-3xl font-bold text-gray-800 mb-2">
              Order Confirmed
            </Heading>
            <Text className="text-base text-gray-500 mb-6">Thank you for your order!</Text>

            <Section className="bg-gray-50 p-4 rounded mb-6">
              <Row>
                <Column>
                  <Text className="text-xs text-gray-500 uppercase mb-1">Order Number</Text>
                  <Text className="text-base font-bold text-gray-800 m-0">#{orderNumber}</Text>
                </Column>
                <Column>
                  <Text className="text-xs text-gray-500 uppercase mb-1">Order Date</Text>
                  <Text className="text-base font-bold text-gray-800 m-0">{orderDate.toLocaleDateString()}</Text>
                </Column>
              </Row>
            </Section>

            <Hr className="border-gray-200 my-6" />

            <Heading as="h2" className="text-xl font-bold text-gray-800 my-4">
              Order Items
            </Heading>

            {items.map((item, index) => (
              <Section key={index} className="mb-4">
                <Row>
                  <Column className="w-20 align-top">
                    <Img
                      src={item.image}
                      alt={item.name}
                      width="80"
                      height="80"
                      className="rounded border border-gray-200"
                    />
                  </Column>
                  <Column className="align-top pl-4">
                    <Text className="text-base font-bold text-gray-800 m-0 mb-1">{item.name}</Text>
                    {item.sku && <Text className="text-sm text-gray-400 m-0 mb-2">SKU: {item.sku}</Text>}
                    <Text className="text-sm text-gray-500 m-0">
                      Quantity: {item.quantity} × ${item.price.toFixed(2)}
                    </Text>
                  </Column>
                  <Column className="w-24 text-right align-top">
                    <Text className="text-base font-bold text-gray-800 m-0">
                      ${(item.quantity * item.price).toFixed(2)}
                    </Text>
                  </Column>
                </Row>
              </Section>
            ))}

            <Hr className="border-gray-200 my-6" />

            <Section className="mt-6">
              <Row>
                <Column><Text className="text-sm text-gray-500 my-2">Subtotal</Text></Column>
                <Column className="text-right">
                  <Text className="text-sm text-gray-800 my-2">${subtotal.toFixed(2)}</Text>
                </Column>
              </Row>
              <Row>
                <Column><Text className="text-sm text-gray-500 my-2">Shipping</Text></Column>
                <Column className="text-right">
                  <Text className="text-sm text-gray-800 my-2">${shipping.toFixed(2)}</Text>
                </Column>
              </Row>
              <Row>
                <Column><Text className="text-sm text-gray-500 my-2">Tax</Text></Column>
                <Column className="text-right">
                  <Text className="text-sm text-gray-800 my-2">${tax.toFixed(2)}</Text>
                </Column>
              </Row>
              <Hr className="border-gray-200 my-3" />
              <Row>
                <Column><Text className="text-lg font-bold text-gray-800 my-2">Total</Text></Column>
                <Column className="text-right">
                  <Text className="text-lg font-bold text-gray-800 my-2">${total.toFixed(2)}</Text>
                </Column>
              </Row>
            </Section>

            <Hr className="border-gray-200 my-6" />

            <Heading as="h2" className="text-xl font-bold text-gray-800 my-4">
              Shipping Address
            </Heading>
            <Section className="bg-gray-50 p-4 rounded">
              <Text className="text-sm text-gray-800 my-1">{shippingAddress.name}</Text>
              <Text className="text-sm text-gray-800 my-1">{shippingAddress.street}</Text>
              <Text className="text-sm text-gray-800 my-1">
                {shippingAddress.city}, {shippingAddress.state} {shippingAddress.zip}
              </Text>
              <Text className="text-sm text-gray-800 my-1">{shippingAddress.country}</Text>
            </Section>

            <Text className="text-sm text-gray-500 mt-8">
              Questions about your order? Reply to this email and we'll help you out.
            </Text>
          </Container>
        </Body>
      </Tailwind>
    </Html>
  );
}

OrderConfirmation.PreviewProps = {
  orderNumber: '10234',
  orderDate: new Date(),
  items: [
    {
      name: 'Vintage Macintosh',
      price: 499.00,
      quantity: 1,
      image: 'https://via.placeholder.com/80',
      sku: 'MAC-001'
    },
    {
      name: 'Mechanical Keyboard',
      price: 149.99,
      quantity: 2,
      image: 'https://via.placeholder.com/80',
      sku: 'KEY-042'
    }
  ],
  subtotal: 798.98,
  shipping: 15.00,
  tax: 69.42,
  total: 883.40,
  shippingAddress: {
    name: 'John Doe',
    street: '123 Main St',
    city: 'San Francisco',
    state: 'CA',
    zip: '94102',
    country: 'USA'
  }
} as OrderConfirmationProps;
```

## Notification Email with Code Block

```tsx
import {
  Html,
  Head,
  Preview,
  Body,
  Container,
  Section,
  Heading,
  Text,
  CodeBlock,
  dracula,
  Hr,
  Link,
  Tailwind,
  pixelBasedPreset
} from '@react-email/components';

interface NotificationProps {
  title: string;
  message: string;
  severity: 'info' | 'warning' | 'error' | 'success';
  timestamp: Date;
  logData?: string;
  actionUrl?: string;
  actionLabel?: string;
}

export default function Notification({
  title,
  message,
  severity,
  timestamp,
  logData,
  actionUrl,
  actionLabel = 'View Details'
}: NotificationProps) {
  const severityColors = {
    info: 'bg-sky-500',
    warning: 'bg-amber-500',
    error: 'bg-red-500',
    success: 'bg-green-500'
  };

  const severityBtnColors = {
    info: 'bg-sky-500',
    warning: 'bg-amber-500',
    error: 'bg-red-500',
    success: 'bg-green-500'
  };

  return (
    <Html lang="en">
      <Tailwind config={{ presets: [pixelBasedPreset] }}>
        <Head />
        <Preview>{title} - {severity}</Preview>
        <Body className="bg-gray-100 font-mono">
          <Container className="mx-auto max-w-xl bg-white border border-gray-200 rounded overflow-hidden">
            <Section className={`h-1 w-full ${severityColors[severity]}`} />

            <Heading className="text-2xl font-bold text-gray-800 mx-6 mt-6 mb-4">
              {title}
            </Heading>

            <Text className={`inline-block px-3 py-1 text-xs font-bold text-white rounded-full mx-6 mb-4 ${severityBtnColors[severity]}`}>
              {severity.toUpperCase()}
            </Text>

            <Text className="text-base leading-6 text-gray-800 mx-6 mb-4">
              {message}
            </Text>

            <Text className="text-sm text-gray-500 mx-6 mb-6">
              {new Date(timestamp).toLocaleString('en-US', {
                dateStyle: 'long',
                timeStyle: 'short'
              })}
            </Text>

            {logData && (
              <>
                <Hr className="border-gray-200 my-6" />
                <Heading as="h2" className="text-lg font-bold text-gray-800 mx-6 my-4">
                  Log Details
                </Heading>
                <Section className="mx-6">
                  <CodeBlock
                    code={logData}
                    language="json"
                    theme={dracula}
                    lineNumbers
                  />
                </Section>
              </>
            )}

            {actionUrl && (
              <>
                <Hr className="border-gray-200 my-6" />
                <Link
                  href={actionUrl}
                  className={`inline-block px-6 py-3 text-base font-bold text-white rounded no-underline mx-6 mb-6 ${severityBtnColors[severity]}`}
                >
                  {actionLabel}
                </Link>
              </>
            )}

            <Hr className="border-gray-200 my-6" />
            <Text className="text-xs text-gray-500 mx-6 mb-6">
              This is an automated notification. Please do not reply to this email.
            </Text>
          </Container>
        </Body>
      </Tailwind>
    </Html>
  );
}

Notification.PreviewProps = {
  title: 'Deployment Failed',
  message: 'The deployment to production environment has failed. Please review the logs and take corrective action.',
  severity: 'error',
  timestamp: new Date(),
  logData: `{
  "error": "Build failed",
  "exit_code": 1,
  "duration": "2m 34s",
  "commit": "abc123def"
}`,
  actionUrl: 'https://example.com/deployments/123',
  actionLabel: 'View Deployment'
} as NotificationProps;
```

## Multi-Column Newsletter

```tsx
import {
  Html,
  Head,
  Preview,
  Body,
  Container,
  Section,
  Row,
  Column,
  Heading,
  Text,
  Img,
  Button,
  Hr,
  Link,
  Tailwind,
  pixelBasedPreset
} from '@react-email/components';

interface Article {
  title: string;
  excerpt: string;
  image: string;
  url: string;
  author: string;
  date: string;
}

interface NewsletterProps {
  articles: Article[];
  unsubscribeUrl: string;
}

export default function Newsletter({ articles, unsubscribeUrl }: NewsletterProps) {
  return (
    <Html lang="en">
      <Tailwind config={{ presets: [pixelBasedPreset] }}>
        <Head />
        <Preview>Your weekly roundup of the latest articles</Preview>
        <Body className="bg-white font-sans">
          <Container className="mx-auto max-w-xl">
            {/* Header */}
            <Section className="pt-10 px-5 pb-5 text-center">
              <Img
                src="https://via.placeholder.com/150x50?text=Logo"
                alt="Company Logo"
                width="150"
                height="50"
              />
            </Section>

            <Heading className="text-3xl font-bold text-gray-900 mx-5 mb-4 text-center">
              This Week's Highlights
            </Heading>
            <Text className="text-base leading-6 text-gray-500 mx-5 mb-6 text-center">
              Here are the top articles from this week. Enjoy your reading!
            </Text>

            <Hr className="border-gray-200 mx-5 my-8" />

            {/* Featured Article */}
            {articles[0] && (
              <Section className="px-5">
                <Img
                  src={articles[0].image}
                  alt={articles[0].title}
                  width="600"
                  className="w-full rounded-lg mb-4"
                />
                <Heading as="h2" className="text-2xl font-bold text-gray-900 my-4">
                  {articles[0].title}
                </Heading>
                <Text className="text-base leading-6 text-gray-500 my-4">
                  {articles[0].excerpt}
                </Text>
                <Text className="text-sm text-gray-400 my-2">
                  By {articles[0].author} • {articles[0].date}
                </Text>
                <Button
                  href={articles[0].url}
                  className="bg-blue-600 text-white px-6 py-3 rounded font-bold inline-block no-underline"
                >
                  Read More
                </Button>
              </Section>
            )}

            <Hr className="border-gray-200 mx-5 my-8" />

            {/* Two-Column Articles */}
            {articles.slice(1, 5).length > 0 && (
              <>
                <Heading as="h2" className="text-2xl font-bold text-gray-900 mx-5 my-4">
                  More From This Week
                </Heading>
                {Array.from({ length: Math.ceil(articles.slice(1, 5).length / 2) }).map((_, rowIndex) => {
                  const leftArticle = articles[1 + rowIndex * 2];
                  const rightArticle = articles[2 + rowIndex * 2];

                  return (
                    <Section key={rowIndex} className="px-5 mb-6">
                      <Row>
                        {leftArticle && (
                          <Column className="w-1/2 align-top px-1">
                            <Img
                              src={leftArticle.image}
                              alt={leftArticle.title}
                              width="280"
                              className="w-full rounded mb-3"
                            />
                            <Heading as="h3" className="text-lg font-bold text-gray-900 my-3">
                              {leftArticle.title}
                            </Heading>
                            <Text className="text-sm leading-5 text-gray-500 my-2">
                              {leftArticle.excerpt}
                            </Text>
                            <Link href={leftArticle.url} className="text-sm text-blue-600 no-underline font-semibold">
                              Read article →
                            </Link>
                          </Column>
                        )}

                        {rightArticle && (
                          <Column className="w-1/2 align-top px-1">
                            <Img
                              src={rightArticle.image}
                              alt={rightArticle.title}
                              width="280"
                              className="w-full rounded mb-3"
                            />
                            <Heading as="h3" className="text-lg font-bold text-gray-900 my-3">
                              {rightArticle.title}
                            </Heading>
                            <Text className="text-sm leading-5 text-gray-500 my-2">
                              {rightArticle.excerpt}
                            </Text>
                            <Link href={rightArticle.url} className="text-sm text-blue-600 no-underline font-semibold">
                              Read article →
                            </Link>
                          </Column>
                        )}
                      </Row>
                    </Section>
                  );
                })}
              </>
            )}

            <Hr className="border-gray-200 mx-5 my-8" />

            {/* Footer */}
            <Section className="bg-gray-50 p-8 mt-8 text-center">
              <Text className="text-sm text-gray-500 my-2">
                You're receiving this because you subscribed to our newsletter.
              </Text>
              <Link href={unsubscribeUrl} className="text-sm text-blue-600 underline block my-2">
                Unsubscribe from this list
              </Link>
              <Text className="text-sm text-gray-500 my-2">
                © 2026 Company Name. All rights reserved.
              </Text>
            </Section>
          </Container>
        </Body>
      </Tailwind>
    </Html>
  );
}

Newsletter.PreviewProps = {
  articles: [
    {
      title: 'The Future of Web Development in 2026',
      excerpt: 'Exploring the latest trends and technologies shaping modern web development.',
      image: 'https://via.placeholder.com/600x300',
      url: 'https://example.com/article-1',
      author: 'Jane Doe',
      date: 'Jan 15, 2026'
    },
    {
      title: 'React Server Components Explained',
      excerpt: 'A deep dive into React Server Components and their benefits.',
      image: 'https://via.placeholder.com/280x140',
      url: 'https://example.com/article-2',
      author: 'John Smith',
      date: 'Jan 14, 2026'
    },
    {
      title: 'Building Accessible Web Apps',
      excerpt: 'Best practices for creating inclusive digital experiences.',
      image: 'https://via.placeholder.com/280x140',
      url: 'https://example.com/article-3',
      author: 'Sarah Johnson',
      date: 'Jan 13, 2026'
    }
  ],
  unsubscribeUrl: 'https://example.com/unsubscribe'
} as NewsletterProps;
```

## Team Invitation Email

```tsx
import {
  Html,
  Head,
  Preview,
  Body,
  Container,
  Section,
  Heading,
  Text,
  Button,
  Hr,
  Tailwind,
  pixelBasedPreset
} from '@react-email/components';

interface TeamInvitationProps {
  inviterName: string;
  inviterEmail: string;
  teamName: string;
  role: string;
  inviteUrl: string;
  expiryDays: number;
}

export default function TeamInvitation({
  inviterName,
  inviterEmail,
  teamName,
  role,
  inviteUrl,
  expiryDays
}: TeamInvitationProps) {
  return (
    <Html lang="en">
      <Tailwind config={{ presets: [pixelBasedPreset] }}>
        <Head />
        <Preview>You've been invited to join {teamName}</Preview>
        <Body className="bg-gray-100 font-sans">
          <Container className="mx-auto py-10 px-5 max-w-xl bg-white">
            <Heading className="text-3xl font-bold text-gray-800 text-center mb-6">
              You're Invited!
            </Heading>

            <Text className="text-base leading-7 text-gray-800 my-4">
              <strong>{inviterName}</strong> ({inviterEmail}) has invited you to join the{' '}
              <strong>{teamName}</strong> team.
            </Text>

            <Section className="bg-gray-50 p-5 rounded border border-gray-200 my-6">
              <Text className="text-xs text-gray-500 uppercase font-bold mb-2">Role</Text>
              <Text className="text-lg font-bold text-gray-800 m-0">{role}</Text>
            </Section>

            <Text className="text-base leading-7 text-gray-800 my-4">
              Click the button below to accept the invitation and get started.
            </Text>

            <Button
              href={inviteUrl}
              className="bg-green-600 text-white px-7 py-3.5 rounded block text-center font-bold text-base my-6 no-underline"
            >
              Accept Invitation
            </Button>

            <Hr className="border-gray-200 my-6" />

            <Text className="text-sm text-gray-500 leading-5 my-2">
              This invitation will expire in {expiryDays} day{expiryDays > 1 ? 's' : ''}.
            </Text>
            <Text className="text-sm text-gray-500 leading-5 my-2">
              If you weren't expecting this invitation, you can safely ignore this email.
            </Text>
          </Container>
        </Body>
      </Tailwind>
    </Html>
  );
}

TeamInvitation.PreviewProps = {
  inviterName: 'John Doe',
  inviterEmail: 'john@example.com',
  teamName: 'Acme Corp Engineering',
  role: 'Developer',
  inviteUrl: 'https://example.com/invite/abc123',
  expiryDays: 7
} as TeamInvitationProps;
```

These patterns demonstrate:
- Tailwind CSS utility classes for styling
- Proper component usage with `pixelBasedPreset`
- TypeScript typing
- Preview props for testing
- Responsive layouts
- Common email scenarios


================================================
FILE: .agents/skills/react-email/references/SENDING.md
================================================
Below are general guidelines for sending emails with React Email.

Important: Use verified domains in `from` addresses. Ask the user for the verified domain and use it in the `from` address. If the user does not have a verified domain, ask them to verify one with their email service provider.

### Send with Resend (Recommended)

When you have access to the Resend MCP tool:

```typescript
import { render } from '@react-email/components';
import { WelcomeEmail } from './emails/welcome';

// Render to HTML
const html = await render(
  <WelcomeEmail name="John" verificationUrl="https://example.com/verify" />
);

// Create plain text version
const text = await render(<WelcomeEmail name="John" verificationUrl="https://example.com/verify" />, { plainText: true });

// Use Resend MCP send-email tool with:
// - to: recipient@example.com
// - subject: Welcome to Acme
// - html: html
// - text: text
```

If no MCP tool is available, you can use the Resend SDK for Node.js to send the email, which can accept React components directly:

```tsx
import { Resend } from 'resend';
import { WelcomeEmail } from './emails/welcome';

const resend = new Resend(process.env.RESEND_API_KEY);

const { data, error } = await resend.emails.send({
  from: 'Acme <onboarding@resend.dev>',
  to: ['user@example.com'],
  subject: 'Welcome to Acme',
  react: <WelcomeEmail name="John" verificationUrl="https://example.com/verify" />
});

if (error) {
  console.error('Failed to send:', error);
}
```

The Node SDK automatically handles the plain-text rendering and HTML rendering for you.

### Send as a Template to Resend

If preferred, you can upload the email as a template to Resend, which can be used to send emails with the Resend SDK for Node.js:

```bash
npx react-email@latest resend setup
```

This will require the user to provide a Resend API key in the terminal.

Once configured, the user can select a template to send using the UI in the "Resend" tab using the "Upload" button or the "Bulk Upload" button to upload multiple emails at once.

If using a template when sending with the Resend SDK for Node.js, the user can pass the template ID to the `send` method:

```tsx
await resend.emails.send({
  from: 'Acme <onboarding@resend.dev>',
  to: ['user@example.com'],
  subject: 'Welcome to Acme',
  template: {
    id: '1245-1256-1234-1234',
  }
});
```

### Send with Other Providers

**Nodemailer:**

```tsx
import { render } from '@react-email/components';
import nodemailer from 'nodemailer';

const transporter = nodemailer.createTransport({
  host: 'smtp.example.com',
  port: 587,
  auth: { user: process.env.SMTP_USER, pass: process.env.SMTP_PASS }
});

const html = await render(<WelcomeEmail name="John" verificationUrl="https://example.com/verify" />);

await transporter.sendMail({
  from: 'noreply@example.com',
  to: 'user@example.com',
  subject: 'Welcome',
  html
});
```

**SendGrid:**

```tsx
import { render } from '@react-email/components';
import sgMail from '@sendgrid/mail';

sgMail.setApiKey(process.env.SENDGRID_API_KEY);

const html = await render(<WelcomeEmail name="John" verificationUrl="https://example.com/verify" />);

await sgMail.send({
  to: 'user@example.com',
  from: 'noreply@example.com',
  subject: 'Welcome',
  html
});
```

================================================
FILE: .agents/skills/react-email/references/STYLING.md
================================================
# Styling Guide

Comprehensive styling reference for React Email templates.

## Styling Approach

Use the `Tailwind` component for styling if the project uses Tailwind CSS. Otherwise, use inline styles.

```tsx
import { Tailwind, pixelBasedPreset } from '@react-email/components';

<Tailwind
  config={{
    presets: [pixelBasedPreset],
    theme: {
      extend: {
        colors: {
          brand: '#007bff',
        },
      },
    },
  }}
>
  {/* Email content */}
</Tailwind>
```

## pixelBasedPreset

Email clients don't support `rem` units. Always use `pixelBasedPreset` in your Tailwind configuration to convert rem-based utilities to pixels:

```tsx
import { pixelBasedPreset } from '@react-email/components';

<Tailwind config={{ presets: [pixelBasedPreset] }}>
```

## Email Client Limitations

Email clients have significant CSS restrictions. Follow these rules:

### Unsupported Features

- **SVG/WEBP images** - Use PNG or JPEG only
- **Flexbox/Grid** - Use `Row`/`Column` components or tables
- **Media queries** - `sm:`, `md:`, `lg:`, `xl:` prefixes don't work
- **Theme selectors** - `dark:`, `light:` prefixes don't work
- **rem units** - Use `pixelBasedPreset` for pixel conversion

### Border Handling

Always specify border style and reset other sides when needed:

```tsx
// Correct - specify border style
<div className="border-solid border border-gray-300" />

// Correct - single side border with reset
<div className="border-none border-l border-solid border-l-gray-300" />

// Incorrect - missing border style
<div className="border border-gray-300" />
```

## Component Structure

### Head Placement

Always define `<Head />` inside `<Tailwind>` when using Tailwind CSS:

```tsx
<Html>
  <Tailwind config={{ presets: [pixelBasedPreset] }}>
    <Head />
    <Body>...</Body>
  </Tailwind>
</Html>
```

### PreviewProps

Only include props that the component actually uses:

```tsx
const Email = ({ source }: { source: string }) => {
  return (
    <div>
      <a href={source}>Click here</a>
    </div>
  );
};

Email.PreviewProps = {
  source: "https://example.com",
};
```

## Default Layout Structure

### Body

```tsx
<Body className="font-sans py-10 bg-gray-100">
```

### Container

White background, centered, left-aligned content:

```tsx
<Container className="mx-auto bg-white p-6 rounded">
```

### Footer

Include physical address, unsubscribe link, current year:

```tsx
<Section className="text-center text-gray-500 text-sm">
  <Text className="m-0">123 Main St, City, State 12345</Text>
  <Text className="m-0">&copy; {new Date().getFullYear()} Company Name</Text>
  <Link href={unsubscribeUrl}>Unsubscribe</Link>
</Section>
```

## Typography

### Titles

Bold, larger font, larger margins:

```tsx
<Heading className="text-2xl font-bold text-gray-900 mb-4">
```

### Paragraphs

Regular weight, smaller font, smaller margins:

```tsx
<Text className="text-base text-gray-700 mb-3">
```

### Hierarchy

Use consistent spacing that respects content hierarchy. Larger margins for headings, smaller for body text.

## Images

- Only include if user requests
- Content images: use responsive sizing (`w-full`, `h-auto`)
- Small icons (24-48px): fixed dimensions are acceptable
- Never distort user-provided images
- Never create SVG images
- Always use absolute URLs
- Include `alt` text for accessibility

```tsx
<Img
  src="https://example.com/image.png"
  alt="Description"
  className="w-full h-auto"
/>
```

## Buttons

Always use `box-border` to prevent padding overflow:

```tsx
<Button
  href="https://example.com"
  className="bg-blue-600 text-white px-5 py-3 rounded box-border block text-center no-underline"
>
  Click Here
</Button>
```

## Layout

### Mobile-First

Always design for mobile by default:

- Use stacked layouts that work on all screen sizes
- Max-width around 600px for main container
- Remove default spacing/margins/padding between list items

### Multi-Column

Use `Row` and `Column` components instead of flexbox/grid:

```tsx
<Row>
  <Column className="w-1/2">Left content</Column>
  <Column className="w-1/2">Right content</Column>
</Row>
```

## Dark Mode

When requested, use dark backgrounds:

- Container: black (`#000`)
- Background: dark gray (`#151516`)

```tsx
<Body className="bg-[#151516]">
  <Container className="bg-black text-white">
```

## Colors and Brand Consistency

### Gathering Brand Colors

Before creating emails, collect these colors from the user:

- **Primary**: Main brand color for buttons, links, key accents
- **Secondary**: Supporting color for borders, backgrounds, less prominent elements
- **Text**: Main body text color (suggest `#1a1a1a` for light backgrounds)
- **Text muted**: Secondary text like captions, footers (suggest `#6b7280`)
- **Background**: Email body background (suggest `#f4f4f5`)
- **Surface**: Container/card background (typically `#ffffff`)

### Tailwind Configuration File

Create a centralized Tailwind config file that all email templates import. Using `satisfies TailwindConfig` provides intellisense support for all configuration options:

```tsx
// emails/tailwind.config.ts
import { pixelBasedPreset, type TailwindConfig } from '@react-email/components';

export default {
  presets: [pixelBasedPreset],
  theme: {
    extend: {
      colors: {
        brand: {
          primary: '#007bff',
          secondary: '#6c757d',
        },
      },
    },
  },
} satisfies TailwindConfig;

// For non-Tailwind brand assets (optional)
export const brandAssets = {
  logo: {
    src: 'https://example.com/logo.png',
    alt: 'Company Name',
    width: 120,
  },
};
```

### Using Tailwind Config

Import the shared config in every email template:

```tsx
import tailwindConfig, { brandAssets } from './tailwind.config';

<Tailwind config={tailwindConfig}>
  <Body className="bg-gray-100 font-sans">
    <Container className="bg-white p-6">
      <Img src={brandAssets.logo.src} alt={brandAssets.logo.alt} width={brandAssets.logo.width} />
      <Button className="bg-brand-primary text-white">Action</Button>
    </Container>
  </Body>
</Tailwind>
```

### Maintaining Consistency

- **Always use the brand config** - Never hardcode colors in individual templates
- **Update config, not templates** - When colors change, update `tailwind.config.ts` only
- **Use semantic names** - `bg-brand-primary` not `bg-[#007bff]`
- **Ensure contrast** - Test that text is readable against backgrounds (WCAG AA: 4.5:1 ratio)

## Asset Locations

Direct users to place brand assets in appropriate locations:

- **Logo and images**: Host on a CDN or public URL. For local development, place in `emails/static/`.
- **Custom fonts**: Use the `Font` component with a web font URL (Google Fonts, Adobe Fonts, or self-hosted).

**Example prompt for gathering brand info:**
> "Before I create your email template, I need some brand information to ensure consistency. Could you provide:
> 1. Your primary brand color (hex code, e.g., #007bff)
> 2. Your logo URL (must be a publicly accessible PNG or JPEG)
> 3. Any secondary colors you'd like to use
> 4. Style preference (modern/minimal or classic/traditional)"

## Best Practices

1. **Make templates unique** - Not generic, tailored to user's request
2. **Test across clients** - Gmail, Outlook, Apple Mail, Yahoo Mail
3. **Keep file size under 102KB** - Gmail clips larger emails
4. **Use keywords strategically** - Increase engagement in email body
5. **Inline styles as fallback** - Some clients strip `<style>` tags



================================================
FILE: .claude/skills/better-auth-best-practices/SKILL.md
================================================
---
name: better-auth-best-practices
description: Skill for integrating Better Auth - the comprehensive TypeScript authentication framework.
---

# Better Auth Integration Guide

**Always consult [better-auth.com/docs](https://better-auth.com/docs) for code examples and latest API.**

Better Auth is a TypeScript-first, framework-agnostic auth framework supporting email/password, OAuth, magic links, passkeys, and more via plugins.

---

## Quick Reference

### Environment Variables
- `BETTER_AUTH_SECRET` - Encryption secret (min 32 chars). Generate: `openssl rand -base64 32`
- `BETTER_AUTH_URL` - Base URL (e.g., `https://example.com`)

Only define `baseURL`/`secret` in config if env vars are NOT set.

### File Location
CLI looks for `auth.ts` in: `./`, `./lib`, `./utils`, or under `./src`. Use `--config` for custom path.

### CLI Commands
- `npx @better-auth/cli@latest migrate` - Apply schema (built-in adapter)
- `npx @better-auth/cli@latest generate` - Generate schema for Prisma/Drizzle
- `npx @better-auth/cli mcp --cursor` - Add MCP to AI tools

**Re-run after adding/changing plugins.**

---

## Core Config Options

| Option | Notes |
|--------|-------|
| `appName` | Optional display name |
| `baseURL` | Only if `BETTER_AUTH_URL` not set |
| `basePath` | Default `/api/auth`. Set `/` for root. |
| `secret` | Only if `BETTER_AUTH_SECRET` not set |
| `database` | Required for most features. See adapters docs. |
| `secondaryStorage` | Redis/KV for sessions & rate limits |
| `emailAndPassword` | `{ enabled: true }` to activate |
| `socialProviders` | `{ google: { clientId, clientSecret }, ... }` |
| `plugins` | Array of plugins |
| `trustedOrigins` | CSRF whitelist |

---

## Database

**Direct connections:** Pass `pg.Pool`, `mysql2` pool, `better-sqlite3`, or `bun:sqlite` instance.

**ORM adapters:** Import from `better-auth/adapters/drizzle`, `better-auth/adapters/prisma`, `better-auth/adapters/mongodb`.

**Critical:** Better Auth uses adapter model names, NOT underlying table names. If Prisma model is `User` mapping to table `users`, use `modelName: "user"` (Prisma reference), not `"users"`.

---

## Session Management

**Storage priority:**
1. If `secondaryStorage` defined → sessions go there (not DB)
2. Set `session.storeSessionInDatabase: true` to also persist to DB
3. No database + `cookieCache` → fully stateless mode

**Cookie cache strategies:**
- `compact` (default) - Base64url + HMAC. Smallest.
- `jwt` - Standard JWT. Readable but signed.
- `jwe` - Encrypted. Maximum security.

**Key options:** `session.expiresIn` (default 7 days), `session.updateAge` (refresh interval), `session.cookieCache.maxAge`, `session.cookieCache.version` (change to invalidate all sessions).

---

## User & Account Config

**User:** `user.modelName`, `user.fields` (column mapping), `user.additionalFields`, `user.changeEmail.enabled` (disabled by default), `user.deleteUser.enabled` (disabled by default).

**Account:** `account.modelName`, `account.accountLinking.enabled`, `account.storeAccountCookie` (for stateless OAuth).

**Required for registration:** `email` and `name` fields.

---

## Email Flows

- `emailVerification.sendVerificationEmail` - Must be defined for verification to work
- `emailVerification.sendOnSignUp` / `sendOnSignIn` - Auto-send triggers
- `emailAndPassword.sendResetPassword` - Password reset email handler

---

## Security

**In `advanced`:**
- `useSecureCookies` - Force HTTPS cookies
- `disableCSRFCheck` - ⚠️ Security risk
- `disableOriginCheck` - ⚠️ Security risk  
- `crossSubDomainCookies.enabled` - Share cookies across subdomains
- `ipAddress.ipAddressHeaders` - Custom IP headers for proxies
- `database.generateId` - Custom ID generation or `"serial"`/`"uuid"`/`false`

**Rate limiting:** `rateLimit.enabled`, `rateLimit.window`, `rateLimit.max`, `rateLimit.storage` ("memory" | "database" | "secondary-storage").

---

## Hooks

**Endpoint hooks:** `hooks.before` / `hooks.after` - Array of `{ matcher, handler }`. Use `createAuthMiddleware`. Access `ctx.path`, `ctx.context.returned` (after), `ctx.context.session`.

**Database hooks:** `databaseHooks.user.create.before/after`, same for `session`, `account`. Useful for adding default values or post-creation actions.

**Hook context (`ctx.context`):** `session`, `secret`, `authCookies`, `password.hash()`/`verify()`, `adapter`, `internalAdapter`, `generateId()`, `tables`, `baseURL`.

---

## Plugins

**Import from dedicated paths for tree-shaking:**
```
import { twoFactor } from "better-auth/plugins/two-factor"
```
NOT `from "better-auth/plugins"`.

**Popular plugins:** `twoFactor`, `organization`, `passkey`, `magicLink`, `emailOtp`, `username`, `phoneNumber`, `admin`, `apiKey`, `bearer`, `jwt`, `multiSession`, `sso`, `oauthProvider`, `oidcProvider`, `openAPI`, `genericOAuth`.

Client plugins go in `createAuthClient({ plugins: [...] })`.

---

## Client

Import from: `better-auth/client` (vanilla), `better-auth/react`, `better-auth/vue`, `better-auth/svelte`, `better-auth/solid`.

Key methods: `signUp.email()`, `signIn.email()`, `signIn.social()`, `signOut()`, `useSession()`, `getSession()`, `revokeSession()`, `revokeSessions()`.

---

## Type Safety

Infer types: `typeof auth.$Infer.Session`, `typeof auth.$Infer.Session.user`.

For separate client/server projects: `createAuthClient<typeof auth>()`.

---

## Common Gotchas

1. **Model vs table name** - Config uses ORM model name, not DB table name
2. **Plugin schema** - Re-run CLI after adding plugins
3. **Secondary storage** - Sessions go there by default, not DB
4. **Cookie cache** - Custom session fields NOT cached, always re-fetched
5. **Stateless mode** - No DB = session in cookie only, logout on cache expiry
6. **Change email flow** - Sends to current email first, then new email

---

## Resources

- [Docs](https://better-auth.com/docs)
- [Options Reference](https://better-auth.com/docs/reference/options)
- [LLMs.txt](https://better-auth.com/llms.txt)
- [GitHub](https://github.com/better-auth/better-auth)
- [Init Options Source](https://github.com/better-auth/better-auth/blob/main/packages/core/src/types/init-options.ts)

================================================
FILE: .coderabbit.yaml
================================================
early_access: true
reviews:
  high_level_summary: true
  high_level_summary_placeholder: "@coderabbitai summary"
  auto_title_placeholder: "@coderabbitai title"
  poem: false
  request_changes_workflow: false
  collapse_walkthrough: true
  changed_files_summary: true
  sequence_diagrams: true
  assess_linked_issues: true
  related_issues: true
  related_prs: true
  auto_assign_reviewers: false
  auto_title_instructions: |
    Generate the PR title following the Conventional Commits format used in this monorepo.

    Format: `type(scope): description`

    Type — pick ONE based on the nature of the change:
    - `feat` — a new feature or capability
    - `fix` — a bug fix
    - `chore` — maintenance, config, CI, dependency updates, refactoring
    - `docs` — documentation only
    - `test` — adding or updating tests
    - `perf` — performance improvement
    - `ci` — CI/CD changes

    Scope — the monorepo area(s) affected. Use comma-separated values if multiple areas are touched:
    - `api` — apps/api
    - `dashboard` — apps/dashboard
    - `worker` — apps/worker
    - `ws` — apps/ws
    - `webhook` — apps/webhook
    - `shared` — packages/shared
    - `dal` — libs/dal
    - `application-generic` — libs/application-generic
    - `js` — packages/js
    - `react` — packages/react
    - `react-native` — packages/react-native
    - `nextjs` — packages/nextjs
    - `providers` — packages/providers
    - `framework` — packages/framework
    - `root` — repo-wide config, CI, tooling, or cross-cutting changes

    Description: concise, lowercase, imperative mood (e.g., "add retry logic for webhook delivery").

    Linear ticket: if the PR branch name or description contains a Linear ticket ID (e.g., XXX-1234), append ` fixes XXX-1234` at the end of the title.

    Examples:
    - `feat(dashboard): add workflow execution history page fixes NOV-456`
    - `fix(api,worker): handle missing environment gracefully`
    - `chore(root): update Node.js to v22`
  high_level_summary_instructions: |
    Write the summary to answer "What changed? Why was the change needed?" — matching the style of our PR template.

    Structure:

    1. **What changed**: A concise paragraph (80 words max) explaining the change in plain language. Focus on the "what" and "why", not individual files. Mention the motivation or problem being solved.

    2. **Affected areas**: List the monorepo scopes impacted (e.g., `api`, `dashboard`, `worker`, `shared`, `dal`, `js`, `react`, `providers`). For each scope, write one sentence describing what changed there. Omit scopes with no meaningful changes (e.g., skip auto-generated files, lockfile-only changes).

    3. **Key technical decisions**: Only include this section if there are notable architectural choices, new dependencies, database/schema changes, API contract changes, or breaking changes. Keep each bullet to one sentence.

    4. **Testing**: Briefly note what kind of testing applies — new unit tests, e2e tests, manual verification, or if no tests were added. If no tests are needed, say why.

    Rules:
    - Do NOT list every file changed. Summarize by scope/area instead.
    - Do NOT include contributor statistics or line counts.
    - Use plain, direct language. No filler words or marketing speak.
    - If the PR touches enterprise code (`/enterprise` or `*/ee/*`), explicitly call that out.
    - If the PR adds or updates npm dependencies, mention the package names and whether they are new or updated.
    - Keep the entire summary under 300 words.
  auto_review:
    enabled: true
    drafts: false
    ignore_title_keywords:
      - "WIP"
      - "DO NOT MERGE"
    auto_pause_after_reviewed_commits: 5
  path_instructions:
    - path: "apps/api/**"
      instructions: |
        Review with focus on security, authentication, and authorization.
        Ensure CQRS patterns (commands/queries) are followed.
        Check for proper error handling and input validation.
        Flag any endpoints missing auth guards.
    - path: "apps/dashboard/**"
      instructions: |
        Review with focus on UX, accessibility, and performance.
        Ensure components follow Radix UI + Tailwind patterns.
        Check for proper loading/error states.
        Verify React Query usage for data fetching.
    - path: "apps/worker/**"
      instructions: |
        Review with focus on reliability, idempotency, and error handling.
        Ensure Bull queue jobs handle failures gracefully.
        Check for proper retry logic and dead letter handling.
    - path: "libs/dal/**"
      instructions: |
        Review with focus on data integrity and query performance.
        Check for proper MongoDB indexes on new queries.
        Ensure repository pattern is followed.
        Flag any schema changes that need migrations.
    - path: "packages/shared/**"
      instructions: |
        Review with extra care — changes here affect the entire monorepo.
        Check for backward compatibility.
        Ensure exported types and constants are intentional.
    - path: "packages/js/**"
      instructions: |
        Review with focus on bundle size, browser compatibility, and public API surface.
        This is a client-facing SDK — breaking changes must be flagged.
    - path: "packages/providers/**"
      instructions: |
        Ensure the provider implements the standard channel interface.
        Check for proper error handling of provider API failures.
        Verify credentials are not hardcoded.
  finishing_touches:
    docstrings:
      enabled: false
    unit_tests:
      enabled: false
  pre_merge_checks:
    title:
      mode: "warning"
      requirements: |
        Title must follow Conventional Commits: `type(scope): description`
        Valid types: feat, fix, chore, docs, test, perf, ci
        Valid scopes: api, dashboard, worker, ws, webhook, shared, dal, application-generic, js, react, react-native, nextjs, providers, framework, root (comma-separated for multiple)
        Description should be lowercase and imperative mood.
        If a Linear ticket is referenced, title should end with `fixes XXX-XXXX`.
knowledge_base:
  linear:
    usage: enabled
  learnings:
    scope: global
  pull_requests:
    scope: global
  issues:
    scope: global
chat:
  auto_reply: true


================================================
FILE: .copilotignore
================================================
# GitHub Copilot ignore file — same exclusions as .cursorignore

# IDE configs — includes stale run configs for removed projects (apps/web, libs/embed, widget)
.idea/

# Playground / demo apps — not production code
playground/

# CI/CD, build infrastructure, and Docker configs
.github/
scripts/
docker/
.source/

# Inactive apps — no longer under active development
apps/inbound-mail/
apps/webhook/

# Auto-generated code — do not read or modify directly
libs/internal-sdk/

# Generated / large files that add noise without signal
pnpm-lock.yaml
**/dist/
**/*.min.js

# Generator templates — not production code
libs/automation/


================================================
FILE: .cursor/Dockerfile
================================================
# Dockerfile for Novu Development Environment - Optimized for Cursor AI Agent
# Ubuntu-based for full Cursor compatibility while maintaining significant optimizations
# Usage: docker build -t novu-cursor . && docker run -it --name novu-cursor -p 3000:3000 -p 4000:4000 -p 27017:27017 -p 6379:6379 novu-cursor

FROM ubuntu:22.04

# Prevent interactive prompts during package installation
ENV DEBIAN_FRONTEND=noninteractive

# Install essential dependencies and MongoDB/Redis in a single layer
RUN apt-get update && apt-get install -y \
    # Essential tools
    curl \
    wget \
    git \
    bash \
    ca-certificates \
    gnupg \
    lsb-release \
    sudo \
    # Build tools for native modules
    build-essential \
    python3 \
    python3-pip \
    # Process management
    supervisor \
    # Clean up immediately
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

# Install Node.js 22 using NodeSource repository (faster than using Node image)
RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \
    && apt-get install -y nodejs \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

# Install MongoDB and Redis from official repositories
RUN wget -qO - https://www.mongodb.org/static/pgp/server-7.0.asc | apt-key add - \
    && echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/7.0 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-7.0.list \
    && apt-get update \
    && apt-get install -y \
        mongodb-org \
        redis-server \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

# Install pnpm and TypeScript globally
RUN npm --no-update-notifier --no-fund --global install \
    pnpm@10.33.0 \
    typescript@5.6.2 \
    && npm cache clean --force

# Create minimal directory structure and set permissions (must be done as root)
RUN mkdir -p \
    /var/log/supervisor \
    /etc/supervisor/conf.d \
    /data/db \
    /data/redis \
    /workspace \
    /tmp/.pnpm-store \
    && chown mongodb:mongodb /data/db \
    && chown redis:redis /data/redis \
    && chmod 755 /data/db /data/redis

# Create optimized supervisor configuration (must be done as root)
RUN printf '[supervisord]\n\
nodaemon=false\n\
silent=true\n\
logfile=/dev/null\n\
pidfile=/var/run/supervisord.pid\n\
\n\
[include]\n\
files = /etc/supervisor/conf.d/*.conf\n' > /etc/supervisor/supervisord.conf \
    && printf '[program:mongodb]\n\
command=mongod --dbpath /data/db --bind_ip_all --port 27017 --replSet rs0 --quiet\n\
user=mongodb\n\
autorestart=true\n\
redirect_stderr=true\n\
stdout_logfile=/dev/null\n\
\n\
[program:redis]\n\
command=redis-server --bind 0.0.0.0 --port 6379 --dir /data/redis --save "" --logfile ""\n\
user=redis\n\
autorestart=true\n\
redirect_stderr=true\n\
stdout_logfile=/dev/null\n' > /etc/supervisor/conf.d/services.conf

# Create optimized startup script (must be done as root)
RUN printf '#!/bin/bash\n\
set -e\n\
echo "🚀 Novu Cursor Environment"\n\
supervisord -c /etc/supervisor/supervisord.conf\n\
timeout=15\n\
while ! mongosh --eval "db.runCommand('\''ping'\'')" >/dev/null 2>&1 && [ $timeout -gt 0 ]; do\n\
    sleep 1; timeout=$((timeout-1))\n\
done\n\
[ $timeout -gt 0 ] && mongosh --eval "try { rs.initiate() } catch(e) {}" >/dev/null 2>&1 || true\n\
timeout=10\n\
while ! redis-cli ping >/dev/null 2>&1 && [ $timeout -gt 0 ]; do\n\
    sleep 1; timeout=$((timeout-1))\n\
done\n\
echo "✅ MongoDB & Redis ready!"\n\
echo "🚀 Quick start: git clone https://github.com/novuhq/novu.git /workspace/novu"\n\
exec /bin/bash\n' > /usr/local/bin/start-dev.sh \
    && chmod +x /usr/local/bin/start-dev.sh

# Create non-root user as recommended for background agents
RUN useradd -m -s /bin/bash ubuntu \
    && echo "ubuntu ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers \
    && mkdir -p /home/ubuntu/.pnpm-store /home/ubuntu/.pnpm-cache \
    && chown -R ubuntu:ubuntu /home/ubuntu \
    && chown -R ubuntu:ubuntu /workspace

# Switch to non-root user
USER ubuntu

# Set working directory to home directory as recommended
WORKDIR /home/ubuntu

# Set up optimized environment variables
ENV NX_DAEMON=false \
    NODE_ENV=local \
    PNPM_STORE_DIR="/home/ubuntu/.pnpm-store" \
    PNPM_CACHE_DIR="/home/ubuntu/.pnpm-cache"

# Configure pnpm for speed and efficiency
RUN pnpm config set store-dir /home/ubuntu/.pnpm-store \
    && pnpm config set cache-dir /home/ubuntu/.pnpm-cache \
    && pnpm config set network-timeout 60000 \
    && pnpm config set fetch-retries 3

# Set workspace as working directory
WORKDIR /workspace

# Expose all necessary ports
EXPOSE 3000 4000 27017 6379

# Labels for identification
LABEL version="cursor-background-agent-1.0.0" \
      description="Novu development environment optimized for Cursor background agents"

# Health check for both services
HEALTHCHECK --interval=60s --timeout=5s --start-period=30s --retries=2 \
    CMD mongosh --eval "db.runCommand('ping')" >/dev/null 2>&1 && redis-cli ping >/dev/null 2>&1 || exit 1

ENTRYPOINT ["/usr/local/bin/start-dev.sh"]


================================================
FILE: .cursor/agents/impact-checker.md
================================================
---
name: impact-checker
description: Assesses blast radius of changes to shared packages (packages/shared, libs/dal, libs/application-generic). Use proactively before modifying shared code to identify downstream consumers that may break.
model: fast
readonly: true
---

You are a read-only impact analyst for the Novu monorepo.

When invoked with a set of changed files or symbols:

1. Identify which shared packages/libs are involved:
   - `packages/shared` — types, DTOs, enums used by everything
   - `libs/dal` — data access layer used by application-generic
   - `libs/application-generic` — business logic used by api, worker, ws
   - `packages/framework` — workflow SDK used by dashboard

2. Trace downstream consumers using this dependency graph:
   - `libs/dal` → `libs/application-generic` → `apps/api`, `apps/worker`, `apps/ws`
   - `packages/shared` → `apps/api`, `apps/worker`, `libs/application-generic`
   - `packages/js` → `packages/react` → `apps/dashboard`
   - `packages/framework` → `apps/dashboard`

3. Search for direct imports of changed symbols across affected apps

4. Flag any callers that may break due to the change (type changes, removed exports, renamed functions)

5. Note if `pnpm build` is required before changes take effect (yes — any change to `packages/`)

Report:
- Affected apps and packages
- Specific files that import changed symbols
- Risk level: **low** (additive only) / **medium** (behavior change) / **high** (breaking change)
- Whether a separate PR in `novuhq/packages-enterprise` is required (yes — if `enterprise/` is touched)


================================================
FILE: .cursor/agents/verifier.md
================================================
---
name: verifier
description: Validates completed work. Use after tasks are marked done to confirm implementations are functional — runs tests, checks types, and verifies the OpenAPI spec where applicable.
model: fast
---

You are a skeptical validator. Your job is to verify that work claimed as complete actually works.

When invoked:
1. Identify what was claimed to be completed
2. Confirm the implementation files exist and contain the expected changes
3. Run the relevant test suite for the affected app:
   - API/worker: `cd apps/api && pnpm test` or `cd apps/worker && pnpm test`
   - Dashboard: `cd apps/dashboard && pnpm test:e2e` (only if dashboard is running)
4. Run `pnpm check` in the affected app to confirm no lint or type errors
5. For API changes that touch endpoints: run `npm run lint:openapi` (requires API running)
6. Look for edge cases that may have been missed

Report:
- What was verified and passed
- What was claimed but is incomplete or broken
- Specific issues that need to be addressed

Do not accept claims at face value. Test everything you can.


================================================
FILE: .cursor/commands/code-review-checklist.md
================================================
# Code Review Checklist

## Overview
Comprehensive checklist for conducting thorough code reviews to ensure quality, security, and maintainability.

## Review Categories

### Functionality
- [ ] Code does what it's supposed to do
- [ ] Edge cases are handled
- [ ] Error handling is appropriate
- [ ] No obvious bugs or logic errors

### Code Quality
- [ ] Code is readable and well-structured
- [ ] Functions are small and focused
- [ ] Variable names are descriptive
- [ ] No code duplication
- [ ] Follows project conventions

### Security
- [ ] No obvious security vulnerabilities
- [ ] Input validation is present
- [ ] Sensitive data is handled properly
- [ ] No hardcoded secrets


================================================
FILE: .cursor/commands/create-pr.md
================================================
# Create PR

Create a well-structured pull request.

## Steps

1. Ensure all changes are committed and the branch is pushed to remote.
2. Write a PR description following `.cursor/rules/pullrequest.mdc` (includes diagram guidance for non-trivial changes).
3. Create the PR following the title format in `.cursor/rules/pullrequest.mdc`. Assign reviewers and link the related Linear issue.


================================================
FILE: .cursor/rules/api-property-optionality-hygiene.mdc
================================================
---
description: Fix ApiProperty/ApiPropertyOptional optionality mismatches in DTO files; use for scheduled batch fixes or DTO edits
globs: apps/api/**/*.dto.ts, libs/application-generic/**/*.dto.ts
alwaysApply: false
---

## ApiProperty optionality hygiene

Align `@ApiProperty` / `@ApiPropertyOptional` with TypeScript `?` on DTO properties. Detection script: `apps/api/scripts/check-api-property-optionality.ts`.

### Report

Read `.cursor/api-property-optionality-report.json` — only the first ~20 entries to select a batch. If the file is missing or stale, regenerate:

```bash
pnpm --filter @novu/api-service run check:api-property-optionality -- \
  --format json --write-report .cursor/api-property-optionality-report.json
```

Branch on `kind`, not `message` text:

| `kind` | Fix |
|--------|-----|
| `ts_optional_openapi_required` | Change to `@ApiPropertyOptional` (or add `required: false` to existing options) |
| `ts_required_openapi_optional` | Change to `@ApiProperty` (or add `required: true` to existing options) |

Merge into the existing options object — preserve `description`, `example`, `type`, `enum`, etc.

Example — `ts_optional_openapi_required`:

```ts
// Before
@ApiProperty({ description: 'User email' })
email?: string;

// After
@ApiPropertyOptional({ description: 'User email' })
email?: string;
```

### Workflow

1. Fix all issues in the next **1–2 files** from the report (cap ~8 issues). Edit each file once. Skip ambiguous cases (multiple optionality decorators on one property); note skips in the commit message.
2. Verify changed files: `pnpm check:api-property-optionality` exits 0.
3. Regenerate SDK: `pnpm --filter @novu/api-service build:generate`
4. Run e2e: `pnpm --filter @novu/api-service test:e2e:novu-v2`
5. If step 3 or 4 fails, fix the root cause (do not revert the decorator changes - address the downstream breakage). Re-run the failing step before committing.


Batch is complete when steps 3 and 4 succeed. When `issueCount` reaches 0, the task is done — no further commits needed.


================================================
FILE: .cursor/rules/api.mdc
================================================
---
description: Rules for working in the API service (NestJS backend)
globs: apps/api/**/*
alwaysApply: false
---

## API Service

**Stack:** NestJS + Express · MongoDB (via `libs/dal`) · Redis + Bull · ClickHouse (analytics/traces) · Clerk or Better Auth · `@nestjs/swagger`

**Run:** `pnpm start:api:dev` — port 3000, OpenAPI at `http://localhost:3000/openapi`

**Tests/lint:** see testing.mdc

**Key directories:**
```
apps/api/src/app/                          # Route controllers and modules
apps/api/src/ee/                           # Enterprise-only features
apps/api/migrations/                       # MongoDB migrations
apps/api/migrations/clickhouse-migrations/ # ClickHouse schema migrations
```

---

### API Service Conventions

**Controllers**
- Every protected route must use `@RequireAuthentication()`.
- Routes accessible via user API keys or the official SDK must also use `@ExternalApiAccessible`.
- Controller method names follow: `getEntityName`, `listEntityName`, `createEntityName`, `updateEntityName`, `deleteEntityName`.
- List endpoints must support pagination and use `@SdkUsePagination`.
- Group SDK endpoints with `@SdkGroupName` using `.` as the subresource separator (e.g., `Subscribers.Notifications`).

**Use-cases (CQRS)**
- Business logic lives in use-case classes, not controllers.
- Use-cases receive a typed command/query and return a typed result via `execute(command)`.
- Never put database queries directly in controllers.

**Database**
- Always use `libs/dal` repositories for all queries; see dal-repository.mdc for enforcement rules.

**OpenAPI**
- Every endpoint must have `@ApiOperation`, `@ApiResponse`, and `@ApiTags` decorators.
- Validate with `npm run lint:openapi` (run with API started) before submitting a PR.

**Migrations**
- Place migration scripts in `apps/api/migrations/<change-description>/<change-action>.ts`.
- Run via `npm run migration -- ./migrations/<path>.ts`.
- Never rename existing migration scripts — they are referenced in user-facing docs.

**Canonical example:** @apps/api/src/app/tenant/tenant.controller.ts


================================================
FILE: .cursor/rules/clickhouse.mdc
================================================
---
description: Rules for working with ClickHouse analytics and trace logging
globs:
  - "**/analytic-logs/**"
  - "**/clickhouse-migrations/**"
alwaysApply: false
---

### ClickHouse Conventions

**Service layer**
- Use `ClickHouseService` for single queries/inserts and `ClickHouseBatchService` for high-throughput writes.
- Both are registered as custom providers (`clickHouseService`, `clickHouseBatchService`) in each app's `shared.module.ts`.
- Never instantiate ClickHouse clients directly — always inject the providers.

**Repositories**
- All ClickHouse repositories extend `LogRepository` in `libs/application-generic/src/services/analytic-logs/`.
- Each repository has a corresponding schema file defining the table columns and types.
- Always include `_environmentId` and `_organizationId` in queries for tenant isolation (same pattern as MongoDB DAL).

**Migrations**
- ClickHouse migrations are numbered `.sql` files in `apps/api/migrations/clickhouse-migrations/`.
- Run locally with `cd apps/api && pnpm run clickhouse:migrate:local`.
- New migrations must be additive — never alter or drop columns that existing queries depend on. Use temp tables and exchange patterns for schema refactors (see migration 4/5 for the established pattern).

**Feature flags**
- Gate new ClickHouse-dependent behavior behind a feature flag (see `packages/shared/src/types/feature-flags.ts` for existing flags like `IS_CLICKHOUSE_BATCHING_ENABLED`).


================================================
FILE: .cursor/rules/context-engineering.mdc
================================================
---
description: Context quality checklist for system prompts and agent instructions
globs:
  - "**/tools/**/*.ts"
  - "**/prompts/**/*.ts"
  - "**/*.prompt.ts"
  - "AGENTS.md"
  - ".cursor/rules/**"
  - ".cursor/skills/**"
  - ".agents/skills/**"
alwaysApply: false
---

### Context Engineering Checklist

When writing or reviewing agent instructions, tool descriptions, or prompts:

- Is this explained elsewhere? → Consolidate to one location
- Would a senior dev infer this? → Remove if obvious
- Can this be an example instead? → One example beats three paragraphs of rules
- Is this defensive repetition ("MUST", "NEVER", "CRITICAL")? → State once, remove emphasis
- Does this duplicate a tool's own description? → Keep in tool description only
- Are there prescriptive step lists? → Compress to a sentence
- Is the altitude right? → Specific heuristics, not hardcoded logic or vague guidance
- Is this stable across requests? → Move to cached/static portion


================================================
FILE: .cursor/rules/dal-repository.mdc
================================================
---
description: Rules for working with DAL repositories in the Novu monorepo
globs: libs/dal/**/*.ts, **/repositories/**/*.ts
alwaysApply: false
---

### DAL Repository Rules

#### Choosing a base class

- **New repositories must extend `BaseRepositoryV2`** — it enforces required field selection and provides auto-inferred return types (`Pick<Entity, Keys>`).
- **Existing repositories stay on `BaseRepository`** — `BaseRepository` is deprecated but must not be changed; all 32 existing repos continue to extend it.
- Do NOT extend `BaseRepository` for any new repository going forward.

#### BaseRepositoryV2 — required `select`

- Every read method (`find`, `findOne`, `findById`, `findBatch`, `findWithCursorBasedPagination`) requires an explicit `select` argument — there is no default `SELECT *`.
- Use array syntax as the default: `find(query, ['_id', 'name', 'status'])`. Array syntax returns exactly the listed fields — `_id` is excluded unless explicitly included.
- Use object syntax when you need MongoDB-style projections where `_id` is included by default: `findOne(query, { name: 1, email: 1 })`, or explicitly excluded: `findOne(query, { _id: 0, name: 1 })`.
- Exclusion projections for non-`_id` fields (e.g. `{ name: 0 }`) are intentionally unsupported — they are a compile error.
- Return types are automatically inferred as `Pick<Entity, Keys>` — do not manually annotate the return type.
- Use `select: '*'` to retrieve all fields with a fully-typed `Entity` return (instead of a `Pick`). All five read methods support this overload: `find(query, '*')`, `findOne(query, '*')`, `findById(id, '*')`, `findBatch(query, '*')`, and `findWithCursorBasedPagination({ select: '*', ... })`.
- Omitting `select` entirely is still a compile error — `'*'` is the explicit opt-in for SELECT *.

#### Enforcement (applies to both V1 and V2)

- **Never use `_model` or `MongooseModel` directly** in repository methods. Always use the inherited methods (`update`, `find`, `findOne`, `delete`, `create`, `bulkWrite`, etc.) which enforce `_environmentId` / `_organizationId` via the `EnforceEnvOrOrgIds` type.
- All query methods must include `_environmentId` or `_organizationId` in their filter to satisfy the enforcement type constraint.
- When adding new repository methods that need `$push`, `$pull`, or other update operators, pass the `environmentId` as a parameter and use `this.update()` with the enforcement fields.
- For bulk operations, use `this.bulkWrite()` instead of `this._model.updateMany()`.
- **Transactions**: start via `repository.withTransaction(async (session) => { ... })` and pass `session` to every repo call inside it (e.g. `repo.findOne(query, select, { session })`). Run all operations sequentially — parallel execution (`Promise.all`, etc.) inside a transaction is undefined behaviour in Mongoose.


================================================
FILE: .cursor/rules/dashboard.mdc
================================================
---
description: Rules for working in the Dashboard (React frontend)
globs: apps/dashboard/**/*
alwaysApply: false
---

## Dashboard

**Stack:** React 19 + Vite + TypeScript · Radix UI · Tailwind CSS · shadcn/ui · TanStack Query · React Router · motion/react · port 4201

**Run:** Do not build or start the dashboard — the user already has it running on port 4201. Check types via Cursor diagnostics.

**Tests/lint:** see testing.mdc

**Key directories:**
```
apps/dashboard/src/components/   # Shared UI components
apps/dashboard/src/pages/        # Route-level page components
apps/dashboard/src/hooks/        # Custom React hooks
apps/dashboard/src/api/          # API client and query definitions
apps/dashboard/src/ee/           # Enterprise-only features
```

**Agent sign-in:** A default user and org are pre-seeded — sign in at `http://localhost:4201/auth/sign-in`, do not go through onboarding unless asked.

| Field | Value |
|-------|-------|
| Email | `agent@novu.co` |
| Password | `Agent123!@#` |
| Organization | `Agent Organization` |

---

### Dashboard Conventions

**Data fetching**
- Use TanStack Query (`useQuery`, `useMutation`) for all server state — do not use `useEffect` + `fetch` directly.
- Co-locate query keys and fetcher functions in `src/api/` or alongside the feature they belong to.
- Invalidate related queries after mutations; do not manually update the cache unless necessary for optimistic UI.

**UI components**
- Build on Radix UI primitives and the existing shadcn/ui component set in `src/components/ui/`.
- Avoid adding new third-party UI libraries without discussion.
- Use Tailwind utility classes for all styling; avoid inline `style` props except for dynamic values that cannot be expressed as utilities.

**Routing**
- Use React Router v6 `<Link>` and `useNavigate` — do not use `window.location` for in-app navigation.

**Canonical example:** @apps/dashboard/src/components/environments/edit-environment-sheet.tsx


================================================
FILE: .cursor/rules/dependency-graph.mdc
================================================
---
description: Novu monorepo dependency graph — use when assessing blast radius before changing shared code
globs:
alwaysApply: false
---

## Dependency Graph

```mermaid
graph TD
    subgraph apps [Apps]
        api[api]
        dashboard[dashboard]
        worker[worker]
        ws[ws]
    end
    subgraph libs [Libs - internal only]
        dal[dal]
        appGeneric[application-generic]
    end
    subgraph pkgs [Packages - published to NPM]
        shared[shared]
        framework[framework]
        jsSDK[js]
        reactSDK[react]
    end
    api --> appGeneric --> dal
    worker --> appGeneric
    ws --> appGeneric
    api --> shared
    worker --> shared
    dashboard --> reactSDK --> jsSDK --> shared
    dashboard --> framework
```


================================================
FILE: .cursor/rules/infrastructure.mdc
================================================
---
description: Infrastructure setup, Docker services, and environment configuration
globs:
  - "docker/**/*"
  - "*.env*"
  - ".env*"
  - "**/docker-compose*"
alwaysApply: false
---

## Infrastructure

**Start all services:** `docker compose -f docker/local/docker-compose.yml up -d`

**Agent environments** use a separate compose file — restart after a reboot with:
`docker compose -f docker/local/docker-compose.agent.yml up -d`

| Service | Port | Purpose |
|---------|------|---------|
| MongoDB | 27017 | Primary database |
| Redis | 6379 | Caching + Bull queues |
| ClickHouse | 8123 (HTTP) / 9000 (native) | Analytics, activity feed, traces |
| LocalStack | 4566 | S3 emulation (optional) |

**Services needed per task:**
- Dashboard UI only: nothing (user has it running on port 4201)
- API endpoints: `pnpm start:api:dev`
- Notification flows: API + `pnpm start:worker`
- Real-time features: API + Worker + `pnpm start:ws`
- Full stack: `docker compose` + all of the above

Run `pnpm build` before starting services only if changes were made to `libs/`, `packages/`, or `enterprise/`.

================================================
FILE: .cursor/rules/novu.mdc
================================================
---
description: 
globs: 
alwaysApply: true
---
### Novu Conventions

- File/directory names: lowercase with dashes (`components/auth-wizard`)
- Named exports for all components
- TypeScript: `interface` on the backend, `type` on the frontend — this is the project convention and overrides any general "prefer interfaces" guidance
- Blank line before every `return` statement
- Animations: import from `"motion/react"` — not `"framer-motion"` or `"motion-react"`
- No nested ternaries


================================================
FILE: .cursor/rules/packages.mdc
================================================
---
description: Rules for working in the shared NPM packages
globs: packages/**/*
alwaysApply: false
---

### Shared Packages Conventions

**Public API / semver**
- Packages are published to NPM — treat all exported symbols as public API.
- Follow semver: breaking changes require a major bump, new exports are minor, fixes are patch.
- Deprecate symbols with `@deprecated` JSDoc before removing them.

**`packages/shared`**
- Contains types, DTOs, enums, and utility functions used across both frontend and backend.
- Keep this package free of runtime side-effects; it is imported by both Node.js services and browser bundles.

**`packages/framework`**
- Defines the code-first workflow SDK — the interface between user-defined workflows and Novu's engine.
- Changes here affect the developer-facing API; write clear, minimal abstractions.

**`packages/providers`**
- Each provider implements a standard channel interface (e.g., `IEmailProvider`, `ISmsProvider`).
- New providers must be registered in the provider index and follow the existing file structure.


================================================
FILE: .cursor/rules/pullrequest.mdc
================================================
---
description: When creating a new pull request on GitHub, use this to specify the contents
alwaysApply: false
---

### Pull Request Rules

**Title format**: `type(scope): Description fixes NOV-<ticket-id>`

- Examples: `feat(dashboard): add workflow trigger button fixes NOV-123`, `fix(api-service): handle null subscriber case fixes NOV-456`
- Omit the `fixes NOV-XXX` suffix when no Linear ticket is in context

**Scopes**: `dashboard`, `api-service`, `worker`, `shared`, `js`, `react`, `react-native`, `nextjs`, `providers`, `root`

**Description**: Summarize what changed and why. List breaking changes. Add screenshots for UI changes. For non-trivial logic or architecture changes, include a concise Mermaid diagram (flow, sequence, or component) so reviewers can grasp the change at a glance.

**Enterprise packages**: When changes touch `enterprise/`, also open a matching PR in `novuhq/packages-enterprise` on a branch from `next`.


================================================
FILE: .cursor/rules/testing.mdc
================================================
---
description: Rules for writing and running tests
globs:
  - "**/*.spec.ts"
  - "**/*.test.ts"
  - "**/e2e/**"
alwaysApply: false
---

### Testing Conventions

**General**
- Match the test style and tooling already used in the same app (Mocha for API/worker, Playwright for dashboard).
- Shared test utilities and setup helpers live in `libs/testing` — use them instead of rolling custom harnesses.
- Never mock MongoDB models directly; use the test harness in `libs/testing` which manages real database state.

**API (`apps/api`)**
- Unit tests: `cd apps/api && pnpm test` (Mocha + ts-node, matches `src/**/*.spec.ts`)
- Lint: `cd apps/api && pnpm check`
- OpenAPI validation: `npm run lint:openapi` (requires API running)
- E2E tests: see `.cursor/skills/run-api-e2e-tests/SKILL.md`
- Bootstrap a NestJS testing module for integration-style unit tests rather than mocking the entire DI container.

**Dashboard (`apps/dashboard`)**
- E2E tests: `cd apps/dashboard && pnpm test:e2e` (Playwright — start dashboard first)
- Lint: `cd apps/dashboard && pnpm check`
- Tests live in `apps/dashboard/tests/` (or `e2e/`).

**Worker (`apps/worker`)**
- Unit tests: `cd apps/worker && pnpm test` (Mocha + ts-node, matches `src/**/**/*.spec.ts`)
- Lint: `cd apps/worker && pnpm check`
- The worker must be running (`pnpm start:worker`) when testing end-to-end notification flows.


================================================
FILE: .cursor/rules/worker.mdc
================================================
---
description: Rules for working in the Worker service (background job processing)
globs: apps/worker/**/*
alwaysApply: false
---

## Worker Service

**Stack:** NestJS · Bull + Redis · ClickHouse (execution traces) · cron-parser

**Run:** `pnpm start:worker` — only needed when testing notification triggering; skip for dashboard/API-only tasks.

**Tests/lint:** see testing.mdc

**Key directories:**
```
apps/worker/src/app/workflow/usecases/   # Workflow step execution usecases
apps/worker/src/app/workflow/services/   # Queue consumers and job handlers
```

---

### Worker Service Conventions

**Use-case structure**
- Each notification channel has a dedicated `send-message` use-case under `apps/worker/src/app/workflow/usecases/`.
- Follow the CQRS pattern: `execute(command: CommandClass)` returning a typed result.
- Keep channel-specific logic isolated — do not share send-message logic across channels.

**Queue / Bull**
- Job consumers are registered in NestJS modules using Bull's `@Process` decorator.
- Retry logic and failure handling are configured at the queue level in `libs/application-generic` — do not duplicate this in worker use-cases.
- Always handle job failures gracefully and log execution details via `CreateExecutionDetails`.


================================================
FILE: .cursor/rules/ws.mdc
================================================
---
description: Rules for working in the WebSocket service (real-time delivery)
globs: apps/ws/**/*
alwaysApply: false
---

## WebSocket Service

**Stack:** NestJS · Socket.io with Redis adapter (horizontal scaling)

**Run:** `pnpm start:ws` — only needed for real-time features (live inbox, read/unread sync). Skip for most tasks.

**Key directories:**
```
apps/ws/src/   # Socket gateways, guards, and event handlers
```

### WebSocket Conventions

**Authentication:** Clients authenticate via JWT on the Socket.io handshake.

**Scaling:** The Redis adapter is required in production — multiple ws instances share socket state through it.

**Client packages:** Events emitted here are consumed by `@novu/js` and `@novu/react`. New Socket.io event types require matching updates in both packages.


================================================
FILE: .cursor/scripts/dead-code/knip.config.jsonc
================================================
{
  "$schema": "https://unpkg.com/knip@5/schema.json",
  "github-actions": false,
  "ignore": [
    "**/*.spec.ts",
    "**/*.e2e*.ts",
    "**/*.test.ts",
    "**/test/**",
    "**/tests/**",
    "**/e2e/**",
    "**/__mocks__/**",
    "**/fixtures/**",
    "**/migrations/**",
    "**/*.d.ts",
    "**/webpack.config.*",
    "**/swc-register.*",
    "**/newrelic.ts",
    "**/scripts/**",
    "**/self-hosted/**",
    "libs/internal-sdk/**",
    "libs/maily-core/**", // cspell:ignore maily
    "packages/js/**",
    "packages/framework/**",
    ".github/**",
    ".cursor/**",
    ".cursor-artifacts/**",
    "docker/**",
    "playground/**",
    "enterprise/**"
  ],
  "exclude": [
    "classMembers",
    "nsExports",
    "nsTypes",
    "enumMembers",
    "types",
    "dependencies",
    "devDependencies",
    "binaries",
    "unlisted",
    "unresolved",
    "duplicates"
  ],
  "entry": [
    "apps/*/src/main.{ts,tsx}",
    "apps/*/src/**/*.module.ts",
    "libs/*/src/index.ts",
    "packages/*/src/index.ts"
  ],
  "project": [
    "apps/*/src/**/*.{ts,tsx}",
    "libs/*/src/**/*.ts",
    "packages/*/src/**/*.ts"
  ]
}


================================================
FILE: .cursor/scripts/dead-code/scan.sh
================================================
#!/usr/bin/env bash
# Run knip and save Markdown output for the AI agent.
#
# Usage:  bash .cursor/scripts/dead-code/scan.sh
# Env:    KNIP_ARGS  — extra arguments (e.g. "--workspace apps/api")

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
OUT_DIR="$REPO_ROOT/.cursor-artifacts/deadcode"

mkdir -p "$OUT_DIR"
cd "$REPO_ROOT"

CONFIG_REL="${SCRIPT_DIR#"$REPO_ROOT/"}/knip.config.jsonc"

echo "Running knip (config: $CONFIG_REL)..."
KNIP_EXIT=0
npx knip \
  --config="$CONFIG_REL" \
  --reporter markdown \
  --no-progress \
  ${KNIP_ARGS:-} \
  > "$OUT_DIR/knip.md" \
  2> "$OUT_DIR/knip-stderr.txt" \
  || KNIP_EXIT=$?

if [ ! -s "$OUT_DIR/knip.md" ] && [ -s "$OUT_DIR/knip-stderr.txt" ]; then
  echo "ERROR: knip produced no output (exit code $KNIP_EXIT)." >&2
  echo "Stderr:" >&2
  cat "$OUT_DIR/knip-stderr.txt" >&2
  exit 1
fi

echo "Done (exit code $KNIP_EXIT). Output: $OUT_DIR/knip.md"


================================================
FILE: .cursor/settings.json
================================================
{
  "plugins": {
    "figma": {
      "enabled": true
    },
    "linear": {
      "enabled": true
    }
  }
}


================================================
FILE: .cursor/skills/better-auth-best-practices/SKILL.md
================================================
---
name: better-auth-best-practices
description: Skill for integrating Better Auth - the comprehensive TypeScript authentication framework.
---

# Better Auth Integration Guide

**Always consult [better-auth.com/docs](https://better-auth.com/docs) for code examples and latest API.**

Better Auth is a TypeScript-first, framework-agnostic auth framework supporting email/password, OAuth, magic links, passkeys, and more via plugins.

---

## Quick Reference

### Environment Variables
- `BETTER_AUTH_SECRET` - Encryption secret (min 32 chars). Generate: `openssl rand -base64 32`
- `BETTER_AUTH_URL` - Base URL (e.g., `https://example.com`)

Only define `baseURL`/`secret` in config if env vars are NOT set.

### File Location
CLI looks for `auth.ts` in: `./`, `./lib`, `./utils`, or under `./src`. Use `--config` for custom path.

### CLI Commands
- `npx @better-auth/cli@latest migrate` - Apply schema (built-in adapter)
- `npx @better-auth/cli@latest generate` - Generate schema for Prisma/Drizzle
- `npx @better-auth/cli mcp --cursor` - Add MCP to AI tools

**Re-run after adding/changing plugins.**

---

## Core Config Options

| Option | Notes |
|--------|-------|
| `appName` | Optional display name |
| `baseURL` | Only if `BETTER_AUTH_URL` not set |
| `basePath` | Default `/api/auth`. Set `/` for root. |
| `secret` | Only if `BETTER_AUTH_SECRET` not set |
| `database` | Required for most features. See adapters docs. |
| `secondaryStorage` | Redis/KV for sessions & rate limits |
| `emailAndPassword` | `{ enabled: true }` to activate |
| `socialProviders` | `{ google: { clientId, clientSecret }, ... }` |
| `plugins` | Array of plugins |
| `trustedOrigins` | CSRF whitelist |

---

## Database

**Direct connections:** Pass `pg.Pool`, `mysql2` pool, `better-sqlite3`, or `bun:sqlite` instance.

**ORM adapters:** Import from `better-auth/adapters/drizzle`, `better-auth/adapters/prisma`, `better-auth/adapters/mongodb`.

**Critical:** Better Auth uses adapter model names, NOT underlying table names. If Prisma model is `User` mapping to table `users`, use `modelName: "user"` (Prisma reference), not `"users"`.

---

## Session Management

**Storage priority:**
1. If `secondaryStorage` defined → sessions go there (not DB)
2. Set `session.storeSessionInDatabase: true` to also persist to DB
3. No database + `cookieCache` → fully stateless mode

**Cookie cache strategies:**
- `compact` (default) - Base64url + HMAC. Smallest.
- `jwt` - Standard JWT. Readable but signed.
- `jwe` - Encrypted. Maximum security.

**Key options:** `session.expiresIn` (default 7 days), `session.updateAge` (refresh interval), `session.cookieCache.maxAge`, `session.cookieCache.version` (change to invalidate all sessions).

---

## User & Account Config

**User:** `user.modelName`, `user.fields` (column mapping), `user.additionalFields`, `user.changeEmail.enabled` (disabled by default), `user.deleteUser.enabled` (disabled by default).

**Account:** `account.modelName`, `account.accountLinking.enabled`, `account.storeAccountCookie` (for stateless OAuth).

**Required for registration:** `email` and `name` fields.

---

## Email Flows

- `emailVerification.sendVerificationEmail` - Must be defined for verification to work
- `emailVerification.sendOnSignUp` / `sendOnSignIn` - Auto-send triggers
- `emailAndPassword.sendResetPassword` - Password reset email handler

---

## Security

**In `advanced`:**
- `useSecureCookies` - Force HTTPS cookies
- `disableCSRFCheck` - ⚠️ Security risk
- `disableOriginCheck` - ⚠️ Security risk  
- `crossSubDomainCookies.enabled` - Share cookies across subdomains
- `ipAddress.ipAddressHeaders` - Custom IP headers for proxies
- `database.generateId` - Custom ID generation or `"serial"`/`"uuid"`/`false`

**Rate limiting:** `rateLimit.enabled`, `rateLimit.window`, `rateLimit.max`, `rateLimit.storage` ("memory" | "database" | "secondary-storage").

---

## Hooks

**Endpoint hooks:** `hooks.before` / `hooks.after` - Array of `{ matcher, handler }`. Use `createAuthMiddleware`. Access `ctx.path`, `ctx.context.returned` (after), `ctx.context.session`.

**Database hooks:** `databaseHooks.user.create.before/after`, same for `session`, `account`. Useful for adding default values or post-creation actions.

**Hook context (`ctx.context`):** `session`, `secret`, `authCookies`, `password.hash()`/`verify()`, `adapter`, `internalAdapter`, `generateId()`, `tables`, `baseURL`.

---

## Plugins

**Import from dedicated paths for tree-shaking:**
```
import { twoFactor } from "better-auth/plugins/two-factor"
```
NOT `from "better-auth/plugins"`.

**Popular plugins:** `twoFactor`, `organization`, `passkey`, `magicLink`, `emailOtp`, `username`, `phoneNumber`, `admin`, `apiKey`, `bearer`, `jwt`, `multiSession`, `sso`, `oauthProvider`, `oidcProvider`, `openAPI`, `genericOAuth`.

Client plugins go in `createAuthClient({ plugins: [...] })`.

---

## Client

Import from: `better-auth/client` (vanilla), `better-auth/react`, `better-auth/vue`, `better-auth/svelte`, `better-auth/solid`.

Key methods: `signUp.email()`, `signIn.email()`, `signIn.social()`, `signOut()`, `useSession()`, `getSession()`, `revokeSession()`, `revokeSessions()`.

---

## Type Safety

Infer types: `typeof auth.$Infer.Session`, `typeof auth.$Infer.Session.user`.

For separate client/server projects: `createAuthClient<typeof auth>()`.

---

## Common Gotchas

1. **Model vs table name** - Config uses ORM model name, not DB table name
2. **Plugin schema** - Re-run CLI after adding plugins
3. **Secondary storage** - Sessions go there by default, not DB
4. **Cookie cache** - Custom session fields NOT cached, always re-fetched
5. **Stateless mode** - No DB = session in cookie only, logout on cache expiry
6. **Change email flow** - Sends to current email first, then new email

---

## Resources

- [Docs](https://better-auth.com/docs)
- [Options Reference](https://better-auth.com/docs/reference/options)
- [LLMs.txt](https://better-auth.com/llms.txt)
- [GitHub](https://github.com/better-auth/better-auth)
- [Init Options Source](https://github.com/better-auth/better-auth/blob/main/packages/core/src/types/init-options.ts)

================================================
FILE: .cursor/skills/enterprise-submodule/SKILL.md
================================================
# Enterprise Submodule Setup

Use this skill when making changes to the enterprise submodule (`.source/`) or enterprise packages (`enterprise/packages/*`), or when the enterprise submodule needs to be initialized/updated.

## Overview

Novu uses a git submodule at `.source` pointing to `git@github.com:novuhq/packages-enterprise.git`. The enterprise packages in `enterprise/packages/` have their `src` directories symlinked to `.source/<package>/src`.

## Initial Setup

1. **Configure git for submodule recursion:**
   ```bash
   git c
Download .txt
Showing preview only (479K chars total). Download the full file or copy to clipboard to get everything.
gitextract_65y55uur/

├── .agents/
│   └── skills/
│       ├── email-best-practices/
│       │   ├── SKILL.md
│       │   └── resources/
│       │       ├── branding.md
│       │       ├── compliance.md
│       │       ├── deliverability.md
│       │       ├── email-capture.md
│       │       ├── email-types.md
│       │       ├── list-management.md
│       │       ├── marketing-emails.md
│       │       ├── sending-reliability.md
│       │       ├── transactional-email-catalog.md
│       │       ├── transactional-emails.md
│       │       └── webhooks-events.md
│       └── react-email/
│           ├── SKILL.md
│           ├── TESTS.md
│           └── references/
│               ├── COMPONENTS.md
│               ├── I18N.md
│               ├── PATTERNS.md
│               ├── SENDING.md
│               └── STYLING.md
├── .claude/
│   └── skills/
│       └── better-auth-best-practices/
│           └── SKILL.md
├── .coderabbit.yaml
├── .copilotignore
├── .cursor/
│   ├── Dockerfile
│   ├── agents/
│   │   ├── impact-checker.md
│   │   └── verifier.md
│   ├── commands/
│   │   ├── code-review-checklist.md
│   │   └── create-pr.md
│   ├── rules/
│   │   ├── api-property-optionality-hygiene.mdc
│   │   ├── api.mdc
│   │   ├── clickhouse.mdc
│   │   ├── context-engineering.mdc
│   │   ├── dal-repository.mdc
│   │   ├── dashboard.mdc
│   │   ├── dependency-graph.mdc
│   │   ├── infrastructure.mdc
│   │   ├── novu.mdc
│   │   ├── packages.mdc
│   │   ├── pullrequest.mdc
│   │   ├── testing.mdc
│   │   ├── worker.mdc
│   │   └── ws.mdc
│   ├── scripts/
│   │   └── dead-code/
│   │       ├── knip.config.jsonc
│   │       └── scan.sh
│   ├── settings.json
│   └── skills/
│       ├── better-auth-best-practices/
│       │   └── SKILL.md
│       ├── enterprise-submodule/
│       │   └── SKILL.md
│       └── run-api-e2e-tests/
│           └── SKILL.md
├── .cursorignore
├── .deepsource.toml
├── .devcontainer/
│   ├── Dockerfile
│   ├── devcontainer.json
│   └── docker-compose.yml
├── .editorconfig
├── .github/
│   ├── CODEOWNERS
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.yml
│   │   ├── docs_feedback.yml
│   │   ├── feature_request.yml
│   │   └── polishing.yml
│   ├── ISSUE_TEMPLATE.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── actions/
│   │   ├── cache/
│   │   │   └── action.yml
│   │   ├── checkout-submodules/
│   │   │   └── action.yml
│   │   ├── free-space/
│   │   │   └── action.yml
│   │   ├── run-api/
│   │   │   └── action.yml
│   │   ├── run-backend/
│   │   │   └── action.yml
│   │   ├── setup-project/
│   │   │   └── action.yml
│   │   ├── setup-project-minimal/
│   │   │   └── action.yml
│   │   ├── setup-redis-cluster/
│   │   │   └── action.yml
│   │   ├── slack-notify-on-failure/
│   │   │   └── action.yml
│   │   ├── start-localstack/
│   │   │   └── action.yml
│   │   └── validate-openapi/
│   │       └── action.yml
│   ├── labeler.yml
│   └── workflows/
│       ├── check-only.yml
│       ├── check-submodule-sync-merge.yaml
│       ├── check-submodule-sync-pr.yaml
│       ├── codeql-analysis.yml
│       ├── community-label.yml
│       ├── contributor-checks.yml
│       ├── conventional-commit.yml
│       ├── deploy.yml
│       ├── deployment-summary.yml
│       ├── dev-deploy-dashboard.yml
│       ├── dev-deploy-inbound-mail.yml
│       ├── issue-label.yml
│       ├── jarvis.yml
│       ├── on-pr-change.yml
│       ├── on-pr.yml
│       ├── on-push-trigger.yml
│       ├── pr-labeler.yml
│       ├── pr-manager.yml
│       ├── prepare-cloud-release.yaml
│       ├── prepare-enterprise-self-hosted-release.yml
│       ├── prepare-self-hosted-release.yml
│       ├── preview-packages.yml
│       ├── prod-deploy-inbound-mail.yml
│       ├── release-packages.yml
│       ├── reusable-api-e2e.yml
│       ├── reusable-dashboard-deploy.yml
│       ├── reusable-dashboard-e2e.yml
│       ├── reusable-inbound-mail-e2e.yml
│       ├── reusable-webhook-e2e.yml
│       ├── reusable-worker-e2e.yml
│       ├── reusable-ws-e2e.yml
│       ├── rollback.yml
│       └── scripts/
│           ├── add-triage-label.js
│           ├── community-contribution-label.js
│           ├── is-community-contributor.js
│           ├── stop-only.sh
│           └── validate-submodule-sync.sh
├── .gitignore
├── .gitmodules
├── .husky/
│   └── pre-commit
├── .idea/
│   ├── .gitignore
│   ├── aws.xml
│   ├── codeStyles/
│   │   ├── Project.xml
│   │   └── codeStyleConfig.xml
│   ├── discord.xml
│   ├── inspectionProfiles/
│   │   └── Project_Default.xml
│   ├── jsLibraryMappings.xml
│   ├── modules.xml
│   ├── novu.iml
│   ├── nx-angular-config.xml
│   ├── runConfigurations/
│   │   ├── API.xml
│   │   ├── API___TEST.xml
│   │   ├── APPLICATION_GENERIC.xml
│   │   ├── DAL.xml
│   │   ├── DAL2.xml
│   │   ├── DOCS.xml
│   │   ├── EE_AUTH.xml
│   │   ├── EMBED.xml
│   │   ├── RUN_LOCAL_ENV.xml
│   │   ├── RUN_TEST_ENV.xml
│   │   ├── SHARED.xml
│   │   ├── SHARED_WEB.xml
│   │   ├── TESTING.xml
│   │   ├── WEB.xml
│   │   ├── WEBHOOK.xml
│   │   ├── WEB___CYPRESS.xml
│   │   ├── WIDGET.xml
│   │   ├── WIDGET_CLI.xml
│   │   ├── WIDGET___CYPRESS.xml
│   │   ├── WIDGET___TEST.xml
│   │   ├── WORKER.xml
│   │   ├── WORKER___TEST.xml
│   │   ├── WS.xml
│   │   ├── WS___TEST.xml
│   │   └── _template__of_Mocha.xml
│   ├── swagger-settings.xml
│   └── vcs.xml
├── .markdownlint.jsonc
├── .npmrc
├── .npmrc-cloud
├── .nvmrc
├── .nxignore
├── .vscode/
│   ├── extensions.json
│   ├── launch.json
│   ├── settings.json
│   └── tasks.json
├── AGENTS.md
├── CITATION.cff
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── EE-PACKAGES-LICENSE
├── LICENSE-ENTERPRISE
├── LICENSE-MIT
├── README.md
├── SECURITY.md
├── _templates/
│   ├── module/
│   │   └── new/
│   │       ├── controller.ejs.t
│   │       ├── module.ejs.t
│   │       ├── prompt.ejs.t
│   │       └── usecase-index.ejs.t
│   └── usecase/
│       └── new/
│           ├── command.ejs.t
│           ├── import-inject.ejs.t
│           ├── import-row-inject.ejs.t
│           ├── prompt.ejs.t
│           └── usecase.ejs.t
├── apps/
│   ├── api/
│   │   ├── .gitignore
│   │   ├── .mocharc.json
│   │   ├── .spectral.yaml
│   │   ├── .swcrc
│   │   ├── .vscode/
│   │   │   └── settings.json
│   │   ├── Dockerfile
│   │   ├── README.md
│   │   ├── admin/
│   │   │   ├── connect-to-dal.ts
│   │   │   ├── make-json-backup.ts
│   │   │   ├── remove-organization.ts
│   │   │   └── remove-user-account.ts
│   │   ├── e2e/
│   │   │   ├── compile-email-template.e2e.ts
│   │   │   ├── enterprise/
│   │   │   │   └── inbound-webhook/
│   │   │   │       └── process-inbound-webhook.e2e.ts
│   │   │   ├── mock-http-client.ts
│   │   │   ├── retry.e2e.ts
│   │   │   ├── setup.ts
│   │   │   └── test-bridge-server.ts
│   │   ├── exportOpenAPIJSON.ts
│   │   ├── jarvis-api-intro.md
│   │   ├── migrations/
│   │   │   ├── 001-add-default-identifier-to-topic-subscribers/
│   │   │   │   ├── add-default-identifier-to-topic-subscribers-migration.spec.ts
│   │   │   │   └── add-default-identifier-to-topic-subscribers-migration.ts
│   │   │   ├── 002-remove-duplicate-identifiers/
│   │   │   │   ├── remove-duplicate-identifiers.spec.ts
│   │   │   │   └── remove-duplicate-identifiers.ts
│   │   │   ├── add-layout-id-to-email-controls/
│   │   │   │   ├── add-layout-id-to-email-controls-migration.spec.ts
│   │   │   │   └── add-layout-id-to-email-controls-migration.ts
│   │   │   ├── changes-migration.ts
│   │   │   ├── clickhouse-migrations/
│   │   │   │   ├── 1_initial_schema.sql
│   │   │   │   ├── 2_add_workflow_id_to_schema.sql
│   │   │   │   ├── 3_analytics_tables.sql
│   │   │   │   ├── 4_refactor_traces_schema.sql
│   │   │   │   ├── 5_finalize_table_exchange.sql
│   │   │   │   └── README.md
│   │   │   ├── deleteLogs/
│   │   │   │   └── dropLogsCollection.ts
│   │   │   ├── email-step-ui-schema-html-editor/
│   │   │   │   ├── email-step-ui-schema-html-editor-migration.spec.ts
│   │   │   │   └── email-step-ui-schema-html-editor-migration.ts
│   │   │   ├── encrypt-api-keys/
│   │   │   │   ├── encrypt-api-keys-migration.spec.ts
│   │   │   │   └── encrypt-api-keys-migration.ts
│   │   │   ├── encrypt-credentials/
│   │   │   │   ├── encrypt-credentials-migration.spec.ts
│   │   │   │   └── encrypt-credentials-migration.ts
│   │   │   ├── expire-at/
│   │   │   │   ├── expire-at-delay.migration.spec.ts
│   │   │   │   ├── expire-at.migration.spec.ts
│   │   │   │   └── expire-at.migration.ts
│   │   │   ├── fcm-credentials/
│   │   │   │   ├── fcm-credentials-migration.spec.ts
│   │   │   │   └── fcm-credentials-migration.ts
│   │   │   ├── in-app-integration/
│   │   │   │   └── in-app-integration.migration.ts
│   │   │   ├── integration-scheme-update/
│   │   │   │   ├── add-integration-identifier-migration.spec.ts
│   │   │   │   ├── add-integration-identifier-migration.ts
│   │   │   │   ├── add-primary-priority-migration.ts
│   │   │   │   └── update-primary-for-disabled-novu-integrations.ts
│   │   │   ├── layout-identifier-update/
│   │   │   │   ├── add-layout-identifier-migration.spec.ts
│   │   │   │   └── add-layout-identifier-migration.ts
│   │   │   ├── normalize-message-template-cta-action/
│   │   │   │   ├── normalize-message-cta-action-migration.ts
│   │   │   │   ├── normalize-message-template-cta-action-migration.spec.ts
│   │   │   │   └── normalize-message-template-cta-action-migration.ts
│   │   │   ├── normalize-users-email/
│   │   │   │   └── normalize-users-email.migration.ts
│   │   │   ├── novu-integrations/
│   │   │   │   └── novu-integrations.migration.ts
│   │   │   ├── preference-centralization/
│   │   │   │   ├── preference-centralization-migration.spec.ts
│   │   │   │   └── preference-centralization-migration.ts
│   │   │   ├── preferences-uniqueness/
│   │   │   │   ├── preferences-uniqueness-migration.spec.ts
│   │   │   │   └── preferences-uniqueness-migration.ts
│   │   │   ├── secure-to-boolean/
│   │   │   │   ├── secure-to-boolean-migration.spec.ts
│   │   │   │   └── secure-to-boolean-migration.ts
│   │   │   ├── seen-read-support/
│   │   │   │   ├── seen-read-support.migration.spec.ts
│   │   │   │   └── seen-read-support.migration.ts
│   │   │   ├── subscribers/
│   │   │   │   └── remove-duplicated-subscribers/
│   │   │   │       ├── remove-duplicated-subscribers.migration.spec.ts
│   │   │   │       └── remove-duplicated-subscribers.migration.ts
│   │   │   └── topic-subscriber-normalize/
│   │   │       ├── topic-subscriber-normalize.migration.spec.ts
│   │   │       └── topic-subscriber-normalize.migration.ts
│   │   ├── nest-cli.json
│   │   ├── package.json
│   │   ├── project.json
│   │   ├── scripts/
│   │   │   ├── check-api-property-optionality.ts
│   │   │   ├── clickhouse-seeder/
│   │   │   │   ├── README.md
│   │   │   │   ├── config.ts
│   │   │   │   ├── generators.ts
│   │   │   │   ├── inserter.ts
│   │   │   │   └── time-distribution.ts
│   │   │   ├── generate-metadata.ts
│   │   │   ├── run-novu-v2-e2e-shard.cjs
│   │   │   ├── seed-clickhouse.ts
│   │   │   └── seed-triggers.ts
│   │   ├── src/
│   │   │   ├── .example.env
│   │   │   ├── app/
│   │   │   │   ├── activity/
│   │   │   │   │   ├── activity.controller.ts
│   │   │   │   │   ├── activity.module.ts
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── get-charts.request.dto.ts
│   │   │   │   │   │   ├── get-charts.response.dto.ts
│   │   │   │   │   │   ├── get-request.request.dto.ts
│   │   │   │   │   │   ├── get-request.response.dto.ts
│   │   │   │   │   │   ├── get-requests.dto.ts
│   │   │   │   │   │   ├── get-requests.response.dto.ts
│   │   │   │   │   │   ├── shared.dto.ts
│   │   │   │   │   │   ├── workflow-run-response.dto.ts
│   │   │   │   │   │   ├── workflow-runs-request.dto.ts
│   │   │   │   │   │   └── workflow-runs-response.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── get-requests.e2e.ts
│   │   │   │   │   │   ├── get-workflow-run.e2e.ts
│   │   │   │   │   │   └── get-workflow-runs.e2e.ts
│   │   │   │   │   ├── shared/
│   │   │   │   │   │   ├── mappers.ts
│   │   │   │   │   │   └── select.const.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── build-active-subscribers-chart/
│   │   │   │   │       │   ├── build-active-subscribers-chart.command.ts
│   │   │   │   │       │   ├── build-active-subscribers-chart.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── build-active-subscribers-trend-chart/
│   │   │   │   │       │   ├── build-active-subscribers-trend-chart.command.ts
│   │   │   │   │       │   ├── build-active-subscribers-trend-chart.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── build-avg-messages-per-subscriber-chart/
│   │   │   │   │       │   ├── build-avg-messages-per-subscriber-chart.command.ts
│   │   │   │   │       │   ├── build-avg-messages-per-subscriber-chart.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── build-delivery-trend-chart/
│   │   │   │   │       │   ├── build-delivery-trend-chart.command.ts
│   │   │   │   │       │   ├── build-delivery-trend-chart.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── build-interaction-trend-chart/
│   │   │   │   │       │   ├── build-interaction-trend-chart.command.ts
│   │   │   │   │       │   ├── build-interaction-trend-chart.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── build-messages-delivered-chart/
│   │   │   │   │       │   ├── build-messages-delivered-chart.command.ts
│   │   │   │   │       │   ├── build-messages-delivered-chart.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── build-provider-by-volume-chart/
│   │   │   │   │       │   ├── build-provider-by-volume-chart.command.ts
│   │   │   │   │       │   ├── build-provider-by-volume-chart.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── build-total-interactions-chart/
│   │   │   │   │       │   ├── build-total-interactions-chart.command.ts
│   │   │   │   │       │   ├── build-total-interactions-chart.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── build-workflow-by-volume-chart/
│   │   │   │   │       │   ├── build-workflow-by-volume-chart.command.ts
│   │   │   │   │       │   ├── build-workflow-by-volume-chart.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── build-workflow-runs-count-chart/
│   │   │   │   │       │   ├── build-workflow-runs-count-chart.command.ts
│   │   │   │   │       │   ├── build-workflow-runs-count-chart.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── build-workflow-runs-metric-chart/
│   │   │   │   │       │   ├── build-workflow-runs-metric-chart.command.ts
│   │   │   │   │       │   ├── build-workflow-runs-metric-chart.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── build-workflow-runs-trend-chart/
│   │   │   │   │       │   ├── build-workflow-runs-trend-chart.command.ts
│   │   │   │   │       │   ├── build-workflow-runs-trend-chart.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── get-charts/
│   │   │   │   │       │   ├── get-charts.command.ts
│   │   │   │   │       │   └── get-charts.usecase.ts
│   │   │   │   │       ├── get-request/
│   │   │   │   │       │   ├── get-request.command.ts
│   │   │   │   │       │   └── get-request.usecase.ts
│   │   │   │   │       ├── get-requests/
│   │   │   │   │       │   ├── get-requests.command.ts
│   │   │   │   │       │   └── get-requests.usecase.ts
│   │   │   │   │       ├── get-workflow-run/
│   │   │   │   │       │   ├── get-workflow-run.command.ts
│   │   │   │   │       │   └── get-workflow-run.usecase.ts
│   │   │   │   │       └── get-workflow-runs/
│   │   │   │   │           ├── get-workflow-runs.command.ts
│   │   │   │   │           └── get-workflow-runs.usecase.ts
│   │   │   │   ├── analytics/
│   │   │   │   │   ├── analytics.controller.ts
│   │   │   │   │   ├── analytics.module.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       └── hubspot-identify-form/
│   │   │   │   │           ├── hubspot-identify-form.command.ts
│   │   │   │   │           └── hubspot-identify-form.usecase.ts
│   │   │   │   ├── auth/
│   │   │   │   │   ├── auth.controller.ts
│   │   │   │   │   ├── auth.module.ts
│   │   │   │   │   ├── community.auth.module.config.ts
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── login.dto.ts
│   │   │   │   │   │   ├── password-reset.dto.ts
│   │   │   │   │   │   ├── update-password.dto.ts
│   │   │   │   │   │   └── user-registration.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── clerk.strategy.spec.ts
│   │   │   │   │   │   ├── link-entities.service.spec.ts
│   │   │   │   │   │   ├── login.e2e.ts
│   │   │   │   │   │   ├── password-reset.e2e.ts
│   │   │   │   │   │   ├── permissions.guard.e2e.ts
│   │   │   │   │   │   ├── switch-organization.e2e.ts
│   │   │   │   │   │   ├── update-password.e2e.ts
│   │   │   │   │   │   ├── user-registration.e2e.ts
│   │   │   │   │   │   └── user.auth.guard.e2e.ts
│   │   │   │   │   ├── ee.auth.module.config.ts
│   │   │   │   │   ├── framework/
│   │   │   │   │   │   ├── auth.decorator.ts
│   │   │   │   │   │   ├── community.user.auth.guard.ts
│   │   │   │   │   │   ├── external-api.decorator.ts
│   │   │   │   │   │   └── root-environment-guard.service.ts
│   │   │   │   │   ├── services/
│   │   │   │   │   │   ├── auth.service.ts
│   │   │   │   │   │   ├── community.auth.service.ts
│   │   │   │   │   │   └── passport/
│   │   │   │   │   │       ├── apikey.strategy.ts
│   │   │   │   │   │       ├── github.strategy.ts
│   │   │   │   │   │       ├── jwt.strategy.ts
│   │   │   │   │   │       ├── newrelic.util.ts
│   │   │   │   │   │       └── subscriber-jwt.strategy.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── index.ts
│   │   │   │   │       ├── login/
│   │   │   │   │       │   ├── login.command.ts
│   │   │   │   │       │   └── login.usecase.ts
│   │   │   │   │       ├── password-reset/
│   │   │   │   │       │   ├── password-reset.command.ts
│   │   │   │   │       │   └── password-reset.usecase.ts
│   │   │   │   │       ├── password-reset-request/
│   │   │   │   │       │   ├── password-reset-request.command.ts
│   │   │   │   │       │   └── password-reset-request.usecase.ts
│   │   │   │   │       ├── register/
│   │   │   │   │       │   ├── user-register.command.ts
│   │   │   │   │       │   └── user-register.usecase.ts
│   │   │   │   │       ├── switch-environment/
│   │   │   │   │       │   ├── index.ts
│   │   │   │   │       │   ├── switch-environment.command.ts
│   │   │   │   │       │   └── switch-environment.usecase.ts
│   │   │   │   │       ├── switch-organization/
│   │   │   │   │       │   ├── index.ts
│   │   │   │   │       │   ├── switch-organization.command.ts
│   │   │   │   │       │   └── switch-organization.usecase.ts
│   │   │   │   │       └── update-password/
│   │   │   │   │           ├── update-password.command.ts
│   │   │   │   │           └── update-password.usecase.ts
│   │   │   │   ├── billing/
│   │   │   │   │   └── e2e/
│   │   │   │   │       ├── checkout-session-completed.e2e-ee.ts
│   │   │   │   │       ├── create-checkout-session.e2e-ee.ts
│   │   │   │   │       ├── create-subscription.e2e-ee.ts
│   │   │   │   │       ├── create-usage-records.e2e-ee.ts
│   │   │   │   │       ├── customer-subscription-created.e2e-ee.ts
│   │   │   │   │       ├── customer-subscription-deleted.e2e-ee.ts
│   │   │   │   │       ├── get-event-resource-limit.e2e-ee.ts
│   │   │   │   │       ├── get-platform-notification-usage.e2e-ee.ts
│   │   │   │   │       ├── get-portal-link.e2e-ee.ts
│   │   │   │   │       ├── get-prices.e2e-ee.ts
│   │   │   │   │       ├── get-subscription.e2e-ee.ts
│   │   │   │   │       ├── quota-throttler.guard.e2e-ee.ts
│   │   │   │   │       └── verify-customer.e2e-ee.ts
│   │   │   │   ├── blueprint/
│   │   │   │   │   ├── blueprint.controller.ts
│   │   │   │   │   ├── blueprint.module.ts
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── get-blueprint.response.dto.ts
│   │   │   │   │   │   └── grouped-blueprint.response.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── get-blueprints-by-id.e2e.ts
│   │   │   │   │   │   └── get-grouped-blueprints.e2e.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── get-blueprint/
│   │   │   │   │       │   ├── get-blueprint.command.ts
│   │   │   │   │       │   ├── get-blueprint.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── get-grouped-blueprints/
│   │   │   │   │       │   ├── consts.ts
│   │   │   │   │       │   ├── get-grouped-blueprints.command.ts
│   │   │   │   │       │   ├── get-grouped-blueprints.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       └── index.ts
│   │   │   │   ├── bridge/
│   │   │   │   │   ├── bridge.controller.ts
│   │   │   │   │   ├── bridge.module.ts
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── create-bridge-request.dto.ts
│   │   │   │   │   │   ├── create-bridge-response.dto.ts
│   │   │   │   │   │   ├── validate-bridge-url-request.dto.ts
│   │   │   │   │   │   └── validate-bridge-url-response.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── health-check.e2e.ts
│   │   │   │   │   │   └── sync.e2e.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── get-bridge-status/
│   │   │   │   │       │   ├── get-bridge-status.command.ts
│   │   │   │   │       │   ├── get-bridge-status.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       ├── store-control-values/
│   │   │   │   │       │   ├── index.ts
│   │   │   │   │       │   ├── store-control-values.command.ts
│   │   │   │   │       │   └── store-control-values.usecase.ts
│   │   │   │   │       └── sync/
│   │   │   │   │           ├── index.ts
│   │   │   │   │           ├── sync.command.ts
│   │   │   │   │           └── sync.usecase.ts
│   │   │   │   ├── change/
│   │   │   │   │   ├── change.module.ts
│   │   │   │   │   ├── changes.controller.ts
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── bulk-apply-change.dto.ts
│   │   │   │   │   │   ├── change-request.dto.ts
│   │   │   │   │   │   └── change-response.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── get-changes.e2e.ts
│   │   │   │   │   │   ├── promote-changes.e2e.ts
│   │   │   │   │   │   └── promote-layout-changes.e2e.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── apply-change/
│   │   │   │   │       │   ├── apply-change.command.ts
│   │   │   │   │       │   ├── apply-change.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── bulk-apply-change/
│   │   │   │   │       │   ├── bulk-apply-change.command.ts
│   │   │   │   │       │   └── bulk-apply-change.usecase.ts
│   │   │   │   │       ├── count-changes/
│   │   │   │   │       │   ├── count-changes.command.ts
│   │   │   │   │       │   └── count-changes.usecase.ts
│   │   │   │   │       ├── create-change/
│   │   │   │   │       │   └── create-change.spec.ts
│   │   │   │   │       ├── get-changes/
│   │   │   │   │       │   ├── get-changes.command.ts
│   │   │   │   │       │   └── get-changes.usecase.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       ├── promote-change-to-environment/
│   │   │   │   │       │   ├── index.ts
│   │   │   │   │       │   ├── promote-change-to-environment.command.ts
│   │   │   │   │       │   └── promote-change-to-environment.usecase.ts
│   │   │   │   │       ├── promote-feed-change/
│   │   │   │   │       │   └── promote-feed-change.ts
│   │   │   │   │       ├── promote-layout-change/
│   │   │   │   │       │   ├── index.ts
│   │   │   │   │       │   └── promote-layout-change.use-case.ts
│   │   │   │   │       ├── promote-message-template-change/
│   │   │   │   │       │   └── promote-message-template-change.ts
│   │   │   │   │       ├── promote-notification-group-change/
│   │   │   │   │       │   └── promote-notification-group-change.ts
│   │   │   │   │       ├── promote-notification-template-change/
│   │   │   │   │       │   ├── index.ts
│   │   │   │   │       │   └── promote-notification-template-change.usecase.ts
│   │   │   │   │       ├── promote-translation-change/
│   │   │   │   │       │   ├── index.ts
│   │   │   │   │       │   └── promote-translation-change.usecase.ts
│   │   │   │   │       ├── promote-translation-group-change/
│   │   │   │   │       │   ├── index.ts
│   │   │   │   │       │   └── promote-translation-group-change.usecase.ts
│   │   │   │   │       ├── promote-type-change.command.ts
│   │   │   │   │       └── shared/
│   │   │   │   │           ├── index.ts
│   │   │   │   │           └── notification-template-change.interface.ts
│   │   │   │   ├── channel-connections/
│   │   │   │   │   ├── channel-connections.controller.ts
│   │   │   │   │   ├── channel-connections.module.ts
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── create-channel-connection-request.dto.ts
│   │   │   │   │   │   ├── cursor-pagination-query.dto.ts
│   │   │   │   │   │   ├── dto.mapper.ts
│   │   │   │   │   │   ├── get-channel-connection-response.dto.ts
│   │   │   │   │   │   ├── list-channel-connections-query.dto.ts
│   │   │   │   │   │   ├── list-channel-connections-response.dto.ts
│   │   │   │   │   │   ├── shared.dto.ts
│   │   │   │   │   │   └── update-channel-connection-request.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── create-channel-connection.e2e.ts
│   │   │   │   │   │   ├── delete-channel-connection.e2e.ts
│   │   │   │   │   │   ├── get-channel-connection.e2e.ts
│   │   │   │   │   │   ├── helpers/
│   │   │   │   │   │   │   └── channel-helpers.ts
│   │   │   │   │   │   ├── list-channel-connections.e2e.ts
│   │   │   │   │   │   └── update-channel-connection.e2e.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── create-channel-connection/
│   │   │   │   │       │   ├── create-channel-connection.command.ts
│   │   │   │   │       │   └── create-channel-connection.usecase.ts
│   │   │   │   │       ├── delete-channel-connection/
│   │   │   │   │       │   ├── delete-channel-connection.command.ts
│   │   │   │   │       │   └── delete-channel-connection.usecase.ts
│   │   │   │   │       ├── get-channel-connection/
│   │   │   │   │       │   ├── get-channel-connection.command.ts
│   │   │   │   │       │   └── get-channel-connection.usecase.ts
│   │   │   │   │       ├── list-channel-connections/
│   │   │   │   │       │   ├── list-channel-connections.command.ts
│   │   │   │   │       │   └── list-channel-connections.usecase.ts
│   │   │   │   │       └── update-channel-connection/
│   │   │   │   │           ├── update-channel-connection.command.ts
│   │   │   │   │           └── update-channel-connection.usecase.ts
│   │   │   │   ├── channel-endpoints/
│   │   │   │   │   ├── channel-endpoints.controller.ts
│   │   │   │   │   ├── channel-endpoints.module.ts
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── create-channel-endpoint-request.dto.ts
│   │   │   │   │   │   ├── create-channel-endpoint-variants.dto.ts
│   │   │   │   │   │   ├── cursor-pagination-query.dto.ts
│   │   │   │   │   │   ├── dto.mapper.ts
│   │   │   │   │   │   ├── endpoint-types.dto.ts
│   │   │   │   │   │   ├── get-channel-endpoint-response.dto.ts
│   │   │   │   │   │   ├── list-channel-endpoints-query.dto.ts
│   │   │   │   │   │   ├── list-channel-endpoints-response.dto.ts
│   │   │   │   │   │   └── update-channel-endpoint-request.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── create-channel-endpoint.e2e.ts
│   │   │   │   │   │   ├── delete-channel-endpoint.e2e.ts
│   │   │   │   │   │   ├── get-channel-endpoint.e2e.ts
│   │   │   │   │   │   ├── list-channel-endpoints.e2e.ts
│   │   │   │   │   │   └── update-channel-endpoint.e2e.ts
│   │   │   │   │   ├── usecases/
│   │   │   │   │   │   ├── create-channel-endpoint/
│   │   │   │   │   │   │   ├── create-channel-endpoint.command.ts
│   │   │   │   │   │   │   └── create-channel-endpoint.usecase.ts
│   │   │   │   │   │   ├── delete-channel-endpoint/
│   │   │   │   │   │   │   ├── delete-channel-endpoint.command.ts
│   │   │   │   │   │   │   └── delete-channel-endpoint.usecase.ts
│   │   │   │   │   │   ├── get-channel-endpoint/
│   │   │   │   │   │   │   ├── get-channel-endpoint.command.ts
│   │   │   │   │   │   │   └── get-channel-endpoint.usecase.ts
│   │   │   │   │   │   ├── list-channel-endpoints/
│   │   │   │   │   │   │   ├── list-channel-endpoints.command.ts
│   │   │   │   │   │   │   └── list-channel-endpoints.usecase.ts
│   │   │   │   │   │   └── update-channel-endpoint/
│   │   │   │   │   │       ├── update-channel-endpoint.command.ts
│   │   │   │   │   │       └── update-channel-endpoint.usecase.ts
│   │   │   │   │   └── validators/
│   │   │   │   │       └── channel-endpoint.validator.ts
│   │   │   │   ├── content-templates/
│   │   │   │   │   ├── content-templates.controller.ts
│   │   │   │   │   ├── content-templates.module.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── preview-email.e2e.ts
│   │   │   │   │   │   └── preview-step.e2e.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       └── index.ts
│   │   │   │   ├── contexts/
│   │   │   │   │   ├── contexts.controller.ts
│   │   │   │   │   ├── contexts.module.ts
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── create-context-request.dto.ts
│   │   │   │   │   │   ├── cursor-pagination-query.dto.ts
│   │   │   │   │   │   ├── dto.mapper.ts
│   │   │   │   │   │   ├── get-context-response.dto.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── list-contexts-query.dto.ts
│   │   │   │   │   │   ├── list-contexts-response.dto.ts
│   │   │   │   │   │   └── update-context-request.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── create-context.e2e.ts
│   │   │   │   │   │   ├── delete-context.e2e.ts
│   │   │   │   │   │   ├── get-context.e2e.ts
│   │   │   │   │   │   ├── list-contexts.e2e.ts
│   │   │   │   │   │   └── update-context.e2e.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── create-context/
│   │   │   │   │       │   ├── create-context.command.ts
│   │   │   │   │       │   ├── create-context.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── delete-context/
│   │   │   │   │       │   ├── delete-context.command.ts
│   │   │   │   │       │   ├── delete-context.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── get-context/
│   │   │   │   │       │   ├── get-context.command.ts
│   │   │   │   │       │   ├── get-context.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── list-contexts/
│   │   │   │   │       │   ├── index.ts
│   │   │   │   │       │   ├── list-contexts.command.ts
│   │   │   │   │       │   └── list-contexts.usecase.ts
│   │   │   │   │       └── update-context/
│   │   │   │   │           ├── index.ts
│   │   │   │   │           ├── update-context.command.ts
│   │   │   │   │           └── update-context.usecase.ts
│   │   │   │   ├── environment-variables/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── create-environment-variable-request.dto.ts
│   │   │   │   │   │   ├── environment-variable-response.dto.ts
│   │   │   │   │   │   ├── get-environment-variable-usage-response.dto.ts
│   │   │   │   │   │   ├── get-environment-variables-request.dto.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   └── update-environment-variable-request.dto.ts
│   │   │   │   │   ├── environment-variables.controller.ts
│   │   │   │   │   ├── environment-variables.module.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── create-environment-variable/
│   │   │   │   │       │   ├── create-environment-variable.command.ts
│   │   │   │   │       │   └── create-environment-variable.usecase.ts
│   │   │   │   │       ├── delete-environment-variable/
│   │   │   │   │       │   ├── delete-environment-variable.command.ts
│   │   │   │   │       │   └── delete-environment-variable.usecase.ts
│   │   │   │   │       ├── get-environment-variable/
│   │   │   │   │       │   ├── get-environment-variable.command.ts
│   │   │   │   │       │   └── get-environment-variable.usecase.ts
│   │   │   │   │       ├── get-environment-variable-usage/
│   │   │   │   │       │   ├── get-environment-variable-usage.command.ts
│   │   │   │   │       │   ├── get-environment-variable-usage.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── get-environment-variables/
│   │   │   │   │       │   ├── get-environment-variables.command.ts
│   │   │   │   │       │   └── get-environment-variables.usecase.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       └── update-environment-variable/
│   │   │   │   │           ├── update-environment-variable.command.ts
│   │   │   │   │           └── update-environment-variable.usecase.ts
│   │   │   │   ├── environments-v1/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── api-key.dto.ts
│   │   │   │   │   │   ├── create-environment-request.dto.ts
│   │   │   │   │   │   ├── environment-response.dto.ts
│   │   │   │   │   │   └── update-environment-request.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── environments.controller.e2e.ts
│   │   │   │   │   │   ├── get-api-keys.e2e.ts
│   │   │   │   │   │   └── regenerate-api-keys.e2e.ts
│   │   │   │   │   ├── environments-v1.controller.ts
│   │   │   │   │   ├── environments-v1.module.ts
│   │   │   │   │   ├── novu-bridge-client.ts
│   │   │   │   │   ├── novu-bridge.controller.ts
│   │   │   │   │   ├── novu-bridge.module.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── construct-framework-workflow/
│   │   │   │   │       │   ├── construct-framework-workflow.command.ts
│   │   │   │   │       │   ├── construct-framework-workflow.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── create-environment/
│   │   │   │   │       │   ├── create-environment.command.ts
│   │   │   │   │       │   ├── create-environment.e2e.ts
│   │   │   │   │       │   └── create-environment.usecase.ts
│   │   │   │   │       ├── delete-environment/
│   │   │   │   │       │   ├── delete-environment.command.ts
│   │   │   │   │       │   ├── delete-environment.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── generate-unique-api-key/
│   │   │   │   │       │   ├── generate-unique-api-key.spec.ts
│   │   │   │   │       │   └── generate-unique-api-key.usecase.ts
│   │   │   │   │       ├── get-api-keys/
│   │   │   │   │       │   ├── get-api-keys.command.ts
│   │   │   │   │       │   └── get-api-keys.usecase.ts
│   │   │   │   │       ├── get-environment/
│   │   │   │   │       │   ├── get-environment.command.ts
│   │   │   │   │       │   ├── get-environment.e2e.ts
│   │   │   │   │       │   ├── get-environment.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── get-my-environments/
│   │   │   │   │       │   ├── get-my-environments.command.ts
│   │   │   │   │       │   ├── get-my-environments.e2e.ts
│   │   │   │   │       │   └── get-my-environments.usecase.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       ├── output-renderers/
│   │   │   │   │       │   ├── base-translation-renderer.usecase.ts
│   │   │   │   │       │   ├── chat-output-renderer.usecase.ts
│   │   │   │   │       │   ├── delay-output-renderer.usecase.ts
│   │   │   │   │       │   ├── digest-output-renderer.usecase.ts
│   │   │   │   │       │   ├── email-output-renderer.spec.ts
│   │   │   │   │       │   ├── email-output-renderer.usecase.ts
│   │   │   │   │       │   ├── in-app-output-renderer.usecase.ts
│   │   │   │   │       │   ├── index.ts
│   │   │   │   │       │   ├── novu-branding-html.ts
│   │   │   │   │       │   ├── push-output-renderer.usecase.ts
│   │   │   │   │       │   ├── render-command.ts
│   │   │   │   │       │   ├── sms-output-renderer.usecase.ts
│   │   │   │   │       │   └── throttle-output-renderer.usecase.ts
│   │   │   │   │       ├── regenerate-api-keys/
│   │   │   │   │       │   └── regenerate-api-keys.usecase.ts
│   │   │   │   │       └── update-environment/
│   │   │   │   │           ├── update-environment.command.ts
│   │   │   │   │           ├── update-environment.e2e-ee.ts
│   │   │   │   │           ├── update-environment.e2e.ts
│   │   │   │   │           └── update-environment.usecase.ts
│   │   │   │   ├── environments-v2/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── diff-environment.dto.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   └── publish-environment.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── environments-v2-diff.e2e.ts
│   │   │   │   │   │   ├── environments-v2-publish.e2e.ts
│   │   │   │   │   │   └── get-environment-tags.e2e.ts
│   │   │   │   │   ├── environments.controller.ts
│   │   │   │   │   ├── environments.module.ts
│   │   │   │   │   ├── services/
│   │   │   │   │   │   ├── dependency-analyzer.service.ts
│   │   │   │   │   │   ├── environment-validation.service.ts
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── types/
│   │   │   │   │   │   └── sync.types.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── diff-environment/
│   │   │   │   │       │   ├── diff-environment.command.ts
│   │   │   │   │       │   ├── diff-environment.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── publish-environment/
│   │   │   │   │       │   ├── index.ts
│   │   │   │   │       │   ├── publish-environment.command.ts
│   │   │   │   │       │   └── publish-environment.usecase.ts
│   │   │   │   │       └── sync-strategies/
│   │   │   │   │           ├── adapters/
│   │   │   │   │           │   ├── index.ts
│   │   │   │   │           │   ├── layout-comparator.adapter.ts
│   │   │   │   │           │   ├── layout-delete.adapter.ts
│   │   │   │   │           │   ├── layout-repository.adapter.ts
│   │   │   │   │           │   ├── layout-sync.adapter.ts
│   │   │   │   │           │   ├── workflow-comparator.adapter.ts
│   │   │   │   │           │   ├── workflow-delete.adapter.ts
│   │   │   │   │           │   ├── workflow-repository.adapter.ts
│   │   │   │   │           │   └── workflow-sync.adapter.ts
│   │   │   │   │           ├── base/
│   │   │   │   │           │   ├── base-sync.strategy.ts
│   │   │   │   │           │   ├── index.ts
│   │   │   │   │           │   ├── interfaces/
│   │   │   │   │           │   │   ├── base-comparator.interface.ts
│   │   │   │   │           │   │   ├── base-delete.interface.ts
│   │   │   │   │           │   │   ├── base-repository.interface.ts
│   │   │   │   │           │   │   ├── base-sync.interface.ts
│   │   │   │   │           │   │   └── index.ts
│   │   │   │   │           │   └── operations/
│   │   │   │   │           │       ├── base-diff.operation.ts
│   │   │   │   │           │       ├── base-sync.operation.ts
│   │   │   │   │           │       └── index.ts
│   │   │   │   │           ├── builders/
│   │   │   │   │           │   ├── diff-result.builder.ts
│   │   │   │   │           │   └── sync-result.builder.ts
│   │   │   │   │           ├── comparators/
│   │   │   │   │           │   ├── layout.comparator.ts
│   │   │   │   │           │   └── workflow.comparator.ts
│   │   │   │   │           ├── constants/
│   │   │   │   │           │   └── sync.constants.ts
│   │   │   │   │           ├── index.ts
│   │   │   │   │           ├── layout-sync.strategy.ts
│   │   │   │   │           ├── normalizers/
│   │   │   │   │           │   ├── layout.normalizer.ts
│   │   │   │   │           │   └── workflow.normalizer.ts
│   │   │   │   │           ├── operations/
│   │   │   │   │           │   ├── layout-diff.operation.ts
│   │   │   │   │           │   ├── layout-repository.service.ts
│   │   │   │   │           │   ├── layout-sync.operation.ts
│   │   │   │   │           │   ├── workflow-diff.operation.ts
│   │   │   │   │           │   ├── workflow-repository.service.ts
│   │   │   │   │           │   └── workflow-sync.operation.ts
│   │   │   │   │           ├── sync.module.ts
│   │   │   │   │           ├── types/
│   │   │   │   │           │   ├── layout-sync.types.ts
│   │   │   │   │           │   └── workflow-sync.types.ts
│   │   │   │   │           └── workflow-sync.strategy.ts
│   │   │   │   ├── events/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── test-email-request.dto.ts
│   │   │   │   │   │   ├── trigger-event-request.dto.ts
│   │   │   │   │   │   ├── trigger-event-response.dto.ts
│   │   │   │   │   │   └── trigger-event-to-all-request.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── bridge-trigger.e2e.ts
│   │   │   │   │   │   ├── bulk-trigger.e2e.ts
│   │   │   │   │   │   ├── cancel-event.e2e.ts
│   │   │   │   │   │   ├── context-events.e2e.ts
│   │   │   │   │   │   ├── delay-events.e2e.ts
│   │   │   │   │   │   ├── digest-events.e2e.ts
│   │   │   │   │   │   ├── process-subscriber.e2e.ts
│   │   │   │   │   │   ├── scheduled-digest.e2e.ts
│   │   │   │   │   │   ├── send-message-email.e2e.ts
│   │   │   │   │   │   ├── send-message-push.e2e.ts
│   │   │   │   │   │   ├── test-email.e2e.ts
│   │   │   │   │   │   ├── throttle-events.e2e.ts
│   │   │   │   │   │   ├── trigger-event-preferences.e2e.ts
│   │   │   │   │   │   ├── trigger-event-to-all.e2e.ts
│   │   │   │   │   │   ├── trigger-event-topic.e2e.ts
│   │   │   │   │   │   ├── trigger-event.e2e.ts
│   │   │   │   │   │   └── utils/
│   │   │   │   │   │       ├── poll-for-job-status-change.util.ts
│   │   │   │   │   │       └── sleep.util.ts
│   │   │   │   │   ├── events.controller.ts
│   │   │   │   │   ├── events.module.ts
│   │   │   │   │   ├── exceptions/
│   │   │   │   │   │   └── payload-validation-exception.ts
│   │   │   │   │   ├── usecases/
│   │   │   │   │   │   ├── cancel-delayed/
│   │   │   │   │   │   │   ├── cancel-delayed.command.ts
│   │   │   │   │   │   │   ├── cancel-delayed.usecase.ts
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── parse-event-request/
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   ├── parse-event-request.command.ts
│   │   │   │   │   │   │   ├── parse-event-request.e2e.ts
│   │   │   │   │   │   │   └── parse-event-request.usecase.ts
│   │   │   │   │   │   ├── process-bulk-trigger/
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   ├── process-bulk-trigger.command.ts
│   │   │   │   │   │   │   └── process-bulk-trigger.usecase.ts
│   │   │   │   │   │   ├── send-test-email/
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   ├── send-test-email.command.ts
│   │   │   │   │   │   │   └── send-test-email.usecase.ts
│   │   │   │   │   │   └── trigger-event-to-all/
│   │   │   │   │   │       ├── index.ts
│   │   │   │   │   │       ├── trigger-event-to-all.command.ts
│   │   │   │   │   │       ├── trigger-event-to-all.spec.ts
│   │   │   │   │   │       └── trigger-event-to-all.usecase.ts
│   │   │   │   │   └── utils/
│   │   │   │   │       └── trigger-recipient-validation.ts
│   │   │   │   ├── execution-details/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   └── execution-details-request.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   └── get-execution-details.e2e.ts
│   │   │   │   │   ├── execution-details.controller.ts
│   │   │   │   │   ├── execution-details.module.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── get-execution-details/
│   │   │   │   │       │   ├── get-execution-details.command.ts
│   │   │   │   │       │   ├── get-execution-details.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       └── index.ts
│   │   │   │   ├── feeds/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── create-feed-request.dto.ts
│   │   │   │   │   │   └── feed-response.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── create-feed.e2e.ts
│   │   │   │   │   │   ├── delete-feed.e2e.ts
│   │   │   │   │   │   └── get-feeds.e2e.ts
│   │   │   │   │   ├── feeds.controller.ts
│   │   │   │   │   ├── feeds.module.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── create-feed/
│   │   │   │   │       │   ├── create-feed.command.ts
│   │   │   │   │       │   └── create-feed.usecase.ts
│   │   │   │   │       ├── delete-feed/
│   │   │   │   │       │   ├── delete-feed.command.ts
│   │   │   │   │       │   └── delete-feed.usecase.ts
│   │   │   │   │       ├── get-feeds/
│   │   │   │   │       │   ├── get-feeds.command.ts
│   │   │   │   │       │   └── get-feeds.usecase.ts
│   │   │   │   │       └── index.ts
│   │   │   │   ├── health/
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   └── health-check.e2e.ts
│   │   │   │   │   ├── health.controller.ts
│   │   │   │   │   └── health.module.ts
│   │   │   │   ├── inbound-parse/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   └── get-mx-record.dto.ts
│   │   │   │   │   ├── inbound-parse.controller.ts
│   │   │   │   │   ├── inbound-parse.module.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── get-mx-record/
│   │   │   │   │       │   ├── get-mx-record.command.ts
│   │   │   │   │       │   └── get-mx-record.usecase.ts
│   │   │   │   │       └── index.ts
│   │   │   │   ├── inbox/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── action-type-request.dto.ts
│   │   │   │   │   │   ├── bulk-update-preferences-request.dto.ts
│   │   │   │   │   │   ├── create-topic-subscription-request.dto.ts
│   │   │   │   │   │   ├── get-notifications-count-request.dto.ts
│   │   │   │   │   │   ├── get-notifications-count-response.dto.ts
│   │   │   │   │   │   ├── get-notifications-request.dto.ts
│   │   │   │   │   │   ├── get-notifications-response.dto.ts
│   │   │   │   │   │   ├── get-preferences-request.dto.ts
│   │   │   │   │   │   ├── get-preferences-response.dto.ts
│   │   │   │   │   │   ├── inbox-notification.dto.ts
│   │   │   │   │   │   ├── mark-notifications-as-seen-request.dto.ts
│   │   │   │   │   │   ├── snooze-notification-request.dto.ts
│   │   │   │   │   │   ├── subscriber-session-request.dto.ts
│   │   │   │   │   │   ├── subscriber-session-response.dto.ts
│   │   │   │   │   │   ├── update-all-notifications-request.dto.ts
│   │   │   │   │   │   ├── update-preferences-request.dto.ts
│   │   │   │   │   │   └── workflow.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── context-aware-topic-subscriptions.e2e.ts
│   │   │   │   │   │   ├── create-topic-subscription.e2e.ts
│   │   │   │   │   │   ├── delete-notifications.e2e.ts
│   │   │   │   │   │   ├── get-notifications-count.e2e.ts
│   │   │   │   │   │   ├── get-notifications.e2e.ts
│   │   │   │   │   │   ├── get-preferences.e2e.ts
│   │   │   │   │   │   ├── get-topic-subscription.e2e.ts
│   │   │   │   │   │   ├── mark-notification-as.e2e.ts
│   │   │   │   │   │   ├── mark-notifications-as-seen.e2e.ts
│   │   │   │   │   │   ├── session.e2e.ts
│   │   │   │   │   │   ├── snooze-unsnooze-notification.e2e.ts
│   │   │   │   │   │   ├── update-all-notifications.e2e.ts
│   │   │   │   │   │   ├── update-notification-action.e2e.ts
│   │   │   │   │   │   ├── update-preferences.e2e.ts
│   │   │   │   │   │   └── update-subscription-workflow-preferences.e2e.ts
│   │   │   │   │   ├── inbox.controller.ts
│   │   │   │   │   ├── inbox.module.ts
│   │   │   │   │   ├── inbox.topic.controller.ts
│   │   │   │   │   ├── interceptors/
│   │   │   │   │   │   └── context-compatibility.interceptor.ts
│   │   │   │   │   ├── usecases/
│   │   │   │   │   │   ├── bulk-update-preferences/
│   │   │   │   │   │   │   ├── bulk-update-preferences.command.ts
│   │   │   │   │   │   │   ├── bulk-update-preferences.spec.ts
│   │   │   │   │   │   │   └── bulk-update-preferences.usecase.ts
│   │   │   │   │   │   ├── delete-all-notifications/
│   │   │   │   │   │   │   ├── delete-all-notifications.command.ts
│   │   │   │   │   │   │   └── delete-all-notifications.usecase.ts
│   │   │   │   │   │   ├── delete-many-notifications/
│   │   │   │   │   │   │   ├── delete-many-notifications.command.ts
│   │   │   │   │   │   │   └── delete-many-notifications.usecase.ts
│   │   │   │   │   │   ├── delete-notification/
│   │   │   │   │   │   │   ├── delete-notification.command.ts
│   │   │   │   │   │   │   └── delete-notification.usecase.ts
│   │   │   │   │   │   ├── delete-subscription/
│   │   │   │   │   │   │   ├── delete-subscription.command.ts
│   │   │   │   │   │   │   └── delete-subscription.usecase.ts
│   │   │   │   │   │   ├── get-inbox-preferences/
│   │   │   │   │   │   │   ├── get-inbox-preferences.command.ts
│   │   │   │   │   │   │   ├── get-inbox-preferences.spec.ts
│   │   │   │   │   │   │   └── get-inbox-preferences.usecase.ts
│   │   │   │   │   │   ├── get-notifications/
│   │   │   │   │   │   │   ├── get-notifications.command.ts
│   │   │   │   │   │   │   ├── get-notifications.spec.ts
│   │   │   │   │   │   │   └── get-notifications.usecase.ts
│   │   │   │   │   │   ├── get-topic-subscriptions/
│   │   │   │   │   │   │   ├── get-topic-subscriptions.command.ts
│   │   │   │   │   │   │   └── get-topic-subscriptions.usecase.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── mark-many-notifications-as/
│   │   │   │   │   │   │   ├── mark-many-notifications-as.command.ts
│   │   │   │   │   │   │   ├── mark-many-notifications-as.spec.ts
│   │   │   │   │   │   │   └── mark-many-notifications-as.usecase.ts
│   │   │   │   │   │   ├── mark-notification-as/
│   │   │   │   │   │   │   ├── mark-notification-as.command.ts
│   │   │   │   │   │   │   ├── mark-notification-as.spec.ts
│   │   │   │   │   │   │   └── mark-notification-as.usecase.ts
│   │   │   │   │   │   ├── mark-notifications-as-seen/
│   │   │   │   │   │   │   ├── mark-notifications-as-seen.command.ts
│   │   │   │   │   │   │   └── mark-notifications-as-seen.usecase.ts
│   │   │   │   │   │   ├── noop-send-webhook-message.usecase.ts
│   │   │   │   │   │   ├── notifications-count/
│   │   │   │   │   │   │   ├── notifications-count.command.ts
│   │   │   │   │   │   │   ├── notifications-count.spec.ts
│   │   │   │   │   │   │   └── notifications-count.usecase.ts
│   │   │   │   │   │   ├── session/
│   │   │   │   │   │   │   ├── session.command.ts
│   │   │   │   │   │   │   ├── session.spec.ts
│   │   │   │   │   │   │   └── session.usecase.ts
│   │   │   │   │   │   ├── snooze-notification/
│   │   │   │   │   │   │   ├── snooze-notification.command.ts
│   │   │   │   │   │   │   ├── snooze-notification.spec.ts
│   │   │   │   │   │   │   └── snooze-notification.usecase.ts
│   │   │   │   │   │   ├── unsnooze-notification/
│   │   │   │   │   │   │   ├── unsnooze-notification.command.ts
│   │   │   │   │   │   │   ├── unsnooze-notification.spec.ts
│   │   │   │   │   │   │   └── unsnooze-notification.usecase.ts
│   │   │   │   │   │   ├── update-all-notifications/
│   │   │   │   │   │   │   ├── update-all-notifications.command.ts
│   │   │   │   │   │   │   ├── update-all-notifications.spec.ts
│   │   │   │   │   │   │   └── update-all-notifications.usecase.ts
│   │   │   │   │   │   ├── update-notification-action/
│   │   │   │   │   │   │   ├── update-notification-action.command.ts
│   │   │   │   │   │   │   ├── update-notification-action.spec.ts
│   │   │   │   │   │   │   └── update-notification-action.usecase.ts
│   │   │   │   │   │   └── update-preferences/
│   │   │   │   │   │       ├── update-preferences.command.ts
│   │   │   │   │   │       ├── update-preferences.spec.ts
│   │   │   │   │   │       └── update-preferences.usecase.ts
│   │   │   │   │   └── utils/
│   │   │   │   │       ├── analytics.ts
│   │   │   │   │       ├── encryption.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       ├── notification-mapper.ts
│   │   │   │   │       ├── types.ts
│   │   │   │   │       └── validate-data.ts
│   │   │   │   ├── integrations/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── auto-configure-integration-request.dto.ts
│   │   │   │   │   │   ├── auto-configure-integration-response.dto.ts
│   │   │   │   │   │   ├── create-integration-request.dto.ts
│   │   │   │   │   │   ├── generate-chat-oauth-url-response.dto.ts
│   │   │   │   │   │   ├── generate-chat-oauth-url.dto.ts
│   │   │   │   │   │   ├── get-channel-type-limit.sto.ts
│   │   │   │   │   │   └── update-integration.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── create-integration.e2e.ts
│   │   │   │   │   │   ├── deactivate-integration.e2e.ts
│   │   │   │   │   │   ├── get-active-integration.e2e.ts
│   │   │   │   │   │   ├── get-decrypted-integrations.e2e.ts
│   │   │   │   │   │   ├── get-in-app-activated.e2e.ts
│   │   │   │   │   │   ├── get-integration.e2e.ts
│   │   │   │   │   │   ├── get-webhook-support-status.e2e.ts
│   │   │   │   │   │   ├── remove-integration.e2e.ts
│   │   │   │   │   │   ├── set-itegration-as-primary.e2e.ts
│   │   │   │   │   │   └── update-integration.e2e.ts
│   │   │   │   │   ├── integrations.controller.ts
│   │   │   │   │   ├── integrations.module.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── auto-configure-integration/
│   │   │   │   │       │   ├── auto-configure-integration.command.ts
│   │   │   │   │       │   └── auto-configure-integration.usecase.ts
│   │   │   │   │       ├── chat-oauth-callback/
│   │   │   │   │       │   ├── chat-oauth-callback.command.ts
│   │   │   │   │       │   ├── chat-oauth-callback.response.ts
│   │   │   │   │       │   ├── chat-oauth-callback.usecase.ts
│   │   │   │   │       │   ├── msteams-oauth-callback/
│   │   │   │   │       │   │   ├── msteams-oauth-callback.command.ts
│   │   │   │   │       │   │   └── msteams-oauth-callback.usecase.ts
│   │   │   │   │       │   └── slack-oauth-callback/
│   │   │   │   │       │       ├── slack-oauth-callback.command.ts
│   │   │   │   │       │       └── slack-oauth-callback.usecase.ts
│   │   │   │   │       ├── check-integration/
│   │   │   │   │       │   ├── check-integration-email.usecase.ts
│   │   │   │   │       │   ├── check-integration.command.ts
│   │   │   │   │       │   └── check-integration.usecase.ts
│   │   │   │   │       ├── create-integration/
│   │   │   │   │       │   ├── create-integration.command.ts
│   │   │   │   │       │   └── create-integration.usecase.ts
│   │   │   │   │       ├── create-novu-integrations/
│   │   │   │   │       │   ├── create-novu-integrations.command.ts
│   │   │   │   │       │   └── create-novu-integrations.usecase.ts
│   │   │   │   │       ├── generate-chat-oath-url/
│   │   │   │   │       │   ├── chat-oauth.constants.ts
│   │   │   │   │       │   ├── generate-chat-oauth-url.command.ts
│   │   │   │   │       │   ├── generate-chat-oauth-url.usecase.ts
│   │   │   │   │       │   ├── generate-msteams-oath-url/
│   │   │   │   │       │   │   ├── generate-msteams-oauth-url.command.ts
│   │   │   │   │       │   │   └── generate-msteams-oauth-url.usecase.ts
│   │   │   │   │       │   └── generate-slack-oath-url/
│   │   │   │   │       │       ├── generate-slack-oauth-url.command.ts
│   │   │   │   │       │       └── generate-slack-oauth-url.usecase.ts
│   │   │   │   │       ├── get-in-app-activated/
│   │   │   │   │       │   ├── get-in-app-activated.command.ts
│   │   │   │   │       │   └── get-in-app-activated.usecase.ts
│   │   │   │   │       ├── get-integrations/
│   │   │   │   │       │   ├── get-integrations.command.ts
│   │   │   │   │       │   └── get-integrations.usecase.ts
│   │   │   │   │       ├── get-webhook-support-status/
│   │   │   │   │       │   ├── get-webhook-support-status.command.ts
│   │   │   │   │       │   └── get-webhook-support-status.usecase.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       ├── remove-integration/
│   │   │   │   │       │   ├── remove-integration.command.ts
│   │   │   │   │       │   └── remove-integration.usecase.ts
│   │   │   │   │       ├── set-integration-as-primary/
│   │   │   │   │       │   ├── set-integration-as-primary.command.ts
│   │   │   │   │       │   └── set-integration-as-primary.usecase.ts
│   │   │   │   │       └── update-integration/
│   │   │   │   │           ├── update-integration.command.ts
│   │   │   │   │           └── update-integration.usecase.ts
│   │   │   │   ├── internal/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── scheduler-callback.dto.ts
│   │   │   │   │   │   └── subscriber-online-state.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   └── internal.e2e.ts
│   │   │   │   │   ├── guards/
│   │   │   │   │   │   └── internal-callback.guard.ts
│   │   │   │   │   ├── internal.controller.ts
│   │   │   │   │   ├── internal.module.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── handle-scheduler-callback/
│   │   │   │   │       │   ├── handle-scheduler-callback.command.ts
│   │   │   │   │       │   └── handle-scheduler-callback.usecase.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       └── update-subscriber-online-state/
│   │   │   │   │           ├── update-subscriber-online-state.command.ts
│   │   │   │   │           └── update-subscriber-online-state.usecase.ts
│   │   │   │   ├── invites/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── bulk-invite-members.dto.ts
│   │   │   │   │   │   ├── invite-member.dto.ts
│   │   │   │   │   │   └── resend-invite.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── accept-invite.e2e.ts
│   │   │   │   │   │   ├── bulk-invite.e2e.ts
│   │   │   │   │   │   ├── get-invite.e2e.ts
│   │   │   │   │   │   └── resend-invite.e2e.ts
│   │   │   │   │   ├── invites.controller.ts
│   │   │   │   │   ├── invites.module.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── accept-invite/
│   │   │   │   │       │   ├── accept-invite.command.ts
│   │   │   │   │       │   └── accept-invite.usecase.ts
│   │   │   │   │       ├── bulk-invite/
│   │   │   │   │       │   ├── bulk-invite.command.ts
│   │   │   │   │       │   └── bulk-invite.usecase.ts
│   │   │   │   │       ├── get-invite/
│   │   │   │   │       │   ├── get-invite.command.ts
│   │   │   │   │       │   └── get-invite.usecase.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       ├── invite-member/
│   │   │   │   │       │   ├── invite-member.command.ts
│   │   │   │   │       │   └── invite-member.usecase.ts
│   │   │   │   │       └── resend-invite/
│   │   │   │   │           ├── resend-invite.command.ts
│   │   │   │   │           └── resend-invite.usecase.ts
│   │   │   │   ├── layouts-v1/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── create-layout.dto.ts
│   │   │   │   │   │   ├── filter-layouts.dto.ts
│   │   │   │   │   │   ├── get-layout.dto.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   └── update-layout.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── create-layout.e2e.ts
│   │   │   │   │   │   ├── delete-layout.e2e.ts
│   │   │   │   │   │   ├── filter-layouts.e2e.ts
│   │   │   │   │   │   ├── get-layout.e2e.ts
│   │   │   │   │   │   ├── helpers/
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   ├── set-default-layout.e2e.ts
│   │   │   │   │   │   └── update-layout.e2e.ts
│   │   │   │   │   ├── layouts-v1.controller.ts
│   │   │   │   │   ├── layouts-v1.module.ts
│   │   │   │   │   ├── types/
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── check-layout-is-used/
│   │   │   │   │       │   ├── check-layout-is-used.command.ts
│   │   │   │   │       │   ├── check-layout-is-used.use-case.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── create-default-layout/
│   │   │   │   │       │   ├── create-default-layout.command.ts
│   │   │   │   │       │   ├── create-default-layout.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── create-default-layout-change/
│   │   │   │   │       │   ├── create-default-layout-change.command.ts
│   │   │   │   │       │   └── create-default-layout-change.usecase.ts
│   │   │   │   │       ├── create-layout/
│   │   │   │   │       │   ├── create-layout.command.ts
│   │   │   │   │       │   ├── create-layout.use-case.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── create-layout-change/
│   │   │   │   │       │   ├── create-layout-change.command.ts
│   │   │   │   │       │   ├── create-layout-change.use-case.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── delete-layout/
│   │   │   │   │       │   ├── delete-layout.command.ts
│   │   │   │   │       │   ├── delete-layout.use-case.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── filter-layouts/
│   │   │   │   │       │   ├── filter-layouts.command.ts
│   │   │   │   │       │   ├── filter-layouts.use-case.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── find-deleted-layout/
│   │   │   │   │       │   ├── find-deleted-layout.command.ts
│   │   │   │   │       │   ├── find-deleted-layout.spec.ts
│   │   │   │   │       │   ├── find-deleted-layout.use-case.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       ├── set-default-layout/
│   │   │   │   │       │   ├── index.ts
│   │   │   │   │       │   ├── set-default-layout.command.ts
│   │   │   │   │       │   └── set-default-layout.use-case.ts
│   │   │   │   │       └── update-layout/
│   │   │   │   │           ├── index.ts
│   │   │   │   │           ├── update-layout.command.ts
│   │   │   │   │           └── update-layout.use-case.ts
│   │   │   │   ├── layouts-v2/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── duplicate-layout.dto.ts
│   │   │   │   │   │   ├── generate-layout-preview-response.dto.ts
│   │   │   │   │   │   ├── get-layout-list-query-params.dto.ts
│   │   │   │   │   │   ├── get-layout-usage-response.dto.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── layout-preview-payload.dto.ts
│   │   │   │   │   │   ├── layout-preview-request.dto.ts
│   │   │   │   │   │   └── list-layout-response.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── preview-layout.e2e.ts
│   │   │   │   │   │   └── upsert-layout.e2e.ts
│   │   │   │   │   ├── layouts.controller.ts
│   │   │   │   │   ├── layouts.module.ts
│   │   │   │   │   ├── usecases/
│   │   │   │   │   │   ├── build-layout-issues/
│   │   │   │   │   │   │   ├── build-layout-issues.command.ts
│   │   │   │   │   │   │   └── build-layout-issues.usecase.ts
│   │   │   │   │   │   ├── delete-layout/
│   │   │   │   │   │   │   ├── delete-layout.command.ts
│   │   │   │   │   │   │   ├── delete-layout.use-case.spec.ts
│   │   │   │   │   │   │   ├── delete-layout.use-case.ts
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   ├── duplicate-layout/
│   │   │   │   │   │   │   ├── duplicate-layout.command.ts
│   │   │   │   │   │   │   ├── duplicate-layout.use-case.spec.ts
│   │   │   │   │   │   │   ├── duplicate-layout.use-case.ts
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   ├── get-layout-usage/
│   │   │   │   │   │   │   ├── get-layout-usage.command.ts
│   │   │   │   │   │   │   ├── get-layout-usage.usecase.ts
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── list-layouts/
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   ├── list-layouts.command.ts
│   │   │   │   │   │   │   ├── list-layouts.use-case.spec.ts
│   │   │   │   │   │   │   └── list-layouts.use-case.ts
│   │   │   │   │   │   ├── preview-layout/
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   ├── preview-layout.command.ts
│   │   │   │   │   │   │   ├── preview-layout.usecase.spec.ts
│   │   │   │   │   │   │   ├── preview-layout.usecase.ts
│   │   │   │   │   │   │   └── preview-utils.ts
│   │   │   │   │   │   ├── sync-to-environment/
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   ├── layout-sync-to-environment.command.ts
│   │   │   │   │   │   │   └── layout-sync-to-environment.usecase.ts
│   │   │   │   │   │   └── upsert-layout/
│   │   │   │   │   │       ├── index.ts
│   │   │   │   │   │       ├── upsert-layout.command.ts
│   │   │   │   │   │       ├── upsert-layout.usecase.spec.ts
│   │   │   │   │   │       └── upsert-layout.usecase.ts
│   │   │   │   │   └── utils/
│   │   │   │   │       └── layout-templates.ts
│   │   │   │   ├── message-template/
│   │   │   │   │   ├── message-template.controller.ts
│   │   │   │   │   ├── message-template.module.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── find-message-templates-by-layout/
│   │   │   │   │       │   ├── find-message-templates-by-layout.command.ts
│   │   │   │   │       │   ├── find-message-templates-by-layout.use-case.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       └── index.ts
│   │   │   │   ├── messages/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── delete-message-response.dto.ts
│   │   │   │   │   │   ├── get-messages-requests.dto.ts
│   │   │   │   │   │   └── remove-messages-by-transactionId-request.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── get-messages.e2e.ts
│   │   │   │   │   │   ├── remove-message.e2e.ts
│   │   │   │   │   │   └── remove-messages-by-transactionId.e2e.ts
│   │   │   │   │   ├── messages.controller.ts
│   │   │   │   │   ├── messages.module.ts
│   │   │   │   │   ├── params/
│   │   │   │   │   │   └── delete-message.param.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── get-messages/
│   │   │   │   │       │   ├── get-messages.command.ts
│   │   │   │   │       │   ├── get-messages.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       ├── remove-message/
│   │   │   │   │       │   ├── index.ts
│   │   │   │   │       │   ├── remove-message.command.ts
│   │   │   │   │       │   └── remove-message.usecase.ts
│   │   │   │   │       └── remove-messages-by-transactionId/
│   │   │   │   │           ├── remove-messages-by-transactionId.command.ts
│   │   │   │   │           └── remove-messages-by-transactionId.usecase.ts
│   │   │   │   ├── notification-groups/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── create-notification-group-request.dto.ts
│   │   │   │   │   │   ├── delete-notification-group-response.dto.ts
│   │   │   │   │   │   └── notification-group-response.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── create-notification-group.e2e.ts
│   │   │   │   │   │   ├── delete-notification-group.e2e.ts
│   │   │   │   │   │   ├── get-notification-group.e2e.ts
│   │   │   │   │   │   ├── get-notification-groups.e2e.ts
│   │   │   │   │   │   └── update-notification-group.e2e.ts
│   │   │   │   │   ├── notification-groups.controller.ts
│   │   │   │   │   ├── notification-groups.module.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── create-notification-group/
│   │   │   │   │       │   ├── create-notification-group.command.ts
│   │   │   │   │       │   └── create-notification-group.usecase.ts
│   │   │   │   │       ├── delete-notification-group/
│   │   │   │   │       │   ├── delete-notification-group.command.ts
│   │   │   │   │       │   └── delete-notification-group.usecase.ts
│   │   │   │   │       ├── get-notification-group/
│   │   │   │   │       │   ├── get-notification-group.command.ts
│   │   │   │   │       │   └── get-notification-group.usecase.ts
│   │   │   │   │       ├── get-notification-groups/
│   │   │   │   │       │   ├── get-notification-groups.command.ts
│   │   │   │   │       │   └── get-notification-groups.usecase.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       └── update-notification-group/
│   │   │   │   │           ├── update-notification-group.command.ts
│   │   │   │   │           └── update-notification-group.usecase.ts
│   │   │   │   ├── notifications/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── activities-request.dto.ts
│   │   │   │   │   │   ├── activities-response.dto.ts
│   │   │   │   │   │   ├── activity-graph-states-response.dto.ts
│   │   │   │   │   │   └── activity-stats-response.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── get-activity-feed.e2e.ts
│   │   │   │   │   │   └── get-activity.e2e.ts
│   │   │   │   │   ├── notification.controller.ts
│   │   │   │   │   ├── notification.module.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── get-activity/
│   │   │   │   │       │   ├── get-activity.command.ts
│   │   │   │   │       │   └── get-activity.usecase.ts
│   │   │   │   │       ├── get-activity-feed/
│   │   │   │   │       │   ├── get-activity-feed.command.ts
│   │   │   │   │       │   ├── get-activity-feed.usecase.spec.ts
│   │   │   │   │       │   ├── get-activity-feed.usecase.ts
│   │   │   │   │       │   └── map-feed-item-to.dto.ts
│   │   │   │   │       ├── get-activity-graph-states/
│   │   │   │   │       │   ├── get-activity-graph-states.command.ts
│   │   │   │   │       │   └── get-activity-graph-states.usecase.ts
│   │   │   │   │       ├── get-activity-stats/
│   │   │   │   │       │   ├── get-activity-stats.command.ts
│   │   │   │   │       │   ├── get-activity-stats.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       └── index.ts
│   │   │   │   ├── organization/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── create-organization.dto.ts
│   │   │   │   │   │   ├── get-my-organization.dto.ts
│   │   │   │   │   │   ├── get-organization-settings.dto.ts
│   │   │   │   │   │   ├── get-organizations.dto.ts
│   │   │   │   │   │   ├── member-response.dto.ts
│   │   │   │   │   │   ├── organization-response.dto.ts
│   │   │   │   │   │   ├── rename-organization.dto.ts
│   │   │   │   │   │   ├── update-branding-details.dto.ts
│   │   │   │   │   │   ├── update-member-roles.dto.ts
│   │   │   │   │   │   └── update-organization-settings.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── change-member-role.e2e.ts
│   │   │   │   │   │   ├── create-organization.e2e.ts
│   │   │   │   │   │   ├── get-members.e2e.ts
│   │   │   │   │   │   ├── get-my-organization.e2e.ts
│   │   │   │   │   │   ├── get-organizations.e2e.ts
│   │   │   │   │   │   ├── remove-member.e2e.ts
│   │   │   │   │   │   ├── rename-organization.e2e.ts
│   │   │   │   │   │   ├── update-branding-details.e2e.ts
│   │   │   │   │   │   └── update-organization-settings.e2e.ts
│   │   │   │   │   ├── ee.organization.controller.ts
│   │   │   │   │   ├── organization.controller.ts
│   │   │   │   │   ├── organization.module.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── create-organization/
│   │   │   │   │       │   ├── create-organization.command.ts
│   │   │   │   │       │   ├── create-organization.usecase.ts
│   │   │   │   │       │   └── sync-external-organization/
│   │   │   │   │       │       ├── sync-external-organization.command.ts
│   │   │   │   │       │       └── sync-external-organization.usecase.ts
│   │   │   │   │       ├── get-my-organization/
│   │   │   │   │       │   ├── get-my-organization.command.ts
│   │   │   │   │       │   └── get-my-organization.usecase.ts
│   │   │   │   │       ├── get-organization/
│   │   │   │   │       │   ├── get-organization.command.ts
│   │   │   │   │       │   └── get-organization.usecase.ts
│   │   │   │   │       ├── get-organization-settings/
│   │   │   │   │       │   ├── get-organization-settings.command.ts
│   │   │   │   │       │   └── get-organization-settings.usecase.ts
│   │   │   │   │       ├── get-organizations/
│   │   │   │   │       │   ├── get-organizations.command.ts
│   │   │   │   │       │   └── get-organizations.usecase.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       ├── membership/
│   │   │   │   │       │   ├── add-member/
│   │   │   │   │       │   │   ├── add-member.command.ts
│   │   │   │   │       │   │   └── add-member.usecase.ts
│   │   │   │   │       │   ├── change-member-role/
│   │   │   │   │       │   │   ├── change-member-role.command.ts
│   │   │   │   │       │   │   └── change-member-role.usecase.ts
│   │   │   │   │       │   ├── get-members/
│   │   │   │   │       │   │   ├── get-members.command.ts
│   │   │   │   │       │   │   └── get-members.usecase.ts
│   │   │   │   │       │   └── remove-member/
│   │   │   │   │       │       ├── remove-member.command.ts
│   │   │   │   │       │       └── remove-member.usecase.ts
│   │   │   │   │       ├── rename-organization/
│   │   │   │   │       │   ├── rename-organization-command.ts
│   │   │   │   │       │   └── rename-organization.usecase.ts
│   │   │   │   │       ├── update-branding-details/
│   │   │   │   │       │   ├── update-branding-details.command.ts
│   │   │   │   │       │   └── update-branding-details.usecase.ts
│   │   │   │   │       └── update-organization-settings/
│   │   │   │   │           ├── update-organization-settings.command.ts
│   │   │   │   │           └── update-organization-settings.usecase.ts
│   │   │   │   ├── outbound-webhooks/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── create-webhook-portal-response.dto.ts
│   │   │   │   │   │   └── get-webhook-portal-token-response.dto.ts
│   │   │   │   │   ├── outbound-webhooks.controller.ts
│   │   │   │   │   ├── outbound-webhooks.module.ts
│   │   │   │   │   ├── usecases/
│   │   │   │   │   │   ├── create-webhook-portal-token/
│   │   │   │   │   │   │   ├── create-webhook-portal.command.ts
│   │   │   │   │   │   │   └── create-webhook-portal.usecase.ts
│   │   │   │   │   │   └── get-webhook-portal-token/
│   │   │   │   │   │       ├── get-webhook-portal-token.command.ts
│   │   │   │   │   │       └── get-webhook-portal-token.usecase.ts
│   │   │   │   │   └── webhooks.const.ts
│   │   │   │   ├── partner-integrations/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── create-vercel-integration-request.dto.ts
│   │   │   │   │   │   ├── create-vercel-integration-response.dto.ts
│   │   │   │   │   │   └── update-vercel-integration-request.dto.ts
│   │   │   │   │   ├── partner-integrations.controller.ts
│   │   │   │   │   ├── partner-integrations.module.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── create-vercel-integration/
│   │   │   │   │       │   ├── create-vercel-integration.command.ts
│   │   │   │   │       │   ├── create-vercel-integration.spec.ts
│   │   │   │   │       │   └── create-vercel-integration.usecase.ts
│   │   │   │   │       ├── get-vercel-integration/
│   │   │   │   │       │   ├── get-vercel-integration.command.ts
│   │   │   │   │       │   ├── get-vercel-integration.spec.ts
│   │   │   │   │       │   └── get-vercel-integration.usecase.ts
│   │   │   │   │       ├── get-vercel-projects/
│   │   │   │   │       │   ├── get-vercel-integration-projects.command.ts
│   │   │   │   │       │   ├── get-vercel-integration-projects.spec.ts
│   │   │   │   │       │   └── get-vercel-integration-projects.usecase.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       ├── process-vercel-webhook/
│   │   │   │   │       │   ├── process-vercel-webhook.command.ts
│   │   │   │   │       │   ├── process-vercel-webhook.spec.ts
│   │   │   │   │       │   └── process-vercel-webhook.usecase.ts
│   │   │   │   │       └── update-vercel-integration/
│   │   │   │   │           ├── update-vercel-integration.command.ts
│   │   │   │   │           ├── update-vercel-integration.spec.ts
│   │   │   │   │           └── update-vercel-integration.usecase.ts
│   │   │   │   ├── preferences/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── preferences.dto.ts
│   │   │   │   │   │   └── upsert-preferences.dto.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── preferences.controller.ts
│   │   │   │   │   ├── preferences.module.ts
│   │   │   │   │   └── preferences.spec.ts
│   │   │   │   ├── rate-limiting/
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   └── throttler.guard.e2e.ts
│   │   │   │   │   ├── guards/
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── throttler.decorator.ts
│   │   │   │   │   │   └── throttler.guard.ts
│   │   │   │   │   ├── rate-limiting.module.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── evaluate-api-rate-limit/
│   │   │   │   │       │   ├── evaluate-api-rate-limit.command.ts
│   │   │   │   │       │   ├── evaluate-api-rate-limit.spec.ts
│   │   │   │   │       │   ├── evaluate-api-rate-limit.types.ts
│   │   │   │   │       │   ├── evaluate-api-rate-limit.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── evaluate-token-bucket-rate-limit/
│   │   │   │   │       │   ├── evaluate-token-bucket-rate-limit.command.ts
│   │   │   │   │       │   ├── evaluate-token-bucket-rate-limit.spec.ts
│   │   │   │   │       │   ├── evaluate-token-bucket-rate-limit.types.ts
│   │   │   │   │       │   ├── evaluate-token-bucket-rate-limit.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── get-api-rate-limit-algorithm-config/
│   │   │   │   │       │   ├── get-api-rate-limit-algorithm-config.spec.ts
│   │   │   │   │       │   ├── get-api-rate-limit-algorithm-config.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── get-api-rate-limit-cost-config/
│   │   │   │   │       │   ├── get-api-rate-limit-cost-config.spec.ts
│   │   │   │   │       │   ├── get-api-rate-limit-cost-config.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── get-api-rate-limit-maximum/
│   │   │   │   │       │   ├── get-api-rate-limit-maximum.command.ts
│   │   │   │   │       │   ├── get-api-rate-limit-maximum.dto.ts
│   │   │   │   │       │   ├── get-api-rate-limit-maximum.spec.ts
│   │   │   │   │       │   ├── get-api-rate-limit-maximum.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       └── index.ts
│   │   │   │   ├── shared/
│   │   │   │   │   ├── commands/
│   │   │   │   │   │   ├── authenticated.command.ts
│   │   │   │   │   │   ├── organization.command.ts
│   │   │   │   │   │   └── project.command.ts
│   │   │   │   │   ├── constants.ts
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── api-key.ts
│   │   │   │   │   │   ├── base-responses.ts
│   │   │   │   │   │   ├── base-subscriber-fields.dto.ts
│   │   │   │   │   │   ├── channel-preference.ts
│   │   │   │   │   │   ├── cursor-paginated-response.ts
│   │   │   │   │   │   ├── cursor-pagination-request.ts
│   │   │   │   │   │   ├── data-wrapper-dto.ts
│   │   │   │   │   │   ├── limit-offset-pagination.dto.ts
│   │   │   │   │   │   ├── message-template.ts
│   │   │   │   │   │   ├── message.template.dto.ts
│   │   │   │   │   │   ├── notification-step-dto.ts
│   │   │   │   │   │   ├── pagination-request.ts
│   │   │   │   │   │   ├── pagination-response.ts
│   │   │   │   │   │   ├── pagination-with-filters-request.ts
│   │   │   │   │   │   ├── preference-channels.ts
│   │   │   │   │   │   ├── schedule.ts
│   │   │   │   │   │   ├── subscription-details-response.dto.ts
│   │   │   │   │   │   └── subscriptions/
│   │   │   │   │   │       ├── create-subscriptions-response.dto.ts
│   │   │   │   │   │       ├── create-subscriptions.dto.ts
│   │   │   │   │   │       └── update-subscription.dto.ts
│   │   │   │   │   ├── framework/
│   │   │   │   │   │   ├── analytics-logs.guard.ts
│   │   │   │   │   │   ├── analytics-logs.interceptor.ts
│   │   │   │   │   │   ├── constants/
│   │   │   │   │   │   │   ├── headers.schema.ts
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   └── responses.schema.ts
│   │   │   │   │   │   ├── exclude-from-idempotency.ts
│   │   │   │   │   │   ├── idempotency.e2e.ts
│   │   │   │   │   │   ├── idempotency.interceptor.ts
│   │   │   │   │   │   ├── paginated-ok-response.decorator.ts
│   │   │   │   │   │   ├── response.decorator.ts
│   │   │   │   │   │   ├── response.interceptor.ts
│   │   │   │   │   │   ├── swagger/
│   │   │   │   │   │   │   ├── headers.decorator.ts
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   ├── injection.ts
│   │   │   │   │   │   │   ├── keyless.security.ts
│   │   │   │   │   │   │   ├── open.api.manipulation.component.ts
│   │   │   │   │   │   │   ├── responses.decorator.ts
│   │   │   │   │   │   │   ├── sdk.decorators.ts
│   │   │   │   │   │   │   └── swagger.controller.ts
│   │   │   │   │   │   └── user.decorator.ts
│   │   │   │   │   ├── helpers/
│   │   │   │   │   │   ├── content.service.spec.ts
│   │   │   │   │   │   ├── e2e/
│   │   │   │   │   │   │   └── sdk/
│   │   │   │   │   │   │       └── e2e-sdk.helper.ts
│   │   │   │   │   │   ├── generate-transaction-id.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── is-valid-hmac.ts
│   │   │   │   │   │   └── utils/
│   │   │   │   │   │       ├── index.ts
│   │   │   │   │   │       └── mapMarkMessageToWebSocketEvent.ts
│   │   │   │   │   ├── interceptors/
│   │   │   │   │   │   └── product-feature.interceptor.ts
│   │   │   │   │   ├── middleware/
│   │   │   │   │   │   └── request-id.middleware.ts
│   │   │   │   │   ├── services/
│   │   │   │   │   │   └── encryption/
│   │   │   │   │   │       └── index.ts
│   │   │   │   │   ├── shared.module.ts
│   │   │   │   │   ├── types.ts
│   │   │   │   │   ├── utils/
│   │   │   │   │   │   ├── auth.utils.ts
│   │   │   │   │   │   ├── mappers.ts
│   │   │   │   │   │   └── request-transaction.util.ts
│   │   │   │   │   └── validators/
│   │   │   │   │       ├── image.validator.ts
│   │   │   │   │       ├── is-enum-or-array.ts
│   │   │   │   │       ├── is-mongo-id-or-array-of-ids.validator.ts
│   │   │   │   │       ├── is-time-12-hour-format.validator.ts
│   │   │   │   │       ├── json-schema.validator.ts
│   │   │   │   │       └── weekly-schedule-disabled.validator.ts
│   │   │   │   ├── step-resolvers/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── deploy-step-resolver-request.dto.ts
│   │   │   │   │   │   ├── deploy-step-resolver-response.dto.ts
│   │   │   │   │   │   ├── disconnect-step-resolver-request.dto.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   └── step-resolvers-count-response.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   └── step-resolvers.e2e.ts
│   │   │   │   │   ├── services/
│   │   │   │   │   │   └── cloudflare-step-resolver-deploy.service.ts
│   │   │   │   │   ├── step-resolvers.controller.ts
│   │   │   │   │   ├── step-resolvers.module.ts
│   │   │   │   │   ├── usecases/
│   │   │   │   │   │   ├── deploy-step-resolver/
│   │   │   │   │   │   │   ├── deploy-step-resolver.command.ts
│   │   │   │   │   │   │   ├── deploy-step-resolver.usecase.ts
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   ├── get-step-resolvers-count/
│   │   │   │   │   │   │   ├── get-step-resolvers-count.usecase.ts
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   └── sync-step-resolver-to-environment/
│   │   │   │   │   │       ├── index.ts
│   │   │   │   │   │       ├── sync-step-resolver-to-environment.command.ts
│   │   │   │   │   │       └── sync-step-resolver-to-environment.usecase.ts
│   │   │   │   │   └── utils/
│   │   │   │   │       └── generate-step-resolver-worker-id.ts
│   │   │   │   ├── storage/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   └── upload-url-response.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   └── get-signed-url.e2e.ts
│   │   │   │   │   ├── storage.controller.ts
│   │   │   │   │   ├── storage.module.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── get-signed-url/
│   │   │   │   │       │   ├── get-signed-url.command.ts
│   │   │   │   │       │   └── get-signed-url.usecase.ts
│   │   │   │   │       └── index.ts
│   │   │   │   ├── subscribers/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── bulk-create-subscriber-response.dto.ts
│   │   │   │   │   │   ├── chat-oauth-request.dto.ts
│   │   │   │   │   │   ├── create-subscriber-request.dto.ts
│   │   │   │   │   │   ├── delete-subscriber-response.dto.ts
│   │   │   │   │   │   ├── get-in-app-notification-feed-for-subscriber.dto.ts
│   │   │   │   │   │   ├── get-subscriber-preferences-response.dto.ts
│   │   │   │   │   │   ├── get-subscribers.dto.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── mark-all-messages-as-request.dto.ts
│   │   │   │   │   │   ├── subscriber-feed-response.dto.ts
│   │   │   │   │   │   ├── subscriber-preference-override.dto.ts
│   │   │   │   │   │   ├── subscriber-preference-template-response.dto.ts
│   │   │   │   │   │   ├── subscriber-preference.dto.ts
│   │   │   │   │   │   ├── subscribers-response.dto.ts
│   │   │   │   │   │   ├── update-subscriber-global-preferences-request.dto.ts
│   │   │   │   │   │   ├── update-subscriber-online-flag-request.dto.ts
│   │   │   │   │   │   └── update-subscriber-request.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── bulk-create-subscribers.e2e.ts
│   │   │   │   │   │   ├── create-subscriber.e2e.ts
│   │   │   │   │   │   ├── get-notifications-feed.e2e.ts
│   │   │   │   │   │   ├── get-subscriber.e2e.ts
│   │   │   │   │   │   ├── get-unseen-count.e2e.ts
│   │   │   │   │   │   ├── helpers/
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   ├── mark-all-subscriber-messages.e2e.ts
│   │   │   │   │   │   ├── mark-as-by-mark.e2e.ts
│   │   │   │   │   │   ├── remove-subscriber.e2e.ts
│   │   │   │   │   │   └── update-online-flag.e2e.ts
│   │   │   │   │   ├── params/
│   │   │   │   │   │   ├── get-subscriber-preferences-by-level.params.ts
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── query-objects/
│   │   │   │   │   │   └── unseen-count.query.ts
│   │   │   │   │   ├── subscribersV1.controller.ts
│   │   │   │   │   ├── subscribersV1.module.ts
│   │   │   │   │   ├── unit/
│   │   │   │   │   │   └── update-subscriber-channel.spec.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── bulk-create-subscribers/
│   │   │   │   │       │   ├── bulk-create-subscribers.command.ts
│   │   │   │   │       │   ├── bulk-create-subscribers.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── chat-oauth/
│   │   │   │   │       │   ├── chat-oauth.command.ts
│   │   │   │   │       │   └── chat-oauth.usecase.ts
│   │   │   │   │       ├── chat-oauth-callback/
│   │   │   │   │       │   ├── chat-oauth-callback.command.ts
│   │   │   │   │       │   ├── chat-oauth-callback.result.ts
│   │   │   │   │       │   ├── chat-oauth-callback.usecase.ts
│   │   │   │   │       │   └── is-not-empty.spec.ts
│   │   │   │   │       ├── delete-subscriber-credentials/
│   │   │   │   │       │   ├── delete-subscriber-credentials.command.ts
│   │   │   │   │       │   ├── delete-subscriber-credentials.spec.ts
│   │   │   │   │       │   ├── delete-subscriber-credentials.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── get-preferences-by-level/
│   │   │   │   │       │   ├── get-preferences-by-level.command.ts
│   │   │   │   │       │   └── get-preferences-by-level.usecase.ts
│   │   │   │   │       ├── get-subscriber/
│   │   │   │   │       │   ├── get-subscriber.command.ts
│   │   │   │   │       │   ├── get-subscriber.spec.ts
│   │   │   │   │       │   ├── get-subscriber.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── get-subscriber-global-preference/
│   │   │   │   │       │   ├── get-subscriber-global-preference.command.ts
│   │   │   │   │       │   ├── get-subscriber-global-preference.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── get-subscriber-preference/
│   │   │   │   │       │   ├── get-subscriber-preference.command.ts
│   │   │   │   │       │   ├── get-subscriber-preference.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── get-subscribers/
│   │   │   │   │       │   ├── get-subscribers.command.ts
│   │   │   │   │       │   ├── get-subscribers.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       ├── remove-subscriber/
│   │   │   │   │       │   ├── index.ts
│   │   │   │   │       │   ├── remove-subscriber.command.ts
│   │   │   │   │       │   ├── remove-subscriber.spec.ts
│   │   │   │   │       │   └── remove-subscriber.usecase.ts
│   │   │   │   │       ├── search-by-external-subscriber-ids/
│   │   │   │   │       │   ├── index.ts
│   │   │   │   │       │   ├── search-by-external-subscriber-ids.command.ts
│   │   │   │   │       │   ├── search-by-external-subscriber-ids.spec.ts
│   │   │   │   │       │   └── search-by-external-subscriber-ids.use-case.ts
│   │   │   │   │       └── update-subscriber-online-flag/
│   │   │   │   │           ├── index.ts
│   │   │   │   │           ├── update-subscriber-online-flag.command.ts
│   │   │   │   │           └── update-subscriber-online-flag.usecase.ts
│   │   │   │   ├── subscribers-v2/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── bulk-update-subscriber-preferences.dto.ts
│   │   │   │   │   │   ├── context-keys-query.dto.ts
│   │   │   │   │   │   ├── create-subscriber.dto.ts
│   │   │   │   │   │   ├── cursor-pagination-query.dto.ts
│   │   │   │   │   │   ├── get-subscriber-notifications-count-query.dto.ts
│   │   │   │   │   │   ├── get-subscriber-notifications-count-response.dto.ts
│   │   │   │   │   │   ├── get-subscriber-notifications-query.dto.ts
│   │   │   │   │   │   ├── get-subscriber-notifications-response.dto.ts
│   │   │   │   │   │   ├── get-subscriber-preferences-request.dto.ts
│   │   │   │   │   │   ├── get-subscriber-preferences.dto.ts
│   │   │   │   │   │   ├── inbox-notification.dto.ts
│   │   │   │   │   │   ├── list-subscribers-query.dto.ts
│   │   │   │   │   │   ├── list-subscribers-response.dto.ts
│   │   │   │   │   │   ├── mark-subscriber-notifications-as-seen.dto.ts
│   │   │   │   │   │   ├── patch-subscriber-preferences.dto.ts
│   │   │   │   │   │   ├── patch-subscriber.dto.ts
│   │   │   │   │   │   ├── remove-subscriber.dto.ts
│   │   │   │   │   │   ├── snooze-subscriber-notification.dto.ts
│   │   │   │   │   │   ├── subscriber-global-preference.dto.ts
│   │   │   │   │   │   ├── subscriber-notification-action.dto.ts
│   │   │   │   │   │   ├── subscriber-preferences-workflow-info.dto.ts
│   │   │   │   │   │   ├── subscriber-workflow-preference.dto.ts
│   │   │   │   │   │   └── update-all-subscriber-notifications.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── create-subscriber.e2e.ts
│   │   │   │   │   │   ├── delete-subscriber.e2e.ts
│   │   │   │   │   │   ├── get-subscriber-preferences.e2e.ts
│   │   │   │   │   │   ├── get-subscriber.e2e.ts
│   │   │   │   │   │   ├── list-subscriber-subscriptions.e2e.ts
│   │   │   │   │   │   ├── patch-subscriber-preferences.e2e.ts
│   │   │   │   │   │   ├── patch-subscriber.e2e.ts
│   │   │   │   │   │   └── subscriber-notifications.e2e.ts
│   │   │   │   │   ├── subscribers.controller.e2e.ts
│   │   │   │   │   ├── subscribers.controller.ts
│   │   │   │   │   ├── subscribers.module.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── get-subscriber/
│   │   │   │   │       │   ├── get-subscriber.command.ts
│   │   │   │   │       │   └── get-subscriber.usecase.ts
│   │   │   │   │       ├── get-subscriber-preferences/
│   │   │   │   │       │   ├── get-subscriber-preferences.command.ts
│   │   │   │   │       │   └── get-subscriber-preferences.usecase.ts
│   │   │   │   │       ├── list-subscribers/
│   │   │   │   │       │   ├── list-subscribers.command.ts
│   │   │   │   │       │   ├── list-subscribers.usecase.ts
│   │   │   │   │       │   └── map-subscriber-entity-to.dto.ts
│   │   │   │   │       ├── patch-subscriber/
│   │   │   │   │       │   ├── patch-subscriber.command.ts
│   │   │   │   │       │   └── patch-subscriber.usecase.ts
│   │   │   │   │       ├── remove-subscriber/
│   │   │   │   │       │   ├── remove-subscriber.command.ts
│   │   │   │   │       │   └── remove-subscriber.usecase.ts
│   │   │   │   │       └── update-subscriber-preferences/
│   │   │   │   │           ├── update-subscriber-preferences.command.ts
│   │   │   │   │           └── update-subscriber-preferences.usecase.ts
│   │   │   │   ├── subscriptions/
│   │   │   │   │   ├── subscriptions.module.ts
│   │   │   │   │   ├── usecases/
│   │   │   │   │   │   ├── create-subscription-preferences/
│   │   │   │   │   │   │   ├── create-subscription-preferences.command.ts
│   │   │   │   │   │   │   ├── create-subscription-preferences.usecase.ts
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   ├── create-subscriptions/
│   │   │   │   │   │   │   ├── create-subscriptions.command.ts
│   │   │   │   │   │   │   ├── create-subscriptions.usecase.ts
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   ├── get-subscription/
│   │   │   │   │   │   │   ├── get-subscription.command.ts
│   │   │   │   │   │   │   └── get-subscription.usecase.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   └── update-subscription/
│   │   │   │   │   │       ├── index.ts
│   │   │   │   │   │       ├── update-subscription.command.ts
│   │   │   │   │   │       └── update-subscription.usecase.ts
│   │   │   │   │   └── utils/
│   │   │   │   │       └── subscriptions.ts
│   │   │   │   ├── support/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── create-thread.dto.ts
│   │   │   │   │   │   └── plain-card.dto.ts
│   │   │   │   │   ├── guards/
│   │   │   │   │   │   └── plain-cards.guard.ts
│   │   │   │   │   ├── support.controller.ts
│   │   │   │   │   ├── support.module.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── create-thread.command.ts
│   │   │   │   │       ├── create-thread.usecase.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       ├── plain-cards.command.ts
│   │   │   │   │       └── plain-cards.usecase.ts
│   │   │   │   ├── tenant/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── create-tenant-request.dto.ts
│   │   │   │   │   │   ├── create-tenant-response.dto.ts
│   │   │   │   │   │   ├── get-tenant-response.dto.ts
│   │   │   │   │   │   ├── get-tenants-request.dto.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── update-tenant-request.dto.ts
│   │   │   │   │   │   └── update-tenant-response.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── create-tenant.e2e.ts
│   │   │   │   │   │   ├── delete-tenant.e2e.ts
│   │   │   │   │   │   ├── get-tenant.e2e.ts
│   │   │   │   │   │   ├── get-tenants.e2e.ts
│   │   │   │   │   │   └── update-tenant.e2e.ts
│   │   │   │   │   ├── tenant.controller.ts
│   │   │   │   │   ├── tenant.module.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── delete-tenant/
│   │   │   │   │       │   ├── delete-tenant.command.ts
│   │   │   │   │       │   └── delete-tenant.usecase.ts
│   │   │   │   │       ├── get-tenants/
│   │   │   │   │       │   ├── get-tenants.command.ts
│   │   │   │   │       │   └── get-tenants.usecase.ts
│   │   │   │   │       └── index.ts
│   │   │   │   ├── testing/
│   │   │   │   │   ├── auth.controller.ts
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   └── idempotency.dto.ts
│   │   │   │   │   ├── product-feature.e2e.ts
│   │   │   │   │   ├── rate-limiting.controller.ts
│   │   │   │   │   ├── testing.controller.ts
│   │   │   │   │   └── testing.module.ts
│   │   │   │   ├── topics-v1/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── add-subscribers.dto.ts
│   │   │   │   │   │   ├── assignSubscriberToTopicDto.ts
│   │   │   │   │   │   ├── create-topic.dto.ts
│   │   │   │   │   │   ├── filter-topics.dto.ts
│   │   │   │   │   │   ├── get-topic.dto.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── remove-subscribers.dto.ts
│   │   │   │   │   │   ├── rename-topic.dto.ts
│   │   │   │   │   │   ├── topic-subscriber.dto.ts
│   │   │   │   │   │   └── topic.dto.ts
│   │   │   │   │   ├── topics-v1.controller.ts
│   │   │   │   │   ├── topics-v1.module.ts
│   │   │   │   │   ├── types/
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   └── use-cases/
│   │   │   │   │       ├── add-subscribers/
│   │   │   │   │       │   ├── add-subscribers.command.ts
│   │   │   │   │       │   ├── add-subscribers.use-case.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── create-topic/
│   │   │   │   │       │   ├── create-topic.command.ts
│   │   │   │   │       │   ├── create-topic.use-case.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── delete-topic/
│   │   │   │   │       │   ├── delete-topic.command.ts
│   │   │   │   │       │   ├── delete-topic.use-case.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── filter-topics/
│   │   │   │   │       │   ├── filter-topics.command.ts
│   │   │   │   │       │   ├── filter-topics.use-case.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── get-topic/
│   │   │   │   │       │   ├── get-topic.command.ts
│   │   │   │   │       │   ├── get-topic.use-case.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── get-topic-subscriber/
│   │   │   │   │       │   ├── get-topic-subscriber.command.ts
│   │   │   │   │       │   ├── get-topic-subscriber.use-case.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       ├── remove-subscribers/
│   │   │   │   │       │   ├── index.ts
│   │   │   │   │       │   ├── remove-subscribers.command.ts
│   │   │   │   │       │   └── remove-subscribers.use-case.ts
│   │   │   │   │       └── rename-topic/
│   │   │   │   │           ├── index.ts
│   │   │   │   │           ├── rename-topic.command.ts
│   │   │   │   │           └── rename-topic.use-case.ts
│   │   │   │   ├── topics-v2/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── create-topic-subscriptions.dto.ts
│   │   │   │   │   │   ├── create-update-topic.dto.ts
│   │   │   │   │   │   ├── cursor-pagination-query.dto.ts
│   │   │   │   │   │   ├── delete-topic-response.dto.ts
│   │   │   │   │   │   ├── delete-topic-subscriptions-response.dto.ts
│   │   │   │   │   │   ├── delete-topic-subscriptions.dto.ts
│   │   │   │   │   │   ├── list-subscriber-subscriptions-query.dto.ts
│   │   │   │   │   │   ├── list-topic-subscriptions-query.dto.ts
│   │   │   │   │   │   ├── list-topic-subscriptions-response.dto.ts
│   │   │   │   │   │   ├── list-topics-query.dto.ts
│   │   │   │   │   │   ├── list-topics-response.dto.ts
│   │   │   │   │   │   ├── topic-response.dto.ts
│   │   │   │   │   │   ├── topic-subscription-response.dto.ts
│   │   │   │   │   │   ├── update-topic-subscription.dto.ts
│   │   │   │   │   │   └── update-topic.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── create-topic-subscriptions.e2e.ts
│   │   │   │   │   │   ├── delete-topic-subscriptions.e2e.ts
│   │   │   │   │   │   ├── delete-topic.e2e.ts
│   │   │   │   │   │   ├── get-topic.e2e.ts
│   │   │   │   │   │   ├── list-topic-subscriptions.e2e.ts
│   │   │   │   │   │   ├── list-topics.e2e.ts
│   │   │   │   │   │   ├── update-topic-subscription.e2e.ts
│   │   │   │   │   │   ├── update-topic.e2e.ts
│   │   │   │   │   │   └── upsert-topic.e2e.ts
│   │   │   │   │   ├── topics-v2.module.ts
│   │   │   │   │   ├── topics.controller.ts
│   │   │   │   │   └── usecases/
│   │   │   │   │       ├── delete-topic/
│   │   │   │   │       │   ├── delete-topic.command.ts
│   │   │   │   │       │   └── delete-topic.usecase.ts
│   │   │   │   │       ├── delete-topic-subscriptions/
│   │   │   │   │       │   ├── delete-topic-subscriptions.command.ts
│   │   │   │   │       │   ├── delete-topic-subscriptions.usecase.ts
│   │   │   │   │       │   └── index.ts
│   │   │   │   │       ├── get-topic/
│   │   │   │   │       │   ├── get-topic.command.ts
│   │   │   │   │       │   └── get-topic.usecase.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       ├── list-subscriber-subscriptions/
│   │   │   │   │       │   ├── index.ts
│   │   │   │   │       │   ├── list-subscriber-subscriptions.command.ts
│   │   │   │   │       │   └── list-subscriber-subscriptions.usecase.ts
│   │   │   │   │       ├── list-topic-subscriptions/
│   │   │   │   │       │   ├── list-topic-subscriptions.command.ts
│   │   │   │   │       │   └── list-topic-subscriptions.usecase.ts
│   │   │   │   │       ├── list-topics/
│   │   │   │   │       │   ├── list-topics.command.ts
│   │   │   │   │       │   ├── list-topics.usecase.ts
│   │   │   │   │       │   └── map-topic-entity-to.dto.ts
│   │   │   │   │       ├── update-topic/
│   │   │   │   │       │   ├── update-topic.command.ts
│   │   │   │   │       │   └── update-topic.usecase.ts
│   │   │   │   │       └── upsert-topic/
│   │   │   │   │           ├── upsert-topic.command.ts
│   │   │   │   │           └── upsert-topic.usecase.ts
│   │   │   │   ├── translations/
│   │   │   │   │   └── e2e/
│   │   │   │   │       ├── v1/
│   │   │   │   │       │   ├── create-translation.e2e-ee.ts
│   │   │   │   │       │   ├── delete-translation-group.e2e-ee.ts
│   │   │   │   │       │   ├── delete-translation.e2e-ee.ts
│   │   │   │   │       │   ├── edit-translation.e2e-ee.ts
│   │   │   │   │       │   ├── get-locales-from-content.e2e-ee.ts
│   │   │   │   │       │   ├── get-locales.e2e-ee.ts
│   │   │   │   │       │   ├── get-translation-group.e2e-ee.ts
│   │   │   │   │       │   ├── get-translation-groups.e2e-ee.ts
│   │   │   │   │       │   ├── get-translation.e2e-ee.ts
│   │   │   │   │       │   ├── update-default-locale.e2e-ee.ts
│   │   │   │   │       │   └── update-translation.e2e-ee.ts
│   │   │   │   │       └── v2/
│   │   │   │   │           ├── create-translation.e2e-ee.ts
│   │   │   │   │           ├── delete-translation-group.e2e-ee.ts
│   │   │   │   │           ├── delete-translation.e2e-ee.ts
│   │   │   │   │           ├── export-master-json.e2e-ee.ts
│   │   │   │   │           ├── get-translation-group.e2e-ee.ts
│   │   │   │   │           ├── get-translation.e2e-ee.ts
│   │   │   │   │           ├── get-translations-list.e2e-ee.ts
│   │   │   │   │           ├── import-master-json.e2e-ee.ts
│   │   │   │   │           ├── translation-replacement.e2e-ee.ts
│   │   │   │   │           ├── upload-master-json.e2e-ee.ts
│   │   │   │   │           └── upload-translations.e2e-ee.ts
│   │   │   │   ├── user/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── change-profile-email.dto.ts
│   │   │   │   │   │   ├── update-profile-request.dto.ts
│   │   │   │   │   │   ├── user-onboarding-request.dto.ts
│   │   │   │   │   │   ├── user-onboarding-tour-request.dto.ts
│   │   │   │   │   │   └── user-response.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── email-change.e2e.ts
│   │   │   │   │   │   ├── get-me.e2e.ts
│   │   │   │   │   │   └── update-name-and-profile-picture.e2e.ts
│   │   │   │   │   ├── usecases/
│   │   │   │   │   │   ├── base-user-profile.usecase.ts
│   │   │   │   │   │   ├── create-user/
│   │   │   │   │   │   │   ├── create-user.command.ts
│   │   │   │   │   │   │   ├── create-user.usecase.ts
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   ├── get-my-profile/
│   │   │   │   │   │   │   ├── get-my-profile.dto.ts
│   │   │   │   │   │   │   └── get-my-profile.usecase.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── update-name-and-profile-picture/
│   │   │   │   │   │   │   ├── update-name-and-profile-picture.command.ts
│   │   │   │   │   │   │   └── update-name-and-profile-picture.usecase.ts
│   │   │   │   │   │   ├── update-on-boarding/
│   │   │   │   │   │   │   ├── update-on-boarding.command.ts
│   │   │   │   │   │   │   └── update-on-boarding.usecase.ts
│   │   │   │   │   │   ├── update-on-boarding-tour/
│   │   │   │   │   │   │   ├── update-on-boarding-tour.command.ts
│   │   │   │   │   │   │   └── update-on-boarding-tour.usecase.ts
│   │   │   │   │   │   └── update-profile-email/
│   │   │   │   │   │       ├── update-profile-email.command.ts
│   │   │   │   │   │       └── update-profile-email.usecase.ts
│   │   │   │   │   ├── user.controller.ts
│   │   │   │   │   └── user.module.ts
│   │   │   │   ├── widgets/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── feeds-response.dto.ts
│   │   │   │   │   │   ├── get-notifications-feed-request.dto.ts
│   │   │   │   │   │   ├── log-usage-request.dto.ts
│   │   │   │   │   │   ├── log-usage-response.dto.ts
│   │   │   │   │   │   ├── mark-as-request.dto.ts
│   │   │   │   │   │   ├── mark-message-action-as-seen.dto.ts
│   │   │   │   │   │   ├── mark-message-as-request.dto.ts
│   │   │   │   │   │   ├── message-response.dto.ts
│   │   │   │   │   │   ├── organization-response.dto.ts
│   │   │   │   │   │   ├── remove-all-messages.dto.ts
│   │   │   │   │   │   ├── remove-messages-bulk-request.dto.ts
│   │   │   │   │   │   ├── session-initialize-request.dto.ts
│   │   │   │   │   │   ├── session-initialize-response.dto.ts
│   │   │   │   │   │   ├── unseen-count-response.dto.ts
│   │   │   │   │   │   ├── update-subscriber-preference-request.dto.ts
│   │   │   │   │   │   └── update-subscriber-preference-response.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── get-count.e2e.ts
│   │   │   │   │   │   ├── get-notification-feed.e2e.ts
│   │   │   │   │   │   ├── get-subscriber-preference.e2e.ts
│   │   │   │   │   │   ├── get-unread-count.e2e.ts
│   │   │   │   │   │   ├── get-unseen-count.e2e.ts
│   │   │   │   │   │   ├── initialize-widget-session.e2e.ts
│   │   │   │   │   │   ├── mark-all-as-read.e2e.ts
│   │   │   │   │   │   ├── mark-as-by-mark.e2e.ts
│   │   │   │   │   │   ├── mark-as.e2e.ts
│   │   │   │   │   │   ├── remove-all-messages.e2e.ts
│   │   │   │   │   │   ├── remove-messages-bulk.e2e.ts
│   │   │   │   │   │   └── update-subscriber-preference.e2e.ts
│   │   │   │   │   ├── pipes/
│   │   │   │   │   │   └── limit-pipe/
│   │   │   │   │   │       ├── limit-pipe.spec.ts
│   │   │   │   │   │       └── limit-pipe.ts
│   │   │   │   │   ├── queries/
│   │   │   │   │   │   ├── get-count.query.ts
│   │   │   │   │   │   └── store.query.ts
│   │   │   │   │   ├── usecases/
│   │   │   │   │   │   ├── get-feed-count/
│   │   │   │   │   │   │   ├── get-feed-count.command.ts
│   │   │   │   │   │   │   └── get-feed-count.usecase.ts
│   │   │   │   │   │   ├── get-notifications-feed/
│   │   │   │   │   │   │   ├── get-notifications-feed.command.ts
│   │   │   │   │   │   │   └── get-notifications-feed.usecase.ts
│   │   │   │   │   │   ├── get-organization-data/
│   │   │   │   │   │   │   ├── get-organization-data.command.ts
│   │   │   │   │   │   │   └── get-organization-data.usecase.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── initialize-session/
│   │   │   │   │   │   │   ├── initialize-session.command.ts
│   │   │   │   │   │   │   └── initialize-session.usecase.ts
│   │   │   │   │   │   ├── mark-action-as-done/
│   │   │   │   │   │   │   ├── update-message-actions.command.ts
│   │   │   │   │   │   │   └── update-message-actions.usecase.ts
│   │   │   │   │   │   ├── mark-all-messages-as/
│   │   │   │   │   │   │   ├── mark-all-messages-as.command.ts
│   │   │   │   │   │   │   └── mark-all-messages-as.usecase.ts
│   │   │   │   │   │   ├── mark-message-as/
│   │   │   │   │   │   │   ├── mark-message-as.command.ts
│   │   │   │   │   │   │   └── mark-message-as.usecase.ts
│   │   │   │   │   │   ├── mark-message-as-by-mark/
│   │   │   │   │   │   │   ├── mark-message-as-by-mark.command.ts
│   │   │   │   │   │   │   └── mark-message-as-by-mark.usecase.ts
│   │   │   │   │   │   ├── remove-message/
│   │   │   │   │   │   │   ├── remove-message.command.ts
│   │   │   │   │   │   │   └── remove-message.usecase.ts
│   │   │   │   │   │   ├── remove-messages/
│   │   │   │   │   │   │   ├── remove-all-messages.command.ts
│   │   │   │   │   │   │   └── remove-all-messages.usecase.ts
│   │   │   │   │   │   └── remove-messages-bulk/
│   │   │   │   │   │       ├── remove-messages-bulk.command.ts
│   │   │   │   │   │       └── remove-messages-bulk.usecase.ts
│   │   │   │   │   ├── widgets.controller.ts
│   │   │   │   │   └── widgets.module.ts
│   │   │   │   ├── workflow-overrides/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── create-workflow-override-request.dto.ts
│   │   │   │   │   │   ├── create-workflow-override-response.dto.ts
│   │   │   │   │   │   ├── get-workflow-override-response.dto.ts
│   │   │   │   │   │   ├── get-workflow-overrides-request.dto.ts
│   │   │   │   │   │   ├── get-workflow-overrides-response.dto.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── shared.ts
│   │   │   │   │   │   ├── update-workflow-override-request.dto.ts
│   │   │   │   │   │   └── update-workflow-override-response.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── create-workflow-override.e2e.ts
│   │   │   │   │   │   ├── delete-workflow-override.e2e.ts
│   │   │   │   │   │   ├── get-workflow-override-by-id.e2e.ts
│   │   │   │   │   │   ├── get-workflow-override.e2e.ts
│   │   │   │   │   │   ├── get-workflow-overrides.e2e.ts
│   │   │   │   │   │   ├── update-workflow-override-by-id.e2e.ts
│   │   │   │   │   │   └── update-workflow-override.e2e.ts
│   │   │   │   │   ├── usecases/
│   │   │   │   │   │   ├── create-workflow-override/
│   │   │   │   │   │   │   ├── create-workflow-override.command.ts
│   │   │   │   │   │   │   └── create-workflow-override.usecase.ts
│   │   │   │   │   │   ├── delete-workflow-override/
│   │   │   │   │   │   │   ├── delete-workflow-override.command.ts
│   │   │   │   │   │   │   └── delete-workflow-override.usecase.ts
│   │   │   │   │   │   ├── get-workflow-override/
│   │   │   │   │   │   │   ├── get-workflow-override.command.ts
│   │   │   │   │   │   │   └── get-workflow-override.usecase.ts
│   │   │   │   │   │   ├── get-workflow-override-by-id/
│   │   │   │   │   │   │   ├── get-workflow-override-by-id.command.ts
│   │   │   │   │   │   │   └── get-workflow-override-by-id.usecase.ts
│   │   │   │   │   │   ├── get-workflow-overrides/
│   │   │   │   │   │   │   ├── get-workflow-overrides.command.ts
│   │   │   │   │   │   │   └── get-workflow-overrides.usecase.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── update-workflow-override/
│   │   │   │   │   │   │   ├── update-workflow-override.command.ts
│   │   │   │   │   │   │   └── update-workflow-override.usecase.ts
│   │   │   │   │   │   └── update-workflow-override-by-id/
│   │   │   │   │   │       ├── update-workflow-override-by-id.command.ts
│   │   │   │   │   │       └── update-workflow-override-by-id.usecase.ts
│   │   │   │   │   ├── workflow-overrides.controller.ts
│   │   │   │   │   └── workflow-overrides.module.ts
│   │   │   │   ├── workflows-v1/
│   │   │   │   │   ├── dtos/
│   │   │   │   │   │   ├── change-workflow-status-request.dto.ts
│   │   │   │   │   │   ├── create-workflow.request.dto.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── update-workflow-request.dto.ts
│   │   │   │   │   │   ├── variables.response.dto.ts
│   │   │   │   │   │   ├── workflow-response.dto.ts
│   │   │   │   │   │   ├── workflows-request.dto.ts
│   │   │   │   │   │   └── workflows.response.dto.ts
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   ├── change-template-status.e2e.ts
│   │   │   │   │   │   ├── create-notification-templates.e2e.ts
│   │   │   │   │   │   ├── delete-notification-template.e2e.ts
│   │   │   │   │   │   ├── get-notification-template.e2e.ts
│   │   │   │   │   │   ├── get-notification-templates.e2e.ts
│   │   │   │   │   │   └── update-notification-template.e2e.ts
│   │   │   │   │   ├── notification-template.controller.ts
│   │   │   │   │   ├── queries/
│   │   │   │   │   │   ├── CreateWorkflowQuery.ts
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── usecases/
│   │   │   │   │   │   ├── change-template-active-status/
│   │   │   │   │   │   │   ├── change-template-active-status.command.ts
│   │   │   │   │   │   │   └── change-template-active-status.usecase.ts
│   │   │   │   │   │   ├── delete-notification-template/
│   │   │   │   │   │   │   ├── delete-notification-template.command.ts
│   │   │   │   │   │   │   └── delete-notification-template.usecase.ts
│   │   │   │   │   │   ├── delete-workflow/
│   │   │   │   │   │   │   ├── delete-workflow.command.ts
│   │   │   │   │   │   │   └── delete-workflow.usecase.ts
│   │   │   │   │   │   ├── get-active-integrations-status/
│   │   │   │   │   │   │   ├── get-active-integrations-status.command.ts
│   │   │   │   │   │   │   ├── get-active-integrations-status.spec.ts
│   │   │   │   │   │   │   └── get-active-integrations-status.usecase.ts
│   │   │   │   │   │   ├── get-notification-template/
│   │   │   │   │   │   │   ├── get-notification-template.command.ts
│   │   │   │   │   │   │   └── get-notification-template.usecase.ts
│   │   │   │   │   │   ├── get-notification-templates/
│   │   │   │   │   │   │   ├── get-notification-templates.command.ts
│   │   │   │   │   │   │   └── get-notification-templates.usecase.ts
│   │   │   │   │   │   ├── get-workflow-variables/
│   │   │   │   │   │   │   ├── get-workflow-variables.command.ts
│   │   │   │   │   │   │   └── get-workflow-variables.usecase.ts
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── workflow-v1.controller.ts
│   │   │   │   │   └── workflow-v1.module.ts
│   │   │   │   └── workflows-v2/
│   │   │   │       ├── dtos/
│   │   │   │       │   ├── base-step-issue.dto.ts
│   │   │   │       │   ├── control-schemas.dto.ts
│   │   │   │       │   ├── create-step.dto.ts
│   │   │   │       │   ├── create-workflow.dto.ts
│   │   │   │       │   ├── duplicate-workflow.dto.ts
│   │   │   │       │   ├── get-list-query-params.ts
│   │   │   │       │   ├── index.ts
│   │   │   │       │   ├── list-workflow.dto.ts
│   │   │   │       │   ├── patch-step-data.dto.ts
│   │   │   │       │   ├── patch-workflow.dto.ts
│   │   │   │       │   ├── preferences.request.dto.ts
│   │   │   │       │   ├── sync-workflow.dto.ts
│   │   │   │       │   ├── test-http-endpoint.dto.ts
│   │   │   │       │   ├── update-workflow.dto.ts
│   │   │   │       │   └── workflow-test-data.dto.ts
│   │   │   │       ├── e2e/
│   │   │   │       │   ├── generate-preview.e2e.ts
│   │   │   │       │   ├── list-workflows.e2e.ts
│   │   │   │       │   └── upsert-workflow.e2e.ts
│   │   │   │       ├── exceptions/
│   │   │   │       │   ├── workflow-not-duplicable-exception.ts
│   │   │   │       │   └── workflow-not-syncable-exception.ts
│   │   │   │       ├── maily-test-data.ts
│   │   │   │       ├── usecases/
│   │   │   │       │   ├── build-test-data/
│   │   │   │       │   │   ├── build-workflow-test-data.command.ts
│   │   │   │       │   │   ├── build-workflow-test-data.usecase.ts
│   │   │   │       │   │   └── index.ts
│   │   │   │       │   ├── duplicate-workflow/
│   │   │   │       │   │   ├── duplicate-workflow.command.ts
│   │   │   │       │   │   ├── duplicate-workflow.usecase.ts
│   │   │   │       │   │   └── index.ts
│   │   │   │       │   ├── index.ts
│   │   │   │       │   ├── list-workflows/
│   │   │   │       │   │   ├── index.ts
│   │   │   │       │   │   ├── list-workflow.usecase.ts
│   │   │   │       │   │   └── list-workflows.command.ts
│   │   │   │       │   ├── patch-workflow/
│   │   │   │       │   │   ├── index.ts
│   │   │   │       │   │   ├── patch-workflow.command.ts
│   │   │   │       │   │   └── patch-workflow.usecase.ts
│   │   │   │       │   ├── sync-to-environment/
│   │   │   │       │   │   ├── index.ts
│   │   │   │       │   │   ├── sync-to-environment.command.ts
│   │   │   │       │   │   └── sync-to-environment.usecase.ts
│   │   │   │       │   └── test-http-endpoint/
│   │   │   │       │       ├── index.ts
│   │   │   │       │       ├── test-http-endpoint.command.ts
│   │   │   │       │       └── test-http-endpoint.usecase.ts
│   │   │   │       ├── workflow.controller.e2e.ts
│   │   │   │       ├── workflow.controller.ts
│   │   │   │       └── workflow.module.ts
│   │   │   ├── app.module.ts
│   │   │   ├── bootstrap.ts
│   │   │   ├── config/
│   │   │   │   ├── cors.config.spec.ts
│   │   │   │   ├── cors.config.ts
│   │   │   │   ├── env.config.ts
│   │   │   │   ├── env.validators.ts
│   │   │   │   └── index.ts
│   │   │   ├── error-dto.ts
│   │   │   ├── exception-filter.ts
│   │   │   ├── instrument.ts
│   │   │   ├── main.ts
│   │   │   ├── newrelic.ts
│   │   │   ├── types/
│   │   │   │   └── env.d.ts
│   │   │   └── utils/
│   │   │       └── payload-sanitizer.ts
│   │   ├── swagger-spec.json
│   │   ├── swc-register.js
│   │   ├── tsconfig.build.json
│   │   ├── tsconfig.json
│   │   ├── tsconfig.spec.json
│   │   └── webpack.config.js
│   ├── dashboard/
│   │   ├── .example.env
│   │   ├── .gitignore
│   │   ├── .vscode/
│   │   │   └── settings.json
│   │   ├── README.md
│   │   ├── components.json
│   │   ├── docker-entrypoint.sh
│   │   ├── dockerfile
│   │   ├── index.html
│   │   ├── netlify.toml
│   │   ├── package.json
│   │   ├── playwright.config.ts
│   │   ├── postcss.config.js
│   │   ├── public/
│   │   │   └── manifest.json
│   │   ├── src/
│   │   │   ├── api/
│   │   │   │   ├── activity.ts
│   │   │   │   ├── ai.ts
│   │   │   │   ├── api.client.ts
│   │   │   │   ├── billing.ts
│   │   │   │   ├── bridge.ts
│   │   │   │   ├── contexts.ts
│   │   │   │   ├── environment-variables.ts
│   │   │   │   ├── environments.ts
│   │   │   │   ├── integrations.ts
│   │   │   │   ├── layouts.ts
│   │   │   │   ├── logs.ts
│   │   │   │   ├── organization.ts
│   │   │   │   ├── partner-integrations.ts
│   │   │   │   ├── step-resolvers.ts
│   │   │   │   ├── steps.ts
│   │   │   │   ├── subscribers.ts
│   │   │   │   ├── telemetry.ts
│   │   │   │   ├── topics.ts
│   │   │   │   ├── translations.ts
│   │   │   │   ├── webhooks.ts
│   │   │   │   └── workflows.ts
│   │   │   ├── components/
│   │   │   │   ├── activity/
│   │   │   │   │   ├── activity-detail-card.tsx
│   │   │   │   │   ├── activity-empty-state.tsx
│   │   │   │   │   ├── activity-error.tsx
│   │   │   │   │   ├── activity-feed-content.tsx
│   │   │   │   │   ├── activity-filters.tsx
│   │   │   │   │   ├── activity-header.tsx
│   │   │   │   │   ├── activity-job-item.tsx
│   │   │   │   │   ├── activity-logs.tsx
│   │   │   │   │   ├── activity-panel.tsx
│   │   │   │   │   ├── activity-skeleton.tsx
│   │   │   │   │   ├── activity-table.tsx
│   │   │   │   │   ├── components/
│   │   │   │   │   │   ├── activity-overview.tsx
│   │   │   │   │   │   ├── activity-table-row.tsx
│   │   │   │   │   │   ├── overview-item.tsx
│   │   │   │   │   │   ├── status-badge.tsx
│   │   │   │   │   │   ├── status-preview-card.tsx
│   │   │   │   │   │   └── step-indicators.tsx
│   │   │   │   │   ├── constants.ts
│   │   │   │   │   ├── execution-detail-item.tsx
│   │   │   │   │   └── helpers.ts
│   │   │   │   ├── ai-drawer/
│   │   │   │   │   ├── ai-drawer-provider.tsx
│   │   │   │   │   ├── ai-drawer.tsx
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── use-ai-drawer.ts
│   │   │   │   ├── ai-elements/
│   │   │   │   │   ├── chain-of-thought.tsx
│   │   │   │   │   ├── conversation.tsx
│   │   │   │   │   ├── message.tsx
│   │   │   │   │   ├── prompt-input.tsx
│   │   │   │   │   └── shimmer.tsx
│   │   │   │   ├── ai-sidekick/
│   │   │   │   │   ├── ai-chat-context.tsx
│   │   │   │   │   ├── assistant-message.tsx
│   │   │   │   │   ├── chat-body.tsx
│   │   │   │   │   ├── chat-chain-of-thought.tsx
│   │   │   │   │   ├── chat-message-actions.tsx
│   │   │   │   │   ├── chat-message-response.tsx
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── message-utils.ts
│   │   │   │   │   ├── novu-copilot-panel.tsx
│   │   │   │   │   ├── sidekick-toast.tsx
│   │   │   │   │   └── user-message.tsx
│   │   │   │   ├── amount-input.tsx
│   │   │   │   ├── analytics/
│   │   │   │   │   ├── charts/
│   │   │   │   │   │   ├── active-subscribers-tooltip.tsx
│   │   │   │   │   │   ├── active-subscribers-trend-chart.tsx
│   │   │   │   │   │   ├── chart-dummy-data.tsx
│   │   │   │   │   │   ├── chart-empty-state.tsx
│   │   │   │   │   │   ├── chart-types.ts
│   │   │   │   │   │   ├── chart-wrapper.tsx
│   │   │   │   │   │   ├── delivery-trends-chart.tsx
│   │   │   │   │   │   ├── flickering-grid.tsx
│   │   │   │   │   │   ├── interaction-trend-chart.tsx
│   │   │   │   │   │   ├── providers-by-volume.tsx
│   │   │   │   │   │   ├── workflow-runs-trend-chart.tsx
│   │   │   │   │   │   └── workflows-by-volume.tsx
│   │   │   │   │   ├── components/
│   │   │   │   │   │   ├── analytics-page-skeleton.tsx
│   │   │   │   │   │   ├── analytics-section.tsx
│   │   │   │   │   │   ├── analytics-upgrade-cta-icon.tsx
│   │   │   │   │   │   ├── charts-section.tsx
│   │   │   │   │   │   └── flickering-grid-placeholder.tsx
│   │   │   │   │   ├── constants/
│   │   │   │   │   │   ├── analytics-page.consts.ts
│   │   │   │   │   │   └── analytics-tooltips.ts
│   │   │   │   │   ├── hooks/
│   │   │   │   │   │   └── use-analytics-page-date-filter.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── utils/
│   │   │   │   │       └── chart-validation.ts
│   │   │   │   ├── animated-outlet.tsx
│   │   │   │   ├── auth/
│   │   │   │   │   ├── auth-card.tsx
│   │   │   │   │   ├── auth-feature-row.tsx
│   │   │   │   │   ├── auth-side-banner.tsx
│   │   │   │   │   ├── create-organization.tsx
│   │   │   │   │   ├── inbox-playground.tsx
│   │   │   │   │   ├── inbox-preview-content.tsx
│   │   │   │   │   ├── mobile-message.tsx
│   │   │   │   │   ├── questionnaire-form.tsx
│   │   │   │   │   ├── region-picker.tsx
│   │   │   │   │   ├── shared.tsx
│   │   │   │   │   ├── trusted-companies.tsx
│   │   │   │   │   ├── usecase-selector.tsx
│   │   │   │   │   └── usecases-list.utils.tsx
│   │   │   │   ├── auth-layout.tsx
│   │   │   │   ├── billing/
│   │   │   │   │   ├── active-plan-banner.tsx
│   │   │   │   │   ├── contact-sales-button.tsx
│   │   │   │   │   ├── features-config.ts
│   │   │   │   │   ├── features.tsx
│   │   │   │   │   ├── plan-action-button.tsx
│   │   │   │   │   ├── plan-switcher.tsx
│   │   │   │   │   ├── plan.tsx
│   │   │   │   │   ├── plans-row.tsx
│   │   │   │   │   └── utils/
│   │   │   │   │       └── action.button.constants.ts
│   │   │   │   ├── command-palette/
│   │   │   │   │   ├── command-menu.tsx
│   │   │   │   │   ├── command-palette-provider.tsx
│   │   │   │   │   ├── command-palette.tsx
│   │   │   │   │   ├── command-types.ts
│   │   │   │   │   ├── commands/
│   │   │   │   │   │   ├── action-commands.tsx
│   │   │   │   │   │   ├── environment-commands.tsx
│   │   │   │   │   │   ├── help-commands.tsx
│   │   │   │   │   │   ├── navigation-commands.tsx
│   │   │   │   │   │   ├── settings-commands.tsx
│   │   │   │   │   │   ├── subscriber-commands.tsx
│   │   │   │   │   │   ├── workflow-commands.tsx
│   │   │   │   │   │   └── workflow-editor-commands.tsx
│   │   │   │   │   ├── hooks/
│   │   │   │   │   │   ├── use-command-palette.ts
│   │   │   │   │   │   ├── use-command-registry.ts
│   │   │   │   │   │   └── use-workflow-editor-context.ts
│   │   │   │   │   └── index.ts
│   │   │   │   ├── conditions-editor/
│   │   │   │   │   ├── add-condition-action.tsx
│   │   │   │   │   ├── add-group-action.tsx
│   │   │   │   │   ├── combinator-selector.tsx
│   │   │   │   │   ├── conditions-editor-context.tsx
│   │   │   │   │   ├── conditions-editor.tsx
│   │   │   │   │   ├── field-selector.tsx
│   │   │   │   │   ├── field-type-editors.ts
│   │   │   │   │   ├── field-type-operators.ts
│   │   │   │   │   ├── help-icon.tsx
│   │   │   │   │   ├── operator-selector.tsx
│   │   │   │   │   ├── rule-actions.tsx
│   │   │   │   │   ├── select-option-utils.tsx
│   │   │   │   │   ├── types.ts
│   │   │   │   │   ├── value-editor.tsx
│   │   │   │   │   └── variable-select.tsx
│   │   │   │   ├── confirmation-modal.tsx
│   │   │   │   ├── context-search-editor.tsx
│   │   │   │   ├── contexts/
│   │   │   │   │   ├── context-activity.tsx
│   │   │   │   │   ├── context-drawer.tsx
│   │   │   │   │   ├── context-filter.tsx
│   │   │   │   │   ├── context-list-blank.tsx
│   │   │   │   │   ├── context-list.tsx
│   │   │   │   │   ├── context-overview.tsx
│   │   │   │   │   ├── context-row.tsx
│   │   │   │   │   ├── contexts-filters.tsx
│   │   │   │   │   ├── create-context-drawer.tsx
│   │   │   │   │   ├── create-context-form.tsx
│   │   │   │   │   ├── empty-contexts-illustration.tsx
│   │   │   │   │   ├── hooks/
│   │   │   │   │   │   ├── use-contexts-navigate.ts
│   │   │   │   │   │   └── use-contexts-url-state.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── schema.ts
│   │   │   │   ├── create-workflow-modal.tsx
│   │   │   │   ├── dashboard-layout.tsx
│   │   │   │   ├── default-pagination.tsx
│   │   │   │   ├── delete-resource-confirmation-dialog.tsx
│   │   │   │   ├── delete-workflow-dialog.tsx
│   │   │   │   ├── editor-overlays.tsx
│   │   │   │   ├── email-editor-select.tsx
│   │   │   │   ├── environments/
│   │   │   │   │   ├── create-environment-button.tsx
│   │   │   │   │   ├── delete-environment-dialog.tsx
│   │   │   │   │   ├── edit-environment-sheet.tsx
│   │   │   │   │   ├── environment-form.tsx
│   │   │   │   │   ├── environments-free-state.tsx
│   │   │   │   │   └── environments-list.tsx
│   │   │   │   ├── flag-circle.tsx
│   │   │   │   ├── full-page-layout.tsx
│   │   │   │   ├── header-navigation/
│   │   │   │   │   ├── customer-support-button.tsx
│   │   │   │   │   ├── edit-bridge-url-button.tsx
│   │   │   │   │   ├── header-button.tsx
│   │   │   │   │   ├── header-navigation.tsx
│   │   │   │   │   ├── layout-usage-indicator.tsx
│   │   │   │   │   ├── no-changes-modal.tsx
│   │   │   │   │   ├── publish-button.tsx
│   │   │   │   │   ├── publish-modal.tsx
│   │   │   │   │   ├── publish-success-modal.tsx
│   │   │   │   │   ├── support-drawer-components.tsx
│   │   │   │   │   ├── support-drawer-constants.ts
│   │   │   │   │   ├── support-drawer.tsx
│   │   │   │   │   └── workflow-hover-card.tsx
│   │   │   │   ├── html-editor.tsx
│   │   │   │   ├── http-logs/
│   │   │   │   │   ├── api-traces-content.tsx
│   │   │   │   │   ├── hooks/
│   │   │   │   │   │   └── use-workflow-runs-url-state.ts
│   │   │   │   │   ├── http-status-badge.tsx
│   │   │   │   │   ├── logs-detail-content.tsx
│   │   │   │   │   ├── logs-detail-empty.tsx
│   │   │   │   │   ├── logs-detail-error.tsx
│   │   │   │   │   ├── logs-detail-header.tsx
│   │   │   │   │   ├── logs-detail-panel.tsx
│   │   │   │   │   ├── logs-detail-skeleton.tsx
│   │   │   │   │   ├── logs-empty-state.tsx
│   │   │   │   │   ├── logs-filters.tsx
│   │   │   │   │   ├── logs-table-row.tsx
│   │   │   │   │   ├── logs-table-skeleton-row.tsx
│   │   │   │   │   ├── logs-table.tsx
│   │   │   │   │   ├── method-badge.tsx
│   │   │   │   │   ├── transaction-id-display.tsx
│   │   │   │   │   ├── workflow-run-activity-drawer.tsx
│   │   │   │   │   ├── workflow-runs-content.tsx
│   │   │   │   │   └── workflow-runs-filters.tsx
│   │   │   │   ├── icons/
│   │   │   │   │   ├── add-subscriber-illustration.tsx
│   │   │   │   │   ├── api.tsx
│   │   │   │   │   ├── arrow-right.tsx
│   │   │   │   │   ├── bell.tsx
│   │   │   │   │   ├── broom-sparkle.tsx
│   │   │   │   │   ├── broom.tsx
│   │   │   │   │   ├── cards-blocks.tsx
│   │   │   │   │   ├── circle-check.tsx
│   │   │   │   │   ├── code-2.tsx
│   │   │   │   │   ├── delete.tsx
│   │   │   │   │   ├── digest-variable-icon.tsx
│   │   │   │   │   ├── email-footer-logo-with-text-stacked.tsx
│   │   │   │   │   ├── email-footer-plain-text.tsx
│   │   │   │   │   ├── email-footer.tsx
│   │   │   │   │   ├── email-header-centered-logo-with-border.tsx
│   │   │   │   │   ├── email-header-logo-with-cover-image.tsx
│   │   │   │   │   ├── email-header-logo-with-text.tsx
│   │   │   │   │   ├── email-header.tsx
│   │   │   │   │   ├── enter-line.tsx
│   │   │   │   │   ├── flags/
│   │   │   │   │   │   ├── eu.tsx
│   │   │   │   │   │   └── us.tsx
│   │   │   │   │   ├── horizontal-card-with-image.tsx
│   │   │   │   │   ├── inbox-arrow-down.tsx
│   │   │   │   │   ├── inbox-bell-filled-dev.tsx
│   │   │   │   │   ├── inbox-bell-filled.tsx
│   │   │   │   │   ├── inbox-bell.tsx
│   │   │   │   │   ├── inbox-ellipsis.tsx
│   │   │   │   │   ├── inbox-settings.tsx
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── information-card-with-logo.tsx
│   │   │   │   │   ├── logo-circle.tsx
│   │   │   │   │   ├── mail-3-fill.tsx
│   │   │   │   │   ├── notification-5-fill.tsx
│   │   │   │   │   ├── novu-icon.tsx
│   │   │   │   │   ├── onboarding-arrow-left.tsx
│   │   │   │   │   ├── paragraph-with-image.tsx
│   │   │   │   │   ├── plug.tsx
│   │   │   │   │   ├── preferences-blank-illustration.tsx
│   │   │   │   │   ├── refresh.tsx
│   │   │   │   │   ├── repeat-play.tsx
│   │   │   │   │   ├── repeat-variable.tsx
│   │   │   │   │   ├── route-fill.tsx
│   │   │   │   │   ├── shield-zap.tsx
│   │   │   │   │   ├── sms.tsx
│   │   │   │   │   ├── sparkling.tsx
│   │   │   │   │   ├── square-two-stack.tsx
│   │   │   │   │   ├── stacked-dots.tsx
│   │   │   │   │   ├── stacked-plus-line.tsx
│   │   │   │   │   ├── target-arrow.tsx
│   │   │   │   │   ├── translate-variable.tsx
│   │   │   │   │   ├── translated-layout-icon.tsx
│   │   │   │   │   ├── translated-workflow.tsx
│   │   │   │   │   ├── trend-line-down.tsx
│   │   │   │   │   ├── trend-line-up.tsx
│   │   │   │   │   ├── utils.ts
│   │   │   │   │   ├── version-control-dev.tsx
│   │   │   │   │   ├── version-control-prod.tsx
│   │   │   │   │   └── workflow-trigger-inbox.tsx
│   │   │   │   ├── in-app-action-dropdown.tsx
│   │   │   │   ├── inbox-button.tsx
│   │   │   │   ├── integrations/
│   │   │   │   │   ├── components/
│   │   │   │   │   │   ├── channel-tabs.tsx
│   │   │   │   │   │   ├── configuration-group.tsx
│   │   │   │   │   │   ├── create-integration-sidebar.tsx
│   │   │   │   │   │   ├── credential-section.tsx
│   │   │   │   │   │   ├── cross-channel-configs-group.tsx
│   │   │   │   │   │   ├── description-with-links.tsx
│   │   │   │   │   │   ├── hooks/
│   │   │   │   │   │   │   ├── use-integration-list.ts
│   │   │   │   │   │   │   ├── use-integration-primary-modal.tsx
│   │   │   │   │   │   │   └── use-sidebar-navigation-manager.ts
│   │   │   │   │   │   ├── inbound-webhook-group.tsx
│   │   │   │   │   │   ├── inbound-webhook-url.tsx
│   │   │   │   │   │   ├── integration-card.tsx
│   │   │   │   │   │   ├── integration-channel-group.tsx
│   │   │   │   │   │   ├── integration-general-settings.tsx
│   │   │   │   │   │   ├── integration-list-item.tsx
│   │   │   │   │   │   ├── integration-settings.tsx
│   │   │   │   │   │   ├── integration-sheet-header.tsx
│   │   │   │   │   │   ├── integration-sheet.tsx
│   │   │   │   │   │   ├── integrations-list.tsx
│   │   │   │   │   │   ├── modals/
│   │   │   │   │   │   │   ├── delete-integration-modal.tsx
│   │   │   │   │   │   │   └── select-primary-integration-modal.tsx
│   │   │   │   │   │   ├── provider-icon.tsx
│   │   │   │   │   │   ├── update-integration-sidebar.tsx
│   │   │   │   │   │   └── utils/
│   │   │   │   │   │       ├── handle-integration-error.ts
│   │   │   │   │   │       └── helpers.ts
│   │   │   │   │   ├── types.ts
│   │   │   │   │   └── utils/
│   │   │   │   │       └── channels.ts
│   │   │   │   ├── issues-panel.tsx
│   │   │   │   ├── layouts/
│   │   │   │   │   ├── component-utils.tsx
│   │   │   │   │   ├── create-layout-btn.tsx
│   │   │   │   │   ├── create-layout-form.tsx
│   │   │   │   │   ├── delete-layout-dialog.tsx
│   │   │   │   │   ├── empty-layouts-illustration.tsx
│   │   │   │   │   ├── hooks/
│   │   │   │   │   │   └── use-layouts-url-state.tsx
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── layout-breadcrumbs.tsx
│   │   │   │   │   ├── layout-control-input.tsx
│   │   │   │   │   ├── layout-editor-factory.tsx
│   │   │   │   │   ├── layout-editor-provider.tsx
│   │   │   │   │   ├── layout-editor-settings-drawer.tsx
│   │   │   │   │   ├── layout-editor-skeleton.tsx
│   │   │   │   │   ├── layout-editor.tsx
│   │   │   │   │   ├── layout-email-body.tsx
│   │   │   │   │   ├── layout-email-editor.tsx
│   │   │   │   │   ├── layout-list-blank.tsx
│   │   │   │   │   ├── layout-list.tsx
│   │   │   │   │   ├── layout-preview-context-panel.tsx
│   │   │   │   │   ├── layout-preview-factory.tsx
│   │   │   │   │   ├── layout-row.tsx
│   │   │   │   │   ├── layouts-filters.tsx
│   │   │   │   │   ├── layouts-list-upgrade-cta.tsx
│   │   │   │   │   └── schema.ts
│   │   │   │   ├── list-no-results.tsx
│   │   │   │   ├── maily/
│   │   │   │   │   ├── blocks/
│   │   │   │   │   │   ├── block-custom-preview.tsx
│   │   │   │   │   │   ├── cards.tsx
│   │   │   │   │   │   ├── digest.tsx
│   │   │   │   │   │   ├── footers.tsx
│   │   │   │   │   │   ├── headers.tsx
│   │   │   │   │   │   └── html.tsx
│   │   │   │   │   ├── maily-config.tsx
│   │   │   │   │   ├── maily-utils.ts
│   │   │   │   │   ├── maily.tsx
│   │   │   │   │   ├── repeat-block-aliases.ts
│   │   │   │   │   ├── repeat-menu-description.tsx
│   │   │   │   │   ├── types.ts
│   │   │   │   │   ├── variables.ts
│   │   │   │   │   └── views/
│   │   │   │   │       ├── for-view.tsx
│   │   │   │   │       ├── html-view.tsx
│   │   │   │   │       ├── maily-variables-list-view.tsx
│   │   │   │   │       └── variable-view.tsx
│   │   │   │   ├── mobile-desktop-prompt.tsx
│   │   │   │   ├── onboarding/
│   │   │   │   │   ├── animated-page.tsx
│   │   │   │   │   └── stepper.tsx
│   │   │   │   ├── page-meta.tsx
│   │   │   │   ├── pause-workflow-dialog.tsx
│   │   │   │   ├── preview-context-section.tsx
│   │   │   │   ├── preview-env-section.tsx
│   │   │   │   ├── preview-subscriber-section.tsx
│   │   │   │   ├── primitives/
│   │   │   │   │   ├── accordion.tsx
│   │   │   │   │   ├── analytics-card.tsx
│   │   │   │   │   ├── animated-number.tsx
│   │   │   │   │   ├── autocomplete.tsx
│   │   │   │   │   ├── avatar.tsx
│   │   │   │   │   ├── badge.tsx
│   │   │   │   │   ├── breadcrumb.tsx
│   │   │   │   │   ├── button-compact.tsx
│   │   │   │   │   ├── button-group.tsx
│   │   │   │   │   ├── button-link.tsx
│   │   │   │   │   ├── button.tsx
│   │   │   │   │   ├── card.tsx
│   │   │   │   │   ├── chart.tsx
│   │   │   │   │   ├── checkbox.tsx
│   │   │   │   │   ├── code-block.tsx
│   │   │   │   │   ├── collapsible.tsx
│   │   │   │   │   ├── color-picker.tsx
│   │   │   │   │   ├── command.tsx
│   │   │   │   │   ├── constants.ts
│   │   │   │   │   ├── container.tsx
│   │   │   │   │   ├── copy-button.tsx
│   │   │   │   │   ├── copy-to-clipboard.tsx
│   │   │   │   │   ├── dialog.tsx
│   │   │   │   │   ├── dropdown-menu.tsx
│   │   │   │   │   ├── editor.tsx
│   │   │   │   │   ├── environment-branch-icon.tsx
│   │   │   │   │   ├── form/
│   │   │   │   │   │   ├── avatar-picker.tsx
│   │   │   │   │   │   ├── faceted-filter/
│   │   │   │   │   │   │   ├── components/
│   │   │   │   │   │   │   │   ├── base-filter-content.tsx
│   │   │   │   │   │   │   │   ├── clear-button.tsx
│   │   │   │   │   │   │   │   ├── filter-badge.tsx
│   │   │   │   │   │   │   │   ├── filter-input.tsx
│   │   │   │   │   │   │   │   ├── multi-filter-content.tsx
│   │   │   │   │   │   │   │   ├── single-filter-content.tsx
│   │   │   │   │   │   │   │   └── text-filter-content.tsx
│   │   │   │   │   │   │   ├── facated-form-filter.tsx
│   │   │   │   │   │   │   ├── hooks/
│   │   │   │   │   │   │   │   └── use-keyboard-navigation.ts
│   │   │   │   │   │   │   ├── styles.ts
│   │   │   │   │   │   │   └── types.ts
│   │   │   │   │   │   ├── form-context.ts
│   │   │   │   │   │   └── form.tsx
│   │   │   │   │   ├── help-tooltip-indicator.tsx
│   │   │   │   │   ├── hint.tsx
│   │   │   │   │   ├── hover-card.tsx
│   │   │   │   │   ├── inline-toast.tsx
│   │   │   │   │   ├── input-group.tsx
│   │   │   │   │   ├── input.tsx
│   │   │   │   │   ├── kbd.tsx
│   │   │   │   │   ├── label.tsx
│   │   │   │   │   ├── loading-indicator.tsx
│   │   │   │   │   ├── locale-select.tsx
│   │   │   │   │   ├── multi-select.tsx
│   │   │   │   │   ├── pagination.tsx
│   │   │   │   │   ├── panel.tsx
│   │   │   │   │   ├── permission-button.tsx
│   │   │   │   │   ├── permission-switch.tsx
│   │   │   │   │   ├── phone-input.tsx
│   │   │   │   │   ├── popover.tsx
│   │   │   │   │   ├── progress.tsx
│   │   │   │   │   ├── radio-group.tsx
│   │   │   │   │   ├── resizable.tsx
│   │   │   │   │   ├── scroll-area.tsx
│   │   │   │   │   ├── secret-input.tsx
│   │   │   │   │   ├── segmented-control.tsx
│   │   │   │   │   ├── select.tsx
│   │   │   │   │   ├── separator.tsx
│   │   │   │   │   ├── sheet.tsx
│   │   │   │   │   ├── skeleton.tsx
│   │   │   │   │   ├── sonner-helpers.tsx
│   │   │   │   │   ├── sonner.tsx
│   │   │   │   │   ├── status-badge.tsx
│   │   │   │   │   ├── step.tsx
│   │   │   │   │   ├── switch.tsx
│   │   │   │   │   ├── table-pagination-footer.tsx
│   │   │   │   │   ├── table.tsx
│   │   │   │   │   ├── tabs.tsx
│   │   │   │   │   ├── tag-input.tsx
│   │   │   │   │   ├── tag.tsx
│   │   │   │   │   ├── text-separator.tsx
│   │   │   │   │   ├── textarea.tsx
│   │   │   │   │   ├── timeline.tsx
│   │   │   │   │   ├── toggle-group.tsx
│   │   │   │   │   ├── toggle.tsx
│   │   │   │   │   ├── tooltip.tsx
│   │   │   │   │   ├── translation-plugin/
│   │   │   │   │   │   ├── autocomplete.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── pill-widget.ts
│   │   │   │   │   │   ├── plugin-view.ts
│   │   │   │   │   │   └── utils.ts
│   │   │   │   │   ├── variable-editor.tsx
│   │   │   │   │   ├── variable-plugin/
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── plugin-view.ts
│   │   │   │   │   │   ├── types.ts
│   │   │   │   │   │   ├── utils.ts
│   │   │   │   │   │   ├── variable-pill-widget.ts
│   │   │   │   │   │   └── variable-theme.ts
│   │   │   │   │   └── visually-hidden.tsx
│   │   │   │   ├── promotional/
│   │   │   │   │   └── coming-soon-banner.tsx
│   │   │   │   ├── protected-drawer.tsx
│   │   │   │   ├── regenerate-api-keys-dialog.tsx
│   │   │   │   ├── schema-editor/
│   │   │   │   │   ├── components/
│   │   │   │   │   │   ├── array-section.tsx
│   │   │   │   │   │   ├── enum-section.tsx
│   │   │   │   │   │   ├── object-section.tsx
│   │   │   │   │   │   ├── property-actions.tsx
│   │   │   │   │   │   ├── property-name-input.tsx
│   │   │   │   │   │   └── property-type-selector.tsx
│   │   │   │   │   ├── constants.ts
│   │   │   │   │   ├── hooks/
│   │   │   │   │   │   ├── use-property-paths.ts
│   │   │   │   │   │   └── use-schema-property-type.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── json-schema.ts
│   │   │   │   │   ├── schema-editor.tsx
│   │   │   │   │   ├── schema-property-row.tsx
│   │   │   │   │   ├── schema-property-settings-popover.tsx
│   │   │   │   │   ├── types/
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   └── schema-form.types.ts
│   │   │   │   │   ├── use-schema-form.ts
│   │   │   │   │   └── utils/
│   │   │   │   │       ├── check-variable-usage.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       ├── json-helpers.ts
│   │   │   │   │       ├── property-manager.ts
│   │   │   │   │       ├── schema-change-detection.ts
│   │   │   │   │       ├── schema-converter.ts
│   │   │   │   │       ├── ui-helpers.ts
│   │   │   │   │       └── validation-schema.ts
│   │   │   │   ├── settings/
│   │   │   │   │   ├── novu-branding-switch.tsx
│   │   │   │   │   └── organization-settings.tsx
│   │   │   │   ├── shared/
│   │   │   │   │   └── external-link.tsx
│   │   │   │   ├── side-navigation/
│   │   │   │   │   ├── changelog-cards.tsx
│   │   │   │   │   ├── environment-dropdown.tsx
│   │   │   │   │   ├── free-trial-card.tsx
│   │   │   │   │   ├── getting-started-menu-item.tsx
│   │   │   │   │   ├── mobile-side-navigation.tsx
│   │   │   │   │   ├── navigation-link.tsx
│   │   │   │   │   ├── organization-dropdown-clerk.tsx
│   │   │   │   │   ├── organization-dropdown.tsx
│   │   │   │   │   ├── side-navigation.tsx
│   │   │   │   │   ├── sidebar.tsx
│   │   │   │   │   └── usage-card.tsx
│   │   │   │   ├── step-preview-hover-card.tsx
│   │   │   │   ├── subscribers/
│   │   │   │   │   ├── create-subscriber-form.tsx
│   │   │   │   │   ├── hooks/
│   │   │   │   │   │   ├── use-delete-subscription.ts
│   │   │   │   │   │   ├── use-get-subscription.ts
│   │   │   │   │   │   ├── use-subscriber-search.ts
│   │   │   │   │   │   ├── use-subscribers-navigate.ts
│   │   │   │   │   │   └── use-subscribers-url-state.ts
│   │   │   │   │   ├── preferences/
│   │   │   │   │   │   ├── day-schedule-copy.tsx
│   │   │   │   │   │   ├── preferences-blank.tsx
│   │   │   │   │   │   ├── preferences-item.tsx
│   │   │   │   │   │   ├── preferences-skeleton.tsx
│   │   │   │   │   │   ├── preferences.tsx
│   │   │   │   │   │   ├── schedule-table.tsx
│   │   │   │   │   │   ├── subscribers-schedule.tsx
│   │   │   │   │   │   ├── utils.ts
│   │   │   │   │   │   └── workflow-preferences.tsx
│   │   │   │   │   ├── schema.ts
│   │   │   │   │   ├── subscriber-activity-drawer.tsx
│   │   │   │   │   ├── subscriber-activity-list.tsx
│   │   │   │   │   ├── subscriber-activity.tsx
│   │   │   │   │   ├── subscriber-autocomplete.tsx
│   │   │   │   │   ├── subscriber-drawer.tsx
│   │   │   │   │   ├── subscriber-list-blank.tsx
│   │   │   │   │   ├── subscriber-list.tsx
│   │   │   │   │   ├── subscriber-overview-form.tsx
│   │   │   │   │   ├── subscriber-overview-skeleton.tsx
│   │   │   │   │   ├── subscriber-row.tsx
│   │   │   │   │   ├── subscriber-tabs.tsx
│   │   │   │   │   ├── subscribers-filters.tsx
│   │   │   │   │   ├── subscriptions/
│   │   │   │   │   │   ├── subscriber-subscriptions.tsx
│   │   │   │   │   │   ├── subscription-item.tsx
│   │   │   │   │   │   ├── subscription-preference-rule.tsx
│   │   │   │   │   │   ├── subscription-preferences-drawer.tsx
│   │   │   │   │   │   ├── subscription-preferences.tsx
│   │   │   │   │   │   └── subscriptions-empty-state.tsx
│   │   │   │   │   ├── timezone-select.tsx
│   │   │   │   │   └── utils.ts
│   │   │   │   ├── success-button-toast.tsx
│   │   │   │   ├── template-store/
│   │   │   │   │   ├── components/
│   │   │   │   │   │   └── workflow-results.tsx
│   │   │   │   │   ├── featured.ts
│   │   │   │   │   ├── types.ts
│   │   │   │   │   ├── workflow-card.tsx
│   │   │   │   │   ├── workflow-sidebar.tsx
│   │   │   │   │   └── workflow-template-modal.tsx
│   │   │   │   ├── time-display-hover-card.tsx
│   │   │   │   ├── topics/
│   │   │   │   │   ├── add-subscriber-form.tsx
│   │   │   │   │   ├── create-topic-drawer.tsx
│   │   │   │   │   ├── create-topic-form.tsx
│   │   │   │   │   ├── empty-topics-illustration.tsx
│   │   │   │   │   ├── hooks/
│   │   │   │   │   │   ├── use-delete-topic.ts
│   │   │   │   │   │   ├── use-topic-subscribers.ts
│   │   │   │   │   │   ├── use-topic.ts
│   │   │   │   │   │   ├── use-topics-navigate.ts
│   │   │   │   │   │   └── use-topics-url-state.ts
│   │   │   │   │   ├── subscription-count-badge.tsx
│   │   │   │   │   ├── topic-activity.tsx
│   │   │   │   │   ├── topic-drawer.tsx
│   │   │   │   │   ├── topic-list-blank.tsx
│   │   │   │   │   ├── topic-list.tsx
│   │   │   │   │   ├── topic-overview-form.tsx
│   │   │   │   │   ├── topic-row.tsx
│   │   │   │   │   ├── topic-subscriber-filter.tsx
│   │   │   │   │   ├── topic-subscriber-item.tsx
│   │   │   │   │   ├── topics-filters.tsx
│   │   │   │   │   └── types.ts
│   │   │   │   ├── translations/
│   │   │   │   │   ├── constants.ts
│   │   │   │   │   ├── delete-translation-modal.tsx
│   │   │   │   │   ├── empty-translations-illustration.tsx
│   │   │   │   │   ├── hooks/
│   │   │   │   │   │   ├── use-delete-translation-modal.ts
│   │   │   │   │   │   ├── use-translation-list-logic.ts
│   │   │   │   │   │   └── use-translations-url-state.tsx
│   │   │   │   │   ├── translation-drawer/
│   │   │   │   │   │   ├── editor-actions.tsx
│   │   │   │   │   │   ├── editor-panel.tsx
│   │   │   │   │   │   ├── hooks/
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   ├── use-translation-editor.ts
│   │   │   │   │   │   │   └── use-translation-file-operations.ts
│   │   │   │   │   │   ├── locale-list.tsx
│   │   │   │   │   │   ├── translation-drawer-content.tsx
│   │   │   │   │   │   ├── translation-drawer.tsx
│   │   │   │   │   │   ├── translation-header.tsx
│   │   │   │   │   │   └── use-translation-drawer-logic.tsx
│   │   │   │   │   ├── translation-import-trigger.tsx
│   │   │   │   │   ├── translation-list-upgrade-cta.tsx
│   │   │   │   │   ├── translation-list.tsx
│   │   │   │   │   ├── translation-onboarding-page.tsx
│   │   │   │   │   ├── translation-row.tsx
│   │   │   │   │   ├── translation-settings-drawer.tsx
│   │   │   │   │   ├── translation-status.tsx
│   │   │   │   │   ├── translation-switch.tsx
│   │   │   │   │   ├── translations-filters.tsx
│   │   │   │   │   └── utils.ts
│   │   │   │   ├── truncated-text.tsx
│   │   │   │   ├── unsaved-changes-alert-dialog.tsx
│   │   │   │   ├── updated-ago.tsx
│   │   │   │   ├── upgrade-cta-tooltip.tsx
│   │   │   │   ├── usecase-playground-header.tsx
│   │   │   │   ├── user-profile.tsx
│   │   │   │   ├── variable/
│   │   │   │   │   ├── components/
│   │   │   │   │   │   ├── digest-count-summary-preview.tsx
│   │   │   │   │   │   ├── digest-sentence-summary-preview.tsx
│   │   │   │   │   │   ├── filter-item.tsx
│   │   │   │   │   │   ├── new-variable-preview.tsx
│   │   │   │   │   │   ├── reorder-filter-item.tsx
│   │   │   │   │   │   ├── reorder-filters-group.tsx
│   │   │   │   │   │   ├── variable-icon.tsx
│   │   │   │   │   │   └── variable-preview.tsx
│   │   │   │   │   ├── constants.ts
│   │   │   │   │   ├── edit-variable-popover.tsx
│   │   │   │   │   ├── hooks/
│   │   │   │   │   │   ├── use-create-variable.tsx
│   │   │   │   │   │   ├── use-filter-manager.ts
│   │   │   │   │   │   ├── use-suggested-filters.ts
│   │   │   │   │   │   ├── use-variable-parser.ts
│   │   │   │   │   │   └── use-variable-validation.ts
│   │   │   │   │   ├── types.ts
│   │   │   │   │   ├── utils/
│   │   │   │   │   │   ├── digest-variables.tsx
│   │   │   │   │   │   └── get-variable-error-message.ts
│   │   │   │   │   ├── utils.ts
│   │   │   │   │   ├── variable-list.tsx
│   │   │   │   │   ├── variable-pill.tsx
│   │   │   │   │   └── variable-tooltip.tsx
│   │   │   │   ├── variables/
│   │   │   │   │   ├── delete-variable-dialog.tsx
│   │   │   │   │   ├── system-variable-definitions.ts
│   │   │   │   │   ├── system-variable-row.tsx
│   │   │   │   │   ├── upsert-variable-drawer.tsx
│   │   │   │   │   ├── upsert-variable-form.tsx
│   │   │   │   │   ├── variable-list-upgrade-cta.tsx
│   │   │   │   │   ├── variable-list.tsx
│   │   │   │   │   └── variable-row.tsx
│   │   │   │   ├── vercel-integration-form.tsx
│   │   │   │   ├── webhooks/
│   │   │   │   │   ├── webhooks-empty-state-svg.tsx
│   │   │   │   │   └── webhooks-paywall-state.tsx
│   │   │   │   ├── welcome/
│   │   │   │   │   ├── ai-prompts/
│   │   │   │   │   │   ├── framework-prompts/
│   │   │   │   │   │   │   ├── angular-prompt.ts
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   ├── javascript-prompt.ts
│   │   │   │   │   │   │   ├── nextjs-prompt.ts
│   │   │   │   │   │   │   ├── react-native-prompt.ts
│   │   │   │   │   │   │   ├── react-prompt.ts
│   │   │   │   │   │   │   ├── remix-prompt.ts
│   │   │   │   │   │   │   ├── types.ts
│   │   │   │   │   │   │   └── vue-prompt.ts
│   │   │   │   │   │   └── simple-prompt-getter.ts
│   │   │   │   │   ├── framework-guides.instructions.tsx
│   │   │   │   │   ├── framework-guides.tsx
│   │   │   │   │   ├── icons.tsx
│   │   │   │   │   ├── inbox-connected-guide.tsx
│   │   │   │   │   ├── inbox-embed.tsx
│   │   │   │   │   ├── inbox-framework-guide/
│   │   │   │   │   │   ├── framework-card.tsx
│   │   │   │   │   │   ├── framework-grid.tsx
│   │   │   │   │   │   ├── header-section.tsx
│   │   │   │   │   │   ├── helpers.ts
│   │   │   │   │   │   ├── instructions-panel.tsx
│   │   │   │   │   │   └── types.ts
│   │   │   │   │   ├── inbox-framework-guide.tsx
│   │   │   │   │   ├── progress-section.animations.ts
│   │   │   │   │   ├── progress-section.tsx
│   │   │   │   │   └── resources-list.tsx
│   │   │   │   ├── workflow-editor/
│   │   │   │   │   ├── add-step-menu.tsx
│   │   │   │   │   ├── animation-step-wrapper.tsx
│   │   │   │   │   ├── base-node.tsx
│   │   │   │   │   ├── channel-preferences-form.tsx
│   │   │   │   │   ├── channel-preferences.tsx
│   │   │   │   │   ├── condition-badge.tsx
│   │   │   │   │   ├── configure-workflow-form.tsx
│   │   │   │   │   ├── configure-workflow.tsx
│   │   │   │   │   ├── control-input/
│   │   │   │   │   │   ├── control-input.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── create-workflow-form.tsx
│   │   │   │   │   ├── drag-context.tsx
│   │   │   │   │   ├── edges.tsx
│   │   │   │   │   ├── editor-breadcrumbs.tsx
│   │   │   │   │   ├── in-app-preview.tsx
│   │   │   │   │   ├── node-utils.ts
│   │   │   │   │   ├── nodes.tsx
│   │   │   │   │   ├── payload-schema/
│   │   │   │   │   │   ├── components/
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   ├── payload-import-editor.tsx
│   │   │   │   │   │   │   └── payload-schema-empty-state.tsx
│   │   │   │   │   │   ├── hooks/
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   └── use-import-schema.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   └── utils/
│   │   │   │   │   │       ├── generate-schema.ts
│   │   │   │   │   │       └── index.ts
│   │   │   │   │   ├── payload-schema-drawer.tsx
│   │   │   │   │   ├── saving-status-indicator.tsx
│   │   │   │   │   ├── schema-change-confirmation-modal.tsx
│   │   │   │   │   ├── schema.ts
│   │   │   │   │   ├── severity-select-item.tsx
│   │   │   │   │   ├── step-utils.ts
│   │   │   │   │   ├── steps/
│   │   │   │   │   │   ├── base/
│   │   │   │   │   │   │   ├── base-body.tsx
│   │   │   │   │   │   │   ├── base-subject.tsx
│   │   │   │   │   │   │   └── data-object.tsx
│   │   │   │   │   │   ├── chat/
│   │   │   │   │   │   │   ├── chat-editor.tsx
│   │   │   │   │   │   │   ├── chat-preview.tsx
│   │   │   │   │   │   │   └── configure-chat-step-preview.tsx
│   │   │   │   │   │   ├── component-utils.tsx
│   │   │   │   │   │   ├── components/
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   ├── preview-payload-section.tsx
│   │   │   │   │   │   │   └── preview-step-results-section.tsx
│   │   │   │   │   │   ├── conditions/
│   │   │   │   │   │   │   ├── edit-step-conditions-form.tsx
│   │   │   │   │   │   │   ├── edit-step-conditions-layout.tsx
│   │   │   │   │   │   │   ├── edit-step-conditions-skeleton.tsx
│   │   │   │   │   │   │   └── edit-step-conditions.tsx
│   │   │   │   │   │   ├── configure-step-form.tsx
│   │   │   │   │   │   ├── configure-step-template-issue-cta.tsx
│   │   │   │   │   │   ├── configure-step.tsx
│   │   │   │   │   │   ├── constants/
│   │   │   │   │   │   │   └── preview-context.constants.ts
│   │   │   │   │   │   ├── context/
│   │   │   │   │   │   │   ├── preview-context-container.tsx
│   │   │   │   │   │   │   └── step-editor-context.tsx
│   │   │   │   │   │   ├── controls/
│   │   │   │   │   │   │   ├── array-field-item-template.tsx
│   │   │   │   │   │   │   ├── array-field-template.tsx
│   │   │   │   │   │   │   ├── array-field-title-template.tsx
│   │   │   │   │   │   │   ├── button-templates.tsx
│   │   │   │   │   │   │   ├── custom-step-controls.tsx
│   │   │   │   │   │   │   ├── json-form.tsx
│   │   │   │   │   │   │   ├── object-field-template.tsx
│   │   │   │   │   │   │   ├── select-widget.tsx
│   │   │   │   │   │   │   ├── switch-widget.tsx
│   │   │   │   │   │   │   ├── template-utils.tsx
│   │   │   │   │   │   │   └── text-widget.tsx
│   │   │   │   │   │   ├── delay/
│   │   │   │   │   │   │   ├── delay-control-values.tsx
│   │   │   │   │   │   │   ├── delay-window.tsx
│   │   │   │   │   │   │   ├── dynamic-delay.tsx
│   │   │   │   │   │   │   └── fixed-delay.tsx
│   │   │   │   │   │   ├── digest-delay-tabs/
│   │   │   │   │   │   │   ├── days-of-week.tsx
│   │   │   │   │   │   │   ├── digest-control-values.tsx
│   │   │   │   │   │   │   ├── digest-delay-tabs.tsx
│   │   │   │   │   │   │   ├── digest-key.tsx
│   │   │   │   │   │   │   ├── keys.ts
│   │   │   │   │   │   │   ├── lookback-window.tsx
│   │   │   │   │   │   │   ├── numbers-picker.tsx
│   │   │   │   │   │   │   ├── period.tsx
│   │   │   │   │   │   │   ├── regular-type.tsx
│   │   │   │   │   │   │   ├── scheduled-type.tsx
│   │   │   │   │   │   │   └── utils.ts
│   │   │   │   │   │   ├── editor/
│   │   │   │   │   │   │   └── step-editor-factory.tsx
│   │   │   │   │   │   ├── email/
│   │   │   │   │   │   │   ├── configure-email-step-preview.tsx
│   │   │   │   │   │   │   ├── email-body-html.tsx
│   │   │   │   │   │   │   ├── email-body-maily.tsx
│   │   │   │   │   │   │   ├── email-body.tsx
│   │   │   │   │   │   │   ├── email-editor.tsx
│   │   │   │   │   │   │   ├── email-preview.tsx
│   │   │   │   │   │   │   ├── email-subject.tsx
│   │   │   │   │   │   │   ├── email-tabs-section.tsx
│   │   │   │   │   │   │   ├── layout-select.tsx
│   │   │   │   │   │   │   ├── novu-branding.tsx
│   │   │   │   │   │   │   ├── sender-config-drawer.tsx
│   │   │   │   │   │   │   └── translations/
│   │   │   │   │   │   │       ├── edit-translation-popover/
│   │   │   │   │   │   │       │   ├── edit-translation-popover.tsx
│   │   │   │   │   │   │       │   ├── use-translation-editor.ts
│   │   │   │   │   │   │       │   ├── use-translation-form.ts
│   │   │   │   │   │   │       │   └── use-virtual-anchor.ts
│   │   │   │   │   │   │       ├── index.ts
│   │   │   │   │   │   │       ├── new-translation-key-preview.tsx
│   │   │   │   │   │   │       ├── translation-decorator.tsx
│   │   │   │   │   │   │       ├── translation-pill.tsx
│   │   │   │   │   │   │       ├── translation-suggestions-list-view.tsx
│   │   │   │   │   │   │       └── translation-tooltip.tsx
│   │   │   │   │   │   ├── hooks/
│   │   │   │   │   │   │   ├── use-persisted-preview-context.ts
│   │   │   │   │   │   │   └── use-preview-data-initialization.ts
│   │   │   │   │   │   ├── http-request/
│   │   │   │   │   │   │   ├── configure-http-request-step-preview.tsx
│   │   │   │   │   │   │   ├── continue-on-failure.tsx
│   │   │   │   │   │   │   ├── curl-display.tsx
│   │   │   │   │   │   │   ├── curl-utils.ts
│   │   │   │   │   │   │   ├── enforce-schema-validation.tsx
│   │   │   │   │   │   │   ├── http-request-console-preview.tsx
│   │   │   │   │   │   │   ├── http-request-editor.tsx
│   │   │   │   │   │   │   ├── http-request-test-context.ts
│   │   │   │   │   │   │   ├── http-request-test-provider.tsx
│   │   │   │   │   │   │   ├── key-value-pair-list.tsx
│   │   │   │   │   │   │   ├── novu-signature-header.tsx
│   │   │   │   │   │   │   ├── request-endpoint.tsx
│   │   │   │   │   │   │   ├── response-body-schema.tsx
│   │   │   │   │   │   │   ├── section-header.tsx
│   │   │   │   │   │   │   ├── use-copy-prompt.tsx
│   │   │   │   │   │   │   └── use-http-request-test.ts
│   │   │   │   │   │   ├── in-app/
│   │   │   │   │   │   │   ├── configure-in-app-step-preview.tsx
│   │   │   │   │   │   │   ├── in-app-action.tsx
│   │   │   │   │   │   │   ├── in-app-avatar.tsx
│   │   │   │   │   │   │   ├── in-app-body.tsx
│   │   │   │   │   │   │   ├── in-app-editor.tsx
│   │   │   │   │   │   │   ├── in-app-redirect.tsx
│   │   │   │   │   │   │   ├── in-app-subject.tsx
│   │   │   │   │   │   │   ├── in-app-tabs-section.tsx
│   │   │   │   │   │   │   └── inbox-preview.tsx
│   │   │   │   │   │   ├── layout/
│   │   │   │   │   │   │   ├── copilot-sidebar.tsx
│   │   │   │   │   │   │   ├── panel-header.tsx
│   │   │   │   │   │   │   └── resizable-layout.tsx
│   │   │   │   │   │   ├── preview/
│   │   │   │   │   │   │   ├── previews/
│   │   │   │   │   │   │   │   └── email-preview-wrapper.tsx
│   │   │   │   │   │   │   ├── step-preview-factory.tsx
│   │   │   │   │   │   │   └── step-resolver-preview-error.tsx
│   │   │   │   │   │   ├── preview-context-panel.tsx
│   │   │   │   │   │   ├── push/
│   │   │   │   │   │   │   ├── configure-push-step-preview.tsx
│   │   │   │   │   │   │   ├── push-editor.tsx
│   │   │   │   │   │   │   └── push-preview.tsx
│   │   │   │   │   │   ├── save-form-context.ts
│   │   │   │   │   │   ├── sdk-banner.tsx
│   │   │   │   │   │   ├── shared/
│   │   │   │   │   │   │   ├── bypass-sanitization-switch.tsx
│   │   │   │   │   │   │   ├── editable-json-viewer/
│   │   │   │   │   │   │   │   ├── constants.ts
│   │   │   │   │   │   │   │   ├── custom-text-editor.tsx
│   │   │   │   │   │   │   │   ├── editable-json-viewer.tsx
│   │   │   │   │   │   │   │   ├── icons.tsx
│   │   │   │   │   │   │   │   ├── single-click-editable-value.tsx
│   │   │   │   │   │   │   │   ├── types.ts
│   │   │   │   │   │   │   │   └── use-hide-root-node.ts
│   │   │   │   │   │   │   ├── extend-to-schedule.tsx
│   │   │   │   │   │   │   ├── step-editor-mode-toggle.tsx
│   │   │   │   │   │   │   ├── step-resolver-active-panel.tsx
│   │   │   │   │   │   │   ├── step-resolver-empty-preview.tsx
│   │   │   │   │   │   │   ├── step-resolver-not-published.tsx
│   │   │   │   │   │   │   └── use-step-resolver-hint.tsx
│   │   │   │   │   │   ├── skip-conditions-button.tsx
│   │   │   │   │   │   ├── sms/
│   │   │   │   │   │   │   ├── configure-sms-step-preview.tsx
│   │   │   │   │   │   │   ├── sms-editor.tsx
│   │   │   │   │   │   │   ├── sms-phone.tsx
│   │   │   │   │   │   │   └── sms-preview.tsx
│   │   │   │   │   │   ├── step-drawer.tsx
│   │   │   │   │   │   ├── step-editor-layout.tsx
│   │   │   │   │   │   ├── step-editor-unavailable.tsx
│   │   │   │   │   │   ├── tabs-section.tsx
│   │   │   │   │   │   ├── throttle/
│   │   │   │   │   │   │   ├── dynamic-throttle.tsx
│   │   │   │   │   │   │   ├── fixed-throttle.tsx
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   ├── throttle-control-values.tsx
│   │   │   │   │   │   │   ├── throttle-editor.tsx
│   │   │   │   │   │   │   ├── throttle-key.tsx
│   │   │   │   │   │   │   ├── throttle-threshold.tsx
│   │   │   │   │   │   │   └── throttle-window.tsx
│   │   │   │   │   │   ├── time-units.ts
│   │   │   │   │   │   ├── types/
│   │   │   │   │   │   │   └── preview-context.types.ts
│   │   │   │   │   │   ├── use-editor-preview.tsx
│   │   │   │   │   │   └── utils/
│   │   │   │   │   │       ├── digest-sync.utils.ts
│   │   │   │   │   │       ├── preview-context-storage.utils.ts
│   │   │   │   │   │       ├── preview-context.utils.ts
│   │   │   │   │   │       └── step-utils.tsx
│   │   │   │   │   ├── test-workflow/
│   │   │   │   │   │   ├── snippet-editor.tsx
│   │   │   │   │   │   ├── test-workflow-activity-drawer.tsx
│   │   │   │   │   │   ├── test-workflow-content.tsx
│   │   │   │   │   │   ├── test-workflow-drawer.tsx
│   │   │   │   │   │   ├── test-workflow-form.tsx
│   │   │   │   │   │   ├── test-workflow-instructions.tsx
│   │   │   │   │   │   ├── test-workflow-logs-sidebar.tsx
│   │   │   │   │   │   ├── test-workflow-tabs.tsx
│   │   │   │   │   │   └── types.ts
│   │   │   │   │   ├── toasts.tsx
│   │   │   │   │   ├── translation-status.tsx
│   │   │   │   │   ├── translation-toggle-section.tsx
│   │   │   │   │   ├── url-input.tsx
│   │   │   │   │   ├── use-animated-nodes.ts
│   │   │   │   │   ├── use-canvas-nodes-edges.ts
│   │   │   │   │   ├── use-workflow-schema-manager.ts
│   │   │   │   │   ├── workflow-activity.tsx
│   │   │   │   │   ├── workflow-canvas.tsx
│   │   │   │   │   ├── workflow-checklist.tsx
│   │   │   │   │   ├── workflow-node-action-bar.tsx
│   │   │   │   │   ├── workflow-provider.tsx
│   │   │   │   │   ├── workflow-schema-provider.tsx
│   │   │   │   │   └── workflow-tabs.tsx
│   │   │   │   ├── workflow-issues-popover.tsx
│   │   │   │   ├── workflow-list-empty.tsx
│   │   │   │   ├── workflow-list.tsx
│   │   │   │   ├── workflow-row.tsx
│   │   │   │   ├── workflow-status.tsx
│   │   │   │   ├── workflow-step.tsx
│   │   │   │   ├── workflow-steps.tsx
│   │   │   │   └── workflow-tags.tsx
│   │   │   ├── config/
│   │   │   │   └── index.ts
│   │   │   ├── context/
│   │   │   │   ├── auth/
│   │   │   │   │   ├── auth-context.tsx
│   │   │   │   │   ├── auth-provider.tsx
│   │   │   │   │   ├── hooks.ts
│   │   │   │   │   ├── mappers.ts
│   │   │   │   │   └── types.ts
│   │   │   │   ├── customer-io/
│   │   │   │   │   ├── customer-io-provider.tsx
│   │   │   │   │   ├── hooks.ts
│   │   │   │   │   └── index.ts
│   │   │   │   ├── ee-auth-provider.tsx
│   │   │   │   ├── environment/
│   │   │   │   │   ├── environment-context.tsx
│   │   │   │   │   ├── environment-provider.tsx
│   │   │   │   │   └── hooks.ts
│   │   │   │   ├── escape-key-manager/
│   │   │   │   │   ├── escape-key-context.tsx
│   │   │   │   │   ├── escape-key-manager.tsx
│   │   │   │   │   ├── hooks.ts
│   │   │   │   │   └── priority.ts
│   │   │   │   ├── feature-flags-provider.tsx
│   │   │   │   ├── identity-provider.tsx
│   │   │   │   ├── opt-in-provider.tsx
│   │   │   │   ├── region/
│   │   │   │   │   ├── index.self-hosted.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── region-config.ts
│   │   │   │   │   ├── region-context.self-hosted.tsx
│   │   │   │   │   ├── region-context.tsx
│   │   │   │   │   ├── region-modals.tsx
│   │   │   │   │   ├── region-selector.tsx
│   │   │   │   │   ├── region-types.ts
│   │   │   │   │   └── region-utils.ts
│   │   │   │   └── segment/
│   │   │   │       ├── hooks.ts
│   │   │   │       ├── index.ts
│   │   │   │       └── segment-provider.tsx
│   │   │   ├── hooks/
│   │   │   │   ├── use-activity-url-state.ts
│   │   │   │   ├── use-ai-chat-stream.ts
│   │   │   │   ├── use-auto-configure-integration.ts
│   │   │   │   ├── use-before-unload.ts
│   │   │   │   ├── use-billing-portal.ts
│   │   │   │   ├── use-checkout-session.ts
│   │   │   │   ├── use-combined-refs.ts
│   │   │   │   ├── use-conditions-count.ts
│   │   │   │   ├── use-create-ai-chat.ts
│   │   │   │   ├── use-create-context.ts
│   │   │   │   ├── use-create-environment-variable.ts
│   │   │   │   ├── use-create-integration.ts
│   │   │   │   ├── use-create-layout.ts
│   │   │   │   ├── use-create-subscriber.ts
│   │   │   │   ├── use-create-topic.ts
│   │   │   │   ├── use-create-translation-key.ts
│   │   │   │   ├── use-create-vercel-integration.ts
│   │   │   │   ├── use-create-workflow.ts
│   │   │   │   ├── use-data-ref.ts
│   │   │   │   ├── use-debounce.ts
│   │   │   │   ├── use-debounced-form.ts
│   │   │   │   ├── use-debounced-value.ts
│   │   │   │   ├── use-default-subscriber-data.ts
│   │   │   │   ├── use-delayed-loading.ts
│   │   │   │   ├── use-delete-context.ts
│   │   │   │   ├── use-delete-environment-variable.ts
│   │   │   │   ├── use-delete-integration.ts
│   │   │   │   ├── use-delete-layout.ts
│   │   │   │   ├── use-delete-subscriber.ts
│   │   │   │   ├── use-delete-translation-group.ts
│   │   │   │   ├── use-delete-workflow.ts
│   │   │   │   ├── use-disconnect-step-resolver.ts
│   │   │   │   ├── use-duplicate-layout.ts
│   │   │   │   ├── use-duplicate-workflow.ts
│   │   │   │   ├── use-dynamic-preview-schema.ts
│   │   │   │   ├── use-editor-translation-overlay.ts
│   │   │   │   ├── use-enhanced-variable-validation.ts
│   │   │   │   ├── use-environments.ts
│   │   │   │   ├── use-export-master-json.ts
│   │   │   │   ├── use-feature-flag.tsx
│   │   │   │   ├── use-fetch-activities.ts
│   │   │   │   ├── use-fetch-activity.ts
│   │   │   │   ├── use-fetch-api-keys.ts
│   │   │   │   ├── use-fetch-bridge-health-check.ts
│   │   │   │   ├── use-fetch-charts.ts
│   │   │   │   ├── use-fetch-context.ts
│   │   │   │   ├── use-fetch-contexts.ts
│   │   │   │   ├── use-fetch-environment-variable-usage.ts
│   │   │   │   ├── use-fetch-environment-variables.ts
│   │   │   │   ├── use-fetch-integrations.ts
│   │   │   │   ├── use-fetch-latest-ai-chat.ts
│   │   │   │   ├── use-fetch-layout-usage.ts
│   │   │   │   ├── use-fetch-layout.ts
│   │   │   │   ├── use-fetch-layouts.tsx
│   │   │   │   ├── use-fetch-organization-settings.ts
│   │   │   │   ├── use-fetch-request-logs.ts
│   │   │   │   ├── use-fetch-request-traces.ts
│   │   │   │   ├── use-fetch-subscriber-preferences.ts
│   │   │   │   ├── use-fetch-subscriber-subscriptions.ts
│   │   │   │   ├── use-fetch-subscriber.ts
│   │   │   │   ├── use-fetch-subscribers.ts
│   │   │   │   ├── use-fetch-subscription.ts
│   │   │   │   ├── use-fetch-topics.ts
│   │   │   │   ├── use-fetch-translation-group.ts
│   │   │   │   ├── use-fetch-translation-keys.ts
│   │   │   │   ├── use-fetch-translation-list.ts
│   │   │   │   ├── use-fetch-translation.ts
│   │   │   │   ├── use-fetch-vercel-integration-projects.tsx
│   │   │   │   ├── use-fetch-vercel-integration.ts
│   │   │   │   ├── use-fetch-workflow-runs-count.ts
│   │   │   │   ├── use-fetch-workflow-test-data.ts
│   │   │   │   ├── use-fetch-workflow.ts
│   │   │   │   ├── use-fetch-workflows.ts
│   │   │   │   ├── use-find-dirty-form.ts
│   │   │   │   ├── use-first-trigger-detection.ts
│   │   │   │   ├── use-form-autosave.ts
│   │   │   │   ├── use-form-protection.tsx
│   │   │   │   ├── use-has-permission.tsx
│   │   │   │   ├── use-init-demo-workflow.ts
│   │   │   │   ├── use-invocation-queue.ts
│   │   │   │   ├── use-is-mobile.ts
│   │   │   │   ├── use-is-payload-schema-enabled.ts
│   │   │   │   ├── use-is-translation-enabled.ts
│   │   │   │   ├── use-keep-ai-changes.ts
│   │   │   │   ├── use-layout-preview.ts
│   │   │   │   ├── use-logs-url-state.ts
│   │   │   │   ├── use-metric-data.ts
│   │   │   │   ├── use-mutation-observer.ts
│   │   │   │   ├── use-new-dashboard-opt-in.ts
│   │   │   │   ├── use-on-element-unmount.ts
│   │   │   │   ├── use-onboarding-steps.ts
│   │   │   │   ├── use-optimistic-channel-preferences.ts
│   │   │   │   ├── use-optimistic-schedule-update.ts
│   │   │   │   ├── use-page-visit-timestamp.ts
│   │   │   │   ├── use-parse-variables.ts
│   │   │   │   ├── use-patch-subscriber.ts
│   │   │   │   ├── use-patch-workflow.ts
│   │   │   │   ├── use-persisted-page-size.ts
│   │   │   │   ├── use-plain-chat.ts
│   │   │   │   ├── use-preview-context.ts
│   │   │   │   ├── use-preview-step.ts
│   │   │   │   ├── use-primary-email-integration.ts
│   │   │   │   ├── use-pull-activity.ts
│   │   │   │   ├── use-remove-grammarly.ts
│   │   │   │   ├── use-resource-dependencies.ts
│   │   │   │   ├── use-revert-message.ts
│   │   │   │   ├── use-save-translation.ts
│   │   │   │   ├── use-set-primary-integration.ts
│   │   │   │   ├── use-step-resolver-polling.ts
│   │   │   │   ├── use-step-resolvers-count.ts
│   │   │   │   ├── use-sync-workflow.tsx
│   │   │   │   ├── use-tab-observer.ts
│   │   │   │   ├── use-tags.ts
│   │   │   │   ├── use-telemetry.ts
│   │   │   │   ├── use-template-store.ts
│   │   │   │   ├── use-test-http-endpoint.ts
│   │   │   │   ├── use-translation-completion-source.ts
│   │   │   │   ├── use-translation-plugin-extension.ts
│   │   │   │   ├── use-translation-validation.ts
│   │   │   │   ├── use-translations.ts
│   │   │   │   ├── use-trigger-workflow.ts
│   │   │   │   ├── use-update-bridge-url.ts
│   │   │   │   ├── use-update-context.ts
│   │   │   │   ├── use-update-environment-variable.ts
│   │   │   │   ├── use-update-integration.ts
│   │   │   │   ├── use-update-layout.ts
│   │   │   │   ├── use-update-organization-settings.ts
│   │   │   │   ├── use-update-translation-value.ts
│   │   │   │   ├── use-update-vercel-integration.ts
│   │   │   │   ├── use-update-workflow.ts
│   │   │   │   ├── use-upload-master-json.ts
│   │   │   │   ├── use-upload-translations.ts
│   │   │   │   ├── use-validate-bridge-url.ts
│   │   │   │   ├── use-variables.ts
│   │   │   │   ├── use-vercel-params.ts
│   │   │   │   └── use-workflow-editor-page.ts
│   │   │   ├── index.css
│   │   │   ├── main.tsx
│   │   │   ├── pages/
│   │   │   │   ├── access-denied-page.tsx
│   │   │   │   ├── activity-feed.tsx
│   │   │   │   ├── analytics.tsx
│   │   │   │   ├── api-keys.tsx
│   │   │   │   ├── contexts.tsx
│   │   │   │   ├── create-context.tsx
│   │   │   │   ├── create-layout.tsx
│   │   │   │   ├── create-subscriber.tsx
│   │   │   │   ├── create-topic.tsx
│   │   │   │   ├── create-workflow.tsx
│   │   │   │   ├── duplicate-layout-page.tsx
│   │   │   │   ├── duplicate-workflow.tsx
│   │   │   │   ├── edit-context.tsx
│   │   │   │   ├── edit-layout.tsx
│   │   │   │   ├── edit-step-template-v2.tsx
│   │   │   │   ├── edit-subscriber-page.tsx
│   │   │   │   ├── edit-topic.tsx
│   │   │   │   ├── edit-translation.tsx
│   │   │   │   ├── edit-workflow.tsx
│   │   │   │   ├── environments.tsx
│   │   │   │   ├── error-page.tsx
│   │   │   │   ├── forgot-password.tsx
│   │   │   │   ├── inbox-embed-page.tsx
│   │   │   │   ├── inbox-embed-success-page.tsx
│   │   │   │   ├── inbox-usecase-page.tsx
│   │   │   │   ├── index.ts
│   │   │   │   ├── integrations-list-page.tsx
│   │   │   │   ├── invitation-accept.tsx
│   │   │   │   ├── landing-1-signup.tsx
│   │   │   │   ├── layouts.tsx
│   │   │   │   ├── new-layout-drawer.tsx
│   │   │   │   ├── new-workflow-drawer.tsx
│   │   │   │   ├── organization-list.tsx
│   │   │   │   ├── questionnaire-page.tsx
│   │   │   │   ├── redirect-to-legacy-studio-auth.tsx
│   │   │   │   ├── reset-password.tsx
│   │   │   │   ├── server-error-page.tsx
│   │   │   │   ├── settings.tsx
│   │   │   │   ├── sign-in.tsx
│   │   │   │   ├── sign-up.tsx
│   │   │   │   ├── sso-sign-in.tsx
│   │   │   │   ├── subscribers.tsx
│   │   │   │   ├── test-workflow-drawer-page.tsx
│   │   │   │   ├── test-workflow-route-handler.tsx
│   │   │   │   ├── test-workflow.tsx
│   │   │   │   ├── topics.tsx
│   │   │   │   ├── translation-settings-page.tsx
│   │   │   │   ├── translations.tsx
│   │   │   │   ├── upsert-variable.tsx
│   │   │   │   ├── usecase-select-page.tsx
│   │   │   │   ├── variables.tsx
│   │   │   │   ├── vercel-integration-page.tsx
│   │   │   │   ├── verify-email.tsx
│   │   │   │   ├── webhooks-page.tsx
│   │   │   │   ├── welcome-page.tsx
│   │   │   │   └── workflows.tsx
│   │   │   ├── routes/
│   │   │   │   ├── auth.tsx
│   │   │   │   ├── catch-all.tsx
│   │   │   │   ├── dashboard.tsx
│   │   │   │   ├── index.ts
│   │   │   │   ├── onboarding.tsx
│   │   │   │   ├── permission-protected-route.tsx
│   │   │   │   ├── protected-route.tsx
│   │   │   │   └── root.tsx
│   │   │   ├── types/
│   │   │   │   ├── activity.ts
│   │   │   │   ├── global.ts
│   │   │   │   ├── logs.ts
│   │   │   │   └── translations.ts
│   │   │   ├── utils/
│   │   │   │   ├── activityFilters.ts
│   │   │   │   ├── analytics-mock-data.ts
│   │   │   │   ├── animation.ts
│   │   │   │   ├── api-hostname-manager.ts
│   │   │   │   ├── api-response-normalizer.ts
│   │   │   │   ├── arrays.ts
│   │   │   │   ├── auth.ts
│   │   │   │   ├── avatars.ts
│   │   │   │   ├── better-auth/
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── components/
│   │   │   │   │   │   ├── forgot-password.tsx
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── invitation-accept.tsx
│   │   │   │   │   │   ├── organization-create.tsx
│   │   │   │   │   │   ├── organization-dropdown.tsx
│   │   │   │   │   │   ├── organization-list.tsx
│   │   │   │   │   │   ├── organization-settings.tsx
│   │   │   │   │   │   ├── organization-switcher.tsx
│   │   │   │   │   │   ├── reset-password.tsx
│   │   │   │   │   │   ├── sign-in.tsx
│   │   │   │   │   │   ├── sign-up.tsx
│   │   │   │   │   │   ├── sso-sign-in.tsx
│   │   │   │   │   │   ├── team-members.tsx
│   │   │   │   │   │   ├── user-button.tsx
│   │   │   │   │   │   ├── user-profile.tsx
│   │   │   │   │   │   └── verify-email.tsx
│   │   │   │   │   ├── index.tsx
│   │   │   │   │   └── role-permissions.ts
│   │   │   │   ├── channels.ts
│   │   │   │   ├── clerk-appearance.ts
│   │   │   │   ├── code-snippets.ts
│   │   │   │   ├── color.ts
│   │   │   │   ├── conditions.ts
│   │   │   │   ├── constants.ts
│   │   │   │   ├── context-variable-utils.ts
│   │   │   │   ├── context.ts
│   │   │   │   ├── customer-io.ts
│   │   │   │   ├── default-values.ts
│   │   │   │   ├── enums.ts
│   │   │   │   ├── format-count.ts
│   │   │   │   ├── format-date.ts
│   │   │   │   ├── formatter.ts
│   │   │   │   ├── id-utils.ts
│   │   │   │   ├── inbox.ts
│   │   │   │   ├── json.ts
│   │   │   │   ├── liquid-autocomplete.tsx
│   │   │   │   ├── liquid-scope-analyzer.ts
│   │   │   │   ├── liquid.ts
│   │   │   │   ├── local-storage.ts
│   │   │   │   ├── logs-filters.utils.ts
│   │   │   │   ├── number-formatting.ts
│   │   │   │   ├── parse-page-param.ts
│   │   │   │   ├── parseStepVariables.ts
│   │   │   │   ├── polymorphic.ts
│   │   │   │   ├── protect.tsx
│   │   │   │   ├── query-keys.ts
│   │   │   │   ├── recursive-clone-children.tsx
│   │   │   │   ├── routes.ts
│   │   │   │   ├── schema.ts
│   │   │   │   ├── segment.ts
│   │   │   │   ├── self-hosted/
│   │   │   │   │   ├── api-interceptor.tsx
│   │   │   │   │   ├── auth.resource.tsx
│   │   │   │   │   ├── components.tsx
│   │   │   │   │   ├── icons.tsx
│   │   │   │   │   ├── index.tsx
│   │   │   │   │   ├── jwt-manager.tsx
│   │   │   │   │   ├── organization-switcher.tsx
│   │   │   │   │   ├── organization.resource.tsx
│   │   │   │   │   ├── user-button.tsx
│   │   │   │   │   ├── user.resource.tsx
│   │   │   │   │   └── user.types.ts
│   │   │   │   ├── sentry.ts
│   │   │   │   ├── string.ts
│   │   │   │   ├── telemetry.ts
│   │   │   │   ├── titleize.ts
│   │   │   │   ├── tracking.ts
│   │   │   │   ├── tv.ts
│   │   │   │   ├── types.ts
│   │   │   │   ├── ui.ts
│   │   │   │   ├── url.ts
│   │   │   │   ├── uuid/
│   │   │   │   │   └── index.ts
│   │   │   │   ├── validation.ts
│   │   │   │   └── workflow-trigger-ai-prompt.ts
│   │   │   └── vite-env.d.ts
│   │   ├── tailwind.config.ts
│   │   ├── tests/
│   │   │   ├── manage-workflows.e2e.ts
│   │   │   ├── package.json
│   │   │   ├── page-object-models/
│   │   │   │   ├── create-workflow-sidebar.ts
│   │   │   │   ├── in-app-step-editor.ts
│   │   │   │   ├── step-config-sidebar.ts
│   │   │   │   ├── trigger-workflow-page.ts
│   │   │   │   ├── workflow-editor-page.ts
│   │   │   │   └── workflows-page.ts
│   │   │   ├── sync-workflow.e2e.ts
│   │   │   ├── tsconfig.json
│   │   │   └── utils/
│   │   │       ├── api.ts
│   │   │       ├── environment-service.ts
│   │   │       ├── fixtures.ts
│   │   │       ├── integration-service.ts
│   │   │       ├── organization-service.ts
│   │   │       ├── session.ts
│   │   │       ├── test-bridge-server.ts
│   │   │       └── user-service.ts
│   │   ├── tests-examples/
│   │   │   └── demo-todo-app.spec.ts
│   │   ├── tsconfig.app.json
│   │   ├── tsconfig.json
│   │   ├── tsconfig.node.json
│   │   └── vite.config.ts
│   ├── inbound-mail/
│   │   ├── .example.env
│   │   ├── .gitignore
│   │   ├── Dockerfile
│   │   ├── e2e/
│   │   │   └── setup.ts
│   │   ├── nodemon.json
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── config/
│   │   │   │   ├── env.config.ts
│   │   │   │   ├── env.validators.ts
│   │   │   │   └── index.ts
│   │   │   ├── instrument.ts
│   │   │   ├── main.ts
│   │   │   ├── python/
│   │   │   │   ├── DNS/
│   │   │   │   │   ├── Base.py
│   │   │   │   │   ├── Class.py
│   │   │   │   │   ├── Lib.py
│   │   │   │   │   ├── Opcode.py
│   │   │   │   │   ├── Status.py
│   │   │   │   │   ├── Type.py
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── lazy.py
│   │   │   │   │   └── win32dns.py
│   │   │   │   ├── dkim/
│   │   │   │   │   ├── .__init__.py.swo
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── __main__.py
│   │   │   │   │   ├── asn1.py
│   │   │   │   │   ├── canonicalization.py
│   │   │   │   │   ├── crypto.py
│   │   │   │   │   ├── dnsplug.py
│   │   │   │   │   └── util.py
│   │   │   │   ├── ipaddr.py
│   │   │   │   ├── spf.py
│   │   │   │   ├── verifydkim.py
│   │   │   │   └── verifyspf.py
│   │   │   ├── server/
│   │   │   │   ├── inbound-mail.service.spec.ts
│   │   │   │   ├── inbound-mail.service.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── logger.ts
│   │   │   │   └── mailUtilities.ts
│   │   │   └── types/
│   │   │       └── env.d.ts
│   │   └── tsconfig.json
│   ├── webhook/
│   │   ├── .dockerignore
│   │   ├── .gitignore
│   │   ├── Dockerfile
│   │   ├── e2e/
│   │   │   ├── mocha.e2e.opts
│   │   │   └── setup.ts
│   │   ├── nest-cli.json
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── .example.env
│   │   │   ├── app.controller.ts
│   │   │   ├── app.module.ts
│   │   │   ├── app.service.ts
│   │   │   ├── bootstrap.ts
│   │   │   ├── config/
│   │   │   │   ├── env.config.ts
│   │   │   │   └── env.validators.ts
│   │   │   ├── health/
│   │   │   │   ├── health.controller.ts
│   │   │   │   └── health.module.ts
│   │   │   ├── instrument.ts
│   │   │   ├── main.ts
│   │   │   ├── shared/
│   │   │   │   ├── constants.ts
│   │   │   │   ├── framework/
│   │   │   │   │   ├── response.interceptor.ts
│   │   │   │   │   └── user.decorator.ts
│   │   │   │   ├── helpers/
│   │   │   │   │   └── regex.service.ts
│   │   │   │   └── shared.module.ts
│   │   │   ├── types/
│   │   │   │   └── env.d.ts
│   │   │   └── webhooks/
│   │   │       ├── dtos/
│   │   │       │   └── webhooks-response.dto.ts
│   │   │       ├── e2e/
│   │   │       │   └── email-webhook.e2e.ts
│   │   │       ├── interfaces/
│   │   │       │   └── webhook.interface.ts
│   │   │       ├── usecases/
│   │   │       │   ├── execution-details/
│   │   │       │   │   ├── create-execution-details.command.ts
│   │   │       │   │   └── create-execution-details.usecase.ts
│   │   │       │   ├── index.ts
│   │   │       │   └── webhook/
│   │   │       │       ├── webhook.command.ts
│   │   │       │       └── webhook.usecase.ts
│   │   │       ├── webhooks.controller.ts
│   │   │       └── webhooks.module.ts
│   │   ├── tsconfig.build.json
│   │   └── tsconfig.json
│   ├── worker/
│   │   ├── .gitignore
│   │   ├── .mocharc.json
│   │   ├── .vscode/
│   │   │   └── settings.json
│   │   ├── Dockerfile
│   │   ├── README.md
│   │   ├── e2e/
│   │   │   └── setup.ts
│   │   ├── nest-cli.json
│   │   ├── package.json
│   │   ├── project.json
│   │   ├── src/
│   │   │   ├── .example.env
│   │   │   ├── app/
│   │   │   │   ├── health/
│   │   │   │   │   ├── e2e/
│   │   │   │   │   │   └── health-check.e2e.ts
│   │   │   │   │   ├── health.controller.ts
│   │   │   │   │   └── health.module.ts
│   │   │   │   ├── shared/
│   │   │   │   │   ├── response.interceptor.ts
│   │   │   │   │   ├── shared.module.ts
│   │   │   │   │   └── utils/
│   │   │   │   │       ├── constants.ts
│   │   │   │   │       ├── exceptions.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       └── should-halt-on-step-failure.ts
│   │   │   │   ├── telemetry/
│   │   │   │   │   ├── telemetry.module.ts
│   │   │   │   │   ├── usecases/
│   │   │   │   │   │   ├── machineInfoService.usecase.ts
│   │   │   │   │   │   └── userInfoService.usecase.ts
│   │   │   │   │   └── utils/
│   │   │   │   │       ├── machine.utils.ts
│   │   │   │   │       └── sendDataToNovuTrace.utils.ts
│   │   │   │   └── workflow/
│   │   │   │       ├── services/
│   │   │   │       │   ├── active-jobs-metric.service.ts
│   │   │   │       │   ├── cold-start.service.ts
│   │   │   │       │   ├── index.ts
│   │   │   │       │   ├── standard.worker.spec.ts
│   │   │   │       │   ├── standard.worker.ts
│   │   │   │       │   ├── subscriber-process.worker.ts
│   │   │   │       │   ├── workflow.worker.spec.ts
│   │   │   │       │   └── workflow.worker.ts
│   │   │   │       ├── specs/
│   │   │   │       │   ├── conditions-filter.usecase.spec.ts
│   │   │   │       │   └── inbound-email-parse.spec.ts
│   │   │   │       ├── usecases/
│   │   │   │       │   ├── add-job/
│   │   │   │       │   │   ├── add-job.command.ts
│   │   │   │       │   │   ├── add-job.usecase.ts
│   │   │   │       │   │   ├── index.ts
│   │   │   │       │   │   ├── merge-or-create-digest.command.ts
│   │   │   │       │   │   ├── merge-or-create-digest.usecase.ts
│   │   │   │       │   │   └── validation.ts
│   │   │   │       │   ├── execute-bridge-job/
│   │   │   │       │   │   ├── execute-bridge-job.command.ts
│   │   │   │       │   │   ├── execute-bridge-job.usecase.ts
│   │   │   │       │   │   └── index.ts
│   │   │   │       │   ├── handle-last-failed-job/
│   │   │   │       │   │   ├── handle-last-failed-job.command.ts
│   │   │   │       │   │   ├── handle-last-failed-job.usecase.ts
│   │   │   │       │   │   └── index.ts
│   │   │   │       │   ├── inbound-email-parse/
│   │   │   │       │   │   ├── inbound-email-parse.command.ts
│   │   │   │       │   │   └── inbound-email-parse.usecase.ts
│   │   │   │       │   ├── index.ts
│   │   │   │       │   ├── noop-send-webhook-message.usecase.ts
│   │   │   │       │   ├── process-unsnooze-job/
│   │   │   │       │   │   ├── index.ts
│   │   │   │       │   │   ├── process-unsnooze-job.command.ts
│   │   │   │       │   │   └── process-unsnooze-job.usecase.ts
│   │   │   │       │   ├── queue-next-job/
│   │   │   │       │   │   ├── index.ts
│   │   │   │       │   │   ├── queue-next-job.command.ts
│   │   │   │       │   │   └── queue-next-job.usecase.ts
│   │   │   │       │   ├── run-job/
│   │   │   │       │   │   ├── index.ts
│   │   │   │       │   │   ├── run-job.command.ts
│   │   │   │       │   │   ├── run-job.usecase.ts
│   │   │   │       │   │   ├── schedule-validator.spec.ts
│   │   │   │       │   │   └── schedule-validator.ts
│   │   │   │       │   ├── send-message/
│   │   │   │       │   │   ├── channel-endpoint-resolution/
│   │   │   │       │   │   │   ├── resolve-channel-endpoints.command.ts
│   │   │   │       │   │   │   └── resolve-channel-endpoints.usecase.ts
│   │   │   │       │   │   ├── digest/
│   │   │   │       │   │   │   ├── digest-events.command.ts
│   │   │   │       │   │   │   ├── digest.usecase.ts
│   │   │   │       │   │   │   ├── get-digest-events-backoff.usecase.ts
│   │   │   │       │   │   │   ├── get-digest-events-regular.usecase.ts
│   │   │   │       │   │   │   ├── get-digest-events.usecase.ts
│   │   │   │       │   │   │   └── index.ts
│   │   │   │       │   │   ├── execute-code-first-custom-step.usecase.ts
│   │   │   │       │   │   ├── execute-http-request-step.usecase.ts
│   │   │   │       │   │   ├── index.ts
│   │   │   │       │   │   ├── send-message-channel.command.ts
│   │   │   │       │   │   ├── send-message-chat.usecase.ts
│   │   │   │       │   │   ├── send-message-delay.usecase.ts
│   │   │   │       │   │   ├── send-message-email.usecase.ts
│   │   │   │       │   │   ├── send-message-in-app.usecase.ts
│   │   │   │       │   │   ├── send-message-push.usecase.spec.ts
│   │   │   │       │   │   ├── send-message-push.usecase.ts
│   │   │   │       │   │   ├── send-message-sms.usecase.ts
│   │   │   │       │   │   ├── send-message-type.usecase.ts
│   │   │   │       │   │   ├── send-message.base.ts
│   │   │   │       │   │   ├── send-message.command.ts
│   │   │   │       │   │   ├── send-message.usecase.ts
│   │   │   │       │   │   └── throttle/
│   │   │   │       │   │       ├── index.ts
│   │   │   │       │   │       └── throttle.usecase.ts
│   │   │   │       │   ├── store-subscriber-jobs/
│   │   │   │       │   │   ├── index.ts
│   │   │   │       │   │   ├── store-subscriber-jobs.command.ts
│   │   │   │       │   │   └── store-subscriber-jobs.usecase.ts
│   │   │   │       │   ├── subscriber-job-bound/
│   │   │   │       │   │   ├── subscriber-job-bound.command.ts
│   │   │   │       │   │   └── subscriber-job-bound.usecase.ts
│   │   │   │       │   ├── update-job-status/
│   │   │   │       │   │   ├── index.ts
│   │   │   │       │   │   ├── set-job-as-completed.usecase.ts
│   │   │   │       │   │   ├── set-job-as-failed.usecase.ts
│   │   │   │       │   │   ├── set-job-as.command.ts
│   │   │   │       │   │   ├── update-job-status.command.ts
│   │   │   │       │   │   └── update-job-status.usecase.ts
│   │   │   │       │   └── webhook-filter-backoff-strategy/
│   │   │   │       │       ├── event-job.dto.ts
│   │   │   │       │       ├── index.ts
│   │   │   │       │       ├── webhook-filter-backoff-strategy.command.ts
│   │   │   │       │       └── webhook-filter-backoff-strategy.usecase.ts
│   │   │   │       ├── workers/
│   │   │   │       │   └── inbound-parse.worker.service.ts
│   │   │   │       └── workflow.module.ts
│   │   │   ├── app.module.ts
│   │   │   ├── bootstrap.ts
│   │   │   ├── config/
│   │   │   │   ├── env.config.ts
│   │   │   │   ├── env.validators.ts
│   │   │   │   ├── index.ts
│   │   │   │   └── worker-init.config.ts
│   │   │   ├── instrument.ts
│   │   │   ├── main.ts
│   │   │   ├── newrelic.ts
│   │   │   └── types/
│   │   │       └── env.d.ts
│   │   ├── tsconfig.build.json
│   │   ├── tsconfig.json
│   │   ├── tsconfig.spec.json
│   │   └── webpack.config.js
│   └── ws/
│       ├── .gitignore
│       ├── Dockerfile
│       ├── e2e/
│       │   └── setup.ts
│       ├── nest-cli.json
│       ├── package.json
│       ├── src/
│       │   ├── .example.env
│       │   ├── app.controller.ts
│       │   ├── app.module.ts
│       │   ├── app.service.ts
│       │   ├── bootstrap.ts
│       │   ├── config/
│       │   │   ├── env.config.ts
│       │   │   ├── env.validators.ts
│       │   │   └── index.ts
│       │   ├── health/
│       │   │   ├── health.controller.ts
│       │   │   └── health.module.ts
│       │   ├── instrument.ts
│       │   ├── main.ts
│       │   ├── shared/
│       │   │   ├── framework/
│       │   │   │   └── in-memory-io.adapter.ts
│       │   │   ├── shared.module.ts
│       │   │   └── subscriber-online/
│       │   │       ├── index.ts
│       │   │       └── subscriber-online.service.ts
│       │   ├── socket/
│       │   │   ├── services/
│       │   │   │   ├── cold-start.service.ts
│       │   │   │   ├── index.ts
│       │   │   │   ├── web-socket.worker.spec.ts
│       │   │   │   ├── web-socket.worker.ts
│       │   │   │   └── ws-server-health-indicator.service.ts
│       │   │   ├── socket.module.ts
│       │   │   ├── usecases/
│       │   │   │   └── external-services-route/
│       │   │   │       ├── external-services-route.command.ts
│       │   │   │       ├── external-services-route.spec.ts
│       │   │   │       ├── external-services-route.usecase.ts
│       │   │   │       ├── index.ts
│       │   │   │       └── types.ts
│       │   │   └── ws.gateway.ts
│       │   └── types/
│       │       └── env.d.ts
│       ├── tsconfig.build.json
│       └── tsconfig.json
├── biome-plugins/
│   ├── api-property-optionality-required-prop.grit
│   ├── api-property-optionality.grit
│   ├── api-property-record-type.grit
│   ├── command-session-exclusion.grit
│   └── pino-logger-arg-order.grit
├── biome.json
├── docker/
│   ├── Readme.md
│   ├── community/
│   │   └── docker-compose.yml
│   └── local/
│       ├── docker-compose.agent.yml
│       ├── docker-compose.e2e.yml
│       ├── docker-compose.local.yml
│       └── docker-compose.yml
├── enterprise/
│   ├── packages/
│   │   ├── ai/
│   │   │   ├── .gitignore
│   │   │   ├── check-ee.mjs
│   │   │   ├── package.json
│   │   │   ├── project.json
│   │   │   ├── tsconfig.build.json
│   │   │   └── tsconfig.json
│   │   ├── api/
│   │   │   ├── .gitignore
│   │   │   ├── check-ee.mjs
│   │   │   ├── package.json
│   │   │   ├── project.json
│   │   │   ├── tsconfig.build.json
│   │   │   └── tsconfig.json
│   │   ├── auth/
│   │   │   ├── .gitignore
│   │   │   ├── check-ee.mjs
│   │   │   ├── package.json
│   │   │   ├── project.json
│   │   │   ├── tsconfig.json
│   │   │   └── tsconfig.spec.json
│   │   ├── billing/
│   │   │   ├── .gitignore
│   │   │   ├── check-ee.mjs
│   │   │   ├── package.json
│   │   │   ├── project.json
│   │   │   ├── tsconfig.json
│   │   │   └── tsconfig.spec.json
│   │   ├── shared-services/
│   │   │   ├── .czrc
│   │   │   ├── .gitignore
│   │   │   ├── README.md
│   │   │   ├── check-ee.mjs
│   │   │   ├── package.json
│   │   │   ├── project.json
│   │   │   ├── tsconfig.json
│   │   │   └── tsconfig.module.json
│   │   └── translation/
│   │       ├── .gitignore
│   │       ├── check-ee.mjs
│   │       ├── package.json
│   │       ├── project.json
│   │       ├── tsconfig.json
│   │       └── tsconfig.spec.json
│   └── workers/
│       ├── scheduler/
│       │   ├── .editorconfig
│       │   ├── .gitignore
│       │   ├── .prettierrc
│       │   ├── package.json
│       │   ├── src/
│       │   │   ├── auth.ts
│       │   │   ├── env.d.ts
│       │   │   ├── index.ts
│       │   │   ├── scheduler.ts
│       │   │   └── types.ts
│       │   ├── test/
│       │   │   ├── env.d.ts
│       │   │   ├── index.spec.ts
│       │   │   ├── scheduler.spec.ts
│       │   │   └── tsconfig.json
│       │   ├── tsconfig.json
│       │   ├── vitest.config.mts
│       │   ├── worker-configuration.d.ts
│       │   ├── wrangler.jsonc
│       │   └── wrangler.local.jsonc
│       ├── socket/
│       │   ├── .editorconfig
│       │   ├── .gitignore
│       │   ├── .vscode/
│       │   │   └── settings.json
│       │   ├── package.json
│       │   ├── src/
│       │   │   ├── durable-objects/
│       │   │   │   └── websocket-room.ts
│       │   │   ├── handlers/
│       │   │   │   └── websocket.ts
│       │   │   ├── index.ts
│       │   │   ├── middleware/
│       │   │   │   ├── auth.ts
│       │   │   │   └── internal-auth.ts
│       │   │   └── types/
│       │   │       └── index.ts
│       │   ├── tsconfig.json
│       │   ├── worker-configuration.d.ts
│       │   └── wrangler.jsonc
│       └── step-resolver/
│           ├── .gitignore
│           ├── README.md
│           ├── package.json
│           ├── src/
│           │   ├── auth/
│           │   │   └── hmac.ts
│           │   ├── index.ts
│           │   ├── types.ts
│           │   └── utils/
│           │       └── worker-id.ts
│           ├── tsconfig.json
│           ├── worker-configuration.d.ts
│           └── wrangler.jsonc
├── jest.config.js
├── libs/
│   ├── application-generic/
│   │   ├── .czrc
│   │   ├── .gitignore
│   │   ├── README.md
│   │   ├── jest.config.js
│   │   ├── jest.setup.js
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── commands/
│   │   │   │   ├── authenticated.command.ts
│   │   │   │   ├── base.command.spec.ts
│   │   │   │   ├── base.command.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── organization.command.ts
│   │   │   │   └── project.command.ts
│   │   │   ├── config/
│   │   │   │   ├── index.ts
│   │   │   │   ├── workers.config.spec.ts
│   │   │   │   └── workers.ts
│   │   │   ├── custom-providers/
│   │   │   │   └── index.ts
│   │   │   ├── decorators/
│   │   │   │   ├── context-payload.decorator.ts
│   │   │   │   ├── external-api.decorator.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── is-valid-context-payload.decorator.ts
│   │   │   │   ├── is-valid-locale.decorator.ts
│   │   │   │   ├── json-schema.validator.ts
│   │   │   │   ├── permissions.decorator.ts
│   │   │   │   ├── product-feature.decorator.ts
│   │   │   │   ├── resource-category.decorator.ts
│   │   │   │   ├── retry-on-error-decorator.spec.ts
│   │   │   │   ├── retry-on-error-decorator.ts
│   │   │   │   ├── to-boolean.spec.ts
│   │   │   │   ├── to-boolean.ts
│   │   │   │   └── user-session.decorator.ts
│   │   │   ├── dtos/
│   │   │   │   ├── base-issue.dto.ts
│   │   │   │   ├── configurations.dto.ts
│   │   │   │   ├── controls-metadata.dto.ts
│   │   │   │   ├── credentials.dto.ts
│   │   │   │   ├── get-environment-tags.dto.ts
│   │   │   │   ├── get-workflow-with-preferences.dto.ts
│   │   │   │   ├── inbound-parse-job.dto.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── integration-issue.dto.ts
│   │   │   │   ├── integration-response.dto.ts
│   │   │   │   ├── json-schema.dto.ts
│   │   │   │   ├── layout/
│   │   │   │   │   ├── create-layout.dto.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── layout-controls.dto.ts
│   │   │   │   │   ├── layout-response.dto.ts
│   │   │   │   │   ├── update-layout.dto.ts
│   │   │   │   │   └── v0/
│   │   │   │   │       └── layout.dto.ts
│   │   │   │   ├── process-subscriber-job.dto.ts
│   │   │   │   ├── standard-job.dto.ts
│   │   │   │   ├── step-content-issue.dto.ts
│   │   │   │   ├── step-filter-dto.ts
│   │   │   │   ├── step-issues.dto.ts
│   │   │   │   ├── subscriber-topic-preference.dto.ts
│   │   │   │   ├── subscribers/
│   │   │   │   │   ├── channelSettingsDto.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── subscriber-channel.ts
│   │   │   │   │   ├── subscriber-response.dto.ts
│   │   │   │   │   └── update-subscriber-channel-request.dto.ts
│   │   │   │   ├── ui-schema-property.dto.ts
│   │   │   │   ├── ui-schema.dto.ts
│   │   │   │   ├── user-response.dto.ts
│   │   │   │   ├── web-sockets-job.dto.ts
│   │   │   │   ├── workflow/
│   │   │   │   │   ├── channel-preference.dto.ts
│   │   │   │   │   ├── chat-control.dto.ts
│   │   │   │   │   ├── controls/
│   │   │   │   │   │   ├── custom-control.dto.ts
│   │   │   │   │   │   ├── delay-control.dto.ts
│   │   │   │   │   │   ├── digest-control.dto.ts
│   │   │   │   │   │   ├── email-control.dto.ts
│   │   │   │   │   │   ├── http-request-control.dto.ts
│   │   │   │   │   │   ├── in-app-control.dto.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── look-back-window.dto.ts
│   │   │   │   │   │   ├── push-control.dto.ts
│   │   │   │   │   │   ├── sms-control.dto.ts
│   │   │   │   │   │   └── throttle-control.dto.ts
│   │   │   │   │   ├── generate-preview-request.dto.ts
│   │   │   │   │   ├── generate-preview-response.dto.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── preferences.response.dto.ts
│   │   │   │   │   ├── preview-payload.dto.ts
│   │   │   │   │   ├── runtime-issue.dto.ts
│   │   │   │   │   ├── skip.dto.ts
│   │   │   │   │   ├── step-list-response.dto.ts
│   │   │   │   │   ├── step-responses/
│   │   │   │   │   │   ├── chat-step.response.dto.ts
│   │   │   │   │   │   ├── custom-step.response.dto.ts
│   │   │   │   │   │   ├── delay-step.response.dto.ts
│   │   │   │   │   │   ├── digest-step.response.dto.ts
│   │   │   │   │   │   ├── email-step.response.dto.ts
│   │   │   │   │   │   ├── http-request-step.response.dto.ts
│   │   │   │   │   │   ├── in-app-step.response.dto.ts
│   │   │   │   │   │   ├── push-step.response.dto.ts
│   │   │   │   │   │   ├── sms-step.response.dto.ts
│   │   │   │   │   │   └── throttle-step.response.dto.ts
│   │   │   │   │   ├── step.response.dto.ts
│   │   │   │   │   ├── workflow-commons.dto.ts
│   │   │   │   │   ├── workflow-list-response.dto.ts
│   │   │   │   │   ├── workflow-preference.dto.ts
│   │   │   │   │   ├── workflow-preferences.dto.ts
│   │   │   │   │   └── workflow-response.dto.ts
│   │   │   │   └── workflow-job.dto.ts
│   │   │   ├── encryption/
│   │   │   │   ├── cipher.spec.ts
│   │   │   │   ├── cipher.ts
│   │   │   │   ├── encrypt-environment-variable.ts
│   │   │   │   ├── encrypt-provider.spec.ts
│   │   │   │   ├── encrypt-provider.ts
│   │   │   │   └── index.ts
│   │   │   ├── factories/
│   │   │   │   ├── channel.factory.ts
│   │   │   │   ├── chat/
│   │   │   │   │   ├── chat.factory.ts
│   │   │   │   │   ├── handlers/
│   │   │   │   │   │   ├── base.handler.ts
│   │   │   │   │   │   ├── chat-webhook.handler.ts
│   │   │   │   │   │   ├── discord.handler.ts
│   │   │   │   │   │   ├── getstream.handler.ts
│   │   │   │   │   │   ├── grafana-on-call.handler.ts
│   │   │   │   │   │   ├── mattermost.handler.ts
│   │   │   │   │   │   ├── msteams.handler.ts
│   │   │   │   │   │   ├── novu-slack.handler.ts
│   │   │   │   │   │   ├── rocket-chat.handler.ts
│   │   │   │   │   │   ├── ryver.handler.ts
│   │   │   │   │   │   ├── slack.handler.ts
│   │   │   │   │   │   ├── whatsapp-business.handler.ts
│   │   │   │   │   │   └── zulip.handler.ts
│   │   │   │   │   └── interfaces/
│   │   │   │   │       └── index.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── mail/
│   │   │   │   │   ├── handlers/
│   │   │   │   │   │   ├── base.handler.ts
│   │   │   │   │   │   ├── braze.handler.ts
│   │   │   │   │   │   ├── email-webhook.handler.ts
│   │   │   │   │   │   ├── emailjs.handler.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── infobip.handler.ts
│   │   │   │   │   │   ├── mailersend.handler.ts
│   │   │   │   │   │   ├── mailgun.handler.ts
│   │   │   │   │   │   ├── mailjet.handler.ts
│   │   │   │   │   │   ├── mailtrap.handler.ts
│   │   │   │   │   │   ├── mandrill.handler.ts
│   │   │   │   │   │   ├── netcore.handler.ts
│   │   │   │   │   │   ├── nodemailer.handler.ts
│   │   │   │   │   │   ├── novu.handler.ts
│   │   │   │   │   │   ├── outlook365.handler.ts
│   │   │   │   │   │   ├── plunk.handler.ts
│   │   │   │   │   │   ├── postmark.handler.ts
│   │   │   │   │   │   ├── resend.handler.ts
│   │   │   │   │   │   ├── sendgrid.handler.ts
│   │   │   │   │   │   ├── sendinblue.handler.ts
│   │   │   │   │   │   ├── ses.handler.ts
│   │   │   │   │   │   └── sparkpost.handler.ts
│   │   │   │   │   ├── interfaces/
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   └── mail.factory.ts
│   │   │   │   ├── push/
│   │   │   │   │   ├── handlers/
│   │   │   │   │   │   ├── apns.handler.ts
│   │   │   │   │   │   ├── appio.handler.ts
│   │   │   │   │   │   ├── base.handler.ts
│   │   │   │   │   │   ├── expo.handler.ts
│   │   │   │   │   │   ├── fcm.handler.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── one-signal.handler.ts
│   │   │   │   │   │   ├── push-webhook.handler.ts
│   │   │   │   │   │   ├── pusher-beams.handler.ts
│   │   │   │   │   │   └── pushpad.handler.ts
│   │   
Download .txt
Showing preview only (1,995K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (18882 symbols across 4582 files)

FILE: .github/workflows/scripts/add-triage-label.js
  function start (line 10) | async function start() {

FILE: apps/api/admin/connect-to-dal.ts
  function connect (line 5) | async function connect(databaseQuery: () => Promise<void>) {

FILE: apps/api/admin/make-json-backup.ts
  function makeJsonBackup (line 6) | async function makeJsonBackup(folder: string, fileName: string, obj: unk...

FILE: apps/api/admin/remove-organization.ts
  constant ORG_ID (line 28) | const ORG_ID = args[0];
  function removeData (line 31) | async function removeData<T extends BaseRepository<object, E, EnforceEnv...

FILE: apps/api/admin/remove-user-account.ts
  constant EMAIL (line 8) | const EMAIL = args[0];

FILE: apps/api/e2e/mock-http-client.ts
  class MockHTTPClient (line 3) | class MockHTTPClient extends HTTPClient {
    method constructor (line 7) | constructor(mockConfigs: MockConfig[] = [], options: HTTPClientOptions...
    method initializeMockResponses (line 16) | private initializeMockResponses(mockConfigs: MockConfig[]) {
    method request (line 41) | async request(request: Request): Promise<Response> {
    method getRecordedRequests (line 81) | getRecordedRequests(): Array<{ request: Request; response: Response }> {
  type MockConfig (line 86) | interface MockConfig {

FILE: apps/api/e2e/retry.e2e.ts
  function getIdempotencyKeys (line 8) | function getIdempotencyKeys(mockHTTPClient: MockHTTPClient) {
  constant BACKEND_URL (line 257) | const BACKEND_URL = 'http://example.com';
  constant TOPICS_PATH (line 258) | const TOPICS_PATH = '/v1/topics';
  constant TRIGGER_PATH (line 259) | const TRIGGER_PATH = '/v1/events/trigger';
  function buildErrorDto (line 266) | function buildErrorDto(path: string, message: string, status: number): E...
  constant IDEMPOTENCY_HEADER_KEY (line 275) | const IDEMPOTENCY_HEADER_KEY = 'idempotency-key';
  function getIdempotencyRequestKeys (line 277) | function getIdempotencyRequestKeys(mockHTTPClient: MockHTTPClient) {

FILE: apps/api/e2e/setup.ts
  function getDatabaseConnection (line 34) | async function getDatabaseConnection(): Promise<Connection> {
  function dropDatabase (line 42) | async function dropDatabase(): Promise<void> {
  function ensureIndexes (line 51) | async function ensureIndexes(conn: Connection): Promise<void> {
  function closeDatabaseConnection (line 68) | async function closeDatabaseConnection(): Promise<void> {
  function getClickHouseConnection (line 74) | async function getClickHouseConnection(): Promise<ClickHouseClient | und...
  function createClickHouseTestClient (line 86) | function createClickHouseTestClient(database?: string): ClickHouseClient {
  function ensureClickHouseDatabase (line 95) | async function ensureClickHouseDatabase(databaseName: string): Promise<v...
  function getClickHouseTables (line 107) | async function getClickHouseTables(databaseName: string): Promise<string...
  function truncateClickHouseTable (line 127) | async function truncateClickHouseTable(databaseName: string, tableName: ...
  function cleanupClickHouseDatabase (line 139) | async function cleanupClickHouseDatabase(): Promise<void> {
  function closeClickHouseConnection (line 162) | async function closeClickHouseConnection(): Promise<void> {
  function waitForHealthCheck (line 171) | async function waitForHealthCheck(): Promise<void> {
  function formatZodError (line 205) | function formatZodError(err: ZodError, level = 0): string {
  function isResponseValidationError (line 264) | function isResponseValidationError(error: unknown): error is {
  function isValidationErrorDto (line 283) | function isValidationErrorDto(error: unknown): error is Error & {
  function logE2EFailure (line 307) | function logE2EFailure(error: unknown): void {
  function getFailedHookError (line 385) | function getFailedHookError(test: Mocha.Test | undefined): unknown {

FILE: apps/api/e2e/test-bridge-server.ts
  class TestBridgeServer (line 5) | class TestBridgeServer {
    method constructor (line 12) | constructor(port = 49999) {
    method log (line 16) | private log(level: 'info' | 'error' | 'warn', message: string, ...args...
    method serverPath (line 20) | get serverPath() {
    method start (line 24) | async start(options) {
    method stop (line 95) | async stop() {

FILE: apps/api/exportOpenAPIJSON.ts
  function exportOpenAPIJSON (line 4) | async function exportOpenAPIJSON() {

FILE: apps/api/migrations/001-add-default-identifier-to-topic-subscribers/add-default-identifier-to-topic-subscribers-migration.ts
  function run (line 8) | async function run() {

FILE: apps/api/migrations/002-remove-duplicate-identifiers/remove-duplicate-identifiers.ts
  type DuplicateGroup (line 8) | interface DuplicateGroup {
  constant BATCH_SIZE (line 17) | const BATCH_SIZE = 500;
  function run (line 19) | async function run() {

FILE: apps/api/migrations/add-layout-id-to-email-controls/add-layout-id-to-email-controls-migration.ts
  function run (line 10) | async function run() {

FILE: apps/api/migrations/changes-migration.ts
  function run (line 20) | async function run(): Promise<void> {

FILE: apps/api/migrations/clickhouse-migrations/1_initial_schema.sql
  type step_runs (line 7) | CREATE TABLE IF NOT EXISTS step_runs (
  type traces (line 40) | CREATE TABLE IF NOT EXISTS traces (
  type requests (line 65) | CREATE TABLE IF NOT EXISTS requests (
  type workflow_runs (line 93) | CREATE TABLE IF NOT EXISTS workflow_runs (

FILE: apps/api/migrations/clickhouse-migrations/3_analytics_tables.sql
  type delivery_trend_counts (line 5) | CREATE TABLE IF NOT EXISTS delivery_trend_counts (
  type trace_rollup (line 41) | CREATE TABLE IF NOT EXISTS trace_rollup (

FILE: apps/api/migrations/clickhouse-migrations/4_refactor_traces_schema.sql
  type traces_temp (line 25) | CREATE TABLE IF NOT EXISTS traces_temp (
  type delivery_trend_counts_temp (line 127) | CREATE TABLE IF NOT EXISTS delivery_trend_counts_temp (
  type workflow_run_count (line 160) | CREATE TABLE IF NOT EXISTS workflow_run_count (

FILE: apps/api/migrations/email-step-ui-schema-html-editor/email-step-ui-schema-html-editor-migration.ts
  function run (line 8) | async function run() {

FILE: apps/api/migrations/encrypt-api-keys/encrypt-api-keys-migration.spec.ts
  function pruneIntegration (line 11) | async function pruneIntegration({ environmentRepository }: { environment...

FILE: apps/api/migrations/encrypt-api-keys/encrypt-api-keys-migration.ts
  function encryptApiKeysMigration (line 11) | async function encryptApiKeysMigration() {
  function encryptApiKeysWithGuard (line 56) | function encryptApiKeysWithGuard(apiKeys: IApiKey[]): IEncryptedApiKey[] {
  function isEncrypted (line 70) | function isEncrypted(apiKey: string): apiKey is EncryptedSecret {
  type IEncryptedApiKey (line 74) | interface IEncryptedApiKey {

FILE: apps/api/migrations/encrypt-credentials/encrypt-credentials-migration.spec.ts
  function pruneIntegration (line 85) | async function pruneIntegration(integrationRepository) {

FILE: apps/api/migrations/encrypt-credentials/encrypt-credentials-migration.ts
  function encryptOldCredentialsMigration (line 7) | async function encryptOldCredentialsMigration() {
  function encryptCredentialsWithGuard (line 43) | function encryptCredentialsWithGuard(integration: IntegrationEntity): IC...
  function needEncryption (line 60) | function needEncryption(key: string, credential: string, integration: In...
  function secureKey (line 64) | function secureKey(key: string): boolean {
  function alreadyEncrypted (line 68) | function alreadyEncrypted(credential: string, integration: IntegrationEn...

FILE: apps/api/migrations/expire-at/expire-at-delay.migration.spec.ts
  function createDelayTemplate (line 106) | async function createDelayTemplate(session) {
  function createDigestTemplate (line 129) | async function createDigestTemplate(session) {

FILE: apps/api/migrations/expire-at/expire-at.migration.spec.ts
  function createTemplate (line 81) | async function createTemplate(session) {

FILE: apps/api/migrations/expire-at/expire-at.migration.ts
  function createExpireAt (line 25) | async function createExpireAt() {
  function messagesSetExpireAt (line 63) | async function messagesSetExpireAt(query) {
  function notificationExpireAt (line 81) | async function notificationExpireAt(query) {
  function getExcludedNotificationIds (line 100) | async function getExcludedNotificationIds(query) {

FILE: apps/api/migrations/fcm-credentials/fcm-credentials-migration.spec.ts
  function pruneIntegration (line 53) | async function pruneIntegration(integrationRepository: IntegrationReposi...

FILE: apps/api/migrations/fcm-credentials/fcm-credentials-migration.ts
  function updateFcmCredentials (line 4) | async function updateFcmCredentials() {

FILE: apps/api/migrations/in-app-integration/in-app-integration.migration.ts
  function createInAppIntegration (line 12) | async function createInAppIntegration() {

FILE: apps/api/migrations/integration-scheme-update/add-integration-identifier-migration.spec.ts
  function pruneIntegration (line 128) | async function pruneIntegration(integrationRepository) {

FILE: apps/api/migrations/integration-scheme-update/add-integration-identifier-migration.ts
  constant ENVIRONMENT_NAME_TO_SHORT_NAME (line 7) | const ENVIRONMENT_NAME_TO_SHORT_NAME = { Development: 'dev', Production:...
  function addIntegrationIdentifierMigrationBatched (line 9) | async function addIntegrationIdentifierMigrationBatched() {
  function addIntegrationIdentifierMigration (line 36) | async function addIntegrationIdentifierMigration() {
  function getUpdatePayload (line 64) | async function getUpdatePayload(integration: IntegrationEntity, environm...
  function genIntegrationIdentificationDetails (line 79) | function genIntegrationIdentificationDetails({

FILE: apps/api/migrations/integration-scheme-update/add-primary-priority-migration.ts
  function run (line 11) | async function run() {

FILE: apps/api/migrations/integration-scheme-update/update-primary-for-disabled-novu-integrations.ts
  function run (line 11) | async function run() {

FILE: apps/api/migrations/layout-identifier-update/add-layout-identifier-migration.spec.ts
  function pruneLayouts (line 104) | async function pruneLayouts(layoutRepository) {

FILE: apps/api/migrations/layout-identifier-update/add-layout-identifier-migration.ts
  function addLayoutIdentifierMigration (line 7) | async function addLayoutIdentifierMigration() {

FILE: apps/api/migrations/normalize-message-template-cta-action/normalize-message-cta-action-migration.ts
  function normalizeMessageCtaAction (line 3) | async function normalizeMessageCtaAction() {

FILE: apps/api/migrations/normalize-message-template-cta-action/normalize-message-template-cta-action-migration.ts
  function normalizeMessageTemplateCtaAction (line 3) | async function normalizeMessageTemplateCtaAction() {

FILE: apps/api/migrations/normalize-users-email/normalize-users-email.migration.ts
  function run (line 9) | async function run() {

FILE: apps/api/migrations/novu-integrations/novu-integrations.migration.ts
  function createNovuIntegrations (line 56) | async function createNovuIntegrations() {

FILE: apps/api/migrations/preference-centralization/preference-centralization-migration.ts
  function preferenceCentralization (line 22) | async function preferenceCentralization() {

FILE: apps/api/migrations/preferences-uniqueness/preferences-uniqueness-migration.spec.ts
  function cleanupPreferences (line 19) | async function cleanupPreferences() {
  function cleanupSubscribers (line 23) | async function cleanupSubscribers() {

FILE: apps/api/migrations/preferences-uniqueness/preferences-uniqueness-migration.ts
  function run (line 10) | async function run() {
  function deletePreferenceDuplicates (line 88) | async function deletePreferenceDuplicates({

FILE: apps/api/migrations/secure-to-boolean/secure-to-boolean-migration.spec.ts
  constant STR_FALSE_AMOUNT (line 8) | const STR_FALSE_AMOUNT = 10;
  constant FALSE_AMOUNT (line 9) | const FALSE_AMOUNT = 12;
  constant STR_TRUE_AMOUNT (line 11) | const STR_TRUE_AMOUNT = 15;
  constant TRUE_AMOUNT (line 12) | const TRUE_AMOUNT = 10;
  function clearIntegrationCollection (line 42) | async function clearIntegrationCollection() {
  function seedIntegrationCollection (line 46) | async function seedIntegrationCollection(secureValue: any, amount: numbe...
  function countAfterChange (line 64) | async function countAfterChange(secureValue: boolean) {

FILE: apps/api/migrations/secure-to-boolean/secure-to-boolean-migration.ts
  function run (line 9) | async function run() {
  type UpdateResult (line 30) | type UpdateResult = { matchedCount: number; modifiedCount: number };
  function updateTrueValues (line 32) | function updateTrueValues() {
  function updateFalseValues (line 43) | function updateFalseValues() {

FILE: apps/api/migrations/seen-read-support/seen-read-support.migration.spec.ts
  function createTemplate (line 58) | async function createTemplate(session) {

FILE: apps/api/migrations/seen-read-support/seen-read-support.migration.ts
  function updateSeenRead (line 5) | async function updateSeenRead() {
  function seenToRead (line 23) | async function seenToRead() {
  function inAppAsSeen (line 27) | async function inAppAsSeen() {
  function notInAppAsUnseen (line 37) | async function notInAppAsUnseen() {

FILE: apps/api/migrations/subscribers/remove-duplicated-subscribers/remove-duplicated-subscribers.migration.ts
  function removeDuplicatedSubscribers (line 7) | async function removeDuplicatedSubscribers() {
  function mergeSubscribers (line 112) | function mergeSubscribers(subscribers) {
  function mergeChannels (line 157) | function mergeChannels(existingChannel: IChannelSettings, newChannel: IC...
  function mergeSubscriberChannels (line 174) | function mergeSubscriberChannels(subscriber: ISubscriber, mergedChannels...

FILE: apps/api/migrations/topic-subscriber-normalize/topic-subscriber-normalize.migration.spec.ts
  constant TOPIC_PATH (line 11) | const TOPIC_PATH = '/v1/topics';

FILE: apps/api/migrations/topic-subscriber-normalize/topic-subscriber-normalize.migration.ts
  function topicSubscriberNormalize (line 11) | async function topicSubscriberNormalize() {

FILE: apps/api/scripts/check-api-property-optionality.ts
  constant REPORT_VERSION (line 18) | const REPORT_VERSION = 1 as const;
  type IssueKind (line 20) | type IssueKind = 'ts_optional_openapi_required' | 'ts_required_openapi_o...
  type Issue (line 22) | type Issue = {
  type Report (line 30) | type Report = {
  constant REPO_ROOT (line 36) | const REPO_ROOT = path.resolve(__dirname, '../../..');
  constant DTO_GLOBS (line 38) | const DTO_GLOBS = ['apps/api/**/*.dto.ts', 'libs/application-generic/**/...
  constant SWAGGER_DECORATORS (line 40) | const SWAGGER_DECORATORS = new Set(['ApiProperty', 'ApiPropertyOptional']);
  function getRequiredFromDecoratorOptions (line 42) | function getRequiredFromDecoratorOptions(decorator: Decorator): boolean ...
  function effectiveOpenApiRequired (line 86) | function effectiveOpenApiRequired(decorator: Decorator): boolean {
  function collectIssuesForFile (line 105) | function collectIssuesForFile(sourceFile: SourceFile): Issue[] {
  function buildReport (line 152) | function buildReport(issues: Issue[]): Report {
  function printHelp (line 160) | function printHelp(): void {
  type ParsedArgs (line 170) | type ParsedArgs = {
  function parseArgs (line 177) | function parseArgs(argv: string[]): ParsedArgs {
  function resolveReportPath (line 228) | function resolveReportPath(reportPath: string): string {
  function isUnderNodeModules (line 236) | function isUnderNodeModules(filePath: string): boolean {
  function main (line 240) | async function main(): Promise<void> {

FILE: apps/api/scripts/clickhouse-seeder/config.ts
  type SingleEnvironmentConfig (line 1) | interface SingleEnvironmentConfig {
  type SeederConfig (line 12) | interface SeederConfig {
  type OrganizationProfile (line 21) | interface OrganizationProfile {
  constant ORGANIZATION_PROFILES (line 33) | const ORGANIZATION_PROFILES: Record<string, OrganizationProfile> = {
  constant ENTERPRISE_HEAVY_DISTRIBUTION (line 69) | const ENTERPRISE_HEAVY_DISTRIBUTION = {
  type WorkflowTemplate (line 75) | interface WorkflowTemplate {
  constant WORKFLOW_TEMPLATES (line 82) | const WORKFLOW_TEMPLATES: WorkflowTemplate[] = [
  constant WORKFLOW_RUN_STATUS_DISTRIBUTION (line 89) | const WORKFLOW_RUN_STATUS_DISTRIBUTION = {
  constant STEP_RUN_STATUS_DISTRIBUTION (line 95) | const STEP_RUN_STATUS_DISTRIBUTION = {
  constant DELIVERY_LIFECYCLE_STATUS_DISTRIBUTION (line 102) | const DELIVERY_LIFECYCLE_STATUS_DISTRIBUTION = {
  constant TRACE_EVENT_TYPES (line 111) | const TRACE_EVENT_TYPES = {
  constant DEFAULT_SINGLE_ENV_CONFIG (line 117) | const DEFAULT_SINGLE_ENV_CONFIG: SingleEnvironmentConfig = {
  constant DEFAULT_CONFIG (line 124) | const DEFAULT_CONFIG: SeederConfig = {
  function parseCliArgs (line 131) | function parseCliArgs(): SeederConfig {
  function printHelp (line 226) | function printHelp() {

FILE: apps/api/scripts/clickhouse-seeder/generators.ts
  type Organization (line 17) | interface Organization {
  type Environment (line 24) | interface Environment {
  type Workflow (line 32) | interface Workflow {
  type Subscriber (line 42) | interface Subscriber {
  type WorkflowRunRecord (line 49) | interface WorkflowRunRecord {
  type StepRunRecord (line 79) | interface StepRunRecord {
  type TraceRecord (line 107) | interface TraceRecord {
  function generateId (line 129) | function generateId(): string {
  function randomInt (line 133) | function randomInt(min: number, max: number): number {
  function randomChoice (line 137) | function randomChoice<T>(items: T[]): T {
  function weightedRandomChoice (line 141) | function weightedRandomChoice(distribution: Record<string, number>): str...
  function generateOrganizations (line 155) | function generateOrganizations(config: SeederConfig): Organization[] {
  function generateSingleEnvironment (line 229) | function generateSingleEnvironment(singleEnvConfig: SingleEnvironmentCon...
  function selectWorkflowTemplate (line 288) | function selectWorkflowTemplate(): WorkflowTemplate {
  function generateWorkflowRuns (line 302) | function generateWorkflowRuns(organizations: Organization[], config: See...
  type GenerationProgress (line 330) | interface GenerationProgress {
  type ProgressCallback (line 337) | type ProgressCallback = (progress: GenerationProgress) => void;
  type DataBatch (line 339) | interface DataBatch {
  function estimateTotalWorkflowRuns (line 345) | function estimateTotalWorkflowRuns(organizations: Organization[], config...
  function generateStepRunsForWorkflow (line 441) | function generateStepRunsForWorkflow(workflowRun: WorkflowRunRecord, wor...
  function generateTracesForStepRun (line 456) | function generateTracesForStepRun(stepRun: StepRunRecord): TraceRecord[] {
  function createWorkflowRunRecord (line 471) | function createWorkflowRunRecord(
  function generateStepRuns (line 517) | function generateStepRuns(workflowRuns: WorkflowRunRecord[], organizatio...
  function createStepRunRecord (line 552) | function createStepRunRecord(
  function generateTraces (line 604) | function generateTraces(stepRuns: StepRunRecord[]): TraceRecord[] {
  function selectTraceEventType (line 622) | function selectTraceEventType(index: number, total: number, stepStatus: ...
  function createTraceRecord (line 656) | function createTraceRecord(stepRun: StepRunRecord, eventType: string, cr...
  function formatEventTitle (line 698) | function formatEventTitle(eventType: string): string {

FILE: apps/api/scripts/clickhouse-seeder/inserter.ts
  type InsertStats (line 3) | interface InsertStats {
  function formatDateForClickHouse (line 10) | function formatDateForClickHouse(date: Date): string {
  function transformRecordDates (line 22) | function transformRecordDates(record: any): any {
  class ClickHouseInserter (line 33) | class ClickHouseInserter {
    method constructor (line 41) | constructor(
    method insertWorkflowRuns (line 46) | async insertWorkflowRuns(records: any[]): Promise<void> {
    method insertStepRuns (line 53) | async insertStepRuns(records: any[]): Promise<void> {
    method insertTraces (line 60) | async insertTraces(records: any[]): Promise<void> {
    method insertWorkflowRunsSilent (line 67) | async insertWorkflowRunsSilent(records: any[]): Promise<void> {
    method insertStepRunsSilent (line 76) | async insertStepRunsSilent(records: any[]): Promise<void> {
    method insertTracesSilent (line 85) | async insertTracesSilent(records: any[]): Promise<void> {
    method insertDirect (line 94) | private async insertDirect(table: string, records: any[]): Promise<voi...
    method insertInBatches (line 107) | private async insertInBatches(table: string, records: any[], logProgre...
    method logProgress (line 130) | private logProgress(table: string, currentBatch: number, totalBatches:...
    method getStats (line 135) | getStats(): InsertStats {
    method printStats (line 139) | printStats(): void {
  function formatBytes (line 154) | function formatBytes(bytes: number): string {
  function estimateDataSize (line 164) | function estimateDataSize(stats: InsertStats): string {

FILE: apps/api/scripts/clickhouse-seeder/time-distribution.ts
  type TimeDistributionConfig (line 1) | interface TimeDistributionConfig {
  constant DEFAULT_TIME_CONFIG (line 7) | const DEFAULT_TIME_CONFIG: TimeDistributionConfig = {
  function isWeekend (line 13) | function isWeekend(date: Date): boolean {
  function isBusinessHours (line 18) | function isBusinessHours(date: Date): boolean {
  function getHourWeight (line 23) | function getHourWeight(hour: number): number {
  function getDayWeight (line 39) | function getDayWeight(date: Date, config: TimeDistributionConfig = DEFAU...
  function getTimestampWeight (line 49) | function getTimestampWeight(date: Date, config: TimeDistributionConfig =...
  function getPeakPatternModifier (line 65) | function getPeakPatternModifier(date: Date): number {
  function generateRandomTimestampsForDay (line 89) | function generateRandomTimestampsForDay(
  function calculateHourDistribution (line 127) | function calculateHourDistribution(totalCount: number, config: TimeDistr...
  function addRandomJitter (line 155) | function addRandomJitter(baseDate: Date, maxJitterMs: number = 5000): Da...
  function generateWorkflowRunTimestamps (line 160) | function generateWorkflowRunTimestamps(

FILE: apps/api/scripts/run-novu-v2-e2e-shard.cjs
  constant ROOT (line 7) | const ROOT = path.resolve(__dirname, '..');
  constant DEFAULT_SHARD_INDEX (line 8) | const DEFAULT_SHARD_INDEX = 1;
  constant DEFAULT_TOTAL_SHARDS (line 9) | const DEFAULT_TOTAL_SHARDS = 1;
  constant NOVU_V2_TAG (line 10) | const NOVU_V2_TAG = '#novu-v2';
  constant TEST_FILE_PATTERN (line 11) | const TEST_FILE_PATTERN = /\.e2e(-ee)?\.ts$/;
  constant TEST_CASE_PATTERN (line 12) | const TEST_CASE_PATTERN = /\bit(?:\.only)?\s*\(/g;
  constant DEFAULT_MOCHA_REPORTER (line 13) | const DEFAULT_MOCHA_REPORTER = process.env.CI ? 'dot' : 'spec';
  constant MOCHA_REPORTER (line 14) | const MOCHA_REPORTER = process.env.NOVU_V2_MOCHA_REPORTER || DEFAULT_MOC...
  constant MOCHA_ARGS (line 16) | const MOCHA_ARGS = [
  function toPosixPath (line 33) | function toPosixPath(filePath) {
  function compareFileNames (line 37) | function compareFileNames(left, right) {
  function readSortedEntries (line 41) | function readSortedEntries(dir) {
  function collectTestFiles (line 45) | function collectTestFiles(dir, files = []) {
  function getCliArgs (line 66) | function getCliArgs() {
  function parseShardValue (line 70) | function parseShardValue(rawValue) {
  function parseShardConfig (line 80) | function parseShardConfig() {
  function applyDefaultEnv (line 101) | function applyDefaultEnv() {
  function readSource (line 119) | function readSource(relativePath) {
  function countTestCases (line 123) | function countTestCases(source) {
  function compareWeightedFiles (line 127) | function compareWeightedFiles(left, right) {
  function collectWeightedFiles (line 131) | function collectWeightedFiles() {
  function isLighterShard (line 154) | function isLighterShard(candidate, current) {
  function pickLightestShard (line 158) | function pickLightestShard(shards) {
  function buildShards (line 170) | function buildShards(weightedFiles, totalShards) {
  function getShard (line 185) | function getShard(weightedFiles, shardIndex, totalShards) {
  function printShardSummary (line 189) | function printShardSummary(shardIndex, totalShards, shard) {
  function runMocha (line 193) | function runMocha(filePaths) {
  function run (line 201) | function run() {

FILE: apps/api/scripts/seed-clickhouse.ts
  function formatProgress (line 17) | function formatProgress(progress: GenerationProgress): string {
  function main (line 25) | async function main() {
  function printOrganizationBreakdown (line 178) | function printOrganizationBreakdown(organizations: Organization[]) {

FILE: apps/api/scripts/seed-triggers.ts
  type SeedConfig (line 14) | interface SeedConfig {
  function parseCliArgs (line 26) | function parseCliArgs(): SeedConfig {
  function printHelp (line 126) | function printHelp() {
  function formatProgress (line 179) | function formatProgress(current: number, total: number): string {
  function sleep (line 188) | async function sleep(ms: number): Promise<void> {
  function triggerBatch (line 192) | async function triggerBatch(
  function main (line 231) | async function main() {

FILE: apps/api/src/app.module.ts
  class AppModule (line 216) | class AppModule {}

FILE: apps/api/src/app/activity/activity.controller.ts
  class ActivityController (line 30) | class ActivityController {
    method constructor (line 31) | constructor(
    method getLogs (line 47) | async getLogs(@UserSession() user: UserSessionData, @Query() query: Ge...
    method getRequestTraces (line 66) | async getRequestTraces(@UserSession() user, @Param('requestId') reques...
    method getWorkflowRuns (line 84) | async getWorkflowRuns(
    method getWorkflowRun (line 107) | async getWorkflowRun(
    method getCharts (line 129) | async getCharts(

FILE: apps/api/src/app/activity/activity.module.ts
  constant USE_CASES (line 23) | const USE_CASES = [
  class ActivityModule (line 49) | class ActivityModule {}

FILE: apps/api/src/app/activity/dtos/get-charts.request.dto.ts
  class GetChartsRequestDto (line 4) | class GetChartsRequestDto {

FILE: apps/api/src/app/activity/dtos/get-charts.response.dto.ts
  class ChartDataPointDto (line 5) | class ChartDataPointDto {
  class InteractionTrendDataPointDto (line 31) | class InteractionTrendDataPointDto {
  class WorkflowVolumeDataPointDto (line 53) | class WorkflowVolumeDataPointDto {
  class ProviderVolumeDataPointDto (line 63) | class ProviderVolumeDataPointDto {
  class MessagesDeliveredDataPointDto (line 73) | class MessagesDeliveredDataPointDto {
  class ActiveSubscribersDataPointDto (line 83) | class ActiveSubscribersDataPointDto {
  class AvgMessagesPerSubscriberDataPointDto (line 93) | class AvgMessagesPerSubscriberDataPointDto {
  class WorkflowRunsMetricDataPointDto (line 103) | class WorkflowRunsMetricDataPointDto {
  class TotalInteractionsDataPointDto (line 113) | class TotalInteractionsDataPointDto {
  class WorkflowRunsTrendDataPointDto (line 123) | class WorkflowRunsTrendDataPointDto {
  class ActiveSubscribersTrendDataPointDto (line 141) | class ActiveSubscribersTrendDataPointDto {
  class WorkflowRunsCountDataPointDto (line 151) | class WorkflowRunsCountDataPointDto {
  class GetChartsResponseDto (line 157) | class GetChartsResponseDto {

FILE: apps/api/src/app/activity/dtos/get-request.request.dto.ts
  class GetRequestRequestDto (line 4) | class GetRequestRequestDto {

FILE: apps/api/src/app/activity/dtos/get-request.response.dto.ts
  class TraceResponseDto (line 6) | class TraceResponseDto {
  class GetRequestResponseDto (line 69) | class GetRequestResponseDto {

FILE: apps/api/src/app/activity/dtos/get-requests.dto.ts
  class GetRequestsDto (line 33) | class GetRequestsDto {

FILE: apps/api/src/app/activity/dtos/get-requests.response.dto.ts
  class RequestLogResponseDto (line 5) | class RequestLogResponseDto {
  class GetRequestsResponseDto (line 80) | class GetRequestsResponseDto {

FILE: apps/api/src/app/activity/dtos/shared.dto.ts
  type WorkflowRunStatusDtoEnum (line 5) | enum WorkflowRunStatusDtoEnum {
  class TopicResponseDto (line 11) | class TopicResponseDto {
  class GetWorkflowRunResponseBaseDto (line 20) | class GetWorkflowRunResponseBaseDto {
  type ReportTypeEnum (line 106) | enum ReportTypeEnum {

FILE: apps/api/src/app/activity/dtos/workflow-run-response.dto.ts
  class StepExecutionDetailDto (line 10) | class StepExecutionDetailDto {
  class StepRunDto (line 43) | class StepRunDto {
  class GetWorkflowRunResponseDto (line 103) | class GetWorkflowRunResponseDto extends GetWorkflowRunResponseBaseDto {

FILE: apps/api/src/app/activity/dtos/workflow-runs-request.dto.ts
  class GetWorkflowRunsRequestDto (line 6) | class GetWorkflowRunsRequestDto {

FILE: apps/api/src/app/activity/dtos/workflow-runs-response.dto.ts
  class WorkflowRunStepsDetailsDto (line 7) | class WorkflowRunStepsDetailsDto {
  class GetWorkflowRunsDto (line 47) | class GetWorkflowRunsDto extends GetWorkflowRunResponseBaseDto {
  class GetWorkflowRunsResponseDto (line 53) | class GetWorkflowRunsResponseDto {

FILE: apps/api/src/app/activity/e2e/get-requests.e2e.ts
  function normalizeRequestLogForTesting (line 214) | function normalizeRequestLogForTesting(requestLog: RequestLogResponseDto...

FILE: apps/api/src/app/activity/e2e/get-workflow-runs.e2e.ts
  function createMultipleWorkflowRuns (line 25) | async function createMultipleWorkflowRuns(options: {
  function createMultipleWorkflowRunsByDb (line 46) | async function createMultipleWorkflowRunsByDb(options: {

FILE: apps/api/src/app/activity/shared/mappers.ts
  function mapWorkflowRunStatusToDto (line 7) | function mapWorkflowRunStatusToDto(workflowRunStatus: WorkflowRunStatusE...
  function mapTraceToResponseDto (line 22) | function mapTraceToResponseDto({
  function mapTraceToExecutionDetailDto (line 71) | function mapTraceToExecutionDetailDto(
  function mapTraceStatusToExecutionDetailsStatus (line 84) | function mapTraceStatusToExecutionDetailsStatus(traceStatus: TraceStatus...

FILE: apps/api/src/app/activity/shared/select.const.ts
  type GetTraceFetchResult (line 19) | type GetTraceFetchResult = Pick<Trace, (typeof traceSelectColumns)[numbe...
  type GetRequestLogFetchResult (line 42) | type GetRequestLogFetchResult = Pick<RequestLog, (typeof requestLogSelec...

FILE: apps/api/src/app/activity/usecases/build-active-subscribers-chart/build-active-subscribers-chart.command.ts
  class BuildActiveSubscribersChartCommand (line 4) | class BuildActiveSubscribersChartCommand extends EnvironmentCommand {

FILE: apps/api/src/app/activity/usecases/build-active-subscribers-chart/build-active-subscribers-chart.usecase.ts
  class BuildActiveSubscribersChart (line 14) | class BuildActiveSubscribersChart {
    method constructor (line 15) | constructor(
    method execute (line 25) | async execute(command: BuildActiveSubscribersChartCommand): Promise<Ac...

FILE: apps/api/src/app/activity/usecases/build-active-subscribers-trend-chart/build-active-subscribers-trend-chart.command.ts
  class BuildActiveSubscribersTrendChartCommand (line 4) | class BuildActiveSubscribersTrendChartCommand extends EnvironmentCommand {

FILE: apps/api/src/app/activity/usecases/build-active-subscribers-trend-chart/build-active-subscribers-trend-chart.usecase.ts
  class BuildActiveSubscribersTrendChart (line 14) | class BuildActiveSubscribersTrendChart {
    method constructor (line 15) | constructor(
    method execute (line 25) | async execute(command: BuildActiveSubscribersTrendChartCommand): Promi...

FILE: apps/api/src/app/activity/usecases/build-avg-messages-per-subscriber-chart/build-avg-messages-per-subscriber-chart.command.ts
  class BuildAvgMessagesPerSubscriberChartCommand (line 4) | class BuildAvgMessagesPerSubscriberChartCommand extends EnvironmentComma...

FILE: apps/api/src/app/activity/usecases/build-avg-messages-per-subscriber-chart/build-avg-messages-per-subscriber-chart.usecase.ts
  class BuildAvgMessagesPerSubscriberChart (line 14) | class BuildAvgMessagesPerSubscriberChart {
    method constructor (line 15) | constructor(
    method execute (line 25) | async execute(command: BuildAvgMessagesPerSubscriberChartCommand): Pro...

FILE: apps/api/src/app/activity/usecases/build-delivery-trend-chart/build-delivery-trend-chart.command.ts
  class BuildDeliveryTrendChartCommand (line 4) | class BuildDeliveryTrendChartCommand extends EnvironmentCommand {

FILE: apps/api/src/app/activity/usecases/build-delivery-trend-chart/build-delivery-trend-chart.usecase.ts
  class BuildDeliveryTrendChart (line 14) | class BuildDeliveryTrendChart {
    method constructor (line 15) | constructor(
    method execute (line 25) | async execute(command: BuildDeliveryTrendChartCommand): Promise<ChartD...

FILE: apps/api/src/app/activity/usecases/build-interaction-trend-chart/build-interaction-trend-chart.command.ts
  class BuildInteractionTrendChartCommand (line 4) | class BuildInteractionTrendChartCommand extends EnvironmentCommand {

FILE: apps/api/src/app/activity/usecases/build-interaction-trend-chart/build-interaction-trend-chart.usecase.ts
  class BuildInteractionTrendChart (line 14) | class BuildInteractionTrendChart {
    method constructor (line 15) | constructor(
    method execute (line 25) | async execute(command: BuildInteractionTrendChartCommand): Promise<Int...

FILE: apps/api/src/app/activity/usecases/build-messages-delivered-chart/build-messages-delivered-chart.command.ts
  class BuildMessagesDeliveredChartCommand (line 4) | class BuildMessagesDeliveredChartCommand extends EnvironmentCommand {

FILE: apps/api/src/app/activity/usecases/build-messages-delivered-chart/build-messages-delivered-chart.usecase.ts
  class BuildMessagesDeliveredChart (line 14) | class BuildMessagesDeliveredChart {
    method constructor (line 15) | constructor(
    method execute (line 25) | async execute(command: BuildMessagesDeliveredChartCommand): Promise<Me...

FILE: apps/api/src/app/activity/usecases/build-provider-by-volume-chart/build-provider-by-volume-chart.command.ts
  class BuildProviderByVolumeChartCommand (line 4) | class BuildProviderByVolumeChartCommand extends EnvironmentCommand {

FILE: apps/api/src/app/activity/usecases/build-provider-by-volume-chart/build-provider-by-volume-chart.usecase.ts
  class BuildProviderByVolumeChart (line 14) | class BuildProviderByVolumeChart {
    method constructor (line 15) | constructor(
    method execute (line 25) | async execute(command: BuildProviderByVolumeChartCommand): Promise<Pro...

FILE: apps/api/src/app/activity/usecases/build-total-interactions-chart/build-total-interactions-chart.command.ts
  class BuildTotalInteractionsChartCommand (line 4) | class BuildTotalInteractionsChartCommand extends EnvironmentCommand {

FILE: apps/api/src/app/activity/usecases/build-total-interactions-chart/build-total-interactions-chart.usecase.ts
  class BuildTotalInteractionsChart (line 14) | class BuildTotalInteractionsChart {
    method constructor (line 15) | constructor(
    method execute (line 25) | async execute(command: BuildTotalInteractionsChartCommand): Promise<To...

FILE: apps/api/src/app/activity/usecases/build-workflow-by-volume-chart/build-workflow-by-volume-chart.command.ts
  class BuildWorkflowByVolumeChartCommand (line 4) | class BuildWorkflowByVolumeChartCommand extends EnvironmentCommand {

FILE: apps/api/src/app/activity/usecases/build-workflow-by-volume-chart/build-workflow-by-volume-chart.usecase.ts
  class BuildWorkflowByVolumeChart (line 15) | class BuildWorkflowByVolumeChart {
    method constructor (line 16) | constructor(
    method execute (line 27) | async execute(command: BuildWorkflowByVolumeChartCommand): Promise<Wor...
    method buildChartFromWorkflowRunCount (line 44) | private async buildChartFromWorkflowRunCount(
    method buildChartFromWorkflowRuns (line 83) | private async buildChartFromWorkflowRuns(

FILE: apps/api/src/app/activity/usecases/build-workflow-runs-count-chart/build-workflow-runs-count-chart.command.ts
  class BuildWorkflowRunsCountChartCommand (line 5) | class BuildWorkflowRunsCountChartCommand extends EnvironmentCommand {

FILE: apps/api/src/app/activity/usecases/build-workflow-runs-count-chart/build-workflow-runs-count-chart.usecase.ts
  class BuildWorkflowRunsCountChart (line 15) | class BuildWorkflowRunsCountChart {
    method constructor (line 16) | constructor(
    method execute (line 24) | async execute(command: BuildWorkflowRunsCountChartCommand): Promise<Wo...

FILE: apps/api/src/app/activity/usecases/build-workflow-runs-metric-chart/build-workflow-runs-metric-chart.command.ts
  class BuildWorkflowRunsMetricChartCommand (line 4) | class BuildWorkflowRunsMetricChartCommand extends EnvironmentCommand {

FILE: apps/api/src/app/activity/usecases/build-workflow-runs-metric-chart/build-workflow-runs-metric-chart.usecase.ts
  class BuildWorkflowRunsMetricChart (line 7) | class BuildWorkflowRunsMetricChart {
    method constructor (line 8) | constructor(
    method execute (line 16) | async execute(command: BuildWorkflowRunsMetricChartCommand): Promise<W...

FILE: apps/api/src/app/activity/usecases/build-workflow-runs-trend-chart/build-workflow-runs-trend-chart.command.ts
  class BuildWorkflowRunsTrendChartCommand (line 4) | class BuildWorkflowRunsTrendChartCommand extends EnvironmentCommand {

FILE: apps/api/src/app/activity/usecases/build-workflow-runs-trend-chart/build-workflow-runs-trend-chart.usecase.ts
  class BuildWorkflowRunsTrendChart (line 14) | class BuildWorkflowRunsTrendChart {
    method constructor (line 15) | constructor(
    method execute (line 25) | async execute(command: BuildWorkflowRunsTrendChartCommand): Promise<Wo...
    method buildChartFromWorkflowRunCount (line 42) | private async buildChartFromWorkflowRunCount(
    method buildChartFromWorkflowRuns (line 94) | private async buildChartFromWorkflowRuns(

FILE: apps/api/src/app/activity/usecases/get-charts/get-charts.command.ts
  class GetChartsCommand (line 5) | class GetChartsCommand extends EnvironmentCommand {

FILE: apps/api/src/app/activity/usecases/get-charts/get-charts.usecase.ts
  class GetCharts (line 40) | class GetCharts {
    method constructor (line 41) | constructor(
    method execute (line 60) | async execute(command: GetChartsCommand): Promise<GetChartsResponseDto> {
    method validateRetentionLimitForTier (line 319) | private async validateRetentionLimitForTier(organizationId: string, cr...
    method validateDateRange (line 342) | private validateDateRange(earliestAllowedDate: Date, startDate: Date, ...
    method getMaxRetentionPeriodByOrganization (line 371) | private getMaxRetentionPeriodByOrganization(organization: Organization...

FILE: apps/api/src/app/activity/usecases/get-request/get-request.command.ts
  class GetRequestCommand (line 4) | class GetRequestCommand extends EnvironmentCommand {

FILE: apps/api/src/app/activity/usecases/get-request/get-request.usecase.ts
  class GetRequest (line 9) | class GetRequest {
    method constructor (line 10) | constructor(
    method execute (line 15) | async execute(command: GetRequestCommand): Promise<GetRequestResponseD...

FILE: apps/api/src/app/activity/usecases/get-requests/get-requests.command.ts
  class GetRequestsCommand (line 4) | class GetRequestsCommand extends EnvironmentCommand {

FILE: apps/api/src/app/activity/usecases/get-requests/get-requests.usecase.ts
  class GetRequests (line 8) | class GetRequests {
    method constructor (line 9) | constructor(private readonly requestLogRepository: RequestLogRepositor...
    method execute (line 11) | async execute(command: GetRequestsCommand): Promise<GetRequestsRespons...

FILE: apps/api/src/app/activity/usecases/get-workflow-run/get-workflow-run.command.ts
  class GetWorkflowRunCommand (line 4) | class GetWorkflowRunCommand extends EnvironmentWithUserCommand {

FILE: apps/api/src/app/activity/usecases/get-workflow-run/get-workflow-run.usecase.ts
  type WorkflowRunFetchResult (line 43) | type WorkflowRunFetchResult = Pick<WorkflowRun, (typeof workflowRunSelec...
  type StepRunFetchResult (line 64) | type StepRunFetchResult = Pick<StepRun, (typeof stepRunSelectColumns)[nu...
  type TraceFetchResult (line 67) | type TraceFetchResult = Pick<Trace, (typeof traceSelectColumns)[number]>;
  type IStepRunWithDetails (line 69) | interface IStepRunWithDetails extends StepRunFetchResult {
  class GetWorkflowRun (line 74) | class GetWorkflowRun {
    method constructor (line 75) | constructor(
    method execute (line 85) | async execute(command: GetWorkflowRunCommand): Promise<GetWorkflowRunR...
    method getJobDigestDataByTransactionId (line 141) | private async getJobDigestDataByTransactionId(
    method getOverridesByTransactionId (line 176) | private async getOverridesByTransactionId(
    method getStepRunsForWorkflowRun (line 199) | private async getStepRunsForWorkflowRun(
    method getExecutionDetailsByEntityId (line 260) | private async getExecutionDetailsByEntityId(
    method mapStepRunToDto (line 310) | private mapStepRunToDto(stepRun: IStepRunWithDetails): StepRunDto {
    method mapWorkflowRunToDto (line 325) | private mapWorkflowRunToDto(

FILE: apps/api/src/app/activity/usecases/get-workflow-runs/get-workflow-runs.command.ts
  class GetWorkflowRunsCommand (line 6) | class GetWorkflowRunsCommand extends EnvironmentWithUserCommand {

FILE: apps/api/src/app/activity/usecases/get-workflow-runs/get-workflow-runs.usecase.ts
  type CursorData (line 21) | type CursorData = {
  type WorkflowRunFetchResult (line 44) | type WorkflowRunFetchResult = Pick<WorkflowRun, (typeof workflowRunSelec...
  type StepRunFetchResult (line 61) | type StepRunFetchResult = Pick<StepRun, (typeof stepRunSelectColumns)[nu...
  class GetWorkflowRuns (line 64) | class GetWorkflowRuns {
    method constructor (line 65) | constructor(
    method execute (line 74) | async execute(command: GetWorkflowRunsCommand): Promise<GetWorkflowRun...
    method generatePreviousCursor (line 272) | private async generatePreviousCursor(
    method encodeCursor (line 334) | private encodeCursor(data: CursorData): string {
    method decodeCursor (line 338) | private decodeCursor(cursor: string): CursorData {
    method parseClickHouseTimestamp (line 351) | private parseClickHouseTimestamp(timestamp: string | Date): Date {
    method getStepRunsForWorkflowRuns (line 370) | private async getStepRunsForWorkflowRuns(
    method mapWorkflowRunToDto (line 421) | private mapWorkflowRunToDto(workflowRun: WorkflowRunFetchResult, stepR...

FILE: apps/api/src/app/analytics/analytics.controller.ts
  class AnalyticsController (line 16) | class AnalyticsController {
    method constructor (line 17) | constructor(
    method trackEvent (line 25) | async trackEvent(@Body('event') event, @Body('data') data = {}, @UserS...
    method identifyUser (line 40) | async identifyUser(@Body() body: any, @UserSession() user: UserSession...

FILE: apps/api/src/app/analytics/analytics.module.ts
  class AnalyticsModule (line 12) | class AnalyticsModule {}

FILE: apps/api/src/app/analytics/usecases/hubspot-identify-form/hubspot-identify-form.command.ts
  class HubspotIdentifyFormCommand (line 4) | class HubspotIdentifyFormCommand extends BaseCommand {

FILE: apps/api/src/app/analytics/usecases/hubspot-identify-form/hubspot-identify-form.usecase.ts
  constant LOG_CONTEXT (line 7) | const LOG_CONTEXT = 'HubspotIdentifyFormUsecase';
  class HubspotIdentifyFormUsecase (line 10) | class HubspotIdentifyFormUsecase {
    method constructor (line 14) | constructor(
    method execute (line 21) | async execute(command: HubspotIdentifyFormCommand) {

FILE: apps/api/src/app/auth/auth.controller.ts
  class AuthController (line 50) | class AuthController {
    method constructor (line 51) | constructor(
    method githubAuth (line 67) | githubAuth() {
    method githubCallback (line 85) | async githubCallback(@Req() request, @Res() response) {
    method refreshToken (line 94) | refreshToken(@UserSession() user: UserSessionData) {
    method userRegistration (line 102) | async userRegistration(@Body() body: UserRegistrationBodyDto) {
    method forgotPasswordRequest (line 120) | async forgotPasswordRequest(@Body() body: PasswordResetRequestBodyDto,...
    method passwordReset (line 130) | async passwordReset(@Body() body: PasswordResetBodyDto) {
    method userLogin (line 141) | async userLogin(@Body() body: LoginBodyDto) {
    method organizationSwitch (line 154) | async organizationSwitch(@UserSession() user: UserSessionData, @Param(...
    method updatePassword (line 167) | async updatePassword(@UserSession() user: UserSessionData, @Body() bod...
    method authenticateTest (line 181) | async authenticateTest(@Param('userId') userId: string, @Query('organi...

FILE: apps/api/src/app/auth/auth.module.ts
  function getModuleConfig (line 6) | function getModuleConfig(): ModuleMetadata {
  class AuthModule (line 16) | class AuthModule {
    method configure (line 17) | public configure(consumer: MiddlewareConsumer) {

FILE: apps/api/src/app/auth/community.auth.module.config.ts
  constant AUTH_STRATEGIES (line 20) | const AUTH_STRATEGIES: Provider[] = [JwtStrategy, ApiKeyStrategy, JwtSub...
  function getCommunityAuthModuleConfig (line 26) | function getCommunityAuthModuleConfig(): ModuleMetadata {
  function configure (line 76) | function configure(consumer: MiddlewareConsumer) {

FILE: apps/api/src/app/auth/dtos/login.dto.ts
  class LoginBodyDto (line 3) | class LoginBodyDto {

FILE: apps/api/src/app/auth/dtos/password-reset.dto.ts
  class PasswordResetBodyDto (line 4) | class PasswordResetBodyDto {
  class PasswordResetRequestBodyDto (line 21) | class PasswordResetRequestBodyDto {

FILE: apps/api/src/app/auth/dtos/update-password.dto.ts
  class UpdatePasswordBodyDto (line 4) | class UpdatePasswordBodyDto {

FILE: apps/api/src/app/auth/dtos/user-registration.dto.ts
  class UserRegistrationBodyDto (line 4) | class UserRegistrationBodyDto {

FILE: apps/api/src/app/auth/e2e/update-password.e2e.ts
  constant NEW_PASSWORD (line 6) | const NEW_PASSWORD = 'newPassword123@';
  constant PASSWORD_ERROR_MESSAGE (line 7) | const PASSWORD_ERROR_MESSAGE =

FILE: apps/api/src/app/auth/ee.auth.module.config.ts
  function getEEModuleConfig (line 13) | function getEEModuleConfig(): ModuleMetadata {
  function configure (line 39) | function configure(consumer: MiddlewareConsumer) {

FILE: apps/api/src/app/auth/framework/auth.decorator.ts
  function RequireAuthentication (line 7) | function RequireAuthentication() {

FILE: apps/api/src/app/auth/framework/community.user.auth.guard.ts
  class CommunityUserAuthGuard (line 8) | class CommunityUserAuthGuard extends AuthGuard([PassportStrategyEnum.JWT...
    method constructor (line 9) | constructor(
    method getAuthenticateOptions (line 17) | getAuthenticateOptions(context: ExecutionContext): IAuthModuleOptions<...

FILE: apps/api/src/app/auth/framework/root-environment-guard.service.ts
  class RootEnvironmentGuard (line 5) | class RootEnvironmentGuard implements CanActivate {
    method constructor (line 6) | constructor(private authService: AuthService) {}
    method canActivate (line 8) | async canActivate(context: ExecutionContext) {

FILE: apps/api/src/app/auth/services/auth.service.ts
  class AuthService (line 7) | class AuthService implements IAuthService {
    method constructor (line 8) | constructor(@Inject('AUTH_SERVICE') private authService: IAuthService) {}
    method authenticate (line 10) | authenticate(
    method refreshToken (line 27) | refreshToken(userId: string): Promise<string> {
    method isAuthenticatedForOrganization (line 31) | isAuthenticatedForOrganization(userId: string, organizationId: string)...
    method getUserByApiKey (line 35) | getUserByApiKey(apiKey: string): Promise<UserSessionData> {
    method getSubscriberWidgetToken (line 39) | getSubscriberWidgetToken(subscriber: SubscriberEntity, contextKeys: st...
    method generateUserToken (line 43) | generateUserToken(user: UserEntity): Promise<string> {
    method getSignedToken (line 47) | getSignedToken(
    method validateUser (line 56) | validateUser(payload: UserSessionData): Promise<UserEntity> {
    method validateSubscriber (line 60) | validateSubscriber(payload: ISubscriberJwt): Promise<SubscriberEntity ...
    method isRootEnvironment (line 64) | isRootEnvironment(payload: UserSessionData): Promise<boolean> {

FILE: apps/api/src/app/auth/services/community.auth.service.ts
  class CommunityAuthService (line 47) | class CommunityAuthService implements IAuthService {
    method constructor (line 48) | constructor(
    method authenticate (line 61) | public async authenticate(
    method updateUserUsername (line 127) | private async updateUserUsername(
    method refreshToken (line 163) | public async refreshToken(userId: string) {
    method isAuthenticatedForOrganization (line 171) | public async isAuthenticatedForOrganization(userId: string, organizati...
    method getUserByApiKey (line 176) | public async getUserByApiKey(apiKey: string): Promise<UserSessionData> {
    method getSubscriberWidgetToken (line 199) | public async getSubscriberWidgetToken(subscriber: SubscriberEntity, co...
    method generateUserToken (line 219) | public async generateUserToken(user: UserEntity) {
    method getSignedToken (line 236) | public async getSignedToken(
    method validateUser (line 270) | public async validateUser(payload: UserSessionData): Promise<UserEntit...
    method validateSubscriber (line 287) | public async validateSubscriber(payload: ISubscriberJwt): Promise<Subs...
    method isRootEnvironment (line 294) | public async isRootEnvironment(payload: UserSessionData): Promise<bool...
    method getUser (line 310) | private async getUser({ _id }: { _id: string }) {
    method getSubscriber (line 321) | private async getSubscriber({
    method getApiKeyUser (line 331) | private async getApiKeyUser({ apiKey }: { apiKey: string }): Promise<{

FILE: apps/api/src/app/auth/services/passport/apikey.strategy.ts
  class ApiKeyStrategy (line 16) | class ApiKeyStrategy extends PassportStrategy(HeaderAPIKeyStrategy) {
    method constructor (line 17) | constructor(
    method validateApiKey (line 43) | private async validateApiKey(apiKey: string): Promise<UserSessionData ...
    method checkKillSwitch (line 62) | private async checkKillSwitch(user: UserSessionData): Promise<void> {

FILE: apps/api/src/app/auth/services/passport/github.strategy.ts
  class GitHubStrategy (line 9) | class GitHubStrategy extends PassportStrategy(githubPassport.Strategy, '...
    method constructor (line 10) | constructor(private authService: AuthService) {
    method validate (line 28) | async validate(req, accessToken: string, refreshToken: string, githubP...
    method parseState (line 51) | private parseState(req) {

FILE: apps/api/src/app/auth/services/passport/jwt.strategy.ts
  class JwtStrategy (line 12) | class JwtStrategy extends PassportStrategy(Strategy) {
    method constructor (line 13) | constructor(
    method validate (line 24) | async validate(req: http.IncomingMessage, session: UserSessionData) {
    method resolveEnvironmentId (line 57) | resolveEnvironmentId(req: http.IncomingMessage, session: UserSessionDa...

FILE: apps/api/src/app/auth/services/passport/newrelic.util.ts
  function addNewRelicTraceAttributes (line 11) | function addNewRelicTraceAttributes(session: UserSessionData) {

FILE: apps/api/src/app/auth/services/passport/subscriber-jwt.strategy.ts
  class JwtSubscriberStrategy (line 9) | class JwtSubscriberStrategy extends PassportStrategy(Strategy, 'subscrib...
    method constructor (line 10) | constructor(private readonly authService: AuthService) {
    method validate (line 17) | async validate(payload: ISubscriberJwt): Promise<SubscriberSession> {

FILE: apps/api/src/app/auth/usecases/index.ts
  constant USE_CASES (line 12) | const USE_CASES = [

FILE: apps/api/src/app/auth/usecases/login/login.command.ts
  class LoginCommand (line 4) | class LoginCommand extends BaseCommand {

FILE: apps/api/src/app/auth/usecases/login/login.usecase.ts
  class Login (line 11) | class Login {
    method constructor (line 14) | constructor(
    method execute (line 21) | async execute(command: LoginCommand) {
    method isAccountBlocked (line 83) | private isAccountBlocked(user: UserEntity) {
    method updateFailedAttempts (line 94) | private async updateFailedAttempts(user: UserEntity) {
    method resetFailedAttempts (line 121) | private async resetFailedAttempts(user: UserEntity) {
    method getTimeDiffForAttempt (line 134) | private getTimeDiffForAttempt(lastFailedAttempt: string) {
    method getBlockedMinutesLeft (line 142) | private getBlockedMinutesLeft(lastFailedAttempt: string) {

FILE: apps/api/src/app/auth/usecases/password-reset-request/password-reset-request.command.ts
  class PasswordResetRequestCommand (line 5) | class PasswordResetRequestCommand extends BaseCommand {

FILE: apps/api/src/app/auth/usecases/password-reset-request/password-reset-request.usecase.ts
  class PasswordResetRequest (line 10) | class PasswordResetRequest {
    method constructor (line 15) | constructor(
    method execute (line 20) | async execute(command: PasswordResetRequestCommand): Promise<{ success...
    method getResetRedirectLink (line 49) | private static getResetRedirectLink(token: string, user: UserEntity, s...
    method isRequestBlocked (line 64) | private isRequestBlocked(user: UserEntity) {
    method getUpdatedRequestCount (line 105) | private getUpdatedRequestCount(user: UserEntity): IUserResetTokenCount {

FILE: apps/api/src/app/auth/usecases/password-reset/password-reset.command.ts
  class PasswordResetCommand (line 4) | class PasswordResetCommand extends BaseCommand {

FILE: apps/api/src/app/auth/usecases/password-reset/password-reset.usecase.ts
  class PasswordReset (line 10) | class PasswordReset {
    method constructor (line 11) | constructor(
    method execute (line 17) | async execute(command: PasswordResetCommand): Promise<{ token: string ...

FILE: apps/api/src/app/auth/usecases/register/user-register.command.ts
  class UserRegisterCommand (line 6) | class UserRegisterCommand extends BaseCommand {

FILE: apps/api/src/app/auth/usecases/register/user-register.usecase.ts
  class UserRegister (line 12) | class UserRegister {
    method constructor (line 13) | constructor(
    method execute (line 20) | async execute(command: UserRegisterCommand) {

FILE: apps/api/src/app/auth/usecases/switch-environment/switch-environment.command.ts
  class SwitchEnvironmentCommand (line 4) | class SwitchEnvironmentCommand extends OrganizationCommand {

FILE: apps/api/src/app/auth/usecases/switch-environment/switch-environment.usecase.ts
  class SwitchEnvironment (line 7) | class SwitchEnvironment {
    method constructor (line 8) | constructor(
    method execute (line 15) | async execute(command: SwitchEnvironmentCommand) {

FILE: apps/api/src/app/auth/usecases/switch-organization/switch-organization.command.ts
  class SwitchOrganizationCommand (line 4) | class SwitchOrganizationCommand extends AuthenticatedCommand {

FILE: apps/api/src/app/auth/usecases/switch-organization/switch-organization.usecase.ts
  class SwitchOrganization (line 7) | class SwitchOrganization {
    method constructor (line 8) | constructor(
    method execute (line 14) | async execute(command: SwitchOrganizationCommand) {

FILE: apps/api/src/app/auth/usecases/update-password/update-password.command.ts
  class UpdatePasswordCommand (line 5) | class UpdatePasswordCommand extends EnvironmentWithUserCommand {

FILE: apps/api/src/app/auth/usecases/update-password/update-password.usecase.ts
  class UpdatePassword (line 9) | class UpdatePassword {
    method constructor (line 10) | constructor(
    method execute (line 15) | async execute(command: UpdatePasswordCommand) {
    method setNewPassword (line 43) | private async setNewPassword(userId: string, newPassword: string) {

FILE: apps/api/src/app/billing/e2e/get-subscription.e2e-ee.ts
  type DeepPartial (line 7) | type DeepPartial<T> = T extends object ? { [P in keyof T]?: DeepPartial<...

FILE: apps/api/src/app/blueprint/blueprint.controller.ts
  class BlueprintController (line 16) | class BlueprintController {
    method constructor (line 17) | constructor(
    method getGroupedBlueprints (line 24) | async getGroupedBlueprints(): Promise<GroupedBlueprintResponse> {
    method getProdEnvironmentId (line 32) | private async getProdEnvironmentId() {
    method getBlueprintById (line 47) | getBlueprintById(@Param('templateIdOrIdentifier') templateIdOrIdentifi...

FILE: apps/api/src/app/blueprint/blueprint.module.ts
  class BlueprintModule (line 14) | class BlueprintModule implements NestModule {
    method configure (line 15) | configure(consumer: MiddlewareConsumer): MiddlewareConsumer | void {}

FILE: apps/api/src/app/blueprint/dtos/get-blueprint.response.dto.ts
  class GetBlueprintResponse (line 3) | class GetBlueprintResponse {

FILE: apps/api/src/app/blueprint/dtos/grouped-blueprint.response.dto.ts
  class GroupedBlueprintResponse (line 3) | class GroupedBlueprintResponse {

FILE: apps/api/src/app/blueprint/e2e/get-blueprints-by-id.e2e.ts
  function getProductionEnvironment (line 86) | async function getProductionEnvironment() {
  function createTemplateFromBlueprint (line 93) | async function createTemplateFromBlueprint({

FILE: apps/api/src/app/blueprint/e2e/get-grouped-blueprints.e2e.ts
  function updateBlueprintCategory (line 141) | async function updateBlueprintCategory({ categoryName }: { categoryName:...
  function getProductionEnvironment (line 155) | async function getProductionEnvironment() {
  function createTemplateFromBlueprint (line 162) | async function createTemplateFromBlueprint({

FILE: apps/api/src/app/blueprint/usecases/get-blueprint/get-blueprint.command.ts
  class GetBlueprintCommand (line 4) | class GetBlueprintCommand extends BaseCommand {

FILE: apps/api/src/app/blueprint/usecases/get-blueprint/get-blueprint.usecase.ts
  class GetBlueprint (line 8) | class GetBlueprint {
    method constructor (line 9) | constructor(private notificationTemplateRepository: NotificationTempla...
    method execute (line 11) | async execute(command: GetBlueprintCommand): Promise<GetBlueprintRespo...

FILE: apps/api/src/app/blueprint/usecases/get-grouped-blueprints/consts.ts
  constant POPULAR_GROUPED_NAME (line 3) | const POPULAR_GROUPED_NAME = 'Popular';
  constant POPULAR_TEMPLATES_ID_LIST (line 4) | const POPULAR_TEMPLATES_ID_LIST = getPopularTemplateIds({ production: pr...

FILE: apps/api/src/app/blueprint/usecases/get-grouped-blueprints/get-grouped-blueprints.command.ts
  class GetGroupedBlueprintsCommand (line 3) | class GetGroupedBlueprintsCommand extends EnvironmentLevelCommand {}

FILE: apps/api/src/app/blueprint/usecases/get-grouped-blueprints/get-grouped-blueprints.usecase.ts
  constant WEEK_IN_SECONDS (line 9) | const WEEK_IN_SECONDS = 60 * 60 * 24 * 7;
  class GetGroupedBlueprints (line 12) | class GetGroupedBlueprints {
    method constructor (line 13) | constructor(
    method execute (line 20) | async execute(command: GetGroupedBlueprintsCommand): Promise<GroupedBl...
    method fetchGroupedBlueprints (line 33) | private async fetchGroupedBlueprints() {
    method groupedToBlueprintsArray (line 44) | private groupedToBlueprintsArray(groups: { name: string; blueprints: N...
    method getPopularGroupBlueprints (line 48) | private getPopularGroupBlueprints(

FILE: apps/api/src/app/blueprint/usecases/index.ts
  constant USE_CASES (line 4) | const USE_CASES = [

FILE: apps/api/src/app/bridge/bridge.controller.ts
  class BridgeController (line 50) | class BridgeController {
    method constructor (line 51) | constructor(
    method health (line 64) | async health(@UserSession() user: UserSessionData) {
    method preview (line 74) | async preview(
    method createBridgesByDiscovery (line 97) | async createBridgesByDiscovery(
    method createDiscoverySoft (line 122) | async createDiscoverySoft(
    method getControls (line 164) | async getControls(
    method createControls (line 198) | async createControls(
    method validateBridgeUrl (line 219) | async validateBridgeUrl(

FILE: apps/api/src/app/bridge/bridge.module.ts
  constant PROVIDERS (line 29) | const PROVIDERS = [
  constant MODULES (line 54) | const MODULES = [SharedModule, OutboundWebhooksModule.forRoot()];
  class BridgeModule (line 62) | class BridgeModule implements NestModule {
    method configure (line 63) | public configure(consumer: MiddlewareConsumer) {}

FILE: apps/api/src/app/bridge/dtos/create-bridge-request.dto.ts
  class CreateBridgeRequestDto (line 4) | class CreateBridgeRequestDto implements ICreateBridges {

FILE: apps/api/src/app/bridge/dtos/create-bridge-response.dto.ts
  class CreateBridgeResponseDto (line 1) | class CreateBridgeResponseDto {}

FILE: apps/api/src/app/bridge/dtos/validate-bridge-url-request.dto.ts
  class ValidateBridgeUrlRequestDto (line 4) | class ValidateBridgeUrlRequestDto {

FILE: apps/api/src/app/bridge/dtos/validate-bridge-url-response.dto.ts
  class ValidateBridgeUrlResponseDto (line 3) | class ValidateBridgeUrlResponseDto {

FILE: apps/api/src/app/bridge/usecases/get-bridge-status/get-bridge-status.command.ts
  class GetBridgeStatusCommand (line 3) | class GetBridgeStatusCommand extends EnvironmentLevelCommand {

FILE: apps/api/src/app/bridge/usecases/get-bridge-status/get-bridge-status.usecase.ts
  class GetBridgeStatus (line 8) | class GetBridgeStatus {
    method constructor (line 9) | constructor(private executeBridgeRequest: ExecuteBridgeRequest) {}
    method execute (line 11) | async execute(command: GetBridgeStatusCommand): Promise<HealthCheck> {

FILE: apps/api/src/app/bridge/usecases/index.ts
  constant USECASES (line 6) | const USECASES = [GetBridgeStatus, PreviewStep, StoreControlValuesUseCas...

FILE: apps/api/src/app/bridge/usecases/store-control-values/store-control-values.command.ts
  class StoreControlValuesCommand (line 3) | class StoreControlValuesCommand extends EnvironmentWithUserCommand {

FILE: apps/api/src/app/bridge/usecases/store-control-values/store-control-values.usecase.ts
  class StoreControlValuesUseCase (line 8) | class StoreControlValuesUseCase {
    method constructor (line 9) | constructor(
    method execute (line 14) | async execute(command: StoreControlValuesCommand) {

FILE: apps/api/src/app/bridge/usecases/sync/sync.command.ts
  type IStepOutput (line 6) | interface IStepOutput {
  type IWorkflowDefineStep (line 10) | interface IWorkflowDefineStep {
  type IStepDefineOptions (line 26) | interface IStepDefineOptions {
  class WorkflowDefineStep (line 33) | class WorkflowDefineStep implements IWorkflowDefineStep {
  type IWorkflowDefine (line 50) | interface IWorkflowDefine {
  class WorkflowDefine (line 60) | class WorkflowDefine implements IWorkflowDefine {
  type ICreateBridges (line 73) | interface ICreateBridges {
  class SyncCommand (line 77) | class SyncCommand extends EnvironmentWithUserCommand implements ICreateB...

FILE: apps/api/src/app/bridge/usecases/sync/sync.usecase.ts
  class Sync (line 43) | class Sync {
    method constructor (line 44) | constructor(
    method execute (line 56) | async execute(command: SyncCommand): Promise<CreateBridgeResponseDto> {
    method sendAnalytics (line 68) | private sendAnalytics(command: SyncCommand, environment: EnvironmentEn...
    method executeDiscover (line 81) | private async executeDiscover(command: SyncCommand): Promise<DiscoverO...
    method findEnvironment (line 106) | private async findEnvironment(command: SyncCommand): Promise<Environme...
    method updateBridgeUrl (line 116) | private async updateBridgeUrl(command: SyncCommand): Promise<void> {
    method disposeOldWorkflows (line 132) | private async disposeOldWorkflows(
    method findAllWorkflowsWithOtherIds (line 152) | private async findAllWorkflowsWithOtherIds(
    method processWorkflows (line 168) | private async processWorkflows(
    method upsertWorkflow (line 199) | private async upsertWorkflow(
    method createWorkflow (line 215) | private async createWorkflow(
    method mapDiscoverWorkflowToUpdateWorkflowCommand (line 259) | private async mapDiscoverWorkflowToUpdateWorkflowCommand(
    method mapSteps (line 290) | private async mapSteps(
    method getNotificationGroup (line 347) | private async getNotificationGroup(
    method getWorkflowPreferences (line 368) | private getWorkflowPreferences(workflow: DiscoverWorkflowOutput): Work...
    method getWorkflowName (line 372) | private getWorkflowName(workflow: DiscoverWorkflowOutput): string {
    method getWorkflowDescription (line 376) | private getWorkflowDescription(workflow: DiscoverWorkflowOutput): stri...
    method getWorkflowTags (line 380) | private getWorkflowTags(workflow: DiscoverWorkflowOutput): string[] {
    method buildRawData (line 384) | private buildRawData(workflow: DiscoverWorkflowOutput): Record<string,...
    method castToAnyNotSupportedParam (line 400) | private castToAnyNotSupportedParam(param: any): any {

FILE: apps/api/src/app/change/change.module.ts
  class ChangeModule (line 39) | class ChangeModule implements NestModule {
    method configure (line 40) | configure(consumer: MiddlewareConsumer): MiddlewareConsumer | void {}

FILE: apps/api/src/app/change/changes.controller.ts
  class ChangesController (line 29) | class ChangesController {
    method constructor (line 30) | constructor(
    method getChanges (line 45) | async getChanges(
    method getChangesCount (line 70) | async getChangesCount(@UserSession() user: UserSessionData): Promise<n...
    method bulkApplyDiff (line 88) | async bulkApplyDiff(
    method applyDiff (line 109) | async applyDiff(

FILE: apps/api/src/app/change/dtos/bulk-apply-change.dto.ts
  class BulkApplyChangeDto (line 3) | class BulkApplyChangeDto {

FILE: apps/api/src/app/change/dtos/change-request.dto.ts
  class ChangesRequestDto (line 5) | class ChangesRequestDto extends PaginationRequestDto(10, 100) {

FILE: apps/api/src/app/change/dtos/change-response.dto.ts
  class ChangeResponseDto (line 4) | class ChangeResponseDto {
  class ChangesResponseDto (line 38) | class ChangesResponseDto {

FILE: apps/api/src/app/change/e2e/promote-changes.e2e.ts
  function getProductionEnvironment (line 757) | async function getProductionEnvironment(): Promise<EnvironmentEntity> {

FILE: apps/api/src/app/change/e2e/promote-layout-changes.e2e.ts
  function createLayout (line 329) | async function createLayout(
  function getProductionEnvironment (line 357) | async function getProductionEnvironment() {

FILE: apps/api/src/app/change/usecases/apply-change/apply-change.command.ts
  class ApplyChangeCommand (line 4) | class ApplyChangeCommand extends EnvironmentWithUserCommand {

FILE: apps/api/src/app/change/usecases/apply-change/apply-change.usecase.ts
  class ApplyChange (line 9) | class ApplyChange {
    method constructor (line 10) | constructor(
    method execute (line 15) | async execute(command: ApplyChangeCommand): Promise<ChangeEntity[]> {
    method applyChange (line 41) | async applyChange(change, command: ApplyChangeCommand): Promise<Change...

FILE: apps/api/src/app/change/usecases/bulk-apply-change/bulk-apply-change.command.ts
  class BulkApplyChangeCommand (line 4) | class BulkApplyChangeCommand extends EnvironmentWithUserCommand {

FILE: apps/api/src/app/change/usecases/bulk-apply-change/bulk-apply-change.usecase.ts
  class BulkApplyChange (line 7) | class BulkApplyChange {
    method constructor (line 8) | constructor(
    method execute (line 13) | async execute(command: BulkApplyChangeCommand): Promise<ChangeEntity[]...

FILE: apps/api/src/app/change/usecases/count-changes/count-changes.command.ts
  class CountChangesCommand (line 3) | class CountChangesCommand extends EnvironmentWithUserCommand {}

FILE: apps/api/src/app/change/usecases/count-changes/count-changes.usecase.ts
  class CountChanges (line 6) | class CountChanges {
    method constructor (line 7) | constructor(private changeRepository: ChangeRepository) {}
    method execute (line 9) | async execute(command: CountChangesCommand): Promise<number> {

FILE: apps/api/src/app/change/usecases/get-changes/get-changes.command.ts
  class GetChangesCommand (line 4) | class GetChangesCommand extends EnvironmentWithUserCommand {

FILE: apps/api/src/app/change/usecases/get-changes/get-changes.usecase.ts
  type IViewEntity (line 18) | interface IViewEntity {
  type IChangeViewEntity (line 24) | interface IChangeViewEntity extends ChangeEntity {
  class GetChanges (line 31) | class GetChanges {
    method constructor (line 32) | constructor(
    method execute (line 45) | async execute(command: GetChangesCommand): Promise<ChangesResponseDto> {
    method getTemplateDataForMessageTemplate (line 93) | private async getTemplateDataForMessageTemplate(
    method getTemplateDataForNotificationTemplate (line 120) | private async getTemplateDataForNotificationTemplate(
    method getTemplateDataForTranslationGroup (line 149) | private async getTemplateDataForTranslationGroup(
    method getTemplateDataForTranslation (line 173) | private async getTemplateDataForTranslation(
    method getTemplateDataForNotificationGroup (line 197) | private async getTemplateDataForNotificationGroup(
    method getTemplateDataForFeed (line 217) | private async getTemplateDataForFeed(
    method getTemplateDataForLayout (line 241) | private async getTemplateDataForLayout(
    method getTemplateDataForDefaultLayout (line 264) | private async getTemplateDataForDefaultLayout(

FILE: apps/api/src/app/change/usecases/index.ts
  constant USE_CASES (line 25) | const USE_CASES = [

FILE: apps/api/src/app/change/usecases/promote-change-to-environment/promote-change-to-environment.command.ts
  class PromoteChangeToEnvironmentCommand (line 5) | class PromoteChangeToEnvironmentCommand extends EnvironmentWithUserComma...

FILE: apps/api/src/app/change/usecases/promote-change-to-environment/promote-change-to-environment.usecase.ts
  function sanitizeDiff (line 16) | function sanitizeDiff(diff: unknown): rdiffResult[] {
  class PromoteChangeToEnvironment (line 23) | class PromoteChangeToEnvironment {
    method constructor (line 24) | constructor(
    method execute (line 40) | async execute(command: PromoteChangeToEnvironmentCommand) {

FILE: apps/api/src/app/change/usecases/promote-feed-change/promote-feed-change.ts
  class PromoteFeedChange (line 6) | class PromoteFeedChange {
    method constructor (line 7) | constructor(private feedRepository: FeedRepository) {}
    method execute (line 9) | async execute(command: PromoteTypeChangeCommand) {

FILE: apps/api/src/app/change/usecases/promote-layout-change/promote-layout-change.use-case.ts
  class PromoteLayoutChange (line 7) | class PromoteLayoutChange {
    method constructor (line 8) | constructor(private layoutRepository: LayoutRepository) {}
    method execute (line 10) | async execute(command: PromoteTypeChangeCommand) {

FILE: apps/api/src/app/change/usecases/promote-message-template-change/promote-message-template-change.ts
  class PromoteMessageTemplateChange (line 6) | class PromoteMessageTemplateChange {
    method constructor (line 7) | constructor(
    method execute (line 13) | async execute(command: PromoteTypeChangeCommand) {

FILE: apps/api/src/app/change/usecases/promote-notification-group-change/promote-notification-group-change.ts
  class PromoteNotificationGroupChange (line 6) | class PromoteNotificationGroupChange {
    method constructor (line 7) | constructor(private notificationGroupRepository: NotificationGroupRepo...
    method execute (line 9) | async execute(command: PromoteTypeChangeCommand) {

FILE: apps/api/src/app/change/usecases/promote-notification-template-change/promote-notification-template-change.usecase.ts
  class PromoteNotificationTemplateChange (line 44) | class PromoteNotificationTemplateChange implements INotificationTemplate...
    method constructor (line 45) | constructor(
    method execute (line 60) | async execute(command: PromoteTypeChangeCommand) {
    method updateWorkflowPreferences (line 243) | private async updateWorkflowPreferences(
    method deleteWorkflowPreferences (line 269) | private async deleteWorkflowPreferences(workflowId: string, command: P...
    method getProductionEnvironmentId (line 291) | private async getProductionEnvironmentId(organizationId: string) {
    method blueprintOrganizationId (line 303) | private get blueprintOrganizationId() {
    method invalidateBlueprints (line 307) | private async invalidateBlueprints(command: PromoteTypeChangeCommand) {

FILE: apps/api/src/app/change/usecases/promote-translation-change/promote-translation-change.usecase.ts
  class PromoteTranslationChange (line 10) | class PromoteTranslationChange {
    method constructor (line 11) | constructor(
    method execute (line 20) | async execute(command: PromoteTypeChangeCommand) {
    method applyGroupChange (line 36) | private async applyGroupChange(command: PromoteTypeChangeCommand) {

FILE: apps/api/src/app/change/usecases/promote-translation-group-change/promote-translation-group-change.usecase.ts
  class PromoteTranslationGroupChange (line 10) | class PromoteTranslationGroupChange {
    method constructor (line 11) | constructor(
    method execute (line 20) | async execute(command: PromoteTypeChangeCommand) {
    method applyDefaultTranslationChange (line 36) | private async applyDefaultTranslationChange(command: PromoteTypeChange...

FILE: apps/api/src/app/change/usecases/shared/notification-template-change.interface.ts
  type INotificationTemplateChangeService (line 3) | interface INotificationTemplateChangeService {

FILE: apps/api/src/app/channel-connections/channel-connections.controller.ts
  class ChannelConnectionsController (line 47) | class ChannelConnectionsController {
    method constructor (line 48) | constructor(
    method checkFeatureEnabled (line 57) | private async checkFeatureEnabled(user: UserSessionData) {
    method listChannelConnections (line 78) | async listChannelConnections(
    method createChannelConnection (line 119) | async createChannelConnection(
    method getChannelConnectionByIdentifier (line 151) | async getChannelConnectionByIdentifier(
    method updateChannelConnection (line 178) | async updateChannelConnection(
    method deleteChannelConnection (line 208) | async deleteChannelConnection(

FILE: apps/api/src/app/channel-connections/channel-connections.module.ts
  constant USE_CASES (line 18) | const USE_CASES = [
  constant DAL_MODELS (line 26) | const DAL_MODELS = [
  class ChannelConnectionsModule (line 40) | class ChannelConnectionsModule {}

FILE: apps/api/src/app/channel-connections/dtos/create-channel-connection-request.dto.ts
  class CreateChannelConnectionRequestDto (line 8) | class CreateChannelConnectionRequestDto {

FILE: apps/api/src/app/channel-connections/dtos/cursor-pagination-query.dto.ts
  class CursorPaginationQueryDto (line 6) | class CursorPaginationQueryDto<T, K extends keyof T> {

FILE: apps/api/src/app/channel-connections/dtos/dto.mapper.ts
  function mapChannelConnectionEntityToDto (line 4) | function mapChannelConnectionEntityToDto(

FILE: apps/api/src/app/channel-connections/dtos/get-channel-connection-response.dto.ts
  class GetChannelConnectionResponseDto (line 5) | class GetChannelConnectionResponseDto {

FILE: apps/api/src/app/channel-connections/dtos/list-channel-connections-query.dto.ts
  class ListChannelConnectionsQueryDto (line 8) | class ListChannelConnectionsQueryDto extends CursorPaginationQueryDto<

FILE: apps/api/src/app/channel-connections/dtos/list-channel-connections-response.dto.ts
  class ListChannelConnectionsResponseDto (line 4) | class ListChannelConnectionsResponseDto extends withCursorPagination(Get...

FILE: apps/api/src/app/channel-connections/dtos/shared.dto.ts
  class WorkspaceDto (line 4) | class WorkspaceDto {
  class AuthDto (line 16) | class AuthDto {

FILE: apps/api/src/app/channel-connections/dtos/update-channel-connection-request.dto.ts
  class UpdateChannelConnectionRequestDto (line 6) | class UpdateChannelConnectionRequestDto {

FILE: apps/api/src/app/channel-connections/e2e/helpers/channel-helpers.ts
  function createSlackIntegration (line 14) | async function createSlackIntegration(session: UserSession) {
  function createSubscribersService (line 26) | function createSubscribersService(session: UserSession) {
  function createConnection (line 30) | async function createConnection(
  function createSlackChannelEndpoint (line 53) | async function createSlackChannelEndpoint(
  function createWebhookEndpoint (line 73) | async function createWebhookEndpoint(
  function setupChannelTests (line 93) | function setupChannelTests(session: UserSession) {

FILE: apps/api/src/app/channel-connections/usecases/create-channel-connection/create-channel-connection.command.ts
  class CreateChannelConnectionCommand (line 8) | class CreateChannelConnectionCommand extends EnvironmentCommand {

FILE: apps/api/src/app/channel-connections/usecases/create-channel-connection/create-channel-connection.usecase.ts
  class CreateChannelConnection (line 14) | class CreateChannelConnection {
    method constructor (line 15) | constructor(
    method execute (line 23) | async execute(command: CreateChannelConnectionCommand): Promise<Channe...
    method validateResourceOrContext (line 52) | private validateResourceOrContext(command: CreateChannelConnectionComm...
    method resolveContexts (line 60) | private async resolveContexts(command: CreateChannelConnectionCommand)...
    method ensureUniqueConnectionForResourceAndContext (line 78) | private async ensureUniqueConnectionForResourceAndContext(
    method createChannelConnection (line 107) | private async createChannelConnection(
    method assertSubscriberExists (line 129) | private async assertSubscriberExists(command: CreateChannelConnectionC...
    method findIntegration (line 145) | private async findIntegration(command: CreateChannelConnectionCommand) {
    method generateIdentifier (line 159) | private generateIdentifier(): string {

FILE: apps/api/src/app/channel-connections/usecases/delete-channel-connection/delete-channel-connection.command.ts
  class DeleteChannelConnectionCommand (line 4) | class DeleteChannelConnectionCommand extends EnvironmentCommand {

FILE: apps/api/src/app/channel-connections/usecases/delete-channel-connection/delete-channel-connection.usecase.ts
  class DeleteChannelConnection (line 7) | class DeleteChannelConnection {
    method constructor (line 8) | constructor(private readonly channelConnectionRepository: ChannelConne...
    method execute (line 11) | async execute(command: DeleteChannelConnectionCommand): Promise<void> {

FILE: apps/api/src/app/channel-connections/usecases/get-channel-connection/get-channel-connection.command.ts
  class GetChannelConnectionCommand (line 4) | class GetChannelConnectionCommand extends EnvironmentCommand {

FILE: apps/api/src/app/channel-connections/usecases/get-channel-connection/get-channel-connection.usecase.ts
  class GetChannelConnection (line 13) | class GetChannelConnection {
    method constructor (line 14) | constructor(private readonly channelConnectionRepository: ChannelConne...
    method execute (line 17) | async execute(command: GetChannelConnectionCommand): Promise<ChannelCo...

FILE: apps/api/src/app/channel-connections/usecases/list-channel-connections/list-channel-connections.command.ts
  class ListChannelConnectionsCommand (line 6) | class ListChannelConnectionsCommand extends CursorBasedPaginatedCommand<

FILE: apps/api/src/app/channel-connections/usecases/list-channel-connections/list-channel-connections.usecase.ts
  class ListChannelConnections (line 14) | class ListChannelConnections {
    method constructor (line 15) | constructor(private readonly channelConnectionRepository: ChannelConne...
    method execute (line 18) | async execute(command: ListChannelConnectionsCommand) {

FILE: apps/api/src/app/channel-connections/usecases/update-channel-connection/update-channel-connection.command.ts
  class UpdateChannelConnectionCommand (line 6) | class UpdateChannelConnectionCommand extends EnvironmentCommand {

FILE: apps/api/src/app/channel-connections/usecases/update-channel-connection/update-channel-connection.usecase.ts
  class UpdateChannelConnection (line 7) | class UpdateChannelConnection {
    method constructor (line 8) | constructor(private readonly channelConnectionRepository: ChannelConne...
    method execute (line 11) | async execute(command: UpdateChannelConnectionCommand): Promise<Channe...
    method updateChannelConnection (line 17) | private async updateChannelConnection(command: UpdateChannelConnection...

FILE: apps/api/src/app/channel-endpoints/channel-endpoints.controller.ts
  class ChannelEndpointsController (line 86) | class ChannelEndpointsController {
    method constructor (line 87) | constructor(
    method checkFeatureEnabled (line 96) | private async checkFeatureEnabled(user: UserSessionData) {
    method listChannelEndpoints (line 117) | async listChannelEndpoints(
    method getChannelEndpoint (line 160) | async getChannelEndpoint(
    method createChannelEndpoint (line 210) | async createChannelEndpoint(
    method updateChannelEndpoint (line 243) | async updateChannelEndpoint(
    method deleteChannelEndpoint (line 272) | async deleteChannelEndpoint(

FILE: apps/api/src/app/channel-endpoints/channel-endpoints.module.ts
  constant USE_CASES (line 19) | const USE_CASES = [
  constant DAL_MODELS (line 27) | const DAL_MODELS = [
  class ChannelEndpointsModule (line 42) | class ChannelEndpointsModule {}

FILE: apps/api/src/app/channel-endpoints/dtos/create-channel-endpoint-request.dto.ts
  type CreateChannelEndpointRequest (line 10) | type CreateChannelEndpointRequest =

FILE: apps/api/src/app/channel-endpoints/dtos/create-channel-endpoint-variants.dto.ts
  class CreateChannelEndpointBaseDto (line 15) | class CreateChannelEndpointBaseDto {
  class CreateSlackChannelEndpointDto (line 59) | class CreateSlackChannelEndpointDto extends CreateChannelEndpointBaseDto {
  class CreateSlackUserEndpointDto (line 79) | class CreateSlackUserEndpointDto extends CreateChannelEndpointBaseDto {
  class CreateWebhookEndpointDto (line 99) | class CreateWebhookEndpointDto extends CreateChannelEndpointBaseDto {
  class CreatePhoneEndpointDto (line 119) | class CreatePhoneEndpointDto extends CreateChannelEndpointBaseDto {
  class CreateMsTeamsChannelEndpointDto (line 139) | class CreateMsTeamsChannelEndpointDto extends CreateChannelEndpointBaseD...
  class CreateMsTeamsUserEndpointDto (line 159) | class CreateMsTeamsUserEndpointDto extends CreateChannelEndpointBaseDto {

FILE: apps/api/src/app/channel-endpoints/dtos/cursor-pagination-query.dto.ts
  class CursorPaginationQueryDto (line 6) | class CursorPaginationQueryDto<T, K extends keyof T> {

FILE: apps/api/src/app/channel-endpoints/dtos/dto.mapper.ts
  function mapChannelEndpointEntityToDto (line 4) | function mapChannelEndpointEntityToDto(channelEndpoint: ChannelEndpointE...

FILE: apps/api/src/app/channel-endpoints/dtos/endpoint-types.dto.ts
  class SlackChannelEndpointDto (line 4) | class SlackChannelEndpointDto {
  class SlackUserEndpointDto (line 14) | class SlackUserEndpointDto {
  class WebhookEndpointDto (line 24) | class WebhookEndpointDto {
  class PhoneEndpointDto (line 41) | class PhoneEndpointDto {
  class MsTeamsChannelEndpointDto (line 51) | class MsTeamsChannelEndpointDto {
  class MsTeamsUserEndpointDto (line 69) | class MsTeamsUserEndpointDto {

FILE: apps/api/src/app/channel-endpoints/dtos/get-channel-endpoint-response.dto.ts
  class GetChannelEndpointResponseDto (line 16) | class GetChannelEndpointResponseDto {

FILE: apps/api/src/app/channel-endpoints/dtos/list-channel-endpoints-query.dto.ts
  class ListChannelEndpointsQueryDto (line 8) | class ListChannelEndpointsQueryDto extends CursorPaginationQueryDto<

FILE: apps/api/src/app/channel-endpoints/dtos/list-channel-endpoints-response.dto.ts
  class ListChannelEndpointsResponseDto (line 4) | class ListChannelEndpointsResponseDto extends withCursorPagination(GetCh...

FILE: apps/api/src/app/channel-endpoints/dtos/update-channel-endpoint-request.dto.ts
  class UpdateChannelEndpointRequestDto (line 10) | class UpdateChannelEndpointRequestDto {

FILE: apps/api/src/app/channel-endpoints/usecases/create-channel-endpoint/create-channel-endpoint.command.ts
  class CreateChannelEndpointCommand (line 8) | class CreateChannelEndpointCommand<
    method create (line 39) | static create<T extends ChannelEndpointType>(data: {

FILE: apps/api/src/app/channel-endpoints/usecases/create-channel-endpoint/create-channel-endpoint.usecase.ts
  class CreateChannelEndpoint (line 17) | class CreateChannelEndpoint {
    method constructor (line 18) | constructor(
    method execute (line 27) | async execute(command: CreateChannelEndpointCommand): Promise<ChannelE...
    method resolveContexts (line 59) | private async resolveContexts(command: CreateChannelEndpointCommand<Ch...
    method createChannelEndpoint (line 73) | private async createChannelEndpoint(
    method assertSubscriberExists (line 97) | private async assertSubscriberExists(command: CreateChannelEndpointCom...
    method findIntegration (line 113) | private async findIntegration(command: CreateChannelEndpointCommand) {
    method findChannelConnection (line 127) | private async findChannelConnection(command: CreateChannelEndpointComm...
    method generateIdentifier (line 141) | private generateIdentifier(): string {

FILE: apps/api/src/app/channel-endpoints/usecases/delete-channel-endpoint/delete-channel-endpoint.command.ts
  class DeleteChannelEndpointCommand (line 4) | class DeleteChannelEndpointCommand extends EnvironmentCommand {

FILE: apps/api/src/app/channel-endpoints/usecases/delete-channel-endpoint/delete-channel-endpoint.usecase.ts
  class DeleteChannelEndpoint (line 7) | class DeleteChannelEndpoint {
    method constructor (line 8) | constructor(private readonly channelEndpointRepository: ChannelEndpoin...
    method execute (line 11) | async execute(command: DeleteChannelEndpointCommand): Promise<void> {

FILE: apps/api/src/app/channel-endpoints/usecases/get-channel-endpoint/get-channel-endpoint.command.ts
  class GetChannelEndpointCommand (line 4) | class GetChannelEndpointCommand extends EnvironmentCommand {

FILE: apps/api/src/app/channel-endpoints/usecases/get-channel-endpoint/get-channel-endpoint.usecase.ts
  class GetChannelEndpoint (line 7) | class GetChannelEndpoint {
    method constructor (line 8) | constructor(private readonly channelEndpointRepository: ChannelEndpoin...
    method execute (line 11) | async execute(command: GetChannelEndpointCommand): Promise<ChannelEndp...

FILE: apps/api/src/app/channel-endpoints/usecases/list-channel-endpoints/list-channel-endpoints.command.ts
  class ListChannelEndpointsCommand (line 6) | class ListChannelEndpointsCommand extends CursorBasedPaginatedCommand<

FILE: apps/api/src/app/channel-endpoints/usecases/list-channel-endpoints/list-channel-endpoints.usecase.ts
  class ListChannelEndpoints (line 10) | class ListChannelEndpoints {
    method constructor (line 11) | constructor(private readonly channelEndpointRepository: ChannelEndpoin...
    method execute (line 14) | async execute(command: ListChannelEndpointsCommand) {

FILE: apps/api/src/app/channel-endpoints/usecases/update-channel-endpoint/update-channel-endpoint.command.ts
  class UpdateChannelEndpointCommand (line 8) | class UpdateChannelEndpointCommand<
    method create (line 19) | static create<T extends ChannelEndpointType>(data: {

FILE: apps/api/src/app/channel-endpoints/usecases/update-channel-endpoint/update-channel-endpoint.usecase.ts
  class UpdateChannelEndpoint (line 7) | class UpdateChannelEndpoint {
    method constructor (line 8) | constructor(private readonly channelEndpointRepository: ChannelEndpoin...
    method execute (line 11) | async execute(command: UpdateChannelEndpointCommand): Promise<ChannelE...
    method updateChannelEndpoint (line 33) | private async updateChannelEndpoint(command: UpdateChannelEndpointComm...

FILE: apps/api/src/app/channel-endpoints/validators/channel-endpoint.validator.ts
  function IsValidChannelEndpoint (line 5) | function IsValidChannelEndpoint(validationOptions?: ValidationOptions) {

FILE: apps/api/src/app/content-templates/content-templates.controller.ts
  class ContentTemplatesController (line 23) | class ContentTemplatesController {
    method constructor (line 24) | constructor(
    method previewEmail (line 35) | public async previewEmail(
    method previewInApp (line 63) | public async previewInApp(
    method previewSms (line 87) | public async previewSms(
    method previewChat (line 109) | public async previewChat(
    method previewPush (line 131) | public async previewPush(
    method initiateTranslations (line 154) | protected async initiateTranslations(environmentId: string, organizati...

FILE: apps/api/src/app/content-templates/content-templates.module.ts
  class ContentTemplatesModule (line 27) | class ContentTemplatesModule {}

FILE: apps/api/src/app/content-templates/usecases/index.ts
  constant USE_CASES (line 8) | const USE_CASES = [CompileTemplate, CompileEmailTemplate, CompileInAppTe...

FILE: apps/api/src/app/contexts/contexts.controller.ts
  class ContextsController (line 45) | class ContextsController {
    method constructor (line 46) | constructor(
    method createContext (line 63) | async createContext(
    method updateContext (line 93) | async updateContext(
    method listContexts (line 124) | async listContexts(
    method getContext (line 163) | async getContext(
    method deleteContext (line 191) | async deleteContext(

FILE: apps/api/src/app/contexts/contexts.module.ts
  constant USE_CASES (line 12) | const USE_CASES = [CreateContext, UpdateContext, GetContext, ListContext...
  constant DAL_MODELS (line 14) | const DAL_MODELS = [ContextRepository];
  class ContextsModule (line 22) | class ContextsModule {}

FILE: apps/api/src/app/contexts/dtos/create-context-request.dto.ts
  class CreateContextRequestDto (line 6) | class CreateContextRequestDto {

FILE: apps/api/src/app/contexts/dtos/cursor-pagination-query.dto.ts
  class CursorPaginationQueryDto (line 6) | class CursorPaginationQueryDto<T, K extends keyof T> {

FILE: apps/api/src/app/contexts/dtos/dto.mapper.ts
  function mapContextEntityToDto (line 4) | function mapContextEntityToDto(context: ContextEntity): GetContextRespon...

FILE: apps/api/src/app/contexts/dtos/get-context-response.dto.ts
  class GetContextResponseDto (line 4) | class GetContextResponseDto {

FILE: apps/api/src/app/contexts/dtos/list-contexts-query.dto.ts
  class ListContextsQueryDto (line 7) | class ListContextsQueryDto extends CursorPaginationQueryDto<GetContextRe...

FILE: apps/api/src/app/contexts/dtos/list-contexts-response.dto.ts
  class ListContextsResponseDto (line 4) | class ListContextsResponseDto extends withCursorPagination(GetContextRes...

FILE: apps/api/src/app/contexts/dtos/update-context-request.dto.ts
  class UpdateContextRequestDto (line 6) | class UpdateContextRequestDto {

FILE: apps/api/src/app/contexts/e2e/list-contexts.e2e.ts
  function timeout (line 228) | function timeout(ms: number): Promise<void> {

FILE: apps/api/src/app/contexts/usecases/create-context/create-context.command.ts
  class CreateContextCommand (line 5) | class CreateContextCommand extends EnvironmentWithUserCommand {

FILE: apps/api/src/app/contexts/usecases/create-context/create-context.usecase.ts
  class CreateContext (line 7) | class CreateContext {
    method constructor (line 8) | constructor(private contextRepository: ContextRepository) {}
    method execute (line 10) | async execute(command: CreateContextCommand): Promise<ContextEntity> {

FILE: apps/api/src/app/contexts/usecases/delete-context/delete-context.command.ts
  class DeleteContextCommand (line 5) | class DeleteContextCommand extends EnvironmentCommand {

FILE: apps/api/src/app/contexts/usecases/delete-context/delete-context.usecase.ts
  class DeleteContext (line 6) | class DeleteContext {
    method constructor (line 7) | constructor(private contextRepository: ContextRepository) {}
    method execute (line 9) | async execute(command: DeleteContextCommand) {

FILE: apps/api/src/app/contexts/usecases/get-context/get-context.command.ts
  class GetContextCommand (line 5) | class GetContextCommand extends EnvironmentCommand {

FILE: apps/api/src/app/contexts/usecases/get-context/get-context.usecase.ts
  class GetContext (line 6) | class GetContext {
    method constructor (line 7) | constructor(private contextRepository: ContextRepository) {}
    method execute (line 9) | async execute(command: GetContextCommand): Promise<ContextEntity> {

FILE: apps/api/src/app/contexts/usecases/list-contexts/list-contexts.command.ts
  class ListContextsCommand (line 5) | class ListContextsCommand extends CursorBasedPaginatedCommand<Context, '...

FILE: apps/api/src/app/contexts/usecases/list-contexts/list-contexts.usecase.ts
  class ListContexts (line 8) | class ListContexts {
    method constructor (line 9) | constructor(private contextRepository: ContextRepository) {}
    method execute (line 11) | async execute(command: ListContextsCommand) {

FILE: apps/api/src/app/contexts/usecases/update-context/update-context.command.ts
  class UpdateContextCommand (line 5) | class UpdateContextCommand extends EnvironmentWithUserCommand {

FILE: apps/api/src/app/contexts/usecases/update-context/update-context.usecase.ts
  class UpdateContext (line 6) | class UpdateContext {
    method constructor (line 7) | constructor(private contextRepository: ContextRepository) {}
    method execute (line 9) | async execute(command: UpdateContextCommand): Promise<ContextEntity> {

FILE: apps/api/src/app/environment-variables/dtos/create-environment-variable-request.dto.ts
  class EnvironmentVariableValueDto (line 16) | class EnvironmentVariableValueDto implements IEnvironmentVariableValueDto {
  class CreateEnvironmentVariableRequestDto (line 27) | class CreateEnvironmentVariableRequestDto implements ICreateEnvironmentV...

FILE: apps/api/src/app/environment-variables/dtos/environment-variable-response.dto.ts
  constant SECRET_MASK (line 4) | const SECRET_MASK = '••••••••';
  class EnvironmentVariableValueResponseDto (line 6) | class EnvironmentVariableValueResponseDto {
  class EnvironmentVariableResponseDto (line 14) | class EnvironmentVariableResponseDto {

FILE: apps/api/src/app/environment-variables/dtos/get-environment-variable-usage-response.dto.ts
  class EnvironmentVariableWorkflowInfoDto (line 3) | class EnvironmentVariableWorkflowInfoDto {
  class GetEnvironmentVariableUsageResponseDto (line 17) | class GetEnvironmentVariableUsageResponseDto {

FILE: apps/api/src/app/environment-variables/dtos/get-environment-variables-request.dto.ts
  class GetEnvironmentVariablesRequestDto (line 4) | class GetEnvironmentVariablesRequestDto {

FILE: apps/api/src/app/environment-variables/dtos/update-environment-variable-request.dto.ts
  class UpdateEnvironmentVariableRequestDto (line 7) | class UpdateEnvironmentVariableRequestDto implements IUpdateEnvironmentV...

FILE: apps/api/src/app/environment-variables/environment-variables.controller.ts
  class EnvironmentVariablesController (line 60) | class EnvironmentVariablesController {
    method constructor (line 61) | constructor(
    method listEnvironmentVariables (line 78) | async listEnvironmentVariables(
    method getEnvironmentVariableUsage (line 102) | async getEnvironmentVariableUsage(
    method getEnvironmentVariable (line 125) | async getEnvironmentVariable(
    method createEnvironmentVariable (line 149) | async createEnvironmentVariable(
    method updateEnvironmentVariable (line 175) | async updateEnvironmentVariable(
    method deleteEnvironmentVariable (line 203) | async deleteEnvironmentVariable(

FILE: apps/api/src/app/environment-variables/environment-variables.module.ts
  class EnvironmentVariablesModule (line 14) | class EnvironmentVariablesModule {}

FILE: apps/api/src/app/environment-variables/usecases/create-environment-variable/create-environment-variable.command.ts
  class EnvironmentVariableValueCommand (line 6) | class EnvironmentVariableValueCommand {
  class CreateEnvironmentVariableCommand (line 15) | class CreateEnvironmentVariableCommand extends OrganizationLevelWithUser...

FILE: apps/api/src/app/environment-variables/usecases/create-environment-variable/create-environment-variable.usecase.ts
  class CreateEnvironmentVariable (line 10) | class CreateEnvironmentVariable {
    method constructor (line 11) | constructor(
    method execute (line 16) | async execute(command: CreateEnvironmentVariableCommand): Promise<Envi...

FILE: apps/api/src/app/environment-variables/usecases/delete-environment-variable/delete-environment-variable.command.ts
  class DeleteEnvironmentVariableCommand (line 4) | class DeleteEnvironmentVariableCommand extends OrganizationLevelWithUser...

FILE: apps/api/src/app/environment-variables/usecases/delete-environment-variable/delete-environment-variable.usecase.ts
  class DeleteEnvironmentVariable (line 6) | class DeleteEnvironmentVariable {
    method constructor (line 7) | constructor(private environmentVariableRepository: EnvironmentVariable...
    method execute (line 9) | async execute(command: DeleteEnvironmentVariableCommand): Promise<void> {

FILE: apps/api/src/app/environment-variables/usecases/get-environment-variable-usage/get-environment-variable-usage.command.ts
  class GetEnvironmentVariableUsageCommand (line 4) | class GetEnvironmentVariableUsageCommand extends OrganizationLevelWithUs...

FILE: apps/api/src/app/environment-variables/usecases/get-environment-variable-usage/get-environment-variable-usage.usecase.ts
  constant CONTROL_VALUES_SELECT (line 17) | const CONTROL_VALUES_SELECT = ['_workflowId', '_environmentId', 'control...
  type ControlValuesUsageFetchResult (line 18) | type ControlValuesUsageFetchResult = Pick<ControlValuesEntity, (typeof C...
  class GetEnvironmentVariableUsage (line 21) | class GetEnvironmentVariableUsage {
    method constructor (line 22) | constructor(
    method execute (line 32) | async execute(command: GetEnvironmentVariableUsageCommand): Promise<Ge...
    method controlsReferenceEnvVar (line 91) | private controlsReferenceEnvVar(controls: ControlValuesUsageFetchResul...

FILE: apps/api/src/app/environment-variables/usecases/get-environment-variable/get-environment-variable.command.ts
  class GetEnvironmentVariableCommand (line 4) | class GetEnvironmentVariableCommand extends OrganizationLevelWithUserCom...

FILE: apps/api/src/app/environment-variables/usecases/get-environment-variable/get-environment-variable.usecase.ts
  class GetEnvironmentVariable (line 8) | class GetEnvironmentVariable {
    method constructor (line 9) | constructor(private environmentVariableRepository: EnvironmentVariable...
    method execute (line 11) | async execute(command: GetEnvironmentVariableCommand): Promise<Environ...

FILE: apps/api/src/app/environment-variables/usecases/get-environment-variables/get-environment-variables.command.ts
  class GetEnvironmentVariablesCommand (line 4) | class GetEnvironmentVariablesCommand extends OrganizationLevelWithUserCo...

FILE: apps/api/src/app/environment-variables/usecases/get-environment-variables/get-environment-variables.usecase.ts
  class GetEnvironmentVariables (line 9) | class GetEnvironmentVariables {
    method constructor (line 10) | constructor(private environmentVariableRepository: EnvironmentVariable...
    method execute (line 12) | async execute(command: GetEnvironmentVariablesCommand): Promise<Enviro...
  function toEnvironmentVariableResponseDto (line 29) | function toEnvironmentVariableResponseDto(variable: EnvironmentVariableE...

FILE: apps/api/src/app/environment-variables/usecases/index.ts
  constant USE_CASES (line 8) | const USE_CASES = [

FILE: apps/api/src/app/environment-variables/usecases/update-environment-variable/update-environment-variable.command.ts
  class UpdateEnvironmentVariableCommand (line 7) | class UpdateEnvironmentVariableCommand extends OrganizationLevelWithUser...

FILE: apps/api/src/app/environment-variables/usecases/update-environment-variable/update-environment-variable.usecase.ts
  class UpdateEnvironmentVariable (line 9) | class UpdateEnvironmentVariable {
    method constructor (line 10) | constructor(private environmentVariableRepository: EnvironmentVariable...
    method execute (line 12) | async execute(command: UpdateEnvironmentVariableCommand): Promise<Envi...

FILE: apps/api/src/app/environments-v1/dtos/api-key.dto.ts
  class ApiKeyDto (line 3) | class ApiKeyDto {

FILE: apps/api/src/app/environments-v1/dtos/create-environment-request.dto.ts
  class CreateEnvironmentRequestDto (line 4) | class CreateEnvironmentRequestDto {

FILE: apps/api/src/app/environments-v1/dtos/environment-response.dto.ts
  class EnvironmentResponseDto (line 5) | class EnvironmentResponseDto {

FILE: apps/api/src/app/environments-v1/dtos/update-environment-request.dto.ts
  class InBoundParseDomainDto (line 4) | class InBoundParseDomainDto {
  class BridgeConfigurationDto (line 9) | class BridgeConfigurationDto {
  class UpdateEnvironmentRequestDto (line 14) | class UpdateEnvironmentRequestDto {

FILE: apps/api/src/app/environments-v1/e2e/environments.controller.e2e.ts
  function generateRandomEnvRequest (line 129) | function generateRandomEnvRequest() {
  function generateRandomName (line 141) | function generateRandomName(prefix: string = 'env'): string {

FILE: apps/api/src/app/environments-v1/environments-v1.controller.ts
  class EnvironmentsControllerV1 (line 60) | class EnvironmentsControllerV1 {
    method constructor (line 61) | constructor(
    method getCurrentEnvironment (line 81) | async getCurrentEnvironment(@UserSession() user: UserSessionData): Pro...
    method createEnvironment (line 105) | async createEnvironment(
    method listMyEnvironments (line 134) | async listMyEnvironments(@UserSession() user: UserSessionData): Promis...
    method updateMyEnvironment (line 159) | async updateMyEnvironment(
    method listOrganizationApiKeys (line 188) | async listOrganizationApiKeys(@UserSession() user: UserSessionData): P...
    method regenerateOrganizationApiKeys (line 202) | async regenerateOrganizationApiKeys(@UserSession() user: UserSessionDa...
    method deleteEnvironment (line 224) | async deleteEnvironment(@UserSession() user: UserSessionData, @Param('...
    method canUserAccessApiKeys (line 234) | private async canUserAccessApiKeys(user: UserSessionData): Promise<boo...

FILE: apps/api/src/app/environments-v1/environments-v1.module.ts
  class EnvironmentsModuleV1 (line 25) | class EnvironmentsModuleV1 {}

FILE: apps/api/src/app/environments-v1/novu-bridge-client.ts
  class NovuBridgeClient (line 18) | class NovuBridgeClient {
    method constructor (line 19) | constructor(
    method handleRequest (line 24) | public async handleRequest(req: Request, res: Response) {

FILE: apps/api/src/app/environments-v1/novu-bridge.controller.ts
  class NovuBridgeController (line 9) | class NovuBridgeController {
    method constructor (line 10) | constructor(@Inject(NovuClient) private novuService: NovuBridgeClient) {}
    method handleGet (line 13) | async handleGet(@Req() req: Request, @Res() res: Response) {
    method handlePost (line 18) | async handlePost(@Req() req: Request, @Res() res: Response) {
    method handleOptions (line 23) | async handleOptions(@Req() req: Request, @Res() res: Response) {

FILE: apps/api/src/app/environments-v1/novu-bridge.module.ts
  class NovuBridgeModule (line 92) | class NovuBridgeModule {}

FILE: apps/api/src/app/environments-v1/usecases/construct-framework-workflow/construct-framework-workflow.command.ts
  class ConstructFrameworkWorkflowCommand (line 6) | class ConstructFrameworkWorkflowCommand extends EnvironmentLevelCommand {

FILE: apps/api/src/app/environments-v1/usecases/construct-framework-workflow/construct-framework-workflow.usecase.ts
  constant LOG_CONTEXT (line 39) | const LOG_CONTEXT = 'ConstructFrameworkWorkflow';
  class ConstructFrameworkWorkflow (line 42) | class ConstructFrameworkWorkflow {
    method constructor (line 43) | constructor(
    method execute (line 61) | async execute(command: ConstructFrameworkWorkflowCommand): Promise<Wor...
    method constructLayoutPreviewWorkflow (line 87) | private async constructLayoutPreviewWorkflow(command: ConstructFramewo...
    method constructFrameworkWorkflow (line 128) | private constructFrameworkWorkflow({
    method constructStep (line 182) | private constructStep({
    method constructChannelStepOptions (line 352) | private constructChannelStepOptions(
    method constructActionStepOptions (line 368) | private constructActionStepOptions(
    method optionalAugmentControlSchemaDueToAjvBug (line 381) | private optionalAugmentControlSchemaDueToAjvBug(staticStep: Notificati...
    method getWorkflow (line 399) | private async getWorkflow(
    method getOrganization (line 433) | private async getOrganization(
    method processSkipOption (line 452) | private async processSkipOption(
  constant PERMISSIVE_EMPTY_SCHEMA (line 479) | const PERMISSIVE_EMPTY_SCHEMA = {

FILE: apps/api/src/app/environments-v1/usecases/create-environment/create-environment.command.ts
  class CreateEnvironmentCommand (line 5) | class CreateEnvironmentCommand extends OrganizationCommand {

FILE: apps/api/src/app/environments-v1/usecases/create-environment/create-environment.e2e.ts
  function createEnv (line 6) | async function createEnv(name: string, session) {

FILE: apps/api/src/app/environments-v1/usecases/create-environment/create-environment.usecase.ts
  class CreateEnvironment (line 15) | class CreateEnvironment {
    method constructor (line 16) | constructor(
    method execute (line 25) | async execute(command: CreateEnvironmentCommand): Promise<EnvironmentR...
    method convertEnvironmentEntityToDto (line 126) | private convertEnvironmentEntityToDto(environment: EnvironmentEntity, ...
    method getEnvironmentColor (line 147) | private getEnvironmentColor(name: string, commandColor?: string): stri...
    method getEnvironmentType (line 154) | private async getEnvironmentType(name: string, commandType?: Environme...
    method getMaxEnvironmentCount (line 163) | private async getMaxEnvironmentCount(organizationId: string): Promise<...

FILE: apps/api/src/app/environments-v1/usecases/delete-environment/delete-environment.command.ts
  class DeleteEnvironmentCommand (line 3) | class DeleteEnvironmentCommand extends EnvironmentWithUserCommand {}

FILE: apps/api/src/app/environments-v1/usecases/delete-environment/delete-environment.usecase.ts
  class DeleteEnvironment (line 9) | class DeleteEnvironment {
    method constructor (line 10) | constructor(
    method execute (line 16) | async execute(command: DeleteEnvironmentCommand): Promise<void> {

FILE: apps/api/src/app/environments-v1/usecases/generate-unique-api-key/generate-unique-api-key.usecase.ts
  constant API_KEY_GENERATION_MAX_RETRIES (line 5) | const API_KEY_GENERATION_MAX_RETRIES = 3;
  class GenerateUniqueApiKey (line 8) | class GenerateUniqueApiKey {
    method constructor (line 9) | constructor(private environmentRepository: EnvironmentRepository) {}
    method execute (line 11) | async execute(): Promise<string> {
    method validateIsApiKeyUsed (line 29) | private async validateIsApiKeyUsed(apiKey: string) {
    method generateApiKey (line 39) | private generateApiKey(): string {

FILE: apps/api/src/app/environments-v1/usecases/get-api-keys/get-api-keys.command.ts
  class GetApiKeysCommand (line 3) | class GetApiKeysCommand extends EnvironmentWithUserCommand {}

FILE: apps/api/src/app/environments-v1/usecases/get-api-keys/get-api-keys.usecase.ts
  class GetApiKeys (line 8) | class GetApiKeys {
    method constructor (line 9) | constructor(private environmentRepository: EnvironmentRepository) {}
    method execute (line 11) | async execute(command: GetApiKeysCommand): Promise<ApiKey[]> {

FILE: apps/api/src/app/environments-v1/usecases/get-environment/get-environment.command.ts
  class GetEnvironmentCommand (line 3) | class GetEnvironmentCommand extends EnvironmentWithUserCommand {}

FILE: apps/api/src/app/environments-v1/usecases/get-environment/get-environment.usecase.ts
  class GetEnvironment (line 8) | class GetEnvironment {
    method constructor (line 9) | constructor(private environmentRepository: EnvironmentRepository) {}
    method execute (line 11) | async execute(command: GetEnvironmentCommand): Promise<EnvironmentResp...

FILE: apps/api/src/app/environments-v1/usecases/get-my-environments/get-my-environments.command.ts
  class GetMyEnvironmentsCommand (line 4) | class GetMyEnvironmentsCommand extends BaseCommand {

FILE: apps/api/src/app/environments-v1/usecases/get-my-environments/get-my-environments.usecase.ts
  class GetMyEnvironments (line 11) | class GetMyEnvironments {
    method constructor (line 12) | constructor(
    method execute (line 19) | async execute(command: GetMyEnvironmentsCommand): Promise<EnvironmentR...
    method decryptApiKeys (line 42) | private decryptApiKeys(apiKeys: EnvironmentEntity['apiKeys']) {
  function shortenEnvironmentName (line 50) | function shortenEnvironmentName(name: string): string {

FILE: apps/api/src/app/environments-v1/usecases/index.ts
  constant USE_CASES (line 11) | const USE_CASES = [

FILE: apps/api/src/app/environments-v1/usecases/output-renderers/base-translation-renderer.usecase.ts
  type TranslationContext (line 8) | type TranslationContext = {
  method constructor (line 17) | constructor(
  method processTranslations (line 22) | protected async processTranslations({
  method processStringTranslations (line 60) | protected async processStringTranslations({
  method createTranslationContext (line 95) | protected async createTranslationContext({
  method processStringWithContext (line 159) | protected async processStringWithContext({
  method executeTranslation (line 190) | private async executeTranslation({
  method getTranslationModule (line 261) | private getTranslationModule() {

FILE: apps/api/src/app/environments-v1/usecases/output-renderers/chat-output-renderer.usecase.ts
  class ChatOutputRendererCommand (line 9) | class ChatOutputRendererCommand extends RenderCommand {
  class ChatOutputRendererUsecase (line 15) | class ChatOutputRendererUsecase extends BaseTranslationRendererUsecase {
    method constructor (line 16) | constructor(
    method execute (line 24) | async execute(renderCommand: ChatOutputRendererCommand): Promise<ChatR...

FILE: apps/api/src/app/environments-v1/usecases/output-renderers/delay-output-renderer.usecase.ts
  class DelayOutputRendererUsecase (line 7) | class DelayOutputRendererUsecase {
    method execute (line 9) | execute(renderCommand: RenderCommand): DelayRenderOutput {

FILE: apps/api/src/app/environments-v1/usecases/output-renderers/digest-output-renderer.usecase.ts
  class DigestOutputRendererUsecase (line 7) | class DigestOutputRendererUsecase {
    method execute (line 9) | execute(renderCommand: RenderCommand): DigestRenderOutput {

FILE: apps/api/src/app/environments-v1/usecases/output-renderers/email-output-renderer.spec.ts
  function setupTranslationMocks (line 23) | function setupTranslationMocks(moduleRef: sinon.SinonStubbedInstance<Mod...

FILE: apps/api/src/app/environments-v1/usecases/output-renderers/email-output-renderer.usecase.ts
  type TranslationContext (line 52) | type TranslationContext = {
  type MailyJSONMarks (line 59) | type MailyJSONMarks = NonNullable<MailyJSONContent['marks']>[number];
  class EmailOutputRendererCommand (line 61) | class EmailOutputRendererCommand extends RenderCommand {
  function isJsonString (line 70) | function isJsonString(str: string): boolean {
  class EmailOutputRendererUsecase (line 81) | class EmailOutputRendererUsecase extends BaseTranslationRendererUsecase {
    method constructor (line 84) | constructor(
    method execute (line 126) | async execute(renderCommand: EmailOutputRendererCommand): Promise<Emai...
    method getOverrideLayoutId (line 223) | private async getOverrideLayoutId({
    method renderWithLayout (line 261) | private async renderWithLayout({
    method enhanceContentVariable (line 416) | private enhanceContentVariable(body: string) {
    method processBodyContent (line 433) | private async processBodyContent({
    method processSubjectTranslations (line 492) | private async processSubjectTranslations(
    method processMailyTranslations (line 524) | private async processMailyTranslations({
    method processTextTranslations (line 572) | private async processTextTranslations({
    method parseMailyContentByLiquid (line 616) | private async parseMailyContentByLiquid(
    method transformMailyContent (line 631) | private async transformMailyContent(
    method handleShowNode (line 667) | private async handleShowNode(
    method handleEachNode (line 682) | private async handleEachNode(
    method evaluateShowCondition (line 697) | private async evaluateShowCondition(
    method processVariableNodeTypes (line 707) | private processVariableNodeTypes(node: MailyJSONContent) {
    method multiplyForEachNode (line 740) | private async multiplyForEachNode(
    method getIterableArray (line 753) | private async getIterableArray(iterablePath: string, variables: FullPa...
    method processForEachNodes (line 769) | private processForEachNodes(
    method addIndexToLiquidExpression (line 847) | private addIndexToLiquidExpression(text: string, iterablePath: string,...
    method stringToBoolean (line 861) | private stringToBoolean(value: string): boolean {
    method appendNovuBranding (line 872) | private async appendNovuBranding(
    method insertBrandingHtml (line 896) | private insertBrandingHtml(html: string): string {
    method deepEscapePayloadStrings (line 912) | private deepEscapePayloadStrings(payload: FullPayloadForRender): FullP...
    method deepEscapeObject (line 916) | private deepEscapeObject(obj: unknown): unknown {
    method escapeStringForJson (line 945) | private escapeStringForJson(str: string): string {
    method unescapeJsonString (line 954) | private unescapeJsonString(str: string): string {
    method deepUnescapeTranslationStrings (line 964) | private deepUnescapeTranslationStrings(obj: unknown): unknown {
    method cleanupRenderedHtml (line 993) | private cleanupRenderedHtml(html: string): string {

FILE: apps/api/src/app/environments-v1/usecases/output-renderers/in-app-output-renderer.usecase.ts
  class InAppOutputRendererCommand (line 9) | class InAppOutputRendererCommand extends RenderCommand {
  class InAppOutputRendererUsecase (line 15) | class InAppOutputRendererUsecase extends BaseTranslationRendererUsecase {
    method constructor (line 16) | constructor(
    method execute (line 24) | async execute(renderCommand: InAppOutputRendererCommand): Promise<InAp...

FILE: apps/api/src/app/environments-v1/usecases/output-renderers/novu-branding-html.ts
  constant NOVU_BRANDING_HTML (line 6) | const NOVU_BRANDING_HTML = `

FILE: apps/api/src/app/environments-v1/usecases/output-renderers/push-output-renderer.usecase.ts
  class PushOutputRendererCommand (line 9) | class PushOutputRendererCommand extends RenderCommand {
  class PushOutputRendererUsecase (line 15) | class PushOutputRendererUsecase extends BaseTranslationRendererUsecase {
    method constructor (line 16) | constructor(
    method execute (line 24) | async execute(renderCommand: PushOutputRendererCommand): Promise<PushR...

FILE: apps/api/src/app/environments-v1/usecases/output-renderers/render-command.ts
  class RenderCommand (line 6) | class RenderCommand extends BaseCommand {
  class FullPayloadForRender (line 11) | class FullPayloadForRender {

FILE: apps/api/src/app/environments-v1/usecases/output-renderers/sms-output-renderer.usecase.ts
  class SmsOutputRendererCommand (line 9) | class SmsOutputRendererCommand extends RenderCommand {
  class SmsOutputRendererUsecase (line 15) | class SmsOutputRendererUsecase extends BaseTranslationRendererUsecase {
    method constructor (line 16) | constructor(
    method execute (line 24) | async execute(renderCommand: SmsOutputRendererCommand): Promise<SmsRen...

FILE: apps/api/src/app/environments-v1/usecases/output-renderers/throttle-output-renderer.usecase.ts
  class ThrottleOutputRendererUsecase (line 7) | class ThrottleOutputRendererUsecase {
    method execute (line 9) | execute(renderCommand: RenderCommand): ThrottleRenderOutput {

FILE: apps/api/src/app/environments-v1/usecases/regenerate-api-keys/regenerate-api-keys.usecase.ts
  class RegenerateApiKeys (line 11) | class RegenerateApiKeys {
    method constructor (line 12) | constructor(
    method execute (line 17) | async execute(command: GetApiKeysCommand): Promise<ApiKeyDto[]> {

FILE: apps/api/src/app/environments-v1/usecases/update-environment/update-environment.command.ts
  class UpdateEnvironmentCommand (line 4) | class UpdateEnvironmentCommand extends OrganizationCommand {

FILE: apps/api/src/app/environments-v1/usecases/update-environment/update-environment.usecase.ts
  class UpdateEnvironment (line 7) | class UpdateEnvironment {
    method constructor (line 8) | constructor(private environmentRepository: EnvironmentRepository) {}
    method execute (line 10) | async execute(command: UpdateEnvironmentCommand) {

FILE: apps/api/src/app/environments-v2/dtos/diff-environment.dto.ts
  class DiffEnvironmentRequestDto (line 6) | class DiffEnvironmentRequestDto {
  class UserInfoDto (line 16) | class UserInfoDto {
  class ResourceInfoDto (line 36) | class ResourceInfoDto {
  class ResourceDiffDto (line 77) | class ResourceDiffDto {
  class DiffSummaryDto (line 154) | class DiffSummaryDto {
  class ResourceDependencyDto (line 172) | class ResourceDependencyDto {
  class ResourceDiffResultDto (line 208) | class ResourceDiffResultDto {
  class EnvironmentDiffSummaryDto (line 259) | class EnvironmentDiffSummaryDto {
  class DiffEnvironmentResponseDto (line 273) | class DiffEnvironmentResponseDto {

FILE: apps/api/src/app/environments-v2/dtos/publish-environment.dto.ts
  class ResourceToPublishDto (line 6) | class ResourceToPublishDto {
  class PublishEnvironmentRequestDto (line 23) | class PublishEnvironmentRequestDto {
  class SyncedWorkflowDto (line 51) | class SyncedWorkflowDto {
  class FailedWorkflowDto (line 73) | class FailedWorkflowDto {
  class SkippedWorkflowDto (line 94) | class SkippedWorkflowDto {
  class SyncResultDto (line 112) | class SyncResultDto {
  class PublishSummaryDto (line 133) | class PublishSummaryDto {
  class PublishEnvironmentResponseDto (line 147) | class PublishEnvironmentResponseDto {

FILE: apps/api/src/app/environments-v2/e2e/environments-v2-diff.e2e.ts
  function getProductionEnvironment (line 21) | async function getProductionEnvironment() {

FILE: apps/api/src/app/environments-v2/e2e/environments-v2-publish.e2e.ts
  function createWorkflow (line 488) | async function createWorkflow(workflow: CreateWorkflowDto): Promise<Work...

FILE: apps/api/src/app/environments-v2/environments.controller.ts
  class EnvironmentsController (line 42) | class EnvironmentsController {
    method constructor (line 43) | constructor(
    method getEnvironmentTags (line 65) | async getEnvironmentTags(
    method publishEnvironment (line 96) | async publishEnvironment(
    method diffEnvironment (line 130) | async diffEnvironment(

FILE: apps/api/src/app/environments-v2/environments.module.ts
  class EnvironmentsModule (line 23) | class EnvironmentsModule {}

FILE: apps/api/src/app/environments-v2/services/dependency-analyzer.service.ts
  class DependencyAnalyzerService (line 14) | class DependencyAnalyzerService {
    method constructor (line 15) | constructor(
    method analyzeDependencies (line 24) | async analyzeDependencies(
    method getWorkflowDependencies (line 107) | async getWorkflowDependencies(
    method getLayoutReverseDependencies (line 186) | async getLayoutReverseDependencies(
    method extractLayoutIdsFromStepChange (line 257) | extractLayoutIdsFromStepChange(stepChange: IResourceDiff): string[] {
    method createLayoutDependency (line 275) | async createLayoutDependency(
    method isDependencyBlocking (line 325) | isDependencyBlocking(targetLayout: unknown, layoutDiff?: IDiffResult):...

FILE: apps/api/src/app/environments-v2/services/environment-validation.service.ts
  type IEnvironmentValidationParams (line 5) | interface IEnvironmentValidationParams {
  class EnvironmentValidationService (line 12) | class EnvironmentValidationService {
    method constructor (line 13) | constructor(private environmentRepository: EnvironmentRepository) {}
    method validateEnvironments (line 15) | async validateEnvironments(params: IEnvironmentValidationParams): Prom...
    method getDevelopmentEnvironmentId (line 53) | async getDevelopmentEnvironmentId(organizationId: string): Promise<str...

FILE: apps/api/src/app/environments-v2/types/sync.types.ts
  type ResourceTypeEnum (line 4) | enum ResourceTypeEnum {
  type DependencyReasonEnum (line 11) | enum DependencyReasonEnum {
  type SyncActionEnum (line 16) | enum SyncActionEnum {
  type IResourceDependency (line 23) | interface IResourceDependency {
  type IResourceToPublish (line 31) | interface IResourceToPublish {
  type ISyncOptions (line 36) | interface ISyncOptions {
  type ISyncContext (line 42) | interface ISyncContext {
  type ISyncedEntity (line 50) | interface ISyncedEntity {
  type IFailedEntity (line 57) | interface IFailedEntity {
  type ISkippedEntity (line 65) | interface ISkippedEntity {
  type ISyncResult (line 72) | interface ISyncResult {
  type IPublishResult (line 80) | interface IPublishResult {
  type DiffActionEnum (line 90) | enum DiffActionEnum {
  type IUserInfo (line 98) | interface IUserInfo {
  type IResourceInfo (line 105) | interface IResourceInfo {
  type IResourceDiff (line 112) | interface IResourceDiff {
  type IDiffResult (line 127) | interface IDiffResult {
  type IEnvironmentDiffResult (line 141) | interface IEnvironmentDiffResult {
  type ISyncStrategy (line 152) | interface ISyncStrategy {
  type ISyncProgress (line 164) | interface ISyncProgress {

FILE: apps/api/src/app/environments-v2/usecases/diff-environment/diff-environment.command.ts
  class DiffEnvironmentCommand (line 4) | class DiffEnvironmentCommand extends EnvironmentWithUserObjectCommand {

FILE: apps/api/src/app/environments-v2/usecases/diff-environment/diff-environment.usecase.ts
  class DiffEnvironmentUseCase (line 17) | class DiffEnvironmentUseCase {
    method constructor (line 18) | constructor(
    method execute (line 32) | async execute(command: DiffEnvironmentCommand): Promise<IEnvironmentDi...
    method calculateSummary (line 125) | private calculateSummary(resources: IDiffResult[]) {

FILE: apps/api/src/app/environments-v2/usecases/publish-environment/publish-environment.command.ts
  type IResourceToPublish (line 6) | interface IResourceToPublish {
  class PublishEnvironmentCommand (line 11) | class PublishEnvironmentCommand extends EnvironmentWithUserObjectCommand {

FILE: apps/api/src/app/environments-v2/usecases/publish-environment/publish-environment.usecase.ts
  constant PUBLISH_BATCH_SIZE (line 10) | const PUBLISH_BATCH_SIZE = 100;
  class PublishEnvironmentUseCase (line 13) | class PublishEnvironmentUseCase {
    method constructor (line 14) | constructor(
    method execute (line 24) | async execute(command: PublishEnvironmentCommand): Promise<IPublishRes...
    method executeSync (line 77) | private async executeSync(strategies: ISyncStrategy[], context: ISyncC...
    method calculateSummary (line 98) | private calculateSummary(results: ISyncResult[]) {

FILE: apps/api/src/app/environments-v2/usecases/sync-strategies/adapters/layout-comparator.adapter.ts
  class LayoutComparatorAdapter (line 9) | class LayoutComparatorAdapter implements IBaseComparator<LayoutEntity> {
    method constructor (line 10) | constructor(private readonly layoutComparator: LayoutComparator) {}
    method compareResources (line 12) | async compareResources(

FILE: apps/api/src/app/environments-v2/usecases/sync-strategies/adapters/layout-delete.adapter.ts
  class LayoutDeleteAdapter (line 11) | class LayoutDeleteAdapter implements IBaseDeleteService<LayoutEntity> {
    method constructor (line 12) | constructor(private readonly deleteLayoutUseCase: DeleteLayoutUseCase) {}
    method deleteResourceFromTarget (line 14) | async deleteResourceFromTarget(context: ISyncContext, resource: Layout...

FILE: apps/api/src/app/environments-v2/usecases/sync-strategies/adapters/layout-repository.adapter.ts
  class LayoutRepositoryAdapter (line 7) | class LayoutRepositoryAdapter implements IBaseRepositoryService<LayoutEn...
    method constructor (line 8) | constructor(private readonly layoutRepositoryService: LayoutRepository...
    method fetchSyncableResources (line 10) | async fetchSyncableResources(environmentId: string, organizationId: st...
    method createResourceMap (line 14) | createResourceMap(resources: LayoutEntity[]): Map<string, LayoutEntity> {
    method getResourceIdentifier (line 18) | getResourceIdentifier(resource: LayoutEntity): string {

FILE: apps/api/src/app/environments-v2/usecases/sync-strategies/adapters/layout-sync.adapter.ts
  class LayoutSyncAdapter (line 11) | class LayoutSyncAdapter implements IBaseSyncService<LayoutEntity> {
    method constructor (line 12) | constructor(private readonly layoutSyncToEnvironmentUseCase: LayoutSyn...
    method syncResourceToTarget (line 14) | async syncResourceToTarget(context: ISyncContext, resource: LayoutEnti...

FILE: apps/api/src/app/environments-v2/usecases/sync-strategies/adapters/workflow-comparator.adapter.ts
  class WorkflowComparatorAdapter (line 10) | class WorkflowComparatorAdapter implements IBaseComparator<NotificationT...
    method constructor (line 11) | constructor(private readonly workflowComparator: WorkflowComparator) {}
    method compareResources (line 13) | async compareResources(

FILE: apps/api/src/app/environments-v2/usecases/sync-strategies/adapters/workflow-delete.adapter.ts
  class WorkflowDeleteAdapter (line 9) | class WorkflowDeleteAdapter implements IBaseDeleteService<NotificationTe...
    method constructor (line 10) | constructor(private readonly deleteWorkflowUseCase: DeleteWorkflowUseC...
    method deleteResourceFromTarget (line 12) | async deleteResourceFromTarget(context: ISyncContext, resource: Notifi...

FILE: apps/api/src/app/environments-v2/usecases/sync-strategies/adapters/workflow-repository.adapter.ts
  class WorkflowRepositoryAdapter (line 7) | class WorkflowRepositoryAdapter implements IBaseRepositoryService<Notifi...
    method constructor (line 8) | constructor(private readonly workflowRepositoryService: WorkflowReposi...
    method fetchSyncableResources (line 10) | async fetchSyncableResources(environmentId: string, organizationId: st...
    method createResourceMap (line 14) | createResourceMap(resources: NotificationTemplateEntity[]): Map<string...
    method getResourceIdentifier (line 18) | getResourceIdentifier(resource: NotificationTemplateEntity): string {

FILE: apps/api/src/app/environments-v2/usecases/sync-strategies/adapters/workflow-sync.adapter.ts
  class WorkflowSyncAdapter (line 9) | class WorkflowSyncAdapter implements IBaseSyncService<NotificationTempla...
    method constructor (line 10) | constructor(private readonly syncToEnvironmentUseCase: SyncToEnvironme...
    method syncResourceToTarget (line 12) | async syncResourceToTarget(context: ISyncContext, resource: Notificati...

FILE: apps/api/src/app/environments-v2/usecases/sync-strategies/base/base-sync.strategy.ts
  method constructor (line 8) | constructor(protected logger: PinoLogger) {
  method processBatch (line 22) | protected async processBatch<T>(

FILE: apps/api/src/app/environments-v2/usecases/sync-strategies/base/interfaces/base-comparator.interface.ts
  type IBaseComparator (line 5) | interface IBaseComparator<T> {

FILE: apps/api/src/app/environments-v2/usecases/sync-strategies/base/interfaces/base-delete.interface.ts
  type IBaseDeleteService (line 3) | interface IBaseDeleteService<T> {

FILE: apps/api/src/app/environments-v2/usecases/sync-strategies/base/interfaces/base-repository.interface.ts
  type IBaseRepositoryService (line 1) | interface IBaseRepositoryService<T> {

FILE: apps/api/src/app/environments-v2/usecases/sync-strategies/base/interfaces/base-sync.interface.ts
  type IBaseSyncService (line 3) | interface IBaseSyncService<T> {

FILE: apps/api/src/app/environments-v2/usecases/sync-strategies/base/operations/base-diff.operation.ts
  method constructor (line 10) | constructor(
  method getStartingDiffMessage (line 21) | private getStartingDiffMessage(sourceEnvId: string, targetEnvId: string)...
  method getDiffCompleteFailedMessage (line 25) | private getDiffCompleteFailedMessage(error: string): string {
  method execute (line 30) | async execute(
  method processResourceDiffs (line 63) | private async processResourceDiffs(
  method processBatch (line 86) | private async processBatch(
  method createBatches (line 137) | private createBatches<U>(items: U[], batchSize: number): U[][] {
  method processDeletedResources (line 147) | private async processDeletedResources(
  method handleNewResource (line 167) | protected async handleNewResource(
  method createResourceDiffs (line 180) | private createResourceDiffs(

FILE: apps/api/src/app/environments-v2/usecases/sync-strategies/base/operations/base-sync.operation.ts
  type IResourceSyncDecision (line 13) | interface IResourceSyncDecision<T> {
  method constructor (line 24) | constructor(
  method getAvailableResourceIds (line 36) | async getAvailableResourceIds(sourceEnvironmentId: string, organizationI...
  method getResourceTypeMessage (line 42) | private getResourceTypeMessage(): string {
  method getStartingSyncMessage (line 46) | private getStartingSyncMessage(sourceEnvId: string, targetEnvId: string)...
  method getFoundResourcesMessage (line 50) | private getFoundResourcesMessage(count: number): string {
  method getDryRunMessage (line 54) | private getDryRunMessage(): string {
  method getSyncCompleteFailedMessage (line 58) | private getSyncCompleteFailedMessage(error: string): string {
  method getSyncSuccessMessage (line 62) | private getSyncSuccessMessage(resourceName: string, action: string): str...
  method getSyncSkipMessage (line 66) | private getSyncSkipMessage(resourceName: string, action: string): string {
  method getSyncFailedMessage (line 70) | private getSyncFailedMessage(resourceName: string, error: string): string {
  method getDeleteSuccessMessage (line 74) | private getDeleteSuccessMessage(resourceName: string): string {
  method getDeleteFailedMessage (line 78) | private getDeleteFailedMessage(resourceName: string, error: string): str...
  method execute (line 83) | async execute(context: ISyncContext): Promise<ISyncResult> {
  method filterResourcesForSelectiveSync (line 125) | private filterResourcesForSelectiveSync(sourceResources: T[], resources:...
  method syncResources (line 144) | private async syncResources(
  method determineSyncDecisions (line 194) | private async determineSyncDecisions(
  method processSyncDecisionBatch (line 218) | private async processSyncDecisionBatch(
  method createBatches (line 241) | private createBatches<U>(items: U[], batchSize: number): U[][] {
  method handleDeletedResources (line 251) | private async handleDeletedResources(
  method shouldSyncResource (line 292) | private async shouldSyncResource(

FILE: apps/api/src/app/environments-v2/usecases/sync-strategies/builders/diff-result.builder.ts
  class DiffResultBuilder (line 10) | class DiffResultBuilder {
    method constructor (line 13) | constructor(private readonly resourceType: ResourceTypeEnum) {}
    method addResourceDiff (line 15) | addResourceDiff(
    method addResourceAdded (line 33) | addResourceAdded(sourceResource: IResourceInfo): this {
    method addResourceDeleted (line 52) | addResourceDeleted(targetResource: IResourceInfo): this {
    method addWorkflowDiff (line 72) | addWorkflowDiff(
    method addWorkflowAdded (line 106) | addWorkflowAdded(
    method addWorkflowDeleted (line 122) | addWorkflowDeleted(
    method build (line 138) | build(): IDiffResult[] {
    method getStats (line 142) | getStats() {
    method calculateSummaryForResource (line 161) | private calculateSummaryForResource(
    method calculateSummary (line 185) | private calculateSummary(diffs: IResourceDiff[]) {

FILE: apps/api/src/app/environments-v2/usecases/sync-strategies/builders/sync-result.builder.ts
  class SyncResultBuilder (line 10) | class SyncResultBuilder {
    method constructor (line 15) | constructor(private readonly resourceType: ResourceTypeEnum) {}
    method addSuccess (line 17) | addSuccess(resourceId: string, resourceName: string, action: SyncActio...
    method addFailure (line 28) | addFailure(resourceId: string, resourceName: string, error: string, st...
    method addSkipped (line 40) | addSkipped(resourceId: string, resourceName: string, reason: string): ...
    method addSuccessfulEntities (line 51) | addSuccessfulEntities(entities: ISyncedEntity[]): this {
    method addFailedEntities (line 57) | addFailedEntities(entities: IFailedEntity[]): this {
    method addSkippedEntities (line 63) | addSkippedEntities(entities: ISkippedEntity[]): this {
    method build (line 69) | build(): ISyncResult {
    method getStats (line 79) | getStats() {

FILE: apps/api/src/app/environments-v2/usecases/sync-strategies/comparators/layout.comparator.ts
  class LayoutComparator (line 9) | class LayoutComparator {
    method constructor (line 10) | constructor(
    method compareLayouts (line 16) | async compareLayouts(sourceLayout: LayoutEntity, targetLayout: LayoutE...

FILE: apps/api/src/app/environments-v2/usecases/sync-strategies/comparators/workflow.comparator.ts
  class WorkflowComparator (line 19) | class WorkflowComparator {
    method constructor (line 20) | constructor(
    method compareWorkflows (line 28) | async compareWorkflows(
    method getLocalizationDiffs (line 102) | private async getLocalizationDiffs(
    method compareStepsAsEntities (line 134) | compareStepsAsEntities(sourceSteps: INormalizedStep[], targetSteps: IN...
    method compareIndividualStep (line 179) | private compareIndividualStep(
    method createStepAddedDiff (line 198) | private createStepAddedDiff(sourceStep: INormalizedStep, sourceIndex: ...
    method createStepModifiedDiff (line 218) | private createStepModifiedDiff(
    method createStepMovedDiff (line 250) | private createStepMovedDiff(
    method createStepDeletedDiff (line 277) | private createStepDeletedDiff(targetStep: INormalizedStep, targetIndex...

FILE: apps/api/src/app/environments-v2/usecases/sync-strategies/constants/sync.constants.ts
  constant SYNC_ACTIONS (line 3) | const SYNC_ACTIONS = {
  constant SKIP_REASONS (line 10) | const SKIP_REASONS = {

FILE: apps/api/src/app/environments-v2/usecases/sync-strategies/layout-sync.strategy.ts
  class LayoutSyncStrategy (line 10) | class LayoutSyncStrategy extends BaseSyncStrategy {
    method constructor (line 11) | constructor(
    method getResourceType (line 19) | getResourceType(): ResourceTypeEnum {
    method execute (line 23) | async execute(context: ISyncContext): Promise<ISyncResult> {
    method diff (line 27) | async diff(
    method getAvailableResourceIds (line 36) | async getAvailableResourceIds(sourceEnvironmentId: string, organizatio...

FILE: apps/api/src/app/environments-v2/usecases/sync-strategies/normalizers/layout.normalizer.ts
  class LayoutNormalizer (line 6) | class LayoutNormalizer {
    method normalizeLayout (line 11) | normalizeLayout(layout: LayoutResponseDto): INormalizedLayout {

FILE: apps/api/src/app/environments-v2/usecases/sync-strategies/normalizers/workflow.normalizer.ts
  class WorkflowNormalizer (line 6) | class WorkflowNormalizer {
    method normalizeWorkflow (line 11) | normalizeWorkflow(workflow: WorkflowResponseDto): INormalizedWorkflow {
    method normalizeStep (line 36) | normalizeStep(step: StepResponseDto): INormalizedStep {

FILE: apps/api/src/app/environments-v2/usecases/sync-strategies/operations/layout-diff.operation.ts
  class LayoutDiffOperation (line 10) | class LayoutDiffOperation extends BaseDiffOperation<LayoutEntity> {
    method constructor (line 11) | constructor(
    method getResourceType (line 19) | protected getResourceType(): ResourceTypeEnum {
    method getResourceName (line 23) | protected getResourceName(resource: LayoutEntity): string {
    method extractUpdatedByInfo (line 27) | protected extractUpdatedByInfo(resource: LayoutEntity): IUserInfo | nu...
    method extractUpdatedAtInfo (line 40) | protected extractUpdatedAtInfo(resource: LayoutEntity): string | null {

FILE: apps/api/src/app/environments-v2/usecases/sync-strategies/operations/layout-repository.service.ts
  class LayoutRepositoryService (line 5) | class LayoutRepositoryService {
    method constructor (line 6) | constructor(private layoutRepository: LayoutRepository) {}
    method fetchSyncableLayouts (line 8) | async fetchSyncableLayouts(environmentId: string, organizationId: stri...
    method getLayoutIdentifier (line 12) | getLayoutIdentifier(layout: LayoutEntity): string {
    method createLayoutMap (line 16) | createLayoutMap(layouts: LayoutEntity[]): Map<string, LayoutEntity> {

FILE: apps/api/src/app/environments-v2/usecases/sync-strategies/operations/layout-sync.operation.ts
  class LayoutSyncOperation (line 12) | class LayoutSyncOperation extends BaseSyncOperation<LayoutEntity> {
    method constructor (line 13) | constructor(
    method getResourceType (line 23) | protected getResourceType(): ResourceTypeEnum {
    method getResourceName (line 27) | protected getResourceName(resource: LayoutEntity): string {

FILE: apps/api/src/app/environments-v2/usecases/sync-strategies/operations/workflow-diff.operation.ts
  class WorkflowDiffOperation (line 12) | class WorkflowDiffOperation extends BaseDiffOperation<NotificationTempla...
    method constructor (line 13) | constructor(
    method getResourceType (line 23) | protected getResourceType(): ResourceTypeEnum {
    method execute (line 27) | async execute(
    method getWorkflowDiffStartMessage (line 67) | private getWorkflowDiffStartMessage(sourceEnvId: string, targetEnvId: ...
    method getWorkflowDiffFailedMessage (line 71) | private getWorkflowDiffFailedMessage(error: string): string {
    method processWorkflowResourceDiffs (line 75) | private async processWorkflowResourceDiffs(
    method processWorkflowBatch (line 97) | private async processWorkflowBatch(
    method createWorkflowBatches (line 155) | private createWorkflowBatches<U>(items: U[], batchSize: number): U[][] {
    method handleNewWorkflowResource (line 165) | private async handleNewWorkflowResource(
    method createWorkflowResourceDiffs (line 198) | private createWorkflowResourceDiffs(
    method processDeletedWorkflowResources (line 252) | private async processDeletedWorkflowResources(
    method getResourceName (line 272) | protected getResourceName(resource: NotificationTemplateEntity): string {
    method extractUpdatedByInfo (line 276) | protected extractUpdatedByInfo(resource: NotificationTemplateEntity): ...
    method extractUpdatedAtInfo (line 289) | protected extractUpdatedAtInfo(resource: NotificationTemplateEntity): ...
    method extractStepsFromNewWorkflow (line 297) | private async extractStepsFromNewWorkflow(

FILE: apps/api/src/app/environments-v2/usecases/sync-strategies/operations/workflow-repository.service.ts
  class WorkflowRepositoryService (line 5) | class WorkflowRepositoryService {
    method constructor (line 6) | constructor(private notificationTemplateRepository: NotificationTempla...
    method fetchSyncableWorkflows (line 8) | async fetchSyncableWorkflows(environmentId: string, organizationId: st...
    method getWorkflowIdentifier (line 12) | getWorkflowIdentifier(workflow: NotificationTemplateEntity): string {
    method createWorkflowMap (line 16) | createWorkflowMap(workflows: NotificationTemplateEntity[]): Map<string...

FILE: apps/api/src/app/environments-v2/usecases/sync-strategies/operations/workflow-sync.operation.ts
  class WorkflowSyncOperation (line 14) | class WorkflowSyncOperation extends BaseSyncOperation<NotificationTempla...
    method constructor (line 15) | constructor(
    method getResourceType (line 25) | protected getResourceType(): ResourceTypeEnum {
    method getResourceName (line 29) | protected getResourceName(resource: NotificationTemplateEntity): string {

FILE: apps/api/src/app/environments-v2/usecases/sync-strategies/sync.module.ts
  class SyncModule (line 82) | class SyncModule {}

FILE: apps/api/src/app/environments-v2/usecases/sync-strategies/types/layout-sync.types.ts
  type INormalizedLayout (line 3) | type INormalizedLayout = Omit<
  type ILayoutComparison (line 15) | interface ILayoutComparison {

FILE: apps/api/src/app/environments-v2/usecases/sync-strategies/types/workflow-sync.types.ts
  type INormalizedWorkflow (line 4) | type INormalizedWorkflow = Omit<
  type INormalizedStep (line 20) | type INormalizedStep = Omit<
  type IWorkflowComparison (line 32) | interface IWorkflowComparison {

FILE: apps/api/src/app/environments-v2/usecases/sync-strategies/workflow-sync.strategy.ts
  class WorkflowSyncStrategy (line 10) | class WorkflowSyncStrategy extends BaseSyncStrategy {
    method constructor (line 11) | constructor(
    method getResourceType (line 19) | getResourceType(): ResourceTypeEnum {
    method execute (line 23) | async execute(context: ISyncContext): Promise<ISyncResult> {
    method diff (line 27) | async diff(
    method getAvailableResourceIds (line 47) | async getAvailableResourceIds(sourceEnvironmentId: string, organizatio...

FILE: apps/api/src/app/events/dtos/test-email-request.dto.ts
  class TestSendEmailRequestDto (line 4) | class TestSendEmailRequestDto {

FILE: apps/api/src/app/events/dtos/trigger-event-request.dto.ts
  class WorkflowToStepControlValuesDto (line 27) | class WorkflowToStepControlValuesDto {
  class SubscriberPayloadDto (line 47) | class SubscriberPayloadDto extends CreateSubscriberRequestDto {}
  class TenantPayloadDto (line 48) | class TenantPayloadDto extends UpdateTenantRequestDto {}
  class TopicPayloadDto (line 50) | class TopicPayloadDto {
  class StepsOverrides (line 71) | class StepsOverrides {
  class EmailChannelOverrides (line 98) | class EmailChannelOverrides {
  class ChannelOverrides (line 110) | class ChannelOverrides {
  class TriggerOverrides (line 118) | class TriggerOverrides {
  class TriggerEventRequestDto (line 219) | class TriggerEventRequestDto {
  class BulkTriggerEventDto (line 356) | class BulkTriggerEventDto {

FILE: apps/api/src/app/events/dtos/trigger-event-response.dto.ts
  class TriggerEventResponseDto (line 6) | class TriggerEventResponseDto {

FILE: apps/api/src/app/events/dtos/trigger-event-to-all-request.dto.ts
  class TriggerEventToAllRequestDto (line 8) | class TriggerEventToAllRequestDto {

FILE: apps/api/src/app/events/e2e/bridge-trigger.e2e.ts
  type Context (line 30) | type Context = { name: string; isStateful: boolean };
  function syncWorkflow (line 2129) | async function syncWorkflow(
  function triggerEvent (line 2145) | async function triggerEvent(
  function discoverAndSyncBridge (line 2179) | async function discoverAndSyncBridge(
  function saveControlValues (line 2203) | async function saveControlValues(
  function markAllSubscriberMessagesAs (line 2212) | async function markAllSubscriberMessagesAs(session: UserSession, subscri...

FILE: apps/api/src/app/events/e2e/cancel-event.e2e.ts
  function cancelEvent (line 20) | async function cancelEvent(transactionId: string) {

FILE: apps/api/src/app/events/e2e/context-events.e2e.ts
  function sendTrigger (line 79) | async function sendTrigger(

FILE: apps/api/src/app/events/e2e/process-subscriber.e2e.ts
  function triggerEvent (line 201) | async function triggerEvent(session: UserSession, template: Notification...
  function updateSubscriberPreference (line 212) | async function updateSubscriberPreference(

FILE: apps/api/src/app/events/e2e/send-message-push.e2e.ts
  function triggerEvent (line 316) | async function triggerEvent(template2) {
  function updateCredentials (line 323) | async function updateCredentials(subscriberId: string, providerId: PushP...

FILE: apps/api/src/app/events/e2e/trigger-event-to-all.e2e.ts
  constant TOPIC_PATH (line 32) | const TOPIC_PATH = '/v1/topics';
  constant TOPIC_KEY_PREFIX (line 33) | const TOPIC_KEY_PREFIX = 'topic-key-trigger-event_';
  constant TOPIC_NAME_PREFIX (line 34) | const TOPIC_NAME_PREFIX = 'topic-name-trigger-event_';
  class MockSubscriberProcessQueueService (line 50) | class MockSubscriberProcessQueueService {
    method addBulk (line 51) | addBulk(data: IProcessSubscriberBulkJobDto[]) {}
  function mapSubscriberToSubscriberDefine (line 54) | function mapSubscriberToSubscriberDefine(firstTopicSubscribers: Subscrib...
  function expectBulkTopicStub (line 58) | function expectBulkTopicStub(secondCallStubArgs: IProcessSubscriberBulkJ...
  function expectBulkSingleSubscriberStub (line 78) | function expectBulkSingleSubscriberStub(
  function initializeTopic (line 138) | async function initializeTopic(subscribersToAdd: SubscriberEntity[], top...
  type TriggerMulticastCommandOverrides (line 445) | type TriggerMulticastCommandOverrides = Partial<TriggerMulticastCommand>...

FILE: apps/api/src/app/events/e2e/trigger-event.e2e.ts
  function sendTrigger (line 3272) | async function sendTrigger(
  function createScheduleOutsideCurrentTime (line 4000) | function createScheduleOutsideCurrentTime(timezone: string = 'America/Ne...
  function createScheduleIncludingCurrentTime (line 4034) | function createScheduleIncludingCurrentTime(timezone: string = 'America/...
  function createTemplate (line 4903) | async function createTemplate(session, channelType) {
  function createSimpleWorkflow (line 4913) | async function createSimpleWorkflow(session) {
  function simpleTrigger (line 4924) | function simpleTrigger(novuClient: Novu, template, subscriberID: string) {

FILE: apps/api/src/app/events/e2e/utils/poll-for-job-status-change.util.ts
  type EnforceEnvOrOrgIds (line 4) | type EnforceEnvOrOrgIds = { _environmentId: string } | { _organizationId...
  type IPollForJobOptions (line 6) | interface IPollForJobOptions {
  function pollForJobStatusChange (line 22) | async function pollForJobStatusChange({

FILE: apps/api/src/app/events/e2e/utils/sleep.util.ts
  function sleep (line 1) | function sleep(ms: number): Promise<void> {

FILE: apps/api/src/app/events/events.controller.ts
  function RequestAnalytics (line 42) | function RequestAnalytics(strategy: AnalyticsStrategyEnum = AnalyticsStr...
  class EventsController (line 61) | class EventsController {
    method constructor (line 62) | constructor(
    method checkKillSwitch (line 71) | private async checkKillSwitch(user: UserSessionData): Promise<void> {
    method trigger (line 104) | async trigger(
    method triggerBulk (line 155) | async triggerBulk(
    method broadcastEventToAll (line 195) | async broadcastEventToAll(
    method testEmailMessage (line 222) | async testEmailMessage(@UserSession() user: UserSessionData, @Body() b...
    method cancel (line 259) | async cancel(@UserSession() user: UserSessionData, @Param('transaction...

FILE: apps/api/src/app/events/events.module.ts
  constant PROVIDERS (line 20) | const PROVIDERS = [GetNovuProviderCredentials, StorageHelperService, Com...
  class EventsModule (line 39) | class EventsModule {}

FILE: apps/api/src/app/events/exceptions/payload-validation-exception.ts
  type IPayloadValidationError (line 4) | interface IPayloadValidationError {
  class PayloadValidationException (line 11) | class PayloadValidationException extends BadRequestException {
    method constructor (line 12) | constructor(
    method fromAjvErrors (line 26) | static fromAjvErrors(ajvErrors: ErrorObject[], payload: any, schema: a...

FILE: apps/api/src/app/events/usecases/cancel-delayed/cancel-delayed.command.ts
  class CancelDelayedCommand (line 4) | class CancelDelayedCommand extends EnvironmentWithUserCommand {

FILE: apps/api/src/app/events/usecases/cancel-delayed/cancel-delayed.usecase.ts
  class CancelDelayed (line 18) | class CancelDelayed {
    method constructor (line 19) | constructor(
    method execute (line 28) | public async execute(command: CancelDelayedCommand): Promise<boolean> {
    method assignNextDigestJob (line 82) | private async assignNextDigestJob(job: JobEntity) {
    method recordCancellationTraces (line 145) | private async recordCancellationTraces(jobs: JobEntity[]): Promise<voi...
    method mapStepTypeEnumToStepType (line 179) | private mapStepTypeEnumToStepType(stepType: StepTypeEnum | undefined):...

FILE: apps/api/src/app/events/usecases/index.ts
  constant USE_CASES (line 7) | const USE_CASES = [CancelDelayed, TriggerEventToAll, ParseEventRequest, ...

FILE: apps/api/src/app/events/usecases/parse-event-request/parse-event-request.command.ts
  class ParseEventRequestBaseCommand (line 16) | class ParseEventRequestBaseCommand extends EnvironmentWithUserCommand {
  class ParseEventRequestMulticastCommand (line 71) | class ParseEventRequestMulticastCommand extends ParseEventRequestBaseCom...
  class ParseEventRequestBroadcastCommand (line 79) | class ParseEventRequestBroadcastCommand extends ParseEventRequestBaseCom...
  type ParseEventRequestCommand (line 84) | type ParseEventRequestCommand = ParseEventRequestMulticastCommand | Pars...

FILE: apps/api/src/app/events/usecases/parse-event-request/parse-event-request.usecase.ts
  function getSchemaHash (line 56) | function getSchemaHash(schema: object): string {
  type ParseEventRequestResult (line 60) | type ParseEventRequestResult = {
  class ParseEventRequest (line 69) | class ParseEventRequest {
    method constructor (line 70) | constructor(
    method execute (line 87) | public async execute(command: ParseEventRequestCommand): Promise<Parse...
    method createRequestTrace (line 231) | private async createRequestTrace({
    method queryDiscoverWorkflow (line 292) | private async queryDiscoverWorkflow(command: ParseEventRequestCommand)...
    method dispatchEventToWorkflowQueue (line 310) | private async dispatchEventToWorkflowQueue({
    method isStatelessWorkflowAllowed (line 394) | private isStatelessWorkflowAllowed(bridgeUrl: string | undefined) {
    method getNotificationTemplateByTriggerIdentifier (line 403) | private async getNotificationTemplateByTriggerIdentifier(command: {
    method modifyAttachments (line 418) | private modifyAttachments(command: ParseEventRequestCommand): void {
    method validateItem (line 439) | private validateItem(item: unknown, invalidValues: unknown[]) {
    method parseRecipients (line 466) | private parseRecipients(input: unknown) {
    method validateAndApplyPayloadDefaults (line 489) | private validateAndApplyPayloadDefaults(payload: Record<string, unknow...
    method getCompiledValidator (line 501) | private getCompiledValidator(schema: object): ValidateFunction {

FILE: apps/api/src/app/events/usecases/process-bulk-trigger/process-bulk-trigger.command.ts
  class ProcessBulkTriggerCommand (line 5) | class ProcessBulkTriggerCommand extends EnvironmentWithUserCommand {

FILE: apps/api/src/app/events/usecases/process-bulk-trigger/process-bulk-trigger.usecase.ts
  class ProcessBulkTrigger (line 11) | class ProcessBulkTrigger {
    method constructor (line 12) | constructor(
    method execute (line 18) | async execute(command: ProcessBulkTriggerCommand) {

FILE: apps/api/src/app/events/usecases/send-test-email/send-test-email.command.ts
  class SendTestEmailCommand (line 5) | class SendTestEmailCommand extends EnvironmentWithUserCommand {

FILE: apps/api/src/app/events/usecases/send-test-email/send-test-email.usecase.ts
  class SendTestEmail (line 20) | class SendTestEmail {
    method constructor (line 21) | constructor(
    method execute (line 31) | public async execute(command: SendTestEmailCommand) {
    method sendMessage (line 132) | private async sendMessage(
    method getSystemVariables (line 155) | private getSystemVariables(variableType: 'subscriber' | 'step' | 'bran...

FILE: apps/api/src/app/events/usecases/trigger-event-to-all/trigger-event-to-all.command.ts
  class TriggerEventToAllCommand (line 7) | class TriggerEventToAllCommand extends EnvironmentWithUserCommand {

FILE: apps/api/src/app/events/usecases/trigger-event-to-all/trigger-event-to-all.usecase.ts
  class TriggerEventToAll (line 7) | class TriggerEventToAll {
    method constructor (line 8) | constructor(private parseEventRequest: ParseEventRequest) {}
    method execute (line 10) | public async execute(command: TriggerEventToAllCommand) {

FILE: apps/api/src/app/execution-details/dtos/execution-details-request.dto.ts
  class ExecutionDetailsRequestDto (line 3) | class ExecutionDetailsRequestDto {

FILE: apps/api/src/app/execution-details/execution-details.controller.ts
  class ExecutionDetailsController (line 18) | class ExecutionDetailsController {
    method constructor (line 19) | constructor(private getExecutionDetails: GetExecutionDetails) {}
    method getExecutionDetailsForNotification (line 27) | async getExecutionDetailsForNotification(

FILE: apps/api/src/app/execution-details/execution-details.module.ts
  class ExecutionDetailsModule (line 13) | class ExecutionDetailsModule {}

FILE: apps/api/src/app/execution-details/usecases/get-execution-details/get-execution-details.command.ts
  class GetExecutionDetailsCommand (line 4) | class GetExecutionDetailsCommand extends EnvironmentWithUserCommand {

FILE: apps/api/src/app/execution-details/usecases/get-execution-details/get-execution-details.usecase.ts
  class GetExecutionDetails (line 7) | class GetExecutionDetails {
    method constructor (line 8) | constructor(
    method execute (line 13) | async execute(command: GetExecutionDetailsCommand): Promise<ExecutionD...
    method fetchSubscriber (line 36) | private async fetchSubscriber({

FILE: apps/api/src/app/execution-details/usecases/index.ts
  constant USE_CASES (line 5) | const USE_CASES = [CreateExecutionDetails, BulkCreateExecutionDetails, G...

FILE: apps/api/src/app/feeds/dtos/create-feed-request.dto.ts
  class CreateFeedRequestDto (line 4) | class CreateFeedRequestDto {

FILE: apps/api/src/app/feeds/dtos/feed-response.dto.ts
  class FeedResponseDto (line 3) | class FeedResponseDto {

FILE: apps/api/src/app/feeds/feeds.controller.ts
  class FeedsController (line 33) | class FeedsController {
    method constructor (line 34) | constructor(
    method createFeed (line 46) | createFeed(@UserSession() user: UserSessionData, @Body() body: CreateF...
    method getFeeds (line 63) | getFeeds(@UserSession() user: UserSessionData): Promise<FeedResponseDt...
    method deleteFeedById (line 79) | deleteFeedById(@UserSession() user: UserSessionData, @Param('feedId') ...

FILE: apps/api/src/app/feeds/feeds.module.ts
  class FeedsModule (line 15) | class FeedsModule {}

FILE: apps/api/src/app/feeds/usecases/create-feed/create-feed.command.ts
  class CreateFeedCommand (line 4) | class CreateFeedCommand extends EnvironmentWithUserCommand {

FILE: apps/api/src/app/feeds/usecases/create-feed/create-feed.usecase.ts
  class CreateFeed (line 8) | class CreateFeed {
    method constructor (line 9) | constructor(
    method execute (line 14) | async execute(command: CreateFeedCommand): Promise<FeedEntity> {

FILE: apps/api/src/app/feeds/usecases/delete-feed/delete-feed.command.ts
  class DeleteFeedCommand (line 4) | class DeleteFeedCommand extends EnvironmentWithUserCommand {

FILE: apps/api/src/app/feeds/usecases/delete-feed/delete-feed.usecase.ts
  class DeleteFeed (line 8) | class DeleteFeed {
    method constructor (line 9) | constructor(
    method execute (line 15) | async execute(command: DeleteFeedCommand) {

FILE: apps/api/src/app/feeds/usecases/get-feeds/get-feeds.command.ts
  class GetFeedsCommand (line 3) | class GetFeedsCommand extends EnvironmentWithUserCommand {}

FILE: apps/api/src/app/feeds/usecases/get-feeds/get-feeds.usecase.ts
  class GetFeeds (line 6) | class GetFeeds {
    method constructor (line 7) | constructor(private feedsRepository: FeedRepository) {}
    method execute (line 9) | async execute(command: GetFeedsCommand): Promise<FeedEntity[]> {

FILE: apps/api/src/app/feeds/usecases/index.ts
  constant USE_CASES (line 5) | const USE_CASES = [CreateFeed, GetFeeds, DeleteFeed];

FILE: apps/api/src/app/health/health.controller.ts
  class HealthController (line 24) | class HealthController {
    method constructor (line 25) | constructor(
    method healthCheck (line 34) | healthCheck(): Promise<HealthCheckResult> {
    method testIdempotency (line 61) | async testIdempotency(@Body() body: IdempotencyTestingDto): Promise<Id...
    method generateRandomNumber (line 88) | async generateRandomNumber(): Promise<IdempotenceTestingResponse> {

FILE: apps/api/src/app/health/health.module.ts
  class HealthModule (line 11) | class HealthModule {}

FILE: apps/api/src/app/inbound-parse/dtos/get-mx-record.dto.ts
  class GetMxRecordResponseDto (line 3) | class GetMxRecordResponseDto {

FILE: apps/api/src/app/inbound-parse/inbound-parse.controller.ts
  class InboundParseController (line 19) | class InboundParseController {
    method constructor (line 20) | constructor(
    method getMxRecordStatus (line 33) | async getMxRecordStatus(@UserSession() user: UserSessionData): Promise...

FILE: apps/api/src/app/inbound-parse/inbound-parse.module.ts
  constant PROVIDERS (line 8) | const PROVIDERS = [CompileTemplate];
  class InboundParseModule (line 26) | class InboundParseModule implements NestModule, OnApplicationShutdown {
    method constructor (line 27) | constructor(private workflowInMemoryProviderService: WorkflowInMemoryP...
    method configure (line 28) | configure(consumer: MiddlewareConsumer): MiddlewareConsumer | void {}
    method onApplicationShutdown (line 30) | async onApplicationShutdown() {

FILE: apps/api/src/app/inbound-parse/usecases/get-mx-record/get-mx-record.command.ts
  class GetMxRecordCommand (line 3) | class GetMxRecordCommand extends EnvironmentCommand {}

FILE: apps/api/src/app/inbound-parse/usecases/get-mx-record/get-mx-record.usecase.ts
  class GetMxRecord (line 10) | class GetMxRecord {
    method constructor (line 11) | constructor(private environmentRepository: EnvironmentRepository) {}
    method execute (line 13) | async execute(command: GetMxRecordCommand): Promise<GetMxRecordRespons...
    method updateMxRecord (line 32) | private async updateMxRecord(mxRecordExist: boolean, command: GetMxRec...
    method checkMxRecordExistence (line 46) | private async checkMxRecordExistence(inboundParseDomain: string) {
    method getMxRecords (line 56) | async getMxRecords(domain: string): Promise<MxRecord[]> {

FILE: apps/api/src/app/inbound-parse/usecases/index.ts
  constant USE_CASES (line 3) | const USE_CASES = [GetMxRecord];

FILE: apps/api/src/app/inbox/dtos/action-type-request.dto.ts
  class ActionTypeRequestDto (line 4) | class ActionTypeRequestDto {

FILE: apps/api/src/app/inbox/dtos/bulk-update-preferences-request.dto.ts
  class BulkUpdatePreferenceItemDto (line 6) | class BulkUpdatePreferenceItemDto extends UpdatePreferencesRequestDto {
  class BulkUpdatePreferencesRequestDto (line 16) | class BulkUpdatePreferencesRequestDto {

FILE: apps/api/src/app/inbox/dtos/create-topic-subscription-request.dto.ts
  class TopicIdentifierDto (line 9) | class TopicIdentifierDto {
  class CreateTopicSubscriptionRequestDto (line 20) | class CreateTopicSubscriptionRequestDto {

FILE: apps/api/src/app/inbox/dtos/get-notifications-count-request.dto.ts
  class NotificationsFilter (line 17) | class NotificationsFilter implements NotificationFilter {
  class GetNotificationsCountRequestDto (line 44) | class GetNotificationsCountRequestDto {

FILE: apps/api/src/app/inbox/dtos/get-notifications-count-response.dto.ts
  class GetNotificationsCountResponseDto (line 3) | class GetNotificationsCountResponseDto {

FILE: apps/api/src/app/inbox/dtos/get-notifications-request.dto.ts
  constant LIMIT (line 10) | const LIMIT = {
  class GetNotificationsRequestDto (line 15) | class GetNotificationsRequestDto

FILE: apps/api/src/app/inbox/dtos/get-notifications-response.dto.ts
  class GetNotificationsResponseDto (line 4) | class GetNotificationsResponseDto {

FILE: apps/api/src/app/inbox/dtos/get-preferences-request.dto.ts
  class GetPreferencesRequestDto (line 5) | class GetPreferencesRequestDto {

FILE: apps/api/src/app/inbox/dtos/get-preferences-response.dto.ts
  class GetPreferencesResponseDto (line 9) | class GetPreferencesResponseDto {

FILE: apps/api/src/app/inbox/dtos/inbox-notification.dto.ts
  class InboxSubscriberResponseDto (line 4) | class InboxSubscriberResponseDto {
  class RedirectDto (line 21) | class RedirectDto {
  class InboxActionDto (line 33) | class InboxActionDto {
  class NotificationWorkflowDto (line 44) | class NotificationWorkflowDto {
  class InboxNotificationDto (line 75) | class InboxNotificationDto {

FILE: apps/api/src/app/inbox/dtos/mark-notifications-as-seen-request.dto.ts
  class MarkNotificationsAsSeenRequestDto (line 3) | class MarkNotificationsAsSeenRequestDto {

FILE: apps/api/src/app/inbox/dtos/snooze-notification-request.dto.ts
  function IsFutureDate (line 4) | function IsFutureDate(
  class SnoozeNotificationRequestDto (line 37) | class SnoozeNotificationRequestDto {

FILE: apps/api/src/app/inbox/dtos/subscriber-session-request.dto.ts
  class SubscriberSessionRequestDto (line 7) | class SubscriberSessionRequestDto {
  class SubscriberDto (line 42) | class SubscriberDto {

FILE: apps/api/src/app/inbox/dtos/subscriber-session-response.dto.ts
  type SeverityCounts (line 3) | type SeverityCounts = {
  type UnreadCount (line 10) | type UnreadCount = {
  class SubscriberSessionResponseDto (line 15) | class SubscriberSessionResponseDto {

FILE: apps/api/src/app/inbox/dtos/update-all-notifications-request.dto.ts
  class UpdateAllNotificationsRequestDto (line 3) | class UpdateAllNotificationsRequestDto {

FILE: apps/api/src/app/inbox/dtos/update-preferences-request.dto.ts
  class UpdatePreferencesRequestDto (line 7) | class UpdatePreferencesRequestDto {

FILE: apps/api/src/app/inbox/dtos/workflow.dto.ts
  class WorkflowDto (line 5) | class WorkflowDto {

FILE: apps/api/src/app/inbox/e2e/context-aware-topic-subscriptions.e2e.ts
  constant CONTEXT_A (line 13) | const CONTEXT_A: ContextPayload = { tenant: 'tenant-a', project: 'projec...
  constant CONTEXT_B (line 14) | const CONTEXT_B: ContextPayload = { tenant: 'tenant-b', project: 'projec...
  function generateUniqueId (line 379) | function generateUniqueId(prefix: string): string {
  function initializeSessionWithContext (line 383) | async function initializeSessionWithContext(session: UserSession, contex...
  function setIntegrationConfig (line 391) | async function setIntegrationConfig(environmentId: string, organizationI...
  function createSubscription (line 409) | async function createSubscription(
  function getSubscription (line 421) | async function getSubscription(session: UserSession, topicKey: string, i...
  function getTopicSubscriptions (line 427) | async function getTopicSubscriptions(session: UserSession, topicKey: str...
  function updateSubscription (line 433) | async function updateSubscription(
  function deleteSubscription (line 446) | async function deleteSubscription(session: UserSession, topicKey: string...
  function updateSubscriptionPreferences (line 452) | async function updateSubscriptionPreferences(

FILE: apps/api/src/app/inbox/e2e/create-topic-subscription.e2e.ts
  function createSubscription (line 126) | async function createSubscription({

FILE: apps/api/src/app/inbox/e2e/get-topic-subscription.e2e.ts
  function extractWorkflowIdentifiers (line 280) | function extractWorkflowIdentifiers(preferences: Array<{ workflow: { ide...
  function createSubscription (line 284) | async function createSubscription({
  function getSubscription (line 299) | async function getSubscription(

FILE: apps/api/src/app/inbox/e2e/session.e2e.ts
  function setIntegrationConfig (line 1296) | async function setIntegrationConfig({

FILE: apps/api/src/app/inbox/e2e/update-subscription-workflow-preferences.e2e.ts
  function updateSubscriptionPreferences (line 228) | async function updateSubscriptionPreferences(
  function getTopicSubscriptions (line 240) | async function getTopicSubscriptions(session: UserSession, topicKey: str...
  function createSubscription (line 246) | async function createSubscription({

FILE: apps/api/src/app/inbox/inbox.controller.ts
  class InboxController (line 89) | class InboxController {
    method constructor (line 90) | constructor(
    method sessionInitialize (line 111) | async sessionInitialize(
    method getNotifications (line 125) | async getNotifications(
    method getNotificationsCount (line 153) | async getNotificationsCount(
    method getAllPreferences (line 172) | async getAllPreferences(
    method getSchedule (line 191) | async getSchedule(@SubscriberSession() subscriberSession: SubscriberSe...
    method markNotificationAsRead (line 211) | async markNotificationAsRead(
    method markNotificationAsUnread (line 229) | async markNotificationAsUnread(
    method markNotificationAsArchived (line 247) | async markNotificationAsArchived(
    method markNotificationAsUnarchived (line 265) | async markNotificationAsUnarchived(
    method snoozeNotification (line 283) | async snoozeNotification(
    method unsnoozeNotification (line 302) | async unsnoozeNotification(
    method deleteNotification (line 320) | async deleteNotification(
    method completeAction (line 337) | async completeAction(
    method revertAction (line 357) | async revertAction(
    method updateGlobalPreference (line 377) | async updateGlobalPreference(
    method bulkUpdateWorkflowPreferences (line 405) | async bulkUpdateWorkflowPreferences(
    method updateWorkflowPreference (line 422) | async updateWorkflowPreference(
    method updateSubscriptionWorkflowPreference (line 453) | async updateSubscriptionWorkflowPreference(
    method markNotificationsAsSeen (line 486) | async markNotificationsAsSeen(
    method markAllAsRead (line 506) | async markAllAsRead(
    method markAllAsArchived (line 530) | async markAllAsArchived(
    method markAllAsReadArchived (line 554) | async markAllAsReadArchived(
    method deleteAllNotifications (line 579) | async deleteAllNotifications(
    method keylessEvents (line 600) | async keylessEvents(

FILE: apps/api/src/app/inbox/inbox.module.ts
  class InboxModule (line 53) | class InboxModule {}

FILE: apps/api/src/app/inbox/inbox.topic.controller.ts
  class InboxTopicController (line 44) | class InboxTopicController {
    method constructor (line 45) | constructor(
    method getTopicSubscriptions (line 55) | async getTopicSubscriptions(
    method getTopicSubscription (line 73) | async getTopicSubscription(
    method createTopicSubscription (line 107) | async createTopicSubscription(
    method updateTopicSubscription (line 151) | async updateTopicSubscription(
    method deleteTopicSubscription (line 180) | async deleteTopicSubscription(
    method convertPreferencesToGroupFilters (line 198) | private convertPreferencesToGroupFilters(
    method isGroupPreferenceFilter (line 224) | private isGroupPreferenceFilter(

FILE: apps/api/src/app/inbox/interceptors/context-compatibility.interceptor.ts
  function parseClientVersion (line 8) | function parseClientVersion(clientVersion?: string): string | null {
  function isContextAwareVersion (line 18) | function isContextAwareVersion(version: string): boolean {
  function shouldDisableContextForOldClient (line 34) | function shouldDisableContextForOldClient(clientVersion?: string): boole...
  class ContextCompatibilityInterceptor (line 56) | class ContextCompatibilityInterceptor implements NestInterceptor {
    method intercept (line 57) | intercept(context: ExecutionContext, next: CallHandler): Observable<un...

FILE: apps/api/src/app/inbox/usecases/bulk-update-preferences/bulk-update-preferences.command.ts
  class BulkUpdatePreferencesCommand (line 11) | class BulkUpdatePreferencesCommand extends EnvironmentWithSubscriber {

FILE: apps/api/src/app/inbox/usecases/bulk-update-preferences/bulk-update-preferences.usecase.ts
  constant MAX_BULK_LIMIT (line 19) | const MAX_BULK_LIMIT = 100;
  class BulkUpdatePreferences (line 22) | class BulkUpdatePreferences {
    method constructor (line 23) | constructor(
    method execute (line 34) | async execute(command: BulkUpdatePreferencesCommand): Promise<InboxPre...
    method resolveContexts (line 145) | private async resolveContexts(

FILE: apps/api/src/app/inbox/usecases/delete-all-notifications/delete-all-notifications.command.ts
  class Filter (line 7) | class Filter implements NotificationFilter {
  class DeleteAllNotificationsCommand (line 26) | class DeleteAllNotificationsCommand extends EnvironmentWithSubscriber {

FILE: apps/api/src/app/inbox/usecases/delete-all-notifications/delete-all-notifications.usecase.ts
  class DeleteAllNotifications (line 20) | class DeleteAllNotifications {
    method constructor (line 21) | constructor(
    method execute (line 31) | async execute(command: DeleteAllNotificationsCommand): Promise<void> {
    method sendWebhookEvents (line 99) | private async sendWebhookEvents(command: DeleteAllNotificationsCommand...
    method processWebhooksInBatches (line 113) | private async processWebhooksInBatches(
    method chunkArray (line 133) | private chunkArray<T>(array: T[], chunkSize: number): T[][] {
    method createWebhookPromises (line 142) | private createWebhookPromises(

FILE: apps/api/src/app/inbox/usecases/delete-many-notifications/delete-many-notifications.command.ts
  class DeleteManyNotificationsCommand (line 5) | class DeleteManyNotificationsCommand extends EnvironmentWithSubscriber {

FILE: apps/api/src/app/inbox/usecases/delete-many-notifications/delete-many-notifications.usecase.ts
  class DeleteManyNotifications (line 23) | class DeleteManyNotifications {
    method constructor (line 24) | constructor(
    method execute (line 37) | async execute(command: DeleteManyNotificationsCommand): Promise<void> {
    method processWebhooksInBatches (line 91) | private async processWebhooksInBatches(
    method chunkArray (line 111) | private chunkArray<T>(array: T[], chunkSize: number): T[][] {
    method sendWebhookEvents (line 120) | private sendWebhookEvents(
    method logTraces (line 140) | private async logTraces({
  function createTraceLog (line 181) | function createTraceLog({

FILE: apps/api/src/app/inbox/usecases/delete-notification/delete-notification.command.ts
  class DeleteNotificationCommand (line 5) | class DeleteNotificationCommand extends EnvironmentWithSubscriber {

FILE: apps/api/src/app/inbox/usecases/delete-notification/delete-notification.usecase.ts
  class DeleteNotification (line 12) | class DeleteNotification {
    method constructor (line 13) | constructor(
    method execute (line 20) | async execute(command: DeleteNotificationCommand): Promise<void> {

FILE: apps/api/src/app/inbox/usecases/delete-subscription/delete-subscription.command.ts
  class DeleteTopicSubscriptionCommand (line 4) | class DeleteTopicSubscriptionCommand extends EnvironmentWithSubscriber {

FILE: apps/api/src/app/inbox/usecases/delete-subscription/delete-subscription.usecase.ts
  class DeleteTopicSubscription (line 9) | class DeleteTopicSubscription {
    method constructor (line 10) | constructor(
    method execute (line 18) | async execute(command: DeleteTopicSubscriptionCommand): Promise<{ succ...

FILE: apps/api/src/app/inbox/usecases/get-inbox-preferences/get-inbox-preferences.command.ts
  class GetInboxPreferencesCommand (line 6) | class GetInboxPreferencesCommand extends EnvironmentWithSubscriber {

FILE: apps/api/src/app/inbox/usecases/get-inbox-preferences/get-inbox-preferences.usecase.ts
  class GetInboxPreferences (line 18) | class GetInboxPreferences {
    method constructor (line 19) | constructor(
    method execute (line 27) | async execute(command: GetInboxPreferencesCommand): Promise<InboxPrefe...
    method getSubscriber (line 106) | private async getSubscriber(command: GetInboxPreferencesCommand): Prom...

FILE: apps/api/src/app/inbox/usecases/get-notifications/get-notifications.command.ts
  class GetNotificationsCommand (line 8) | class GetNotificationsCommand extends EnvironmentWithSubscriber implemen...

FILE: apps/api/src/app/inbox/usecases/get-notifications/get-notifications.usecase.ts
  class GetNotifications (line 14) | class GetNotifications {
    method constructor (line 15) | constructor(
    method execute (line 21) | async execute(command: GetNotificationsCommand): Promise<GetNotificati...

FILE: apps/api/src/app/inbox/usecases/get-topic-subscriptions/get-topic-subscriptions.command.ts
  class GetTopicSubscriptionsCommand (line 4) | class GetTopicSubscriptionsCommand extends EnvironmentWithSubscriber {

FILE: apps/api/src/app/inbox/usecases/get-topic-subscriptions/get-topic-subscriptions.usecase.ts
  class GetTopicSubscriptions (line 20) | class GetTopicSubscriptions {
    method constructor (line 21) | constructor(
    method execute (line 29) | async execute(command: GetTopicSubscriptionsCommand): Promise<Subscrip...
    method buildSubscriptionsResponse (line 42) | private async buildSubscriptionsResponse(
    method findWorkflows (line 81) | private async findWorkflows(
    method buildContextQuery (line 112) | private async buildContextQuery(contextKeys?: string[], organizationId...

FILE: apps/api/src/app/inbox/usecases/index.ts
  constant USE_CASES (line 35) | const USE_CASES = [

FILE: apps/api/src/app/inbox/usecases/mark-many-notifications-as/mark-many-notifications-as.command.ts
  class MarkManyNotificationsAsCommand (line 5) | class MarkManyNotificationsAsCommand extends EnvironmentWithSubscriber {

FILE: apps/api/src/app/inbox/usecases/mark-many-notifications-as/mark-many-notifications-as.usecase.ts
  class MarkManyNotificationsAs (line 24) | class MarkManyNotificationsAs {
    method constructor (line 25) | constructor(
    method execute (line 38) | async execute(command: MarkManyNotificationsAsCommand): Promise<void> {
    method processWebhooksInBatches (line 113) | private async processWebhooksInBatches(
    method chunkArray (line 133) | private chunkArray<T>(array: T[], chunkSize: number): T[][] {
    method sendWebhookEvents (line 142) | private sendWebhookEvents(
    method logTraces (line 162) | private async logTraces({
  function createTraceLog (line 229) | function createTraceLog({

FILE: apps/api/src/app/inbox/usecases/mark-notification-as/mark-notification-as.command.ts
  class MarkNotificationAsCommand (line 5) | class MarkNotificationAsCommand extends EnvironmentWithSubscriber {

FILE: apps/api/src/app/inbox/usecases/mark-notification-as/mark-notification-as.usecase.ts
  class MarkNotificationAs (line 14) | class MarkNotificationAs {
    method constructor (line 15) | constructor(
    method execute (line 22) | async execute(command: MarkNotificationAsCommand): Promise<InboxNotifi...

FILE: apps/api/src/app/inbox/usecases/mark-notifications-as-seen/mark-notifications-as-seen.command.ts
  class MarkNotificationsAsSeenCommand (line 5) | class MarkNotificationsAsSeenCommand extends EnvironmentWithSubscriber {

FILE: apps/api/src/app/inbox/usecases/mark-notifications-as-seen/mark-notifications-as-seen.usecase.ts
  class MarkNotificationsAsSeen (line 25) | class MarkNotificationsAsSeen {
    method constructor (line 26) | constructor(
    method execute (line 40) | async execute(command: MarkNotificationsAsSeenCommand): Promise<void> {
    method processWebhooksInBatches (line 168) | private async processWebhooksInBatches(
    method chunkArray (line 195) | private chunkArray<T>(array: T[], chunkSize: number): T[][] {
    method logTraces (line 204) | private async logTraces({
    method createTraceLog (line 243) | private createTraceLog({

FILE: apps/api/src/app/inbox/usecases/noop-send-webhook-message.usecase.ts
  class NoopSendWebhookMessage (line 5) | class NoopSendWebhookMessage {
    method execute (line 6) | async execute(_command: SendWebhookMessageCommand): Promise<{ eventId:...

FILE: apps/api/src/app/inbox/usecases/notifications-count/notifications-count.command.ts
  class NotificationsFilter (line 8) | class NotificationsFilter implements NotificationFilter {
  class NotificationsCountCommand (line 35) | class NotificationsCountCommand extends EnvironmentWithSubscriber {

FILE: apps/api/src/app/inbox/usecases/notifications-count/notifications-count.usecase.ts
  constant MAX_NOTIFICATIONS_COUNT (line 8) | const MAX_NOTIFICATIONS_COUNT = 100;
  class NotificationsCount (line 11) | class NotificationsCount {
    method constructor (line 12) | constructor(
    method execute (line 31) | async execute(

FILE: apps/api/src/app/inbox/usecases/session/session.command.ts
  class SessionCommand (line 7) | class SessionCommand extends BaseCommand {

FILE: apps/api/src/app/inbox/usecases/session/session.usecase.ts
  constant ALLOWED_ORIGINS_REGEX (line 76) | const ALLOWED_ORIGINS_REGEX = new RegExp(process.env.FRONT_BASE_URL || '');
  constant KEYLESS_RETENTION_TIME_IN_HOURS (line 77) | const KEYLESS_RETENTION_TIME_IN_HOURS = parseInt(process.env.KEYLESS_RET...
  constant MAX_NOTIFICATIONS_COUNT (line 78) | const MAX_NOTIFICATIONS_COUNT = 100;
  class Session (line 81) | class Session {
    method constructor (line 84) | constructor(
    method execute (line 113) | async execute(command: SessionCommand): Promise<SubscriberSessionRespo...
    method createDefaultSchedule (line 292) | private async createDefaultSchedule({
    method validateRequestData (line 332) | private validateRequestData(requestData: SubscriberSessionRequestDto):...
    method buildPlatformSubscriber (line 340) | private buildPlatformSubscriber(requestData: SubscriberSessionRequestD...
    method isKeylessApplication (line 348) | private isKeylessApplication(applicationIdentifier: string): boolean {
    method extractSubscriberInfo (line 354) | private extractSubscriberInfo(requestData: SubscriberSessionRequestDto...
    method normalizeSubscriber (line 373) | private normalizeSubscriber(subscriber: string | SubscriberDto | null ...
    method getApplicationIdentifier (line 385) | private async getApplicationIdentifier(requestData: SubscriberSessionR...
    method resolveContexts (line 398) | private async resolveContexts(
    method getMaxSnoozeDurationHours (line 416) | private async getMaxSnoozeDurationHours(apiServiceLevel: ApiServiceLev...
    method isKeylessExpired (line 430) | async isKeylessExpired(applicationIdentifier: string | undefined) {
    method processKeyless (line 465) | async processKeyless(): Promise<EnvironmentResponseDto> {
    method createWorkflowsUsecase (line 531) | async createWorkflowsUsecase(environmentId: string, organizationId: st...
    method convertEnvironmentEntityToDto (line 828) | private convertEnvironmentEntityToDto(environment: EnvironmentEntity) {
  function timestampHexToDate (line 849) | function timestampHexToDate(timestampHex) {

FILE: apps/api/src/app/inbox/usecases/snooze-notification/snooze-notification.command.ts
  class SnoozeNotificationCommand (line 5) | class SnoozeNotificationCommand extends EnvironmentWithSubscriber {

FILE: apps/api/src/app/inbox/usecases/snooze-notification/snooze-notification.spec.ts
  function createCommand (line 238) | function createCommand(days: number): SnoozeNotificationCommand {

FILE: apps/api/src/app/inbox/usecases/snooze-notification/snooze-notification.usecase.ts
  class SnoozeNotification (line 35) | class SnoozeNotification {
    method constructor (line 38) | constructor(
    method execute (line 49) | public async execute(command: SnoozeNotificationCommand): Promise<Inbo...
    method enqueueJob (line 94) | public async enqueueJob(job: JobEntity, delay: number) {
    method validateSnoozeDuration (line 108) | private async validateSnoozeDuration(command: SnoozeNotificationComman...
    method calculateDelayInMs (line 130) | private calculateDelayInMs(snoozeUntil: Date): number {
    method getOrganization (line 134) | private async getOrganization(organizationId: string): Promise<Organiz...
    method findNotification (line 146) | private async findNotification(command: SnoozeNotificationCommand): Pr...
    method createScheduledUnsnoozeJob (line 161) | private async createScheduledUnsnoozeJob(notification: MessageEntity, ...
    method markNotificationAsSnoozed (line 188) | private async markNotificationAsSnoozed(command: SnoozeNotificationCom...

FILE: apps/api/src/app/inbox/usecases/unsnooze-notification/unsnooze-notification.command.ts
  class UnsnoozeNotificationCommand (line 4) | class UnsnoozeNotificationCommand extends EnvironmentWithSubscriber {

FILE: apps/api/src/app/inbox/usecases/unsnooze-notification/unsnooze-notification.spec.ts
  function createCommand (line 159) | function createCommand(): UnsnoozeNotificationCommand {

FILE: apps/api/src/app/inbox/usecases/unsnooze-notification/unsnooze-notification.usecase.ts
  class UnsnoozeNotification (line 16) | class UnsnoozeNotification {
    method constructor (line 17) | constructor(
    method execute (line 25) | async execute(command: UnsnoozeNotificationCommand): Promise<InboxNoti...
    method unsnoozeNotification (line 49) | private async unsnoozeNotification(

FILE: apps/api/src/app/inbox/usecases/update-all-notifications/update-all-notifications.command.ts
  class Filter (line 7) | class Filter implements NotificationFilter {
  class UpdateAllNotificationsCommand (line 26) | class UpdateAllNotificationsCommand extends EnvironmentWithSubscriber {

FILE: apps/api/src/app/inbox/usecases/update-all-notifications/update-all-notifications.usecase.ts
  class UpdateAllNotifications (line 20) | class UpdateAllNotifications {
    method constructor (line 21) | constructor(
    method execute (line 31) | async execute(command: UpdateAllNotificationsCommand): Promise<void> {
    method sendWebhookEvents (line 101) | private async sendWebhookEvents(command: UpdateAllNotificationsCommand...
    method processWebhooksInBatches (line 129) | private async processWebhooksInBatches(
    method chunkArray (line 149) | private chunkArray<T>(array: T[], chunkSize: number): T[][] {
    method createWebhookPromises (line 158) | private createWebhookPromises(

FILE: apps/api/src/app/inbox/usecases/update-notification-action/update-notification-action.command.ts
  class UpdateNotificationActionCommand (line 6) | class UpdateNotificationActionCommand extends EnvironmentWithSubscriber {

FILE: apps/api/src/app/inbox/usecases/update-notification-action/update-notification-action.usecase.ts
  class UpdateNotificationAction (line 13) | class UpdateNotificationAction {
    method constructor (line 14) | constructor(
    method execute (line 21) | async execute(command: UpdateNotificationActionCommand): Promise<Inbox...

FILE: apps/api/src/app/inbox/usecases/update-preferences/update-preferences.command.ts
  class AllPreferences (line 17) | class AllPreferences {
  class UpdatePreferencesCommand (line 27) | class UpdatePreferencesCommand extends EnvironmentWithSubscriber {

FILE: apps/api/src/app/inbox/usecases/update-preferences/update-preferences.usecase.ts
  class UpdatePreferences (line 49) | class UpdatePreferences {
    method constructor (line 50) | constructor(
    method execute (line 63) | async execute(command: UpdatePreferencesCommand): Promise<InboxPrefere...
    method getWorkflow (line 93) | private async getWorkflow(command: UpdatePreferencesCommand): Promise<...
    method getSubscriptionId (line 115) | private async getSubscriptionId(command: UpdatePreferencesCommand): Pr...
    method updateSubscriberPreference (line 163) | private async updateSubscriberPreference(
    method buildPreferenceChannels (line 184) | private buildPreferenceChannels(command: UpdatePreferencesCommand): IP...
    method findPreference (line 195) | private async findPreference(
    method storePreferences (line 295) | private async storePreferences(item: {

FILE: apps/api/src/app/inbox/utils/analytics.ts
  type AnalyticsEventsEnum (line 1) | enum AnalyticsEventsEnum {

FILE: apps/api/src/app/inbox/utils/encryption.ts
  function validateHmacEncryption (line 5) | function validateHmacEncryption({
  function validateContextHmacEncryption (line 19) | function validateContextHmacEncryption({

FILE: apps/api/src/app/inbox/utils/notification-mapper.ts
  function mapToDto (line 111) | function mapToDto(notification: MessageEntity | MessageEntity[]): InboxN...

FILE: apps/api/src/app/inbox/utils/types.ts
  type NotificationFilter (line 10) | type NotificationFilter = {
  type InboxPreference (line 22) | type InboxPreference = {

FILE: apps/api/src/app/inbox/utils/validate-data.ts
  function validateDataStructure (line 10) | function validateDataStructure(data: unknown): void {
  function validateScalarValue (line 37) | function validateScalarValue(key: string, value: unknown): void {

FILE: apps/api/src/app/integrations/dtos/auto-configure-integration-request.dto.ts
  class AutoConfigureIntegrationRequestDto (line 1) | class AutoConfigureIntegrationRequestDto {}

FILE: apps/api/src/app/integrations/dtos/auto-configure-integration-response.dto.ts
  class AutoConfigureIntegrationResponseDto (line 4) | class AutoConfigureIntegrationResponseDto {

FILE: apps/api/src/app/integrations/dtos/create-integration-request.dto.ts
  class CreateIntegrationRequestDto (line 17) | class CreateIntegrationRequestDto implements ICreateIntegrationBodyDto {

FILE: apps/api/src/app/integrations/dtos/generate-chat-oauth-url-response.dto.ts
  class GenerateChatOAuthUrlResponseDto (line 3) | class GenerateChatOAuthUrlResponseDto {

FILE: apps/api/src/app/integrations/dtos/generate-chat-oauth-url.dto.ts
  class GenerateChatOauthUrlRequestDto (line 7) | class GenerateChatOauthUrlRequestDto {

FILE: apps/api/src/app/integrations/dtos/get-channel-type-limit.sto.ts
  class ChannelTypeLimitDto (line 3) | class ChannelTypeLimitDto {

FILE: apps/api/src/app/integrations/dtos/update-integration.dto.ts
  class UpdateIntegrationRequestDto (line 7) | class UpdateIntegrationRequestDto implements IUpdateIntegrationBodyDto {

FILE: apps/api/src/app/integrations/e2e/create-integration.e2e.ts
  function insertIntegrationTwice (line 636) | async function insertIntegrationTwice(

FILE: apps/api/src/app/integrations/e2e/get-active-integration.e2e.ts
  function filterEnvIntegrations (line 103) | function filterEnvIntegrations(integrations: IntegrationEntity[], enviro...
  function splitByChannels (line 109) | function splitByChannels(activeIntegrations: IntegrationEntity[]) {

FILE: apps/api/src/app/integrations/integrations.controller.ts
  class IntegrationsController (line 81) | class IntegrationsController {
    method constructor (line 82) | constructor(
    method listIntegrations (line 111) | async listIntegrations(@UserSession() user: UserSessionData): Promise<...
    method getActiveIntegrations (line 137) | async getActiveIntegrations(@UserSession() user: UserSessionData): Pro...
    method getWebhookSupportStatus (line 165) | async getWebhookSupportStatus(
    method createIntegration (line 189) | async createIntegration(
    method updateIntegrationById (line 241) | async updateIntegrationById(
    method autoConfigureIntegration (line 294) | async autoConfigureIntegration(
    method setIntegrationAsPrimary (line 324) | async setIntegrationAsPrimary(
    method removeIntegration (line 357) | async removeIntegration(
    method getProviderLimit (line 376) | async getProviderLimit(
    method getInAppActivated (line 399) | async getInAppActivated(@UserSession() user: UserSessionData) {
    method getChatOAuthUrl (line 420) | async getChatOAuthUrl(
    method handleChatOAuthCallback (line 448) | async handleChatOAuthCallback(
    method checkFeatureEnabled (line 489) | private async checkFeatureEnabled(user: UserSessionData) {
    method canUserAccessCredentials (line 501) | private async canUserAccessCredentials(user: UserSessionData): Promise...

FILE: apps/api/src/app/integrations/integrations.module.ts
  constant PROVIDERS (line 16) | const PROVIDERS = [ChannelFactory, CompileTemplate, GetNovuProviderCrede...
  class IntegrationModule (line 24) | class IntegrationModule {}

FILE: apps/api/src/app/integrations/usecases/auto-configure-integration/auto-configure-integration.command.ts
  class AutoConfigureIntegrationCommand (line 4) | class AutoConfigureIntegrationCommand extends OrganizationCommand {

FILE: apps/api/src/app/integrations/usecases/auto-configure-integration/auto-configure-integration.usecase.ts
  class AutoConfigureIntegration (line 8) | class AutoConfigureIntegration {
    method constructor (line 9) | constructor(
    method execute (line 17) | async execute(command: AutoConfigureIntegrationCommand): Promise<AutoC...

FILE: apps/api/src/app/integrations/usecases/chat-oauth-callback/chat-oauth-callback.command.ts
  class ChatOauthCallbackCommand (line 4) | class ChatOauthCallbackCommand extends BaseCommand {

FILE: apps/api/src/app/integrations/usecases/chat-oauth-callback/chat-oauth-callback.response.ts
  type ResponseTypeEnum (line 1) | enum ResponseTypeEnum {
  class ChatOauthCallbackResult (line 6) | class ChatOauthCallbackResult {

FILE: apps/api/src/app/integrations/usecases/chat-oauth-callback/chat-oauth-callback.usecase.ts
  class ChatOauthCallback (line 11) | class ChatOauthCallback {
    method constructor (line 12) | constructor(
    method execute (line 17) | async execute(command: ChatOauthCallbackCommand): Promise<ChatOauthCal...
    method extractProviderIdFromState (line 52) | private extractProviderIdFromState(state: string): ChatProviderIdEnum {

FILE: apps/api/src/app/integrations/usecases/chat-oauth-callback/msteams-oauth-callback/msteams-oauth-callback.command.ts
  class MsTeamsOauthCallbackCommand (line 4) | class MsTeamsOauthCallbackCommand extends BaseCommand {

FILE: apps/api/src/app/integrations/usecases/chat-oauth-callback/msteams-oauth-callback/msteams-oauth-callback.usecase.ts
  class MsTeamsOauthCallback (line 21) | class MsTeamsOauthCallback {
    method constructor (line 24) | constructor(
    method execute (line 33) | async execute(command: MsTeamsOauthCallbackCommand): Promise<ChatOauth...
    method getIntegration (line 85) | private async getIntegration(stateData: StateData): Promise<Integratio...
    method getIntegrationCredentials (line 103) | private async getIntegrationCredentials(integration: IntegrationEntity...
    method decodeMsTeamsState (line 117) | private async decodeMsTeamsState(state: string): Promise<StateData> {

FILE: apps/api/src/app/integrations/usecases/chat-oauth-callback/slack-oauth-callback/slack-oauth-callback.command.ts
  class SlackOauthCallbackCommand (line 4) | class SlackOauthCallbackCommand extends BaseCommand {

FILE: apps/api/src/app/integrations/usecases/chat-oauth-callback/slack-oauth-callback/slack-oauth-callback.usecase.ts
  class SlackOauthCallback (line 28) | class SlackOauthCallback {
    method constructor (line 32) | constructor(
    method execute (line 40) | async execute(command: SlackOauthCallbackCommand): Promise<ChatOauthCa...
    method createIncomingWebhookEndpoint (line 92) | private async createIncomingWebhookEndpoint(
    method getIntegration (line 116) | private async getIntegration(stateData: StateData): Promise<Integratio...
    method getIntegrationCredentials (line 134) | private async getIntegrationCredentials(integration: IntegrationEntity...
    method getDemoNovuSlackCredentials (line 150) | private async getDemoNovuSlackCredentials(integration: IntegrationEnti...
    method exchangeCodeForAuthData (line 162) | private async exchangeCodeForAuthData(providerCode: string, integratio...
    method decodeSlackState (line 189) | private async decodeSlackState(state: string): Promise<StateData> {

FILE: apps/api/src/app/integrations/usecases/check-integration/check-integration-email.usecase.ts
  class CheckIntegrationEMail (line 7) | class CheckIntegrationEMail {
    method execute (line 8) | public async execute(command: CheckIntegrationCommand) {

FILE: apps/api/src/app/integrations/usecases/check-integration/check-integration.command.ts
  class CheckIntegrationCommand (line 5) | class CheckIntegrationCommand extends EnvironmentCommand {

FILE: apps/api/src/app/integrations/usecases/check-integration/check-integration.usecase.ts
  class CheckIntegration (line 7) | class CheckIntegration {
    method constructor (line 8) | constructor(private checkIntegrationEmail: CheckIntegrationEMail) {}
    method execute (line 10) | public async execute(command: CheckIntegrationCommand) {

FILE: apps/api/src/app/integrations/usecases/create-integration/create-integration.command.ts
  class CreateIntegrationCommand (line 7) | class CreateIntegrationCommand extends EnvironmentCommand {

FILE: apps/api/src/app/integrations/usecases/create-integration/create-integration.usecase.ts
  class CreateIntegration (line 26) | class CreateIntegration {
    method constructor (line 29) | constructor(
    method calculatePriorityAndPrimary (line 34) | private async calculatePriorityAndPrimary(command: CreateIntegrationCo...
    method validate (line 68) | private async validate(command: CreateIntegrationCommand): Promise<voi...
    method execute (line 118) | async execute(command: CreateIntegrationCommand): Promise<IntegrationE...

FILE: apps/api/src/app/integrations/usecases/create-novu-integrations/create-novu-integrations.command.ts
  class CreateNovuIntegrationsCommand (line 5) | class CreateNovuIntegrationsCommand extends EnvironmentWithUserCommand {

FILE: apps/api/src/app/integrations/usecases/create-novu-integrations/create-novu-integrations.usecase.ts
  class CreateNovuIntegrations (line 20) | class CreateNovuIntegrations {
    method constructor (line 21) | constructor(
    method createEmailIntegration (line 28) | private async createEmailIntegration(command: CreateNovuIntegrationsCo...
    method createInAppIntegration (line 64) | private async createInAppIntegration(command: CreateNovuIntegrationsCo...
    method createSlackIntegration (line 97) | private async createSlackIntegration(command: CreateNovuIntegrationsCo...
    method execute (line 133) | async execute(command: CreateNovuIntegrationsCommand): Promise<void> {

FILE: apps/api/src/app/integrations/usecases/generate-chat-oath-url/chat-oauth.constants.ts
  constant CHAT_OAUTH_CALLBACK_PATH (line 1) | const CHAT_OAUTH_CALLBACK_PATH = '/v1/integrations/chat/oauth/callback';

FILE: apps/api/src/app/integrations/usecases/generate-chat-oath-url/generate-chat-oauth-url.command.ts
  class GenerateChatOauthUrlCommand (line 6) | class GenerateChatOauthUrlCommand extends EnvironmentCommand {

FILE: apps/api/src/app/integrations/usecases/generate-chat-oath-url/generate-chat-oauth-url.usecase.ts
  class GenerateChatOauthUrl (line 11) | class GenerateChatOauthUrl {
    method constructor (line 12) | constructor(
    method execute (line 18) | async execute(command: GenerateChatOauthUrlCommand): Promise<string> {
    method getIntegration (line 53) | private async getIntegration(command: GenerateChatOauthUrlCommand): Pr...

FILE: apps/api/src/app/integrations/usecases/generate-chat-oath-url/generate-msteams-oath-url/generate-msteams-oauth-url.command.ts
  class GenerateMsTeamsOauthUrlCommand (line 7) | class GenerateMsTeamsOauthUrlCommand extends EnvironmentCommand {

FILE: apps/api/src/app/integrations/usecases/generate-chat-oath-url/generate-msteams-oath-url/generate-msteams-oauth-url.usecase.ts
  type StateData (line 8) | type StateData = {
  class GenerateMsTeamsOauthUrl (line 20) | class GenerateMsTeamsOauthUrl {
    method constructor (line 33) | constructor(
    method execute (line 38) | async execute(command: GenerateMsTeamsOauthUrlCommand): Promise<string> {
    method validateSubscriberIdOrContext (line 58) | private validateSubscriberIdOrContext(command: GenerateMsTeamsOauthUrl...
    method assertResourceExists (line 66) | private async assertResourceExists(command: GenerateMsTeamsOauthUrlCom...
    method getOAuthUrl (line 84) | private async getOAuthUrl(clientId: string, secureState: string): Prom...
    method createSecureState (line 95) | private async createSecureState(
    method validateAndDecodeState (line 125) | static async validateAndDecodeState(state: string, environmentApiKey: ...
    method buildRedirectUri (line 149) | static buildRedirectUri(): string {
    method getIntegrationCredentials (line 158) | private async getIntegrationCredentials(integration: IntegrationEntity...
    method getEnvironmentApiKey (line 170) | private async getEnvironmentApiKey(environmentId: string): Promise<str...

FILE: apps/api/src/app/integrations/usecases/generate-chat-oath-url/generate-slack-oath-url/generate-slack-oauth-url.command.ts
  class GenerateSlackOauthUrlCommand (line 7) | class GenerateSlackOauthUrlCommand extends EnvironmentCommand {

FILE: apps/api/src/app/integrations/usecases/generate-chat-oath-url/generate-slack-oath-url/generate-slack-oauth-url.usecase.ts
  type StateData (line 8) | type StateData = {
  constant SLACK_DEFAULT_OAUTH_SCOPES (line 19) | const SLACK_DEFAULT_OAUTH_SCOPES = [
  class GenerateSlackOauthUrl (line 29) | class GenerateSlackOauthUrl {
    method constructor (line 32) | constructor(
    method execute (line 38) | async execute(command: GenerateSlackOauthUrlCommand): Promise<string> {
    method validateSubscriberIdOrContext (line 53) | private validateSubscriberIdOrContext(command: GenerateSlackOauthUrlCo...
    method assertResourceExists (line 67) | private async assertResourceExists(command: GenerateSlackOauthUrlComma...
    method getOAuthUrl (line 85) | private async getOAuthUrl(clientId: string, secureState: string, scope...
    method createSecureState (line 96) | private async createSecureState(
    method validateAndDecodeState (line 126) | static async validateAndDecodeState(state: string, environmentApiKey: ...
    method buildRedirectUri (line 150) | static buildRedirectUri(): string {
    method getIntegrationCredentials (line 159) | private async getIntegrationCredentials(integration: IntegrationEntity...
    method getDemoNovuSlackCredentials (line 175) | private async getDemoNovuSlackCredentials(integration: IntegrationEnti...
    method getEnvironmentApiKey (line 187) | private async getEnvironmentApiKey(environmentId: string): Promise<str...

FILE: apps/api/src/app/integrations/usecases/get-in-app-activated/get-in-app-activated.command.ts
  class GetInAppActivatedCommand (line 3) | class GetInAppActivatedCommand extends EnvironmentCommand {}

FILE: apps/api/src/app/integrations/usecases/get-in-app-
Copy disabled (too large) Download .json
Condensed preview — 6554 files, each showing path, character count, and a content snippet. Download the .json file for the full structured content (23,365K chars).
[
  {
    "path": ".agents/skills/email-best-practices/SKILL.md",
    "chars": 3148,
    "preview": "---\nname: email-best-practices\ndescription: Use when building email features, emails going to spam, high bounce rates, s"
  },
  {
    "path": ".agents/skills/email-best-practices/resources/branding.md",
    "chars": 119,
    "preview": "# Email Branding\n\n## BIMI (Optional)\n\nDisplay brand logo in email clients. Requires DMARC `p=quarantine` or `p=reject`."
  },
  {
    "path": ".agents/skills/email-best-practices/resources/compliance.md",
    "chars": 3676,
    "preview": "# Email Compliance\n\nLegal requirements for email by jurisdiction. **Not legal advice—consult an attorney for your specif"
  },
  {
    "path": ".agents/skills/email-best-practices/resources/deliverability.md",
    "chars": 3114,
    "preview": "# Email Deliverability\n\nMaximizing the chances that your emails are delivered successfully to the recipients.\n\n## Email "
  },
  {
    "path": ".agents/skills/email-best-practices/resources/email-capture.md",
    "chars": 3373,
    "preview": "# Email Capture Best Practices\n\nCollecting email addresses responsibly with validation, verification, and proper consent"
  },
  {
    "path": ".agents/skills/email-best-practices/resources/email-types.md",
    "chars": 5821,
    "preview": "# Email Types: Transactional vs Marketing\n\nUnderstanding the difference between transactional and marketing emails is cr"
  },
  {
    "path": ".agents/skills/email-best-practices/resources/list-management.md",
    "chars": 4606,
    "preview": "# List Management\n\nMaintaining clean email lists through suppression, hygiene, and data retention.\n\n## Suppression Lists"
  },
  {
    "path": ".agents/skills/email-best-practices/resources/marketing-emails.md",
    "chars": 3305,
    "preview": "# Marketing Email Best Practices\n\nPromotional emails that require explicit consent and provide value to recipients.\n\n## "
  },
  {
    "path": ".agents/skills/email-best-practices/resources/sending-reliability.md",
    "chars": 4746,
    "preview": "# Sending Reliability\n\nEnsuring emails are sent exactly once and handling failures gracefully.\n\n## Idempotency\n\nPrevent "
  },
  {
    "path": ".agents/skills/email-best-practices/resources/transactional-email-catalog.md",
    "chars": 10338,
    "preview": "# Transactional Email Catalog\n\nA comprehensive catalog of transactional emails organized by category, plus recommended e"
  },
  {
    "path": ".agents/skills/email-best-practices/resources/transactional-emails.md",
    "chars": 2481,
    "preview": "# Transactional Email Best Practices\n\nClear, actionable emails that users expect and need—password resets, confirmations"
  },
  {
    "path": ".agents/skills/email-best-practices/resources/webhooks-events.md",
    "chars": 4351,
    "preview": "# Webhooks and Events\n\nReceiving and processing email delivery events in real-time.\n\n## Event Types\n\n| Event | When Fire"
  },
  {
    "path": ".agents/skills/react-email/SKILL.md",
    "chars": 14446,
    "preview": "---\nname: react-email\ndescription: Use when creating HTML email templates with React components - welcome emails, passwo"
  },
  {
    "path": ".agents/skills/react-email/TESTS.md",
    "chars": 21412,
    "preview": "# React Email Skill Tests\n\nTest scenarios for verifying skill compliance. Follow TDD: run these WITHOUT skill to establi"
  },
  {
    "path": ".agents/skills/react-email/references/COMPONENTS.md",
    "chars": 11331,
    "preview": "# React Email Components Reference\n\nComplete reference for all React Email components. All examples use the Tailwind com"
  },
  {
    "path": ".agents/skills/react-email/references/I18N.md",
    "chars": 14228,
    "preview": "# Internationalization (i18n) Guide\n\nComplete guide for implementing multi-language email support with React Email using"
  },
  {
    "path": ".agents/skills/react-email/references/PATTERNS.md",
    "chars": 22134,
    "preview": "# Common Email Patterns\n\nReal-world examples of common email templates using React Email with Tailwind CSS styling.\n\n## "
  },
  {
    "path": ".agents/skills/react-email/references/SENDING.md",
    "chars": 3262,
    "preview": "Below are general guidelines for sending emails with React Email.\n\nImportant: Use verified domains in `from` addresses. "
  },
  {
    "path": ".agents/skills/react-email/references/STYLING.md",
    "chars": 7481,
    "preview": "# Styling Guide\n\nComprehensive styling reference for React Email templates.\n\n## Styling Approach\n\nUse the `Tailwind` com"
  },
  {
    "path": ".claude/skills/better-auth-best-practices/SKILL.md",
    "chars": 6141,
    "preview": "---\nname: better-auth-best-practices\ndescription: Skill for integrating Better Auth - the comprehensive TypeScript authe"
  },
  {
    "path": ".coderabbit.yaml",
    "chars": 6281,
    "preview": "early_access: true\nreviews:\n  high_level_summary: true\n  high_level_summary_placeholder: \"@coderabbitai summary\"\n  auto_"
  },
  {
    "path": ".copilotignore",
    "chars": 630,
    "preview": "# GitHub Copilot ignore file — same exclusions as .cursorignore\n\n# IDE configs — includes stale run configs for removed "
  },
  {
    "path": ".cursor/Dockerfile",
    "chars": 5033,
    "preview": "# Dockerfile for Novu Development Environment - Optimized for Cursor AI Agent\n# Ubuntu-based for full Cursor compatibili"
  },
  {
    "path": ".cursor/agents/impact-checker.md",
    "chars": 1579,
    "preview": "---\nname: impact-checker\ndescription: Assesses blast radius of changes to shared packages (packages/shared, libs/dal, li"
  },
  {
    "path": ".cursor/agents/verifier.md",
    "chars": 1080,
    "preview": "---\nname: verifier\ndescription: Validates completed work. Use after tasks are marked done to confirm implementations are"
  },
  {
    "path": ".cursor/commands/code-review-checklist.md",
    "chars": 687,
    "preview": "# Code Review Checklist\n\n## Overview\nComprehensive checklist for conducting thorough code reviews to ensure quality, sec"
  },
  {
    "path": ".cursor/commands/create-pr.md",
    "chars": 388,
    "preview": "# Create PR\n\nCreate a well-structured pull request.\n\n## Steps\n\n1. Ensure all changes are committed and the branch is pus"
  },
  {
    "path": ".cursor/rules/api-property-optionality-hygiene.mdc",
    "chars": 2039,
    "preview": "---\ndescription: Fix ApiProperty/ApiPropertyOptional optionality mismatches in DTO files; use for scheduled batch fixes "
  },
  {
    "path": ".cursor/rules/api.mdc",
    "chars": 2087,
    "preview": "---\ndescription: Rules for working in the API service (NestJS backend)\nglobs: apps/api/**/*\nalwaysApply: false\n---\n\n## A"
  },
  {
    "path": ".cursor/rules/clickhouse.mdc",
    "chars": 1448,
    "preview": "---\ndescription: Rules for working with ClickHouse analytics and trace logging\nglobs:\n  - \"**/analytic-logs/**\"\n  - \"**/"
  },
  {
    "path": ".cursor/rules/context-engineering.mdc",
    "chars": 965,
    "preview": "---\ndescription: Context quality checklist for system prompts and agent instructions\nglobs:\n  - \"**/tools/**/*.ts\"\n  - \""
  },
  {
    "path": ".cursor/rules/dal-repository.mdc",
    "chars": 2833,
    "preview": "---\ndescription: Rules for working with DAL repositories in the Novu monorepo\nglobs: libs/dal/**/*.ts, **/repositories/*"
  },
  {
    "path": ".cursor/rules/dashboard.mdc",
    "chars": 1963,
    "preview": "---\ndescription: Rules for working in the Dashboard (React frontend)\nglobs: apps/dashboard/**/*\nalwaysApply: false\n---\n\n"
  },
  {
    "path": ".cursor/rules/dependency-graph.mdc",
    "chars": 755,
    "preview": "---\ndescription: Novu monorepo dependency graph — use when assessing blast radius before changing shared code\nglobs:\nalw"
  },
  {
    "path": ".cursor/rules/infrastructure.mdc",
    "chars": 1095,
    "preview": "---\ndescription: Infrastructure setup, Docker services, and environment configuration\nglobs:\n  - \"docker/**/*\"\n  - \"*.en"
  },
  {
    "path": ".cursor/rules/novu.mdc",
    "chars": 485,
    "preview": "---\ndescription: \nglobs: \nalwaysApply: true\n---\n### Novu Conventions\n\n- File/directory names: lowercase with dashes (`co"
  },
  {
    "path": ".cursor/rules/packages.mdc",
    "chars": 1063,
    "preview": "---\ndescription: Rules for working in the shared NPM packages\nglobs: packages/**/*\nalwaysApply: false\n---\n\n### Shared Pa"
  },
  {
    "path": ".cursor/rules/pullrequest.mdc",
    "chars": 943,
    "preview": "---\ndescription: When creating a new pull request on GitHub, use this to specify the contents\nalwaysApply: false\n---\n\n##"
  },
  {
    "path": ".cursor/rules/testing.mdc",
    "chars": 1373,
    "preview": "---\ndescription: Rules for writing and running tests\nglobs:\n  - \"**/*.spec.ts\"\n  - \"**/*.test.ts\"\n  - \"**/e2e/**\"\nalways"
  },
  {
    "path": ".cursor/rules/worker.mdc",
    "chars": 1258,
    "preview": "---\ndescription: Rules for working in the Worker service (background job processing)\nglobs: apps/worker/**/*\nalwaysApply"
  },
  {
    "path": ".cursor/rules/ws.mdc",
    "chars": 799,
    "preview": "---\ndescription: Rules for working in the WebSocket service (real-time delivery)\nglobs: apps/ws/**/*\nalwaysApply: false\n"
  },
  {
    "path": ".cursor/scripts/dead-code/knip.config.jsonc",
    "chars": 1133,
    "preview": "{\n  \"$schema\": \"https://unpkg.com/knip@5/schema.json\",\n  \"github-actions\": false,\n  \"ignore\": [\n    \"**/*.spec.ts\",\n    "
  },
  {
    "path": ".cursor/scripts/dead-code/scan.sh",
    "chars": 975,
    "preview": "#!/usr/bin/env bash\n# Run knip and save Markdown output for the AI agent.\n#\n# Usage:  bash .cursor/scripts/dead-code/sca"
  },
  {
    "path": ".cursor/settings.json",
    "chars": 111,
    "preview": "{\n  \"plugins\": {\n    \"figma\": {\n      \"enabled\": true\n    },\n    \"linear\": {\n      \"enabled\": true\n    }\n  }\n}\n"
  },
  {
    "path": ".cursor/skills/better-auth-best-practices/SKILL.md",
    "chars": 6141,
    "preview": "---\nname: better-auth-best-practices\ndescription: Skill for integrating Better Auth - the comprehensive TypeScript authe"
  },
  {
    "path": ".cursor/skills/enterprise-submodule/SKILL.md",
    "chars": 3432,
    "preview": "# Enterprise Submodule Setup\n\nUse this skill when making changes to the enterprise submodule (`.source/`) or enterprise "
  },
  {
    "path": ".cursor/skills/run-api-e2e-tests/SKILL.md",
    "chars": 2507,
    "preview": "---\nname: run-api-e2e-tests\ndescription: Run e2e tests for the API service. Use when the user wants to run API E2E tests"
  },
  {
    "path": ".cursorignore",
    "chars": 664,
    "preview": "# Add directories or file patterns to ignore during indexing (e.g. foo/ or *.csv)\napps/api/src/metadata.ts\n\n# IDE config"
  },
  {
    "path": ".deepsource.toml",
    "chars": 301,
    "preview": "version = 1\n\ntest_patterns = [\n  \"apps/api/src/**/**/*.spec.ts\",\n  \"apps/api/e2e/**/*.e2e.ts\",\n  \"apps/api/src/**/*.e2e."
  },
  {
    "path": ".devcontainer/Dockerfile",
    "chars": 1568,
    "preview": "# [Choice] Node.js version (use -bullseye variants on local arm64/Apple Silicon): 16, 14, 12, 16-bullseye, 14-bullseye, "
  },
  {
    "path": ".devcontainer/devcontainer.json",
    "chars": 1072,
    "preview": "// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:\n// https://github.co"
  },
  {
    "path": ".devcontainer/docker-compose.yml",
    "chars": 1375,
    "preview": "services:\n  app:\n    build:\n      context: .\n      dockerfile: Dockerfile\n      args:\n        # Update 'VARIANT' to pick"
  },
  {
    "path": ".editorconfig",
    "chars": 236,
    "preview": "# EditorConfig is awesome: https://EditorConfig.org\n\n# top-most EditorConfig file\nroot = true\n\n# Unix-style newlines wit"
  },
  {
    "path": ".github/CODEOWNERS",
    "chars": 488,
    "preview": "# Reference: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-re"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yml",
    "chars": 3303,
    "preview": "name: '🐛 Bug Report'\ndescription: 'Submit a bug report to help us improve'\ntitle: '🐛 Bug Report: '\nlabels: ['type: bug']"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/docs_feedback.yml",
    "chars": 1629,
    "preview": "name: 📚 Docs Feedback\ndescription: Improve Novu documentation\nlabels: ['type: docs-feedback']\ntitle: '📚 Docs Feedback: '"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.yml",
    "chars": 2257,
    "preview": "name: 🚀 Feature\ndescription: 'Submit a proposal for a new feature'\ntitle: '🚀 Feature: '\nlabels: [feature]\nbody:\n  - type"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/polishing.yml",
    "chars": 1878,
    "preview": "name: \"✨ Polishing Season\"\ndescription: \"Submit a polishing report to help us improve\"\ntitle: \"✨ Polishing: \"\nlabels: [\""
  },
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "chars": 371,
    "preview": "- **I'm submitting a...**\n\n  - [ ] bug report\n  - [ ] feature request\n  - [ ] question about the decisions made in the r"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 553,
    "preview": "### What changed? Why was the change needed?\n\n@coderabbitai summary\n\n<!-- Also include any relevant links, such as Linea"
  },
  {
    "path": ".github/actions/cache/action.yml",
    "chars": 261,
    "preview": "name: Cache\ndescription: GitHub Action to expose GitHub runtime to the workflow\nruns:\n  using: composite\n  steps:\n    - "
  },
  {
    "path": ".github/actions/checkout-submodules/action.yml",
    "chars": 678,
    "preview": "name: Checkout Submodules\n\ndescription: Checkout private enterprise submodule\n\ninputs:\n  enabled:\n    description: 'Run "
  },
  {
    "path": ".github/actions/free-space/action.yml",
    "chars": 2125,
    "preview": "name: Extend Disk Space\ndescription: This action removes some preinstalled tools in favor of opening space for our docke"
  },
  {
    "path": ".github/actions/run-api/action.yml",
    "chars": 688,
    "preview": "name: Run API\n\ndescription: Starts and waits for an API running instance\n\ninputs:\n  launch_darkly_sdk_key:\n    descripti"
  },
  {
    "path": ".github/actions/run-backend/action.yml",
    "chars": 1497,
    "preview": "name: Run Backend\n\ndescription: Starts and waits for the API and Worker instance\n\ninputs:\n  launch_darkly_sdk_key:\n    d"
  },
  {
    "path": ".github/actions/setup-project/action.yml",
    "chars": 1501,
    "preview": "name: Setup Novu Monorepo\n\ndescription: Sets up the whole monorepo and install dependencies\n\ninputs:\n  slim:\n    descrip"
  },
  {
    "path": ".github/actions/setup-project-minimal/action.yml",
    "chars": 799,
    "preview": "name: Setup Novu Monorepo (Minimal)\n\ndescription: Minimal setup for Nx operations with separate cache from main CI\n\nruns"
  },
  {
    "path": ".github/actions/setup-redis-cluster/action.yml",
    "chars": 350,
    "preview": "name: Setup Novu Redis Cluster\n\ndescription: Sets up a Redis Cluster instance needed to run the tests\n\nruns:\n  using: co"
  },
  {
    "path": ".github/actions/slack-notify-on-failure/action.yml",
    "chars": 663,
    "preview": "name: 'Notify Slack on workflow failure'\n\ninputs:\n  slackWebhookURL:\n    required: true\n    type: string\n\nruns:\n  using:"
  },
  {
    "path": ".github/actions/start-localstack/action.yml",
    "chars": 955,
    "preview": "name: Start LocalStack\n\ndescription: Sets up the LocalStack\n\nruns:\n  using: composite\n  steps:\n    - name: Start LocalSt"
  },
  {
    "path": ".github/actions/validate-openapi/action.yml",
    "chars": 378,
    "preview": "name: Validate OpenAPI\n\ndescription: Validates the OpenAPI from the API\n\nruns:\n  using: composite\n\n  steps:\n    - uses: "
  },
  {
    "path": ".github/labeler.yml",
    "chars": 420,
    "preview": "'@novu/api-service':\n  - apps/api/**/*\n'@novu/worker':\n  - apps/worker/**/*\n'@novu/dashboard':\n  - apps/dashboard/**/*\n'"
  },
  {
    "path": ".github/workflows/check-only.yml",
    "chars": 413,
    "preview": "name: Check for .only flags\n\non:\n  pull_request:\n    branches: [ \"**\" ]\n\njobs:\n  check-only:\n    name: Check for .only i"
  },
  {
    "path": ".github/workflows/check-submodule-sync-merge.yaml",
    "chars": 1994,
    "preview": "name: Validate Submodule Sync Post-Merge\n\n# This workflow validates submodule synchronization specifically after merges "
  },
  {
    "path": ".github/workflows/check-submodule-sync-pr.yaml",
    "chars": 1797,
    "preview": "name: Validate Submodule Sync\n\n# This workflow validates submodule synchronization when PRs are opened/updated and when "
  },
  {
    "path": ".github/workflows/codeql-analysis.yml",
    "chars": 2872,
    "preview": "# For most projects, this workflow file will not need changing; you simply need\n# to commit it to your repository.\n#\n# Y"
  },
  {
    "path": ".github/workflows/community-label.yml",
    "chars": 714,
    "preview": "name: Add Community Label\n\non:\n  pull_request_target:\n    types: [opened]\n    branches:\n      - '!prod'\n\nconcurrency:\n  "
  },
  {
    "path": ".github/workflows/contributor-checks.yml",
    "chars": 5821,
    "preview": "name: Contributor Checks\n\nconcurrency:\n  group: \"${{ github.workflow }}-${{ github.ref }}\"\n  cancel-in-progress: true\n\no"
  },
  {
    "path": ".github/workflows/conventional-commit.yml",
    "chars": 8594,
    "preview": "name: 'Lint PR title'\n\non:\n  pull_request_target:\n    types:\n      - opened\n      - edited\n      - synchronize\n\npermissi"
  },
  {
    "path": ".github/workflows/deploy.yml",
    "chars": 19528,
    "preview": "name: Deploy to Novu Cloud\nrun-name: >\n  Deploying to\n  ${{\n    github.event.inputs.deploy_api == 'true' && 'api, ' || '"
  },
  {
    "path": ".github/workflows/deployment-summary.yml",
    "chars": 25324,
    "preview": "name: 'Generate Deployment Summary'\n\non:\n  workflow_dispatch:\n    inputs:\n      days_back:\n        description: 'Number "
  },
  {
    "path": ".github/workflows/dev-deploy-dashboard.yml",
    "chars": 1051,
    "preview": "name: Deploy DEV DASHBOARD\n\n# Controls when the action will run. Triggers the workflow on push or pull request\n# events "
  },
  {
    "path": ".github/workflows/dev-deploy-inbound-mail.yml",
    "chars": 6255,
    "preview": "name: Deploy DEV Inbound Mail\n\nenv:\n  NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}\n\n# Controls when the a"
  },
  {
    "path": ".github/workflows/issue-label.yml",
    "chars": 650,
    "preview": "name: Add Triage Label\n\non:\n  issues:\n    types: [opened]\n\nconcurrency:\n  group: '${{ github.workflow }}-${{ github.ref "
  },
  {
    "path": ".github/workflows/jarvis.yml",
    "chars": 727,
    "preview": "name: Add comment\non:\n  issues:\n    types:\n      - labeled\njobs:\n  add-comment:\n    if: github.event.label.name == '@nov"
  },
  {
    "path": ".github/workflows/on-pr-change.yml",
    "chars": 535,
    "preview": "name: Check pull request source branch\non:\n  pull_request_target:\n    types:\n      - opened\n      - reopened\n      - syn"
  },
  {
    "path": ".github/workflows/on-pr.yml",
    "chars": 11112,
    "preview": "name: Check pull request\nconcurrency:\n  group: '${{ github.workflow }}-${{ github.ref }}'\n  cancel-in-progress: true\n\nen"
  },
  {
    "path": ".github/workflows/on-push-trigger.yml",
    "chars": 4432,
    "preview": "name: Trigger Staging Deployment on Push\n\non:\n  push:\n    branches:\n      - next\n\npermissions:\n  contents: read\n  action"
  },
  {
    "path": ".github/workflows/pr-labeler.yml",
    "chars": 624,
    "preview": "name: 'Pull Request Labeler'\n\non:\n  - pull_request_target\n\njobs:\n  on_pr:\n    permissions:\n      contents: read\n      pu"
  },
  {
    "path": ".github/workflows/pr-manager.yml",
    "chars": 1099,
    "preview": "name: 'Pull Request Manager'\non:\n  schedule:\n    - cron: '0 * * * *'\n\njobs:\n  stale:\n    permissions:\n      contents: re"
  },
  {
    "path": ".github/workflows/prepare-cloud-release.yaml",
    "chars": 3226,
    "preview": "name: 'Prepare Cloud Release'\n\nenv:\n  NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}\n\non:\n  workflow_dispat"
  },
  {
    "path": ".github/workflows/prepare-enterprise-self-hosted-release.yml",
    "chars": 6178,
    "preview": "name: Prepare Enterprise Self-hosted Release\nrun-name: >\n  Building enterprise v${{ github.event.inputs.version }}:\n  ${"
  },
  {
    "path": ".github/workflows/prepare-self-hosted-release.yml",
    "chars": 9027,
    "preview": "name: Prepare Self-hosted Release\nrun-name: >\n  Building self-hosted${{ github.event.inputs.nightly == 'true' && ' (nigh"
  },
  {
    "path": ".github/workflows/preview-packages.yml",
    "chars": 1188,
    "preview": "name: Publish NPM Packages Previews\n\nenv:\n  NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}\n\non:\n  workflow_"
  },
  {
    "path": ".github/workflows/prod-deploy-inbound-mail.yml",
    "chars": 4527,
    "preview": "name: Deploy PROD Inbound Mail\n\n# Controls when the action will run. Triggers the workflow on push or pull request\n# eve"
  },
  {
    "path": ".github/workflows/release-packages.yml",
    "chars": 12578,
    "preview": "name: Release Packages\nrun-name: >\n  ${{\n    github.event_name == 'workflow_dispatch' && format('Release {0} ({1}) - {2}"
  },
  {
    "path": ".github/workflows/reusable-api-e2e.yml",
    "chars": 2593,
    "preview": "name: E2E API Tests\n\nenv:\n  NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}\n\n# Controls when the action will"
  },
  {
    "path": ".github/workflows/reusable-dashboard-deploy.yml",
    "chars": 2196,
    "preview": "name: Deploy Dashboard to Netlify\n\nenv:\n  NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}\n\n# Controls when t"
  },
  {
    "path": ".github/workflows/reusable-dashboard-e2e.yml",
    "chars": 6958,
    "preview": "name: Test DASHBOARD\n\nenv:\n  NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}\n\n# Controls when the action wil"
  },
  {
    "path": ".github/workflows/reusable-inbound-mail-e2e.yml",
    "chars": 1870,
    "preview": "name: E2E Inbound Mail Tests\n\nenv:\n  NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}\n\n# Controls when the ac"
  },
  {
    "path": ".github/workflows/reusable-webhook-e2e.yml",
    "chars": 1052,
    "preview": "name: E2E WEBHOOK Tests\n\n# Controls when the action will run. Triggers the workflow on push or pull request\non:\n  workfl"
  },
  {
    "path": ".github/workflows/reusable-worker-e2e.yml",
    "chars": 1992,
    "preview": "name: E2E worker Tests\n\nenv:\n  NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}\n\n# Controls when the action w"
  },
  {
    "path": ".github/workflows/reusable-ws-e2e.yml",
    "chars": 1775,
    "preview": "name: E2E WebSocket Tests\n\nenv:\n  NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}\n\n# Controls when the actio"
  },
  {
    "path": ".github/workflows/rollback.yml",
    "chars": 14621,
    "preview": "name: Rollback Deployment\nrun-name: >\n  Rollback \n  ${{\n    github.event.inputs.rollback_api == 'true' && 'api, ' || ''\n"
  },
  {
    "path": ".github/workflows/scripts/add-triage-label.js",
    "chars": 1069,
    "preview": "const { Octokit } = require('@octokit/action');\nconst { isCommunityContributor } = require('./is-community-contributor')"
  },
  {
    "path": ".github/workflows/scripts/community-contribution-label.js",
    "chars": 976,
    "preview": "const { Octokit } = require('@octokit/action');\nconst { isCommunityContributor } = require('./is-community-contributor')"
  },
  {
    "path": ".github/workflows/scripts/is-community-contributor.js",
    "chars": 478,
    "preview": "const { Octokit } = require('@octokit/action');\n\nconst octokit = new Octokit();\n\nconst isCommunityContributor = async (o"
  },
  {
    "path": ".github/workflows/scripts/stop-only.sh",
    "chars": 768,
    "preview": "#!/bin/bash\n\n# Define the search directory (default to current directory)\nSEARCH_DIR=${1:-.}\n\n# Find all matching test f"
  },
  {
    "path": ".github/workflows/scripts/validate-submodule-sync.sh",
    "chars": 3272,
    "preview": "#!/bin/bash\n\n# Configuration\nSUBMODULES_TOKEN=\"$SUBMODULES_TOKEN\"\nTARGET_BRANCH=\"${1:-next}\"  \nSOURCE_SUBMODULE=\".source"
  },
  {
    "path": ".gitignore",
    "chars": 2143,
    "preview": ".worktrees/\n.nyc_output\nbuild\nnode_modules\nsrc/**.js\ncoverage\n*.log\npackage-lock.json\n\nnode_modules\nbuild\n*.log\ncoverage"
  },
  {
    "path": ".gitmodules",
    "chars": 94,
    "preview": "[submodule \"enterprise\"]\n\tpath = .source\n\turl = git@github.com:novuhq/packages-enterprise.git\n"
  },
  {
    "path": ".husky/pre-commit",
    "chars": 73,
    "preview": "#!/usr/bin/env sh\n. \"$(dirname -- \"$0\")/_/husky.sh\"\n\nnpm run lint-staged\n"
  },
  {
    "path": ".idea/.gitignore",
    "chars": 161,
    "preview": "# Default ignored files\n/shelf/\n/workspace.xml\n# Editor-based HTTP Client requests\n/httpRequests/\n# GitHub Copilot persi"
  },
  {
    "path": ".idea/aws.xml",
    "chars": 485,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"accountSettings\">\n    <option name=\"acti"
  },
  {
    "path": ".idea/codeStyles/Project.xml",
    "chars": 2897,
    "preview": "<component name=\"ProjectCodeStyleConfiguration\">\n  <code_scheme name=\"Project\" version=\"173\">\n    <HTMLCodeStyleSettings"
  },
  {
    "path": ".idea/codeStyles/codeStyleConfig.xml",
    "chars": 142,
    "preview": "<component name=\"ProjectCodeStyleConfiguration\">\n  <state>\n    <option name=\"USE_PER_PROJECT_SETTINGS\" value=\"true\" />\n "
  },
  {
    "path": ".idea/discord.xml",
    "chars": 212,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"DiscordProjectSettings\">\n    <option nam"
  },
  {
    "path": ".idea/inspectionProfiles/Project_Default.xml",
    "chars": 488,
    "preview": "<component name=\"InspectionProjectProfileManager\">\n  <profile version=\"1.0\">\n    <option name=\"myName\" value=\"Project De"
  },
  {
    "path": ".idea/jsLibraryMappings.xml",
    "chars": 242,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"JavaScriptLibraryMappings\">\n    <include"
  },
  {
    "path": ".idea/modules.xml",
    "chars": 261,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"ProjectModuleManager\">\n    <modules>\n   "
  },
  {
    "path": ".idea/novu.iml",
    "chars": 1365,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<module type=\"WEB_MODULE\" version=\"4\">\n  <component name=\"NewModuleRootManager\">\n"
  },
  {
    "path": ".idea/nx-angular-config.xml",
    "chars": 303,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"NxAngularConfigService\" workspaceLocatio"
  },
  {
    "path": ".idea/runConfigurations/API.xml",
    "chars": 493,
    "preview": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"API\" type=\"js.build_tools.npm\">"
  },
  {
    "path": ".idea/runConfigurations/API___TEST.xml",
    "chars": 453,
    "preview": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"API - TEST\" type=\"js.build_tool"
  },
  {
    "path": ".idea/runConfigurations/APPLICATION_GENERIC.xml",
    "chars": 418,
    "preview": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"APPLICATION GENERIC\" type=\"js.b"
  },
  {
    "path": ".idea/runConfigurations/DAL.xml",
    "chars": 384,
    "preview": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"DAL\" type=\"js.build_tools.npm\">"
  },
  {
    "path": ".idea/runConfigurations/DAL2.xml",
    "chars": 384,
    "preview": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"DAL\" type=\"js.build_tools.npm\">"
  },
  {
    "path": ".idea/runConfigurations/DOCS.xml",
    "chars": 377,
    "preview": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"DOCS\" type=\"js.build_tools.npm\""
  },
  {
    "path": ".idea/runConfigurations/EE_AUTH.xml",
    "chars": 406,
    "preview": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"EE-AUTH\" type=\"js.build_tools.n"
  },
  {
    "path": ".idea/runConfigurations/EMBED.xml",
    "chars": 388,
    "preview": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"EMBED\" type=\"js.build_tools.npm"
  },
  {
    "path": ".idea/runConfigurations/RUN_LOCAL_ENV.xml",
    "chars": 411,
    "preview": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"RUN LOCAL ENV\" type=\"CompoundRu"
  },
  {
    "path": ".idea/runConfigurations/RUN_TEST_ENV.xml",
    "chars": 640,
    "preview": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"RUN TEST ENV\" type=\"CompoundRun"
  },
  {
    "path": ".idea/runConfigurations/SHARED.xml",
    "chars": 395,
    "preview": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"SHARED\" type=\"js.build_tools.np"
  },
  {
    "path": ".idea/runConfigurations/SHARED_WEB.xml",
    "chars": 405,
    "preview": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"SHARED-WEB\" type=\"js.build_tool"
  },
  {
    "path": ".idea/runConfigurations/TESTING.xml",
    "chars": 392,
    "preview": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"TESTING\" type=\"js.build_tools.n"
  },
  {
    "path": ".idea/runConfigurations/WEB.xml",
    "chars": 445,
    "preview": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"WEB\" type=\"js.build_tools.npm\">"
  },
  {
    "path": ".idea/runConfigurations/WEBHOOK.xml",
    "chars": 392,
    "preview": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"WEBHOOK\" type=\"js.build_tools.n"
  },
  {
    "path": ".idea/runConfigurations/WEB___CYPRESS.xml",
    "chars": 397,
    "preview": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"WEB - CYPRESS\" type=\"js.build_t"
  },
  {
    "path": ".idea/runConfigurations/WIDGET.xml",
    "chars": 390,
    "preview": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"WIDGET\" type=\"js.build_tools.np"
  },
  {
    "path": ".idea/runConfigurations/WIDGET_CLI.xml",
    "chars": 400,
    "preview": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"WIDGET-CLI\" type=\"js.build_tool"
  },
  {
    "path": ".idea/runConfigurations/WIDGET___CYPRESS.xml",
    "chars": 403,
    "preview": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"WIDGET - CYPRESS\" type=\"js.buil"
  },
  {
    "path": ".idea/runConfigurations/WIDGET___TEST.xml",
    "chars": 398,
    "preview": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"WIDGET - TEST\" type=\"js.build_t"
  },
  {
    "path": ".idea/runConfigurations/WORKER.xml",
    "chars": 451,
    "preview": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"WORKER\" type=\"js.build_tools.np"
  },
  {
    "path": ".idea/runConfigurations/WORKER___TEST.xml",
    "chars": 564,
    "preview": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"WORKER - TEST\" type=\"js.build_t"
  },
  {
    "path": ".idea/runConfigurations/WS.xml",
    "chars": 382,
    "preview": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"WS\" type=\"js.build_tools.npm\">\n"
  },
  {
    "path": ".idea/runConfigurations/WS___TEST.xml",
    "chars": 390,
    "preview": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"WS - TEST\" type=\"js.build_tools"
  },
  {
    "path": ".idea/runConfigurations/_template__of_Mocha.xml",
    "chars": 769,
    "preview": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"true\" type=\"mocha-javascript-test-runner\">\n "
  },
  {
    "path": ".idea/swagger-settings.xml",
    "chars": 183,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"SwaggerSettings\">\n    <option name=\"defa"
  },
  {
    "path": ".idea/vcs.xml",
    "chars": 914,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"CommitMessageInspectionProfile\">\n    <pr"
  },
  {
    "path": ".markdownlint.jsonc",
    "chars": 1236,
    "preview": "{\n  // MD013/line-length - Line length\n  \"MD013\": false,\n\n  // MD024/no-duplicate-heading/no-duplicate-header - Multiple"
  },
  {
    "path": ".npmrc",
    "chars": 113,
    "preview": "auto-install-peers=true\nstrict-peer-dependencies=false\nfetch-retry-maxtimeout=10000\nenable-pre-post-scripts=true\n"
  },
  {
    "path": ".npmrc-cloud",
    "chars": 176,
    "preview": "auto-install-peers=true\nstrict-peer-dependencies=false\n@taskforcesh:registry=https://npm.taskforce.sh/\n//npm.taskforce.s"
  },
  {
    "path": ".nvmrc",
    "chars": 8,
    "preview": "22.22.1\n"
  },
  {
    "path": ".nxignore",
    "chars": 98,
    "preview": ".cspell.json\n.devcontainer\n.github\n.source\nnovu.code-workspace\npnpm-lock.yaml\nscripts\nplayground/\n"
  },
  {
    "path": ".vscode/extensions.json",
    "chars": 43,
    "preview": "{\n  \"recommendations\": [\"biomejs.biome\"]\n}\n"
  },
  {
    "path": ".vscode/launch.json",
    "chars": 4195,
    "preview": "{\n  \"version\": \"0.2.0\",\n  \"configurations\": [\n    {\n      \"name\": \"API - TEST ENV\",\n      \"request\": \"launch\",\n      \"ru"
  },
  {
    "path": ".vscode/settings.json",
    "chars": 1103,
    "preview": "{\n  \"typescript.enablePromptUseWorkspaceTsdk\": true,\n  \"jest.enable\": false,\n  \"editor.defaultFormatter\": \"biomejs.biome"
  },
  {
    "path": ".vscode/tasks.json",
    "chars": 4631,
    "preview": "{\n  // For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558\n  \"version\": \"2.0.0\",\n  \"tasks\": [\n  "
  },
  {
    "path": "AGENTS.md",
    "chars": 1582,
    "preview": "# AGENTS.md\n\n## Cursor Cloud specific instructions\n\n`pnpm setup:agent` has already been run. Do not run it again. The en"
  },
  {
    "path": "CITATION.cff",
    "chars": 386,
    "preview": "cff-version: 1.2.0\nmessage: \"If you use this software in your academic work, please use the citation below.\"\n- family-na"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 5216,
    "preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participa"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 5200,
    "preview": "# Contributing to Novu\n\nThank you for showing an interest in contributing to Novu! All kinds of contributions are valuab"
  },
  {
    "path": "EE-PACKAGES-LICENSE",
    "chars": 3299,
    "preview": "Novu Proprietary Software License\n\nIMPORTANT – READ CAREFULLY: This License Agreement (\"Agreement\") is a legal agreement"
  },
  {
    "path": "LICENSE-ENTERPRISE",
    "chars": 3841,
    "preview": "Portions of this software are licensed as follows:\n\n* All content that resides under https://github.com/novuhq/novu/tree"
  },
  {
    "path": "LICENSE-MIT",
    "chars": 1076,
    "preview": "MIT License\n\nCopyright (c) 2019 Noti-fire Apps Ltd.\n\nPermission is hereby granted, free of charge, to any person obtaini"
  },
  {
    "path": "README.md",
    "chars": 11545,
    "preview": "<div align=\"center\">\n  <a href=\"https://go.novu.co/github?utm_campaign=readme-logo\" target=\"_blank\" rel=\"noopener norefe"
  },
  {
    "path": "SECURITY.md",
    "chars": 2816,
    "preview": "# Security\n\n**Contact:** security@novu.co\n\nSafeguarding our Novu systems is a top concern for us. Nevertheless, despite "
  },
  {
    "path": "_templates/module/new/controller.ejs.t",
    "chars": 220,
    "preview": "---\nto: apps/api/src/app/<%= name %>/<%= name %>.controller.ts\n---\nimport { Controller } from '@nestjs/common';\n\n@Contro"
  },
  {
    "path": "_templates/module/new/module.ejs.t",
    "chars": 473,
    "preview": "---\nto: apps/api/src/app/<%= name %>/<%= name %>.module.ts\n---\nimport { Module } from '@nestjs/common';\nimport { USE_CAS"
  },
  {
    "path": "_templates/module/new/prompt.ejs.t",
    "chars": 202,
    "preview": "// see types of prompts:\n// https://github.com/enquirer/enquirer/tree/master/examples\n//\nmodule.exports = [\n  {\n    type"
  },
  {
    "path": "_templates/module/new/usecase-index.ejs.t",
    "chars": 95,
    "preview": "---\nto: apps/api/src/app/<%= name %>/usecases/index.ts\n---\nexport const USE_CASES = [\n  //\n];\n\n"
  },
  {
    "path": "_templates/usecase/new/command.ejs.t",
    "chars": 268,
    "preview": "---\nto: apps/api/src/app/<%= module %>/usecases/<%= name %>/<%= name %>.command.ts\n---\nimport { EnvironmentWithUserComma"
  },
  {
    "path": "_templates/usecase/new/import-inject.ejs.t",
    "chars": 199,
    "preview": "---\nto: apps/api/src/app/<%= module %>/usecases/index.ts\ninject: true\nskip_if: <%= h.changeCase.pascal(name) %>\nafter: \""
  },
  {
    "path": "_templates/usecase/new/import-row-inject.ejs.t",
    "chars": 241,
    "preview": "---\nto: apps/api/src/app/<%= module %>/usecases/index.ts\ninject: true\nskip_if: import { <%= h.changeCase.pascal(name) %>"
  },
  {
    "path": "_templates/usecase/new/prompt.ejs.t",
    "chars": 299,
    "preview": "// see types of prompts:\n// https://github.com/enquirer/enquirer/tree/master/examples\n//\nmodule.exports = [\n  {\n    type"
  },
  {
    "path": "_templates/usecase/new/usecase.ejs.t",
    "chars": 412,
    "preview": "---\nto: apps/api/src/app/<%= module %>/usecases/<%= name %>/<%= name %>.usecase.ts\n---\nimport { Injectable } from '@nest"
  },
  {
    "path": "apps/api/.gitignore",
    "chars": 2138,
    "preview": "# Created by .ignore support plugin (hsz.mobi)\n### Node template\n# Logs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error."
  },
  {
    "path": "apps/api/.mocharc.json",
    "chars": 226,
    "preview": "{\n  \"timeout\": 35000,\n  \"require\": \"@swc-node/register\",\n  \"node-option\": [\"no-experimental-strip-types\"],\n  \"file\": [\"e"
  },
  {
    "path": "apps/api/.spectral.yaml",
    "chars": 910,
    "preview": "# cSpell:disable\n\n# Spectral is a flexible JSON/YAML linter and validator, which can be used for OpenAPI, AsyncAPI, or a"
  },
  {
    "path": "apps/api/.swcrc",
    "chars": 276,
    "preview": "{\n  \"$schema\": \"https://swc.rs/schema.json\",\n  \"jsc\": {\n    \"target\": \"es5\",\n    \"parser\": {\n      \"syntax\": \"typescript"
  },
  {
    "path": "apps/api/.vscode/settings.json",
    "chars": 747,
    "preview": "{\n  \"mochaExplorer.configFile\": \".mocharc.json\",\n  \"mochaExplorer.files\": [\"e2e/setup.ts\", \"e2e/**/*.e2e.ts\", \"src/**/*."
  },
  {
    "path": "apps/api/Dockerfile",
    "chars": 2244,
    "preview": "FROM node:22.22.1-alpine3.22 AS dev_base\nRUN apk add --no-cache g++ make py3-pip\nENV NX_DAEMON=false\n\n# Install global d"
  },
  {
    "path": "apps/api/README.md",
    "chars": 4726,
    "preview": "<div align=\"center\">\n  <a href=\"https://novu.co\" target=\"_blank\">\n  <picture>\n    <source media=\"(prefers-color-scheme: "
  },
  {
    "path": "apps/api/admin/connect-to-dal.ts",
    "chars": 393,
    "preview": "import { DalService } from '@novu/dal';\n\nconst dalService = new DalService();\n\nexport async function connect(databaseQue"
  },
  {
    "path": "apps/api/admin/make-json-backup.ts",
    "chars": 763,
    "preview": "import { existsSync, promises } from 'node:fs';\nimport { format } from 'date-fns';\n\nconst backupFolder = `${__dirname}/b"
  },
  {
    "path": "apps/api/admin/remove-organization.ts",
    "chars": 4431,
    "preview": "import '../src/config';\nimport {\n  BaseRepository,\n  ChangeRepository,\n  CommunityMemberRepository,\n  CommunityOrganizat"
  },
  {
    "path": "apps/api/admin/remove-user-account.ts",
    "chars": 1304,
    "preview": "import '../src/config';\nimport { CommunityMemberRepository, CommunityUserRepository } from '@novu/dal';\nimport { normali"
  },
  {
    "path": "apps/api/e2e/compile-email-template.e2e.ts",
    "chars": 12227,
    "preview": "import { Test } from '@nestjs/testing';\nimport {\n  CompileEmailTemplate,\n  CompileEmailTemplateCommand,\n  CompileTemplat"
  },
  {
    "path": "apps/api/e2e/enterprise/inbound-webhook/process-inbound-webhook.e2e.ts",
    "chars": 6392,
    "preview": "import { Novu } from '@novu/api';\nimport { QueryBuilder, Trace, TraceLogRepository } from '@novu/application-generic';\ni"
  },
  {
    "path": "apps/api/e2e/mock-http-client.ts",
    "chars": 3179,
    "preview": "import { HTTPClient, HTTPClientOptions } from '@novu/api/lib/http';\n\nexport class MockHTTPClient extends HTTPClient {\n  "
  },
  {
    "path": "apps/api/e2e/retry.e2e.ts",
    "chars": 8264,
    "preview": "import { Novu } from '@novu/api';\nimport { topicsList } from '@novu/api/funcs/topicsList';\nimport { expect } from 'chai'"
  },
  {
    "path": "apps/api/e2e/setup.ts",
    "chars": 11717,
    "preview": "import { ClickHouseClient, ClickHouseService, createClickHouseClient } from '@novu/application-generic';\nimport { DalSer"
  },
  {
    "path": "apps/api/e2e/test-bridge-server.ts",
    "chars": 2967,
    "preview": "import http from 'node:http';\nimport { Client, serve } from '@novu/framework/express';\nimport express from 'express';\n\ne"
  },
  {
    "path": "apps/api/exportOpenAPIJSON.ts",
    "chars": 775,
    "preview": "import { writeFileSync } from 'node:fs';\nimport { bootstrap } from './src/bootstrap'; // Adjust the path according to yo"
  },
  {
    "path": "apps/api/jarvis-api-intro.md",
    "chars": 1434,
    "preview": "Hi, I'm Jarvis 🤖\n\nI'm a bot built to help you with your contribution to Novu.\nI will add instructions and guides on how "
  },
  {
    "path": "apps/api/migrations/001-add-default-identifier-to-topic-subscribers/add-default-identifier-to-topic-subscribers-migration.spec.ts",
    "chars": 9599,
    "preview": "import { NestFactory } from '@nestjs/core';\nimport { buildDefaultSubscriptionIdentifier } from '@novu/application-generi"
  },
  {
    "path": "apps/api/migrations/001-add-default-identifier-to-topic-subscribers/add-default-identifier-to-topic-subscribers-migration.ts",
    "chars": 2935,
    "preview": "import '../../src/config';\n\nimport { NestFactory } from '@nestjs/core';\nimport { buildDefaultSubscriptionIdentifier, Pin"
  },
  {
    "path": "apps/api/migrations/002-remove-duplicate-identifiers/remove-duplicate-identifiers.spec.ts",
    "chars": 9133,
    "preview": "import { NestFactory } from '@nestjs/core';\nimport { expect } from 'chai';\nimport { afterEach, beforeEach, describe, it "
  },
  {
    "path": "apps/api/migrations/002-remove-duplicate-identifiers/remove-duplicate-identifiers.ts",
    "chars": 3491,
    "preview": "import '../../src/config';\n\nimport { NestFactory } from '@nestjs/core';\nimport { PinoLogger } from '@novu/application-ge"
  },
  {
    "path": "apps/api/migrations/add-layout-id-to-email-controls/add-layout-id-to-email-controls-migration.spec.ts",
    "chars": 4659,
    "preview": "import { MessageTemplateRepository } from '@novu/dal';\nimport { StepTypeEnum, UiComponentEnum, WorkflowCreationSourceEnu"
  }
]

// ... and 6354 more files (download for full content)

About this extraction

This page contains the full source code of the novuhq/novu GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 6554 files (20.9 MB), approximately 5.9M tokens, and a symbol index with 18882 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!