@dunx/http 3.2.0 → 3.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-3j2n1n11.js +71 -0
- package/dist/chunk-3nbj06q8.js +174 -0
- package/dist/{chunk-9x3evk19.js → chunk-sz4pvqxy.js} +1 -70
- package/dist/client.js +124 -8
- package/dist/index.js +1156 -81
- package/dist/inspect.d.ts +9 -0
- package/dist/internal.d.ts +8 -25
- package/dist/internal.js +12 -84
- package/dist/server/application.d.ts +2 -3
- package/dist/server/html.d.ts +10 -0
- package/dist/ws/decorators.d.ts +1 -1
- package/dist/ws/postgres-relay.d.ts +0 -2
- package/dist/ws/relay.d.ts +8 -0
- package/package.json +2 -2
- package/dist/chunk-e8a9c6j2.js +0 -130
- package/dist/chunk-y85wcdhw.js +0 -1297
package/dist/inspect.d.ts
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
import { type Dependency, type ModuleRef } from '@dunx/core';
|
|
2
|
+
/**
|
|
3
|
+
* Routes and gateways read off the module graph, constructing nothing. The
|
|
4
|
+
* traversal is `@dunx/core`'s; here is the half needing this package's metadata -
|
|
5
|
+
* route markers, guards, `@Roles`/`@Public`, the gateway marker.
|
|
6
|
+
*
|
|
7
|
+
* `discoverRoutes` walks a prototype chain, and
|
|
8
|
+
* `Object.create(Controller.prototype)` is that chain with nothing behind it, so
|
|
9
|
+
* no constructor or dependency of one has to exist.
|
|
10
|
+
*/
|
|
2
11
|
export interface RouteInputs {
|
|
3
12
|
readonly body?: string;
|
|
4
13
|
readonly query?: string;
|
package/dist/internal.d.ts
CHANGED
|
@@ -3,34 +3,17 @@
|
|
|
3
3
|
*
|
|
4
4
|
* The barrel was a semver promise 173 symbols wide, which is more than this
|
|
5
5
|
* package meant to make. What stays public there is the surface an app writes
|
|
6
|
-
* against
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* against. What is here is the discovery readers and route metadata that
|
|
7
|
+
* `@dunx/dashboard`, `@dunx/mcp`, `@dunx/openapi` and `@dunx/testing` import.
|
|
8
|
+
*
|
|
9
|
+
* It held 62 symbols and 50 had no importer, all of them reachable from inside
|
|
10
|
+
* this package by relative import. Add one back when a sibling needs it.
|
|
11
11
|
*
|
|
12
12
|
* No stability promise attaches to this subpath.
|
|
13
13
|
*/
|
|
14
14
|
export { discoverRoutes, joinPath, type DiscoveredRoute, } from './route/discover.js';
|
|
15
|
-
export { defaultStatusFor
|
|
16
|
-
export { guardsOf } from './route/metadata.js';
|
|
15
|
+
export { defaultStatusFor } from './route/marker.js';
|
|
17
16
|
export { gatewaysOf, routesOf, type GatewayHandler, type GatewayNode, type RouteInputs, type RouteNode, } from './inspect.js';
|
|
18
17
|
export { buildContext } from './server/context.js';
|
|
19
|
-
export {
|
|
20
|
-
export {
|
|
21
|
-
export { compose } from './server/middleware.js';
|
|
22
|
-
export { assertNoCollisions, assertNoGatewayCollisions, buildRoutes, withUpgradeRoutes, type BunRoutes, type GuardResolver, type RouteMethod, type ServeRoutes, } from './server/routes.js';
|
|
23
|
-
export { normalizePrefix } from './static/options.js';
|
|
24
|
-
export { negotiate } from './compression/negotiate.js';
|
|
25
|
-
export { isCompressibleType } from './compression/options.js';
|
|
26
|
-
export { buildWebSocket, type UpgradeHandler, type WebSocketRuntime, } from './ws/adapter.js';
|
|
27
|
-
export { discoverGateway, discoverGateways, normalizePath, type DiscoveredGateway, type DiscoveredHandler, type Invoke, } from './ws/discover.js';
|
|
28
|
-
export { decode, encode } from './ws/envelope.js';
|
|
29
|
-
export { composeSocket, observe } from './ws/middleware.js';
|
|
30
|
-
export { HandlerKind, isGateway, type HandlerMeta } from './ws/marker.js';
|
|
31
|
-
export { defaultRelayUrl } from './ws/redis-relay.js';
|
|
32
|
-
export { decodeRelay, encodeRelay, type RelayFrame, type RelayPhase, } from './ws/relay.js';
|
|
33
|
-
export { buildGateways, buildRuntime, type GatewayRuntime, } from './ws/runtime.js';
|
|
34
|
-
export { HiddenHealthController } from './health/controller.js';
|
|
35
|
-
export { backoffDelay, executeWithRetry, isRetryableStatus, retryAfterMs, } from './client/retry.js';
|
|
36
|
-
export { isJsonBody, isPlainObject, safeStringify } from './client/json.js';
|
|
18
|
+
export { embedJson } from './server/html.js';
|
|
19
|
+
export { isGateway } from './ws/marker.js';
|
package/dist/internal.js
CHANGED
|
@@ -1,56 +1,22 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
3
|
HIDDEN,
|
|
4
|
-
HandlerKind,
|
|
5
|
-
HiddenHealthController,
|
|
6
4
|
PUBLIC,
|
|
7
5
|
ROLES,
|
|
8
|
-
assertNoCollisions,
|
|
9
|
-
assertNoGatewayCollisions,
|
|
10
6
|
buildContext,
|
|
11
|
-
buildGateways,
|
|
12
|
-
buildRoutes,
|
|
13
|
-
buildRuntime,
|
|
14
|
-
buildWebSocket,
|
|
15
|
-
compose,
|
|
16
|
-
composeSocket,
|
|
17
|
-
decode,
|
|
18
|
-
decodeRelay,
|
|
19
|
-
defaultRelayUrl,
|
|
20
7
|
defaultStatusFor,
|
|
21
8
|
discoverGateway,
|
|
22
|
-
discoverGateways,
|
|
23
9
|
discoverRoutes,
|
|
24
|
-
encode,
|
|
25
|
-
encodeRelay,
|
|
26
|
-
guardsOf,
|
|
27
|
-
isCompressibleType,
|
|
28
|
-
isErrorFilter,
|
|
29
10
|
isGateway,
|
|
30
|
-
joinPath
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
normalizePrefix,
|
|
34
|
-
observe,
|
|
35
|
-
preflight,
|
|
36
|
-
toErrorMapper,
|
|
37
|
-
withCors,
|
|
38
|
-
withUpgradeRoutes
|
|
39
|
-
} from "./chunk-y85wcdhw.js";
|
|
40
|
-
import {
|
|
41
|
-
backoffDelay,
|
|
42
|
-
executeWithRetry,
|
|
43
|
-
isJsonBody,
|
|
44
|
-
isPlainObject,
|
|
45
|
-
isRetryableStatus,
|
|
46
|
-
retryAfterMs,
|
|
47
|
-
safeStringify
|
|
48
|
-
} from "./chunk-e8a9c6j2.js";
|
|
49
|
-
import"./chunk-9x3evk19.js";
|
|
11
|
+
joinPath
|
|
12
|
+
} from "./chunk-3nbj06q8.js";
|
|
13
|
+
import"./chunk-sz4pvqxy.js";
|
|
50
14
|
// src/inspect.ts
|
|
51
15
|
import {
|
|
16
|
+
classOf,
|
|
52
17
|
collectModules,
|
|
53
18
|
dependenciesOf,
|
|
19
|
+
inertInstance,
|
|
54
20
|
readControllers
|
|
55
21
|
} from "@dunx/core";
|
|
56
22
|
var vendorOf = (schema) => schema?.["~standard"]?.vendor;
|
|
@@ -85,11 +51,10 @@ var nodeFor = (route, module) => ({
|
|
|
85
51
|
responses: Object.keys(route.options?.response ?? {}).map(Number)
|
|
86
52
|
});
|
|
87
53
|
var routesOf = (root) => collectModules(root).flatMap((module) => readControllers(module).flatMap((controller) => {
|
|
88
|
-
|
|
89
|
-
return discoverRoutes(Object.create(prototype)).map((route) => nodeFor(route, module.name));
|
|
54
|
+
return discoverRoutes(inertInstance(controller)).map((route) => nodeFor(route, module.name));
|
|
90
55
|
}));
|
|
91
56
|
var gatewayFor = (ctor, module) => {
|
|
92
|
-
const { name, path, handlers } = discoverGateway(
|
|
57
|
+
const { name, path, handlers } = discoverGateway(inertInstance(ctor));
|
|
93
58
|
return {
|
|
94
59
|
name,
|
|
95
60
|
path,
|
|
@@ -102,53 +67,16 @@ var gatewayFor = (ctor, module) => {
|
|
|
102
67
|
}))
|
|
103
68
|
};
|
|
104
69
|
};
|
|
105
|
-
var
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
return entry.provider.kind === "class" ? entry.provider.ctor : undefined;
|
|
109
|
-
};
|
|
110
|
-
var gatewaysOf = (root) => collectModules(root).flatMap((module) => (module.options.providers ?? []).map(classOf).filter((ctor) => ctor !== undefined).filter(isGateway).map((ctor) => gatewayFor(ctor, module.name)));
|
|
70
|
+
var gatewaysOf = (root) => collectModules(root).flatMap((module) => (module.options.providers ?? []).map((entry) => classOf(entry)?.ctor).filter((ctor) => ctor !== undefined).filter(isGateway).map((ctor) => gatewayFor(ctor, module.name)));
|
|
71
|
+
// src/server/html.ts
|
|
72
|
+
var embedJson = (value) => JSON.stringify(value).replaceAll("<", "\\u003c");
|
|
111
73
|
export {
|
|
112
|
-
HandlerKind,
|
|
113
|
-
HiddenHealthController,
|
|
114
|
-
assertNoCollisions,
|
|
115
|
-
assertNoGatewayCollisions,
|
|
116
|
-
backoffDelay,
|
|
117
74
|
buildContext,
|
|
118
|
-
buildGateways,
|
|
119
|
-
buildRoutes,
|
|
120
|
-
buildRuntime,
|
|
121
|
-
buildWebSocket,
|
|
122
|
-
compose,
|
|
123
|
-
composeSocket,
|
|
124
|
-
decode,
|
|
125
|
-
decodeRelay,
|
|
126
|
-
defaultRelayUrl,
|
|
127
75
|
defaultStatusFor,
|
|
128
|
-
discoverGateway,
|
|
129
|
-
discoverGateways,
|
|
130
76
|
discoverRoutes,
|
|
131
|
-
|
|
132
|
-
encodeRelay,
|
|
133
|
-
executeWithRetry,
|
|
77
|
+
embedJson,
|
|
134
78
|
gatewaysOf,
|
|
135
|
-
guardsOf,
|
|
136
|
-
isCompressibleType,
|
|
137
|
-
isErrorFilter,
|
|
138
79
|
isGateway,
|
|
139
|
-
isJsonBody,
|
|
140
|
-
isPlainObject,
|
|
141
|
-
isRetryableStatus,
|
|
142
80
|
joinPath,
|
|
143
|
-
|
|
144
|
-
normalizePath,
|
|
145
|
-
normalizePrefix,
|
|
146
|
-
observe,
|
|
147
|
-
preflight,
|
|
148
|
-
retryAfterMs,
|
|
149
|
-
routesOf,
|
|
150
|
-
safeStringify,
|
|
151
|
-
toErrorMapper,
|
|
152
|
-
withCors,
|
|
153
|
-
withUpgradeRoutes
|
|
81
|
+
routesOf
|
|
154
82
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { BunRequest } from 'bun';
|
|
2
|
-
import { type App, type AppOptions, type Ctor, type InjectionToken, type ModuleRef, type ShutdownHookOptions, type ShutdownSignal } from '@dunx/core';
|
|
2
|
+
import { ShutdownAware, type App, type AppOptions, type Ctor, type InjectionToken, type ModuleRef, type ShutdownHookOptions, type ShutdownSignal } from '@dunx/core';
|
|
3
3
|
import { type DiscoveredRoute } from '../route/discover.js';
|
|
4
4
|
import type { WebSocketRuntime } from '../ws/adapter.js';
|
|
5
5
|
import type { SocketLoggingOptions } from '../ws/logging.js';
|
|
@@ -120,7 +120,7 @@ export interface HttpApp extends App {
|
|
|
120
120
|
readonly gatewayPaths: readonly string[];
|
|
121
121
|
listen(port?: number): Promise<string>;
|
|
122
122
|
}
|
|
123
|
-
export declare class HttpApplication implements HttpApp {
|
|
123
|
+
export declare class HttpApplication extends ShutdownAware implements HttpApp {
|
|
124
124
|
#private;
|
|
125
125
|
/** Forwarded from the container so an app can log scope warnings at boot. */
|
|
126
126
|
readonly warnings: readonly string[];
|
|
@@ -146,5 +146,4 @@ export declare class HttpApplication implements HttpApp {
|
|
|
146
146
|
* `closed` unresolved; failures are collected and thrown at the end.
|
|
147
147
|
*/
|
|
148
148
|
shutdown(): Promise<void>;
|
|
149
|
-
enableShutdownHooks(signals?: readonly ShutdownSignal[], options?: ShutdownHookOptions): this;
|
|
150
149
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Serialise a value for a `<script type="application/json">` block.
|
|
3
|
+
*
|
|
4
|
+
* `<` is the only character that can end the data block early, and escaping it as
|
|
5
|
+
* `\u003c` keeps the text valid JSON - the parser sees the same document either
|
|
6
|
+
* way. `@dunx/dashboard` and `@dunx/openapi` both inline a model into a page they
|
|
7
|
+
* serve, and both had a copy of this; if the escaping ever proves insufficient,
|
|
8
|
+
* one fix should cover both pages.
|
|
9
|
+
*/
|
|
10
|
+
export declare const embedJson: (value: unknown) => string;
|
package/dist/ws/decorators.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import type { HandlerMethod } from '@dunx/core';
|
|
1
2
|
type GatewayTarget = abstract new (...args: never[]) => object;
|
|
2
|
-
type HandlerMethod = (...args: never[]) => unknown;
|
|
3
3
|
export declare const Gateway: (path?: string) => <T extends GatewayTarget>(target: T) => T;
|
|
4
4
|
/** Runs before the socket exists. Return a `Response` to refuse the upgrade. */
|
|
5
5
|
export declare const OnUpgrade: () => <T extends HandlerMethod>(value: T) => T;
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import { WsRelay } from './relay.js';
|
|
2
|
-
/** The same fallback chain `Bun.SQL` uses when given no URL. */
|
|
3
|
-
export declare const defaultPostgresRelayUrl: () => string;
|
|
4
2
|
export interface PostgresRelayOptions {
|
|
5
3
|
/** @default `$POSTGRES_URL`, `$DATABASE_URL`, then `postgres://localhost:5432` */
|
|
6
4
|
readonly url?: string;
|
package/dist/ws/relay.d.ts
CHANGED
|
@@ -27,6 +27,14 @@ export interface PubSubRelay {
|
|
|
27
27
|
*/
|
|
28
28
|
close?(): unknown;
|
|
29
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Both relays take a URL, and a wrong scheme has to fail here rather than at
|
|
32
|
+
* connect time: an absence-tolerant relay swallows the connection error, so a
|
|
33
|
+
* typo would degrade silently to single-node fan-out.
|
|
34
|
+
*/
|
|
35
|
+
export declare const assertRelayUrl: (url: string, protocols: readonly string[], example: string) => string;
|
|
36
|
+
/** The URL with any password removed, for logs and error messages. */
|
|
37
|
+
export declare const redactUrl: (url: string) => string;
|
|
30
38
|
/** Which relay call failed, so one message can say what degraded. */
|
|
31
39
|
export type RelayPhase = 'publish' | 'subscribe' | 'close';
|
|
32
40
|
export interface RelayOptions {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dunx/http",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.1",
|
|
4
4
|
"description": "Bun.serve adapter for the dunx framework: controllers, middleware and WebSocket gateways",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bun",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"@opentelemetry/sdk-trace-node": "2.11.0"
|
|
66
66
|
},
|
|
67
67
|
"peerDependencies": {
|
|
68
|
-
"@dunx/core": "^3.2.
|
|
68
|
+
"@dunx/core": "^3.2.1",
|
|
69
69
|
"@types/bun": ">=1.3.0"
|
|
70
70
|
},
|
|
71
71
|
"peerDependenciesMeta": {
|
package/dist/chunk-e8a9c6j2.js
DELETED
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
// @bun
|
|
2
|
-
import {
|
|
3
|
-
HttpStatusCode
|
|
4
|
-
} from "./chunk-9x3evk19.js";
|
|
5
|
-
|
|
6
|
-
// src/client/errors.ts
|
|
7
|
-
import { AppError } from "@dunx/core";
|
|
8
|
-
|
|
9
|
-
class FetchError extends AppError {
|
|
10
|
-
status;
|
|
11
|
-
statusText;
|
|
12
|
-
body;
|
|
13
|
-
response;
|
|
14
|
-
name = "FetchError";
|
|
15
|
-
constructor(status, statusText, body, response) {
|
|
16
|
-
super(`HTTP ${status} ${statusText} from ${response.method} ${response.url}`);
|
|
17
|
-
this.status = status;
|
|
18
|
-
this.statusText = statusText;
|
|
19
|
-
this.body = body;
|
|
20
|
-
this.response = response;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
Object.defineProperty(FetchError, Symbol.for("dunx.deps"), { value: () => [{ unresolved: "override readonly status: number" }, { unresolved: "readonly statusText: string" }, { unresolved: "readonly body: unknown" }, { unresolved: `readonly response: {
|
|
24
|
-
readonly method: string;
|
|
25
|
-
readonly url: string;
|
|
26
|
-
readonly headers: Headers;
|
|
27
|
-
}` }] });
|
|
28
|
-
|
|
29
|
-
class FetchTransportError extends AppError {
|
|
30
|
-
response;
|
|
31
|
-
aborted;
|
|
32
|
-
name = "FetchTransportError";
|
|
33
|
-
constructor(response, aborted, options) {
|
|
34
|
-
super(`${response.method} ${response.url} failed: ${aborted ? "aborted" : "transport error"}`, options);
|
|
35
|
-
this.response = response;
|
|
36
|
-
this.aborted = aborted;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
Object.defineProperty(FetchTransportError, Symbol.for("dunx.deps"), { value: () => [{ unresolved: "readonly response: { readonly method: string; readonly url: string }" }, { unresolved: "readonly aborted: boolean" }, ErrorOptions] });
|
|
40
|
-
|
|
41
|
-
// src/client/json.ts
|
|
42
|
-
var safeStringify = (value) => {
|
|
43
|
-
const seen = new WeakSet;
|
|
44
|
-
return JSON.stringify(value, (_key, entry) => {
|
|
45
|
-
if (typeof entry === "object" && entry !== null) {
|
|
46
|
-
if (seen.has(entry))
|
|
47
|
-
return "[Circular]";
|
|
48
|
-
seen.add(entry);
|
|
49
|
-
}
|
|
50
|
-
return entry;
|
|
51
|
-
});
|
|
52
|
-
};
|
|
53
|
-
var isPlainObject = (value) => {
|
|
54
|
-
if (typeof value !== "object" || value === null)
|
|
55
|
-
return false;
|
|
56
|
-
const proto = Object.getPrototypeOf(value);
|
|
57
|
-
return proto === Object.prototype || proto === null;
|
|
58
|
-
};
|
|
59
|
-
var isJsonBody = (payload) => {
|
|
60
|
-
if (payload === null || payload === undefined)
|
|
61
|
-
return false;
|
|
62
|
-
if (typeof payload !== "object")
|
|
63
|
-
return typeof payload !== "string";
|
|
64
|
-
return !(payload instanceof FormData || payload instanceof URLSearchParams || payload instanceof Blob || payload instanceof ArrayBuffer || payload instanceof ReadableStream || ArrayBuffer.isView(payload));
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
// src/client/retry.ts
|
|
68
|
-
var uniform = () => {
|
|
69
|
-
const buffer = new Uint32Array(1);
|
|
70
|
-
crypto.getRandomValues(buffer);
|
|
71
|
-
return (buffer[0] ?? 0) / 2 ** 32;
|
|
72
|
-
};
|
|
73
|
-
var backoffDelay = (attempt, { baseMs, power = 2, jitterMs = 1000, maxMs = 30000 }) => Math.min(baseMs * power ** attempt + uniform() * jitterMs, maxMs);
|
|
74
|
-
var retryAfterMs = (headers, now = Date.now()) => {
|
|
75
|
-
const header = headers.get("retry-after");
|
|
76
|
-
if (header === null)
|
|
77
|
-
return;
|
|
78
|
-
const seconds = Number(header);
|
|
79
|
-
if (Number.isFinite(seconds))
|
|
80
|
-
return Math.max(0, seconds * 1000);
|
|
81
|
-
const at = Date.parse(header);
|
|
82
|
-
return Number.isNaN(at) ? undefined : Math.max(0, at - now);
|
|
83
|
-
};
|
|
84
|
-
var isRetryableStatus = (status) => status >= HttpStatusCode.INTERNAL_SERVER_ERROR || status === HttpStatusCode.REQUEST_TIMEOUT || status === HttpStatusCode.TOO_MANY_REQUESTS;
|
|
85
|
-
var decide = (error, attempt, options) => {
|
|
86
|
-
const {
|
|
87
|
-
retryDelayMs = 1000,
|
|
88
|
-
backoff,
|
|
89
|
-
shouldRetryOnStatus = isRetryableStatus,
|
|
90
|
-
respectRetryAfter = true
|
|
91
|
-
} = options;
|
|
92
|
-
const computed = backoffDelay(attempt, { baseMs: retryDelayMs, ...backoff });
|
|
93
|
-
if (error instanceof FetchTransportError) {
|
|
94
|
-
return { retry: !error.aborted, delayMs: computed };
|
|
95
|
-
}
|
|
96
|
-
if (error instanceof FetchError) {
|
|
97
|
-
if (!shouldRetryOnStatus(error.status))
|
|
98
|
-
return { retry: false, delayMs: 0 };
|
|
99
|
-
const asked = respectRetryAfter ? retryAfterMs(error.response.headers) : undefined;
|
|
100
|
-
const maxMs = backoff?.maxMs ?? 30000;
|
|
101
|
-
return {
|
|
102
|
-
retry: true,
|
|
103
|
-
delayMs: asked === undefined ? computed : Math.min(asked, maxMs)
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
return { retry: true, delayMs: computed };
|
|
107
|
-
};
|
|
108
|
-
var executeWithRetry = async (operation, options = {}) => {
|
|
109
|
-
const { maxRetries = 3, onAttempt, onError, onSuccess } = options;
|
|
110
|
-
let lastError;
|
|
111
|
-
for (let attempt = 0;attempt <= maxRetries; attempt += 1) {
|
|
112
|
-
onAttempt?.(attempt + 1, attempt > 0);
|
|
113
|
-
try {
|
|
114
|
-
const result = await operation();
|
|
115
|
-
onSuccess?.(result, attempt + 1);
|
|
116
|
-
return result;
|
|
117
|
-
} catch (error) {
|
|
118
|
-
lastError = error;
|
|
119
|
-
const { retry, delayMs } = decide(error, attempt, options);
|
|
120
|
-
const willRetry = retry && attempt < maxRetries;
|
|
121
|
-
onError?.(error, attempt + 1, willRetry);
|
|
122
|
-
if (!willRetry)
|
|
123
|
-
throw error;
|
|
124
|
-
await Bun.sleep(delayMs);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
throw lastError;
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
export { FetchError, FetchTransportError, safeStringify, isPlainObject, isJsonBody, backoffDelay, retryAfterMs, isRetryableStatus, executeWithRetry };
|