@fdcn/sourcefed 0.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 (220) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +51 -0
  3. package/dist/chunk-B7T6IZYS.js +5155 -0
  4. package/dist/chunk-MLKGABMK.js +9 -0
  5. package/dist/chunk-NGJ6F5ST.js +766 -0
  6. package/dist/cli.js +30491 -0
  7. package/dist/core-storage.js +13 -0
  8. package/dist/core.js +643 -0
  9. package/dist/daemon.js +5190 -0
  10. package/dist/data/core/SKILL.md +71 -0
  11. package/dist/data/core/references/commands.md +75 -0
  12. package/dist/data/sourcefed/SKILL.md +31 -0
  13. package/dist/guidance.md +71 -0
  14. package/dist/mcp.js +4800 -0
  15. package/dist/opencode.js +12646 -0
  16. package/dist/pi.js +249 -0
  17. package/dist/tui-compiled.mjs +4096 -0
  18. package/dist.dts/adapters/opencode/bridge.d.ts +20 -0
  19. package/dist.dts/adapters/opencode/index.d.ts +3 -0
  20. package/dist.dts/adapters/opencode/tool-result.d.ts +1 -0
  21. package/dist.dts/adapters/opencode/tools/monitor_create.d.ts +3 -0
  22. package/dist.dts/adapters/opencode/tools/monitor_list.d.ts +3 -0
  23. package/dist.dts/adapters/opencode/tools/monitor_start.d.ts +3 -0
  24. package/dist.dts/adapters/opencode/tools/monitor_status.d.ts +3 -0
  25. package/dist.dts/adapters/opencode/tools/monitor_stop.d.ts +3 -0
  26. package/dist.dts/adapters/opencode/tui/plugin.d.ts +3 -0
  27. package/dist.dts/adapters/opencode/tui/sidebar.d.ts +13 -0
  28. package/dist.dts/adapters/opencode/tui.d.ts +7 -0
  29. package/dist.dts/adapters/pi/src/dialog.d.ts +12 -0
  30. package/dist.dts/adapters/pi/src/index.d.ts +2 -0
  31. package/dist.dts/index.d.ts +1 -0
  32. package/dist.dts/packages/cli/src/index.d.ts +2 -0
  33. package/dist.dts/packages/cli/src/skills/command.d.ts +1 -0
  34. package/dist.dts/packages/cli/src/skills/discovery.d.ts +7 -0
  35. package/dist.dts/packages/cli/src/skills/frontmatter.d.ts +5 -0
  36. package/dist.dts/packages/cli/src/skills/index.d.ts +1 -0
  37. package/dist.dts/packages/core/src/events.d.ts +24 -0
  38. package/dist.dts/packages/core/src/events.test.d.ts +1 -0
  39. package/dist.dts/packages/core/src/index.d.ts +9 -0
  40. package/dist.dts/packages/core/src/monitors/index.d.ts +6 -0
  41. package/dist.dts/packages/core/src/monitors/monitor.d.ts +26 -0
  42. package/dist.dts/packages/core/src/monitors/poll/index.d.ts +1 -0
  43. package/dist.dts/packages/core/src/monitors/poll/monitor.d.ts +12 -0
  44. package/dist.dts/packages/core/src/monitors/poll/types/index.d.ts +2 -0
  45. package/dist.dts/packages/core/src/monitors/poll/types/poll-monitor-options.d.ts +6 -0
  46. package/dist.dts/packages/core/src/monitors/poll/types/poll-runner.d.ts +3 -0
  47. package/dist.dts/packages/core/src/monitors/poll/types/routed-event-cursor.d.ts +2 -0
  48. package/dist.dts/packages/core/src/monitors/poll/utils/event-delivery-id.d.ts +2 -0
  49. package/dist.dts/packages/core/src/monitors/poll/utils/tick.d.ts +2 -0
  50. package/dist.dts/packages/core/src/monitors/schema.d.ts +37 -0
  51. package/dist.dts/packages/core/src/monitors/service.d.ts +35 -0
  52. package/dist.dts/packages/core/src/monitors/service.test.d.ts +1 -0
  53. package/dist.dts/packages/core/src/monitors/transport.test.d.ts +1 -0
  54. package/dist.dts/packages/core/src/monitors/types/index.d.ts +2 -0
  55. package/dist.dts/packages/core/src/monitors/types/monitor-record.d.ts +3 -0
  56. package/dist.dts/packages/core/src/monitors/types/monitor-registry.d.ts +3 -0
  57. package/dist.dts/packages/core/src/monitors/utils/monitor-identity.d.ts +5 -0
  58. package/dist.dts/packages/core/src/monitors/utils/monitor-identity.test.d.ts +1 -0
  59. package/dist.dts/packages/core/src/monitors/webhook/index.d.ts +1 -0
  60. package/dist.dts/packages/core/src/monitors/webhook/monitor.d.ts +24 -0
  61. package/dist.dts/packages/core/src/monitors/webhook/types/index.d.ts +3 -0
  62. package/dist.dts/packages/core/src/monitors/webhook/types/pending-webhook-event.d.ts +19 -0
  63. package/dist.dts/packages/core/src/monitors/webhook/types/webhook-monitor-options.d.ts +13 -0
  64. package/dist.dts/packages/core/src/monitors/webhook/utils/constants.d.ts +2 -0
  65. package/dist.dts/packages/core/src/monitors/webhook/utils/deliver-webhook.d.ts +3 -0
  66. package/dist.dts/packages/core/src/monitors/webhook/utils/handle-webhook.d.ts +2 -0
  67. package/dist.dts/packages/core/src/monitors/webhook/utils/handle-webhook.test.d.ts +1 -0
  68. package/dist.dts/packages/core/src/monitors/webhook/utils/heartbeat-webhooks.d.ts +3 -0
  69. package/dist.dts/packages/core/src/runtime.d.ts +23 -0
  70. package/dist.dts/packages/core/src/runtime.test.d.ts +1 -0
  71. package/dist.dts/packages/core/src/sources/index.d.ts +1 -0
  72. package/dist.dts/packages/core/src/sources/schema.d.ts +4 -0
  73. package/dist.dts/packages/core/src/sources/types/index.d.ts +3 -0
  74. package/dist.dts/packages/core/src/sources/types/monitor-source.d.ts +3 -0
  75. package/dist.dts/packages/core/src/sources/types/source-input.d.ts +4 -0
  76. package/dist.dts/packages/core/src/sources/types/source-registry.d.ts +2 -0
  77. package/dist.dts/packages/core/src/storage/in-memory-monitor-store.d.ts +8 -0
  78. package/dist.dts/packages/core/src/storage/index.d.ts +2 -0
  79. package/dist.dts/packages/core/src/storage/json.d.ts +30 -0
  80. package/dist.dts/packages/core/src/storage/json.test.d.ts +1 -0
  81. package/dist.dts/packages/core/src/types/delivery.d.ts +1 -0
  82. package/dist.dts/packages/core/src/types/event-sink.d.ts +12 -0
  83. package/dist.dts/packages/core/src/types/index.d.ts +11 -0
  84. package/dist.dts/packages/core/src/types/monitor-context.d.ts +10 -0
  85. package/dist.dts/packages/core/src/types/monitor-create-input.d.ts +8 -0
  86. package/dist.dts/packages/core/src/types/monitor-event-queue.d.ts +18 -0
  87. package/dist.dts/packages/core/src/types/monitor-event.d.ts +10 -0
  88. package/dist.dts/packages/core/src/types/monitor-options.d.ts +7 -0
  89. package/dist.dts/packages/core/src/types/monitor-store.d.ts +6 -0
  90. package/dist.dts/packages/core/src/types/monitor-target.d.ts +4 -0
  91. package/dist.dts/packages/core/src/types/source-event.d.ts +9 -0
  92. package/dist.dts/packages/core/src/types/source-poll-result.d.ts +6 -0
  93. package/dist.dts/packages/core/src/utils/event-to-text.d.ts +2 -0
  94. package/dist.dts/packages/core/src/utils/to-monitor-event.d.ts +3 -0
  95. package/dist.dts/packages/daemon/src/client.d.ts +2 -0
  96. package/dist.dts/packages/daemon/src/client.test.d.ts +1 -0
  97. package/dist.dts/packages/daemon/src/daemon.d.ts +36 -0
  98. package/dist.dts/packages/daemon/src/daemon.test.d.ts +1 -0
  99. package/dist.dts/packages/daemon/src/dispatch.d.ts +2 -0
  100. package/dist.dts/packages/daemon/src/http.d.ts +5 -0
  101. package/dist.dts/packages/daemon/src/index.d.ts +8 -0
  102. package/dist.dts/packages/daemon/src/registry.d.ts +14 -0
  103. package/dist.dts/packages/daemon/src/types/client.d.ts +21 -0
  104. package/dist.dts/packages/daemon/src/types/daemon.d.ts +58 -0
  105. package/dist.dts/packages/daemon/src/types/index.d.ts +3 -0
  106. package/dist.dts/packages/daemon/src/types/protocol.d.ts +21 -0
  107. package/dist.dts/packages/daemon/src/utils/env.d.ts +1 -0
  108. package/dist.dts/packages/daemon/src/utils/http-server.d.ts +10 -0
  109. package/dist.dts/packages/daemon/src/utils/index.d.ts +6 -0
  110. package/dist.dts/packages/daemon/src/utils/request-router.d.ts +11 -0
  111. package/dist.dts/packages/daemon/src/utils/sink.d.ts +13 -0
  112. package/dist.dts/packages/daemon/src/utils/spawn.d.ts +19 -0
  113. package/dist.dts/packages/daemon/src/utils/target.d.ts +4 -0
  114. package/dist.dts/packages/mcp/src/index.d.ts +2 -0
  115. package/dist.dts/packages/mcp/src/server.d.ts +14 -0
  116. package/dist.dts/packages/mcp/src/server.test.d.ts +1 -0
  117. package/dist.dts/packages/mcp/src/uris.d.ts +3 -0
  118. package/dist.dts/packages/sourcefed/src/server.d.ts +7 -0
  119. package/dist.dts/providers/github/src/api/client.d.ts +3 -0
  120. package/dist.dts/providers/github/src/api/comments.d.ts +1 -0
  121. package/dist.dts/providers/github/src/api/github-api.test.d.ts +1 -0
  122. package/dist.dts/providers/github/src/api/index.d.ts +4 -0
  123. package/dist.dts/providers/github/src/api/logs.d.ts +5 -0
  124. package/dist.dts/providers/github/src/api/pr.d.ts +8 -0
  125. package/dist.dts/providers/github/src/github.test.d.ts +1 -0
  126. package/dist.dts/providers/github/src/index.d.ts +2 -0
  127. package/dist.dts/providers/github/src/monitor.d.ts +15 -0
  128. package/dist.dts/providers/github/src/poll/index.d.ts +2 -0
  129. package/dist.dts/providers/github/src/poll/monitor.d.ts +3 -0
  130. package/dist.dts/providers/github/src/poll/utils/append-comments.d.ts +2 -0
  131. package/dist.dts/providers/github/src/poll/utils/append-github-ci-event.d.ts +2 -0
  132. package/dist.dts/providers/github/src/poll/utils/append-github-conflict-event.d.ts +2 -0
  133. package/dist.dts/providers/github/src/poll/utils/append-github-reviews.d.ts +2 -0
  134. package/dist.dts/providers/github/src/poll/utils/append-github-state-event.d.ts +2 -0
  135. package/dist.dts/providers/github/src/poll/utils/ci-failure-detail.d.ts +1 -0
  136. package/dist.dts/providers/github/src/poll/utils/clean-ci-log.d.ts +1 -0
  137. package/dist.dts/providers/github/src/poll/utils/confirm-still-failing.d.ts +1 -0
  138. package/dist.dts/providers/github/src/poll/utils/empty-cursor.d.ts +2 -0
  139. package/dist.dts/providers/github/src/poll/utils/failure-run-ids.d.ts +1 -0
  140. package/dist.dts/providers/github/src/poll/utils/failure-run-log.d.ts +1 -0
  141. package/dist.dts/providers/github/src/poll/utils/format-failed-check.d.ts +1 -0
  142. package/dist.dts/providers/github/src/poll/utils/index.d.ts +1 -0
  143. package/dist.dts/providers/github/src/poll/utils/poll-github.d.ts +6 -0
  144. package/dist.dts/providers/github/src/poll/utils/poll-helpers.test.d.ts +1 -0
  145. package/dist.dts/providers/github/src/schema.d.ts +17 -0
  146. package/dist.dts/providers/github/src/types/cursor.d.ts +11 -0
  147. package/dist.dts/providers/github/src/types/event.d.ts +11 -0
  148. package/dist.dts/providers/github/src/types/index.d.ts +3 -0
  149. package/dist.dts/providers/github/src/types/source-record.d.ts +5 -0
  150. package/dist.dts/providers/github/src/webhook/index.d.ts +3 -0
  151. package/dist.dts/providers/github/src/webhook/monitor.d.ts +3 -0
  152. package/dist.dts/providers/github/src/webhook/types/context.d.ts +7 -0
  153. package/dist.dts/providers/github/src/webhook/types/event-handler.d.ts +3 -0
  154. package/dist.dts/providers/github/src/webhook/types/event-shape.d.ts +7 -0
  155. package/dist.dts/providers/github/src/webhook/types/index.d.ts +1 -0
  156. package/dist.dts/providers/github/src/webhook/utils/fallback-github-event.d.ts +3 -0
  157. package/dist.dts/providers/github/src/webhook/utils/github-event-handlers.d.ts +2 -0
  158. package/dist.dts/providers/github/src/webhook/utils/index.d.ts +2 -0
  159. package/dist.dts/providers/github/src/webhook/utils/parse-check-run.d.ts +3 -0
  160. package/dist.dts/providers/github/src/webhook/utils/parse-check-suite.d.ts +3 -0
  161. package/dist.dts/providers/github/src/webhook/utils/parse-github-comment.d.ts +3 -0
  162. package/dist.dts/providers/github/src/webhook/utils/parse-github-webhook.d.ts +2 -0
  163. package/dist.dts/providers/github/src/webhook/utils/parse-pull-request-review.d.ts +3 -0
  164. package/dist.dts/providers/github/src/webhook/utils/parse-pull-request.d.ts +3 -0
  165. package/dist.dts/providers/github/src/webhook/utils/pull-request-number.d.ts +1 -0
  166. package/dist.dts/providers/github/src/webhook/utils/update-github-cursor.d.ts +3 -0
  167. package/dist.dts/providers/github/src/webhook/utils/verify-github-webhook.d.ts +1 -0
  168. package/dist.dts/providers/github/src/webhook/utils/verify-hmac.d.ts +1 -0
  169. package/dist.dts/providers/jira/src/index.d.ts +2 -0
  170. package/dist.dts/providers/jira/src/monitor.d.ts +14 -0
  171. package/dist.dts/providers/jira/src/poll/index.d.ts +2 -0
  172. package/dist.dts/providers/jira/src/poll/monitor.d.ts +3 -0
  173. package/dist.dts/providers/jira/src/poll/utils/adf-text.d.ts +1 -0
  174. package/dist.dts/providers/jira/src/poll/utils/append-jira-changelog.d.ts +2 -0
  175. package/dist.dts/providers/jira/src/poll/utils/append-jira-comments.d.ts +2 -0
  176. package/dist.dts/providers/jira/src/poll/utils/append-jira-description.d.ts +2 -0
  177. package/dist.dts/providers/jira/src/poll/utils/empty-cursor.d.ts +2 -0
  178. package/dist.dts/providers/jira/src/poll/utils/format-jira-changelog-item.d.ts +1 -0
  179. package/dist.dts/providers/jira/src/poll/utils/index.d.ts +1 -0
  180. package/dist.dts/providers/jira/src/poll/utils/is-terminal-status.d.ts +1 -0
  181. package/dist.dts/providers/jira/src/poll/utils/jira-auth.d.ts +1 -0
  182. package/dist.dts/providers/jira/src/poll/utils/jira-change-value.d.ts +1 -0
  183. package/dist.dts/providers/jira/src/poll/utils/jira-changelog-event.d.ts +2 -0
  184. package/dist.dts/providers/jira/src/poll/utils/jira-fetch.d.ts +1 -0
  185. package/dist.dts/providers/jira/src/poll/utils/meaningful-changelog-items.d.ts +1 -0
  186. package/dist.dts/providers/jira/src/poll/utils/poll-helpers.test.d.ts +1 -0
  187. package/dist.dts/providers/jira/src/poll/utils/poll-jira.d.ts +6 -0
  188. package/dist.dts/providers/jira/src/schema.d.ts +10 -0
  189. package/dist.dts/providers/jira/src/types/cursor.d.ts +5 -0
  190. package/dist.dts/providers/jira/src/types/event.d.ts +11 -0
  191. package/dist.dts/providers/jira/src/types/index.d.ts +3 -0
  192. package/dist.dts/providers/jira/src/types/source-record.d.ts +4 -0
  193. package/dist.dts/providers/slack/src/index.d.ts +2 -0
  194. package/dist.dts/providers/slack/src/monitor.d.ts +15 -0
  195. package/dist.dts/providers/slack/src/poll/index.d.ts +2 -0
  196. package/dist.dts/providers/slack/src/poll/monitor.d.ts +3 -0
  197. package/dist.dts/providers/slack/src/poll/utils/index.d.ts +2 -0
  198. package/dist.dts/providers/slack/src/poll/utils/parse-slack-read-result.d.ts +5 -0
  199. package/dist.dts/providers/slack/src/poll/utils/poll-slack.d.ts +6 -0
  200. package/dist.dts/providers/slack/src/poll/utils/slack-api.d.ts +4 -0
  201. package/dist.dts/providers/slack/src/schema.d.ts +11 -0
  202. package/dist.dts/providers/slack/src/slack.test.d.ts +1 -0
  203. package/dist.dts/providers/slack/src/types/cursor.d.ts +5 -0
  204. package/dist.dts/providers/slack/src/types/event.d.ts +8 -0
  205. package/dist.dts/providers/slack/src/types/index.d.ts +4 -0
  206. package/dist.dts/providers/slack/src/types/message.d.ts +6 -0
  207. package/dist.dts/providers/slack/src/types/read-result.d.ts +10 -0
  208. package/dist.dts/providers/slack/src/types/source-record.d.ts +5 -0
  209. package/dist.dts/providers/slack/src/utils/index.d.ts +2 -0
  210. package/dist.dts/providers/slack/src/utils/message-at.d.ts +1 -0
  211. package/dist.dts/providers/slack/src/utils/message-timestamp.d.ts +1 -0
  212. package/dist.dts/providers/slack/src/webhook/index.d.ts +2 -0
  213. package/dist.dts/providers/slack/src/webhook/monitor.d.ts +3 -0
  214. package/dist.dts/providers/slack/src/webhook/utils/index.d.ts +3 -0
  215. package/dist.dts/providers/slack/src/webhook/utils/parse-slack-webhook.d.ts +2 -0
  216. package/dist.dts/providers/slack/src/webhook/utils/slack-signature.d.ts +1 -0
  217. package/dist.dts/providers/slack/src/webhook/utils/update-slack-cursor.d.ts +3 -0
  218. package/dist.dts/providers/slack/src/webhook/utils/valid-slack-signature.d.ts +1 -0
  219. package/dist.dts/tui.d.ts +1 -0
  220. package/package.json +68 -0
@@ -0,0 +1,4096 @@
1
+ // .tui-build/tui/plugin.mjs
2
+ import { memo as _$memo2 } from "opentui:runtime-module:%40opentui%2Fsolid";
3
+ import { effect as _$effect2 } from "opentui:runtime-module:%40opentui%2Fsolid";
4
+ import { insert as _$insert2 } from "opentui:runtime-module:%40opentui%2Fsolid";
5
+ import { createTextNode as _$createTextNode2 } from "opentui:runtime-module:%40opentui%2Fsolid";
6
+ import { insertNode as _$insertNode2 } from "opentui:runtime-module:%40opentui%2Fsolid";
7
+ import { setProp as _$setProp2 } from "opentui:runtime-module:%40opentui%2Fsolid";
8
+ import { createElement as _$createElement2 } from "opentui:runtime-module:%40opentui%2Fsolid";
9
+ import { createComponent as _$createComponent2 } from "opentui:runtime-module:%40opentui%2Fsolid";
10
+ import { createMemo as createMemo2, Show as Show2 } from "opentui:runtime-module:solid-js";
11
+
12
+ // packages/daemon/dist/index.js
13
+ import { link, mkdir, open, readFile, rename, rm, writeFile } from "node:fs/promises";
14
+ import os from "node:os";
15
+ import path from "node:path";
16
+ import { link as link2, mkdir as mkdir2, open as open2, readFile as readFile2, rename as rename2, rm as rm2, writeFile as writeFile2 } from "node:fs/promises";
17
+ import os2 from "node:os";
18
+ import path2 from "node:path";
19
+ import { inflateRawSync } from "node:zlib";
20
+ import { createHmac, timingSafeEqual } from "node:crypto";
21
+ import { link as link3, mkdir as mkdir3, open as open3, readFile as readFile3, rename as rename3, rm as rm3, writeFile as writeFile3 } from "node:fs/promises";
22
+ import os3 from "node:os";
23
+ import path3 from "node:path";
24
+ import { link as link4, mkdir as mkdir4, open as open4, readFile as readFile4, rename as rename4, rm as rm4, writeFile as writeFile4 } from "node:fs/promises";
25
+ import os4 from "node:os";
26
+ import path4 from "node:path";
27
+ import { createHmac as createHmac2 } from "node:crypto";
28
+ import { timingSafeEqual as timingSafeEqual2 } from "node:crypto";
29
+ import os5 from "node:os";
30
+ import path5 from "node:path";
31
+ import { createServer } from "node:http";
32
+ import { Readable } from "node:stream";
33
+ import { spawn } from "node:child_process";
34
+ import path6 from "node:path";
35
+ var EMOJI_REGEX = new RegExp("^(?:[\\u{1F1E6}-\\u{1F1FF}]{2}|\\u{1F3F4}[\\u{E0061}-\\u{E007A}]{2}[\\u{E0030}-\\u{E0039}\\u{E0061}-\\u{E007A}]{1,3}\\u{E007F}|(?:\\p{Emoji}\\uFE0F\\u20E3?|\\p{Emoji_Modifier_Base}\\p{Emoji_Modifier}?|(?![\\p{Emoji_Modifier_Base}\\u{1F1E6}-\\u{1F1FF}])\\p{Emoji_Presentation})(?:\\u200D(?:\\p{Emoji}\\uFE0F\\u20E3?|\\p{Emoji_Modifier_Base}\\p{Emoji_Modifier}?|(?![\\p{Emoji_Modifier_Base}\\u{1F1E6}-\\u{1F1FF}])\\p{Emoji_Presentation}))*)+$", "u");
36
+ var MAX_WEBHOOK_BODY_BYTES = 1024 * 1024;
37
+ var store$42;
38
+ var DEFAULT_CONFIG2 = {
39
+ lang: void 0,
40
+ message: void 0,
41
+ abortEarly: void 0,
42
+ abortPipeEarly: void 0
43
+ };
44
+ // @__NO_SIDE_EFFECTS__
45
+ function getGlobalConfig2(config$1) {
46
+ if (!config$1 && !store$42) return DEFAULT_CONFIG2;
47
+ return {
48
+ lang: config$1?.lang ?? store$42?.lang,
49
+ message: config$1?.message,
50
+ abortEarly: config$1?.abortEarly ?? store$42?.abortEarly,
51
+ abortPipeEarly: config$1?.abortPipeEarly ?? store$42?.abortPipeEarly
52
+ };
53
+ }
54
+ var store$32;
55
+ // @__NO_SIDE_EFFECTS__
56
+ function getGlobalMessage2(lang) {
57
+ return store$32?.get(lang);
58
+ }
59
+ var store$22;
60
+ // @__NO_SIDE_EFFECTS__
61
+ function getSchemaMessage2(lang) {
62
+ return store$22?.get(lang);
63
+ }
64
+ var store$12;
65
+ // @__NO_SIDE_EFFECTS__
66
+ function getSpecificMessage2(reference, lang) {
67
+ return store$12?.get(reference)?.get(lang);
68
+ }
69
+ // @__NO_SIDE_EFFECTS__
70
+ function _stringify2(input) {
71
+ const type = typeof input;
72
+ if (type === "string") return `"${input}"`;
73
+ if (type === "number" || type === "bigint" || type === "boolean") return `${input}`;
74
+ if (type === "object" || type === "function") return (input && Object.getPrototypeOf(input)?.constructor?.name) ?? "null";
75
+ return type;
76
+ }
77
+ function _addIssue2(context, label, dataset, config$1, other) {
78
+ const input = other && "input" in other ? other.input : dataset.value;
79
+ const expected = other?.expected ?? context.expects ?? null;
80
+ const received = other?.received ?? /* @__PURE__ */ _stringify2(input);
81
+ const issue = {
82
+ kind: context.kind,
83
+ type: context.type,
84
+ input,
85
+ expected,
86
+ received,
87
+ message: `Invalid ${label}: ${expected ? `Expected ${expected} but r` : "R"}eceived ${received}`,
88
+ requirement: context.requirement,
89
+ path: other?.path,
90
+ issues: other?.issues,
91
+ lang: config$1.lang,
92
+ abortEarly: config$1.abortEarly,
93
+ abortPipeEarly: config$1.abortPipeEarly
94
+ };
95
+ const isSchema = context.kind === "schema";
96
+ const message$1 = other?.message ?? context.message ?? /* @__PURE__ */ getSpecificMessage2(context.reference, issue.lang) ?? (isSchema ? /* @__PURE__ */ getSchemaMessage2(issue.lang) : null) ?? config$1.message ?? /* @__PURE__ */ getGlobalMessage2(issue.lang);
97
+ if (message$1 !== void 0) issue.message = typeof message$1 === "function" ? message$1(issue) : message$1;
98
+ if (isSchema) dataset.typed = false;
99
+ if (dataset.issues) dataset.issues.push(issue);
100
+ else dataset.issues = [issue];
101
+ }
102
+ var _standardCache2 = /* @__PURE__ */ new WeakMap();
103
+ // @__NO_SIDE_EFFECTS__
104
+ function _getStandardProps2(context) {
105
+ let cached = _standardCache2.get(context);
106
+ if (!cached) {
107
+ cached = {
108
+ version: 1,
109
+ vendor: "valibot",
110
+ validate(value$1) {
111
+ return context["~run"]({ value: value$1 }, /* @__PURE__ */ getGlobalConfig2());
112
+ }
113
+ };
114
+ _standardCache2.set(context, cached);
115
+ }
116
+ return cached;
117
+ }
118
+ var EMOJI_REGEX2 = new RegExp("^(?:[\\u{1F1E6}-\\u{1F1FF}]{2}|\\u{1F3F4}[\\u{E0061}-\\u{E007A}]{2}[\\u{E0030}-\\u{E0039}\\u{E0061}-\\u{E007A}]{1,3}\\u{E007F}|(?:\\p{Emoji}\\uFE0F\\u20E3?|\\p{Emoji_Modifier_Base}\\p{Emoji_Modifier}?|(?![\\p{Emoji_Modifier_Base}\\u{1F1E6}-\\u{1F1FF}])\\p{Emoji_Presentation})(?:\\u200D(?:\\p{Emoji}\\uFE0F\\u20E3?|\\p{Emoji_Modifier_Base}\\p{Emoji_Modifier}?|(?![\\p{Emoji_Modifier_Base}\\u{1F1E6}-\\u{1F1FF}])\\p{Emoji_Presentation}))*)+$", "u");
119
+ // @__NO_SIDE_EFFECTS__
120
+ function getFallback2(schema, dataset, config$1) {
121
+ return typeof schema.fallback === "function" ? schema.fallback(dataset, config$1) : schema.fallback;
122
+ }
123
+ // @__NO_SIDE_EFFECTS__
124
+ function getDefault2(schema, dataset, config$1) {
125
+ return typeof schema.default === "function" ? schema.default(dataset, config$1) : schema.default;
126
+ }
127
+ // @__NO_SIDE_EFFECTS__
128
+ function array2(item, message$1) {
129
+ return {
130
+ kind: "schema",
131
+ type: "array",
132
+ reference: array2,
133
+ expects: "Array",
134
+ async: false,
135
+ item,
136
+ message: message$1,
137
+ get "~standard"() {
138
+ return /* @__PURE__ */ _getStandardProps2(this);
139
+ },
140
+ "~run"(dataset, config$1) {
141
+ const input = dataset.value;
142
+ if (Array.isArray(input)) {
143
+ dataset.typed = true;
144
+ dataset.value = [];
145
+ for (let key = 0; key < input.length; key++) {
146
+ const value$1 = input[key];
147
+ const itemDataset = this.item["~run"]({ value: value$1 }, config$1);
148
+ if (itemDataset.issues) {
149
+ const pathItem = {
150
+ type: "array",
151
+ origin: "value",
152
+ input,
153
+ key,
154
+ value: value$1
155
+ };
156
+ for (const issue of itemDataset.issues) {
157
+ if (issue.path) issue.path.unshift(pathItem);
158
+ else issue.path = [pathItem];
159
+ dataset.issues?.push(issue);
160
+ }
161
+ if (!dataset.issues) dataset.issues = itemDataset.issues;
162
+ if (config$1.abortEarly) {
163
+ dataset.typed = false;
164
+ break;
165
+ }
166
+ }
167
+ if (!itemDataset.typed) dataset.typed = false;
168
+ dataset.value.push(itemDataset.value);
169
+ }
170
+ } else _addIssue2(this, "type", dataset, config$1);
171
+ return dataset;
172
+ }
173
+ };
174
+ }
175
+ // @__NO_SIDE_EFFECTS__
176
+ function boolean2(message$1) {
177
+ return {
178
+ kind: "schema",
179
+ type: "boolean",
180
+ reference: boolean2,
181
+ expects: "boolean",
182
+ async: false,
183
+ message: message$1,
184
+ get "~standard"() {
185
+ return /* @__PURE__ */ _getStandardProps2(this);
186
+ },
187
+ "~run"(dataset, config$1) {
188
+ if (typeof dataset.value === "boolean") dataset.typed = true;
189
+ else _addIssue2(this, "type", dataset, config$1);
190
+ return dataset;
191
+ }
192
+ };
193
+ }
194
+ // @__NO_SIDE_EFFECTS__
195
+ function object2(entries$1, message$1) {
196
+ return {
197
+ kind: "schema",
198
+ type: "object",
199
+ reference: object2,
200
+ expects: "Object",
201
+ async: false,
202
+ entries: entries$1,
203
+ message: message$1,
204
+ get "~standard"() {
205
+ return /* @__PURE__ */ _getStandardProps2(this);
206
+ },
207
+ "~run"(dataset, config$1) {
208
+ const input = dataset.value;
209
+ if (input && typeof input === "object") {
210
+ dataset.typed = true;
211
+ dataset.value = {};
212
+ for (const key in this.entries) {
213
+ const valueSchema = this.entries[key];
214
+ if (key in input || (valueSchema.type === "exact_optional" || valueSchema.type === "optional" || valueSchema.type === "nullish") && valueSchema.default !== void 0) {
215
+ const value$1 = key in input ? input[key] : /* @__PURE__ */ getDefault2(valueSchema);
216
+ const valueDataset = valueSchema["~run"]({ value: value$1 }, config$1);
217
+ if (valueDataset.issues) {
218
+ const pathItem = {
219
+ type: "object",
220
+ origin: "value",
221
+ input,
222
+ key,
223
+ value: value$1
224
+ };
225
+ for (const issue of valueDataset.issues) {
226
+ if (issue.path) issue.path.unshift(pathItem);
227
+ else issue.path = [pathItem];
228
+ dataset.issues?.push(issue);
229
+ }
230
+ if (!dataset.issues) dataset.issues = valueDataset.issues;
231
+ if (config$1.abortEarly) {
232
+ dataset.typed = false;
233
+ break;
234
+ }
235
+ }
236
+ if (!valueDataset.typed) dataset.typed = false;
237
+ dataset.value[key] = valueDataset.value;
238
+ } else if (valueSchema.fallback !== void 0) dataset.value[key] = /* @__PURE__ */ getFallback2(valueSchema);
239
+ else if (valueSchema.type !== "exact_optional" && valueSchema.type !== "optional" && valueSchema.type !== "nullish") {
240
+ _addIssue2(this, "key", dataset, config$1, {
241
+ input: void 0,
242
+ expected: `"${key}"`,
243
+ path: [{
244
+ type: "object",
245
+ origin: "key",
246
+ input,
247
+ key,
248
+ value: input[key]
249
+ }]
250
+ });
251
+ if (config$1.abortEarly) break;
252
+ }
253
+ }
254
+ } else _addIssue2(this, "type", dataset, config$1);
255
+ return dataset;
256
+ }
257
+ };
258
+ }
259
+ // @__NO_SIDE_EFFECTS__
260
+ function optional(wrapped, default_) {
261
+ return {
262
+ kind: "schema",
263
+ type: "optional",
264
+ reference: optional,
265
+ expects: `(${wrapped.expects} | undefined)`,
266
+ async: false,
267
+ wrapped,
268
+ default: default_,
269
+ get "~standard"() {
270
+ return /* @__PURE__ */ _getStandardProps2(this);
271
+ },
272
+ "~run"(dataset, config$1) {
273
+ if (dataset.value === void 0) {
274
+ if (this.default !== void 0) dataset.value = /* @__PURE__ */ getDefault2(this, dataset, config$1);
275
+ if (dataset.value === void 0) {
276
+ dataset.typed = true;
277
+ return dataset;
278
+ }
279
+ }
280
+ return this.wrapped["~run"](dataset, config$1);
281
+ }
282
+ };
283
+ }
284
+ // @__NO_SIDE_EFFECTS__
285
+ function string2(message$1) {
286
+ return {
287
+ kind: "schema",
288
+ type: "string",
289
+ reference: string2,
290
+ expects: "string",
291
+ async: false,
292
+ message: message$1,
293
+ get "~standard"() {
294
+ return /* @__PURE__ */ _getStandardProps2(this);
295
+ },
296
+ "~run"(dataset, config$1) {
297
+ if (typeof dataset.value === "string") dataset.typed = true;
298
+ else _addIssue2(this, "type", dataset, config$1);
299
+ return dataset;
300
+ }
301
+ };
302
+ }
303
+ // @__NO_SIDE_EFFECTS__
304
+ function unknown2() {
305
+ return {
306
+ kind: "schema",
307
+ type: "unknown",
308
+ reference: unknown2,
309
+ expects: "unknown",
310
+ async: false,
311
+ get "~standard"() {
312
+ return /* @__PURE__ */ _getStandardProps2(this);
313
+ },
314
+ "~run"(dataset) {
315
+ dataset.typed = true;
316
+ return dataset;
317
+ }
318
+ };
319
+ }
320
+ // @__NO_SIDE_EFFECTS__
321
+ function safeParse2(schema, input, config$1) {
322
+ const dataset = schema["~run"]({ value: input }, /* @__PURE__ */ getGlobalConfig2(config$1));
323
+ return {
324
+ typed: dataset.typed,
325
+ success: !dataset.issues,
326
+ output: dataset.value,
327
+ issues: dataset.issues
328
+ };
329
+ }
330
+ var Monitor = class {
331
+ type;
332
+ schema;
333
+ poll;
334
+ webhook;
335
+ constructor(options) {
336
+ this.type = options.type;
337
+ this.schema = options.schema;
338
+ }
339
+ validateSource(source) {
340
+ const result = /* @__PURE__ */ safeParse2(this.schema, source);
341
+ if (result.success) return result.output;
342
+ throw new Error(`invalid ${this.type} monitor source`);
343
+ }
344
+ initialDelivery(source) {
345
+ this.validateSource(source);
346
+ if (this.webhook && this.webhook.preferred && this.webhook.configured()) return "webhook";
347
+ if (this.poll) return "poll";
348
+ if (this.webhook) return "webhook";
349
+ throw new Error(`source ${this.type} has no configured transport`);
350
+ }
351
+ resolveDelivery(monitor) {
352
+ let webhookHealthy = false;
353
+ if (this.webhook) webhookHealthy = this.webhook.isHealthy(monitor);
354
+ if (monitor.delivery === "webhook") {
355
+ if (this.webhook && webhookHealthy) return "webhook";
356
+ if (this.poll) return "poll";
357
+ if (this.webhook) return "webhook";
358
+ return void 0;
359
+ }
360
+ if (this.webhook && webhookHealthy) return "webhook";
361
+ if (this.poll) return "poll";
362
+ if (this.webhook) return "webhook";
363
+ return void 0;
364
+ }
365
+ async tick(context, record2) {
366
+ const fresh = await context.service.get(record2.id);
367
+ if (!fresh || !fresh.enabled) return 0;
368
+ if (this.webhook) await this.webhook.retryPending(context, this, fresh);
369
+ const current = await context.service.get(record2.id);
370
+ if (!current || !current.enabled) return 0;
371
+ const nextDelivery = this.resolveDelivery(current);
372
+ if (!nextDelivery) return 0;
373
+ if (nextDelivery !== current.delivery) {
374
+ await context.service.setDelivery(current.id, nextDelivery);
375
+ current.delivery = nextDelivery;
376
+ }
377
+ if (current.delivery !== "poll" || !this.poll) return 0;
378
+ const lastAttemptAt = Number(current.cursors.__lastAttemptAt ?? current.cursors.__lastPolledAt ?? 0);
379
+ const pollIntervalSec = current.pollIntervalSec || 60;
380
+ if (lastAttemptAt && Date.now() - lastAttemptAt < pollIntervalSec * 1e3) return 0;
381
+ return this.poll.run(context, this, current);
382
+ }
383
+ async deliver(context, record2, event, deliveryId) {
384
+ if (!this.webhook) return 0;
385
+ return this.webhook.deliver(context, this, record2, event, deliveryId);
386
+ }
387
+ unresponsive(record2, now = Date.now()) {
388
+ if (!record2.enabled) return false;
389
+ if (record2.delivery === "poll") {
390
+ if (!this.poll) return true;
391
+ return this.poll.isUnresponsive(record2, now);
392
+ }
393
+ if (!this.webhook) return true;
394
+ return this.webhook.isUnresponsive(record2, now);
395
+ }
396
+ };
397
+ var RoutedEventCursorSchema = /* @__PURE__ */ array2(/* @__PURE__ */ string2());
398
+ function eventDeliveryId(event) {
399
+ if (event.id) return event.id;
400
+ return `${event.kind}:${event.at}:${event.summary}`;
401
+ }
402
+ var activePolls = /* @__PURE__ */ new Set();
403
+ var PollMonitor = class {
404
+ constructor(options) {
405
+ this.options = options;
406
+ }
407
+ async run(context, source, record2) {
408
+ if (activePolls.has(record2.id)) return 0;
409
+ activePolls.add(record2.id);
410
+ try {
411
+ const fresh = await context.service.get(record2.id);
412
+ if (!fresh) return 0;
413
+ const sourceRecord = source.validateSource(fresh.source);
414
+ const cursorKey = source.key(sourceRecord);
415
+ const result = await this.options.run(sourceRecord, fresh.cursors[cursorKey]);
416
+ const current = await context.service.get(record2.id);
417
+ if (!current || !current.enabled) return 0;
418
+ const parsedRoutedIds = /* @__PURE__ */ safeParse2(RoutedEventCursorSchema, fresh.cursors.__routedEventIds);
419
+ let routedIds = [];
420
+ if (parsedRoutedIds.success) routedIds = parsedRoutedIds.output;
421
+ let routeFailed = false;
422
+ for (const event of result.events) {
423
+ const eventId = eventDeliveryId(event);
424
+ if (routedIds.includes(eventId)) continue;
425
+ const routed = await context.sink.deliver({ monitor: fresh, event });
426
+ if (!routed.ok) {
427
+ routeFailed = true;
428
+ console.error(`[sourcefed] route failed for ${fresh.id}: ${routed.error}`);
429
+ continue;
430
+ }
431
+ if (!routedIds.includes(eventId)) routedIds.push(eventId);
432
+ }
433
+ if (routeFailed) {
434
+ await context.service.updateCursor(fresh, "__routedEventIds", routedIds);
435
+ return 0;
436
+ }
437
+ await context.service.updateCursorValue(fresh, cursorKey, (current2) => {
438
+ if (this.options.mergeCursor) return this.options.mergeCursor(current2, result.cursor);
439
+ return result.cursor;
440
+ });
441
+ await context.service.updateCursor(fresh, "__routedEventIds", []);
442
+ if (result.terminal) await context.service.remove(fresh.id);
443
+ await context.service.updateCursor(fresh, "__lastPolledAt", Date.now());
444
+ return result.events.length;
445
+ } finally {
446
+ activePolls.delete(record2.id);
447
+ }
448
+ }
449
+ isUnresponsive(record2, now) {
450
+ const intervalMs = Math.max((record2.pollIntervalSec || 60) * 3e3, 12e4);
451
+ const lastPolledAt = Number(record2.cursors.__lastPolledAt ?? 0);
452
+ if (lastPolledAt > 0) return now - lastPolledAt > intervalMs;
453
+ return now - Date.parse(record2.createdAt) > intervalMs;
454
+ }
455
+ };
456
+ function isRecord(value) {
457
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
458
+ }
459
+ function mergeCursors(current, result) {
460
+ if (Array.isArray(current) && Array.isArray(result)) {
461
+ return [.../* @__PURE__ */ new Set([...current, ...result])];
462
+ }
463
+ if (isRecord(current) && isRecord(result)) {
464
+ const merged = { ...current };
465
+ for (const [key, value] of Object.entries(result)) {
466
+ if (value === void 0) continue;
467
+ merged[key] = key in current ? mergeCursors(current[key], value) : value;
468
+ }
469
+ return merged;
470
+ }
471
+ return result === void 0 ? current : result;
472
+ }
473
+ var WEBHOOK_STALE_MS = 9e4;
474
+ var WEBHOOK_STARTUP_GRACE_MS = 12e4;
475
+ var PendingWebhookEventSchema = /* @__PURE__ */ object2({
476
+ deliveryId: /* @__PURE__ */ string2(),
477
+ event: /* @__PURE__ */ object2({
478
+ source: /* @__PURE__ */ unknown2(),
479
+ kind: /* @__PURE__ */ string2(),
480
+ id: /* @__PURE__ */ optional(/* @__PURE__ */ string2()),
481
+ at: /* @__PURE__ */ string2(),
482
+ summary: /* @__PURE__ */ string2(),
483
+ body: /* @__PURE__ */ optional(/* @__PURE__ */ string2()),
484
+ actionable: /* @__PURE__ */ boolean2(),
485
+ terminal: /* @__PURE__ */ optional(/* @__PURE__ */ boolean2())
486
+ })
487
+ });
488
+ var PendingWebhookEventsSchema = /* @__PURE__ */ array2(PendingWebhookEventSchema);
489
+ var pendingDeliveries = /* @__PURE__ */ new Set();
490
+ var PENDING_WEBHOOK_EVENTS = "__pendingWebhookEvents";
491
+ var WebhookMonitor = class {
492
+ path;
493
+ preferred;
494
+ configured;
495
+ verify;
496
+ acknowledgeBeforeDelivery;
497
+ challenge;
498
+ deliveryId;
499
+ eventName;
500
+ parse;
501
+ updateCursor;
502
+ constructor(options) {
503
+ this.path = options.path;
504
+ this.preferred = options.preferred ?? true;
505
+ this.configured = options.configured;
506
+ this.verify = options.verify;
507
+ this.acknowledgeBeforeDelivery = options.acknowledgeBeforeDelivery ?? false;
508
+ this.challenge = options.challenge;
509
+ this.deliveryId = options.deliveryId;
510
+ this.eventName = options.eventName;
511
+ this.parse = options.parse;
512
+ this.updateCursor = options.updateCursor;
513
+ }
514
+ isHealthy(record2) {
515
+ if (!this.preferred || !this.configured()) return false;
516
+ const heartbeatAt = Number(record2.cursors.__webhookHeartbeatAt ?? 0);
517
+ if (heartbeatAt > 0) return Date.now() - heartbeatAt <= WEBHOOK_STALE_MS;
518
+ return Date.now() - Date.parse(record2.createdAt) <= WEBHOOK_STARTUP_GRACE_MS;
519
+ }
520
+ isUnresponsive(record2, now) {
521
+ const heartbeatAt = Number(record2.cursors.__webhookHeartbeatAt ?? 0);
522
+ if (heartbeatAt > 0) return now - heartbeatAt > WEBHOOK_STALE_MS;
523
+ return now - Date.parse(record2.createdAt) > WEBHOOK_STARTUP_GRACE_MS;
524
+ }
525
+ async retryPending(context, source, record2) {
526
+ const fresh = await context.service.get(record2.id);
527
+ if (!fresh || !fresh.enabled) return 0;
528
+ const parsed = /* @__PURE__ */ safeParse2(PendingWebhookEventsSchema, fresh.cursors[PENDING_WEBHOOK_EVENTS]);
529
+ if (!parsed.success) return 0;
530
+ let delivered = 0;
531
+ for (const pending of parsed.output) {
532
+ const current = await context.service.get(record2.id);
533
+ if (!current || !current.enabled) break;
534
+ const cursorKey = `webhook:${pending.deliveryId}`;
535
+ if (current.cursors[cursorKey]) {
536
+ await this.removePending(context, current, pending.deliveryId);
537
+ continue;
538
+ }
539
+ delivered += await this.deliver(context, source, current, pending.event, pending.deliveryId);
540
+ }
541
+ return delivered;
542
+ }
543
+ async addPending(context, monitor, deliveryId, event) {
544
+ await context.service.updateCursorValue(monitor, PENDING_WEBHOOK_EVENTS, (cursor) => {
545
+ const parsed = /* @__PURE__ */ safeParse2(PendingWebhookEventsSchema, cursor);
546
+ let pending = [];
547
+ if (parsed.success) pending = parsed.output;
548
+ if (pending.some((entry) => entry.deliveryId === deliveryId)) return pending;
549
+ pending.push({ deliveryId, event });
550
+ return pending;
551
+ });
552
+ }
553
+ async removePending(context, monitor, deliveryId) {
554
+ await context.service.updateCursorValue(monitor, PENDING_WEBHOOK_EVENTS, (cursor) => {
555
+ const parsed = /* @__PURE__ */ safeParse2(PendingWebhookEventsSchema, cursor);
556
+ if (!parsed.success) return [];
557
+ return parsed.output.filter((entry) => entry.deliveryId !== deliveryId);
558
+ });
559
+ }
560
+ async deliver(context, source, record2, event, deliveryId) {
561
+ const fresh = await context.service.get(record2.id);
562
+ if (!fresh || !fresh.enabled) return 0;
563
+ const freshSource = source.validateSource(fresh.source);
564
+ const eventSource = source.validateSource(event.source);
565
+ if (source.key(freshSource) !== source.key(eventSource)) return 0;
566
+ if (event.kind === "ci" && !event.actionable) return 0;
567
+ const cursorKey = `webhook:${deliveryId}`;
568
+ const reservationKey = `${fresh.id}:${deliveryId}`;
569
+ if (pendingDeliveries.has(reservationKey)) return 0;
570
+ if (fresh.cursors[cursorKey]) {
571
+ await this.removePending(context, fresh, deliveryId);
572
+ return 0;
573
+ }
574
+ pendingDeliveries.add(reservationKey);
575
+ try {
576
+ await this.addPending(context, fresh, deliveryId, event);
577
+ if (this.updateCursor) {
578
+ const sourceCursorKey = source.key(freshSource);
579
+ await context.service.updateCursorValue(fresh, sourceCursorKey, (cursor) => this.updateCursor(event, cursor));
580
+ }
581
+ const result = await context.sink.deliver({ monitor: fresh, event });
582
+ if (!result.ok) {
583
+ console.error(`[sourcefed] webhook route failed for ${fresh.id}: ${result.error}`);
584
+ return 0;
585
+ }
586
+ await context.service.setDelivery(fresh.id, "webhook");
587
+ await context.service.updateCursor(fresh, cursorKey, true);
588
+ await this.removePending(context, fresh, deliveryId);
589
+ if (event.terminal) await context.service.remove(fresh.id);
590
+ return 1;
591
+ } finally {
592
+ pendingDeliveries.delete(reservationKey);
593
+ }
594
+ }
595
+ };
596
+ var MAX_WEBHOOK_BODY_BYTES2 = 1024 * 1024;
597
+ function toMonitorEvent(event, source) {
598
+ return {
599
+ source,
600
+ kind: event.kind,
601
+ id: event.id,
602
+ at: event.at,
603
+ summary: event.summary,
604
+ body: event.body,
605
+ actionable: event.actionable,
606
+ terminal: event.terminal
607
+ };
608
+ }
609
+ var store$422;
610
+ var DEFAULT_CONFIG22 = {
611
+ lang: void 0,
612
+ message: void 0,
613
+ abortEarly: void 0,
614
+ abortPipeEarly: void 0
615
+ };
616
+ // @__NO_SIDE_EFFECTS__
617
+ function getGlobalConfig22(config$1) {
618
+ if (!config$1 && !store$422) return DEFAULT_CONFIG22;
619
+ return {
620
+ lang: config$1?.lang ?? store$422?.lang,
621
+ message: config$1?.message,
622
+ abortEarly: config$1?.abortEarly ?? store$422?.abortEarly,
623
+ abortPipeEarly: config$1?.abortPipeEarly ?? store$422?.abortPipeEarly
624
+ };
625
+ }
626
+ var store$322;
627
+ // @__NO_SIDE_EFFECTS__
628
+ function getGlobalMessage22(lang) {
629
+ return store$322?.get(lang);
630
+ }
631
+ var store$222;
632
+ // @__NO_SIDE_EFFECTS__
633
+ function getSchemaMessage22(lang) {
634
+ return store$222?.get(lang);
635
+ }
636
+ var store$122;
637
+ // @__NO_SIDE_EFFECTS__
638
+ function getSpecificMessage22(reference, lang) {
639
+ return store$122?.get(reference)?.get(lang);
640
+ }
641
+ // @__NO_SIDE_EFFECTS__
642
+ function _stringify22(input) {
643
+ const type = typeof input;
644
+ if (type === "string") return `"${input}"`;
645
+ if (type === "number" || type === "bigint" || type === "boolean") return `${input}`;
646
+ if (type === "object" || type === "function") return (input && Object.getPrototypeOf(input)?.constructor?.name) ?? "null";
647
+ return type;
648
+ }
649
+ function _addIssue22(context, label, dataset, config$1, other) {
650
+ const input = other && "input" in other ? other.input : dataset.value;
651
+ const expected = other?.expected ?? context.expects ?? null;
652
+ const received = other?.received ?? /* @__PURE__ */ _stringify22(input);
653
+ const issue = {
654
+ kind: context.kind,
655
+ type: context.type,
656
+ input,
657
+ expected,
658
+ received,
659
+ message: `Invalid ${label}: ${expected ? `Expected ${expected} but r` : "R"}eceived ${received}`,
660
+ requirement: context.requirement,
661
+ path: other?.path,
662
+ issues: other?.issues,
663
+ lang: config$1.lang,
664
+ abortEarly: config$1.abortEarly,
665
+ abortPipeEarly: config$1.abortPipeEarly
666
+ };
667
+ const isSchema = context.kind === "schema";
668
+ const message$1 = other?.message ?? context.message ?? /* @__PURE__ */ getSpecificMessage22(context.reference, issue.lang) ?? (isSchema ? /* @__PURE__ */ getSchemaMessage22(issue.lang) : null) ?? config$1.message ?? /* @__PURE__ */ getGlobalMessage22(issue.lang);
669
+ if (message$1 !== void 0) issue.message = typeof message$1 === "function" ? message$1(issue) : message$1;
670
+ if (isSchema) dataset.typed = false;
671
+ if (dataset.issues) dataset.issues.push(issue);
672
+ else dataset.issues = [issue];
673
+ }
674
+ var _standardCache22 = /* @__PURE__ */ new WeakMap();
675
+ // @__NO_SIDE_EFFECTS__
676
+ function _getStandardProps22(context) {
677
+ let cached = _standardCache22.get(context);
678
+ if (!cached) {
679
+ cached = {
680
+ version: 1,
681
+ vendor: "valibot",
682
+ validate(value$1) {
683
+ return context["~run"]({ value: value$1 }, /* @__PURE__ */ getGlobalConfig22());
684
+ }
685
+ };
686
+ _standardCache22.set(context, cached);
687
+ }
688
+ return cached;
689
+ }
690
+ var EMOJI_REGEX22 = new RegExp("^(?:[\\u{1F1E6}-\\u{1F1FF}]{2}|\\u{1F3F4}[\\u{E0061}-\\u{E007A}]{2}[\\u{E0030}-\\u{E0039}\\u{E0061}-\\u{E007A}]{1,3}\\u{E007F}|(?:\\p{Emoji}\\uFE0F\\u20E3?|\\p{Emoji_Modifier_Base}\\p{Emoji_Modifier}?|(?![\\p{Emoji_Modifier_Base}\\u{1F1E6}-\\u{1F1FF}])\\p{Emoji_Presentation})(?:\\u200D(?:\\p{Emoji}\\uFE0F\\u20E3?|\\p{Emoji_Modifier_Base}\\p{Emoji_Modifier}?|(?![\\p{Emoji_Modifier_Base}\\u{1F1E6}-\\u{1F1FF}])\\p{Emoji_Presentation}))*)+$", "u");
691
+ // @__NO_SIDE_EFFECTS__
692
+ function getFallback22(schema, dataset, config$1) {
693
+ return typeof schema.fallback === "function" ? schema.fallback(dataset, config$1) : schema.fallback;
694
+ }
695
+ // @__NO_SIDE_EFFECTS__
696
+ function getDefault22(schema, dataset, config$1) {
697
+ return typeof schema.default === "function" ? schema.default(dataset, config$1) : schema.default;
698
+ }
699
+ // @__NO_SIDE_EFFECTS__
700
+ function array22(item, message$1) {
701
+ return {
702
+ kind: "schema",
703
+ type: "array",
704
+ reference: array22,
705
+ expects: "Array",
706
+ async: false,
707
+ item,
708
+ message: message$1,
709
+ get "~standard"() {
710
+ return /* @__PURE__ */ _getStandardProps22(this);
711
+ },
712
+ "~run"(dataset, config$1) {
713
+ const input = dataset.value;
714
+ if (Array.isArray(input)) {
715
+ dataset.typed = true;
716
+ dataset.value = [];
717
+ for (let key = 0; key < input.length; key++) {
718
+ const value$1 = input[key];
719
+ const itemDataset = this.item["~run"]({ value: value$1 }, config$1);
720
+ if (itemDataset.issues) {
721
+ const pathItem = {
722
+ type: "array",
723
+ origin: "value",
724
+ input,
725
+ key,
726
+ value: value$1
727
+ };
728
+ for (const issue of itemDataset.issues) {
729
+ if (issue.path) issue.path.unshift(pathItem);
730
+ else issue.path = [pathItem];
731
+ dataset.issues?.push(issue);
732
+ }
733
+ if (!dataset.issues) dataset.issues = itemDataset.issues;
734
+ if (config$1.abortEarly) {
735
+ dataset.typed = false;
736
+ break;
737
+ }
738
+ }
739
+ if (!itemDataset.typed) dataset.typed = false;
740
+ dataset.value.push(itemDataset.value);
741
+ }
742
+ } else _addIssue22(this, "type", dataset, config$1);
743
+ return dataset;
744
+ }
745
+ };
746
+ }
747
+ // @__NO_SIDE_EFFECTS__
748
+ function boolean22(message$1) {
749
+ return {
750
+ kind: "schema",
751
+ type: "boolean",
752
+ reference: boolean22,
753
+ expects: "boolean",
754
+ async: false,
755
+ message: message$1,
756
+ get "~standard"() {
757
+ return /* @__PURE__ */ _getStandardProps22(this);
758
+ },
759
+ "~run"(dataset, config$1) {
760
+ if (typeof dataset.value === "boolean") dataset.typed = true;
761
+ else _addIssue22(this, "type", dataset, config$1);
762
+ return dataset;
763
+ }
764
+ };
765
+ }
766
+ // @__NO_SIDE_EFFECTS__
767
+ function literal(literal_, message$1) {
768
+ return {
769
+ kind: "schema",
770
+ type: "literal",
771
+ reference: literal,
772
+ expects: /* @__PURE__ */ _stringify22(literal_),
773
+ async: false,
774
+ literal: literal_,
775
+ message: message$1,
776
+ get "~standard"() {
777
+ return /* @__PURE__ */ _getStandardProps22(this);
778
+ },
779
+ "~run"(dataset, config$1) {
780
+ if (dataset.value === this.literal) dataset.typed = true;
781
+ else _addIssue22(this, "type", dataset, config$1);
782
+ return dataset;
783
+ }
784
+ };
785
+ }
786
+ // @__NO_SIDE_EFFECTS__
787
+ function number2(message$1) {
788
+ return {
789
+ kind: "schema",
790
+ type: "number",
791
+ reference: number2,
792
+ expects: "number",
793
+ async: false,
794
+ message: message$1,
795
+ get "~standard"() {
796
+ return /* @__PURE__ */ _getStandardProps22(this);
797
+ },
798
+ "~run"(dataset, config$1) {
799
+ if (typeof dataset.value === "number" && !isNaN(dataset.value)) dataset.typed = true;
800
+ else _addIssue22(this, "type", dataset, config$1);
801
+ return dataset;
802
+ }
803
+ };
804
+ }
805
+ // @__NO_SIDE_EFFECTS__
806
+ function object22(entries$1, message$1) {
807
+ return {
808
+ kind: "schema",
809
+ type: "object",
810
+ reference: object22,
811
+ expects: "Object",
812
+ async: false,
813
+ entries: entries$1,
814
+ message: message$1,
815
+ get "~standard"() {
816
+ return /* @__PURE__ */ _getStandardProps22(this);
817
+ },
818
+ "~run"(dataset, config$1) {
819
+ const input = dataset.value;
820
+ if (input && typeof input === "object") {
821
+ dataset.typed = true;
822
+ dataset.value = {};
823
+ for (const key in this.entries) {
824
+ const valueSchema = this.entries[key];
825
+ if (key in input || (valueSchema.type === "exact_optional" || valueSchema.type === "optional" || valueSchema.type === "nullish") && valueSchema.default !== void 0) {
826
+ const value$1 = key in input ? input[key] : /* @__PURE__ */ getDefault22(valueSchema);
827
+ const valueDataset = valueSchema["~run"]({ value: value$1 }, config$1);
828
+ if (valueDataset.issues) {
829
+ const pathItem = {
830
+ type: "object",
831
+ origin: "value",
832
+ input,
833
+ key,
834
+ value: value$1
835
+ };
836
+ for (const issue of valueDataset.issues) {
837
+ if (issue.path) issue.path.unshift(pathItem);
838
+ else issue.path = [pathItem];
839
+ dataset.issues?.push(issue);
840
+ }
841
+ if (!dataset.issues) dataset.issues = valueDataset.issues;
842
+ if (config$1.abortEarly) {
843
+ dataset.typed = false;
844
+ break;
845
+ }
846
+ }
847
+ if (!valueDataset.typed) dataset.typed = false;
848
+ dataset.value[key] = valueDataset.value;
849
+ } else if (valueSchema.fallback !== void 0) dataset.value[key] = /* @__PURE__ */ getFallback22(valueSchema);
850
+ else if (valueSchema.type !== "exact_optional" && valueSchema.type !== "optional" && valueSchema.type !== "nullish") {
851
+ _addIssue22(this, "key", dataset, config$1, {
852
+ input: void 0,
853
+ expected: `"${key}"`,
854
+ path: [{
855
+ type: "object",
856
+ origin: "key",
857
+ input,
858
+ key,
859
+ value: input[key]
860
+ }]
861
+ });
862
+ if (config$1.abortEarly) break;
863
+ }
864
+ }
865
+ } else _addIssue22(this, "type", dataset, config$1);
866
+ return dataset;
867
+ }
868
+ };
869
+ }
870
+ // @__NO_SIDE_EFFECTS__
871
+ function optional2(wrapped, default_) {
872
+ return {
873
+ kind: "schema",
874
+ type: "optional",
875
+ reference: optional2,
876
+ expects: `(${wrapped.expects} | undefined)`,
877
+ async: false,
878
+ wrapped,
879
+ default: default_,
880
+ get "~standard"() {
881
+ return /* @__PURE__ */ _getStandardProps22(this);
882
+ },
883
+ "~run"(dataset, config$1) {
884
+ if (dataset.value === void 0) {
885
+ if (this.default !== void 0) dataset.value = /* @__PURE__ */ getDefault22(this, dataset, config$1);
886
+ if (dataset.value === void 0) {
887
+ dataset.typed = true;
888
+ return dataset;
889
+ }
890
+ }
891
+ return this.wrapped["~run"](dataset, config$1);
892
+ }
893
+ };
894
+ }
895
+ // @__NO_SIDE_EFFECTS__
896
+ function string22(message$1) {
897
+ return {
898
+ kind: "schema",
899
+ type: "string",
900
+ reference: string22,
901
+ expects: "string",
902
+ async: false,
903
+ message: message$1,
904
+ get "~standard"() {
905
+ return /* @__PURE__ */ _getStandardProps22(this);
906
+ },
907
+ "~run"(dataset, config$1) {
908
+ if (typeof dataset.value === "string") dataset.typed = true;
909
+ else _addIssue22(this, "type", dataset, config$1);
910
+ return dataset;
911
+ }
912
+ };
913
+ }
914
+ // @__NO_SIDE_EFFECTS__
915
+ function safeParse22(schema, input, config$1) {
916
+ const dataset = schema["~run"]({ value: input }, /* @__PURE__ */ getGlobalConfig22(config$1));
917
+ return {
918
+ typed: dataset.typed,
919
+ success: !dataset.issues,
920
+ output: dataset.value,
921
+ issues: dataset.issues
922
+ };
923
+ }
924
+ var GithubSourceSchema = /* @__PURE__ */ object22({
925
+ type: /* @__PURE__ */ literal("github"),
926
+ repo: /* @__PURE__ */ string22(),
927
+ prNumber: /* @__PURE__ */ number2()
928
+ });
929
+ var GithubCursorSchema = /* @__PURE__ */ object22({
930
+ reviewIds: /* @__PURE__ */ array22(/* @__PURE__ */ string22()),
931
+ commentIds: /* @__PURE__ */ array22(/* @__PURE__ */ string22()),
932
+ historyPrimed: /* @__PURE__ */ optional2(/* @__PURE__ */ boolean22(), false),
933
+ lineCommentsPrimed: /* @__PURE__ */ optional2(/* @__PURE__ */ boolean22(), false),
934
+ conversationCommentsPrimed: /* @__PURE__ */ optional2(/* @__PURE__ */ boolean22(), false),
935
+ ciState: /* @__PURE__ */ string22(),
936
+ ciFailureState: /* @__PURE__ */ optional2(/* @__PURE__ */ string22(), ""),
937
+ mergeable: /* @__PURE__ */ string22(),
938
+ prState: /* @__PURE__ */ string22()
939
+ });
940
+ function failureRunIds(failed) {
941
+ const runIds = /* @__PURE__ */ new Set();
942
+ for (const check of failed) {
943
+ const details = check.detailsUrl ?? check.targetUrl ?? "";
944
+ const runId = String(details).match(/\/actions\/runs\/(\d+)/)?.[1];
945
+ if (runId) runIds.add(runId);
946
+ }
947
+ return runIds;
948
+ }
949
+ var ANSI_ESCAPE = /\u001B(?:\[[0-?]*[ -/]*[@-~]|\][^\u0007]*(?:\u0007|\u001B\\))/g;
950
+ var LOG_PREFIX = /^.*?\b\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z(?:\s+(?:stdout|stderr))?\s*(?:\|\s*)?/;
951
+ function cleanCiLog(raw) {
952
+ return raw.replace(/\r/g, "").replace(ANSI_ESCAPE, "").split("\n").map((line) => line.replace(LOG_PREFIX, "").trimEnd()).filter((line) => !/\bis in dev mode\. Not recommended for production!/i.test(line)).join("\n").replace(/\n{3,}/g, "\n\n").trim();
953
+ }
954
+ var API = "https://api.github.com";
955
+ var GRAPHQL = "https://api.github.com/graphql";
956
+ function githubToken() {
957
+ return process.env.GH_TOKEN ?? process.env.GITHUB_TOKEN;
958
+ }
959
+ async function githubJson(path22) {
960
+ const token = githubToken();
961
+ if (!token) return void 0;
962
+ try {
963
+ const response = await fetch(`${API}${path22}`, {
964
+ headers: { authorization: `Bearer ${token}`, "user-agent": "sourcefed", accept: "application/vnd.github+json" }
965
+ });
966
+ if (!response.ok) return void 0;
967
+ return await response.json();
968
+ } catch {
969
+ return void 0;
970
+ }
971
+ }
972
+ async function githubGraphQL(query, variables) {
973
+ const token = githubToken();
974
+ if (!token) return void 0;
975
+ try {
976
+ const response = await fetch(GRAPHQL, {
977
+ method: "POST",
978
+ headers: { authorization: `Bearer ${token}`, "user-agent": "sourcefed", "content-type": "application/json" },
979
+ body: JSON.stringify({ query, variables })
980
+ });
981
+ const body = await response.json();
982
+ if (!response.ok || body.errors) return void 0;
983
+ return body.data;
984
+ } catch {
985
+ return void 0;
986
+ }
987
+ }
988
+ var MAX_COMMENT_PAGES = 10;
989
+ async function fetchGithubComments(path22) {
990
+ const comments = [];
991
+ for (let page = 1; page <= MAX_COMMENT_PAGES; page++) {
992
+ const batch = await githubJson(`/${path22}?per_page=100&page=${page}`);
993
+ if (!Array.isArray(batch)) return void 0;
994
+ comments.push(...batch);
995
+ if (batch.length < 100) return comments;
996
+ }
997
+ return comments;
998
+ }
999
+ var API2 = "https://api.github.com";
1000
+ var MAX_LOG_BYTES = 1e7;
1001
+ var MAX_ENTRY_BYTES = 5e6;
1002
+ var MAX_TOTAL_BYTES = 5e7;
1003
+ async function fetchRunLog(repo, runId) {
1004
+ const token = githubToken();
1005
+ if (!token) return void 0;
1006
+ try {
1007
+ const response = await fetch(`${API2}/repos/${repo}/actions/runs/${runId}/logs`, {
1008
+ headers: { authorization: `Bearer ${token}`, "user-agent": "sourcefed", accept: "application/vnd.github+json" }
1009
+ });
1010
+ if (!response.ok || !response.body) return void 0;
1011
+ const length = Number(response.headers.get("content-length") ?? 0);
1012
+ if (length > MAX_LOG_BYTES) return void 0;
1013
+ const reader = response.body.getReader();
1014
+ const chunks = [];
1015
+ let total = 0;
1016
+ while (true) {
1017
+ const { done, value } = await reader.read();
1018
+ if (done) break;
1019
+ total += value.byteLength;
1020
+ if (total > MAX_LOG_BYTES) return void 0;
1021
+ chunks.push(value);
1022
+ }
1023
+ const buffer = concatChunks(chunks);
1024
+ const entries = extractZipEntries(buffer);
1025
+ return entries.map((entry) => new TextDecoder().decode(entry.content)).join("\n");
1026
+ } catch {
1027
+ return void 0;
1028
+ }
1029
+ }
1030
+ function concatChunks(chunks) {
1031
+ const total = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
1032
+ const result = new Uint8Array(total);
1033
+ let offset = 0;
1034
+ for (const chunk of chunks) {
1035
+ result.set(chunk, offset);
1036
+ offset += chunk.length;
1037
+ }
1038
+ return result;
1039
+ }
1040
+ function extractZipEntries(buffer) {
1041
+ const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
1042
+ let end = -1;
1043
+ const scanStart = Math.max(0, buffer.length - 65557);
1044
+ for (let offset = buffer.length - 22; offset >= scanStart; offset--) {
1045
+ if (view.getUint32(offset, true) === 101010256) {
1046
+ end = offset;
1047
+ break;
1048
+ }
1049
+ }
1050
+ if (end < 0) return [];
1051
+ const entryCount = view.getUint16(end + 10, true);
1052
+ let cursor = view.getUint32(end + 16, true);
1053
+ const entries = [];
1054
+ let totalBytes = 0;
1055
+ for (let index = 0; index < entryCount; index++) {
1056
+ if (view.getUint32(cursor, true) !== 33639248) break;
1057
+ const method = view.getUint16(cursor + 10, true);
1058
+ const compressedSize = view.getUint32(cursor + 20, true);
1059
+ const nameLength = view.getUint16(cursor + 28, true);
1060
+ const extraLength = view.getUint16(cursor + 30, true);
1061
+ const commentLength = view.getUint16(cursor + 32, true);
1062
+ const localOffset = view.getUint32(cursor + 42, true);
1063
+ const name = new TextDecoder().decode(buffer.slice(cursor + 46, cursor + 46 + nameLength));
1064
+ const localNameLength = view.getUint16(localOffset + 26, true);
1065
+ const localExtraLength = view.getUint16(localOffset + 28, true);
1066
+ const dataStart = localOffset + 30 + localNameLength + localExtraLength;
1067
+ const compressed = buffer.slice(dataStart, dataStart + compressedSize);
1068
+ let content;
1069
+ if (method === 8) {
1070
+ const declaredSize = view.getUint32(localOffset + 22, true);
1071
+ if (declaredSize > MAX_ENTRY_BYTES) {
1072
+ cursor += 46 + nameLength + extraLength + commentLength;
1073
+ continue;
1074
+ }
1075
+ try {
1076
+ content = inflateRawSync(compressed);
1077
+ } catch {
1078
+ content = compressed;
1079
+ }
1080
+ } else {
1081
+ content = compressed;
1082
+ }
1083
+ totalBytes += content.length;
1084
+ if (totalBytes > MAX_TOTAL_BYTES) break;
1085
+ entries.push({ name, content });
1086
+ cursor += 46 + nameLength + extraLength + commentLength;
1087
+ }
1088
+ return entries;
1089
+ }
1090
+ var PR_DATA_QUERY = `
1091
+ query PullRequestData($owner: String!, $name: String!, $number: Int!, $reviewsAfter: String, $contextsAfter: String) {
1092
+ repository(owner: $owner, name: $name) {
1093
+ pullRequest(number: $number) {
1094
+ state
1095
+ mergeable
1096
+ mergeStateStatus
1097
+ reviews(first: 100, after: $reviewsAfter) {
1098
+ nodes {
1099
+ databaseId
1100
+ author { login }
1101
+ state
1102
+ submittedAt
1103
+ body
1104
+ }
1105
+ pageInfo {
1106
+ hasNextPage
1107
+ endCursor
1108
+ }
1109
+ }
1110
+ statusCheckRollup {
1111
+ contexts(first: 100, after: $contextsAfter) {
1112
+ nodes {
1113
+ ... on StatusContext {
1114
+ context
1115
+ state
1116
+ targetUrl
1117
+ }
1118
+ ... on CheckRun {
1119
+ name
1120
+ conclusion
1121
+ detailsUrl
1122
+ }
1123
+ }
1124
+ pageInfo {
1125
+ hasNextPage
1126
+ endCursor
1127
+ }
1128
+ }
1129
+ }
1130
+ }
1131
+ }
1132
+ }`;
1133
+ var MAX_REVIEW_PAGES = 300;
1134
+ async function fetchPrData(repo, prNumber) {
1135
+ const [owner, name] = repo.split("/");
1136
+ if (!owner || !name) return void 0;
1137
+ const data = await githubGraphQL(PR_DATA_QUERY, { owner, name, number: prNumber, reviewsAfter: void 0, contextsAfter: void 0 });
1138
+ const pullRequest = data?.repository?.pullRequest;
1139
+ if (!pullRequest) return void 0;
1140
+ const reviews = [];
1141
+ let page = pullRequest.reviews;
1142
+ while (page?.nodes && page.nodes.length > 0) {
1143
+ reviews.push(...page.nodes.map((node) => ({
1144
+ id: node.databaseId,
1145
+ author: node.author,
1146
+ state: node.state,
1147
+ submittedAt: node.submittedAt,
1148
+ body: node.body
1149
+ })));
1150
+ if (!page.pageInfo?.hasNextPage || reviews.length >= MAX_REVIEW_PAGES) break;
1151
+ const next = await githubGraphQL(PR_DATA_QUERY, { owner, name, number: prNumber, reviewsAfter: page.pageInfo.endCursor, contextsAfter: void 0 });
1152
+ page = next?.repository?.pullRequest?.reviews;
1153
+ }
1154
+ const statusCheckRollup = [];
1155
+ let contextsPage = pullRequest.statusCheckRollup?.contexts;
1156
+ while (contextsPage?.nodes && contextsPage.nodes.length > 0) {
1157
+ statusCheckRollup.push(...contextsPage.nodes);
1158
+ if (!contextsPage.pageInfo?.hasNextPage) break;
1159
+ const next = await githubGraphQL(PR_DATA_QUERY, { owner, name, number: prNumber, reviewsAfter: void 0, contextsAfter: contextsPage.pageInfo.endCursor });
1160
+ contextsPage = next?.repository?.pullRequest?.statusCheckRollup?.contexts;
1161
+ }
1162
+ return {
1163
+ reviews,
1164
+ statusCheckRollup,
1165
+ mergeable: pullRequest.mergeable,
1166
+ mergeStateStatus: pullRequest.mergeStateStatus,
1167
+ state: pullRequest.state
1168
+ };
1169
+ }
1170
+ async function failureRunLog(repo, runId) {
1171
+ const log = await fetchRunLog(repo, runId);
1172
+ if (!log || !log.trim()) return void 0;
1173
+ const tail = cleanCiLog(log).split("\n").slice(-60).join("\n");
1174
+ return `
1175
+ ### Failing log (run ${runId})
1176
+ \`\`\`
1177
+ ${tail}
1178
+ \`\`\``;
1179
+ }
1180
+ function formatFailedCheck(check) {
1181
+ let name = check.name;
1182
+ if (!name) name = check.context;
1183
+ if (!name) name = "check";
1184
+ let conclusion = check.conclusion;
1185
+ if (!conclusion) conclusion = check.state;
1186
+ if (!conclusion) conclusion = "FAILURE";
1187
+ return `- ${name}: ${conclusion}`;
1188
+ }
1189
+ async function ciFailureDetail(repo, failed) {
1190
+ const lines = failed.map(formatFailedCheck);
1191
+ const runIds = failureRunIds(failed);
1192
+ for (const runId of [...runIds].slice(0, 3)) {
1193
+ const log = await failureRunLog(repo, runId);
1194
+ if (log) lines.push(log);
1195
+ }
1196
+ return lines.join("\n");
1197
+ }
1198
+ async function confirmStillFailing(repo, prNumber, failed) {
1199
+ const data = await fetchPrData(repo, prNumber);
1200
+ if (!data) return failed;
1201
+ const rollup = data.statusCheckRollup ?? [];
1202
+ const current = /* @__PURE__ */ new Map();
1203
+ for (const check of rollup) {
1204
+ let name = check.name;
1205
+ if (!name) name = check.context;
1206
+ if (!name) name = "check";
1207
+ let state = check.conclusion;
1208
+ if (!state) state = check.state;
1209
+ if (!state) state = "PENDING";
1210
+ current.set(name, String(state).toUpperCase());
1211
+ }
1212
+ return failed.filter((check) => {
1213
+ let name = check.name;
1214
+ if (!name) name = check.context;
1215
+ if (!name) name = "check";
1216
+ const state = current.get(name);
1217
+ if (state === void 0) return false;
1218
+ return /FAIL|ERROR|CANCEL/i.test(state);
1219
+ });
1220
+ }
1221
+ async function appendGithubCiEvent(repo, prNumber, cursor, events, rollup) {
1222
+ const ciState = rollup.map((check) => check.conclusion ?? check.state ?? "PENDING").sort().join(",");
1223
+ if (!ciState || ciState === cursor.ciState) return;
1224
+ const previousCiState = cursor.ciState;
1225
+ cursor.ciState = ciState;
1226
+ if (previousCiState === void 0) return;
1227
+ let failed = rollup.filter((check) => /FAIL|ERROR|CANCEL/i.test(check.conclusion ?? ""));
1228
+ failed = await confirmStillFailing(repo, prNumber, failed);
1229
+ const failureState = failed.map((check) => `${check.name ?? check.context ?? "check"}:${check.conclusion ?? check.state ?? "FAILURE"}`).sort().join(",");
1230
+ if (failed.length > 0 && failureState !== cursor.ciFailureState) {
1231
+ const names = failed.map((check) => check.name ?? check.context ?? "check").join(", ");
1232
+ events.push({
1233
+ kind: "ci",
1234
+ id: `ci:${failureState}`,
1235
+ repo,
1236
+ prNumber,
1237
+ at: (/* @__PURE__ */ new Date()).toISOString(),
1238
+ summary: `CI failed on #${prNumber}: ${names}`,
1239
+ body: await ciFailureDetail(repo, failed),
1240
+ actionable: true
1241
+ });
1242
+ }
1243
+ cursor.ciFailureState = failureState;
1244
+ }
1245
+ function appendGithubConflictEvent(repo, prNumber, cursor, events, mergeable) {
1246
+ if (mergeable === cursor.mergeable) return;
1247
+ const previousMergeable = cursor.mergeable;
1248
+ cursor.mergeable = mergeable;
1249
+ if (previousMergeable === void 0) return;
1250
+ if (mergeable !== "CONFLICTING" && mergeable !== "DIRTY") return;
1251
+ events.push({
1252
+ kind: "conflict",
1253
+ id: `conflict:${mergeable}`,
1254
+ repo,
1255
+ prNumber,
1256
+ at: (/* @__PURE__ */ new Date()).toISOString(),
1257
+ summary: `PR #${prNumber} now has merge conflicts with its base (${mergeable})`,
1258
+ actionable: true
1259
+ });
1260
+ }
1261
+ function appendGithubReviews(repo, prNumber, cursor, events, reviews, historyPrimed) {
1262
+ const ref = `#${prNumber}`;
1263
+ for (const review of reviews) {
1264
+ const id2 = "rev:" + String(review.id ?? `${review.author?.login}-${review.submittedAt}`);
1265
+ if (cursor.reviewIds.includes(id2)) continue;
1266
+ cursor.reviewIds.push(id2);
1267
+ if (review.state === "PENDING" || !historyPrimed) continue;
1268
+ if (!review.body?.trim()) continue;
1269
+ let actionable = false;
1270
+ if (review.state === "CHANGES_REQUESTED") actionable = true;
1271
+ if (review.state === "COMMENTED" && review.body?.trim()) actionable = true;
1272
+ events.push({
1273
+ kind: "review",
1274
+ id: id2,
1275
+ repo,
1276
+ prNumber,
1277
+ at: review.submittedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
1278
+ summary: `Review by ${review.author?.login ?? "someone"} [${review.state}] on ${ref}`,
1279
+ body: review.body,
1280
+ actionable
1281
+ });
1282
+ }
1283
+ }
1284
+ function appendGithubStateEvent(repo, prNumber, cursor, events, prState) {
1285
+ const isTerminal = prState === "MERGED" || prState === "CLOSED";
1286
+ if (prState === cursor.prState) return isTerminal;
1287
+ const previousPrState = cursor.prState;
1288
+ cursor.prState = prState;
1289
+ if (previousPrState !== void 0 && isTerminal) {
1290
+ let summary = `PR #${prNumber} was CLOSED without merging`;
1291
+ if (prState === "MERGED") summary = `PR #${prNumber} was MERGED`;
1292
+ events.push({
1293
+ kind: "merged",
1294
+ id: `state:${prState}`,
1295
+ repo,
1296
+ prNumber,
1297
+ at: (/* @__PURE__ */ new Date()).toISOString(),
1298
+ summary,
1299
+ actionable: prState === "MERGED",
1300
+ terminal: isTerminal
1301
+ });
1302
+ }
1303
+ return isTerminal;
1304
+ }
1305
+ function appendComments(repo, prNumber, cursor, events, comments, idPrefix, historyPrimed, summary, body) {
1306
+ for (const comment of comments) {
1307
+ const id2 = idPrefix + String(comment.id);
1308
+ if (cursor.commentIds.includes(id2)) continue;
1309
+ cursor.commentIds.push(id2);
1310
+ if (!historyPrimed) continue;
1311
+ const text = body(comment);
1312
+ events.push({
1313
+ kind: "comment",
1314
+ id: id2,
1315
+ repo,
1316
+ prNumber,
1317
+ at: comment.created_at ?? (/* @__PURE__ */ new Date()).toISOString(),
1318
+ summary: summary(comment),
1319
+ body: text,
1320
+ actionable: Boolean(text.trim())
1321
+ });
1322
+ }
1323
+ }
1324
+ function emptyGithubCursor() {
1325
+ return {
1326
+ reviewIds: [],
1327
+ commentIds: [],
1328
+ historyPrimed: false,
1329
+ lineCommentsPrimed: false,
1330
+ conversationCommentsPrimed: false,
1331
+ ciState: "",
1332
+ ciFailureState: "",
1333
+ mergeable: "",
1334
+ prState: ""
1335
+ };
1336
+ }
1337
+ var warnedMissingToken = false;
1338
+ async function pollGithub(repo, prNumber, cursorRaw) {
1339
+ const parsedCursor = /* @__PURE__ */ safeParse22(GithubCursorSchema, cursorRaw);
1340
+ let cursor = emptyGithubCursor();
1341
+ if (parsedCursor.success) cursor = parsedCursor.output;
1342
+ const events = [];
1343
+ const ref = `#${prNumber}`;
1344
+ if (!githubToken()) {
1345
+ if (!warnedMissingToken) {
1346
+ warnedMissingToken = true;
1347
+ console.warn("[sourcefed] GH_TOKEN or GITHUB_TOKEN is not set; GitHub monitors will not poll");
1348
+ }
1349
+ return { events, cursor, terminal: false };
1350
+ }
1351
+ const data = await fetchPrData(repo, prNumber);
1352
+ if (!data) return { events, cursor, terminal: false };
1353
+ let historyPrimed = cursor.historyPrimed;
1354
+ if (!historyPrimed) historyPrimed = Boolean(cursor.reviewIds.length || cursor.commentIds.length || cursor.ciState || cursor.mergeable || cursor.prState);
1355
+ const lineCommentsPrimed = cursor.lineCommentsPrimed;
1356
+ const conversationCommentsPrimed = cursor.conversationCommentsPrimed;
1357
+ appendGithubReviews(repo, prNumber, cursor, events, data.reviews ?? [], historyPrimed);
1358
+ if (!cursor.historyPrimed) cursor.historyPrimed = true;
1359
+ const lineComments = await fetchGithubComments(`repos/${repo}/pulls/${prNumber}/comments`);
1360
+ if (lineComments) {
1361
+ appendComments(
1362
+ repo,
1363
+ prNumber,
1364
+ cursor,
1365
+ events,
1366
+ lineComments,
1367
+ "c:",
1368
+ lineCommentsPrimed,
1369
+ (comment) => `Comment by ${comment.user?.login ?? "someone"} on ${comment.path ?? ref}`,
1370
+ (comment) => {
1371
+ let location = comment.path ?? "";
1372
+ if (comment.line) location += `:${comment.line}`;
1373
+ return `${location} \u2014 ${comment.body ?? ""}`;
1374
+ }
1375
+ );
1376
+ cursor.lineCommentsPrimed = true;
1377
+ }
1378
+ const conversationComments = await fetchGithubComments(`repos/${repo}/issues/${prNumber}/comments`);
1379
+ if (conversationComments) {
1380
+ appendComments(
1381
+ repo,
1382
+ prNumber,
1383
+ cursor,
1384
+ events,
1385
+ conversationComments,
1386
+ "issue:",
1387
+ conversationCommentsPrimed,
1388
+ (comment) => `Comment by ${comment.user?.login ?? "someone"} on ${ref}`,
1389
+ (comment) => comment.body ?? ""
1390
+ );
1391
+ cursor.conversationCommentsPrimed = true;
1392
+ }
1393
+ await appendGithubCiEvent(repo, prNumber, cursor, events, data.statusCheckRollup ?? []);
1394
+ const mergeable = data.mergeable ?? data.mergeStateStatus ?? "UNKNOWN";
1395
+ appendGithubConflictEvent(repo, prNumber, cursor, events, mergeable);
1396
+ const prState = data.state ?? "OPEN";
1397
+ const terminal = appendGithubStateEvent(repo, prNumber, cursor, events, prState);
1398
+ return { events, cursor, terminal };
1399
+ }
1400
+ var githubPollMonitor = new PollMonitor({
1401
+ run: async (source, cursor) => {
1402
+ const result = await pollGithub(source.repo, source.prNumber, cursor);
1403
+ return {
1404
+ cursor: result.cursor,
1405
+ events: result.events.map((event) => toMonitorEvent(event, source)),
1406
+ terminal: result.terminal
1407
+ };
1408
+ },
1409
+ mergeCursor: mergeCursors
1410
+ });
1411
+ function updateGithubCursor(event, cursorRaw) {
1412
+ const parsed = /* @__PURE__ */ safeParse22(GithubCursorSchema, cursorRaw);
1413
+ let cursor = emptyGithubCursor();
1414
+ if (parsed.success) cursor = parsed.output;
1415
+ if (event.id?.startsWith("review:")) {
1416
+ const id2 = `rev:${event.id.slice("review:".length)}`;
1417
+ if (!cursor.reviewIds.includes(id2)) cursor.reviewIds.push(id2);
1418
+ }
1419
+ if (event.id?.startsWith("c:") || event.id?.startsWith("issue:")) {
1420
+ if (!cursor.commentIds.includes(event.id)) cursor.commentIds.push(event.id);
1421
+ }
1422
+ if (event.kind === "merged") cursor.prState = "MERGED";
1423
+ if (event.kind === "closed") cursor.prState = "CLOSED";
1424
+ if (event.kind === "conflict") cursor.mergeable = "CONFLICTING";
1425
+ return cursor;
1426
+ }
1427
+ function fallbackGithubEvent({ payload, actor, repo, prNumber, eventName }) {
1428
+ return {
1429
+ kind: eventName,
1430
+ summary: `GitHub ${eventName} ${payload.action ?? "changed"} by ${actor} on ${repo}#${prNumber}`,
1431
+ actionable: true
1432
+ };
1433
+ }
1434
+ function parseCheckRun({ payload, repo, prNumber }) {
1435
+ const conclusion = payload.check_run?.conclusion ?? payload.check_run?.status ?? "changed";
1436
+ return {
1437
+ kind: "ci",
1438
+ summary: `GitHub CI ${conclusion} on ${repo}#${prNumber}`,
1439
+ body: payload.check_run?.output?.text ?? payload.check_run?.name,
1440
+ actionable: /FAIL|ERROR|CANCEL/i.test(String(conclusion))
1441
+ };
1442
+ }
1443
+ function parseCheckSuite({ payload, repo, prNumber }) {
1444
+ const conclusion = payload.check_suite?.conclusion ?? payload.check_suite?.status ?? "changed";
1445
+ return {
1446
+ kind: "ci",
1447
+ summary: `GitHub CI ${conclusion} on ${repo}#${prNumber}`,
1448
+ body: payload.check_suite?.latest_check_runs?.map((check) => check.name).join(", "),
1449
+ actionable: /FAIL|ERROR|CANCEL/i.test(String(conclusion))
1450
+ };
1451
+ }
1452
+ function parseGithubComment({ payload, actor, repo, prNumber, eventName }) {
1453
+ return {
1454
+ kind: "comment",
1455
+ id: payload.comment?.id === void 0 ? void 0 : `${eventName === "issue_comment" ? "issue:" : "c:"}${payload.comment.id}`,
1456
+ summary: `GitHub comment by ${actor} on ${repo}#${prNumber}`,
1457
+ body: payload.comment?.body,
1458
+ actionable: true
1459
+ };
1460
+ }
1461
+ function parsePullRequest({ payload, repo, prNumber }) {
1462
+ const merged = payload.action === "closed" && payload.pull_request?.merged === true;
1463
+ let kind = payload.action ?? "changed";
1464
+ if (payload.action === "synchronize") kind = "commit";
1465
+ if (merged) kind = "merged";
1466
+ let summary = `GitHub PR ${payload.action ?? "changed"}`;
1467
+ if (merged) summary += " and merged";
1468
+ summary += ` on ${repo}#${prNumber}`;
1469
+ let body = payload.pull_request?.body;
1470
+ if (merged) body = void 0;
1471
+ let id2;
1472
+ if (merged) id2 = "pr:MERGED";
1473
+ if (payload.action === "closed" && !merged) id2 = "pr:CLOSED";
1474
+ return { kind, id: id2, summary, body, actionable: true };
1475
+ }
1476
+ function parsePullRequestReview({ payload, actor, repo, prNumber }) {
1477
+ let actionable = false;
1478
+ if (["changes_requested", "commented"].includes(String(payload.review?.state ?? "").toLowerCase())) actionable = true;
1479
+ return {
1480
+ kind: "review",
1481
+ id: payload.review?.id === void 0 ? void 0 : `review:${payload.review.id}`,
1482
+ summary: `GitHub review ${payload.action ?? "changed"} by ${actor} on ${repo}#${prNumber}`,
1483
+ body: payload.review?.body,
1484
+ actionable
1485
+ };
1486
+ }
1487
+ var githubEventHandlers = {
1488
+ pull_request_review: parsePullRequestReview,
1489
+ pull_request_review_comment: parseGithubComment,
1490
+ issue_comment: parseGithubComment,
1491
+ check_run: parseCheckRun,
1492
+ check_suite: parseCheckSuite,
1493
+ pull_request: parsePullRequest
1494
+ };
1495
+ function githubPullRequestNumber(payload, eventName) {
1496
+ let value = payload.pull_request?.number;
1497
+ if (value === void 0) value = payload.issue?.number;
1498
+ if (value === void 0 && eventName === "check_run") value = payload.check_run?.pull_requests?.[0]?.number;
1499
+ if (value === void 0 && eventName === "check_suite") value = payload.check_suite?.pull_requests?.[0]?.number;
1500
+ if (value === void 0) return void 0;
1501
+ const number22 = Number(value);
1502
+ if (!Number.isInteger(number22)) return void 0;
1503
+ return number22;
1504
+ }
1505
+ function parseGithubWebhook(payload, eventName, _deliveryId) {
1506
+ const repo = payload.repository?.full_name;
1507
+ const prNumber = githubPullRequestNumber(payload, eventName);
1508
+ if (!repo || prNumber === void 0) return void 0;
1509
+ if (eventName === "pull_request" && payload.action === "edited") return void 0;
1510
+ const handler = githubEventHandlers[eventName] ?? fallbackGithubEvent;
1511
+ const actor = payload.sender?.login ?? payload.review?.user?.login ?? "someone";
1512
+ const shape = handler({ payload, actor, repo, prNumber, eventName });
1513
+ if (eventName === "pull_request_review" && !shape.body?.trim()) return void 0;
1514
+ let body = shape.body;
1515
+ if (shape.kind === "merged") body = "";
1516
+ const event = {
1517
+ source: { type: "github", repo, prNumber },
1518
+ kind: shape.kind,
1519
+ id: shape.id,
1520
+ at: payload.repository?.updated_at ?? (/* @__PURE__ */ new Date()).toISOString(),
1521
+ summary: shape.summary,
1522
+ body: body || void 0,
1523
+ actionable: shape.actionable,
1524
+ terminal: shape.kind === "merged" || shape.kind === "closed"
1525
+ };
1526
+ return event;
1527
+ }
1528
+ function verifyHmac(body, signature, secret) {
1529
+ if (!secret || !signature) return false;
1530
+ const expected = `sha256=${createHmac("sha256", secret).update(body).digest("hex")}`;
1531
+ const actual = Buffer.from(signature);
1532
+ const wanted = Buffer.from(expected);
1533
+ return actual.length === wanted.length && timingSafeEqual(actual, wanted);
1534
+ }
1535
+ function verifyGithubWebhook(body, request) {
1536
+ return verifyHmac(body, request.headers.get("x-hub-signature-256"), process.env.SOURCEFED_GITHUB_WEBHOOK_SECRET);
1537
+ }
1538
+ var githubWebhookMonitor = new WebhookMonitor({
1539
+ path: "/webhooks/github",
1540
+ preferred: true,
1541
+ configured: () => Boolean(process.env.SOURCEFED_GITHUB_WEBHOOK_SECRET),
1542
+ verify: verifyGithubWebhook,
1543
+ deliveryId: (request) => request.headers.get("x-github-delivery") ?? void 0,
1544
+ eventName: (request) => request.headers.get("x-github-event") ?? "github",
1545
+ parse: parseGithubWebhook,
1546
+ updateCursor: updateGithubCursor
1547
+ });
1548
+ var GithubMonitor = class extends Monitor {
1549
+ poll = githubPollMonitor;
1550
+ webhook = githubWebhookMonitor;
1551
+ constructor() {
1552
+ super({ type: "github", schema: GithubSourceSchema });
1553
+ }
1554
+ key = (source) => `github:${source.repo}#${source.prNumber}`;
1555
+ icon = "\u{F02A4}";
1556
+ label = (source) => `#${source.prNumber}`;
1557
+ detail = (source) => ` ${source.repo}#${source.prNumber}`;
1558
+ describe = (source) => `GitHub ${source.repo}#${source.prNumber}`;
1559
+ build(input) {
1560
+ if (input.repo && input.prNumber) return { type: "github", repo: input.repo, prNumber: input.prNumber };
1561
+ return { error: "repo and prNumber are required for a github monitor" };
1562
+ }
1563
+ };
1564
+ var store$43;
1565
+ var DEFAULT_CONFIG3 = {
1566
+ lang: void 0,
1567
+ message: void 0,
1568
+ abortEarly: void 0,
1569
+ abortPipeEarly: void 0
1570
+ };
1571
+ // @__NO_SIDE_EFFECTS__
1572
+ function getGlobalConfig3(config$1) {
1573
+ if (!config$1 && !store$43) return DEFAULT_CONFIG3;
1574
+ return {
1575
+ lang: config$1?.lang ?? store$43?.lang,
1576
+ message: config$1?.message,
1577
+ abortEarly: config$1?.abortEarly ?? store$43?.abortEarly,
1578
+ abortPipeEarly: config$1?.abortPipeEarly ?? store$43?.abortPipeEarly
1579
+ };
1580
+ }
1581
+ var store$33;
1582
+ // @__NO_SIDE_EFFECTS__
1583
+ function getGlobalMessage3(lang) {
1584
+ return store$33?.get(lang);
1585
+ }
1586
+ var store$23;
1587
+ // @__NO_SIDE_EFFECTS__
1588
+ function getSchemaMessage3(lang) {
1589
+ return store$23?.get(lang);
1590
+ }
1591
+ var store$13;
1592
+ // @__NO_SIDE_EFFECTS__
1593
+ function getSpecificMessage3(reference, lang) {
1594
+ return store$13?.get(reference)?.get(lang);
1595
+ }
1596
+ // @__NO_SIDE_EFFECTS__
1597
+ function _stringify3(input) {
1598
+ const type = typeof input;
1599
+ if (type === "string") return `"${input}"`;
1600
+ if (type === "number" || type === "bigint" || type === "boolean") return `${input}`;
1601
+ if (type === "object" || type === "function") return (input && Object.getPrototypeOf(input)?.constructor?.name) ?? "null";
1602
+ return type;
1603
+ }
1604
+ function _addIssue3(context, label, dataset, config$1, other) {
1605
+ const input = other && "input" in other ? other.input : dataset.value;
1606
+ const expected = other?.expected ?? context.expects ?? null;
1607
+ const received = other?.received ?? /* @__PURE__ */ _stringify3(input);
1608
+ const issue = {
1609
+ kind: context.kind,
1610
+ type: context.type,
1611
+ input,
1612
+ expected,
1613
+ received,
1614
+ message: `Invalid ${label}: ${expected ? `Expected ${expected} but r` : "R"}eceived ${received}`,
1615
+ requirement: context.requirement,
1616
+ path: other?.path,
1617
+ issues: other?.issues,
1618
+ lang: config$1.lang,
1619
+ abortEarly: config$1.abortEarly,
1620
+ abortPipeEarly: config$1.abortPipeEarly
1621
+ };
1622
+ const isSchema = context.kind === "schema";
1623
+ const message$1 = other?.message ?? context.message ?? /* @__PURE__ */ getSpecificMessage3(context.reference, issue.lang) ?? (isSchema ? /* @__PURE__ */ getSchemaMessage3(issue.lang) : null) ?? config$1.message ?? /* @__PURE__ */ getGlobalMessage3(issue.lang);
1624
+ if (message$1 !== void 0) issue.message = typeof message$1 === "function" ? message$1(issue) : message$1;
1625
+ if (isSchema) dataset.typed = false;
1626
+ if (dataset.issues) dataset.issues.push(issue);
1627
+ else dataset.issues = [issue];
1628
+ }
1629
+ var _standardCache3 = /* @__PURE__ */ new WeakMap();
1630
+ // @__NO_SIDE_EFFECTS__
1631
+ function _getStandardProps3(context) {
1632
+ let cached = _standardCache3.get(context);
1633
+ if (!cached) {
1634
+ cached = {
1635
+ version: 1,
1636
+ vendor: "valibot",
1637
+ validate(value$1) {
1638
+ return context["~run"]({ value: value$1 }, /* @__PURE__ */ getGlobalConfig3());
1639
+ }
1640
+ };
1641
+ _standardCache3.set(context, cached);
1642
+ }
1643
+ return cached;
1644
+ }
1645
+ var EMOJI_REGEX3 = new RegExp("^(?:[\\u{1F1E6}-\\u{1F1FF}]{2}|\\u{1F3F4}[\\u{E0061}-\\u{E007A}]{2}[\\u{E0030}-\\u{E0039}\\u{E0061}-\\u{E007A}]{1,3}\\u{E007F}|(?:\\p{Emoji}\\uFE0F\\u20E3?|\\p{Emoji_Modifier_Base}\\p{Emoji_Modifier}?|(?![\\p{Emoji_Modifier_Base}\\u{1F1E6}-\\u{1F1FF}])\\p{Emoji_Presentation})(?:\\u200D(?:\\p{Emoji}\\uFE0F\\u20E3?|\\p{Emoji_Modifier_Base}\\p{Emoji_Modifier}?|(?![\\p{Emoji_Modifier_Base}\\u{1F1E6}-\\u{1F1FF}])\\p{Emoji_Presentation}))*)+$", "u");
1646
+ // @__NO_SIDE_EFFECTS__
1647
+ function array3(item, message$1) {
1648
+ return {
1649
+ kind: "schema",
1650
+ type: "array",
1651
+ reference: array3,
1652
+ expects: "Array",
1653
+ async: false,
1654
+ item,
1655
+ message: message$1,
1656
+ get "~standard"() {
1657
+ return /* @__PURE__ */ _getStandardProps3(this);
1658
+ },
1659
+ "~run"(dataset, config$1) {
1660
+ const input = dataset.value;
1661
+ if (Array.isArray(input)) {
1662
+ dataset.typed = true;
1663
+ dataset.value = [];
1664
+ for (let key = 0; key < input.length; key++) {
1665
+ const value$1 = input[key];
1666
+ const itemDataset = this.item["~run"]({ value: value$1 }, config$1);
1667
+ if (itemDataset.issues) {
1668
+ const pathItem = {
1669
+ type: "array",
1670
+ origin: "value",
1671
+ input,
1672
+ key,
1673
+ value: value$1
1674
+ };
1675
+ for (const issue of itemDataset.issues) {
1676
+ if (issue.path) issue.path.unshift(pathItem);
1677
+ else issue.path = [pathItem];
1678
+ dataset.issues?.push(issue);
1679
+ }
1680
+ if (!dataset.issues) dataset.issues = itemDataset.issues;
1681
+ if (config$1.abortEarly) {
1682
+ dataset.typed = false;
1683
+ break;
1684
+ }
1685
+ }
1686
+ if (!itemDataset.typed) dataset.typed = false;
1687
+ dataset.value.push(itemDataset.value);
1688
+ }
1689
+ } else _addIssue3(this, "type", dataset, config$1);
1690
+ return dataset;
1691
+ }
1692
+ };
1693
+ }
1694
+ // @__NO_SIDE_EFFECTS__
1695
+ function string3(message$1) {
1696
+ return {
1697
+ kind: "schema",
1698
+ type: "string",
1699
+ reference: string3,
1700
+ expects: "string",
1701
+ async: false,
1702
+ message: message$1,
1703
+ get "~standard"() {
1704
+ return /* @__PURE__ */ _getStandardProps3(this);
1705
+ },
1706
+ "~run"(dataset, config$1) {
1707
+ if (typeof dataset.value === "string") dataset.typed = true;
1708
+ else _addIssue3(this, "type", dataset, config$1);
1709
+ return dataset;
1710
+ }
1711
+ };
1712
+ }
1713
+ // @__NO_SIDE_EFFECTS__
1714
+ function safeParse3(schema, input, config$1) {
1715
+ const dataset = schema["~run"]({ value: input }, /* @__PURE__ */ getGlobalConfig3(config$1));
1716
+ return {
1717
+ typed: dataset.typed,
1718
+ success: !dataset.issues,
1719
+ output: dataset.value,
1720
+ issues: dataset.issues
1721
+ };
1722
+ }
1723
+ var Monitor2 = class {
1724
+ type;
1725
+ schema;
1726
+ poll;
1727
+ webhook;
1728
+ constructor(options) {
1729
+ this.type = options.type;
1730
+ this.schema = options.schema;
1731
+ }
1732
+ validateSource(source) {
1733
+ const result = /* @__PURE__ */ safeParse3(this.schema, source);
1734
+ if (result.success) return result.output;
1735
+ throw new Error(`invalid ${this.type} monitor source`);
1736
+ }
1737
+ initialDelivery(source) {
1738
+ this.validateSource(source);
1739
+ if (this.webhook && this.webhook.preferred && this.webhook.configured()) return "webhook";
1740
+ if (this.poll) return "poll";
1741
+ if (this.webhook) return "webhook";
1742
+ throw new Error(`source ${this.type} has no configured transport`);
1743
+ }
1744
+ resolveDelivery(monitor) {
1745
+ let webhookHealthy = false;
1746
+ if (this.webhook) webhookHealthy = this.webhook.isHealthy(monitor);
1747
+ if (monitor.delivery === "webhook") {
1748
+ if (this.webhook && webhookHealthy) return "webhook";
1749
+ if (this.poll) return "poll";
1750
+ if (this.webhook) return "webhook";
1751
+ return void 0;
1752
+ }
1753
+ if (this.webhook && webhookHealthy) return "webhook";
1754
+ if (this.poll) return "poll";
1755
+ if (this.webhook) return "webhook";
1756
+ return void 0;
1757
+ }
1758
+ async tick(context, record2) {
1759
+ const fresh = await context.service.get(record2.id);
1760
+ if (!fresh || !fresh.enabled) return 0;
1761
+ if (this.webhook) await this.webhook.retryPending(context, this, fresh);
1762
+ const current = await context.service.get(record2.id);
1763
+ if (!current || !current.enabled) return 0;
1764
+ const nextDelivery = this.resolveDelivery(current);
1765
+ if (!nextDelivery) return 0;
1766
+ if (nextDelivery !== current.delivery) {
1767
+ await context.service.setDelivery(current.id, nextDelivery);
1768
+ current.delivery = nextDelivery;
1769
+ }
1770
+ if (current.delivery !== "poll" || !this.poll) return 0;
1771
+ const lastAttemptAt = Number(current.cursors.__lastAttemptAt ?? current.cursors.__lastPolledAt ?? 0);
1772
+ const pollIntervalSec = current.pollIntervalSec || 60;
1773
+ if (lastAttemptAt && Date.now() - lastAttemptAt < pollIntervalSec * 1e3) return 0;
1774
+ return this.poll.run(context, this, current);
1775
+ }
1776
+ async deliver(context, record2, event, deliveryId) {
1777
+ if (!this.webhook) return 0;
1778
+ return this.webhook.deliver(context, this, record2, event, deliveryId);
1779
+ }
1780
+ unresponsive(record2, now = Date.now()) {
1781
+ if (!record2.enabled) return false;
1782
+ if (record2.delivery === "poll") {
1783
+ if (!this.poll) return true;
1784
+ return this.poll.isUnresponsive(record2, now);
1785
+ }
1786
+ if (!this.webhook) return true;
1787
+ return this.webhook.isUnresponsive(record2, now);
1788
+ }
1789
+ };
1790
+ var RoutedEventCursorSchema2 = /* @__PURE__ */ array3(/* @__PURE__ */ string3());
1791
+ function eventDeliveryId2(event) {
1792
+ if (event.id) return event.id;
1793
+ return `${event.kind}:${event.at}:${event.summary}`;
1794
+ }
1795
+ var activePolls2 = /* @__PURE__ */ new Set();
1796
+ var PollMonitor2 = class {
1797
+ constructor(options) {
1798
+ this.options = options;
1799
+ }
1800
+ async run(context, source, record2) {
1801
+ if (activePolls2.has(record2.id)) return 0;
1802
+ activePolls2.add(record2.id);
1803
+ try {
1804
+ const fresh = await context.service.get(record2.id);
1805
+ if (!fresh) return 0;
1806
+ const sourceRecord = source.validateSource(fresh.source);
1807
+ const cursorKey = source.key(sourceRecord);
1808
+ const result = await this.options.run(sourceRecord, fresh.cursors[cursorKey]);
1809
+ const current = await context.service.get(record2.id);
1810
+ if (!current || !current.enabled) return 0;
1811
+ const parsedRoutedIds = /* @__PURE__ */ safeParse3(RoutedEventCursorSchema2, fresh.cursors.__routedEventIds);
1812
+ let routedIds = [];
1813
+ if (parsedRoutedIds.success) routedIds = parsedRoutedIds.output;
1814
+ let routeFailed = false;
1815
+ for (const event of result.events) {
1816
+ const eventId = eventDeliveryId2(event);
1817
+ if (routedIds.includes(eventId)) continue;
1818
+ const routed = await context.sink.deliver({ monitor: fresh, event });
1819
+ if (!routed.ok) {
1820
+ routeFailed = true;
1821
+ console.error(`[sourcefed] route failed for ${fresh.id}: ${routed.error}`);
1822
+ continue;
1823
+ }
1824
+ if (!routedIds.includes(eventId)) routedIds.push(eventId);
1825
+ }
1826
+ if (routeFailed) {
1827
+ await context.service.updateCursor(fresh, "__routedEventIds", routedIds);
1828
+ return 0;
1829
+ }
1830
+ await context.service.updateCursorValue(fresh, cursorKey, (current2) => {
1831
+ if (this.options.mergeCursor) return this.options.mergeCursor(current2, result.cursor);
1832
+ return result.cursor;
1833
+ });
1834
+ await context.service.updateCursor(fresh, "__routedEventIds", []);
1835
+ if (result.terminal) await context.service.remove(fresh.id);
1836
+ await context.service.updateCursor(fresh, "__lastPolledAt", Date.now());
1837
+ return result.events.length;
1838
+ } finally {
1839
+ activePolls2.delete(record2.id);
1840
+ }
1841
+ }
1842
+ isUnresponsive(record2, now) {
1843
+ const intervalMs = Math.max((record2.pollIntervalSec || 60) * 3e3, 12e4);
1844
+ const lastPolledAt = Number(record2.cursors.__lastPolledAt ?? 0);
1845
+ if (lastPolledAt > 0) return now - lastPolledAt > intervalMs;
1846
+ return now - Date.parse(record2.createdAt) > intervalMs;
1847
+ }
1848
+ };
1849
+ var MAX_WEBHOOK_BODY_BYTES3 = 1024 * 1024;
1850
+ function toMonitorEvent2(event, source) {
1851
+ return {
1852
+ source,
1853
+ kind: event.kind,
1854
+ id: event.id,
1855
+ at: event.at,
1856
+ summary: event.summary,
1857
+ body: event.body,
1858
+ actionable: event.actionable,
1859
+ terminal: event.terminal
1860
+ };
1861
+ }
1862
+ var store$423;
1863
+ var DEFAULT_CONFIG23 = {
1864
+ lang: void 0,
1865
+ message: void 0,
1866
+ abortEarly: void 0,
1867
+ abortPipeEarly: void 0
1868
+ };
1869
+ // @__NO_SIDE_EFFECTS__
1870
+ function getGlobalConfig23(config$1) {
1871
+ if (!config$1 && !store$423) return DEFAULT_CONFIG23;
1872
+ return {
1873
+ lang: config$1?.lang ?? store$423?.lang,
1874
+ message: config$1?.message,
1875
+ abortEarly: config$1?.abortEarly ?? store$423?.abortEarly,
1876
+ abortPipeEarly: config$1?.abortPipeEarly ?? store$423?.abortPipeEarly
1877
+ };
1878
+ }
1879
+ var store$323;
1880
+ // @__NO_SIDE_EFFECTS__
1881
+ function getGlobalMessage23(lang) {
1882
+ return store$323?.get(lang);
1883
+ }
1884
+ var store$223;
1885
+ // @__NO_SIDE_EFFECTS__
1886
+ function getSchemaMessage23(lang) {
1887
+ return store$223?.get(lang);
1888
+ }
1889
+ var store$123;
1890
+ // @__NO_SIDE_EFFECTS__
1891
+ function getSpecificMessage23(reference, lang) {
1892
+ return store$123?.get(reference)?.get(lang);
1893
+ }
1894
+ // @__NO_SIDE_EFFECTS__
1895
+ function _stringify23(input) {
1896
+ const type = typeof input;
1897
+ if (type === "string") return `"${input}"`;
1898
+ if (type === "number" || type === "bigint" || type === "boolean") return `${input}`;
1899
+ if (type === "object" || type === "function") return (input && Object.getPrototypeOf(input)?.constructor?.name) ?? "null";
1900
+ return type;
1901
+ }
1902
+ function _addIssue23(context, label, dataset, config$1, other) {
1903
+ const input = other && "input" in other ? other.input : dataset.value;
1904
+ const expected = other?.expected ?? context.expects ?? null;
1905
+ const received = other?.received ?? /* @__PURE__ */ _stringify23(input);
1906
+ const issue = {
1907
+ kind: context.kind,
1908
+ type: context.type,
1909
+ input,
1910
+ expected,
1911
+ received,
1912
+ message: `Invalid ${label}: ${expected ? `Expected ${expected} but r` : "R"}eceived ${received}`,
1913
+ requirement: context.requirement,
1914
+ path: other?.path,
1915
+ issues: other?.issues,
1916
+ lang: config$1.lang,
1917
+ abortEarly: config$1.abortEarly,
1918
+ abortPipeEarly: config$1.abortPipeEarly
1919
+ };
1920
+ const isSchema = context.kind === "schema";
1921
+ const message$1 = other?.message ?? context.message ?? /* @__PURE__ */ getSpecificMessage23(context.reference, issue.lang) ?? (isSchema ? /* @__PURE__ */ getSchemaMessage23(issue.lang) : null) ?? config$1.message ?? /* @__PURE__ */ getGlobalMessage23(issue.lang);
1922
+ if (message$1 !== void 0) issue.message = typeof message$1 === "function" ? message$1(issue) : message$1;
1923
+ if (isSchema) dataset.typed = false;
1924
+ if (dataset.issues) dataset.issues.push(issue);
1925
+ else dataset.issues = [issue];
1926
+ }
1927
+ var _standardCache23 = /* @__PURE__ */ new WeakMap();
1928
+ // @__NO_SIDE_EFFECTS__
1929
+ function _getStandardProps23(context) {
1930
+ let cached = _standardCache23.get(context);
1931
+ if (!cached) {
1932
+ cached = {
1933
+ version: 1,
1934
+ vendor: "valibot",
1935
+ validate(value$1) {
1936
+ return context["~run"]({ value: value$1 }, /* @__PURE__ */ getGlobalConfig23());
1937
+ }
1938
+ };
1939
+ _standardCache23.set(context, cached);
1940
+ }
1941
+ return cached;
1942
+ }
1943
+ var EMOJI_REGEX23 = new RegExp("^(?:[\\u{1F1E6}-\\u{1F1FF}]{2}|\\u{1F3F4}[\\u{E0061}-\\u{E007A}]{2}[\\u{E0030}-\\u{E0039}\\u{E0061}-\\u{E007A}]{1,3}\\u{E007F}|(?:\\p{Emoji}\\uFE0F\\u20E3?|\\p{Emoji_Modifier_Base}\\p{Emoji_Modifier}?|(?![\\p{Emoji_Modifier_Base}\\u{1F1E6}-\\u{1F1FF}])\\p{Emoji_Presentation})(?:\\u200D(?:\\p{Emoji}\\uFE0F\\u20E3?|\\p{Emoji_Modifier_Base}\\p{Emoji_Modifier}?|(?![\\p{Emoji_Modifier_Base}\\u{1F1E6}-\\u{1F1FF}])\\p{Emoji_Presentation}))*)+$", "u");
1944
+ // @__NO_SIDE_EFFECTS__
1945
+ function getFallback3(schema, dataset, config$1) {
1946
+ return typeof schema.fallback === "function" ? schema.fallback(dataset, config$1) : schema.fallback;
1947
+ }
1948
+ // @__NO_SIDE_EFFECTS__
1949
+ function getDefault3(schema, dataset, config$1) {
1950
+ return typeof schema.default === "function" ? schema.default(dataset, config$1) : schema.default;
1951
+ }
1952
+ // @__NO_SIDE_EFFECTS__
1953
+ function array23(item, message$1) {
1954
+ return {
1955
+ kind: "schema",
1956
+ type: "array",
1957
+ reference: array23,
1958
+ expects: "Array",
1959
+ async: false,
1960
+ item,
1961
+ message: message$1,
1962
+ get "~standard"() {
1963
+ return /* @__PURE__ */ _getStandardProps23(this);
1964
+ },
1965
+ "~run"(dataset, config$1) {
1966
+ const input = dataset.value;
1967
+ if (Array.isArray(input)) {
1968
+ dataset.typed = true;
1969
+ dataset.value = [];
1970
+ for (let key = 0; key < input.length; key++) {
1971
+ const value$1 = input[key];
1972
+ const itemDataset = this.item["~run"]({ value: value$1 }, config$1);
1973
+ if (itemDataset.issues) {
1974
+ const pathItem = {
1975
+ type: "array",
1976
+ origin: "value",
1977
+ input,
1978
+ key,
1979
+ value: value$1
1980
+ };
1981
+ for (const issue of itemDataset.issues) {
1982
+ if (issue.path) issue.path.unshift(pathItem);
1983
+ else issue.path = [pathItem];
1984
+ dataset.issues?.push(issue);
1985
+ }
1986
+ if (!dataset.issues) dataset.issues = itemDataset.issues;
1987
+ if (config$1.abortEarly) {
1988
+ dataset.typed = false;
1989
+ break;
1990
+ }
1991
+ }
1992
+ if (!itemDataset.typed) dataset.typed = false;
1993
+ dataset.value.push(itemDataset.value);
1994
+ }
1995
+ } else _addIssue23(this, "type", dataset, config$1);
1996
+ return dataset;
1997
+ }
1998
+ };
1999
+ }
2000
+ // @__NO_SIDE_EFFECTS__
2001
+ function literal2(literal_, message$1) {
2002
+ return {
2003
+ kind: "schema",
2004
+ type: "literal",
2005
+ reference: literal2,
2006
+ expects: /* @__PURE__ */ _stringify23(literal_),
2007
+ async: false,
2008
+ literal: literal_,
2009
+ message: message$1,
2010
+ get "~standard"() {
2011
+ return /* @__PURE__ */ _getStandardProps23(this);
2012
+ },
2013
+ "~run"(dataset, config$1) {
2014
+ if (dataset.value === this.literal) dataset.typed = true;
2015
+ else _addIssue23(this, "type", dataset, config$1);
2016
+ return dataset;
2017
+ }
2018
+ };
2019
+ }
2020
+ // @__NO_SIDE_EFFECTS__
2021
+ function number3(message$1) {
2022
+ return {
2023
+ kind: "schema",
2024
+ type: "number",
2025
+ reference: number3,
2026
+ expects: "number",
2027
+ async: false,
2028
+ message: message$1,
2029
+ get "~standard"() {
2030
+ return /* @__PURE__ */ _getStandardProps23(this);
2031
+ },
2032
+ "~run"(dataset, config$1) {
2033
+ if (typeof dataset.value === "number" && !isNaN(dataset.value)) dataset.typed = true;
2034
+ else _addIssue23(this, "type", dataset, config$1);
2035
+ return dataset;
2036
+ }
2037
+ };
2038
+ }
2039
+ // @__NO_SIDE_EFFECTS__
2040
+ function object3(entries$1, message$1) {
2041
+ return {
2042
+ kind: "schema",
2043
+ type: "object",
2044
+ reference: object3,
2045
+ expects: "Object",
2046
+ async: false,
2047
+ entries: entries$1,
2048
+ message: message$1,
2049
+ get "~standard"() {
2050
+ return /* @__PURE__ */ _getStandardProps23(this);
2051
+ },
2052
+ "~run"(dataset, config$1) {
2053
+ const input = dataset.value;
2054
+ if (input && typeof input === "object") {
2055
+ dataset.typed = true;
2056
+ dataset.value = {};
2057
+ for (const key in this.entries) {
2058
+ const valueSchema = this.entries[key];
2059
+ if (key in input || (valueSchema.type === "exact_optional" || valueSchema.type === "optional" || valueSchema.type === "nullish") && valueSchema.default !== void 0) {
2060
+ const value$1 = key in input ? input[key] : /* @__PURE__ */ getDefault3(valueSchema);
2061
+ const valueDataset = valueSchema["~run"]({ value: value$1 }, config$1);
2062
+ if (valueDataset.issues) {
2063
+ const pathItem = {
2064
+ type: "object",
2065
+ origin: "value",
2066
+ input,
2067
+ key,
2068
+ value: value$1
2069
+ };
2070
+ for (const issue of valueDataset.issues) {
2071
+ if (issue.path) issue.path.unshift(pathItem);
2072
+ else issue.path = [pathItem];
2073
+ dataset.issues?.push(issue);
2074
+ }
2075
+ if (!dataset.issues) dataset.issues = valueDataset.issues;
2076
+ if (config$1.abortEarly) {
2077
+ dataset.typed = false;
2078
+ break;
2079
+ }
2080
+ }
2081
+ if (!valueDataset.typed) dataset.typed = false;
2082
+ dataset.value[key] = valueDataset.value;
2083
+ } else if (valueSchema.fallback !== void 0) dataset.value[key] = /* @__PURE__ */ getFallback3(valueSchema);
2084
+ else if (valueSchema.type !== "exact_optional" && valueSchema.type !== "optional" && valueSchema.type !== "nullish") {
2085
+ _addIssue23(this, "key", dataset, config$1, {
2086
+ input: void 0,
2087
+ expected: `"${key}"`,
2088
+ path: [{
2089
+ type: "object",
2090
+ origin: "key",
2091
+ input,
2092
+ key,
2093
+ value: input[key]
2094
+ }]
2095
+ });
2096
+ if (config$1.abortEarly) break;
2097
+ }
2098
+ }
2099
+ } else _addIssue23(this, "type", dataset, config$1);
2100
+ return dataset;
2101
+ }
2102
+ };
2103
+ }
2104
+ // @__NO_SIDE_EFFECTS__
2105
+ function optional3(wrapped, default_) {
2106
+ return {
2107
+ kind: "schema",
2108
+ type: "optional",
2109
+ reference: optional3,
2110
+ expects: `(${wrapped.expects} | undefined)`,
2111
+ async: false,
2112
+ wrapped,
2113
+ default: default_,
2114
+ get "~standard"() {
2115
+ return /* @__PURE__ */ _getStandardProps23(this);
2116
+ },
2117
+ "~run"(dataset, config$1) {
2118
+ if (dataset.value === void 0) {
2119
+ if (this.default !== void 0) dataset.value = /* @__PURE__ */ getDefault3(this, dataset, config$1);
2120
+ if (dataset.value === void 0) {
2121
+ dataset.typed = true;
2122
+ return dataset;
2123
+ }
2124
+ }
2125
+ return this.wrapped["~run"](dataset, config$1);
2126
+ }
2127
+ };
2128
+ }
2129
+ // @__NO_SIDE_EFFECTS__
2130
+ function string23(message$1) {
2131
+ return {
2132
+ kind: "schema",
2133
+ type: "string",
2134
+ reference: string23,
2135
+ expects: "string",
2136
+ async: false,
2137
+ message: message$1,
2138
+ get "~standard"() {
2139
+ return /* @__PURE__ */ _getStandardProps23(this);
2140
+ },
2141
+ "~run"(dataset, config$1) {
2142
+ if (typeof dataset.value === "string") dataset.typed = true;
2143
+ else _addIssue23(this, "type", dataset, config$1);
2144
+ return dataset;
2145
+ }
2146
+ };
2147
+ }
2148
+ // @__NO_SIDE_EFFECTS__
2149
+ function safeParse23(schema, input, config$1) {
2150
+ const dataset = schema["~run"]({ value: input }, /* @__PURE__ */ getGlobalConfig23(config$1));
2151
+ return {
2152
+ typed: dataset.typed,
2153
+ success: !dataset.issues,
2154
+ output: dataset.value,
2155
+ issues: dataset.issues
2156
+ };
2157
+ }
2158
+ var JiraSourceSchema = /* @__PURE__ */ object3({
2159
+ type: /* @__PURE__ */ literal2("jira"),
2160
+ issueKey: /* @__PURE__ */ string23()
2161
+ });
2162
+ var JiraCursorSchema = /* @__PURE__ */ object3({
2163
+ commentIds: /* @__PURE__ */ array23(/* @__PURE__ */ number3()),
2164
+ descriptionVersion: /* @__PURE__ */ optional3(/* @__PURE__ */ string23()),
2165
+ changelogCount: /* @__PURE__ */ number3()
2166
+ });
2167
+ function jiraChangeValue(item, side) {
2168
+ let value = item.toString ?? item.to;
2169
+ if (side === "from") value = item.fromString ?? item.from;
2170
+ if (value === void 0 || value === null || value === "") return "none";
2171
+ return String(value);
2172
+ }
2173
+ function formatJiraChangelogItem(item) {
2174
+ return `${item.field}: ${jiraChangeValue(item, "from")} \u2192 ${jiraChangeValue(item, "to")}`;
2175
+ }
2176
+ function isTerminalStatus(status) {
2177
+ if (typeof status !== "string") return false;
2178
+ const terminalStatus = process.env.SOURCEFED_JIRA_TERMINAL_STATUS;
2179
+ if (!terminalStatus) return false;
2180
+ return status.trim().toLowerCase() === terminalStatus.trim().toLowerCase();
2181
+ }
2182
+ var TRACKED_CHANGE_FIELDS = /* @__PURE__ */ new Set(["summary", "status", "assignee", "priority", "labels"]);
2183
+ function meaningfulChangelogItems(items) {
2184
+ return items.filter((item) => {
2185
+ if (!TRACKED_CHANGE_FIELDS.has(String(item.field))) return false;
2186
+ return jiraChangeValue(item, "from") !== jiraChangeValue(item, "to");
2187
+ });
2188
+ }
2189
+ function jiraChangelogEvent(issueKey, history) {
2190
+ const changes = meaningfulChangelogItems(history.items ?? []);
2191
+ const items = changes.map(formatJiraChangelogItem).join("; ");
2192
+ if (!items) return void 0;
2193
+ const terminal = changes.some((item) => item.field === "status" && isTerminalStatus(jiraChangeValue(item, "to")));
2194
+ return {
2195
+ kind: "changelog",
2196
+ id: `history:${history.id ?? history.created ?? items}`,
2197
+ issueKey,
2198
+ at: history.created ?? (/* @__PURE__ */ new Date()).toISOString(),
2199
+ summary: `Jira ${issueKey} ${items}`,
2200
+ body: items,
2201
+ author: history.author?.displayName,
2202
+ actionable: true,
2203
+ terminal
2204
+ };
2205
+ }
2206
+ function appendJiraChangelog(issueKey, cursor, events, histories) {
2207
+ const total = histories.length;
2208
+ if (cursor.changelogCount !== 0 && total > cursor.changelogCount) {
2209
+ for (const history of histories.slice(cursor.changelogCount)) {
2210
+ const event = jiraChangelogEvent(issueKey, history);
2211
+ if (event) events.push(event);
2212
+ }
2213
+ }
2214
+ cursor.changelogCount = total;
2215
+ }
2216
+ function adfText(node) {
2217
+ if (!node || typeof node !== "object") return "";
2218
+ if (typeof node.text === "string") return node.text;
2219
+ if (Array.isArray(node.content)) return node.content.map(adfText).join("");
2220
+ return "";
2221
+ }
2222
+ function appendJiraComments(issueKey, cursor, events, comments) {
2223
+ const freshComments = [];
2224
+ for (const comment of comments) {
2225
+ const id2 = Number(comment.id);
2226
+ if (cursor.commentIds.includes(id2)) continue;
2227
+ freshComments.push(comment);
2228
+ }
2229
+ if (cursor.commentIds.length === 0) {
2230
+ cursor.commentIds = freshComments.map((comment) => Number(comment.id));
2231
+ return;
2232
+ }
2233
+ for (const comment of freshComments) {
2234
+ const body = adfText(comment.body);
2235
+ events.push({
2236
+ kind: "comment",
2237
+ id: `comment:${comment.id}`,
2238
+ issueKey,
2239
+ at: comment.created ?? (/* @__PURE__ */ new Date()).toISOString(),
2240
+ summary: `Jira ${issueKey} comment by ${comment.author?.displayName ?? "someone"}`,
2241
+ body,
2242
+ author: comment.author?.displayName,
2243
+ actionable: true
2244
+ });
2245
+ cursor.commentIds.push(Number(comment.id));
2246
+ }
2247
+ }
2248
+ function appendJiraDescription(issueKey, cursor, events, description) {
2249
+ let descriptionVersion = "none";
2250
+ if (description) descriptionVersion = JSON.stringify(description).slice(0, 40);
2251
+ if (cursor.descriptionVersion !== void 0 && descriptionVersion !== cursor.descriptionVersion) {
2252
+ events.push({
2253
+ kind: "description",
2254
+ id: `description:${descriptionVersion}`,
2255
+ issueKey,
2256
+ at: (/* @__PURE__ */ new Date()).toISOString(),
2257
+ summary: `Jira ${issueKey} description updated`,
2258
+ body: adfText(description),
2259
+ actionable: true
2260
+ });
2261
+ }
2262
+ cursor.descriptionVersion = descriptionVersion;
2263
+ }
2264
+ function emptyJiraCursor() {
2265
+ return { commentIds: [], descriptionVersion: void 0, changelogCount: 0 };
2266
+ }
2267
+ function jiraAuth() {
2268
+ const email = process.env.ATLASSIAN_EMAIL;
2269
+ const key = process.env.ATLASSIAN_API_KEY;
2270
+ if (!email || !key) throw new Error("ATLASSIAN_EMAIL / ATLASSIAN_API_KEY not set");
2271
+ return "Basic " + Buffer.from(`${email}:${key}`).toString("base64");
2272
+ }
2273
+ var JIRA_BASE = process.env.SOURCEFED_JIRA_BASE_URL;
2274
+ async function jiraFetch(path22) {
2275
+ if (!JIRA_BASE) throw new Error("SOURCEFED_JIRA_BASE_URL is required for Jira monitors");
2276
+ const response = await fetch(`${JIRA_BASE}/rest/api/3${path22}`, {
2277
+ headers: { Authorization: jiraAuth(), Accept: "application/json" }
2278
+ });
2279
+ if (!response.ok) throw new Error(`Jira ${response.status} for ${path22}`);
2280
+ return response.json();
2281
+ }
2282
+ async function pollJira(issueKey, cursorRaw) {
2283
+ const parsedCursor = /* @__PURE__ */ safeParse23(JiraCursorSchema, cursorRaw);
2284
+ let cursor = emptyJiraCursor();
2285
+ if (parsedCursor.success) cursor = parsedCursor.output;
2286
+ const events = [];
2287
+ const comments = await jiraFetch(`/issue/${issueKey}/comment?maxResults=50&orderBy=-created`);
2288
+ appendJiraComments(issueKey, cursor, events, comments.comments ?? []);
2289
+ const issue = await jiraFetch(`/issue/${issueKey}?expand=changelog&fields=status,description`);
2290
+ const statusName = issue.fields?.status?.name;
2291
+ appendJiraDescription(issueKey, cursor, events, issue.fields?.description);
2292
+ const changelog = issue.changelog?.histories ?? [];
2293
+ appendJiraChangelog(issueKey, cursor, events, changelog);
2294
+ return { events, cursor, terminal: isTerminalStatus(statusName) };
2295
+ }
2296
+ var jiraPollMonitor = new PollMonitor2({
2297
+ run: async (source, cursor) => {
2298
+ const result = await pollJira(source.issueKey, cursor);
2299
+ return {
2300
+ cursor: result.cursor,
2301
+ events: result.events.map((event) => toMonitorEvent2(event, source)),
2302
+ terminal: result.terminal
2303
+ };
2304
+ }
2305
+ });
2306
+ var JiraMonitor = class extends Monitor2 {
2307
+ poll = jiraPollMonitor;
2308
+ constructor() {
2309
+ super({ type: "jira", schema: JiraSourceSchema });
2310
+ }
2311
+ key = (source) => `jira:${source.issueKey}`;
2312
+ icon = "\u{F0303}";
2313
+ label = (source) => source.issueKey;
2314
+ detail = (source) => ` ${source.issueKey}`;
2315
+ describe = (source) => `Jira ${source.issueKey}`;
2316
+ build(input) {
2317
+ if (input.issueKey) return { type: "jira", issueKey: input.issueKey };
2318
+ return { error: "issueKey is required for a jira monitor" };
2319
+ }
2320
+ };
2321
+ var store$44;
2322
+ var DEFAULT_CONFIG4 = {
2323
+ lang: void 0,
2324
+ message: void 0,
2325
+ abortEarly: void 0,
2326
+ abortPipeEarly: void 0
2327
+ };
2328
+ // @__NO_SIDE_EFFECTS__
2329
+ function getGlobalConfig4(config$1) {
2330
+ if (!config$1 && !store$44) return DEFAULT_CONFIG4;
2331
+ return {
2332
+ lang: config$1?.lang ?? store$44?.lang,
2333
+ message: config$1?.message,
2334
+ abortEarly: config$1?.abortEarly ?? store$44?.abortEarly,
2335
+ abortPipeEarly: config$1?.abortPipeEarly ?? store$44?.abortPipeEarly
2336
+ };
2337
+ }
2338
+ var store$34;
2339
+ // @__NO_SIDE_EFFECTS__
2340
+ function getGlobalMessage4(lang) {
2341
+ return store$34?.get(lang);
2342
+ }
2343
+ var store$24;
2344
+ // @__NO_SIDE_EFFECTS__
2345
+ function getSchemaMessage4(lang) {
2346
+ return store$24?.get(lang);
2347
+ }
2348
+ var store$14;
2349
+ // @__NO_SIDE_EFFECTS__
2350
+ function getSpecificMessage4(reference, lang) {
2351
+ return store$14?.get(reference)?.get(lang);
2352
+ }
2353
+ // @__NO_SIDE_EFFECTS__
2354
+ function _stringify4(input) {
2355
+ const type = typeof input;
2356
+ if (type === "string") return `"${input}"`;
2357
+ if (type === "number" || type === "bigint" || type === "boolean") return `${input}`;
2358
+ if (type === "object" || type === "function") return (input && Object.getPrototypeOf(input)?.constructor?.name) ?? "null";
2359
+ return type;
2360
+ }
2361
+ function _addIssue4(context, label, dataset, config$1, other) {
2362
+ const input = other && "input" in other ? other.input : dataset.value;
2363
+ const expected = other?.expected ?? context.expects ?? null;
2364
+ const received = other?.received ?? /* @__PURE__ */ _stringify4(input);
2365
+ const issue = {
2366
+ kind: context.kind,
2367
+ type: context.type,
2368
+ input,
2369
+ expected,
2370
+ received,
2371
+ message: `Invalid ${label}: ${expected ? `Expected ${expected} but r` : "R"}eceived ${received}`,
2372
+ requirement: context.requirement,
2373
+ path: other?.path,
2374
+ issues: other?.issues,
2375
+ lang: config$1.lang,
2376
+ abortEarly: config$1.abortEarly,
2377
+ abortPipeEarly: config$1.abortPipeEarly
2378
+ };
2379
+ const isSchema = context.kind === "schema";
2380
+ const message$1 = other?.message ?? context.message ?? /* @__PURE__ */ getSpecificMessage4(context.reference, issue.lang) ?? (isSchema ? /* @__PURE__ */ getSchemaMessage4(issue.lang) : null) ?? config$1.message ?? /* @__PURE__ */ getGlobalMessage4(issue.lang);
2381
+ if (message$1 !== void 0) issue.message = typeof message$1 === "function" ? message$1(issue) : message$1;
2382
+ if (isSchema) dataset.typed = false;
2383
+ if (dataset.issues) dataset.issues.push(issue);
2384
+ else dataset.issues = [issue];
2385
+ }
2386
+ var _standardCache4 = /* @__PURE__ */ new WeakMap();
2387
+ // @__NO_SIDE_EFFECTS__
2388
+ function _getStandardProps4(context) {
2389
+ let cached = _standardCache4.get(context);
2390
+ if (!cached) {
2391
+ cached = {
2392
+ version: 1,
2393
+ vendor: "valibot",
2394
+ validate(value$1) {
2395
+ return context["~run"]({ value: value$1 }, /* @__PURE__ */ getGlobalConfig4());
2396
+ }
2397
+ };
2398
+ _standardCache4.set(context, cached);
2399
+ }
2400
+ return cached;
2401
+ }
2402
+ var EMOJI_REGEX4 = new RegExp("^(?:[\\u{1F1E6}-\\u{1F1FF}]{2}|\\u{1F3F4}[\\u{E0061}-\\u{E007A}]{2}[\\u{E0030}-\\u{E0039}\\u{E0061}-\\u{E007A}]{1,3}\\u{E007F}|(?:\\p{Emoji}\\uFE0F\\u20E3?|\\p{Emoji_Modifier_Base}\\p{Emoji_Modifier}?|(?![\\p{Emoji_Modifier_Base}\\u{1F1E6}-\\u{1F1FF}])\\p{Emoji_Presentation})(?:\\u200D(?:\\p{Emoji}\\uFE0F\\u20E3?|\\p{Emoji_Modifier_Base}\\p{Emoji_Modifier}?|(?![\\p{Emoji_Modifier_Base}\\u{1F1E6}-\\u{1F1FF}])\\p{Emoji_Presentation}))*)+$", "u");
2403
+ // @__NO_SIDE_EFFECTS__
2404
+ function getFallback4(schema, dataset, config$1) {
2405
+ return typeof schema.fallback === "function" ? schema.fallback(dataset, config$1) : schema.fallback;
2406
+ }
2407
+ // @__NO_SIDE_EFFECTS__
2408
+ function getDefault4(schema, dataset, config$1) {
2409
+ return typeof schema.default === "function" ? schema.default(dataset, config$1) : schema.default;
2410
+ }
2411
+ // @__NO_SIDE_EFFECTS__
2412
+ function array4(item, message$1) {
2413
+ return {
2414
+ kind: "schema",
2415
+ type: "array",
2416
+ reference: array4,
2417
+ expects: "Array",
2418
+ async: false,
2419
+ item,
2420
+ message: message$1,
2421
+ get "~standard"() {
2422
+ return /* @__PURE__ */ _getStandardProps4(this);
2423
+ },
2424
+ "~run"(dataset, config$1) {
2425
+ const input = dataset.value;
2426
+ if (Array.isArray(input)) {
2427
+ dataset.typed = true;
2428
+ dataset.value = [];
2429
+ for (let key = 0; key < input.length; key++) {
2430
+ const value$1 = input[key];
2431
+ const itemDataset = this.item["~run"]({ value: value$1 }, config$1);
2432
+ if (itemDataset.issues) {
2433
+ const pathItem = {
2434
+ type: "array",
2435
+ origin: "value",
2436
+ input,
2437
+ key,
2438
+ value: value$1
2439
+ };
2440
+ for (const issue of itemDataset.issues) {
2441
+ if (issue.path) issue.path.unshift(pathItem);
2442
+ else issue.path = [pathItem];
2443
+ dataset.issues?.push(issue);
2444
+ }
2445
+ if (!dataset.issues) dataset.issues = itemDataset.issues;
2446
+ if (config$1.abortEarly) {
2447
+ dataset.typed = false;
2448
+ break;
2449
+ }
2450
+ }
2451
+ if (!itemDataset.typed) dataset.typed = false;
2452
+ dataset.value.push(itemDataset.value);
2453
+ }
2454
+ } else _addIssue4(this, "type", dataset, config$1);
2455
+ return dataset;
2456
+ }
2457
+ };
2458
+ }
2459
+ // @__NO_SIDE_EFFECTS__
2460
+ function boolean3(message$1) {
2461
+ return {
2462
+ kind: "schema",
2463
+ type: "boolean",
2464
+ reference: boolean3,
2465
+ expects: "boolean",
2466
+ async: false,
2467
+ message: message$1,
2468
+ get "~standard"() {
2469
+ return /* @__PURE__ */ _getStandardProps4(this);
2470
+ },
2471
+ "~run"(dataset, config$1) {
2472
+ if (typeof dataset.value === "boolean") dataset.typed = true;
2473
+ else _addIssue4(this, "type", dataset, config$1);
2474
+ return dataset;
2475
+ }
2476
+ };
2477
+ }
2478
+ // @__NO_SIDE_EFFECTS__
2479
+ function object4(entries$1, message$1) {
2480
+ return {
2481
+ kind: "schema",
2482
+ type: "object",
2483
+ reference: object4,
2484
+ expects: "Object",
2485
+ async: false,
2486
+ entries: entries$1,
2487
+ message: message$1,
2488
+ get "~standard"() {
2489
+ return /* @__PURE__ */ _getStandardProps4(this);
2490
+ },
2491
+ "~run"(dataset, config$1) {
2492
+ const input = dataset.value;
2493
+ if (input && typeof input === "object") {
2494
+ dataset.typed = true;
2495
+ dataset.value = {};
2496
+ for (const key in this.entries) {
2497
+ const valueSchema = this.entries[key];
2498
+ if (key in input || (valueSchema.type === "exact_optional" || valueSchema.type === "optional" || valueSchema.type === "nullish") && valueSchema.default !== void 0) {
2499
+ const value$1 = key in input ? input[key] : /* @__PURE__ */ getDefault4(valueSchema);
2500
+ const valueDataset = valueSchema["~run"]({ value: value$1 }, config$1);
2501
+ if (valueDataset.issues) {
2502
+ const pathItem = {
2503
+ type: "object",
2504
+ origin: "value",
2505
+ input,
2506
+ key,
2507
+ value: value$1
2508
+ };
2509
+ for (const issue of valueDataset.issues) {
2510
+ if (issue.path) issue.path.unshift(pathItem);
2511
+ else issue.path = [pathItem];
2512
+ dataset.issues?.push(issue);
2513
+ }
2514
+ if (!dataset.issues) dataset.issues = valueDataset.issues;
2515
+ if (config$1.abortEarly) {
2516
+ dataset.typed = false;
2517
+ break;
2518
+ }
2519
+ }
2520
+ if (!valueDataset.typed) dataset.typed = false;
2521
+ dataset.value[key] = valueDataset.value;
2522
+ } else if (valueSchema.fallback !== void 0) dataset.value[key] = /* @__PURE__ */ getFallback4(valueSchema);
2523
+ else if (valueSchema.type !== "exact_optional" && valueSchema.type !== "optional" && valueSchema.type !== "nullish") {
2524
+ _addIssue4(this, "key", dataset, config$1, {
2525
+ input: void 0,
2526
+ expected: `"${key}"`,
2527
+ path: [{
2528
+ type: "object",
2529
+ origin: "key",
2530
+ input,
2531
+ key,
2532
+ value: input[key]
2533
+ }]
2534
+ });
2535
+ if (config$1.abortEarly) break;
2536
+ }
2537
+ }
2538
+ } else _addIssue4(this, "type", dataset, config$1);
2539
+ return dataset;
2540
+ }
2541
+ };
2542
+ }
2543
+ // @__NO_SIDE_EFFECTS__
2544
+ function optional4(wrapped, default_) {
2545
+ return {
2546
+ kind: "schema",
2547
+ type: "optional",
2548
+ reference: optional4,
2549
+ expects: `(${wrapped.expects} | undefined)`,
2550
+ async: false,
2551
+ wrapped,
2552
+ default: default_,
2553
+ get "~standard"() {
2554
+ return /* @__PURE__ */ _getStandardProps4(this);
2555
+ },
2556
+ "~run"(dataset, config$1) {
2557
+ if (dataset.value === void 0) {
2558
+ if (this.default !== void 0) dataset.value = /* @__PURE__ */ getDefault4(this, dataset, config$1);
2559
+ if (dataset.value === void 0) {
2560
+ dataset.typed = true;
2561
+ return dataset;
2562
+ }
2563
+ }
2564
+ return this.wrapped["~run"](dataset, config$1);
2565
+ }
2566
+ };
2567
+ }
2568
+ // @__NO_SIDE_EFFECTS__
2569
+ function string4(message$1) {
2570
+ return {
2571
+ kind: "schema",
2572
+ type: "string",
2573
+ reference: string4,
2574
+ expects: "string",
2575
+ async: false,
2576
+ message: message$1,
2577
+ get "~standard"() {
2578
+ return /* @__PURE__ */ _getStandardProps4(this);
2579
+ },
2580
+ "~run"(dataset, config$1) {
2581
+ if (typeof dataset.value === "string") dataset.typed = true;
2582
+ else _addIssue4(this, "type", dataset, config$1);
2583
+ return dataset;
2584
+ }
2585
+ };
2586
+ }
2587
+ // @__NO_SIDE_EFFECTS__
2588
+ function unknown3() {
2589
+ return {
2590
+ kind: "schema",
2591
+ type: "unknown",
2592
+ reference: unknown3,
2593
+ expects: "unknown",
2594
+ async: false,
2595
+ get "~standard"() {
2596
+ return /* @__PURE__ */ _getStandardProps4(this);
2597
+ },
2598
+ "~run"(dataset) {
2599
+ dataset.typed = true;
2600
+ return dataset;
2601
+ }
2602
+ };
2603
+ }
2604
+ // @__NO_SIDE_EFFECTS__
2605
+ function safeParse4(schema, input, config$1) {
2606
+ const dataset = schema["~run"]({ value: input }, /* @__PURE__ */ getGlobalConfig4(config$1));
2607
+ return {
2608
+ typed: dataset.typed,
2609
+ success: !dataset.issues,
2610
+ output: dataset.value,
2611
+ issues: dataset.issues
2612
+ };
2613
+ }
2614
+ var Monitor3 = class {
2615
+ type;
2616
+ schema;
2617
+ poll;
2618
+ webhook;
2619
+ constructor(options) {
2620
+ this.type = options.type;
2621
+ this.schema = options.schema;
2622
+ }
2623
+ validateSource(source) {
2624
+ const result = /* @__PURE__ */ safeParse4(this.schema, source);
2625
+ if (result.success) return result.output;
2626
+ throw new Error(`invalid ${this.type} monitor source`);
2627
+ }
2628
+ initialDelivery(source) {
2629
+ this.validateSource(source);
2630
+ if (this.webhook && this.webhook.preferred && this.webhook.configured()) return "webhook";
2631
+ if (this.poll) return "poll";
2632
+ if (this.webhook) return "webhook";
2633
+ throw new Error(`source ${this.type} has no configured transport`);
2634
+ }
2635
+ resolveDelivery(monitor) {
2636
+ let webhookHealthy = false;
2637
+ if (this.webhook) webhookHealthy = this.webhook.isHealthy(monitor);
2638
+ if (monitor.delivery === "webhook") {
2639
+ if (this.webhook && webhookHealthy) return "webhook";
2640
+ if (this.poll) return "poll";
2641
+ if (this.webhook) return "webhook";
2642
+ return void 0;
2643
+ }
2644
+ if (this.webhook && webhookHealthy) return "webhook";
2645
+ if (this.poll) return "poll";
2646
+ if (this.webhook) return "webhook";
2647
+ return void 0;
2648
+ }
2649
+ async tick(context, record2) {
2650
+ const fresh = await context.service.get(record2.id);
2651
+ if (!fresh || !fresh.enabled) return 0;
2652
+ if (this.webhook) await this.webhook.retryPending(context, this, fresh);
2653
+ const current = await context.service.get(record2.id);
2654
+ if (!current || !current.enabled) return 0;
2655
+ const nextDelivery = this.resolveDelivery(current);
2656
+ if (!nextDelivery) return 0;
2657
+ if (nextDelivery !== current.delivery) {
2658
+ await context.service.setDelivery(current.id, nextDelivery);
2659
+ current.delivery = nextDelivery;
2660
+ }
2661
+ if (current.delivery !== "poll" || !this.poll) return 0;
2662
+ const lastAttemptAt = Number(current.cursors.__lastAttemptAt ?? current.cursors.__lastPolledAt ?? 0);
2663
+ const pollIntervalSec = current.pollIntervalSec || 60;
2664
+ if (lastAttemptAt && Date.now() - lastAttemptAt < pollIntervalSec * 1e3) return 0;
2665
+ return this.poll.run(context, this, current);
2666
+ }
2667
+ async deliver(context, record2, event, deliveryId) {
2668
+ if (!this.webhook) return 0;
2669
+ return this.webhook.deliver(context, this, record2, event, deliveryId);
2670
+ }
2671
+ unresponsive(record2, now = Date.now()) {
2672
+ if (!record2.enabled) return false;
2673
+ if (record2.delivery === "poll") {
2674
+ if (!this.poll) return true;
2675
+ return this.poll.isUnresponsive(record2, now);
2676
+ }
2677
+ if (!this.webhook) return true;
2678
+ return this.webhook.isUnresponsive(record2, now);
2679
+ }
2680
+ };
2681
+ var RoutedEventCursorSchema3 = /* @__PURE__ */ array4(/* @__PURE__ */ string4());
2682
+ function eventDeliveryId3(event) {
2683
+ if (event.id) return event.id;
2684
+ return `${event.kind}:${event.at}:${event.summary}`;
2685
+ }
2686
+ var activePolls3 = /* @__PURE__ */ new Set();
2687
+ var PollMonitor3 = class {
2688
+ constructor(options) {
2689
+ this.options = options;
2690
+ }
2691
+ async run(context, source, record2) {
2692
+ if (activePolls3.has(record2.id)) return 0;
2693
+ activePolls3.add(record2.id);
2694
+ try {
2695
+ const fresh = await context.service.get(record2.id);
2696
+ if (!fresh) return 0;
2697
+ const sourceRecord = source.validateSource(fresh.source);
2698
+ const cursorKey = source.key(sourceRecord);
2699
+ const result = await this.options.run(sourceRecord, fresh.cursors[cursorKey]);
2700
+ const current = await context.service.get(record2.id);
2701
+ if (!current || !current.enabled) return 0;
2702
+ const parsedRoutedIds = /* @__PURE__ */ safeParse4(RoutedEventCursorSchema3, fresh.cursors.__routedEventIds);
2703
+ let routedIds = [];
2704
+ if (parsedRoutedIds.success) routedIds = parsedRoutedIds.output;
2705
+ let routeFailed = false;
2706
+ for (const event of result.events) {
2707
+ const eventId = eventDeliveryId3(event);
2708
+ if (routedIds.includes(eventId)) continue;
2709
+ const routed = await context.sink.deliver({ monitor: fresh, event });
2710
+ if (!routed.ok) {
2711
+ routeFailed = true;
2712
+ console.error(`[sourcefed] route failed for ${fresh.id}: ${routed.error}`);
2713
+ continue;
2714
+ }
2715
+ if (!routedIds.includes(eventId)) routedIds.push(eventId);
2716
+ }
2717
+ if (routeFailed) {
2718
+ await context.service.updateCursor(fresh, "__routedEventIds", routedIds);
2719
+ return 0;
2720
+ }
2721
+ await context.service.updateCursorValue(fresh, cursorKey, (current2) => {
2722
+ if (this.options.mergeCursor) return this.options.mergeCursor(current2, result.cursor);
2723
+ return result.cursor;
2724
+ });
2725
+ await context.service.updateCursor(fresh, "__routedEventIds", []);
2726
+ if (result.terminal) await context.service.remove(fresh.id);
2727
+ await context.service.updateCursor(fresh, "__lastPolledAt", Date.now());
2728
+ return result.events.length;
2729
+ } finally {
2730
+ activePolls3.delete(record2.id);
2731
+ }
2732
+ }
2733
+ isUnresponsive(record2, now) {
2734
+ const intervalMs = Math.max((record2.pollIntervalSec || 60) * 3e3, 12e4);
2735
+ const lastPolledAt = Number(record2.cursors.__lastPolledAt ?? 0);
2736
+ if (lastPolledAt > 0) return now - lastPolledAt > intervalMs;
2737
+ return now - Date.parse(record2.createdAt) > intervalMs;
2738
+ }
2739
+ };
2740
+ var WEBHOOK_STALE_MS2 = 9e4;
2741
+ var WEBHOOK_STARTUP_GRACE_MS2 = 12e4;
2742
+ var PendingWebhookEventSchema2 = /* @__PURE__ */ object4({
2743
+ deliveryId: /* @__PURE__ */ string4(),
2744
+ event: /* @__PURE__ */ object4({
2745
+ source: /* @__PURE__ */ unknown3(),
2746
+ kind: /* @__PURE__ */ string4(),
2747
+ id: /* @__PURE__ */ optional4(/* @__PURE__ */ string4()),
2748
+ at: /* @__PURE__ */ string4(),
2749
+ summary: /* @__PURE__ */ string4(),
2750
+ body: /* @__PURE__ */ optional4(/* @__PURE__ */ string4()),
2751
+ actionable: /* @__PURE__ */ boolean3(),
2752
+ terminal: /* @__PURE__ */ optional4(/* @__PURE__ */ boolean3())
2753
+ })
2754
+ });
2755
+ var PendingWebhookEventsSchema2 = /* @__PURE__ */ array4(PendingWebhookEventSchema2);
2756
+ var pendingDeliveries2 = /* @__PURE__ */ new Set();
2757
+ var PENDING_WEBHOOK_EVENTS2 = "__pendingWebhookEvents";
2758
+ var WebhookMonitor2 = class {
2759
+ path;
2760
+ preferred;
2761
+ configured;
2762
+ verify;
2763
+ acknowledgeBeforeDelivery;
2764
+ challenge;
2765
+ deliveryId;
2766
+ eventName;
2767
+ parse;
2768
+ updateCursor;
2769
+ constructor(options) {
2770
+ this.path = options.path;
2771
+ this.preferred = options.preferred ?? true;
2772
+ this.configured = options.configured;
2773
+ this.verify = options.verify;
2774
+ this.acknowledgeBeforeDelivery = options.acknowledgeBeforeDelivery ?? false;
2775
+ this.challenge = options.challenge;
2776
+ this.deliveryId = options.deliveryId;
2777
+ this.eventName = options.eventName;
2778
+ this.parse = options.parse;
2779
+ this.updateCursor = options.updateCursor;
2780
+ }
2781
+ isHealthy(record2) {
2782
+ if (!this.preferred || !this.configured()) return false;
2783
+ const heartbeatAt = Number(record2.cursors.__webhookHeartbeatAt ?? 0);
2784
+ if (heartbeatAt > 0) return Date.now() - heartbeatAt <= WEBHOOK_STALE_MS2;
2785
+ return Date.now() - Date.parse(record2.createdAt) <= WEBHOOK_STARTUP_GRACE_MS2;
2786
+ }
2787
+ isUnresponsive(record2, now) {
2788
+ const heartbeatAt = Number(record2.cursors.__webhookHeartbeatAt ?? 0);
2789
+ if (heartbeatAt > 0) return now - heartbeatAt > WEBHOOK_STALE_MS2;
2790
+ return now - Date.parse(record2.createdAt) > WEBHOOK_STARTUP_GRACE_MS2;
2791
+ }
2792
+ async retryPending(context, source, record2) {
2793
+ const fresh = await context.service.get(record2.id);
2794
+ if (!fresh || !fresh.enabled) return 0;
2795
+ const parsed = /* @__PURE__ */ safeParse4(PendingWebhookEventsSchema2, fresh.cursors[PENDING_WEBHOOK_EVENTS2]);
2796
+ if (!parsed.success) return 0;
2797
+ let delivered = 0;
2798
+ for (const pending of parsed.output) {
2799
+ const current = await context.service.get(record2.id);
2800
+ if (!current || !current.enabled) break;
2801
+ const cursorKey = `webhook:${pending.deliveryId}`;
2802
+ if (current.cursors[cursorKey]) {
2803
+ await this.removePending(context, current, pending.deliveryId);
2804
+ continue;
2805
+ }
2806
+ delivered += await this.deliver(context, source, current, pending.event, pending.deliveryId);
2807
+ }
2808
+ return delivered;
2809
+ }
2810
+ async addPending(context, monitor, deliveryId, event) {
2811
+ await context.service.updateCursorValue(monitor, PENDING_WEBHOOK_EVENTS2, (cursor) => {
2812
+ const parsed = /* @__PURE__ */ safeParse4(PendingWebhookEventsSchema2, cursor);
2813
+ let pending = [];
2814
+ if (parsed.success) pending = parsed.output;
2815
+ if (pending.some((entry) => entry.deliveryId === deliveryId)) return pending;
2816
+ pending.push({ deliveryId, event });
2817
+ return pending;
2818
+ });
2819
+ }
2820
+ async removePending(context, monitor, deliveryId) {
2821
+ await context.service.updateCursorValue(monitor, PENDING_WEBHOOK_EVENTS2, (cursor) => {
2822
+ const parsed = /* @__PURE__ */ safeParse4(PendingWebhookEventsSchema2, cursor);
2823
+ if (!parsed.success) return [];
2824
+ return parsed.output.filter((entry) => entry.deliveryId !== deliveryId);
2825
+ });
2826
+ }
2827
+ async deliver(context, source, record2, event, deliveryId) {
2828
+ const fresh = await context.service.get(record2.id);
2829
+ if (!fresh || !fresh.enabled) return 0;
2830
+ const freshSource = source.validateSource(fresh.source);
2831
+ const eventSource = source.validateSource(event.source);
2832
+ if (source.key(freshSource) !== source.key(eventSource)) return 0;
2833
+ if (event.kind === "ci" && !event.actionable) return 0;
2834
+ const cursorKey = `webhook:${deliveryId}`;
2835
+ const reservationKey = `${fresh.id}:${deliveryId}`;
2836
+ if (pendingDeliveries2.has(reservationKey)) return 0;
2837
+ if (fresh.cursors[cursorKey]) {
2838
+ await this.removePending(context, fresh, deliveryId);
2839
+ return 0;
2840
+ }
2841
+ pendingDeliveries2.add(reservationKey);
2842
+ try {
2843
+ await this.addPending(context, fresh, deliveryId, event);
2844
+ if (this.updateCursor) {
2845
+ const sourceCursorKey = source.key(freshSource);
2846
+ await context.service.updateCursorValue(fresh, sourceCursorKey, (cursor) => this.updateCursor(event, cursor));
2847
+ }
2848
+ const result = await context.sink.deliver({ monitor: fresh, event });
2849
+ if (!result.ok) {
2850
+ console.error(`[sourcefed] webhook route failed for ${fresh.id}: ${result.error}`);
2851
+ return 0;
2852
+ }
2853
+ await context.service.setDelivery(fresh.id, "webhook");
2854
+ await context.service.updateCursor(fresh, cursorKey, true);
2855
+ await this.removePending(context, fresh, deliveryId);
2856
+ if (event.terminal) await context.service.remove(fresh.id);
2857
+ return 1;
2858
+ } finally {
2859
+ pendingDeliveries2.delete(reservationKey);
2860
+ }
2861
+ }
2862
+ };
2863
+ var MAX_WEBHOOK_BODY_BYTES4 = 1024 * 1024;
2864
+ function toMonitorEvent3(event, source) {
2865
+ return {
2866
+ source,
2867
+ kind: event.kind,
2868
+ id: event.id,
2869
+ at: event.at,
2870
+ summary: event.summary,
2871
+ body: event.body,
2872
+ actionable: event.actionable,
2873
+ terminal: event.terminal
2874
+ };
2875
+ }
2876
+ var store$424;
2877
+ var DEFAULT_CONFIG24 = {
2878
+ lang: void 0,
2879
+ message: void 0,
2880
+ abortEarly: void 0,
2881
+ abortPipeEarly: void 0
2882
+ };
2883
+ // @__NO_SIDE_EFFECTS__
2884
+ function getGlobalConfig24(config$1) {
2885
+ if (!config$1 && !store$424) return DEFAULT_CONFIG24;
2886
+ return {
2887
+ lang: config$1?.lang ?? store$424?.lang,
2888
+ message: config$1?.message,
2889
+ abortEarly: config$1?.abortEarly ?? store$424?.abortEarly,
2890
+ abortPipeEarly: config$1?.abortPipeEarly ?? store$424?.abortPipeEarly
2891
+ };
2892
+ }
2893
+ var store$324;
2894
+ // @__NO_SIDE_EFFECTS__
2895
+ function getGlobalMessage24(lang) {
2896
+ return store$324?.get(lang);
2897
+ }
2898
+ var store$224;
2899
+ // @__NO_SIDE_EFFECTS__
2900
+ function getSchemaMessage24(lang) {
2901
+ return store$224?.get(lang);
2902
+ }
2903
+ var store$124;
2904
+ // @__NO_SIDE_EFFECTS__
2905
+ function getSpecificMessage24(reference, lang) {
2906
+ return store$124?.get(reference)?.get(lang);
2907
+ }
2908
+ // @__NO_SIDE_EFFECTS__
2909
+ function _stringify24(input) {
2910
+ const type = typeof input;
2911
+ if (type === "string") return `"${input}"`;
2912
+ if (type === "number" || type === "bigint" || type === "boolean") return `${input}`;
2913
+ if (type === "object" || type === "function") return (input && Object.getPrototypeOf(input)?.constructor?.name) ?? "null";
2914
+ return type;
2915
+ }
2916
+ function _addIssue24(context, label, dataset, config$1, other) {
2917
+ const input = other && "input" in other ? other.input : dataset.value;
2918
+ const expected = other?.expected ?? context.expects ?? null;
2919
+ const received = other?.received ?? /* @__PURE__ */ _stringify24(input);
2920
+ const issue = {
2921
+ kind: context.kind,
2922
+ type: context.type,
2923
+ input,
2924
+ expected,
2925
+ received,
2926
+ message: `Invalid ${label}: ${expected ? `Expected ${expected} but r` : "R"}eceived ${received}`,
2927
+ requirement: context.requirement,
2928
+ path: other?.path,
2929
+ issues: other?.issues,
2930
+ lang: config$1.lang,
2931
+ abortEarly: config$1.abortEarly,
2932
+ abortPipeEarly: config$1.abortPipeEarly
2933
+ };
2934
+ const isSchema = context.kind === "schema";
2935
+ const message$1 = other?.message ?? context.message ?? /* @__PURE__ */ getSpecificMessage24(context.reference, issue.lang) ?? (isSchema ? /* @__PURE__ */ getSchemaMessage24(issue.lang) : null) ?? config$1.message ?? /* @__PURE__ */ getGlobalMessage24(issue.lang);
2936
+ if (message$1 !== void 0) issue.message = typeof message$1 === "function" ? message$1(issue) : message$1;
2937
+ if (isSchema) dataset.typed = false;
2938
+ if (dataset.issues) dataset.issues.push(issue);
2939
+ else dataset.issues = [issue];
2940
+ }
2941
+ var _standardCache24 = /* @__PURE__ */ new WeakMap();
2942
+ // @__NO_SIDE_EFFECTS__
2943
+ function _getStandardProps24(context) {
2944
+ let cached = _standardCache24.get(context);
2945
+ if (!cached) {
2946
+ cached = {
2947
+ version: 1,
2948
+ vendor: "valibot",
2949
+ validate(value$1) {
2950
+ return context["~run"]({ value: value$1 }, /* @__PURE__ */ getGlobalConfig24());
2951
+ }
2952
+ };
2953
+ _standardCache24.set(context, cached);
2954
+ }
2955
+ return cached;
2956
+ }
2957
+ var EMOJI_REGEX24 = new RegExp("^(?:[\\u{1F1E6}-\\u{1F1FF}]{2}|\\u{1F3F4}[\\u{E0061}-\\u{E007A}]{2}[\\u{E0030}-\\u{E0039}\\u{E0061}-\\u{E007A}]{1,3}\\u{E007F}|(?:\\p{Emoji}\\uFE0F\\u20E3?|\\p{Emoji_Modifier_Base}\\p{Emoji_Modifier}?|(?![\\p{Emoji_Modifier_Base}\\u{1F1E6}-\\u{1F1FF}])\\p{Emoji_Presentation})(?:\\u200D(?:\\p{Emoji}\\uFE0F\\u20E3?|\\p{Emoji_Modifier_Base}\\p{Emoji_Modifier}?|(?![\\p{Emoji_Modifier_Base}\\u{1F1E6}-\\u{1F1FF}])\\p{Emoji_Presentation}))*)+$", "u");
2958
+ // @__NO_SIDE_EFFECTS__
2959
+ function getFallback23(schema, dataset, config$1) {
2960
+ return typeof schema.fallback === "function" ? schema.fallback(dataset, config$1) : schema.fallback;
2961
+ }
2962
+ // @__NO_SIDE_EFFECTS__
2963
+ function getDefault23(schema, dataset, config$1) {
2964
+ return typeof schema.default === "function" ? schema.default(dataset, config$1) : schema.default;
2965
+ }
2966
+ // @__NO_SIDE_EFFECTS__
2967
+ function array24(item, message$1) {
2968
+ return {
2969
+ kind: "schema",
2970
+ type: "array",
2971
+ reference: array24,
2972
+ expects: "Array",
2973
+ async: false,
2974
+ item,
2975
+ message: message$1,
2976
+ get "~standard"() {
2977
+ return /* @__PURE__ */ _getStandardProps24(this);
2978
+ },
2979
+ "~run"(dataset, config$1) {
2980
+ const input = dataset.value;
2981
+ if (Array.isArray(input)) {
2982
+ dataset.typed = true;
2983
+ dataset.value = [];
2984
+ for (let key = 0; key < input.length; key++) {
2985
+ const value$1 = input[key];
2986
+ const itemDataset = this.item["~run"]({ value: value$1 }, config$1);
2987
+ if (itemDataset.issues) {
2988
+ const pathItem = {
2989
+ type: "array",
2990
+ origin: "value",
2991
+ input,
2992
+ key,
2993
+ value: value$1
2994
+ };
2995
+ for (const issue of itemDataset.issues) {
2996
+ if (issue.path) issue.path.unshift(pathItem);
2997
+ else issue.path = [pathItem];
2998
+ dataset.issues?.push(issue);
2999
+ }
3000
+ if (!dataset.issues) dataset.issues = itemDataset.issues;
3001
+ if (config$1.abortEarly) {
3002
+ dataset.typed = false;
3003
+ break;
3004
+ }
3005
+ }
3006
+ if (!itemDataset.typed) dataset.typed = false;
3007
+ dataset.value.push(itemDataset.value);
3008
+ }
3009
+ } else _addIssue24(this, "type", dataset, config$1);
3010
+ return dataset;
3011
+ }
3012
+ };
3013
+ }
3014
+ // @__NO_SIDE_EFFECTS__
3015
+ function boolean23(message$1) {
3016
+ return {
3017
+ kind: "schema",
3018
+ type: "boolean",
3019
+ reference: boolean23,
3020
+ expects: "boolean",
3021
+ async: false,
3022
+ message: message$1,
3023
+ get "~standard"() {
3024
+ return /* @__PURE__ */ _getStandardProps24(this);
3025
+ },
3026
+ "~run"(dataset, config$1) {
3027
+ if (typeof dataset.value === "boolean") dataset.typed = true;
3028
+ else _addIssue24(this, "type", dataset, config$1);
3029
+ return dataset;
3030
+ }
3031
+ };
3032
+ }
3033
+ // @__NO_SIDE_EFFECTS__
3034
+ function literal3(literal_, message$1) {
3035
+ return {
3036
+ kind: "schema",
3037
+ type: "literal",
3038
+ reference: literal3,
3039
+ expects: /* @__PURE__ */ _stringify24(literal_),
3040
+ async: false,
3041
+ literal: literal_,
3042
+ message: message$1,
3043
+ get "~standard"() {
3044
+ return /* @__PURE__ */ _getStandardProps24(this);
3045
+ },
3046
+ "~run"(dataset, config$1) {
3047
+ if (dataset.value === this.literal) dataset.typed = true;
3048
+ else _addIssue24(this, "type", dataset, config$1);
3049
+ return dataset;
3050
+ }
3051
+ };
3052
+ }
3053
+ // @__NO_SIDE_EFFECTS__
3054
+ function object23(entries$1, message$1) {
3055
+ return {
3056
+ kind: "schema",
3057
+ type: "object",
3058
+ reference: object23,
3059
+ expects: "Object",
3060
+ async: false,
3061
+ entries: entries$1,
3062
+ message: message$1,
3063
+ get "~standard"() {
3064
+ return /* @__PURE__ */ _getStandardProps24(this);
3065
+ },
3066
+ "~run"(dataset, config$1) {
3067
+ const input = dataset.value;
3068
+ if (input && typeof input === "object") {
3069
+ dataset.typed = true;
3070
+ dataset.value = {};
3071
+ for (const key in this.entries) {
3072
+ const valueSchema = this.entries[key];
3073
+ if (key in input || (valueSchema.type === "exact_optional" || valueSchema.type === "optional" || valueSchema.type === "nullish") && valueSchema.default !== void 0) {
3074
+ const value$1 = key in input ? input[key] : /* @__PURE__ */ getDefault23(valueSchema);
3075
+ const valueDataset = valueSchema["~run"]({ value: value$1 }, config$1);
3076
+ if (valueDataset.issues) {
3077
+ const pathItem = {
3078
+ type: "object",
3079
+ origin: "value",
3080
+ input,
3081
+ key,
3082
+ value: value$1
3083
+ };
3084
+ for (const issue of valueDataset.issues) {
3085
+ if (issue.path) issue.path.unshift(pathItem);
3086
+ else issue.path = [pathItem];
3087
+ dataset.issues?.push(issue);
3088
+ }
3089
+ if (!dataset.issues) dataset.issues = valueDataset.issues;
3090
+ if (config$1.abortEarly) {
3091
+ dataset.typed = false;
3092
+ break;
3093
+ }
3094
+ }
3095
+ if (!valueDataset.typed) dataset.typed = false;
3096
+ dataset.value[key] = valueDataset.value;
3097
+ } else if (valueSchema.fallback !== void 0) dataset.value[key] = /* @__PURE__ */ getFallback23(valueSchema);
3098
+ else if (valueSchema.type !== "exact_optional" && valueSchema.type !== "optional" && valueSchema.type !== "nullish") {
3099
+ _addIssue24(this, "key", dataset, config$1, {
3100
+ input: void 0,
3101
+ expected: `"${key}"`,
3102
+ path: [{
3103
+ type: "object",
3104
+ origin: "key",
3105
+ input,
3106
+ key,
3107
+ value: input[key]
3108
+ }]
3109
+ });
3110
+ if (config$1.abortEarly) break;
3111
+ }
3112
+ }
3113
+ } else _addIssue24(this, "type", dataset, config$1);
3114
+ return dataset;
3115
+ }
3116
+ };
3117
+ }
3118
+ // @__NO_SIDE_EFFECTS__
3119
+ function optional22(wrapped, default_) {
3120
+ return {
3121
+ kind: "schema",
3122
+ type: "optional",
3123
+ reference: optional22,
3124
+ expects: `(${wrapped.expects} | undefined)`,
3125
+ async: false,
3126
+ wrapped,
3127
+ default: default_,
3128
+ get "~standard"() {
3129
+ return /* @__PURE__ */ _getStandardProps24(this);
3130
+ },
3131
+ "~run"(dataset, config$1) {
3132
+ if (dataset.value === void 0) {
3133
+ if (this.default !== void 0) dataset.value = /* @__PURE__ */ getDefault23(this, dataset, config$1);
3134
+ if (dataset.value === void 0) {
3135
+ dataset.typed = true;
3136
+ return dataset;
3137
+ }
3138
+ }
3139
+ return this.wrapped["~run"](dataset, config$1);
3140
+ }
3141
+ };
3142
+ }
3143
+ // @__NO_SIDE_EFFECTS__
3144
+ function string24(message$1) {
3145
+ return {
3146
+ kind: "schema",
3147
+ type: "string",
3148
+ reference: string24,
3149
+ expects: "string",
3150
+ async: false,
3151
+ message: message$1,
3152
+ get "~standard"() {
3153
+ return /* @__PURE__ */ _getStandardProps24(this);
3154
+ },
3155
+ "~run"(dataset, config$1) {
3156
+ if (typeof dataset.value === "string") dataset.typed = true;
3157
+ else _addIssue24(this, "type", dataset, config$1);
3158
+ return dataset;
3159
+ }
3160
+ };
3161
+ }
3162
+ // @__NO_SIDE_EFFECTS__
3163
+ function safeParse24(schema, input, config$1) {
3164
+ const dataset = schema["~run"]({ value: input }, /* @__PURE__ */ getGlobalConfig24(config$1));
3165
+ return {
3166
+ typed: dataset.typed,
3167
+ success: !dataset.issues,
3168
+ output: dataset.value,
3169
+ issues: dataset.issues
3170
+ };
3171
+ }
3172
+ var SlackSourceSchema = /* @__PURE__ */ object23({
3173
+ type: /* @__PURE__ */ literal3("slack"),
3174
+ channelId: /* @__PURE__ */ string24(),
3175
+ threadTs: /* @__PURE__ */ string24()
3176
+ });
3177
+ var SlackCursorSchema = /* @__PURE__ */ object23({
3178
+ primed: /* @__PURE__ */ boolean23(),
3179
+ messageIds: /* @__PURE__ */ array24(/* @__PURE__ */ string24()),
3180
+ latestTs: /* @__PURE__ */ optional22(/* @__PURE__ */ string24())
3181
+ });
3182
+ function messageTimestamp(ts) {
3183
+ const value = Number(ts ?? 0);
3184
+ if (Number.isFinite(value)) return value;
3185
+ return 0;
3186
+ }
3187
+ function messageAt(ts) {
3188
+ const value = messageTimestamp(ts);
3189
+ if (value > 0) return new Date(value * 1e3).toISOString();
3190
+ return (/* @__PURE__ */ new Date()).toISOString();
3191
+ }
3192
+ function parseSlackReadResult(result, cursorRaw) {
3193
+ const parsedCursor = /* @__PURE__ */ safeParse24(SlackCursorSchema, cursorRaw);
3194
+ let previous = {};
3195
+ if (parsedCursor.success) previous = parsedCursor.output;
3196
+ const cursor = {
3197
+ primed: previous.primed ?? false,
3198
+ messageIds: previous.messageIds ?? [],
3199
+ latestTs: previous.latestTs
3200
+ };
3201
+ const users = new Map((result.users ?? []).map((user) => [user.id, user.real_name ?? user.name ?? user.id ?? "someone"]));
3202
+ const messages = (result.messages ?? []).filter((message) => message.ts);
3203
+ const freshMessages = messages.filter((message) => {
3204
+ if (cursor.messageIds.includes(message.ts)) return false;
3205
+ if (!cursor.latestTs) return true;
3206
+ return messageTimestamp(message.ts) > messageTimestamp(cursor.latestTs);
3207
+ });
3208
+ let events = [];
3209
+ if (cursor.primed) {
3210
+ events = freshMessages.map((message) => {
3211
+ let text = "[message without text]";
3212
+ if (message.text?.trim()) text = message.text.trim();
3213
+ let author = users.get(message.user);
3214
+ if (!author) author = message.user;
3215
+ if (!author) author = "someone";
3216
+ return {
3217
+ kind: "message",
3218
+ id: `message:${message.ts}`,
3219
+ at: messageAt(message.ts),
3220
+ summary: `Slack thread message by ${author}`,
3221
+ body: text,
3222
+ actionable: true
3223
+ };
3224
+ });
3225
+ }
3226
+ cursor.primed = true;
3227
+ cursor.messageIds = [.../* @__PURE__ */ new Set([...cursor.messageIds, ...messages.map((message) => message.ts)])].slice(-500);
3228
+ let latestTs = cursor.latestTs;
3229
+ for (const message of messages) {
3230
+ if (messageTimestamp(message.ts) > messageTimestamp(latestTs)) latestTs = message.ts;
3231
+ }
3232
+ cursor.latestTs = latestTs;
3233
+ return { events, cursor };
3234
+ }
3235
+ var SLACK_API = "https://slack.com/api";
3236
+ var warnedMissingToken2 = false;
3237
+ function slackToken() {
3238
+ return process.env.SOURCEFED_SLACK_TOKEN;
3239
+ }
3240
+ async function fetchSlackThread(channelId, threadTs) {
3241
+ const messages = [];
3242
+ let cursor = "";
3243
+ let received = false;
3244
+ do {
3245
+ const page = await slackApi("conversations.replies", {
3246
+ channel: channelId,
3247
+ ts: threadTs,
3248
+ limit: "1000",
3249
+ inclusive: "false",
3250
+ ...cursor ? { cursor } : {}
3251
+ });
3252
+ if (!page) break;
3253
+ received = true;
3254
+ if (Array.isArray(page.messages)) messages.push(...page.messages);
3255
+ cursor = page.response_metadata?.next_cursor ?? "";
3256
+ if (messages.length >= 1e4) break;
3257
+ } while (cursor);
3258
+ if (!received) return void 0;
3259
+ const users = [];
3260
+ let usersCursor = "";
3261
+ do {
3262
+ const page = await slackApi("users.list", { limit: "200", ...usersCursor ? { cursor: usersCursor } : {} });
3263
+ if (!page) break;
3264
+ if (Array.isArray(page.members)) {
3265
+ users.push(...page.members.map((member) => ({
3266
+ id: member.id,
3267
+ name: member.name,
3268
+ real_name: member.real_name
3269
+ })));
3270
+ }
3271
+ usersCursor = page.response_metadata?.next_cursor ?? "";
3272
+ } while (usersCursor);
3273
+ const result = { messages, users };
3274
+ return result;
3275
+ }
3276
+ async function slackApi(method, params) {
3277
+ const token = slackToken();
3278
+ if (!token) {
3279
+ if (!warnedMissingToken2) {
3280
+ warnedMissingToken2 = true;
3281
+ console.warn("[sourcefed] SOURCEFED_SLACK_TOKEN is not set; Slack monitors will not poll");
3282
+ }
3283
+ return void 0;
3284
+ }
3285
+ try {
3286
+ const response = await fetch(`${SLACK_API}/${method}`, {
3287
+ method: "POST",
3288
+ headers: { authorization: `Bearer ${token}`, "content-type": "application/x-www-form-urlencoded" },
3289
+ body: new URLSearchParams(params)
3290
+ });
3291
+ const result = await response.json();
3292
+ if (!response.ok || result?.ok !== true) return void 0;
3293
+ return result;
3294
+ } catch {
3295
+ return void 0;
3296
+ }
3297
+ }
3298
+ async function pollSlack(source, cursorRaw) {
3299
+ const result = await fetchSlackThread(source.channelId, source.threadTs);
3300
+ if (!result) throw new Error("slack API request failed; check SOURCEFED_SLACK_TOKEN");
3301
+ const parsed = parseSlackReadResult(result, cursorRaw);
3302
+ return { ...parsed, terminal: false };
3303
+ }
3304
+ var slackPollMonitor = new PollMonitor3({
3305
+ run: async (source, cursor) => {
3306
+ const result = await pollSlack(source, cursor);
3307
+ return {
3308
+ cursor: result.cursor,
3309
+ events: result.events.map((event) => toMonitorEvent3(event, source)),
3310
+ terminal: result.terminal
3311
+ };
3312
+ }
3313
+ });
3314
+ function parseSlackWebhook(payload, _eventName, deliveryId) {
3315
+ const event = payload.event;
3316
+ const channelId = event?.channel;
3317
+ const threadTs = event?.thread_ts;
3318
+ if (payload.type !== "event_callback" || event?.type !== "message" || !channelId || !threadTs) return void 0;
3319
+ let text = "[message without text]";
3320
+ if (event.text?.trim()) text = event.text.trim();
3321
+ let author = event.username;
3322
+ if (!author) author = event.user;
3323
+ if (!author) author = event.bot_id;
3324
+ if (!author) author = "someone";
3325
+ let at = messageAt(event.event_ts);
3326
+ if (!event.event_ts) at = messageAt(event.ts);
3327
+ return {
3328
+ source: { type: "slack", channelId, threadTs },
3329
+ kind: "message",
3330
+ id: event.ts === void 0 ? void 0 : `message:${event.ts}`,
3331
+ at,
3332
+ summary: `Slack thread message by ${author}`,
3333
+ body: `${text}
3334
+
3335
+ Slack event: ${deliveryId}`,
3336
+ actionable: true
3337
+ };
3338
+ }
3339
+ function slackSignature(body, timestamp, secret) {
3340
+ return `v0=${createHmac2("sha256", secret).update(`v0:${timestamp}:${body}`).digest("hex")}`;
3341
+ }
3342
+ function validSlackSignature(body, request, secret = process.env.SOURCEFED_SLACK_SIGNING_SECRET) {
3343
+ const timestamp = request.headers.get("x-slack-request-timestamp");
3344
+ const signature = request.headers.get("x-slack-signature");
3345
+ const timestampValue = Number(timestamp);
3346
+ if (!timestamp || !signature || !secret || !Number.isFinite(timestampValue) || Math.abs(Date.now() / 1e3 - timestampValue) > 300) return false;
3347
+ const expected = slackSignature(body, timestamp, secret);
3348
+ const actual = Buffer.from(signature);
3349
+ const wanted = Buffer.from(expected);
3350
+ return actual.length === wanted.length && timingSafeEqual2(actual, wanted);
3351
+ }
3352
+ function updateSlackCursor(event, cursorRaw) {
3353
+ const parsed = /* @__PURE__ */ safeParse24(SlackCursorSchema, cursorRaw);
3354
+ let cursor = { primed: false, messageIds: [], latestTs: void 0 };
3355
+ if (parsed.success) cursor = parsed.output;
3356
+ if (event.id?.startsWith("message:")) {
3357
+ const messageId = event.id.slice("message:".length);
3358
+ if (!cursor.messageIds.includes(messageId)) cursor.messageIds.push(messageId);
3359
+ if (messageTimestamp(messageId) > messageTimestamp(cursor.latestTs)) cursor.latestTs = messageId;
3360
+ }
3361
+ cursor.primed = true;
3362
+ return cursor;
3363
+ }
3364
+ var slackWebhookMonitor = new WebhookMonitor2({
3365
+ path: "/webhooks/slack",
3366
+ preferred: true,
3367
+ configured: () => Boolean(process.env.SOURCEFED_SLACK_SIGNING_SECRET),
3368
+ verify: (body, request) => validSlackSignature(body, request),
3369
+ acknowledgeBeforeDelivery: true,
3370
+ challenge: (payload) => {
3371
+ if (payload.type === "url_verification" && payload.challenge) return { challenge: payload.challenge };
3372
+ return void 0;
3373
+ },
3374
+ deliveryId: (_request, payload) => payload.event_id,
3375
+ eventName: () => "slack",
3376
+ parse: parseSlackWebhook,
3377
+ updateCursor: updateSlackCursor
3378
+ });
3379
+ var SlackMonitor = class extends Monitor3 {
3380
+ poll = slackPollMonitor;
3381
+ webhook = slackWebhookMonitor;
3382
+ constructor() {
3383
+ super({ type: "slack", schema: SlackSourceSchema });
3384
+ }
3385
+ key = (source) => `slack:${source.channelId}#${source.threadTs}`;
3386
+ icon = "\u{F04B1}";
3387
+ label = () => "thread";
3388
+ detail = (source) => ` ${source.channelId}`;
3389
+ describe = (source) => `Slack ${source.channelId} thread ${source.threadTs}`;
3390
+ build(input) {
3391
+ if (input.channelId && input.threadTs) return { type: "slack", channelId: input.channelId, threadTs: input.threadTs };
3392
+ if (!input.threadUrl) return { error: "channelId + threadTs or threadUrl is required for a slack monitor" };
3393
+ let url;
3394
+ try {
3395
+ url = new URL(input.threadUrl);
3396
+ } catch {
3397
+ return { error: "threadUrl must be a valid Slack URL" };
3398
+ }
3399
+ const channelId = url.pathname.match(/\/archives\/([^/]+)/)?.[1];
3400
+ const pathTimestamp = url.pathname.match(/\/p(\d{10,})/)?.[1];
3401
+ let threadTs = url.searchParams.get("thread_ts") ?? void 0;
3402
+ if (!threadTs && pathTimestamp) threadTs = `${pathTimestamp.slice(0, -6)}.${pathTimestamp.slice(-6)}`;
3403
+ if (channelId && threadTs) return { type: "slack", channelId, threadTs };
3404
+ return { error: "threadUrl does not contain a Slack channel and thread timestamp" };
3405
+ }
3406
+ };
3407
+ var SOURCE_MAP = {
3408
+ jira: new JiraMonitor(),
3409
+ github: new GithubMonitor(),
3410
+ slack: new SlackMonitor()
3411
+ };
3412
+ var SOURCE_TYPES = Object.keys(SOURCE_MAP);
3413
+ var RequestRouter = class {
3414
+ pending = /* @__PURE__ */ new Map();
3415
+ cursor = 1;
3416
+ nextID() {
3417
+ return this.cursor++;
3418
+ }
3419
+ register(id2) {
3420
+ return new Promise((resolve, reject) => {
3421
+ this.pending.set(id2, { resolve, reject });
3422
+ });
3423
+ }
3424
+ settle(id2, frame) {
3425
+ const pending = this.pending.get(id2);
3426
+ if (!pending) return;
3427
+ this.pending.delete(id2);
3428
+ if (frame.error) pending.reject(new Error(frame.error));
3429
+ else pending.resolve(frame.result);
3430
+ }
3431
+ rejectAll(error) {
3432
+ for (const { reject } of this.pending.values()) reject(error);
3433
+ this.pending.clear();
3434
+ }
3435
+ };
3436
+ function defaultDaemonUrl() {
3437
+ const port = process.env.SOURCEFED_DAEMON_PORT ?? 18787;
3438
+ return `http://127.0.0.1:${port}`;
3439
+ }
3440
+ function sleep(ms) {
3441
+ return new Promise((resolve) => setTimeout(resolve, ms));
3442
+ }
3443
+ async function withTimeout(promise, ms, message) {
3444
+ let timer;
3445
+ try {
3446
+ return await Promise.race([
3447
+ promise,
3448
+ new Promise((_resolve, reject) => {
3449
+ timer = setTimeout(() => reject(new Error(message)), ms);
3450
+ })
3451
+ ]);
3452
+ } finally {
3453
+ if (timer) clearTimeout(timer);
3454
+ }
3455
+ }
3456
+ function encodeTarget(target) {
3457
+ return Buffer.from(JSON.stringify(target), "utf8").toString("base64url");
3458
+ }
3459
+ function targetKey(target) {
3460
+ return JSON.stringify(target);
3461
+ }
3462
+ function parseDaemonFrame(line) {
3463
+ try {
3464
+ return JSON.parse(line);
3465
+ } catch {
3466
+ return void 0;
3467
+ }
3468
+ }
3469
+ async function connectDaemonClient(options) {
3470
+ const url = options.url ?? process.env.SOURCEFED_DAEMON_URL ?? defaultDaemonUrl();
3471
+ return new HttpDaemonClient(url, options.token ?? process.env.SOURCEFED_DAEMON_TOKEN);
3472
+ }
3473
+ var HttpDaemonClient = class {
3474
+ baseURL;
3475
+ token;
3476
+ router = new RequestRouter();
3477
+ listeners = /* @__PURE__ */ new Map();
3478
+ streams = /* @__PURE__ */ new Set();
3479
+ constructor(baseURL, token) {
3480
+ this.baseURL = baseURL.replace(/\/+$/, "");
3481
+ this.token = token;
3482
+ }
3483
+ headers() {
3484
+ return this.token ? { authorization: `Bearer ${this.token}` } : {};
3485
+ }
3486
+ async request(method, params) {
3487
+ const id2 = this.router.nextID();
3488
+ const promise = this.router.register(id2);
3489
+ try {
3490
+ const response = await fetch(`${this.baseURL}/rpc`, {
3491
+ method: "POST",
3492
+ headers: { "content-type": "application/json", ...this.headers() },
3493
+ body: JSON.stringify({ id: id2, method, params })
3494
+ });
3495
+ const frame = await response.json();
3496
+ if (!response.ok || frame.error) throw new Error(frame.error ?? `daemon request failed: ${response.status}`);
3497
+ this.router.settle(id2, { result: frame.result });
3498
+ } catch (error) {
3499
+ this.router.settle(id2, { error: error instanceof Error ? error.message : String(error) });
3500
+ }
3501
+ return promise;
3502
+ }
3503
+ async subscribe(target, onEvents) {
3504
+ const key = targetKey(target);
3505
+ let callbacks = this.listeners.get(key);
3506
+ if (!callbacks) {
3507
+ callbacks = /* @__PURE__ */ new Set();
3508
+ this.listeners.set(key, callbacks);
3509
+ }
3510
+ callbacks.add(onEvents);
3511
+ const controller = new AbortController();
3512
+ this.streams.add(controller);
3513
+ const url = `${this.baseURL}/events?target=${encodeTarget(target)}`;
3514
+ let markSubscribed;
3515
+ const subscribed = new Promise((resolve) => {
3516
+ markSubscribed = () => resolve();
3517
+ });
3518
+ let drainComplete = false;
3519
+ let draining = false;
3520
+ let redrainRequested = false;
3521
+ let retryTimer;
3522
+ let markDrained;
3523
+ const drained = new Promise((resolve) => {
3524
+ markDrained = resolve;
3525
+ });
3526
+ let buffered = [];
3527
+ let delivering = Promise.resolve();
3528
+ const deliver = (events) => {
3529
+ if (!callbacks?.has(onEvents) || events.length === 0) return Promise.resolve();
3530
+ delivering = delivering.then(() => deliverWithRetry(events, 0)).catch((error) => {
3531
+ console.error(`[sourcefed] event delivery failed: ${error instanceof Error ? error.message : String(error)}`);
3532
+ });
3533
+ return delivering;
3534
+ };
3535
+ const deliverWithRetry = async (events, attempt) => {
3536
+ try {
3537
+ await onEvents(events);
3538
+ } catch (error) {
3539
+ if (attempt >= 2) {
3540
+ console.error(`[sourcefed] event delivery failed after retries; re-draining queue: ${error instanceof Error ? error.message : String(error)}`);
3541
+ redrainRequested = true;
3542
+ void performDrain();
3543
+ return;
3544
+ }
3545
+ await sleep(250 * (attempt + 1));
3546
+ await deliverWithRetry(events, attempt + 1);
3547
+ }
3548
+ };
3549
+ const performDrain = async () => {
3550
+ if (draining) {
3551
+ redrainRequested = true;
3552
+ return;
3553
+ }
3554
+ draining = true;
3555
+ try {
3556
+ let passes = 0;
3557
+ do {
3558
+ redrainRequested = false;
3559
+ passes += 1;
3560
+ try {
3561
+ const drainResult = await this.request("monitor.events", { target });
3562
+ drainComplete = true;
3563
+ const drainedIds = new Set((drainResult.events ?? []).map((event) => event.id));
3564
+ if (drainResult.events && drainResult.events.length > 0) await deliver(drainResult.events);
3565
+ for (const batch of buffered) {
3566
+ const fresh = batch.filter((event) => !drainedIds.has(event.id));
3567
+ if (fresh.length > 0) await deliver(fresh);
3568
+ }
3569
+ buffered = [];
3570
+ markDrained();
3571
+ } catch (error) {
3572
+ console.error(`[sourcefed] event drain failed: ${error instanceof Error ? error.message : String(error)}`);
3573
+ redrainRequested = true;
3574
+ }
3575
+ } while (redrainRequested && passes < 3);
3576
+ if (redrainRequested) markDrained();
3577
+ } finally {
3578
+ draining = false;
3579
+ }
3580
+ };
3581
+ const runStream = async () => {
3582
+ try {
3583
+ const response = await fetch(url, { signal: controller.signal, headers: this.headers() });
3584
+ if (!response.ok || !response.body) throw new Error(`event stream failed: ${response.status}`);
3585
+ const reader = response.body.getReader();
3586
+ const decoder = new TextDecoder();
3587
+ let buffer = "";
3588
+ while (true) {
3589
+ const { done, value } = await reader.read();
3590
+ if (done) break;
3591
+ buffer += decoder.decode(value, { stream: true });
3592
+ const frames = buffer.split("\n\n");
3593
+ buffer = frames.pop() ?? "";
3594
+ for (const chunk of frames) {
3595
+ for (const line of chunk.split("\n")) {
3596
+ if (!line.startsWith("data: ")) continue;
3597
+ const frame = parseDaemonFrame(line.slice(6));
3598
+ if (!frame || !("type" in frame)) continue;
3599
+ if (frame.type === "subscribed") {
3600
+ markSubscribed();
3601
+ drainComplete = false;
3602
+ void performDrain();
3603
+ }
3604
+ if (frame.type === "event") {
3605
+ if (drainComplete) void deliver(frame.events);
3606
+ else buffered.push(frame.events);
3607
+ }
3608
+ }
3609
+ }
3610
+ }
3611
+ consecutiveFailures = 0;
3612
+ } catch (error) {
3613
+ if (!controller.signal.aborted) {
3614
+ consecutiveFailures += 1;
3615
+ if (consecutiveFailures === 1) {
3616
+ console.error(`[sourcefed] event stream ended: ${error instanceof Error ? error.message : String(error)}`);
3617
+ }
3618
+ }
3619
+ }
3620
+ };
3621
+ let consecutiveFailures = 0;
3622
+ void (async () => {
3623
+ while (!controller.signal.aborted && callbacks?.has(onEvents)) {
3624
+ await runStream();
3625
+ if (controller.signal.aborted) break;
3626
+ await sleep(500);
3627
+ }
3628
+ })();
3629
+ try {
3630
+ await withTimeout(subscribed, 15e3, `sourcefed event stream did not subscribe at ${url}`);
3631
+ await withTimeout(drained, 15e3, "sourcefed initial event drain timed out");
3632
+ } catch (error) {
3633
+ if (retryTimer) clearTimeout(retryTimer);
3634
+ const callbacks2 = this.listeners.get(key);
3635
+ callbacks2?.delete(onEvents);
3636
+ if (callbacks2 && callbacks2.size === 0) this.listeners.delete(key);
3637
+ controller.abort();
3638
+ this.streams.delete(controller);
3639
+ throw error;
3640
+ }
3641
+ return {
3642
+ close: async () => {
3643
+ if (retryTimer) clearTimeout(retryTimer);
3644
+ const callbacks2 = this.listeners.get(key);
3645
+ callbacks2?.delete(onEvents);
3646
+ if (callbacks2 && callbacks2.size === 0) this.listeners.delete(key);
3647
+ controller.abort();
3648
+ this.streams.delete(controller);
3649
+ }
3650
+ };
3651
+ }
3652
+ async close() {
3653
+ for (const controller of this.streams) controller.abort();
3654
+ this.streams.clear();
3655
+ this.router.rejectAll(new Error("daemon closed"));
3656
+ this.listeners.clear();
3657
+ }
3658
+ };
3659
+
3660
+ // .tui-build/tui/sidebar.mjs
3661
+ import { memo as _$memo } from "opentui:runtime-module:%40opentui%2Fsolid";
3662
+ import { effect as _$effect } from "opentui:runtime-module:%40opentui%2Fsolid";
3663
+ import { createComponent as _$createComponent } from "opentui:runtime-module:%40opentui%2Fsolid";
3664
+ import { insert as _$insert } from "opentui:runtime-module:%40opentui%2Fsolid";
3665
+ import { createTextNode as _$createTextNode } from "opentui:runtime-module:%40opentui%2Fsolid";
3666
+ import { insertNode as _$insertNode } from "opentui:runtime-module:%40opentui%2Fsolid";
3667
+ import { setProp as _$setProp } from "opentui:runtime-module:%40opentui%2Fsolid";
3668
+ import { createElement as _$createElement } from "opentui:runtime-module:%40opentui%2Fsolid";
3669
+ import { Show, createMemo, createSignal, onCleanup } from "opentui:runtime-module:solid-js";
3670
+ var REFRESH_MS = 3e3;
3671
+ function Sidebar(props) {
3672
+ const [monitors, setMonitors] = createSignal([]);
3673
+ const theme = createMemo(() => props.api.theme.current);
3674
+ const active = createMemo(() => monitors().filter((monitor) => monitor.enabled));
3675
+ const refresh = async () => {
3676
+ let client;
3677
+ try {
3678
+ client = await connectDaemonClient({
3679
+ name: "sourcefed-opencode-tui",
3680
+ url: process.env.SOURCEFED_DAEMON_URL ?? defaultDaemonUrl()
3681
+ });
3682
+ const result = await client.request("monitor.list", {
3683
+ target: {
3684
+ kind: "opencode-session",
3685
+ id: props.sessionID
3686
+ }
3687
+ });
3688
+ setMonitors(result?.monitors ?? []);
3689
+ } catch {
3690
+ setMonitors([]);
3691
+ } finally {
3692
+ await client?.close();
3693
+ }
3694
+ };
3695
+ void refresh();
3696
+ const timer = setInterval(() => void refresh(), REFRESH_MS);
3697
+ onCleanup(() => clearInterval(timer));
3698
+ return (() => {
3699
+ var _el$ = _$createElement("box"), _el$2 = _$createElement("box"), _el$3 = _$createElement("text"), _el$5 = _$createElement("text"), _el$6 = _$createTextNode(` (`), _el$7 = _$createTextNode(`)`);
3700
+ _$insertNode(_el$, _el$2);
3701
+ _$setProp(_el$, "flexDirection", "column");
3702
+ _$setProp(_el$, "width", "100%");
3703
+ _$setProp(_el$, "marginTop", 1);
3704
+ _$insertNode(_el$2, _el$3);
3705
+ _$insertNode(_el$2, _el$5);
3706
+ _$setProp(_el$2, "flexDirection", "row");
3707
+ _$setProp(_el$2, "width", "100%");
3708
+ _$insertNode(_el$3, _$createTextNode(`Sourcefed`));
3709
+ _$insertNode(_el$5, _el$6);
3710
+ _$insertNode(_el$5, _el$7);
3711
+ _$insert(_el$5, () => active().length, _el$7);
3712
+ _$insert(_el$, _$createComponent(MonitorRows, {
3713
+ monitors: active,
3714
+ get theme() {
3715
+ return theme();
3716
+ },
3717
+ compact: true
3718
+ }), null);
3719
+ _$effect((_p$) => {
3720
+ var _v$ = theme().accent, _v$2 = theme().textMuted;
3721
+ _v$ !== _p$.e && (_p$.e = _$setProp(_el$3, "fg", _v$, _p$.e));
3722
+ _v$2 !== _p$.t && (_p$.t = _$setProp(_el$5, "fg", _v$2, _p$.t));
3723
+ return _p$;
3724
+ }, {
3725
+ e: void 0,
3726
+ t: void 0
3727
+ });
3728
+ return _el$;
3729
+ })();
3730
+ }
3731
+ function MonitorRows(props) {
3732
+ const visible = createMemo(() => props.monitors().slice(0, 4));
3733
+ return (() => {
3734
+ var _el$8 = _$createElement("box");
3735
+ _$setProp(_el$8, "flexDirection", "column");
3736
+ _$setProp(_el$8, "width", "100%");
3737
+ _$insert(_el$8, _$createComponent(Show, {
3738
+ get when() {
3739
+ return visible().length > 0;
3740
+ },
3741
+ get fallback() {
3742
+ return (() => {
3743
+ var _el$1 = _$createElement("text");
3744
+ _$insertNode(_el$1, _$createTextNode(`No active monitors`));
3745
+ _$effect((_$p) => _$setProp(_el$1, "fg", props.theme.textMuted, _$p));
3746
+ return _el$1;
3747
+ })();
3748
+ },
3749
+ get children() {
3750
+ return visible().map((monitor) => (() => {
3751
+ var _el$11 = _$createElement("box"), _el$12 = _$createElement("text"), _el$13 = _$createElement("text");
3752
+ _$insertNode(_el$11, _el$12);
3753
+ _$insertNode(_el$11, _el$13);
3754
+ _$setProp(_el$11, "flexDirection", "row");
3755
+ _$setProp(_el$11, "width", "100%");
3756
+ _$insert(_el$12, () => monitor.icon);
3757
+ _$insert(_el$13, () => monitor.detail);
3758
+ _$effect((_p$) => {
3759
+ var _v$3 = monitorTone(monitor, props.theme), _v$4 = props.theme.textMuted;
3760
+ _v$3 !== _p$.e && (_p$.e = _$setProp(_el$12, "fg", _v$3, _p$.e));
3761
+ _v$4 !== _p$.t && (_p$.t = _$setProp(_el$13, "fg", _v$4, _p$.t));
3762
+ return _p$;
3763
+ }, {
3764
+ e: void 0,
3765
+ t: void 0
3766
+ });
3767
+ return _el$11;
3768
+ })());
3769
+ }
3770
+ }), null);
3771
+ _$insert(_el$8, _$createComponent(Show, {
3772
+ get when() {
3773
+ return _$memo(() => !!props.compact)() && visible().length < props.monitors().length;
3774
+ },
3775
+ get children() {
3776
+ var _el$9 = _$createElement("text");
3777
+ _$insertNode(_el$9, _$createTextNode(`Open Sourcefed for more`));
3778
+ _$effect((_$p) => _$setProp(_el$9, "fg", props.theme.textMuted, _$p));
3779
+ return _el$9;
3780
+ }
3781
+ }), null);
3782
+ return _el$8;
3783
+ })();
3784
+ }
3785
+ function monitorTone(monitor, theme) {
3786
+ if (!monitor.enabled) return theme.textMuted;
3787
+ return theme.success;
3788
+ }
3789
+
3790
+ // .tui-build/tui/plugin.mjs
3791
+ var sourcefedTui = async (api) => {
3792
+ api.slots.register({
3793
+ order: 190,
3794
+ slots: {
3795
+ sidebar_content: (_context, value) => _$createComponent2(Sidebar, {
3796
+ api,
3797
+ get sessionID() {
3798
+ return value.session_id;
3799
+ }
3800
+ })
3801
+ }
3802
+ });
3803
+ let client;
3804
+ const getSessionID = () => {
3805
+ const currentRoute = api.route.current;
3806
+ return "params" in currentRoute && typeof currentRoute.params?.sessionID === "string" ? currentRoute.params.sessionID : void 0;
3807
+ };
3808
+ const getClient = async () => {
3809
+ if (client) return client;
3810
+ client = await connectDaemonClient({
3811
+ name: "sourcefed-opencode-tui",
3812
+ url: process.env.SOURCEFED_DAEMON_URL ?? defaultDaemonUrl()
3813
+ });
3814
+ return client;
3815
+ };
3816
+ const unregister = api.command?.register(() => [{
3817
+ value: "sourcefed",
3818
+ title: "Sourcefed monitors",
3819
+ description: "Show monitors for the current OpenCode session",
3820
+ slash: {
3821
+ name: "sourcefed"
3822
+ },
3823
+ onSelect: async (dialog) => {
3824
+ const sessionID = getSessionID();
3825
+ if (!sessionID) {
3826
+ api.ui.toast({
3827
+ variant: "warning",
3828
+ message: "No active OpenCode session"
3829
+ });
3830
+ return;
3831
+ }
3832
+ try {
3833
+ const daemon = await getClient();
3834
+ if (!daemon) throw new Error("no daemon client");
3835
+ const result = await daemon.request("monitor.list", {
3836
+ target: {
3837
+ kind: "opencode-session",
3838
+ id: sessionID
3839
+ }
3840
+ });
3841
+ const monitors = result?.monitors ?? [];
3842
+ api.ui.dialog.replace(() => _$createComponent2(MonitorDialog, {
3843
+ api,
3844
+ monitors
3845
+ }));
3846
+ api.ui.dialog.setSize("large");
3847
+ } catch (error) {
3848
+ api.ui.toast({
3849
+ variant: "error",
3850
+ message: `Sourcefed daemon unavailable: ${error instanceof Error ? error.message : String(error)}`
3851
+ });
3852
+ }
3853
+ }
3854
+ }, {
3855
+ value: "sourcefed-logs",
3856
+ title: "Sourcefed logs",
3857
+ description: "Show recent Sourcefed notifications for the current OpenCode session",
3858
+ slash: {
3859
+ name: "sourcefed-logs"
3860
+ },
3861
+ onSelect: async (dialog) => {
3862
+ const sessionID = getSessionID();
3863
+ if (!sessionID) {
3864
+ api.ui.toast({
3865
+ variant: "warning",
3866
+ message: "No active OpenCode session"
3867
+ });
3868
+ return;
3869
+ }
3870
+ try {
3871
+ const daemon = await getClient();
3872
+ if (!daemon) throw new Error("no daemon client");
3873
+ const result = await daemon.request("monitor.logs", {
3874
+ target: {
3875
+ kind: "opencode-session",
3876
+ id: sessionID
3877
+ }
3878
+ });
3879
+ const logs = result?.logs ?? [];
3880
+ api.ui.dialog.replace(() => _$createComponent2(LogsDialog, {
3881
+ api,
3882
+ logs
3883
+ }));
3884
+ api.ui.dialog.setSize("large");
3885
+ } catch (error) {
3886
+ api.ui.toast({
3887
+ variant: "error",
3888
+ message: `Sourcefed daemon unavailable: ${error instanceof Error ? error.message : String(error)}`
3889
+ });
3890
+ }
3891
+ }
3892
+ }]);
3893
+ void unregister;
3894
+ };
3895
+ function MonitorDialog(props) {
3896
+ const theme = createMemo2(() => props.api.theme.current);
3897
+ const active = props.monitors.filter((monitor) => monitor.enabled);
3898
+ const maxListRows = Math.max(6, Math.floor(props.api.renderer.height * 0.45));
3899
+ return (() => {
3900
+ var _el$ = _$createElement2("box"), _el$2 = _$createElement2("box"), _el$3 = _$createElement2("text"), _el$4 = _$createTextNode2(`Sourcefed monitors (`), _el$5 = _$createTextNode2(`)`), _el$6 = _$createElement2("box"), _el$7 = _$createElement2("text");
3901
+ _$insertNode2(_el$, _el$2);
3902
+ _$setProp2(_el$, "flexDirection", "column");
3903
+ _$setProp2(_el$, "width", "100%");
3904
+ _$setProp2(_el$, "paddingX", 2);
3905
+ _$setProp2(_el$, "paddingY", 1);
3906
+ _$insertNode2(_el$2, _el$3);
3907
+ _$insertNode2(_el$2, _el$6);
3908
+ _$insertNode2(_el$2, _el$7);
3909
+ _$setProp2(_el$2, "flexDirection", "row");
3910
+ _$setProp2(_el$2, "width", "100%");
3911
+ _$setProp2(_el$2, "minWidth", 0);
3912
+ _$insertNode2(_el$3, _el$4);
3913
+ _$insertNode2(_el$3, _el$5);
3914
+ _$setProp2(_el$3, "attributes", 1);
3915
+ _$insert2(_el$3, () => active.length, _el$5);
3916
+ _$setProp2(_el$6, "flexGrow", 1);
3917
+ _$insertNode2(_el$7, _$createTextNode2(`esc`));
3918
+ _$insert2(_el$, _$createComponent2(Show2, {
3919
+ get when() {
3920
+ return active.length === 0;
3921
+ },
3922
+ get fallback() {
3923
+ return (() => {
3924
+ var _el$1 = _$createElement2("scrollbox"), _el$10 = _$createElement2("box");
3925
+ _$insertNode2(_el$1, _el$10);
3926
+ _$setProp2(_el$1, "maxHeight", maxListRows);
3927
+ _$setProp2(_el$1, "scrollY", true);
3928
+ _$setProp2(_el$10, "flexDirection", "column");
3929
+ _$setProp2(_el$10, "width", "100%");
3930
+ _$insert2(_el$10, () => active.map((monitor) => (() => {
3931
+ var _el$11 = _$createElement2("box"), _el$12 = _$createElement2("text"), _el$14 = _$createElement2("text"), _el$15 = _$createTextNode2(` `), _el$16 = _$createTextNode2(` `), _el$17 = _$createElement2("text");
3932
+ _$insertNode2(_el$11, _el$12);
3933
+ _$insertNode2(_el$11, _el$14);
3934
+ _$insertNode2(_el$11, _el$17);
3935
+ _$setProp2(_el$11, "flexDirection", "row");
3936
+ _$setProp2(_el$11, "width", "100%");
3937
+ _$setProp2(_el$11, "minWidth", 0);
3938
+ _$insertNode2(_el$12, _$createTextNode2(`\u25CF`));
3939
+ _$insertNode2(_el$14, _el$15);
3940
+ _$insertNode2(_el$14, _el$16);
3941
+ _$insert2(_el$14, () => monitor.icon, _el$16);
3942
+ _$setProp2(_el$17, "flexGrow", 1);
3943
+ _$setProp2(_el$17, "flexShrink", 1);
3944
+ _$setProp2(_el$17, "minWidth", 0);
3945
+ _$setProp2(_el$17, "truncate", true);
3946
+ _$insert2(_el$17, () => monitor.describe);
3947
+ _$effect2((_p$) => {
3948
+ var _v$3 = monitor.unresponsive ? theme().error : theme().success, _v$4 = theme().text, _v$5 = theme().text;
3949
+ _v$3 !== _p$.e && (_p$.e = _$setProp2(_el$12, "fg", _v$3, _p$.e));
3950
+ _v$4 !== _p$.t && (_p$.t = _$setProp2(_el$14, "fg", _v$4, _p$.t));
3951
+ _v$5 !== _p$.a && (_p$.a = _$setProp2(_el$17, "fg", _v$5, _p$.a));
3952
+ return _p$;
3953
+ }, {
3954
+ e: void 0,
3955
+ t: void 0,
3956
+ a: void 0
3957
+ });
3958
+ return _el$11;
3959
+ })()));
3960
+ return _el$1;
3961
+ })();
3962
+ },
3963
+ get children() {
3964
+ var _el$9 = _$createElement2("text");
3965
+ _$insertNode2(_el$9, _$createTextNode2(`No active monitors`));
3966
+ _$effect2((_$p) => _$setProp2(_el$9, "fg", theme().textMuted, _$p));
3967
+ return _el$9;
3968
+ }
3969
+ }), null);
3970
+ _$effect2((_p$) => {
3971
+ var _v$ = theme().text, _v$2 = theme().textMuted;
3972
+ _v$ !== _p$.e && (_p$.e = _$setProp2(_el$3, "fg", _v$, _p$.e));
3973
+ _v$2 !== _p$.t && (_p$.t = _$setProp2(_el$7, "fg", _v$2, _p$.t));
3974
+ return _p$;
3975
+ }, {
3976
+ e: void 0,
3977
+ t: void 0
3978
+ });
3979
+ return _el$;
3980
+ })();
3981
+ }
3982
+ function LogsDialog(props) {
3983
+ const theme = createMemo2(() => props.api.theme.current);
3984
+ const maxListRows = Math.max(6, Math.floor(props.api.renderer.height * 0.45));
3985
+ return (() => {
3986
+ var _el$18 = _$createElement2("box"), _el$19 = _$createElement2("box"), _el$20 = _$createElement2("text"), _el$21 = _$createTextNode2(`Sourcefed notifications (`), _el$22 = _$createTextNode2(`)`), _el$23 = _$createElement2("box"), _el$24 = _$createElement2("text");
3987
+ _$insertNode2(_el$18, _el$19);
3988
+ _$setProp2(_el$18, "flexDirection", "column");
3989
+ _$setProp2(_el$18, "width", "100%");
3990
+ _$setProp2(_el$18, "paddingX", 2);
3991
+ _$setProp2(_el$18, "paddingY", 1);
3992
+ _$insertNode2(_el$19, _el$20);
3993
+ _$insertNode2(_el$19, _el$23);
3994
+ _$insertNode2(_el$19, _el$24);
3995
+ _$setProp2(_el$19, "flexDirection", "row");
3996
+ _$setProp2(_el$19, "width", "100%");
3997
+ _$setProp2(_el$19, "minWidth", 0);
3998
+ _$insertNode2(_el$20, _el$21);
3999
+ _$insertNode2(_el$20, _el$22);
4000
+ _$setProp2(_el$20, "attributes", 1);
4001
+ _$insert2(_el$20, () => props.logs.length, _el$22);
4002
+ _$setProp2(_el$23, "flexGrow", 1);
4003
+ _$insertNode2(_el$24, _$createTextNode2(`esc`));
4004
+ _$insert2(_el$18, _$createComponent2(Show2, {
4005
+ get when() {
4006
+ return props.logs.length > 0;
4007
+ },
4008
+ get fallback() {
4009
+ return (() => {
4010
+ var _el$28 = _$createElement2("text");
4011
+ _$insertNode2(_el$28, _$createTextNode2(`No notifications sent yet`));
4012
+ _$effect2((_$p) => _$setProp2(_el$28, "fg", theme().textMuted, _$p));
4013
+ return _el$28;
4014
+ })();
4015
+ },
4016
+ get children() {
4017
+ var _el$26 = _$createElement2("scrollbox"), _el$27 = _$createElement2("box");
4018
+ _$insertNode2(_el$26, _el$27);
4019
+ _$setProp2(_el$26, "maxHeight", maxListRows);
4020
+ _$setProp2(_el$26, "scrollY", true);
4021
+ _$setProp2(_el$26, "stickyScroll", true);
4022
+ _$setProp2(_el$26, "stickyStart", "bottom");
4023
+ _$setProp2(_el$27, "flexDirection", "column");
4024
+ _$setProp2(_el$27, "width", "100%");
4025
+ _$insert2(_el$27, () => props.logs.map((entry) => (() => {
4026
+ var _el$30 = _$createElement2("box"), _el$31 = _$createElement2("box"), _el$32 = _$createElement2("text"), _el$33 = _$createElement2("text"), _el$34 = _$createTextNode2(` `), _el$35 = _$createElement2("text"), _el$36 = _$createTextNode2(` `), _el$37 = _$createTextNode2(` `);
4027
+ _$insertNode2(_el$30, _el$31);
4028
+ _$setProp2(_el$30, "flexDirection", "column");
4029
+ _$setProp2(_el$30, "width", "100%");
4030
+ _$insertNode2(_el$31, _el$32);
4031
+ _$insertNode2(_el$31, _el$33);
4032
+ _$insertNode2(_el$31, _el$35);
4033
+ _$setProp2(_el$31, "flexDirection", "row");
4034
+ _$setProp2(_el$31, "width", "100%");
4035
+ _$setProp2(_el$31, "minWidth", 0);
4036
+ _$insert2(_el$32, () => entry.actionable ? "\u25B6" : "\xB7");
4037
+ _$insertNode2(_el$33, _el$34);
4038
+ _$setProp2(_el$33, "flexShrink", 0);
4039
+ _$setProp2(_el$33, "minWidth", 0);
4040
+ _$insert2(_el$33, () => entry.icon, null);
4041
+ _$insertNode2(_el$35, _el$36);
4042
+ _$insertNode2(_el$35, _el$37);
4043
+ _$setProp2(_el$35, "flexGrow", 1);
4044
+ _$setProp2(_el$35, "flexShrink", 1);
4045
+ _$setProp2(_el$35, "minWidth", 0);
4046
+ _$setProp2(_el$35, "truncate", true);
4047
+ _$insert2(_el$35, () => new Date(entry.at).toLocaleString(), _el$37);
4048
+ _$insert2(_el$35, () => entry.summary, null);
4049
+ _$insert2(_el$30, _$createComponent2(Show2, {
4050
+ get when() {
4051
+ return entry.body;
4052
+ },
4053
+ get children() {
4054
+ var _el$38 = _$createElement2("text"), _el$39 = _$createTextNode2(` `);
4055
+ _$insertNode2(_el$38, _el$39);
4056
+ _$insert2(_el$38, () => entry.body, null);
4057
+ _$effect2((_$p) => _$setProp2(_el$38, "fg", theme().textMuted, _$p));
4058
+ return _el$38;
4059
+ }
4060
+ }), null);
4061
+ _$effect2((_p$) => {
4062
+ var _v$8 = entry.actionable ? theme().warning : theme().textMuted, _v$9 = theme().text, _v$0 = theme().text;
4063
+ _v$8 !== _p$.e && (_p$.e = _$setProp2(_el$32, "fg", _v$8, _p$.e));
4064
+ _v$9 !== _p$.t && (_p$.t = _$setProp2(_el$33, "fg", _v$9, _p$.t));
4065
+ _v$0 !== _p$.a && (_p$.a = _$setProp2(_el$35, "fg", _v$0, _p$.a));
4066
+ return _p$;
4067
+ }, {
4068
+ e: void 0,
4069
+ t: void 0,
4070
+ a: void 0
4071
+ });
4072
+ return _el$30;
4073
+ })()));
4074
+ return _el$26;
4075
+ }
4076
+ }), null);
4077
+ _$effect2((_p$) => {
4078
+ var _v$6 = theme().text, _v$7 = theme().textMuted;
4079
+ _v$6 !== _p$.e && (_p$.e = _$setProp2(_el$20, "fg", _v$6, _p$.e));
4080
+ _v$7 !== _p$.t && (_p$.t = _$setProp2(_el$24, "fg", _v$7, _p$.t));
4081
+ return _p$;
4082
+ }, {
4083
+ e: void 0,
4084
+ t: void 0
4085
+ });
4086
+ return _el$18;
4087
+ })();
4088
+ }
4089
+
4090
+ // .tui-build/tui/index.mjs
4091
+ var id = "sourcefed-tui";
4092
+ var index_default = { id, tui: sourcefedTui };
4093
+ export {
4094
+ index_default as default,
4095
+ id
4096
+ };