@tstdl/base 0.93.182 → 0.93.184

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 (353) hide show
  1. package/api/server/api-request-token.provider.js +1 -1
  2. package/api/server/gateway.js +6 -1
  3. package/authentication/authentication.api.d.ts +13 -40
  4. package/authentication/authentication.api.js +5 -14
  5. package/authentication/client/authentication.service.d.ts +6 -14
  6. package/authentication/client/authentication.service.js +22 -4
  7. package/authentication/client/module.d.ts +1 -1
  8. package/authentication/client/module.js +4 -4
  9. package/authentication/models/index.d.ts +1 -0
  10. package/authentication/models/index.js +1 -0
  11. package/authentication/models/totp-results.model.d.ts +11 -0
  12. package/authentication/models/totp-results.model.js +37 -0
  13. package/authentication/server/authentication.api-controller.d.ts +3 -3
  14. package/authentication/server/authentication.api-controller.js +31 -4
  15. package/authentication/server/authentication.service.d.ts +5 -14
  16. package/authentication/server/authentication.service.js +6 -4
  17. package/core.d.ts +0 -5
  18. package/core.js +0 -8
  19. package/document-management/api/document-management.api.d.ts +2 -2
  20. package/document-management/service-models/document.service-model.d.ts +1 -1
  21. package/examples/config.d.ts +25 -0
  22. package/examples/config.js +26 -0
  23. package/notification/server/module.d.ts +1 -1
  24. package/notification/server/module.js +1 -1
  25. package/package.json +5 -5
  26. package/signals/api.d.ts +5 -1
  27. package/signals/api.js +3 -1
  28. package/signals/implementation/api.d.ts +10 -1
  29. package/signals/implementation/api.js +7 -1
  30. package/signals/implementation/asserts.d.ts +2 -2
  31. package/signals/implementation/asserts.js +3 -3
  32. package/signals/implementation/computed.d.ts +7 -34
  33. package/signals/implementation/computed.js +14 -83
  34. package/signals/implementation/configure.js +6 -2
  35. package/signals/implementation/effect.d.ts +65 -46
  36. package/signals/implementation/effect.js +97 -62
  37. package/signals/implementation/index.d.ts +2 -4
  38. package/signals/implementation/index.js +2 -4
  39. package/signals/implementation/linked_signal.d.ts +36 -0
  40. package/signals/implementation/linked_signal.js +34 -0
  41. package/signals/implementation/primitive/computed.d.ts +55 -0
  42. package/signals/implementation/primitive/computed.js +107 -0
  43. package/signals/implementation/primitive/effect.d.ts +26 -0
  44. package/signals/implementation/primitive/effect.js +31 -0
  45. package/signals/implementation/{equality.d.ts → primitive/equality.d.ts} +1 -1
  46. package/signals/implementation/{equality.js → primitive/equality.js} +1 -1
  47. package/signals/implementation/primitive/errors.d.ts +10 -0
  48. package/signals/implementation/{errors.js → primitive/errors.js} +3 -4
  49. package/signals/implementation/primitive/formatter.d.ts +19 -0
  50. package/signals/implementation/primitive/formatter.js +136 -0
  51. package/signals/implementation/{graph.d.ts → primitive/graph.d.ts} +68 -36
  52. package/signals/implementation/primitive/graph.js +386 -0
  53. package/signals/implementation/primitive/linked_signal.d.ts +46 -0
  54. package/signals/implementation/primitive/linked_signal.js +110 -0
  55. package/signals/implementation/primitive/signal.d.ts +31 -0
  56. package/signals/implementation/primitive/signal.js +80 -0
  57. package/signals/implementation/primitive/untracked.d.ts +12 -0
  58. package/signals/implementation/primitive/untracked.js +23 -0
  59. package/signals/implementation/{watch.d.ts → primitive/watch.d.ts} +1 -2
  60. package/signals/implementation/{watch.js → primitive/watch.js} +22 -16
  61. package/signals/implementation/resource/api.d.ts +275 -0
  62. package/signals/implementation/resource/api.js +26 -0
  63. package/signals/implementation/resource/debounce.d.ts +13 -0
  64. package/signals/implementation/resource/debounce.js +113 -0
  65. package/signals/implementation/resource/from_snapshots.d.ts +16 -0
  66. package/signals/implementation/resource/from_snapshots.js +44 -0
  67. package/signals/implementation/resource/index.d.ts +11 -0
  68. package/signals/implementation/resource/index.js +11 -0
  69. package/signals/implementation/resource/resource.d.ts +110 -0
  70. package/signals/implementation/resource/resource.js +402 -0
  71. package/signals/implementation/root_effect_scheduler.d.ts +50 -0
  72. package/signals/implementation/root_effect_scheduler.js +66 -0
  73. package/signals/implementation/signal.d.ts +42 -18
  74. package/signals/implementation/signal.js +29 -49
  75. package/signals/implementation/to-observable.d.ts +12 -5
  76. package/signals/implementation/to-observable.js +12 -2
  77. package/signals/implementation/to-signal.d.ts +9 -18
  78. package/signals/implementation/to-signal.js +46 -13
  79. package/signals/implementation/untracked.d.ts +1 -1
  80. package/signals/implementation/untracked.js +3 -11
  81. package/signals/operators/debounce.d.ts +8 -0
  82. package/signals/operators/debounce.js +19 -0
  83. package/signals/operators/derive-async.js +43 -15
  84. package/signals/operators/index.d.ts +2 -0
  85. package/signals/operators/index.js +2 -0
  86. package/signals/operators/throttle.d.ts +8 -0
  87. package/signals/operators/throttle.js +31 -0
  88. package/ai/genkit/tests/multi-region.test.d.ts +0 -2
  89. package/ai/genkit/tests/multi-region.test.js +0 -179
  90. package/ai/genkit/tests/token-limit-fallback.test.d.ts +0 -2
  91. package/ai/genkit/tests/token-limit-fallback.test.js +0 -209
  92. package/ai/prompts/tests/prompt-builder.test.d.ts +0 -1
  93. package/ai/prompts/tests/prompt-builder.test.js +0 -22
  94. package/ai/tests/instructions-formatter.test.d.ts +0 -1
  95. package/ai/tests/instructions-formatter.test.js +0 -116
  96. package/ai/tests/steering.test.d.ts +0 -1
  97. package/ai/tests/steering.test.js +0 -37
  98. package/api/client/tests/api-client.test.d.ts +0 -1
  99. package/api/client/tests/api-client.test.js +0 -194
  100. package/api/server/tests/csrf.middleware.test.d.ts +0 -1
  101. package/api/server/tests/csrf.middleware.test.js +0 -91
  102. package/authentication/tests/authentication-password-requirements.validator.test.d.ts +0 -1
  103. package/authentication/tests/authentication-password-requirements.validator.test.js +0 -29
  104. package/authentication/tests/authentication.api-controller.test.d.ts +0 -1
  105. package/authentication/tests/authentication.api-controller.test.js +0 -156
  106. package/authentication/tests/authentication.api-request-token.provider.test.d.ts +0 -1
  107. package/authentication/tests/authentication.api-request-token.provider.test.js +0 -48
  108. package/authentication/tests/authentication.client-error-handling.test.d.ts +0 -1
  109. package/authentication/tests/authentication.client-error-handling.test.js +0 -123
  110. package/authentication/tests/authentication.client-middleware.test.d.ts +0 -1
  111. package/authentication/tests/authentication.client-middleware.test.js +0 -118
  112. package/authentication/tests/authentication.client-service-methods.test.d.ts +0 -1
  113. package/authentication/tests/authentication.client-service-methods.test.js +0 -177
  114. package/authentication/tests/authentication.client-service-refresh.test.d.ts +0 -1
  115. package/authentication/tests/authentication.client-service-refresh.test.js +0 -153
  116. package/authentication/tests/authentication.client-service.test.d.ts +0 -1
  117. package/authentication/tests/authentication.client-service.test.js +0 -76
  118. package/authentication/tests/authentication.refresh-busy-loop.test.d.ts +0 -1
  119. package/authentication/tests/authentication.refresh-busy-loop.test.js +0 -84
  120. package/authentication/tests/authentication.service.test.d.ts +0 -1
  121. package/authentication/tests/authentication.service.test.js +0 -167
  122. package/authentication/tests/authentication.test-ancillary-service.d.ts +0 -9
  123. package/authentication/tests/authentication.test-ancillary-service.js +0 -27
  124. package/authentication/tests/brute-force-protection.test.d.ts +0 -1
  125. package/authentication/tests/brute-force-protection.test.js +0 -211
  126. package/authentication/tests/helper.test.d.ts +0 -1
  127. package/authentication/tests/helper.test.js +0 -122
  128. package/authentication/tests/password-requirements.error.test.d.ts +0 -1
  129. package/authentication/tests/password-requirements.error.test.js +0 -14
  130. package/authentication/tests/remember.api.test.d.ts +0 -1
  131. package/authentication/tests/remember.api.test.js +0 -117
  132. package/authentication/tests/remember.service.test.d.ts +0 -1
  133. package/authentication/tests/remember.service.test.js +0 -83
  134. package/authentication/tests/subject.service.test.d.ts +0 -1
  135. package/authentication/tests/subject.service.test.js +0 -140
  136. package/authentication/tests/suspended-subject.test.d.ts +0 -1
  137. package/authentication/tests/suspended-subject.test.js +0 -120
  138. package/authentication/tests/totp.enrollment.test.d.ts +0 -1
  139. package/authentication/tests/totp.enrollment.test.js +0 -123
  140. package/authentication/tests/totp.login.test.d.ts +0 -1
  141. package/authentication/tests/totp.login.test.js +0 -213
  142. package/authentication/tests/totp.recovery-codes.test.d.ts +0 -1
  143. package/authentication/tests/totp.recovery-codes.test.js +0 -97
  144. package/authentication/tests/totp.status.test.d.ts +0 -1
  145. package/authentication/tests/totp.status.test.js +0 -72
  146. package/cancellation/tests/coverage.test.d.ts +0 -1
  147. package/cancellation/tests/coverage.test.js +0 -49
  148. package/cancellation/tests/leak.test.d.ts +0 -1
  149. package/cancellation/tests/leak.test.js +0 -35
  150. package/cancellation/tests/token.test.d.ts +0 -1
  151. package/cancellation/tests/token.test.js +0 -136
  152. package/circuit-breaker/tests/circuit-breaker.test.d.ts +0 -1
  153. package/circuit-breaker/tests/circuit-breaker.test.js +0 -116
  154. package/cryptography/tests/cryptography.test.d.ts +0 -1
  155. package/cryptography/tests/cryptography.test.js +0 -175
  156. package/cryptography/tests/jwt.test.d.ts +0 -1
  157. package/cryptography/tests/jwt.test.js +0 -54
  158. package/cryptography/tests/modern.test.d.ts +0 -1
  159. package/cryptography/tests/modern.test.js +0 -105
  160. package/cryptography/tests/module.test.d.ts +0 -1
  161. package/cryptography/tests/module.test.js +0 -100
  162. package/cryptography/tests/totp.test.d.ts +0 -1
  163. package/cryptography/tests/totp.test.js +0 -108
  164. package/document-management/tests/ai-config-hierarchy.test.d.ts +0 -1
  165. package/document-management/tests/ai-config-hierarchy.test.js +0 -59
  166. package/document-management/tests/ai-config-integration.test.d.ts +0 -1
  167. package/document-management/tests/ai-config-integration.test.js +0 -125
  168. package/document-management/tests/ai-config-merge.test.d.ts +0 -1
  169. package/document-management/tests/ai-config-merge.test.js +0 -46
  170. package/document-management/tests/document-management-ai-overrides.test.d.ts +0 -1
  171. package/document-management/tests/document-management-ai-overrides.test.js +0 -63
  172. package/document-management/tests/document-management-core.test.d.ts +0 -1
  173. package/document-management/tests/document-management-core.test.js +0 -157
  174. package/document-management/tests/document-management.api.test.d.ts +0 -1
  175. package/document-management/tests/document-management.api.test.js +0 -101
  176. package/document-management/tests/document-statistics.service.test.d.ts +0 -1
  177. package/document-management/tests/document-statistics.service.test.js +0 -498
  178. package/document-management/tests/document-validation-ai-overrides.test.d.ts +0 -1
  179. package/document-management/tests/document-validation-ai-overrides.test.js +0 -87
  180. package/document-management/tests/document.service.test.d.ts +0 -1
  181. package/document-management/tests/document.service.test.js +0 -143
  182. package/document-management/tests/enum-helpers.test.d.ts +0 -1
  183. package/document-management/tests/enum-helpers.test.js +0 -452
  184. package/document-management/tests/helper.d.ts +0 -24
  185. package/document-management/tests/helper.js +0 -39
  186. package/errors/tests/format.test.d.ts +0 -1
  187. package/errors/tests/format.test.js +0 -84
  188. package/http/tests/server-timing.test.d.ts +0 -1
  189. package/http/tests/server-timing.test.js +0 -42
  190. package/injector/tests/advanced.test.d.ts +0 -1
  191. package/injector/tests/advanced.test.js +0 -116
  192. package/injector/tests/async-init.test.d.ts +0 -1
  193. package/injector/tests/async-init.test.js +0 -77
  194. package/injector/tests/basic.test.d.ts +0 -1
  195. package/injector/tests/basic.test.js +0 -114
  196. package/injector/tests/hierarchical.test.d.ts +0 -1
  197. package/injector/tests/hierarchical.test.js +0 -59
  198. package/injector/tests/leak.test.d.ts +0 -1
  199. package/injector/tests/leak.test.js +0 -45
  200. package/injector/tests/lifecycles.test.d.ts +0 -1
  201. package/injector/tests/lifecycles.test.js +0 -109
  202. package/logger/tests/pretty-print.test.d.ts +0 -1
  203. package/logger/tests/pretty-print.test.js +0 -60
  204. package/notification/tests/notification-api.test.d.ts +0 -1
  205. package/notification/tests/notification-api.test.js +0 -124
  206. package/notification/tests/notification-client.test.d.ts +0 -1
  207. package/notification/tests/notification-client.test.js +0 -101
  208. package/notification/tests/notification-flow.test.d.ts +0 -1
  209. package/notification/tests/notification-flow.test.js +0 -296
  210. package/notification/tests/notification-sse.service.test.d.ts +0 -1
  211. package/notification/tests/notification-sse.service.test.js +0 -43
  212. package/notification/tests/notification-type.service.test.d.ts +0 -1
  213. package/notification/tests/notification-type.service.test.js +0 -41
  214. package/object-storage/s3/tests/s3.object-storage.integration.test.d.ts +0 -1
  215. package/object-storage/s3/tests/s3.object-storage.integration.test.js +0 -303
  216. package/orm/tests/build-jsonb.test.d.ts +0 -1
  217. package/orm/tests/build-jsonb.test.js +0 -39
  218. package/orm/tests/data-types.test.d.ts +0 -1
  219. package/orm/tests/data-types.test.js +0 -39
  220. package/orm/tests/database-extension.test.d.ts +0 -1
  221. package/orm/tests/database-extension.test.js +0 -63
  222. package/orm/tests/database-migration.test.d.ts +0 -1
  223. package/orm/tests/database-migration.test.js +0 -83
  224. package/orm/tests/decorators.test.d.ts +0 -1
  225. package/orm/tests/decorators.test.js +0 -77
  226. package/orm/tests/encryption.test.d.ts +0 -1
  227. package/orm/tests/encryption.test.js +0 -31
  228. package/orm/tests/query-complex.test.d.ts +0 -1
  229. package/orm/tests/query-complex.test.js +0 -172
  230. package/orm/tests/query-converter-complex.test.d.ts +0 -1
  231. package/orm/tests/query-converter-complex.test.js +0 -131
  232. package/orm/tests/query-converter.test.d.ts +0 -1
  233. package/orm/tests/query-converter.test.js +0 -123
  234. package/orm/tests/repository-advanced.test.d.ts +0 -1
  235. package/orm/tests/repository-advanced.test.js +0 -189
  236. package/orm/tests/repository-attributes.test.d.ts +0 -1
  237. package/orm/tests/repository-attributes.test.js +0 -83
  238. package/orm/tests/repository-compound-primary-key.test.d.ts +0 -2
  239. package/orm/tests/repository-compound-primary-key.test.js +0 -226
  240. package/orm/tests/repository-comprehensive.test.d.ts +0 -1
  241. package/orm/tests/repository-comprehensive.test.js +0 -162
  242. package/orm/tests/repository-coverage.test.d.ts +0 -2
  243. package/orm/tests/repository-coverage.test.js +0 -242
  244. package/orm/tests/repository-cti-complex.test.d.ts +0 -1
  245. package/orm/tests/repository-cti-complex.test.js +0 -151
  246. package/orm/tests/repository-cti-embedded.test.d.ts +0 -1
  247. package/orm/tests/repository-cti-embedded.test.js +0 -178
  248. package/orm/tests/repository-cti-extensive.test.d.ts +0 -2
  249. package/orm/tests/repository-cti-extensive.test.js +0 -279
  250. package/orm/tests/repository-cti-mapping.test.d.ts +0 -2
  251. package/orm/tests/repository-cti-mapping.test.js +0 -108
  252. package/orm/tests/repository-cti-search.test.d.ts +0 -1
  253. package/orm/tests/repository-cti-search.test.js +0 -141
  254. package/orm/tests/repository-cti-soft-delete.test.d.ts +0 -2
  255. package/orm/tests/repository-cti-soft-delete.test.js +0 -103
  256. package/orm/tests/repository-cti-transactions.test.d.ts +0 -1
  257. package/orm/tests/repository-cti-transactions.test.js +0 -112
  258. package/orm/tests/repository-cti-upsert-many.test.d.ts +0 -2
  259. package/orm/tests/repository-cti-upsert-many.test.js +0 -115
  260. package/orm/tests/repository-cti.test.d.ts +0 -2
  261. package/orm/tests/repository-cti.test.js +0 -390
  262. package/orm/tests/repository-edge-cases.test.d.ts +0 -1
  263. package/orm/tests/repository-edge-cases.test.js +0 -178
  264. package/orm/tests/repository-expiration.test.d.ts +0 -2
  265. package/orm/tests/repository-expiration.test.js +0 -140
  266. package/orm/tests/repository-extra-coverage.test.d.ts +0 -2
  267. package/orm/tests/repository-extra-coverage.test.js +0 -402
  268. package/orm/tests/repository-mapping.test.d.ts +0 -2
  269. package/orm/tests/repository-mapping.test.js +0 -65
  270. package/orm/tests/repository-regression.test.d.ts +0 -1
  271. package/orm/tests/repository-regression.test.js +0 -288
  272. package/orm/tests/repository-search-coverage.test.d.ts +0 -1
  273. package/orm/tests/repository-search-coverage.test.js +0 -107
  274. package/orm/tests/repository-search.test.d.ts +0 -1
  275. package/orm/tests/repository-search.test.js +0 -105
  276. package/orm/tests/repository-soft-delete.test.d.ts +0 -1
  277. package/orm/tests/repository-soft-delete.test.js +0 -118
  278. package/orm/tests/repository-transactions-nested.test.d.ts +0 -1
  279. package/orm/tests/repository-transactions-nested.test.js +0 -178
  280. package/orm/tests/repository-types.test.d.ts +0 -1
  281. package/orm/tests/repository-types.test.js +0 -184
  282. package/orm/tests/repository-undelete.test.d.ts +0 -2
  283. package/orm/tests/repository-undelete.test.js +0 -201
  284. package/orm/tests/schema-converter.test.d.ts +0 -1
  285. package/orm/tests/schema-converter.test.js +0 -82
  286. package/orm/tests/schema-generation.test.d.ts +0 -2
  287. package/orm/tests/schema-generation.test.js +0 -174
  288. package/orm/tests/sql-helpers.test.d.ts +0 -1
  289. package/orm/tests/sql-helpers.test.js +0 -67
  290. package/orm/tests/transaction-safety.test.d.ts +0 -1
  291. package/orm/tests/transaction-safety.test.js +0 -81
  292. package/orm/tests/transactional.test.d.ts +0 -1
  293. package/orm/tests/transactional.test.js +0 -215
  294. package/orm/tests/utils.test.d.ts +0 -1
  295. package/orm/tests/utils.test.js +0 -70
  296. package/pdf/tests/utils.test.d.ts +0 -1
  297. package/pdf/tests/utils.test.js +0 -187
  298. package/process/tests/spawn.test.d.ts +0 -1
  299. package/process/tests/spawn.test.js +0 -182
  300. package/rate-limit/tests/postgres-rate-limiter.test.d.ts +0 -1
  301. package/rate-limit/tests/postgres-rate-limiter.test.js +0 -84
  302. package/renderer/tests/renderer.test.d.ts +0 -1
  303. package/renderer/tests/renderer.test.js +0 -88
  304. package/rpc/tests/rpc.integration.test.d.ts +0 -1
  305. package/rpc/tests/rpc.integration.test.js +0 -615
  306. package/signals/implementation/errors.d.ts +0 -2
  307. package/signals/implementation/graph.js +0 -312
  308. package/signals/implementation/writable-signal.d.ts +0 -48
  309. package/signals/implementation/writable-signal.js +0 -32
  310. package/task-queue/tests/coverage-branch.test.d.ts +0 -1
  311. package/task-queue/tests/coverage-branch.test.js +0 -395
  312. package/task-queue/tests/coverage-enhancement.test.d.ts +0 -1
  313. package/task-queue/tests/coverage-enhancement.test.js +0 -150
  314. package/task-queue/tests/dag.test.d.ts +0 -1
  315. package/task-queue/tests/dag.test.js +0 -188
  316. package/task-queue/tests/dependencies.test.d.ts +0 -1
  317. package/task-queue/tests/dependencies.test.js +0 -296
  318. package/task-queue/tests/enqueue-batch.test.d.ts +0 -1
  319. package/task-queue/tests/enqueue-batch.test.js +0 -125
  320. package/task-queue/tests/enqueue-item.test.d.ts +0 -1
  321. package/task-queue/tests/enqueue-item.test.js +0 -12
  322. package/task-queue/tests/fan-out-spawning.test.d.ts +0 -1
  323. package/task-queue/tests/fan-out-spawning.test.js +0 -94
  324. package/task-queue/tests/idempotent-replacement.test.d.ts +0 -1
  325. package/task-queue/tests/idempotent-replacement.test.js +0 -114
  326. package/task-queue/tests/missing-idempotent-tasks.test.d.ts +0 -1
  327. package/task-queue/tests/missing-idempotent-tasks.test.js +0 -39
  328. package/task-queue/tests/optimization-edge-cases.test.d.ts +0 -1
  329. package/task-queue/tests/optimization-edge-cases.test.js +0 -124
  330. package/task-queue/tests/queue-generic.test.d.ts +0 -1
  331. package/task-queue/tests/queue-generic.test.js +0 -8
  332. package/task-queue/tests/queue.test.d.ts +0 -1
  333. package/task-queue/tests/queue.test.js +0 -756
  334. package/task-queue/tests/shutdown.test.d.ts +0 -1
  335. package/task-queue/tests/shutdown.test.js +0 -41
  336. package/task-queue/tests/task-context.test.d.ts +0 -1
  337. package/task-queue/tests/task-context.test.js +0 -7
  338. package/task-queue/tests/task-union.test.d.ts +0 -1
  339. package/task-queue/tests/task-union.test.js +0 -18
  340. package/task-queue/tests/transactions.test.d.ts +0 -1
  341. package/task-queue/tests/transactions.test.js +0 -47
  342. package/task-queue/tests/typing.test.d.ts +0 -1
  343. package/task-queue/tests/typing.test.js +0 -9
  344. package/task-queue/tests/worker.test.d.ts +0 -1
  345. package/task-queue/tests/worker.test.js +0 -258
  346. package/task-queue/tests/zombie-parent.test.d.ts +0 -1
  347. package/task-queue/tests/zombie-parent.test.js +0 -45
  348. package/task-queue/tests/zombie-recovery.test.d.ts +0 -1
  349. package/task-queue/tests/zombie-recovery.test.js +0 -51
  350. package/utils/tests/backoff.test.d.ts +0 -1
  351. package/utils/tests/backoff.test.js +0 -41
  352. package/utils/tests/retry-with-backoff.test.d.ts +0 -1
  353. package/utils/tests/retry-with-backoff.test.js +0 -49
@@ -1 +0,0 @@
1
- export {};
@@ -1,41 +0,0 @@
1
- import { afterAll, beforeAll, describe, expect, it } from 'vitest';
2
- import { CancellationSignal } from '../../cancellation/token.js';
3
- import { DeferredPromise } from '../../promise/deferred-promise.js';
4
- import { TaskProcessResult, TaskQueueProvider } from '../../task-queue/index.js';
5
- import { setupIntegrationTest } from '../../testing/index.js';
6
- import { timeout } from '../../utils/timing.js';
7
- describe('Graceful Worker Shutdown', () => {
8
- let injector;
9
- let queue;
10
- beforeAll(async () => {
11
- ({ injector } = await setupIntegrationTest({ modules: { taskQueue: true } }));
12
- const queueProvider = injector.resolve(TaskQueueProvider);
13
- queue = queueProvider.get(`shutdown-queue-${crypto.randomUUID()}`);
14
- });
15
- afterAll(async () => {
16
- await injector?.dispose();
17
- });
18
- it('should wait for active handler to finish on disposal', async () => {
19
- const cancellationSignal = injector.resolve(CancellationSignal);
20
- await queue.enqueue('shutdown-test', {});
21
- let handlerFinished = false;
22
- const handlerStarted = new DeferredPromise();
23
- queue.process({ cancellationSignal }, async () => {
24
- handlerStarted.resolve();
25
- await timeout(300); // simulate work
26
- handlerFinished = true;
27
- return TaskProcessResult.Complete();
28
- });
29
- queue.notify();
30
- // Wait for worker to pick it up
31
- await handlerStarted;
32
- // Call disposal
33
- const disposePromise = injector.dispose();
34
- // Check that disposal waits for the handler
35
- expect(handlerFinished).toBe(false);
36
- await disposePromise;
37
- expect(handlerFinished).toBe(true);
38
- // Note: We can't call queue.getTask here because pool is closed.
39
- // But handlerFinished = true and successful disposePromise already prove it waited.
40
- });
41
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,7 +0,0 @@
1
- import { describe, expectTypeOf, test } from 'vitest';
2
- describe('TaskContext Type Definitions', () => {
3
- test('TaskContext should infer types from definition map', () => {
4
- expectTypeOf().toEqualTypeOf();
5
- expectTypeOf().toEqualTypeOf();
6
- });
7
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,18 +0,0 @@
1
- import { describe, expectTypeOf, test } from 'vitest';
2
- describe('Task Discriminated Union', () => {
3
- test('Task should be a discriminated union based on definition map', () => {
4
- // Should be a union
5
- expectTypeOf().toBeObject();
6
- // Discrimination check
7
- const task = {};
8
- if (task.type == 'test-task') {
9
- expectTypeOf(task.data).toEqualTypeOf();
10
- expectTypeOf(task.state).toEqualTypeOf();
11
- expectTypeOf(task.result).toEqualTypeOf();
12
- }
13
- else if (task.type == 'other-task') {
14
- expectTypeOf(task.data).toEqualTypeOf();
15
- expectTypeOf(task.result).toEqualTypeOf();
16
- }
17
- });
18
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,47 +0,0 @@
1
- import { afterAll, beforeAll, describe, expect, it } from 'vitest';
2
- import { TaskQueueProvider, TaskStatus } from '../../task-queue/index.js';
3
- import { setupIntegrationTest } from '../../testing/index.js';
4
- describe('Task Queue Transactions', () => {
5
- let injector;
6
- let queue;
7
- beforeAll(async () => {
8
- ({ injector } = await setupIntegrationTest({ modules: { taskQueue: true } }));
9
- const queueProvider = injector.resolve(TaskQueueProvider);
10
- queue = queueProvider.get(`tx-queue-${crypto.randomUUID()}`);
11
- });
12
- afterAll(async () => {
13
- await injector?.dispose();
14
- });
15
- it('should respect transaction rollbacks', async () => {
16
- let taskId;
17
- await queue.transaction(async (tx) => {
18
- const task = await queue.enqueue('tx-task', { foo: 'bar' }, { transaction: tx });
19
- taskId = task.id;
20
- const hasTask = await queue.has(taskId, { transaction: tx });
21
- expect(hasTask).toBe(true);
22
- await tx.rollback();
23
- });
24
- const hasTaskAfterRollback = await queue.has(taskId);
25
- expect(hasTaskAfterRollback).toBe(false);
26
- });
27
- it('should participate in external transaction (commit)', async () => {
28
- let taskId;
29
- await queue.transaction(async (tx) => {
30
- const task = await queue.enqueue('tx-commit', { foo: 'bar' }, { transaction: tx });
31
- taskId = task.id;
32
- });
33
- const hasTaskAfterCommit = await queue.has(taskId);
34
- expect(hasTaskAfterCommit).toBe(true);
35
- });
36
- it('should handle multiple operations in one transaction', async () => {
37
- await queue.transaction(async (tx) => {
38
- const t1 = await queue.enqueue('t1', {}, { transaction: tx });
39
- const t2 = await queue.enqueue('t2', {}, { transaction: tx });
40
- await queue.cancel(t1.id, { transaction: tx });
41
- const u1 = await queue.getTask(t1.id, { transaction: tx });
42
- expect(u1?.status).toBe(TaskStatus.Cancelled);
43
- const u2 = await queue.getTask(t2.id, { transaction: tx });
44
- expect(u2?.status).toBe(TaskStatus.Pending);
45
- });
46
- });
47
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,9 +0,0 @@
1
- import { describe, expectTypeOf, test } from 'vitest';
2
- describe('TaskQueue Type Definitions', () => {
3
- test('TaskDefinition and TaskDefinitionMap should be defined', () => {
4
- expectTypeOf().toEqualTypeOf();
5
- expectTypeOf().toEqualTypeOf();
6
- expectTypeOf().toEqualTypeOf();
7
- expectTypeOf().toEqualTypeOf();
8
- });
9
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,258 +0,0 @@
1
- import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
2
- import { CancellationToken } from '../../cancellation/index.js';
3
- import { getRepository } from '../../orm/server/index.js';
4
- import { TaskProcessResult, TaskQueueProvider, TaskStatus } from '../../task-queue/index.js';
5
- import { PostgresTask } from '../../task-queue/postgres/index.js';
6
- import { setupIntegrationTest } from '../../testing/index.js';
7
- import { timeout } from '../../utils/timing.js';
8
- describe('Worker & Base Class Tests', () => {
9
- let injector;
10
- let queue;
11
- let token;
12
- let otherQueueName;
13
- let taskRepository;
14
- beforeAll(async () => {
15
- ({ injector } = await setupIntegrationTest({ modules: { taskQueue: true } }));
16
- taskRepository = injector.resolve(getRepository(PostgresTask));
17
- otherQueueName = `other-queue-${crypto.randomUUID()}`;
18
- });
19
- beforeEach(() => {
20
- const queueProvider = injector.resolve(TaskQueueProvider);
21
- const queueName = `worker-queue-${crypto.randomUUID()}`;
22
- queue = queueProvider.get(queueName, {
23
- visibilityTimeout: 200, // Short visibility for testing lease loss
24
- });
25
- token = new CancellationToken();
26
- });
27
- afterEach(async () => {
28
- token.set();
29
- const namespace = queue.getTransactionalContextData().namespace;
30
- // Clear foreign keys
31
- await taskRepository.updateManyByQuery({ namespace }, { parentId: null });
32
- await taskRepository.updateManyByQuery({ namespace: otherQueueName }, { parentId: null });
33
- await queue.clear();
34
- const queueProvider = injector.resolve(TaskQueueProvider);
35
- await queueProvider.get(otherQueueName).clear();
36
- vi.restoreAllMocks();
37
- });
38
- afterAll(async () => {
39
- await injector?.dispose();
40
- });
41
- it('should process tasks using process() helper', async () => {
42
- const t1 = await queue.enqueue('work', { val: 1 });
43
- const t2 = await queue.enqueue('work', { val: 2 });
44
- const processed = [];
45
- queue.process({ cancellationSignal: token }, async (context) => {
46
- processed.push(context.data['val']);
47
- return TaskProcessResult.Complete();
48
- });
49
- // Wait until 2 tasks are processed
50
- for (let i = 0; i < 50; i++) {
51
- if (processed.length == 2)
52
- break;
53
- queue.notify();
54
- await timeout(20);
55
- }
56
- token.set(); // Stop worker
57
- await queue.waitForTasks([t1.id, t2.id], { interval: 50, timeout: 1000 });
58
- expect(processed).toContain(1);
59
- expect(processed).toContain(2);
60
- expect(processed.length).toBe(2);
61
- const check1 = await queue.getTask(t1.id);
62
- const check2 = await queue.getTask(t2.id);
63
- expect(check1?.status).toBe(TaskStatus.Completed);
64
- expect(check2?.status).toBe(TaskStatus.Completed);
65
- });
66
- it('should handle errors in worker gracefully', async () => {
67
- const task = await queue.enqueue('fail', {});
68
- queue.process({ cancellationSignal: token }, async () => {
69
- throw new Error('worker error');
70
- });
71
- // Wait until task is processed (error recorded and status is Retrying)
72
- for (let i = 0; i < 50; i++) {
73
- const updated = await queue.getTask(task.id);
74
- if (updated?.tries == 1 && updated.status == TaskStatus.Retrying) {
75
- break;
76
- }
77
- queue.notify();
78
- await timeout(20);
79
- }
80
- token.set();
81
- const updated = await queue.getTask(task.id);
82
- expect(updated?.status).toBe(TaskStatus.Retrying); // Should retry
83
- expect(updated?.tries).toBe(1);
84
- expect(updated?.errors[0]?.message).toBe('worker error');
85
- });
86
- it('should extend lease (heartbeat) during long processing', async () => {
87
- const task = await queue.enqueue('long', {});
88
- let executed = false;
89
- queue.process({ cancellationSignal: token }, async (_context) => {
90
- // Simulate long work > visibilityTimeout (200ms)
91
- await timeout(300);
92
- executed = true;
93
- return TaskProcessResult.Complete();
94
- });
95
- queue.notify();
96
- await queue.waitForTasks([task.id], { timeout: 5000 });
97
- token.set();
98
- expect(executed).toBe(true);
99
- const updated = await queue.getTask(task.id);
100
- expect(updated?.status).toBe(TaskStatus.Completed);
101
- });
102
- it('should handle TaskResult actions (Fail, Reschedule)', async () => {
103
- const tFail = await queue.enqueue('fail-action', {});
104
- const tResched = await queue.enqueue('resched-action', {});
105
- const processed = new Set();
106
- queue.process({ cancellationSignal: token }, async (context) => {
107
- processed.add(context.id);
108
- if (context.id == tFail.id) {
109
- return TaskProcessResult.Fail(new Error('explicit fail'));
110
- }
111
- if (context.id == tResched.id) {
112
- return TaskProcessResult.RescheduleBy(1000);
113
- }
114
- return TaskProcessResult.Complete();
115
- });
116
- // Wait until tasks are processed (error/reschedule recorded and status is Retrying/Pending)
117
- for (let i = 0; i < 50; i++) {
118
- const uFail = await queue.getTask(tFail.id);
119
- const uResched = await queue.getTask(tResched.id);
120
- if (uFail?.tries == 1 && uFail.status == TaskStatus.Retrying && uResched?.status == TaskStatus.Pending && (uResched?.scheduleTimestamp ?? 0) > Date.now()) {
121
- break;
122
- }
123
- queue.notify();
124
- await timeout(20);
125
- }
126
- token.set();
127
- const uFail = await queue.getTask(tFail.id);
128
- expect(uFail?.status).toBe(TaskStatus.Retrying); // Retry
129
- expect(uFail?.errors[0]?.message).toBe('explicit fail');
130
- const uResched = await queue.getTask(tResched.id);
131
- expect(uResched?.status).toBe(TaskStatus.Pending);
132
- expect(uResched?.scheduleTimestamp).toBeGreaterThan(Date.now());
133
- });
134
- it('should exercise TaskContext methods', async () => {
135
- const task = await queue.enqueue('context-test', { val: 1 });
136
- let executed = false;
137
- queue.process({ cancellationSignal: token, types: ['context-test'] }, async (context) => {
138
- expect(context.id).toBe(task.id);
139
- expect(context.data).toEqual({ val: 1 });
140
- expect(context.attempt).toBe(1);
141
- expect(context.triesLeft).toBeGreaterThan(0);
142
- expect(context.logger).toBeDefined();
143
- expect(context.signal).toBeDefined();
144
- await context.checkpoint({ progress: 0.5 });
145
- const child = await context.spawn('child', { c: 1 });
146
- expect(child.parentId).toBe(task.id);
147
- const children = await context.spawnMany([{ type: 'child', data: { c: 2 } }]);
148
- expect(children[0]?.parentId).toBe(task.id);
149
- // Other queue spawn
150
- const otherQueue = injector.resolve(TaskQueueProvider).get(otherQueueName);
151
- const otherChild = await context.spawn(otherQueue, 'other', { x: 1 });
152
- expect(otherChild.namespace).toBe(otherQueueName);
153
- await context.spawnMany(otherQueue, [{ type: 'other', data: { x: 2 } }]);
154
- executed = true;
155
- return TaskProcessResult.Complete();
156
- });
157
- // Complete children so parent can finalize
158
- void (async () => {
159
- while (!executed) {
160
- await timeout(50);
161
- queue.notify();
162
- }
163
- // At this point parent should be WaitingChildren if children are not done
164
- await queue.waitForTasks([task.id], { statuses: [TaskStatus.WaitingChildren], timeout: 2000 });
165
- const midTask = await queue.getTask(task.id);
166
- expect(midTask?.status).toBe(TaskStatus.WaitingChildren);
167
- while (true) {
168
- const dChild = await queue.dequeue({ types: ['child'] });
169
- if (!dChild) {
170
- // Check other-queue as well
171
- const otherQueue = injector.resolve(TaskQueueProvider).get(otherQueueName);
172
- const dOther = await otherQueue.dequeue();
173
- if (dOther) {
174
- await otherQueue.complete(dOther);
175
- continue;
176
- }
177
- break;
178
- }
179
- await queue.complete(dChild);
180
- }
181
- })();
182
- queue.notify();
183
- await queue.waitForTasks([task.id], { interval: 50, timeout: 2000 });
184
- token.set();
185
- expect(executed).toBe(true);
186
- const finalTask = await queue.getTask(task.id);
187
- expect(finalTask?.status).toBe(TaskStatus.Completed);
188
- });
189
- it('should correctly report isFinalAttempt in TaskContext', async () => {
190
- const queueProvider = injector.resolve(TaskQueueProvider);
191
- const queueName = `final-try-test-${crypto.randomUUID()}`;
192
- const testQueue = queueProvider.get(queueName, {
193
- maxTries: 2,
194
- retryDelayMinimum: 0,
195
- retryDelayGrowth: 1,
196
- });
197
- await testQueue.enqueue('work', {});
198
- testQueue.notify();
199
- const finalAttemptValues = [];
200
- testQueue.process({ cancellationSignal: token }, async (context) => {
201
- finalAttemptValues.push(context.isFinalAttempt);
202
- if (context.attempt == 1) {
203
- throw new Error('fail first attempt');
204
- }
205
- return TaskProcessResult.Complete();
206
- });
207
- for (let i = 0; i < 100; i++) {
208
- if (finalAttemptValues.length == 2)
209
- break;
210
- testQueue.notify();
211
- await timeout(20);
212
- }
213
- token.set();
214
- expect(finalAttemptValues).toEqual([false, true]);
215
- await testQueue.clear();
216
- });
217
- it('should handle unsupported task result action', async () => {
218
- const task = await queue.enqueue('unsupported-action', {});
219
- const workerPromise = queue.processWorker(token, () => ({ payload: { action: 'magic' } }));
220
- await expect(workerPromise).rejects.toThrow('Unsupported task result action');
221
- const updated = await queue.getTask(task.id);
222
- expect(updated?.status).toBe(TaskStatus.Dead); // Fatal now
223
- expect(updated?.errors[0]?.message).toContain('Unsupported task result action');
224
- });
225
- it('should not crash worker lane if fail() throws', async () => {
226
- const t1 = await queue.enqueue('task1', {});
227
- const t2 = await queue.enqueue('task2', {});
228
- let t1Processed = false;
229
- let t2Processed = false;
230
- // Mock fail to throw once
231
- const failSpy = vi.spyOn(queue, 'fail').mockRejectedValueOnce(new Error('DB DOWN'));
232
- queue.process({ cancellationSignal: token }, async (context) => {
233
- if (context.id === t1.id) {
234
- t1Processed = true;
235
- throw new Error('Task 1 failed');
236
- }
237
- if (context.id === t2.id) {
238
- t2Processed = true;
239
- return TaskProcessResult.Complete();
240
- }
241
- return TaskProcessResult.Complete();
242
- });
243
- // Wait until both tasks are processed
244
- // If the bug exists, the worker lane will crash after t1 and t2 will never be processed
245
- for (let i = 0; i < 40; i++) {
246
- if (t1Processed && t2Processed)
247
- break;
248
- queue.notify();
249
- await timeout(50);
250
- }
251
- token.set();
252
- expect(t1Processed).toBe(true);
253
- expect(t2Processed).toBe(true);
254
- expect(failSpy).toHaveBeenCalled();
255
- const check2 = await queue.getTask(t2.id);
256
- expect(check2?.status).toBe(TaskStatus.Completed);
257
- });
258
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,45 +0,0 @@
1
- import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from 'vitest';
2
- import { TaskQueueProvider, TaskStatus } from '../../task-queue/index.js';
3
- import { setupIntegrationTest } from '../../testing/index.js';
4
- import { timeout } from '../../utils/timing.js';
5
- describe('Zombie Parent Deadlock', () => {
6
- let injector;
7
- let queue;
8
- beforeAll(async () => {
9
- ({ injector } = await setupIntegrationTest({ modules: { taskQueue: true } }));
10
- });
11
- beforeEach(() => {
12
- const queueProvider = injector.resolve(TaskQueueProvider);
13
- const queueName = `zombie-queue-${crypto.randomUUID()}`;
14
- queue = queueProvider.get(queueName, {
15
- visibilityTimeout: 1000,
16
- });
17
- });
18
- afterEach(async () => {
19
- await queue.clear();
20
- });
21
- afterAll(async () => {
22
- await injector?.dispose();
23
- });
24
- it('should resolve parent even if child fails (abortOnDependencyFailure: false)', async () => {
25
- const parent = await queue.enqueue('parent', {});
26
- const dParent = await queue.dequeue();
27
- // Spawn a child that will fail. Parent has abortOnDependencyFailure: false by default.
28
- const [child] = await queue.enqueueMany([{ type: 'child', data: {}, parentId: parent.id, abortOnDependencyFailure: false }], { returnTasks: true });
29
- await queue.complete(dParent);
30
- // Parent should be WaitingChildren
31
- const uParent = await queue.getTask(parent.id);
32
- expect(uParent?.status).toBe(TaskStatus.WaitingChildren);
33
- // Fail the child fatally
34
- const dChild = await queue.dequeue();
35
- await queue.fail(dChild, new Error('child failed'), { fatal: true });
36
- // Verify child is Dead
37
- const uChild = await queue.getTask(child.id);
38
- expect(uChild?.status).toBe(TaskStatus.Dead);
39
- // Wait a bit for dependency resolution
40
- await timeout(200);
41
- // Parent should NOT be stuck. It should transition to Completed because the child is terminal.
42
- const fParent = await queue.getTask(parent.id);
43
- expect(fParent?.status).toBe(TaskStatus.Completed);
44
- });
45
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,51 +0,0 @@
1
- import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from 'vitest';
2
- import { TaskQueueProvider, TaskStatus } from '../../task-queue/index.js';
3
- import { setupIntegrationTest } from '../../testing/index.js';
4
- import { timeout } from '../../utils/timing.js';
5
- describe('Zombie Recovery Race Condition', () => {
6
- let injector;
7
- let queue;
8
- beforeAll(async () => {
9
- ({ injector } = await setupIntegrationTest({ modules: { taskQueue: true } }));
10
- });
11
- beforeEach(() => {
12
- const queueProvider = injector.resolve(TaskQueueProvider);
13
- const queueName = `zombie-recovery-queue-${crypto.randomUUID()}`;
14
- queue = queueProvider.get(queueName, {
15
- visibilityTimeout: 100, // Very short visibility timeout
16
- retryDelayMinimum: 0,
17
- });
18
- });
19
- afterEach(async () => {
20
- await queue.clear();
21
- });
22
- afterAll(async () => {
23
- await injector?.dispose();
24
- });
25
- it('should NOT prematurely complete a recovered parent when children finish', async () => {
26
- // 1. Enqueue parent
27
- const parent = await queue.enqueue('parent', {});
28
- // 2. Dequeue parent (it is now Running, startTimestamp is set)
29
- const dParent = await queue.dequeue();
30
- expect(dParent?.id).toBe(parent.id);
31
- expect(dParent?.startTimestamp).not.toBeNull();
32
- // 3. Parent spawns a child that it waits for
33
- const [child] = await queue.enqueueMany([{ type: 'child', data: {}, parentId: parent.id }], { returnTasks: true });
34
- // 4. Simulate crash: Wait for visibility timeout so it becomes a zombie
35
- await timeout(200);
36
- // 5. Run maintenance to recover the zombie parent
37
- await queue.maintenance();
38
- // Verify it was recovered to Pending
39
- const recoveredParent = await queue.getTask(parent.id);
40
- expect(recoveredParent?.status).toBe(TaskStatus.Retrying);
41
- // If the bug exists, startTimestamp is still set here.
42
- // 6. Complete the child while parent is still Pending
43
- const dChild = await queue.dequeue({ types: ['child'] });
44
- await queue.complete(dChild);
45
- // 7. Verify parent status
46
- // BUG: evaluateTaskStatus will see unresolvedCompleteDependencies=0 and startTimestamp != null,
47
- // and incorrectly transition the Pending parent to Completed.
48
- const finalParent = await queue.getTask(parent.id);
49
- expect(finalParent?.status).toBe(TaskStatus.Retrying); // It should still be pending execution!
50
- });
51
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,41 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { BackoffHelper } from '../backoff.js';
3
- describe('BackoffHelper', () => {
4
- it('should support full jitter', () => {
5
- const helper = new BackoffHelper({
6
- initialDelay: 1000,
7
- jitter: 1,
8
- jitterStrategy: 'full',
9
- });
10
- for (let i = 0; i < 100; i++) {
11
- const delay = helper.getNextDelay();
12
- // Full jitter should be between 0 and the base delay (1000 for first call, then 2000, 4000, etc.)
13
- expect(delay).toBeGreaterThanOrEqual(0);
14
- expect(delay).toBeLessThanOrEqual(1000 * Math.pow(2, i));
15
- }
16
- });
17
- it('should support proportional jitter (default)', () => {
18
- const helper = new BackoffHelper({
19
- initialDelay: 1000,
20
- jitter: 0.1,
21
- jitterStrategy: 'proportional',
22
- });
23
- for (let i = 0; i < 1; i++) {
24
- const delay = helper.getNextDelay();
25
- // Proportional jitter should be 1000 ± 100 (900 to 1100)
26
- expect(delay).toBeGreaterThanOrEqual(900);
27
- expect(delay).toBeLessThanOrEqual(1100);
28
- }
29
- });
30
- it('should provide status emission', () => {
31
- const helper = new BackoffHelper({ initialDelay: 1000 });
32
- expect(helper.status.attempt).toBe(0);
33
- expect(helper.status.currentDelay).toBe(1000);
34
- helper.getNextDelay();
35
- expect(helper.status.attempt).toBe(1);
36
- expect(helper.status.currentDelay).toBe(2000);
37
- helper.reset();
38
- expect(helper.status.attempt).toBe(0);
39
- expect(helper.status.currentDelay).toBe(1000);
40
- });
41
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,49 +0,0 @@
1
- import { TimeoutError } from '../../errors/timeout.error.js';
2
- import { describe, expect, it } from 'vitest';
3
- import { retryWithBackoff } from '../retry-with-backoff.js';
4
- describe('retryWithBackoff', () => {
5
- it('should return result on success', async () => {
6
- const result = await retryWithBackoff(async () => 'success');
7
- expect(result).toBe('success');
8
- });
9
- it('should retry on failure if shouldRetry returns true', async () => {
10
- let attempts = 0;
11
- const result = await retryWithBackoff(async () => {
12
- attempts++;
13
- if (attempts < 3) {
14
- throw new Error('fail');
15
- }
16
- return 'success';
17
- }, {
18
- retry: {
19
- shouldRetry: (_error, attempt) => attempt < 5,
20
- backoff: { initialDelay: 0 },
21
- },
22
- });
23
- expect(result).toBe('success');
24
- expect(attempts).toBe(3);
25
- });
26
- it('should throw after max retries', async () => {
27
- let attempts = 0;
28
- const execute = retryWithBackoff(async () => {
29
- attempts++;
30
- throw new Error('fail');
31
- }, {
32
- retry: {
33
- shouldRetry: (_error, attempt) => attempt < 2,
34
- backoff: { initialDelay: 0 },
35
- },
36
- });
37
- await expect(execute).rejects.toThrow('fail');
38
- expect(attempts).toBe(3); // 0, 1, 2
39
- });
40
- it('should timeout if execution takes too long', async () => {
41
- const execute = retryWithBackoff(async () => {
42
- await new Promise((resolve) => setTimeout(resolve, 100));
43
- return 'success';
44
- }, {
45
- timeout: 50,
46
- });
47
- await expect(execute).rejects.toThrow(TimeoutError);
48
- });
49
- });