@stripe/link-sdk 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,773 @@
1
+ // src/errors.ts
2
+ import { ZodError, prettifyError } from "zod";
3
+ var LinkSdkError = class extends Error {
4
+ code;
5
+ constructor(message, options) {
6
+ super(message, { cause: options?.cause });
7
+ this.name = new.target.name;
8
+ this.code = options?.code ?? "sdk_error";
9
+ }
10
+ };
11
+ var LinkResponseError = class extends LinkSdkError {
12
+ status;
13
+ constructor(operation, status, options) {
14
+ const detail = options?.cause instanceof ZodError ? `: ${prettifyError(options.cause)}` : options?.cause instanceof Error ? `: ${options.cause.message}` : "";
15
+ super(
16
+ `Invalid response while attempting to ${operation} (${status})${detail}`,
17
+ { code: "invalid_response", cause: options?.cause }
18
+ );
19
+ this.status = status;
20
+ }
21
+ };
22
+ var LinkConfigurationError = class extends LinkSdkError {
23
+ constructor(message, options) {
24
+ super(message, { code: "configuration_error", ...options });
25
+ }
26
+ };
27
+ var LinkTransportError = class extends LinkSdkError {
28
+ constructor(message, options) {
29
+ super(message, { code: "transport_error", ...options });
30
+ }
31
+ };
32
+ var LinkApiError = class extends LinkSdkError {
33
+ status;
34
+ rawBody;
35
+ details;
36
+ constructor(message, options) {
37
+ super(message, { code: options.code ?? "api_error", cause: options.cause });
38
+ this.status = options.status;
39
+ this.rawBody = options.rawBody;
40
+ this.details = options.details;
41
+ }
42
+ };
43
+
44
+ // src/config.ts
45
+ var DEFAULT_API_BASE_URL = "https://api.link.com";
46
+ function createDefaultHeadersFetch(baseFetch, defaultHeaders) {
47
+ return ((input, init) => {
48
+ const headers = new Headers(init?.headers);
49
+ for (const [key, value] of Object.entries(defaultHeaders)) {
50
+ if (!headers.has(key)) {
51
+ headers.set(key, value);
52
+ }
53
+ }
54
+ return baseFetch(input, { ...init, headers });
55
+ });
56
+ }
57
+ function createDefaultLogger() {
58
+ return {
59
+ debug() {
60
+ }
61
+ };
62
+ }
63
+ function resolveLinkSdkConfig(options, defaults = {}) {
64
+ const verbose = options.verbose ?? false;
65
+ const logger = options.logger ?? createDefaultLogger();
66
+ if (options.accessToken !== void 0 && options.getAccessToken !== void 0) {
67
+ throw new LinkConfigurationError(
68
+ "Pass either `accessToken` or `getAccessToken`, not both."
69
+ );
70
+ }
71
+ let getAccessToken;
72
+ let canRefreshAccessToken;
73
+ if (options.accessToken !== void 0) {
74
+ if (options.accessToken.trim().length === 0) {
75
+ throw new LinkConfigurationError("`accessToken` cannot be empty.");
76
+ }
77
+ const accessToken = options.accessToken;
78
+ getAccessToken = () => accessToken;
79
+ canRefreshAccessToken = false;
80
+ } else if (typeof options.getAccessToken === "function") {
81
+ getAccessToken = options.getAccessToken;
82
+ canRefreshAccessToken = true;
83
+ } else {
84
+ throw new LinkConfigurationError(
85
+ "Pass `accessToken` or `getAccessToken` to the Link client."
86
+ );
87
+ }
88
+ const apiBaseUrl = options.apiBaseUrl ?? defaults.apiBaseUrl ?? DEFAULT_API_BASE_URL;
89
+ const spendRequestBaseUrl = options.spendRequestBaseUrl ?? defaults.spendRequestBaseUrl ?? apiBaseUrl;
90
+ const baseFetch = options.fetch ?? globalThis.fetch;
91
+ const effectiveFetch = options.defaultHeaders && Object.keys(options.defaultHeaders).length > 0 ? createDefaultHeadersFetch(baseFetch, options.defaultHeaders) : baseFetch;
92
+ return {
93
+ verbose,
94
+ getAccessToken,
95
+ canRefreshAccessToken,
96
+ fetch: effectiveFetch,
97
+ apiBaseUrl,
98
+ spendRequestBaseUrl,
99
+ logger
100
+ };
101
+ }
102
+ function requireFetchImplementation(config) {
103
+ if (!config.fetch) {
104
+ throw new LinkConfigurationError(
105
+ "No fetch implementation available. Pass `fetch` in Link SDK options."
106
+ );
107
+ }
108
+ return config.fetch;
109
+ }
110
+
111
+ // src/resources/base.ts
112
+ function isRecord(value) {
113
+ return value !== null && typeof value === "object" && !Array.isArray(value);
114
+ }
115
+ function extractErrorMessage(data, rawBody) {
116
+ if (isRecord(data)) {
117
+ if (typeof data.error === "string") {
118
+ return data.error;
119
+ }
120
+ if (isRecord(data.error)) {
121
+ const nested = data.error;
122
+ if (typeof nested.message === "string") {
123
+ return nested.message;
124
+ }
125
+ if (typeof nested.code === "string") {
126
+ return nested.code;
127
+ }
128
+ }
129
+ if (typeof data.message === "string") {
130
+ return data.message;
131
+ }
132
+ }
133
+ return rawBody || "unknown error";
134
+ }
135
+ var BaseResource = class {
136
+ verbose;
137
+ getAccessToken;
138
+ canRefreshAccessToken;
139
+ fetchImpl;
140
+ endpoint;
141
+ logger;
142
+ constructor(options, endpointPath, base = "api") {
143
+ const config = resolveLinkSdkConfig(options);
144
+ this.verbose = config.verbose;
145
+ this.getAccessToken = config.getAccessToken;
146
+ this.canRefreshAccessToken = config.canRefreshAccessToken;
147
+ this.fetchImpl = requireFetchImplementation(config);
148
+ const baseUrl = base === "spend" ? config.spendRequestBaseUrl : config.apiBaseUrl;
149
+ this.endpoint = `${baseUrl}${endpointPath}`;
150
+ this.logger = config.logger;
151
+ }
152
+ async rawFetch(opts) {
153
+ if (this.verbose) {
154
+ this.logger.debug(`> ${opts.method} ${opts.url}`);
155
+ }
156
+ let response;
157
+ try {
158
+ const init = {
159
+ method: opts.method,
160
+ ...opts.headers !== void 0 && { headers: opts.headers },
161
+ ...opts.body !== void 0 && { body: opts.body },
162
+ ...opts.signal !== void 0 && { signal: opts.signal }
163
+ };
164
+ response = await this.fetchImpl(opts.url, init);
165
+ } catch (error) {
166
+ throw new LinkTransportError(
167
+ `Request failed: ${opts.method} ${opts.url}`,
168
+ { cause: error }
169
+ );
170
+ }
171
+ const rawBody = await response.text();
172
+ let data = null;
173
+ try {
174
+ data = JSON.parse(rawBody);
175
+ } catch {
176
+ }
177
+ if (this.verbose) {
178
+ this.logger.debug(`< ${response.status} ${response.statusText}`);
179
+ }
180
+ return { status: response.status, data, rawBody };
181
+ }
182
+ async apiFetch(opts) {
183
+ const token = await this.getAccessToken();
184
+ const authedOpts = {
185
+ ...opts,
186
+ headers: {
187
+ ...opts.headers,
188
+ Authorization: `Bearer ${token}`
189
+ }
190
+ };
191
+ const res = await this.rawFetch(authedOpts);
192
+ if (res.status === 401 && this.canRefreshAccessToken) {
193
+ const refreshedToken = await this.getAccessToken({ forceRefresh: true });
194
+ authedOpts.headers.Authorization = `Bearer ${refreshedToken}`;
195
+ return this.rawFetch(authedOpts);
196
+ }
197
+ return res;
198
+ }
199
+ throwApiError(operation, status, data, rawBody, cause) {
200
+ const msg = extractErrorMessage(data, rawBody);
201
+ throw new LinkApiError(`Failed to ${operation} (${status}): ${msg}`, {
202
+ status,
203
+ rawBody,
204
+ details: data,
205
+ cause
206
+ });
207
+ }
208
+ parseResponse(operation, status, parser) {
209
+ try {
210
+ return parser();
211
+ } catch (error) {
212
+ throw new LinkResponseError(operation, status, { cause: error });
213
+ }
214
+ }
215
+ };
216
+
217
+ // src/resources/balances.ts
218
+ import { z } from "zod";
219
+ var currencyAmountsSchema = z.record(z.string(), z.number());
220
+ var balanceSchema = z.looseObject({
221
+ source_id: z.string(),
222
+ type: z.enum(["cash", "credit"]),
223
+ cash: z.looseObject({ available: currencyAmountsSchema }).nullable().optional(),
224
+ credit: z.looseObject({ used: currencyAmountsSchema }).nullable().optional(),
225
+ current: z.number(),
226
+ currency: z.string(),
227
+ as_of: z.string()
228
+ });
229
+ var balancesPageSchema = z.looseObject({
230
+ data: z.array(balanceSchema),
231
+ has_more: z.boolean().optional()
232
+ });
233
+ var BalancesResource = class extends BaseResource {
234
+ constructor(options) {
235
+ super(options, "/balances");
236
+ }
237
+ buildUrl(params) {
238
+ const url = new URL(this.endpoint);
239
+ if (params.sources !== void 0) {
240
+ for (const source of params.sources) {
241
+ url.searchParams.append("sources[]", source);
242
+ }
243
+ }
244
+ if (params.limit !== void 0) {
245
+ url.searchParams.set("limit", String(params.limit));
246
+ }
247
+ if (params.starting_after !== void 0) {
248
+ url.searchParams.set("starting_after", params.starting_after);
249
+ }
250
+ if (params.ending_before !== void 0) {
251
+ url.searchParams.set("ending_before", params.ending_before);
252
+ }
253
+ return url.toString();
254
+ }
255
+ async list(params = {}) {
256
+ const { status, data, rawBody } = await this.apiFetch({
257
+ method: "GET",
258
+ url: this.buildUrl(params)
259
+ });
260
+ if (status < 200 || status >= 300) {
261
+ this.throwApiError("list balances", status, data, rawBody);
262
+ }
263
+ return this.parseResponse(
264
+ "list balances",
265
+ status,
266
+ () => balancesPageSchema.parse(data)
267
+ );
268
+ }
269
+ };
270
+
271
+ // src/resources/payment-methods.ts
272
+ import { z as z2 } from "zod";
273
+ var paymentMethodSchema = z2.looseObject({
274
+ id: z2.string(),
275
+ type: z2.string(),
276
+ is_default: z2.boolean()
277
+ });
278
+ var paymentMethodsResponseSchema = z2.looseObject({
279
+ payment_details: z2.array(paymentMethodSchema)
280
+ });
281
+ var PaymentMethodsResource = class extends BaseResource {
282
+ constructor(options) {
283
+ super(options, "/payment-details");
284
+ }
285
+ async list() {
286
+ const { status, data, rawBody } = await this.apiFetch({
287
+ method: "GET",
288
+ url: this.endpoint
289
+ });
290
+ if (status < 200 || status >= 300) {
291
+ this.throwApiError("list payment methods", status, data, rawBody);
292
+ }
293
+ return this.parseResponse(
294
+ "list payment methods",
295
+ status,
296
+ () => paymentMethodsResponseSchema.parse(data).payment_details
297
+ );
298
+ }
299
+ };
300
+
301
+ // src/resources/report.ts
302
+ import { z as z3 } from "zod";
303
+ var reportRecordSchema = z3.looseObject({
304
+ object: z3.string(),
305
+ created_at: z3.string(),
306
+ domain: z3.string(),
307
+ outcome: z3.string(),
308
+ spend_request_id: z3.string(),
309
+ status: z3.string()
310
+ });
311
+ var ReportResource = class extends BaseResource {
312
+ constructor(options) {
313
+ super(options, "/agent_observations");
314
+ }
315
+ async create(params) {
316
+ const { status, data, rawBody } = await this.apiFetch({
317
+ method: "POST",
318
+ url: this.endpoint,
319
+ headers: { "Content-Type": "application/json" },
320
+ body: JSON.stringify(params)
321
+ });
322
+ if (status < 200 || status >= 300) {
323
+ this.throwApiError("create report", status, data, rawBody);
324
+ }
325
+ return this.parseResponse(
326
+ "create report",
327
+ status,
328
+ () => reportRecordSchema.parse(data)
329
+ );
330
+ }
331
+ };
332
+
333
+ // src/resources/shipping-address.ts
334
+ import { z as z4 } from "zod";
335
+ var shippingAddressSchema = z4.looseObject({
336
+ id: z4.string(),
337
+ is_default: z4.boolean(),
338
+ nickname: z4.string().nullable(),
339
+ address: z4.looseObject({}).nullable()
340
+ });
341
+ var shippingAddressesResponseSchema = z4.looseObject({
342
+ shipping_addresses: z4.array(shippingAddressSchema)
343
+ });
344
+ var ShippingAddressResource = class extends BaseResource {
345
+ constructor(options) {
346
+ super(options, "/shipping_addresses");
347
+ }
348
+ async list() {
349
+ const { status, data, rawBody } = await this.apiFetch({
350
+ method: "GET",
351
+ url: this.endpoint
352
+ });
353
+ if (status < 200 || status >= 300) {
354
+ this.throwApiError("list shipping addresses", status, data, rawBody);
355
+ }
356
+ return this.parseResponse(
357
+ "list shipping addresses",
358
+ status,
359
+ () => shippingAddressesResponseSchema.parse(data).shipping_addresses
360
+ );
361
+ }
362
+ };
363
+
364
+ // src/resources/sources.ts
365
+ import { z as z5 } from "zod";
366
+ var nullableRecordSchema = z5.record(z5.string(), z5.unknown()).nullable().optional();
367
+ var sourceSchema = z5.looseObject({
368
+ id: z5.string().nullable().optional(),
369
+ name: z5.string().nullable().optional(),
370
+ type: z5.string().nullable().optional(),
371
+ capabilities: nullableRecordSchema,
372
+ external_connection: nullableRecordSchema,
373
+ granted_actions: z5.array(z5.string()).nullable().optional(),
374
+ bank_account: nullableRecordSchema,
375
+ card: nullableRecordSchema
376
+ });
377
+ var sourcesPageSchema = z5.looseObject({
378
+ data: z5.array(sourceSchema),
379
+ has_more: z5.boolean().optional()
380
+ });
381
+ var SourcesResource = class extends BaseResource {
382
+ constructor(options) {
383
+ super(options, "/sources");
384
+ }
385
+ buildUrl(params) {
386
+ const url = new URL(this.endpoint);
387
+ if (params.limit !== void 0) {
388
+ url.searchParams.set("limit", String(params.limit));
389
+ }
390
+ if (params.starting_after !== void 0) {
391
+ url.searchParams.set("starting_after", params.starting_after);
392
+ }
393
+ if (params.ending_before !== void 0) {
394
+ url.searchParams.set("ending_before", params.ending_before);
395
+ }
396
+ return url.toString();
397
+ }
398
+ async list(params = {}) {
399
+ const { status, data, rawBody } = await this.apiFetch({
400
+ method: "GET",
401
+ url: this.buildUrl(params)
402
+ });
403
+ if (status < 200 || status >= 300) {
404
+ this.throwApiError("list sources", status, data, rawBody);
405
+ }
406
+ return this.parseResponse(
407
+ "list sources",
408
+ status,
409
+ () => sourcesPageSchema.parse(data)
410
+ );
411
+ }
412
+ };
413
+
414
+ // src/resources/spend-request.ts
415
+ import { z as z6 } from "zod";
416
+ var sharedPaymentTokenSchema = z6.union([
417
+ z6.string().transform((id) => ({ id })),
418
+ z6.looseObject({ id: z6.string() })
419
+ ]);
420
+ var spendRequestSchema = z6.looseObject({
421
+ id: z6.string(),
422
+ payment_details: z6.string().optional(),
423
+ status: z6.string(),
424
+ line_items: z6.array(z6.unknown()).optional(),
425
+ totals: z6.array(z6.unknown()).optional(),
426
+ created_at: z6.string(),
427
+ updated_at: z6.string(),
428
+ shared_payment_token: sharedPaymentTokenSchema.nullable().optional()
429
+ });
430
+ var spendRequestsResponseSchema = z6.looseObject({
431
+ data: z6.array(spendRequestSchema)
432
+ });
433
+ var requestApprovalResponseSchema = z6.looseObject({
434
+ id: z6.string(),
435
+ approval_link: z6.string()
436
+ }).transform(({ approval_link, ...response }) => ({
437
+ ...response,
438
+ approval_url: approval_link
439
+ }));
440
+ var duplicateSpendRequestErrorSchema = z6.looseObject({
441
+ error: z6.looseObject({
442
+ duplicate_spend_request: z6.unknown().optional()
443
+ })
444
+ });
445
+ function parseSpendRequest(value) {
446
+ return spendRequestSchema.parse(value);
447
+ }
448
+ function getDuplicateSpendRequest(error) {
449
+ if (!(error instanceof LinkApiError)) return null;
450
+ const details = duplicateSpendRequestErrorSchema.safeParse(error.details);
451
+ if (!details.success) return null;
452
+ const duplicate = spendRequestSchema.safeParse(
453
+ details.data.error.duplicate_spend_request
454
+ );
455
+ return duplicate.success ? duplicate.data : null;
456
+ }
457
+ var SpendRequestResource = class extends BaseResource {
458
+ constructor(options) {
459
+ super(options, "/spend_requests", "spend");
460
+ }
461
+ async list(opts) {
462
+ const url = opts?.includeHistory ? `${this.endpoint}?include_history=true` : this.endpoint;
463
+ const { status, data, rawBody } = await this.apiFetch({
464
+ method: "GET",
465
+ url
466
+ });
467
+ if (status < 200 || status >= 300) {
468
+ this.throwApiError("list spend requests", status, data, rawBody);
469
+ }
470
+ return this.parseResponse(
471
+ "list spend requests",
472
+ status,
473
+ () => spendRequestsResponseSchema.parse(data).data
474
+ );
475
+ }
476
+ async create(params) {
477
+ const { approve, ...body } = params;
478
+ const url = approve ? `${this.endpoint}/create_delegated` : this.endpoint;
479
+ const { status, data, rawBody } = await this.apiFetch({
480
+ method: "POST",
481
+ url,
482
+ headers: { "Content-Type": "application/json" },
483
+ body: JSON.stringify(body)
484
+ });
485
+ if (status < 200 || status >= 300) {
486
+ this.throwApiError("create spend request", status, data, rawBody);
487
+ }
488
+ return this.parseResponse(
489
+ "create spend request",
490
+ status,
491
+ () => parseSpendRequest(data)
492
+ );
493
+ }
494
+ async update(id, params) {
495
+ const { status, data, rawBody } = await this.apiFetch({
496
+ method: "POST",
497
+ url: `${this.endpoint}/${id}`,
498
+ headers: { "Content-Type": "application/json" },
499
+ body: JSON.stringify(params)
500
+ });
501
+ if (status < 200 || status >= 300) {
502
+ this.throwApiError("update spend request", status, data, rawBody);
503
+ }
504
+ return this.parseResponse(
505
+ "update spend request",
506
+ status,
507
+ () => parseSpendRequest(data)
508
+ );
509
+ }
510
+ async cancel(id) {
511
+ const { status, data, rawBody } = await this.apiFetch({
512
+ method: "POST",
513
+ url: `${this.endpoint}/${id}/cancel`
514
+ });
515
+ if (status < 200 || status >= 300) {
516
+ this.throwApiError("cancel spend request", status, data, rawBody);
517
+ }
518
+ return this.parseResponse(
519
+ "cancel spend request",
520
+ status,
521
+ () => parseSpendRequest(data)
522
+ );
523
+ }
524
+ async requestApproval(id) {
525
+ const { status, data, rawBody } = await this.apiFetch({
526
+ method: "POST",
527
+ url: `${this.endpoint}/${id}/request_approval`
528
+ });
529
+ if (status < 200 || status >= 300) {
530
+ this.throwApiError("request approval", status, data, rawBody);
531
+ }
532
+ return this.parseResponse(
533
+ "request approval",
534
+ status,
535
+ () => requestApprovalResponseSchema.parse(data)
536
+ );
537
+ }
538
+ async retrieve(id, opts) {
539
+ const url = new URL(`${this.endpoint}/${id}`);
540
+ if (opts?.include?.length) {
541
+ url.searchParams.set("include", opts.include.join(","));
542
+ }
543
+ const { status, data, rawBody } = await this.apiFetch({
544
+ method: "GET",
545
+ url: url.toString()
546
+ });
547
+ if (status === 404) return null;
548
+ if (status < 200 || status >= 300) {
549
+ this.throwApiError("retrieve spend request", status, data, rawBody);
550
+ }
551
+ return this.parseResponse(
552
+ "retrieve spend request",
553
+ status,
554
+ () => parseSpendRequest(data)
555
+ );
556
+ }
557
+ };
558
+
559
+ // src/resources/transactions.ts
560
+ import { z as z7 } from "zod";
561
+ var transactionSchema = z7.looseObject({
562
+ id: z7.string(),
563
+ source_id: z7.string().nullable(),
564
+ amount: z7.number(),
565
+ currency: z7.string(),
566
+ created_date: z7.string(),
567
+ description: z7.string(),
568
+ origin: z7.enum(["link", "external_connection"]),
569
+ category: z7.string().nullable(),
570
+ status: z7.string()
571
+ });
572
+ var transactionsPageSchema = z7.union([
573
+ z7.array(transactionSchema).transform((data) => ({ data })),
574
+ z7.looseObject({
575
+ data: z7.array(transactionSchema),
576
+ has_more: z7.boolean().optional()
577
+ })
578
+ ]);
579
+ var TransactionsResource = class extends BaseResource {
580
+ constructor(options) {
581
+ super(options, "/transactions");
582
+ }
583
+ buildUrl(params) {
584
+ const url = new URL(this.endpoint);
585
+ if (params.limit !== void 0) {
586
+ url.searchParams.set("limit", String(params.limit));
587
+ }
588
+ if (params.starting_after !== void 0) {
589
+ url.searchParams.set("starting_after", params.starting_after);
590
+ }
591
+ if (params.ending_before !== void 0) {
592
+ url.searchParams.set("ending_before", params.ending_before);
593
+ }
594
+ if (params.start_date !== void 0) {
595
+ url.searchParams.set("date_start", params.start_date);
596
+ }
597
+ if (params.end_date !== void 0) {
598
+ url.searchParams.set("date_end", params.end_date);
599
+ }
600
+ if (params.category !== void 0) {
601
+ url.searchParams.set("category", params.category);
602
+ }
603
+ if (params.origin !== void 0) {
604
+ url.searchParams.set("origin", params.origin);
605
+ }
606
+ if (params.sources !== void 0) {
607
+ for (const source of params.sources) {
608
+ url.searchParams.append("sources[]", source);
609
+ }
610
+ }
611
+ return url.toString();
612
+ }
613
+ async list(params = {}) {
614
+ const { status, data, rawBody } = await this.apiFetch({
615
+ method: "GET",
616
+ url: this.buildUrl(params)
617
+ });
618
+ if (status < 200 || status >= 300) {
619
+ this.throwApiError("list transactions", status, data, rawBody);
620
+ }
621
+ return this.parseResponse(
622
+ "list transactions",
623
+ status,
624
+ () => transactionsPageSchema.parse(data)
625
+ );
626
+ }
627
+ };
628
+
629
+ // src/resources/user-info.ts
630
+ import { z as z8 } from "zod";
631
+ var userInfoSchema = z8.looseObject({
632
+ email: z8.string().nullable().optional(),
633
+ name: z8.string().nullable().optional(),
634
+ first_name: z8.string().nullable().optional(),
635
+ last_name: z8.string().nullable().optional(),
636
+ phone: z8.string().nullable().optional()
637
+ }).transform(({ email, name, first_name, last_name, phone }) => ({
638
+ email: email ?? null,
639
+ name: name ?? null,
640
+ first_name: first_name ?? null,
641
+ last_name: last_name ?? null,
642
+ phone: phone ?? null
643
+ }));
644
+ var UserInfoResource = class extends BaseResource {
645
+ constructor(options) {
646
+ super(options, "/userinfo");
647
+ }
648
+ async retrieve() {
649
+ const { status, data, rawBody } = await this.apiFetch({
650
+ method: "GET",
651
+ url: this.endpoint
652
+ });
653
+ if (status < 200 || status >= 300) {
654
+ this.throwApiError("retrieve user info", status, data, rawBody);
655
+ }
656
+ return this.parseResponse(
657
+ "retrieve user info",
658
+ status,
659
+ () => userInfoSchema.parse(data)
660
+ );
661
+ }
662
+ };
663
+
664
+ // src/resources/web-bot-auth.ts
665
+ import { z as z9 } from "zod";
666
+ var EXPIRY_BUFFER_MS = 3e4;
667
+ var webBotAuthBlockSchema = z9.looseObject({
668
+ signature: z9.string(),
669
+ signature_input: z9.string(),
670
+ signature_agent: z9.string(),
671
+ authority: z9.string(),
672
+ expires_at: z9.string()
673
+ });
674
+ var webBotAuthResponseSchema = z9.looseObject({
675
+ web_bot_auth: webBotAuthBlockSchema
676
+ });
677
+ var WebBotAuthResource = class extends BaseResource {
678
+ cache = /* @__PURE__ */ new Map();
679
+ constructor(options) {
680
+ super(options, "/web_bot_auth/sign");
681
+ }
682
+ async signUrl(url) {
683
+ let authority;
684
+ try {
685
+ authority = new URL(url).hostname;
686
+ } catch (error) {
687
+ throw new LinkSdkError(`Invalid URL: ${url}`, { cause: error });
688
+ }
689
+ const cached = this.cache.get(authority);
690
+ if (cached && Date.now() < cached.expiresAt - EXPIRY_BUFFER_MS) {
691
+ return cached.block;
692
+ }
693
+ const { status, data, rawBody } = await this.apiFetch({
694
+ method: "POST",
695
+ url: this.endpoint,
696
+ headers: { "Content-Type": "application/json" },
697
+ body: JSON.stringify({ url })
698
+ });
699
+ if (status < 200 || status >= 300) {
700
+ this.throwApiError("get web bot auth headers", status, data, rawBody);
701
+ }
702
+ const webBotAuth = this.parseResponse(
703
+ "get web bot auth headers",
704
+ status,
705
+ () => webBotAuthResponseSchema.parse(data).web_bot_auth
706
+ );
707
+ const expiresAt = Date.parse(webBotAuth.expires_at);
708
+ if (Number.isNaN(expiresAt)) {
709
+ throw new LinkSdkError(
710
+ `Credentials response has invalid expires_at: ${webBotAuth.expires_at}`
711
+ );
712
+ }
713
+ this.cache.set(authority, { block: webBotAuth, expiresAt });
714
+ return webBotAuth;
715
+ }
716
+ };
717
+
718
+ // src/client.ts
719
+ var Link = class {
720
+ spendRequests;
721
+ paymentMethods;
722
+ shippingAddresses;
723
+ userInfo;
724
+ transactions;
725
+ sources;
726
+ balances;
727
+ webBotAuth;
728
+ reports;
729
+ constructor(options) {
730
+ this.spendRequests = new SpendRequestResource(options);
731
+ this.paymentMethods = new PaymentMethodsResource(options);
732
+ this.shippingAddresses = new ShippingAddressResource(options);
733
+ this.userInfo = new UserInfoResource(options);
734
+ this.transactions = new TransactionsResource(options);
735
+ this.sources = new SourcesResource(options);
736
+ this.balances = new BalancesResource(options);
737
+ this.webBotAuth = new WebBotAuthResource(options);
738
+ this.reports = new ReportResource(options);
739
+ }
740
+ };
741
+ var client_default = Link;
742
+
743
+ // src/resources/interfaces.ts
744
+ var REPORT_OUTCOMES = ["success", "blocked", "abandoned"];
745
+ var REPORT_TAGS = [
746
+ "stripe_checkout",
747
+ "captcha",
748
+ "anti_bot_script",
749
+ "cdn_block",
750
+ "waf_block",
751
+ "dns_block",
752
+ "rate_limited",
753
+ "login_required",
754
+ "3ds_challenge",
755
+ "page_inaccessible",
756
+ "timeout",
757
+ "site_error",
758
+ "payment_declined",
759
+ "other"
760
+ ];
761
+ export {
762
+ Link,
763
+ LinkApiError,
764
+ LinkConfigurationError,
765
+ LinkResponseError,
766
+ LinkSdkError,
767
+ LinkTransportError,
768
+ REPORT_OUTCOMES,
769
+ REPORT_TAGS,
770
+ client_default as default,
771
+ getDuplicateSpendRequest
772
+ };
773
+ //# sourceMappingURL=index.js.map