@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
@@ -0,0 +1,1054 @@
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
+ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
8
+ if (value !== null && value !== void 0) {
9
+ if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
10
+ var dispose, inner;
11
+ if (async) {
12
+ if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
13
+ dispose = value[Symbol.asyncDispose];
14
+ }
15
+ if (dispose === void 0) {
16
+ if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
17
+ dispose = value[Symbol.dispose];
18
+ if (async) inner = dispose;
19
+ }
20
+ if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
21
+ if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
22
+ env.stack.push({ value: value, dispose: dispose, async: async });
23
+ }
24
+ else if (async) {
25
+ env.stack.push({ async: true });
26
+ }
27
+ return value;
28
+ };
29
+ var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
30
+ return function (env) {
31
+ function fail(e) {
32
+ env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
33
+ env.hasError = true;
34
+ }
35
+ var r, s = 0;
36
+ function next() {
37
+ while (r = env.stack.pop()) {
38
+ try {
39
+ if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
40
+ if (r.dispose) {
41
+ var result = r.dispose.call(r.value);
42
+ if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
43
+ }
44
+ else s |= 1;
45
+ }
46
+ catch (e) {
47
+ fail(e);
48
+ }
49
+ }
50
+ if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
51
+ if (env.hasError) throw env.error;
52
+ }
53
+ return next();
54
+ };
55
+ })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
56
+ var e = new Error(message);
57
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
58
+ });
59
+ import { and, asc, count, eq, gt, gte, inArray, lt, lte, notInArray, or, sql, isNull as sqlIsNull } from 'drizzle-orm';
60
+ import { merge } from 'rxjs';
61
+ import { CancellationSignal } from '../../cancellation/index.js';
62
+ import { CircuitBreaker, CircuitBreakerState } from '../../circuit-breaker/index.js';
63
+ import { afterResolve, inject, provide, Singleton } from '../../injector/index.js';
64
+ import { Logger } from '../../logger/index.js';
65
+ import { MessageBus } from '../../message-bus/index.js';
66
+ import { arrayOverlaps, coalesce, interval, RANDOM_UUID_V4, TRANSACTION_TIMESTAMP, unnest } from '../../orm/index.js';
67
+ import { DatabaseConfig, injectRepository } from '../../orm/server/index.js';
68
+ import { RateLimiter } from '../../rate-limit/index.js';
69
+ import { createArray, distinct, toArray } from '../../utils/array/array.js';
70
+ import { digest } from '../../utils/cryptography.js';
71
+ import { currentTimestamp } from '../../utils/date-time.js';
72
+ import { serializeError } from '../../utils/format-error.js';
73
+ import { cancelableTimeout } from '../../utils/timing.js';
74
+ import { isDefined, isNotNull, isNull, isString, isUndefined } from '../../utils/type-guards.js';
75
+ import { millisecondsPerSecond } from '../../utils/units.js';
76
+ import { defaultQueueConfig, DependencyJoinMode, TaskQueue, TaskResult, TaskState } from '../task-queue.js';
77
+ import { PostgresTaskQueueModuleConfig } from './module.js';
78
+ import { taskArchive as taskArchiveTable, taskState, task as taskTable } from './schemas.js';
79
+ import { PostgresTask, PostgresTaskArchive } from './task.model.js';
80
+ let PostgresQueue = class PostgresQueue extends TaskQueue {
81
+ #repository = injectRepository(PostgresTask);
82
+ #archiveRepository = injectRepository(PostgresTaskArchive);
83
+ #config = this.config;
84
+ #namespace = isString(this.#config) ? this.#config : this.#config.namespace;
85
+ #messageBus = inject((MessageBus), `PostgresTaskQueue:${this.#namespace}`);
86
+ #logger = inject(Logger, `PostgresTaskQueue:${this.#namespace}`);
87
+ #cancellationSignal = inject(CancellationSignal);
88
+ #rateLimiter = inject(RateLimiter, {
89
+ resource: this.#namespace,
90
+ burstCapacity: this.#config.rateLimit ?? defaultQueueConfig.rateLimit,
91
+ refillIntervalMs: this.#config.rateInterval ?? defaultQueueConfig.rateInterval,
92
+ });
93
+ #circuitBreaker = inject(CircuitBreaker, {
94
+ key: this.#namespace,
95
+ threshold: this.#config.circuitBreakerThreshold ?? defaultQueueConfig.circuitBreakerThreshold,
96
+ resetTimeout: this.#config.circuitBreakerResetTimeout ?? defaultQueueConfig.circuitBreakerResetTimeout,
97
+ });
98
+ visibilityTimeout = this.#config.visibilityTimeout ?? defaultQueueConfig.visibilityTimeout;
99
+ maxExecutionTime = this.#config.maxExecutionTime ?? defaultQueueConfig.maxExecutionTime;
100
+ maxTries = this.#config.maxTries ?? defaultQueueConfig.maxTries;
101
+ retryDelayMinimum = this.#config.retryDelayMinimum ?? defaultQueueConfig.retryDelayMinimum;
102
+ retryDelayMaximum = this.#config.retryDelayMaximum ?? defaultQueueConfig.retryDelayMaximum;
103
+ retryDelayGrowth = this.#config.retryDelayGrowth ?? defaultQueueConfig.retryDelayGrowth;
104
+ retention = this.#config.retention ?? defaultQueueConfig.retention;
105
+ archiveRetention = this.#config.archiveRetention ?? defaultQueueConfig.archiveRetention;
106
+ priorityAgingInterval = this.#config.priorityAgingInterval ?? defaultQueueConfig.priorityAgingInterval;
107
+ priorityAgingStep = this.#config.priorityAgingStep ?? defaultQueueConfig.priorityAgingStep;
108
+ defaultTimeToLive = this.#config.defaultTimeToLive ?? defaultQueueConfig.defaultTimeToLive;
109
+ rateLimit = this.#config.rateLimit ?? defaultQueueConfig.rateLimit;
110
+ rateInterval = this.#config.rateInterval ?? defaultQueueConfig.rateInterval;
111
+ idempotencyWindow = this.#config.idempotencyWindow ?? defaultQueueConfig.idempotencyWindow;
112
+ globalConcurrency = this.#config.globalConcurrency ?? defaultQueueConfig.globalConcurrency;
113
+ #takeNewUpdate = {
114
+ id: RANDOM_UUID_V4,
115
+ namespace: this.#namespace,
116
+ type: sql `excluded.type`,
117
+ status: TaskState.Pending,
118
+ token: null,
119
+ priority: sql `excluded.priority`,
120
+ idempotencyKey: sql `excluded.idempotency_key`,
121
+ traceId: sql `excluded.trace_id`,
122
+ tags: sql `excluded.tags`,
123
+ completeAfterTags: sql `excluded.complete_after_tags`,
124
+ scheduleAfterTags: sql `excluded.schedule_after_tags`,
125
+ failFast: sql `excluded.fail_fast`,
126
+ dependencyJoinMode: sql `excluded.dependency_join_mode`,
127
+ dependencyTriggerStates: sql `excluded.dependency_trigger_states`,
128
+ tries: 0,
129
+ creationTimestamp: TRANSACTION_TIMESTAMP,
130
+ priorityAgeTimestamp: TRANSACTION_TIMESTAMP,
131
+ scheduleTimestamp: sql `excluded.schedule_timestamp`,
132
+ startTimestamp: null,
133
+ timeToLive: sql `excluded.time_to_live`,
134
+ visibilityDeadline: null,
135
+ completeTimestamp: null,
136
+ progress: 0,
137
+ data: sql `excluded.data`,
138
+ state: null,
139
+ error: null,
140
+ result: null,
141
+ };
142
+ [afterResolve]() {
143
+ if (!this.isInTransaction) {
144
+ this.startSystemWorker();
145
+ void this.maintenanceLoop();
146
+ }
147
+ }
148
+ async enqueue(type, data, options) {
149
+ const tasks = await this.enqueueMany([{ type, data, ...options }], { replace: options?.replace ?? false, returnTasks: true, transaction: options?.transaction });
150
+ return tasks[0];
151
+ }
152
+ async enqueueMany(items, options) {
153
+ const env_1 = { stack: [], error: void 0, hasError: false };
154
+ try {
155
+ if (items.length == 0) {
156
+ return (options?.returnTasks == true) ? [] : undefined;
157
+ }
158
+ const newEntities = items.map((item) => {
159
+ const hasDependencies = (item.completeAfterTags?.length ?? 0) > 0 || (item.scheduleAfterTags?.length ?? 0) > 0;
160
+ return {
161
+ namespace: this.#namespace,
162
+ type: item.type,
163
+ status: hasDependencies ? TaskState.Waiting : TaskState.Pending,
164
+ token: null,
165
+ priority: item.priority ?? 1000,
166
+ idempotencyKey: item.idempotencyKey ?? null,
167
+ traceId: null,
168
+ tags: item.tags ?? [],
169
+ completeAfterTags: item.completeAfterTags ?? [],
170
+ scheduleAfterTags: item.scheduleAfterTags ?? [],
171
+ failFast: item.failFast ?? false,
172
+ dependencyJoinMode: item.dependencyJoinMode ?? DependencyJoinMode.And,
173
+ dependencyTriggerStates: item.dependencyTriggerStates ?? [TaskState.Completed],
174
+ parentId: item.parentId ?? null,
175
+ tries: 0,
176
+ progress: 0,
177
+ creationTimestamp: TRANSACTION_TIMESTAMP,
178
+ priorityAgeTimestamp: TRANSACTION_TIMESTAMP,
179
+ scheduleTimestamp: item.scheduleTimestamp ?? TRANSACTION_TIMESTAMP,
180
+ startTimestamp: null,
181
+ timeToLive: item.timeToLive ?? sql `${TRANSACTION_TIMESTAMP} + ${interval(this.defaultTimeToLive, 'milliseconds')}`,
182
+ visibilityDeadline: null,
183
+ completeTimestamp: null,
184
+ data: item.data,
185
+ state: null,
186
+ result: null,
187
+ error: null,
188
+ };
189
+ });
190
+ const itemsWithIdempotency = newEntities.filter((e) => isNotNull(e.idempotencyKey));
191
+ const itemsWithoutIdempotency = newEntities.filter((e) => isNull(e.idempotencyKey));
192
+ const hasDependencies = items.some((item) => ((item.completeAfterTags?.length ?? 0) > 0) || ((item.scheduleAfterTags?.length ?? 0) > 0));
193
+ const mustUseTransaction = ((itemsWithoutIdempotency.length + itemsWithIdempotency.length) > 1) || hasDependencies;
194
+ const newTransaction = __addDisposableResource(env_1, (mustUseTransaction && isUndefined(options?.transaction)) ? await this.#repository.startTransaction() : undefined, true);
195
+ const transaction = newTransaction ?? options?.transaction;
196
+ const tasks = [];
197
+ if (itemsWithoutIdempotency.length > 0) {
198
+ const insertedTasks = await this.#repository.withOptionalTransaction(transaction).insertMany(itemsWithoutIdempotency);
199
+ tasks.push(...insertedTasks);
200
+ }
201
+ if (itemsWithIdempotency.length > 0) {
202
+ const repository = this.#repository.withOptionalTransaction(transaction);
203
+ const windowCutoff = sql `${TRANSACTION_TIMESTAMP} - ${interval(this.idempotencyWindow, 'milliseconds')}`;
204
+ const setWhere = (options?.replace == true) ? undefined : sql `${taskTable.creationTimestamp} < ${windowCutoff}`;
205
+ const upsertedTasks = await repository.upsertMany(['namespace', 'idempotencyKey'], itemsWithIdempotency, this.#takeNewUpdate, { set: setWhere });
206
+ tasks.push(...upsertedTasks);
207
+ if (options?.returnTasks == true && upsertedTasks.length < itemsWithIdempotency.length) {
208
+ const upsertedKeys = new Set(upsertedTasks.map((t) => t.idempotencyKey));
209
+ const missingKeys = itemsWithIdempotency.map((i) => i.idempotencyKey).filter((k) => !upsertedKeys.has(k));
210
+ if (missingKeys.length > 0) {
211
+ const existingTasks = await repository.loadManyByQuery({
212
+ namespace: this.#namespace,
213
+ idempotencyKey: { $in: missingKeys },
214
+ });
215
+ tasks.push(...existingTasks);
216
+ }
217
+ }
218
+ }
219
+ await newTransaction?.commit();
220
+ this.#messageBus.publishAndForget();
221
+ const anyWaiting = tasks.some((t) => t.status == TaskState.Waiting);
222
+ if (anyWaiting) {
223
+ const tagsToTrigger = distinct(tasks.filter((t) => t.status == TaskState.Waiting).flatMap((t) => [...t.completeAfterTags, ...t.scheduleAfterTags]));
224
+ await this.triggerTagFanIn(tagsToTrigger, transaction);
225
+ }
226
+ if (options?.returnTasks == true) {
227
+ return tasks;
228
+ }
229
+ return undefined;
230
+ }
231
+ catch (e_1) {
232
+ env_1.error = e_1;
233
+ env_1.hasError = true;
234
+ }
235
+ finally {
236
+ const result_1 = __disposeResources(env_1);
237
+ if (result_1)
238
+ await result_1;
239
+ }
240
+ }
241
+ async has(id) {
242
+ return await this.#repository.hasByQuery({ namespace: this.#namespace, id });
243
+ }
244
+ async countByTags(tags) {
245
+ const tagArray = toArray(tags);
246
+ if (tagArray.length == 0) {
247
+ return 0;
248
+ }
249
+ const [result] = await this.#repository.session
250
+ .select({ count: count() })
251
+ .from(taskTable)
252
+ .where(and(eq(taskTable.namespace, this.#namespace), arrayOverlaps(taskTable.tags, tagArray)))
253
+ .execute();
254
+ return result?.count ?? 0;
255
+ }
256
+ async getTask(id) {
257
+ const active = await this.#repository.tryLoadByQuery({ namespace: this.#namespace, id });
258
+ if (isDefined(active)) {
259
+ return active;
260
+ }
261
+ return await this.#archiveRepository.tryLoadByQuery({ namespace: this.#namespace, id });
262
+ }
263
+ async getManyByTags(tags) {
264
+ const tagArray = toArray(tags);
265
+ if (tagArray.length == 0) {
266
+ return [];
267
+ }
268
+ const rows = await this.#repository.session
269
+ .select()
270
+ .from(taskTable)
271
+ .where(and(eq(taskTable.namespace, this.#namespace), arrayOverlaps(taskTable.tags, tagArray)))
272
+ .execute();
273
+ return await this.#repository.mapManyToEntity(rows);
274
+ }
275
+ async getTree(idOrIds, transaction) {
276
+ const ids = toArray(idOrIds);
277
+ return await this.getTreeByQuery({ id: { $in: ids } }, transaction);
278
+ }
279
+ async getTreeByQuery(query, transaction) {
280
+ return await this.#repository.useTransaction(transaction, async (tx) => {
281
+ const repositoryWithTransaction = this.#repository.withTransaction(tx);
282
+ const roots = await repositoryWithTransaction.loadManyByQuery(query);
283
+ if (roots.length == 0) {
284
+ return [];
285
+ }
286
+ const rootIds = roots.map((t) => t.id);
287
+ const rawResult = await repositoryWithTransaction.session.execute(sql `
288
+ WITH RECURSIVE task_tree AS (
289
+ SELECT *, 0 as depth
290
+ FROM ${taskTable}
291
+ WHERE ${inArray(taskTable.id, rootIds)}
292
+
293
+ UNION
294
+
295
+ SELECT child.*, parent.depth + 1
296
+ FROM ${taskTable} child
297
+ JOIN task_tree parent ON child.parent_id = parent.id
298
+ WHERE parent.depth < 10000
299
+ )
300
+ SELECT * FROM task_tree
301
+ `);
302
+ const rows = rawResult.rows ?? rawResult;
303
+ return await repositoryWithTransaction.mapManyToEntity(rows);
304
+ });
305
+ }
306
+ async cancel(id, transaction) {
307
+ await this.cancelMany([id], transaction);
308
+ }
309
+ async cancelMany(ids, transaction) {
310
+ await this.#repository.useTransaction(transaction, async (tx) => {
311
+ const tree = await this.getTree(ids, tx);
312
+ const treeIds = tree.map((task) => task.id);
313
+ if (treeIds.length == 0) {
314
+ return;
315
+ }
316
+ await this.#repository.withTransaction(tx).updateMany(treeIds, {
317
+ status: TaskState.Cancelled,
318
+ token: null,
319
+ completeTimestamp: TRANSACTION_TIMESTAMP,
320
+ });
321
+ const tags = tree.flatMap((t) => t.tags);
322
+ await this.triggerTagFanIn(tags, tx);
323
+ });
324
+ }
325
+ async cancelManyByTags(tags) {
326
+ await this.#repository.transaction(async (tx) => {
327
+ const tasks = await this.getManyByTags(tags);
328
+ const ids = tasks.map((t) => t.id);
329
+ await this.cancelMany(ids, tx);
330
+ });
331
+ }
332
+ async clear() {
333
+ await this.#repository.transaction(async (tx) => {
334
+ const repository = this.#repository.withTransaction(tx);
335
+ const parentIds = repository.session
336
+ .select({ id: taskTable.id })
337
+ .from(taskTable)
338
+ .where(eq(taskTable.namespace, this.#namespace));
339
+ await repository.session
340
+ .update(taskTable)
341
+ .set({ parentId: null })
342
+ .where(inArray(taskTable.parentId, parentIds))
343
+ .execute();
344
+ await repository.hardDeleteManyByQuery({ namespace: this.#namespace });
345
+ });
346
+ }
347
+ async dequeue(options) {
348
+ const tasks = await this.dequeueMany(1, options);
349
+ if (tasks.length == 0) {
350
+ return undefined;
351
+ }
352
+ return tasks[0];
353
+ }
354
+ async dequeueMany(count, options) {
355
+ // 1. Flow Control
356
+ const forceDequeue = options?.forceDequeue ?? false;
357
+ let circuitBreakerResult;
358
+ if (!forceDequeue) {
359
+ circuitBreakerResult = await this.#circuitBreaker.check();
360
+ if (!circuitBreakerResult.allowed) {
361
+ return [];
362
+ }
363
+ const rateLimitAllowed = await this.#rateLimiter.tryAcquire(this.#namespace, count);
364
+ if (!rateLimitAllowed) {
365
+ return [];
366
+ }
367
+ }
368
+ const tasks = await this.#repository.transaction(async (tx) => {
369
+ const repository = this.#repository.withTransaction(tx);
370
+ let effectiveCount = count;
371
+ if (!forceDequeue && circuitBreakerResult?.state == CircuitBreakerState.HalfOpen) {
372
+ if (circuitBreakerResult.isProbe != true) {
373
+ const runningCount = await repository.countByQuery({ namespace: this.#namespace, status: TaskState.Running });
374
+ if (runningCount > 0) {
375
+ return [];
376
+ }
377
+ }
378
+ effectiveCount = 1;
379
+ }
380
+ // 2. Check Global Concurrency
381
+ if (!forceDequeue && isNotNull(this.globalConcurrency)) {
382
+ // WARN: This is a check-then-act race condition.
383
+ // A distributed lock or an atomic update strategy is needed for strict enforcement.
384
+ const runningCount = await repository.countByQuery({ namespace: this.#namespace, status: TaskState.Running });
385
+ if (runningCount >= this.globalConcurrency) {
386
+ return [];
387
+ }
388
+ effectiveCount = Math.min(effectiveCount, this.globalConcurrency - runningCount);
389
+ }
390
+ /*
391
+ * Materialization required for LIMIT clause
392
+ * https://stackoverflow.com/questions/73966670/select-for-update-subquery-not-respecting-limit-clause-under-load
393
+ * https://dba.stackexchange.com/questions/69471/postgres-update-limit-1
394
+ */
395
+ const selection = repository.session.$with('selection').as((qb) => qb
396
+ .select({ id: taskTable.id })
397
+ .from(taskTable)
398
+ .where(and(eq(taskTable.namespace, this.#namespace), lte(taskTable.scheduleTimestamp, TRANSACTION_TIMESTAMP), eq(taskTable.status, TaskState.Pending), or(sqlIsNull(taskTable.timeToLive), lt(TRANSACTION_TIMESTAMP, taskTable.timeToLive)), isDefined(options?.types) ? inArray(taskTable.type, options.types) : undefined, sql `pg_sleep(0) IS NOT NULL` // Materialization hack until drizzle implements https://github.com/drizzle-team/drizzle-orm/issues/2318
399
+ ))
400
+ .orderBy(asc(taskTable.priority), asc(taskTable.scheduleTimestamp))
401
+ .limit(effectiveCount)
402
+ .for('update', { skipLocked: true }));
403
+ const rows = await repository.session
404
+ .with(selection)
405
+ .update(taskTable)
406
+ .set({
407
+ status: TaskState.Running,
408
+ token: RANDOM_UUID_V4,
409
+ visibilityDeadline: sql `${TRANSACTION_TIMESTAMP} + ${interval(this.visibilityTimeout, 'milliseconds')}`,
410
+ startTimestamp: TRANSACTION_TIMESTAMP,
411
+ // If it was PENDING, it's the first try (tries=0) -> tries=1.
412
+ // If it was RUNNING (Zombie), previous try failed -> increment tries.
413
+ tries: sql `${taskTable.tries} + 1`,
414
+ })
415
+ .where(inArray(taskTable.id, repository.session.select().from(selection)))
416
+ .returning();
417
+ return await this.#repository.mapManyToEntity(rows);
418
+ });
419
+ // 3. Compensation
420
+ if (!forceDequeue && (tasks.length < count)) {
421
+ const shortfall = count - tasks.length;
422
+ await this.#rateLimiter.refund(this.#namespace, shortfall);
423
+ }
424
+ return tasks;
425
+ }
426
+ async reschedule(id, timestamp, transaction) {
427
+ await this.rescheduleMany([id], timestamp, transaction);
428
+ }
429
+ async rescheduleMany(ids, timestamp, transaction) {
430
+ await this.#repository.withOptionalTransaction(transaction).updateMany(ids, {
431
+ status: TaskState.Pending,
432
+ token: null,
433
+ scheduleTimestamp: timestamp,
434
+ visibilityDeadline: null,
435
+ tries: sql `CASE
436
+ WHEN ${taskTable.status} = ${TaskState.Running} THEN GREATEST(0, ${taskTable.tries} - 1)
437
+ ELSE ${taskTable.tries}
438
+ END`,
439
+ });
440
+ }
441
+ async rescheduleManyByTags(tags, timestamp, transaction) {
442
+ await this.#repository.withOptionalTransaction(transaction).updateManyByQuery(and(eq(taskTable.namespace, this.#namespace), arrayOverlaps(taskTable.tags, toArray(tags))), {
443
+ status: TaskState.Pending,
444
+ token: null,
445
+ scheduleTimestamp: timestamp,
446
+ visibilityDeadline: null,
447
+ tries: sql `CASE
448
+ WHEN ${taskTable.status} = ${TaskState.Running} THEN GREATEST(0, ${taskTable.tries} - 1)
449
+ ELSE ${taskTable.tries}
450
+ END`,
451
+ });
452
+ }
453
+ async touch(task, options) {
454
+ if (isNull(task.token)) {
455
+ return undefined;
456
+ }
457
+ return await this.#repository.useTransaction(options?.transaction, async (tx) => {
458
+ const repository = this.#repository.withTransaction(tx);
459
+ const update = {
460
+ visibilityDeadline: sql `${TRANSACTION_TIMESTAMP} + ${interval(this.visibilityTimeout, 'milliseconds')}`,
461
+ };
462
+ if (isDefined(options?.progress)) {
463
+ update.progress = options.progress;
464
+ }
465
+ if (isDefined(options?.state)) {
466
+ update.state = options.state;
467
+ }
468
+ const result = await repository.tryUpdateByQuery({
469
+ namespace: this.#namespace,
470
+ id: task.id,
471
+ status: TaskState.Running,
472
+ token: task.token,
473
+ startTimestamp: { $gt: sql `${TRANSACTION_TIMESTAMP} - ${interval(this.maxExecutionTime, 'milliseconds')}` },
474
+ }, update);
475
+ // TODO: reduce required DB roundtrips
476
+ if (isUndefined(result)) {
477
+ const existing = await repository.tryLoadByQuery({ id: task.id, namespace: this.#namespace, status: TaskState.Running, token: task.token });
478
+ if (isDefined(existing) && isNotNull(existing.startTimestamp) && (currentTimestamp() - existing.startTimestamp) > this.maxExecutionTime) {
479
+ await this.fail(task, { message: 'Hard Execution Timeout' }, true, tx);
480
+ }
481
+ }
482
+ return result;
483
+ });
484
+ }
485
+ async touchMany(tasks, progresses, states, transaction) {
486
+ if (tasks.length == 0) {
487
+ return [];
488
+ }
489
+ const repository = this.#repository.withOptionalTransaction(transaction);
490
+ const rows = tasks.map((t, i) => {
491
+ const progress = progresses?.[i] ?? null;
492
+ const state = states?.[i] ?? null;
493
+ return sql `(${t.id}::uuid, ${t.token}::uuid, ${progress}::numeric, ${state}::jsonb)`;
494
+ });
495
+ const updates = repository.session.$with('updates').as((qb) => qb
496
+ .select({
497
+ updateId: sql `(id)::uuid`.as('update_id'),
498
+ updateToken: sql `(token)::uuid`.as('update_token'),
499
+ updateProgress: sql `(progress)::numeric`.as('update_progress'),
500
+ updateState: sql `(state)::jsonb`.as('update_state'),
501
+ })
502
+ .from(sql `(VALUES ${sql.join(rows, sql `, `)}) AS t(id, token, progress, state)`));
503
+ const updated = repository.session.$with('updated').as(() => repository.session
504
+ .update(taskTable)
505
+ .set({
506
+ visibilityDeadline: sql `${TRANSACTION_TIMESTAMP} + ${interval(this.visibilityTimeout, 'milliseconds')}`,
507
+ progress: coalesce(updates.updateProgress, taskTable.progress),
508
+ state: coalesce(updates.updateState, taskTable.state),
509
+ })
510
+ .from(updates)
511
+ .where(and(eq(taskTable.id, updates.updateId), eq(taskTable.namespace, this.#namespace), eq(taskTable.token, updates.updateToken), eq(taskTable.status, TaskState.Running), gt(taskTable.startTimestamp, sql `${TRANSACTION_TIMESTAMP} - ${interval(this.maxExecutionTime, 'milliseconds')}`)))
512
+ .returning({ id: taskTable.id }));
513
+ const result = await repository.session
514
+ .with(updates, updated)
515
+ .select({ id: updated.id })
516
+ .from(updated)
517
+ .execute();
518
+ return result.map((r) => r.id);
519
+ }
520
+ async complete(task, result, transaction) {
521
+ await this.#repository.useTransaction(transaction, async (tx) => {
522
+ const repository = this.#repository.withTransaction(tx);
523
+ const updatedTask = await repository.tryUpdateByQuery({
524
+ id: task.id,
525
+ token: task.token,
526
+ }, {
527
+ status: TaskState.Completed,
528
+ token: null,
529
+ result: result,
530
+ progress: 1,
531
+ completeTimestamp: TRANSACTION_TIMESTAMP,
532
+ visibilityDeadline: null,
533
+ });
534
+ if (isUndefined(updatedTask)) {
535
+ return;
536
+ }
537
+ await this.#circuitBreaker.recordSuccess();
538
+ await this.triggerTagFanIn(task.tags, tx);
539
+ });
540
+ }
541
+ async completeMany(tasks, results, transaction) {
542
+ if (tasks.length == 0) {
543
+ return;
544
+ }
545
+ await this.#repository.useTransaction(transaction, async (tx) => {
546
+ const repository = this.#repository.withTransaction(tx);
547
+ const rows = tasks.map((t, i) => {
548
+ const result = isDefined(results) ? results[i] : null;
549
+ return sql `(${t.id}::uuid, ${t.token}::uuid, ${result}::jsonb)`;
550
+ });
551
+ const updates = repository.session.$with('updates').as((qb) => qb
552
+ .select({
553
+ updateId: sql `(id)::uuid`.as('update_id'),
554
+ updateToken: sql `(token)::uuid`.as('update_token'),
555
+ updateResult: sql `(result)::jsonb`.as('update_result'),
556
+ })
557
+ .from(sql `(VALUES ${sql.join(rows, sql `, `)}) AS t(id, token, result)`));
558
+ const updated = repository.session.$with('updated').as(() => repository.session
559
+ .update(taskTable)
560
+ .set({
561
+ status: TaskState.Completed,
562
+ token: null,
563
+ result: updates.updateResult,
564
+ progress: 1,
565
+ completeTimestamp: TRANSACTION_TIMESTAMP,
566
+ visibilityDeadline: null,
567
+ })
568
+ .from(updates)
569
+ .where(and(eq(taskTable.id, updates.updateId), eq(taskTable.token, updates.updateToken), eq(taskTable.namespace, this.#namespace)))
570
+ .returning({ id: taskTable.id, tags: taskTable.tags }));
571
+ const updatedRows = await repository.session
572
+ .with(updates, updated)
573
+ .select({ id: updated.id, tags: updated.tags })
574
+ .from(updated)
575
+ .execute();
576
+ if (updatedRows.length > 0) {
577
+ await this.#circuitBreaker.recordSuccess();
578
+ const tags = updatedRows.flatMap((t) => t.tags);
579
+ await this.triggerTagFanIn(tags, tx);
580
+ }
581
+ });
582
+ }
583
+ async fail(task, error, fatal = false, transaction) {
584
+ const isRetryable = !fatal && (task.tries < this.maxTries);
585
+ const nextStatus = isRetryable ? TaskState.Pending : TaskState.Dead;
586
+ const delay = isRetryable
587
+ ? Math.min(this.retryDelayMaximum, this.retryDelayMinimum * (this.retryDelayGrowth ** task.tries))
588
+ : 0;
589
+ const nextSchedule = currentTimestamp() + delay;
590
+ await this.#repository.useTransaction(transaction, async (tx) => {
591
+ const updatedTask = await this.#repository.withTransaction(tx).tryUpdateByQuery({
592
+ namespace: this.#namespace,
593
+ id: task.id,
594
+ token: task.token,
595
+ tries: task.tries,
596
+ }, {
597
+ status: nextStatus,
598
+ token: null,
599
+ error: serializeError(error),
600
+ visibilityDeadline: null,
601
+ scheduleTimestamp: nextSchedule,
602
+ startTimestamp: null,
603
+ completeTimestamp: (nextStatus == TaskState.Dead) ? TRANSACTION_TIMESTAMP : null,
604
+ });
605
+ if (isUndefined(updatedTask)) {
606
+ return;
607
+ }
608
+ await this.#circuitBreaker.recordFailure();
609
+ if (nextStatus == TaskState.Dead) {
610
+ await this.triggerTagFanIn(task.tags, tx);
611
+ }
612
+ });
613
+ }
614
+ async triggerTagFanIn(tags, transaction) {
615
+ if (tags.length == 0) {
616
+ return;
617
+ }
618
+ const distinctSortedTags = distinct(tags).toSorted();
619
+ const hash = await digest('SHA-256', distinctSortedTags.join(',')).toHex();
620
+ const idempotencyKey = `Sys:FanIn:${hash}`;
621
+ await this.enqueue('[SystemWorker]:FanIn', { targetTags: distinctSortedTags }, {
622
+ priority: 0,
623
+ idempotencyKey,
624
+ replace: true,
625
+ transaction,
626
+ });
627
+ }
628
+ async failMany(tasks, errors, transaction) {
629
+ if (tasks.length == 0) {
630
+ return;
631
+ }
632
+ await this.#repository.useTransaction(transaction, async (tx) => {
633
+ const repository = this.#repository.withTransaction(tx);
634
+ const rows = tasks.map((task, index) => {
635
+ const error = errors[index];
636
+ const isRetryable = (task.tries < this.maxTries);
637
+ const nextStatus = isRetryable ? TaskState.Pending : TaskState.Dead;
638
+ const delay = isRetryable
639
+ ? Math.min(this.retryDelayMaximum, this.retryDelayMinimum * (this.retryDelayGrowth ** task.tries))
640
+ : 0;
641
+ const nextSchedule = new Date(currentTimestamp() + delay);
642
+ const completeTimestamp = (nextStatus == TaskState.Dead) ? new Date() : null;
643
+ return sql `(${task.id}::uuid, ${task.token}::uuid, ${task.tries}::int, ${nextStatus}::text, ${serializeError(error)}::jsonb, ${nextSchedule}::timestamptz, ${completeTimestamp}::timestamptz)`;
644
+ });
645
+ const updates = repository.session.$with('updates').as((qb) => qb
646
+ .select({
647
+ updateId: sql `(id)::uuid`.as('update_id'),
648
+ updateToken: sql `(token)::uuid`.as('update_token'),
649
+ updateTries: sql `(tries)::int`.as('update_tries'),
650
+ updateStatus: sql `(status)::text`.as('update_status'),
651
+ updateError: sql `(error)::jsonb`.as('update_error'),
652
+ updateSchedule: sql `(schedule_timestamp)::timestamptz`.as('update_schedule'),
653
+ updateComplete: sql `(complete_timestamp)::timestamptz`.as('update_complete'),
654
+ })
655
+ .from(sql `(VALUES ${sql.join(rows, sql `, `)}) AS t(id, token, tries, status, error, schedule_timestamp, complete_timestamp)`));
656
+ const updated = repository.session.$with('updated').as(() => repository.session
657
+ .update(taskTable)
658
+ .set({
659
+ status: sql `${updates.updateStatus}::${taskState}`,
660
+ token: null,
661
+ error: sql `${updates.updateError}`,
662
+ visibilityDeadline: null,
663
+ scheduleTimestamp: sql `${updates.updateSchedule}`,
664
+ startTimestamp: null,
665
+ completeTimestamp: sql `${updates.updateComplete}`,
666
+ })
667
+ .from(updates)
668
+ .where(and(eq(taskTable.namespace, this.#namespace), eq(taskTable.id, updates.updateId), eq(taskTable.token, updates.updateToken), eq(taskTable.tries, updates.updateTries)))
669
+ .returning({ id: taskTable.id, status: taskTable.status, tags: taskTable.tags }));
670
+ const result = await repository.session
671
+ .with(updates, updated)
672
+ .select()
673
+ .from(updated)
674
+ .execute();
675
+ if (result.length > 0) {
676
+ await Promise.all(createArray(result.length, async () => await this.#circuitBreaker.recordFailure()));
677
+ const deadTags = result
678
+ .filter((r) => r.status == TaskState.Dead)
679
+ .flatMap((r) => r.tags);
680
+ await this.triggerTagFanIn(deadTags, tx);
681
+ }
682
+ });
683
+ }
684
+ async resolveDependencies(targetTags) {
685
+ await this.#repository.transaction(async (tx) => {
686
+ const repository = this.#repository.withTransaction(tx);
687
+ // Identify Dependents
688
+ const waiters = await repository.session
689
+ .select()
690
+ .from(taskTable)
691
+ .where(and(eq(taskTable.namespace, this.#namespace), eq(taskTable.status, TaskState.Waiting), or(arrayOverlaps(taskTable.completeAfterTags, targetTags), arrayOverlaps(taskTable.scheduleAfterTags, targetTags))))
692
+ .for('update', { skipLocked: true })
693
+ .execute();
694
+ const entities = await repository.mapManyToEntity(waiters);
695
+ if (entities.length == 0) {
696
+ return;
697
+ }
698
+ const allRequiredTags = distinct(entities.flatMap((t) => [...t.completeAfterTags, ...t.scheduleAfterTags]));
699
+ const statsRows = await repository.session
700
+ .select({
701
+ tag: unnest(taskTable.tags).as('tag'),
702
+ status: taskTable.status,
703
+ count: count().as('count'),
704
+ })
705
+ .from(taskTable)
706
+ .where(and(eq(taskTable.namespace, this.#namespace), arrayOverlaps(taskTable.tags, allRequiredTags)))
707
+ .groupBy(unnest(taskTable.tags), taskTable.status);
708
+ const tagStats = new Map();
709
+ for (const row of statsRows) {
710
+ if (!tagStats.has(row.tag)) {
711
+ tagStats.set(row.tag, new Map());
712
+ }
713
+ tagStats.get(row.tag).set(row.status, row.count);
714
+ }
715
+ const getCount = (tags, states) => {
716
+ let sum = 0;
717
+ for (const tag of tags) {
718
+ const stats = tagStats.get(tag);
719
+ if (isDefined(stats)) {
720
+ for (const state of states) {
721
+ sum += stats.get(state) ?? 0;
722
+ }
723
+ }
724
+ }
725
+ return sum;
726
+ };
727
+ const idsToFail = [];
728
+ const idsToSchedule = [];
729
+ const idsToComplete = [];
730
+ const fanInTags = [];
731
+ for (const waiter of entities) {
732
+ const requiredTags = distinct([...waiter.completeAfterTags, ...waiter.scheduleAfterTags]);
733
+ let isReady = false;
734
+ let shouldFail = false;
735
+ // 1. Check Fail Fast
736
+ if (waiter.failFast) {
737
+ if (getCount(requiredTags, [TaskState.Dead, TaskState.Cancelled]) > 0) {
738
+ shouldFail = true;
739
+ }
740
+ }
741
+ // 2. Check Trigger Conditions
742
+ if (!shouldFail) {
743
+ if (waiter.dependencyJoinMode == DependencyJoinMode.Or) {
744
+ if (getCount(requiredTags, waiter.dependencyTriggerStates) > 0) {
745
+ isReady = true;
746
+ }
747
+ }
748
+ else if (waiter.dependencyJoinMode == DependencyJoinMode.And) {
749
+ // For AND, we need "No active tasks".
750
+ // Active = Pending, Running, Waiting.
751
+ if (getCount(requiredTags, [TaskState.Pending, TaskState.Running, TaskState.Waiting]) == 0) {
752
+ isReady = true;
753
+ }
754
+ }
755
+ }
756
+ // 3. Transition
757
+ if (shouldFail) {
758
+ idsToFail.push(waiter.id);
759
+ }
760
+ else if (isReady) {
761
+ if (waiter.scheduleAfterTags.length > 0) {
762
+ idsToSchedule.push(waiter.id);
763
+ }
764
+ else {
765
+ idsToComplete.push(waiter.id);
766
+ fanInTags.push(...waiter.tags);
767
+ }
768
+ }
769
+ }
770
+ if (idsToFail.length > 0) {
771
+ const rows = await repository.updateMany(idsToFail, {
772
+ status: TaskState.Dead,
773
+ error: { code: 'DependencyFailed' },
774
+ completeTimestamp: TRANSACTION_TIMESTAMP,
775
+ });
776
+ const tags = rows.flatMap((r) => r.tags);
777
+ await this.triggerTagFanIn(tags, tx);
778
+ }
779
+ if (idsToSchedule.length > 0) {
780
+ await repository.updateMany(idsToSchedule, {
781
+ status: TaskState.Pending,
782
+ scheduleTimestamp: TRANSACTION_TIMESTAMP,
783
+ });
784
+ }
785
+ if (idsToComplete.length > 0) {
786
+ await repository.updateMany(idsToComplete, {
787
+ status: TaskState.Completed,
788
+ completeTimestamp: TRANSACTION_TIMESTAMP,
789
+ progress: 1,
790
+ token: null,
791
+ });
792
+ await this.triggerTagFanIn(fanInTags, tx);
793
+ }
794
+ });
795
+ }
796
+ async maintenance() {
797
+ const repository = this.#repository;
798
+ const archiveRepository = this.#archiveRepository;
799
+ // 1. Archival: Move old terminal tasks to archive
800
+ while (true) {
801
+ const archivedCount = await repository.transaction(async (tx) => {
802
+ const repositoryWithTx = repository.withTransaction(tx);
803
+ const archiveRepositoryWithTx = archiveRepository.withTransaction(tx);
804
+ const tasksToArchive = await repositoryWithTx.loadManyByQuery({
805
+ $and: [
806
+ {
807
+ namespace: this.#namespace,
808
+ status: { $in: [TaskState.Completed, TaskState.Dead, TaskState.Cancelled] },
809
+ completeTimestamp: { $lt: sql `${TRANSACTION_TIMESTAMP} - ${interval(this.retention, 'milliseconds')}` },
810
+ },
811
+ notInArray(taskTable.id, repositoryWithTx.session
812
+ .select({ parentId: taskTable.parentId })
813
+ .from(taskTable)
814
+ .where(and(eq(taskTable.namespace, this.#namespace), sql `${taskTable.parentId} IS NOT NULL`))),
815
+ ],
816
+ }, { limit: 1000, for: { mode: 'update', skipLocked: true } });
817
+ if (tasksToArchive.length > 0) {
818
+ await archiveRepositoryWithTx.insertMany(tasksToArchive);
819
+ await repositoryWithTx.hardDeleteMany(tasksToArchive.map((t) => t.id));
820
+ }
821
+ return tasksToArchive.length;
822
+ });
823
+ if (archivedCount < 1000) {
824
+ break;
825
+ }
826
+ }
827
+ // 2. Purge Archive: Remove very old archived tasks
828
+ while (true) {
829
+ const deletedArchiveCount = await archiveRepository.transaction(async (tx) => {
830
+ const repositoryWithTx = archiveRepository.withTransaction(tx);
831
+ const selection = repositoryWithTx.session.$with('archive_purge_selection').as((qb) => qb
832
+ .select({ id: taskArchiveTable.id })
833
+ .from(taskArchiveTable)
834
+ .where(and(eq(taskArchiveTable.namespace, this.#namespace), lt(taskArchiveTable.completeTimestamp, sql `${TRANSACTION_TIMESTAMP} - ${interval(this.archiveRetention, 'milliseconds')}`)))
835
+ .limit(1000));
836
+ const result = await repositoryWithTx.session
837
+ .with(selection)
838
+ .delete(taskArchiveTable)
839
+ .where(inArray(taskArchiveTable.id, repositoryWithTx.session.select().from(selection)))
840
+ .returning({ id: taskArchiveTable.id });
841
+ return result.length;
842
+ });
843
+ if (deletedArchiveCount < 1000) {
844
+ break;
845
+ }
846
+ }
847
+ // 3. Maintenance Loop
848
+ while (true) {
849
+ const maintenanceCount = await repository.transaction(async (tx) => {
850
+ const repositoryWithTx = repository.withTransaction(tx);
851
+ let totalUpdated = 0;
852
+ // 3.1 Handle Pending Expiration
853
+ const expiredSelection = repositoryWithTx.session.$with('expired_selection').as((qb) => qb
854
+ .select({ id: taskTable.id })
855
+ .from(taskTable)
856
+ .where(and(eq(taskTable.namespace, this.#namespace), eq(taskTable.status, TaskState.Pending), lt(taskTable.timeToLive, TRANSACTION_TIMESTAMP)))
857
+ .limit(1000)
858
+ .for('update', { skipLocked: true }));
859
+ const expiredRows = await repositoryWithTx.session
860
+ .with(expiredSelection)
861
+ .update(taskTable)
862
+ .set({
863
+ status: TaskState.Dead,
864
+ token: null,
865
+ error: { code: 'Expired', message: 'Task expired before processing' },
866
+ completeTimestamp: TRANSACTION_TIMESTAMP,
867
+ })
868
+ .where(inArray(taskTable.id, repositoryWithTx.session.select().from(expiredSelection)))
869
+ .returning({ tags: taskTable.tags });
870
+ totalUpdated += expiredRows.length;
871
+ if (expiredRows.length > 0) {
872
+ await this.triggerTagFanIn(expiredRows.flatMap((r) => r.tags), tx);
873
+ }
874
+ // 3.2 Handle Zombie Tasks (Retry)
875
+ const zombieRetrySelection = repositoryWithTx.session.$with('zombie_retry_selection').as((qb) => qb
876
+ .select({ id: taskTable.id })
877
+ .from(taskTable)
878
+ .where(and(eq(taskTable.namespace, this.#namespace), eq(taskTable.status, TaskState.Running), lt(taskTable.visibilityDeadline, TRANSACTION_TIMESTAMP), lt(taskTable.tries, this.maxTries)))
879
+ .limit(1000)
880
+ .for('update', { skipLocked: true }));
881
+ const zombieRetryRows = await repositoryWithTx.session
882
+ .with(zombieRetrySelection)
883
+ .update(taskTable)
884
+ .set({
885
+ status: TaskState.Pending,
886
+ token: null,
887
+ visibilityDeadline: null,
888
+ scheduleTimestamp: sql `${TRANSACTION_TIMESTAMP} + ${interval(this.retryDelayMinimum, 'milliseconds')}`, // Simple backoff for zombies
889
+ error: sql `jsonb_build_object('code', 'VisibilityTimeout', 'message', 'Worker Lost', 'last_error', ${taskTable.error})`,
890
+ })
891
+ .where(inArray(taskTable.id, repositoryWithTx.session.select().from(zombieRetrySelection)))
892
+ .returning({ id: taskTable.id });
893
+ totalUpdated += zombieRetryRows.length;
894
+ // 3.3 Handle Zombie Tasks (Exhaustion)
895
+ const zombieExhaustionSelection = repositoryWithTx.session.$with('zombie_exhaustion_selection').as((qb) => qb
896
+ .select({ id: taskTable.id })
897
+ .from(taskTable)
898
+ .where(and(eq(taskTable.namespace, this.#namespace), eq(taskTable.status, TaskState.Running), lt(taskTable.visibilityDeadline, TRANSACTION_TIMESTAMP), gte(taskTable.tries, this.maxTries)))
899
+ .limit(1000)
900
+ .for('update', { skipLocked: true }));
901
+ const exhaustionRows = await repositoryWithTx.session
902
+ .with(zombieExhaustionSelection)
903
+ .update(taskTable)
904
+ .set({
905
+ status: TaskState.Dead,
906
+ token: null,
907
+ visibilityDeadline: null,
908
+ completeTimestamp: TRANSACTION_TIMESTAMP,
909
+ error: sql `jsonb_build_object('code', 'ZombieExhausted', 'message', 'Exceeded max retries after repeated crashes', 'last_error', ${taskTable.error})`,
910
+ })
911
+ .where(inArray(taskTable.id, repositoryWithTx.session.select().from(zombieExhaustionSelection)))
912
+ .returning({ tags: taskTable.tags });
913
+ totalUpdated += exhaustionRows.length;
914
+ if (exhaustionRows.length > 0) {
915
+ await this.triggerTagFanIn(exhaustionRows.flatMap((r) => r.tags), tx);
916
+ }
917
+ // 3.4 Handle Hard Timeout
918
+ const timeoutSelection = repositoryWithTx.session.$with('timeout_selection').as((qb) => qb
919
+ .select({ id: taskTable.id })
920
+ .from(taskTable)
921
+ .where(and(eq(taskTable.namespace, this.#namespace), eq(taskTable.status, TaskState.Running), lt(taskTable.startTimestamp, sql `${TRANSACTION_TIMESTAMP} - ${interval(this.maxExecutionTime, 'milliseconds')}`)))
922
+ .limit(1000)
923
+ .for('update', { skipLocked: true }));
924
+ const timeoutRows = await repositoryWithTx.session
925
+ .with(timeoutSelection)
926
+ .update(taskTable)
927
+ .set({
928
+ status: TaskState.Dead,
929
+ token: null,
930
+ visibilityDeadline: null,
931
+ completeTimestamp: TRANSACTION_TIMESTAMP,
932
+ error: sql `jsonb_build_object('code', 'MaxTimeExceeded', 'message', 'Hard Execution Timeout: Task ran longer than ' || ${this.maxExecutionTime} || 'ms', 'last_error', ${taskTable.error})`,
933
+ })
934
+ .where(inArray(taskTable.id, repositoryWithTx.session.select().from(timeoutSelection)))
935
+ .returning({ tags: taskTable.tags });
936
+ totalUpdated += timeoutRows.length;
937
+ if (timeoutRows.length > 0) {
938
+ await this.triggerTagFanIn(timeoutRows.flatMap((r) => r.tags), tx);
939
+ }
940
+ // 3.5 Promote Priority (Aging)
941
+ const agingSelection = repositoryWithTx.session.$with('aging_selection').as((qb) => qb
942
+ .select({ id: taskTable.id })
943
+ .from(taskTable)
944
+ .where(and(eq(taskTable.namespace, this.#namespace), eq(taskTable.status, TaskState.Pending), lt(taskTable.priorityAgeTimestamp, sql `${TRANSACTION_TIMESTAMP} - ${interval(this.priorityAgingInterval, 'milliseconds')}`)))
945
+ .limit(1000)
946
+ .for('update', { skipLocked: true }));
947
+ const agingRows = await repositoryWithTx.session
948
+ .with(agingSelection)
949
+ .update(taskTable)
950
+ .set({
951
+ priority: sql `${taskTable.priority} - ${this.priorityAgingStep}`,
952
+ priorityAgeTimestamp: TRANSACTION_TIMESTAMP,
953
+ })
954
+ .where(inArray(taskTable.id, repositoryWithTx.session.select().from(agingSelection)))
955
+ .returning({ id: taskTable.id });
956
+ totalUpdated += agingRows.length;
957
+ return totalUpdated;
958
+ });
959
+ if (maintenanceCount == 0) {
960
+ break;
961
+ }
962
+ }
963
+ }
964
+ async restart(id, options) {
965
+ const repository = this.#repository.withOptionalTransaction(options?.transaction);
966
+ await repository.session
967
+ .update(taskTable)
968
+ .set({
969
+ status: TaskState.Pending,
970
+ token: null,
971
+ error: null,
972
+ result: null,
973
+ scheduleTimestamp: TRANSACTION_TIMESTAMP,
974
+ completeTimestamp: null,
975
+ tries: 0,
976
+ progress: 0,
977
+ priorityAgeTimestamp: TRANSACTION_TIMESTAMP,
978
+ state: options?.resetState == true ? null : undefined,
979
+ })
980
+ .where(and(eq(taskTable.namespace, this.#namespace), eq(taskTable.id, id), or(eq(taskTable.status, TaskState.Pending), eq(taskTable.status, TaskState.Completed), eq(taskTable.status, TaskState.Cancelled), eq(taskTable.status, TaskState.Dead), lt(taskTable.visibilityDeadline, TRANSACTION_TIMESTAMP))))
981
+ .execute();
982
+ }
983
+ async *getConsumer(cancellationSignal, options) {
984
+ const continue$ = merge(this.#messageBus.allMessages$, cancellationSignal);
985
+ while (cancellationSignal.isUnset) {
986
+ const task = await this.dequeue(options);
987
+ if (isDefined(task)) {
988
+ yield task;
989
+ continue;
990
+ }
991
+ await cancelableTimeout(5 * millisecondsPerSecond, continue$);
992
+ }
993
+ }
994
+ async maintenanceLoop() {
995
+ while (this.#cancellationSignal.isUnset) {
996
+ try {
997
+ await this.maintenance();
998
+ }
999
+ catch (error) {
1000
+ this.#logger.error('Error during maintenance loop', error);
1001
+ }
1002
+ finally {
1003
+ await cancelableTimeout(30 * millisecondsPerSecond, this.#cancellationSignal);
1004
+ }
1005
+ }
1006
+ }
1007
+ async processPendingFanIn(transaction) {
1008
+ let processedCount = 0;
1009
+ while (true) {
1010
+ const task = await this.dequeue({ types: ['[SystemWorker]:FanIn'], forceDequeue: true });
1011
+ if (isUndefined(task)) {
1012
+ break;
1013
+ }
1014
+ try {
1015
+ const data = task.data;
1016
+ await this.resolveDependencies(data.targetTags);
1017
+ await this.complete(task, undefined, transaction);
1018
+ processedCount++;
1019
+ }
1020
+ catch (error) {
1021
+ await this.fail(task, error, false, transaction);
1022
+ throw error;
1023
+ }
1024
+ }
1025
+ return processedCount;
1026
+ }
1027
+ startSystemWorker() {
1028
+ this.process({ concurrency: 1, cancellationSignal: this.#cancellationSignal, forceDequeue: true, types: ['[SystemWorker]:FanIn'] }, async (context) => {
1029
+ const data = context.data;
1030
+ await this.resolveDependencies(data.targetTags);
1031
+ return TaskResult.Complete();
1032
+ });
1033
+ }
1034
+ async *getBatchConsumer(size, cancellationSignal, options) {
1035
+ const continue$ = merge(this.#messageBus.allMessages$, cancellationSignal);
1036
+ while (cancellationSignal.isUnset) {
1037
+ const tasks = await this.dequeueMany(size, options);
1038
+ if (tasks.length > 0) {
1039
+ yield tasks;
1040
+ continue;
1041
+ }
1042
+ await cancelableTimeout(5 * millisecondsPerSecond, continue$);
1043
+ }
1044
+ }
1045
+ };
1046
+ PostgresQueue = __decorate([
1047
+ Singleton({
1048
+ argumentIdentityProvider: JSON.stringify,
1049
+ providers: [
1050
+ provide(DatabaseConfig, { useFactory: (_, context) => context.resolve(PostgresTaskQueueModuleConfig).database ?? context.resolve(DatabaseConfig, undefined, { skipSelf: 2 }) }),
1051
+ ],
1052
+ })
1053
+ ], PostgresQueue);
1054
+ export { PostgresQueue };