@whooshbang/contracts 1.0.0-rc.13
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/CHANGELOG.md +198 -0
- package/LICENSE +21 -0
- package/README.md +120 -0
- package/contract-manifest.json +390 -0
- package/dist/base64url.d.ts +2 -0
- package/dist/base64url.js +51 -0
- package/dist/base64url.js.map +1 -0
- package/dist/generated/artifact-index.d.ts +205 -0
- package/dist/generated/artifact-index.js +396 -0
- package/dist/generated/artifact-index.js.map +1 -0
- package/dist/generated/contract-manifest.d.ts +66 -0
- package/dist/generated/contract-manifest.js +6 -0
- package/dist/generated/contract-manifest.js.map +1 -0
- package/dist/generated/core-scenario.d.ts +18 -0
- package/dist/generated/core-scenario.js +6 -0
- package/dist/generated/core-scenario.js.map +1 -0
- package/dist/generated/fixture-index.d.ts +33 -0
- package/dist/generated/fixture-index.js +6 -0
- package/dist/generated/fixture-index.js.map +1 -0
- package/dist/generated/interaction-machine-scenario.d.ts +263 -0
- package/dist/generated/interaction-machine-scenario.js +6 -0
- package/dist/generated/interaction-machine-scenario.js.map +1 -0
- package/dist/generated/interaction-v1.d.ts +152 -0
- package/dist/generated/interaction-v1.js +6 -0
- package/dist/generated/interaction-v1.js.map +1 -0
- package/dist/generated/machine-v1.d.ts +169 -0
- package/dist/generated/machine-v1.js +6 -0
- package/dist/generated/machine-v1.js.map +1 -0
- package/dist/generated/provider-capabilities-v1.d.ts +78 -0
- package/dist/generated/provider-capabilities-v1.js +6 -0
- package/dist/generated/provider-capabilities-v1.js.map +1 -0
- package/dist/generated/validators-standalone.d.ts +659 -0
- package/dist/generated/validators-standalone.js +47800 -0
- package/dist/generated/validators-standalone.js.map +1 -0
- package/dist/generated/validators.d.ts +74 -0
- package/dist/generated/validators.js +493 -0
- package/dist/generated/validators.js.map +1 -0
- package/dist/generated/webhook-delivery-case-v1.d.ts +91 -0
- package/dist/generated/webhook-delivery-case-v1.js +6 -0
- package/dist/generated/webhook-delivery-case-v1.js.map +1 -0
- package/dist/generated/webhook-signing-vector-v1.d.ts +88 -0
- package/dist/generated/webhook-signing-vector-v1.js +6 -0
- package/dist/generated/webhook-signing-vector-v1.js.map +1 -0
- package/dist/generated/whooshbang-event-v1.d.ts +191 -0
- package/dist/generated/whooshbang-event-v1.js +6 -0
- package/dist/generated/whooshbang-event-v1.js.map +1 -0
- package/dist/generated/whooshbang-v1.d.ts +1162 -0
- package/dist/generated/whooshbang-v1.js +6 -0
- package/dist/generated/whooshbang-v1.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/machine-credential.d.ts +31 -0
- package/dist/machine-credential.js +78 -0
- package/dist/machine-credential.js.map +1 -0
- package/dist/webhook-consumer.d.ts +50 -0
- package/dist/webhook-consumer.js +71 -0
- package/dist/webhook-consumer.js.map +1 -0
- package/dist/webhook-delivery.d.ts +46 -0
- package/dist/webhook-delivery.js +126 -0
- package/dist/webhook-delivery.js.map +1 -0
- package/dist/webhook-signature.d.ts +36 -0
- package/dist/webhook-signature.js +255 -0
- package/dist/webhook-signature.js.map +1 -0
- package/examples/python/verify_webhook.py +283 -0
- package/fixtures/.gitkeep +1 -0
- package/fixtures/core-v1/fixture-index.json +963 -0
- package/fixtures/core-v1/problems/auth-invalid.json +9 -0
- package/fixtures/core-v1/problems/authentication-required.json +9 -0
- package/fixtures/core-v1/problems/cancellation-too-late.json +9 -0
- package/fixtures/core-v1/problems/endpoint-rotation-conflict.json +10 -0
- package/fixtures/core-v1/problems/environment-mismatch.json +9 -0
- package/fixtures/core-v1/problems/idempotency-conflict.json +9 -0
- package/fixtures/core-v1/problems/idempotency-key-required.json +9 -0
- package/fixtures/core-v1/problems/project-slug-conflict.json +16 -0
- package/fixtures/core-v1/problems/provider-outcome-unknown.json +9 -0
- package/fixtures/core-v1/problems/provider-retryable.json +10 -0
- package/fixtures/core-v1/problems/provider-terminal.json +9 -0
- package/fixtures/core-v1/problems/rate-limited.json +10 -0
- package/fixtures/core-v1/problems/replay-unavailable-not-terminal.json +16 -0
- package/fixtures/core-v1/problems/replay-unavailable-payload.json +16 -0
- package/fixtures/core-v1/problems/replay-unavailable-rollout.json +16 -0
- package/fixtures/core-v1/problems/request-invalid.json +16 -0
- package/fixtures/core-v1/problems/resource-expired.json +9 -0
- package/fixtures/core-v1/problems/resource-not-found.json +9 -0
- package/fixtures/core-v1/problems/scope-forbidden.json +9 -0
- package/fixtures/core-v1/problems/subscriber-unbound.json +9 -0
- package/fixtures/core-v1/requests/create-api-credential.json +9 -0
- package/fixtures/core-v1/requests/create-customer-endpoint.json +5 -0
- package/fixtures/core-v1/requests/create-message.json +15 -0
- package/fixtures/core-v1/requests/create-project.json +4 -0
- package/fixtures/core-v1/requests/create-subscription-link.json +5 -0
- package/fixtures/core-v1/requests/rotate-api-credential.json +3 -0
- package/fixtures/core-v1/requests/rotate-customer-endpoint.json +3 -0
- package/fixtures/core-v1/scenarios/api-credential-invalid-indistinguishable.json +14 -0
- package/fixtures/core-v1/scenarios/api-credential-one-time-reveal.json +21 -0
- package/fixtures/core-v1/scenarios/api-credential-revocation-replay.json +17 -0
- package/fixtures/core-v1/scenarios/api-credential-rotation-overlap.json +18 -0
- package/fixtures/core-v1/scenarios/cancellation-too-late.json +15 -0
- package/fixtures/core-v1/scenarios/cross-account-project.json +14 -0
- package/fixtures/core-v1/scenarios/cross-project-resource.json +17 -0
- package/fixtures/core-v1/scenarios/environment-mismatch.json +17 -0
- package/fixtures/core-v1/scenarios/idempotency-conflict.json +35 -0
- package/fixtures/core-v1/scenarios/idempotency-equivalent-replay.json +27 -0
- package/fixtures/core-v1/scenarios/malformed-bearer.json +14 -0
- package/fixtures/core-v1/scenarios/metadata-overrun.json +14 -0
- package/fixtures/core-v1/scenarios/missing-scope.json +15 -0
- package/fixtures/core-v1/scenarios/project-create-equivalent-replay.json +26 -0
- package/fixtures/core-v1/scenarios/project-slug-conflict.json +21 -0
- package/fixtures/core-v1/scenarios/provider-outcome-unknown.json +15 -0
- package/fixtures/core-v1/scenarios/subscription-activation-replay.json +18 -0
- package/fixtures/core-v1/scenarios/subscription-expired-replay.json +16 -0
- package/fixtures/core-v1/success/account-projection.json +11 -0
- package/fixtures/core-v1/success/api-credential-collection.json +22 -0
- package/fixtures/core-v1/success/api-credential-created.json +20 -0
- package/fixtures/core-v1/success/api-credential-revoked.json +19 -0
- package/fixtures/core-v1/success/api-credential-rotated.json +41 -0
- package/fixtures/core-v1/success/customer-endpoint-attempts.json +15 -0
- package/fixtures/core-v1/success/customer-endpoint-collection.json +26 -0
- package/fixtures/core-v1/success/customer-endpoint-create-replayed.json +16 -0
- package/fixtures/core-v1/success/customer-endpoint-created.json +17 -0
- package/fixtures/core-v1/success/customer-endpoint-rotated.json +21 -0
- package/fixtures/core-v1/success/message-accepted.json +30 -0
- package/fixtures/core-v1/success/message-cancellation-too-late.json +27 -0
- package/fixtures/core-v1/success/message-cancelled.json +27 -0
- package/fixtures/core-v1/success/message-outcome-unknown.json +33 -0
- package/fixtures/core-v1/success/message-provider-accepted.json +30 -0
- package/fixtures/core-v1/success/project-collection.json +42 -0
- package/fixtures/core-v1/success/project-created.json +38 -0
- package/fixtures/core-v1/success/subscription-link-activated.json +22 -0
- package/fixtures/core-v1/success/subscription-link-cancelled.json +14 -0
- package/fixtures/core-v1/success/subscription-link-paused.json +22 -0
- package/fixtures/core-v1/success/subscription-link-pending.json +14 -0
- package/fixtures/core-v1/success/subscription-link-revoked.json +22 -0
- package/fixtures/interaction-machine-v1/capabilities/providers.json +57 -0
- package/fixtures/interaction-machine-v1/capabilities/telegram.json +52 -0
- package/fixtures/interaction-machine-v1/events/confirm-answered.json +22 -0
- package/fixtures/interaction-machine-v1/events/input-answered.json +22 -0
- package/fixtures/interaction-machine-v1/events/input-redacted.json +18 -0
- package/fixtures/interaction-machine-v1/events/select-answered.json +22 -0
- package/fixtures/interaction-machine-v1/requests/ack-processed.json +5 -0
- package/fixtures/interaction-machine-v1/requests/ack-quarantined.json +5 -0
- package/fixtures/interaction-machine-v1/requests/create-machine-client.json +6 -0
- package/fixtures/interaction-machine-v1/requests/create-message-confirm.json +18 -0
- package/fixtures/interaction-machine-v1/requests/create-message-input.json +16 -0
- package/fixtures/interaction-machine-v1/requests/create-message-select.json +26 -0
- package/fixtures/interaction-machine-v1/requests/register-machine-credential.json +4 -0
- package/fixtures/interaction-machine-v1/scenarios/ack-conflict.json +14 -0
- package/fixtures/interaction-machine-v1/scenarios/ack-cross-machine.json +15 -0
- package/fixtures/interaction-machine-v1/scenarios/ack-out-of-order.json +15 -0
- package/fixtures/interaction-machine-v1/scenarios/ack-replay.json +17 -0
- package/fixtures/interaction-machine-v1/scenarios/ambiguous-free-text.json +15 -0
- package/fixtures/interaction-machine-v1/scenarios/callback-token-opacity.json +16 -0
- package/fixtures/interaction-machine-v1/scenarios/credential-registration-conflict.json +16 -0
- package/fixtures/interaction-machine-v1/scenarios/credential-registration-replay.json +15 -0
- package/fixtures/interaction-machine-v1/scenarios/credential-revocation-replay.json +14 -0
- package/fixtures/interaction-machine-v1/scenarios/credential-rotation.json +16 -0
- package/fixtures/interaction-machine-v1/scenarios/first-credential-activates.json +15 -0
- package/fixtures/interaction-machine-v1/scenarios/interaction-duplicate.json +16 -0
- package/fixtures/interaction-machine-v1/scenarios/interaction-expired.json +17 -0
- package/fixtures/interaction-machine-v1/scenarios/interaction-mismatch.json +16 -0
- package/fixtures/interaction-machine-v1/scenarios/interaction-retired.json +16 -0
- package/fixtures/interaction-machine-v1/scenarios/interaction-stale.json +16 -0
- package/fixtures/interaction-machine-v1/scenarios/machine-admin-denied.json +14 -0
- package/fixtures/interaction-machine-v1/scenarios/machine-bound-message-create.json +22 -0
- package/fixtures/interaction-machine-v1/scenarios/machine-cancel-denied.json +14 -0
- package/fixtures/interaction-machine-v1/scenarios/machine-crash-before-ack.json +16 -0
- package/fixtures/interaction-machine-v1/scenarios/machine-notifier-mismatch.json +14 -0
- package/fixtures/interaction-machine-v1/scenarios/machine-other-message-read.json +14 -0
- package/fixtures/interaction-machine-v1/scenarios/machine-owned-message-read.json +22 -0
- package/fixtures/interaction-machine-v1/scenarios/machine-subscriber-mismatch.json +14 -0
- package/fixtures/interaction-machine-v1/scenarios/poll-uncommitted-cursor-rejected.json +17 -0
- package/fixtures/interaction-machine-v1/scenarios/project-credential-machine-endpoint-denied.json +14 -0
- package/fixtures/interaction-machine-v1/scenarios/quarantine-integrity-rejected.json +15 -0
- package/fixtures/interaction-machine-v1/scenarios/quarantine-signature-rejected.json +16 -0
- package/fixtures/interaction-machine-v1/scenarios/quarantine-valid.json +15 -0
- package/fixtures/interaction-machine-v1/scenarios/revoked-machine-credential.json +15 -0
- package/fixtures/interaction-machine-v1/success/ack-out-of-order.json +8 -0
- package/fixtures/interaction-machine-v1/success/ack-processed.json +8 -0
- package/fixtures/interaction-machine-v1/success/ack-quarantined.json +9 -0
- package/fixtures/interaction-machine-v1/success/interaction-answered-confirm.json +10 -0
- package/fixtures/interaction-machine-v1/success/interaction-answered-input.json +10 -0
- package/fixtures/interaction-machine-v1/success/interaction-answered-redacted.json +8 -0
- package/fixtures/interaction-machine-v1/success/interaction-answered-select.json +10 -0
- package/fixtures/interaction-machine-v1/success/interaction-open.json +7 -0
- package/fixtures/interaction-machine-v1/success/machine-client-active.json +20 -0
- package/fixtures/interaction-machine-v1/success/machine-client-provisioning.json +19 -0
- package/fixtures/interaction-machine-v1/success/machine-client-revoked.json +21 -0
- package/fixtures/interaction-machine-v1/success/machine-credential-active.json +13 -0
- package/fixtures/interaction-machine-v1/success/machine-credential-revoked.json +14 -0
- package/fixtures/interaction-machine-v1/success/message-interaction-open.json +33 -0
- package/fixtures/interaction-machine-v1/success/poll-empty.json +6 -0
- package/fixtures/interaction-machine-v1/success/poll-events.json +51 -0
- package/fixtures/interaction-machine-v1/tokens/callback-token.json +1 -0
- package/fixtures/whooshbang-events-v1/delivery/cases.json +418 -0
- package/fixtures/whooshbang-events-v1/events/agent-binding-degraded.json +17 -0
- package/fixtures/whooshbang-events-v1/events/interaction-expired.json +16 -0
- package/fixtures/whooshbang-events-v1/events/interaction-received.json +23 -0
- package/fixtures/whooshbang-events-v1/events/message-delivered.json +16 -0
- package/fixtures/whooshbang-events-v1/events/message-expired.json +15 -0
- package/fixtures/whooshbang-events-v1/events/message-failed.json +17 -0
- package/fixtures/whooshbang-events-v1/events/message-provider-accepted.json +15 -0
- package/fixtures/whooshbang-events-v1/events/subscription-activated.json +18 -0
- package/fixtures/whooshbang-events-v1/events/subscription-revoked.json +18 -0
- package/fixtures/whooshbang-events-v1/signing/vectors.json +852 -0
- package/openapi/.gitkeep +1 -0
- package/openapi/generated/whooshbang-v1.bundle.json +5913 -0
- package/openapi/whooshbang-v1.yaml +2334 -0
- package/package.json +63 -0
- package/schemas/contract-manifest.schema.json +140 -0
- package/schemas/core-scenario.schema.json +38 -0
- package/schemas/events/agent-binding-degraded-v1.schema.json +8 -0
- package/schemas/events/interaction-expired-v1.schema.json +8 -0
- package/schemas/events/interaction-received-v1.schema.json +8 -0
- package/schemas/events/message-delivered-v1.schema.json +8 -0
- package/schemas/events/message-expired-v1.schema.json +8 -0
- package/schemas/events/message-failed-v1.schema.json +8 -0
- package/schemas/events/message-provider-accepted-v1.schema.json +8 -0
- package/schemas/events/subscription-activated-v1.schema.json +8 -0
- package/schemas/events/subscription-revoked-v1.schema.json +8 -0
- package/schemas/events/whooshbang-event-v1.schema.json +689 -0
- package/schemas/fixture-index.schema.json +67 -0
- package/schemas/interaction-machine-scenario.schema.json +700 -0
- package/schemas/interaction-v1.schema.json +483 -0
- package/schemas/machine-v1.schema.json +430 -0
- package/schemas/provider-capabilities-v1.schema.json +263 -0
- package/schemas/webhooks/webhook-delivery-case-v1.schema.json +167 -0
- package/schemas/webhooks/webhook-signing-vector-v1.schema.json +197 -0
- package/schemas/whooshbang-v1.schema.json +2173 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"whooshbang-v1.js","sourceRoot":"","sources":["../../src/generated/whooshbang-v1.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export { contractManifest, contractVersion, } from "./generated/artifact-index.js";
|
|
2
|
+
export { createMachineCredentialMaterial, verifyMachineCredentialToken, } from "./machine-credential.js";
|
|
3
|
+
export { DEFAULT_WEBHOOK_TIMESTAMP_TOLERANCE_SECONDS, MAX_WEBHOOK_SECRETS, MAX_WEBHOOK_SIGNATURES, verifyWebhookSignature, } from "./webhook-signature.js";
|
|
4
|
+
export { consumeVerifiedWebhook, MIN_WEBHOOK_DEDUP_RETENTION_SECONDS, } from "./webhook-consumer.js";
|
|
5
|
+
export { classifyWebhookDeliveryOutcome, MAX_WEBHOOK_DELIVERY_ATTEMPTS, MAX_WEBHOOK_DELIVERY_WINDOW_SECONDS, parseWebhookRetryAfter, WEBHOOK_FETCH_REDIRECT_MODE, } from "./webhook-delivery.js";
|
|
6
|
+
export type { MachineCredentialCrypto, MachineCredentialMaterial, MachineCredentialRegistration, MachineCredentialVerificationRecord, } from "./machine-credential.js";
|
|
7
|
+
export type { RejectedWebhookSignature, VerifiedWebhookSignature, VerifyWebhookSignatureOptions, WebhookCrypto, WebhookHeaderEntry, WebhookHeaderRecord, WebhookHeaders, WebhookSignatureVerification, WebhookVerificationFailureReason, } from "./webhook-signature.js";
|
|
8
|
+
export type { ConsumeVerifiedWebhookOptions, DuplicateWebhookConsumption, ProcessedWebhookConsumption, RejectedWebhookConsumption, WebhookConsumptionResult, WebhookConsumerRejectionReason, WebhookDeduplicationInput, WebhookDeduplicationOutcome, WebhookDeduplicationStore, } from "./webhook-consumer.js";
|
|
9
|
+
export type { AcceptedWebhookDelivery, ClassifyWebhookDeliveryOptions, HttpDeliveryOutcome, NetworkFailureOutcome, RetryWebhookDelivery, TerminalWebhookDelivery, WebhookDeliveryClassification, WebhookDeliveryOutcome, WebhookDeliveryRetryReason, WebhookDeliveryTerminalReason, } from "./webhook-delivery.js";
|
|
10
|
+
export { contractValidationErrors, validateAccountProjection, validateApiCredential, validateApiCredentialCollection, validateApiCredentialRotation, validateApiCredentialSecretReveal, validateBindingSummary, validateCreateApiCredentialRequest, validateCreateCustomerEndpointRequest, validateCreateProjectRequest, validateCreateMachineClientRequest, validateCreateMessageRequest, validateCreateSubscriptionLinkRequest, validateDeliveryAttemptSummary, validateCustomerEndpoint, validateCustomerEndpointAttemptCollection, validateCustomerEndpointCollection, validateCustomerEndpointMutationResult, validateCustomerEndpointVerificationChallenge, validateIdempotencyKey, validateAgentBindingDegradedEvent, validateInteractionEvent, validateInteractionExpiredEvent, validateInteractionReceivedEvent, validateInteractionRequest, validateInteractionSummary, validateMachineClient, validateMachineCredential, validateMachineCredentialBearer, validateMachineEventAckRequest, validateMachineEventAckResponse, validateMachineEventPollResponse, validateMessage, validateMessageDeliveredEvent, validateMessageExpiredEvent, validateMessageFailedEvent, validateMessageProviderAcceptedEvent, validateWhooshBangEvent, validateProblem, validateProject, validateProjectCollection, validateProviderCallbackToken, validateProviderCapabilities, validateProviderCapabilitiesCollection, validateRegisterMachineCredentialRequest, validateRotateApiCredentialRequest, validateRotateCustomerEndpointRequest, validateSubscriptionActivatedEvent, validateSubscriptionLink, validateSubscriptionRevokedEvent, validateWebhookDeliveryCaseSet, validateWebhookSigningVectorSet, } from "./generated/validators.js";
|
|
11
|
+
export type { Artifact, ContractManifest, FixtureSet, GeneratedArtifact, } from "./generated/contract-manifest.js";
|
|
12
|
+
export type { AgentBindingDegradedData, AgentBindingDegradedEvent, ConfirmInteractionResponse as CustomerEventConfirmInteractionResponse, InteractionExpiredData, InteractionExpiredEvent, InteractionReceivedData, InteractionReceivedEvent, InteractionResponse as CustomerEventInteractionResponse, MessageDeliveredData, MessageDeliveredEvent, MessageExpiredData, MessageExpiredEvent, MessageFailedData, MessageFailedEvent, MessageProviderAcceptedData, MessageProviderAcceptedEvent, WhooshBangEventV1ContractDocument, SelectInteractionResponse as CustomerEventSelectInteractionResponse, SubscriptionActivatedData, SubscriptionActivatedEvent, SubscriptionRevokedData, SubscriptionRevokedEvent, InputInteractionResponse as CustomerEventInputInteractionResponse, } from "./generated/whooshbang-event-v1.js";
|
|
13
|
+
export type { WebhookDeliveryCaseSetV1, DeliveryCase as WebhookDeliveryFixtureCase, } from "./generated/webhook-delivery-case-v1.js";
|
|
14
|
+
export type { WebhookSigningVectorSetV1, VerificationCase as WebhookSigningVerificationCase, } from "./generated/webhook-signing-vector-v1.js";
|
|
15
|
+
export type { AccountMembershipSummary, AccountProjection, AccountRole, ApiCredential, ApiCredentialCollection, ApiCredentialId, ApiCredentialPrefix, ApiCredentialRotation, ApiCredentialScope, ApiCredentialScopeSet, ApiCredentialSecret, ApiCredentialSecretReveal, BindingSummary, ClassifiedError, CreateApiCredentialRequest, CreateCustomerEndpointRequest, CreateMessageRequest, CreateProjectRequest, DefaultNotifierSummary, CreateSubscriptionLinkRequest, DeliveryAttemptSummary, CustomerEndpoint, CustomerEndpointAttemptCollection, CustomerEndpointAttemptDiagnostic, CustomerEndpointCollection, CustomerEndpointFailure, CustomerEndpointMutationResult, CustomerEndpointSecret, CustomerEndpointStatus, CustomerEndpointVerificationChallenge, CustomerEventType, Environment, IdempotencyKey, Message, MessageState, WhooshBangV1ContractDocument, Problem, ProblemCode, Project, ProjectCollection, ProjectEnvironment, ProviderCapabilitiesCollection, RotateApiCredentialRequest, RotateCustomerEndpointRequest, SubscriptionLink, } from "./generated/whooshbang-v1.js";
|
|
16
|
+
export type { ConfirmInteractionRequest, ConfirmInteractionResponse, InputInteractionRequest, InputInteractionResponse, InteractionEvent, InteractionOption, InteractionRequest, InteractionResponse, InteractionState, InteractionSummary, ProviderCallbackToken, SafeChannelContext, SelectInteractionRequest, SelectInteractionResponse, } from "./generated/interaction-v1.js";
|
|
17
|
+
export type { InteractionMachineScenario } from "./generated/interaction-machine-scenario.js";
|
|
18
|
+
export type { CreateMachineClientRequest, MachineClient, MachineCredential, MachineCredentialBearer, MachineCredentialId, MachineCursor, MachineEventAckDisposition, MachineEventAckRequest, MachineEventAckResponse, MachineEventPollResponse, MachineScope, MachineScopeSummary, MachineSecretDigest, QuarantineReasonCode, RegisterMachineCredentialRequest, } from "./generated/machine-v1.js";
|
|
19
|
+
export type { ContextSupport, Degradation, InteractionCapability, ProviderCapabilitiesV1, } from "./generated/provider-capabilities-v1.js";
|
|
20
|
+
export type { ContractValidationError, ContractValidator, } from "./generated/validators.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { contractManifest, contractVersion, } from "./generated/artifact-index.js";
|
|
2
|
+
export { createMachineCredentialMaterial, verifyMachineCredentialToken, } from "./machine-credential.js";
|
|
3
|
+
export { DEFAULT_WEBHOOK_TIMESTAMP_TOLERANCE_SECONDS, MAX_WEBHOOK_SECRETS, MAX_WEBHOOK_SIGNATURES, verifyWebhookSignature, } from "./webhook-signature.js";
|
|
4
|
+
export { consumeVerifiedWebhook, MIN_WEBHOOK_DEDUP_RETENTION_SECONDS, } from "./webhook-consumer.js";
|
|
5
|
+
export { classifyWebhookDeliveryOutcome, MAX_WEBHOOK_DELIVERY_ATTEMPTS, MAX_WEBHOOK_DELIVERY_WINDOW_SECONDS, parseWebhookRetryAfter, WEBHOOK_FETCH_REDIRECT_MODE, } from "./webhook-delivery.js";
|
|
6
|
+
export { contractValidationErrors, validateAccountProjection, validateApiCredential, validateApiCredentialCollection, validateApiCredentialRotation, validateApiCredentialSecretReveal, validateBindingSummary, validateCreateApiCredentialRequest, validateCreateCustomerEndpointRequest, validateCreateProjectRequest, validateCreateMachineClientRequest, validateCreateMessageRequest, validateCreateSubscriptionLinkRequest, validateDeliveryAttemptSummary, validateCustomerEndpoint, validateCustomerEndpointAttemptCollection, validateCustomerEndpointCollection, validateCustomerEndpointMutationResult, validateCustomerEndpointVerificationChallenge, validateIdempotencyKey, validateAgentBindingDegradedEvent, validateInteractionEvent, validateInteractionExpiredEvent, validateInteractionReceivedEvent, validateInteractionRequest, validateInteractionSummary, validateMachineClient, validateMachineCredential, validateMachineCredentialBearer, validateMachineEventAckRequest, validateMachineEventAckResponse, validateMachineEventPollResponse, validateMessage, validateMessageDeliveredEvent, validateMessageExpiredEvent, validateMessageFailedEvent, validateMessageProviderAcceptedEvent, validateWhooshBangEvent, validateProblem, validateProject, validateProjectCollection, validateProviderCallbackToken, validateProviderCapabilities, validateProviderCapabilitiesCollection, validateRegisterMachineCredentialRequest, validateRotateApiCredentialRequest, validateRotateCustomerEndpointRequest, validateSubscriptionActivatedEvent, validateSubscriptionLink, validateSubscriptionRevokedEvent, validateWebhookDeliveryCaseSet, validateWebhookSigningVectorSet, } from "./generated/validators.js";
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,eAAe,GAChB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,+BAA+B,EAC/B,4BAA4B,GAC7B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,2CAA2C,EAC3C,mBAAmB,EACnB,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,sBAAsB,EACtB,mCAAmC,GACpC,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,8BAA8B,EAC9B,6BAA6B,EAC7B,mCAAmC,EACnC,sBAAsB,EACtB,2BAA2B,GAC5B,MAAM,uBAAuB,CAAC;AAyC/B,OAAO,EACL,wBAAwB,EACxB,yBAAyB,EACzB,qBAAqB,EACrB,+BAA+B,EAC/B,6BAA6B,EAC7B,iCAAiC,EACjC,sBAAsB,EACtB,kCAAkC,EAClC,qCAAqC,EACrC,4BAA4B,EAC5B,kCAAkC,EAClC,4BAA4B,EAC5B,qCAAqC,EACrC,8BAA8B,EAC9B,wBAAwB,EACxB,yCAAyC,EACzC,kCAAkC,EAClC,sCAAsC,EACtC,6CAA6C,EAC7C,sBAAsB,EACtB,iCAAiC,EACjC,wBAAwB,EACxB,+BAA+B,EAC/B,gCAAgC,EAChC,0BAA0B,EAC1B,0BAA0B,EAC1B,qBAAqB,EACrB,yBAAyB,EACzB,+BAA+B,EAC/B,8BAA8B,EAC9B,+BAA+B,EAC/B,gCAAgC,EAChC,eAAe,EACf,6BAA6B,EAC7B,2BAA2B,EAC3B,0BAA0B,EAC1B,oCAAoC,EACpC,uBAAuB,EACvB,eAAe,EACf,eAAe,EACf,yBAAyB,EACzB,6BAA6B,EAC7B,4BAA4B,EAC5B,sCAAsC,EACtC,wCAAwC,EACxC,kCAAkC,EAClC,qCAAqC,EACrC,kCAAkC,EAClC,wBAAwB,EACxB,gCAAgC,EAChC,8BAA8B,EAC9B,+BAA+B,GAChC,MAAM,2BAA2B,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface MachineCredentialCrypto {
|
|
2
|
+
getRandomValues(array: Uint8Array): Uint8Array;
|
|
3
|
+
readonly subtle: Pick<SubtleCrypto, "digest">;
|
|
4
|
+
}
|
|
5
|
+
export interface MachineCredentialRegistration {
|
|
6
|
+
readonly credential_id: string;
|
|
7
|
+
readonly secret_sha256: `sha256:${string}`;
|
|
8
|
+
}
|
|
9
|
+
export interface MachineCredentialMaterial {
|
|
10
|
+
readonly credentialId: string;
|
|
11
|
+
readonly bearerToken: string;
|
|
12
|
+
readonly registration: MachineCredentialRegistration;
|
|
13
|
+
}
|
|
14
|
+
export interface MachineCredentialVerificationRecord {
|
|
15
|
+
readonly credentialId: string;
|
|
16
|
+
readonly secretSha256: `sha256:${string}`;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Generates runtime credential material entirely on the caller's machine.
|
|
20
|
+
*
|
|
21
|
+
* Only `registration` is safe to send to the administration endpoint.
|
|
22
|
+
* `bearerToken` contains the one-time readable secret and must remain local.
|
|
23
|
+
*/
|
|
24
|
+
export declare function createMachineCredentialMaterial(crypto?: MachineCredentialCrypto): Promise<MachineCredentialMaterial>;
|
|
25
|
+
/**
|
|
26
|
+
* Authentication-bound verification for the exact C0 machine bearer syntax.
|
|
27
|
+
*
|
|
28
|
+
* The readable token is parsed only here; callers receive a boolean and cannot
|
|
29
|
+
* derive machine, tenant, subscriber, or notifier identity from it.
|
|
30
|
+
*/
|
|
31
|
+
export declare function verifyMachineCredentialToken(token: string, expected: MachineCredentialVerificationRecord, crypto?: Pick<MachineCredentialCrypto, "subtle">): Promise<boolean>;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { decodeBase64Url, encodeBase64Url } from "./base64url.js";
|
|
2
|
+
const machineCredentialToken = /^wb_mc1\.(mcred_[A-Za-z0-9_-]{21}[AQgw])\.([A-Za-z0-9_-]{42}[AEIMQUYcgkosw048])$/u;
|
|
3
|
+
const sha256Digest = /^sha256:([a-f0-9]{64})$/u;
|
|
4
|
+
const runtimeGlobal = globalThis;
|
|
5
|
+
function randomBytes(crypto, length) {
|
|
6
|
+
return crypto.getRandomValues(new Uint8Array(length));
|
|
7
|
+
}
|
|
8
|
+
function hex(bytes) {
|
|
9
|
+
return [...bytes].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
10
|
+
}
|
|
11
|
+
function decodeHex(value) {
|
|
12
|
+
return Uint8Array.from(Array.from({ length: value.length / 2 }, (_, index) => Number.parseInt(value.slice(index * 2, index * 2 + 2), 16)));
|
|
13
|
+
}
|
|
14
|
+
function constantTimeEqual(left, right) {
|
|
15
|
+
let mismatch = left.byteLength ^ right.byteLength;
|
|
16
|
+
const length = Math.max(left.byteLength, right.byteLength);
|
|
17
|
+
for (let index = 0; index < length; index += 1) {
|
|
18
|
+
mismatch |=
|
|
19
|
+
(left[index % left.byteLength] ?? 0) ^
|
|
20
|
+
(right[index % right.byteLength] ?? 0);
|
|
21
|
+
}
|
|
22
|
+
return mismatch === 0;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Generates runtime credential material entirely on the caller's machine.
|
|
26
|
+
*
|
|
27
|
+
* Only `registration` is safe to send to the administration endpoint.
|
|
28
|
+
* `bearerToken` contains the one-time readable secret and must remain local.
|
|
29
|
+
*/
|
|
30
|
+
export async function createMachineCredentialMaterial(crypto) {
|
|
31
|
+
const runtimeCrypto = crypto ?? runtimeGlobal.crypto;
|
|
32
|
+
if (runtimeCrypto === undefined) {
|
|
33
|
+
throw new Error("Web Crypto is required to generate a machine credential");
|
|
34
|
+
}
|
|
35
|
+
const credentialId = `mcred_${encodeBase64Url(randomBytes(runtimeCrypto, 16))}`;
|
|
36
|
+
const secret = randomBytes(runtimeCrypto, 32);
|
|
37
|
+
const encodedSecret = encodeBase64Url(secret);
|
|
38
|
+
const digestBuffer = await runtimeCrypto.subtle.digest("SHA-256", Uint8Array.from(secret));
|
|
39
|
+
const secretDigest = `sha256:${hex(new Uint8Array(digestBuffer))}`;
|
|
40
|
+
return {
|
|
41
|
+
bearerToken: `wb_mc1.${credentialId}.${encodedSecret}`,
|
|
42
|
+
credentialId,
|
|
43
|
+
registration: {
|
|
44
|
+
credential_id: credentialId,
|
|
45
|
+
secret_sha256: secretDigest,
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Authentication-bound verification for the exact C0 machine bearer syntax.
|
|
51
|
+
*
|
|
52
|
+
* The readable token is parsed only here; callers receive a boolean and cannot
|
|
53
|
+
* derive machine, tenant, subscriber, or notifier identity from it.
|
|
54
|
+
*/
|
|
55
|
+
export async function verifyMachineCredentialToken(token, expected, crypto) {
|
|
56
|
+
const runtimeCrypto = crypto ?? runtimeGlobal.crypto;
|
|
57
|
+
if (runtimeCrypto === undefined) {
|
|
58
|
+
throw new Error("Web Crypto is required to verify a machine credential");
|
|
59
|
+
}
|
|
60
|
+
const tokenMatch = machineCredentialToken.exec(token);
|
|
61
|
+
const digestMatch = sha256Digest.exec(expected.secretSha256);
|
|
62
|
+
const credentialId = tokenMatch?.[1];
|
|
63
|
+
const encodedSecret = tokenMatch?.[2];
|
|
64
|
+
const expectedDigest = digestMatch?.[1];
|
|
65
|
+
if (credentialId === undefined ||
|
|
66
|
+
encodedSecret === undefined ||
|
|
67
|
+
expectedDigest === undefined ||
|
|
68
|
+
credentialId !== expected.credentialId) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
const secret = decodeBase64Url(encodedSecret);
|
|
72
|
+
if (secret === undefined || secret.byteLength !== 32) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
const candidateDigest = new Uint8Array(await runtimeCrypto.subtle.digest("SHA-256", Uint8Array.from(secret)));
|
|
76
|
+
return constantTimeEqual(candidateDigest, decodeHex(expectedDigest));
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=machine-credential.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"machine-credential.js","sourceRoot":"","sources":["../src/machine-credential.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { WhooshBangEventV1ContractDocument } from "./generated/whooshbang-event-v1.js";
|
|
2
|
+
import type { WebhookCrypto, WebhookHeaders } from "./webhook-signature.js";
|
|
3
|
+
export declare const MIN_WEBHOOK_DEDUP_RETENTION_SECONDS: number;
|
|
4
|
+
export interface WebhookDeduplicationInput {
|
|
5
|
+
readonly eventId: string;
|
|
6
|
+
readonly projectId: string;
|
|
7
|
+
readonly retainUntilEpochSeconds: number;
|
|
8
|
+
}
|
|
9
|
+
export type WebhookDeduplicationOutcome = "processed" | "duplicate";
|
|
10
|
+
/**
|
|
11
|
+
* A durable consumer supplies this boundary.
|
|
12
|
+
*
|
|
13
|
+
* Implementations must atomically check/claim `eventId` before invoking the
|
|
14
|
+
* handler, commit the claim with the handler's business transaction, and keep
|
|
15
|
+
* the committed claim through `retainUntilEpochSeconds`. A failed handler must
|
|
16
|
+
* not leave a committed claim that suppresses a later delivery.
|
|
17
|
+
*/
|
|
18
|
+
export interface WebhookDeduplicationStore {
|
|
19
|
+
executeOnce(input: WebhookDeduplicationInput, handler: () => Promise<void>): Promise<WebhookDeduplicationOutcome>;
|
|
20
|
+
}
|
|
21
|
+
export interface ConsumeVerifiedWebhookOptions {
|
|
22
|
+
readonly crypto?: WebhookCrypto;
|
|
23
|
+
readonly deduplication: WebhookDeduplicationStore;
|
|
24
|
+
readonly expectedProjectId: string;
|
|
25
|
+
readonly handle: (event: WhooshBangEventV1ContractDocument) => Promise<void>;
|
|
26
|
+
readonly headers: WebhookHeaders;
|
|
27
|
+
readonly nowSeconds?: number;
|
|
28
|
+
readonly rawBody: Uint8Array;
|
|
29
|
+
readonly secrets: readonly string[];
|
|
30
|
+
readonly toleranceSeconds?: number;
|
|
31
|
+
}
|
|
32
|
+
export type WebhookConsumerRejectionReason = "event_id_mismatch" | "invalid_event" | "invalid_json" | "processing_failed" | "project_mismatch" | "signature_rejected";
|
|
33
|
+
export interface RejectedWebhookConsumption {
|
|
34
|
+
readonly reason: WebhookConsumerRejectionReason;
|
|
35
|
+
readonly status: "rejected";
|
|
36
|
+
}
|
|
37
|
+
export interface ProcessedWebhookConsumption {
|
|
38
|
+
readonly eventId: string;
|
|
39
|
+
readonly status: "processed";
|
|
40
|
+
}
|
|
41
|
+
export interface DuplicateWebhookConsumption {
|
|
42
|
+
readonly eventId: string;
|
|
43
|
+
readonly status: "duplicate";
|
|
44
|
+
}
|
|
45
|
+
export type WebhookConsumptionResult = RejectedWebhookConsumption | ProcessedWebhookConsumption | DuplicateWebhookConsumption;
|
|
46
|
+
/**
|
|
47
|
+
* Minimal receiver flow: verify raw bytes, validate the event and tenant, then
|
|
48
|
+
* hand business processing to a caller-owned durable deduplication boundary.
|
|
49
|
+
*/
|
|
50
|
+
export declare function consumeVerifiedWebhook(options: ConsumeVerifiedWebhookOptions): Promise<WebhookConsumptionResult>;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { validateWhooshBangEvent } from "./generated/validators.js";
|
|
2
|
+
import { verifyWebhookSignature } from "./webhook-signature.js";
|
|
3
|
+
export const MIN_WEBHOOK_DEDUP_RETENTION_SECONDS = 7 * 24 * 60 * 60;
|
|
4
|
+
function rejected(reason) {
|
|
5
|
+
return { reason, status: "rejected" };
|
|
6
|
+
}
|
|
7
|
+
function decodeEvent(rawBody) {
|
|
8
|
+
try {
|
|
9
|
+
const text = new TextDecoder("utf-8", { fatal: true }).decode(rawBody);
|
|
10
|
+
return JSON.parse(text);
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Minimal receiver flow: verify raw bytes, validate the event and tenant, then
|
|
18
|
+
* hand business processing to a caller-owned durable deduplication boundary.
|
|
19
|
+
*/
|
|
20
|
+
export async function consumeVerifiedWebhook(options) {
|
|
21
|
+
const nowSeconds = options.nowSeconds === undefined
|
|
22
|
+
? Math.floor(Date.now() / 1000)
|
|
23
|
+
: options.nowSeconds;
|
|
24
|
+
const verificationOptions = {
|
|
25
|
+
headers: options.headers,
|
|
26
|
+
nowSeconds,
|
|
27
|
+
rawBody: options.rawBody,
|
|
28
|
+
secrets: options.secrets,
|
|
29
|
+
...(options.crypto === undefined ? {} : { crypto: options.crypto }),
|
|
30
|
+
...(options.toleranceSeconds === undefined
|
|
31
|
+
? {}
|
|
32
|
+
: { toleranceSeconds: options.toleranceSeconds }),
|
|
33
|
+
};
|
|
34
|
+
const verification = await verifyWebhookSignature(verificationOptions);
|
|
35
|
+
if (!verification.verified) {
|
|
36
|
+
return rejected("signature_rejected");
|
|
37
|
+
}
|
|
38
|
+
const candidate = decodeEvent(options.rawBody);
|
|
39
|
+
if (candidate === undefined) {
|
|
40
|
+
return rejected("invalid_json");
|
|
41
|
+
}
|
|
42
|
+
if (!validateWhooshBangEvent(candidate)) {
|
|
43
|
+
return rejected("invalid_event");
|
|
44
|
+
}
|
|
45
|
+
if (candidate.id !== verification.eventId) {
|
|
46
|
+
return rejected("event_id_mismatch");
|
|
47
|
+
}
|
|
48
|
+
if (candidate.project_id !== options.expectedProjectId) {
|
|
49
|
+
return rejected("project_mismatch");
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
const outcome = await options.deduplication.executeOnce({
|
|
53
|
+
eventId: candidate.id,
|
|
54
|
+
projectId: candidate.project_id,
|
|
55
|
+
retainUntilEpochSeconds: nowSeconds + MIN_WEBHOOK_DEDUP_RETENTION_SECONDS,
|
|
56
|
+
}, async () => {
|
|
57
|
+
await options.handle(candidate);
|
|
58
|
+
});
|
|
59
|
+
if (outcome === "duplicate") {
|
|
60
|
+
return { eventId: candidate.id, status: "duplicate" };
|
|
61
|
+
}
|
|
62
|
+
if (outcome === "processed") {
|
|
63
|
+
return { eventId: candidate.id, status: "processed" };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return rejected("processing_failed");
|
|
68
|
+
}
|
|
69
|
+
return rejected("processing_failed");
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=webhook-consumer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook-consumer.js","sourceRoot":"","sources":["../src/webhook-consumer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export declare const MAX_WEBHOOK_DELIVERY_ATTEMPTS = 8;
|
|
2
|
+
export declare const MAX_WEBHOOK_DELIVERY_WINDOW_SECONDS: number;
|
|
3
|
+
export declare const WEBHOOK_FETCH_REDIRECT_MODE: "manual";
|
|
4
|
+
export interface NetworkFailureOutcome {
|
|
5
|
+
readonly kind: "network_failure";
|
|
6
|
+
}
|
|
7
|
+
export interface HttpDeliveryOutcome {
|
|
8
|
+
readonly kind: "http";
|
|
9
|
+
readonly retryAfter?: string;
|
|
10
|
+
readonly status: number;
|
|
11
|
+
}
|
|
12
|
+
export type WebhookDeliveryOutcome = NetworkFailureOutcome | HttpDeliveryOutcome;
|
|
13
|
+
export interface ClassifyWebhookDeliveryOptions {
|
|
14
|
+
readonly attempt: number;
|
|
15
|
+
readonly firstAttemptAtEpochSeconds: number;
|
|
16
|
+
readonly nowEpochSeconds: number;
|
|
17
|
+
readonly outcome: WebhookDeliveryOutcome;
|
|
18
|
+
}
|
|
19
|
+
export type WebhookDeliveryTerminalReason = "attempt_limit_exhausted" | "delivery_window_exhausted" | "http_terminal" | "invalid_input" | "redirect_not_followed" | "unexpected_http_status";
|
|
20
|
+
export type WebhookDeliveryRetryReason = "network_failure" | "retryable_http_status";
|
|
21
|
+
export interface AcceptedWebhookDelivery {
|
|
22
|
+
readonly action: "accepted";
|
|
23
|
+
readonly reason: "http_2xx";
|
|
24
|
+
}
|
|
25
|
+
export interface TerminalWebhookDelivery {
|
|
26
|
+
readonly action: "terminal";
|
|
27
|
+
readonly reason: WebhookDeliveryTerminalReason;
|
|
28
|
+
}
|
|
29
|
+
export interface RetryWebhookDelivery {
|
|
30
|
+
readonly action: "retry";
|
|
31
|
+
readonly latestRetryAtEpochSeconds: number;
|
|
32
|
+
readonly reason: WebhookDeliveryRetryReason;
|
|
33
|
+
readonly retryAfterSeconds?: number;
|
|
34
|
+
}
|
|
35
|
+
export type WebhookDeliveryClassification = AcceptedWebhookDelivery | TerminalWebhookDelivery | RetryWebhookDelivery;
|
|
36
|
+
/**
|
|
37
|
+
* Parses only bounded canonical delta-seconds. HTTP-date values, signs,
|
|
38
|
+
* whitespace, and values longer than the delivery window are ignored.
|
|
39
|
+
*/
|
|
40
|
+
export declare function parseWebhookRetryAfter(value: string | undefined): number | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* Classifies one completed attempt. It does not perform network I/O or choose
|
|
43
|
+
* jitter; callers must use `redirect: "manual"` and schedule no later than the
|
|
44
|
+
* returned delivery-window deadline.
|
|
45
|
+
*/
|
|
46
|
+
export declare function classifyWebhookDeliveryOutcome(options: ClassifyWebhookDeliveryOptions): WebhookDeliveryClassification;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
export const MAX_WEBHOOK_DELIVERY_ATTEMPTS = 8;
|
|
2
|
+
export const MAX_WEBHOOK_DELIVERY_WINDOW_SECONDS = 24 * 60 * 60;
|
|
3
|
+
export const WEBHOOK_FETCH_REDIRECT_MODE = "manual";
|
|
4
|
+
const retryAfterDelta = /^(?:0|[1-9][0-9]{0,4})$/u;
|
|
5
|
+
/**
|
|
6
|
+
* Parses only bounded canonical delta-seconds. HTTP-date values, signs,
|
|
7
|
+
* whitespace, and values longer than the delivery window are ignored.
|
|
8
|
+
*/
|
|
9
|
+
export function parseWebhookRetryAfter(value) {
|
|
10
|
+
if (typeof value !== "string" || !retryAfterDelta.test(value)) {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
const seconds = Number(value);
|
|
14
|
+
return seconds <= MAX_WEBHOOK_DELIVERY_WINDOW_SECONDS ? seconds : undefined;
|
|
15
|
+
}
|
|
16
|
+
function terminal(reason) {
|
|
17
|
+
return { action: "terminal", reason };
|
|
18
|
+
}
|
|
19
|
+
function isSafeInteger(value) {
|
|
20
|
+
return Number.isSafeInteger(value);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Classifies one completed attempt. It does not perform network I/O or choose
|
|
24
|
+
* jitter; callers must use `redirect: "manual"` and schedule no later than the
|
|
25
|
+
* returned delivery-window deadline.
|
|
26
|
+
*/
|
|
27
|
+
export function classifyWebhookDeliveryOutcome(options) {
|
|
28
|
+
const runtimeOptions = options;
|
|
29
|
+
if (typeof runtimeOptions !== "object" ||
|
|
30
|
+
runtimeOptions === null ||
|
|
31
|
+
Array.isArray(runtimeOptions)) {
|
|
32
|
+
return terminal("invalid_input");
|
|
33
|
+
}
|
|
34
|
+
const input = runtimeOptions;
|
|
35
|
+
const attempt = input.attempt;
|
|
36
|
+
const firstAttemptAtEpochSeconds = input.firstAttemptAtEpochSeconds;
|
|
37
|
+
const nowEpochSeconds = input.nowEpochSeconds;
|
|
38
|
+
if (typeof attempt !== "number" ||
|
|
39
|
+
!isSafeInteger(attempt) ||
|
|
40
|
+
attempt < 1 ||
|
|
41
|
+
attempt > MAX_WEBHOOK_DELIVERY_ATTEMPTS ||
|
|
42
|
+
typeof firstAttemptAtEpochSeconds !== "number" ||
|
|
43
|
+
!isSafeInteger(firstAttemptAtEpochSeconds) ||
|
|
44
|
+
firstAttemptAtEpochSeconds < 0 ||
|
|
45
|
+
typeof nowEpochSeconds !== "number" ||
|
|
46
|
+
!isSafeInteger(nowEpochSeconds) ||
|
|
47
|
+
nowEpochSeconds < firstAttemptAtEpochSeconds) {
|
|
48
|
+
return terminal("invalid_input");
|
|
49
|
+
}
|
|
50
|
+
let retryReason;
|
|
51
|
+
let retryAfter;
|
|
52
|
+
const runtimeOutcome = input.outcome;
|
|
53
|
+
if (typeof runtimeOutcome !== "object" ||
|
|
54
|
+
runtimeOutcome === null ||
|
|
55
|
+
Array.isArray(runtimeOutcome)) {
|
|
56
|
+
return terminal("invalid_input");
|
|
57
|
+
}
|
|
58
|
+
const outcome = runtimeOutcome;
|
|
59
|
+
if (outcome.kind === "network_failure") {
|
|
60
|
+
if (Object.keys(outcome).some((key) => key !== "kind")) {
|
|
61
|
+
return terminal("invalid_input");
|
|
62
|
+
}
|
|
63
|
+
retryReason = "network_failure";
|
|
64
|
+
}
|
|
65
|
+
else if (outcome.kind === "http") {
|
|
66
|
+
const retryAfterHeader = outcome.retryAfter;
|
|
67
|
+
if (Object.keys(outcome).some((key) => !["kind", "status", "retryAfter"].includes(key)) ||
|
|
68
|
+
(retryAfterHeader !== undefined && typeof retryAfterHeader !== "string")) {
|
|
69
|
+
return terminal("invalid_input");
|
|
70
|
+
}
|
|
71
|
+
const status = outcome.status;
|
|
72
|
+
if (typeof status !== "number" ||
|
|
73
|
+
!isSafeInteger(status) ||
|
|
74
|
+
status < 100 ||
|
|
75
|
+
status > 599) {
|
|
76
|
+
return terminal("invalid_input");
|
|
77
|
+
}
|
|
78
|
+
if (status >= 200 && status <= 299) {
|
|
79
|
+
return { action: "accepted", reason: "http_2xx" };
|
|
80
|
+
}
|
|
81
|
+
if (status >= 300 && status <= 399) {
|
|
82
|
+
return terminal("redirect_not_followed");
|
|
83
|
+
}
|
|
84
|
+
if (status === 408 || status === 425 || status === 429 || status >= 500) {
|
|
85
|
+
retryReason = "retryable_http_status";
|
|
86
|
+
if (status === 429 || status === 503) {
|
|
87
|
+
retryAfter = parseWebhookRetryAfter(retryAfterHeader);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
else if (status >= 400) {
|
|
91
|
+
return terminal("http_terminal");
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
return terminal("unexpected_http_status");
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
return terminal("invalid_input");
|
|
99
|
+
}
|
|
100
|
+
if (attempt >= MAX_WEBHOOK_DELIVERY_ATTEMPTS) {
|
|
101
|
+
return terminal("attempt_limit_exhausted");
|
|
102
|
+
}
|
|
103
|
+
const latestRetryAtEpochSeconds = firstAttemptAtEpochSeconds + MAX_WEBHOOK_DELIVERY_WINDOW_SECONDS;
|
|
104
|
+
if (!Number.isSafeInteger(latestRetryAtEpochSeconds)) {
|
|
105
|
+
return terminal("invalid_input");
|
|
106
|
+
}
|
|
107
|
+
if (nowEpochSeconds >= latestRetryAtEpochSeconds) {
|
|
108
|
+
return terminal("delivery_window_exhausted");
|
|
109
|
+
}
|
|
110
|
+
const retryAfterEpochSeconds = retryAfter === undefined ? undefined : nowEpochSeconds + retryAfter;
|
|
111
|
+
const safeRetryAfter = retryAfter !== undefined &&
|
|
112
|
+
retryAfterEpochSeconds !== undefined &&
|
|
113
|
+
Number.isSafeInteger(retryAfterEpochSeconds) &&
|
|
114
|
+
retryAfterEpochSeconds <= latestRetryAtEpochSeconds
|
|
115
|
+
? retryAfter
|
|
116
|
+
: undefined;
|
|
117
|
+
return {
|
|
118
|
+
action: "retry",
|
|
119
|
+
latestRetryAtEpochSeconds,
|
|
120
|
+
reason: retryReason,
|
|
121
|
+
...(safeRetryAfter === undefined
|
|
122
|
+
? {}
|
|
123
|
+
: { retryAfterSeconds: safeRetryAfter }),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=webhook-delivery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook-delivery.js","sourceRoot":"","sources":["../src/webhook-delivery.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export declare const DEFAULT_WEBHOOK_TIMESTAMP_TOLERANCE_SECONDS = 300;
|
|
2
|
+
export declare const MAX_WEBHOOK_SIGNATURES = 2;
|
|
3
|
+
export declare const MAX_WEBHOOK_SECRETS = 2;
|
|
4
|
+
export interface WebhookCrypto {
|
|
5
|
+
readonly subtle: Pick<SubtleCrypto, "importKey" | "sign">;
|
|
6
|
+
}
|
|
7
|
+
export type WebhookHeaderEntry = readonly [name: string, value: string];
|
|
8
|
+
export type WebhookHeaderRecord = Readonly<Record<string, string | readonly string[] | undefined>>;
|
|
9
|
+
export type WebhookHeaders = readonly WebhookHeaderEntry[] | WebhookHeaderRecord;
|
|
10
|
+
export type WebhookVerificationFailureReason = "crypto_unavailable" | "duplicate_header" | "duplicate_secret" | "duplicate_signature" | "invalid_body" | "invalid_clock" | "invalid_secret" | "invalid_tolerance" | "malformed_attempt" | "malformed_header" | "malformed_signature" | "malformed_timestamp" | "malformed_webhook_id" | "missing_header" | "signature_mismatch" | "timestamp_outside_tolerance" | "too_many_secrets" | "too_many_signatures" | "unsupported_signature_version";
|
|
11
|
+
export interface VerifiedWebhookSignature {
|
|
12
|
+
readonly attempt: number;
|
|
13
|
+
readonly eventId: string;
|
|
14
|
+
readonly timestamp: number;
|
|
15
|
+
readonly verified: true;
|
|
16
|
+
}
|
|
17
|
+
export interface RejectedWebhookSignature {
|
|
18
|
+
readonly reason: WebhookVerificationFailureReason;
|
|
19
|
+
readonly verified: false;
|
|
20
|
+
}
|
|
21
|
+
export type WebhookSignatureVerification = VerifiedWebhookSignature | RejectedWebhookSignature;
|
|
22
|
+
export interface VerifyWebhookSignatureOptions {
|
|
23
|
+
readonly crypto?: WebhookCrypto;
|
|
24
|
+
readonly headers: WebhookHeaders;
|
|
25
|
+
readonly nowSeconds?: number;
|
|
26
|
+
readonly rawBody: Uint8Array;
|
|
27
|
+
readonly secrets: readonly string[];
|
|
28
|
+
readonly toleranceSeconds?: number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Verifies the Standard Webhooks V1 signature over the exact request bytes.
|
|
32
|
+
*
|
|
33
|
+
* Header entries are accepted as tuples so callers can preserve and reject
|
|
34
|
+
* duplicate security headers before framework normalization.
|
|
35
|
+
*/
|
|
36
|
+
export declare function verifyWebhookSignature(options: VerifyWebhookSignatureOptions): Promise<WebhookSignatureVerification>;
|