@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.
Files changed (178) hide show
  1. package/.turbo/turbo-build.log +179 -132
  2. package/CHANGELOG.md +30 -0
  3. package/dist/adapters/adapters.d.ts +27 -1
  4. package/dist/adapters/adapters.d.ts.map +1 -1
  5. package/dist/adapters/adapters.js.map +1 -1
  6. package/dist/adapters/drizzle/drizzle-adapter.d.ts +5 -1
  7. package/dist/adapters/drizzle/drizzle-adapter.d.ts.map +1 -1
  8. package/dist/adapters/drizzle/drizzle-adapter.js +15 -3
  9. package/dist/adapters/drizzle/drizzle-adapter.js.map +1 -1
  10. package/dist/adapters/drizzle/drizzle-query.js +7 -5
  11. package/dist/adapters/drizzle/drizzle-query.js.map +1 -1
  12. package/dist/adapters/drizzle/drizzle-uow-compiler.d.ts +0 -1
  13. package/dist/adapters/drizzle/drizzle-uow-compiler.d.ts.map +1 -1
  14. package/dist/adapters/drizzle/drizzle-uow-compiler.js +76 -44
  15. package/dist/adapters/drizzle/drizzle-uow-compiler.js.map +1 -1
  16. package/dist/adapters/drizzle/drizzle-uow-decoder.js +23 -16
  17. package/dist/adapters/drizzle/drizzle-uow-decoder.js.map +1 -1
  18. package/dist/adapters/drizzle/drizzle-uow-executor.js +18 -7
  19. package/dist/adapters/drizzle/drizzle-uow-executor.js.map +1 -1
  20. package/dist/adapters/drizzle/generate.d.ts +4 -1
  21. package/dist/adapters/drizzle/generate.d.ts.map +1 -1
  22. package/dist/adapters/drizzle/generate.js +11 -18
  23. package/dist/adapters/drizzle/generate.js.map +1 -1
  24. package/dist/adapters/drizzle/shared.d.ts +14 -1
  25. package/dist/adapters/drizzle/shared.d.ts.map +1 -0
  26. package/dist/adapters/kysely/kysely-adapter.d.ts +5 -1
  27. package/dist/adapters/kysely/kysely-adapter.d.ts.map +1 -1
  28. package/dist/adapters/kysely/kysely-adapter.js +14 -3
  29. package/dist/adapters/kysely/kysely-adapter.js.map +1 -1
  30. package/dist/adapters/kysely/kysely-query-builder.js +1 -1
  31. package/dist/adapters/kysely/kysely-query-compiler.js +3 -2
  32. package/dist/adapters/kysely/kysely-query-compiler.js.map +1 -1
  33. package/dist/adapters/kysely/kysely-query.d.ts +1 -0
  34. package/dist/adapters/kysely/kysely-query.d.ts.map +1 -1
  35. package/dist/adapters/kysely/kysely-query.js +28 -19
  36. package/dist/adapters/kysely/kysely-query.js.map +1 -1
  37. package/dist/adapters/kysely/kysely-shared.d.ts +14 -0
  38. package/dist/adapters/kysely/kysely-shared.d.ts.map +1 -0
  39. package/dist/adapters/kysely/kysely-shared.js +16 -1
  40. package/dist/adapters/kysely/kysely-shared.js.map +1 -1
  41. package/dist/adapters/kysely/kysely-uow-compiler.js +68 -16
  42. package/dist/adapters/kysely/kysely-uow-compiler.js.map +1 -1
  43. package/dist/adapters/kysely/kysely-uow-executor.js +8 -4
  44. package/dist/adapters/kysely/kysely-uow-executor.js.map +1 -1
  45. package/dist/adapters/kysely/migration/execute-base.js +1 -1
  46. package/dist/adapters/kysely/migration/execute-base.js.map +1 -1
  47. package/dist/db-fragment-definition-builder.d.ts +152 -0
  48. package/dist/db-fragment-definition-builder.d.ts.map +1 -0
  49. package/dist/db-fragment-definition-builder.js +137 -0
  50. package/dist/db-fragment-definition-builder.js.map +1 -0
  51. package/dist/fragments/internal-fragment.d.ts +19 -0
  52. package/dist/fragments/internal-fragment.d.ts.map +1 -0
  53. package/dist/fragments/internal-fragment.js +39 -0
  54. package/dist/fragments/internal-fragment.js.map +1 -0
  55. package/dist/migration-engine/generation-engine.d.ts.map +1 -1
  56. package/dist/migration-engine/generation-engine.js +35 -15
  57. package/dist/migration-engine/generation-engine.js.map +1 -1
  58. package/dist/mod.d.ts +8 -18
  59. package/dist/mod.d.ts.map +1 -1
  60. package/dist/mod.js +7 -34
  61. package/dist/mod.js.map +1 -1
  62. package/dist/node_modules/.pnpm/rou3@0.7.8/node_modules/rou3/dist/index.js +165 -0
  63. package/dist/node_modules/.pnpm/rou3@0.7.8/node_modules/rou3/dist/index.js.map +1 -0
  64. package/dist/packages/fragno/dist/api/bind-services.js +20 -0
  65. package/dist/packages/fragno/dist/api/bind-services.js.map +1 -0
  66. package/dist/packages/fragno/dist/api/error.js +48 -0
  67. package/dist/packages/fragno/dist/api/error.js.map +1 -0
  68. package/dist/packages/fragno/dist/api/fragment-definition-builder.js +320 -0
  69. package/dist/packages/fragno/dist/api/fragment-definition-builder.js.map +1 -0
  70. package/dist/packages/fragno/dist/api/fragment-instantiator.js +487 -0
  71. package/dist/packages/fragno/dist/api/fragment-instantiator.js.map +1 -0
  72. package/dist/packages/fragno/dist/api/fragno-response.js +73 -0
  73. package/dist/packages/fragno/dist/api/fragno-response.js.map +1 -0
  74. package/dist/packages/fragno/dist/api/internal/response-stream.js +81 -0
  75. package/dist/packages/fragno/dist/api/internal/response-stream.js.map +1 -0
  76. package/dist/packages/fragno/dist/api/internal/route.js +10 -0
  77. package/dist/packages/fragno/dist/api/internal/route.js.map +1 -0
  78. package/dist/packages/fragno/dist/api/mutable-request-state.js +97 -0
  79. package/dist/packages/fragno/dist/api/mutable-request-state.js.map +1 -0
  80. package/dist/packages/fragno/dist/api/request-context-storage.js +43 -0
  81. package/dist/packages/fragno/dist/api/request-context-storage.js.map +1 -0
  82. package/dist/packages/fragno/dist/api/request-input-context.js +118 -0
  83. package/dist/packages/fragno/dist/api/request-input-context.js.map +1 -0
  84. package/dist/packages/fragno/dist/api/request-middleware.js +83 -0
  85. package/dist/packages/fragno/dist/api/request-middleware.js.map +1 -0
  86. package/dist/packages/fragno/dist/api/request-output-context.js +119 -0
  87. package/dist/packages/fragno/dist/api/request-output-context.js.map +1 -0
  88. package/dist/packages/fragno/dist/api/route.js +17 -0
  89. package/dist/packages/fragno/dist/api/route.js.map +1 -0
  90. package/dist/packages/fragno/dist/internal/symbols.js +10 -0
  91. package/dist/packages/fragno/dist/internal/symbols.js.map +1 -0
  92. package/dist/query/cursor.d.ts +10 -2
  93. package/dist/query/cursor.d.ts.map +1 -1
  94. package/dist/query/cursor.js +11 -4
  95. package/dist/query/cursor.js.map +1 -1
  96. package/dist/query/execute-unit-of-work.d.ts +123 -0
  97. package/dist/query/execute-unit-of-work.d.ts.map +1 -0
  98. package/dist/query/execute-unit-of-work.js +184 -0
  99. package/dist/query/execute-unit-of-work.js.map +1 -0
  100. package/dist/query/query.d.ts +3 -3
  101. package/dist/query/query.d.ts.map +1 -1
  102. package/dist/query/result-transform.js +4 -2
  103. package/dist/query/result-transform.js.map +1 -1
  104. package/dist/query/retry-policy.d.ts +88 -0
  105. package/dist/query/retry-policy.d.ts.map +1 -0
  106. package/dist/query/retry-policy.js +61 -0
  107. package/dist/query/retry-policy.js.map +1 -0
  108. package/dist/query/unit-of-work.d.ts +171 -32
  109. package/dist/query/unit-of-work.d.ts.map +1 -1
  110. package/dist/query/unit-of-work.js +530 -133
  111. package/dist/query/unit-of-work.js.map +1 -1
  112. package/dist/schema/serialize.js +12 -7
  113. package/dist/schema/serialize.js.map +1 -1
  114. package/dist/with-database.d.ts +28 -0
  115. package/dist/with-database.d.ts.map +1 -0
  116. package/dist/with-database.js +34 -0
  117. package/dist/with-database.js.map +1 -0
  118. package/package.json +10 -3
  119. package/src/adapters/adapters.ts +30 -0
  120. package/src/adapters/drizzle/drizzle-adapter-pglite.test.ts +86 -17
  121. package/src/adapters/drizzle/drizzle-adapter-sqlite.test.ts +291 -7
  122. package/src/adapters/drizzle/drizzle-adapter.test.ts +3 -51
  123. package/src/adapters/drizzle/drizzle-adapter.ts +35 -7
  124. package/src/adapters/drizzle/drizzle-query.ts +25 -15
  125. package/src/adapters/drizzle/drizzle-uow-compiler-mysql.test.ts +1442 -0
  126. package/src/adapters/drizzle/drizzle-uow-compiler-sqlite.test.ts +1414 -0
  127. package/src/adapters/drizzle/drizzle-uow-compiler.test.ts +78 -61
  128. package/src/adapters/drizzle/drizzle-uow-compiler.ts +123 -42
  129. package/src/adapters/drizzle/drizzle-uow-decoder.ts +34 -27
  130. package/src/adapters/drizzle/drizzle-uow-executor.ts +41 -8
  131. package/src/adapters/drizzle/generate.test.ts +102 -269
  132. package/src/adapters/drizzle/generate.ts +12 -30
  133. package/src/adapters/drizzle/test-utils.ts +36 -5
  134. package/src/adapters/kysely/kysely-adapter-pglite.test.ts +66 -22
  135. package/src/adapters/kysely/kysely-adapter-sqlite.test.ts +156 -0
  136. package/src/adapters/kysely/kysely-adapter.ts +25 -2
  137. package/src/adapters/kysely/kysely-query-compiler.ts +3 -8
  138. package/src/adapters/kysely/kysely-query.ts +57 -37
  139. package/src/adapters/kysely/kysely-shared.ts +34 -0
  140. package/src/adapters/kysely/kysely-uow-compiler.test.ts +62 -74
  141. package/src/adapters/kysely/kysely-uow-compiler.ts +92 -24
  142. package/src/adapters/kysely/kysely-uow-executor.ts +26 -7
  143. package/src/adapters/kysely/kysely-uow-joins.test.ts +33 -50
  144. package/src/adapters/kysely/migration/execute-base.ts +1 -1
  145. package/src/db-fragment-definition-builder.test.ts +887 -0
  146. package/src/db-fragment-definition-builder.ts +506 -0
  147. package/src/db-fragment-instantiator.test.ts +467 -0
  148. package/src/db-fragment-integration.test.ts +408 -0
  149. package/src/fragments/internal-fragment.test.ts +160 -0
  150. package/src/fragments/internal-fragment.ts +85 -0
  151. package/src/migration-engine/generation-engine.test.ts +58 -15
  152. package/src/migration-engine/generation-engine.ts +78 -25
  153. package/src/mod.ts +35 -43
  154. package/src/query/cursor.test.ts +119 -0
  155. package/src/query/cursor.ts +17 -4
  156. package/src/query/execute-unit-of-work.test.ts +1310 -0
  157. package/src/query/execute-unit-of-work.ts +463 -0
  158. package/src/query/query.ts +4 -4
  159. package/src/query/result-transform.test.ts +129 -0
  160. package/src/query/result-transform.ts +4 -1
  161. package/src/query/retry-policy.test.ts +217 -0
  162. package/src/query/retry-policy.ts +141 -0
  163. package/src/query/unit-of-work-coordinator.test.ts +833 -0
  164. package/src/query/unit-of-work-types.test.ts +15 -2
  165. package/src/query/unit-of-work.test.ts +878 -200
  166. package/src/query/unit-of-work.ts +963 -321
  167. package/src/schema/serialize.ts +22 -11
  168. package/src/with-database.ts +140 -0
  169. package/tsdown.config.ts +1 -0
  170. package/dist/fragment.d.ts +0 -54
  171. package/dist/fragment.d.ts.map +0 -1
  172. package/dist/fragment.js +0 -92
  173. package/dist/fragment.js.map +0 -1
  174. package/dist/shared/settings-schema.js +0 -36
  175. package/dist/shared/settings-schema.js.map +0 -1
  176. package/src/fragment.test.ts +0 -341
  177. package/src/fragment.ts +0 -198
  178. 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
+ }