@fragno-dev/db 0.1.13 → 0.1.15
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/.turbo/turbo-build.log +179 -132
- package/CHANGELOG.md +30 -0
- package/dist/adapters/adapters.d.ts +27 -1
- package/dist/adapters/adapters.d.ts.map +1 -1
- package/dist/adapters/adapters.js.map +1 -1
- package/dist/adapters/drizzle/drizzle-adapter.d.ts +5 -1
- package/dist/adapters/drizzle/drizzle-adapter.d.ts.map +1 -1
- package/dist/adapters/drizzle/drizzle-adapter.js +15 -3
- package/dist/adapters/drizzle/drizzle-adapter.js.map +1 -1
- package/dist/adapters/drizzle/drizzle-query.js +7 -5
- package/dist/adapters/drizzle/drizzle-query.js.map +1 -1
- package/dist/adapters/drizzle/drizzle-uow-compiler.d.ts +0 -1
- package/dist/adapters/drizzle/drizzle-uow-compiler.d.ts.map +1 -1
- package/dist/adapters/drizzle/drizzle-uow-compiler.js +76 -44
- package/dist/adapters/drizzle/drizzle-uow-compiler.js.map +1 -1
- package/dist/adapters/drizzle/drizzle-uow-decoder.js +23 -16
- package/dist/adapters/drizzle/drizzle-uow-decoder.js.map +1 -1
- package/dist/adapters/drizzle/drizzle-uow-executor.js +18 -7
- package/dist/adapters/drizzle/drizzle-uow-executor.js.map +1 -1
- package/dist/adapters/drizzle/generate.d.ts +4 -1
- package/dist/adapters/drizzle/generate.d.ts.map +1 -1
- package/dist/adapters/drizzle/generate.js +11 -18
- package/dist/adapters/drizzle/generate.js.map +1 -1
- package/dist/adapters/drizzle/shared.d.ts +14 -1
- package/dist/adapters/drizzle/shared.d.ts.map +1 -0
- package/dist/adapters/kysely/kysely-adapter.d.ts +5 -1
- package/dist/adapters/kysely/kysely-adapter.d.ts.map +1 -1
- package/dist/adapters/kysely/kysely-adapter.js +14 -3
- package/dist/adapters/kysely/kysely-adapter.js.map +1 -1
- package/dist/adapters/kysely/kysely-query-builder.js +1 -1
- package/dist/adapters/kysely/kysely-query-compiler.js +3 -2
- package/dist/adapters/kysely/kysely-query-compiler.js.map +1 -1
- package/dist/adapters/kysely/kysely-query.d.ts +1 -0
- package/dist/adapters/kysely/kysely-query.d.ts.map +1 -1
- package/dist/adapters/kysely/kysely-query.js +28 -19
- package/dist/adapters/kysely/kysely-query.js.map +1 -1
- package/dist/adapters/kysely/kysely-shared.d.ts +14 -0
- package/dist/adapters/kysely/kysely-shared.d.ts.map +1 -0
- package/dist/adapters/kysely/kysely-shared.js +16 -1
- package/dist/adapters/kysely/kysely-shared.js.map +1 -1
- package/dist/adapters/kysely/kysely-uow-compiler.js +68 -16
- package/dist/adapters/kysely/kysely-uow-compiler.js.map +1 -1
- package/dist/adapters/kysely/kysely-uow-executor.js +8 -4
- package/dist/adapters/kysely/kysely-uow-executor.js.map +1 -1
- package/dist/adapters/kysely/migration/execute-base.js +1 -1
- package/dist/adapters/kysely/migration/execute-base.js.map +1 -1
- package/dist/db-fragment-definition-builder.d.ts +152 -0
- package/dist/db-fragment-definition-builder.d.ts.map +1 -0
- package/dist/db-fragment-definition-builder.js +137 -0
- package/dist/db-fragment-definition-builder.js.map +1 -0
- package/dist/fragments/internal-fragment.d.ts +19 -0
- package/dist/fragments/internal-fragment.d.ts.map +1 -0
- package/dist/fragments/internal-fragment.js +39 -0
- package/dist/fragments/internal-fragment.js.map +1 -0
- package/dist/migration-engine/generation-engine.d.ts.map +1 -1
- package/dist/migration-engine/generation-engine.js +35 -15
- package/dist/migration-engine/generation-engine.js.map +1 -1
- package/dist/mod.d.ts +8 -18
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +7 -34
- package/dist/mod.js.map +1 -1
- package/dist/node_modules/.pnpm/rou3@0.7.8/node_modules/rou3/dist/index.js +165 -0
- package/dist/node_modules/.pnpm/rou3@0.7.8/node_modules/rou3/dist/index.js.map +1 -0
- package/dist/packages/fragno/dist/api/bind-services.js +20 -0
- package/dist/packages/fragno/dist/api/bind-services.js.map +1 -0
- package/dist/packages/fragno/dist/api/error.js +48 -0
- package/dist/packages/fragno/dist/api/error.js.map +1 -0
- package/dist/packages/fragno/dist/api/fragment-definition-builder.js +320 -0
- package/dist/packages/fragno/dist/api/fragment-definition-builder.js.map +1 -0
- package/dist/packages/fragno/dist/api/fragment-instantiator.js +487 -0
- package/dist/packages/fragno/dist/api/fragment-instantiator.js.map +1 -0
- package/dist/packages/fragno/dist/api/fragno-response.js +73 -0
- package/dist/packages/fragno/dist/api/fragno-response.js.map +1 -0
- package/dist/packages/fragno/dist/api/internal/response-stream.js +81 -0
- package/dist/packages/fragno/dist/api/internal/response-stream.js.map +1 -0
- package/dist/packages/fragno/dist/api/internal/route.js +10 -0
- package/dist/packages/fragno/dist/api/internal/route.js.map +1 -0
- package/dist/packages/fragno/dist/api/mutable-request-state.js +97 -0
- package/dist/packages/fragno/dist/api/mutable-request-state.js.map +1 -0
- package/dist/packages/fragno/dist/api/request-context-storage.js +43 -0
- package/dist/packages/fragno/dist/api/request-context-storage.js.map +1 -0
- package/dist/packages/fragno/dist/api/request-input-context.js +118 -0
- package/dist/packages/fragno/dist/api/request-input-context.js.map +1 -0
- package/dist/packages/fragno/dist/api/request-middleware.js +83 -0
- package/dist/packages/fragno/dist/api/request-middleware.js.map +1 -0
- package/dist/packages/fragno/dist/api/request-output-context.js +119 -0
- package/dist/packages/fragno/dist/api/request-output-context.js.map +1 -0
- package/dist/packages/fragno/dist/api/route.js +17 -0
- package/dist/packages/fragno/dist/api/route.js.map +1 -0
- package/dist/packages/fragno/dist/internal/symbols.js +10 -0
- package/dist/packages/fragno/dist/internal/symbols.js.map +1 -0
- package/dist/query/cursor.d.ts +10 -2
- package/dist/query/cursor.d.ts.map +1 -1
- package/dist/query/cursor.js +11 -4
- package/dist/query/cursor.js.map +1 -1
- package/dist/query/execute-unit-of-work.d.ts +123 -0
- package/dist/query/execute-unit-of-work.d.ts.map +1 -0
- package/dist/query/execute-unit-of-work.js +184 -0
- package/dist/query/execute-unit-of-work.js.map +1 -0
- package/dist/query/query.d.ts +3 -3
- package/dist/query/query.d.ts.map +1 -1
- package/dist/query/result-transform.js +4 -2
- package/dist/query/result-transform.js.map +1 -1
- package/dist/query/retry-policy.d.ts +88 -0
- package/dist/query/retry-policy.d.ts.map +1 -0
- package/dist/query/retry-policy.js +61 -0
- package/dist/query/retry-policy.js.map +1 -0
- package/dist/query/unit-of-work.d.ts +171 -32
- package/dist/query/unit-of-work.d.ts.map +1 -1
- package/dist/query/unit-of-work.js +530 -133
- package/dist/query/unit-of-work.js.map +1 -1
- package/dist/schema/serialize.js +12 -7
- package/dist/schema/serialize.js.map +1 -1
- package/dist/with-database.d.ts +28 -0
- package/dist/with-database.d.ts.map +1 -0
- package/dist/with-database.js +34 -0
- package/dist/with-database.js.map +1 -0
- package/package.json +10 -3
- package/src/adapters/adapters.ts +30 -0
- package/src/adapters/drizzle/drizzle-adapter-pglite.test.ts +86 -17
- package/src/adapters/drizzle/drizzle-adapter-sqlite.test.ts +291 -7
- package/src/adapters/drizzle/drizzle-adapter.test.ts +3 -51
- package/src/adapters/drizzle/drizzle-adapter.ts +35 -7
- package/src/adapters/drizzle/drizzle-query.ts +25 -15
- package/src/adapters/drizzle/drizzle-uow-compiler-mysql.test.ts +1442 -0
- package/src/adapters/drizzle/drizzle-uow-compiler-sqlite.test.ts +1414 -0
- package/src/adapters/drizzle/drizzle-uow-compiler.test.ts +78 -61
- package/src/adapters/drizzle/drizzle-uow-compiler.ts +123 -42
- package/src/adapters/drizzle/drizzle-uow-decoder.ts +34 -27
- package/src/adapters/drizzle/drizzle-uow-executor.ts +41 -8
- package/src/adapters/drizzle/generate.test.ts +102 -269
- package/src/adapters/drizzle/generate.ts +12 -30
- package/src/adapters/drizzle/test-utils.ts +36 -5
- package/src/adapters/kysely/kysely-adapter-pglite.test.ts +66 -22
- package/src/adapters/kysely/kysely-adapter-sqlite.test.ts +156 -0
- package/src/adapters/kysely/kysely-adapter.ts +25 -2
- package/src/adapters/kysely/kysely-query-compiler.ts +3 -8
- package/src/adapters/kysely/kysely-query.ts +57 -37
- package/src/adapters/kysely/kysely-shared.ts +34 -0
- package/src/adapters/kysely/kysely-uow-compiler.test.ts +62 -74
- package/src/adapters/kysely/kysely-uow-compiler.ts +92 -24
- package/src/adapters/kysely/kysely-uow-executor.ts +26 -7
- package/src/adapters/kysely/kysely-uow-joins.test.ts +33 -50
- package/src/adapters/kysely/migration/execute-base.ts +1 -1
- package/src/db-fragment-definition-builder.test.ts +887 -0
- package/src/db-fragment-definition-builder.ts +506 -0
- package/src/db-fragment-instantiator.test.ts +467 -0
- package/src/db-fragment-integration.test.ts +408 -0
- package/src/fragments/internal-fragment.test.ts +160 -0
- package/src/fragments/internal-fragment.ts +85 -0
- package/src/migration-engine/generation-engine.test.ts +58 -15
- package/src/migration-engine/generation-engine.ts +78 -25
- package/src/mod.ts +35 -43
- package/src/query/cursor.test.ts +119 -0
- package/src/query/cursor.ts +17 -4
- package/src/query/execute-unit-of-work.test.ts +1310 -0
- package/src/query/execute-unit-of-work.ts +463 -0
- package/src/query/query.ts +4 -4
- package/src/query/result-transform.test.ts +129 -0
- package/src/query/result-transform.ts +4 -1
- package/src/query/retry-policy.test.ts +217 -0
- package/src/query/retry-policy.ts +141 -0
- package/src/query/unit-of-work-coordinator.test.ts +833 -0
- package/src/query/unit-of-work-types.test.ts +15 -2
- package/src/query/unit-of-work.test.ts +878 -200
- package/src/query/unit-of-work.ts +963 -321
- package/src/schema/serialize.ts +22 -11
- package/src/with-database.ts +140 -0
- package/tsdown.config.ts +1 -0
- package/dist/fragment.d.ts +0 -54
- package/dist/fragment.d.ts.map +0 -1
- package/dist/fragment.js +0 -92
- package/dist/fragment.js.map +0 -1
- package/dist/shared/settings-schema.js +0 -36
- package/dist/shared/settings-schema.js.map +0 -1
- package/src/fragment.test.ts +0 -341
- package/src/fragment.ts +0 -198
- package/src/shared/settings-schema.ts +0 -61
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
ExponentialBackoffRetryPolicy,
|
|
4
|
+
LinearBackoffRetryPolicy,
|
|
5
|
+
NoRetryPolicy,
|
|
6
|
+
} from "./retry-policy";
|
|
7
|
+
|
|
8
|
+
describe("ExponentialBackoffRetryPolicy", () => {
|
|
9
|
+
describe("default options", () => {
|
|
10
|
+
it("should use default maxRetries of 3", () => {
|
|
11
|
+
const policy = new ExponentialBackoffRetryPolicy();
|
|
12
|
+
|
|
13
|
+
expect(policy.shouldRetry(0)).toBe(true);
|
|
14
|
+
expect(policy.shouldRetry(1)).toBe(true);
|
|
15
|
+
expect(policy.shouldRetry(2)).toBe(true);
|
|
16
|
+
expect(policy.shouldRetry(3)).toBe(false);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("should use default initialDelayMs of 100", () => {
|
|
20
|
+
const policy = new ExponentialBackoffRetryPolicy();
|
|
21
|
+
|
|
22
|
+
expect(policy.getDelayMs(0)).toBe(100);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("should use default backoffMultiplier of 2", () => {
|
|
26
|
+
const policy = new ExponentialBackoffRetryPolicy();
|
|
27
|
+
|
|
28
|
+
expect(policy.getDelayMs(0)).toBe(100);
|
|
29
|
+
expect(policy.getDelayMs(1)).toBe(200);
|
|
30
|
+
expect(policy.getDelayMs(2)).toBe(400);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("should cap at maxDelayMs of 10000", () => {
|
|
34
|
+
const policy = new ExponentialBackoffRetryPolicy();
|
|
35
|
+
|
|
36
|
+
expect(policy.getDelayMs(10)).toBe(10000);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe("custom options", () => {
|
|
41
|
+
it("should respect custom maxRetries", () => {
|
|
42
|
+
const policy = new ExponentialBackoffRetryPolicy({ maxRetries: 5 });
|
|
43
|
+
|
|
44
|
+
expect(policy.shouldRetry(4)).toBe(true);
|
|
45
|
+
expect(policy.shouldRetry(5)).toBe(false);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("should respect custom initialDelayMs", () => {
|
|
49
|
+
const policy = new ExponentialBackoffRetryPolicy({ initialDelayMs: 50 });
|
|
50
|
+
|
|
51
|
+
expect(policy.getDelayMs(0)).toBe(50);
|
|
52
|
+
expect(policy.getDelayMs(1)).toBe(100);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("should respect custom maxDelayMs", () => {
|
|
56
|
+
const policy = new ExponentialBackoffRetryPolicy({ maxDelayMs: 500 });
|
|
57
|
+
|
|
58
|
+
expect(policy.getDelayMs(10)).toBe(500);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("should respect custom backoffMultiplier", () => {
|
|
62
|
+
const policy = new ExponentialBackoffRetryPolicy({
|
|
63
|
+
initialDelayMs: 100,
|
|
64
|
+
backoffMultiplier: 3,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
expect(policy.getDelayMs(0)).toBe(100);
|
|
68
|
+
expect(policy.getDelayMs(1)).toBe(300);
|
|
69
|
+
expect(policy.getDelayMs(2)).toBe(900);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe("AbortSignal handling", () => {
|
|
74
|
+
it("should return false if signal is aborted", () => {
|
|
75
|
+
const policy = new ExponentialBackoffRetryPolicy();
|
|
76
|
+
const controller = new AbortController();
|
|
77
|
+
controller.abort();
|
|
78
|
+
|
|
79
|
+
expect(policy.shouldRetry(0, undefined, controller.signal)).toBe(false);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("should return true if signal is not aborted", () => {
|
|
83
|
+
const policy = new ExponentialBackoffRetryPolicy();
|
|
84
|
+
const controller = new AbortController();
|
|
85
|
+
|
|
86
|
+
expect(policy.shouldRetry(0, undefined, controller.signal)).toBe(true);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe("exponential delay calculation", () => {
|
|
91
|
+
it("should calculate correct exponential delays", () => {
|
|
92
|
+
const policy = new ExponentialBackoffRetryPolicy({
|
|
93
|
+
initialDelayMs: 10,
|
|
94
|
+
backoffMultiplier: 2,
|
|
95
|
+
maxDelayMs: 1000,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
expect(policy.getDelayMs(0)).toBe(10);
|
|
99
|
+
expect(policy.getDelayMs(1)).toBe(20);
|
|
100
|
+
expect(policy.getDelayMs(2)).toBe(40);
|
|
101
|
+
expect(policy.getDelayMs(3)).toBe(80);
|
|
102
|
+
expect(policy.getDelayMs(4)).toBe(160);
|
|
103
|
+
expect(policy.getDelayMs(5)).toBe(320);
|
|
104
|
+
expect(policy.getDelayMs(6)).toBe(640);
|
|
105
|
+
expect(policy.getDelayMs(7)).toBe(1000); // capped at maxDelayMs
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
describe("LinearBackoffRetryPolicy", () => {
|
|
111
|
+
describe("default options", () => {
|
|
112
|
+
it("should use default maxRetries of 3", () => {
|
|
113
|
+
const policy = new LinearBackoffRetryPolicy();
|
|
114
|
+
|
|
115
|
+
expect(policy.shouldRetry(0)).toBe(true);
|
|
116
|
+
expect(policy.shouldRetry(1)).toBe(true);
|
|
117
|
+
expect(policy.shouldRetry(2)).toBe(true);
|
|
118
|
+
expect(policy.shouldRetry(3)).toBe(false);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it("should use default delayMs of 100", () => {
|
|
122
|
+
const policy = new LinearBackoffRetryPolicy();
|
|
123
|
+
|
|
124
|
+
expect(policy.getDelayMs(0)).toBe(100);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("should use default incrementMs of 100", () => {
|
|
128
|
+
const policy = new LinearBackoffRetryPolicy();
|
|
129
|
+
|
|
130
|
+
expect(policy.getDelayMs(0)).toBe(100);
|
|
131
|
+
expect(policy.getDelayMs(1)).toBe(200);
|
|
132
|
+
expect(policy.getDelayMs(2)).toBe(300);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
describe("custom options", () => {
|
|
137
|
+
it("should respect custom maxRetries", () => {
|
|
138
|
+
const policy = new LinearBackoffRetryPolicy({ maxRetries: 5 });
|
|
139
|
+
|
|
140
|
+
expect(policy.shouldRetry(4)).toBe(true);
|
|
141
|
+
expect(policy.shouldRetry(5)).toBe(false);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it("should respect custom delayMs", () => {
|
|
145
|
+
const policy = new LinearBackoffRetryPolicy({ delayMs: 50 });
|
|
146
|
+
|
|
147
|
+
expect(policy.getDelayMs(0)).toBe(50);
|
|
148
|
+
expect(policy.getDelayMs(1)).toBe(150);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it("should respect custom incrementMs", () => {
|
|
152
|
+
const policy = new LinearBackoffRetryPolicy({ incrementMs: 50 });
|
|
153
|
+
|
|
154
|
+
expect(policy.getDelayMs(0)).toBe(100);
|
|
155
|
+
expect(policy.getDelayMs(1)).toBe(150);
|
|
156
|
+
expect(policy.getDelayMs(2)).toBe(200);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
describe("AbortSignal handling", () => {
|
|
161
|
+
it("should return false if signal is aborted", () => {
|
|
162
|
+
const policy = new LinearBackoffRetryPolicy();
|
|
163
|
+
const controller = new AbortController();
|
|
164
|
+
controller.abort();
|
|
165
|
+
|
|
166
|
+
expect(policy.shouldRetry(0, undefined, controller.signal)).toBe(false);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it("should return true if signal is not aborted", () => {
|
|
170
|
+
const policy = new LinearBackoffRetryPolicy();
|
|
171
|
+
const controller = new AbortController();
|
|
172
|
+
|
|
173
|
+
expect(policy.shouldRetry(0, undefined, controller.signal)).toBe(true);
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
describe("linear delay calculation", () => {
|
|
178
|
+
it("should calculate correct linear delays", () => {
|
|
179
|
+
const policy = new LinearBackoffRetryPolicy({
|
|
180
|
+
delayMs: 10,
|
|
181
|
+
incrementMs: 5,
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
expect(policy.getDelayMs(0)).toBe(10);
|
|
185
|
+
expect(policy.getDelayMs(1)).toBe(15);
|
|
186
|
+
expect(policy.getDelayMs(2)).toBe(20);
|
|
187
|
+
expect(policy.getDelayMs(3)).toBe(25);
|
|
188
|
+
expect(policy.getDelayMs(4)).toBe(30);
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
describe("NoRetryPolicy", () => {
|
|
194
|
+
it("should always return false for shouldRetry", () => {
|
|
195
|
+
const policy = new NoRetryPolicy();
|
|
196
|
+
|
|
197
|
+
expect(policy.shouldRetry(0)).toBe(false);
|
|
198
|
+
expect(policy.shouldRetry(1)).toBe(false);
|
|
199
|
+
expect(policy.shouldRetry(100)).toBe(false);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it("should always return 0 for getDelayMs", () => {
|
|
203
|
+
const policy = new NoRetryPolicy();
|
|
204
|
+
|
|
205
|
+
expect(policy.getDelayMs(0)).toBe(0);
|
|
206
|
+
expect(policy.getDelayMs(1)).toBe(0);
|
|
207
|
+
expect(policy.getDelayMs(100)).toBe(0);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it("should ignore abort signal", () => {
|
|
211
|
+
const policy = new NoRetryPolicy();
|
|
212
|
+
const controller = new AbortController();
|
|
213
|
+
controller.abort();
|
|
214
|
+
|
|
215
|
+
expect(policy.shouldRetry(0, undefined, controller.signal)).toBe(false);
|
|
216
|
+
});
|
|
217
|
+
});
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Policy for retrying failed Unit of Work operations
|
|
3
|
+
*/
|
|
4
|
+
export interface RetryPolicy {
|
|
5
|
+
/**
|
|
6
|
+
* Determines if the operation should be retried
|
|
7
|
+
* @param attempt - The current attempt number (0-indexed)
|
|
8
|
+
* @param error - Optional error from the previous attempt
|
|
9
|
+
* @param signal - Optional AbortSignal to check for cancellation
|
|
10
|
+
* @returns true if the operation should be retried, false otherwise
|
|
11
|
+
*/
|
|
12
|
+
shouldRetry(attempt: number, error?: unknown, signal?: AbortSignal): boolean;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Gets the delay in milliseconds before the next retry attempt
|
|
16
|
+
* @param attempt - The current attempt number (0-indexed)
|
|
17
|
+
* @returns Delay in milliseconds
|
|
18
|
+
*/
|
|
19
|
+
getDelayMs(attempt: number): number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Options for exponential backoff retry policy
|
|
24
|
+
*/
|
|
25
|
+
export interface ExponentialBackoffRetryPolicyOptions {
|
|
26
|
+
/**
|
|
27
|
+
* Maximum number of retry attempts (default: 3)
|
|
28
|
+
*/
|
|
29
|
+
maxRetries?: number;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Initial delay in milliseconds (default: 100)
|
|
33
|
+
*/
|
|
34
|
+
initialDelayMs?: number;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Maximum delay in milliseconds (default: 10000)
|
|
38
|
+
*/
|
|
39
|
+
maxDelayMs?: number;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Multiplier for exponential backoff (default: 2)
|
|
43
|
+
*/
|
|
44
|
+
backoffMultiplier?: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Exponential backoff retry policy
|
|
49
|
+
* Delay increases exponentially: initialDelay * (multiplier ^ attempt)
|
|
50
|
+
*/
|
|
51
|
+
export class ExponentialBackoffRetryPolicy implements RetryPolicy {
|
|
52
|
+
readonly #maxRetries: number;
|
|
53
|
+
readonly #initialDelayMs: number;
|
|
54
|
+
readonly #maxDelayMs: number;
|
|
55
|
+
readonly #backoffMultiplier: number;
|
|
56
|
+
|
|
57
|
+
constructor(options: ExponentialBackoffRetryPolicyOptions = {}) {
|
|
58
|
+
this.#maxRetries = options.maxRetries ?? 3;
|
|
59
|
+
this.#initialDelayMs = options.initialDelayMs ?? 100;
|
|
60
|
+
this.#maxDelayMs = options.maxDelayMs ?? 10000;
|
|
61
|
+
this.#backoffMultiplier = options.backoffMultiplier ?? 2;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
shouldRetry(attempt: number, _error?: unknown, signal?: AbortSignal): boolean {
|
|
65
|
+
// Check if operation was aborted
|
|
66
|
+
if (signal?.aborted) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Check if we've exceeded max retries
|
|
71
|
+
return attempt < this.#maxRetries;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
getDelayMs(attempt: number): number {
|
|
75
|
+
const delay = this.#initialDelayMs * Math.pow(this.#backoffMultiplier, attempt);
|
|
76
|
+
return Math.min(delay, this.#maxDelayMs);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Options for linear backoff retry policy
|
|
82
|
+
*/
|
|
83
|
+
export interface LinearBackoffRetryPolicyOptions {
|
|
84
|
+
/**
|
|
85
|
+
* Maximum number of retry attempts (default: 3)
|
|
86
|
+
*/
|
|
87
|
+
maxRetries?: number;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Base delay in milliseconds (default: 100)
|
|
91
|
+
*/
|
|
92
|
+
delayMs?: number;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Increment added to delay for each attempt in milliseconds (default: 100)
|
|
96
|
+
*/
|
|
97
|
+
incrementMs?: number;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Linear backoff retry policy
|
|
102
|
+
* Delay increases linearly: delayMs + (attempt * incrementMs)
|
|
103
|
+
*/
|
|
104
|
+
export class LinearBackoffRetryPolicy implements RetryPolicy {
|
|
105
|
+
readonly #maxRetries: number;
|
|
106
|
+
readonly #delayMs: number;
|
|
107
|
+
readonly #incrementMs: number;
|
|
108
|
+
|
|
109
|
+
constructor(options: LinearBackoffRetryPolicyOptions = {}) {
|
|
110
|
+
this.#maxRetries = options.maxRetries ?? 3;
|
|
111
|
+
this.#delayMs = options.delayMs ?? 100;
|
|
112
|
+
this.#incrementMs = options.incrementMs ?? 100;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
shouldRetry(attempt: number, _error?: unknown, signal?: AbortSignal): boolean {
|
|
116
|
+
// Check if operation was aborted
|
|
117
|
+
if (signal?.aborted) {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Check if we've exceeded max retries
|
|
122
|
+
return attempt < this.#maxRetries;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
getDelayMs(attempt: number): number {
|
|
126
|
+
return this.#delayMs + attempt * this.#incrementMs;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* No retry policy - never retries failed operations
|
|
132
|
+
*/
|
|
133
|
+
export class NoRetryPolicy implements RetryPolicy {
|
|
134
|
+
shouldRetry(_attempt: number, _error?: unknown, _signal?: AbortSignal): boolean {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
getDelayMs(_attempt: number): number {
|
|
139
|
+
return 0;
|
|
140
|
+
}
|
|
141
|
+
}
|