@panorama-industries/effect-vercel-core 0.1.0 → 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/contracts/vercel-system-env.d.ts +9 -0
- package/dist/contracts/vercel-system-env.d.ts.map +1 -1
- package/dist/contracts/vercel-system-env.js.map +1 -1
- package/dist/http/vercel-api-errors.d.ts +27 -1
- package/dist/http/vercel-api-errors.d.ts.map +1 -1
- package/dist/http/vercel-api-errors.js +26 -6
- package/dist/http/vercel-api-errors.js.map +1 -1
- package/dist/http/vercel-api-retry.d.ts +8 -1
- package/dist/http/vercel-api-retry.d.ts.map +1 -1
- package/dist/http/vercel-api-retry.js +4 -2
- package/dist/http/vercel-api-retry.js.map +1 -1
- package/dist/implementation/vercel-system-env-live.d.ts.map +1 -1
- package/dist/implementation/vercel-system-env-live.js +6 -0
- package/dist/implementation/vercel-system-env-live.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/contracts/vercel-system-env.ts +12 -0
- package/src/http/vercel-api-errors.ts +67 -19
- package/src/http/vercel-api-retry.ts +11 -2
- package/src/implementation/vercel-system-env-live.ts +6 -0
- package/src/index.ts +2 -0
|
@@ -12,6 +12,15 @@ declare const VercelSystemEnv_base: Context.ServiceClass<VercelSystemEnv, "@effe
|
|
|
12
12
|
readonly target: Option.Option<VercelTarget>;
|
|
13
13
|
/** Deployment host name without a scheme (`VERCEL_URL`). */
|
|
14
14
|
readonly url: Option.Option<string>;
|
|
15
|
+
/** Host name of the Git branch's preview deployment, without a scheme (`VERCEL_BRANCH_URL`). */
|
|
16
|
+
readonly branchUrl: Option.Option<string>;
|
|
17
|
+
/**
|
|
18
|
+
* Host name of the project's production domain, without a scheme
|
|
19
|
+
* (`VERCEL_PROJECT_PRODUCTION_URL`). Vercel also sets it in preview deployments.
|
|
20
|
+
*/
|
|
21
|
+
readonly projectProductionUrl: Option.Option<string>;
|
|
22
|
+
/** True when the code runs on Vercel (`VERCEL` is `1`). */
|
|
23
|
+
readonly isVercel: boolean;
|
|
15
24
|
/** True when `NODE_ENV` is `development`, which enables local-development behavior. */
|
|
16
25
|
readonly isDevelopment: boolean;
|
|
17
26
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vercel-system-env.d.ts","sourceRoot":"","sources":["../../src/contracts/vercel-system-env.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAErC,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEjC,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;;IAW1F,qDAAqD;qBACpC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC;IAE5C,mEAAmE;2BAC5C,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC;IAElD,+DAA+D;wBAC3C,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC;IAE5C,wCAAwC;qBACvB,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC;IAE5C,4DAA4D;kBAC9C,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;IAEnC,uFAAuF;4BAC/D,OAAO;IAE/B;;;OAGG;yBACkB,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;;
|
|
1
|
+
{"version":3,"file":"vercel-system-env.d.ts","sourceRoot":"","sources":["../../src/contracts/vercel-system-env.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAErC,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEjC,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;;IAW1F,qDAAqD;qBACpC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC;IAE5C,mEAAmE;2BAC5C,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC;IAElD,+DAA+D;wBAC3C,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC;IAE5C,wCAAwC;qBACvB,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC;IAE5C,4DAA4D;kBAC9C,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;IAEnC,gGAAgG;wBAC5E,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;IAEzC;;;OAGG;mCAC4B,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;IAEpD,2DAA2D;uBACxC,OAAO;IAE1B,uFAAuF;4BAC/D,OAAO;IAE/B;;;OAGG;yBACkB,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;;AA3C9C;;;;;GAKG;AACH,qBAAa,eAAgB,SAAQ,oBAuCK;CAAG"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vercel-system-env.js","sourceRoot":"","sources":["../../src/contracts/vercel-system-env.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAIjC;;;;;GAKG;AACH,MAAM,OAAO,eAAgB,SAAQ,OAAO,CAAC,OAAO,
|
|
1
|
+
{"version":3,"file":"vercel-system-env.js","sourceRoot":"","sources":["../../src/contracts/vercel-system-env.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAIjC;;;;;GAKG;AACH,MAAM,OAAO,eAAgB,SAAQ,OAAO,CAAC,OAAO,EAuCjD,CAAC,qCAAqC,CAAC;CAAG"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { HttpClientError, HttpClientResponse } from "effect/http";
|
|
2
|
-
import { Effect } from "effect";
|
|
2
|
+
import { Effect, Option } from "effect";
|
|
3
|
+
import { Headers } from "effect/http";
|
|
3
4
|
import { VercelApiError } from "../contracts/vercel-api-error.ts";
|
|
4
5
|
/** Identifies the failed call in a `VercelApiError`. */
|
|
5
6
|
export interface VercelApiErrorOptions {
|
|
@@ -10,6 +11,31 @@ export interface VercelApiErrorOptions {
|
|
|
10
11
|
}
|
|
11
12
|
/** Reads the most useful human-readable message from an error response body. */
|
|
12
13
|
export declare const responseErrorMessage: (response: HttpClientResponse.HttpClientResponse) => Effect.Effect<string, never, never>;
|
|
14
|
+
/** A non-2xx response whose body the caller has already read. */
|
|
15
|
+
export interface VercelApiErrorStatus {
|
|
16
|
+
/** Traced operation name stored in `VercelApiError.operation`, such as `QueueApi.send`. */
|
|
17
|
+
readonly operation: string;
|
|
18
|
+
/** HTTP status of the response. */
|
|
19
|
+
readonly status: number;
|
|
20
|
+
/** Human-readable message for the reason, usually read from the response body. */
|
|
21
|
+
readonly message: string;
|
|
22
|
+
/** Machine-readable error code found in the response header or body, if any. */
|
|
23
|
+
readonly errorCode: Option.Option<string>;
|
|
24
|
+
/** Response headers; `Retry-After` is read from them on 429. */
|
|
25
|
+
readonly headers: Headers.Headers;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Build the `VercelApiError` matching a non-2xx status, for callers that read the body themselves
|
|
29
|
+
* (for example to find a domain error code in JSON before falling back to `VercelApiError`).
|
|
30
|
+
*
|
|
31
|
+
* Maps 400, 401, 403, 413, 429 (with `Retry-After`) and 5xx to their `Api*` reasons and any other
|
|
32
|
+
* status to `ApiUnexpectedResponse`, exactly like {@link vercelApiErrorFromResponse}.
|
|
33
|
+
*
|
|
34
|
+
* @param now - Current time in epoch milliseconds (`Clock.currentTimeMillis`), used to turn an
|
|
35
|
+
* HTTP-date `Retry-After` into a delay.
|
|
36
|
+
* @returns The error value; fail with it using `yield*` or `Effect.fail`.
|
|
37
|
+
*/
|
|
38
|
+
export declare const vercelApiErrorFromStatus: (response: VercelApiErrorStatus, now: number) => VercelApiError;
|
|
13
39
|
/**
|
|
14
40
|
* Fail with the `VercelApiError` matching a non-2xx response.
|
|
15
41
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vercel-api-errors.d.ts","sourceRoot":"","sources":["../../src/http/vercel-api-errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEvE,OAAO,EAAS,MAAM,
|
|
1
|
+
{"version":3,"file":"vercel-api-errors.d.ts","sourceRoot":"","sources":["../../src/http/vercel-api-errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEvE,OAAO,EAAS,MAAM,EAAS,MAAM,EAAkB,MAAM,QAAQ,CAAC;AACtE,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEtC,OAAO,EASL,cAAc,EACf,MAAM,kCAAkC,CAAC;AAE1C,wDAAwD;AACxD,MAAM,WAAW,qBAAqB;IACpC,2FAA2F;IAC3F,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAE3B,wFAAwF;IACxF,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/C;AAmBD,gFAAgF;AAChF,eAAO,MAAM,oBAAoB,0FAY/B,CAAC;AAEH,iEAAiE;AACjE,MAAM,WAAW,oBAAoB;IACnC,2FAA2F;IAC3F,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAE3B,mCAAmC;IACnC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAExB,kFAAkF;IAClF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB,gFAAgF;IAChF,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAE1C,gEAAgE;IAChE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC;CACnC;AAQD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,wBAAwB,aACzB,oBAAoB,OACzB,MAAM,KACV,cAsBF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,kIAmBrC,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,iCAAiC,UACrC,eAAe,CAAC,eAAe,WAC7B,qBAAqB,KAC7B,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,cAAc,CAsBrC,CAAC"}
|
|
@@ -24,7 +24,27 @@ export const responseErrorMessage = Effect.fnUntraced(function* (response) {
|
|
|
24
24
|
const trimmed = text.trim();
|
|
25
25
|
return contentType.startsWith("text/") && trimmed.length > 0 ? trimmed : fallback;
|
|
26
26
|
});
|
|
27
|
-
const retryAfterOf = (
|
|
27
|
+
const retryAfterOf = (headers, now) => Headers.get(headers, "retry-after").pipe(Option.flatMap((value) => Result.getSuccess(Headers.parseRetryAfter(value))), Option.map((retryAfter) => Headers.retryAfterDelay(retryAfter, now)));
|
|
28
|
+
/**
|
|
29
|
+
* Build the `VercelApiError` matching a non-2xx status, for callers that read the body themselves
|
|
30
|
+
* (for example to find a domain error code in JSON before falling back to `VercelApiError`).
|
|
31
|
+
*
|
|
32
|
+
* Maps 400, 401, 403, 413, 429 (with `Retry-After`) and 5xx to their `Api*` reasons and any other
|
|
33
|
+
* status to `ApiUnexpectedResponse`, exactly like {@link vercelApiErrorFromResponse}.
|
|
34
|
+
*
|
|
35
|
+
* @param now - Current time in epoch milliseconds (`Clock.currentTimeMillis`), used to turn an
|
|
36
|
+
* HTTP-date `Retry-After` into a delay.
|
|
37
|
+
* @returns The error value; fail with it using `yield*` or `Effect.fail`.
|
|
38
|
+
*/
|
|
39
|
+
export const vercelApiErrorFromStatus = (response, now) => {
|
|
40
|
+
const { message, status } = response;
|
|
41
|
+
const reason = Match.value(status).pipe(Match.when(400, () => new ApiBadRequest({ message })), Match.when(401, () => new ApiUnauthorized({ message })), Match.when(403, () => new ApiForbidden({ message })), Match.when(413, () => new ApiPayloadTooLarge({ message })), Match.when(429, () => new ApiRateLimited({ message, retryAfter: retryAfterOf(response.headers, now) })), Match.when((code) => code >= 500 && code <= 599, () => new ApiServerError({ message, status })), Match.orElse(() => new ApiUnexpectedResponse({ message, status })));
|
|
42
|
+
return new VercelApiError({
|
|
43
|
+
operation: response.operation,
|
|
44
|
+
errorCode: response.errorCode,
|
|
45
|
+
reason,
|
|
46
|
+
});
|
|
47
|
+
};
|
|
28
48
|
/**
|
|
29
49
|
* Fail with the `VercelApiError` matching a non-2xx response.
|
|
30
50
|
*
|
|
@@ -35,15 +55,15 @@ const retryAfterOf = (response, now) => Headers.get(response.headers, "retry-aft
|
|
|
35
55
|
export const vercelApiErrorFromResponse = Effect.fnUntraced(function* (response, options) {
|
|
36
56
|
const message = yield* responseErrorMessage(response);
|
|
37
57
|
const now = yield* Clock.currentTimeMillis;
|
|
38
|
-
|
|
39
|
-
const reason = Match.value(status).pipe(Match.when(400, () => new ApiBadRequest({ message })), Match.when(401, () => new ApiUnauthorized({ message })), Match.when(403, () => new ApiForbidden({ message })), Match.when(413, () => new ApiPayloadTooLarge({ message })), Match.when(429, () => new ApiRateLimited({ message, retryAfter: retryAfterOf(response, now) })), Match.when((code) => code >= 500 && code <= 599, () => new ApiServerError({ message, status })), Match.orElse(() => new ApiUnexpectedResponse({ message, status })));
|
|
40
|
-
return yield* new VercelApiError({
|
|
58
|
+
return yield* vercelApiErrorFromStatus({
|
|
41
59
|
operation: options.operation,
|
|
60
|
+
status: response.status,
|
|
61
|
+
message,
|
|
42
62
|
errorCode: options.errorCodeHeader === undefined
|
|
43
63
|
? Option.none()
|
|
44
64
|
: Headers.get(response.headers, options.errorCodeHeader),
|
|
45
|
-
|
|
46
|
-
});
|
|
65
|
+
headers: response.headers,
|
|
66
|
+
}, now);
|
|
47
67
|
});
|
|
48
68
|
/**
|
|
49
69
|
* Fail with the `VercelApiError` matching an `HttpClientError`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vercel-api-errors.js","sourceRoot":"","sources":["../../src/http/vercel-api-errors.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACtE,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEtC,OAAO,EACL,aAAa,EACb,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,eAAe,EACf,qBAAqB,EACrB,cAAc,GACf,MAAM,kCAAkC,CAAC;AAW1C,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC;IAC7B,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;IACvC,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC;IACnE,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;CAC1C,CAAC,CAAC;AAEH,MAAM,eAAe,GAAG,MAAM,CAAC,mBAAmB,CAAC,MAAM,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC,CAAC;AAErF,MAAM,eAAe,GAAG,CAAC,IAAe,EAAU,EAAE,CAClD,SAAS,IAAI,IAAI;IACf,CAAC,CAAC,IAAI,CAAC,OAAO;IACd,CAAC,CAAC,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ;QAC9B,CAAC,CAAC,IAAI,CAAC,KAAK;QACZ,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC;AAI3B,gFAAgF;AAChF,MAAM,CAAC,MAAM,oBAAoB,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,EAC7D,QAA+C;IAE/C,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;IAClE,MAAM,QAAQ,GAAG,QAAQ,QAAQ,CAAC,MAAM,EAAE,CAAC;IAC3C,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,eAAe,CAAC,CAAC;IACpE,IAAI,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5B,OAAO,QAAQ,CAAC,KAAK,CAAC;IACxB,CAAC;IACD,MAAM,WAAW,GAAG,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;IAC9F,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAC5B,OAAO,WAAW,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC;AACpF,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"vercel-api-errors.js","sourceRoot":"","sources":["../../src/http/vercel-api-errors.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,10 +5,17 @@ export interface VercelApiTransientRetryOptions {
|
|
|
5
5
|
readonly maxAttempts?: number | undefined;
|
|
6
6
|
/** First backoff delay, doubled (with jitter) on each retry. Defaults to 100 milliseconds. */
|
|
7
7
|
readonly baseDelay?: Duration.Input | undefined;
|
|
8
|
+
/**
|
|
9
|
+
* Decides which failures are retried, replacing the default (`VercelApiError` with an
|
|
10
|
+
* `ApiNetworkFailure` or `ApiRateLimited` reason). A rate-limited failure it accepts still waits
|
|
11
|
+
* its `Retry-After` delay.
|
|
12
|
+
*/
|
|
13
|
+
readonly isRetryable?: ((error: unknown) => boolean) | undefined;
|
|
8
14
|
}
|
|
9
15
|
/**
|
|
10
16
|
* Retry schedule for Vercel API calls that retries only transient failures:
|
|
11
|
-
* `VercelApiError` with an `ApiNetworkFailure` or `ApiRateLimited` reason
|
|
17
|
+
* `VercelApiError` with an `ApiNetworkFailure` or `ApiRateLimited` reason, unless `isRetryable`
|
|
18
|
+
* says otherwise.
|
|
12
19
|
*
|
|
13
20
|
* Waits the `Retry-After` delay of a rate-limited response when the service sent one, otherwise
|
|
14
21
|
* backs off exponentially with jitter. Use it with `Effect.retry(vercelApiTransientRetry())`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vercel-api-retry.d.ts","sourceRoot":"","sources":["../../src/http/vercel-api-retry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAA6B,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAIvE,kDAAkD;AAClD,MAAM,WAAW,8BAA8B;IAC7C,8DAA8D;IAC9D,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAE1C,8FAA8F;IAC9F,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,KAAK,GAAG,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"vercel-api-retry.d.ts","sourceRoot":"","sources":["../../src/http/vercel-api-retry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAA6B,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAIvE,kDAAkD;AAClD,MAAM,WAAW,8BAA8B;IAC7C,8DAA8D;IAC9D,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAE1C,8FAA8F;IAC9F,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,KAAK,GAAG,SAAS,CAAC;IAEhD;;;;OAIG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,GAAG,SAAS,CAAC;CAClE;AAYD;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,aACxB,8BAA8B,KACvC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAW9C,CAAC"}
|
|
@@ -8,13 +8,15 @@ const isTransient = (error) => error instanceof VercelApiError &&
|
|
|
8
8
|
Predicate.isTagged(error.reason, "ApiRateLimited"));
|
|
9
9
|
/**
|
|
10
10
|
* Retry schedule for Vercel API calls that retries only transient failures:
|
|
11
|
-
* `VercelApiError` with an `ApiNetworkFailure` or `ApiRateLimited` reason
|
|
11
|
+
* `VercelApiError` with an `ApiNetworkFailure` or `ApiRateLimited` reason, unless `isRetryable`
|
|
12
|
+
* says otherwise.
|
|
12
13
|
*
|
|
13
14
|
* Waits the `Retry-After` delay of a rate-limited response when the service sent one, otherwise
|
|
14
15
|
* backs off exponentially with jitter. Use it with `Effect.retry(vercelApiTransientRetry())`.
|
|
15
16
|
*/
|
|
16
17
|
export const vercelApiTransientRetry = (options) => {
|
|
17
18
|
const maxAttempts = options?.maxAttempts ?? 3;
|
|
18
|
-
|
|
19
|
+
const isRetryable = options?.isRetryable ?? isTransient;
|
|
20
|
+
return Schedule.exponential(options?.baseDelay ?? "100 millis").pipe(Schedule.jittered, Schedule.setInputType(), Schedule.while(({ input, attempt }) => attempt < maxAttempts && isRetryable(input)), Schedule.modifyDelay(({ input, duration }) => Effect.succeed(Option.getOrElse(retryAfterOf(input), () => duration))));
|
|
19
21
|
};
|
|
20
22
|
//# sourceMappingURL=vercel-api-retry.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vercel-api-retry.js","sourceRoot":"","sources":["../../src/http/vercel-api-retry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAEvE,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;
|
|
1
|
+
{"version":3,"file":"vercel-api-retry.js","sourceRoot":"","sources":["../../src/http/vercel-api-retry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAEvE,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAkBlE,MAAM,YAAY,GAAG,CAAC,KAAc,EAAoC,EAAE,CACxE,KAAK,YAAY,cAAc,IAAI,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,gBAAgB,CAAC;IACnF,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU;IACzB,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;AAEpB,MAAM,WAAW,GAAG,CAAC,KAAc,EAAW,EAAE,CAC9C,KAAK,YAAY,cAAc;IAC/B,CAAC,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,mBAAmB,CAAC;QACpD,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC;AAExD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CACrC,OAAwC,EACO,EAAE;IACjD,MAAM,WAAW,GAAG,OAAO,EAAE,WAAW,IAAI,CAAC,CAAC;IAC9C,MAAM,WAAW,GAAG,OAAO,EAAE,WAAW,IAAI,WAAW,CAAC;IACxD,OAAO,QAAQ,CAAC,WAAW,CAAC,OAAO,EAAE,SAAS,IAAI,YAAY,CAAC,CAAC,IAAI,CAClE,QAAQ,CAAC,QAAQ,EACjB,QAAQ,CAAC,YAAY,EAAW,EAChC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,OAAO,GAAG,WAAW,IAAI,WAAW,CAAC,KAAK,CAAC,CAAC,EACnF,QAAQ,CAAC,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,CAC3C,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,CAAC,CACtE,CACF,CAAC;AACJ,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vercel-system-env-live.d.ts","sourceRoot":"","sources":["../../src/implementation/vercel-system-env-live.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAU,KAAK,EAAU,MAAM,QAAQ,CAAC;AAGvD,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAEpE;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,
|
|
1
|
+
{"version":3,"file":"vercel-system-env-live.d.ts","sourceRoot":"","sources":["../../src/implementation/vercel-system-env-live.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAU,KAAK,EAAU,MAAM,QAAQ,CAAC;AAGvD,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAEpE;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,yDA4B/B,CAAC"}
|
|
@@ -14,6 +14,9 @@ export const VercelSystemEnvLive = Layer.effect(VercelSystemEnv, Effect.gen(func
|
|
|
14
14
|
projectId: Config.option(Config.schema(ProjectId, "VERCEL_PROJECT_ID")),
|
|
15
15
|
target: Config.option(Config.schema(VercelTarget, "VERCEL_ENV")),
|
|
16
16
|
url: Config.option(Config.String("VERCEL_URL")),
|
|
17
|
+
branchUrl: Config.option(Config.String("VERCEL_BRANCH_URL")),
|
|
18
|
+
projectProductionUrl: Config.option(Config.String("VERCEL_PROJECT_PRODUCTION_URL")),
|
|
19
|
+
vercel: Config.option(Config.String("VERCEL")),
|
|
17
20
|
nodeEnv: Config.option(Config.String("NODE_ENV")),
|
|
18
21
|
cliVersion: Config.option(Config.String("VERCEL_CLI_VERSION")),
|
|
19
22
|
});
|
|
@@ -23,6 +26,9 @@ export const VercelSystemEnvLive = Layer.effect(VercelSystemEnv, Effect.gen(func
|
|
|
23
26
|
projectId: env.projectId,
|
|
24
27
|
target: env.target,
|
|
25
28
|
url: env.url,
|
|
29
|
+
branchUrl: env.branchUrl,
|
|
30
|
+
projectProductionUrl: env.projectProductionUrl,
|
|
31
|
+
isVercel: Option.contains(env.vercel, "1"),
|
|
26
32
|
isDevelopment: Option.contains(env.nodeEnv, "development"),
|
|
27
33
|
cliVersion: env.cliVersion,
|
|
28
34
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vercel-system-env-live.js","sourceRoot":"","sources":["../../src/implementation/vercel-system-env-live.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEvD,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AACpG,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAEpE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,KAAK,CAAC,MAAM,CAC7C,eAAe,EACf,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;QAC5B,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,CAAC;QACnE,YAAY,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,sBAAsB,CAAC,CAAC;QAChF,SAAS,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC;QACvE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;QAChE,GAAG,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QAC/C,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QACjD,UAAU,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;KAC/D,CAAC,CAAC;IACH,OAAO,eAAe,CAAC,EAAE,CAAC;QACxB,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,YAAY,EAAE,GAAG,CAAC,YAAY;QAC9B,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,GAAG,EAAE,GAAG,CAAC,GAAG;QACZ,aAAa,EAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,EAAE,aAAa,CAAC;QAC1D,UAAU,EAAE,GAAG,CAAC,UAAU;KAC3B,CAAC,CAAC;AACL,CAAC,CAAC,CACH,CAAC"}
|
|
1
|
+
{"version":3,"file":"vercel-system-env-live.js","sourceRoot":"","sources":["../../src/implementation/vercel-system-env-live.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEvD,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AACpG,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAEpE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,KAAK,CAAC,MAAM,CAC7C,eAAe,EACf,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;QAC5B,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,CAAC;QACnE,YAAY,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,sBAAsB,CAAC,CAAC;QAChF,SAAS,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC;QACvE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;QAChE,GAAG,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QAC/C,SAAS,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;QAC5D,oBAAoB,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,+BAA+B,CAAC,CAAC;QACnF,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC9C,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QACjD,UAAU,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;KAC/D,CAAC,CAAC;IACH,OAAO,eAAe,CAAC,EAAE,CAAC;QACxB,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,YAAY,EAAE,GAAG,CAAC,YAAY;QAC9B,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,GAAG,EAAE,GAAG,CAAC,GAAG;QACZ,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,oBAAoB,EAAE,GAAG,CAAC,oBAAoB;QAC9C,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC;QAC1C,aAAa,EAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,EAAE,aAAa,CAAC;QAC1D,UAAU,EAAE,GAAG,CAAC,UAAU;KAC3B,CAAC,CAAC;AACL,CAAC,CAAC,CACH,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export { VercelSystemEnvLive } from "./implementation/vercel-system-env-live.ts"
|
|
|
6
6
|
export { VercelOidcLive, vercelOidcFromToken } from "./implementation/vercel-oidc-live.ts";
|
|
7
7
|
export { type VercelOidcRefreshingOptions, VercelOidcRefreshingLive, vercelOidcRefreshing, } from "./implementation/vercel-oidc-refreshing-live.ts";
|
|
8
8
|
export { type VercelApiClientOptions, withVercelApi } from "./http/with-vercel-api.ts";
|
|
9
|
-
export { type VercelApiErrorOptions, vercelApiErrorFromHttpClientError, vercelApiErrorFromResponse, } from "./http/vercel-api-errors.ts";
|
|
9
|
+
export { type VercelApiErrorOptions, type VercelApiErrorStatus, vercelApiErrorFromHttpClientError, vercelApiErrorFromResponse, vercelApiErrorFromStatus, } from "./http/vercel-api-errors.ts";
|
|
10
10
|
export { type VercelApiTransientRetryOptions, vercelApiTransientRetry, } from "./http/vercel-api-retry.ts";
|
|
11
11
|
export { spanLinkFromTraceparent, traceparentHeaders } from "./http/trace-context.ts";
|
|
12
12
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,kCAAkC,CAAC;AACjD,cAAc,4BAA4B,CAAC;AAC3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,4CAA4C,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAC3F,OAAO,EACL,KAAK,2BAA2B,EAChC,wBAAwB,EACxB,oBAAoB,GACrB,MAAM,iDAAiD,CAAC;AACzD,OAAO,EAAE,KAAK,sBAAsB,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AACvF,OAAO,EACL,KAAK,qBAAqB,EAC1B,iCAAiC,EACjC,0BAA0B,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,kCAAkC,CAAC;AACjD,cAAc,4BAA4B,CAAC;AAC3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,4CAA4C,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAC3F,OAAO,EACL,KAAK,2BAA2B,EAChC,wBAAwB,EACxB,oBAAoB,GACrB,MAAM,iDAAiD,CAAC;AACzD,OAAO,EAAE,KAAK,sBAAsB,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AACvF,OAAO,EACL,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,iCAAiC,EACjC,0BAA0B,EAC1B,wBAAwB,GACzB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,KAAK,8BAA8B,EACnC,uBAAuB,GACxB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ export { VercelSystemEnvLive } from "./implementation/vercel-system-env-live.js"
|
|
|
6
6
|
export { VercelOidcLive, vercelOidcFromToken } from "./implementation/vercel-oidc-live.js";
|
|
7
7
|
export { VercelOidcRefreshingLive, vercelOidcRefreshing, } from "./implementation/vercel-oidc-refreshing-live.js";
|
|
8
8
|
export { withVercelApi } from "./http/with-vercel-api.js";
|
|
9
|
-
export { vercelApiErrorFromHttpClientError, vercelApiErrorFromResponse, } from "./http/vercel-api-errors.js";
|
|
9
|
+
export { vercelApiErrorFromHttpClientError, vercelApiErrorFromResponse, vercelApiErrorFromStatus, } from "./http/vercel-api-errors.js";
|
|
10
10
|
export { vercelApiTransientRetry, } from "./http/vercel-api-retry.js";
|
|
11
11
|
export { spanLinkFromTraceparent, traceparentHeaders } from "./http/trace-context.js";
|
|
12
12
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,kCAAkC,CAAC;AACjD,cAAc,4BAA4B,CAAC;AAC3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,4CAA4C,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAC3F,OAAO,EAEL,wBAAwB,EACxB,oBAAoB,GACrB,MAAM,iDAAiD,CAAC;AACzD,OAAO,EAA+B,aAAa,EAAE,MAAM,2BAA2B,CAAC;AACvF,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,kCAAkC,CAAC;AACjD,cAAc,4BAA4B,CAAC;AAC3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,4CAA4C,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAC3F,OAAO,EAEL,wBAAwB,EACxB,oBAAoB,GACrB,MAAM,iDAAiD,CAAC;AACzD,OAAO,EAA+B,aAAa,EAAE,MAAM,2BAA2B,CAAC;AACvF,OAAO,EAGL,iCAAiC,EACjC,0BAA0B,EAC1B,wBAAwB,GACzB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAEL,uBAAuB,GACxB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "../../.vscode/schemas/package.schema.json",
|
|
3
3
|
"name": "@panorama-industries/effect-vercel-core",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.2.0",
|
|
5
5
|
"description": "Shared Effect building blocks for Vercel service SDKs: environment, OIDC credentials, API client, errors and test utilities.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"effect",
|
|
@@ -28,6 +28,18 @@ export class VercelSystemEnv extends Context.Service<
|
|
|
28
28
|
/** Deployment host name without a scheme (`VERCEL_URL`). */
|
|
29
29
|
readonly url: Option.Option<string>;
|
|
30
30
|
|
|
31
|
+
/** Host name of the Git branch's preview deployment, without a scheme (`VERCEL_BRANCH_URL`). */
|
|
32
|
+
readonly branchUrl: Option.Option<string>;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Host name of the project's production domain, without a scheme
|
|
36
|
+
* (`VERCEL_PROJECT_PRODUCTION_URL`). Vercel also sets it in preview deployments.
|
|
37
|
+
*/
|
|
38
|
+
readonly projectProductionUrl: Option.Option<string>;
|
|
39
|
+
|
|
40
|
+
/** True when the code runs on Vercel (`VERCEL` is `1`). */
|
|
41
|
+
readonly isVercel: boolean;
|
|
42
|
+
|
|
31
43
|
/** True when `NODE_ENV` is `development`, which enables local-development behavior. */
|
|
32
44
|
readonly isDevelopment: boolean;
|
|
33
45
|
|
|
@@ -56,46 +56,94 @@ export const responseErrorMessage = Effect.fnUntraced(function* (
|
|
|
56
56
|
return contentType.startsWith("text/") && trimmed.length > 0 ? trimmed : fallback;
|
|
57
57
|
});
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
/** A non-2xx response whose body the caller has already read. */
|
|
60
|
+
export interface VercelApiErrorStatus {
|
|
61
|
+
/** Traced operation name stored in `VercelApiError.operation`, such as `QueueApi.send`. */
|
|
62
|
+
readonly operation: string;
|
|
63
|
+
|
|
64
|
+
/** HTTP status of the response. */
|
|
65
|
+
readonly status: number;
|
|
66
|
+
|
|
67
|
+
/** Human-readable message for the reason, usually read from the response body. */
|
|
68
|
+
readonly message: string;
|
|
69
|
+
|
|
70
|
+
/** Machine-readable error code found in the response header or body, if any. */
|
|
71
|
+
readonly errorCode: Option.Option<string>;
|
|
72
|
+
|
|
73
|
+
/** Response headers; `Retry-After` is read from them on 429. */
|
|
74
|
+
readonly headers: Headers.Headers;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const retryAfterOf = (headers: Headers.Headers, now: number) =>
|
|
78
|
+
Headers.get(headers, "retry-after").pipe(
|
|
61
79
|
Option.flatMap((value) => Result.getSuccess(Headers.parseRetryAfter(value))),
|
|
62
80
|
Option.map((retryAfter) => Headers.retryAfterDelay(retryAfter, now)),
|
|
63
81
|
);
|
|
64
82
|
|
|
65
83
|
/**
|
|
66
|
-
*
|
|
84
|
+
* Build the `VercelApiError` matching a non-2xx status, for callers that read the body themselves
|
|
85
|
+
* (for example to find a domain error code in JSON before falling back to `VercelApiError`).
|
|
67
86
|
*
|
|
68
87
|
* Maps 400, 401, 403, 413, 429 (with `Retry-After`) and 5xx to their `Api*` reasons and any other
|
|
69
|
-
* status to `ApiUnexpectedResponse
|
|
70
|
-
*
|
|
88
|
+
* status to `ApiUnexpectedResponse`, exactly like {@link vercelApiErrorFromResponse}.
|
|
89
|
+
*
|
|
90
|
+
* @param now - Current time in epoch milliseconds (`Clock.currentTimeMillis`), used to turn an
|
|
91
|
+
* HTTP-date `Retry-After` into a delay.
|
|
92
|
+
* @returns The error value; fail with it using `yield*` or `Effect.fail`.
|
|
71
93
|
*/
|
|
72
|
-
export const
|
|
73
|
-
response:
|
|
74
|
-
|
|
75
|
-
):
|
|
76
|
-
const message =
|
|
77
|
-
const now = yield* Clock.currentTimeMillis;
|
|
78
|
-
const status = response.status;
|
|
94
|
+
export const vercelApiErrorFromStatus = (
|
|
95
|
+
response: VercelApiErrorStatus,
|
|
96
|
+
now: number,
|
|
97
|
+
): VercelApiError => {
|
|
98
|
+
const { message, status } = response;
|
|
79
99
|
const reason: VercelApiError["reason"] = Match.value(status).pipe(
|
|
80
100
|
Match.when(400, () => new ApiBadRequest({ message })),
|
|
81
101
|
Match.when(401, () => new ApiUnauthorized({ message })),
|
|
82
102
|
Match.when(403, () => new ApiForbidden({ message })),
|
|
83
103
|
Match.when(413, () => new ApiPayloadTooLarge({ message })),
|
|
84
|
-
Match.when(
|
|
104
|
+
Match.when(
|
|
105
|
+
429,
|
|
106
|
+
() => new ApiRateLimited({ message, retryAfter: retryAfterOf(response.headers, now) }),
|
|
107
|
+
),
|
|
85
108
|
Match.when(
|
|
86
109
|
(code) => code >= 500 && code <= 599,
|
|
87
110
|
() => new ApiServerError({ message, status }),
|
|
88
111
|
),
|
|
89
112
|
Match.orElse(() => new ApiUnexpectedResponse({ message, status })),
|
|
90
113
|
);
|
|
91
|
-
return
|
|
92
|
-
operation:
|
|
93
|
-
errorCode:
|
|
94
|
-
options.errorCodeHeader === undefined
|
|
95
|
-
? Option.none()
|
|
96
|
-
: Headers.get(response.headers, options.errorCodeHeader),
|
|
114
|
+
return new VercelApiError({
|
|
115
|
+
operation: response.operation,
|
|
116
|
+
errorCode: response.errorCode,
|
|
97
117
|
reason,
|
|
98
118
|
});
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Fail with the `VercelApiError` matching a non-2xx response.
|
|
123
|
+
*
|
|
124
|
+
* Maps 400, 401, 403, 413, 429 (with `Retry-After`) and 5xx to their `Api*` reasons and any other
|
|
125
|
+
* status to `ApiUnexpectedResponse`. Handle domain statuses such as 404 before delegating here.
|
|
126
|
+
* Consumes the response body to build the message.
|
|
127
|
+
*/
|
|
128
|
+
export const vercelApiErrorFromResponse = Effect.fnUntraced(function* (
|
|
129
|
+
response: HttpClientResponse.HttpClientResponse,
|
|
130
|
+
options: VercelApiErrorOptions,
|
|
131
|
+
): Effect.fn.Return<never, VercelApiError> {
|
|
132
|
+
const message = yield* responseErrorMessage(response);
|
|
133
|
+
const now = yield* Clock.currentTimeMillis;
|
|
134
|
+
return yield* vercelApiErrorFromStatus(
|
|
135
|
+
{
|
|
136
|
+
operation: options.operation,
|
|
137
|
+
status: response.status,
|
|
138
|
+
message,
|
|
139
|
+
errorCode:
|
|
140
|
+
options.errorCodeHeader === undefined
|
|
141
|
+
? Option.none()
|
|
142
|
+
: Headers.get(response.headers, options.errorCodeHeader),
|
|
143
|
+
headers: response.headers,
|
|
144
|
+
},
|
|
145
|
+
now,
|
|
146
|
+
);
|
|
99
147
|
});
|
|
100
148
|
|
|
101
149
|
/**
|
|
@@ -9,6 +9,13 @@ export interface VercelApiTransientRetryOptions {
|
|
|
9
9
|
|
|
10
10
|
/** First backoff delay, doubled (with jitter) on each retry. Defaults to 100 milliseconds. */
|
|
11
11
|
readonly baseDelay?: Duration.Input | undefined;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Decides which failures are retried, replacing the default (`VercelApiError` with an
|
|
15
|
+
* `ApiNetworkFailure` or `ApiRateLimited` reason). A rate-limited failure it accepts still waits
|
|
16
|
+
* its `Retry-After` delay.
|
|
17
|
+
*/
|
|
18
|
+
readonly isRetryable?: ((error: unknown) => boolean) | undefined;
|
|
12
19
|
}
|
|
13
20
|
|
|
14
21
|
const retryAfterOf = (error: unknown): Option.Option<Duration.Duration> =>
|
|
@@ -23,7 +30,8 @@ const isTransient = (error: unknown): boolean =>
|
|
|
23
30
|
|
|
24
31
|
/**
|
|
25
32
|
* Retry schedule for Vercel API calls that retries only transient failures:
|
|
26
|
-
* `VercelApiError` with an `ApiNetworkFailure` or `ApiRateLimited` reason
|
|
33
|
+
* `VercelApiError` with an `ApiNetworkFailure` or `ApiRateLimited` reason, unless `isRetryable`
|
|
34
|
+
* says otherwise.
|
|
27
35
|
*
|
|
28
36
|
* Waits the `Retry-After` delay of a rate-limited response when the service sent one, otherwise
|
|
29
37
|
* backs off exponentially with jitter. Use it with `Effect.retry(vercelApiTransientRetry())`.
|
|
@@ -32,10 +40,11 @@ export const vercelApiTransientRetry = (
|
|
|
32
40
|
options?: VercelApiTransientRetryOptions,
|
|
33
41
|
): Schedule.Schedule<Duration.Duration, unknown> => {
|
|
34
42
|
const maxAttempts = options?.maxAttempts ?? 3;
|
|
43
|
+
const isRetryable = options?.isRetryable ?? isTransient;
|
|
35
44
|
return Schedule.exponential(options?.baseDelay ?? "100 millis").pipe(
|
|
36
45
|
Schedule.jittered,
|
|
37
46
|
Schedule.setInputType<unknown>(),
|
|
38
|
-
Schedule.while(({ input, attempt }) => attempt < maxAttempts &&
|
|
47
|
+
Schedule.while(({ input, attempt }) => attempt < maxAttempts && isRetryable(input)),
|
|
39
48
|
Schedule.modifyDelay(({ input, duration }) =>
|
|
40
49
|
Effect.succeed(Option.getOrElse(retryAfterOf(input), () => duration)),
|
|
41
50
|
),
|
|
@@ -18,6 +18,9 @@ export const VercelSystemEnvLive = Layer.effect(
|
|
|
18
18
|
projectId: Config.option(Config.schema(ProjectId, "VERCEL_PROJECT_ID")),
|
|
19
19
|
target: Config.option(Config.schema(VercelTarget, "VERCEL_ENV")),
|
|
20
20
|
url: Config.option(Config.String("VERCEL_URL")),
|
|
21
|
+
branchUrl: Config.option(Config.String("VERCEL_BRANCH_URL")),
|
|
22
|
+
projectProductionUrl: Config.option(Config.String("VERCEL_PROJECT_PRODUCTION_URL")),
|
|
23
|
+
vercel: Config.option(Config.String("VERCEL")),
|
|
21
24
|
nodeEnv: Config.option(Config.String("NODE_ENV")),
|
|
22
25
|
cliVersion: Config.option(Config.String("VERCEL_CLI_VERSION")),
|
|
23
26
|
});
|
|
@@ -27,6 +30,9 @@ export const VercelSystemEnvLive = Layer.effect(
|
|
|
27
30
|
projectId: env.projectId,
|
|
28
31
|
target: env.target,
|
|
29
32
|
url: env.url,
|
|
33
|
+
branchUrl: env.branchUrl,
|
|
34
|
+
projectProductionUrl: env.projectProductionUrl,
|
|
35
|
+
isVercel: Option.contains(env.vercel, "1"),
|
|
30
36
|
isDevelopment: Option.contains(env.nodeEnv, "development"),
|
|
31
37
|
cliVersion: env.cliVersion,
|
|
32
38
|
});
|
package/src/index.ts
CHANGED
|
@@ -12,8 +12,10 @@ export {
|
|
|
12
12
|
export { type VercelApiClientOptions, withVercelApi } from "./http/with-vercel-api.ts";
|
|
13
13
|
export {
|
|
14
14
|
type VercelApiErrorOptions,
|
|
15
|
+
type VercelApiErrorStatus,
|
|
15
16
|
vercelApiErrorFromHttpClientError,
|
|
16
17
|
vercelApiErrorFromResponse,
|
|
18
|
+
vercelApiErrorFromStatus,
|
|
17
19
|
} from "./http/vercel-api-errors.ts";
|
|
18
20
|
export {
|
|
19
21
|
type VercelApiTransientRetryOptions,
|