@webhooks-cc/sdk 0.2.0 → 0.3.1
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/README.md +127 -34
- package/dist/index.d.mts +137 -7
- package/dist/index.d.ts +137 -7
- package/dist/index.js +501 -10
- package/dist/index.mjs +488 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,52 +1,69 @@
|
|
|
1
1
|
# @webhooks-cc/sdk
|
|
2
2
|
|
|
3
|
-
TypeScript SDK for [webhooks.cc](https://webhooks.cc). Create
|
|
3
|
+
TypeScript SDK for [webhooks.cc](https://webhooks.cc). Create webhook endpoints, capture requests, match and replay them, and stream events in real time.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
npm install @webhooks-cc/sdk
|
|
9
|
+
# or: pnpm add / yarn add / bun add
|
|
9
10
|
```
|
|
10
11
|
|
|
11
|
-
## Quick
|
|
12
|
+
## Quick start
|
|
12
13
|
|
|
13
14
|
```typescript
|
|
14
|
-
import { WebhooksCC } from "@webhooks-cc/sdk";
|
|
15
|
+
import { WebhooksCC, matchAll, matchMethod, matchHeader } from "@webhooks-cc/sdk";
|
|
15
16
|
|
|
16
17
|
const client = new WebhooksCC({ apiKey: "whcc_..." });
|
|
17
18
|
|
|
18
|
-
// Create
|
|
19
|
-
const endpoint = await client.endpoints.create({ name: "
|
|
19
|
+
// Create an endpoint
|
|
20
|
+
const endpoint = await client.endpoints.create({ name: "stripe-test" });
|
|
20
21
|
console.log(endpoint.url); // https://go.webhooks.cc/w/abc123
|
|
21
22
|
|
|
22
23
|
// Point your service at endpoint.url, then wait for the webhook
|
|
23
24
|
const request = await client.requests.waitFor(endpoint.slug, {
|
|
24
|
-
timeout:
|
|
25
|
-
match: (
|
|
25
|
+
timeout: "30s",
|
|
26
|
+
match: matchAll(matchMethod("POST"), matchHeader("stripe-signature")),
|
|
26
27
|
});
|
|
27
28
|
|
|
28
|
-
console.log(request.body); // '{"
|
|
29
|
-
console.log(request.headers); // { 'content-type': 'application/json', ... }
|
|
29
|
+
console.log(request.body); // '{"type":"checkout.session.completed",...}'
|
|
30
30
|
|
|
31
31
|
// Clean up
|
|
32
32
|
await client.endpoints.delete(endpoint.slug);
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
-
##
|
|
35
|
+
## Client options
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
```typescript
|
|
38
|
+
new WebhooksCC(options);
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
| Option | Type | Default | Description |
|
|
42
|
+
| ------------ | ------------- | ------------------------ | ------------------------- |
|
|
43
|
+
| `apiKey` | `string` | _required_ | API key (`whcc_...`) |
|
|
44
|
+
| `baseUrl` | `string` | `https://webhooks.cc` | API base URL |
|
|
45
|
+
| `webhookUrl` | `string` | `https://go.webhooks.cc` | Webhook receiver URL |
|
|
46
|
+
| `timeout` | `number` | `30000` | HTTP request timeout (ms) |
|
|
47
|
+
| `hooks` | `ClientHooks` | — | Lifecycle callbacks |
|
|
38
48
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
49
|
+
### Hooks
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
const client = new WebhooksCC({
|
|
53
|
+
apiKey: "whcc_...",
|
|
54
|
+
hooks: {
|
|
55
|
+
onRequest: (info) => console.log(info.method, info.url),
|
|
56
|
+
onResponse: (info) => console.log(info.status),
|
|
57
|
+
onError: (info) => console.error(info.error),
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
```
|
|
44
61
|
|
|
45
|
-
|
|
62
|
+
## Endpoints
|
|
46
63
|
|
|
47
64
|
```typescript
|
|
48
65
|
// Create
|
|
49
|
-
const endpoint = await client.endpoints.create({ name: "
|
|
66
|
+
const endpoint = await client.endpoints.create({ name: "my-test" });
|
|
50
67
|
|
|
51
68
|
// List all
|
|
52
69
|
const endpoints = await client.endpoints.list();
|
|
@@ -54,17 +71,33 @@ const endpoints = await client.endpoints.list();
|
|
|
54
71
|
// Get by slug
|
|
55
72
|
const endpoint = await client.endpoints.get("abc123");
|
|
56
73
|
|
|
74
|
+
// Update name or mock response
|
|
75
|
+
await client.endpoints.update("abc123", {
|
|
76
|
+
name: "New Name",
|
|
77
|
+
mockResponse: { status: 201, body: '{"ok":true}', headers: {} },
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Clear mock response
|
|
81
|
+
await client.endpoints.update("abc123", { mockResponse: null });
|
|
82
|
+
|
|
83
|
+
// Send a test webhook
|
|
84
|
+
const res = await client.endpoints.send("abc123", {
|
|
85
|
+
method: "POST",
|
|
86
|
+
headers: { "content-type": "application/json" },
|
|
87
|
+
body: { event: "test" },
|
|
88
|
+
});
|
|
89
|
+
|
|
57
90
|
// Delete
|
|
58
91
|
await client.endpoints.delete("abc123");
|
|
59
92
|
```
|
|
60
93
|
|
|
61
|
-
|
|
94
|
+
## Requests
|
|
62
95
|
|
|
63
96
|
```typescript
|
|
64
|
-
// List captured requests
|
|
97
|
+
// List captured requests
|
|
65
98
|
const requests = await client.requests.list("endpoint-slug", {
|
|
66
|
-
limit: 50,
|
|
67
|
-
since: Date.now() - 60000,
|
|
99
|
+
limit: 50,
|
|
100
|
+
since: Date.now() - 60000,
|
|
68
101
|
});
|
|
69
102
|
|
|
70
103
|
// Get a single request by ID
|
|
@@ -72,27 +105,82 @@ const request = await client.requests.get("request-id");
|
|
|
72
105
|
|
|
73
106
|
// Poll until a matching request arrives
|
|
74
107
|
const request = await client.requests.waitFor("endpoint-slug", {
|
|
75
|
-
timeout:
|
|
76
|
-
pollInterval:
|
|
77
|
-
match: (
|
|
108
|
+
timeout: "30s", // human-readable or milliseconds
|
|
109
|
+
pollInterval: "500ms",
|
|
110
|
+
match: matchHeader("stripe-signature"),
|
|
78
111
|
});
|
|
112
|
+
|
|
113
|
+
// Replay a captured request to a target URL
|
|
114
|
+
const res = await client.requests.replay(request.id, "http://localhost:3000/webhooks");
|
|
115
|
+
|
|
116
|
+
// Stream requests in real time via SSE
|
|
117
|
+
for await (const req of client.requests.subscribe("endpoint-slug")) {
|
|
118
|
+
console.log(req.method, req.body);
|
|
119
|
+
}
|
|
79
120
|
```
|
|
80
121
|
|
|
81
|
-
|
|
122
|
+
## Matchers
|
|
123
|
+
|
|
124
|
+
Composable functions for `waitFor`'s `match` option:
|
|
82
125
|
|
|
83
126
|
```typescript
|
|
84
|
-
import {
|
|
127
|
+
import { matchMethod, matchHeader, matchBodyPath, matchAll, matchAny } from "@webhooks-cc/sdk";
|
|
128
|
+
|
|
129
|
+
// Match POST requests with a specific header
|
|
130
|
+
matchAll(matchMethod("POST"), matchHeader("x-event-type", "payment.success"));
|
|
131
|
+
|
|
132
|
+
// Match header presence (any value)
|
|
133
|
+
matchHeader("stripe-signature");
|
|
134
|
+
|
|
135
|
+
// Match a nested JSON body field
|
|
136
|
+
matchBodyPath("data.object.id", "sub_123");
|
|
137
|
+
|
|
138
|
+
// Match any of several conditions
|
|
139
|
+
matchAny(matchHeader("stripe-signature"), matchHeader("x-github-event"));
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Provider helpers
|
|
143
|
+
|
|
144
|
+
Detect webhook sources by their signature headers:
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
import { isStripeWebhook, isGitHubWebhook } from "@webhooks-cc/sdk";
|
|
148
|
+
|
|
149
|
+
if (isStripeWebhook(request)) {
|
|
150
|
+
// has stripe-signature header
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Available: `isStripeWebhook`, `isGitHubWebhook`, `isShopifyWebhook`, `isSlackWebhook`, `isTwilioWebhook`, `isPaddleWebhook`, `isLinearWebhook`.
|
|
155
|
+
|
|
156
|
+
## Self-description
|
|
157
|
+
|
|
158
|
+
AI agents can call `client.describe()` to get a structured summary of all SDK operations, parameters, and return types — no API call required.
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
const desc = client.describe();
|
|
162
|
+
// { version: "0.3.0", endpoints: { create: { ... }, ... }, requests: { ... } }
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Errors
|
|
166
|
+
|
|
167
|
+
All API errors extend `WebhooksCCError` and include actionable recovery hints:
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
import { WebhooksCC, WebhooksCCError, NotFoundError } from "@webhooks-cc/sdk";
|
|
85
171
|
|
|
86
172
|
try {
|
|
87
173
|
await client.endpoints.get("nonexistent");
|
|
88
174
|
} catch (error) {
|
|
89
|
-
if (error instanceof
|
|
175
|
+
if (error instanceof WebhooksCCError) {
|
|
90
176
|
console.log(error.statusCode); // 404
|
|
91
|
-
console.log(error.message); //
|
|
177
|
+
console.log(error.message); // includes what went wrong and how to fix it
|
|
92
178
|
}
|
|
93
179
|
}
|
|
94
180
|
```
|
|
95
181
|
|
|
182
|
+
Error classes: `WebhooksCCError`, `UnauthorizedError`, `NotFoundError`, `TimeoutError`, `RateLimitError`. The legacy `ApiError` alias is still exported for backward compatibility.
|
|
183
|
+
|
|
96
184
|
## GitHub Actions
|
|
97
185
|
|
|
98
186
|
Add your API key as a repository secret named `WHK_API_KEY`:
|
|
@@ -107,14 +195,14 @@ Add your API key as a repository secret named `WHK_API_KEY`:
|
|
|
107
195
|
```typescript
|
|
108
196
|
// webhook.test.ts
|
|
109
197
|
import { describe, it, expect, afterAll } from "vitest";
|
|
110
|
-
import { WebhooksCC } from "@webhooks-cc/sdk";
|
|
198
|
+
import { WebhooksCC, matchHeader } from "@webhooks-cc/sdk";
|
|
111
199
|
|
|
112
200
|
const client = new WebhooksCC({ apiKey: process.env.WHK_API_KEY! });
|
|
113
201
|
|
|
114
202
|
describe("webhook integration", () => {
|
|
115
203
|
let slug: string;
|
|
116
204
|
|
|
117
|
-
it("receives
|
|
205
|
+
it("receives Stripe webhook", async () => {
|
|
118
206
|
const endpoint = await client.endpoints.create({ name: "CI Test" });
|
|
119
207
|
slug = endpoint.slug;
|
|
120
208
|
|
|
@@ -123,12 +211,12 @@ describe("webhook integration", () => {
|
|
|
123
211
|
await yourService.createOrder();
|
|
124
212
|
|
|
125
213
|
const req = await client.requests.waitFor(slug, {
|
|
126
|
-
timeout:
|
|
127
|
-
match: (
|
|
214
|
+
timeout: "15s",
|
|
215
|
+
match: matchHeader("stripe-signature"),
|
|
128
216
|
});
|
|
129
217
|
|
|
130
218
|
const body = JSON.parse(req.body!);
|
|
131
|
-
expect(body.
|
|
219
|
+
expect(body.type).toBe("checkout.session.completed");
|
|
132
220
|
});
|
|
133
221
|
|
|
134
222
|
afterAll(async () => {
|
|
@@ -144,11 +232,16 @@ All types are exported:
|
|
|
144
232
|
```typescript
|
|
145
233
|
import type {
|
|
146
234
|
ClientOptions,
|
|
235
|
+
ClientHooks,
|
|
147
236
|
Endpoint,
|
|
148
237
|
Request,
|
|
149
238
|
CreateEndpointOptions,
|
|
239
|
+
UpdateEndpointOptions,
|
|
240
|
+
SendOptions,
|
|
150
241
|
ListRequestsOptions,
|
|
151
242
|
WaitForOptions,
|
|
243
|
+
SubscribeOptions,
|
|
244
|
+
SDKDescription,
|
|
152
245
|
} from "@webhooks-cc/sdk";
|
|
153
246
|
```
|
|
154
247
|
|
package/dist/index.d.mts
CHANGED
|
@@ -49,6 +49,30 @@ interface CreateEndpointOptions {
|
|
|
49
49
|
/** Display name for the endpoint */
|
|
50
50
|
name?: string;
|
|
51
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Options for updating an existing endpoint.
|
|
54
|
+
*/
|
|
55
|
+
interface UpdateEndpointOptions {
|
|
56
|
+
/** New display name */
|
|
57
|
+
name?: string;
|
|
58
|
+
/** Mock response config, or null to clear */
|
|
59
|
+
mockResponse?: {
|
|
60
|
+
status: number;
|
|
61
|
+
body: string;
|
|
62
|
+
headers: Record<string, string>;
|
|
63
|
+
} | null;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Options for sending a test webhook to an endpoint.
|
|
67
|
+
*/
|
|
68
|
+
interface SendOptions {
|
|
69
|
+
/** HTTP method (default: "POST") */
|
|
70
|
+
method?: string;
|
|
71
|
+
/** HTTP headers to include */
|
|
72
|
+
headers?: Record<string, string>;
|
|
73
|
+
/** Request body (will be JSON-serialized if not a string) */
|
|
74
|
+
body?: unknown;
|
|
75
|
+
}
|
|
52
76
|
/**
|
|
53
77
|
* Options for listing captured requests.
|
|
54
78
|
*/
|
|
@@ -62,13 +86,22 @@ interface ListRequestsOptions {
|
|
|
62
86
|
* Options for waitFor() polling behavior.
|
|
63
87
|
*/
|
|
64
88
|
interface WaitForOptions {
|
|
65
|
-
/** Maximum time to wait
|
|
66
|
-
timeout?: number;
|
|
67
|
-
/** Interval between polls
|
|
68
|
-
pollInterval?: number;
|
|
89
|
+
/** Maximum time to wait (ms or duration string like "30s", "5m") (default: 30000) */
|
|
90
|
+
timeout?: number | string;
|
|
91
|
+
/** Interval between polls (ms or duration string) (default: 500, min: 10, max: 60000) */
|
|
92
|
+
pollInterval?: number | string;
|
|
69
93
|
/** Filter function to match specific requests */
|
|
70
94
|
match?: (request: Request) => boolean;
|
|
71
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* Options for subscribe() SSE streaming.
|
|
98
|
+
*/
|
|
99
|
+
interface SubscribeOptions {
|
|
100
|
+
/** AbortSignal to cancel the subscription */
|
|
101
|
+
signal?: AbortSignal;
|
|
102
|
+
/** Maximum time to stream (ms or duration string like "30m") */
|
|
103
|
+
timeout?: number | string;
|
|
104
|
+
}
|
|
72
105
|
/** Info passed to the onRequest hook before a request is sent. */
|
|
73
106
|
interface RequestHookInfo {
|
|
74
107
|
method: string;
|
|
@@ -105,11 +138,24 @@ interface ClientOptions {
|
|
|
105
138
|
apiKey: string;
|
|
106
139
|
/** Base URL for the API (default: https://webhooks.cc) */
|
|
107
140
|
baseUrl?: string;
|
|
141
|
+
/** Base URL for sending webhooks (default: https://go.webhooks.cc) */
|
|
142
|
+
webhookUrl?: string;
|
|
108
143
|
/** Request timeout in milliseconds (default: 30000) */
|
|
109
144
|
timeout?: number;
|
|
110
145
|
/** Lifecycle hooks for observability */
|
|
111
146
|
hooks?: ClientHooks;
|
|
112
147
|
}
|
|
148
|
+
/** Description of a single SDK operation. */
|
|
149
|
+
interface OperationDescription {
|
|
150
|
+
description: string;
|
|
151
|
+
params: Record<string, string>;
|
|
152
|
+
}
|
|
153
|
+
/** Self-describing schema returned by client.describe(). */
|
|
154
|
+
interface SDKDescription {
|
|
155
|
+
version: string;
|
|
156
|
+
endpoints: Record<string, OperationDescription>;
|
|
157
|
+
requests: Record<string, OperationDescription>;
|
|
158
|
+
}
|
|
113
159
|
|
|
114
160
|
/**
|
|
115
161
|
* Base error class for all webhooks.cc SDK errors.
|
|
@@ -143,11 +189,13 @@ declare class RateLimitError extends WebhooksCCError {
|
|
|
143
189
|
*
|
|
144
190
|
* @example
|
|
145
191
|
* ```typescript
|
|
192
|
+
* import { WebhooksCC, matchMethod } from '@webhooks-cc/sdk';
|
|
193
|
+
*
|
|
146
194
|
* const client = new WebhooksCC({ apiKey: 'whcc_...' });
|
|
147
195
|
* const endpoint = await client.endpoints.create({ name: 'My Webhook' });
|
|
148
196
|
* const request = await client.requests.waitFor(endpoint.slug, {
|
|
149
|
-
* timeout:
|
|
150
|
-
* match: (
|
|
197
|
+
* timeout: '30s',
|
|
198
|
+
* match: matchMethod('POST'),
|
|
151
199
|
* });
|
|
152
200
|
* ```
|
|
153
201
|
*/
|
|
@@ -166,15 +214,20 @@ declare const ApiError: typeof WebhooksCCError;
|
|
|
166
214
|
declare class WebhooksCC {
|
|
167
215
|
private readonly apiKey;
|
|
168
216
|
private readonly baseUrl;
|
|
217
|
+
private readonly webhookUrl;
|
|
169
218
|
private readonly timeout;
|
|
170
219
|
private readonly hooks;
|
|
171
220
|
constructor(options: ClientOptions);
|
|
172
221
|
private request;
|
|
222
|
+
/** Returns a static description of all SDK operations (no API call). */
|
|
223
|
+
describe(): SDKDescription;
|
|
173
224
|
endpoints: {
|
|
174
225
|
create: (options?: CreateEndpointOptions) => Promise<Endpoint>;
|
|
175
226
|
list: () => Promise<Endpoint[]>;
|
|
176
227
|
get: (slug: string) => Promise<Endpoint>;
|
|
228
|
+
update: (slug: string, options: UpdateEndpointOptions) => Promise<Endpoint>;
|
|
177
229
|
delete: (slug: string) => Promise<void>;
|
|
230
|
+
send: (slug: string, options?: SendOptions) => Promise<Response>;
|
|
178
231
|
};
|
|
179
232
|
requests: {
|
|
180
233
|
list: (endpointSlug: string, options?: ListRequestsOptions) => Promise<Request[]>;
|
|
@@ -192,6 +245,23 @@ declare class WebhooksCC {
|
|
|
192
245
|
* @throws Error if timeout expires or max iterations (10000) reached
|
|
193
246
|
*/
|
|
194
247
|
waitFor: (endpointSlug: string, options?: WaitForOptions) => Promise<Request>;
|
|
248
|
+
/**
|
|
249
|
+
* Replay a captured request to a target URL.
|
|
250
|
+
*
|
|
251
|
+
* Fetches the original request by ID and re-sends it to the specified URL
|
|
252
|
+
* with the original method, headers, and body. Hop-by-hop headers are stripped.
|
|
253
|
+
*/
|
|
254
|
+
replay: (requestId: string, targetUrl: string) => Promise<Response>;
|
|
255
|
+
/**
|
|
256
|
+
* Stream incoming requests via SSE as an async iterator.
|
|
257
|
+
*
|
|
258
|
+
* Connects to the SSE endpoint and yields Request objects as they arrive.
|
|
259
|
+
* The connection is closed when the iterator is broken, the signal is aborted,
|
|
260
|
+
* or the timeout expires.
|
|
261
|
+
*
|
|
262
|
+
* No automatic reconnection — if the connection drops, the iterator ends.
|
|
263
|
+
*/
|
|
264
|
+
subscribe: (slug: string, options?: SubscribeOptions) => AsyncIterable<Request>;
|
|
195
265
|
};
|
|
196
266
|
}
|
|
197
267
|
|
|
@@ -222,5 +292,65 @@ declare function isGitHubWebhook(request: Request): boolean;
|
|
|
222
292
|
* ```
|
|
223
293
|
*/
|
|
224
294
|
declare function matchJsonField(field: string, value: unknown): (request: Request) => boolean;
|
|
295
|
+
/** Check if a request looks like a Shopify webhook. */
|
|
296
|
+
declare function isShopifyWebhook(request: Request): boolean;
|
|
297
|
+
/** Check if a request looks like a Slack webhook. */
|
|
298
|
+
declare function isSlackWebhook(request: Request): boolean;
|
|
299
|
+
/** Check if a request looks like a Twilio webhook. */
|
|
300
|
+
declare function isTwilioWebhook(request: Request): boolean;
|
|
301
|
+
/** Check if a request looks like a Paddle webhook. */
|
|
302
|
+
declare function isPaddleWebhook(request: Request): boolean;
|
|
303
|
+
/** Check if a request looks like a Linear webhook. */
|
|
304
|
+
declare function isLinearWebhook(request: Request): boolean;
|
|
305
|
+
|
|
306
|
+
/** Match requests by HTTP method (case-insensitive). */
|
|
307
|
+
declare function matchMethod(method: string): (request: Request) => boolean;
|
|
308
|
+
/** Match requests that have a specific header, optionally with a specific value. Header names are matched case-insensitively; values are matched with exact (case-sensitive) equality. */
|
|
309
|
+
declare function matchHeader(name: string, value?: string): (request: Request) => boolean;
|
|
310
|
+
/**
|
|
311
|
+
* Match requests by a dot-notation path into the JSON body.
|
|
312
|
+
* Supports array indexing with numeric path segments (e.g., `"items.0.id"`).
|
|
313
|
+
*
|
|
314
|
+
* @example
|
|
315
|
+
* ```ts
|
|
316
|
+
* matchBodyPath("data.object.id", "obj_123")
|
|
317
|
+
* matchBodyPath("type", "checkout.session.completed")
|
|
318
|
+
* matchBodyPath("items.0.name", "Widget")
|
|
319
|
+
* ```
|
|
320
|
+
*/
|
|
321
|
+
declare function matchBodyPath(path: string, value: unknown): (request: Request) => boolean;
|
|
322
|
+
/** Match when ALL matchers return true. Requires at least one matcher. */
|
|
323
|
+
declare function matchAll(first: (request: Request) => boolean, ...rest: Array<(request: Request) => boolean>): (request: Request) => boolean;
|
|
324
|
+
/** Match when ANY matcher returns true. Requires at least one matcher. */
|
|
325
|
+
declare function matchAny(first: (request: Request) => boolean, ...rest: Array<(request: Request) => boolean>): (request: Request) => boolean;
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Parse a duration value into milliseconds.
|
|
329
|
+
*
|
|
330
|
+
* Accepts:
|
|
331
|
+
* - Numbers: passed through as-is (treated as milliseconds)
|
|
332
|
+
* - Numeric strings: `"500"` → 500
|
|
333
|
+
* - Duration strings: `"30s"` → 30000, `"5m"` → 300000, `"1.5s"` → 1500, `"500ms"` → 500
|
|
334
|
+
*
|
|
335
|
+
* @throws {Error} If the input string is not a valid duration format
|
|
336
|
+
*/
|
|
337
|
+
declare function parseDuration(input: number | string): number;
|
|
338
|
+
|
|
339
|
+
/** A parsed SSE frame with event type and data. */
|
|
340
|
+
interface SSEFrame {
|
|
341
|
+
event: string;
|
|
342
|
+
data: string;
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Async generator that parses SSE frames from a ReadableStream.
|
|
346
|
+
*
|
|
347
|
+
* Handles:
|
|
348
|
+
* - Multi-line `data:` fields (joined with newlines)
|
|
349
|
+
* - `event:` type fields
|
|
350
|
+
* - Comment lines (`: ...`) — yielded with event "comment"
|
|
351
|
+
* - Empty data fields
|
|
352
|
+
* - Frames terminated by blank lines
|
|
353
|
+
*/
|
|
354
|
+
declare function parseSSE(stream: ReadableStream<Uint8Array>): AsyncGenerator<SSEFrame, void, undefined>;
|
|
225
355
|
|
|
226
|
-
export { ApiError, type ClientHooks, type ClientOptions, type CreateEndpointOptions, type Endpoint, type ErrorHookInfo, type ListRequestsOptions, NotFoundError, RateLimitError, type Request, type RequestHookInfo, type ResponseHookInfo, TimeoutError, UnauthorizedError, type WaitForOptions, WebhooksCC, WebhooksCCError, isGitHubWebhook, isStripeWebhook, matchJsonField, parseJsonBody };
|
|
356
|
+
export { ApiError, type ClientHooks, type ClientOptions, type CreateEndpointOptions, type Endpoint, type ErrorHookInfo, type ListRequestsOptions, NotFoundError, type OperationDescription, RateLimitError, type Request, type RequestHookInfo, type ResponseHookInfo, type SDKDescription, type SSEFrame, type SendOptions, type SubscribeOptions, TimeoutError, UnauthorizedError, type UpdateEndpointOptions, type WaitForOptions, WebhooksCC, WebhooksCCError, isGitHubWebhook, isLinearWebhook, isPaddleWebhook, isShopifyWebhook, isSlackWebhook, isStripeWebhook, isTwilioWebhook, matchAll, matchAny, matchBodyPath, matchHeader, matchJsonField, matchMethod, parseDuration, parseJsonBody, parseSSE };
|
package/dist/index.d.ts
CHANGED
|
@@ -49,6 +49,30 @@ interface CreateEndpointOptions {
|
|
|
49
49
|
/** Display name for the endpoint */
|
|
50
50
|
name?: string;
|
|
51
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Options for updating an existing endpoint.
|
|
54
|
+
*/
|
|
55
|
+
interface UpdateEndpointOptions {
|
|
56
|
+
/** New display name */
|
|
57
|
+
name?: string;
|
|
58
|
+
/** Mock response config, or null to clear */
|
|
59
|
+
mockResponse?: {
|
|
60
|
+
status: number;
|
|
61
|
+
body: string;
|
|
62
|
+
headers: Record<string, string>;
|
|
63
|
+
} | null;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Options for sending a test webhook to an endpoint.
|
|
67
|
+
*/
|
|
68
|
+
interface SendOptions {
|
|
69
|
+
/** HTTP method (default: "POST") */
|
|
70
|
+
method?: string;
|
|
71
|
+
/** HTTP headers to include */
|
|
72
|
+
headers?: Record<string, string>;
|
|
73
|
+
/** Request body (will be JSON-serialized if not a string) */
|
|
74
|
+
body?: unknown;
|
|
75
|
+
}
|
|
52
76
|
/**
|
|
53
77
|
* Options for listing captured requests.
|
|
54
78
|
*/
|
|
@@ -62,13 +86,22 @@ interface ListRequestsOptions {
|
|
|
62
86
|
* Options for waitFor() polling behavior.
|
|
63
87
|
*/
|
|
64
88
|
interface WaitForOptions {
|
|
65
|
-
/** Maximum time to wait
|
|
66
|
-
timeout?: number;
|
|
67
|
-
/** Interval between polls
|
|
68
|
-
pollInterval?: number;
|
|
89
|
+
/** Maximum time to wait (ms or duration string like "30s", "5m") (default: 30000) */
|
|
90
|
+
timeout?: number | string;
|
|
91
|
+
/** Interval between polls (ms or duration string) (default: 500, min: 10, max: 60000) */
|
|
92
|
+
pollInterval?: number | string;
|
|
69
93
|
/** Filter function to match specific requests */
|
|
70
94
|
match?: (request: Request) => boolean;
|
|
71
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* Options for subscribe() SSE streaming.
|
|
98
|
+
*/
|
|
99
|
+
interface SubscribeOptions {
|
|
100
|
+
/** AbortSignal to cancel the subscription */
|
|
101
|
+
signal?: AbortSignal;
|
|
102
|
+
/** Maximum time to stream (ms or duration string like "30m") */
|
|
103
|
+
timeout?: number | string;
|
|
104
|
+
}
|
|
72
105
|
/** Info passed to the onRequest hook before a request is sent. */
|
|
73
106
|
interface RequestHookInfo {
|
|
74
107
|
method: string;
|
|
@@ -105,11 +138,24 @@ interface ClientOptions {
|
|
|
105
138
|
apiKey: string;
|
|
106
139
|
/** Base URL for the API (default: https://webhooks.cc) */
|
|
107
140
|
baseUrl?: string;
|
|
141
|
+
/** Base URL for sending webhooks (default: https://go.webhooks.cc) */
|
|
142
|
+
webhookUrl?: string;
|
|
108
143
|
/** Request timeout in milliseconds (default: 30000) */
|
|
109
144
|
timeout?: number;
|
|
110
145
|
/** Lifecycle hooks for observability */
|
|
111
146
|
hooks?: ClientHooks;
|
|
112
147
|
}
|
|
148
|
+
/** Description of a single SDK operation. */
|
|
149
|
+
interface OperationDescription {
|
|
150
|
+
description: string;
|
|
151
|
+
params: Record<string, string>;
|
|
152
|
+
}
|
|
153
|
+
/** Self-describing schema returned by client.describe(). */
|
|
154
|
+
interface SDKDescription {
|
|
155
|
+
version: string;
|
|
156
|
+
endpoints: Record<string, OperationDescription>;
|
|
157
|
+
requests: Record<string, OperationDescription>;
|
|
158
|
+
}
|
|
113
159
|
|
|
114
160
|
/**
|
|
115
161
|
* Base error class for all webhooks.cc SDK errors.
|
|
@@ -143,11 +189,13 @@ declare class RateLimitError extends WebhooksCCError {
|
|
|
143
189
|
*
|
|
144
190
|
* @example
|
|
145
191
|
* ```typescript
|
|
192
|
+
* import { WebhooksCC, matchMethod } from '@webhooks-cc/sdk';
|
|
193
|
+
*
|
|
146
194
|
* const client = new WebhooksCC({ apiKey: 'whcc_...' });
|
|
147
195
|
* const endpoint = await client.endpoints.create({ name: 'My Webhook' });
|
|
148
196
|
* const request = await client.requests.waitFor(endpoint.slug, {
|
|
149
|
-
* timeout:
|
|
150
|
-
* match: (
|
|
197
|
+
* timeout: '30s',
|
|
198
|
+
* match: matchMethod('POST'),
|
|
151
199
|
* });
|
|
152
200
|
* ```
|
|
153
201
|
*/
|
|
@@ -166,15 +214,20 @@ declare const ApiError: typeof WebhooksCCError;
|
|
|
166
214
|
declare class WebhooksCC {
|
|
167
215
|
private readonly apiKey;
|
|
168
216
|
private readonly baseUrl;
|
|
217
|
+
private readonly webhookUrl;
|
|
169
218
|
private readonly timeout;
|
|
170
219
|
private readonly hooks;
|
|
171
220
|
constructor(options: ClientOptions);
|
|
172
221
|
private request;
|
|
222
|
+
/** Returns a static description of all SDK operations (no API call). */
|
|
223
|
+
describe(): SDKDescription;
|
|
173
224
|
endpoints: {
|
|
174
225
|
create: (options?: CreateEndpointOptions) => Promise<Endpoint>;
|
|
175
226
|
list: () => Promise<Endpoint[]>;
|
|
176
227
|
get: (slug: string) => Promise<Endpoint>;
|
|
228
|
+
update: (slug: string, options: UpdateEndpointOptions) => Promise<Endpoint>;
|
|
177
229
|
delete: (slug: string) => Promise<void>;
|
|
230
|
+
send: (slug: string, options?: SendOptions) => Promise<Response>;
|
|
178
231
|
};
|
|
179
232
|
requests: {
|
|
180
233
|
list: (endpointSlug: string, options?: ListRequestsOptions) => Promise<Request[]>;
|
|
@@ -192,6 +245,23 @@ declare class WebhooksCC {
|
|
|
192
245
|
* @throws Error if timeout expires or max iterations (10000) reached
|
|
193
246
|
*/
|
|
194
247
|
waitFor: (endpointSlug: string, options?: WaitForOptions) => Promise<Request>;
|
|
248
|
+
/**
|
|
249
|
+
* Replay a captured request to a target URL.
|
|
250
|
+
*
|
|
251
|
+
* Fetches the original request by ID and re-sends it to the specified URL
|
|
252
|
+
* with the original method, headers, and body. Hop-by-hop headers are stripped.
|
|
253
|
+
*/
|
|
254
|
+
replay: (requestId: string, targetUrl: string) => Promise<Response>;
|
|
255
|
+
/**
|
|
256
|
+
* Stream incoming requests via SSE as an async iterator.
|
|
257
|
+
*
|
|
258
|
+
* Connects to the SSE endpoint and yields Request objects as they arrive.
|
|
259
|
+
* The connection is closed when the iterator is broken, the signal is aborted,
|
|
260
|
+
* or the timeout expires.
|
|
261
|
+
*
|
|
262
|
+
* No automatic reconnection — if the connection drops, the iterator ends.
|
|
263
|
+
*/
|
|
264
|
+
subscribe: (slug: string, options?: SubscribeOptions) => AsyncIterable<Request>;
|
|
195
265
|
};
|
|
196
266
|
}
|
|
197
267
|
|
|
@@ -222,5 +292,65 @@ declare function isGitHubWebhook(request: Request): boolean;
|
|
|
222
292
|
* ```
|
|
223
293
|
*/
|
|
224
294
|
declare function matchJsonField(field: string, value: unknown): (request: Request) => boolean;
|
|
295
|
+
/** Check if a request looks like a Shopify webhook. */
|
|
296
|
+
declare function isShopifyWebhook(request: Request): boolean;
|
|
297
|
+
/** Check if a request looks like a Slack webhook. */
|
|
298
|
+
declare function isSlackWebhook(request: Request): boolean;
|
|
299
|
+
/** Check if a request looks like a Twilio webhook. */
|
|
300
|
+
declare function isTwilioWebhook(request: Request): boolean;
|
|
301
|
+
/** Check if a request looks like a Paddle webhook. */
|
|
302
|
+
declare function isPaddleWebhook(request: Request): boolean;
|
|
303
|
+
/** Check if a request looks like a Linear webhook. */
|
|
304
|
+
declare function isLinearWebhook(request: Request): boolean;
|
|
305
|
+
|
|
306
|
+
/** Match requests by HTTP method (case-insensitive). */
|
|
307
|
+
declare function matchMethod(method: string): (request: Request) => boolean;
|
|
308
|
+
/** Match requests that have a specific header, optionally with a specific value. Header names are matched case-insensitively; values are matched with exact (case-sensitive) equality. */
|
|
309
|
+
declare function matchHeader(name: string, value?: string): (request: Request) => boolean;
|
|
310
|
+
/**
|
|
311
|
+
* Match requests by a dot-notation path into the JSON body.
|
|
312
|
+
* Supports array indexing with numeric path segments (e.g., `"items.0.id"`).
|
|
313
|
+
*
|
|
314
|
+
* @example
|
|
315
|
+
* ```ts
|
|
316
|
+
* matchBodyPath("data.object.id", "obj_123")
|
|
317
|
+
* matchBodyPath("type", "checkout.session.completed")
|
|
318
|
+
* matchBodyPath("items.0.name", "Widget")
|
|
319
|
+
* ```
|
|
320
|
+
*/
|
|
321
|
+
declare function matchBodyPath(path: string, value: unknown): (request: Request) => boolean;
|
|
322
|
+
/** Match when ALL matchers return true. Requires at least one matcher. */
|
|
323
|
+
declare function matchAll(first: (request: Request) => boolean, ...rest: Array<(request: Request) => boolean>): (request: Request) => boolean;
|
|
324
|
+
/** Match when ANY matcher returns true. Requires at least one matcher. */
|
|
325
|
+
declare function matchAny(first: (request: Request) => boolean, ...rest: Array<(request: Request) => boolean>): (request: Request) => boolean;
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Parse a duration value into milliseconds.
|
|
329
|
+
*
|
|
330
|
+
* Accepts:
|
|
331
|
+
* - Numbers: passed through as-is (treated as milliseconds)
|
|
332
|
+
* - Numeric strings: `"500"` → 500
|
|
333
|
+
* - Duration strings: `"30s"` → 30000, `"5m"` → 300000, `"1.5s"` → 1500, `"500ms"` → 500
|
|
334
|
+
*
|
|
335
|
+
* @throws {Error} If the input string is not a valid duration format
|
|
336
|
+
*/
|
|
337
|
+
declare function parseDuration(input: number | string): number;
|
|
338
|
+
|
|
339
|
+
/** A parsed SSE frame with event type and data. */
|
|
340
|
+
interface SSEFrame {
|
|
341
|
+
event: string;
|
|
342
|
+
data: string;
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Async generator that parses SSE frames from a ReadableStream.
|
|
346
|
+
*
|
|
347
|
+
* Handles:
|
|
348
|
+
* - Multi-line `data:` fields (joined with newlines)
|
|
349
|
+
* - `event:` type fields
|
|
350
|
+
* - Comment lines (`: ...`) — yielded with event "comment"
|
|
351
|
+
* - Empty data fields
|
|
352
|
+
* - Frames terminated by blank lines
|
|
353
|
+
*/
|
|
354
|
+
declare function parseSSE(stream: ReadableStream<Uint8Array>): AsyncGenerator<SSEFrame, void, undefined>;
|
|
225
355
|
|
|
226
|
-
export { ApiError, type ClientHooks, type ClientOptions, type CreateEndpointOptions, type Endpoint, type ErrorHookInfo, type ListRequestsOptions, NotFoundError, RateLimitError, type Request, type RequestHookInfo, type ResponseHookInfo, TimeoutError, UnauthorizedError, type WaitForOptions, WebhooksCC, WebhooksCCError, isGitHubWebhook, isStripeWebhook, matchJsonField, parseJsonBody };
|
|
356
|
+
export { ApiError, type ClientHooks, type ClientOptions, type CreateEndpointOptions, type Endpoint, type ErrorHookInfo, type ListRequestsOptions, NotFoundError, type OperationDescription, RateLimitError, type Request, type RequestHookInfo, type ResponseHookInfo, type SDKDescription, type SSEFrame, type SendOptions, type SubscribeOptions, TimeoutError, UnauthorizedError, type UpdateEndpointOptions, type WaitForOptions, WebhooksCC, WebhooksCCError, isGitHubWebhook, isLinearWebhook, isPaddleWebhook, isShopifyWebhook, isSlackWebhook, isStripeWebhook, isTwilioWebhook, matchAll, matchAny, matchBodyPath, matchHeader, matchJsonField, matchMethod, parseDuration, parseJsonBody, parseSSE };
|