@messagebird/sdk 0.1.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/dist/index.js ADDED
@@ -0,0 +1,1608 @@
1
+ import { Webhook } from 'standardwebhooks';
2
+
3
+ // src/generated/core/bodySerializer.gen.ts
4
+ var jsonBodySerializer = {
5
+ bodySerializer: (body) => JSON.stringify(
6
+ body,
7
+ (_key, value) => typeof value === "bigint" ? value.toString() : value
8
+ )
9
+ };
10
+
11
+ // src/generated/core/serverSentEvents.gen.ts
12
+ function createSseClient({
13
+ onRequest,
14
+ onSseError,
15
+ onSseEvent,
16
+ responseTransformer,
17
+ responseValidator,
18
+ sseDefaultRetryDelay,
19
+ sseMaxRetryAttempts,
20
+ sseMaxRetryDelay,
21
+ sseSleepFn,
22
+ url,
23
+ ...options
24
+ }) {
25
+ let lastEventId;
26
+ const sleep2 = sseSleepFn ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
27
+ const createStream = async function* () {
28
+ let retryDelay2 = sseDefaultRetryDelay ?? 3e3;
29
+ let attempt = 0;
30
+ const signal = options.signal ?? new AbortController().signal;
31
+ while (true) {
32
+ if (signal.aborted) break;
33
+ attempt++;
34
+ const headers = options.headers instanceof Headers ? options.headers : new Headers(options.headers);
35
+ if (lastEventId !== void 0) {
36
+ headers.set("Last-Event-ID", lastEventId);
37
+ }
38
+ try {
39
+ const requestInit = {
40
+ redirect: "follow",
41
+ ...options,
42
+ body: options.serializedBody,
43
+ headers,
44
+ signal
45
+ };
46
+ let request = new Request(url, requestInit);
47
+ if (onRequest) {
48
+ request = await onRequest(url, requestInit);
49
+ }
50
+ const _fetch = options.fetch ?? globalThis.fetch;
51
+ const response = await _fetch(request);
52
+ if (!response.ok)
53
+ throw new Error(
54
+ `SSE failed: ${response.status} ${response.statusText}`
55
+ );
56
+ if (!response.body) throw new Error("No body in SSE response");
57
+ const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
58
+ let buffer = "";
59
+ const abortHandler = () => {
60
+ try {
61
+ reader.cancel();
62
+ } catch {
63
+ }
64
+ };
65
+ signal.addEventListener("abort", abortHandler);
66
+ try {
67
+ while (true) {
68
+ const { done, value } = await reader.read();
69
+ if (done) break;
70
+ buffer += value;
71
+ buffer = buffer.replace(/\r\n?/g, "\n");
72
+ const chunks = buffer.split("\n\n");
73
+ buffer = chunks.pop() ?? "";
74
+ for (const chunk of chunks) {
75
+ const lines = chunk.split("\n");
76
+ const dataLines = [];
77
+ let eventName;
78
+ for (const line of lines) {
79
+ if (line.startsWith("data:")) {
80
+ dataLines.push(line.replace(/^data:\s*/, ""));
81
+ } else if (line.startsWith("event:")) {
82
+ eventName = line.replace(/^event:\s*/, "");
83
+ } else if (line.startsWith("id:")) {
84
+ lastEventId = line.replace(/^id:\s*/, "");
85
+ } else if (line.startsWith("retry:")) {
86
+ const parsed = Number.parseInt(
87
+ line.replace(/^retry:\s*/, ""),
88
+ 10
89
+ );
90
+ if (!Number.isNaN(parsed)) {
91
+ retryDelay2 = parsed;
92
+ }
93
+ }
94
+ }
95
+ let data;
96
+ let parsedJson = false;
97
+ if (dataLines.length) {
98
+ const rawData = dataLines.join("\n");
99
+ try {
100
+ data = JSON.parse(rawData);
101
+ parsedJson = true;
102
+ } catch {
103
+ data = rawData;
104
+ }
105
+ }
106
+ if (parsedJson) {
107
+ if (responseValidator) {
108
+ await responseValidator(data);
109
+ }
110
+ if (responseTransformer) {
111
+ data = await responseTransformer(data);
112
+ }
113
+ }
114
+ onSseEvent?.({
115
+ data,
116
+ event: eventName,
117
+ id: lastEventId,
118
+ retry: retryDelay2
119
+ });
120
+ if (dataLines.length) {
121
+ yield data;
122
+ }
123
+ }
124
+ }
125
+ } finally {
126
+ signal.removeEventListener("abort", abortHandler);
127
+ reader.releaseLock();
128
+ }
129
+ break;
130
+ } catch (error) {
131
+ onSseError?.(error);
132
+ if (sseMaxRetryAttempts !== void 0 && attempt >= sseMaxRetryAttempts) {
133
+ break;
134
+ }
135
+ const backoff = Math.min(
136
+ retryDelay2 * 2 ** (attempt - 1),
137
+ sseMaxRetryDelay ?? 3e4
138
+ );
139
+ await sleep2(backoff);
140
+ }
141
+ }
142
+ };
143
+ const stream = createStream();
144
+ return { stream };
145
+ }
146
+
147
+ // src/generated/core/pathSerializer.gen.ts
148
+ var separatorArrayExplode = (style) => {
149
+ switch (style) {
150
+ case "label":
151
+ return ".";
152
+ case "matrix":
153
+ return ";";
154
+ case "simple":
155
+ return ",";
156
+ default:
157
+ return "&";
158
+ }
159
+ };
160
+ var separatorArrayNoExplode = (style) => {
161
+ switch (style) {
162
+ case "form":
163
+ return ",";
164
+ case "pipeDelimited":
165
+ return "|";
166
+ case "spaceDelimited":
167
+ return "%20";
168
+ default:
169
+ return ",";
170
+ }
171
+ };
172
+ var separatorObjectExplode = (style) => {
173
+ switch (style) {
174
+ case "label":
175
+ return ".";
176
+ case "matrix":
177
+ return ";";
178
+ case "simple":
179
+ return ",";
180
+ default:
181
+ return "&";
182
+ }
183
+ };
184
+ var serializeArrayParam = ({
185
+ allowReserved,
186
+ explode,
187
+ name,
188
+ style,
189
+ value
190
+ }) => {
191
+ if (!explode) {
192
+ const joinedValues2 = (allowReserved ? value : value.map((v) => encodeURIComponent(v))).join(separatorArrayNoExplode(style));
193
+ switch (style) {
194
+ case "label":
195
+ return `.${joinedValues2}`;
196
+ case "matrix":
197
+ return `;${name}=${joinedValues2}`;
198
+ case "simple":
199
+ return joinedValues2;
200
+ default:
201
+ return `${name}=${joinedValues2}`;
202
+ }
203
+ }
204
+ const separator = separatorArrayExplode(style);
205
+ const joinedValues = value.map((v) => {
206
+ if (style === "label" || style === "simple") {
207
+ return allowReserved ? v : encodeURIComponent(v);
208
+ }
209
+ return serializePrimitiveParam({
210
+ allowReserved,
211
+ name,
212
+ value: v
213
+ });
214
+ }).join(separator);
215
+ return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues;
216
+ };
217
+ var serializePrimitiveParam = ({
218
+ allowReserved,
219
+ name,
220
+ value
221
+ }) => {
222
+ if (value === void 0 || value === null) {
223
+ return "";
224
+ }
225
+ if (typeof value === "object") {
226
+ throw new Error(
227
+ "Deeply-nested arrays/objects aren\u2019t supported. Provide your own `querySerializer()` to handle these."
228
+ );
229
+ }
230
+ return `${name}=${allowReserved ? value : encodeURIComponent(value)}`;
231
+ };
232
+ var serializeObjectParam = ({
233
+ allowReserved,
234
+ explode,
235
+ name,
236
+ style,
237
+ value,
238
+ valueOnly
239
+ }) => {
240
+ if (value instanceof Date) {
241
+ return valueOnly ? value.toISOString() : `${name}=${value.toISOString()}`;
242
+ }
243
+ if (style !== "deepObject" && !explode) {
244
+ let values = [];
245
+ Object.entries(value).forEach(([key, v]) => {
246
+ values = [
247
+ ...values,
248
+ key,
249
+ allowReserved ? v : encodeURIComponent(v)
250
+ ];
251
+ });
252
+ const joinedValues2 = values.join(",");
253
+ switch (style) {
254
+ case "form":
255
+ return `${name}=${joinedValues2}`;
256
+ case "label":
257
+ return `.${joinedValues2}`;
258
+ case "matrix":
259
+ return `;${name}=${joinedValues2}`;
260
+ default:
261
+ return joinedValues2;
262
+ }
263
+ }
264
+ const separator = separatorObjectExplode(style);
265
+ const joinedValues = Object.entries(value).map(
266
+ ([key, v]) => serializePrimitiveParam({
267
+ allowReserved,
268
+ name: style === "deepObject" ? `${name}[${key}]` : key,
269
+ value: v
270
+ })
271
+ ).join(separator);
272
+ return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues;
273
+ };
274
+
275
+ // src/generated/core/utils.gen.ts
276
+ var PATH_PARAM_RE = /\{[^{}]+\}/g;
277
+ var defaultPathSerializer = ({ path, url: _url }) => {
278
+ let url = _url;
279
+ const matches = _url.match(PATH_PARAM_RE);
280
+ if (matches) {
281
+ for (const match of matches) {
282
+ let explode = false;
283
+ let name = match.substring(1, match.length - 1);
284
+ let style = "simple";
285
+ if (name.endsWith("*")) {
286
+ explode = true;
287
+ name = name.substring(0, name.length - 1);
288
+ }
289
+ if (name.startsWith(".")) {
290
+ name = name.substring(1);
291
+ style = "label";
292
+ } else if (name.startsWith(";")) {
293
+ name = name.substring(1);
294
+ style = "matrix";
295
+ }
296
+ const value = path[name];
297
+ if (value === void 0 || value === null) {
298
+ continue;
299
+ }
300
+ if (Array.isArray(value)) {
301
+ url = url.replace(
302
+ match,
303
+ serializeArrayParam({ explode, name, style, value })
304
+ );
305
+ continue;
306
+ }
307
+ if (typeof value === "object") {
308
+ url = url.replace(
309
+ match,
310
+ serializeObjectParam({
311
+ explode,
312
+ name,
313
+ style,
314
+ value,
315
+ valueOnly: true
316
+ })
317
+ );
318
+ continue;
319
+ }
320
+ if (style === "matrix") {
321
+ url = url.replace(
322
+ match,
323
+ `;${serializePrimitiveParam({
324
+ name,
325
+ value
326
+ })}`
327
+ );
328
+ continue;
329
+ }
330
+ const replaceValue = encodeURIComponent(
331
+ style === "label" ? `.${value}` : value
332
+ );
333
+ url = url.replace(match, replaceValue);
334
+ }
335
+ }
336
+ return url;
337
+ };
338
+ var getUrl = ({
339
+ baseUrl,
340
+ path,
341
+ query,
342
+ querySerializer,
343
+ url: _url
344
+ }) => {
345
+ const pathUrl = _url.startsWith("/") ? _url : `/${_url}`;
346
+ let url = (baseUrl ?? "") + pathUrl;
347
+ if (path) {
348
+ url = defaultPathSerializer({ path, url });
349
+ }
350
+ let search = query ? querySerializer(query) : "";
351
+ if (search.startsWith("?")) {
352
+ search = search.substring(1);
353
+ }
354
+ if (search) {
355
+ url += `?${search}`;
356
+ }
357
+ return url;
358
+ };
359
+ function getValidRequestBody(options) {
360
+ const hasBody = options.body !== void 0;
361
+ const isSerializedBody = hasBody && options.bodySerializer;
362
+ if (isSerializedBody) {
363
+ if ("serializedBody" in options) {
364
+ const hasSerializedBody = options.serializedBody !== void 0 && options.serializedBody !== "";
365
+ return hasSerializedBody ? options.serializedBody : null;
366
+ }
367
+ return options.body !== "" ? options.body : null;
368
+ }
369
+ if (hasBody) {
370
+ return options.body;
371
+ }
372
+ return void 0;
373
+ }
374
+
375
+ // src/generated/core/auth.gen.ts
376
+ var getAuthToken = async (auth, callback) => {
377
+ const token = typeof callback === "function" ? await callback(auth) : callback;
378
+ if (!token) {
379
+ return;
380
+ }
381
+ if (auth.scheme === "bearer") {
382
+ return `Bearer ${token}`;
383
+ }
384
+ if (auth.scheme === "basic") {
385
+ return `Basic ${btoa(token)}`;
386
+ }
387
+ return token;
388
+ };
389
+
390
+ // src/generated/client/utils.gen.ts
391
+ var createQuerySerializer = ({
392
+ parameters = {},
393
+ ...args
394
+ } = {}) => {
395
+ const querySerializer = (queryParams) => {
396
+ const search = [];
397
+ if (queryParams && typeof queryParams === "object") {
398
+ for (const name in queryParams) {
399
+ const value = queryParams[name];
400
+ if (value === void 0 || value === null) {
401
+ continue;
402
+ }
403
+ const options = parameters[name] || args;
404
+ if (Array.isArray(value)) {
405
+ const serializedArray = serializeArrayParam({
406
+ allowReserved: options.allowReserved,
407
+ explode: true,
408
+ name,
409
+ style: "form",
410
+ value,
411
+ ...options.array
412
+ });
413
+ if (serializedArray) search.push(serializedArray);
414
+ } else if (typeof value === "object") {
415
+ const serializedObject = serializeObjectParam({
416
+ allowReserved: options.allowReserved,
417
+ explode: true,
418
+ name,
419
+ style: "deepObject",
420
+ value,
421
+ ...options.object
422
+ });
423
+ if (serializedObject) search.push(serializedObject);
424
+ } else {
425
+ const serializedPrimitive = serializePrimitiveParam({
426
+ allowReserved: options.allowReserved,
427
+ name,
428
+ value
429
+ });
430
+ if (serializedPrimitive) search.push(serializedPrimitive);
431
+ }
432
+ }
433
+ }
434
+ return search.join("&");
435
+ };
436
+ return querySerializer;
437
+ };
438
+ var getParseAs = (contentType) => {
439
+ if (!contentType) {
440
+ return "stream";
441
+ }
442
+ const cleanContent = contentType.split(";")[0]?.trim();
443
+ if (!cleanContent) {
444
+ return;
445
+ }
446
+ if (cleanContent.startsWith("application/json") || cleanContent.endsWith("+json")) {
447
+ return "json";
448
+ }
449
+ if (cleanContent === "multipart/form-data") {
450
+ return "formData";
451
+ }
452
+ if (["application/", "audio/", "image/", "video/"].some(
453
+ (type) => cleanContent.startsWith(type)
454
+ )) {
455
+ return "blob";
456
+ }
457
+ if (cleanContent.startsWith("text/")) {
458
+ return "text";
459
+ }
460
+ return;
461
+ };
462
+ var checkForExistence = (options, name) => {
463
+ if (!name) {
464
+ return false;
465
+ }
466
+ if (options.headers.has(name) || options.query?.[name] || options.headers.get("Cookie")?.includes(`${name}=`)) {
467
+ return true;
468
+ }
469
+ return false;
470
+ };
471
+ var setAuthParams = async ({
472
+ security,
473
+ ...options
474
+ }) => {
475
+ for (const auth of security) {
476
+ if (checkForExistence(options, auth.name)) {
477
+ continue;
478
+ }
479
+ const token = await getAuthToken(auth, options.auth);
480
+ if (!token) {
481
+ continue;
482
+ }
483
+ const name = auth.name ?? "Authorization";
484
+ switch (auth.in) {
485
+ case "query":
486
+ if (!options.query) {
487
+ options.query = {};
488
+ }
489
+ options.query[name] = token;
490
+ break;
491
+ case "cookie":
492
+ options.headers.append("Cookie", `${name}=${token}`);
493
+ break;
494
+ case "header":
495
+ default:
496
+ options.headers.set(name, token);
497
+ break;
498
+ }
499
+ }
500
+ };
501
+ var buildUrl = (options) => getUrl({
502
+ baseUrl: options.baseUrl,
503
+ path: options.path,
504
+ query: options.query,
505
+ querySerializer: typeof options.querySerializer === "function" ? options.querySerializer : createQuerySerializer(options.querySerializer),
506
+ url: options.url
507
+ });
508
+ var mergeConfigs = (a, b) => {
509
+ const config = { ...a, ...b };
510
+ if (config.baseUrl?.endsWith("/")) {
511
+ config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1);
512
+ }
513
+ config.headers = mergeHeaders(a.headers, b.headers);
514
+ return config;
515
+ };
516
+ var headersEntries = (headers) => {
517
+ const entries = [];
518
+ headers.forEach((value, key) => {
519
+ entries.push([key, value]);
520
+ });
521
+ return entries;
522
+ };
523
+ var mergeHeaders = (...headers) => {
524
+ const mergedHeaders = new Headers();
525
+ for (const header of headers) {
526
+ if (!header) {
527
+ continue;
528
+ }
529
+ const iterator = header instanceof Headers ? headersEntries(header) : Object.entries(header);
530
+ for (const [key, value] of iterator) {
531
+ if (value === null) {
532
+ mergedHeaders.delete(key);
533
+ } else if (Array.isArray(value)) {
534
+ for (const v of value) {
535
+ mergedHeaders.append(key, v);
536
+ }
537
+ } else if (value !== void 0) {
538
+ mergedHeaders.set(
539
+ key,
540
+ typeof value === "object" ? JSON.stringify(value) : value
541
+ );
542
+ }
543
+ }
544
+ }
545
+ return mergedHeaders;
546
+ };
547
+ var Interceptors = class {
548
+ fns = [];
549
+ clear() {
550
+ this.fns = [];
551
+ }
552
+ eject(id) {
553
+ const index = this.getInterceptorIndex(id);
554
+ if (this.fns[index]) {
555
+ this.fns[index] = null;
556
+ }
557
+ }
558
+ exists(id) {
559
+ const index = this.getInterceptorIndex(id);
560
+ return Boolean(this.fns[index]);
561
+ }
562
+ getInterceptorIndex(id) {
563
+ if (typeof id === "number") {
564
+ return this.fns[id] ? id : -1;
565
+ }
566
+ return this.fns.indexOf(id);
567
+ }
568
+ update(id, fn) {
569
+ const index = this.getInterceptorIndex(id);
570
+ if (this.fns[index]) {
571
+ this.fns[index] = fn;
572
+ return id;
573
+ }
574
+ return false;
575
+ }
576
+ use(fn) {
577
+ this.fns.push(fn);
578
+ return this.fns.length - 1;
579
+ }
580
+ };
581
+ var createInterceptors = () => ({
582
+ error: new Interceptors(),
583
+ request: new Interceptors(),
584
+ response: new Interceptors()
585
+ });
586
+ var defaultQuerySerializer = createQuerySerializer({
587
+ allowReserved: false,
588
+ array: {
589
+ explode: true,
590
+ style: "form"
591
+ },
592
+ object: {
593
+ explode: true,
594
+ style: "deepObject"
595
+ }
596
+ });
597
+ var defaultHeaders = {
598
+ "Content-Type": "application/json"
599
+ };
600
+ var createConfig = (override = {}) => ({
601
+ ...jsonBodySerializer,
602
+ headers: defaultHeaders,
603
+ parseAs: "auto",
604
+ querySerializer: defaultQuerySerializer,
605
+ ...override
606
+ });
607
+
608
+ // src/generated/client/client.gen.ts
609
+ var createClient = (config = {}) => {
610
+ let _config = mergeConfigs(createConfig(), config);
611
+ const getConfig = () => ({ ..._config });
612
+ const setConfig = (config2) => {
613
+ _config = mergeConfigs(_config, config2);
614
+ return getConfig();
615
+ };
616
+ const interceptors = createInterceptors();
617
+ const beforeRequest = async (options) => {
618
+ const opts = {
619
+ ..._config,
620
+ ...options,
621
+ fetch: options.fetch ?? _config.fetch ?? globalThis.fetch,
622
+ headers: mergeHeaders(_config.headers, options.headers),
623
+ serializedBody: void 0
624
+ };
625
+ if (opts.security) {
626
+ await setAuthParams({
627
+ ...opts,
628
+ security: opts.security
629
+ });
630
+ }
631
+ if (opts.requestValidator) {
632
+ await opts.requestValidator(opts);
633
+ }
634
+ if (opts.body !== void 0 && opts.bodySerializer) {
635
+ opts.serializedBody = opts.bodySerializer(opts.body);
636
+ }
637
+ if (opts.body === void 0 || opts.serializedBody === "") {
638
+ opts.headers.delete("Content-Type");
639
+ }
640
+ const resolvedOpts = opts;
641
+ const url = buildUrl(resolvedOpts);
642
+ return { opts: resolvedOpts, url };
643
+ };
644
+ const request = async (options) => {
645
+ const throwOnError = options.throwOnError ?? _config.throwOnError;
646
+ const responseStyle = options.responseStyle ?? _config.responseStyle;
647
+ let request2;
648
+ let response;
649
+ try {
650
+ const { opts, url } = await beforeRequest(options);
651
+ const requestInit = {
652
+ redirect: "follow",
653
+ ...opts,
654
+ body: getValidRequestBody(opts)
655
+ };
656
+ request2 = new Request(url, requestInit);
657
+ for (const fn of interceptors.request.fns) {
658
+ if (fn) {
659
+ request2 = await fn(request2, opts);
660
+ }
661
+ }
662
+ const _fetch = opts.fetch;
663
+ response = await _fetch(request2);
664
+ for (const fn of interceptors.response.fns) {
665
+ if (fn) {
666
+ response = await fn(response, request2, opts);
667
+ }
668
+ }
669
+ const result = {
670
+ request: request2,
671
+ response
672
+ };
673
+ if (response.ok) {
674
+ const parseAs = (opts.parseAs === "auto" ? getParseAs(response.headers.get("Content-Type")) : opts.parseAs) ?? "json";
675
+ if (response.status === 204 || response.headers.get("Content-Length") === "0") {
676
+ let emptyData;
677
+ switch (parseAs) {
678
+ case "arrayBuffer":
679
+ case "blob":
680
+ case "text":
681
+ emptyData = await response[parseAs]();
682
+ break;
683
+ case "formData":
684
+ emptyData = new FormData();
685
+ break;
686
+ case "stream":
687
+ emptyData = response.body;
688
+ break;
689
+ case "json":
690
+ default:
691
+ emptyData = {};
692
+ break;
693
+ }
694
+ return opts.responseStyle === "data" ? emptyData : {
695
+ data: emptyData,
696
+ ...result
697
+ };
698
+ }
699
+ let data;
700
+ switch (parseAs) {
701
+ case "arrayBuffer":
702
+ case "blob":
703
+ case "formData":
704
+ case "text":
705
+ data = await response[parseAs]();
706
+ break;
707
+ case "json": {
708
+ const text = await response.text();
709
+ data = text ? JSON.parse(text) : {};
710
+ break;
711
+ }
712
+ case "stream":
713
+ return opts.responseStyle === "data" ? response.body : {
714
+ data: response.body,
715
+ ...result
716
+ };
717
+ }
718
+ if (parseAs === "json") {
719
+ if (opts.responseValidator) {
720
+ await opts.responseValidator(data);
721
+ }
722
+ if (opts.responseTransformer) {
723
+ data = await opts.responseTransformer(data);
724
+ }
725
+ }
726
+ return opts.responseStyle === "data" ? data : {
727
+ data,
728
+ ...result
729
+ };
730
+ }
731
+ const textError = await response.text();
732
+ let jsonError;
733
+ try {
734
+ jsonError = JSON.parse(textError);
735
+ } catch {
736
+ }
737
+ throw jsonError ?? textError;
738
+ } catch (error) {
739
+ let finalError = error;
740
+ for (const fn of interceptors.error.fns) {
741
+ if (fn) {
742
+ finalError = await fn(
743
+ finalError,
744
+ response,
745
+ request2,
746
+ options
747
+ );
748
+ }
749
+ }
750
+ finalError = finalError || {};
751
+ if (throwOnError) {
752
+ throw finalError;
753
+ }
754
+ return responseStyle === "data" ? void 0 : {
755
+ error: finalError,
756
+ request: request2,
757
+ response
758
+ };
759
+ }
760
+ };
761
+ const makeMethodFn = (method) => (options) => request({ ...options, method });
762
+ const makeSseFn = (method) => async (options) => {
763
+ const { opts, url } = await beforeRequest(options);
764
+ return createSseClient({
765
+ ...opts,
766
+ body: opts.body,
767
+ method,
768
+ onRequest: async (url2, init) => {
769
+ let request2 = new Request(url2, init);
770
+ for (const fn of interceptors.request.fns) {
771
+ if (fn) {
772
+ request2 = await fn(request2, opts);
773
+ }
774
+ }
775
+ return request2;
776
+ },
777
+ serializedBody: getValidRequestBody(opts),
778
+ url
779
+ });
780
+ };
781
+ const _buildUrl = (options) => buildUrl({ ..._config, ...options });
782
+ return {
783
+ buildUrl: _buildUrl,
784
+ connect: makeMethodFn("CONNECT"),
785
+ delete: makeMethodFn("DELETE"),
786
+ get: makeMethodFn("GET"),
787
+ getConfig,
788
+ head: makeMethodFn("HEAD"),
789
+ interceptors,
790
+ options: makeMethodFn("OPTIONS"),
791
+ patch: makeMethodFn("PATCH"),
792
+ post: makeMethodFn("POST"),
793
+ put: makeMethodFn("PUT"),
794
+ request,
795
+ setConfig,
796
+ sse: {
797
+ connect: makeSseFn("CONNECT"),
798
+ delete: makeSseFn("DELETE"),
799
+ get: makeSseFn("GET"),
800
+ head: makeSseFn("HEAD"),
801
+ options: makeSseFn("OPTIONS"),
802
+ patch: makeSseFn("PATCH"),
803
+ post: makeSseFn("POST"),
804
+ put: makeSseFn("PUT"),
805
+ trace: makeSseFn("TRACE")
806
+ },
807
+ trace: makeMethodFn("TRACE")
808
+ };
809
+ };
810
+
811
+ // src/region.ts
812
+ var REGION_PATTERN = /^[a-z]{2}[0-9]+$/;
813
+ function regionFromApiKey(apiKey) {
814
+ const [prefix, region, token] = apiKey.split("_");
815
+ if (prefix !== "bk" || !region || !token) return void 0;
816
+ return REGION_PATTERN.test(region) ? region : void 0;
817
+ }
818
+ function baseUrlForRegion(region) {
819
+ return `https://${region}.platform.bird.com`;
820
+ }
821
+
822
+ // src/errors.ts
823
+ var BirdError = class extends Error {
824
+ constructor(message) {
825
+ super(message);
826
+ this.name = "BirdError";
827
+ Object.setPrototypeOf(this, new.target.prototype);
828
+ }
829
+ };
830
+ var BirdConnectionError = class extends BirdError {
831
+ constructor(message) {
832
+ super(message);
833
+ this.name = "BirdConnectionError";
834
+ Object.setPrototypeOf(this, new.target.prototype);
835
+ }
836
+ };
837
+ var BirdTimeoutError = class extends BirdError {
838
+ timeoutMs;
839
+ constructor(message, timeoutMs) {
840
+ super(message);
841
+ this.name = "BirdTimeoutError";
842
+ this.timeoutMs = timeoutMs;
843
+ Object.setPrototypeOf(this, new.target.prototype);
844
+ }
845
+ };
846
+ var BirdWebhookVerificationError = class extends BirdError {
847
+ constructor(message) {
848
+ super(message);
849
+ this.name = "BirdWebhookVerificationError";
850
+ Object.setPrototypeOf(this, new.target.prototype);
851
+ }
852
+ };
853
+ var BirdAPIError = class extends BirdError {
854
+ statusCode;
855
+ code;
856
+ type;
857
+ errorName;
858
+ docUrl;
859
+ requestId;
860
+ param;
861
+ vendorCode;
862
+ constructor(fields) {
863
+ super(fields.message);
864
+ this.name = "BirdAPIError";
865
+ this.statusCode = fields.statusCode;
866
+ this.code = fields.code;
867
+ this.type = fields.type;
868
+ this.errorName = fields.errorName;
869
+ this.docUrl = fields.docUrl;
870
+ this.requestId = fields.requestId;
871
+ this.param = fields.param;
872
+ this.vendorCode = fields.vendorCode;
873
+ Object.setPrototypeOf(this, new.target.prototype);
874
+ }
875
+ };
876
+ var BirdAuthError = class extends BirdAPIError {
877
+ constructor(fields) {
878
+ super(fields);
879
+ this.name = "BirdAuthError";
880
+ Object.setPrototypeOf(this, new.target.prototype);
881
+ }
882
+ };
883
+ var BirdPermissionError = class extends BirdAPIError {
884
+ constructor(fields) {
885
+ super(fields);
886
+ this.name = "BirdPermissionError";
887
+ Object.setPrototypeOf(this, new.target.prototype);
888
+ }
889
+ };
890
+ var BirdNotFoundError = class extends BirdAPIError {
891
+ constructor(fields) {
892
+ super(fields);
893
+ this.name = "BirdNotFoundError";
894
+ Object.setPrototypeOf(this, new.target.prototype);
895
+ }
896
+ };
897
+ var BirdConflictError = class extends BirdAPIError {
898
+ constructor(fields) {
899
+ super(fields);
900
+ this.name = "BirdConflictError";
901
+ Object.setPrototypeOf(this, new.target.prototype);
902
+ }
903
+ };
904
+ var BirdBadRequestError = class extends BirdAPIError {
905
+ constructor(fields) {
906
+ super(fields);
907
+ this.name = "BirdBadRequestError";
908
+ Object.setPrototypeOf(this, new.target.prototype);
909
+ }
910
+ };
911
+ var BirdBillingError = class extends BirdAPIError {
912
+ constructor(fields) {
913
+ super(fields);
914
+ this.name = "BirdBillingError";
915
+ Object.setPrototypeOf(this, new.target.prototype);
916
+ }
917
+ };
918
+ var BirdPreconditionError = class extends BirdAPIError {
919
+ constructor(fields) {
920
+ super(fields);
921
+ this.name = "BirdPreconditionError";
922
+ Object.setPrototypeOf(this, new.target.prototype);
923
+ }
924
+ };
925
+ var BirdPayloadTooLargeError = class extends BirdAPIError {
926
+ constructor(fields) {
927
+ super(fields);
928
+ this.name = "BirdPayloadTooLargeError";
929
+ Object.setPrototypeOf(this, new.target.prototype);
930
+ }
931
+ };
932
+ var BirdInternalError = class extends BirdAPIError {
933
+ constructor(fields) {
934
+ super(fields);
935
+ this.name = "BirdInternalError";
936
+ Object.setPrototypeOf(this, new.target.prototype);
937
+ }
938
+ };
939
+ var BirdNotImplementedError = class extends BirdAPIError {
940
+ constructor(fields) {
941
+ super(fields);
942
+ this.name = "BirdNotImplementedError";
943
+ Object.setPrototypeOf(this, new.target.prototype);
944
+ }
945
+ };
946
+ var BirdMisdirectedError = class extends BirdAPIError {
947
+ constructor(fields) {
948
+ super(fields);
949
+ this.name = "BirdMisdirectedError";
950
+ Object.setPrototypeOf(this, new.target.prototype);
951
+ }
952
+ };
953
+ var BirdServiceUnavailableError = class extends BirdAPIError {
954
+ constructor(fields) {
955
+ super(fields);
956
+ this.name = "BirdServiceUnavailableError";
957
+ Object.setPrototypeOf(this, new.target.prototype);
958
+ }
959
+ };
960
+ var BirdValidationError = class extends BirdAPIError {
961
+ details;
962
+ constructor(fields) {
963
+ super(fields);
964
+ this.name = "BirdValidationError";
965
+ this.details = fields.details;
966
+ Object.setPrototypeOf(this, new.target.prototype);
967
+ }
968
+ };
969
+ var BirdRateLimitError = class extends BirdAPIError {
970
+ retryAfter;
971
+ constructor(fields) {
972
+ super(fields);
973
+ this.name = "BirdRateLimitError";
974
+ this.retryAfter = fields.retryAfter;
975
+ Object.setPrototypeOf(this, new.target.prototype);
976
+ }
977
+ };
978
+ function parseRetryAfter(headers) {
979
+ const header = headers?.get("Retry-After");
980
+ if (!header) return void 0;
981
+ const seconds = Number(header);
982
+ const value = Number.isFinite(seconds) ? seconds : (Date.parse(header) - Date.now()) / 1e3;
983
+ return Number.isFinite(value) && value >= 0 ? Math.round(value) : void 0;
984
+ }
985
+ function inferType(status) {
986
+ switch (status) {
987
+ case 400:
988
+ return "bad_request_error";
989
+ case 401:
990
+ return "auth_error";
991
+ case 402:
992
+ return "billing_error";
993
+ case 403:
994
+ return "permission_error";
995
+ case 404:
996
+ return "not_found_error";
997
+ case 409:
998
+ return "conflict_error";
999
+ case 412:
1000
+ case 428:
1001
+ return "precondition_error";
1002
+ case 413:
1003
+ return "payload_too_large_error";
1004
+ case 421:
1005
+ return "misdirected_error";
1006
+ case 422:
1007
+ return "validation_error";
1008
+ case 429:
1009
+ return "rate_limit_error";
1010
+ case 501:
1011
+ return "not_implemented_error";
1012
+ case 503:
1013
+ return "service_unavailable_error";
1014
+ default:
1015
+ return status >= 500 ? "internal_error" : "bad_request_error";
1016
+ }
1017
+ }
1018
+ function mapResponseToError(status, body, headers) {
1019
+ const raw = body ?? {};
1020
+ const b = raw.error ?? raw ?? {};
1021
+ const fields = {
1022
+ statusCode: status,
1023
+ code: b.code ?? "unknown",
1024
+ type: b.type ?? inferType(status),
1025
+ errorName: b.name ?? "",
1026
+ message: b.message ?? `Request failed with status ${status}`,
1027
+ docUrl: b.doc_url ?? "",
1028
+ requestId: b.request_id ?? headers?.get("X-Request-Id") ?? "",
1029
+ param: b.param,
1030
+ vendorCode: b.vendor_code
1031
+ };
1032
+ switch (fields.type) {
1033
+ case "auth_error":
1034
+ return new BirdAuthError(fields);
1035
+ case "permission_error":
1036
+ return new BirdPermissionError(fields);
1037
+ case "not_found_error":
1038
+ return new BirdNotFoundError(fields);
1039
+ case "conflict_error":
1040
+ return new BirdConflictError(fields);
1041
+ case "bad_request_error":
1042
+ return new BirdBadRequestError(fields);
1043
+ case "billing_error":
1044
+ return new BirdBillingError(fields);
1045
+ case "precondition_error":
1046
+ return new BirdPreconditionError(fields);
1047
+ case "payload_too_large_error":
1048
+ return new BirdPayloadTooLargeError(fields);
1049
+ case "internal_error":
1050
+ return new BirdInternalError(fields);
1051
+ case "not_implemented_error":
1052
+ return new BirdNotImplementedError(fields);
1053
+ case "misdirected_error":
1054
+ return new BirdMisdirectedError(fields);
1055
+ case "service_unavailable_error":
1056
+ return new BirdServiceUnavailableError(fields);
1057
+ case "rate_limit_error":
1058
+ return new BirdRateLimitError({
1059
+ ...fields,
1060
+ retryAfter: parseRetryAfter(headers)
1061
+ });
1062
+ case "validation_error":
1063
+ return new BirdValidationError({ ...fields, details: b.details ?? [] });
1064
+ default:
1065
+ return new BirdAPIError(fields);
1066
+ }
1067
+ }
1068
+
1069
+ // src/core/http.ts
1070
+ var BACKOFF_BASE_MS = 500;
1071
+ var BACKOFF_CAP_MS = 8e3;
1072
+ var RETRY_AFTER_CAP_MS = 6e4;
1073
+ var BirdHTTPClient = class {
1074
+ constructor(defaults) {
1075
+ this.defaults = defaults;
1076
+ }
1077
+ defaults;
1078
+ /**
1079
+ * Run a generated hey-api SDK call through the request lifecycle.
1080
+ *
1081
+ * @param call Invokes the SDK function; receives the per-attempt signal and
1082
+ * the idempotency key to set as a header.
1083
+ * @returns the parsed body plus transport metadata.
1084
+ * @throws a `BirdError` subclass on terminal failure; the native
1085
+ * `AbortError` if the caller's signal aborts.
1086
+ */
1087
+ async request(call, options) {
1088
+ const maxRetries = options.maxRetries ?? this.defaults.maxRetries;
1089
+ const timeout = options.timeout ?? this.defaults.timeout;
1090
+ const idempotencyKey = options.idempotencyKey ?? (isMutation(options.method) ? crypto.randomUUID() : void 0);
1091
+ for (let attempt = 0; ; attempt++) {
1092
+ throwIfAborted(options.signal);
1093
+ const retryOrThrow = async (terminal) => {
1094
+ if (attempt >= maxRetries) throw terminal();
1095
+ await sleep(backoffDelay(attempt), options.signal);
1096
+ };
1097
+ const timeoutSignal = AbortSignal.timeout(timeout);
1098
+ const signal = options.signal ? AbortSignal.any([options.signal, timeoutSignal]) : timeoutSignal;
1099
+ let outcome;
1100
+ try {
1101
+ outcome = await call({ signal, idempotencyKey });
1102
+ } catch (err) {
1103
+ throwIfAborted(options.signal);
1104
+ await retryOrThrow(
1105
+ () => timeoutSignal.aborted ? new BirdTimeoutError(`Request timed out after ${timeout}ms`, timeout) : new BirdConnectionError(errorMessage(err))
1106
+ );
1107
+ continue;
1108
+ }
1109
+ const res = outcome.response;
1110
+ if (!res) {
1111
+ await retryOrThrow(() => new BirdConnectionError("No response received from the server"));
1112
+ continue;
1113
+ }
1114
+ if (res.ok) {
1115
+ return { data: outcome.data, response: toBirdResponse(res) };
1116
+ }
1117
+ if (!isRetryableStatus(res.status) || attempt >= maxRetries) {
1118
+ throw mapResponseToError(res.status, outcome.error, res.headers);
1119
+ }
1120
+ await sleep(retryDelay(attempt, res.headers), options.signal);
1121
+ }
1122
+ }
1123
+ };
1124
+ function isMutation(method) {
1125
+ return ["POST", "PATCH", "DELETE"].includes(method.toUpperCase());
1126
+ }
1127
+ function isRetryableStatus(status) {
1128
+ return [408, 429, 500, 502, 503, 504].includes(status);
1129
+ }
1130
+ function backoffDelay(attempt) {
1131
+ const ceiling = Math.min(BACKOFF_CAP_MS, BACKOFF_BASE_MS * 2 ** attempt);
1132
+ return Math.random() * ceiling;
1133
+ }
1134
+ function retryDelay(attempt, headers) {
1135
+ const seconds = parseRetryAfter(headers);
1136
+ return seconds === void 0 ? backoffDelay(attempt) : Math.min(seconds * 1e3, RETRY_AFTER_CAP_MS);
1137
+ }
1138
+ function toBirdResponse(res) {
1139
+ return {
1140
+ status: res.status,
1141
+ headers: res.headers,
1142
+ requestId: res.headers.get("X-Request-Id") ?? ""
1143
+ };
1144
+ }
1145
+ function abortReason(signal) {
1146
+ return signal?.reason ?? new DOMException("Aborted", "AbortError");
1147
+ }
1148
+ function throwIfAborted(signal) {
1149
+ if (signal?.aborted) throw abortReason(signal);
1150
+ }
1151
+ function sleep(ms, signal) {
1152
+ return new Promise((resolve, reject) => {
1153
+ if (signal?.aborted) {
1154
+ reject(abortReason(signal));
1155
+ return;
1156
+ }
1157
+ const timer = setTimeout(() => {
1158
+ signal?.removeEventListener("abort", onAbort);
1159
+ resolve();
1160
+ }, ms);
1161
+ const onAbort = () => {
1162
+ clearTimeout(timer);
1163
+ reject(abortReason(signal));
1164
+ };
1165
+ signal?.addEventListener("abort", onAbort, { once: true });
1166
+ });
1167
+ }
1168
+ function errorMessage(err) {
1169
+ if (err instanceof Error) return err.message;
1170
+ return String(err);
1171
+ }
1172
+
1173
+ // src/core/result.ts
1174
+ function basePromise(inner) {
1175
+ const promise = inner.then((r) => r.data);
1176
+ void promise.catch(() => {
1177
+ });
1178
+ promise.withResponse = () => inner;
1179
+ promise.safe = () => toSafe(inner);
1180
+ return promise;
1181
+ }
1182
+ function apiPromise(inner) {
1183
+ return basePromise(inner);
1184
+ }
1185
+ function paginate(fetchPage) {
1186
+ const first = fetchPage();
1187
+ const promise = basePromise(first);
1188
+ promise[Symbol.asyncIterator] = async function* () {
1189
+ let result = await first;
1190
+ for (; ; ) {
1191
+ for (const item of result.data.data) yield item;
1192
+ if (result.data.next_cursor == null) return;
1193
+ result = await fetchPage(result.data.next_cursor);
1194
+ }
1195
+ };
1196
+ return promise;
1197
+ }
1198
+ function toSafe(inner) {
1199
+ return inner.then(
1200
+ ({ data, response }) => ({ data, error: null, response }),
1201
+ (error) => {
1202
+ if (error instanceof BirdError) return { data: null, error, response: null };
1203
+ throw error;
1204
+ }
1205
+ );
1206
+ }
1207
+
1208
+ // src/generated/client.gen.ts
1209
+ var client = createClient(createConfig());
1210
+
1211
+ // src/generated/sdk.gen.ts
1212
+ var listEmailMessages = (options) => (options?.client ?? client).get({
1213
+ security: [
1214
+ { scheme: "bearer", type: "http" },
1215
+ {
1216
+ in: "cookie",
1217
+ name: "bird_session",
1218
+ type: "apiKey"
1219
+ }
1220
+ ],
1221
+ url: "/v1/email/messages",
1222
+ ...options
1223
+ });
1224
+ var createEmailMessage = (options) => (options.client ?? client).post({
1225
+ security: [
1226
+ { scheme: "bearer", type: "http" },
1227
+ {
1228
+ in: "cookie",
1229
+ name: "bird_session",
1230
+ type: "apiKey"
1231
+ }
1232
+ ],
1233
+ url: "/v1/email/messages",
1234
+ ...options,
1235
+ headers: {
1236
+ "Content-Type": "application/json",
1237
+ ...options.headers
1238
+ }
1239
+ });
1240
+ var getEmailMessage = (options) => (options.client ?? client).get({
1241
+ security: [
1242
+ { scheme: "bearer", type: "http" },
1243
+ {
1244
+ in: "cookie",
1245
+ name: "bird_session",
1246
+ type: "apiKey"
1247
+ }
1248
+ ],
1249
+ url: "/v1/email/messages/{message_id}",
1250
+ ...options
1251
+ });
1252
+
1253
+ // src/resources/base.ts
1254
+ var Resource = class {
1255
+ constructor(core, client2) {
1256
+ this.core = core;
1257
+ this.client = client2;
1258
+ }
1259
+ core;
1260
+ client;
1261
+ /** Run a single typed call through the lifecycle. */
1262
+ call(method, options, invoke) {
1263
+ return apiPromise(
1264
+ this.core.request((ctx) => invoke(callContext(ctx, options)), lifecycle(method, options))
1265
+ );
1266
+ }
1267
+ /** Run a cursor-paginated list through the lifecycle (each page retried independently). */
1268
+ paginated(method, options, invoke) {
1269
+ return paginate(
1270
+ (cursor) => this.core.request(
1271
+ (ctx) => invoke(callContext(ctx, options), cursor),
1272
+ lifecycle(method, options)
1273
+ )
1274
+ );
1275
+ }
1276
+ };
1277
+ function callContext(ctx, options) {
1278
+ return { signal: ctx.signal, headers: mergeHeaders2(ctx.idempotencyKey, options?.headers) };
1279
+ }
1280
+ function lifecycle(method, options) {
1281
+ return {
1282
+ method,
1283
+ idempotencyKey: options?.idempotencyKey,
1284
+ signal: options?.signal,
1285
+ timeout: options?.timeout,
1286
+ maxRetries: options?.maxRetries
1287
+ };
1288
+ }
1289
+ function mergeHeaders2(idempotencyKey, extra) {
1290
+ return {
1291
+ ...extra,
1292
+ ...idempotencyKey ? { "Idempotency-Key": idempotencyKey } : {}
1293
+ };
1294
+ }
1295
+
1296
+ // src/resources/email.ts
1297
+ var EmailResource = class extends Resource {
1298
+ #defaults;
1299
+ constructor(core, client2, defaults) {
1300
+ super(core, client2);
1301
+ this.#defaults = defaults;
1302
+ }
1303
+ /**
1304
+ * Send an email message. Resolves once the message is accepted for delivery
1305
+ * (the API's 202). Throws on failure — a 422 (unverified sender, all
1306
+ * recipients suppressed, validation) is a `BirdValidationError`. Fields set as
1307
+ * channel defaults may be omitted (per-send value wins).
1308
+ *
1309
+ * @example Send a message
1310
+ * // bird:snippet:start email.send
1311
+ * const msg = await bird.email.send({
1312
+ * from: { email: "onboarding@messagebird.dev", name: "Bird" },
1313
+ * to: ["delivered@messagebird.dev"],
1314
+ * subject: "Hello from Bird",
1315
+ * html: "<p>My first Bird email.</p>",
1316
+ * });
1317
+ * console.log(msg.id, msg.status); // "em_…", "accepted"
1318
+ * // bird:snippet:end email.send
1319
+ *
1320
+ * @example A richer send — cc/bcc, reply-to, tags, metadata, click-tracking off, and an idempotency key (safe to retry; the server dedupes)
1321
+ * await bird.email.send(
1322
+ * {
1323
+ * from: "hello@acme.com",
1324
+ * to: ["a@example.com", "b@example.com"],
1325
+ * cc: ["manager@example.com"],
1326
+ * reply_to: ["support@acme.com"],
1327
+ * subject: "Your March invoice",
1328
+ * html: "<p>Attached.</p>",
1329
+ * tags: [{ name: "category", value: "billing" }],
1330
+ * metadata: { invoice_id: "inv_123" },
1331
+ * track_clicks: false,
1332
+ * },
1333
+ * { idempotencyKey: "invoice-march/cust_1" },
1334
+ * );
1335
+ *
1336
+ * @example Branch on the typed error hierarchy
1337
+ * // bird:snippet:start email.errors
1338
+ * import { BirdRateLimitError, BirdValidationError, BirdAPIError } from "@messagebird/sdk";
1339
+ *
1340
+ * try {
1341
+ * await bird.email.send({
1342
+ * from: "onboarding@messagebird.dev",
1343
+ * to: ["delivered@messagebird.dev"],
1344
+ * subject: "Hello from Bird",
1345
+ * html: "<p>My first Bird email.</p>",
1346
+ * });
1347
+ * } catch (err) {
1348
+ * if (err instanceof BirdRateLimitError) console.log(`rate limited — retry in ${err.retryAfter}s`);
1349
+ * else if (err instanceof BirdValidationError) console.error(err.details);
1350
+ * else if (err instanceof BirdAPIError) console.error(err.code, err.requestId);
1351
+ * else throw err;
1352
+ * }
1353
+ * // bird:snippet:end email.errors
1354
+ *
1355
+ * @example Errors as values with `.safe()`
1356
+ * // bird:snippet:start email.safe
1357
+ * const { data, error } = await bird.email
1358
+ * .send({
1359
+ * from: "onboarding@messagebird.dev",
1360
+ * to: ["delivered@messagebird.dev"],
1361
+ * subject: "Hello from Bird",
1362
+ * html: "<p>My first Bird email.</p>",
1363
+ * })
1364
+ * .safe();
1365
+ * if (error) console.error(error.message);
1366
+ * else console.log(data.id);
1367
+ * // bird:snippet:end email.safe
1368
+ */
1369
+ send(params, options) {
1370
+ const body = { ...this.#defaults, ...params };
1371
+ return this.call(
1372
+ "POST",
1373
+ options,
1374
+ ({ signal, headers }) => createEmailMessage({ client: this.client, body, headers, signal })
1375
+ );
1376
+ }
1377
+ /**
1378
+ * Fetch a message with aggregate delivery status.
1379
+ *
1380
+ * @example
1381
+ * // bird:snippet:start email.get
1382
+ * const msg = await bird.email.get("em_abc123");
1383
+ * msg.status; // "accepted" | "processed" | "delivered" | "bounced" | …
1384
+ * msg.delivered_count;
1385
+ * msg.bounced_count;
1386
+ * // bird:snippet:end email.get
1387
+ */
1388
+ get(messageId, options) {
1389
+ return this.call(
1390
+ "GET",
1391
+ options,
1392
+ ({ signal, headers }) => getEmailMessage({ client: this.client, path: { message_id: messageId }, headers, signal })
1393
+ );
1394
+ }
1395
+ /**
1396
+ * List messages, newest first. `await` resolves the first page; `for await`
1397
+ * walks every message across all pages.
1398
+ *
1399
+ * @example Iterate every message, or take one page
1400
+ * // bird:snippet:start email.list.paginate
1401
+ * // bird:snippet:start email.list.iterate
1402
+ * for await (const message of bird.email.list({ status: "bounced" })) {
1403
+ * console.log(message.id);
1404
+ * }
1405
+ * // bird:snippet:end email.list.iterate
1406
+ * const page = await bird.email.list({ limit: 50 }); // page.data, page.next_cursor
1407
+ * // bird:snippet:end email.list.paginate
1408
+ */
1409
+ list(query, options) {
1410
+ return this.paginated(
1411
+ "GET",
1412
+ options,
1413
+ ({ signal, headers }, cursor) => listEmailMessages({
1414
+ client: this.client,
1415
+ query: { ...query, starting_after: cursor ?? query?.starting_after },
1416
+ headers,
1417
+ signal
1418
+ })
1419
+ );
1420
+ }
1421
+ };
1422
+ var WebhooksResource = class {
1423
+ #secret;
1424
+ constructor(config) {
1425
+ this.#secret = config?.secret;
1426
+ }
1427
+ /**
1428
+ * Verify a webhook delivery and return the typed event.
1429
+ *
1430
+ * **Pass the raw request body**, exactly as received — do NOT parse it first.
1431
+ * The Standard Webhooks signature is computed over the raw bytes, so parsing
1432
+ * and re-serializing before verifying is the classic webhook bug.
1433
+ *
1434
+ * The secret comes from `webhooks.secret` on the client; pass `{ secret }` to
1435
+ * override per call. Throws {@link BirdWebhookVerificationError} on a bad
1436
+ * signature, a stale timestamp, or missing/malformed headers. Unknown event
1437
+ * types are returned as-is (handle them in a `default` case) so a newer server
1438
+ * event can't break an older SDK.
1439
+ *
1440
+ * @example Verify and dispatch — pass the raw request body, never the parsed JSON
1441
+ * // new BirdClient({ apiKey, webhooks: { secret } })
1442
+ * try {
1443
+ * const event = bird.webhooks.unwrap(rawBody, req.headers);
1444
+ * switch (event.type) {
1445
+ * case "email.delivered":
1446
+ * markDelivered(event.email_id, event.recipient); // narrowed; fields are flat
1447
+ * break;
1448
+ * case "email.bounced":
1449
+ * case "email.complained":
1450
+ * suppress(event.recipient);
1451
+ * break;
1452
+ * default: // unknown future event types — an older SDK won't break on a new one
1453
+ * }
1454
+ * } catch (err) {
1455
+ * if (err instanceof BirdWebhookVerificationError) {
1456
+ * // reject with 400 — bad signature, stale timestamp, or missing/malformed headers
1457
+ * } else throw err;
1458
+ * }
1459
+ */
1460
+ unwrap(payload, headers, options) {
1461
+ const secret = options?.secret ?? this.#secret;
1462
+ if (!secret) {
1463
+ throw new Error(
1464
+ "No webhook secret. Set `webhooks: { secret }` on the client, or pass `{ secret }` to unwrap."
1465
+ );
1466
+ }
1467
+ const wh = new Webhook(secret);
1468
+ let verified;
1469
+ try {
1470
+ verified = wh.verify(payload, toHeaderRecord(headers));
1471
+ } catch (err) {
1472
+ throw new BirdWebhookVerificationError(
1473
+ err instanceof Error ? err.message : "Webhook signature verification failed"
1474
+ );
1475
+ }
1476
+ return verified;
1477
+ }
1478
+ };
1479
+ function toHeaderRecord(headers) {
1480
+ return headers instanceof Headers ? Object.fromEntries(headers) : headers;
1481
+ }
1482
+
1483
+ // src/client.ts
1484
+ var DEFAULT_TIMEOUT_MS = 6e4;
1485
+ var DEFAULT_MAX_RETRIES = 2;
1486
+ function resolveBaseUrl(options) {
1487
+ if (options.baseUrl) return options.baseUrl;
1488
+ const region = options.region ?? regionFromApiKey(options.apiKey);
1489
+ if (!region) {
1490
+ throw new Error(
1491
+ "Unable to determine region: API key is not in the expected bk_{region}_{token} format. Pass an explicit `region` or `baseUrl`."
1492
+ );
1493
+ }
1494
+ return baseUrlForRegion(region);
1495
+ }
1496
+ function resolveRawRequestUrl(baseUrl, path) {
1497
+ if (!path.startsWith("/") || path.startsWith("//")) {
1498
+ throw new TypeError(
1499
+ "bird.request path must be an absolute path starting with a single `/`"
1500
+ );
1501
+ }
1502
+ const base = new URL(baseUrl);
1503
+ const url = new URL(baseUrl + path);
1504
+ if (url.origin !== base.origin) {
1505
+ throw new TypeError(
1506
+ "bird.request path must stay on the configured Bird API origin"
1507
+ );
1508
+ }
1509
+ return url;
1510
+ }
1511
+ var BirdClient = class {
1512
+ core;
1513
+ // The generated hey-api client, configured with this instance's base URL,
1514
+ // auth, and fetch. Resources call the generated SDK functions through it.
1515
+ #client;
1516
+ #baseUrl;
1517
+ #fetch;
1518
+ #headers;
1519
+ /** The email channel — `bird.email.send(...)`, `.get(...)`, `.list(...)`. */
1520
+ email;
1521
+ /** Webhooks — `bird.webhooks.unwrap(payload, headers)` verifies an inbound delivery. */
1522
+ webhooks;
1523
+ constructor(options) {
1524
+ const opts = options;
1525
+ this.#baseUrl = resolveBaseUrl(opts);
1526
+ this.#fetch = opts.fetch ?? fetch;
1527
+ this.#headers = {
1528
+ ...opts.defaultHeaders,
1529
+ Authorization: `Bearer ${opts.apiKey}`,
1530
+ "User-Agent": `bird-sdk-js/${"0.1.1"}`
1531
+ };
1532
+ this.#client = createClient(
1533
+ createConfig({
1534
+ baseUrl: this.#baseUrl,
1535
+ fetch: this.#fetch,
1536
+ headers: this.#headers
1537
+ })
1538
+ );
1539
+ this.core = new BirdHTTPClient({
1540
+ timeout: opts.timeout ?? DEFAULT_TIMEOUT_MS,
1541
+ maxRetries: opts.maxRetries ?? DEFAULT_MAX_RETRIES
1542
+ });
1543
+ this.email = new EmailResource(
1544
+ this.core,
1545
+ this.#client,
1546
+ opts.email
1547
+ );
1548
+ this.webhooks = new WebhooksResource(opts.webhooks);
1549
+ }
1550
+ /**
1551
+ * Escape hatch for endpoints the typed resources don't cover. Runs the full
1552
+ * lifecycle (auth, retries, idempotency, error mapping); you supply the
1553
+ * response type. Prefer a typed resource method where one exists.
1554
+ *
1555
+ * @throws {TypeError} if `req.path` does not start with exactly one `/` or
1556
+ * resolves to a different origin than the configured Bird API base URL.
1557
+ *
1558
+ * @example Reach an endpoint outside the curated surface — you supply the response type
1559
+ * type Suppressions = { data: Array<{ recipient: string }> };
1560
+ * const suppressions = await bird.request<Suppressions>({ method: "GET", path: "/v1/email/suppressions" });
1561
+ * console.log(suppressions.data.length);
1562
+ */
1563
+ request(req, options) {
1564
+ const url = resolveRawRequestUrl(this.#baseUrl, req.path);
1565
+ return apiPromise(
1566
+ this.core.request(
1567
+ (ctx) => this.#raw(url, req, ctx, options?.headers),
1568
+ {
1569
+ method: req.method,
1570
+ idempotencyKey: options?.idempotencyKey,
1571
+ signal: options?.signal,
1572
+ timeout: options?.timeout,
1573
+ maxRetries: options?.maxRetries
1574
+ }
1575
+ )
1576
+ );
1577
+ }
1578
+ async #raw(url, req, ctx, extraHeaders) {
1579
+ url = new URL(url);
1580
+ if (req.query) {
1581
+ for (const [key, value] of Object.entries(req.query)) {
1582
+ if (value !== void 0) url.searchParams.set(key, String(value));
1583
+ }
1584
+ }
1585
+ const headers = {
1586
+ ...extraHeaders,
1587
+ ...this.#headers
1588
+ };
1589
+ if (ctx.idempotencyKey) headers["Idempotency-Key"] = ctx.idempotencyKey;
1590
+ if (req.body !== void 0) headers["Content-Type"] = "application/json";
1591
+ const response = await this.#fetch(url, {
1592
+ method: req.method,
1593
+ headers,
1594
+ body: req.body !== void 0 ? JSON.stringify(req.body) : void 0,
1595
+ signal: ctx.signal
1596
+ });
1597
+ if (response.ok) {
1598
+ const data = response.status === 204 ? void 0 : await response.json().catch(() => void 0);
1599
+ return { data, response };
1600
+ }
1601
+ const error = await response.clone().json().catch(() => void 0);
1602
+ return { error, response };
1603
+ }
1604
+ };
1605
+
1606
+ export { BirdAPIError, BirdAuthError, BirdBadRequestError, BirdBillingError, BirdClient, BirdConflictError, BirdConnectionError, BirdError, BirdInternalError, BirdMisdirectedError, BirdNotFoundError, BirdNotImplementedError, BirdPayloadTooLargeError, BirdPermissionError, BirdPreconditionError, BirdRateLimitError, BirdServiceUnavailableError, BirdTimeoutError, BirdValidationError, BirdWebhookVerificationError, baseUrlForRegion, regionFromApiKey };
1607
+ //# sourceMappingURL=index.js.map
1608
+ //# sourceMappingURL=index.js.map