@webhooks-cc/sdk 1.2.1 → 1.3.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.
@@ -18,6 +18,10 @@ interface Endpoint {
18
18
  name?: string;
19
19
  /** Full URL where webhooks should be sent (undefined if server is misconfigured) */
20
20
  url?: string;
21
+ /** URL to POST a JSON summary to after each captured request (e.g. Slack/Discord webhook) */
22
+ notificationUrl?: string;
23
+ /** Ordered conditional response rules (first match wins, then falls back to default mock) */
24
+ responseRules?: ResponseRule[];
21
25
  /** Whether the endpoint auto-expires and may be cleaned up automatically */
22
26
  isEphemeral?: boolean;
23
27
  /** Unix timestamp (ms) when the endpoint expires, if ephemeral */
@@ -29,6 +33,34 @@ interface Endpoint {
29
33
  /** Team this endpoint was shared from (present when shared with you) */
30
34
  fromTeam?: TeamShare;
31
35
  }
36
+ /** A single condition within a response rule. */
37
+ interface ResponseRuleCondition {
38
+ /** Field to match: method, path, header, body_contains, body_path, query */
39
+ field: "method" | "path" | "header" | "body_contains" | "body_path" | "query";
40
+ /** Comparison operator */
41
+ op: "eq" | "contains" | "starts_with" | "matches" | "exists";
42
+ /** Value to compare against (not required for "exists") */
43
+ value?: string;
44
+ /** Header name or query param name (required for header/query conditions) */
45
+ name?: string;
46
+ /** JSON dot-notation path (required for body_path conditions) */
47
+ path?: string;
48
+ }
49
+ /** A conditional response rule: when conditions match, return this response. */
50
+ interface ResponseRule {
51
+ /** Stable identifier for UI drag-reorder */
52
+ id?: string;
53
+ /** Human-readable rule name */
54
+ name?: string;
55
+ /** Whether this rule is active (default: true) */
56
+ enabled?: boolean;
57
+ /** How to combine conditions: "and" (all match) or "or" (any match). Default: "and" */
58
+ logic?: "and" | "or";
59
+ /** Conditions to evaluate against the incoming request */
60
+ conditions: ResponseRuleCondition[];
61
+ /** Response to return when conditions match */
62
+ response: MockResponse;
63
+ }
32
64
  /** Mock response returned by the receiver instead of the default 200 OK. */
33
65
  interface MockResponse {
34
66
  /** HTTP status code (100-599) */
@@ -57,6 +89,8 @@ interface Request {
57
89
  headers: Record<string, string>;
58
90
  /** Request body, if present */
59
91
  body?: string;
92
+ /** Base64-encoded raw bytes, present only for non-UTF-8 payloads */
93
+ bodyRaw?: string;
60
94
  /** URL query parameters */
61
95
  queryParams: Record<string, string>;
62
96
  /** Content-Type header value, if present */
@@ -121,6 +155,10 @@ interface CreateEndpointOptions {
121
155
  expiresIn?: number | string;
122
156
  /** Optional mock response to configure at creation time */
123
157
  mockResponse?: MockResponse;
158
+ /** URL to POST a JSON summary to after each captured request */
159
+ notificationUrl?: string;
160
+ /** Ordered conditional response rules */
161
+ responseRules?: ResponseRule[];
124
162
  }
125
163
  /**
126
164
  * Options for updating an existing endpoint.
@@ -130,6 +168,10 @@ interface UpdateEndpointOptions {
130
168
  name?: string;
131
169
  /** Mock response config, or null to clear */
132
170
  mockResponse?: MockResponse | null;
171
+ /** Notification webhook URL, or null to clear */
172
+ notificationUrl?: string | null;
173
+ /** Ordered conditional response rules, or null to clear */
174
+ responseRules?: ResponseRule[] | null;
133
175
  }
134
176
  /**
135
177
  * Options for sending a test webhook to an endpoint.
@@ -726,4 +768,4 @@ type ComparableRequest = Pick<Request, "method" | "path" | "headers" | "body"> &
726
768
  */
727
769
  declare function diffRequests(left: ComparableRequest | SearchResult, right: ComparableRequest | SearchResult, options?: DiffRequestsOptions): DiffResult;
728
770
 
729
- export { WebhookFlowBuilder as $, ApiError as A, type BodyDiff as B, type ClearRequestsOptions as C, type DiffRequestsOptions as D, type Endpoint as E, type FormBodyValue as F, type TemplateProviderInfo as G, type HarExport as H, type TextBodyDiff as I, type JsonBodyDiff as J, TimeoutError as K, type ListPaginatedRequestsOptions as L, type MockResponse as M, NotFoundError as N, type OperationDescription as O, type ParsedBody as P, type UpdateEndpointOptions as Q, type Request as R, type SearchResult as S, type TeamShare as T, UnauthorizedError as U, type VerifySignatureOptions as V, type UsageInfo as W, type ValueDifference as X, type VerifyProvider as Y, type WaitForAllOptions as Z, type WaitForOptions as _, type ParsedFormBody as a, type WebhookFlowResult as a0, type WebhookFlowVerifyOptions as a1, WebhooksCC as a2, WebhooksCCError as a3, diffRequests as a4, type SignatureVerificationResult as b, type ClientHooks as c, type ClientOptions as d, type CreateEndpointOptions as e, type CurlExport as f, type DiffResult as g, type ErrorHookInfo as h, type ExportRequestsOptions as i, type HeaderDiff as j, type ListRequestsOptions as k, type PaginatedResult as l, RateLimitError as m, type RateLimitMeta as n, type RequestDifferences as o, type RequestHookInfo as p, type RequestsExport as q, type ResponseHookInfo as r, type RetryOptions as s, type SDKDescription as t, type SearchFilters as u, type SendOptions as v, type SendTemplateOptions as w, type SendToOptions as x, type SubscribeOptions as y, type TemplateProvider as z };
771
+ export { type WaitForAllOptions as $, ApiError as A, type BodyDiff as B, type ClearRequestsOptions as C, type DiffRequestsOptions as D, type Endpoint as E, type FormBodyValue as F, type SubscribeOptions as G, type HarExport as H, type TemplateProvider as I, type JsonBodyDiff as J, type TemplateProviderInfo as K, type ListPaginatedRequestsOptions as L, type MockResponse as M, NotFoundError as N, type OperationDescription as O, type ParsedBody as P, type TextBodyDiff as Q, type Request as R, type SendTemplateOptions as S, type TeamShare as T, TimeoutError as U, type VerifySignatureOptions as V, UnauthorizedError as W, type UpdateEndpointOptions as X, type UsageInfo as Y, type ValueDifference as Z, type VerifyProvider as _, type ParsedFormBody as a, type WaitForOptions as a0, WebhookFlowBuilder as a1, type WebhookFlowResult as a2, type WebhookFlowVerifyOptions as a3, WebhooksCC as a4, WebhooksCCError as a5, diffRequests as a6, type SendOptions as b, type ResponseRule as c, type SearchResult as d, type SignatureVerificationResult as e, type ClientHooks as f, type ClientOptions as g, type CreateEndpointOptions as h, type CurlExport as i, type DiffResult as j, type ErrorHookInfo as k, type ExportRequestsOptions as l, type HeaderDiff as m, type ListRequestsOptions as n, type PaginatedResult as o, RateLimitError as p, type RateLimitMeta as q, type RequestDifferences as r, type RequestHookInfo as s, type RequestsExport as t, type ResponseHookInfo as u, type ResponseRuleCondition as v, type RetryOptions as w, type SDKDescription as x, type SearchFilters as y, type SendToOptions as z };
@@ -18,6 +18,10 @@ interface Endpoint {
18
18
  name?: string;
19
19
  /** Full URL where webhooks should be sent (undefined if server is misconfigured) */
20
20
  url?: string;
21
+ /** URL to POST a JSON summary to after each captured request (e.g. Slack/Discord webhook) */
22
+ notificationUrl?: string;
23
+ /** Ordered conditional response rules (first match wins, then falls back to default mock) */
24
+ responseRules?: ResponseRule[];
21
25
  /** Whether the endpoint auto-expires and may be cleaned up automatically */
22
26
  isEphemeral?: boolean;
23
27
  /** Unix timestamp (ms) when the endpoint expires, if ephemeral */
@@ -29,6 +33,34 @@ interface Endpoint {
29
33
  /** Team this endpoint was shared from (present when shared with you) */
30
34
  fromTeam?: TeamShare;
31
35
  }
36
+ /** A single condition within a response rule. */
37
+ interface ResponseRuleCondition {
38
+ /** Field to match: method, path, header, body_contains, body_path, query */
39
+ field: "method" | "path" | "header" | "body_contains" | "body_path" | "query";
40
+ /** Comparison operator */
41
+ op: "eq" | "contains" | "starts_with" | "matches" | "exists";
42
+ /** Value to compare against (not required for "exists") */
43
+ value?: string;
44
+ /** Header name or query param name (required for header/query conditions) */
45
+ name?: string;
46
+ /** JSON dot-notation path (required for body_path conditions) */
47
+ path?: string;
48
+ }
49
+ /** A conditional response rule: when conditions match, return this response. */
50
+ interface ResponseRule {
51
+ /** Stable identifier for UI drag-reorder */
52
+ id?: string;
53
+ /** Human-readable rule name */
54
+ name?: string;
55
+ /** Whether this rule is active (default: true) */
56
+ enabled?: boolean;
57
+ /** How to combine conditions: "and" (all match) or "or" (any match). Default: "and" */
58
+ logic?: "and" | "or";
59
+ /** Conditions to evaluate against the incoming request */
60
+ conditions: ResponseRuleCondition[];
61
+ /** Response to return when conditions match */
62
+ response: MockResponse;
63
+ }
32
64
  /** Mock response returned by the receiver instead of the default 200 OK. */
33
65
  interface MockResponse {
34
66
  /** HTTP status code (100-599) */
@@ -57,6 +89,8 @@ interface Request {
57
89
  headers: Record<string, string>;
58
90
  /** Request body, if present */
59
91
  body?: string;
92
+ /** Base64-encoded raw bytes, present only for non-UTF-8 payloads */
93
+ bodyRaw?: string;
60
94
  /** URL query parameters */
61
95
  queryParams: Record<string, string>;
62
96
  /** Content-Type header value, if present */
@@ -121,6 +155,10 @@ interface CreateEndpointOptions {
121
155
  expiresIn?: number | string;
122
156
  /** Optional mock response to configure at creation time */
123
157
  mockResponse?: MockResponse;
158
+ /** URL to POST a JSON summary to after each captured request */
159
+ notificationUrl?: string;
160
+ /** Ordered conditional response rules */
161
+ responseRules?: ResponseRule[];
124
162
  }
125
163
  /**
126
164
  * Options for updating an existing endpoint.
@@ -130,6 +168,10 @@ interface UpdateEndpointOptions {
130
168
  name?: string;
131
169
  /** Mock response config, or null to clear */
132
170
  mockResponse?: MockResponse | null;
171
+ /** Notification webhook URL, or null to clear */
172
+ notificationUrl?: string | null;
173
+ /** Ordered conditional response rules, or null to clear */
174
+ responseRules?: ResponseRule[] | null;
133
175
  }
134
176
  /**
135
177
  * Options for sending a test webhook to an endpoint.
@@ -726,4 +768,4 @@ type ComparableRequest = Pick<Request, "method" | "path" | "headers" | "body"> &
726
768
  */
727
769
  declare function diffRequests(left: ComparableRequest | SearchResult, right: ComparableRequest | SearchResult, options?: DiffRequestsOptions): DiffResult;
728
770
 
729
- export { WebhookFlowBuilder as $, ApiError as A, type BodyDiff as B, type ClearRequestsOptions as C, type DiffRequestsOptions as D, type Endpoint as E, type FormBodyValue as F, type TemplateProviderInfo as G, type HarExport as H, type TextBodyDiff as I, type JsonBodyDiff as J, TimeoutError as K, type ListPaginatedRequestsOptions as L, type MockResponse as M, NotFoundError as N, type OperationDescription as O, type ParsedBody as P, type UpdateEndpointOptions as Q, type Request as R, type SearchResult as S, type TeamShare as T, UnauthorizedError as U, type VerifySignatureOptions as V, type UsageInfo as W, type ValueDifference as X, type VerifyProvider as Y, type WaitForAllOptions as Z, type WaitForOptions as _, type ParsedFormBody as a, type WebhookFlowResult as a0, type WebhookFlowVerifyOptions as a1, WebhooksCC as a2, WebhooksCCError as a3, diffRequests as a4, type SignatureVerificationResult as b, type ClientHooks as c, type ClientOptions as d, type CreateEndpointOptions as e, type CurlExport as f, type DiffResult as g, type ErrorHookInfo as h, type ExportRequestsOptions as i, type HeaderDiff as j, type ListRequestsOptions as k, type PaginatedResult as l, RateLimitError as m, type RateLimitMeta as n, type RequestDifferences as o, type RequestHookInfo as p, type RequestsExport as q, type ResponseHookInfo as r, type RetryOptions as s, type SDKDescription as t, type SearchFilters as u, type SendOptions as v, type SendTemplateOptions as w, type SendToOptions as x, type SubscribeOptions as y, type TemplateProvider as z };
771
+ export { type WaitForAllOptions as $, ApiError as A, type BodyDiff as B, type ClearRequestsOptions as C, type DiffRequestsOptions as D, type Endpoint as E, type FormBodyValue as F, type SubscribeOptions as G, type HarExport as H, type TemplateProvider as I, type JsonBodyDiff as J, type TemplateProviderInfo as K, type ListPaginatedRequestsOptions as L, type MockResponse as M, NotFoundError as N, type OperationDescription as O, type ParsedBody as P, type TextBodyDiff as Q, type Request as R, type SendTemplateOptions as S, type TeamShare as T, TimeoutError as U, type VerifySignatureOptions as V, UnauthorizedError as W, type UpdateEndpointOptions as X, type UsageInfo as Y, type ValueDifference as Z, type VerifyProvider as _, type ParsedFormBody as a, type WaitForOptions as a0, WebhookFlowBuilder as a1, type WebhookFlowResult as a2, type WebhookFlowVerifyOptions as a3, WebhooksCC as a4, WebhooksCCError as a5, diffRequests as a6, type SendOptions as b, type ResponseRule as c, type SearchResult as d, type SignatureVerificationResult as e, type ClientHooks as f, type ClientOptions as g, type CreateEndpointOptions as h, type CurlExport as i, type DiffResult as j, type ErrorHookInfo as k, type ExportRequestsOptions as l, type HeaderDiff as m, type ListRequestsOptions as n, type PaginatedResult as o, RateLimitError as p, type RateLimitMeta as q, type RequestDifferences as r, type RequestHookInfo as s, type RequestsExport as t, type ResponseHookInfo as u, type ResponseRuleCondition as v, type RetryOptions as w, type SDKDescription as x, type SearchFilters as y, type SendToOptions as z };
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { R as Request, P as ParsedBody, a as ParsedFormBody, S as SearchResult, V as VerifySignatureOptions, b as SignatureVerificationResult } from './diff-kGWh0pPP.mjs';
2
- export { A as ApiError, B as BodyDiff, C as ClearRequestsOptions, c as ClientHooks, d as ClientOptions, e as CreateEndpointOptions, f as CurlExport, D as DiffRequestsOptions, g as DiffResult, E as Endpoint, h as ErrorHookInfo, i as ExportRequestsOptions, F as FormBodyValue, H as HarExport, j as HeaderDiff, J as JsonBodyDiff, L as ListPaginatedRequestsOptions, k as ListRequestsOptions, M as MockResponse, N as NotFoundError, O as OperationDescription, l as PaginatedResult, m as RateLimitError, n as RateLimitMeta, o as RequestDifferences, p as RequestHookInfo, q as RequestsExport, r as ResponseHookInfo, s as RetryOptions, t as SDKDescription, u as SearchFilters, v as SendOptions, w as SendTemplateOptions, x as SendToOptions, y as SubscribeOptions, T as TeamShare, z as TemplateProvider, G as TemplateProviderInfo, I as TextBodyDiff, K as TimeoutError, U as UnauthorizedError, Q as UpdateEndpointOptions, W as UsageInfo, X as ValueDifference, Y as VerifyProvider, Z as WaitForAllOptions, _ as WaitForOptions, $ as WebhookFlowBuilder, a0 as WebhookFlowResult, a1 as WebhookFlowVerifyOptions, a2 as WebhooksCC, a3 as WebhooksCCError, a4 as diffRequests } from './diff-kGWh0pPP.mjs';
1
+ import { R as Request, P as ParsedBody, a as ParsedFormBody, S as SendTemplateOptions, b as SendOptions, M as MockResponse, c as ResponseRule, d as SearchResult, V as VerifySignatureOptions, e as SignatureVerificationResult } from './diff-CRMlHQPX.mjs';
2
+ export { A as ApiError, B as BodyDiff, C as ClearRequestsOptions, f as ClientHooks, g as ClientOptions, h as CreateEndpointOptions, i as CurlExport, D as DiffRequestsOptions, j as DiffResult, E as Endpoint, k as ErrorHookInfo, l as ExportRequestsOptions, F as FormBodyValue, H as HarExport, m as HeaderDiff, J as JsonBodyDiff, L as ListPaginatedRequestsOptions, n as ListRequestsOptions, N as NotFoundError, O as OperationDescription, o as PaginatedResult, p as RateLimitError, q as RateLimitMeta, r as RequestDifferences, s as RequestHookInfo, t as RequestsExport, u as ResponseHookInfo, v as ResponseRuleCondition, w as RetryOptions, x as SDKDescription, y as SearchFilters, z as SendToOptions, G as SubscribeOptions, T as TeamShare, I as TemplateProvider, K as TemplateProviderInfo, Q as TextBodyDiff, U as TimeoutError, W as UnauthorizedError, X as UpdateEndpointOptions, Y as UsageInfo, Z as ValueDifference, _ as VerifyProvider, $ as WaitForAllOptions, a0 as WaitForOptions, a1 as WebhookFlowBuilder, a2 as WebhookFlowResult, a3 as WebhookFlowVerifyOptions, a4 as WebhooksCC, a5 as WebhooksCCError, a6 as diffRequests } from './diff-CRMlHQPX.mjs';
3
3
 
4
4
  /**
5
5
  * Safely parse a JSON request body.
@@ -140,6 +140,8 @@ interface SSEFrame {
140
140
  */
141
141
  declare function parseSSE(stream: ReadableStream<Uint8Array>): AsyncGenerator<SSEFrame, void, undefined>;
142
142
 
143
+ declare const TEMPLATE_PROVIDERS: readonly ["stripe", "github", "shopify", "twilio", "slack", "paddle", "linear", "sendgrid", "clerk", "discord", "vercel", "gitlab", "standard-webhooks"];
144
+ declare const VERIFY_PROVIDERS: readonly ["stripe", "github", "shopify", "twilio", "slack", "paddle", "linear", "clerk", "discord", "vercel", "gitlab", "standard-webhooks"];
143
145
  declare const TEMPLATE_METADATA: Readonly<{
144
146
  stripe: Readonly<{
145
147
  provider: "stripe";
@@ -241,6 +243,24 @@ declare const TEMPLATE_METADATA: Readonly<{
241
243
  signatureAlgorithm: "hmac-sha256";
242
244
  }>;
243
245
  }>;
246
+ /**
247
+ * Build method/headers/body for a provider template webhook.
248
+ */
249
+ declare function buildTemplateSendOptions(endpointUrl: string, options: SendTemplateOptions): Promise<SendOptions>;
250
+
251
+ declare const MOCK_RESPONSE_STATUS_MIN = 100;
252
+ declare const MOCK_RESPONSE_STATUS_MAX = 599;
253
+ declare const MOCK_RESPONSE_DELAY_MIN = 0;
254
+ declare const MOCK_RESPONSE_DELAY_MAX = 30000;
255
+ declare const MAX_RESPONSE_RULES = 50;
256
+ declare const MAX_CONDITIONS_PER_RULE = 10;
257
+ declare const MAX_CONDITION_VALUE_LEN = 4096;
258
+ declare const MAX_CONDITION_NAME_LEN = 256;
259
+ declare const MAX_CONDITION_PATH_LEN = 256;
260
+ declare const MAX_RULE_NAME_LEN = 200;
261
+ declare const MAX_GLOB_PATTERN_LEN = 500;
262
+ declare function validateMockResponse(mockResponse: MockResponse, fieldName?: string): void;
263
+ declare function validateResponseRules(rules: ResponseRule[]): void;
244
264
 
245
265
  type VerifyableRequest = Pick<Request, "body" | "headers"> | Pick<SearchResult, "body" | "headers">;
246
266
  /**
@@ -299,4 +319,4 @@ declare function verifyStandardWebhookSignature(body: string | undefined, header
299
319
  */
300
320
  declare function verifySignature(request: VerifyableRequest, options: VerifySignatureOptions): Promise<SignatureVerificationResult>;
301
321
 
302
- export { ParsedBody, ParsedFormBody, Request, type SSEFrame, SearchResult, SignatureVerificationResult, TEMPLATE_METADATA, VerifySignatureOptions, extractJsonField, isClerkWebhook, isDiscordWebhook, isGitHubWebhook, isGitLabWebhook, isLinearWebhook, isPaddleWebhook, isSendGridWebhook, isShopifyWebhook, isSlackWebhook, isStandardWebhook, isStripeWebhook, isTwilioWebhook, isVercelWebhook, matchAll, matchAny, matchBodyPath, matchBodySubset, matchContentType, matchHeader, matchJsonField, matchMethod, matchPath, matchQueryParam, parseBody, parseDuration, parseFormBody, parseJsonBody, parseSSE, verifyClerkSignature, verifyDiscordSignature, verifyGitHubSignature, verifyGitLabSignature, verifyLinearSignature, verifyPaddleSignature, verifyShopifySignature, verifySignature, verifySlackSignature, verifyStandardWebhookSignature, verifyStripeSignature, verifyTwilioSignature, verifyVercelSignature };
322
+ export { MAX_CONDITIONS_PER_RULE, MAX_CONDITION_NAME_LEN, MAX_CONDITION_PATH_LEN, MAX_CONDITION_VALUE_LEN, MAX_GLOB_PATTERN_LEN, MAX_RESPONSE_RULES, MAX_RULE_NAME_LEN, MOCK_RESPONSE_DELAY_MAX, MOCK_RESPONSE_DELAY_MIN, MOCK_RESPONSE_STATUS_MAX, MOCK_RESPONSE_STATUS_MIN, MockResponse, ParsedBody, ParsedFormBody, Request, ResponseRule, type SSEFrame, SearchResult, SendOptions, SendTemplateOptions, SignatureVerificationResult, TEMPLATE_METADATA, TEMPLATE_PROVIDERS, VERIFY_PROVIDERS, VerifySignatureOptions, buildTemplateSendOptions, extractJsonField, isClerkWebhook, isDiscordWebhook, isGitHubWebhook, isGitLabWebhook, isLinearWebhook, isPaddleWebhook, isSendGridWebhook, isShopifyWebhook, isSlackWebhook, isStandardWebhook, isStripeWebhook, isTwilioWebhook, isVercelWebhook, matchAll, matchAny, matchBodyPath, matchBodySubset, matchContentType, matchHeader, matchJsonField, matchMethod, matchPath, matchQueryParam, parseBody, parseDuration, parseFormBody, parseJsonBody, parseSSE, validateMockResponse, validateResponseRules, verifyClerkSignature, verifyDiscordSignature, verifyGitHubSignature, verifyGitLabSignature, verifyLinearSignature, verifyPaddleSignature, verifyShopifySignature, verifySignature, verifySlackSignature, verifyStandardWebhookSignature, verifyStripeSignature, verifyTwilioSignature, verifyVercelSignature };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { R as Request, P as ParsedBody, a as ParsedFormBody, S as SearchResult, V as VerifySignatureOptions, b as SignatureVerificationResult } from './diff-kGWh0pPP.js';
2
- export { A as ApiError, B as BodyDiff, C as ClearRequestsOptions, c as ClientHooks, d as ClientOptions, e as CreateEndpointOptions, f as CurlExport, D as DiffRequestsOptions, g as DiffResult, E as Endpoint, h as ErrorHookInfo, i as ExportRequestsOptions, F as FormBodyValue, H as HarExport, j as HeaderDiff, J as JsonBodyDiff, L as ListPaginatedRequestsOptions, k as ListRequestsOptions, M as MockResponse, N as NotFoundError, O as OperationDescription, l as PaginatedResult, m as RateLimitError, n as RateLimitMeta, o as RequestDifferences, p as RequestHookInfo, q as RequestsExport, r as ResponseHookInfo, s as RetryOptions, t as SDKDescription, u as SearchFilters, v as SendOptions, w as SendTemplateOptions, x as SendToOptions, y as SubscribeOptions, T as TeamShare, z as TemplateProvider, G as TemplateProviderInfo, I as TextBodyDiff, K as TimeoutError, U as UnauthorizedError, Q as UpdateEndpointOptions, W as UsageInfo, X as ValueDifference, Y as VerifyProvider, Z as WaitForAllOptions, _ as WaitForOptions, $ as WebhookFlowBuilder, a0 as WebhookFlowResult, a1 as WebhookFlowVerifyOptions, a2 as WebhooksCC, a3 as WebhooksCCError, a4 as diffRequests } from './diff-kGWh0pPP.js';
1
+ import { R as Request, P as ParsedBody, a as ParsedFormBody, S as SendTemplateOptions, b as SendOptions, M as MockResponse, c as ResponseRule, d as SearchResult, V as VerifySignatureOptions, e as SignatureVerificationResult } from './diff-CRMlHQPX.js';
2
+ export { A as ApiError, B as BodyDiff, C as ClearRequestsOptions, f as ClientHooks, g as ClientOptions, h as CreateEndpointOptions, i as CurlExport, D as DiffRequestsOptions, j as DiffResult, E as Endpoint, k as ErrorHookInfo, l as ExportRequestsOptions, F as FormBodyValue, H as HarExport, m as HeaderDiff, J as JsonBodyDiff, L as ListPaginatedRequestsOptions, n as ListRequestsOptions, N as NotFoundError, O as OperationDescription, o as PaginatedResult, p as RateLimitError, q as RateLimitMeta, r as RequestDifferences, s as RequestHookInfo, t as RequestsExport, u as ResponseHookInfo, v as ResponseRuleCondition, w as RetryOptions, x as SDKDescription, y as SearchFilters, z as SendToOptions, G as SubscribeOptions, T as TeamShare, I as TemplateProvider, K as TemplateProviderInfo, Q as TextBodyDiff, U as TimeoutError, W as UnauthorizedError, X as UpdateEndpointOptions, Y as UsageInfo, Z as ValueDifference, _ as VerifyProvider, $ as WaitForAllOptions, a0 as WaitForOptions, a1 as WebhookFlowBuilder, a2 as WebhookFlowResult, a3 as WebhookFlowVerifyOptions, a4 as WebhooksCC, a5 as WebhooksCCError, a6 as diffRequests } from './diff-CRMlHQPX.js';
3
3
 
4
4
  /**
5
5
  * Safely parse a JSON request body.
@@ -140,6 +140,8 @@ interface SSEFrame {
140
140
  */
141
141
  declare function parseSSE(stream: ReadableStream<Uint8Array>): AsyncGenerator<SSEFrame, void, undefined>;
142
142
 
143
+ declare const TEMPLATE_PROVIDERS: readonly ["stripe", "github", "shopify", "twilio", "slack", "paddle", "linear", "sendgrid", "clerk", "discord", "vercel", "gitlab", "standard-webhooks"];
144
+ declare const VERIFY_PROVIDERS: readonly ["stripe", "github", "shopify", "twilio", "slack", "paddle", "linear", "clerk", "discord", "vercel", "gitlab", "standard-webhooks"];
143
145
  declare const TEMPLATE_METADATA: Readonly<{
144
146
  stripe: Readonly<{
145
147
  provider: "stripe";
@@ -241,6 +243,24 @@ declare const TEMPLATE_METADATA: Readonly<{
241
243
  signatureAlgorithm: "hmac-sha256";
242
244
  }>;
243
245
  }>;
246
+ /**
247
+ * Build method/headers/body for a provider template webhook.
248
+ */
249
+ declare function buildTemplateSendOptions(endpointUrl: string, options: SendTemplateOptions): Promise<SendOptions>;
250
+
251
+ declare const MOCK_RESPONSE_STATUS_MIN = 100;
252
+ declare const MOCK_RESPONSE_STATUS_MAX = 599;
253
+ declare const MOCK_RESPONSE_DELAY_MIN = 0;
254
+ declare const MOCK_RESPONSE_DELAY_MAX = 30000;
255
+ declare const MAX_RESPONSE_RULES = 50;
256
+ declare const MAX_CONDITIONS_PER_RULE = 10;
257
+ declare const MAX_CONDITION_VALUE_LEN = 4096;
258
+ declare const MAX_CONDITION_NAME_LEN = 256;
259
+ declare const MAX_CONDITION_PATH_LEN = 256;
260
+ declare const MAX_RULE_NAME_LEN = 200;
261
+ declare const MAX_GLOB_PATTERN_LEN = 500;
262
+ declare function validateMockResponse(mockResponse: MockResponse, fieldName?: string): void;
263
+ declare function validateResponseRules(rules: ResponseRule[]): void;
244
264
 
245
265
  type VerifyableRequest = Pick<Request, "body" | "headers"> | Pick<SearchResult, "body" | "headers">;
246
266
  /**
@@ -299,4 +319,4 @@ declare function verifyStandardWebhookSignature(body: string | undefined, header
299
319
  */
300
320
  declare function verifySignature(request: VerifyableRequest, options: VerifySignatureOptions): Promise<SignatureVerificationResult>;
301
321
 
302
- export { ParsedBody, ParsedFormBody, Request, type SSEFrame, SearchResult, SignatureVerificationResult, TEMPLATE_METADATA, VerifySignatureOptions, extractJsonField, isClerkWebhook, isDiscordWebhook, isGitHubWebhook, isGitLabWebhook, isLinearWebhook, isPaddleWebhook, isSendGridWebhook, isShopifyWebhook, isSlackWebhook, isStandardWebhook, isStripeWebhook, isTwilioWebhook, isVercelWebhook, matchAll, matchAny, matchBodyPath, matchBodySubset, matchContentType, matchHeader, matchJsonField, matchMethod, matchPath, matchQueryParam, parseBody, parseDuration, parseFormBody, parseJsonBody, parseSSE, verifyClerkSignature, verifyDiscordSignature, verifyGitHubSignature, verifyGitLabSignature, verifyLinearSignature, verifyPaddleSignature, verifyShopifySignature, verifySignature, verifySlackSignature, verifyStandardWebhookSignature, verifyStripeSignature, verifyTwilioSignature, verifyVercelSignature };
322
+ export { MAX_CONDITIONS_PER_RULE, MAX_CONDITION_NAME_LEN, MAX_CONDITION_PATH_LEN, MAX_CONDITION_VALUE_LEN, MAX_GLOB_PATTERN_LEN, MAX_RESPONSE_RULES, MAX_RULE_NAME_LEN, MOCK_RESPONSE_DELAY_MAX, MOCK_RESPONSE_DELAY_MIN, MOCK_RESPONSE_STATUS_MAX, MOCK_RESPONSE_STATUS_MIN, MockResponse, ParsedBody, ParsedFormBody, Request, ResponseRule, type SSEFrame, SearchResult, SendOptions, SendTemplateOptions, SignatureVerificationResult, TEMPLATE_METADATA, TEMPLATE_PROVIDERS, VERIFY_PROVIDERS, VerifySignatureOptions, buildTemplateSendOptions, extractJsonField, isClerkWebhook, isDiscordWebhook, isGitHubWebhook, isGitLabWebhook, isLinearWebhook, isPaddleWebhook, isSendGridWebhook, isShopifyWebhook, isSlackWebhook, isStandardWebhook, isStripeWebhook, isTwilioWebhook, isVercelWebhook, matchAll, matchAny, matchBodyPath, matchBodySubset, matchContentType, matchHeader, matchJsonField, matchMethod, matchPath, matchQueryParam, parseBody, parseDuration, parseFormBody, parseJsonBody, parseSSE, validateMockResponse, validateResponseRules, verifyClerkSignature, verifyDiscordSignature, verifyGitHubSignature, verifyGitLabSignature, verifyLinearSignature, verifyPaddleSignature, verifyShopifySignature, verifySignature, verifySlackSignature, verifyStandardWebhookSignature, verifyStripeSignature, verifyTwilioSignature, verifyVercelSignature };
package/dist/index.js CHANGED
@@ -21,14 +21,28 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var src_exports = {};
22
22
  __export(src_exports, {
23
23
  ApiError: () => ApiError,
24
+ MAX_CONDITIONS_PER_RULE: () => MAX_CONDITIONS_PER_RULE,
25
+ MAX_CONDITION_NAME_LEN: () => MAX_CONDITION_NAME_LEN,
26
+ MAX_CONDITION_PATH_LEN: () => MAX_CONDITION_PATH_LEN,
27
+ MAX_CONDITION_VALUE_LEN: () => MAX_CONDITION_VALUE_LEN,
28
+ MAX_GLOB_PATTERN_LEN: () => MAX_GLOB_PATTERN_LEN,
29
+ MAX_RESPONSE_RULES: () => MAX_RESPONSE_RULES,
30
+ MAX_RULE_NAME_LEN: () => MAX_RULE_NAME_LEN,
31
+ MOCK_RESPONSE_DELAY_MAX: () => MOCK_RESPONSE_DELAY_MAX,
32
+ MOCK_RESPONSE_DELAY_MIN: () => MOCK_RESPONSE_DELAY_MIN,
33
+ MOCK_RESPONSE_STATUS_MAX: () => MOCK_RESPONSE_STATUS_MAX,
34
+ MOCK_RESPONSE_STATUS_MIN: () => MOCK_RESPONSE_STATUS_MIN,
24
35
  NotFoundError: () => NotFoundError,
25
36
  RateLimitError: () => RateLimitError,
26
37
  TEMPLATE_METADATA: () => TEMPLATE_METADATA,
38
+ TEMPLATE_PROVIDERS: () => TEMPLATE_PROVIDERS,
27
39
  TimeoutError: () => TimeoutError,
28
40
  UnauthorizedError: () => UnauthorizedError,
41
+ VERIFY_PROVIDERS: () => VERIFY_PROVIDERS,
29
42
  WebhookFlowBuilder: () => WebhookFlowBuilder,
30
43
  WebhooksCC: () => WebhooksCC,
31
44
  WebhooksCCError: () => WebhooksCCError,
45
+ buildTemplateSendOptions: () => buildTemplateSendOptions,
32
46
  diffRequests: () => diffRequests,
33
47
  extractJsonField: () => extractJsonField,
34
48
  isClerkWebhook: () => isClerkWebhook,
@@ -59,6 +73,8 @@ __export(src_exports, {
59
73
  parseFormBody: () => parseFormBody,
60
74
  parseJsonBody: () => parseJsonBody,
61
75
  parseSSE: () => parseSSE,
76
+ validateMockResponse: () => validateMockResponse,
77
+ validateResponseRules: () => validateResponseRules,
62
78
  verifyClerkSignature: () => verifyClerkSignature,
63
79
  verifyDiscordSignature: () => verifyDiscordSignature,
64
80
  verifyGitHubSignature: () => verifyGitHubSignature,
@@ -273,6 +289,20 @@ var TEMPLATE_PROVIDERS = [
273
289
  "gitlab",
274
290
  "standard-webhooks"
275
291
  ];
292
+ var VERIFY_PROVIDERS = [
293
+ "stripe",
294
+ "github",
295
+ "shopify",
296
+ "twilio",
297
+ "slack",
298
+ "paddle",
299
+ "linear",
300
+ "clerk",
301
+ "discord",
302
+ "vercel",
303
+ "gitlab",
304
+ "standard-webhooks"
305
+ ];
276
306
  var TEMPLATE_METADATA = Object.freeze({
277
307
  stripe: Object.freeze({
278
308
  provider: "stripe",
@@ -2186,6 +2216,122 @@ var WebhookFlowBuilder = class {
2186
2216
  }
2187
2217
  };
2188
2218
 
2219
+ // src/validation.ts
2220
+ var MOCK_RESPONSE_STATUS_MIN = 100;
2221
+ var MOCK_RESPONSE_STATUS_MAX = 599;
2222
+ var MOCK_RESPONSE_DELAY_MIN = 0;
2223
+ var MOCK_RESPONSE_DELAY_MAX = 3e4;
2224
+ var MAX_RESPONSE_RULES = 50;
2225
+ var MAX_CONDITIONS_PER_RULE = 10;
2226
+ var VALID_CONDITION_FIELDS = /* @__PURE__ */ new Set([
2227
+ "method",
2228
+ "path",
2229
+ "header",
2230
+ "body_contains",
2231
+ "body_path",
2232
+ "query"
2233
+ ]);
2234
+ var VALID_CONDITION_OPS = /* @__PURE__ */ new Set(["eq", "contains", "starts_with", "matches", "exists"]);
2235
+ var VALID_OPS_BY_FIELD = {
2236
+ method: /* @__PURE__ */ new Set(["eq"]),
2237
+ path: /* @__PURE__ */ new Set(["eq", "contains", "starts_with", "matches"]),
2238
+ header: /* @__PURE__ */ new Set(["exists", "eq", "contains"]),
2239
+ body_contains: /* @__PURE__ */ new Set(["contains"]),
2240
+ body_path: /* @__PURE__ */ new Set(["exists", "eq", "contains"]),
2241
+ query: /* @__PURE__ */ new Set(["exists", "eq"])
2242
+ };
2243
+ var MAX_CONDITION_VALUE_LEN = 4096;
2244
+ var MAX_CONDITION_NAME_LEN = 256;
2245
+ var MAX_CONDITION_PATH_LEN = 256;
2246
+ var MAX_RULE_NAME_LEN = 200;
2247
+ var MAX_GLOB_PATTERN_LEN = 500;
2248
+ function validateMockResponse(mockResponse, fieldName = "mock response") {
2249
+ const { status, body, headers, delay } = mockResponse;
2250
+ if (!Number.isInteger(status) || status < MOCK_RESPONSE_STATUS_MIN || status > MOCK_RESPONSE_STATUS_MAX) {
2251
+ throw new Error(
2252
+ `Invalid ${fieldName} status: ${status}. Must be an integer ${MOCK_RESPONSE_STATUS_MIN}-${MOCK_RESPONSE_STATUS_MAX}.`
2253
+ );
2254
+ }
2255
+ if (body !== void 0 && typeof body !== "string") {
2256
+ throw new Error(`Invalid ${fieldName} body: must be a string.`);
2257
+ }
2258
+ if (headers !== void 0) {
2259
+ if (typeof headers !== "object" || headers === null || Array.isArray(headers)) {
2260
+ throw new Error(`Invalid ${fieldName} headers: must be a Record<string, string>.`);
2261
+ }
2262
+ for (const val of Object.values(headers)) {
2263
+ if (typeof val !== "string") {
2264
+ throw new Error(`Invalid ${fieldName} headers: all values must be strings.`);
2265
+ }
2266
+ }
2267
+ }
2268
+ if (delay !== void 0 && (!Number.isInteger(delay) || delay < MOCK_RESPONSE_DELAY_MIN || delay > MOCK_RESPONSE_DELAY_MAX)) {
2269
+ throw new Error(
2270
+ `Invalid ${fieldName} delay: ${delay}. Must be an integer ${MOCK_RESPONSE_DELAY_MIN}-${MOCK_RESPONSE_DELAY_MAX}.`
2271
+ );
2272
+ }
2273
+ }
2274
+ function validateResponseRules(rules) {
2275
+ if (!Array.isArray(rules)) {
2276
+ throw new Error("responseRules must be an array");
2277
+ }
2278
+ if (rules.length > MAX_RESPONSE_RULES) {
2279
+ throw new Error(`responseRules: max ${MAX_RESPONSE_RULES} rules allowed`);
2280
+ }
2281
+ for (let i = 0; i < rules.length; i++) {
2282
+ const rule = rules[i];
2283
+ const prefix = `responseRules[${i}]`;
2284
+ if (rule.name !== void 0 && (typeof rule.name !== "string" || rule.name.length > MAX_RULE_NAME_LEN)) {
2285
+ throw new Error(`${prefix}: name must be a string (max ${MAX_RULE_NAME_LEN} chars)`);
2286
+ }
2287
+ if (!Array.isArray(rule.conditions) || rule.conditions.length === 0) {
2288
+ throw new Error(`${prefix}: conditions must be a non-empty array`);
2289
+ }
2290
+ if (rule.conditions.length > MAX_CONDITIONS_PER_RULE) {
2291
+ throw new Error(`${prefix}: max ${MAX_CONDITIONS_PER_RULE} conditions per rule`);
2292
+ }
2293
+ if (rule.logic !== void 0 && rule.logic !== "and" && rule.logic !== "or") {
2294
+ throw new Error(`${prefix}: logic must be "and" or "or"`);
2295
+ }
2296
+ for (let j = 0; j < rule.conditions.length; j++) {
2297
+ const c = rule.conditions[j];
2298
+ const cp = `${prefix}.conditions[${j}]`;
2299
+ if (!VALID_CONDITION_FIELDS.has(c.field)) {
2300
+ throw new Error(`${cp}: invalid field "${c.field}"`);
2301
+ }
2302
+ if (!VALID_CONDITION_OPS.has(c.op)) {
2303
+ throw new Error(`${cp}: invalid op "${c.op}"`);
2304
+ }
2305
+ const fieldOps = VALID_OPS_BY_FIELD[c.field];
2306
+ if (fieldOps && !fieldOps.has(c.op)) {
2307
+ throw new Error(`${cp}: op "${c.op}" is not valid for field "${c.field}"`);
2308
+ }
2309
+ if (c.value !== void 0 && typeof c.value === "string" && c.value.length > MAX_CONDITION_VALUE_LEN) {
2310
+ throw new Error(`${cp}: value too long (max ${MAX_CONDITION_VALUE_LEN} chars)`);
2311
+ }
2312
+ if (c.name !== void 0 && typeof c.name === "string" && c.name.length > MAX_CONDITION_NAME_LEN) {
2313
+ throw new Error(`${cp}: name too long (max ${MAX_CONDITION_NAME_LEN} chars)`);
2314
+ }
2315
+ if (c.path !== void 0 && typeof c.path === "string" && c.path.length > MAX_CONDITION_PATH_LEN) {
2316
+ throw new Error(`${cp}: path too long (max ${MAX_CONDITION_PATH_LEN} chars)`);
2317
+ }
2318
+ if ((c.field === "header" || c.field === "query") && (!c.name || c.name.length === 0)) {
2319
+ throw new Error(`${cp}: name is required for ${c.field} conditions`);
2320
+ }
2321
+ if (c.field === "body_path" && (!c.path || c.path.length === 0)) {
2322
+ throw new Error(`${cp}: path is required for body_path conditions`);
2323
+ }
2324
+ if (c.op !== "exists" && (c.value === void 0 || c.value === null)) {
2325
+ throw new Error(`${cp}: value is required when op is "${c.op}"`);
2326
+ }
2327
+ if (c.op === "matches" && c.value && c.value.length > MAX_GLOB_PATTERN_LEN) {
2328
+ throw new Error(`${cp}: matches pattern too long (max ${MAX_GLOB_PATTERN_LEN} chars)`);
2329
+ }
2330
+ }
2331
+ validateMockResponse(rule.response, `${prefix}.response`);
2332
+ }
2333
+ }
2334
+
2189
2335
  // src/client.ts
2190
2336
  var DEFAULT_BASE_URL = "https://webhooks.cc";
2191
2337
  var DEFAULT_WEBHOOK_URL = "https://go.webhooks.cc";
@@ -2193,7 +2339,7 @@ var DEFAULT_TIMEOUT = 3e4;
2193
2339
  var DEFAULT_RETRY_ATTEMPTS = 1;
2194
2340
  var DEFAULT_RETRY_BACKOFF_MS = 1e3;
2195
2341
  var DEFAULT_RETRY_STATUSES = [429, 500, 502, 503, 504];
2196
- var SDK_VERSION = "0.6.0";
2342
+ var SDK_VERSION = true ? "1.3.0" : "0.0.0-dev";
2197
2343
  var WAIT_FOR_LOOKBACK_MS = 5 * 60 * 1e3;
2198
2344
  var DEFAULT_EXPORT_PAGE_SIZE = 100;
2199
2345
  var PROVIDER_PARAM_DESCRIPTION = TEMPLATE_PROVIDERS.map((provider) => `"${provider}"`).join("|");
@@ -2452,15 +2598,6 @@ async function collectMatchingRequests(fetchRequests, options) {
2452
2598
  }
2453
2599
  throw new TimeoutError(timeout);
2454
2600
  }
2455
- function validateMockResponse(mockResponse, fieldName) {
2456
- const { status, delay } = mockResponse;
2457
- if (!Number.isInteger(status) || status < 100 || status > 599) {
2458
- throw new Error(`Invalid ${fieldName} status: ${status}. Must be an integer 100-599.`);
2459
- }
2460
- if (delay !== void 0 && (!Number.isInteger(delay) || delay < 0 || delay > 3e4)) {
2461
- throw new Error(`Invalid ${fieldName} delay: ${delay}. Must be an integer 0-30000.`);
2462
- }
2463
- }
2464
2601
  var WebhooksCC = class {
2465
2602
  constructor(options) {
2466
2603
  this.endpoints = {
@@ -2468,6 +2605,9 @@ var WebhooksCC = class {
2468
2605
  if (options.mockResponse) {
2469
2606
  validateMockResponse(options.mockResponse, "mock response");
2470
2607
  }
2608
+ if (options.responseRules) {
2609
+ validateResponseRules(options.responseRules);
2610
+ }
2471
2611
  const body = {};
2472
2612
  if (options.name !== void 0) {
2473
2613
  body.name = options.name;
@@ -2475,6 +2615,12 @@ var WebhooksCC = class {
2475
2615
  if (options.mockResponse !== void 0) {
2476
2616
  body.mockResponse = options.mockResponse;
2477
2617
  }
2618
+ if (options.notificationUrl !== void 0) {
2619
+ body.notificationUrl = options.notificationUrl;
2620
+ }
2621
+ if (options.responseRules !== void 0) {
2622
+ body.responseRules = options.responseRules;
2623
+ }
2478
2624
  const isEphemeral = options.ephemeral === true || options.expiresIn !== void 0;
2479
2625
  if (isEphemeral) {
2480
2626
  body.isEphemeral = true;
@@ -2501,9 +2647,12 @@ var WebhooksCC = class {
2501
2647
  },
2502
2648
  update: async (slug, options) => {
2503
2649
  validatePathSegment(slug, "slug");
2504
- if (options.mockResponse && options.mockResponse !== null) {
2650
+ if (options.mockResponse != null) {
2505
2651
  validateMockResponse(options.mockResponse, "mock response");
2506
2652
  }
2653
+ if (options.responseRules != null) {
2654
+ validateResponseRules(options.responseRules);
2655
+ }
2507
2656
  return this.request("PATCH", `/endpoints/${slug}`, options);
2508
2657
  },
2509
2658
  delete: async (slug) => {
@@ -2813,7 +2962,18 @@ var WebhooksCC = class {
2813
2962
  }
2814
2963
  }
2815
2964
  const upperMethod = captured.method.toUpperCase();
2816
- const body = upperMethod === "GET" || upperMethod === "HEAD" ? void 0 : captured.body ?? void 0;
2965
+ let body;
2966
+ if (upperMethod === "GET" || upperMethod === "HEAD") {
2967
+ body = void 0;
2968
+ } else if (captured.bodyRaw) {
2969
+ try {
2970
+ body = Uint8Array.from(atob(captured.bodyRaw), (c) => c.charCodeAt(0));
2971
+ } catch {
2972
+ body = captured.body ?? void 0;
2973
+ }
2974
+ } else {
2975
+ body = captured.body ?? void 0;
2976
+ }
2817
2977
  return fetch(targetUrl, {
2818
2978
  method: captured.method,
2819
2979
  headers,
@@ -3099,7 +3259,9 @@ var WebhooksCC = class {
3099
3259
  name: "string?",
3100
3260
  ephemeral: "boolean?",
3101
3261
  expiresIn: "number|string?",
3102
- mockResponse: "object?"
3262
+ mockResponse: "object?",
3263
+ responseRules: "ResponseRule[]? \u2014 ordered conditional rules (first match wins, max 50). Each rule has conditions (field/op/value) and a response.",
3264
+ notificationUrl: "string?"
3103
3265
  }
3104
3266
  },
3105
3267
  list: {
@@ -3112,7 +3274,13 @@ var WebhooksCC = class {
3112
3274
  },
3113
3275
  update: {
3114
3276
  description: "Update endpoint settings",
3115
- params: { slug: "string", name: "string?", mockResponse: "object?" }
3277
+ params: {
3278
+ slug: "string",
3279
+ name: "string?",
3280
+ mockResponse: "object? \u2014 default response when no rule matches",
3281
+ responseRules: "ResponseRule[]|null? \u2014 conditional rules (first match wins), or null to clear",
3282
+ notificationUrl: "string?"
3283
+ }
3116
3284
  },
3117
3285
  delete: {
3118
3286
  description: "Delete endpoint and its requests",
@@ -3701,14 +3869,28 @@ function diffRequests(left, right, options = {}) {
3701
3869
  // Annotate the CommonJS export names for ESM import in node:
3702
3870
  0 && (module.exports = {
3703
3871
  ApiError,
3872
+ MAX_CONDITIONS_PER_RULE,
3873
+ MAX_CONDITION_NAME_LEN,
3874
+ MAX_CONDITION_PATH_LEN,
3875
+ MAX_CONDITION_VALUE_LEN,
3876
+ MAX_GLOB_PATTERN_LEN,
3877
+ MAX_RESPONSE_RULES,
3878
+ MAX_RULE_NAME_LEN,
3879
+ MOCK_RESPONSE_DELAY_MAX,
3880
+ MOCK_RESPONSE_DELAY_MIN,
3881
+ MOCK_RESPONSE_STATUS_MAX,
3882
+ MOCK_RESPONSE_STATUS_MIN,
3704
3883
  NotFoundError,
3705
3884
  RateLimitError,
3706
3885
  TEMPLATE_METADATA,
3886
+ TEMPLATE_PROVIDERS,
3707
3887
  TimeoutError,
3708
3888
  UnauthorizedError,
3889
+ VERIFY_PROVIDERS,
3709
3890
  WebhookFlowBuilder,
3710
3891
  WebhooksCC,
3711
3892
  WebhooksCCError,
3893
+ buildTemplateSendOptions,
3712
3894
  diffRequests,
3713
3895
  extractJsonField,
3714
3896
  isClerkWebhook,
@@ -3739,6 +3921,8 @@ function diffRequests(left, right, options = {}) {
3739
3921
  parseFormBody,
3740
3922
  parseJsonBody,
3741
3923
  parseSSE,
3924
+ validateMockResponse,
3925
+ validateResponseRules,
3742
3926
  verifyClerkSignature,
3743
3927
  verifyDiscordSignature,
3744
3928
  verifyGitHubSignature,
package/dist/index.mjs CHANGED
@@ -127,6 +127,20 @@ var TEMPLATE_PROVIDERS = [
127
127
  "gitlab",
128
128
  "standard-webhooks"
129
129
  ];
130
+ var VERIFY_PROVIDERS = [
131
+ "stripe",
132
+ "github",
133
+ "shopify",
134
+ "twilio",
135
+ "slack",
136
+ "paddle",
137
+ "linear",
138
+ "clerk",
139
+ "discord",
140
+ "vercel",
141
+ "gitlab",
142
+ "standard-webhooks"
143
+ ];
130
144
  var TEMPLATE_METADATA = Object.freeze({
131
145
  stripe: Object.freeze({
132
146
  provider: "stripe",
@@ -2040,6 +2054,122 @@ var WebhookFlowBuilder = class {
2040
2054
  }
2041
2055
  };
2042
2056
 
2057
+ // src/validation.ts
2058
+ var MOCK_RESPONSE_STATUS_MIN = 100;
2059
+ var MOCK_RESPONSE_STATUS_MAX = 599;
2060
+ var MOCK_RESPONSE_DELAY_MIN = 0;
2061
+ var MOCK_RESPONSE_DELAY_MAX = 3e4;
2062
+ var MAX_RESPONSE_RULES = 50;
2063
+ var MAX_CONDITIONS_PER_RULE = 10;
2064
+ var VALID_CONDITION_FIELDS = /* @__PURE__ */ new Set([
2065
+ "method",
2066
+ "path",
2067
+ "header",
2068
+ "body_contains",
2069
+ "body_path",
2070
+ "query"
2071
+ ]);
2072
+ var VALID_CONDITION_OPS = /* @__PURE__ */ new Set(["eq", "contains", "starts_with", "matches", "exists"]);
2073
+ var VALID_OPS_BY_FIELD = {
2074
+ method: /* @__PURE__ */ new Set(["eq"]),
2075
+ path: /* @__PURE__ */ new Set(["eq", "contains", "starts_with", "matches"]),
2076
+ header: /* @__PURE__ */ new Set(["exists", "eq", "contains"]),
2077
+ body_contains: /* @__PURE__ */ new Set(["contains"]),
2078
+ body_path: /* @__PURE__ */ new Set(["exists", "eq", "contains"]),
2079
+ query: /* @__PURE__ */ new Set(["exists", "eq"])
2080
+ };
2081
+ var MAX_CONDITION_VALUE_LEN = 4096;
2082
+ var MAX_CONDITION_NAME_LEN = 256;
2083
+ var MAX_CONDITION_PATH_LEN = 256;
2084
+ var MAX_RULE_NAME_LEN = 200;
2085
+ var MAX_GLOB_PATTERN_LEN = 500;
2086
+ function validateMockResponse(mockResponse, fieldName = "mock response") {
2087
+ const { status, body, headers, delay } = mockResponse;
2088
+ if (!Number.isInteger(status) || status < MOCK_RESPONSE_STATUS_MIN || status > MOCK_RESPONSE_STATUS_MAX) {
2089
+ throw new Error(
2090
+ `Invalid ${fieldName} status: ${status}. Must be an integer ${MOCK_RESPONSE_STATUS_MIN}-${MOCK_RESPONSE_STATUS_MAX}.`
2091
+ );
2092
+ }
2093
+ if (body !== void 0 && typeof body !== "string") {
2094
+ throw new Error(`Invalid ${fieldName} body: must be a string.`);
2095
+ }
2096
+ if (headers !== void 0) {
2097
+ if (typeof headers !== "object" || headers === null || Array.isArray(headers)) {
2098
+ throw new Error(`Invalid ${fieldName} headers: must be a Record<string, string>.`);
2099
+ }
2100
+ for (const val of Object.values(headers)) {
2101
+ if (typeof val !== "string") {
2102
+ throw new Error(`Invalid ${fieldName} headers: all values must be strings.`);
2103
+ }
2104
+ }
2105
+ }
2106
+ if (delay !== void 0 && (!Number.isInteger(delay) || delay < MOCK_RESPONSE_DELAY_MIN || delay > MOCK_RESPONSE_DELAY_MAX)) {
2107
+ throw new Error(
2108
+ `Invalid ${fieldName} delay: ${delay}. Must be an integer ${MOCK_RESPONSE_DELAY_MIN}-${MOCK_RESPONSE_DELAY_MAX}.`
2109
+ );
2110
+ }
2111
+ }
2112
+ function validateResponseRules(rules) {
2113
+ if (!Array.isArray(rules)) {
2114
+ throw new Error("responseRules must be an array");
2115
+ }
2116
+ if (rules.length > MAX_RESPONSE_RULES) {
2117
+ throw new Error(`responseRules: max ${MAX_RESPONSE_RULES} rules allowed`);
2118
+ }
2119
+ for (let i = 0; i < rules.length; i++) {
2120
+ const rule = rules[i];
2121
+ const prefix = `responseRules[${i}]`;
2122
+ if (rule.name !== void 0 && (typeof rule.name !== "string" || rule.name.length > MAX_RULE_NAME_LEN)) {
2123
+ throw new Error(`${prefix}: name must be a string (max ${MAX_RULE_NAME_LEN} chars)`);
2124
+ }
2125
+ if (!Array.isArray(rule.conditions) || rule.conditions.length === 0) {
2126
+ throw new Error(`${prefix}: conditions must be a non-empty array`);
2127
+ }
2128
+ if (rule.conditions.length > MAX_CONDITIONS_PER_RULE) {
2129
+ throw new Error(`${prefix}: max ${MAX_CONDITIONS_PER_RULE} conditions per rule`);
2130
+ }
2131
+ if (rule.logic !== void 0 && rule.logic !== "and" && rule.logic !== "or") {
2132
+ throw new Error(`${prefix}: logic must be "and" or "or"`);
2133
+ }
2134
+ for (let j = 0; j < rule.conditions.length; j++) {
2135
+ const c = rule.conditions[j];
2136
+ const cp = `${prefix}.conditions[${j}]`;
2137
+ if (!VALID_CONDITION_FIELDS.has(c.field)) {
2138
+ throw new Error(`${cp}: invalid field "${c.field}"`);
2139
+ }
2140
+ if (!VALID_CONDITION_OPS.has(c.op)) {
2141
+ throw new Error(`${cp}: invalid op "${c.op}"`);
2142
+ }
2143
+ const fieldOps = VALID_OPS_BY_FIELD[c.field];
2144
+ if (fieldOps && !fieldOps.has(c.op)) {
2145
+ throw new Error(`${cp}: op "${c.op}" is not valid for field "${c.field}"`);
2146
+ }
2147
+ if (c.value !== void 0 && typeof c.value === "string" && c.value.length > MAX_CONDITION_VALUE_LEN) {
2148
+ throw new Error(`${cp}: value too long (max ${MAX_CONDITION_VALUE_LEN} chars)`);
2149
+ }
2150
+ if (c.name !== void 0 && typeof c.name === "string" && c.name.length > MAX_CONDITION_NAME_LEN) {
2151
+ throw new Error(`${cp}: name too long (max ${MAX_CONDITION_NAME_LEN} chars)`);
2152
+ }
2153
+ if (c.path !== void 0 && typeof c.path === "string" && c.path.length > MAX_CONDITION_PATH_LEN) {
2154
+ throw new Error(`${cp}: path too long (max ${MAX_CONDITION_PATH_LEN} chars)`);
2155
+ }
2156
+ if ((c.field === "header" || c.field === "query") && (!c.name || c.name.length === 0)) {
2157
+ throw new Error(`${cp}: name is required for ${c.field} conditions`);
2158
+ }
2159
+ if (c.field === "body_path" && (!c.path || c.path.length === 0)) {
2160
+ throw new Error(`${cp}: path is required for body_path conditions`);
2161
+ }
2162
+ if (c.op !== "exists" && (c.value === void 0 || c.value === null)) {
2163
+ throw new Error(`${cp}: value is required when op is "${c.op}"`);
2164
+ }
2165
+ if (c.op === "matches" && c.value && c.value.length > MAX_GLOB_PATTERN_LEN) {
2166
+ throw new Error(`${cp}: matches pattern too long (max ${MAX_GLOB_PATTERN_LEN} chars)`);
2167
+ }
2168
+ }
2169
+ validateMockResponse(rule.response, `${prefix}.response`);
2170
+ }
2171
+ }
2172
+
2043
2173
  // src/client.ts
2044
2174
  var DEFAULT_BASE_URL = "https://webhooks.cc";
2045
2175
  var DEFAULT_WEBHOOK_URL = "https://go.webhooks.cc";
@@ -2047,7 +2177,7 @@ var DEFAULT_TIMEOUT = 3e4;
2047
2177
  var DEFAULT_RETRY_ATTEMPTS = 1;
2048
2178
  var DEFAULT_RETRY_BACKOFF_MS = 1e3;
2049
2179
  var DEFAULT_RETRY_STATUSES = [429, 500, 502, 503, 504];
2050
- var SDK_VERSION = "0.6.0";
2180
+ var SDK_VERSION = true ? "1.3.0" : "0.0.0-dev";
2051
2181
  var WAIT_FOR_LOOKBACK_MS = 5 * 60 * 1e3;
2052
2182
  var DEFAULT_EXPORT_PAGE_SIZE = 100;
2053
2183
  var PROVIDER_PARAM_DESCRIPTION = TEMPLATE_PROVIDERS.map((provider) => `"${provider}"`).join("|");
@@ -2306,15 +2436,6 @@ async function collectMatchingRequests(fetchRequests, options) {
2306
2436
  }
2307
2437
  throw new TimeoutError(timeout);
2308
2438
  }
2309
- function validateMockResponse(mockResponse, fieldName) {
2310
- const { status, delay } = mockResponse;
2311
- if (!Number.isInteger(status) || status < 100 || status > 599) {
2312
- throw new Error(`Invalid ${fieldName} status: ${status}. Must be an integer 100-599.`);
2313
- }
2314
- if (delay !== void 0 && (!Number.isInteger(delay) || delay < 0 || delay > 3e4)) {
2315
- throw new Error(`Invalid ${fieldName} delay: ${delay}. Must be an integer 0-30000.`);
2316
- }
2317
- }
2318
2439
  var WebhooksCC = class {
2319
2440
  constructor(options) {
2320
2441
  this.endpoints = {
@@ -2322,6 +2443,9 @@ var WebhooksCC = class {
2322
2443
  if (options.mockResponse) {
2323
2444
  validateMockResponse(options.mockResponse, "mock response");
2324
2445
  }
2446
+ if (options.responseRules) {
2447
+ validateResponseRules(options.responseRules);
2448
+ }
2325
2449
  const body = {};
2326
2450
  if (options.name !== void 0) {
2327
2451
  body.name = options.name;
@@ -2329,6 +2453,12 @@ var WebhooksCC = class {
2329
2453
  if (options.mockResponse !== void 0) {
2330
2454
  body.mockResponse = options.mockResponse;
2331
2455
  }
2456
+ if (options.notificationUrl !== void 0) {
2457
+ body.notificationUrl = options.notificationUrl;
2458
+ }
2459
+ if (options.responseRules !== void 0) {
2460
+ body.responseRules = options.responseRules;
2461
+ }
2332
2462
  const isEphemeral = options.ephemeral === true || options.expiresIn !== void 0;
2333
2463
  if (isEphemeral) {
2334
2464
  body.isEphemeral = true;
@@ -2355,9 +2485,12 @@ var WebhooksCC = class {
2355
2485
  },
2356
2486
  update: async (slug, options) => {
2357
2487
  validatePathSegment(slug, "slug");
2358
- if (options.mockResponse && options.mockResponse !== null) {
2488
+ if (options.mockResponse != null) {
2359
2489
  validateMockResponse(options.mockResponse, "mock response");
2360
2490
  }
2491
+ if (options.responseRules != null) {
2492
+ validateResponseRules(options.responseRules);
2493
+ }
2361
2494
  return this.request("PATCH", `/endpoints/${slug}`, options);
2362
2495
  },
2363
2496
  delete: async (slug) => {
@@ -2667,7 +2800,18 @@ var WebhooksCC = class {
2667
2800
  }
2668
2801
  }
2669
2802
  const upperMethod = captured.method.toUpperCase();
2670
- const body = upperMethod === "GET" || upperMethod === "HEAD" ? void 0 : captured.body ?? void 0;
2803
+ let body;
2804
+ if (upperMethod === "GET" || upperMethod === "HEAD") {
2805
+ body = void 0;
2806
+ } else if (captured.bodyRaw) {
2807
+ try {
2808
+ body = Uint8Array.from(atob(captured.bodyRaw), (c) => c.charCodeAt(0));
2809
+ } catch {
2810
+ body = captured.body ?? void 0;
2811
+ }
2812
+ } else {
2813
+ body = captured.body ?? void 0;
2814
+ }
2671
2815
  return fetch(targetUrl, {
2672
2816
  method: captured.method,
2673
2817
  headers,
@@ -2953,7 +3097,9 @@ var WebhooksCC = class {
2953
3097
  name: "string?",
2954
3098
  ephemeral: "boolean?",
2955
3099
  expiresIn: "number|string?",
2956
- mockResponse: "object?"
3100
+ mockResponse: "object?",
3101
+ responseRules: "ResponseRule[]? \u2014 ordered conditional rules (first match wins, max 50). Each rule has conditions (field/op/value) and a response.",
3102
+ notificationUrl: "string?"
2957
3103
  }
2958
3104
  },
2959
3105
  list: {
@@ -2966,7 +3112,13 @@ var WebhooksCC = class {
2966
3112
  },
2967
3113
  update: {
2968
3114
  description: "Update endpoint settings",
2969
- params: { slug: "string", name: "string?", mockResponse: "object?" }
3115
+ params: {
3116
+ slug: "string",
3117
+ name: "string?",
3118
+ mockResponse: "object? \u2014 default response when no rule matches",
3119
+ responseRules: "ResponseRule[]|null? \u2014 conditional rules (first match wins), or null to clear",
3120
+ notificationUrl: "string?"
3121
+ }
2970
3122
  },
2971
3123
  delete: {
2972
3124
  description: "Delete endpoint and its requests",
@@ -3401,14 +3553,28 @@ function matchAny(first, ...rest) {
3401
3553
  }
3402
3554
  export {
3403
3555
  ApiError,
3556
+ MAX_CONDITIONS_PER_RULE,
3557
+ MAX_CONDITION_NAME_LEN,
3558
+ MAX_CONDITION_PATH_LEN,
3559
+ MAX_CONDITION_VALUE_LEN,
3560
+ MAX_GLOB_PATTERN_LEN,
3561
+ MAX_RESPONSE_RULES,
3562
+ MAX_RULE_NAME_LEN,
3563
+ MOCK_RESPONSE_DELAY_MAX,
3564
+ MOCK_RESPONSE_DELAY_MIN,
3565
+ MOCK_RESPONSE_STATUS_MAX,
3566
+ MOCK_RESPONSE_STATUS_MIN,
3404
3567
  NotFoundError,
3405
3568
  RateLimitError,
3406
3569
  TEMPLATE_METADATA,
3570
+ TEMPLATE_PROVIDERS,
3407
3571
  TimeoutError,
3408
3572
  UnauthorizedError,
3573
+ VERIFY_PROVIDERS,
3409
3574
  WebhookFlowBuilder,
3410
3575
  WebhooksCC,
3411
3576
  WebhooksCCError,
3577
+ buildTemplateSendOptions,
3412
3578
  diffRequests,
3413
3579
  extractJsonField,
3414
3580
  isClerkWebhook,
@@ -3439,6 +3605,8 @@ export {
3439
3605
  parseFormBody,
3440
3606
  parseJsonBody,
3441
3607
  parseSSE,
3608
+ validateMockResponse,
3609
+ validateResponseRules,
3442
3610
  verifyClerkSignature,
3443
3611
  verifyDiscordSignature,
3444
3612
  verifyGitHubSignature,
@@ -1,4 +1,4 @@
1
- import { g as DiffResult, e as CreateEndpointOptions, R as Request, a2 as WebhooksCC, E as Endpoint } from './diff-kGWh0pPP.mjs';
1
+ import { j as DiffResult, h as CreateEndpointOptions, R as Request, a4 as WebhooksCC, E as Endpoint } from './diff-CRMlHQPX.mjs';
2
2
 
3
3
  type TestingClient = Pick<WebhooksCC, "endpoints" | "requests">;
4
4
  interface AssertRequestExpectation {
package/dist/testing.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { g as DiffResult, e as CreateEndpointOptions, R as Request, a2 as WebhooksCC, E as Endpoint } from './diff-kGWh0pPP.js';
1
+ import { j as DiffResult, h as CreateEndpointOptions, R as Request, a4 as WebhooksCC, E as Endpoint } from './diff-CRMlHQPX.js';
2
2
 
3
3
  type TestingClient = Pick<WebhooksCC, "endpoints" | "requests">;
4
4
  interface AssertRequestExpectation {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webhooks-cc/sdk",
3
- "version": "1.2.1",
3
+ "version": "1.3.0",
4
4
  "description": "TypeScript SDK for webhooks.cc — create endpoints, capture requests, assert in tests",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -37,9 +37,10 @@
37
37
  },
38
38
  "homepage": "https://webhooks.cc",
39
39
  "devDependencies": {
40
+ "@types/node": "^24.0.0",
40
41
  "tsup": "^8.5.1",
41
- "typescript": "^5.9.3",
42
- "vitest": "^4.1.1"
42
+ "typescript": "^6.0.2",
43
+ "vitest": "^4.1.2"
43
44
  },
44
45
  "scripts": {
45
46
  "build": "tsup --config tsup.config.ts",