@tstdl/base 0.93.181 → 0.93.183

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 +8 -3
  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 +13 -5
  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,188 +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('Extensive Task Queue Dependency Tests', () => {
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 = `extensive-dep-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
- async function completeTask(type) {
25
- const dequeued = await queue.dequeue({ types: [type] });
26
- if (!dequeued)
27
- throw new Error(`Could not dequeue task of type ${type}`);
28
- await queue.complete(dequeued);
29
- }
30
- it('should handle complex mixed chain: A -> (B -> D, C -> E) -> F', async () => {
31
- // A is the root
32
- const taskA = await queue.enqueue('A', {});
33
- // B and C depend on A
34
- const taskB = await queue.enqueue('B', {}, { scheduleAfter: [taskA.id] });
35
- const taskC = await queue.enqueue('C', {}, { scheduleAfter: [taskA.id] });
36
- // D depends on B, E depends on C
37
- const taskD = await queue.enqueue('D', {}, { scheduleAfter: [taskB.id] });
38
- const taskE = await queue.enqueue('E', {}, { scheduleAfter: [taskC.id] });
39
- // F depends on D and E
40
- const taskF = await queue.enqueue('F', {}, { scheduleAfter: [taskD.id, taskE.id] });
41
- expect(taskA.status).toBe(TaskStatus.Pending);
42
- expect(taskB.status).toBe(TaskStatus.Waiting);
43
- expect(taskC.status).toBe(TaskStatus.Waiting);
44
- expect(taskD.status).toBe(TaskStatus.Waiting);
45
- expect(taskE.status).toBe(TaskStatus.Waiting);
46
- expect(taskF.status).toBe(TaskStatus.Waiting);
47
- // 1. Complete A
48
- await completeTask('A');
49
- await queue.waitForTasks([taskB.id, taskC.id], { statuses: [TaskStatus.Pending] });
50
- // 2. Complete B
51
- await completeTask('B');
52
- await queue.waitForTasks([taskD.id], { statuses: [TaskStatus.Pending] });
53
- // 3. Complete C
54
- await completeTask('C');
55
- await queue.waitForTasks([taskE.id], { statuses: [TaskStatus.Pending] });
56
- // F should still be waiting
57
- const uF = await queue.getTask(taskF.id);
58
- expect(uF?.status).toBe(TaskStatus.Waiting);
59
- // 4. Complete D
60
- await completeTask('D');
61
- // F still waiting for E
62
- expect((await queue.getTask(taskF.id))?.status).toBe(TaskStatus.Waiting);
63
- // 5. Complete E
64
- await completeTask('E');
65
- await queue.waitForTasks([taskF.id], { statuses: [TaskStatus.Pending] });
66
- });
67
- it('should respect abortOnDependencyFailure = false (continue other branches)', async () => {
68
- const taskA = await queue.enqueue('A', {});
69
- const taskB = await queue.enqueue('B', {}, { scheduleAfter: [taskA.id], abortOnDependencyFailure: false });
70
- const taskC = await queue.enqueue('C', {}); // Independent
71
- const dA = await queue.dequeue({ types: ['A'] });
72
- await queue.fail(dA, new Error('fatal'), { fatal: true });
73
- // taskB should stay Waiting (default requiredStatus is Completed)
74
- // If abortOnDependencyFailure is false, it should transition to Pending once the dependency is terminal, even if it failed.
75
- await timeout(100);
76
- const uB = await queue.getTask(taskB.id);
77
- expect(uB?.status).toBe(TaskStatus.Pending);
78
- const dC = await queue.dequeue({ types: ['C'] });
79
- expect(dC?.id).toBe(taskC.id);
80
- });
81
- it('should handle priority aging', async () => {
82
- const agingQueue = injector.resolve(TaskQueueProvider).get(`aging-${crypto.randomUUID()}`, {
83
- priorityAgingInterval: 50,
84
- priorityAgingStep: 10,
85
- });
86
- const t1 = await agingQueue.enqueue('low', {}, { priority: 2000 });
87
- const t2 = await agingQueue.enqueue('high', {}, { priority: 1000 });
88
- // Wait for aging to trigger
89
- await timeout(100);
90
- await agingQueue.maintenance();
91
- const u1 = await agingQueue.getTask(t1.id);
92
- const u2 = await agingQueue.getTask(t2.id);
93
- expect(u1.priority).toBeLessThan(2000);
94
- expect(u2.priority).toBeLessThan(1000);
95
- });
96
- it('should handle rate limiting', async () => {
97
- const rateQueue = injector.resolve(TaskQueueProvider).get(`rate-${crypto.randomUUID()}`, {
98
- rateLimit: 5,
99
- rateInterval: 200,
100
- });
101
- // Limit is 5 per 200ms
102
- for (let i = 0; i < 10; i++) {
103
- await rateQueue.enqueue('task', { i });
104
- }
105
- const batch1 = await rateQueue.dequeueMany(10);
106
- expect(batch1.length).toBe(5);
107
- await timeout(500);
108
- const batch2 = await rateQueue.dequeueMany(10);
109
- expect(batch2.length).toBe(5);
110
- });
111
- it('should handle Diamond Dependency (A -> B, A -> C, (B&C) -> D)', async () => {
112
- const taskA = await queue.enqueue('A', {});
113
- const taskB = await queue.enqueue('B', {}, { scheduleAfter: [taskA.id] });
114
- const taskC = await queue.enqueue('C', {}, { scheduleAfter: [taskA.id] });
115
- const taskD = await queue.enqueue('D', {}, { scheduleAfter: [taskB.id, taskC.id] });
116
- await completeTask('A');
117
- await queue.waitForTasks([taskB.id, taskC.id], { statuses: [TaskStatus.Pending] });
118
- await completeTask('B');
119
- expect((await queue.getTask(taskD.id))?.status).toBe(TaskStatus.Waiting);
120
- await completeTask('C');
121
- await queue.waitForTasks([taskD.id], { statuses: [TaskStatus.Pending] });
122
- });
123
- it('should handle Deep Chain (A -> B -> C -> D)', async () => {
124
- const taskA = await queue.enqueue('A', {});
125
- const taskB = await queue.enqueue('B', {}, { scheduleAfter: [taskA.id] });
126
- const taskC = await queue.enqueue('C', {}, { scheduleAfter: [taskB.id] });
127
- const taskD = await queue.enqueue('D', {}, { scheduleAfter: [taskC.id] });
128
- await completeTask('A');
129
- await queue.waitForTasks([taskB.id], { statuses: [TaskStatus.Pending] });
130
- await completeTask('B');
131
- await queue.waitForTasks([taskC.id], { statuses: [TaskStatus.Pending] });
132
- await completeTask('C');
133
- await queue.waitForTasks([taskD.id], { statuses: [TaskStatus.Pending] });
134
- });
135
- it('should handle wide fan-out (A -> [B1...B50] -> C)', async () => {
136
- const taskA = await queue.enqueue('A', {});
137
- const bTasks = await queue.enqueueMany(Array.from({ length: 50 }, (_, i) => ({ type: `B${i}`, data: {}, scheduleAfter: [taskA.id] })), { returnTasks: true });
138
- const taskC = await queue.enqueue('C', {}, { scheduleAfter: bTasks.map(t => t.id) });
139
- await completeTask('A');
140
- for (let i = 0; i < 49; i++) {
141
- await completeTask(`B${i}`);
142
- expect((await queue.getTask(taskC.id))?.status).toBe(TaskStatus.Waiting);
143
- }
144
- await completeTask('B49');
145
- await queue.waitForTasks([taskC.id], { statuses: [TaskStatus.Pending] });
146
- });
147
- it('should handle deep tree with branching and merging', async () => {
148
- const taskA = await queue.enqueue('A', {});
149
- const taskB1 = await queue.enqueue('B1', {}, { scheduleAfter: [taskA.id] });
150
- const taskB2 = await queue.enqueue('B2', {}, { scheduleAfter: [taskA.id] });
151
- const taskC = await queue.enqueue('C', {}, { scheduleAfter: [taskB1.id, taskB2.id] });
152
- const taskD1 = await queue.enqueue('D1', {}, { scheduleAfter: [taskC.id] });
153
- const taskD2 = await queue.enqueue('D2', {}, { scheduleAfter: [taskC.id] });
154
- const taskE = await queue.enqueue('E', {}, { scheduleAfter: [taskD1.id, taskD2.id] });
155
- await completeTask('A');
156
- await completeTask('B1');
157
- await completeTask('B2');
158
- await queue.waitForTasks([taskC.id], { statuses: [TaskStatus.Pending] });
159
- await completeTask('C');
160
- await queue.waitForTasks([taskD1.id], { statuses: [TaskStatus.Pending] });
161
- await queue.waitForTasks([taskD2.id], { statuses: [TaskStatus.Pending] });
162
- await completeTask('D1');
163
- await completeTask('D2');
164
- await queue.waitForTasks([taskE.id], { statuses: [TaskStatus.Pending] });
165
- });
166
- it('should handle abort cascade', async () => {
167
- const taskA = await queue.enqueue('A', {});
168
- const taskB = await queue.enqueue('B', {}, { scheduleAfter: [taskA.id], abortOnDependencyFailure: true });
169
- const taskC = await queue.enqueue('C', {}, { scheduleAfter: [taskB.id], abortOnDependencyFailure: true });
170
- const taskD = await queue.enqueue('D', {}, { scheduleAfter: [taskA.id], abortOnDependencyFailure: true });
171
- const taskE = await queue.enqueue('E', {}, { scheduleAfter: [taskC.id, taskD.id], abortOnDependencyFailure: true });
172
- const dA = await queue.dequeue({ types: ['A'] });
173
- await queue.fail(dA, new Error('fatal'), { fatal: true });
174
- await queue.waitForTasks([taskB.id, taskC.id, taskD.id, taskE.id], { statuses: [TaskStatus.Skipped] });
175
- });
176
- it('should NOT schedule if only one of two dependencies is met (AND logic)', async () => {
177
- const taskA = await queue.enqueue('A', {});
178
- const taskB = await queue.enqueue('B', {});
179
- const taskC = await queue.enqueue('C', {}, { scheduleAfter: [taskA.id, taskB.id] });
180
- await completeTask('A');
181
- expect((await queue.getTask(taskC.id))?.status).toBe(TaskStatus.Waiting);
182
- // Still waiting even after some time
183
- await timeout(100);
184
- expect((await queue.getTask(taskC.id))?.status).toBe(TaskStatus.Waiting);
185
- await completeTask('B');
186
- await queue.waitForTasks([taskC.id], { statuses: [TaskStatus.Pending] });
187
- });
188
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,296 +0,0 @@
1
- import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from 'vitest';
2
- import { CancellationToken } from '../../cancellation/index.js';
3
- import { TaskQueueProvider, TaskStatus } from '../../task-queue/index.js';
4
- import { setupIntegrationTest } from '../../testing/index.js';
5
- describe('Queue Dependencies & Tree Tests', () => {
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 = `dep-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
- describe('Dependencies (DAG)', () => {
25
- it('should schedule a task only after dependency completes (completeAfter)', async () => {
26
- // 1. Prereq
27
- const prereq = await queue.enqueue('prereq', { val: 1 });
28
- // 2. Dependent (Waiting)
29
- const dependent = await queue.enqueue('dependent', { foo: 'bar' }, {
30
- completeAfter: [prereq.id],
31
- });
32
- expect(dependent.status).toBe(TaskStatus.Pending);
33
- // 3. Complete prereq
34
- const dequeued = await queue.dequeue({ types: ['prereq'] });
35
- expect(dequeued?.id).toBe(prereq.id);
36
- await queue.complete(dequeued);
37
- // Complete dependent too so it reaches finalized state for waitForTasks
38
- const dDependent = await queue.dequeue({ types: ['dependent'] });
39
- expect(dDependent?.id).toBe(dependent.id);
40
- await queue.complete(dDependent);
41
- await queue.waitForTasks([dependent.id]);
42
- const updatedDependent = await queue.getTask(dependent.id);
43
- expect(updatedDependent?.status).toBe(TaskStatus.Completed);
44
- });
45
- it('should schedule a task to run after dependency completes (scheduleAfter)', async () => {
46
- // 1. Prereq
47
- const prereq = await queue.enqueue('prereq', {});
48
- // 2. Dependent task
49
- const dependent = await queue.enqueue('dependent', { foo: 'bar' }, {
50
- scheduleAfter: [prereq.id],
51
- });
52
- expect(dependent.status).toBe(TaskStatus.Waiting);
53
- // 3. Complete prereq
54
- const dequeued = await queue.dequeue({ types: ['prereq'] });
55
- await queue.complete(dequeued);
56
- await queue.waitForTasks([dependent.id], { statuses: [TaskStatus.Pending] });
57
- // 5. Dependent should be Pending (ready to run)
58
- const updatedDependent = await queue.getTask(dependent.id);
59
- expect(updatedDependent?.status).toBe(TaskStatus.Pending);
60
- // 6. Should be dequeueable
61
- const d2 = await queue.dequeue({ types: ['dependent'] });
62
- expect(d2?.id).toBe(dependent.id);
63
- });
64
- it('should abort if dependency fails', async () => {
65
- const prereq = await queue.enqueue('prereq', {});
66
- const dependent = await queue.enqueue('dependent', {}, {
67
- scheduleAfter: [prereq.id],
68
- abortOnDependencyFailure: true,
69
- });
70
- const dequeued = await queue.dequeue({ types: ['prereq'] });
71
- // Fail fatally
72
- await queue.fail(dequeued, new Error('boom'), { fatal: true });
73
- await queue.waitForTasks([dependent.id]);
74
- const updatedDependent = await queue.getTask(dependent.id);
75
- expect(updatedDependent?.status).toBe(TaskStatus.Skipped);
76
- expect(updatedDependent?.errors[0]?.code).toBe('DependencyFailed');
77
- });
78
- it('should NOT overwrite terminal states during cancellation (abortOnDependencyFailure + complete)', async () => {
79
- const dep = await queue.enqueue('dep', {});
80
- // completeAfter allows 'main' to be Running while 'dep' is not finished
81
- const main = await queue.enqueue('main', {}, { completeAfter: [dep.id], abortOnDependencyFailure: true });
82
- const runningDep = await queue.dequeue();
83
- expect(runningDep?.id).toBe(dep.id);
84
- const runningMain = await queue.dequeue();
85
- expect(runningMain?.id).toBe(main.id);
86
- expect(runningMain?.status).toBe(TaskStatus.Running);
87
- // Fail 'dep' -> this should make 'main' Skipped
88
- await queue.fail(runningDep, new Error('Dependency failed'), { fatal: true });
89
- const skippedMain = await queue.getTask(main.id);
90
- expect(skippedMain?.status).toBe(TaskStatus.Skipped);
91
- expect(skippedMain?.token).toBeNull();
92
- // Call complete() on the old task reference (which still has the token)
93
- await queue.complete(runningMain);
94
- const finalMain = await queue.getTask(main.id);
95
- expect(finalMain?.status).toBe(TaskStatus.Skipped);
96
- });
97
- it('should resolve dependencies across namespaces', async () => {
98
- const queueProvider = injector.resolve(TaskQueueProvider);
99
- const queueA = queueProvider.get(`QueueA-${crypto.randomUUID()}`);
100
- const queueB = queueProvider.get(`QueueB-${crypto.randomUUID()}`);
101
- const taskA = await queueA.enqueue('test', { value: 'A' });
102
- const taskB = await queueB.enqueue('test', { value: 'B' }, { scheduleAfter: [{ id: taskA.id }] });
103
- expect(taskB.status).toBe(TaskStatus.Waiting);
104
- const dequeuedA = await queueA.dequeue();
105
- await queueA.complete(dequeuedA);
106
- queueB.notify();
107
- await queueB.waitForTasks([taskB.id], { statuses: [TaskStatus.Pending], timeout: 10000 });
108
- const updatedB = await queueB.getTask(taskB.id);
109
- expect(updatedB?.status).toBe(TaskStatus.Pending);
110
- await queueA.clear();
111
- await queueB.clear();
112
- });
113
- it('should correctly skip dependent tasks across namespaces when abortOnDependencyFailure is triggered', async () => {
114
- const queueProvider = injector.resolve(TaskQueueProvider);
115
- const queueA = queueProvider.get(`QueueA-${crypto.randomUUID()}`);
116
- const queueB = queueProvider.get(`QueueB-${crypto.randomUUID()}`);
117
- const taskA = await queueA.enqueue('test', { value: 'A' });
118
- const taskB = await queueB.enqueue('test', { value: 'B' }, {
119
- abortOnDependencyFailure: true,
120
- scheduleAfter: [{ id: taskA.id }],
121
- });
122
- const dequeuedA = await queueA.dequeue();
123
- await queueA.fail(dequeuedA, new Error('Failed'), { fatal: true });
124
- queueB.notify();
125
- await queueB.waitForTasks([taskB.id], { statuses: [TaskStatus.Skipped], timeout: 10000 });
126
- const updatedB = await queueB.getTask(taskB.id);
127
- expect(updatedB?.status).toBe(TaskStatus.Skipped);
128
- await queueA.clear();
129
- await queueB.clear();
130
- });
131
- it('should respect requiredStatuses', async () => {
132
- const prereq = await queue.enqueue('prereq', {});
133
- // Wait for prereq to be Dead or Completed
134
- const dependent = await queue.enqueue('dependent', {}, {
135
- scheduleAfter: [{ id: prereq.id, requiredStatuses: [TaskStatus.Dead, TaskStatus.Completed] }],
136
- });
137
- // Fail fatally -> Dead
138
- const d1 = await queue.dequeue({ types: ['prereq'] });
139
- await queue.fail(d1, new Error('boom'), { fatal: true });
140
- await queue.waitForTasks([dependent.id], { statuses: [TaskStatus.Pending] });
141
- const updated = await queue.getTask(dependent.id);
142
- expect(updated?.status).toBe(TaskStatus.Pending);
143
- });
144
- it('should schedule a task immediately if dependency is already completed (Bug 1)', async () => {
145
- // 1. Enqueue and complete task A
146
- const taskA = await queue.enqueue('taskA', {});
147
- const dequeuedA = await queue.dequeue({ types: ['taskA'] });
148
- await queue.complete(dequeuedA);
149
- const finishedA = await queue.getTask(taskA.id);
150
- expect(finishedA?.status).toBe(TaskStatus.Completed);
151
- // 2. Enqueue task B with scheduleAfter: [A.id]
152
- const taskB = await queue.enqueue('taskB', {}, {
153
- scheduleAfter: [taskA.id],
154
- });
155
- // Wait for proactive resolution to trigger
156
- await queue.waitForTasks([taskB.id], { statuses: [TaskStatus.Pending] });
157
- const updatedB = await queue.getTask(taskB.id);
158
- expect(updatedB?.status).toBe(TaskStatus.Pending);
159
- });
160
- it('should transition to Pending even if duplicate dependencies are specified (Bug 2)', async () => {
161
- // 1. Enqueue task A
162
- const taskA = await queue.enqueue('taskA', {});
163
- // 2. Enqueue task B with duplicate scheduleAfter: [A.id, A.id]
164
- const taskB = await queue.enqueue('taskB', {}, {
165
- scheduleAfter: [taskA.id, taskA.id],
166
- });
167
- expect(taskB.status).toBe(TaskStatus.Waiting);
168
- // 3. Complete task A
169
- const dequeuedA = await queue.dequeue({ types: ['taskA'] });
170
- await queue.complete(dequeuedA);
171
- await queue.waitForTasks([taskB.id], { statuses: [TaskStatus.Pending] });
172
- const updatedB = await queue.getTask(taskB.id);
173
- expect(updatedB?.status).toBe(TaskStatus.Pending);
174
- });
175
- it('should NOT resurrect terminal tasks when calling rescheduleMany (Issue 1)', async () => {
176
- const task = await queue.enqueue('test', { val: 1 });
177
- const dequeued = await queue.dequeue();
178
- await queue.complete(dequeued);
179
- const completedTask = await queue.getTask(task.id);
180
- expect(completedTask?.status).toBe(TaskStatus.Completed);
181
- // Attempt to reschedule
182
- await queue.rescheduleMany([task.id], Date.now());
183
- const stillCompletedTask = await queue.getTask(task.id);
184
- expect(stillCompletedTask?.status).toBe(TaskStatus.Completed);
185
- expect(stillCompletedTask?.completeTimestamp).toBeDefined();
186
- });
187
- });
188
- describe('Tree Operations & Cancellation', () => {
189
- it('should retrieve task tree and cancel hierarchy (multiple children)', async () => {
190
- // Root
191
- const root = await queue.enqueue('root', {});
192
- // Children
193
- const child1 = await queue.enqueue('child1', {}, { parentId: root.id });
194
- const child2 = await queue.enqueue('child2', {}, { parentId: root.id });
195
- // Grandchild
196
- const grandchild = await queue.enqueue('grandchild', {}, { parentId: child1.id });
197
- // Get Tree
198
- const tree = await queue.getTree(root.id);
199
- expect(tree.length).toBe(4);
200
- expect(tree.map((t) => t.id)).toContain(grandchild.id);
201
- expect(tree.map((t) => t.id)).toContain(child2.id);
202
- // Cancel Root
203
- await queue.cancel(root.id);
204
- // Verify all cancelled
205
- const tRoot = await queue.getTask(root.id);
206
- const tChild1 = await queue.getTask(child1.id);
207
- const tChild2 = await queue.getTask(child2.id);
208
- const tGrand = await queue.getTask(grandchild.id);
209
- expect(tRoot?.status).toBe(TaskStatus.Cancelled);
210
- expect(tChild1?.status).toBe(TaskStatus.Cancelled);
211
- expect(tChild2?.status).toBe(TaskStatus.Cancelled);
212
- expect(tGrand?.status).toBe(TaskStatus.Cancelled);
213
- });
214
- it('should retrieve subtree via getTreeByQuery', async () => {
215
- const root = await queue.enqueue('root', { val: 'root' });
216
- const child = await queue.enqueue('child', { val: 'child' }, { parentId: root.id });
217
- await queue.enqueue('other', { val: 'other' });
218
- const tree = await queue.getTreeByQuery({ id: root.id });
219
- expect(tree.length).toBe(2);
220
- expect(tree.map(t => t.id)).toContain(root.id);
221
- expect(tree.map(t => t.id)).toContain(child.id);
222
- });
223
- it('should cancel many by tags', async () => {
224
- await queue.enqueue('t1', {}, { tags: ['group-a'] });
225
- await queue.enqueue('t2', {}, { tags: ['group-a'] });
226
- await queue.enqueue('t3', {}, { tags: ['group-b'] });
227
- await queue.cancelManyByTags(['group-a']);
228
- // Check status
229
- const tasks = await queue.getManyByTags('group-a');
230
- expect(tasks.length).toBe(2);
231
- expect(tasks.every((t) => t.status === TaskStatus.Cancelled)).toBe(true);
232
- const tasksB = await queue.getManyByTags('group-b');
233
- expect(tasksB[0]?.status).toBe(TaskStatus.Pending);
234
- });
235
- it('should NOT resolve completed tasks as Cancelled when calling cancelMany (Issue 3)', async () => {
236
- const taskA = await queue.enqueue('test', { name: 'A' });
237
- const taskB = await queue.enqueue('test', { name: 'B' }, { scheduleAfter: [taskA.id], abortOnDependencyFailure: true });
238
- const dequeuedA = await queue.dequeue();
239
- await queue.complete(dequeuedA);
240
- // B should have unresolvedScheduleDependencies = 0 and be Pending because A completed
241
- const freshB = await queue.getTask(taskB.id);
242
- expect(freshB?.unresolvedScheduleDependencies).toBe(0);
243
- expect(freshB?.status).toBe(TaskStatus.Pending);
244
- // Cancel A. Since A is Completed, it shouldn't be touched in DB.
245
- // Importantly, it shouldn't trigger dependency resolution for B as 'Cancelled'.
246
- // In the old code, this would have caused B to be transitioned to 'Skipped' because A was resolved as 'Cancelled' but B required 'Completed' (and abortOnDependencyFailure is true).
247
- await queue.cancelMany([taskA.id]);
248
- const stillFreshA = await queue.getTask(taskA.id);
249
- expect(stillFreshA?.status).toBe(TaskStatus.Completed);
250
- const stillFreshB = await queue.getTask(taskB.id);
251
- expect(stillFreshB?.status).toBe(TaskStatus.Pending); // Should STILL be Pending
252
- expect(stillFreshB?.unresolvedScheduleDependencies).toBe(0);
253
- });
254
- });
255
- describe('Restart & Consumers', () => {
256
- it('should restart a dead/cancelled task', async () => {
257
- const task = await queue.enqueue('restart-test', {});
258
- const d = await queue.dequeue({ types: ['restart-test'] });
259
- await queue.fail(d, new Error('fatal'), { fatal: true }); // Dead
260
- await queue.restart(task.id);
261
- const updated = await queue.getTask(task.id);
262
- expect(updated?.status).toBe(TaskStatus.Pending);
263
- expect(updated?.tries).toBe(0);
264
- expect(updated?.errors).toEqual([]);
265
- });
266
- it('should consume tasks via async iterator (getConsumer)', async () => {
267
- await queue.enqueue('c1', { val: 1 });
268
- await queue.enqueue('c2', { val: 2 });
269
- const token = new CancellationToken();
270
- const consumer = queue.getConsumer(token);
271
- const t1 = (await consumer.next()).value;
272
- expect(t1.data['val']).toBe(1);
273
- const t2 = (await consumer.next()).value;
274
- expect(t2.data['val']).toBe(2);
275
- token.set(); // Stop consumer
276
- const t3 = await consumer.next();
277
- expect(t3.done).toBe(true);
278
- });
279
- it('should consume batches via async iterator (getBatchConsumer)', async () => {
280
- await queue.enqueueMany([
281
- { type: 'b', data: { val: 1 } },
282
- { type: 'b', data: { val: 2 } },
283
- { type: 'b', data: { val: 3 } },
284
- ]);
285
- const token = new CancellationToken();
286
- const batchConsumer = queue.getBatchConsumer(2, token);
287
- const b1 = (await batchConsumer.next()).value;
288
- expect(b1.length).toBe(2);
289
- const b2 = (await batchConsumer.next()).value;
290
- expect(b2.length).toBe(1);
291
- token.set();
292
- const b3 = await batchConsumer.next();
293
- expect(b3.done).toBe(true);
294
- });
295
- });
296
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,125 +0,0 @@
1
- import { eq, inArray } from 'drizzle-orm';
2
- import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest';
3
- import { Database } from '../../orm/server/index.js';
4
- import { TaskQueueEnqueueBatch } from '../../task-queue/enqueue-batch.js';
5
- import { TaskQueueProvider } from '../../task-queue/index.js';
6
- import { taskDependency as taskDependencyTable } from '../../task-queue/postgres/schemas.js';
7
- import { setupIntegrationTest } from '../../testing/index.js';
8
- describe('TaskQueueEnqueueBatch', () => {
9
- let injector;
10
- let queue;
11
- let database;
12
- let queueProvider;
13
- beforeAll(async () => {
14
- ({ injector } = await setupIntegrationTest({ modules: { taskQueue: true } }));
15
- queueProvider = injector.resolve(TaskQueueProvider);
16
- queue = queueProvider.get(`batch-queue-${crypto.randomUUID()}`);
17
- database = injector.resolve(Database);
18
- });
19
- afterAll(async () => {
20
- await injector?.dispose();
21
- });
22
- it('should restore items if enqueueMany fails', async () => {
23
- const batch = new TaskQueueEnqueueBatch(queue);
24
- batch.add('t1', { val: 1 });
25
- batch.add('t2', { val: 2 });
26
- expect(batch.items.length).toBe(2);
27
- // Mock enqueueMany to throw
28
- vi.spyOn(queue, 'enqueueMany').mockRejectedValueOnce(new Error('db failure'));
29
- await expect(batch.enqueue()).rejects.toThrow('db failure');
30
- // Check items restored
31
- expect(batch.items.length).toBe(2);
32
- expect(batch.items[0].type).toBe('t1');
33
- expect(batch.items[1].type).toBe('t2');
34
- // Should work on second attempt (without mock)
35
- vi.restoreAllMocks();
36
- const tasks = await batch.enqueue({ returnTasks: true });
37
- expect(batch.items.length).toBe(0);
38
- const t1 = await queue.getTask(tasks[0].id);
39
- expect(t1).toBeDefined();
40
- expect(t1?.type).toBe('t1');
41
- });
42
- it('should work correctly with queue.batch()', async () => {
43
- const batch = queue.batch();
44
- batch.add('b1', { x: 1 });
45
- batch.add('b2', { x: 2 });
46
- const tasks = await batch.enqueue({ returnTasks: true });
47
- expect(tasks.length).toBe(2);
48
- expect(batch.items.length).toBe(0);
49
- });
50
- describe('enqueueMany Alignment and Dependencies', () => {
51
- it('should correctly map dependencies when enqueuing a mix of tasks with and without idempotency keys', async () => {
52
- const q = queueProvider.get(`q-${crypto.randomUUID()}`);
53
- const items = [
54
- { type: 'test', data: { value: '1' } },
55
- { type: 'test', data: { value: '2' }, idempotencyKey: 'key-2' },
56
- { type: 'test', data: { value: '3' }, scheduleAfter: [0] },
57
- { type: 'test', data: { value: '4' }, idempotencyKey: 'key-4' },
58
- { type: 'test', data: { value: '5' }, scheduleAfter: [0] },
59
- ];
60
- const tasks = await q.enqueueMany(items, { returnTasks: true });
61
- expect(tasks).toHaveLength(5);
62
- const task1Id = tasks[0].id;
63
- const task3Id = tasks[2].id;
64
- const task5Id = tasks[4].id;
65
- const deps = await database
66
- .select()
67
- .from(taskDependencyTable)
68
- .where(inArray(taskDependencyTable.taskId, [task3Id, task5Id]));
69
- expect(deps).toHaveLength(2);
70
- expect(deps.every(d => d.dependencyTaskId === task1Id)).toBe(true);
71
- });
72
- it('should correctly map dependencies when batch-enqueuing with pre-existing idempotency keys', async () => {
73
- const q = queueProvider.get(`q-${crypto.randomUUID()}`);
74
- const existingTask = await q.enqueue('test', { value: 'existing' }, { idempotencyKey: 'shared-key' });
75
- const items = [
76
- { type: 'test', data: { value: 'new' } },
77
- { type: 'test', data: { value: 'duplicate' }, idempotencyKey: 'shared-key' },
78
- { type: 'test', data: { value: 'dependent' }, scheduleAfter: [1] },
79
- ];
80
- const tasks = await q.enqueueMany(items, { returnTasks: true, replace: false });
81
- expect(tasks[1].id).toBe(existingTask.id);
82
- const dependentTaskId = tasks[2].id;
83
- const deps = await database
84
- .select()
85
- .from(taskDependencyTable)
86
- .where(eq(taskDependencyTable.taskId, dependentTaskId));
87
- expect(deps).toHaveLength(1);
88
- expect(deps[0].dependencyTaskId).toBe(existingTask.id);
89
- });
90
- it('should accurately increment parent unresolvedCompleteDependencies across multiple namespaces in a single batch', async () => {
91
- const queueA = queueProvider.get(`QueueA-${crypto.randomUUID()}`);
92
- const queueB = queueProvider.get(`QueueB-${crypto.randomUUID()}`);
93
- const queueC = queueProvider.get(`QueueC-${crypto.randomUUID()}`);
94
- const parentA = await queueA.enqueue('test', { value: 'parentA' });
95
- const parentB = await queueB.enqueue('test', { value: 'parentB' });
96
- await queueC.enqueueMany([
97
- { type: 'test', data: { value: 'child1' }, parentId: parentA.id },
98
- { type: 'test', data: { value: 'child2' }, parentId: parentA.id },
99
- { type: 'test', data: { value: 'child3' }, parentId: parentB.id },
100
- ]);
101
- const upA = await queueA.getTask(parentA.id);
102
- const upB = await queueB.getTask(parentB.id);
103
- expect(upA?.unresolvedCompleteDependencies).toBe(2);
104
- expect(upB?.unresolvedCompleteDependencies).toBe(1);
105
- });
106
- it('should correctly map dependencies when using object with index', async () => {
107
- const q = queueProvider.get(`q-${crypto.randomUUID()}`);
108
- const items = [
109
- { type: 'test', data: { value: '1' } },
110
- { type: 'test', data: { value: '2' }, scheduleAfter: [0] },
111
- { type: 'test', data: { value: '3' }, scheduleAfter: [{ index: 0 }] },
112
- ];
113
- const tasks = await q.enqueueMany(items, { returnTasks: true });
114
- const task1Id = tasks[0].id;
115
- const task2Id = tasks[1].id;
116
- const task3Id = tasks[2].id;
117
- const deps = await database
118
- .select()
119
- .from(taskDependencyTable)
120
- .where(inArray(taskDependencyTable.taskId, [task2Id, task3Id]));
121
- expect(deps).toHaveLength(2);
122
- expect(deps.every(d => d.dependencyTaskId === task1Id)).toBe(true);
123
- });
124
- });
125
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,12 +0,0 @@
1
- import { describe, expectTypeOf, test } from 'vitest';
2
- describe('EnqueueManyItem Type Definitions', () => {
3
- test('EnqueueManyItem should be a discriminated union', () => {
4
- const item = {};
5
- if (item.type == 'test-task') {
6
- expectTypeOf(item.data).toEqualTypeOf();
7
- }
8
- else if (item.type == 'other-task') {
9
- expectTypeOf(item.data).toEqualTypeOf();
10
- }
11
- });
12
- });
@@ -1 +0,0 @@
1
- export {};