@elevasis/core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +435 -0
- package/dist/index.js +403 -0
- package/dist/organization-model/index.d.ts +435 -0
- package/dist/organization-model/index.js +403 -0
- package/package.json +62 -0
- package/src/README.md +34 -0
- package/src/__tests__/observability-exports.test.ts +36 -0
- package/src/__tests__/publish.test.ts +18 -0
- package/src/__tests__/template-foundations-compatibility.test.ts +34 -0
- package/src/auth/index.ts +8 -0
- package/src/auth/multi-tenancy/credentials/README.md +38 -0
- package/src/auth/multi-tenancy/credentials/__tests__/encryption.test.ts +216 -0
- package/src/auth/multi-tenancy/credentials/__tests__/service.test.ts +174 -0
- package/src/auth/multi-tenancy/credentials/index.ts +6 -0
- package/src/auth/multi-tenancy/credentials/server/encryption.ts +39 -0
- package/src/auth/multi-tenancy/credentials/server/service.ts +60 -0
- package/src/auth/multi-tenancy/index.ts +17 -0
- package/src/auth/multi-tenancy/invitations/__tests__/invitation.test.ts +237 -0
- package/src/auth/multi-tenancy/invitations/api-schemas.ts +107 -0
- package/src/auth/multi-tenancy/invitations/index.ts +38 -0
- package/src/auth/multi-tenancy/invitations/invitation.ts +86 -0
- package/src/auth/multi-tenancy/invitations/server/index.ts +25 -0
- package/src/auth/multi-tenancy/invitations/server/transforms.ts +24 -0
- package/src/auth/multi-tenancy/invitations/server/workos.ts +24 -0
- package/src/auth/multi-tenancy/invitations/supabase.ts +50 -0
- package/src/auth/multi-tenancy/memberships/__tests__/membership.test.ts +227 -0
- package/src/auth/multi-tenancy/memberships/__tests__/supabase-transforms.test.ts +88 -0
- package/src/auth/multi-tenancy/memberships/__tests__/workos-transforms.test.ts +139 -0
- package/src/auth/multi-tenancy/memberships/api-schemas.ts +126 -0
- package/src/auth/multi-tenancy/memberships/index.ts +22 -0
- package/src/auth/multi-tenancy/memberships/membership.ts +138 -0
- package/src/auth/multi-tenancy/memberships/server/index.ts +15 -0
- package/src/auth/multi-tenancy/memberships/server/transforms.ts +32 -0
- package/src/auth/multi-tenancy/memberships/server/workos.ts +21 -0
- package/src/auth/multi-tenancy/memberships/supabase.ts +46 -0
- package/src/auth/multi-tenancy/organizations/__tests__/organization.test.ts +249 -0
- package/src/auth/multi-tenancy/organizations/api-schemas.ts +128 -0
- package/src/auth/multi-tenancy/organizations/index.ts +23 -0
- package/src/auth/multi-tenancy/organizations/organization.ts +25 -0
- package/src/auth/multi-tenancy/organizations/server/index.ts +10 -0
- package/src/auth/multi-tenancy/organizations/server/transforms.ts +35 -0
- package/src/auth/multi-tenancy/organizations/server/workos.ts +20 -0
- package/src/auth/multi-tenancy/types.ts +89 -0
- package/src/auth/multi-tenancy/users/__tests__/user.test.ts +208 -0
- package/src/auth/multi-tenancy/users/api-schemas.ts +194 -0
- package/src/auth/multi-tenancy/users/index.ts +28 -0
- package/src/auth/multi-tenancy/users/server/index.ts +19 -0
- package/src/auth/multi-tenancy/users/server/transforms.ts +21 -0
- package/src/auth/multi-tenancy/users/server/workos.ts +16 -0
- package/src/auth/multi-tenancy/users/user.ts +65 -0
- package/src/business/acquisition/api-schemas.ts +759 -0
- package/src/business/acquisition/index.ts +109 -0
- package/src/business/acquisition/types.ts +400 -0
- package/src/business/crm/api-schemas.ts +75 -0
- package/src/business/delivery/index.ts +1 -0
- package/src/business/delivery/types.ts +89 -0
- package/src/business/index.ts +12 -0
- package/src/business/pdf/assets/ElevasisLogo.png +0 -0
- package/src/business/pdf/browser/image-utils.ts +74 -0
- package/src/business/pdf/browser/index.ts +16 -0
- package/src/business/pdf/browser/pdfmake-browser.ts +229 -0
- package/src/business/pdf/index.ts +10 -0
- package/src/business/pdf/sections/acceptance.ts +112 -0
- package/src/business/pdf/sections/automation.ts +56 -0
- package/src/business/pdf/sections/cover.ts +51 -0
- package/src/business/pdf/sections/index.ts +57 -0
- package/src/business/pdf/sections/investment.ts +69 -0
- package/src/business/pdf/sections/proposal-document.ts +200 -0
- package/src/business/pdf/sections/summary-investment.ts +124 -0
- package/src/business/pdf/sections/summary.ts +55 -0
- package/src/business/pdf/sections/table-summary.ts +59 -0
- package/src/business/pdf/sections/types.ts +124 -0
- package/src/business/pdf/server/__tests__/pdfmake-test.ts +219 -0
- package/src/business/pdf/server/index.ts +21 -0
- package/src/business/pdf/server/pdfmake-service.ts +237 -0
- package/src/business/pdf/server/themes/default.ts +8 -0
- package/src/business/pdf/server/themes/index.ts +9 -0
- package/src/business/pdf/server/themes/types.ts +8 -0
- package/src/business/pdf/shared/convert.ts +514 -0
- package/src/business/pdf/shared/index.ts +12 -0
- package/src/business/pdf/themes.ts +78 -0
- package/src/business/pdf/types.ts +272 -0
- package/src/business/seo/__tests__/linking.test.ts +549 -0
- package/src/business/seo/__tests__/types.test.ts +404 -0
- package/src/business/seo/index.ts +2 -0
- package/src/business/seo/linking.ts +281 -0
- package/src/business/seo/types.ts +199 -0
- package/src/commands/index.ts +8 -0
- package/src/commands/queue/index.ts +3 -0
- package/src/commands/queue/schemas.test.ts +593 -0
- package/src/commands/queue/schemas.ts +125 -0
- package/src/commands/queue/sse-events.ts +61 -0
- package/src/commands/queue/types/action.ts +52 -0
- package/src/commands/queue/types/checkpoint.ts +44 -0
- package/src/commands/queue/types/index.ts +7 -0
- package/src/commands/queue/types/task.ts +116 -0
- package/src/commands/queue/types.ts +14 -0
- package/src/content/distribution-metadata.ts +61 -0
- package/src/content/index.ts +10 -0
- package/src/deployments/index.ts +22 -0
- package/src/execution/calibration/__tests__/schemas.test.ts +320 -0
- package/src/execution/calibration/index.ts +3 -0
- package/src/execution/calibration/schemas.ts +121 -0
- package/src/execution/calibration/sse-events.ts +125 -0
- package/src/execution/calibration/types.ts +190 -0
- package/src/execution/core/__tests__/api-schemas.test.ts +667 -0
- package/src/execution/core/__tests__/archived-logs.test.ts +72 -0
- package/src/execution/core/api-schemas.ts +312 -0
- package/src/execution/core/index.ts +11 -0
- package/src/execution/core/resource-validator.test.ts +63 -0
- package/src/execution/core/runner-types.ts +80 -0
- package/src/execution/core/server/environment.ts +31 -0
- package/src/execution/core/sse-executions.ts +119 -0
- package/src/execution/core/types.ts +29 -0
- package/src/execution/engine/__tests__/fixtures/index.ts +2 -0
- package/src/execution/engine/__tests__/fixtures/mock-scenarios.ts +60 -0
- package/src/execution/engine/__tests__/fixtures/test-agents.ts +85 -0
- package/src/execution/engine/__tests__/integration/agent-framework.integration.test.ts +1031 -0
- package/src/execution/engine/__tests__/timeout.test.ts +565 -0
- package/src/execution/engine/agent/__tests__/errors.test.ts +508 -0
- package/src/execution/engine/agent/actions/__tests__/processor.test.ts +531 -0
- package/src/execution/engine/agent/actions/executor.ts +205 -0
- package/src/execution/engine/agent/actions/navigate-knowledge-executor.ts +230 -0
- package/src/execution/engine/agent/actions/processor.ts +116 -0
- package/src/execution/engine/agent/actions/types.ts +70 -0
- package/src/execution/engine/agent/core/__tests__/agent.test.ts +614 -0
- package/src/execution/engine/agent/core/__tests__/error-passthrough.test.ts +134 -0
- package/src/execution/engine/agent/core/agent.ts +810 -0
- package/src/execution/engine/agent/core/types.ts +155 -0
- package/src/execution/engine/agent/errors.ts +251 -0
- package/src/execution/engine/agent/index.ts +78 -0
- package/src/execution/engine/agent/knowledge-map/__tests__/navigate-knowledge-executor.test.ts +580 -0
- package/src/execution/engine/agent/knowledge-map/__tests__/utils.test.ts +622 -0
- package/src/execution/engine/agent/knowledge-map/types.ts +106 -0
- package/src/execution/engine/agent/knowledge-map/utils.ts +101 -0
- package/src/execution/engine/agent/memory/__tests__/domains.test.ts +72 -0
- package/src/execution/engine/agent/memory/__tests__/manager.test.ts +754 -0
- package/src/execution/engine/agent/memory/__tests__/utils.test.ts +285 -0
- package/src/execution/engine/agent/memory/domains.ts +99 -0
- package/src/execution/engine/agent/memory/manager.ts +365 -0
- package/src/execution/engine/agent/memory/processor.ts +66 -0
- package/src/execution/engine/agent/memory/types.ts +90 -0
- package/src/execution/engine/agent/memory/utils.ts +134 -0
- package/src/execution/engine/agent/observability/logging.ts +467 -0
- package/src/execution/engine/agent/observability/types.ts +64 -0
- package/src/execution/engine/agent/reasoning/__tests__/request-builder.test.ts +209 -0
- package/src/execution/engine/agent/reasoning/adapters/agent-adapter-helpers.ts +349 -0
- package/src/execution/engine/agent/reasoning/processor.ts +92 -0
- package/src/execution/engine/agent/reasoning/prompt-sections/base-actions.ts +134 -0
- package/src/execution/engine/agent/reasoning/prompt-sections/completion.ts +49 -0
- package/src/execution/engine/agent/reasoning/prompt-sections/knowledge-map.ts +93 -0
- package/src/execution/engine/agent/reasoning/prompt-sections/memory.ts +65 -0
- package/src/execution/engine/agent/reasoning/prompt-sections/security.ts +32 -0
- package/src/execution/engine/agent/reasoning/prompt-sections/tools.ts +44 -0
- package/src/execution/engine/agent/reasoning/request-builder.ts +169 -0
- package/src/execution/engine/agent/reasoning/types.ts +18 -0
- package/src/execution/engine/base/__tests__/errors.test.ts +246 -0
- package/src/execution/engine/base/__tests__/serialization.test.ts +670 -0
- package/src/execution/engine/base/__tests__/utils.test.ts +45 -0
- package/src/execution/engine/base/errors.ts +118 -0
- package/src/execution/engine/base/index.ts +2 -0
- package/src/execution/engine/base/logging.ts +31 -0
- package/src/execution/engine/base/serialization.ts +324 -0
- package/src/execution/engine/base/types.ts +126 -0
- package/src/execution/engine/base/utils.ts +41 -0
- package/src/execution/engine/index.ts +440 -0
- package/src/execution/engine/interface/index.ts +1 -0
- package/src/execution/engine/interface/types.ts +62 -0
- package/src/execution/engine/llm/__tests__/errors.test.ts +318 -0
- package/src/execution/engine/llm/__tests__/input-sanitizer.test.ts +286 -0
- package/src/execution/engine/llm/__tests__/model-info.test.ts +50 -0
- package/src/execution/engine/llm/__tests__/model-validation.test.ts +321 -0
- package/src/execution/engine/llm/__tests__/response-schema-validator.test.ts +115 -0
- package/src/execution/engine/llm/adapters/__tests__/adapter-factory.test.ts +375 -0
- package/src/execution/engine/llm/adapters/__tests__/anthropic-adapter.test.ts +463 -0
- package/src/execution/engine/llm/adapters/__tests__/anthropic.integration.test.ts +177 -0
- package/src/execution/engine/llm/adapters/__tests__/circuit-breaker-error.test.ts +94 -0
- package/src/execution/engine/llm/adapters/__tests__/google-adapter.test.ts +722 -0
- package/src/execution/engine/llm/adapters/__tests__/google.integration.test.ts +376 -0
- package/src/execution/engine/llm/adapters/__tests__/mock-adapter.test.ts +432 -0
- package/src/execution/engine/llm/adapters/__tests__/openai-adapter.test.ts +551 -0
- package/src/execution/engine/llm/adapters/__tests__/openrouter-adapter.test.ts +563 -0
- package/src/execution/engine/llm/adapters/__tests__/openrouter.integration.test.ts +105 -0
- package/src/execution/engine/llm/adapters/__tests__/universal-adapter.test.ts +537 -0
- package/src/execution/engine/llm/adapters/circuit-breaker.ts +147 -0
- package/src/execution/engine/llm/adapters/index.ts +17 -0
- package/src/execution/engine/llm/adapters/mock-adapter.ts +116 -0
- package/src/execution/engine/llm/adapters/server/adapter-factory.ts +130 -0
- package/src/execution/engine/llm/adapters/server/anthropic.ts +137 -0
- package/src/execution/engine/llm/adapters/server/compose-signal.ts +18 -0
- package/src/execution/engine/llm/adapters/server/google.ts +283 -0
- package/src/execution/engine/llm/adapters/server/index.ts +12 -0
- package/src/execution/engine/llm/adapters/server/openai.ts +206 -0
- package/src/execution/engine/llm/adapters/server/openrouter.ts +235 -0
- package/src/execution/engine/llm/adapters/universal-adapter.ts +230 -0
- package/src/execution/engine/llm/errors.ts +186 -0
- package/src/execution/engine/llm/input-sanitizer.ts +129 -0
- package/src/execution/engine/llm/model-info.ts +332 -0
- package/src/execution/engine/llm/response-schema-validator.ts +113 -0
- package/src/execution/engine/llm/types.ts +86 -0
- package/src/execution/engine/test-utils/index.ts +6 -0
- package/src/execution/engine/test-utils/mocks.ts +56 -0
- package/src/execution/engine/tools/__tests__/tooling-error.test.ts +265 -0
- package/src/execution/engine/tools/__tests__/types.test.ts +47 -0
- package/src/execution/engine/tools/integration/base-integration-adapter.ts +50 -0
- package/src/execution/engine/tools/integration/index.ts +53 -0
- package/src/execution/engine/tools/integration/server/adapters/anymailfinder/anymailfinder-adapter.ts +73 -0
- package/src/execution/engine/tools/integration/server/adapters/anymailfinder/anymailfinder-tools.ts +209 -0
- package/src/execution/engine/tools/integration/server/adapters/anymailfinder/fetch/find-company-email/index.ts +82 -0
- package/src/execution/engine/tools/integration/server/adapters/anymailfinder/fetch/find-decision-maker-email/index.ts +122 -0
- package/src/execution/engine/tools/integration/server/adapters/anymailfinder/fetch/find-person-email/index.ts +89 -0
- package/src/execution/engine/tools/integration/server/adapters/anymailfinder/fetch/verify-email/index.ts +84 -0
- package/src/execution/engine/tools/integration/server/adapters/anymailfinder/index.ts +16 -0
- package/src/execution/engine/tools/integration/server/adapters/apify/__tests__/apify-run-actor.integration.test.ts +293 -0
- package/src/execution/engine/tools/integration/server/adapters/apify/apify-adapter.ts +100 -0
- package/src/execution/engine/tools/integration/server/adapters/apify/apify-tools.ts +217 -0
- package/src/execution/engine/tools/integration/server/adapters/apify/fetch/get-dataset-items/index.ts +92 -0
- package/src/execution/engine/tools/integration/server/adapters/apify/fetch/run-actor/index.ts +218 -0
- package/src/execution/engine/tools/integration/server/adapters/apify/fetch/start-actor/index.ts +87 -0
- package/src/execution/engine/tools/integration/server/adapters/apify/index.ts +11 -0
- package/src/execution/engine/tools/integration/server/adapters/attio/__tests__/attio-crud.integration.test.ts +362 -0
- package/src/execution/engine/tools/integration/server/adapters/attio/attio-adapter.ts +162 -0
- package/src/execution/engine/tools/integration/server/adapters/attio/attio-tools.ts +594 -0
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/README.md +632 -0
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/create-attribute/index.ts +214 -0
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/create-note/index.ts +152 -0
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/create-record/index.ts +141 -0
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/delete-note/index.ts +86 -0
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/delete-record/index.ts +105 -0
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/get-record/index.test.ts +186 -0
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/get-record/index.ts +118 -0
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/list-attributes/index.ts +165 -0
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/list-notes/index.ts +96 -0
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/list-objects/index.ts +104 -0
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/list-records/index.test.ts +338 -0
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/list-records/index.ts +156 -0
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/update-attribute/index.ts +220 -0
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/update-record/index.ts +140 -0
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/utils/types.ts +147 -0
- package/src/execution/engine/tools/integration/server/adapters/attio/index.ts +31 -0
- package/src/execution/engine/tools/integration/server/adapters/dropbox/__tests__/dropbox-adapter.test.ts +409 -0
- package/src/execution/engine/tools/integration/server/adapters/dropbox/dropbox-adapter.ts +281 -0
- package/src/execution/engine/tools/integration/server/adapters/dropbox/dropbox-tools.ts +106 -0
- package/src/execution/engine/tools/integration/server/adapters/dropbox/fetch/create-folder/__tests__/index.test.ts +451 -0
- package/src/execution/engine/tools/integration/server/adapters/dropbox/fetch/create-folder/index.ts +114 -0
- package/src/execution/engine/tools/integration/server/adapters/dropbox/fetch/upload-file/__tests__/index.test.ts +415 -0
- package/src/execution/engine/tools/integration/server/adapters/dropbox/fetch/upload-file/index.ts +111 -0
- package/src/execution/engine/tools/integration/server/adapters/dropbox/index.ts +25 -0
- package/src/execution/engine/tools/integration/server/adapters/gmail/gmail-adapter.ts +210 -0
- package/src/execution/engine/tools/integration/server/adapters/gmail/gmail-tools.ts +104 -0
- package/src/execution/engine/tools/integration/server/adapters/google-sheets/__tests__/google-sheets.integration.test.ts +261 -0
- package/src/execution/engine/tools/integration/server/adapters/google-sheets/google-sheets-adapter.ts +1189 -0
- package/src/execution/engine/tools/integration/server/adapters/google-sheets/google-sheets-tools.ts +641 -0
- package/src/execution/engine/tools/integration/server/adapters/google-sheets/index.ts +18 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/activate-campaign/index.ts +86 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/add-to-campaign/__tests__/index.test.ts +289 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/add-to-campaign/index.ts +154 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/bulk-add-leads/__tests__/index.test.ts +325 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/bulk-add-leads/index.ts +153 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/bulk-delete-leads/index.ts +84 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/create-campaign/index.ts +125 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/create-inbox-test/index.ts +107 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/delete-campaign/index.ts +85 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-account-health/index.ts +91 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-campaign/index.ts +92 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-campaign-analytics/__tests__/index.test.ts +195 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-campaign-analytics/index.ts +113 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-daily-campaign-analytics/index.ts +104 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-emails/index.ts +155 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-step-analytics/__tests__/index.test.ts +196 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-step-analytics/index.ts +102 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/list-campaigns/__tests__/index.test.ts +189 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/list-campaigns/index.ts +87 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/list-leads/index.ts +112 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/patch-lead/index.ts +76 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/pause-campaign/index.ts +86 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/remove-from-subsequence/index.ts +98 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/send-reply/index.ts +126 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/update-campaign/__tests__/index.test.ts +193 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/update-campaign/index.ts +99 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/update-interest-status/__tests__/index.test.ts +621 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/update-interest-status/index.ts +125 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/index.ts +29 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/instantly-adapter.ts +178 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/instantly-tools.ts +1473 -0
- package/src/execution/engine/tools/integration/server/adapters/millionverifier/fetch/check-credits/index.ts +59 -0
- package/src/execution/engine/tools/integration/server/adapters/millionverifier/fetch/verify-email/index.ts +102 -0
- package/src/execution/engine/tools/integration/server/adapters/millionverifier/index.ts +17 -0
- package/src/execution/engine/tools/integration/server/adapters/millionverifier/millionverifier-adapter.ts +80 -0
- package/src/execution/engine/tools/integration/server/adapters/millionverifier/millionverifier-tools.ts +102 -0
- package/src/execution/engine/tools/integration/server/adapters/resend/fetch/get-email/index.ts +102 -0
- package/src/execution/engine/tools/integration/server/adapters/resend/fetch/send-email/index.ts +134 -0
- package/src/execution/engine/tools/integration/server/adapters/resend/fetch/utils/types.ts +75 -0
- package/src/execution/engine/tools/integration/server/adapters/resend/index.ts +27 -0
- package/src/execution/engine/tools/integration/server/adapters/resend/resend-adapter.ts +108 -0
- package/src/execution/engine/tools/integration/server/adapters/resend/resend-tools.ts +132 -0
- package/src/execution/engine/tools/integration/server/adapters/resend/types.ts +44 -0
- package/src/execution/engine/tools/integration/server/adapters/signature-api/fetch/create-envelope/index.ts +274 -0
- package/src/execution/engine/tools/integration/server/adapters/signature-api/fetch/download-document/index.ts +230 -0
- package/src/execution/engine/tools/integration/server/adapters/signature-api/fetch/get-envelope/index.ts +133 -0
- package/src/execution/engine/tools/integration/server/adapters/signature-api/fetch/utils/types.ts +246 -0
- package/src/execution/engine/tools/integration/server/adapters/signature-api/fetch/void-envelope/index.ts +90 -0
- package/src/execution/engine/tools/integration/server/adapters/signature-api/index.ts +38 -0
- package/src/execution/engine/tools/integration/server/adapters/signature-api/signature-api-adapter.ts +87 -0
- package/src/execution/engine/tools/integration/server/adapters/signature-api/signature-api-tools.ts +179 -0
- package/src/execution/engine/tools/integration/server/adapters/stripe/fetch/utils/types.ts +210 -0
- package/src/execution/engine/tools/integration/server/adapters/stripe/index.ts +44 -0
- package/src/execution/engine/tools/integration/server/adapters/stripe/stripe-adapter.ts +517 -0
- package/src/execution/engine/tools/integration/server/adapters/stripe/stripe-tools.ts +309 -0
- package/src/execution/engine/tools/integration/server/adapters/tomba/fetch/domain-search/index.ts +133 -0
- package/src/execution/engine/tools/integration/server/adapters/tomba/fetch/email-finder/index.ts +122 -0
- package/src/execution/engine/tools/integration/server/adapters/tomba/fetch/email-verifier/index.ts +111 -0
- package/src/execution/engine/tools/integration/server/adapters/tomba/index.ts +11 -0
- package/src/execution/engine/tools/integration/server/adapters/tomba/tomba-adapter.ts +78 -0
- package/src/execution/engine/tools/integration/server/adapters/tomba/tomba-tools.ts +222 -0
- package/src/execution/engine/tools/integration/server/index.ts +61 -0
- package/src/execution/engine/tools/integration/service.ts +161 -0
- package/src/execution/engine/tools/integration/tool.ts +253 -0
- package/src/execution/engine/tools/integration/types/anymailfinder.ts +74 -0
- package/src/execution/engine/tools/integration/types/apify.ts +92 -0
- package/src/execution/engine/tools/integration/types/attio.ts +354 -0
- package/src/execution/engine/tools/integration/types/dropbox.ts +64 -0
- package/src/execution/engine/tools/integration/types/gmail.ts +35 -0
- package/src/execution/engine/tools/integration/types/google-sheets.ts +303 -0
- package/src/execution/engine/tools/integration/types/index.ts +19 -0
- package/src/execution/engine/tools/integration/types/instantly.ts +557 -0
- package/src/execution/engine/tools/integration/types/millionverifier.ts +56 -0
- package/src/execution/engine/tools/integration/types/resend.ts +63 -0
- package/src/execution/engine/tools/integration/types/signature-api.ts +164 -0
- package/src/execution/engine/tools/integration/types/stripe.ts +162 -0
- package/src/execution/engine/tools/integration/types/tomba.ts +94 -0
- package/src/execution/engine/tools/lead-service-types.ts +884 -0
- package/src/execution/engine/tools/llm/index.ts +11 -0
- package/src/execution/engine/tools/llm/server/index.ts +8 -0
- package/src/execution/engine/tools/llm/server/llm-call-tool.ts +118 -0
- package/src/execution/engine/tools/platform/__tests__/approval.test.ts +242 -0
- package/src/execution/engine/tools/platform/__tests__/email.test.ts +482 -0
- package/src/execution/engine/tools/platform/__tests__/hitl-cancel.test.ts +97 -0
- package/src/execution/engine/tools/platform/__tests__/notification.test.ts +208 -0
- package/src/execution/engine/tools/platform/__tests__/pdf.test.ts +441 -0
- package/src/execution/engine/tools/platform/__tests__/scheduler.test.ts +189 -0
- package/src/execution/engine/tools/platform/__tests__/schedules.test.ts +336 -0
- package/src/execution/engine/tools/platform/acquisition/company-tools.ts +248 -0
- package/src/execution/engine/tools/platform/acquisition/contact-tools.ts +319 -0
- package/src/execution/engine/tools/platform/acquisition/index.ts +43 -0
- package/src/execution/engine/tools/platform/acquisition/list-tools.ts +148 -0
- package/src/execution/engine/tools/platform/acquisition/types.ts +260 -0
- package/src/execution/engine/tools/platform/approval/cancel-by-metadata.ts +65 -0
- package/src/execution/engine/tools/platform/approval/index.ts +4 -0
- package/src/execution/engine/tools/platform/approval/tool.ts +99 -0
- package/src/execution/engine/tools/platform/email/index.ts +122 -0
- package/src/execution/engine/tools/platform/email/types.ts +96 -0
- package/src/execution/engine/tools/platform/index.ts +181 -0
- package/src/execution/engine/tools/platform/notification.ts +81 -0
- package/src/execution/engine/tools/platform/pdf/index.ts +110 -0
- package/src/execution/engine/tools/platform/pdf/types.ts +77 -0
- package/src/execution/engine/tools/platform/resource-invocation/__tests__/edge-cases.test.ts +507 -0
- package/src/execution/engine/tools/platform/resource-invocation/__tests__/resource-invocation-service.test.ts +500 -0
- package/src/execution/engine/tools/platform/resource-invocation/__tests__/tool.test.ts +555 -0
- package/src/execution/engine/tools/platform/resource-invocation/dynamic-tool.ts +94 -0
- package/src/execution/engine/tools/platform/resource-invocation/index.ts +14 -0
- package/src/execution/engine/tools/platform/resource-invocation/resource-invocation-service.ts +147 -0
- package/src/execution/engine/tools/platform/resource-invocation/tool.ts +115 -0
- package/src/execution/engine/tools/platform/resource-invocation/types.ts +31 -0
- package/src/execution/engine/tools/platform/scheduler.ts +87 -0
- package/src/execution/engine/tools/platform/schedules/cancel-by-key-tool.ts +48 -0
- package/src/execution/engine/tools/platform/schedules/cancel-by-metadata-tool.ts +42 -0
- package/src/execution/engine/tools/platform/schedules/delete-by-key-tool.ts +43 -0
- package/src/execution/engine/tools/platform/schedules/index.ts +13 -0
- package/src/execution/engine/tools/platform/schedules/list-tool.ts +56 -0
- package/src/execution/engine/tools/platform/schedules/types.ts +88 -0
- package/src/execution/engine/tools/platform/storage/__tests__/storage.test.ts +998 -0
- package/src/execution/engine/tools/platform/storage/index.ts +370 -0
- package/src/execution/engine/tools/platform/storage/types.ts +128 -0
- package/src/execution/engine/tools/platform/types.ts +148 -0
- package/src/execution/engine/tools/registry.ts +590 -0
- package/src/execution/engine/tools/tool-maps.ts +694 -0
- package/src/execution/engine/tools/types.ts +233 -0
- package/src/execution/engine/workflow/__tests__/errors.test.ts +139 -0
- package/src/execution/engine/workflow/__tests__/utils.test.ts +645 -0
- package/src/execution/engine/workflow/__tests__/workflow.test.ts +818 -0
- package/src/execution/engine/workflow/errors.ts +63 -0
- package/src/execution/engine/workflow/helpers/index.ts +11 -0
- package/src/execution/engine/workflow/helpers/server/index.ts +8 -0
- package/src/execution/engine/workflow/helpers/server/llm-call.ts +93 -0
- package/src/execution/engine/workflow/index.ts +19 -0
- package/src/execution/engine/workflow/log-truncate.ts +26 -0
- package/src/execution/engine/workflow/logging.ts +191 -0
- package/src/execution/engine/workflow/types.ts +183 -0
- package/src/execution/engine/workflow/utils.ts +280 -0
- package/src/execution/engine/workflow/workflow.ts +168 -0
- package/src/execution/index.ts +20 -0
- package/src/execution/scheduler/__tests__/api-schemas.test.ts +733 -0
- package/src/execution/scheduler/__tests__/retry.test.ts +37 -0
- package/src/execution/scheduler/__tests__/utils.test.ts +1009 -0
- package/src/execution/scheduler/api-schemas.ts +296 -0
- package/src/execution/scheduler/index.ts +50 -0
- package/src/execution/scheduler/schemas.ts +264 -0
- package/src/execution/scheduler/types.ts +111 -0
- package/src/execution/scheduler/utils.ts +364 -0
- package/src/forms/index.ts +7 -0
- package/src/forms/schemas.test.ts +113 -0
- package/src/forms/schemas.ts +69 -0
- package/src/forms/types.ts +70 -0
- package/src/index.ts +54 -0
- package/src/integrations/credentials/__tests__/api-schemas.test.ts +496 -0
- package/src/integrations/credentials/__tests__/schemas.test.ts +82 -0
- package/src/integrations/credentials/__tests__/utils.test.ts +144 -0
- package/src/integrations/credentials/api-schemas.ts +143 -0
- package/src/integrations/credentials/index.ts +32 -0
- package/src/integrations/credentials/schemas.ts +164 -0
- package/src/integrations/credentials/utils.ts +59 -0
- package/src/integrations/oauth/__tests__/provider-registry.test.ts +59 -0
- package/src/integrations/oauth/api-schemas.ts +92 -0
- package/src/integrations/oauth/index.ts +19 -0
- package/src/integrations/oauth/provider-registry.ts +61 -0
- package/src/integrations/oauth/server/__tests__/refresh-concurrent.test.ts +183 -0
- package/src/integrations/oauth/server/__tests__/refresh.integration.test.ts +257 -0
- package/src/integrations/oauth/server/__tests__/refresh.test.ts +577 -0
- package/src/integrations/oauth/server/credentials.ts +39 -0
- package/src/integrations/oauth/server/refresh.ts +214 -0
- package/src/integrations/oauth/types.ts +34 -0
- package/src/integrations/webhook-endpoints/__tests__/api-schemas.test.ts +318 -0
- package/src/integrations/webhook-endpoints/api-schemas.ts +102 -0
- package/src/integrations/webhook-endpoints/index.ts +28 -0
- package/src/integrations/webhook-endpoints/types.ts +51 -0
- package/src/operations/activities/api-schemas.ts +79 -0
- package/src/operations/activities/index.ts +9 -0
- package/src/operations/activities/sse-events.ts +30 -0
- package/src/operations/activities/types.ts +63 -0
- package/src/operations/debug-logs/client.ts +60 -0
- package/src/operations/debug-logs/debug-logger.ts +83 -0
- package/src/operations/debug-logs/index.ts +8 -0
- package/src/operations/debug-logs/server.ts +19 -0
- package/src/operations/debug-logs/types.ts +33 -0
- package/src/operations/index.ts +50 -0
- package/src/operations/notifications/__tests__/api-schemas.test.ts +216 -0
- package/src/operations/notifications/api-schemas.ts +91 -0
- package/src/operations/notifications/index.ts +3 -0
- package/src/operations/notifications/sse-events.ts +21 -0
- package/src/operations/notifications/types.ts +47 -0
- package/src/operations/observability/__tests__/openrouter-cost-flow.test.ts +297 -0
- package/src/operations/observability/__tests__/schemas.test.ts +151 -0
- package/src/operations/observability/__tests__/types.test.ts +109 -0
- package/src/operations/observability/__tests__/utils.test.ts +54 -0
- package/src/operations/observability/ai-usage-collector.ts +64 -0
- package/src/operations/observability/index.ts +13 -0
- package/src/operations/observability/metrics-collector.ts +49 -0
- package/src/operations/observability/schemas.ts +39 -0
- package/src/operations/observability/types.ts +463 -0
- package/src/operations/observability/utils.ts +77 -0
- package/src/operations/sessions/__tests__/api-schemas.test.ts +361 -0
- package/src/operations/sessions/__tests__/manager.test.ts +821 -0
- package/src/operations/sessions/api-schemas.ts +166 -0
- package/src/operations/sessions/index.ts +26 -0
- package/src/operations/sessions/server/manager.ts +90 -0
- package/src/operations/sessions/server/session.ts +180 -0
- package/src/operations/sessions/types.ts +98 -0
- package/src/operations/triggers/index.ts +12 -0
- package/src/operations/triggers/webhook/definitions/__tests__/instantly-reply-received.test.ts +72 -0
- package/src/operations/triggers/webhook/definitions/instantly-account-error.ts +44 -0
- package/src/operations/triggers/webhook/definitions/instantly-auto-reply-received.ts +51 -0
- package/src/operations/triggers/webhook/definitions/instantly-campaign-completed.ts +45 -0
- package/src/operations/triggers/webhook/definitions/instantly-email-bounced.ts +49 -0
- package/src/operations/triggers/webhook/definitions/instantly-lead-unsubscribed.ts +45 -0
- package/src/operations/triggers/webhook/definitions/instantly-reply-received.ts +54 -0
- package/src/operations/triggers/webhook/index.ts +35 -0
- package/src/operations/triggers/webhook/types.ts +74 -0
- package/src/organization-model/README.md +79 -0
- package/src/organization-model/__tests__/graph.test.ts +250 -0
- package/src/organization-model/__tests__/resolve.test.ts +47 -0
- package/src/organization-model/defaults.ts +60 -0
- package/src/organization-model/domains/branding.ts +22 -0
- package/src/organization-model/domains/crm.ts +46 -0
- package/src/organization-model/domains/delivery.ts +48 -0
- package/src/organization-model/domains/features.ts +57 -0
- package/src/organization-model/domains/lead-gen.ts +33 -0
- package/src/organization-model/domains/navigation.ts +103 -0
- package/src/organization-model/domains/shared.ts +42 -0
- package/src/organization-model/graph/build.ts +432 -0
- package/src/organization-model/graph/index.ts +4 -0
- package/src/organization-model/graph/schema.ts +50 -0
- package/src/organization-model/graph/types.ts +52 -0
- package/src/organization-model/index.ts +11 -0
- package/src/organization-model/published.ts +18 -0
- package/src/organization-model/resolve.ts +42 -0
- package/src/organization-model/schema.ts +21 -0
- package/src/organization-model/types.ts +27 -0
- package/src/platform/api/index.ts +1 -0
- package/src/platform/api/types.ts +35 -0
- package/src/platform/constants/http.ts +37 -0
- package/src/platform/constants/index.ts +5 -0
- package/src/platform/constants/limits.ts +32 -0
- package/src/platform/constants/resilience.ts +51 -0
- package/src/platform/constants/timeouts.ts +20 -0
- package/src/platform/constants/versions.ts +3 -0
- package/src/platform/index.ts +27 -0
- package/src/platform/registry/__tests__/command-view.test.ts +410 -0
- package/src/platform/registry/__tests__/resource-registry-static.test.ts +347 -0
- package/src/platform/registry/__tests__/resource-registry.integration.test.ts +1004 -0
- package/src/platform/registry/__tests__/resource-registry.list-executable.test.ts +393 -0
- package/src/platform/registry/__tests__/resource-registry.test.ts +1942 -0
- package/src/platform/registry/__tests__/serialization.test.ts +1127 -0
- package/src/platform/registry/__tests__/validation.test.ts +1086 -0
- package/src/platform/registry/command-view.ts +180 -0
- package/src/platform/registry/domains.ts +165 -0
- package/src/platform/registry/index.ts +93 -0
- package/src/platform/registry/reserved.ts +24 -0
- package/src/platform/registry/resource-metadata.ts +59 -0
- package/src/platform/registry/resource-registry.command-queue-groups.test.ts +129 -0
- package/src/platform/registry/resource-registry.ts +788 -0
- package/src/platform/registry/serialization.ts +273 -0
- package/src/platform/registry/serialized-types.ts +231 -0
- package/src/platform/registry/stats-types.ts +66 -0
- package/src/platform/registry/types.ts +404 -0
- package/src/platform/registry/validation.ts +513 -0
- package/src/platform/resilience/__tests__/circuit-breaker.test.ts +291 -0
- package/src/platform/resilience/__tests__/http-error-mapper.test.ts +173 -0
- package/src/platform/resilience/__tests__/rate-limiter.test.ts +471 -0
- package/src/platform/resilience/__tests__/retry.test.ts +380 -0
- package/src/platform/resilience/__tests__/timeout.test.ts +219 -0
- package/src/platform/resilience/circuit-breaker.ts +164 -0
- package/src/platform/resilience/errors.ts +68 -0
- package/src/platform/resilience/http-error-mapper.ts +129 -0
- package/src/platform/resilience/index.ts +93 -0
- package/src/platform/resilience/rate-limiter-types.ts +46 -0
- package/src/platform/resilience/rate-limiter.ts +140 -0
- package/src/platform/resilience/retry.ts +89 -0
- package/src/platform/resilience/timeout.ts +63 -0
- package/src/platform/sse/events.ts +67 -0
- package/src/platform/sse/index.ts +7 -0
- package/src/platform/utils/__tests__/currency.test.ts +77 -0
- package/src/platform/utils/__tests__/validation.test.ts +1083 -0
- package/src/platform/utils/currency.ts +96 -0
- package/src/platform/utils/debounce.ts +52 -0
- package/src/platform/utils/error.ts +42 -0
- package/src/platform/utils/hmac.test.ts +97 -0
- package/src/platform/utils/index.ts +32 -0
- package/src/platform/utils/server/betterstack-logger.ts +210 -0
- package/src/platform/utils/server/hmac.ts +44 -0
- package/src/platform/utils/server/unsubscribe.ts +111 -0
- package/src/platform/utils/token-counter.ts +96 -0
- package/src/platform/utils/validation.ts +425 -0
- package/src/projects/api-schemas.ts +265 -0
- package/src/published.ts +1 -0
- package/src/server.ts +273 -0
- package/src/supabase/__tests__/helpers.test.ts +51 -0
- package/src/supabase/database.types.ts +2674 -0
- package/src/supabase/helpers.ts +20 -0
- package/src/supabase/index.ts +52 -0
- package/src/supabase/server/client.ts +58 -0
- package/src/test-utils/README.md +150 -0
- package/src/test-utils/browser-mocks.ts +54 -0
- package/src/test-utils/fixtures/api-keys.ts +52 -0
- package/src/test-utils/fixtures/index.ts +4 -0
- package/src/test-utils/fixtures/memberships.ts +80 -0
- package/src/test-utils/fixtures/organizations.ts +69 -0
- package/src/test-utils/fixtures/users.ts +79 -0
- package/src/test-utils/index.ts +11 -0
- package/src/test-utils/mocks/index.ts +2 -0
- package/src/test-utils/mocks/supabase.ts +142 -0
- package/src/test-utils/mocks/workos.ts +108 -0
- package/src/test-utils/rls/RLSTestContext.ts +586 -0
- package/src/test-utils/rls/index.ts +1 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { getProviderConfig } from '../provider-registry'
|
|
2
|
+
import type { OAuthToken } from '../types'
|
|
3
|
+
import { getOAuthCredentials } from './credentials'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* In-flight refresh deduplication map.
|
|
7
|
+
* Keyed by `provider:refreshToken` — concurrent callers with the same expired token
|
|
8
|
+
* share a single refresh request instead of racing against each other.
|
|
9
|
+
*/
|
|
10
|
+
const inflightRefreshes = new Map<string, Promise<OAuthToken>>()
|
|
11
|
+
|
|
12
|
+
/** @internal Exposed for testing — do not use in production code. */
|
|
13
|
+
export function _getInflightRefreshCount(): number {
|
|
14
|
+
return inflightRefreshes.size
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Refresh OAuth token if expired (pure function)
|
|
19
|
+
*
|
|
20
|
+
* Checks if token expires within 5 minutes and refreshes if needed.
|
|
21
|
+
* Automatically determines provider config from token.provider field.
|
|
22
|
+
* Respects provider-specific token exchange methods (basic-auth, form-encoded, json-body).
|
|
23
|
+
* Handles both RFC 6749-compliant (snake_case) and non-compliant (camelCase) providers.
|
|
24
|
+
*/
|
|
25
|
+
export async function refreshOAuthTokenIfExpired(token: OAuthToken): Promise<OAuthToken> {
|
|
26
|
+
// Validate token has provider field
|
|
27
|
+
if (!token.provider) {
|
|
28
|
+
throw new Error('Token missing provider field - cannot refresh')
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Validate refresh token exists
|
|
32
|
+
if (!token.refreshToken) {
|
|
33
|
+
throw new Error('Token cannot be refreshed (no refresh_token). User must re-authorize.')
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Check if token is still valid
|
|
37
|
+
const expiresAt = new Date(token.expiresAt).getTime()
|
|
38
|
+
const now = Date.now()
|
|
39
|
+
const bufferMs = 5 * 60 * 1000 // 5 minutes
|
|
40
|
+
|
|
41
|
+
if (expiresAt > now + bufferMs) {
|
|
42
|
+
console.log('[OAuth] Token still valid, no refresh needed', {
|
|
43
|
+
provider: token.provider,
|
|
44
|
+
expiresAt: token.expiresAt,
|
|
45
|
+
remainingMinutes: Math.floor((expiresAt - now) / 60000)
|
|
46
|
+
})
|
|
47
|
+
return token // Still valid
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Token expired, delegate to force refresh
|
|
51
|
+
return forceRefreshOAuthToken(token)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Force refresh OAuth token (bypasses expiry check)
|
|
56
|
+
*
|
|
57
|
+
* Use this when you receive a 401 from the API even though the token
|
|
58
|
+
* appeared valid based on expiresAt. This handles cases where:
|
|
59
|
+
* - The stored expiresAt is stale/incorrect
|
|
60
|
+
* - The token was revoked before expiry
|
|
61
|
+
* - Clock skew between systems
|
|
62
|
+
*/
|
|
63
|
+
export async function forceRefreshOAuthToken(token: OAuthToken): Promise<OAuthToken> {
|
|
64
|
+
// Validate token has provider field
|
|
65
|
+
if (!token.provider) {
|
|
66
|
+
throw new Error('Token missing provider field - cannot refresh')
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Validate refresh token exists
|
|
70
|
+
if (!token.refreshToken) {
|
|
71
|
+
throw new Error('Token cannot be refreshed (no refresh_token). User must re-authorize.')
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Deduplicate concurrent refreshes for the same token
|
|
75
|
+
const dedupeKey = `${token.provider}:${token.refreshToken}`
|
|
76
|
+
const inflight = inflightRefreshes.get(dedupeKey)
|
|
77
|
+
if (inflight) {
|
|
78
|
+
console.log('[OAuth] Reusing in-flight refresh', { provider: token.provider })
|
|
79
|
+
return inflight
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const refreshPromise = performTokenRefresh(token)
|
|
83
|
+
inflightRefreshes.set(dedupeKey, refreshPromise)
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
return await refreshPromise
|
|
87
|
+
} finally {
|
|
88
|
+
inflightRefreshes.delete(dedupeKey)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function performTokenRefresh(token: OAuthToken): Promise<OAuthToken> {
|
|
93
|
+
console.log('[OAuth] Force refreshing token', {
|
|
94
|
+
provider: token.provider,
|
|
95
|
+
expiresAt: token.expiresAt
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
// Get provider configuration
|
|
99
|
+
const config = getProviderConfig(token.provider)
|
|
100
|
+
|
|
101
|
+
// Get client credentials from environment
|
|
102
|
+
const { clientId, clientSecret } = getOAuthCredentials(config.id)
|
|
103
|
+
|
|
104
|
+
// Perform token refresh using provider-specific exchange method
|
|
105
|
+
let response: Response
|
|
106
|
+
|
|
107
|
+
switch (config.tokenExchange) {
|
|
108
|
+
case 'basic-auth': {
|
|
109
|
+
// Notion pattern: Authorization: Basic {base64(clientId:clientSecret)}
|
|
110
|
+
const credentials = Buffer.from(`${clientId}:${clientSecret}`).toString('base64')
|
|
111
|
+
response = await fetch(config.tokenUrl, {
|
|
112
|
+
method: 'POST',
|
|
113
|
+
headers: {
|
|
114
|
+
Authorization: `Basic ${credentials}`,
|
|
115
|
+
'Content-Type': 'application/json'
|
|
116
|
+
},
|
|
117
|
+
body: JSON.stringify({
|
|
118
|
+
grant_type: 'refresh_token',
|
|
119
|
+
refresh_token: token.refreshToken
|
|
120
|
+
})
|
|
121
|
+
})
|
|
122
|
+
break
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
case 'form-encoded': {
|
|
126
|
+
// Google pattern: application/x-www-form-urlencoded body
|
|
127
|
+
const params = new URLSearchParams({
|
|
128
|
+
grant_type: 'refresh_token',
|
|
129
|
+
refresh_token: token.refreshToken,
|
|
130
|
+
client_id: clientId,
|
|
131
|
+
client_secret: clientSecret
|
|
132
|
+
})
|
|
133
|
+
response = await fetch(config.tokenUrl, {
|
|
134
|
+
method: 'POST',
|
|
135
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
136
|
+
body: params.toString()
|
|
137
|
+
})
|
|
138
|
+
break
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
case 'json-body': {
|
|
142
|
+
// Most providers: application/json body
|
|
143
|
+
response = await fetch(config.tokenUrl, {
|
|
144
|
+
method: 'POST',
|
|
145
|
+
headers: { 'Content-Type': 'application/json' },
|
|
146
|
+
body: JSON.stringify({
|
|
147
|
+
grant_type: 'refresh_token',
|
|
148
|
+
refresh_token: token.refreshToken,
|
|
149
|
+
client_id: clientId,
|
|
150
|
+
client_secret: clientSecret
|
|
151
|
+
})
|
|
152
|
+
})
|
|
153
|
+
break
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
default: {
|
|
157
|
+
throw new Error(`Unsupported token exchange method: ${config.tokenExchange}`)
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Handle error response
|
|
162
|
+
if (!response.ok) {
|
|
163
|
+
let errorMessage = `Failed to refresh OAuth token for ${config.id}: ${response.status}`
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
const errorData = (await response.json()) as Record<string, unknown>
|
|
167
|
+
// Parse OAuth error response (RFC 6749 Section 5.2)
|
|
168
|
+
if (errorData.error) {
|
|
169
|
+
errorMessage += ` - ${errorData.error}`
|
|
170
|
+
if (errorData.error_description) {
|
|
171
|
+
errorMessage += `: ${errorData.error_description}`
|
|
172
|
+
}
|
|
173
|
+
// Identify invalid_grant errors (user must re-authorize)
|
|
174
|
+
if (errorData.error === 'invalid_grant') {
|
|
175
|
+
errorMessage += ' (User must re-authorize)'
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
} catch {
|
|
179
|
+
// If error response is not JSON, use status text
|
|
180
|
+
errorMessage += ` - ${response.statusText}`
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
throw new Error(errorMessage)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const data = (await response.json()) as Record<string, unknown>
|
|
187
|
+
|
|
188
|
+
// Defensive normalization: handle both snake_case (RFC 6749) and camelCase
|
|
189
|
+
const accessToken = (data.access_token ?? data.accessToken) as string | undefined
|
|
190
|
+
const refreshToken = (data.refresh_token ?? data.refreshToken) as string | undefined
|
|
191
|
+
const expiresIn = (data.expires_in ?? data.expiresIn ?? 3600) as number
|
|
192
|
+
|
|
193
|
+
// Validate required fields
|
|
194
|
+
if (!accessToken) {
|
|
195
|
+
throw new Error(
|
|
196
|
+
`Invalid token refresh response: missing access_token. Received keys: ${Object.keys(data).join(', ')}`
|
|
197
|
+
)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
console.log('[OAuth] Token refreshed successfully', {
|
|
201
|
+
provider: token.provider,
|
|
202
|
+
hasNewRefreshToken: !!refreshToken,
|
|
203
|
+
expiresInSeconds: expiresIn,
|
|
204
|
+
validForHours: Math.floor(expiresIn / 3600)
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
// Return refreshed token with all original fields preserved
|
|
208
|
+
return {
|
|
209
|
+
...token,
|
|
210
|
+
accessToken,
|
|
211
|
+
refreshToken: refreshToken || token.refreshToken, // Some providers don't return new refresh token
|
|
212
|
+
expiresAt: new Date(Date.now() + expiresIn * 1000).toISOString()
|
|
213
|
+
}
|
|
214
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export interface OAuthToken extends Record<string, unknown> {
|
|
2
|
+
provider: string // OAuth provider ID (notion, google-sheets)
|
|
3
|
+
accessToken: string
|
|
4
|
+
refreshToken: string
|
|
5
|
+
expiresAt: string // ISO 8601 timestamp
|
|
6
|
+
tokenType: 'Bearer'
|
|
7
|
+
scope?: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface OAuthProviderConfig {
|
|
11
|
+
id: string
|
|
12
|
+
name: string
|
|
13
|
+
authUrl: string
|
|
14
|
+
tokenUrl: string
|
|
15
|
+
scopes?: string[]
|
|
16
|
+
authParams?: Record<string, string> // Provider-specific auth params
|
|
17
|
+
tokenExchange: 'basic-auth' | 'form-encoded' | 'json-body'
|
|
18
|
+
usePKCE?: boolean
|
|
19
|
+
|
|
20
|
+
// Custom override hooks for edge cases (95% won't need these)
|
|
21
|
+
customAuthFlow?: (config: OAuthProviderConfig, state: OAuthState) => URL
|
|
22
|
+
customTokenExchange?: (
|
|
23
|
+
code: string,
|
|
24
|
+
config: OAuthProviderConfig
|
|
25
|
+
) => Promise<OAuthToken>
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface OAuthState {
|
|
29
|
+
organizationId: string
|
|
30
|
+
nonce: string
|
|
31
|
+
timestamp: number
|
|
32
|
+
credentialName: string
|
|
33
|
+
provider: string // Which provider this flow is for
|
|
34
|
+
}
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Webhook Endpoint API schemas tests
|
|
3
|
+
* Tests validation schemas for webhook endpoint CRUD API
|
|
4
|
+
* Focus: Mass assignment prevention, required field enforcement, partial update rules, type coercion
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { describe, it, expect } from 'vitest'
|
|
8
|
+
import {
|
|
9
|
+
CreateWebhookEndpointRequestSchema,
|
|
10
|
+
UpdateWebhookEndpointRequestSchema,
|
|
11
|
+
ListWebhookEndpointsQuerySchema,
|
|
12
|
+
WebhookEndpointResponseSchema
|
|
13
|
+
} from '../api-schemas'
|
|
14
|
+
|
|
15
|
+
const validUuid = 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'
|
|
16
|
+
const validUuid2 = 'b1eebc99-9c0b-4ef8-bb6d-6bb9bd380a22'
|
|
17
|
+
|
|
18
|
+
describe('CreateWebhookEndpointRequestSchema', () => {
|
|
19
|
+
const validPayload = {
|
|
20
|
+
name: 'My Inbound Webhook',
|
|
21
|
+
resourceId: 'my-workflow-workflow'
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
describe('valid requests', () => {
|
|
25
|
+
it('accepts name and resourceId', () => {
|
|
26
|
+
const result = CreateWebhookEndpointRequestSchema.parse(validPayload)
|
|
27
|
+
expect(result.name).toBe('My Inbound Webhook')
|
|
28
|
+
expect(result.resourceId).toBe('my-workflow-workflow')
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('accepts optional description', () => {
|
|
32
|
+
const payload = { ...validPayload, description: 'Receives Stripe events' }
|
|
33
|
+
const result = CreateWebhookEndpointRequestSchema.parse(payload)
|
|
34
|
+
expect(result.description).toBe('Receives Stripe events')
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('omits description when not provided', () => {
|
|
38
|
+
const result = CreateWebhookEndpointRequestSchema.parse(validPayload)
|
|
39
|
+
expect(result.description).toBeUndefined()
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('accepts empty string description (description is z.string, not NonEmptyString)', () => {
|
|
43
|
+
const payload = { ...validPayload, description: '' }
|
|
44
|
+
const result = CreateWebhookEndpointRequestSchema.parse(payload)
|
|
45
|
+
expect(result.description).toBe('')
|
|
46
|
+
})
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
describe('required fields', () => {
|
|
50
|
+
it('rejects missing name', () => {
|
|
51
|
+
const { name: _name, ...payload } = validPayload
|
|
52
|
+
expect(() => CreateWebhookEndpointRequestSchema.parse(payload)).toThrow()
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it('accepts missing resourceId (optional field)', () => {
|
|
56
|
+
const { resourceId: _resourceId, ...payload } = validPayload
|
|
57
|
+
const result = CreateWebhookEndpointRequestSchema.parse(payload)
|
|
58
|
+
expect(result.resourceId).toBeUndefined()
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('rejects empty name (NonEmptyString enforcement)', () => {
|
|
62
|
+
expect(() => CreateWebhookEndpointRequestSchema.parse({ ...validPayload, name: '' })).toThrow()
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('rejects empty resourceId (NonEmptyString enforcement)', () => {
|
|
66
|
+
expect(() => CreateWebhookEndpointRequestSchema.parse({ ...validPayload, resourceId: '' })).toThrow()
|
|
67
|
+
})
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
describe('SECURITY: mass assignment prevention', () => {
|
|
71
|
+
it('rejects organizationId injection', () => {
|
|
72
|
+
const payload = { ...validPayload, organizationId: 'attacker-org-id' }
|
|
73
|
+
expect(() => CreateWebhookEndpointRequestSchema.parse(payload)).toThrow()
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it('rejects id injection (server-generated field)', () => {
|
|
77
|
+
const payload = { ...validPayload, id: validUuid }
|
|
78
|
+
expect(() => CreateWebhookEndpointRequestSchema.parse(payload)).toThrow()
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
it('rejects key injection (server-generated field)', () => {
|
|
82
|
+
const payload = { ...validPayload, key: 'whk_custom_key' }
|
|
83
|
+
expect(() => CreateWebhookEndpointRequestSchema.parse(payload)).toThrow()
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it('rejects status injection', () => {
|
|
87
|
+
const payload = { ...validPayload, status: 'active' }
|
|
88
|
+
expect(() => CreateWebhookEndpointRequestSchema.parse(payload)).toThrow()
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('rejects arbitrary unknown fields', () => {
|
|
92
|
+
const payload = { ...validPayload, malicious: 'value' }
|
|
93
|
+
expect(() => CreateWebhookEndpointRequestSchema.parse(payload)).toThrow()
|
|
94
|
+
})
|
|
95
|
+
})
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
describe('UpdateWebhookEndpointRequestSchema', () => {
|
|
99
|
+
describe('valid partial updates', () => {
|
|
100
|
+
it('accepts update with name only', () => {
|
|
101
|
+
const result = UpdateWebhookEndpointRequestSchema.parse({ name: 'New Name' })
|
|
102
|
+
expect(result.name).toBe('New Name')
|
|
103
|
+
expect(result.status).toBeUndefined()
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
it('accepts update with status only', () => {
|
|
107
|
+
const result = UpdateWebhookEndpointRequestSchema.parse({ status: 'paused' })
|
|
108
|
+
expect(result.status).toBe('paused')
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
it('accepts update with resourceId only', () => {
|
|
112
|
+
const result = UpdateWebhookEndpointRequestSchema.parse({ resourceId: 'other-workflow-workflow' })
|
|
113
|
+
expect(result.resourceId).toBe('other-workflow-workflow')
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
it('accepts update with description only', () => {
|
|
117
|
+
const result = UpdateWebhookEndpointRequestSchema.parse({ description: 'Updated desc' })
|
|
118
|
+
expect(result.description).toBe('Updated desc')
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('accepts update with all fields', () => {
|
|
122
|
+
const payload = {
|
|
123
|
+
name: 'Updated',
|
|
124
|
+
description: 'New desc',
|
|
125
|
+
resourceId: 'new-workflow-workflow',
|
|
126
|
+
status: 'active' as const
|
|
127
|
+
}
|
|
128
|
+
const result = UpdateWebhookEndpointRequestSchema.parse(payload)
|
|
129
|
+
expect(result).toEqual(payload)
|
|
130
|
+
})
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
describe('status values', () => {
|
|
134
|
+
it('accepts active status', () => {
|
|
135
|
+
const result = UpdateWebhookEndpointRequestSchema.parse({ status: 'active' })
|
|
136
|
+
expect(result.status).toBe('active')
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
it('accepts paused status', () => {
|
|
140
|
+
const result = UpdateWebhookEndpointRequestSchema.parse({ status: 'paused' })
|
|
141
|
+
expect(result.status).toBe('paused')
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
it('rejects invalid status value', () => {
|
|
145
|
+
expect(() => UpdateWebhookEndpointRequestSchema.parse({ status: 'disabled' })).toThrow()
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
it('rejects empty string status', () => {
|
|
149
|
+
expect(() => UpdateWebhookEndpointRequestSchema.parse({ status: '' })).toThrow()
|
|
150
|
+
})
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
describe('at least one field required', () => {
|
|
154
|
+
it('rejects empty object', () => {
|
|
155
|
+
expect(() => UpdateWebhookEndpointRequestSchema.parse({})).toThrow(
|
|
156
|
+
'At least one field (name, description, resourceId, or status) must be provided'
|
|
157
|
+
)
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
it('rejects object with all fields explicitly undefined', () => {
|
|
161
|
+
expect(() =>
|
|
162
|
+
UpdateWebhookEndpointRequestSchema.parse({
|
|
163
|
+
name: undefined,
|
|
164
|
+
description: undefined,
|
|
165
|
+
resourceId: undefined,
|
|
166
|
+
status: undefined
|
|
167
|
+
})
|
|
168
|
+
).toThrow('At least one field')
|
|
169
|
+
})
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
describe('SECURITY: mass assignment prevention', () => {
|
|
173
|
+
it('rejects organizationId injection', () => {
|
|
174
|
+
const payload = { name: 'Valid', organizationId: 'attacker-org' }
|
|
175
|
+
expect(() => UpdateWebhookEndpointRequestSchema.parse(payload)).toThrow()
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
it('rejects id injection', () => {
|
|
179
|
+
const payload = { name: 'Valid', id: validUuid }
|
|
180
|
+
expect(() => UpdateWebhookEndpointRequestSchema.parse(payload)).toThrow()
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
it('rejects unknown fields', () => {
|
|
184
|
+
const payload = { name: 'Valid', unknownField: 'value' }
|
|
185
|
+
expect(() => UpdateWebhookEndpointRequestSchema.parse(payload)).toThrow()
|
|
186
|
+
})
|
|
187
|
+
})
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
describe('ListWebhookEndpointsQuerySchema', () => {
|
|
191
|
+
describe('defaults', () => {
|
|
192
|
+
it('accepts empty query and applies defaults', () => {
|
|
193
|
+
const result = ListWebhookEndpointsQuerySchema.parse({})
|
|
194
|
+
expect(result.limit).toBe(20)
|
|
195
|
+
expect(result.offset).toBe(0)
|
|
196
|
+
expect(result.status).toBeUndefined()
|
|
197
|
+
})
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
describe('status filter', () => {
|
|
201
|
+
it('accepts active status filter', () => {
|
|
202
|
+
const result = ListWebhookEndpointsQuerySchema.parse({ status: 'active' })
|
|
203
|
+
expect(result.status).toBe('active')
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
it('accepts paused status filter', () => {
|
|
207
|
+
const result = ListWebhookEndpointsQuerySchema.parse({ status: 'paused' })
|
|
208
|
+
expect(result.status).toBe('paused')
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
it('rejects invalid status value', () => {
|
|
212
|
+
expect(() => ListWebhookEndpointsQuerySchema.parse({ status: 'deleted' })).toThrow()
|
|
213
|
+
})
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
describe('string coercion (querystring params arrive as strings)', () => {
|
|
217
|
+
it('coerces string limit to number', () => {
|
|
218
|
+
const result = ListWebhookEndpointsQuerySchema.parse({ limit: '50' })
|
|
219
|
+
expect(result.limit).toBe(50)
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
it('coerces string offset to number', () => {
|
|
223
|
+
const result = ListWebhookEndpointsQuerySchema.parse({ offset: '100' })
|
|
224
|
+
expect(result.offset).toBe(100)
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
it('coerces both limit and offset together', () => {
|
|
228
|
+
const result = ListWebhookEndpointsQuerySchema.parse({ limit: '10', offset: '30' })
|
|
229
|
+
expect(result.limit).toBe(10)
|
|
230
|
+
expect(result.offset).toBe(30)
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
it('rejects limit above max (100)', () => {
|
|
234
|
+
expect(() => ListWebhookEndpointsQuerySchema.parse({ limit: '101' })).toThrow()
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
it('rejects negative offset', () => {
|
|
238
|
+
expect(() => ListWebhookEndpointsQuerySchema.parse({ offset: '-1' })).toThrow()
|
|
239
|
+
})
|
|
240
|
+
})
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
describe('WebhookEndpointResponseSchema', () => {
|
|
244
|
+
const validResponse = {
|
|
245
|
+
id: validUuid,
|
|
246
|
+
organizationId: validUuid2,
|
|
247
|
+
key: 'whk_abc123def456',
|
|
248
|
+
name: 'Stripe Inbound',
|
|
249
|
+
description: 'Handles Stripe webhook events',
|
|
250
|
+
resourceId: 'stripe-handler-workflow',
|
|
251
|
+
status: 'active' as const,
|
|
252
|
+
lastTriggeredAt: '2026-03-01T12:00:00.000Z',
|
|
253
|
+
requestCount: 42,
|
|
254
|
+
createdAt: '2026-01-01T00:00:00.000Z',
|
|
255
|
+
updatedAt: '2026-03-01T12:00:00.000Z'
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
it('accepts a complete valid response object', () => {
|
|
259
|
+
const result = WebhookEndpointResponseSchema.parse(validResponse)
|
|
260
|
+
expect(result.id).toBe(validUuid)
|
|
261
|
+
expect(result.status).toBe('active')
|
|
262
|
+
expect(result.requestCount).toBe(42)
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
describe('nullable fields', () => {
|
|
266
|
+
it('accepts null description', () => {
|
|
267
|
+
const result = WebhookEndpointResponseSchema.parse({ ...validResponse, description: null })
|
|
268
|
+
expect(result.description).toBeNull()
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
it('accepts null lastTriggeredAt (endpoint never triggered)', () => {
|
|
272
|
+
const result = WebhookEndpointResponseSchema.parse({ ...validResponse, lastTriggeredAt: null })
|
|
273
|
+
expect(result.lastTriggeredAt).toBeNull()
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
it('accepts both nullable fields as null', () => {
|
|
277
|
+
const result = WebhookEndpointResponseSchema.parse({
|
|
278
|
+
...validResponse,
|
|
279
|
+
description: null,
|
|
280
|
+
lastTriggeredAt: null
|
|
281
|
+
})
|
|
282
|
+
expect(result.description).toBeNull()
|
|
283
|
+
expect(result.lastTriggeredAt).toBeNull()
|
|
284
|
+
})
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
describe('forward compatibility (not strict)', () => {
|
|
288
|
+
it('accepts extra fields in response (allows API additions without breaking clients)', () => {
|
|
289
|
+
const result = WebhookEndpointResponseSchema.parse({
|
|
290
|
+
...validResponse,
|
|
291
|
+
newFieldFromFutureApiVersion: 'some-value'
|
|
292
|
+
})
|
|
293
|
+
expect(result.id).toBe(validUuid)
|
|
294
|
+
})
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
describe('field validation', () => {
|
|
298
|
+
it('rejects invalid UUID for id', () => {
|
|
299
|
+
expect(() => WebhookEndpointResponseSchema.parse({ ...validResponse, id: 'not-a-uuid' })).toThrow()
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
it('rejects invalid UUID for organizationId', () => {
|
|
303
|
+
expect(() => WebhookEndpointResponseSchema.parse({ ...validResponse, organizationId: 'not-a-uuid' })).toThrow()
|
|
304
|
+
})
|
|
305
|
+
|
|
306
|
+
it('rejects negative requestCount', () => {
|
|
307
|
+
expect(() => WebhookEndpointResponseSchema.parse({ ...validResponse, requestCount: -1 })).toThrow()
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
it('rejects invalid datetime for createdAt', () => {
|
|
311
|
+
expect(() => WebhookEndpointResponseSchema.parse({ ...validResponse, createdAt: 'not-a-date' })).toThrow()
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
it('rejects invalid status in response', () => {
|
|
315
|
+
expect(() => WebhookEndpointResponseSchema.parse({ ...validResponse, status: 'unknown' })).toThrow()
|
|
316
|
+
})
|
|
317
|
+
})
|
|
318
|
+
})
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { UuidSchema, NonEmptyStringSchema, PaginationSchema } from '../../platform/utils/validation'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Webhook Endpoint API Validation Schemas
|
|
6
|
+
*
|
|
7
|
+
* HTTP request/response validation for the webhook endpoints CRUD API.
|
|
8
|
+
* These schemas are browser-safe and shared between API and frontend.
|
|
9
|
+
*
|
|
10
|
+
* Design notes:
|
|
11
|
+
* - Request schemas use `.strict()` to reject unknown fields (mass assignment prevention)
|
|
12
|
+
* - Response schemas do NOT use `.strict()` (allows forward-compatible additions)
|
|
13
|
+
* - Update schema requires at least one field via `.refine()`
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Status enum for webhook endpoints
|
|
18
|
+
*/
|
|
19
|
+
export const WebhookEndpointStatusSchema = z.enum(['active', 'paused'])
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* POST /api/webhook-endpoints - Create a new webhook endpoint
|
|
23
|
+
*
|
|
24
|
+
* The `key` and `id` are generated server-side and not accepted in the request.
|
|
25
|
+
*/
|
|
26
|
+
export const CreateWebhookEndpointRequestSchema = z
|
|
27
|
+
.object({
|
|
28
|
+
/** User-facing label for the endpoint */
|
|
29
|
+
name: NonEmptyStringSchema,
|
|
30
|
+
/** Target workflow resourceId to invoke on inbound requests (can be set later) */
|
|
31
|
+
resourceId: NonEmptyStringSchema.optional(),
|
|
32
|
+
/** Optional description */
|
|
33
|
+
description: z.string().optional()
|
|
34
|
+
})
|
|
35
|
+
.strict()
|
|
36
|
+
|
|
37
|
+
export type CreateWebhookEndpointRequest = z.infer<typeof CreateWebhookEndpointRequestSchema>
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* PATCH /api/webhook-endpoints/:id - Update an existing webhook endpoint
|
|
41
|
+
*
|
|
42
|
+
* At least one field must be provided.
|
|
43
|
+
*/
|
|
44
|
+
export const UpdateWebhookEndpointRequestSchema = z
|
|
45
|
+
.object({
|
|
46
|
+
name: NonEmptyStringSchema.optional(),
|
|
47
|
+
description: z.string().optional(),
|
|
48
|
+
resourceId: NonEmptyStringSchema.optional(),
|
|
49
|
+
status: WebhookEndpointStatusSchema.optional()
|
|
50
|
+
})
|
|
51
|
+
.strict()
|
|
52
|
+
.refine(
|
|
53
|
+
(data) =>
|
|
54
|
+
data.name !== undefined ||
|
|
55
|
+
data.description !== undefined ||
|
|
56
|
+
data.resourceId !== undefined ||
|
|
57
|
+
data.status !== undefined,
|
|
58
|
+
{ message: 'At least one field (name, description, resourceId, or status) must be provided' }
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
export type UpdateWebhookEndpointRequest = z.infer<typeof UpdateWebhookEndpointRequestSchema>
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Response shape for a single webhook endpoint.
|
|
65
|
+
* NOT strict — response schemas allow extra fields for forward compatibility.
|
|
66
|
+
*/
|
|
67
|
+
export const WebhookEndpointResponseSchema = z.object({
|
|
68
|
+
id: UuidSchema,
|
|
69
|
+
organizationId: UuidSchema,
|
|
70
|
+
key: z.string(),
|
|
71
|
+
name: z.string(),
|
|
72
|
+
description: z.string().nullable(),
|
|
73
|
+
resourceId: z.string().nullable(),
|
|
74
|
+
status: WebhookEndpointStatusSchema,
|
|
75
|
+
lastTriggeredAt: z.string().datetime().nullable(),
|
|
76
|
+
requestCount: z.number().int().min(0),
|
|
77
|
+
createdAt: z.string().datetime(),
|
|
78
|
+
updatedAt: z.string().datetime()
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
export type WebhookEndpointResponse = z.infer<typeof WebhookEndpointResponseSchema>
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* GET /api/webhook-endpoints - List webhook endpoints query params
|
|
85
|
+
*
|
|
86
|
+
* Extends standard pagination with optional status filter.
|
|
87
|
+
*/
|
|
88
|
+
export const ListWebhookEndpointsQuerySchema = PaginationSchema.extend({
|
|
89
|
+
status: WebhookEndpointStatusSchema.optional()
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
export type ListWebhookEndpointsQuery = z.infer<typeof ListWebhookEndpointsQuerySchema>
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Named collection of all webhook endpoint schemas for route registration
|
|
96
|
+
*/
|
|
97
|
+
export const WebhookEndpointSchemas = {
|
|
98
|
+
CreateRequest: CreateWebhookEndpointRequestSchema,
|
|
99
|
+
UpdateRequest: UpdateWebhookEndpointRequestSchema,
|
|
100
|
+
Response: WebhookEndpointResponseSchema,
|
|
101
|
+
ListQuery: ListWebhookEndpointsQuerySchema
|
|
102
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Webhook Endpoints Integration
|
|
3
|
+
*
|
|
4
|
+
* Generic inbound webhook endpoints — provider-agnostic, self-service, revocable.
|
|
5
|
+
* Each endpoint gets a unique opaque URL that maps to a target workflow.
|
|
6
|
+
*
|
|
7
|
+
* @see apps/docs/content/docs/in-progress/active-development/generic-webhook-endpoints/index.mdx
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// Domain types
|
|
11
|
+
export type { WebhookEndpoint, WebhookEndpointStatus } from './types'
|
|
12
|
+
|
|
13
|
+
// API schemas and inferred types
|
|
14
|
+
export {
|
|
15
|
+
WebhookEndpointStatusSchema,
|
|
16
|
+
CreateWebhookEndpointRequestSchema,
|
|
17
|
+
UpdateWebhookEndpointRequestSchema,
|
|
18
|
+
WebhookEndpointResponseSchema,
|
|
19
|
+
ListWebhookEndpointsQuerySchema,
|
|
20
|
+
WebhookEndpointSchemas
|
|
21
|
+
} from './api-schemas'
|
|
22
|
+
|
|
23
|
+
export type {
|
|
24
|
+
CreateWebhookEndpointRequest,
|
|
25
|
+
UpdateWebhookEndpointRequest,
|
|
26
|
+
WebhookEndpointResponse,
|
|
27
|
+
ListWebhookEndpointsQuery
|
|
28
|
+
} from './api-schemas'
|