@tstdl/base 0.93.86 → 0.93.89

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 (318) hide show
  1. package/ai/genkit/helpers.d.ts +3 -1
  2. package/ai/genkit/helpers.js +3 -3
  3. package/api/server/gateway.d.ts +3 -0
  4. package/api/server/gateway.js +15 -4
  5. package/api/server/middlewares/catch-error.middleware.js +2 -4
  6. package/api/server/middlewares/cors.middleware.js +2 -3
  7. package/api/server/middlewares/csrf.middleware.d.ts +41 -0
  8. package/api/server/middlewares/csrf.middleware.js +108 -0
  9. package/api/server/middlewares/index.d.ts +1 -0
  10. package/api/server/middlewares/index.js +1 -0
  11. package/api/server/module.d.ts +8 -2
  12. package/api/server/module.js +14 -8
  13. package/api/server/tests/csrf.middleware.test.js +91 -0
  14. package/audit/drizzle/{0000_bored_stick.sql → 0000_lumpy_thunderball.sql} +3 -3
  15. package/audit/drizzle/meta/0000_snapshot.json +4 -4
  16. package/audit/drizzle/meta/_journal.json +2 -9
  17. package/audit/module.d.ts +4 -1
  18. package/audit/module.js +3 -2
  19. package/audit/schemas.d.ts +1 -1
  20. package/audit/types.d.ts +1 -1
  21. package/audit/types.js +1 -1
  22. package/authentication/client/authentication.service.d.ts +14 -1
  23. package/authentication/client/authentication.service.js +82 -23
  24. package/authentication/client/http-client.middleware.d.ts +6 -0
  25. package/authentication/client/http-client.middleware.js +36 -0
  26. package/authentication/client/module.js +8 -2
  27. package/authentication/models/service-account.model.d.ts +2 -2
  28. package/authentication/models/service-account.model.js +10 -5
  29. package/authentication/models/subject.model.d.ts +19 -5
  30. package/authentication/models/subject.model.js +25 -29
  31. package/authentication/models/system-account.model.d.ts +3 -2
  32. package/authentication/models/system-account.model.js +11 -5
  33. package/authentication/models/user.model.d.ts +2 -11
  34. package/authentication/models/user.model.js +5 -16
  35. package/authentication/server/authentication-api-request-token.provider.d.ts +0 -2
  36. package/authentication/server/authentication-api-request-token.provider.js +3 -11
  37. package/authentication/server/authentication.api-controller.d.ts +1 -2
  38. package/authentication/server/authentication.api-controller.js +8 -9
  39. package/authentication/server/authentication.audit.d.ts +3 -2
  40. package/authentication/server/authentication.service.d.ts +27 -1
  41. package/authentication/server/authentication.service.js +67 -18
  42. package/authentication/server/drizzle/{0000_normal_paper_doll.sql → 0000_soft_tag.sql} +25 -32
  43. package/authentication/server/drizzle/meta/0000_snapshot.json +180 -205
  44. package/authentication/server/drizzle/meta/_journal.json +2 -2
  45. package/authentication/server/helper.js +9 -2
  46. package/authentication/server/module.d.ts +4 -1
  47. package/authentication/server/module.js +9 -5
  48. package/authentication/server/schemas.d.ts +2 -1
  49. package/authentication/server/schemas.js +2 -2
  50. package/authentication/server/subject.service.d.ts +14 -8
  51. package/authentication/server/subject.service.js +86 -84
  52. package/authentication/tests/authentication-ancillary.service.test.d.ts +1 -0
  53. package/authentication/tests/authentication-ancillary.service.test.js +13 -0
  54. package/authentication/tests/authentication-secret-requirements.validator.test.d.ts +1 -0
  55. package/authentication/tests/authentication-secret-requirements.validator.test.js +29 -0
  56. package/authentication/tests/authentication.api-controller.test.d.ts +1 -0
  57. package/authentication/tests/authentication.api-controller.test.js +88 -0
  58. package/authentication/tests/authentication.api-request-token.provider.test.d.ts +1 -0
  59. package/authentication/tests/authentication.api-request-token.provider.test.js +48 -0
  60. package/authentication/tests/authentication.client-middleware.test.d.ts +1 -0
  61. package/authentication/tests/authentication.client-middleware.test.js +23 -0
  62. package/authentication/tests/authentication.client-service.test.d.ts +1 -0
  63. package/authentication/tests/authentication.client-service.test.js +70 -0
  64. package/authentication/tests/authentication.service.test.d.ts +1 -0
  65. package/authentication/tests/authentication.service.test.js +186 -0
  66. package/authentication/tests/authentication.test-ancillary-service.d.ts +9 -0
  67. package/authentication/tests/authentication.test-ancillary-service.js +27 -0
  68. package/authentication/tests/helper.test.d.ts +1 -0
  69. package/authentication/tests/helper.test.js +107 -0
  70. package/authentication/tests/secret-requirements.error.test.d.ts +1 -0
  71. package/authentication/tests/secret-requirements.error.test.js +14 -0
  72. package/authentication/tests/subject.service.test.d.ts +1 -0
  73. package/authentication/tests/subject.service.test.js +140 -0
  74. package/circuit-breaker/postgres/drizzle/meta/0000_snapshot.json +1 -1
  75. package/circuit-breaker/postgres/drizzle/meta/_journal.json +2 -2
  76. package/circuit-breaker/postgres/module.d.ts +7 -1
  77. package/circuit-breaker/postgres/module.js +8 -6
  78. package/circuit-breaker/tests/circuit-breaker.test.js +2 -22
  79. package/document-management/api/document-management.api.js +2 -6
  80. package/document-management/server/services/document-validation.service.js +6 -5
  81. package/document-management/server/services/document-workflow.service.js +5 -5
  82. package/document-management/service-models/document-folders.view-model.d.ts +5 -2
  83. package/document-management/service-models/document-folders.view-model.js +42 -9
  84. package/document-management/service-models/enriched/enriched-document-management-data.view.js +1 -1
  85. package/examples/document-management/main.js +4 -4
  86. package/http/client/adapters/undici.adapter.d.ts +7 -5
  87. package/http/client/adapters/undici.adapter.js +13 -10
  88. package/http/client/module.d.ts +3 -1
  89. package/http/client/module.js +8 -9
  90. package/http/server/http-server.d.ts +2 -0
  91. package/http/server/node/module.d.ts +6 -2
  92. package/http/server/node/module.js +6 -4
  93. package/http/server/node/node-http-server.d.ts +2 -0
  94. package/http/server/node/node-http-server.js +7 -0
  95. package/http/types.d.ts +1 -1
  96. package/key-value-store/postgres/module.d.ts +7 -1
  97. package/key-value-store/postgres/module.js +7 -3
  98. package/lock/postgres/lock.js +0 -1
  99. package/lock/postgres/module.d.ts +7 -1
  100. package/lock/postgres/module.js +9 -5
  101. package/logger/formatter.d.ts +2 -0
  102. package/logger/formatters/json.js +2 -2
  103. package/logger/formatters/pretty-print.js +8 -10
  104. package/logger/logger.d.ts +1 -1
  105. package/logger/logger.js +15 -12
  106. package/message-bus/local/module.d.ts +5 -2
  107. package/message-bus/local/module.js +5 -4
  108. package/module/module.d.ts +2 -1
  109. package/module/module.js +3 -0
  110. package/module/modules/web-server.module.d.ts +11 -6
  111. package/module/modules/web-server.module.js +15 -10
  112. package/orm/decorators.d.ts +24 -1
  113. package/orm/decorators.js +40 -4
  114. package/orm/index.d.ts +1 -1
  115. package/orm/index.js +1 -1
  116. package/orm/query/base.d.ts +17 -17
  117. package/orm/query/base.js +1 -1
  118. package/orm/repository.types.d.ts +46 -2
  119. package/orm/schemas/tsvector.js +1 -1
  120. package/orm/server/drizzle/schema-converter.d.ts +3 -1
  121. package/orm/server/drizzle/schema-converter.js +120 -14
  122. package/orm/server/index.d.ts +1 -0
  123. package/orm/server/index.js +1 -0
  124. package/orm/server/module.d.ts +4 -2
  125. package/orm/server/module.js +6 -5
  126. package/orm/server/query-converter.d.ts +6 -3
  127. package/orm/server/query-converter.js +33 -21
  128. package/orm/server/repository-config.d.ts +8 -0
  129. package/orm/server/repository-config.js +8 -0
  130. package/orm/server/repository.d.ts +117 -43
  131. package/orm/server/repository.js +758 -254
  132. package/orm/server/transaction.d.ts +4 -2
  133. package/orm/server/transaction.js +14 -5
  134. package/orm/server/transactional.d.ts +6 -2
  135. package/orm/server/transactional.js +39 -9
  136. package/orm/server/types.d.ts +2 -0
  137. package/orm/sqls/case-when.d.ts +25 -0
  138. package/orm/sqls/case-when.js +54 -0
  139. package/orm/sqls/index.d.ts +2 -0
  140. package/orm/sqls/index.js +2 -0
  141. package/orm/{sqls.d.ts → sqls/sqls.d.ts} +67 -19
  142. package/orm/{sqls.js → sqls/sqls.js} +116 -22
  143. package/orm/tests/data-types.test.d.ts +1 -0
  144. package/orm/tests/data-types.test.js +39 -0
  145. package/orm/tests/decorators.test.d.ts +1 -0
  146. package/orm/tests/decorators.test.js +77 -0
  147. package/orm/tests/encryption.test.d.ts +1 -0
  148. package/orm/tests/encryption.test.js +34 -0
  149. package/orm/tests/query-complex.test.d.ts +1 -0
  150. package/orm/tests/query-complex.test.js +203 -0
  151. package/orm/tests/query-converter-complex.test.d.ts +1 -0
  152. package/orm/tests/query-converter-complex.test.js +126 -0
  153. package/orm/tests/query-converter.test.d.ts +1 -0
  154. package/orm/tests/query-converter.test.js +123 -0
  155. package/orm/tests/repository-advanced.test.d.ts +1 -0
  156. package/orm/tests/repository-advanced.test.js +232 -0
  157. package/orm/tests/repository-attributes.test.d.ts +1 -0
  158. package/orm/tests/repository-attributes.test.js +99 -0
  159. package/orm/tests/repository-comprehensive.test.d.ts +1 -0
  160. package/orm/tests/repository-comprehensive.test.js +187 -0
  161. package/orm/tests/repository-coverage.test.d.ts +1 -0
  162. package/orm/tests/repository-coverage.test.js +303 -0
  163. package/orm/tests/repository-cti-complex.test.d.ts +1 -0
  164. package/orm/tests/repository-cti-complex.test.js +170 -0
  165. package/orm/tests/repository-cti-embedded.test.d.ts +1 -0
  166. package/orm/tests/repository-cti-embedded.test.js +188 -0
  167. package/orm/tests/repository-cti-extensive.test.d.ts +1 -0
  168. package/orm/tests/repository-cti-extensive.test.js +308 -0
  169. package/orm/tests/repository-cti-mapping.test.d.ts +1 -0
  170. package/orm/tests/repository-cti-mapping.test.js +121 -0
  171. package/orm/tests/repository-cti-search.test.d.ts +1 -0
  172. package/orm/tests/repository-cti-search.test.js +152 -0
  173. package/orm/tests/repository-cti-soft-delete.test.d.ts +1 -0
  174. package/orm/tests/repository-cti-soft-delete.test.js +115 -0
  175. package/orm/tests/repository-cti-transactions.test.d.ts +1 -0
  176. package/orm/tests/repository-cti-transactions.test.js +126 -0
  177. package/orm/tests/repository-cti-upsert-many.test.d.ts +1 -0
  178. package/orm/tests/repository-cti-upsert-many.test.js +127 -0
  179. package/orm/tests/repository-cti.test.d.ts +1 -0
  180. package/orm/tests/repository-cti.test.js +456 -0
  181. package/orm/tests/repository-edge-cases.test.d.ts +1 -0
  182. package/orm/tests/repository-edge-cases.test.js +216 -0
  183. package/orm/tests/repository-expiration.test.d.ts +1 -0
  184. package/orm/tests/repository-expiration.test.js +153 -0
  185. package/orm/tests/repository-extra-coverage.test.d.ts +1 -0
  186. package/orm/tests/repository-extra-coverage.test.js +546 -0
  187. package/orm/tests/repository-mapping.test.d.ts +1 -0
  188. package/orm/tests/repository-mapping.test.js +71 -0
  189. package/orm/tests/repository-regression.test.d.ts +1 -0
  190. package/orm/tests/repository-regression.test.js +330 -0
  191. package/orm/tests/repository-search-coverage.test.d.ts +1 -0
  192. package/orm/tests/repository-search-coverage.test.js +129 -0
  193. package/orm/tests/repository-search.test.d.ts +1 -0
  194. package/orm/tests/repository-search.test.js +116 -0
  195. package/orm/tests/repository-soft-delete.test.d.ts +1 -0
  196. package/orm/tests/repository-soft-delete.test.js +143 -0
  197. package/orm/tests/repository-transactions-nested.test.d.ts +1 -0
  198. package/orm/tests/repository-transactions-nested.test.js +202 -0
  199. package/orm/tests/repository-types.test.d.ts +1 -0
  200. package/orm/tests/repository-types.test.js +218 -0
  201. package/orm/tests/schema-converter.test.d.ts +1 -0
  202. package/orm/tests/schema-converter.test.js +81 -0
  203. package/orm/tests/schema-generation.test.d.ts +1 -0
  204. package/orm/tests/schema-generation.test.js +127 -0
  205. package/orm/tests/sql-helpers.test.d.ts +1 -0
  206. package/orm/tests/sql-helpers.test.js +67 -0
  207. package/orm/tests/transaction-safety.test.d.ts +1 -0
  208. package/orm/tests/transaction-safety.test.js +81 -0
  209. package/orm/tests/transactional.test.d.ts +1 -0
  210. package/orm/tests/transactional.test.js +224 -0
  211. package/orm/tests/utils.test.d.ts +1 -0
  212. package/orm/tests/utils.test.js +70 -0
  213. package/orm/utils.d.ts +7 -0
  214. package/orm/utils.js +26 -6
  215. package/package.json +12 -7
  216. package/pool/pool.js +1 -1
  217. package/rate-limit/index.d.ts +2 -0
  218. package/rate-limit/index.js +2 -0
  219. package/rate-limit/postgres/drizzle/0000_watery_rage.sql +7 -0
  220. package/{queue → rate-limit}/postgres/drizzle/meta/0000_snapshot.json +14 -39
  221. package/rate-limit/postgres/drizzle/meta/_journal.json +13 -0
  222. package/{queue → rate-limit}/postgres/drizzle.config.js +1 -1
  223. package/rate-limit/postgres/index.d.ts +4 -0
  224. package/rate-limit/postgres/index.js +4 -0
  225. package/rate-limit/postgres/module.d.ts +12 -0
  226. package/rate-limit/postgres/module.js +28 -0
  227. package/rate-limit/postgres/postgres-rate-limiter.d.ts +9 -0
  228. package/rate-limit/postgres/postgres-rate-limiter.js +56 -0
  229. package/rate-limit/postgres/rate-limit.model.d.ts +8 -0
  230. package/rate-limit/postgres/rate-limit.model.js +35 -0
  231. package/rate-limit/postgres/rate-limiter.provider.d.ts +6 -0
  232. package/rate-limit/postgres/rate-limiter.provider.js +21 -0
  233. package/rate-limit/postgres/schemas.d.ts +3 -0
  234. package/rate-limit/postgres/schemas.js +4 -0
  235. package/rate-limit/provider.d.ts +9 -0
  236. package/rate-limit/provider.js +2 -0
  237. package/rate-limit/rate-limiter.d.ts +35 -0
  238. package/rate-limit/rate-limiter.js +3 -0
  239. package/rate-limit/tests/postgres-rate-limiter.test.d.ts +1 -0
  240. package/rate-limit/tests/postgres-rate-limiter.test.js +92 -0
  241. package/signals/implementation/configure.d.ts +3 -0
  242. package/signals/implementation/configure.js +3 -0
  243. package/sse/data-stream-source.d.ts +1 -1
  244. package/sse/data-stream-source.js +6 -6
  245. package/task-queue/enqueue-batch.d.ts +17 -0
  246. package/task-queue/enqueue-batch.js +24 -0
  247. package/{queue → task-queue}/index.d.ts +1 -1
  248. package/{queue → task-queue}/index.js +1 -1
  249. package/task-queue/postgres/drizzle/0000_thin_black_panther.sql +74 -0
  250. package/task-queue/postgres/drizzle/meta/0000_snapshot.json +592 -0
  251. package/task-queue/postgres/drizzle/meta/_journal.json +13 -0
  252. package/task-queue/postgres/drizzle.config.d.ts +2 -0
  253. package/task-queue/postgres/drizzle.config.js +11 -0
  254. package/task-queue/postgres/index.d.ts +4 -0
  255. package/task-queue/postgres/index.js +4 -0
  256. package/task-queue/postgres/module.d.ts +12 -0
  257. package/task-queue/postgres/module.js +28 -0
  258. package/task-queue/postgres/schemas.d.ts +16 -0
  259. package/task-queue/postgres/schemas.js +8 -0
  260. package/task-queue/postgres/task-queue.d.ts +83 -0
  261. package/task-queue/postgres/task-queue.js +1054 -0
  262. package/task-queue/postgres/task-queue.provider.d.ts +7 -0
  263. package/{queue/postgres/queue.provider.js → task-queue/postgres/task-queue.provider.js} +8 -8
  264. package/task-queue/postgres/task.model.d.ts +39 -0
  265. package/task-queue/postgres/task.model.js +178 -0
  266. package/{queue → task-queue}/provider.d.ts +3 -3
  267. package/task-queue/provider.js +2 -0
  268. package/{queue → task-queue}/task-context.d.ts +7 -7
  269. package/{queue → task-queue}/task-context.js +8 -8
  270. package/{queue/queue.d.ts → task-queue/task-queue.d.ts} +128 -59
  271. package/task-queue/task-queue.js +200 -0
  272. package/task-queue/tests/complex.test.d.ts +1 -0
  273. package/task-queue/tests/complex.test.js +299 -0
  274. package/task-queue/tests/dependencies.test.d.ts +1 -0
  275. package/task-queue/tests/dependencies.test.js +174 -0
  276. package/task-queue/tests/queue.test.d.ts +1 -0
  277. package/task-queue/tests/queue.test.js +334 -0
  278. package/task-queue/tests/worker.test.d.ts +1 -0
  279. package/task-queue/tests/worker.test.js +163 -0
  280. package/test1.js +1 -1
  281. package/test4.js +2 -2
  282. package/unit-test/index.d.ts +1 -0
  283. package/unit-test/index.js +1 -0
  284. package/unit-test/integration-setup.d.ts +55 -0
  285. package/unit-test/integration-setup.js +182 -0
  286. package/utils/patterns.d.ts +3 -0
  287. package/utils/patterns.js +6 -1
  288. package/audit/drizzle/0001_previous_network.sql +0 -2
  289. package/audit/drizzle/meta/0001_snapshot.json +0 -195
  290. package/queue/enqueue-batch.d.ts +0 -17
  291. package/queue/enqueue-batch.js +0 -18
  292. package/queue/postgres/drizzle/0000_zippy_moondragon.sql +0 -11
  293. package/queue/postgres/drizzle/0001_certain_wild_pack.sql +0 -2
  294. package/queue/postgres/drizzle/0002_dear_meggan.sql +0 -2
  295. package/queue/postgres/drizzle/0003_tricky_venom.sql +0 -30
  296. package/queue/postgres/drizzle/meta/0001_snapshot.json +0 -103
  297. package/queue/postgres/drizzle/meta/0002_snapshot.json +0 -90
  298. package/queue/postgres/drizzle/meta/0003_snapshot.json +0 -288
  299. package/queue/postgres/drizzle/meta/_journal.json +0 -34
  300. package/queue/postgres/index.d.ts +0 -4
  301. package/queue/postgres/index.js +0 -4
  302. package/queue/postgres/module.d.ts +0 -9
  303. package/queue/postgres/module.js +0 -29
  304. package/queue/postgres/queue.d.ts +0 -60
  305. package/queue/postgres/queue.js +0 -681
  306. package/queue/postgres/queue.provider.d.ts +0 -7
  307. package/queue/postgres/schemas.d.ts +0 -14
  308. package/queue/postgres/schemas.js +0 -6
  309. package/queue/postgres/task.model.d.ts +0 -24
  310. package/queue/postgres/task.model.js +0 -115
  311. package/queue/provider.js +0 -2
  312. package/queue/queue.js +0 -131
  313. package/queue/tests/queue.test.js +0 -623
  314. package/test3.d.ts +0 -1
  315. package/test3.js +0 -47
  316. /package/{queue/tests/queue.test.d.ts → api/server/tests/csrf.middleware.test.d.ts} +0 -0
  317. /package/circuit-breaker/postgres/drizzle/{0000_hard_shocker.sql → 0000_cooing_korath.sql} +0 -0
  318. /package/{queue → rate-limit}/postgres/drizzle.config.d.ts +0 -0
@@ -1,681 +0,0 @@
1
- var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
- return c > 3 && r && Object.defineProperty(target, key, r), r;
6
- };
7
- import { and, asc, eq, gt, inArray, lt, lte, or, sql, isNull as sqlIsNull } from 'drizzle-orm';
8
- import { merge } from 'rxjs';
9
- import { CancellationSignal } from '../../cancellation/index.js';
10
- import { CircuitBreaker, CircuitBreakerState } from '../../circuit-breaker/index.js';
11
- import { Enumerable } from '../../enumerable/enumerable.js';
12
- import { inject, injectArgument, provide, Singleton } from '../../injector/index.js';
13
- import { MessageBus } from '../../message-bus/index.js';
14
- import { coalesce, interval, RANDOM_UUID_V4, TRANSACTION_TIMESTAMP } from '../../orm/index.js';
15
- import { DatabaseConfig, injectRepository } from '../../orm/server/index.js';
16
- import { toArray } from '../../utils/array/array.js';
17
- import { currentTimestamp } from '../../utils/date-time.js';
18
- import { serializeError } from '../../utils/format-error.js';
19
- import { cancelableTimeout } from '../../utils/timing.js';
20
- import { isDefined, isNotNull, isNull, isString, isUndefined } from '../../utils/type-guards.js';
21
- import { millisecondsPerSecond } from '../../utils/units.js';
22
- import { defaultQueueConfig, Queue, TaskState, UniqueTagStrategy } from '../queue.js';
23
- import { PostgresQueueModuleConfig } from './module.js';
24
- import { task as taskTable } from './schemas.js';
25
- import { PostgresTask } from './task.model.js';
26
- let PostgresQueue = class PostgresQueue extends Queue {
27
- #repository = injectRepository(PostgresTask);
28
- #config = injectArgument(this);
29
- #queueName = isString(this.#config) ? this.#config : this.#config.name;
30
- #messageBus = inject((MessageBus), `PostgresQueue:${this.#queueName}`);
31
- #circuitBreaker = inject(CircuitBreaker, {
32
- key: this.#queueName,
33
- threshold: (isString(this.#config) ? undefined : this.#config.circuitBreakerThreshold) ?? defaultQueueConfig.circuitBreakerThreshold,
34
- resetTimeout: (isString(this.#config) ? undefined : this.#config.circuitBreakerResetTimeout) ?? defaultQueueConfig.circuitBreakerResetTimeout,
35
- });
36
- processTimeout = (isString(this.#config) ? undefined : this.#config.processTimeout) ?? defaultQueueConfig.processTimeout;
37
- executionTimeout = (isString(this.#config) ? undefined : this.#config.executionTimeout) ?? defaultQueueConfig.executionTimeout;
38
- maxTries = (isString(this.#config) ? undefined : this.#config.maxTries) ?? defaultQueueConfig.maxTries;
39
- retryDelayMinimum = (isString(this.#config) ? undefined : this.#config.retryDelayMinimum) ?? defaultQueueConfig.retryDelayMinimum;
40
- retryDelayMaximum = (isString(this.#config) ? undefined : this.#config.retryDelayMaximum) ?? defaultQueueConfig.retryDelayMaximum;
41
- retryDelayGrowth = (isString(this.#config) ? undefined : this.#config.retryDelayGrowth) ?? defaultQueueConfig.retryDelayGrowth;
42
- retentionPeriod = (isString(this.#config) ? undefined : this.#config.retentionPeriod) ?? defaultQueueConfig.retentionPeriod;
43
- globalConcurrency = (isString(this.#config) ? undefined : this.#config.globalConcurrency) ?? defaultQueueConfig.globalConcurrency;
44
- // -- Updates --
45
- #keepOldUpdate = { id: sql `${taskTable.id}` };
46
- #takeNewUpdate = {
47
- id: RANDOM_UUID_V4,
48
- queue: this.#queueName,
49
- priority: sql `excluded.priority`,
50
- tag: sql `excluded.tag`,
51
- status: TaskState.Pending,
52
- lease: null,
53
- tries: 0,
54
- progress: 0,
55
- enqueueTimestamp: TRANSACTION_TIMESTAMP,
56
- scheduleTimestamp: sql `excluded.schedule_timestamp`,
57
- startTimestamp: null,
58
- expirationTimestamp: sql `excluded.expiration_timestamp`,
59
- lockExpirationTimestamp: null,
60
- data: sql `excluded.data`,
61
- state: null,
62
- error: null,
63
- result: null,
64
- };
65
- async enqueue(data, options) {
66
- const tasks = await this.enqueueMany([{ data, ...options }], { uniqueTag: options?.uniqueTag, returnTasks: true, transaction: options?.transaction });
67
- return tasks[0];
68
- }
69
- async enqueueMany(items, options) {
70
- if (items.length == 0) {
71
- return (options?.returnTasks == true) ? [] : undefined;
72
- }
73
- const newEntities = items.map((item) => ({
74
- queue: this.#queueName,
75
- status: TaskState.Pending,
76
- lease: null,
77
- priority: item.priority ?? 1000,
78
- tag: item.tag ?? null,
79
- parentId: item.parentId ?? null,
80
- tries: 0,
81
- progress: 0,
82
- enqueueTimestamp: TRANSACTION_TIMESTAMP,
83
- scheduleTimestamp: item.scheduleTimestamp ?? TRANSACTION_TIMESTAMP,
84
- startTimestamp: null,
85
- expirationTimestamp: item.expirationTimestamp ?? null,
86
- lockExpirationTimestamp: null,
87
- completeTimestamp: null,
88
- data: item.data,
89
- state: null,
90
- result: null,
91
- error: null,
92
- }));
93
- const update = (options?.uniqueTag == UniqueTagStrategy.TakeNew)
94
- ? this.#takeNewUpdate
95
- : (options?.uniqueTag == UniqueTagStrategy.KeepOld)
96
- ? this.#keepOldUpdate
97
- : undefined;
98
- const repository = this.#repository.withOptionalTransaction(options?.transaction);
99
- const tasks = isUndefined(update)
100
- ? await repository.insertMany(newEntities)
101
- : await repository.upsertMany(['queue', 'tag'], newEntities, update);
102
- this.#messageBus.publishAndForget();
103
- if (options?.returnTasks == true) {
104
- return tasks;
105
- }
106
- return undefined;
107
- }
108
- async has(id) {
109
- return await this.#repository.hasByQuery({ queue: this.#queueName, id });
110
- }
111
- async countByTag(tag) {
112
- return await this.#repository.countByQuery({ queue: this.#queueName, tag });
113
- }
114
- async get(id) {
115
- return await this.#repository.tryLoadByQuery({ queue: this.#queueName, id });
116
- }
117
- async getByTag(tag) {
118
- return await this.#repository.loadManyByQuery({ queue: this.#queueName, tag });
119
- }
120
- async getByTags(tags) {
121
- return await this.#repository.loadManyByQuery({ queue: this.#queueName, tag: { $in: tags } });
122
- }
123
- async getTree(idOrIds, transaction) {
124
- const ids = toArray(idOrIds);
125
- return await this.getTreeByQuery({ id: { $in: ids } }, transaction);
126
- }
127
- async getTreeByQuery(query, transaction) {
128
- return await this.#repository.useTransaction(transaction, async (tx) => {
129
- const repositoryWithTransaction = this.#repository.withTransaction(tx);
130
- const tasks = await repositoryWithTransaction.loadManyByQuery(query);
131
- if (tasks.length == 0) {
132
- return [];
133
- }
134
- let currentLevelIds = tasks.map((task) => task.id);
135
- let depth = 0;
136
- while (true) {
137
- if (depth++ > 100) {
138
- throw new Error('Possible cyclic task parent-child relationship detected in queue "' + this.#queueName + '"');
139
- }
140
- const childTasks = await repositoryWithTransaction.loadManyByQuery({ parentId: { $in: currentLevelIds } });
141
- if (childTasks.length == 0) {
142
- break;
143
- }
144
- currentLevelIds = childTasks.map((task) => task.id);
145
- tasks.push(...childTasks);
146
- }
147
- return tasks;
148
- });
149
- }
150
- async cancel(id, transaction) {
151
- await this.cancelMany([id], transaction);
152
- }
153
- async cancelMany(ids, transaction) {
154
- await this.#repository.useTransaction(transaction, async (tx) => {
155
- const tree = await this.getTree(ids, tx);
156
- const treeIds = tree.map((task) => task.id);
157
- if (treeIds.length == 0) {
158
- return;
159
- }
160
- await this.#repository.withTransaction(tx).updateMany(treeIds, {
161
- status: TaskState.Cancelled,
162
- lease: null,
163
- completeTimestamp: TRANSACTION_TIMESTAMP,
164
- });
165
- const uniqueParents = new Set();
166
- for (const t of tree) {
167
- const task = t;
168
- if (isNotNull(task.parentId)) {
169
- uniqueParents.add(task.parentId);
170
- }
171
- }
172
- for (const parentId of uniqueParents) {
173
- await this.#triggerParentFanIn(parentId, tx);
174
- }
175
- });
176
- }
177
- async cancelByTag(tag) {
178
- await this.cancelManyByTag([tag]);
179
- }
180
- async cancelManyByTag(tags) {
181
- await this.#repository.transaction(async (tx) => {
182
- const task = await this.getByTags(tags);
183
- const ids = task.map((t) => t.id);
184
- await this.cancelMany(ids, tx);
185
- });
186
- }
187
- async clear() {
188
- await this.#repository.hardDeleteManyByQuery({ queue: this.#queueName });
189
- }
190
- async dequeue() {
191
- const tasks = await this.dequeueMany(1);
192
- if (tasks.length == 0) {
193
- return undefined;
194
- }
195
- return tasks[0];
196
- }
197
- async dequeueMany(count) {
198
- return await this.#repository.transaction(async (tx) => {
199
- let effectiveCount = count;
200
- // 1. Check Circuit Breaker
201
- const result = await this.#circuitBreaker.check();
202
- if (!result.allowed) {
203
- return [];
204
- }
205
- if (result.state == CircuitBreakerState.HalfOpen) {
206
- // If we are probing (HalfOpen), we must ensure no other tasks are running locally.
207
- // NOTE: PostgresCircuitBreakerService.check() handles the "one probe per timeout" via the DB state transition.
208
- // However, we still might want to limit this batch to 1 task if we are the probe.
209
- if (result.isProbe != true) {
210
- const runningCount = await this.#repository.withTransaction(tx).countByQuery({ queue: this.#queueName, status: TaskState.Running });
211
- if (runningCount > 0) {
212
- return [];
213
- }
214
- }
215
- effectiveCount = 1;
216
- }
217
- // 2. Check Global Concurrency
218
- if (isNotNull(this.globalConcurrency)) {
219
- const runningCount = await this.#repository.withTransaction(tx).countByQuery({ queue: this.#queueName, status: TaskState.Running });
220
- if (runningCount >= this.globalConcurrency) {
221
- return [];
222
- }
223
- }
224
- /*
225
- * Materialization required for LIMIT clause
226
- * https://stackoverflow.com/questions/73966670/select-for-update-subquery-not-respecting-limit-clause-under-load
227
- * https://dba.stackexchange.com/questions/69471/postgres-update-limit-1
228
- */
229
- const selection = this.#repository.session.$with('selection').as((qb) => qb
230
- .select({ id: taskTable.id })
231
- .from(taskTable)
232
- .where(and(eq(taskTable.queue, this.#queueName), lte(taskTable.scheduleTimestamp, TRANSACTION_TIMESTAMP), or(sqlIsNull(taskTable.expirationTimestamp), lt(TRANSACTION_TIMESTAMP, taskTable.expirationTimestamp)), or(eq(taskTable.status, TaskState.Pending), and(eq(taskTable.status, TaskState.Running), lt(taskTable.lockExpirationTimestamp, TRANSACTION_TIMESTAMP), // Zombie detection (only non-exhausted ones)
233
- lt(taskTable.tries, this.maxTries))), sql `pg_sleep(0) IS NOT NULL` // Materialization hack until drizzle implements https://github.com/drizzle-team/drizzle-orm/issues/2318
234
- ))
235
- .orderBy(asc(taskTable.priority), asc(taskTable.scheduleTimestamp), asc(taskTable.tries))
236
- .limit(effectiveCount)
237
- .for('update', { skipLocked: true }));
238
- const rows = await this.#repository.session
239
- .with(selection)
240
- .update(taskTable)
241
- .set({
242
- status: TaskState.Running,
243
- lease: RANDOM_UUID_V4,
244
- lockExpirationTimestamp: sql `${TRANSACTION_TIMESTAMP} + ${interval(this.processTimeout, 'milliseconds')}`,
245
- startTimestamp: TRANSACTION_TIMESTAMP,
246
- // If it was PENDING, it's the first try (tries=0) -> tries=1.
247
- // If it was RUNNING (Zombie), previous try failed -> increment tries.
248
- tries: sql `${taskTable.tries} + 1`,
249
- })
250
- .where(inArray(taskTable.id, this.#repository.session.select().from(selection)))
251
- .returning();
252
- return await this.#repository.mapManyToEntity(rows);
253
- });
254
- }
255
- async reschedule(id, timestamp, transaction) {
256
- await this.rescheduleMany([id], timestamp, transaction);
257
- }
258
- async rescheduleMany(ids, timestamp, transaction) {
259
- await this.#repository.withOptionalTransaction(transaction).updateMany(ids, {
260
- status: TaskState.Pending,
261
- lease: null,
262
- scheduleTimestamp: timestamp,
263
- lockExpirationTimestamp: null,
264
- tries: sql `CASE
265
- WHEN ${taskTable.status} = ${TaskState.Running} THEN GREATEST(0, ${taskTable.tries} - 1)
266
- ELSE ${taskTable.tries}
267
- END`,
268
- });
269
- }
270
- async rescheduleByTag(tag, timestamp, transaction) {
271
- await this.rescheduleManyByTag([tag], timestamp, transaction);
272
- }
273
- async rescheduleManyByTag(tags, timestamp, transaction) {
274
- await this.#repository.withOptionalTransaction(transaction).updateManyByQuery({
275
- queue: this.#queueName,
276
- tag: { $in: tags },
277
- }, {
278
- status: TaskState.Pending,
279
- lease: null,
280
- scheduleTimestamp: timestamp,
281
- lockExpirationTimestamp: null,
282
- tries: sql `CASE
283
- WHEN ${taskTable.status} = ${TaskState.Running} THEN GREATEST(0, ${taskTable.tries} - 1)
284
- ELSE ${taskTable.tries}
285
- END`,
286
- });
287
- }
288
- async touch(task, options) {
289
- if (isNull(task.lease)) {
290
- return undefined;
291
- }
292
- return await this.#repository.useTransaction(options?.transaction, async (tx) => {
293
- const update = {
294
- lockExpirationTimestamp: sql `${TRANSACTION_TIMESTAMP} + ${interval(this.processTimeout, 'milliseconds')}`,
295
- };
296
- if (isDefined(options?.progress)) {
297
- update.progress = options.progress;
298
- }
299
- if (isDefined(options?.state)) {
300
- update.state = options.state;
301
- }
302
- // Attempt to update lease.
303
- // FAILS (returns undefined) if lease has changed (timeout/cancel/stolen)
304
- // OR if hard execution timeout has passed.
305
- const result = await this.#repository.withTransaction(tx).tryUpdateByQuery({
306
- queue: this.#queueName,
307
- id: task.id,
308
- status: TaskState.Running,
309
- lease: task.lease,
310
- startTimestamp: { $gt: sql `${TRANSACTION_TIMESTAMP} - ${interval(this.executionTimeout, 'milliseconds')}` },
311
- }, update);
312
- return result;
313
- });
314
- }
315
- async touchMany(tasks, progresses, states, transaction) {
316
- if (tasks.length == 0) {
317
- return [];
318
- }
319
- const rows = tasks.map((t, i) => {
320
- const progress = progresses?.[i] ?? null;
321
- const state = states?.[i] ?? null;
322
- return sql `(${t.id}::uuid, ${t.lease}::uuid, ${progress}::numeric, ${state}::jsonb)`;
323
- });
324
- const updates = this.#repository.session.$with('updates').as((qb) => qb
325
- .select({
326
- updateId: sql `(id)::uuid`.as('update_id'),
327
- updateLease: sql `(lease)::uuid`.as('update_lease'),
328
- updateProgress: sql `(progress)::numeric`.as('update_progress'),
329
- updateState: sql `(state)::jsonb`.as('update_state'),
330
- })
331
- .from(sql `(VALUES ${sql.join(rows, sql `, `)}) AS t(id, lease, progress, state)`));
332
- const updated = this.#repository.session.$with('updated').as(() => this.#repository.session
333
- .update(taskTable)
334
- .set({
335
- lockExpirationTimestamp: sql `${TRANSACTION_TIMESTAMP} + ${interval(this.processTimeout, 'milliseconds')}`,
336
- progress: coalesce(updates.updateProgress, taskTable.progress),
337
- state: coalesce(updates.updateState, taskTable['state']),
338
- })
339
- .from(updates)
340
- .where(and(eq(taskTable.id, updates.updateId), eq(taskTable.queue, this.#queueName), eq(taskTable.lease, updates.updateLease), eq(taskTable.status, TaskState.Running), gt(taskTable.startTimestamp, sql `${TRANSACTION_TIMESTAMP} - ${interval(this.executionTimeout, 'milliseconds')}`)))
341
- .returning({ id: taskTable.id }));
342
- const result = await this.#repository.withOptionalTransaction(transaction).session
343
- .with(updates, updated)
344
- .select({ id: updated.id })
345
- .from(updated)
346
- .execute();
347
- return result.map((r) => r.id);
348
- }
349
- async acknowledge(task, result, transaction, { skipFanIn = false } = {}) {
350
- await this.#repository.useTransaction(transaction, async (tx) => {
351
- const repository = this.#repository.withTransaction(tx);
352
- // 1. Fan-Out Check: Does this task have active children?
353
- // If yes, this is a Parent task that has spawned children.
354
- // It should enter 'Waiting' instead of 'Completed'.
355
- const activeChildrenCount = await repository.countByQuery({
356
- parentId: task.id,
357
- status: { $nin: [TaskState.Completed, TaskState.Cancelled, TaskState.Dead] },
358
- });
359
- let updatedTask;
360
- if (activeChildrenCount > 0) {
361
- updatedTask = await repository.tryUpdateByQuery({
362
- id: task.id,
363
- lease: task.lease,
364
- }, {
365
- status: TaskState.Waiting,
366
- lease: null,
367
- lockExpirationTimestamp: null,
368
- state: result,
369
- });
370
- }
371
- else {
372
- updatedTask = await repository.tryUpdateByQuery({
373
- id: task.id,
374
- lease: task.lease,
375
- }, {
376
- status: TaskState.Completed,
377
- lease: null,
378
- result: result,
379
- progress: 1,
380
- completeTimestamp: TRANSACTION_TIMESTAMP,
381
- lockExpirationTimestamp: null,
382
- });
383
- }
384
- if (isUndefined(updatedTask)) {
385
- return;
386
- }
387
- // 2. Reset Circuit Breaker (Success detected)
388
- await this.#circuitBreaker.recordSuccess();
389
- // 3. Fan-In: Wake up parent if all siblings are done
390
- if (!skipFanIn && isNotNull(task.parentId)) {
391
- await this.#triggerParentFanIn(task.parentId, tx);
392
- }
393
- });
394
- }
395
- async acknowledgeMany(tasks, results, transaction) {
396
- if (tasks.length == 0) {
397
- return;
398
- }
399
- await this.#repository.useTransaction(transaction, async (tx) => {
400
- // 1. Bulk Check for Active Children
401
- const taskIds = tasks.map((task) => task.id);
402
- const parentsWithChildrenRows = await this.#repository.withTransaction(tx).session
403
- .selectDistinct({ id: taskTable.parentId }) // Distinct is cleaner
404
- .from(taskTable)
405
- .where(and(inArray(taskTable.parentId, taskIds), inArray(taskTable.status, [TaskState.Pending, TaskState.Running, TaskState.Waiting])))
406
- .execute();
407
- const allParentIds = parentsWithChildrenRows.map((row) => row.id);
408
- const distinctParentIds = new Set(allParentIds);
409
- // 2. Separate tasks
410
- const tasksToWait = [];
411
- const tasksToComplete = [];
412
- const resultsToComplete = [];
413
- const resultsToWait = [];
414
- for (let i = 0; i < tasks.length; i++) {
415
- const task = tasks[i];
416
- if (distinctParentIds.has(task.id)) {
417
- tasksToWait.push(task);
418
- if (isDefined(results)) {
419
- resultsToWait.push(results[i]);
420
- }
421
- }
422
- else {
423
- tasksToComplete.push(task);
424
- if (isDefined(results)) {
425
- resultsToComplete.push(results[i]);
426
- }
427
- }
428
- }
429
- // 3. Update 'Waiting' tasks
430
- if (tasksToWait.length > 0) {
431
- const rows = tasksToWait.map((task, i) => {
432
- const result = isDefined(results) ? resultsToWait[i] : null;
433
- return sql `(${task.id}::uuid, ${task.lease}::uuid, ${result}::jsonb)`;
434
- });
435
- const updates = this.#repository.session.$with('updates').as((qb) => qb
436
- .select({
437
- updateId: sql `(id)::uuid`.as('update_id'),
438
- updateLease: sql `(lease)::uuid`.as('update_lease'),
439
- updateResult: sql `(result)::jsonb`.as('update_result'),
440
- })
441
- .from(sql `(VALUES ${sql.join(rows, sql `, `)}) AS t(id, lease, result)`));
442
- const updated = this.#repository.session.$with('updated').as(() => this.#repository.session
443
- .update(taskTable)
444
- .set({
445
- status: TaskState.Waiting,
446
- lease: null,
447
- lockExpirationTimestamp: null,
448
- result: updates.updateResult,
449
- })
450
- .from(updates)
451
- .where(and(eq(taskTable.id, updates.updateId), eq(taskTable.lease, updates.updateLease), eq(taskTable.queue, this.#queueName)))
452
- .returning({ id: taskTable.id }));
453
- await this.#repository.withTransaction(tx).session
454
- .with(updates, updated)
455
- .select({ id: updated.id })
456
- .from(updated)
457
- .execute();
458
- }
459
- // 4. Update 'Completed' tasks
460
- if (tasksToComplete.length > 0) {
461
- const rows = tasksToComplete.map((t, i) => {
462
- const result = isDefined(results) ? resultsToComplete[i] : null;
463
- return sql `(${t.id}::uuid, ${t.lease}::uuid, ${result}::jsonb)`;
464
- });
465
- const updates = this.#repository.session.$with('updates').as((qb) => qb
466
- .select({
467
- updateId: sql `(id)::uuid`.as('update_id'),
468
- updateLease: sql `(lease)::uuid`.as('update_lease'),
469
- updateResult: sql `(result)::jsonb`.as('update_result'),
470
- })
471
- .from(sql `(VALUES ${sql.join(rows, sql `, `)}) AS t(id, lease, result)`));
472
- const updated = this.#repository.session.$with('updated').as(() => this.#repository.session
473
- .update(taskTable)
474
- .set({
475
- status: TaskState.Completed,
476
- lease: null,
477
- result: updates.updateResult,
478
- progress: 1,
479
- completeTimestamp: TRANSACTION_TIMESTAMP,
480
- lockExpirationTimestamp: null,
481
- })
482
- .from(updates)
483
- .where(and(eq(taskTable.id, updates.updateId), eq(taskTable.lease, updates.updateLease), eq(taskTable.queue, this.#queueName)))
484
- .returning({ id: taskTable.id }));
485
- await this.#repository.withTransaction(tx).session
486
- .with(updates, updated)
487
- .select({ id: updated.id })
488
- .from(updated)
489
- .execute();
490
- // Reset circuit breaker
491
- await this.#circuitBreaker.recordSuccess();
492
- }
493
- // 5. Fan-In
494
- const parentIds = Enumerable.from(tasks)
495
- .filter((task) => isNotNull(task.parentId))
496
- .map((task) => task.parentId)
497
- .distinct();
498
- for (const parentId of parentIds) {
499
- await this.#triggerParentFanIn(parentId, tx);
500
- }
501
- });
502
- }
503
- async fail(task, error, fatal = false, transaction, { skipFanIn = false } = {}) {
504
- const isRetryable = !fatal && (task.tries < this.maxTries);
505
- const nextStatus = isRetryable ? TaskState.Pending : TaskState.Dead;
506
- const delay = isRetryable
507
- ? Math.min(this.retryDelayMaximum, this.retryDelayMinimum * (this.retryDelayGrowth ** task.tries))
508
- : 0;
509
- const nextSchedule = currentTimestamp() + delay;
510
- await this.#repository.useTransaction(transaction, async (tx) => {
511
- const updatedTask = await this.#repository.withTransaction(tx).tryUpdateByQuery({
512
- queue: this.#queueName,
513
- id: task.id,
514
- lease: task.lease,
515
- tries: task.tries,
516
- }, {
517
- status: nextStatus,
518
- lease: null,
519
- error: serializeError(error),
520
- lockExpirationTimestamp: null,
521
- scheduleTimestamp: nextSchedule,
522
- startTimestamp: null,
523
- completeTimestamp: (nextStatus == TaskState.Dead) ? TRANSACTION_TIMESTAMP : null,
524
- });
525
- if (isUndefined(updatedTask)) {
526
- return;
527
- }
528
- await this.#circuitBreaker.recordFailure();
529
- if (!skipFanIn && (nextStatus == TaskState.Dead) && isNotNull(task.parentId)) {
530
- await this.#triggerParentFanIn(task.parentId, tx);
531
- }
532
- });
533
- }
534
- async #triggerParentFanIn(parentId, transaction) {
535
- await this.#repository.useTransaction(transaction, async (tx) => {
536
- // 1. Lock Parent
537
- const [parent] = await this.#repository.withTransaction(tx).session
538
- .select({ id: taskTable.id, status: taskTable.status })
539
- .from(taskTable)
540
- .where(eq(taskTable.id, parentId))
541
- .for('update')
542
- .execute();
543
- if (parent?.status != TaskState.Waiting) {
544
- return;
545
- }
546
- // 2. Check Children
547
- const hasActiveChildren = await this.#repository.withTransaction(tx).hasByQuery({
548
- parentId,
549
- status: { $in: [TaskState.Pending, TaskState.Running, TaskState.Waiting] },
550
- });
551
- if (!hasActiveChildren) {
552
- // 3. Update Parent
553
- await this.#repository.withTransaction(tx).updateByQuery({ id: parentId }, {
554
- status: TaskState.Pending,
555
- scheduleTimestamp: TRANSACTION_TIMESTAMP,
556
- });
557
- }
558
- });
559
- }
560
- async failMany(tasks, errors, transaction) {
561
- if (tasks.length == 0) {
562
- return;
563
- }
564
- await this.#repository.useTransaction(transaction, async (tx) => {
565
- await Promise.all(tasks.map(async (task, index) => await this.fail(task, errors[index], false, tx, { skipFanIn: true })));
566
- const parentIds = Enumerable.from(tasks)
567
- .filter((task) => isNotNull(task.parentId))
568
- .map((task) => task.parentId)
569
- .distinct();
570
- for (const parentId of parentIds) {
571
- await this.#triggerParentFanIn(parentId, tx);
572
- }
573
- });
574
- }
575
- async prune() {
576
- // 1. Clean up old finished tasks
577
- await this.#repository.deleteManyByQuery({
578
- queue: this.#queueName,
579
- status: { $in: [TaskState.Completed, TaskState.Cancelled, TaskState.Dead] },
580
- completeTimestamp: { $lt: sql `${TRANSACTION_TIMESTAMP} - ${interval(this.retentionPeriod, 'milliseconds')}` },
581
- });
582
- await this.#repository.transaction(async (tx) => {
583
- // 2. Fail tasks that sat in Pending state past their expiration
584
- const expiredTasks = await this.#repository.updateManyByQuery({
585
- queue: this.#queueName,
586
- status: TaskState.Pending,
587
- expirationTimestamp: { $lt: TRANSACTION_TIMESTAMP },
588
- }, {
589
- status: TaskState.Dead,
590
- lease: null,
591
- error: { message: 'Queue Timeout: Task expired before processing' },
592
- completeTimestamp: TRANSACTION_TIMESTAMP,
593
- });
594
- // 3. Fail "Rotting Zombies" (Tasks that crashed workers and exceeded max retries)
595
- // These were excluded from dequeueMany to prevent infinite loops, so we capture them here.
596
- const zombieTasks = await this.#repository.updateManyByQuery({
597
- queue: this.#queueName,
598
- status: TaskState.Running,
599
- lockExpirationTimestamp: { $lt: TRANSACTION_TIMESTAMP },
600
- tries: { $gte: this.maxTries },
601
- }, {
602
- status: TaskState.Dead,
603
- lease: null,
604
- error: { message: 'Zombie Task: Exceeded max retries after repeated crashes' },
605
- completeTimestamp: TRANSACTION_TIMESTAMP,
606
- lockExpirationTimestamp: null,
607
- });
608
- // 4. Fail tasks that exceeded hard execution timeout
609
- const hardTimedoutTasks = await this.#repository.updateManyByQuery({
610
- queue: this.#queueName,
611
- status: TaskState.Running,
612
- startTimestamp: { $lt: sql `${TRANSACTION_TIMESTAMP} - ${interval(this.executionTimeout, 'milliseconds')}` },
613
- }, {
614
- status: TaskState.Dead,
615
- lease: null,
616
- error: { message: `Hard Execution Timeout: Task ran longer than ${this.executionTimeout}ms` },
617
- completeTimestamp: TRANSACTION_TIMESTAMP,
618
- lockExpirationTimestamp: null,
619
- });
620
- const distinctParentTaskIds = Enumerable
621
- .from(expiredTasks)
622
- .concat(zombieTasks, hardTimedoutTasks)
623
- .filter((task) => isNotNull(task.parentId))
624
- .map((task) => task.parentId)
625
- .distinct();
626
- // 3. Trigger Fan-In for all affected parents
627
- for (const parentId of distinctParentTaskIds) {
628
- await this.#triggerParentFanIn(parentId, tx);
629
- }
630
- });
631
- }
632
- async restart(id, transaction) {
633
- await this.#repository.withOptionalTransaction(transaction).updateByQuery({
634
- id,
635
- $or: [
636
- { status: { $ne: TaskState.Running } },
637
- { lockExpirationTimestamp: { $lt: TRANSACTION_TIMESTAMP } },
638
- ],
639
- }, {
640
- status: TaskState.Pending,
641
- lease: null,
642
- error: null,
643
- scheduleTimestamp: TRANSACTION_TIMESTAMP,
644
- tries: 0,
645
- progress: 0,
646
- result: null,
647
- completeTimestamp: null,
648
- });
649
- }
650
- async *getConsumer(cancellationSignal) {
651
- const continue$ = merge(this.#messageBus.allMessages$, cancellationSignal);
652
- while (cancellationSignal.isUnset) {
653
- const task = await this.dequeue();
654
- if (isDefined(task)) {
655
- yield task;
656
- continue;
657
- }
658
- await cancelableTimeout(5 * millisecondsPerSecond, continue$);
659
- }
660
- }
661
- async *getBatchConsumer(size, cancellationSignal) {
662
- const continue$ = merge(this.#messageBus.allMessages$, cancellationSignal);
663
- while (cancellationSignal.isUnset) {
664
- const tasks = await this.dequeueMany(size);
665
- if (tasks.length > 0) {
666
- yield tasks;
667
- continue;
668
- }
669
- await cancelableTimeout(5 * millisecondsPerSecond, continue$);
670
- }
671
- }
672
- };
673
- PostgresQueue = __decorate([
674
- Singleton({
675
- argumentIdentityProvider: JSON.stringify,
676
- providers: [
677
- provide(DatabaseConfig, { useFactory: (_, context) => context.resolve(PostgresQueueModuleConfig).database ?? context.resolve(DatabaseConfig, undefined, { skipSelf: 2 }) }),
678
- ],
679
- })
680
- ], PostgresQueue);
681
- export { PostgresQueue };
@@ -1,7 +0,0 @@
1
- import { QueueProvider, type QueueConfig } from '../../queue/index.js';
2
- import type { ObjectLiteral } from '../../types/index.js';
3
- import { PostgresQueue } from './queue.js';
4
- export declare class PostgresQueueProvider extends QueueProvider {
5
- #private;
6
- get<Data extends ObjectLiteral, State extends ObjectLiteral, Result extends ObjectLiteral>(name: string, config?: QueueConfig): PostgresQueue<Data, State, Result>;
7
- }