@distilled.cloud/stripe 1.0.0-rc.13 → 1.0.0-rc.14
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/lib/credentials.d.ts +2 -2
- package/lib/credentials.d.ts.map +1 -1
- package/lib/credentials.js +2 -2
- package/lib/credentials.js.map +1 -1
- package/lib/errors.d.ts +1 -1
- package/lib/errors.d.ts.map +1 -1
- package/lib/errors.js +1 -1
- package/lib/errors.js.map +1 -1
- package/lib/protocol.d.ts +3 -3
- package/lib/protocol.d.ts.map +1 -1
- package/lib/protocol.js +13 -18
- package/lib/protocol.js.map +1 -1
- package/lib/protocol.test.d.ts +2 -0
- package/lib/protocol.test.d.ts.map +1 -0
- package/lib/protocol.test.js +353 -0
- package/lib/protocol.test.js.map +1 -0
- package/lib/retry.d.ts +1 -1
- package/lib/retry.d.ts.map +1 -1
- package/lib/retry.js +1 -1
- package/lib/retry.js.map +1 -1
- package/lib/services/stripe.d.ts +4970 -1571
- package/lib/services/stripe.d.ts.map +1 -1
- package/lib/services/stripe.js +5608 -6855
- package/lib/services/stripe.js.map +1 -1
- package/lib/traits.d.ts.map +1 -1
- package/lib/traits.js +1 -1
- package/lib/traits.js.map +1 -1
- package/lib/webhooks.d.ts.map +1 -1
- package/lib/webhooks.js +4 -4
- package/lib/webhooks.js.map +1 -1
- package/package.json +12 -10
- package/src/credentials.ts +6 -7
- package/src/errors.ts +20 -29
- package/src/protocol.test.ts +519 -0
- package/src/protocol.ts +54 -104
- package/src/retry.ts +2 -8
- package/src/services/stripe.ts +40402 -47044
- package/src/traits.ts +1 -4
- package/src/webhooks.ts +11 -30
- package/lib/response-validation.test.d.ts +0 -4
- package/lib/response-validation.test.d.ts.map +0 -1
- package/lib/response-validation.test.js +0 -38
- package/lib/response-validation.test.js.map +0 -1
- package/src/response-validation.test.ts +0 -52
package/src/protocol.ts
CHANGED
|
@@ -1,3 +1,28 @@
|
|
|
1
|
+
import * as API from "@distilled.cloud/core/api";
|
|
2
|
+
import {
|
|
3
|
+
ConfigError,
|
|
4
|
+
HTTP_STATUS_MAP,
|
|
5
|
+
InternalServerError,
|
|
6
|
+
type DefaultErrors as CoreDefaultErrors,
|
|
7
|
+
BadRequest,
|
|
8
|
+
Conflict,
|
|
9
|
+
Forbidden,
|
|
10
|
+
Locked,
|
|
11
|
+
NotFound,
|
|
12
|
+
UnprocessableEntity,
|
|
13
|
+
} from "@distilled.cloud/core/errors";
|
|
14
|
+
import {
|
|
15
|
+
buildRequest,
|
|
16
|
+
getAnn,
|
|
17
|
+
getProps,
|
|
18
|
+
hasPropAnn,
|
|
19
|
+
mapKeys,
|
|
20
|
+
matchTypedError,
|
|
21
|
+
nameOf,
|
|
22
|
+
} from "@distilled.cloud/core/protocol-http";
|
|
23
|
+
import { unwrapRedactedDeep, wrapSensitive } from "@distilled.cloud/core/protocol-rest";
|
|
24
|
+
import { validateResponse } from "@distilled.cloud/core/response-validation";
|
|
25
|
+
import { parseRetryAfterForStatus } from "@distilled.cloud/core/retry-after";
|
|
1
26
|
/**
|
|
2
27
|
* StripeProtocol — hand-written.
|
|
3
28
|
*
|
|
@@ -31,43 +56,15 @@
|
|
|
31
56
|
*/
|
|
32
57
|
import * as Context from "effect/Context";
|
|
33
58
|
import * as Effect from "effect/Effect";
|
|
34
|
-
import * as
|
|
35
|
-
import * as Option from "effect/Option";
|
|
36
|
-
import * as Redacted from "effect/Redacted";
|
|
37
|
-
import type * as AST from "effect/SchemaAST";
|
|
59
|
+
import * as HttpBody from "effect/http/HttpBody";
|
|
38
60
|
import type * as HttpClient from "effect/http/HttpClient";
|
|
39
61
|
import type * as HttpClientError from "effect/http/HttpClientError";
|
|
40
|
-
import * as HttpBody from "effect/http/HttpBody";
|
|
41
62
|
import * as HttpClientRequest from "effect/http/HttpClientRequest";
|
|
42
63
|
import type * as HttpClientResponse from "effect/http/HttpClientResponse";
|
|
43
|
-
import * as
|
|
44
|
-
import
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
getProps,
|
|
48
|
-
hasPropAnn,
|
|
49
|
-
mapKeys,
|
|
50
|
-
matchTypedError,
|
|
51
|
-
nameOf,
|
|
52
|
-
} from "@distilled.cloud/core/protocol-http";
|
|
53
|
-
import {
|
|
54
|
-
unwrapRedactedDeep,
|
|
55
|
-
wrapSensitive,
|
|
56
|
-
} from "@distilled.cloud/core/protocol-rest";
|
|
57
|
-
import {
|
|
58
|
-
ConfigError,
|
|
59
|
-
HTTP_STATUS_MAP,
|
|
60
|
-
InternalServerError,
|
|
61
|
-
type DefaultErrors as CoreDefaultErrors,
|
|
62
|
-
BadRequest,
|
|
63
|
-
Conflict,
|
|
64
|
-
Forbidden,
|
|
65
|
-
Locked,
|
|
66
|
-
NotFound,
|
|
67
|
-
UnprocessableEntity,
|
|
68
|
-
} from "@distilled.cloud/core/errors";
|
|
69
|
-
import { validateResponse } from "@distilled.cloud/core/response-validation";
|
|
70
|
-
import { parseRetryAfterForStatus } from "@distilled.cloud/core/retry-after";
|
|
64
|
+
import * as Layer from "effect/Layer";
|
|
65
|
+
import * as Option from "effect/Option";
|
|
66
|
+
import * as Redacted from "effect/Redacted";
|
|
67
|
+
import type * as AST from "effect/SchemaAST";
|
|
71
68
|
import { Credentials, type Config } from "./credentials.ts";
|
|
72
69
|
import {
|
|
73
70
|
ApiError,
|
|
@@ -113,10 +110,9 @@ export type StripeRequestOptions = {
|
|
|
113
110
|
* call argument; v1 operations take only their input, so options travel on
|
|
114
111
|
* the calling fiber's context instead — see {@link withRequestOptions}.
|
|
115
112
|
*/
|
|
116
|
-
export class RequestOptions extends Context.Service<
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
>()("StripeRequestOptions") {}
|
|
113
|
+
export class RequestOptions extends Context.Service<RequestOptions, StripeRequestOptions>()(
|
|
114
|
+
"StripeRequestOptions",
|
|
115
|
+
) {}
|
|
120
116
|
|
|
121
117
|
/**
|
|
122
118
|
* Provide {@link StripeRequestOptions} to every Stripe API call below it.
|
|
@@ -186,8 +182,7 @@ export type StripeOpContext = Credentials | HttpClient.HttpClient;
|
|
|
186
182
|
// Stripe failures are real typed errors that an operation re-surfaces via its
|
|
187
183
|
// `errors: [...]` list / StripeOpError annotation. Fail with the instance and
|
|
188
184
|
// erase the error type here; the generated annotations reintroduce it.
|
|
189
|
-
const fail = (e: unknown): Effect.Effect<never> =>
|
|
190
|
-
Effect.fail(e) as Effect.Effect<never>;
|
|
185
|
+
const fail = (e: unknown): Effect.Effect<never> => Effect.fail(e) as Effect.Effect<never>;
|
|
191
186
|
|
|
192
187
|
// =============================================================================
|
|
193
188
|
// Stripe query / form encoding (ported from distilled v0)
|
|
@@ -195,36 +190,23 @@ const fail = (e: unknown): Effect.Effect<never> =>
|
|
|
195
190
|
|
|
196
191
|
const BODYLESS = new Set(["GET", "HEAD"]);
|
|
197
192
|
|
|
198
|
-
const appendQueryValue = (
|
|
199
|
-
query: URLSearchParams,
|
|
200
|
-
key: string,
|
|
201
|
-
value: unknown,
|
|
202
|
-
): void => {
|
|
193
|
+
const appendQueryValue = (query: URLSearchParams, key: string, value: unknown): void => {
|
|
203
194
|
if (value === undefined || value === null) return;
|
|
204
|
-
query.append(
|
|
205
|
-
key,
|
|
206
|
-
typeof value === "boolean" ? (value ? "true" : "false") : String(value),
|
|
207
|
-
);
|
|
195
|
+
query.append(key, typeof value === "boolean" ? (value ? "true" : "false") : String(value));
|
|
208
196
|
};
|
|
209
197
|
|
|
210
198
|
/**
|
|
211
199
|
* Stripe GET-query bracket expansion (v0's `appendStripeQuery`): arrays as
|
|
212
200
|
* repeated `key[]` entries, nested objects as `key[nested]`, recursively.
|
|
213
201
|
*/
|
|
214
|
-
const appendStripeQuery = (
|
|
215
|
-
query: URLSearchParams,
|
|
216
|
-
key: string,
|
|
217
|
-
value: unknown,
|
|
218
|
-
): void => {
|
|
202
|
+
const appendStripeQuery = (query: URLSearchParams, key: string, value: unknown): void => {
|
|
219
203
|
if (value === undefined || value === null) return;
|
|
220
204
|
if (Array.isArray(value)) {
|
|
221
205
|
for (const item of value) appendQueryValue(query, `${key}[]`, item);
|
|
222
206
|
return;
|
|
223
207
|
}
|
|
224
208
|
if (typeof value === "object" && !(value instanceof Date)) {
|
|
225
|
-
for (const [nestedKey, nestedValue] of Object.entries(
|
|
226
|
-
value as Record<string, unknown>,
|
|
227
|
-
)) {
|
|
209
|
+
for (const [nestedKey, nestedValue] of Object.entries(value as Record<string, unknown>)) {
|
|
228
210
|
appendStripeQuery(query, `${key}[${nestedKey}]`, nestedValue);
|
|
229
211
|
}
|
|
230
212
|
return;
|
|
@@ -262,20 +244,13 @@ const flattenToFormPairs = (
|
|
|
262
244
|
typeof item === "object" &&
|
|
263
245
|
!Array.isArray(item)
|
|
264
246
|
) {
|
|
265
|
-
pairs.push(
|
|
266
|
-
...flattenToFormPairs(
|
|
267
|
-
item as Record<string, unknown>,
|
|
268
|
-
`${fullKey}[${i}]`,
|
|
269
|
-
),
|
|
270
|
-
);
|
|
247
|
+
pairs.push(...flattenToFormPairs(item as Record<string, unknown>, `${fullKey}[${i}]`));
|
|
271
248
|
} else if (item !== undefined && item !== null) {
|
|
272
249
|
pairs.push([`${fullKey}[${i}]`, String(item)]);
|
|
273
250
|
}
|
|
274
251
|
}
|
|
275
252
|
} else if (typeof value === "object") {
|
|
276
|
-
pairs.push(
|
|
277
|
-
...flattenToFormPairs(value as Record<string, unknown>, fullKey),
|
|
278
|
-
);
|
|
253
|
+
pairs.push(...flattenToFormPairs(value as Record<string, unknown>, fullKey));
|
|
279
254
|
} else if (typeof value === "boolean") {
|
|
280
255
|
pairs.push([fullKey, value ? "true" : "false"]);
|
|
281
256
|
} else {
|
|
@@ -304,19 +279,11 @@ const buildFormUrlEncoded = (body: Record<string, unknown>): string => {
|
|
|
304
279
|
// per-call RequestOptions) from the calling fiber's context on every
|
|
305
280
|
// request. The requirement is erased at this boundary and reintroduced for
|
|
306
281
|
// callers by the generated `StripeOpContext` annotations.
|
|
307
|
-
const encode = ({
|
|
308
|
-
input,
|
|
309
|
-
inputAst,
|
|
310
|
-
}: {
|
|
311
|
-
readonly input: unknown;
|
|
312
|
-
readonly inputAst: AST.AST;
|
|
313
|
-
}) =>
|
|
282
|
+
const encode = ({ input, inputAst }: { readonly input: unknown; readonly inputAst: AST.AST }) =>
|
|
314
283
|
Effect.gen(function* () {
|
|
315
284
|
const resolveCredentials = yield* Credentials;
|
|
316
285
|
const creds = yield* resolveCredentials as Effect.Effect<Config>;
|
|
317
|
-
const options = Option.getOrUndefined(
|
|
318
|
-
yield* Effect.serviceOption(RequestOptions),
|
|
319
|
-
);
|
|
286
|
+
const options = Option.getOrUndefined(yield* Effect.serviceOption(RequestOptions));
|
|
320
287
|
|
|
321
288
|
const http = getAnn(inputAst, httpSymbol) as HttpTrait | undefined;
|
|
322
289
|
if (!http) {
|
|
@@ -341,10 +308,7 @@ const encode = ({
|
|
|
341
308
|
});
|
|
342
309
|
}
|
|
343
310
|
|
|
344
|
-
const inputObj = (unwrapRedactedDeep(input) ?? {}) as Record<
|
|
345
|
-
string,
|
|
346
|
-
unknown
|
|
347
|
-
>;
|
|
311
|
+
const inputObj = (unwrapRedactedDeep(input) ?? {}) as Record<string, unknown>;
|
|
348
312
|
const headers: Record<string, string> = { ...baseHeaders };
|
|
349
313
|
const body: Record<string, unknown> = {};
|
|
350
314
|
const query = new URLSearchParams();
|
|
@@ -405,10 +369,7 @@ const encode = ({
|
|
|
405
369
|
http.contentType === "form-urlencoded"
|
|
406
370
|
? request.pipe(
|
|
407
371
|
HttpClientRequest.setBody(
|
|
408
|
-
HttpBody.text(
|
|
409
|
-
buildFormUrlEncoded(body),
|
|
410
|
-
"application/x-www-form-urlencoded",
|
|
411
|
-
),
|
|
372
|
+
HttpBody.text(buildFormUrlEncoded(body), "application/x-www-form-urlencoded"),
|
|
412
373
|
),
|
|
413
374
|
)
|
|
414
375
|
: // The /v2/* operations are plain JSON.
|
|
@@ -421,22 +382,17 @@ const encode = ({
|
|
|
421
382
|
// Response decoding / error matching (ported from v0's matchError)
|
|
422
383
|
// =============================================================================
|
|
423
384
|
|
|
424
|
-
const str = (v: unknown): string | undefined =>
|
|
425
|
-
typeof v === "string" ? v : undefined;
|
|
385
|
+
const str = (v: unknown): string | undefined => (typeof v === "string" ? v : undefined);
|
|
426
386
|
|
|
427
387
|
// Structural views of the status maps for numeric lookup (the const objects'
|
|
428
388
|
// per-status class types don't unify without widening the constructor).
|
|
429
|
-
const stripeStatusMap: Readonly<
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
Record<number, (new (args: any) => unknown) | undefined>
|
|
434
|
-
> = HTTP_STATUS_MAP;
|
|
389
|
+
const stripeStatusMap: Readonly<Record<number, (new (args: any) => unknown) | undefined>> =
|
|
390
|
+
STRIPE_HTTP_STATUS_MAP;
|
|
391
|
+
const coreStatusMap: Readonly<Record<number, (new (args: any) => unknown) | undefined>> =
|
|
392
|
+
HTTP_STATUS_MAP;
|
|
435
393
|
|
|
436
394
|
/** Stripe's `{ error: { type, message, … } }` object, when present. */
|
|
437
|
-
const stripeErrorEnvelope = (
|
|
438
|
-
errorBody: unknown,
|
|
439
|
-
): Record<string, unknown> | undefined => {
|
|
395
|
+
const stripeErrorEnvelope = (errorBody: unknown): Record<string, unknown> | undefined => {
|
|
440
396
|
const envelope =
|
|
441
397
|
errorBody !== null && typeof errorBody === "object"
|
|
442
398
|
? (errorBody as Record<string, unknown>).error
|
|
@@ -506,15 +462,11 @@ const matchStripeError = (
|
|
|
506
462
|
return new StripeErrorClass({
|
|
507
463
|
message: common.message,
|
|
508
464
|
code: common.code,
|
|
509
|
-
...(str(err.decline_code) !== undefined
|
|
510
|
-
? { decline_code: str(err.decline_code) }
|
|
511
|
-
: {}),
|
|
465
|
+
...(str(err.decline_code) !== undefined ? { decline_code: str(err.decline_code) } : {}),
|
|
512
466
|
...(str(err.charge) !== undefined ? { charge: str(err.charge) } : {}),
|
|
513
467
|
...(str(err.param) !== undefined ? { param: str(err.param) } : {}),
|
|
514
468
|
...(common.doc_url !== undefined ? { doc_url: common.doc_url } : {}),
|
|
515
|
-
...(common.request_log_url !== undefined
|
|
516
|
-
? { request_log_url: common.request_log_url }
|
|
517
|
-
: {}),
|
|
469
|
+
...(common.request_log_url !== undefined ? { request_log_url: common.request_log_url } : {}),
|
|
518
470
|
});
|
|
519
471
|
}
|
|
520
472
|
|
|
@@ -575,8 +527,7 @@ const decode = ({
|
|
|
575
527
|
if (status >= 400) {
|
|
576
528
|
const errorBody: unknown = nonJson ? text : json;
|
|
577
529
|
const message =
|
|
578
|
-
stripeErrorMessage(errorBody) ??
|
|
579
|
-
(nonJson && text.trim() ? text.trim() : `HTTP ${status}`);
|
|
530
|
+
stripeErrorMessage(errorBody) ?? (nonJson && text.trim() ? text.trim() : `HTTP ${status}`);
|
|
580
531
|
const typed = matchTypedError(errorClasses, status, [{ message }], {
|
|
581
532
|
body: nonJson ? text : json,
|
|
582
533
|
headers,
|
|
@@ -601,8 +552,7 @@ export const StripeProtocol: Layer.Layer<API.Protocol> = Layer.succeed(
|
|
|
601
552
|
API.Protocol,
|
|
602
553
|
API.Protocol.of({
|
|
603
554
|
// Erase encode's Credentials requirement (see comment above).
|
|
604
|
-
encode: (args) =>
|
|
605
|
-
encode(args) as Effect.Effect<HttpClientRequest.HttpClientRequest>,
|
|
555
|
+
encode: (args) => encode(args) as Effect.Effect<HttpClientRequest.HttpClientRequest>,
|
|
606
556
|
decode,
|
|
607
557
|
}),
|
|
608
558
|
);
|
package/src/retry.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type Policy, throttlingFactory, transientFactory } from "@distilled.cloud/core/retry";
|
|
1
2
|
/**
|
|
2
3
|
* Stripe retry configuration.
|
|
3
4
|
*
|
|
@@ -16,11 +17,6 @@
|
|
|
16
17
|
import * as Context from "effect/Context";
|
|
17
18
|
import * as Effect from "effect/Effect";
|
|
18
19
|
import * as Layer from "effect/Layer";
|
|
19
|
-
import {
|
|
20
|
-
type Policy,
|
|
21
|
-
throttlingFactory,
|
|
22
|
-
transientFactory,
|
|
23
|
-
} from "@distilled.cloud/core/retry";
|
|
24
20
|
|
|
25
21
|
export {
|
|
26
22
|
type Options,
|
|
@@ -43,9 +39,7 @@ export const policy = (optionsOrFactory: Policy) =>
|
|
|
43
39
|
Effect.provide(Layer.succeed(Retry, optionsOrFactory));
|
|
44
40
|
|
|
45
41
|
/** Disables all automatic retries. */
|
|
46
|
-
export const none = Effect.provide(
|
|
47
|
-
Layer.succeed(Retry, { while: () => false }),
|
|
48
|
-
);
|
|
42
|
+
export const none = Effect.provide(Layer.succeed(Retry, { while: () => false }));
|
|
49
43
|
|
|
50
44
|
/** Apply the throttling retry policy (retries throttling errors indefinitely). */
|
|
51
45
|
export const throttling = policy(throttlingFactory);
|