@dubsdotapp/node 0.1.2 → 0.2.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 +141 -9
- package/dist/index.d.mts +275 -1
- package/dist/index.d.ts +275 -1
- package/dist/index.js +164 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +157 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +31 -1
- package/src/index.ts +65 -0
- package/src/resources/auth.ts +40 -0
- package/src/resources/base.ts +14 -0
- package/src/resources/events.ts +35 -0
- package/src/resources/games.ts +71 -0
- package/src/resources/index.ts +8 -0
- package/src/resources/transactions.ts +8 -0
- package/src/resources/ufc.ts +8 -0
- package/src/resources/users.ts +16 -0
- package/src/types.ts +270 -0
package/README.md
CHANGED
|
@@ -15,12 +15,144 @@ import { Dubs } from '@dubsdotapp/node';
|
|
|
15
15
|
|
|
16
16
|
const dubs = new Dubs({
|
|
17
17
|
apiKey: process.env.DUBS_API_KEY!,
|
|
18
|
-
resolutionSecret: process.env.DUBS_RESOLUTION_SECRET!,
|
|
18
|
+
resolutionSecret: process.env.DUBS_RESOLUTION_SECRET!, // optional, only needed for resolveGame
|
|
19
19
|
network: 'mainnet-beta', // or 'devnet'
|
|
20
20
|
});
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
##
|
|
23
|
+
## API Reference
|
|
24
|
+
|
|
25
|
+
### Auth
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
// Get a nonce for wallet authentication
|
|
29
|
+
const { nonce } = await dubs.auth.getNonce('walletAddress');
|
|
30
|
+
|
|
31
|
+
// Authenticate with a signed nonce
|
|
32
|
+
const { token, user } = await dubs.auth.authenticate({
|
|
33
|
+
walletAddress: '...',
|
|
34
|
+
signature: '...',
|
|
35
|
+
nonce: '...',
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Register a new user
|
|
39
|
+
const { token, user } = await dubs.auth.register({
|
|
40
|
+
walletAddress: '...',
|
|
41
|
+
signature: '...',
|
|
42
|
+
nonce: '...',
|
|
43
|
+
username: 'player1',
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Get the current user (requires user token)
|
|
47
|
+
const { user } = await dubs.auth.me(userToken);
|
|
48
|
+
|
|
49
|
+
// Logout
|
|
50
|
+
await dubs.auth.logout(userToken);
|
|
51
|
+
|
|
52
|
+
// Check username availability
|
|
53
|
+
const { available } = await dubs.auth.checkUsername('player1');
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Users
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
// Get a user by wallet address
|
|
60
|
+
const { user } = await dubs.users.get('walletAddress');
|
|
61
|
+
|
|
62
|
+
// List users
|
|
63
|
+
const { users, total } = await dubs.users.list({ limit: 20, offset: 0 });
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Events
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
// Get upcoming events
|
|
70
|
+
const { events } = await dubs.events.upcoming({ type: 'sports', page: 1 });
|
|
71
|
+
|
|
72
|
+
// Get sports events by league
|
|
73
|
+
const { events } = await dubs.events.sports('NBA');
|
|
74
|
+
|
|
75
|
+
// Get esports events
|
|
76
|
+
const { events } = await dubs.events.esports({ videogame: 'cs-go' });
|
|
77
|
+
|
|
78
|
+
// Get a specific esports match
|
|
79
|
+
const { match } = await dubs.events.esportsMatch('matchId');
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Games
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
// Validate an event for game creation
|
|
86
|
+
const { valid, event } = await dubs.games.validate('eventId');
|
|
87
|
+
|
|
88
|
+
// Create a game
|
|
89
|
+
const { game, transaction } = await dubs.games.create({
|
|
90
|
+
id: 'unique-game-id',
|
|
91
|
+
playerWallet: '...',
|
|
92
|
+
teamChoice: 'home',
|
|
93
|
+
wagerAmount: 1.5,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Join a game
|
|
97
|
+
const { game, transaction } = await dubs.games.join({
|
|
98
|
+
playerWallet: '...',
|
|
99
|
+
gameId: 'game-id',
|
|
100
|
+
teamChoice: 'away',
|
|
101
|
+
amount: 1.5,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Confirm a game (after signing the transaction)
|
|
105
|
+
const { game } = await dubs.games.confirm({
|
|
106
|
+
gameId: 'game-id',
|
|
107
|
+
playerWallet: '...',
|
|
108
|
+
signature: '...',
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// Get a game by ID
|
|
112
|
+
const { game } = await dubs.games.get('game-id');
|
|
113
|
+
|
|
114
|
+
// List games
|
|
115
|
+
const { games, total } = await dubs.games.list({ status: 'open', limit: 20 });
|
|
116
|
+
|
|
117
|
+
// List network-wide games
|
|
118
|
+
const { games } = await dubs.games.network({ limit: 20 });
|
|
119
|
+
|
|
120
|
+
// Get live score for a game
|
|
121
|
+
const score = await dubs.games.liveScore('game-id');
|
|
122
|
+
|
|
123
|
+
// Create a custom game
|
|
124
|
+
const { game, transaction } = await dubs.games.customCreate({
|
|
125
|
+
playerWallet: '...',
|
|
126
|
+
teamChoice: 'home',
|
|
127
|
+
wagerAmount: 2.0,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// Confirm a custom game
|
|
131
|
+
const { game } = await dubs.games.customConfirm({
|
|
132
|
+
gameId: 'game-id',
|
|
133
|
+
playerWallet: '...',
|
|
134
|
+
signature: '...',
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Transactions
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
// Build a claim transaction
|
|
142
|
+
const { transaction } = await dubs.transactions.buildClaim({
|
|
143
|
+
playerWallet: '...',
|
|
144
|
+
gameId: 'game-id',
|
|
145
|
+
});
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Config
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
// Get platform configuration
|
|
152
|
+
const config = await dubs.config();
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Resolve a Game
|
|
24
156
|
|
|
25
157
|
Resolve a custom game (game_mode=6) by declaring a winner. The SDK automatically computes the HMAC-SHA256 signature using your resolution secret.
|
|
26
158
|
|
|
@@ -40,7 +172,7 @@ console.log(result);
|
|
|
40
172
|
// }
|
|
41
173
|
```
|
|
42
174
|
|
|
43
|
-
|
|
175
|
+
### Verify Webhooks
|
|
44
176
|
|
|
45
177
|
Verify incoming webhook requests from Dubs using your webhook secret.
|
|
46
178
|
|
|
@@ -64,12 +196,12 @@ app.post('/webhooks/dubs', (req, res) => {
|
|
|
64
196
|
|
|
65
197
|
## Configuration
|
|
66
198
|
|
|
67
|
-
| Option | Type
|
|
68
|
-
| ------------------ |
|
|
69
|
-
| `apiKey` | `string`
|
|
70
|
-
| `resolutionSecret` | `string?`
|
|
71
|
-
| `network` | `string?`
|
|
72
|
-
| `baseUrl` | `string?`
|
|
199
|
+
| Option | Type | Description |
|
|
200
|
+
| ------------------ | ---------- | -------------------------------------------------------- |
|
|
201
|
+
| `apiKey` | `string` | Your Dubs API key (from the developer portal) |
|
|
202
|
+
| `resolutionSecret` | `string?` | Secret for signing `resolveGame` requests |
|
|
203
|
+
| `network` | `string?` | `'mainnet-beta'` or `'devnet'` — sets the base URL |
|
|
204
|
+
| `baseUrl` | `string?` | Explicit base URL (overrides `network` if both provided) |
|
|
73
205
|
|
|
74
206
|
## Error Handling
|
|
75
207
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
type RequestFn = <T>(method: string, path: string, body?: string, extraHeaders?: Record<string, string>) => Promise<T>;
|
|
2
|
+
declare class BaseResource {
|
|
3
|
+
protected request: RequestFn;
|
|
4
|
+
constructor(request: RequestFn);
|
|
5
|
+
}
|
|
6
|
+
|
|
1
7
|
declare const DEFAULT_BASE_URL = "https://dubs-server-prod-9c91d3f01199.herokuapp.com/api/developer/v1";
|
|
2
8
|
type DubsNetwork = 'devnet' | 'mainnet-beta';
|
|
3
9
|
declare const NETWORK_CONFIG: Record<DubsNetwork, {
|
|
@@ -36,12 +42,280 @@ interface WebhookEvent {
|
|
|
36
42
|
timestamp: string;
|
|
37
43
|
data: Record<string, unknown>;
|
|
38
44
|
}
|
|
45
|
+
interface User {
|
|
46
|
+
walletAddress: string;
|
|
47
|
+
username: string;
|
|
48
|
+
avatar?: string;
|
|
49
|
+
createdAt: string;
|
|
50
|
+
updatedAt: string;
|
|
51
|
+
[key: string]: unknown;
|
|
52
|
+
}
|
|
53
|
+
interface Game {
|
|
54
|
+
id: string;
|
|
55
|
+
status: string;
|
|
56
|
+
gameMode: number;
|
|
57
|
+
eventId?: string;
|
|
58
|
+
wagerAmount: number;
|
|
59
|
+
players: Record<string, unknown>[];
|
|
60
|
+
winner?: string | null;
|
|
61
|
+
createdAt: string;
|
|
62
|
+
updatedAt: string;
|
|
63
|
+
[key: string]: unknown;
|
|
64
|
+
}
|
|
65
|
+
interface Event {
|
|
66
|
+
id: string;
|
|
67
|
+
name: string;
|
|
68
|
+
type: string;
|
|
69
|
+
league?: string;
|
|
70
|
+
startTime: string;
|
|
71
|
+
status: string;
|
|
72
|
+
teams: {
|
|
73
|
+
home: string;
|
|
74
|
+
away: string;
|
|
75
|
+
};
|
|
76
|
+
[key: string]: unknown;
|
|
77
|
+
}
|
|
78
|
+
interface NonceParams {
|
|
79
|
+
walletAddress: string;
|
|
80
|
+
}
|
|
81
|
+
interface NonceResponse {
|
|
82
|
+
nonce: string;
|
|
83
|
+
}
|
|
84
|
+
interface AuthenticateParams {
|
|
85
|
+
walletAddress: string;
|
|
86
|
+
signature: string;
|
|
87
|
+
nonce: string;
|
|
88
|
+
}
|
|
89
|
+
interface AuthenticateResponse {
|
|
90
|
+
token: string;
|
|
91
|
+
user: User;
|
|
92
|
+
}
|
|
93
|
+
interface RegisterParams {
|
|
94
|
+
walletAddress: string;
|
|
95
|
+
signature: string;
|
|
96
|
+
nonce: string;
|
|
97
|
+
username: string;
|
|
98
|
+
}
|
|
99
|
+
interface RegisterResponse {
|
|
100
|
+
token: string;
|
|
101
|
+
user: User;
|
|
102
|
+
}
|
|
103
|
+
interface MeResponse {
|
|
104
|
+
user: User;
|
|
105
|
+
}
|
|
106
|
+
interface CheckUsernameResponse {
|
|
107
|
+
available: boolean;
|
|
108
|
+
username: string;
|
|
109
|
+
}
|
|
110
|
+
interface UserResponse {
|
|
111
|
+
user: User;
|
|
112
|
+
}
|
|
113
|
+
interface UsersListParams {
|
|
114
|
+
limit?: number;
|
|
115
|
+
offset?: number;
|
|
116
|
+
}
|
|
117
|
+
interface UsersListResponse {
|
|
118
|
+
users: User[];
|
|
119
|
+
total: number;
|
|
120
|
+
}
|
|
121
|
+
interface UpcomingEventsParams {
|
|
122
|
+
type?: string;
|
|
123
|
+
page?: number;
|
|
124
|
+
limit?: number;
|
|
125
|
+
}
|
|
126
|
+
interface EventsResponse {
|
|
127
|
+
events: Event[];
|
|
128
|
+
total?: number;
|
|
129
|
+
}
|
|
130
|
+
interface EsportsParams {
|
|
131
|
+
videogame?: string;
|
|
132
|
+
page?: number;
|
|
133
|
+
limit?: number;
|
|
134
|
+
}
|
|
135
|
+
interface EsportsMatchResponse {
|
|
136
|
+
match: Record<string, unknown>;
|
|
137
|
+
}
|
|
138
|
+
interface ValidateParams {
|
|
139
|
+
eventId: string;
|
|
140
|
+
}
|
|
141
|
+
interface ValidateResponse {
|
|
142
|
+
valid: boolean;
|
|
143
|
+
event: Event;
|
|
144
|
+
}
|
|
145
|
+
interface GameCreateParams {
|
|
146
|
+
id: string;
|
|
147
|
+
playerWallet: string;
|
|
148
|
+
teamChoice: string;
|
|
149
|
+
wagerAmount: number;
|
|
150
|
+
}
|
|
151
|
+
interface GameCreateResponse {
|
|
152
|
+
game: Game;
|
|
153
|
+
transaction: string;
|
|
154
|
+
}
|
|
155
|
+
interface GameJoinParams {
|
|
156
|
+
playerWallet: string;
|
|
157
|
+
gameId: string;
|
|
158
|
+
teamChoice: string;
|
|
159
|
+
amount: number;
|
|
160
|
+
}
|
|
161
|
+
interface GameJoinResponse {
|
|
162
|
+
game: Game;
|
|
163
|
+
transaction: string;
|
|
164
|
+
}
|
|
165
|
+
interface GameConfirmParams {
|
|
166
|
+
gameId: string;
|
|
167
|
+
playerWallet: string;
|
|
168
|
+
signature: string;
|
|
169
|
+
[key: string]: unknown;
|
|
170
|
+
}
|
|
171
|
+
interface GameConfirmResponse {
|
|
172
|
+
game: Game;
|
|
173
|
+
}
|
|
174
|
+
interface GameListParams {
|
|
175
|
+
status?: string;
|
|
176
|
+
limit?: number;
|
|
177
|
+
offset?: number;
|
|
178
|
+
}
|
|
179
|
+
interface GameListResponse {
|
|
180
|
+
games: Game[];
|
|
181
|
+
total: number;
|
|
182
|
+
}
|
|
183
|
+
interface NetworkGamesParams {
|
|
184
|
+
limit?: number;
|
|
185
|
+
offset?: number;
|
|
186
|
+
}
|
|
187
|
+
interface NetworkGamesResponse {
|
|
188
|
+
games: Game[];
|
|
189
|
+
total: number;
|
|
190
|
+
}
|
|
191
|
+
interface LiveScoreResponse {
|
|
192
|
+
gameId: string;
|
|
193
|
+
score: Record<string, unknown>;
|
|
194
|
+
status: string;
|
|
195
|
+
[key: string]: unknown;
|
|
196
|
+
}
|
|
197
|
+
interface CustomGameCreateParams {
|
|
198
|
+
playerWallet: string;
|
|
199
|
+
teamChoice: string;
|
|
200
|
+
wagerAmount: number;
|
|
201
|
+
[key: string]: unknown;
|
|
202
|
+
}
|
|
203
|
+
interface CustomGameCreateResponse {
|
|
204
|
+
game: Game;
|
|
205
|
+
transaction: string;
|
|
206
|
+
}
|
|
207
|
+
interface CustomGameConfirmParams {
|
|
208
|
+
gameId: string;
|
|
209
|
+
playerWallet: string;
|
|
210
|
+
signature: string;
|
|
211
|
+
[key: string]: unknown;
|
|
212
|
+
}
|
|
213
|
+
interface CustomGameConfirmResponse {
|
|
214
|
+
game: Game;
|
|
215
|
+
}
|
|
216
|
+
interface BuildClaimParams {
|
|
217
|
+
playerWallet: string;
|
|
218
|
+
gameId: string;
|
|
219
|
+
}
|
|
220
|
+
interface BuildClaimResponse {
|
|
221
|
+
transaction: string;
|
|
222
|
+
[key: string]: unknown;
|
|
223
|
+
}
|
|
224
|
+
interface AppConfigResponse {
|
|
225
|
+
[key: string]: unknown;
|
|
226
|
+
}
|
|
227
|
+
interface UFCFighter {
|
|
228
|
+
name: string;
|
|
229
|
+
imageUrl: string | null;
|
|
230
|
+
abbreviation: string;
|
|
231
|
+
record: string | null;
|
|
232
|
+
winner: boolean;
|
|
233
|
+
}
|
|
234
|
+
interface UFCData {
|
|
235
|
+
currentRound: number;
|
|
236
|
+
totalRounds: number;
|
|
237
|
+
clock: string;
|
|
238
|
+
fightState: 'pre' | 'in' | 'post';
|
|
239
|
+
statusDetail: string;
|
|
240
|
+
statusShortDetail?: string;
|
|
241
|
+
}
|
|
242
|
+
interface UFCFight {
|
|
243
|
+
id: string | null;
|
|
244
|
+
home: UFCFighter;
|
|
245
|
+
away: UFCFighter;
|
|
246
|
+
weightClass: string | null;
|
|
247
|
+
status: string;
|
|
248
|
+
ufcData: UFCData | null;
|
|
249
|
+
}
|
|
250
|
+
interface UFCEvent {
|
|
251
|
+
eventName: string;
|
|
252
|
+
date: string | null;
|
|
253
|
+
fights: UFCFight[];
|
|
254
|
+
}
|
|
255
|
+
interface UFCFightCardResponse {
|
|
256
|
+
success: boolean;
|
|
257
|
+
events: UFCEvent[];
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
declare class AuthResource extends BaseResource {
|
|
261
|
+
getNonce(walletAddress: string): Promise<NonceResponse>;
|
|
262
|
+
authenticate(params: AuthenticateParams): Promise<AuthenticateResponse>;
|
|
263
|
+
register(params: RegisterParams): Promise<RegisterResponse>;
|
|
264
|
+
me(userToken: string): Promise<MeResponse>;
|
|
265
|
+
logout(userToken: string): Promise<void>;
|
|
266
|
+
checkUsername(username: string): Promise<CheckUsernameResponse>;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
declare class UsersResource extends BaseResource {
|
|
270
|
+
get(walletAddress: string): Promise<UserResponse>;
|
|
271
|
+
list(params?: UsersListParams): Promise<UsersListResponse>;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
declare class EventsResource extends BaseResource {
|
|
275
|
+
upcoming(params?: UpcomingEventsParams): Promise<EventsResponse>;
|
|
276
|
+
sports(league: string): Promise<EventsResponse>;
|
|
277
|
+
esports(params?: EsportsParams): Promise<EventsResponse>;
|
|
278
|
+
esportsMatch(matchId: string): Promise<EsportsMatchResponse>;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
declare class GamesResource extends BaseResource {
|
|
282
|
+
validate(eventId: string): Promise<ValidateResponse>;
|
|
283
|
+
create(params: GameCreateParams): Promise<GameCreateResponse>;
|
|
284
|
+
join(params: GameJoinParams): Promise<GameJoinResponse>;
|
|
285
|
+
confirm(params: GameConfirmParams): Promise<GameConfirmResponse>;
|
|
286
|
+
get(gameId: string): Promise<{
|
|
287
|
+
game: Game;
|
|
288
|
+
}>;
|
|
289
|
+
list(params?: GameListParams): Promise<GameListResponse>;
|
|
290
|
+
network(params?: NetworkGamesParams): Promise<NetworkGamesResponse>;
|
|
291
|
+
liveScore(gameId: string): Promise<LiveScoreResponse>;
|
|
292
|
+
customCreate(params: CustomGameCreateParams): Promise<CustomGameCreateResponse>;
|
|
293
|
+
customConfirm(params: CustomGameConfirmParams): Promise<CustomGameConfirmResponse>;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
declare class TransactionsResource extends BaseResource {
|
|
297
|
+
buildClaim(params: BuildClaimParams): Promise<BuildClaimResponse>;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
declare class UFCResource extends BaseResource {
|
|
301
|
+
fightCard(): Promise<UFCFightCardResponse>;
|
|
302
|
+
}
|
|
39
303
|
|
|
40
304
|
declare class Dubs {
|
|
41
305
|
private readonly apiKey;
|
|
42
306
|
private readonly resolutionSecret?;
|
|
43
307
|
private readonly baseUrl;
|
|
308
|
+
readonly auth: AuthResource;
|
|
309
|
+
readonly users: UsersResource;
|
|
310
|
+
readonly events: EventsResource;
|
|
311
|
+
readonly games: GamesResource;
|
|
312
|
+
readonly transactions: TransactionsResource;
|
|
313
|
+
readonly ufc: UFCResource;
|
|
44
314
|
constructor(config: DubsConfig);
|
|
315
|
+
/**
|
|
316
|
+
* Fetch the platform configuration.
|
|
317
|
+
*/
|
|
318
|
+
config(): Promise<AppConfigResponse>;
|
|
45
319
|
/**
|
|
46
320
|
* Resolve a custom game (game_mode=6).
|
|
47
321
|
*
|
|
@@ -67,4 +341,4 @@ declare class DubsApiError extends Error {
|
|
|
67
341
|
constructor(code: string, message: string, httpStatus: number);
|
|
68
342
|
}
|
|
69
343
|
|
|
70
|
-
export { DEFAULT_BASE_URL, Dubs, DubsApiError, type DubsConfig, type DubsNetwork, NETWORK_CONFIG, type ResolveGameParams, type ResolveGameResult, type WebhookEvent };
|
|
344
|
+
export { type AppConfigResponse, AuthResource, type AuthenticateParams, type AuthenticateResponse, type BuildClaimParams, type BuildClaimResponse, type CheckUsernameResponse, type CustomGameConfirmParams, type CustomGameConfirmResponse, type CustomGameCreateParams, type CustomGameCreateResponse, DEFAULT_BASE_URL, Dubs, DubsApiError, type DubsConfig, type DubsNetwork, type EsportsMatchResponse, type EsportsParams, type Event, EventsResource, type EventsResponse, type Game, type GameConfirmParams, type GameConfirmResponse, type GameCreateParams, type GameCreateResponse, type GameJoinParams, type GameJoinResponse, type GameListParams, type GameListResponse, GamesResource, type LiveScoreResponse, type MeResponse, NETWORK_CONFIG, type NetworkGamesParams, type NetworkGamesResponse, type NonceParams, type NonceResponse, type RegisterParams, type RegisterResponse, type RequestFn, type ResolveGameParams, type ResolveGameResult, TransactionsResource, type UFCData, type UFCEvent, type UFCFight, type UFCFightCardResponse, type UFCFighter, UFCResource, type UpcomingEventsParams, type User, type UserResponse, type UsersListParams, type UsersListResponse, UsersResource, type ValidateParams, type ValidateResponse, type WebhookEvent };
|