@elie-laloum/outpost 11.1.2 → 11.2.0

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 (596) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +2 -1
  3. package/SECURITY.md +5 -1
  4. package/dist/adapters/agents/antigravity/antigravity-adapter.js +9 -0
  5. package/dist/adapters/agents/antigravity/antigravity-adapter.js.map +1 -1
  6. package/dist/adapters/agents/antigravity/antigravity-hook-plan.d.ts +3 -0
  7. package/dist/adapters/agents/antigravity/antigravity-hook-plan.js +39 -0
  8. package/dist/adapters/agents/antigravity/antigravity-hook-plan.js.map +1 -0
  9. package/dist/adapters/agents/antigravity/antigravity-hooks.constants.d.ts +3 -0
  10. package/dist/adapters/agents/antigravity/antigravity-hooks.constants.js +394 -0
  11. package/dist/adapters/agents/antigravity/antigravity-hooks.constants.js.map +1 -0
  12. package/dist/adapters/agents/antigravity/antigravity-hooks.d.ts +2 -0
  13. package/dist/adapters/agents/antigravity/antigravity-hooks.js +6 -0
  14. package/dist/adapters/agents/antigravity/antigravity-hooks.js.map +1 -0
  15. package/dist/adapters/agents/antigravity/antigravity-hooks.types.d.ts +114 -0
  16. package/dist/adapters/agents/antigravity/antigravity-hooks.types.js +2 -0
  17. package/dist/adapters/agents/antigravity/antigravity-hooks.types.js.map +1 -0
  18. package/dist/adapters/agents/antigravity/antigravity.types.d.ts +1 -0
  19. package/dist/adapters/agents/antigravity/antigravity.types.js.map +1 -1
  20. package/dist/adapters/agents/authentication.js +1 -0
  21. package/dist/adapters/agents/authentication.js.map +1 -1
  22. package/dist/adapters/agents/claude/claude-adapter.js +15 -0
  23. package/dist/adapters/agents/claude/claude-adapter.js.map +1 -1
  24. package/dist/adapters/agents/claude/claude-hook-plan.constants.d.ts +1 -0
  25. package/dist/adapters/agents/claude/claude-hook-plan.constants.js +20 -0
  26. package/dist/adapters/agents/claude/claude-hook-plan.constants.js.map +1 -0
  27. package/dist/adapters/agents/claude/claude-hook-plan.d.ts +4 -0
  28. package/dist/adapters/agents/claude/claude-hook-plan.js +60 -0
  29. package/dist/adapters/agents/claude/claude-hook-plan.js.map +1 -0
  30. package/dist/adapters/agents/claude/claude-hooks.constants.d.ts +3 -0
  31. package/dist/adapters/agents/claude/claude-hooks.constants.js +2968 -0
  32. package/dist/adapters/agents/claude/claude-hooks.constants.js.map +1 -0
  33. package/dist/adapters/agents/claude/claude-hooks.d.ts +2 -0
  34. package/dist/adapters/agents/claude/claude-hooks.js +6 -0
  35. package/dist/adapters/agents/claude/claude-hooks.js.map +1 -0
  36. package/dist/adapters/agents/claude/claude-hooks.types.d.ts +900 -0
  37. package/dist/adapters/agents/claude/claude-hooks.types.js +2 -0
  38. package/dist/adapters/agents/claude/claude-hooks.types.js.map +1 -0
  39. package/dist/adapters/agents/claude/claude-profile.d.ts +1 -0
  40. package/dist/adapters/agents/claude/claude-profile.js +23 -11
  41. package/dist/adapters/agents/claude/claude-profile.js.map +1 -1
  42. package/dist/adapters/agents/codex/codex-adapter.js +17 -1
  43. package/dist/adapters/agents/codex/codex-adapter.js.map +1 -1
  44. package/dist/adapters/agents/codex/codex-authentication.js +1 -0
  45. package/dist/adapters/agents/codex/codex-authentication.js.map +1 -1
  46. package/dist/adapters/agents/codex/codex-conversations.constants.d.ts +1 -0
  47. package/dist/adapters/agents/codex/codex-conversations.constants.js +2 -0
  48. package/dist/adapters/agents/codex/codex-conversations.constants.js.map +1 -0
  49. package/dist/adapters/agents/codex/codex-conversations.js +8 -5
  50. package/dist/adapters/agents/codex/codex-conversations.js.map +1 -1
  51. package/dist/adapters/agents/codex/codex-hook-plan.d.ts +3 -0
  52. package/dist/adapters/agents/codex/codex-hook-plan.js +72 -0
  53. package/dist/adapters/agents/codex/codex-hook-plan.js.map +1 -0
  54. package/dist/adapters/agents/codex/codex-hooks.constants.d.ts +3 -0
  55. package/dist/adapters/agents/codex/codex-hooks.constants.js +942 -0
  56. package/dist/adapters/agents/codex/codex-hooks.constants.js.map +1 -0
  57. package/dist/adapters/agents/codex/codex-hooks.d.ts +2 -0
  58. package/dist/adapters/agents/codex/codex-hooks.js +6 -0
  59. package/dist/adapters/agents/codex/codex-hooks.js.map +1 -0
  60. package/dist/adapters/agents/codex/codex-hooks.types.d.ts +255 -0
  61. package/dist/adapters/agents/codex/codex-hooks.types.js +2 -0
  62. package/dist/adapters/agents/codex/codex-hooks.types.js.map +1 -0
  63. package/dist/adapters/agents/codex/codex-provider.js +2 -2
  64. package/dist/adapters/agents/codex/codex-provider.js.map +1 -1
  65. package/dist/adapters/agents/copilot/copilot-adapter.js +14 -0
  66. package/dist/adapters/agents/copilot/copilot-adapter.js.map +1 -1
  67. package/dist/adapters/agents/copilot/copilot-hook-plan.d.ts +3 -0
  68. package/dist/adapters/agents/copilot/copilot-hook-plan.js +26 -0
  69. package/dist/adapters/agents/copilot/copilot-hook-plan.js.map +1 -0
  70. package/dist/adapters/agents/copilot/copilot-hooks.constants.d.ts +3 -0
  71. package/dist/adapters/agents/copilot/copilot-hooks.constants.js +528 -0
  72. package/dist/adapters/agents/copilot/copilot-hooks.constants.js.map +1 -0
  73. package/dist/adapters/agents/copilot/copilot-hooks.d.ts +2 -0
  74. package/dist/adapters/agents/copilot/copilot-hooks.js +6 -0
  75. package/dist/adapters/agents/copilot/copilot-hooks.js.map +1 -0
  76. package/dist/adapters/agents/copilot/copilot-hooks.types.d.ts +166 -0
  77. package/dist/adapters/agents/copilot/copilot-hooks.types.js +2 -0
  78. package/dist/adapters/agents/copilot/copilot-hooks.types.js.map +1 -0
  79. package/dist/adapters/agents/copilot/copilot.types.d.ts +1 -0
  80. package/dist/adapters/agents/copilot/copilot.types.js.map +1 -1
  81. package/dist/adapters/agents/hook-groups.d.ts +2 -0
  82. package/dist/adapters/agents/hook-groups.js +17 -0
  83. package/dist/adapters/agents/hook-groups.js.map +1 -0
  84. package/dist/adapters/agents/kimi/kimi-adapter.js +16 -1
  85. package/dist/adapters/agents/kimi/kimi-adapter.js.map +1 -1
  86. package/dist/adapters/agents/kimi/kimi-hook-plan.d.ts +3 -0
  87. package/dist/adapters/agents/kimi/kimi-hook-plan.js +33 -0
  88. package/dist/adapters/agents/kimi/kimi-hook-plan.js.map +1 -0
  89. package/dist/adapters/agents/kimi/kimi-hooks.constants.d.ts +3 -0
  90. package/dist/adapters/agents/kimi/kimi-hooks.constants.js +747 -0
  91. package/dist/adapters/agents/kimi/kimi-hooks.constants.js.map +1 -0
  92. package/dist/adapters/agents/kimi/kimi-hooks.d.ts +2 -0
  93. package/dist/adapters/agents/kimi/kimi-hooks.js +6 -0
  94. package/dist/adapters/agents/kimi/kimi-hooks.js.map +1 -0
  95. package/dist/adapters/agents/kimi/kimi-hooks.types.d.ts +226 -0
  96. package/dist/adapters/agents/kimi/kimi-hooks.types.js +2 -0
  97. package/dist/adapters/agents/kimi/kimi-hooks.types.js.map +1 -0
  98. package/dist/adapters/agents/kimi/kimi-request.js +2 -0
  99. package/dist/adapters/agents/kimi/kimi-request.js.map +1 -1
  100. package/dist/adapters/agents/kimi/kimi.constants.d.ts +1 -0
  101. package/dist/adapters/agents/kimi/kimi.constants.js +1 -0
  102. package/dist/adapters/agents/kimi/kimi.constants.js.map +1 -1
  103. package/dist/adapters/agents/kimi/kimi.types.d.ts +1 -0
  104. package/dist/adapters/agents/kimi/kimi.types.js.map +1 -1
  105. package/dist/adapters/agents/native-hooks.d.ts +4 -0
  106. package/dist/adapters/agents/native-hooks.js +63 -0
  107. package/dist/adapters/agents/native-hooks.js.map +1 -0
  108. package/dist/adapters/agents/native-hooks.types.d.ts +22 -0
  109. package/dist/adapters/agents/native-hooks.types.js +2 -0
  110. package/dist/adapters/agents/native-hooks.types.js.map +1 -0
  111. package/dist/adapters/agents/settings.types.d.ts +2 -0
  112. package/dist/adapters/agents/settings.types.js.map +1 -1
  113. package/dist/adapters/decisions/system-one-provider.constants.d.ts +1 -1
  114. package/dist/adapters/decisions/system-one-provider.constants.js +1 -1
  115. package/dist/adapters/decisions/system-one-provider.constants.js.map +1 -1
  116. package/dist/adapters/decisions/system-one-provider.js +8 -21
  117. package/dist/adapters/decisions/system-one-provider.js.map +1 -1
  118. package/dist/adapters/models/anthropic-model-provider.constants.d.ts +1 -0
  119. package/dist/adapters/models/anthropic-model-provider.constants.js +4 -0
  120. package/dist/adapters/models/anthropic-model-provider.constants.js.map +1 -1
  121. package/dist/adapters/models/anthropic-model-provider.js +7 -1
  122. package/dist/adapters/models/anthropic-model-provider.js.map +1 -1
  123. package/dist/adapters/models/http-failure.d.ts +3 -0
  124. package/dist/adapters/models/http-failure.js +43 -0
  125. package/dist/adapters/models/http-failure.js.map +1 -0
  126. package/dist/adapters/models/http-provider.js +14 -20
  127. package/dist/adapters/models/http-provider.js.map +1 -1
  128. package/dist/adapters/models/model-protocol.types.d.ts +1 -0
  129. package/dist/adapters/models/model-protocol.types.js.map +1 -1
  130. package/dist/adapters/models/model.constants.d.ts +2 -0
  131. package/dist/adapters/models/model.constants.js +17 -0
  132. package/dist/adapters/models/model.constants.js.map +1 -1
  133. package/dist/adapters/models/openai-chat-stream.js +3 -1
  134. package/dist/adapters/models/openai-chat-stream.js.map +1 -1
  135. package/dist/adapters/models/openai-responses.js +4 -0
  136. package/dist/adapters/models/openai-responses.js.map +1 -1
  137. package/dist/adapters/secrets/aws.js +5 -4
  138. package/dist/adapters/secrets/aws.js.map +1 -1
  139. package/dist/adapters/secrets/azure.constants.d.ts +1 -0
  140. package/dist/adapters/secrets/azure.constants.js +2 -0
  141. package/dist/adapters/secrets/azure.constants.js.map +1 -0
  142. package/dist/adapters/secrets/azure.js +2 -1
  143. package/dist/adapters/secrets/azure.js.map +1 -1
  144. package/dist/adapters/secrets/gcp.constants.d.ts +1 -0
  145. package/dist/adapters/secrets/gcp.constants.js +2 -0
  146. package/dist/adapters/secrets/gcp.constants.js.map +1 -0
  147. package/dist/adapters/secrets/gcp.js +11 -9
  148. package/dist/adapters/secrets/gcp.js.map +1 -1
  149. package/dist/adapters/secrets/infisical.js +3 -2
  150. package/dist/adapters/secrets/infisical.js.map +1 -1
  151. package/dist/adapters/secrets/onepassword.constants.d.ts +1 -0
  152. package/dist/adapters/secrets/onepassword.constants.js +2 -0
  153. package/dist/adapters/secrets/onepassword.constants.js.map +1 -0
  154. package/dist/adapters/secrets/onepassword.js +2 -1
  155. package/dist/adapters/secrets/onepassword.js.map +1 -1
  156. package/dist/adapters/secrets/secret-source.js +3 -1
  157. package/dist/adapters/secrets/secret-source.js.map +1 -1
  158. package/dist/adapters/secrets/vault-response.js +5 -5
  159. package/dist/adapters/secrets/vault-response.js.map +1 -1
  160. package/dist/adapters/secrets/vault.js +4 -3
  161. package/dist/adapters/secrets/vault.js.map +1 -1
  162. package/dist/adapters/testing/scripted-agent.d.ts +3 -1
  163. package/dist/adapters/testing/scripted-agent.js +3 -1
  164. package/dist/adapters/testing/scripted-agent.js.map +1 -1
  165. package/dist/adapters/tools/edit-tools.js +3 -3
  166. package/dist/adapters/tools/edit-tools.js.map +1 -1
  167. package/dist/adapters/tools/git-tools.js +16 -1
  168. package/dist/adapters/tools/git-tools.js.map +1 -1
  169. package/dist/adapters/tools/mcp-launcher.constants.d.ts +1 -1
  170. package/dist/adapters/tools/mcp-launcher.constants.js +29 -7
  171. package/dist/adapters/tools/mcp-launcher.constants.js.map +1 -1
  172. package/dist/adapters/tools/mcp-resources.js +5 -1
  173. package/dist/adapters/tools/mcp-resources.js.map +1 -1
  174. package/dist/adapters/tools/mcp.constants.d.ts +0 -6
  175. package/dist/adapters/tools/mcp.constants.js +0 -6
  176. package/dist/adapters/tools/mcp.constants.js.map +1 -1
  177. package/dist/adapters/tools/mcp.js +1 -0
  178. package/dist/adapters/tools/mcp.js.map +1 -1
  179. package/dist/adapters/tools/sandbox-files.constants.d.ts +1 -0
  180. package/dist/adapters/tools/sandbox-files.constants.js +17 -0
  181. package/dist/adapters/tools/sandbox-files.constants.js.map +1 -0
  182. package/dist/adapters/tools/sandbox-files.d.ts +1 -0
  183. package/dist/adapters/tools/sandbox-files.js +14 -1
  184. package/dist/adapters/tools/sandbox-files.js.map +1 -1
  185. package/dist/adapters/tools/tools.constants.js +1 -0
  186. package/dist/adapters/tools/tools.constants.js.map +1 -1
  187. package/dist/adapters/triggers/github-webhook.js +14 -11
  188. package/dist/adapters/triggers/github-webhook.js.map +1 -1
  189. package/dist/adapters/triggers/gitlab-webhook.js +16 -13
  190. package/dist/adapters/triggers/gitlab-webhook.js.map +1 -1
  191. package/dist/adapters/triggers/slack-request.js +10 -7
  192. package/dist/adapters/triggers/slack-request.js.map +1 -1
  193. package/dist/adapters/triggers/standard-webhook.js +10 -7
  194. package/dist/adapters/triggers/standard-webhook.js.map +1 -1
  195. package/dist/application/agent-authentication.js +4 -1
  196. package/dist/application/agent-authentication.js.map +1 -1
  197. package/dist/application/agent-turn.js +20 -5
  198. package/dist/application/agent-turn.js.map +1 -1
  199. package/dist/application/cli-hooks.constants.d.ts +2 -0
  200. package/dist/application/cli-hooks.constants.js +69 -0
  201. package/dist/application/cli-hooks.constants.js.map +1 -0
  202. package/dist/application/cli-hooks.d.ts +3 -0
  203. package/dist/application/cli-hooks.js +168 -0
  204. package/dist/application/cli-hooks.js.map +1 -0
  205. package/dist/application/cli-steering.js +33 -2
  206. package/dist/application/cli-steering.js.map +1 -1
  207. package/dist/application/custom-turn.js +10 -1
  208. package/dist/application/custom-turn.js.map +1 -1
  209. package/dist/application/diagnostic-probe.d.ts +1 -1
  210. package/dist/application/diagnostic-probe.js +7 -3
  211. package/dist/application/diagnostic-probe.js.map +1 -1
  212. package/dist/application/dispatch-observation.js +4 -0
  213. package/dist/application/dispatch-observation.js.map +1 -1
  214. package/dist/application/doctor-agent.d.ts +1 -1
  215. package/dist/application/doctor-agent.js +2 -2
  216. package/dist/application/doctor-agent.js.map +1 -1
  217. package/dist/application/doctor-deadline.d.ts +1 -0
  218. package/dist/application/doctor-deadline.js +10 -0
  219. package/dist/application/doctor-deadline.js.map +1 -0
  220. package/dist/application/doctor-image.js +9 -7
  221. package/dist/application/doctor-image.js.map +1 -1
  222. package/dist/application/doctor-sandbox.js +4 -4
  223. package/dist/application/doctor-sandbox.js.map +1 -1
  224. package/dist/application/doctor.constants.d.ts +2 -0
  225. package/dist/application/doctor.constants.js +2 -0
  226. package/dist/application/doctor.constants.js.map +1 -1
  227. package/dist/application/doctor.js +12 -8
  228. package/dist/application/doctor.js.map +1 -1
  229. package/dist/application/doctor.types.d.ts +2 -0
  230. package/dist/application/doctor.types.js.map +1 -1
  231. package/dist/application/file-sandbox.js +49 -11
  232. package/dist/application/file-sandbox.js.map +1 -1
  233. package/dist/application/harness-context.js +4 -2
  234. package/dist/application/harness-context.js.map +1 -1
  235. package/dist/application/harness-hook-transforms.d.ts +8 -0
  236. package/dist/application/harness-hook-transforms.js +115 -0
  237. package/dist/application/harness-hook-transforms.js.map +1 -0
  238. package/dist/application/harness-hooks.d.ts +5 -3
  239. package/dist/application/harness-hooks.js +37 -50
  240. package/dist/application/harness-hooks.js.map +1 -1
  241. package/dist/application/harness-loop.d.ts +2 -1
  242. package/dist/application/harness-loop.js +95 -21
  243. package/dist/application/harness-loop.js.map +1 -1
  244. package/dist/application/harness-subagent.d.ts +1 -1
  245. package/dist/application/harness-subagent.js +63 -12
  246. package/dist/application/harness-subagent.js.map +1 -1
  247. package/dist/application/harness.types.d.ts +3 -0
  248. package/dist/application/harness.types.js.map +1 -1
  249. package/dist/application/hook-invocation.d.ts +4 -0
  250. package/dist/application/hook-invocation.js +59 -0
  251. package/dist/application/hook-invocation.js.map +1 -0
  252. package/dist/application/queue-worker.js +51 -9
  253. package/dist/application/queue-worker.js.map +1 -1
  254. package/dist/application/queue-worker.types.d.ts +1 -0
  255. package/dist/application/queue-worker.types.js.map +1 -1
  256. package/dist/application/recipes/native-schemas.constants.js +1439 -218
  257. package/dist/application/recipes/native-schemas.constants.js.map +1 -1
  258. package/dist/application/recovery-inspection.js +4 -1
  259. package/dist/application/recovery-inspection.js.map +1 -1
  260. package/dist/application/remote-download.js +15 -5
  261. package/dist/application/remote-download.js.map +1 -1
  262. package/dist/application/remote-paths.d.ts +1 -0
  263. package/dist/application/remote-paths.js +7 -0
  264. package/dist/application/remote-paths.js.map +1 -0
  265. package/dist/application/remote-validation.js +40 -9
  266. package/dist/application/remote-validation.js.map +1 -1
  267. package/dist/application/remote-workspace.js +17 -2
  268. package/dist/application/remote-workspace.js.map +1 -1
  269. package/dist/application/repetition-watchdog.js +3 -1
  270. package/dist/application/repetition-watchdog.js.map +1 -1
  271. package/dist/application/sandbox-attach.js +38 -9
  272. package/dist/application/sandbox-attach.js.map +1 -1
  273. package/dist/application/sandbox-dispatch.js +7 -1
  274. package/dist/application/sandbox-dispatch.js.map +1 -1
  275. package/dist/application/schedules.js +2 -0
  276. package/dist/application/schedules.js.map +1 -1
  277. package/dist/application/secrets.js +4 -0
  278. package/dist/application/secrets.js.map +1 -1
  279. package/dist/application/tool-execution.js +28 -8
  280. package/dist/application/tool-execution.js.map +1 -1
  281. package/dist/application/transport-retention.js +3 -3
  282. package/dist/application/transport-retention.js.map +1 -1
  283. package/dist/application/workspace-fingerprint.d.ts +1 -0
  284. package/dist/application/workspace-fingerprint.js +19 -4
  285. package/dist/application/workspace-fingerprint.js.map +1 -1
  286. package/dist/cli/doctor-command.js +12 -2
  287. package/dist/cli/doctor-command.js.map +1 -1
  288. package/dist/cli/image.js +2 -0
  289. package/dist/cli/image.js.map +1 -1
  290. package/dist/cli/main.constants.d.ts +14 -2
  291. package/dist/cli/main.constants.js +27 -1
  292. package/dist/cli/main.constants.js.map +1 -1
  293. package/dist/cli/project-settings.js +7 -1
  294. package/dist/cli/project-settings.js.map +1 -1
  295. package/dist/cli/recipe-yaml.js +8 -0
  296. package/dist/cli/recipe-yaml.js.map +1 -1
  297. package/dist/cli/recovery-command.js +3 -0
  298. package/dist/cli/recovery-command.js.map +1 -1
  299. package/dist/cli/recovery-unlock-command.d.ts +2 -0
  300. package/dist/cli/recovery-unlock-command.js +19 -0
  301. package/dist/cli/recovery-unlock-command.js.map +1 -0
  302. package/dist/cli/scaffold-files.js +4 -1
  303. package/dist/cli/scaffold-files.js.map +1 -1
  304. package/dist/cli/scaffold-validation.js +3 -0
  305. package/dist/cli/scaffold-validation.js.map +1 -1
  306. package/dist/cli/scaffold.js +2 -0
  307. package/dist/cli/scaffold.js.map +1 -1
  308. package/dist/domain/agent-hook.types.d.ts +28 -0
  309. package/dist/domain/agent-hook.types.js +2 -0
  310. package/dist/domain/agent-hook.types.js.map +1 -0
  311. package/dist/domain/agent-profile-hooks.d.ts +2 -0
  312. package/dist/domain/agent-profile-hooks.js +52 -0
  313. package/dist/domain/agent-profile-hooks.js.map +1 -0
  314. package/dist/domain/agent-profile-permissions.js +11 -1
  315. package/dist/domain/agent-profile-permissions.js.map +1 -1
  316. package/dist/domain/agent-profile.constants.d.ts +1 -0
  317. package/dist/domain/agent-profile.constants.js +9 -0
  318. package/dist/domain/agent-profile.constants.js.map +1 -1
  319. package/dist/domain/agent-profile.js +6 -1
  320. package/dist/domain/agent-profile.js.map +1 -1
  321. package/dist/domain/agent-profile.types.d.ts +11 -0
  322. package/dist/domain/agent-profile.types.js.map +1 -1
  323. package/dist/domain/agent.types.d.ts +2 -0
  324. package/dist/domain/agent.types.js.map +1 -1
  325. package/dist/domain/container-image.d.ts +1 -0
  326. package/dist/domain/container-image.js +8 -0
  327. package/dist/domain/container-image.js.map +1 -0
  328. package/dist/domain/context.constants.d.ts +1 -0
  329. package/dist/domain/context.constants.js +1 -0
  330. package/dist/domain/context.constants.js.map +1 -1
  331. package/dist/domain/context.js +6 -1
  332. package/dist/domain/context.js.map +1 -1
  333. package/dist/domain/egress.js +6 -1
  334. package/dist/domain/egress.js.map +1 -1
  335. package/dist/domain/glob.js +1 -1
  336. package/dist/domain/glob.js.map +1 -1
  337. package/dist/domain/harness.js +4 -1
  338. package/dist/domain/harness.js.map +1 -1
  339. package/dist/domain/hook-command.d.ts +2 -0
  340. package/dist/domain/hook-command.js +26 -0
  341. package/dist/domain/hook-command.js.map +1 -0
  342. package/dist/domain/hook-command.types.d.ts +7 -0
  343. package/dist/domain/hook-command.types.js +2 -0
  344. package/dist/domain/hook-command.types.js.map +1 -0
  345. package/dist/domain/hook-decision.d.ts +2 -0
  346. package/dist/domain/hook-decision.js +38 -0
  347. package/dist/domain/hook-decision.js.map +1 -0
  348. package/dist/domain/hook.constants.d.ts +3 -0
  349. package/dist/domain/hook.constants.js +31 -1
  350. package/dist/domain/hook.constants.js.map +1 -1
  351. package/dist/domain/hook.js +49 -5
  352. package/dist/domain/hook.js.map +1 -1
  353. package/dist/domain/hook.types.d.ts +82 -5
  354. package/dist/domain/hook.types.js.map +1 -1
  355. package/dist/domain/mcp-server.constants.d.ts +6 -0
  356. package/dist/domain/mcp-server.constants.js +6 -0
  357. package/dist/domain/mcp-server.constants.js.map +1 -1
  358. package/dist/domain/mcp-server.js +1 -0
  359. package/dist/domain/mcp-server.js.map +1 -1
  360. package/dist/domain/permissions.types.d.ts +1 -0
  361. package/dist/domain/permissions.types.js.map +1 -1
  362. package/dist/domain/pricing.js +4 -2
  363. package/dist/domain/pricing.js.map +1 -1
  364. package/dist/domain/redaction.js +6 -3
  365. package/dist/domain/redaction.js.map +1 -1
  366. package/dist/domain/secret-fault.d.ts +4 -0
  367. package/dist/domain/secret-fault.js +11 -0
  368. package/dist/domain/secret-fault.js.map +1 -0
  369. package/dist/domain/secrets.js +3 -2
  370. package/dist/domain/secrets.js.map +1 -1
  371. package/dist/domain/steering.js +2 -0
  372. package/dist/domain/steering.js.map +1 -1
  373. package/dist/domain/steering.types.d.ts +1 -1
  374. package/dist/domain/steering.types.js.map +1 -1
  375. package/dist/domain/transport.js +5 -1
  376. package/dist/domain/transport.js.map +1 -1
  377. package/dist/domain/trigger-payload.d.ts +4 -0
  378. package/dist/domain/trigger-payload.js +15 -0
  379. package/dist/domain/trigger-payload.js.map +1 -0
  380. package/dist/domain/workflow/checkpoint.js +19 -2
  381. package/dist/domain/workflow/checkpoint.js.map +1 -1
  382. package/dist/domain/workflow/retry.constants.d.ts +1 -0
  383. package/dist/domain/workflow/retry.constants.js +1 -0
  384. package/dist/domain/workflow/retry.constants.js.map +1 -1
  385. package/dist/domain/workflow/retry.js +6 -2
  386. package/dist/domain/workflow/retry.js.map +1 -1
  387. package/dist/domain/workflow/task-runner.js +5 -3
  388. package/dist/domain/workflow/task-runner.js.map +1 -1
  389. package/dist/domain/workflow/workflow-state.js +2 -1
  390. package/dist/domain/workflow/workflow-state.js.map +1 -1
  391. package/dist/domain/workflow.types.d.ts +1 -0
  392. package/dist/domain/workflow.types.js.map +1 -1
  393. package/dist/index.d.ts +15 -1
  394. package/dist/index.js +6 -0
  395. package/dist/index.js.map +1 -1
  396. package/dist/infrastructure/conversations/transcript-store.js +32 -5
  397. package/dist/infrastructure/conversations/transcript-store.js.map +1 -1
  398. package/dist/infrastructure/files.d.ts +1 -0
  399. package/dist/infrastructure/files.js +8 -4
  400. package/dist/infrastructure/files.js.map +1 -1
  401. package/dist/infrastructure/git/integration-candidate.js +3 -2
  402. package/dist/infrastructure/git/integration-candidate.js.map +1 -1
  403. package/dist/infrastructure/git/lock-recovery.d.ts +3 -0
  404. package/dist/infrastructure/git/lock-recovery.js +58 -0
  405. package/dist/infrastructure/git/lock-recovery.js.map +1 -0
  406. package/dist/infrastructure/git/lock-recovery.types.d.ts +11 -0
  407. package/dist/infrastructure/git/lock-recovery.types.js +2 -0
  408. package/dist/infrastructure/git/lock-recovery.types.js.map +1 -0
  409. package/dist/infrastructure/git/managed-worktree.js +5 -8
  410. package/dist/infrastructure/git/managed-worktree.js.map +1 -1
  411. package/dist/infrastructure/git/verification-command.js +35 -30
  412. package/dist/infrastructure/git/verification-command.js.map +1 -1
  413. package/dist/infrastructure/git/workspace-lease.js +12 -5
  414. package/dist/infrastructure/git/workspace-lease.js.map +1 -1
  415. package/dist/infrastructure/hook-configuration.constants.d.ts +1 -0
  416. package/dist/infrastructure/hook-configuration.constants.js +59 -0
  417. package/dist/infrastructure/hook-configuration.constants.js.map +1 -0
  418. package/dist/infrastructure/hook-configuration.d.ts +3 -0
  419. package/dist/infrastructure/hook-configuration.js +16 -0
  420. package/dist/infrastructure/hook-configuration.js.map +1 -0
  421. package/dist/infrastructure/hook-configuration.types.d.ts +14 -0
  422. package/dist/infrastructure/hook-configuration.types.js +2 -0
  423. package/dist/infrastructure/hook-configuration.types.js.map +1 -0
  424. package/dist/infrastructure/http-transport.constants.d.ts +1 -0
  425. package/dist/infrastructure/http-transport.constants.js +14 -0
  426. package/dist/infrastructure/http-transport.constants.js.map +1 -0
  427. package/dist/infrastructure/http-transport.d.ts +7 -0
  428. package/dist/infrastructure/http-transport.js +19 -0
  429. package/dist/infrastructure/http-transport.js.map +1 -0
  430. package/dist/infrastructure/input-failure.d.ts +4 -0
  431. package/dist/infrastructure/input-failure.js +14 -0
  432. package/dist/infrastructure/input-failure.js.map +1 -0
  433. package/dist/infrastructure/inspection-file.d.ts +1 -0
  434. package/dist/infrastructure/inspection-file.js +8 -2
  435. package/dist/infrastructure/inspection-file.js.map +1 -1
  436. package/dist/infrastructure/local-transport.js +16 -11
  437. package/dist/infrastructure/local-transport.js.map +1 -1
  438. package/dist/infrastructure/process.constants.d.ts +1 -0
  439. package/dist/infrastructure/process.constants.js +1 -0
  440. package/dist/infrastructure/process.constants.js.map +1 -1
  441. package/dist/infrastructure/process.d.ts +1 -0
  442. package/dist/infrastructure/process.js +48 -8
  443. package/dist/infrastructure/process.js.map +1 -1
  444. package/dist/infrastructure/reservation-ledger.d.ts +2 -0
  445. package/dist/infrastructure/reservation-ledger.js +84 -0
  446. package/dist/infrastructure/reservation-ledger.js.map +1 -0
  447. package/dist/infrastructure/reservation-recovery.d.ts +4 -0
  448. package/dist/infrastructure/reservation-recovery.js +28 -0
  449. package/dist/infrastructure/reservation-recovery.js.map +1 -0
  450. package/dist/infrastructure/s3-tombstones.d.ts +3 -0
  451. package/dist/infrastructure/s3-tombstones.js +46 -0
  452. package/dist/infrastructure/s3-tombstones.js.map +1 -0
  453. package/dist/infrastructure/s3-tombstones.types.d.ts +6 -0
  454. package/dist/infrastructure/s3-tombstones.types.js +2 -0
  455. package/dist/infrastructure/s3-tombstones.types.js.map +1 -0
  456. package/dist/infrastructure/s3-transport.d.ts +2 -0
  457. package/dist/infrastructure/s3-transport.js +4 -2
  458. package/dist/infrastructure/s3-transport.js.map +1 -1
  459. package/dist/infrastructure/sse.js +6 -3
  460. package/dist/infrastructure/sse.js.map +1 -1
  461. package/dist/infrastructure/task-queue-bullmq-connection.d.ts +3 -0
  462. package/dist/infrastructure/task-queue-bullmq-connection.js +32 -0
  463. package/dist/infrastructure/task-queue-bullmq-connection.js.map +1 -0
  464. package/dist/infrastructure/task-queue-bullmq-connection.types.d.ts +8 -0
  465. package/dist/infrastructure/task-queue-bullmq-connection.types.js +2 -0
  466. package/dist/infrastructure/task-queue-bullmq-connection.types.js.map +1 -0
  467. package/dist/infrastructure/task-queue-bullmq-resources.js +21 -15
  468. package/dist/infrastructure/task-queue-bullmq-resources.js.map +1 -1
  469. package/dist/infrastructure/task-queue-bullmq-state.d.ts +1 -0
  470. package/dist/infrastructure/task-queue-bullmq-state.js +1 -0
  471. package/dist/infrastructure/task-queue-bullmq-state.js.map +1 -1
  472. package/dist/infrastructure/task-queue-bullmq.constants.d.ts +2 -1
  473. package/dist/infrastructure/task-queue-bullmq.constants.js +2 -1
  474. package/dist/infrastructure/task-queue-bullmq.constants.js.map +1 -1
  475. package/dist/infrastructure/task-queue-bullmq.js +10 -1
  476. package/dist/infrastructure/task-queue-bullmq.js.map +1 -1
  477. package/dist/infrastructure/task-queue.js +13 -4
  478. package/dist/infrastructure/task-queue.js.map +1 -1
  479. package/dist/infrastructure/transfer-copy.d.ts +1 -0
  480. package/dist/infrastructure/transfer-copy.js +16 -1
  481. package/dist/infrastructure/transfer-copy.js.map +1 -1
  482. package/dist/infrastructure/transport-archive.d.ts +3 -3
  483. package/dist/infrastructure/transport-archive.js +71 -48
  484. package/dist/infrastructure/transport-archive.js.map +1 -1
  485. package/dist/infrastructure/transport-archive.types.d.ts +3 -0
  486. package/dist/infrastructure/transport-archive.types.js.map +1 -1
  487. package/dist/infrastructure/transport-checkpoint.js +14 -4
  488. package/dist/infrastructure/transport-checkpoint.js.map +1 -1
  489. package/dist/infrastructure/transport-envelope.d.ts +1 -0
  490. package/dist/infrastructure/transport-envelope.js +22 -5
  491. package/dist/infrastructure/transport-envelope.js.map +1 -1
  492. package/dist/infrastructure/transport-reservations.constants.d.ts +2 -0
  493. package/dist/infrastructure/transport-reservations.constants.js +3 -0
  494. package/dist/infrastructure/transport-reservations.constants.js.map +1 -0
  495. package/dist/infrastructure/transport-reservations.d.ts +1 -2
  496. package/dist/infrastructure/transport-reservations.js +18 -22
  497. package/dist/infrastructure/transport-reservations.js.map +1 -1
  498. package/dist/infrastructure/transport-reservations.types.d.ts +23 -2
  499. package/dist/infrastructure/transport-reservations.types.js.map +1 -1
  500. package/dist/infrastructure/trigger-server.js +12 -9
  501. package/dist/infrastructure/trigger-server.js.map +1 -1
  502. package/dist/infrastructure/trigger-server.types.d.ts +1 -1
  503. package/dist/infrastructure/trigger-server.types.js.map +1 -1
  504. package/dist/infrastructure/workflow-checkpoint.constants.d.ts +1 -0
  505. package/dist/infrastructure/workflow-checkpoint.constants.js +1 -0
  506. package/dist/infrastructure/workflow-checkpoint.constants.js.map +1 -1
  507. package/dist/infrastructure/workspace-lock.constants.d.ts +2 -1
  508. package/dist/infrastructure/workspace-lock.constants.js +2 -1
  509. package/dist/infrastructure/workspace-lock.constants.js.map +1 -1
  510. package/dist/infrastructure/workspace-lock.js +48 -10
  511. package/dist/infrastructure/workspace-lock.js.map +1 -1
  512. package/dist/providers/cloud-cache.js +14 -9
  513. package/dist/providers/cloud-cache.js.map +1 -1
  514. package/dist/providers/cloud-cache.types.d.ts +1 -0
  515. package/dist/providers/cloud-cache.types.js.map +1 -1
  516. package/dist/providers/cloud-files.js +3 -4
  517. package/dist/providers/cloud-files.js.map +1 -1
  518. package/dist/providers/cloud-input.constants.d.ts +2 -2
  519. package/dist/providers/cloud-input.constants.js +2 -1
  520. package/dist/providers/cloud-input.constants.js.map +1 -1
  521. package/dist/providers/cloud-input.d.ts +2 -2
  522. package/dist/providers/cloud-input.js +14 -3
  523. package/dist/providers/cloud-input.js.map +1 -1
  524. package/dist/providers/container-command.js +30 -5
  525. package/dist/providers/container-command.js.map +1 -1
  526. package/dist/providers/container-egress.constants.d.ts +7 -0
  527. package/dist/providers/container-egress.constants.js +8 -0
  528. package/dist/providers/container-egress.constants.js.map +1 -0
  529. package/dist/providers/container-egress.d.ts +7 -0
  530. package/dist/providers/container-egress.js +101 -0
  531. package/dist/providers/container-egress.js.map +1 -0
  532. package/dist/providers/container-files.js +10 -11
  533. package/dist/providers/container-files.js.map +1 -1
  534. package/dist/providers/container-mounts.js +3 -0
  535. package/dist/providers/container-mounts.js.map +1 -1
  536. package/dist/providers/container-network.js +16 -1
  537. package/dist/providers/container-network.js.map +1 -1
  538. package/dist/providers/container-plan.js +7 -10
  539. package/dist/providers/container-plan.js.map +1 -1
  540. package/dist/providers/container-recovery.js +8 -3
  541. package/dist/providers/container-recovery.js.map +1 -1
  542. package/dist/providers/container.js +36 -5
  543. package/dist/providers/container.js.map +1 -1
  544. package/dist/providers/container.types.d.ts +1 -0
  545. package/dist/providers/container.types.js.map +1 -1
  546. package/dist/providers/daytona-command.constants.js +4 -1
  547. package/dist/providers/daytona-command.constants.js.map +1 -1
  548. package/dist/providers/daytona-command.js +39 -16
  549. package/dist/providers/daytona-command.js.map +1 -1
  550. package/dist/providers/daytona-files.js +5 -5
  551. package/dist/providers/daytona-files.js.map +1 -1
  552. package/dist/providers/daytona-output.js +11 -3
  553. package/dist/providers/daytona-output.js.map +1 -1
  554. package/dist/providers/daytona.js +11 -9
  555. package/dist/providers/daytona.js.map +1 -1
  556. package/dist/providers/egress/address.d.ts +3 -0
  557. package/dist/providers/egress/address.js +26 -0
  558. package/dist/providers/egress/address.js.map +1 -0
  559. package/dist/providers/egress/egress.constants.d.ts +22 -0
  560. package/dist/providers/egress/egress.constants.js +23 -0
  561. package/dist/providers/egress/egress.constants.js.map +1 -0
  562. package/dist/providers/egress/egress.types.d.ts +11 -0
  563. package/dist/providers/egress/egress.types.js +2 -0
  564. package/dist/providers/egress/egress.types.js.map +1 -0
  565. package/dist/providers/egress/firewall.d.ts +3 -0
  566. package/dist/providers/egress/firewall.js +29 -0
  567. package/dist/providers/egress/firewall.js.map +1 -0
  568. package/dist/providers/egress/main.d.ts +1 -0
  569. package/dist/providers/egress/main.js +90 -0
  570. package/dist/providers/egress/main.js.map +1 -0
  571. package/dist/providers/egress/policy.d.ts +5 -0
  572. package/dist/providers/egress/policy.js +54 -0
  573. package/dist/providers/egress/policy.js.map +1 -0
  574. package/dist/providers/egress/proxy.d.ts +2 -0
  575. package/dist/providers/egress/proxy.js +129 -0
  576. package/dist/providers/egress/proxy.js.map +1 -0
  577. package/dist/providers/egress/tls-hello.d.ts +1 -0
  578. package/dist/providers/egress/tls-hello.js +75 -0
  579. package/dist/providers/egress/tls-hello.js.map +1 -0
  580. package/dist/providers/firecracker-command.js +12 -6
  581. package/dist/providers/firecracker-command.js.map +1 -1
  582. package/dist/providers/local-files.d.ts +2 -0
  583. package/dist/providers/local-files.js +24 -0
  584. package/dist/providers/local-files.js.map +1 -0
  585. package/dist/providers/local.js +2 -20
  586. package/dist/providers/local.js.map +1 -1
  587. package/dist/providers/vercel-command.js +33 -9
  588. package/dist/providers/vercel-command.js.map +1 -1
  589. package/dist/providers/vercel.js +11 -9
  590. package/dist/providers/vercel.js.map +1 -1
  591. package/dist/testing.d.ts +1 -1
  592. package/dist/testing.js +1 -1
  593. package/dist/testing.js.map +1 -1
  594. package/package.json +2 -2
  595. package/recipe-configuration.schema.json +2164 -35
  596. package/recipe.schema.json +2512 -404
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reservation-ledger.js","sourceRoot":"","sources":["../../src/infrastructure/reservation-ledger.ts"],"names":[],"mappings":"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","sourcesContent":["import { invariant } from \"../domain/errors.ts\";\nimport { reservationIdPattern } from \"./transport-reservations.constants.ts\";\nimport type {\n ReservationLedger,\n StorageReservationRecord,\n} from \"./transport-reservations.types.ts\";\n\nfunction record(value: unknown): StorageReservationRecord {\n invariant(\n value &&\n typeof value === \"object\" &&\n \"bytes\" in value &&\n typeof value.bytes === \"number\" &&\n Number.isSafeInteger(value.bytes) &&\n value.bytes >= 0,\n \"Invalid reservation entry\",\n );\n if (!(\"owner\" in value)) return { bytes: value.bytes };\n const owner = value.owner;\n invariant(\n owner &&\n typeof owner === \"object\" &&\n \"id\" in owner &&\n typeof owner.id === \"string\" &&\n reservationIdPattern.test(owner.id) &&\n \"host\" in owner &&\n typeof owner.host === \"string\" &&\n owner.host.length > 0 &&\n \"pid\" in owner &&\n typeof owner.pid === \"number\" &&\n Number.isSafeInteger(owner.pid) &&\n owner.pid > 0 &&\n \"createdAt\" in owner &&\n typeof owner.createdAt === \"string\" &&\n Number.isFinite(Date.parse(owner.createdAt)) &&\n \"repository\" in owner &&\n typeof owner.repository === \"string\",\n \"Invalid reservation owner\",\n );\n let identity;\n if (\"identity\" in owner) {\n const value = owner.identity;\n invariant(\n value &&\n typeof value === \"object\" &&\n \"host\" in value &&\n typeof value.host === \"string\" &&\n value.host.length > 0 &&\n \"boot\" in value &&\n typeof value.boot === \"string\" &&\n value.boot.length > 0 &&\n \"namespace\" in value &&\n typeof value.namespace === \"string\" &&\n value.namespace.length > 0 &&\n \"started\" in value &&\n typeof value.started === \"string\" &&\n /^\\d+$/.test(value.started),\n \"Invalid reservation identity\",\n );\n identity = {\n host: value.host,\n boot: value.boot,\n namespace: value.namespace,\n started: value.started,\n };\n }\n return {\n bytes: value.bytes,\n owner: {\n id: owner.id,\n host: owner.host,\n pid: owner.pid,\n createdAt: owner.createdAt,\n repository: owner.repository,\n ...(identity ? { identity } : {}),\n },\n };\n}\n\nexport function reservationLedger(value: unknown): ReservationLedger {\n if (value === undefined) return { format: 2, reservations: {} };\n invariant(\n value &&\n typeof value === \"object\" &&\n \"format\" in value &&\n (value.format === 1 || value.format === 2) &&\n \"reservations\" in value &&\n value.reservations &&\n typeof value.reservations === \"object\" &&\n !Array.isArray(value.reservations),\n \"Invalid reservation ledger\",\n );\n const legacy = value.format === 1;\n const entries = Object.entries(value.reservations).map(([id, entry]) => {\n invariant(reservationIdPattern.test(id), \"Invalid reservation identifier\");\n return [id, record(legacy ? { bytes: entry } : entry)];\n });\n return { format: 2, reservations: Object.fromEntries(entries) };\n}\n"]}
@@ -0,0 +1,4 @@
1
+ import type { Transport } from "../domain/transport.types.ts";
2
+ import type { StorageReservationInspection, StorageReservationRecoveryOptions } from "./transport-reservations.types.ts";
3
+ export declare function inspectStorageReservations(transporter: Transport): Promise<StorageReservationInspection>;
4
+ export declare function recoverStorageReservation(transporter: Transport, options: StorageReservationRecoveryOptions): Promise<void>;
@@ -0,0 +1,28 @@
1
+ import { invariant } from "../domain/errors.js";
2
+ import { TransportConflict } from "../domain/transport.js";
3
+ import { jsonBytes, jsonObject } from "./transport-json.js";
4
+ import { reservationLedger } from "./reservation-ledger.js";
5
+ import { reservationLedgerKey, reservationIdPattern, } from "./transport-reservations.constants.js";
6
+ export async function inspectStorageReservations(transporter) {
7
+ const current = await transporter.read(reservationLedgerKey);
8
+ const ledger = reservationLedger(jsonObject(current));
9
+ return {
10
+ ...(current ? { revision: current.revision } : {}),
11
+ reservations: ledger.reservations,
12
+ };
13
+ }
14
+ export async function recoverStorageReservation(transporter, options) {
15
+ invariant(options.processesStopped === true, "Reservation recovery requires explicit stopped-owner authorization");
16
+ invariant(reservationIdPattern.test(options.id) &&
17
+ typeof options.revision === "string" &&
18
+ options.revision.length > 0, "Reservation recovery requires an identifier and inspected revision");
19
+ const current = await transporter.read(reservationLedgerKey);
20
+ if (!current || current.revision !== options.revision)
21
+ throw new TransportConflict(reservationLedgerKey);
22
+ const ledger = reservationLedger(jsonObject(current));
23
+ invariant(Object.hasOwn(ledger.reservations, options.id), "Reservation no longer exists");
24
+ const reservations = { ...ledger.reservations };
25
+ delete reservations[options.id];
26
+ await transporter.write(reservationLedgerKey, jsonBytes({ format: 2, reservations }), { ifRevision: current.revision });
27
+ }
28
+ //# sourceMappingURL=reservation-recovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reservation-recovery.js","sourceRoot":"","sources":["../../src/infrastructure/reservation-recovery.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAE3D,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EACL,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,uCAAuC,CAAC;AAM/C,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,WAAsB;IAEtB,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;IAC7D,MAAM,MAAM,GAAG,iBAAiB,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;IACtD,OAAO;QACL,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAClD,YAAY,EAAE,MAAM,CAAC,YAAY;KAClC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,WAAsB,EACtB,OAA0C;IAE1C,SAAS,CACP,OAAO,CAAC,gBAAgB,KAAK,IAAI,EACjC,oEAAoE,CACrE,CAAC;IACF,SAAS,CACP,oBAAoB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QACnC,OAAO,OAAO,CAAC,QAAQ,KAAK,QAAQ;QACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAC7B,oEAAoE,CACrE,CAAC;IACF,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;IAC7D,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,QAAQ;QACnD,MAAM,IAAI,iBAAiB,CAAC,oBAAoB,CAAC,CAAC;IACpD,MAAM,MAAM,GAAG,iBAAiB,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;IACtD,SAAS,CACP,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,OAAO,CAAC,EAAE,CAAC,EAC9C,8BAA8B,CAC/B,CAAC;IACF,MAAM,YAAY,GAAG,EAAE,GAAG,MAAM,CAAC,YAAY,EAAE,CAAC;IAChD,OAAO,YAAY,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAChC,MAAM,WAAW,CAAC,KAAK,CACrB,oBAAoB,EACpB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EACtC,EAAE,UAAU,EAAE,OAAO,CAAC,QAAQ,EAAE,CACjC,CAAC;AACJ,CAAC","sourcesContent":["import { invariant } from \"../domain/errors.ts\";\nimport { TransportConflict } from \"../domain/transport.ts\";\nimport type { Transport } from \"../domain/transport.types.ts\";\nimport { jsonBytes, jsonObject } from \"./transport-json.ts\";\nimport { reservationLedger } from \"./reservation-ledger.ts\";\nimport {\n reservationLedgerKey,\n reservationIdPattern,\n} from \"./transport-reservations.constants.ts\";\nimport type {\n StorageReservationInspection,\n StorageReservationRecoveryOptions,\n} from \"./transport-reservations.types.ts\";\n\nexport async function inspectStorageReservations(\n transporter: Transport,\n): Promise<StorageReservationInspection> {\n const current = await transporter.read(reservationLedgerKey);\n const ledger = reservationLedger(jsonObject(current));\n return {\n ...(current ? { revision: current.revision } : {}),\n reservations: ledger.reservations,\n };\n}\n\nexport async function recoverStorageReservation(\n transporter: Transport,\n options: StorageReservationRecoveryOptions,\n): Promise<void> {\n invariant(\n options.processesStopped === true,\n \"Reservation recovery requires explicit stopped-owner authorization\",\n );\n invariant(\n reservationIdPattern.test(options.id) &&\n typeof options.revision === \"string\" &&\n options.revision.length > 0,\n \"Reservation recovery requires an identifier and inspected revision\",\n );\n const current = await transporter.read(reservationLedgerKey);\n if (!current || current.revision !== options.revision)\n throw new TransportConflict(reservationLedgerKey);\n const ledger = reservationLedger(jsonObject(current));\n invariant(\n Object.hasOwn(ledger.reservations, options.id),\n \"Reservation no longer exists\",\n );\n const reservations = { ...ledger.reservations };\n delete reservations[options.id];\n await transporter.write(\n reservationLedgerKey,\n jsonBytes({ format: 2, reservations }),\n { ifRevision: current.revision },\n );\n}\n"]}
@@ -0,0 +1,3 @@
1
+ import type { S3TombstonePruneOptions } from "./s3-tombstones.types.ts";
2
+ /** Remove tombstones only while every writer using the selected prefix is stopped. */
3
+ export declare function pruneS3Tombstones(options: S3TombstonePruneOptions): Promise<number>;
@@ -0,0 +1,46 @@
1
+ import { transportDefaults } from "./transport.constants.js";
2
+ import { DeleteObjectCommand, HeadObjectCommand, ListObjectsV2Command, } from "@aws-sdk/client-s3";
3
+ import { invariant } from "../domain/errors.js";
4
+ import { transportKey } from "../domain/transport.js";
5
+ import { s3TombstoneMetadata } from "./s3-transport.constants.js";
6
+ /** Remove tombstones only while every writer using the selected prefix is stopped. */
7
+ export async function pruneS3Tombstones(options) {
8
+ invariant(options.writersStopped === true, "S3 tombstone pruning requires writersStopped: true");
9
+ invariant(options.bucket.trim(), "S3 bucket must not be empty");
10
+ const prefix = `${transportKey(options.prefix.replace(/\/$/, ""))}/`;
11
+ const settings = options.signal ? { abortSignal: options.signal } : {};
12
+ let removed = 0;
13
+ let cursor;
14
+ const seen = new Set();
15
+ const keys = [];
16
+ do {
17
+ options.signal?.throwIfAborted();
18
+ const page = await options.client.send(new ListObjectsV2Command({
19
+ Bucket: options.bucket,
20
+ Prefix: prefix,
21
+ ...(cursor ? { ContinuationToken: cursor } : {}),
22
+ }), settings);
23
+ for (const item of page.Contents ?? []) {
24
+ options.signal?.throwIfAborted();
25
+ invariant(typeof item.Key === "string" && item.Key.startsWith(prefix), "Invalid S3 listing entry");
26
+ invariant(keys.length < transportDefaults.maxEntries, "Too many objects for offline pruning; select a narrower prefix");
27
+ keys.push(item.Key);
28
+ }
29
+ cursor = page.IsTruncated ? page.NextContinuationToken : undefined;
30
+ invariant(!page.IsTruncated || (cursor && !seen.has(cursor)), "Invalid S3 pagination cursor");
31
+ if (cursor)
32
+ seen.add(cursor);
33
+ } while (cursor);
34
+ for (const key of keys) {
35
+ options.signal?.throwIfAborted();
36
+ const target = { Bucket: options.bucket, Key: key };
37
+ const current = await options.client.send(new HeadObjectCommand(target), settings);
38
+ if (current.Metadata?.[s3TombstoneMetadata] !== "true")
39
+ continue;
40
+ // Quiescence is required because compatible stores may ignore conditional deletes.
41
+ await options.client.send(new DeleteObjectCommand(target), settings);
42
+ removed++;
43
+ }
44
+ return removed;
45
+ }
46
+ //# sourceMappingURL=s3-tombstones.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"s3-tombstones.js","sourceRoot":"","sources":["../../src/infrastructure/s3-tombstones.ts"],"names":[],"mappings":"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","sourcesContent":["import { transportDefaults } from \"./transport.constants.ts\";\nimport {\n DeleteObjectCommand,\n HeadObjectCommand,\n ListObjectsV2Command,\n} from \"@aws-sdk/client-s3\";\nimport { invariant } from \"../domain/errors.ts\";\nimport { transportKey } from \"../domain/transport.ts\";\nimport { s3TombstoneMetadata } from \"./s3-transport.constants.ts\";\nimport type { S3TombstonePruneOptions } from \"./s3-tombstones.types.ts\";\n\n/** Remove tombstones only while every writer using the selected prefix is stopped. */\nexport async function pruneS3Tombstones(\n options: S3TombstonePruneOptions,\n): Promise<number> {\n invariant(\n options.writersStopped === true,\n \"S3 tombstone pruning requires writersStopped: true\",\n );\n invariant(options.bucket.trim(), \"S3 bucket must not be empty\");\n const prefix = `${transportKey(options.prefix.replace(/\\/$/, \"\"))}/`;\n const settings = options.signal ? { abortSignal: options.signal } : {};\n let removed = 0;\n let cursor: string | undefined;\n const seen = new Set<string>();\n const keys: string[] = [];\n do {\n options.signal?.throwIfAborted();\n const page = await options.client.send(\n new ListObjectsV2Command({\n Bucket: options.bucket,\n Prefix: prefix,\n ...(cursor ? { ContinuationToken: cursor } : {}),\n }),\n settings,\n );\n for (const item of page.Contents ?? []) {\n options.signal?.throwIfAborted();\n invariant(\n typeof item.Key === \"string\" && item.Key.startsWith(prefix),\n \"Invalid S3 listing entry\",\n );\n invariant(\n keys.length < transportDefaults.maxEntries,\n \"Too many objects for offline pruning; select a narrower prefix\",\n );\n keys.push(item.Key);\n }\n cursor = page.IsTruncated ? page.NextContinuationToken : undefined;\n invariant(\n !page.IsTruncated || (cursor && !seen.has(cursor)),\n \"Invalid S3 pagination cursor\",\n );\n if (cursor) seen.add(cursor);\n } while (cursor);\n for (const key of keys) {\n options.signal?.throwIfAborted();\n const target = { Bucket: options.bucket, Key: key };\n const current = await options.client.send(\n new HeadObjectCommand(target),\n settings,\n );\n if (current.Metadata?.[s3TombstoneMetadata] !== \"true\") continue;\n // Quiescence is required because compatible stores may ignore conditional deletes.\n await options.client.send(new DeleteObjectCommand(target), settings);\n removed++;\n }\n return removed;\n}\n"]}
@@ -0,0 +1,6 @@
1
+ import type { S3TransportOptions } from "./s3-transport.types.ts";
2
+ export interface S3TombstonePruneOptions extends Pick<S3TransportOptions, "client" | "bucket"> {
3
+ readonly prefix: string;
4
+ readonly writersStopped: true;
5
+ readonly signal?: AbortSignal;
6
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=s3-tombstones.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"s3-tombstones.types.js","sourceRoot":"","sources":["../../src/infrastructure/s3-tombstones.types.ts"],"names":[],"mappings":"","sourcesContent":["import type { S3TransportOptions } from \"./s3-transport.types.ts\";\n\nexport interface S3TombstonePruneOptions extends Pick<\n S3TransportOptions,\n \"client\" | \"bucket\"\n> {\n readonly prefix: string;\n readonly writersStopped: true;\n readonly signal?: AbortSignal;\n}\n"]}
@@ -2,3 +2,5 @@ import type { Transport } from "../domain/transport.types.ts";
2
2
  import type { S3TransportOptions } from "./s3-transport.types.ts";
3
3
  export type { S3TransportOptions } from "./s3-transport.types.ts";
4
4
  export declare function createS3Transport(options: S3TransportOptions): Transport;
5
+ export { pruneS3Tombstones } from "./s3-tombstones.ts";
6
+ export type { S3TombstonePruneOptions } from "./s3-tombstones.types.ts";
@@ -87,7 +87,7 @@ export function createS3Transport(options) {
87
87
  settings.signal?.throwIfAborted();
88
88
  const { entry, data } = encodeObject(key, bytes);
89
89
  let revision = settings.ifRevision;
90
- if (deleteMode === "tombstone" && revision === null) {
90
+ if (revision === null) {
91
91
  const current = await head(key, settings.signal);
92
92
  if (current?.Metadata?.[s3TombstoneMetadata] === "true") {
93
93
  if (!current.ETag)
@@ -162,7 +162,8 @@ export function createS3Transport(options) {
162
162
  item.Size < transportDefaults.headerBytes)
163
163
  throw new Error("Invalid S3 listing entry");
164
164
  const key = transportKey(item.Key.slice(prefix.length));
165
- if (deleteMode === "tombstone") {
165
+ if (deleteMode === "tombstone" ||
166
+ item.Size === transportDefaults.headerBytes) {
166
167
  const current = await head(key, settings.signal);
167
168
  if (!current || current.Metadata?.[s3TombstoneMetadata] === "true")
168
169
  continue;
@@ -195,4 +196,5 @@ export function createS3Transport(options) {
195
196
  },
196
197
  };
197
198
  }
199
+ export { pruneS3Tombstones } from "./s3-tombstones.js";
198
200
  //# sourceMappingURL=s3-transport.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"s3-transport.js","sourceRoot":"","sources":["../../src/infrastructure/s3-transport.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n GetObjectCommand,\n HeadObjectCommand,\n PutObjectCommand,\n DeleteObjectCommand,\n ListObjectsV2Command,\n} from \"@aws-sdk/client-s3\";\nimport {\n TransportConflict,\n transportKey,\n transportCondition,\n} from \"../domain/transport.ts\";\nimport type { Transport } from \"../domain/transport.types.ts\";\nimport type { S3TransportOptions } from \"./s3-transport.types.ts\";\nimport { decodeObject, encodeObject, readLimit } from \"./transport-envelope.ts\";\nimport { transportDefaults } from \"./transport.constants.ts\";\nimport { s3DeleteMode, s3TombstoneMetadata } from \"./s3-transport.constants.ts\";\n\nexport type { S3TransportOptions } from \"./s3-transport.types.ts\";\n\nfunction status(error: unknown): number | undefined {\n if (!error || typeof error !== \"object\" || !(\"$metadata\" in error))\n return undefined;\n const meta = error.$metadata;\n return meta &&\n typeof meta === \"object\" &&\n \"httpStatusCode\" in meta &&\n typeof meta.httpStatusCode === \"number\"\n ? meta.httpStatusCode\n : undefined;\n}\n\nexport function createS3Transport(options: S3TransportOptions): Transport {\n if (!options.bucket.trim()) throw new Error(\"S3 bucket must not be empty\");\n const deleteMode = options.deleteMode ?? s3DeleteMode;\n if (deleteMode !== \"conditional\" && deleteMode !== \"tombstone\")\n throw new Error(\"Invalid S3 delete mode\");\n const prefix = options.prefix\n ? `${transportKey(options.prefix.replace(/\\/$/, \"\"))}/`\n : \"\";\n const target = (key: string) => ({\n Bucket: options.bucket,\n Key: `${prefix}${transportKey(key)}`,\n });\n async function head(key: string, signal?: AbortSignal) {\n try {\n return await options.client.send(\n new HeadObjectCommand(target(key)),\n signal ? { abortSignal: signal } : {},\n );\n } catch (error) {\n if (status(error) === 404) return undefined;\n throw error;\n }\n }\n return {\n name: \"s3\",\n async read(key, settings = {}) {\n settings.signal?.throwIfAborted();\n const maxBytes = readLimit(settings.maxBytes);\n try {\n const value = await options.client.send(\n new GetObjectCommand(target(key)),\n settings.signal ? { abortSignal: settings.signal } : {},\n );\n if (!value.Body) throw new Error(\"Missing S3 object body\");\n const reader = value.Body.transformToWebStream().getReader();\n try {\n if (value.Metadata?.[s3TombstoneMetadata] === \"true\")\n return undefined;\n if (\n !value.ETag ||\n (value.ContentLength ?? Infinity) >\n maxBytes + transportDefaults.headerBytes\n )\n throw new Error(\"Invalid or oversized S3 object\");\n const chunks: Uint8Array[] = [];\n let size = 0;\n for (;;) {\n settings.signal?.throwIfAborted();\n const chunk = await reader.read();\n if (chunk.done) break;\n size += chunk.value.byteLength;\n if (size > maxBytes + transportDefaults.headerBytes)\n throw new Error(\"S3 object exceeds byte limit\");\n chunks.push(chunk.value);\n }\n return {\n ...decodeObject(Buffer.concat(chunks), key, maxBytes),\n revision: value.ETag,\n };\n } finally {\n await reader.cancel().catch(() => {});\n reader.releaseLock();\n }\n } catch (error) {\n if (status(error) === 404) return undefined;\n throw error;\n }\n },\n async write(key, bytes, settings) {\n transportCondition(settings.ifRevision);\n settings.signal?.throwIfAborted();\n const { entry, data } = encodeObject(key, bytes);\n let revision = settings.ifRevision;\n if (deleteMode === \"tombstone\" && revision === null) {\n const current = await head(key, settings.signal);\n if (current?.Metadata?.[s3TombstoneMetadata] === \"true\") {\n if (!current.ETag) throw new Error(\"Missing S3 tombstone revision\");\n revision = current.ETag;\n }\n }\n try {\n const value = await options.client.send(\n new PutObjectCommand({\n ...target(key),\n Body: data,\n ...(revision === null\n ? { IfNoneMatch: \"*\" }\n : { IfMatch: revision }),\n }),\n settings.signal ? { abortSignal: settings.signal } : {},\n );\n if (!value.ETag) throw new Error(\"Missing S3 revision after write\");\n return { ...entry, revision: value.ETag };\n } catch (error) {\n if ([409, 412].includes(status(error) ?? 0))\n throw new TransportConflict(key);\n throw error;\n }\n },\n async remove(key, settings) {\n transportCondition(settings.ifRevision);\n settings.signal?.throwIfAborted();\n if (settings.ifRevision === null)\n throw new Error(\"S3 removal requires an existing revision\");\n try {\n if (deleteMode === \"tombstone\") {\n const { data } = encodeObject(key, new Uint8Array());\n const value = await options.client.send(\n new PutObjectCommand({\n ...target(key),\n IfMatch: settings.ifRevision,\n Body: data,\n Metadata: { [s3TombstoneMetadata]: \"true\" },\n }),\n settings.signal ? { abortSignal: settings.signal } : {},\n );\n if (!value.ETag) throw new Error(\"Missing S3 revision after removal\");\n return;\n }\n await options.client.send(\n new DeleteObjectCommand({\n ...target(key),\n IfMatch: settings.ifRevision,\n }),\n settings.signal ? { abortSignal: settings.signal } : {},\n );\n } catch (error) {\n if ([404, 409, 412].includes(status(error) ?? 0))\n throw new TransportConflict(key);\n throw error;\n }\n },\n async *list(filter = \"\", settings = {}) {\n if (filter) transportKey(filter.replace(/\\/$/, \"\"));\n let cursor: string | undefined;\n const seen = new Set<string>();\n do {\n settings.signal?.throwIfAborted();\n const page = await options.client.send(\n new ListObjectsV2Command({\n Bucket: options.bucket,\n Prefix: prefix + filter,\n ...(cursor ? { ContinuationToken: cursor } : {}),\n }),\n settings.signal ? { abortSignal: settings.signal } : {},\n );\n for (const item of page.Contents ?? []) {\n if (\n !item.Key?.startsWith(prefix + filter) ||\n !item.ETag ||\n !item.LastModified ||\n item.Size === undefined ||\n item.Size < transportDefaults.headerBytes\n )\n throw new Error(\"Invalid S3 listing entry\");\n const key = transportKey(item.Key.slice(prefix.length));\n if (deleteMode === \"tombstone\") {\n const current = await head(key, settings.signal);\n if (!current || current.Metadata?.[s3TombstoneMetadata] === \"true\")\n continue;\n if (\n !current.ETag ||\n !current.LastModified ||\n current.ContentLength === undefined ||\n current.ContentLength < transportDefaults.headerBytes\n )\n throw new Error(\"Invalid S3 listing object\");\n yield {\n key,\n revision: current.ETag,\n size: current.ContentLength - transportDefaults.headerBytes,\n modifiedAt: current.LastModified.toISOString(),\n };\n continue;\n }\n yield {\n key,\n revision: item.ETag,\n size: item.Size - transportDefaults.headerBytes,\n modifiedAt: item.LastModified.toISOString(),\n };\n }\n cursor = page.IsTruncated ? page.NextContinuationToken : undefined;\n if (page.IsTruncated && (!cursor || seen.has(cursor)))\n throw new Error(\"Invalid S3 pagination cursor\");\n if (cursor) seen.add(cursor);\n } while (cursor);\n },\n };\n}\n"]}
1
+ {"version":3,"file":"s3-transport.js","sourceRoot":"","sources":["../../src/infrastructure/s3-transport.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n GetObjectCommand,\n HeadObjectCommand,\n PutObjectCommand,\n DeleteObjectCommand,\n ListObjectsV2Command,\n} from \"@aws-sdk/client-s3\";\nimport {\n TransportConflict,\n transportKey,\n transportCondition,\n} from \"../domain/transport.ts\";\nimport type { Transport } from \"../domain/transport.types.ts\";\nimport type { S3TransportOptions } from \"./s3-transport.types.ts\";\nimport { decodeObject, encodeObject, readLimit } from \"./transport-envelope.ts\";\nimport { transportDefaults } from \"./transport.constants.ts\";\nimport { s3DeleteMode, s3TombstoneMetadata } from \"./s3-transport.constants.ts\";\n\nexport type { S3TransportOptions } from \"./s3-transport.types.ts\";\n\nfunction status(error: unknown): number | undefined {\n if (!error || typeof error !== \"object\" || !(\"$metadata\" in error))\n return undefined;\n const meta = error.$metadata;\n return meta &&\n typeof meta === \"object\" &&\n \"httpStatusCode\" in meta &&\n typeof meta.httpStatusCode === \"number\"\n ? meta.httpStatusCode\n : undefined;\n}\n\nexport function createS3Transport(options: S3TransportOptions): Transport {\n if (!options.bucket.trim()) throw new Error(\"S3 bucket must not be empty\");\n const deleteMode = options.deleteMode ?? s3DeleteMode;\n if (deleteMode !== \"conditional\" && deleteMode !== \"tombstone\")\n throw new Error(\"Invalid S3 delete mode\");\n const prefix = options.prefix\n ? `${transportKey(options.prefix.replace(/\\/$/, \"\"))}/`\n : \"\";\n const target = (key: string) => ({\n Bucket: options.bucket,\n Key: `${prefix}${transportKey(key)}`,\n });\n async function head(key: string, signal?: AbortSignal) {\n try {\n return await options.client.send(\n new HeadObjectCommand(target(key)),\n signal ? { abortSignal: signal } : {},\n );\n } catch (error) {\n if (status(error) === 404) return undefined;\n throw error;\n }\n }\n return {\n name: \"s3\",\n async read(key, settings = {}) {\n settings.signal?.throwIfAborted();\n const maxBytes = readLimit(settings.maxBytes);\n try {\n const value = await options.client.send(\n new GetObjectCommand(target(key)),\n settings.signal ? { abortSignal: settings.signal } : {},\n );\n if (!value.Body) throw new Error(\"Missing S3 object body\");\n const reader = value.Body.transformToWebStream().getReader();\n try {\n if (value.Metadata?.[s3TombstoneMetadata] === \"true\")\n return undefined;\n if (\n !value.ETag ||\n (value.ContentLength ?? Infinity) >\n maxBytes + transportDefaults.headerBytes\n )\n throw new Error(\"Invalid or oversized S3 object\");\n const chunks: Uint8Array[] = [];\n let size = 0;\n for (;;) {\n settings.signal?.throwIfAborted();\n const chunk = await reader.read();\n if (chunk.done) break;\n size += chunk.value.byteLength;\n if (size > maxBytes + transportDefaults.headerBytes)\n throw new Error(\"S3 object exceeds byte limit\");\n chunks.push(chunk.value);\n }\n return {\n ...decodeObject(Buffer.concat(chunks), key, maxBytes),\n revision: value.ETag,\n };\n } finally {\n await reader.cancel().catch(() => {});\n reader.releaseLock();\n }\n } catch (error) {\n if (status(error) === 404) return undefined;\n throw error;\n }\n },\n async write(key, bytes, settings) {\n transportCondition(settings.ifRevision);\n settings.signal?.throwIfAborted();\n const { entry, data } = encodeObject(key, bytes);\n let revision = settings.ifRevision;\n if (revision === null) {\n const current = await head(key, settings.signal);\n if (current?.Metadata?.[s3TombstoneMetadata] === \"true\") {\n if (!current.ETag) throw new Error(\"Missing S3 tombstone revision\");\n revision = current.ETag;\n }\n }\n try {\n const value = await options.client.send(\n new PutObjectCommand({\n ...target(key),\n Body: data,\n ...(revision === null\n ? { IfNoneMatch: \"*\" }\n : { IfMatch: revision }),\n }),\n settings.signal ? { abortSignal: settings.signal } : {},\n );\n if (!value.ETag) throw new Error(\"Missing S3 revision after write\");\n return { ...entry, revision: value.ETag };\n } catch (error) {\n if ([409, 412].includes(status(error) ?? 0))\n throw new TransportConflict(key);\n throw error;\n }\n },\n async remove(key, settings) {\n transportCondition(settings.ifRevision);\n settings.signal?.throwIfAborted();\n if (settings.ifRevision === null)\n throw new Error(\"S3 removal requires an existing revision\");\n try {\n if (deleteMode === \"tombstone\") {\n const { data } = encodeObject(key, new Uint8Array());\n const value = await options.client.send(\n new PutObjectCommand({\n ...target(key),\n IfMatch: settings.ifRevision,\n Body: data,\n Metadata: { [s3TombstoneMetadata]: \"true\" },\n }),\n settings.signal ? { abortSignal: settings.signal } : {},\n );\n if (!value.ETag) throw new Error(\"Missing S3 revision after removal\");\n return;\n }\n await options.client.send(\n new DeleteObjectCommand({\n ...target(key),\n IfMatch: settings.ifRevision,\n }),\n settings.signal ? { abortSignal: settings.signal } : {},\n );\n } catch (error) {\n if ([404, 409, 412].includes(status(error) ?? 0))\n throw new TransportConflict(key);\n throw error;\n }\n },\n async *list(filter = \"\", settings = {}) {\n if (filter) transportKey(filter.replace(/\\/$/, \"\"));\n let cursor: string | undefined;\n const seen = new Set<string>();\n do {\n settings.signal?.throwIfAborted();\n const page = await options.client.send(\n new ListObjectsV2Command({\n Bucket: options.bucket,\n Prefix: prefix + filter,\n ...(cursor ? { ContinuationToken: cursor } : {}),\n }),\n settings.signal ? { abortSignal: settings.signal } : {},\n );\n for (const item of page.Contents ?? []) {\n if (\n !item.Key?.startsWith(prefix + filter) ||\n !item.ETag ||\n !item.LastModified ||\n item.Size === undefined ||\n item.Size < transportDefaults.headerBytes\n )\n throw new Error(\"Invalid S3 listing entry\");\n const key = transportKey(item.Key.slice(prefix.length));\n if (\n deleteMode === \"tombstone\" ||\n item.Size === transportDefaults.headerBytes\n ) {\n const current = await head(key, settings.signal);\n if (!current || current.Metadata?.[s3TombstoneMetadata] === \"true\")\n continue;\n if (\n !current.ETag ||\n !current.LastModified ||\n current.ContentLength === undefined ||\n current.ContentLength < transportDefaults.headerBytes\n )\n throw new Error(\"Invalid S3 listing object\");\n yield {\n key,\n revision: current.ETag,\n size: current.ContentLength - transportDefaults.headerBytes,\n modifiedAt: current.LastModified.toISOString(),\n };\n continue;\n }\n yield {\n key,\n revision: item.ETag,\n size: item.Size - transportDefaults.headerBytes,\n modifiedAt: item.LastModified.toISOString(),\n };\n }\n cursor = page.IsTruncated ? page.NextContinuationToken : undefined;\n if (page.IsTruncated && (!cursor || seen.has(cursor)))\n throw new Error(\"Invalid S3 pagination cursor\");\n if (cursor) seen.add(cursor);\n } while (cursor);\n },\n };\n}\n\nexport { pruneS3Tombstones } from \"./s3-tombstones.ts\";\nexport type { S3TombstonePruneOptions } from \"./s3-tombstones.types.ts\";\n"]}
@@ -18,7 +18,6 @@ export async function* serverSentEvents(body, maxBytes, onChunk = () => undefine
18
18
  for (;;) {
19
19
  const { done, value } = await reader.read();
20
20
  if (!done) {
21
- onChunk();
22
21
  size += value.byteLength;
23
22
  if (size > maxBytes)
24
23
  throw new OutpostError("response", "Model response exceeds maxResponseBytes");
@@ -33,8 +32,10 @@ export async function* serverSentEvents(body, maxBytes, onChunk = () => undefine
33
32
  for (const line of lines) {
34
33
  if (line === "") {
35
34
  const complete = dispatch();
36
- if (complete)
35
+ if (complete) {
36
+ onChunk();
37
37
  yield complete;
38
+ }
38
39
  continue;
39
40
  }
40
41
  if (line.startsWith(":"))
@@ -51,8 +52,10 @@ export async function* serverSentEvents(body, maxBytes, onChunk = () => undefine
51
52
  if (!done)
52
53
  continue;
53
54
  const complete = dispatch();
54
- if (complete)
55
+ if (complete) {
56
+ onChunk();
55
57
  yield complete;
58
+ }
56
59
  return;
57
60
  }
58
61
  }
@@ -1 +1 @@
1
- {"version":3,"file":"sse.js","sourceRoot":"","sources":["../../src/infrastructure/sse.ts"],"names":[],"mappings":"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","sourcesContent":["import { OutpostError } from \"../domain/errors.ts\";\nimport type { ServerSentEvent } from \"./sse.types.ts\";\n\nexport async function* serverSentEvents(\n body: ReadableStream<Uint8Array>,\n maxBytes: number,\n onChunk: () => void = () => undefined,\n): AsyncGenerator<ServerSentEvent> {\n const decoder = new TextDecoder();\n const reader = body.getReader();\n let buffer = \"\";\n let size = 0;\n let event: string | undefined;\n let data: string[] = [];\n const dispatch = (): ServerSentEvent | undefined => {\n const complete = data.length\n ? { ...(event ? { event } : {}), data: data.join(\"\\n\") }\n : undefined;\n event = undefined;\n data = [];\n return complete;\n };\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (!done) {\n onChunk();\n size += value.byteLength;\n if (size > maxBytes)\n throw new OutpostError(\n \"response\",\n \"Model response exceeds maxResponseBytes\",\n );\n }\n buffer += done\n ? decoder.decode()\n : decoder.decode(value, { stream: true });\n // A CR at the end of a chunk may be the first half of a CRLF terminator.\n const carriage = !done && buffer.endsWith(\"\\r\");\n const lines = (carriage ? buffer.slice(0, -1) : buffer).split(\n /\\r\\n|\\r|\\n/,\n );\n buffer = done ? \"\" : `${lines.pop()!}${carriage ? \"\\r\" : \"\"}`;\n for (const line of lines) {\n if (line === \"\") {\n const complete = dispatch();\n if (complete) yield complete;\n continue;\n }\n if (line.startsWith(\":\")) continue;\n const colon = line.indexOf(\":\");\n const field = colon < 0 ? line : line.slice(0, colon);\n const raw = colon < 0 ? \"\" : line.slice(colon + 1);\n const value = raw.startsWith(\" \") ? raw.slice(1) : raw;\n if (field === \"event\") event = value;\n if (field === \"data\") data.push(value);\n }\n if (!done) continue;\n const complete = dispatch();\n if (complete) yield complete;\n return;\n }\n } finally {\n await reader.cancel().catch(() => undefined);\n reader.releaseLock();\n }\n}\n"]}
1
+ {"version":3,"file":"sse.js","sourceRoot":"","sources":["../../src/infrastructure/sse.ts"],"names":[],"mappings":"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","sourcesContent":["import { OutpostError } from \"../domain/errors.ts\";\nimport type { ServerSentEvent } from \"./sse.types.ts\";\n\nexport async function* serverSentEvents(\n body: ReadableStream<Uint8Array>,\n maxBytes: number,\n onChunk: () => void = () => undefined,\n): AsyncGenerator<ServerSentEvent> {\n const decoder = new TextDecoder();\n const reader = body.getReader();\n let buffer = \"\";\n let size = 0;\n let event: string | undefined;\n let data: string[] = [];\n const dispatch = (): ServerSentEvent | undefined => {\n const complete = data.length\n ? { ...(event ? { event } : {}), data: data.join(\"\\n\") }\n : undefined;\n event = undefined;\n data = [];\n return complete;\n };\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (!done) {\n size += value.byteLength;\n if (size > maxBytes)\n throw new OutpostError(\n \"response\",\n \"Model response exceeds maxResponseBytes\",\n );\n }\n buffer += done\n ? decoder.decode()\n : decoder.decode(value, { stream: true });\n // A CR at the end of a chunk may be the first half of a CRLF terminator.\n const carriage = !done && buffer.endsWith(\"\\r\");\n const lines = (carriage ? buffer.slice(0, -1) : buffer).split(\n /\\r\\n|\\r|\\n/,\n );\n buffer = done ? \"\" : `${lines.pop()!}${carriage ? \"\\r\" : \"\"}`;\n for (const line of lines) {\n if (line === \"\") {\n const complete = dispatch();\n if (complete) {\n onChunk();\n yield complete;\n }\n continue;\n }\n if (line.startsWith(\":\")) continue;\n const colon = line.indexOf(\":\");\n const field = colon < 0 ? line : line.slice(0, colon);\n const raw = colon < 0 ? \"\" : line.slice(colon + 1);\n const value = raw.startsWith(\" \") ? raw.slice(1) : raw;\n if (field === \"event\") event = value;\n if (field === \"data\") data.push(value);\n }\n if (!done) continue;\n const complete = dispatch();\n if (complete) {\n onChunk();\n yield complete;\n }\n return;\n }\n } finally {\n await reader.cancel().catch(() => undefined);\n reader.releaseLock();\n }\n}\n"]}
@@ -0,0 +1,3 @@
1
+ import type { BullMQTaskQueueOptions } from "./task-queue-bullmq.types.ts";
2
+ import type { BullMQConnection } from "./task-queue-bullmq-connection.types.ts";
3
+ export declare function createBullMQConnection(options: BullMQTaskQueueOptions["connection"]): BullMQConnection;
@@ -0,0 +1,32 @@
1
+ import { bullMQConnectionTimeoutMs, bullMQConnectionRetries, bullMQRetryDelayMs, bullMQMaxRetryDelayMs, } from "./task-queue-bullmq.constants.js";
2
+ export function createBullMQConnection(options) {
3
+ let ready = false;
4
+ let closed = false;
5
+ const retry = options.retryStrategy === undefined
6
+ ? (attempt) => {
7
+ if (!ready && attempt > bullMQConnectionRetries)
8
+ return null;
9
+ return Math.min(attempt * bullMQRetryDelayMs, bullMQMaxRetryDelayMs);
10
+ }
11
+ : options.retryStrategy;
12
+ return {
13
+ options: {
14
+ connectTimeout: bullMQConnectionTimeoutMs,
15
+ commandTimeout: bullMQConnectionTimeoutMs,
16
+ ...options,
17
+ retryStrategy: (attempt) => {
18
+ if (closed || !retry)
19
+ return null;
20
+ return retry(attempt);
21
+ },
22
+ maxRetriesPerRequest: 1,
23
+ },
24
+ connected() {
25
+ ready = true;
26
+ },
27
+ close() {
28
+ closed = true;
29
+ },
30
+ };
31
+ }
32
+ //# sourceMappingURL=task-queue-bullmq-connection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-queue-bullmq-connection.js","sourceRoot":"","sources":["../../src/infrastructure/task-queue-bullmq-connection.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,yBAAyB,EACzB,uBAAuB,EACvB,kBAAkB,EAClB,qBAAqB,GACtB,MAAM,kCAAkC,CAAC;AAE1C,MAAM,UAAU,sBAAsB,CACpC,OAA6C;IAE7C,IAAI,KAAK,GAAG,KAAK,CAAC;IAClB,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,MAAM,KAAK,GACT,OAAO,CAAC,aAAa,KAAK,SAAS;QACjC,CAAC,CAAC,CAAC,OAAe,EAAE,EAAE;YAClB,IAAI,CAAC,KAAK,IAAI,OAAO,GAAG,uBAAuB;gBAAE,OAAO,IAAI,CAAC;YAC7D,OAAO,IAAI,CAAC,GAAG,CAAC,OAAO,GAAG,kBAAkB,EAAE,qBAAqB,CAAC,CAAC;QACvE,CAAC;QACH,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC;IAC5B,OAAO;QACL,OAAO,EAAE;YACP,cAAc,EAAE,yBAAyB;YACzC,cAAc,EAAE,yBAAyB;YACzC,GAAG,OAAO;YACV,aAAa,EAAE,CAAC,OAAe,EAAE,EAAE;gBACjC,IAAI,MAAM,IAAI,CAAC,KAAK;oBAAE,OAAO,IAAI,CAAC;gBAClC,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC;YACxB,CAAC;YACD,oBAAoB,EAAE,CAAC;SACxB;QACD,SAAS;YACP,KAAK,GAAG,IAAI,CAAC;QACf,CAAC;QACD,KAAK;YACH,MAAM,GAAG,IAAI,CAAC;QAChB,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import type { BullMQTaskQueueOptions } from \"./task-queue-bullmq.types.ts\";\nimport type { BullMQConnection } from \"./task-queue-bullmq-connection.types.ts\";\nimport {\n bullMQConnectionTimeoutMs,\n bullMQConnectionRetries,\n bullMQRetryDelayMs,\n bullMQMaxRetryDelayMs,\n} from \"./task-queue-bullmq.constants.ts\";\n\nexport function createBullMQConnection(\n options: BullMQTaskQueueOptions[\"connection\"],\n): BullMQConnection {\n let ready = false;\n let closed = false;\n const retry =\n options.retryStrategy === undefined\n ? (attempt: number) => {\n if (!ready && attempt > bullMQConnectionRetries) return null;\n return Math.min(attempt * bullMQRetryDelayMs, bullMQMaxRetryDelayMs);\n }\n : options.retryStrategy;\n return {\n options: {\n connectTimeout: bullMQConnectionTimeoutMs,\n commandTimeout: bullMQConnectionTimeoutMs,\n ...options,\n retryStrategy: (attempt: number) => {\n if (closed || !retry) return null;\n return retry(attempt);\n },\n maxRetriesPerRequest: 1,\n },\n connected() {\n ready = true;\n },\n close() {\n closed = true;\n },\n };\n}\n"]}
@@ -0,0 +1,8 @@
1
+ import type { RedisOptions } from "bullmq";
2
+ export interface BullMQConnection {
3
+ readonly options: RedisOptions & {
4
+ readonly retryStrategy: NonNullable<RedisOptions["retryStrategy"]>;
5
+ };
6
+ connected(): void;
7
+ close(): void;
8
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=task-queue-bullmq-connection.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-queue-bullmq-connection.types.js","sourceRoot":"","sources":["../../src/infrastructure/task-queue-bullmq-connection.types.ts"],"names":[],"mappings":"","sourcesContent":["import type { RedisOptions } from \"bullmq\";\n\nexport interface BullMQConnection {\n readonly options: RedisOptions & {\n readonly retryStrategy: NonNullable<RedisOptions[\"retryStrategy\"]>;\n };\n connected(): void;\n close(): void;\n}\n"]}
@@ -1,7 +1,8 @@
1
1
  import { Queue, Worker } from "bullmq";
2
2
  import { queueNumber, queueString } from "../domain/task-queue.js";
3
3
  import { bullMQKey } from "./task-queue-bullmq-state.js";
4
- import { bullMQConnectionTimeoutMs, bullMQConnectionRetries, bullMQRetryDelayMs, bullMQDefaultPrefix, bullMQDefaultStalledIntervalMs, } from "./task-queue-bullmq.constants.js";
4
+ import { createBullMQConnection } from "./task-queue-bullmq-connection.js";
5
+ import { bullMQDefaultPrefix, bullMQDefaultStalledIntervalMs, } from "./task-queue-bullmq.constants.js";
5
6
  export async function openBullMQResources(options) {
6
7
  const name = bullMQKey(queueString(options.name));
7
8
  const prefix = queueString(options.prefix ?? bullMQDefaultPrefix);
@@ -10,15 +11,10 @@ export async function openBullMQResources(options) {
10
11
  throw new Error("BullMQ stalled interval must be positive");
11
12
  if (options.connection.keyPrefix)
12
13
  throw new Error("Use the BullMQ queue prefix instead of connection.keyPrefix");
13
- const connection = {
14
- connectTimeout: bullMQConnectionTimeoutMs,
15
- commandTimeout: bullMQConnectionTimeoutMs,
16
- retryStrategy: (attempt) => attempt <= bullMQConnectionRetries ? attempt * bullMQRetryDelayMs : null,
17
- ...options.connection,
18
- maxRetriesPerRequest: 1,
19
- };
14
+ const connection = createBullMQConnection(options.connection);
15
+ const connections = new Set([connection]);
20
16
  const root = new Queue(name, {
21
- connection,
17
+ connection: connection.options,
22
18
  prefix,
23
19
  });
24
20
  const handlers = new Map();
@@ -33,11 +29,13 @@ export async function openBullMQResources(options) {
33
29
  root.on("error", report);
34
30
  try {
35
31
  await root.waitUntilReady();
32
+ connection.connected();
36
33
  const memory = await (await root.client).info();
37
34
  if (!/^maxmemory_policy:noeviction\r?$/m.test(memory))
38
35
  throw new Error("Redis maxmemory-policy must be noeviction; change the database Data eviction policy in your Redis service settings");
39
36
  }
40
37
  catch (error) {
38
+ connection.close();
41
39
  await root.close();
42
40
  throw error;
43
41
  }
@@ -52,12 +50,14 @@ export async function openBullMQResources(options) {
52
50
  if (existing)
53
51
  return existing;
54
52
  const created = (async () => {
53
+ const connection = createBullMQConnection(options.connection);
54
+ connections.add(connection);
55
55
  const queue = new Queue(nativeName(handler), {
56
- connection,
56
+ connection: connection.options,
57
57
  prefix,
58
58
  });
59
59
  const worker = new Worker(nativeName(handler), null, {
60
- connection: { ...connection, maxRetriesPerRequest: null },
60
+ connection: { ...connection.options, maxRetriesPerRequest: null },
61
61
  prefix,
62
62
  autorun: false,
63
63
  skipLockRenewal: true,
@@ -68,10 +68,13 @@ export async function openBullMQResources(options) {
68
68
  worker.on("error", report);
69
69
  try {
70
70
  await Promise.all([queue.waitUntilReady(), worker.waitUntilReady()]);
71
+ connection.connected();
71
72
  await worker.startStalledCheckTimer();
72
73
  return { queue, worker };
73
74
  }
74
75
  catch (error) {
76
+ connection.close();
77
+ connections.delete(connection);
75
78
  await Promise.all([queue.close(), worker.close(true)]);
76
79
  throw error;
77
80
  }
@@ -103,10 +106,13 @@ export async function openBullMQResources(options) {
103
106
  closing ??= (async () => {
104
107
  await Promise.allSettled([...pending]);
105
108
  const opened = await Promise.allSettled([...handlers.values()]);
106
- const resources = opened.flatMap((entry) => entry.status === "fulfilled"
107
- ? [entry.value.worker, entry.value.queue]
108
- : []);
109
- const results = await Promise.allSettled(resources.map((resource) => resource.close()));
109
+ for (const connection of connections)
110
+ connection.close();
111
+ const resources = opened.flatMap((entry) => entry.status === "fulfilled" ? [entry.value] : []);
112
+ const results = await Promise.allSettled(resources.flatMap(({ worker, queue }) => [
113
+ worker.close(true),
114
+ queue.close(),
115
+ ]));
110
116
  await root.close();
111
117
  const errors = results
112
118
  .filter((entry) => entry.status === "rejected")
@@ -1 +1 @@
1
- {"version":3,"file":"task-queue-bullmq-resources.js","sourceRoot":"","sources":["../../src/infrastructure/task-queue-bullmq-resources.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAMnE,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AACzD,OAAO,EACL,yBAAyB,EACzB,uBAAuB,EACvB,kBAAkB,EAClB,mBAAmB,EACnB,8BAA8B,GAC/B,MAAM,kCAAkC,CAAC;AAE1C,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,OAA+B;IACvE,MAAM,IAAI,GAAG,SAAS,CAAC,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IAClD,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,MAAM,IAAI,mBAAmB,CAAC,CAAC;IAClE,MAAM,eAAe,GAAG,WAAW,CACjC,OAAO,CAAC,iBAAiB,IAAI,8BAA8B,CAC5D,CAAC;IACF,IAAI,CAAC,eAAe;QAClB,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;IAC9D,IAAI,OAAO,CAAC,UAAU,CAAC,SAAS;QAC9B,MAAM,IAAI,KAAK,CACb,6DAA6D,CAC9D,CAAC;IACJ,MAAM,UAAU,GAAG;QACjB,cAAc,EAAE,yBAAyB;QACzC,cAAc,EAAE,yBAAyB;QACzC,aAAa,EAAE,CAAC,OAAe,EAAE,EAAE,CACjC,OAAO,IAAI,uBAAuB,CAAC,CAAC,CAAC,OAAO,GAAG,kBAAkB,CAAC,CAAC,CAAC,IAAI;QAC1E,GAAG,OAAO,CAAC,UAAU;QACrB,oBAAoB,EAAE,CAAC;KACxB,CAAC;IACF,MAAM,IAAI,GAAG,IAAI,KAAK,CAA4B,IAAI,EAAE;QACtD,UAAU;QACV,MAAM;KACP,CAAC,CAAC;IACH,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAuC,CAAC;IAChE,MAAM,OAAO,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC5C,IAAI,OAAkC,CAAC;IACvC,SAAS,MAAM,CAAC,KAAc;QAC5B,IAAI,CAAC;YACH,OAAO,CAAC,OAAO,EAAE,CACf,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAC1D,CAAC;QACJ,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IACZ,CAAC;IACD,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IACzB,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;QAChD,IAAI,CAAC,mCAAmC,CAAC,IAAI,CAAC,MAAM,CAAC;YACnD,MAAM,IAAI,KAAK,CACb,oHAAoH,CACrH,CAAC;IACN,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;QACnB,MAAM,KAAK,CAAC;IACd,CAAC;IACD,SAAS,UAAU,CAAC,OAAe;QACjC,OAAO,GAAG,IAAI,IAAI,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;IACzC,CAAC;IACD,SAAS,SAAS,CAAC,OAAe,EAAE,EAAU;QAC5C,OAAO,GAAG,MAAM,IAAI,UAAU,CAAC,OAAO,CAAC,IAAI,SAAS,CAAC,EAAE,CAAC,EAAE,CAAC;IAC7D,CAAC;IACD,SAAS,YAAY,CAAC,OAAe;QACnC,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACvC,IAAI,QAAQ;YAAE,OAAO,QAAQ,CAAC;QAC9B,MAAM,OAAO,GAAG,CAAC,KAAK,IAAI,EAAE;YAC1B,MAAM,KAAK,GAAG,IAAI,KAAK,CAA4B,UAAU,CAAC,OAAO,CAAC,EAAE;gBACtE,UAAU;gBACV,MAAM;aACP,CAAC,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,MAAM,CACvB,UAAU,CAAC,OAAO,CAAC,EACnB,IAAI,EACJ;gBACE,UAAU,EAAE,EAAE,GAAG,UAAU,EAAE,oBAAoB,EAAE,IAAI,EAAE;gBACzD,MAAM;gBACN,OAAO,EAAE,KAAK;gBACd,eAAe,EAAE,IAAI;gBACrB,eAAe;gBACf,eAAe,EAAE,MAAM,CAAC,gBAAgB;aACzC,CACF,CAAC;YACF,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAC1B,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAC3B,IAAI,CAAC;gBACH,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,cAAc,EAAE,EAAE,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC;gBACrE,MAAM,MAAM,CAAC,sBAAsB,EAAE,CAAC;gBACtC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;YAC3B,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBACvD,MAAM,KAAK,CAAC;YACd,CAAC;QACH,CAAC,CAAC,EAAE,CAAC;QACL,QAAQ,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC/B,KAAK,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE;YACtB,IAAI,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,OAAO;gBAAE,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAClE,CAAC,CAAC,CAAC;QACH,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,SAAS,OAAO,CAAI,SAA2B;QAC7C,IAAI,OAAO;YACT,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC,CAAC;QAClE,MAAM,OAAO,GAAG,SAAS,EAAE,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACrB,KAAK,OAAO,CAAC,IAAI,CACf,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,EAC7B,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAC9B,CAAC;QACF,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,OAAO;QACL,IAAI;QACJ,MAAM;QACN,UAAU;QACV,SAAS;QACT,YAAY;QACZ,OAAO;QACP,MAAM;QACN,KAAK;YACH,OAAO,KAAK,CAAC,KAAK,IAAI,EAAE;gBACtB,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;gBACvC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;gBAChE,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CACzC,KAAK,CAAC,MAAM,KAAK,WAAW;oBAC1B,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC;oBACzC,CAAC,CAAC,EAAE,CACP,CAAC;gBACF,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CACtC,SAAS,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,CAC9C,CAAC;gBACF,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;gBACnB,MAAM,MAAM,GAAG,OAAO;qBACnB,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,KAAK,UAAU,CAAC;qBAC9C,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;gBAChC,IAAI,MAAM,CAAC,MAAM;oBACf,MAAM,IAAI,cAAc,CAAC,MAAM,EAAE,mCAAmC,CAAC,CAAC;YAC1E,CAAC,CAAC,EAAE,CAAC;YACL,OAAO,OAAO,CAAC;QACjB,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import { Queue, Worker } from \"bullmq\";\nimport { queueNumber, queueString } from \"../domain/task-queue.ts\";\nimport type { QueueRequest, QueueResult } from \"../domain/task-queue.types.ts\";\nimport type {\n BullMQTaskQueueOptions,\n BullMQHandlerQueue,\n} from \"./task-queue-bullmq.types.ts\";\nimport { bullMQKey } from \"./task-queue-bullmq-state.ts\";\nimport {\n bullMQConnectionTimeoutMs,\n bullMQConnectionRetries,\n bullMQRetryDelayMs,\n bullMQDefaultPrefix,\n bullMQDefaultStalledIntervalMs,\n} from \"./task-queue-bullmq.constants.ts\";\n\nexport async function openBullMQResources(options: BullMQTaskQueueOptions) {\n const name = bullMQKey(queueString(options.name));\n const prefix = queueString(options.prefix ?? bullMQDefaultPrefix);\n const stalledInterval = queueNumber(\n options.stalledIntervalMs ?? bullMQDefaultStalledIntervalMs,\n );\n if (!stalledInterval)\n throw new Error(\"BullMQ stalled interval must be positive\");\n if (options.connection.keyPrefix)\n throw new Error(\n \"Use the BullMQ queue prefix instead of connection.keyPrefix\",\n );\n const connection = {\n connectTimeout: bullMQConnectionTimeoutMs,\n commandTimeout: bullMQConnectionTimeoutMs,\n retryStrategy: (attempt: number) =>\n attempt <= bullMQConnectionRetries ? attempt * bullMQRetryDelayMs : null,\n ...options.connection,\n maxRetriesPerRequest: 1,\n };\n const root = new Queue<QueueRequest, QueueResult>(name, {\n connection,\n prefix,\n });\n const handlers = new Map<string, Promise<BullMQHandlerQueue>>();\n const pending = new Set<Promise<unknown>>();\n let closing: Promise<void> | undefined;\n function report(error: unknown) {\n try {\n options.onError?.(\n error instanceof Error ? error : new Error(String(error)),\n );\n } catch {}\n }\n root.on(\"error\", report);\n try {\n await root.waitUntilReady();\n const memory = await (await root.client).info();\n if (!/^maxmemory_policy:noeviction\\r?$/m.test(memory))\n throw new Error(\n \"Redis maxmemory-policy must be noeviction; change the database Data eviction policy in your Redis service settings\",\n );\n } catch (error) {\n await root.close();\n throw error;\n }\n function nativeName(handler: string) {\n return `${name}-${bullMQKey(handler)}`;\n }\n function nativeKey(handler: string, id: string) {\n return `${prefix}:${nativeName(handler)}:${bullMQKey(id)}`;\n }\n function handlerQueue(handler: string) {\n const existing = handlers.get(handler);\n if (existing) return existing;\n const created = (async () => {\n const queue = new Queue<QueueRequest, QueueResult>(nativeName(handler), {\n connection,\n prefix,\n });\n const worker = new Worker<QueueRequest, QueueResult>(\n nativeName(handler),\n null,\n {\n connection: { ...connection, maxRetriesPerRequest: null },\n prefix,\n autorun: false,\n skipLockRenewal: true,\n stalledInterval,\n maxStalledCount: Number.MAX_SAFE_INTEGER,\n },\n );\n queue.on(\"error\", report);\n worker.on(\"error\", report);\n try {\n await Promise.all([queue.waitUntilReady(), worker.waitUntilReady()]);\n await worker.startStalledCheckTimer();\n return { queue, worker };\n } catch (error) {\n await Promise.all([queue.close(), worker.close(true)]);\n throw error;\n }\n })();\n handlers.set(handler, created);\n void created.catch(() => {\n if (handlers.get(handler) === created) handlers.delete(handler);\n });\n return created;\n }\n function perform<T>(operation: () => Promise<T>): Promise<T> {\n if (closing)\n return Promise.reject(new Error(\"BullMQ task queue is closed\"));\n const promise = operation();\n pending.add(promise);\n void promise.then(\n () => pending.delete(promise),\n () => pending.delete(promise),\n );\n return promise;\n }\n return {\n root,\n prefix,\n nativeName,\n nativeKey,\n handlerQueue,\n perform,\n report,\n close() {\n closing ??= (async () => {\n await Promise.allSettled([...pending]);\n const opened = await Promise.allSettled([...handlers.values()]);\n const resources = opened.flatMap((entry) =>\n entry.status === \"fulfilled\"\n ? [entry.value.worker, entry.value.queue]\n : [],\n );\n const results = await Promise.allSettled(\n resources.map((resource) => resource.close()),\n );\n await root.close();\n const errors = results\n .filter((entry) => entry.status === \"rejected\")\n .map((entry) => entry.reason);\n if (errors.length)\n throw new AggregateError(errors, \"Failed to close BullMQ task queue\");\n })();\n return closing;\n },\n };\n}\n"]}
1
+ {"version":3,"file":"task-queue-bullmq-resources.js","sourceRoot":"","sources":["../../src/infrastructure/task-queue-bullmq-resources.ts"],"names":[],"mappings":"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","sourcesContent":["import { Queue, Worker } from \"bullmq\";\nimport { queueNumber, queueString } from \"../domain/task-queue.ts\";\nimport type { QueueRequest, QueueResult } from \"../domain/task-queue.types.ts\";\nimport type {\n BullMQTaskQueueOptions,\n BullMQHandlerQueue,\n} from \"./task-queue-bullmq.types.ts\";\nimport { bullMQKey } from \"./task-queue-bullmq-state.ts\";\nimport { createBullMQConnection } from \"./task-queue-bullmq-connection.ts\";\nimport {\n bullMQDefaultPrefix,\n bullMQDefaultStalledIntervalMs,\n} from \"./task-queue-bullmq.constants.ts\";\n\nexport async function openBullMQResources(options: BullMQTaskQueueOptions) {\n const name = bullMQKey(queueString(options.name));\n const prefix = queueString(options.prefix ?? bullMQDefaultPrefix);\n const stalledInterval = queueNumber(\n options.stalledIntervalMs ?? bullMQDefaultStalledIntervalMs,\n );\n if (!stalledInterval)\n throw new Error(\"BullMQ stalled interval must be positive\");\n if (options.connection.keyPrefix)\n throw new Error(\n \"Use the BullMQ queue prefix instead of connection.keyPrefix\",\n );\n const connection = createBullMQConnection(options.connection);\n const connections = new Set([connection]);\n const root = new Queue<QueueRequest, QueueResult>(name, {\n connection: connection.options,\n prefix,\n });\n const handlers = new Map<string, Promise<BullMQHandlerQueue>>();\n const pending = new Set<Promise<unknown>>();\n let closing: Promise<void> | undefined;\n function report(error: unknown) {\n try {\n options.onError?.(\n error instanceof Error ? error : new Error(String(error)),\n );\n } catch {}\n }\n root.on(\"error\", report);\n try {\n await root.waitUntilReady();\n connection.connected();\n const memory = await (await root.client).info();\n if (!/^maxmemory_policy:noeviction\\r?$/m.test(memory))\n throw new Error(\n \"Redis maxmemory-policy must be noeviction; change the database Data eviction policy in your Redis service settings\",\n );\n } catch (error) {\n connection.close();\n await root.close();\n throw error;\n }\n function nativeName(handler: string) {\n return `${name}-${bullMQKey(handler)}`;\n }\n function nativeKey(handler: string, id: string) {\n return `${prefix}:${nativeName(handler)}:${bullMQKey(id)}`;\n }\n function handlerQueue(handler: string) {\n const existing = handlers.get(handler);\n if (existing) return existing;\n const created = (async () => {\n const connection = createBullMQConnection(options.connection);\n connections.add(connection);\n const queue = new Queue<QueueRequest, QueueResult>(nativeName(handler), {\n connection: connection.options,\n prefix,\n });\n const worker = new Worker<QueueRequest, QueueResult>(\n nativeName(handler),\n null,\n {\n connection: { ...connection.options, maxRetriesPerRequest: null },\n prefix,\n autorun: false,\n skipLockRenewal: true,\n stalledInterval,\n maxStalledCount: Number.MAX_SAFE_INTEGER,\n },\n );\n queue.on(\"error\", report);\n worker.on(\"error\", report);\n try {\n await Promise.all([queue.waitUntilReady(), worker.waitUntilReady()]);\n connection.connected();\n await worker.startStalledCheckTimer();\n return { queue, worker };\n } catch (error) {\n connection.close();\n connections.delete(connection);\n await Promise.all([queue.close(), worker.close(true)]);\n throw error;\n }\n })();\n handlers.set(handler, created);\n void created.catch(() => {\n if (handlers.get(handler) === created) handlers.delete(handler);\n });\n return created;\n }\n function perform<T>(operation: () => Promise<T>): Promise<T> {\n if (closing)\n return Promise.reject(new Error(\"BullMQ task queue is closed\"));\n const promise = operation();\n pending.add(promise);\n void promise.then(\n () => pending.delete(promise),\n () => pending.delete(promise),\n );\n return promise;\n }\n return {\n root,\n prefix,\n nativeName,\n nativeKey,\n handlerQueue,\n perform,\n report,\n close() {\n closing ??= (async () => {\n await Promise.allSettled([...pending]);\n const opened = await Promise.allSettled([...handlers.values()]);\n for (const connection of connections) connection.close();\n const resources = opened.flatMap((entry) =>\n entry.status === \"fulfilled\" ? [entry.value] : [],\n );\n const results = await Promise.allSettled(\n resources.flatMap(({ worker, queue }) => [\n worker.close(true),\n queue.close(),\n ]),\n );\n await root.close();\n const errors = results\n .filter((entry) => entry.status === \"rejected\")\n .map((entry) => entry.reason);\n if (errors.length)\n throw new AggregateError(errors, \"Failed to close BullMQ task queue\");\n })();\n return closing;\n },\n };\n}\n"]}
@@ -1,5 +1,6 @@
1
1
  export declare function bullMQKey(value: string): string;
2
2
  export declare function bullMQState(value: unknown): {
3
+ request: string;
3
4
  job: import("../index.ts").QueueJob;
4
5
  token: string | undefined;
5
6
  } | undefined;
@@ -15,6 +15,7 @@ export function bullMQState(value) {
15
15
  throw new Error("Invalid BullMQ queue state");
16
16
  const meta = queueObject(JSON.parse(metadata));
17
17
  return {
18
+ request,
18
19
  job: queueJob({
19
20
  ...queueObject(JSON.parse(request)),
20
21
  ...meta,
@@ -1 +1 @@
1
- {"version":3,"file":"task-queue-bullmq-state.js","sourceRoot":"","sources":["../../src/infrastructure/task-queue-bullmq-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAE7E,MAAM,UAAU,SAAS,CAAC,KAAa;IACrC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IACrC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAC7C,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;IAChD,MAAM,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,GAAc,KAAK,CAAC;IACrD,IACE,OAAO,OAAO,KAAK,QAAQ;QAC3B,OAAO,QAAQ,KAAK,QAAQ;QAC5B,OAAO,MAAM,KAAK,QAAQ;QAE1B,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;IAChD,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC/C,OAAO;QACL,GAAG,EAAE,QAAQ,CAAC;YACZ,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACnC,GAAG,IAAI;YACP,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAClD,CAAC;QACF,KAAK,EAAE,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC;KACtE,CAAC;AACJ,CAAC","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { queueJob, queueObject, queueString } from \"../domain/task-queue.ts\";\n\nexport function bullMQKey(value: string): string {\n return createHash(\"sha256\").update(value).digest(\"hex\");\n}\n\nexport function bullMQState(value: unknown) {\n if (value === null) return undefined;\n if (!Array.isArray(value) || value.length !== 3)\n throw new Error(\"Invalid BullMQ queue state\");\n const [request, metadata, result]: unknown[] = value;\n if (\n typeof request !== \"string\" ||\n typeof metadata !== \"string\" ||\n typeof result !== \"string\"\n )\n throw new Error(\"Invalid BullMQ queue state\");\n const meta = queueObject(JSON.parse(metadata));\n return {\n job: queueJob({\n ...queueObject(JSON.parse(request)),\n ...meta,\n ...(result ? { result: JSON.parse(result) } : {}),\n }),\n token: meta.token === undefined ? undefined : queueString(meta.token),\n };\n}\n"]}
1
+ {"version":3,"file":"task-queue-bullmq-state.js","sourceRoot":"","sources":["../../src/infrastructure/task-queue-bullmq-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAE7E,MAAM,UAAU,SAAS,CAAC,KAAa;IACrC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IACrC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAC7C,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;IAChD,MAAM,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,GAAc,KAAK,CAAC;IACrD,IACE,OAAO,OAAO,KAAK,QAAQ;QAC3B,OAAO,QAAQ,KAAK,QAAQ;QAC5B,OAAO,MAAM,KAAK,QAAQ;QAE1B,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;IAChD,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC/C,OAAO;QACL,OAAO;QACP,GAAG,EAAE,QAAQ,CAAC;YACZ,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACnC,GAAG,IAAI;YACP,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAClD,CAAC;QACF,KAAK,EAAE,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC;KACtE,CAAC;AACJ,CAAC","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { queueJob, queueObject, queueString } from \"../domain/task-queue.ts\";\n\nexport function bullMQKey(value: string): string {\n return createHash(\"sha256\").update(value).digest(\"hex\");\n}\n\nexport function bullMQState(value: unknown) {\n if (value === null) return undefined;\n if (!Array.isArray(value) || value.length !== 3)\n throw new Error(\"Invalid BullMQ queue state\");\n const [request, metadata, result]: unknown[] = value;\n if (\n typeof request !== \"string\" ||\n typeof metadata !== \"string\" ||\n typeof result !== \"string\"\n )\n throw new Error(\"Invalid BullMQ queue state\");\n const meta = queueObject(JSON.parse(metadata));\n return {\n request,\n job: queueJob({\n ...queueObject(JSON.parse(request)),\n ...meta,\n ...(result ? { result: JSON.parse(result) } : {}),\n }),\n token: meta.token === undefined ? undefined : queueString(meta.token),\n };\n}\n"]}
@@ -4,4 +4,5 @@ export declare const bullMQClaimBatchSize = 100;
4
4
  export declare const bullMQConnectionTimeoutMs = 10000;
5
5
  export declare const bullMQConnectionRetries = 3;
6
6
  export declare const bullMQRetryDelayMs = 100;
7
- export declare const bullMQStateScript = "\nlocal operation = ARGV[1]\nlocal request = redis.call('HGET', KEYS[1], 'request')\nif operation == 'enqueue' then\n if request and request ~= ARGV[2] then\n return redis.error_reply('Queue identity already has a different request')\n end\n if not request then\n request = ARGV[2]\n redis.call('HSET', KEYS[1], 'request', request, 'deadline', ARGV[3], 'meta', '{\"status\":\"pending\",\"fence\":0}')\n end\nend\nif not request then\n if operation == 'get' then return nil end\n return redis.error_reply('Queue job does not exist')\nend\nlocal meta = cjson.decode(redis.call('HGET', KEYS[1], 'meta'))\nlocal clock = redis.call('TIME')\nlocal now = tonumber(clock[1]) * 1000 + math.floor(tonumber(clock[2]) / 1000)\nlocal deadline = tonumber(redis.call('HGET', KEYS[1], 'deadline'))\nlocal function live()\n return meta.status == 'pending' or meta.status == 'active'\nend\nlocal function save()\n redis.call('HSET', KEYS[1], 'meta', cjson.encode(meta))\nend\nif live() and deadline and deadline <= now then\n meta.status = 'cancelled'\n meta.fence = meta.fence + 1\n save()\nend\nlocal function owned()\n return meta.status == 'active' and meta.worker == ARGV[2]\n and meta.fence == tonumber(ARGV[3]) and meta.expires > now\n and redis.call('GET', KEYS[2]) == meta.token\nend\nlocal function extend(duration)\n meta.expires = now + duration\n if deadline then meta.expires = math.min(meta.expires, deadline) end\n redis.call('PEXPIRE', KEYS[2], meta.expires - now)\n redis.call('SREM', KEYS[3], ARGV[5])\nend\nif operation == 'claim' and live() then\n if redis.call('GET', KEYS[2]) ~= ARGV[3] then\n return redis.error_reply('Stale queue lease')\n end\n meta.status = 'active'\n meta.worker = ARGV[2]\n meta.token = ARGV[3]\n meta.fence = meta.fence + 1\n extend(tonumber(ARGV[4]))\n save()\nend\nif operation == 'renew' or operation == 'complete' then\n if not owned() then return redis.error_reply('Stale queue lease') end\n if operation == 'renew' then extend(tonumber(ARGV[4])) end\n if operation == 'complete' then\n meta.status = ARGV[5]\n redis.call('HSET', KEYS[1], 'result', ARGV[4])\n end\n save()\nend\nif operation == 'cancel' then\n if meta.fence ~= tonumber(ARGV[2]) then return redis.error_reply('Stale queue fence') end\n if live() then\n meta.status = 'cancelled'\n meta.fence = meta.fence + 1\n save()\n end\nend\nreturn {request, cjson.encode(meta), redis.call('HGET', KEYS[1], 'result') or ''}\n";
7
+ export declare const bullMQMaxRetryDelayMs = 1000;
8
+ export declare const bullMQStateScript = "\nlocal operation = ARGV[1]\nlocal request = redis.call('HGET', KEYS[1], 'request')\nif operation == 'enqueue' then\n if request and request ~= ARGV[2] and request ~= ARGV[4] then\n return redis.error_reply('Queue identity already has a different request')\n end\n if not request then\n request = ARGV[2]\n redis.call('HSET', KEYS[1], 'request', request, 'deadline', ARGV[3], 'meta', '{\"status\":\"pending\",\"fence\":0}')\n end\nend\nif not request then\n if operation == 'get' then return nil end\n return redis.error_reply('Queue job does not exist')\nend\nlocal meta = cjson.decode(redis.call('HGET', KEYS[1], 'meta'))\nlocal clock = redis.call('TIME')\nlocal now = tonumber(clock[1]) * 1000 + math.floor(tonumber(clock[2]) / 1000)\nlocal deadline = tonumber(redis.call('HGET', KEYS[1], 'deadline'))\nlocal function live()\n return meta.status == 'pending' or meta.status == 'active'\nend\nlocal function save()\n redis.call('HSET', KEYS[1], 'meta', cjson.encode(meta))\nend\nif live() and deadline and deadline <= now then\n meta.status = 'cancelled'\n meta.fence = meta.fence + 1\n save()\nend\nlocal function owned()\n return meta.status == 'active' and meta.worker == ARGV[2]\n and meta.fence == tonumber(ARGV[3]) and meta.expires > now\n and redis.call('GET', KEYS[2]) == meta.token\nend\nlocal function extend(duration)\n meta.expires = now + duration\n if deadline then meta.expires = math.min(meta.expires, deadline) end\n redis.call('PEXPIRE', KEYS[2], meta.expires - now)\n redis.call('SREM', KEYS[3], ARGV[5])\nend\nif operation == 'claim' and live() then\n if redis.call('GET', KEYS[2]) ~= ARGV[3] then\n return redis.error_reply('Stale queue lease')\n end\n meta.status = 'active'\n meta.worker = ARGV[2]\n meta.token = ARGV[3]\n meta.fence = meta.fence + 1\n extend(tonumber(ARGV[4]))\n save()\nend\nif operation == 'renew' or operation == 'complete' then\n if not owned() then return redis.error_reply('Stale queue lease') end\n if operation == 'renew' then extend(tonumber(ARGV[4])) end\n if operation == 'complete' then\n meta.status = ARGV[5]\n redis.call('HSET', KEYS[1], 'result', ARGV[4])\n end\n save()\nend\nif operation == 'cancel' then\n if meta.fence ~= tonumber(ARGV[2]) then return redis.error_reply('Stale queue fence') end\n if live() then\n meta.status = 'cancelled'\n meta.fence = meta.fence + 1\n save()\n end\nend\nreturn {request, cjson.encode(meta), redis.call('HGET', KEYS[1], 'result') or ''}\n";
@@ -4,11 +4,12 @@ export const bullMQClaimBatchSize = 100;
4
4
  export const bullMQConnectionTimeoutMs = 10_000;
5
5
  export const bullMQConnectionRetries = 3;
6
6
  export const bullMQRetryDelayMs = 100;
7
+ export const bullMQMaxRetryDelayMs = 1000;
7
8
  export const bullMQStateScript = `
8
9
  local operation = ARGV[1]
9
10
  local request = redis.call('HGET', KEYS[1], 'request')
10
11
  if operation == 'enqueue' then
11
- if request and request ~= ARGV[2] then
12
+ if request and request ~= ARGV[2] and request ~= ARGV[4] then
12
13
  return redis.error_reply('Queue identity already has a different request')
13
14
  end
14
15
  if not request then