@hraness/ghostget 0.17.2

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 (500) hide show
  1. package/CHANGELOG.md +458 -0
  2. package/LICENSE +21 -0
  3. package/README.md +1359 -0
  4. package/bunfig.toml +2 -0
  5. package/dist/apple-photos-client.js +704 -0
  6. package/dist/beeper-client.js +4602 -0
  7. package/dist/client.js +1605 -0
  8. package/dist/index-26yq8q16.js +31 -0
  9. package/dist/index-9t13b4k7.js +508 -0
  10. package/dist/index-axwm9b3v.js +5 -0
  11. package/dist/index-gwk7rbyj.js +30 -0
  12. package/dist/index.js +701 -0
  13. package/dist/messaging.js +1130 -0
  14. package/dist/omni-client.js +1406 -0
  15. package/dist/whatsapp-client.js +355 -0
  16. package/docs/imessage-direct-provider.md +106 -0
  17. package/docs/rental-listings.md +94 -0
  18. package/package.json +600 -0
  19. package/skills/ghostget/SKILL.md +251 -0
  20. package/skills/ghostget/agents/openai.yaml +4 -0
  21. package/skills/ghostget/references/adapter-contract.md +181 -0
  22. package/skills/ghostget/references/apple-photos.md +115 -0
  23. package/skills/ghostget/references/article-drafts.md +235 -0
  24. package/skills/ghostget/references/code-owned-provider-scaffold.md +134 -0
  25. package/skills/ghostget/references/cross-posting-video.md +139 -0
  26. package/skills/ghostget/references/cross-posting.md +82 -0
  27. package/skills/ghostget/references/derivation.md +390 -0
  28. package/skills/ghostget/references/hraness-social-profile-stats.json +269 -0
  29. package/skills/ghostget/references/install.md +66 -0
  30. package/skills/ghostget/references/linkedin-adapter.md +442 -0
  31. package/skills/ghostget/references/messaging.md +168 -0
  32. package/skills/ghostget/references/meta-comet-contract.md +153 -0
  33. package/skills/ghostget/references/platform-patterns.md +122 -0
  34. package/skills/ghostget/references/provider-plugins.md +496 -0
  35. package/skills/ghostget/references/reddit-flair.md +78 -0
  36. package/skills/ghostget/references/safety-and-state.md +214 -0
  37. package/skills/ghostget/references/settlement-and-duplicate-cleanup.md +79 -0
  38. package/skills/ghostget/references/social-platform-routing.md +66 -0
  39. package/skills/ghostget/references/social-profile-stats.md +195 -0
  40. package/skills/ghostget/references/social-video-platform-routing.md +89 -0
  41. package/skills/ghostget/references/whatsapp-adapter.md +101 -0
  42. package/skills/ghostget/references/x-adapter.md +332 -0
  43. package/skills/ghostget/references/x-ai-disclosure.md +71 -0
  44. package/src/apple-photos-cli.ts +639 -0
  45. package/src/apple-photos-client-types.ts +168 -0
  46. package/src/apple-photos-client.ts +201 -0
  47. package/src/apple-photos-contact-evidence.ts +838 -0
  48. package/src/apple-photos-local-source.ts +1131 -0
  49. package/src/args.ts +2053 -0
  50. package/src/article-draft-document.ts +417 -0
  51. package/src/article-draft-embeds.ts +123 -0
  52. package/src/article-draft-images.ts +214 -0
  53. package/src/assets/adapter-template/wrench-adapter.json +40 -0
  54. package/src/assets/adapters/beeper/wrench-web-adapter.json +1500 -0
  55. package/src/assets/adapters/beeper/wrench-web-adapter.v1.0.0.json +129 -0
  56. package/src/assets/adapters/beeper/wrench-web-adapter.v1.1.0.json +207 -0
  57. package/src/assets/adapters/beeper/wrench-web-adapter.v2.0.0.json +1463 -0
  58. package/src/assets/adapters/beeper/wrench-web-adapter.v2.1.0.json +1463 -0
  59. package/src/assets/adapters/beeper/wrench-web-adapter.v2.2.0.json +1475 -0
  60. package/src/assets/adapters/beeper/wrench-web-adapter.v2.3.0.json +1488 -0
  61. package/src/assets/adapters/bluesky/wrench-web-adapter.json +684 -0
  62. package/src/assets/adapters/bluesky/wrench-web-adapter.v1.0.0.json +581 -0
  63. package/src/assets/adapters/bluesky/wrench-web-adapter.v1.1.0.json +581 -0
  64. package/src/assets/adapters/bluesky/wrench-web-adapter.v1.2.0.json +581 -0
  65. package/src/assets/adapters/bluesky/wrench-web-adapter.v1.3.0.json +615 -0
  66. package/src/assets/adapters/bluesky/wrench-web-adapter.v1.4.0.json +684 -0
  67. package/src/assets/adapters/bluesky/wrench-web-adapter.v1.5.0.json +684 -0
  68. package/src/assets/adapters/bluesky/wrench-web-adapter.v1.6.0.json +684 -0
  69. package/src/assets/adapters/clasificados/wrench-web-adapter.json +54 -0
  70. package/src/assets/adapters/facebook/wrench-web-adapter.json +750 -0
  71. package/src/assets/adapters/facebook/wrench-web-adapter.v1.0.0.json +706 -0
  72. package/src/assets/adapters/facebook/wrench-web-adapter.v1.1.0.json +703 -0
  73. package/src/assets/adapters/facebook/wrench-web-adapter.v1.2.0.json +750 -0
  74. package/src/assets/adapters/facebook-group/wrench-web-adapter.json +682 -0
  75. package/src/assets/adapters/facebook-group/wrench-web-adapter.v1.0.0.json +583 -0
  76. package/src/assets/adapters/facebook-group/wrench-web-adapter.v1.1.0.json +682 -0
  77. package/src/assets/adapters/facebook-marketplace/wrench-web-adapter.json +437 -0
  78. package/src/assets/adapters/facebook-marketplace/wrench-web-adapter.v1.0.0.json +383 -0
  79. package/src/assets/adapters/facebook-marketplace/wrench-web-adapter.v1.1.0.json +437 -0
  80. package/src/assets/adapters/facebook-marketplace/wrench-web-adapter.v1.1.1.json +437 -0
  81. package/src/assets/adapters/facebook-page/wrench-web-adapter.json +880 -0
  82. package/src/assets/adapters/facebook-page/wrench-web-adapter.v1.0.0.json +740 -0
  83. package/src/assets/adapters/facebook-page/wrench-web-adapter.v1.1.0.json +880 -0
  84. package/src/assets/adapters/github/wrench-web-adapter.json +69 -0
  85. package/src/assets/adapters/github/wrench-web-adapter.v1.0.0.json +42 -0
  86. package/src/assets/adapters/github/wrench-web-adapter.v1.1.0.json +69 -0
  87. package/src/assets/adapters/gmail/wrench-adapter.json +161 -0
  88. package/src/assets/adapters/gmail/wrench-adapter.v1.2.0.json +157 -0
  89. package/src/assets/adapters/hacker-news/wrench-web-adapter.json +315 -0
  90. package/src/assets/adapters/hacker-news/wrench-web-adapter.v1.0.0.json +315 -0
  91. package/src/assets/adapters/imessage/wrench-web-adapter.json +236 -0
  92. package/src/assets/adapters/instagram/wrench-web-adapter.json +719 -0
  93. package/src/assets/adapters/instagram/wrench-web-adapter.v1.0.0.json +627 -0
  94. package/src/assets/adapters/instagram/wrench-web-adapter.v1.1.0.json +609 -0
  95. package/src/assets/adapters/instagram/wrench-web-adapter.v1.2.0.json +640 -0
  96. package/src/assets/adapters/instagram/wrench-web-adapter.v1.3.0.json +671 -0
  97. package/src/assets/adapters/instagram/wrench-web-adapter.v1.4.0.json +710 -0
  98. package/src/assets/adapters/instagram/wrench-web-adapter.v1.5.0.json +710 -0
  99. package/src/assets/adapters/instagram/wrench-web-adapter.v1.6.0.json +719 -0
  100. package/src/assets/adapters/instagram/wrench-web-adapter.v1.7.0.json +719 -0
  101. package/src/assets/adapters/linkedin/wrench-adapter.json +442 -0
  102. package/src/assets/adapters/linkedin/wrench-adapter.v0.4.0.json +124 -0
  103. package/src/assets/adapters/linkedin/wrench-adapter.v1.0.0.json +411 -0
  104. package/src/assets/adapters/linkedin/wrench-web-adapter.json +914 -0
  105. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.0.0.json +604 -0
  106. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.1.0.json +604 -0
  107. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.10.0.json +809 -0
  108. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.11.0.json +809 -0
  109. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.12.0.json +820 -0
  110. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.13.0.json +819 -0
  111. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.14.0.json +819 -0
  112. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.15.0.json +819 -0
  113. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.16.0.json +874 -0
  114. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.17.0.json +874 -0
  115. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.18.0.json +874 -0
  116. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.19.0.json +874 -0
  117. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.2.0.json +731 -0
  118. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.3.0.json +768 -0
  119. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.4.0.json +808 -0
  120. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.5.0.json +808 -0
  121. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.6.0.json +808 -0
  122. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.7.0.json +825 -0
  123. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.8.0.json +792 -0
  124. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.9.0.json +809 -0
  125. package/src/assets/adapters/reddit/wrench-web-adapter.json +855 -0
  126. package/src/assets/adapters/reddit/wrench-web-adapter.v1.0.0.json +577 -0
  127. package/src/assets/adapters/reddit/wrench-web-adapter.v1.1.0.json +604 -0
  128. package/src/assets/adapters/reddit/wrench-web-adapter.v1.10.0.json +712 -0
  129. package/src/assets/adapters/reddit/wrench-web-adapter.v1.11.0.json +712 -0
  130. package/src/assets/adapters/reddit/wrench-web-adapter.v1.12.0.json +712 -0
  131. package/src/assets/adapters/reddit/wrench-web-adapter.v1.2.0.json +653 -0
  132. package/src/assets/adapters/reddit/wrench-web-adapter.v1.3.0.json +712 -0
  133. package/src/assets/adapters/reddit/wrench-web-adapter.v1.4.0.json +713 -0
  134. package/src/assets/adapters/reddit/wrench-web-adapter.v1.5.0.json +712 -0
  135. package/src/assets/adapters/reddit/wrench-web-adapter.v1.6.0.json +713 -0
  136. package/src/assets/adapters/reddit/wrench-web-adapter.v1.7.0.json +713 -0
  137. package/src/assets/adapters/reddit/wrench-web-adapter.v1.8.0.json +713 -0
  138. package/src/assets/adapters/reddit/wrench-web-adapter.v1.9.0.json +713 -0
  139. package/src/assets/adapters/substack/wrench-web-adapter.json +936 -0
  140. package/src/assets/adapters/substack/wrench-web-adapter.v1.0.0.json +817 -0
  141. package/src/assets/adapters/substack/wrench-web-adapter.v1.1.0.json +812 -0
  142. package/src/assets/adapters/substack/wrench-web-adapter.v1.2.0.json +812 -0
  143. package/src/assets/adapters/substack/wrench-web-adapter.v1.3.0.json +902 -0
  144. package/src/assets/adapters/substack/wrench-web-adapter.v1.4.0.json +936 -0
  145. package/src/assets/adapters/substack/wrench-web-adapter.v1.5.0.json +936 -0
  146. package/src/assets/adapters/substack/wrench-web-adapter.v1.6.0.json +936 -0
  147. package/src/assets/adapters/threads/wrench-web-adapter.json +692 -0
  148. package/src/assets/adapters/threads/wrench-web-adapter.v1.0.0.json +629 -0
  149. package/src/assets/adapters/threads/wrench-web-adapter.v1.1.0.json +623 -0
  150. package/src/assets/adapters/threads/wrench-web-adapter.v1.2.0.json +622 -0
  151. package/src/assets/adapters/threads/wrench-web-adapter.v1.3.0.json +622 -0
  152. package/src/assets/adapters/threads/wrench-web-adapter.v1.4.0.json +622 -0
  153. package/src/assets/adapters/threads/wrench-web-adapter.v1.5.0.json +699 -0
  154. package/src/assets/adapters/threads/wrench-web-adapter.v1.6.0.json +693 -0
  155. package/src/assets/adapters/threads/wrench-web-adapter.v1.7.0.json +692 -0
  156. package/src/assets/adapters/tiktok/wrench-web-adapter.json +762 -0
  157. package/src/assets/adapters/tiktok/wrench-web-adapter.v1.0.0.json +681 -0
  158. package/src/assets/adapters/tiktok/wrench-web-adapter.v1.1.0.json +708 -0
  159. package/src/assets/adapters/tiktok/wrench-web-adapter.v1.2.0.json +762 -0
  160. package/src/assets/adapters/tiktok/wrench-web-adapter.v1.3.0.json +762 -0
  161. package/src/assets/adapters/twitch/wrench-web-adapter.json +42 -0
  162. package/src/assets/adapters/twitch/wrench-web-adapter.v1.0.0.json +42 -0
  163. package/src/assets/adapters/whatsapp/wrench-web-adapter.json +400 -0
  164. package/src/assets/adapters/whatsapp/wrench-web-adapter.v1.0.0.json +355 -0
  165. package/src/assets/adapters/whatsapp/wrench-web-adapter.v1.1.0.json +386 -0
  166. package/src/assets/adapters/whatsapp/wrench-web-adapter.v1.2.0.json +386 -0
  167. package/src/assets/adapters/whatsapp/wrench-web-adapter.v1.3.0.json +400 -0
  168. package/src/assets/adapters/x/wrench-adapter.json +749 -0
  169. package/src/assets/adapters/x/wrench-adapter.v1.0.0.json +700 -0
  170. package/src/assets/adapters/x/wrench-adapter.v1.1.0.json +707 -0
  171. package/src/assets/adapters/x/wrench-web-adapter.json +712 -0
  172. package/src/assets/adapters/x/wrench-web-adapter.v1.1.0.json +579 -0
  173. package/src/assets/adapters/x/wrench-web-adapter.v1.10.0.json +685 -0
  174. package/src/assets/adapters/x/wrench-web-adapter.v1.11.0.json +712 -0
  175. package/src/assets/adapters/x/wrench-web-adapter.v1.12.0.json +712 -0
  176. package/src/assets/adapters/x/wrench-web-adapter.v1.13.0.json +712 -0
  177. package/src/assets/adapters/x/wrench-web-adapter.v1.2.0.json +578 -0
  178. package/src/assets/adapters/x/wrench-web-adapter.v1.3.0.json +610 -0
  179. package/src/assets/adapters/x/wrench-web-adapter.v1.4.0.json +617 -0
  180. package/src/assets/adapters/x/wrench-web-adapter.v1.5.0.json +634 -0
  181. package/src/assets/adapters/x/wrench-web-adapter.v1.6.0.json +632 -0
  182. package/src/assets/adapters/x/wrench-web-adapter.v1.7.0.json +649 -0
  183. package/src/assets/adapters/x/wrench-web-adapter.v1.8.0.json +649 -0
  184. package/src/assets/adapters/x/wrench-web-adapter.v1.9.0.json +683 -0
  185. package/src/assets/adapters/youtube/wrench-web-adapter.json +561 -0
  186. package/src/assets/adapters/youtube/wrench-web-adapter.v1.0.0.json +465 -0
  187. package/src/assets/adapters/youtube/wrench-web-adapter.v1.1.0.json +519 -0
  188. package/src/assets/adapters/youtube/wrench-web-adapter.v1.2.0.json +561 -0
  189. package/src/assets/adapters/youtube/wrench-web-adapter.v1.3.0.json +561 -0
  190. package/src/assets/code-owned-provider-template/plugin.test.ts.template +54 -0
  191. package/src/assets/code-owned-provider-template/plugin.ts.template +102 -0
  192. package/src/assets/code-owned-provider-template/runtime.internal.test.ts.template +44 -0
  193. package/src/assets/code-owned-provider-template/runtime.ts.template +59 -0
  194. package/src/assets/code-owned-provider-template/wrench-adapter.json.template +33 -0
  195. package/src/assets/fonts/nebula-sans/LICENSE.txt +96 -0
  196. package/src/assets/fonts/nebula-sans/NebulaSans-Book.woff2 +0 -0
  197. package/src/assets/fonts/nebula-sans/PROVENANCE.md +14 -0
  198. package/src/assets.d.ts +4 -0
  199. package/src/auth.ts +1157 -0
  200. package/src/beeper-client-types.ts +169 -0
  201. package/src/beeper-client.ts +250 -0
  202. package/src/beeper-contact-interactions-cli.ts +180 -0
  203. package/src/beeper-contact-interactions.ts +1612 -0
  204. package/src/beeper-message-bundle-v1.ts +257 -0
  205. package/src/beeper-message-like-me-cli.ts +84 -0
  206. package/src/beeper-message-like-me-export.ts +2274 -0
  207. package/src/beeper-message-like-me-recovery.ts +931 -0
  208. package/src/beeper-message-like-me-source.ts +4034 -0
  209. package/src/browser-admission.ts +928 -0
  210. package/src/browser-snapshots.ts +260 -0
  211. package/src/browser.ts +3873 -0
  212. package/src/canonical-json.ts +60 -0
  213. package/src/catalog-cli.ts +638 -0
  214. package/src/cli.ts +208 -0
  215. package/src/client-types.ts +301 -0
  216. package/src/client.ts +2726 -0
  217. package/src/confirmed-write-failure.ts +44 -0
  218. package/src/confirmed-write-model.ts +72 -0
  219. package/src/confirmed-write-platform.ts +1055 -0
  220. package/src/confirmed-write-program.ts +224 -0
  221. package/src/confirmed-write-runtime.ts +16 -0
  222. package/src/cursor-token.ts +228 -0
  223. package/src/derivation-dnr-readiness.ts +367 -0
  224. package/src/derivation-file-chooser.ts +384 -0
  225. package/src/derivation-network-boundary.ts +835 -0
  226. package/src/derivation-network-guard.ts +782 -0
  227. package/src/derivation-network-proxy-helper.ts +440 -0
  228. package/src/derivation-network-proxy.ts +385 -0
  229. package/src/derive-command-helper.ts +693 -0
  230. package/src/derive-fixtures.ts +343 -0
  231. package/src/derive-review.ts +980 -0
  232. package/src/derive.ts +4754 -0
  233. package/src/fixtures/beeper-message-like-me-v1/accounts.ndjson +2 -0
  234. package/src/fixtures/beeper-message-like-me-v1/conversations.ndjson +1 -0
  235. package/src/fixtures/beeper-message-like-me-v1/manifest.json +1 -0
  236. package/src/fixtures/beeper-message-like-me-v1/messages.ndjson +2 -0
  237. package/src/fixtures/beeper-message-like-me-v1/participants.ndjson +3 -0
  238. package/src/fixtures/beeper-message-like-me-v1/reactions.ndjson +1 -0
  239. package/src/fixtures/beeper-message-like-me-v1/tombstones.ndjson +1 -0
  240. package/src/ghostget.ts +3672 -0
  241. package/src/gmail-capture.ts +1244 -0
  242. package/src/har-internal.ts +1242 -0
  243. package/src/har.ts +743 -0
  244. package/src/index.ts +218 -0
  245. package/src/invocation-read-platform.ts +55 -0
  246. package/src/invocation-read-program.ts +93 -0
  247. package/src/invocation-read-runtime.ts +11 -0
  248. package/src/linked-device-lifecycle-admission.ts +450 -0
  249. package/src/linked-device-lifecycle-journal.ts +1560 -0
  250. package/src/linked-device-lifecycle-runtime.ts +1602 -0
  251. package/src/local-cli-admission.ts +135 -0
  252. package/src/local-cli-contracts.ts +271 -0
  253. package/src/local-cli-execution.ts +98 -0
  254. package/src/local-cli-surface-contract.ts +1510 -0
  255. package/src/local-cli-tool-identity.ts +336 -0
  256. package/src/media/archive.ts +2325 -0
  257. package/src/media/args.ts +303 -0
  258. package/src/media/cli.ts +287 -0
  259. package/src/media/doctor.ts +475 -0
  260. package/src/media/ffmpeg.ts +1330 -0
  261. package/src/media/http-capture.ts +711 -0
  262. package/src/media/http-probe.ts +593 -0
  263. package/src/media/http.ts +387 -0
  264. package/src/media/index.ts +10 -0
  265. package/src/media/local-transcription.ts +613 -0
  266. package/src/media/lock.ts +321 -0
  267. package/src/media/manifest.ts +1712 -0
  268. package/src/media/metadata.ts +906 -0
  269. package/src/media/process.ts +723 -0
  270. package/src/media/revision.ts +166 -0
  271. package/src/media/runtime-closure.ts +1063 -0
  272. package/src/media/source-router.ts +187 -0
  273. package/src/media/transcriber-config.ts +1122 -0
  274. package/src/media/transcript-persistence-model.ts +15 -0
  275. package/src/media/transcript-persistence-platform.ts +89 -0
  276. package/src/media/transcript-persistence-program.ts +39 -0
  277. package/src/media/transcript-persistence-runtime.ts +24 -0
  278. package/src/media/transcript.ts +616 -0
  279. package/src/media/utf8-order.ts +30 -0
  280. package/src/media/whisper-cpp.ts +464 -0
  281. package/src/media/whisper-language.ts +58 -0
  282. package/src/media/yt-dlp.ts +439 -0
  283. package/src/message-like-me-agentic-messaging.ts +882 -0
  284. package/src/messaging-action-store.ts +793 -0
  285. package/src/messaging-runtime.ts +2136 -0
  286. package/src/messaging-store.ts +539 -0
  287. package/src/messaging-types.ts +1663 -0
  288. package/src/messaging.ts +334 -0
  289. package/src/model.ts +2671 -0
  290. package/src/oauth-google.ts +888 -0
  291. package/src/omni-client-types.ts +306 -0
  292. package/src/omni-client.ts +2225 -0
  293. package/src/omni-limits.ts +34 -0
  294. package/src/omni-model.ts +1614 -0
  295. package/src/omni-request.ts +379 -0
  296. package/src/omni-runtime.ts +1632 -0
  297. package/src/operation-deadline.ts +189 -0
  298. package/src/path-helper.ts +1656 -0
  299. package/src/pinned-https.ts +194 -0
  300. package/src/plan-assets.ts +832 -0
  301. package/src/platform-catalog.ts +1832 -0
  302. package/src/plugins/beeper-linked-device/plugin.ts +310 -0
  303. package/src/plugins/bluesky-web/plugin.ts +178 -0
  304. package/src/plugins/clasificados-web/plugin.ts +66 -0
  305. package/src/plugins/github-web/plugin.ts +80 -0
  306. package/src/plugins/gmail-official/plugin.ts +110 -0
  307. package/src/plugins/hacker-news-web/plugin.ts +52 -0
  308. package/src/plugins/imessage-direct/plugin.ts +195 -0
  309. package/src/plugins/imessage-direct/vendor/0001-fix-keep-AppleScript-send-payloads-out-of-child-argv.patch +1430 -0
  310. package/src/plugins/imessage-direct/vendor/0002-feat-rpc-add-exact-chat-lookup.patch +225 -0
  311. package/src/plugins/imessage-direct/vendor/provenance.json +80 -0
  312. package/src/plugins/linkedin-official/plugin.ts +84 -0
  313. package/src/plugins/linkedin-web/plugin.ts +412 -0
  314. package/src/plugins/meta-web/plugin.ts +317 -0
  315. package/src/plugins/reddit-web/flair.ts +492 -0
  316. package/src/plugins/reddit-web/plugin.ts +477 -0
  317. package/src/plugins/substack-web/plugin.ts +124 -0
  318. package/src/plugins/tiktok-web/plugin.ts +67 -0
  319. package/src/plugins/twitch-web/plugin.ts +54 -0
  320. package/src/plugins/whatsapp-linked-device/plugin.ts +105 -0
  321. package/src/plugins/x-official/plugin.ts +64 -0
  322. package/src/plugins/x-web/plugin.ts +488 -0
  323. package/src/plugins/youtube-web/plugin.ts +101 -0
  324. package/src/portable-run-recovery.ts +466 -0
  325. package/src/process-identity.ts +501 -0
  326. package/src/profile-clone-helper.ts +104 -0
  327. package/src/provider-catalog-views.ts +51 -0
  328. package/src/provider-context.ts +69 -0
  329. package/src/provider-contract-definitions-x.ts +45 -0
  330. package/src/provider-contract-definitions.ts +948 -0
  331. package/src/provider-contract-input-gmail.ts +176 -0
  332. package/src/provider-contract-input-linkedin.ts +77 -0
  333. package/src/provider-contract-input-x.ts +304 -0
  334. package/src/provider-contract-planning.ts +23 -0
  335. package/src/provider-contract-semantic-identity.ts +54 -0
  336. package/src/provider-contracts.ts +251 -0
  337. package/src/provider-http.ts +723 -0
  338. package/src/provider-plugin-auth.ts +36 -0
  339. package/src/provider-plugin-builtins.ts +229 -0
  340. package/src/provider-plugin-cleanup-barrier.ts +229 -0
  341. package/src/provider-plugin-cleanup-execution.ts +79 -0
  342. package/src/provider-plugin-cleanup-resource.ts +470 -0
  343. package/src/provider-plugin-contract-identity.ts +707 -0
  344. package/src/provider-plugin-host.ts +1972 -0
  345. package/src/provider-plugin-identifiers.ts +60 -0
  346. package/src/provider-plugin-invocation-lease.ts +889 -0
  347. package/src/provider-plugin-lifecycle-kernel.ts +566 -0
  348. package/src/provider-plugin-lifecycle.ts +1455 -0
  349. package/src/provider-plugin-module-analysis.ts +336 -0
  350. package/src/provider-plugin-package.ts +2530 -0
  351. package/src/provider-plugin-portable-authority.ts +106 -0
  352. package/src/provider-plugin-portable-catalog.ts +94 -0
  353. package/src/provider-plugin-portable-identity.ts +315 -0
  354. package/src/provider-plugin-portable-runtime.ts +2057 -0
  355. package/src/provider-plugin-protocol.ts +1625 -0
  356. package/src/provider-plugin-registry.ts +3342 -0
  357. package/src/provider-plugin-store.ts +2155 -0
  358. package/src/provider-plugin-test-fixture.ts +5 -0
  359. package/src/provider-plugin.ts +4907 -0
  360. package/src/provider-plugins.generated.ts +43 -0
  361. package/src/provider-plugins.ts +8 -0
  362. package/src/provider-subject.ts +41 -0
  363. package/src/provider.ts +315 -0
  364. package/src/providers/beeper-local-runtime.ts +5426 -0
  365. package/src/providers/beeper-local.ts +2739 -0
  366. package/src/providers/beeper-messaging.ts +462 -0
  367. package/src/providers/beeper-omni.ts +1159 -0
  368. package/src/providers/bluesky-web-runtime.ts +2895 -0
  369. package/src/providers/bluesky-web.ts +1614 -0
  370. package/src/providers/clasificados-web-runtime.ts +255 -0
  371. package/src/providers/clasificados-web.ts +503 -0
  372. package/src/providers/contact-projection.ts +301 -0
  373. package/src/providers/github-read-model.ts +94 -0
  374. package/src/providers/github-read-platform.ts +165 -0
  375. package/src/providers/github-read-program.ts +97 -0
  376. package/src/providers/github-web-runtime.ts +320 -0
  377. package/src/providers/github-web.ts +403 -0
  378. package/src/providers/gmail-api.ts +2834 -0
  379. package/src/providers/gmail-omni.ts +830 -0
  380. package/src/providers/gmail.ts +1130 -0
  381. package/src/providers/hacker-news-web-runtime.ts +220 -0
  382. package/src/providers/hacker-news-web.ts +809 -0
  383. package/src/providers/imessage-direct-install.ts +354 -0
  384. package/src/providers/imessage-direct-messaging.ts +532 -0
  385. package/src/providers/imessage-direct-omni.ts +400 -0
  386. package/src/providers/imessage-direct-runtime.ts +1498 -0
  387. package/src/providers/imessage-direct.ts +351 -0
  388. package/src/providers/instagram-video-foundations.ts +913 -0
  389. package/src/providers/instagram-web-profile-browser.ts +600 -0
  390. package/src/providers/iso-bmff.ts +338 -0
  391. package/src/providers/linkedin-company-platform.ts +42 -0
  392. package/src/providers/linkedin-company-program.ts +88 -0
  393. package/src/providers/linkedin-contact-failure.ts +84 -0
  394. package/src/providers/linkedin-contact-platform.ts +45 -0
  395. package/src/providers/linkedin-contact-program.ts +91 -0
  396. package/src/providers/linkedin-profile-activity-failure.ts +76 -0
  397. package/src/providers/linkedin-profile-activity-platform.ts +41 -0
  398. package/src/providers/linkedin-profile-activity-program.ts +67 -0
  399. package/src/providers/linkedin-read-failure.ts +52 -0
  400. package/src/providers/linkedin-self-platform.ts +53 -0
  401. package/src/providers/linkedin-self-program.ts +122 -0
  402. package/src/providers/linkedin-web-article-browser.ts +794 -0
  403. package/src/providers/linkedin-web-bootstrap.ts +146 -0
  404. package/src/providers/linkedin-web-contact.ts +830 -0
  405. package/src/providers/linkedin-web-feed-browser.ts +832 -0
  406. package/src/providers/linkedin-web-feed.ts +864 -0
  407. package/src/providers/linkedin-web-post-browser.ts +653 -0
  408. package/src/providers/linkedin-web-profile-browser.ts +683 -0
  409. package/src/providers/linkedin-web-runtime.ts +2388 -0
  410. package/src/providers/linkedin-web.ts +3305 -0
  411. package/src/providers/linkedin.ts +1885 -0
  412. package/src/providers/meta-bootstrap.ts +1043 -0
  413. package/src/providers/meta-facebook-group.ts +690 -0
  414. package/src/providers/meta-marketplace-relay.ts +747 -0
  415. package/src/providers/meta-omni.ts +373 -0
  416. package/src/providers/meta-relay-bundle.ts +1049 -0
  417. package/src/providers/meta-web-descriptors.ts +1740 -0
  418. package/src/providers/meta-web-runtime.ts +3388 -0
  419. package/src/providers/meta-web.ts +2931 -0
  420. package/src/providers/read-failure.ts +139 -0
  421. package/src/providers/reddit-omni.ts +462 -0
  422. package/src/providers/reddit-web-runtime.ts +1929 -0
  423. package/src/providers/reddit-web.ts +1611 -0
  424. package/src/providers/rental-listings.ts +460 -0
  425. package/src/providers/substack-omni.ts +406 -0
  426. package/src/providers/substack-video-mp4.ts +33 -0
  427. package/src/providers/substack-web-runtime.ts +2812 -0
  428. package/src/providers/substack-web.ts +1502 -0
  429. package/src/providers/tiktok-video-mp4.ts +33 -0
  430. package/src/providers/tiktok-web-runtime.ts +770 -0
  431. package/src/providers/tiktok-web.ts +2786 -0
  432. package/src/providers/twitch-web-runtime.ts +226 -0
  433. package/src/providers/twitch-web.ts +274 -0
  434. package/src/providers/whatsapp-account-identity.ts +55 -0
  435. package/src/providers/whatsapp-contact-projection-helper.ts +874 -0
  436. package/src/providers/whatsapp-contact-projection-protocol.ts +403 -0
  437. package/src/providers/whatsapp-interaction-projection-helper.ts +1452 -0
  438. package/src/providers/whatsapp-interaction-projection-protocol.ts +462 -0
  439. package/src/providers/whatsapp-message-export-projection-protocol.ts +532 -0
  440. package/src/providers/whatsapp-messaging.ts +132 -0
  441. package/src/providers/whatsapp-omni.ts +586 -0
  442. package/src/providers/whatsapp-web-runtime.ts +3108 -0
  443. package/src/providers/whatsapp-web.ts +1037 -0
  444. package/src/providers/x-image-provenance.ts +523 -0
  445. package/src/providers/x-made-with-ai.ts +106 -0
  446. package/src/providers/x-transaction-id.ts +413 -0
  447. package/src/providers/x-web-runtime.ts +3485 -0
  448. package/src/providers/x-web.ts +2332 -0
  449. package/src/providers/x.ts +1889 -0
  450. package/src/providers/youtube-web-runtime.ts +1400 -0
  451. package/src/providers/youtube-web.ts +1221 -0
  452. package/src/read-admission-runtime.ts +91 -0
  453. package/src/read-client.ts +521 -0
  454. package/src/read-effect-platform.ts +7 -0
  455. package/src/read-effect-runtime.ts +14 -0
  456. package/src/read-effect.ts +52 -0
  457. package/src/read-projection-admission.ts +866 -0
  458. package/src/read-projections.ts +3151 -0
  459. package/src/recovery.ts +1492 -0
  460. package/src/reviewed-template.ts +146 -0
  461. package/src/run-journal.ts +1272 -0
  462. package/src/runtime.ts +5116 -0
  463. package/src/scripts/generate-provider-plugin-catalog.ts +121 -0
  464. package/src/scripts/install-whatsapp-protocol.sh +232 -0
  465. package/src/scripts/recover-install-stages.ts +1491 -0
  466. package/src/scripts/resolve-state-home.ts +30 -0
  467. package/src/scripts/scaffold-web-provider.ts +973 -0
  468. package/src/scripts/sync-bundled-adapters.ts +695 -0
  469. package/src/session-secrets.ts +1183 -0
  470. package/src/state-helper.bunfig.toml +2 -0
  471. package/src/state-helper.ts +2914 -0
  472. package/src/storage.ts +3842 -0
  473. package/src/transport-policy.ts +2 -0
  474. package/src/usage.ts +182 -0
  475. package/src/version.ts +2 -0
  476. package/src/web-session-authentication-policy.ts +136 -0
  477. package/src/web-session-cleanup-admission.ts +2344 -0
  478. package/src/web-session-cleanup-join.ts +99 -0
  479. package/src/web-session-client.ts +996 -0
  480. package/src/web-session-contract-definitions.ts +820 -0
  481. package/src/web-session-contract-planning.ts +42 -0
  482. package/src/web-session-contracts.ts +228 -0
  483. package/src/web-session-cookies.ts +500 -0
  484. package/src/web-session-execution.ts +385 -0
  485. package/src/web-session-http.ts +373 -0
  486. package/src/web-session-read-errors.ts +19 -0
  487. package/src/web-session-read-runtime.ts +124 -0
  488. package/src/web-session-recovery.ts +818 -0
  489. package/src/web-session-sites.ts +58 -0
  490. package/src/web-session-template.ts +994 -0
  491. package/src/web-session.ts +93 -0
  492. package/src/whatsapp-client-binding.ts +21 -0
  493. package/src/whatsapp-client-types.ts +75 -0
  494. package/src/whatsapp-client.ts +395 -0
  495. package/src/whatsapp-export-coordinate.ts +17 -0
  496. package/src/whatsapp-message-bundle-v2.ts +195 -0
  497. package/src/whatsapp-message-like-me-cli.ts +195 -0
  498. package/src/whatsapp-message-like-me-export.ts +1072 -0
  499. package/src/whatsapp-message-like-me-source.ts +1012 -0
  500. package/tsconfig.json +67 -0
@@ -0,0 +1,1406 @@
1
+ // @bun
2
+ import {
3
+ canonicalJson,
4
+ sha256
5
+ } from "./index-gwk7rbyj.js";
6
+
7
+ // src/omni-client.ts
8
+ import { spawn, spawnSync } from "child_process";
9
+ import { existsSync } from "fs";
10
+ import { fileURLToPath } from "url";
11
+ import { types as nodeTypes } from "util";
12
+
13
+ // src/omni-limits.ts
14
+ var OMNI_MAX_REQUEST_BYTES = 1024 * 1024;
15
+ var OMNI_MAX_RESPONSE_BYTES = 20 * 1024 * 1024;
16
+ var OMNI_MAX_REASON_BYTES = 8 * 1024;
17
+ var OMNI_MAX_SOURCES = 32;
18
+ var OMNI_MAX_VIEW_ENTITIES = 500;
19
+ var OMNI_MAX_CURSOR_CHARACTERS = 8192;
20
+ function boundedOmniText(value, maximumBytes, suffix = "\u2026") {
21
+ const bytes = Buffer.from(value, "utf8");
22
+ if (bytes.byteLength <= maximumBytes)
23
+ return value;
24
+ const suffixBytes = Buffer.byteLength(suffix, "utf8");
25
+ if (suffixBytes > maximumBytes) {
26
+ throw new Error("omni text suffix exceeds its byte bound");
27
+ }
28
+ const limit = maximumBytes - suffixBytes;
29
+ let used = 0;
30
+ let prefix = "";
31
+ for (const character of value) {
32
+ const size = Buffer.byteLength(character, "utf8");
33
+ if (used + size > limit)
34
+ break;
35
+ prefix += character;
36
+ used += size;
37
+ }
38
+ return `${prefix}${suffix}`;
39
+ }
40
+
41
+ // src/omni-client.ts
42
+ var MAX_JSON_DEPTH = 64;
43
+ var MAX_JSON_NODES = 1e5;
44
+ var MAX_PARTICIPANTS = 256;
45
+ var MAX_ATTACHMENTS = 64;
46
+ var SYNCHRONOUS_COMMAND_TIMEOUT_MS = 20000;
47
+ var ASYNC_CONTROL_COMMAND_TIMEOUT_MS = 20000;
48
+ var CHILD_GRACEFUL_TERMINATION_MS = 40000;
49
+ var CHILD_FORCEFUL_TERMINATION_MS = 5000;
50
+ var abortSignalAbortedGetter = (() => {
51
+ const descriptor = Object.getOwnPropertyDescriptor(AbortSignal.prototype, "aborted");
52
+ const getter = descriptor === undefined ? undefined : Reflect.get(descriptor, "get");
53
+ return typeof getter !== "function" ? undefined : (value) => Reflect.apply(getter, value, []);
54
+ })();
55
+ function fail(label, message) {
56
+ throw new Error(`${label} ${message}`);
57
+ }
58
+ function record(value, label) {
59
+ if (nodeTypes.isProxy(value) || typeof value !== "object" || value === null || Array.isArray(value) || Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null)
60
+ return fail(label, "must be a plain non-proxy object");
61
+ const descriptors = Object.getOwnPropertyDescriptors(value);
62
+ const keys = Reflect.ownKeys(descriptors);
63
+ if (keys.some((key) => typeof key !== "string")) {
64
+ return fail(label, "must not contain symbol properties");
65
+ }
66
+ for (const key of keys) {
67
+ const descriptor = descriptors[key];
68
+ if (descriptor === undefined || !descriptor.enumerable || !("value" in descriptor))
69
+ return fail(`${label}.${key}`, "must be an enumerable data property");
70
+ }
71
+ return value;
72
+ }
73
+ function exactKeys(value, required, optional, label) {
74
+ const actual = Object.keys(value);
75
+ const allowed = new Set([...required, ...optional]);
76
+ const unknown = actual.find((key) => !allowed.has(key));
77
+ if (unknown !== undefined)
78
+ return fail(`${label}.${unknown}`, "is not reviewed");
79
+ const missing = required.find((key) => !Object.hasOwn(value, key));
80
+ if (missing !== undefined)
81
+ return fail(`${label}.${missing}`, "is required");
82
+ }
83
+ function denseArray(value, label, maximum) {
84
+ if (nodeTypes.isProxy(value) || !Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype || value.length > maximum)
85
+ return fail(label, `must be a plain array of at most ${maximum} items`);
86
+ const descriptors = Object.getOwnPropertyDescriptors(value);
87
+ const keys = Reflect.ownKeys(descriptors);
88
+ const expected = new Set([
89
+ "length",
90
+ ...Array.from({ length: value.length }, (_unused, index) => String(index))
91
+ ]);
92
+ if (keys.length !== expected.size || keys.some((key) => !expected.has(key))) {
93
+ return fail(label, "must be a dense array without named properties");
94
+ }
95
+ for (let index = 0;index < value.length; index += 1) {
96
+ const descriptor = descriptors[String(index)];
97
+ if (descriptor === undefined || !descriptor.enumerable || !("value" in descriptor))
98
+ return fail(`${label}[${index}]`, "must be an enumerable data property");
99
+ }
100
+ return value;
101
+ }
102
+ function text(value, label, maximumBytes, allowEmpty = false) {
103
+ if (typeof value !== "string" || Buffer.byteLength(value, "utf8") > maximumBytes || !allowEmpty && value.length === 0 || [...value].some((character) => {
104
+ const code = character.codePointAt(0) ?? -1;
105
+ return code === 0 || code === 8 || code === 11 || code === 12 || code >= 14 && code <= 31 || code === 127;
106
+ }))
107
+ return fail(label, "must be bounded text");
108
+ return value;
109
+ }
110
+ function nullableText(value, label, maximumBytes, allowEmpty = false) {
111
+ return value === null ? null : text(value, label, maximumBytes, allowEmpty);
112
+ }
113
+ function identifier(value, label) {
114
+ const result = text(value, label, 64);
115
+ if (!/^[a-z][a-z0-9-]{0,63}$/u.test(result)) {
116
+ return fail(label, "must be lowercase kebab-case");
117
+ }
118
+ return result;
119
+ }
120
+ function digest(value, label) {
121
+ const result = text(value, label, 64);
122
+ if (!/^[a-f0-9]{64}$/u.test(result))
123
+ return fail(label, "must be a SHA-256 digest");
124
+ return result;
125
+ }
126
+ function localCursor(value, label) {
127
+ const result = text(value, label, OMNI_MAX_CURSOR_CHARACTERS);
128
+ if (!result.startsWith("smn1.")) {
129
+ return fail(label, "must be an authenticated local omni cursor");
130
+ }
131
+ const encoded = result.slice("smn1.".length);
132
+ if (encoded === "" || /[^A-Za-z0-9_-]/u.test(encoded)) {
133
+ return fail(label, "must be an authenticated local omni cursor");
134
+ }
135
+ const envelope = Buffer.from(encoded, "base64url");
136
+ if (envelope.toString("base64url") !== encoded || envelope.byteLength <= 12 + 16) {
137
+ return fail(label, "must be an authenticated local omni cursor");
138
+ }
139
+ return result;
140
+ }
141
+ function timestamp(value, label) {
142
+ const result = text(value, label, 64);
143
+ const parsed = new Date(result);
144
+ if (!Number.isFinite(parsed.getTime()) || parsed.toISOString() !== result) {
145
+ return fail(label, "must be a canonical ISO timestamp");
146
+ }
147
+ return result;
148
+ }
149
+ function nullableTimestamp(value, label) {
150
+ return value === null ? null : timestamp(value, label);
151
+ }
152
+ function integer(value, label, minimum, maximum) {
153
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < minimum || value > maximum)
154
+ return fail(label, `must be an integer from ${minimum} through ${maximum}`);
155
+ return value;
156
+ }
157
+ function nullableInteger(value, label, minimum, maximum) {
158
+ return value === null ? null : integer(value, label, minimum, maximum);
159
+ }
160
+ function nullableBoolean(value, label) {
161
+ if (value === null || typeof value === "boolean")
162
+ return value;
163
+ return fail(label, "must be boolean or null");
164
+ }
165
+ function boolean(value, label) {
166
+ if (typeof value === "boolean")
167
+ return value;
168
+ return fail(label, "must be boolean");
169
+ }
170
+ function oneOf(value, values, label) {
171
+ if (typeof value !== "string" || !values.includes(value)) {
172
+ return fail(label, `must be one of ${values.join(", ")}`);
173
+ }
174
+ return value;
175
+ }
176
+ function snapshotJson(value, label) {
177
+ let nodes = 0;
178
+ const ancestors = new WeakSet;
179
+ const completed = new WeakMap;
180
+ const visit = (candidate, depth) => {
181
+ nodes += 1;
182
+ if (nodes > MAX_JSON_NODES || depth > MAX_JSON_DEPTH) {
183
+ return fail(label, "exceeds its structural bound");
184
+ }
185
+ if (candidate === null || typeof candidate === "boolean" || typeof candidate === "string")
186
+ return candidate;
187
+ if (typeof candidate === "number") {
188
+ if (!Number.isFinite(candidate))
189
+ return fail(label, "must contain only JSON data");
190
+ return candidate;
191
+ }
192
+ if (typeof candidate !== "object")
193
+ return fail(label, "must contain only JSON data");
194
+ if (nodeTypes.isProxy(candidate))
195
+ return fail(label, "must not contain proxies");
196
+ const prior = completed.get(candidate);
197
+ if (prior !== undefined)
198
+ return prior;
199
+ if (ancestors.has(candidate))
200
+ return fail(label, "must not be circular");
201
+ ancestors.add(candidate);
202
+ try {
203
+ if (Array.isArray(candidate)) {
204
+ const values = denseArray(candidate, label, MAX_JSON_NODES);
205
+ const cloned2 = Object.freeze(values.map((entry) => visit(entry, depth + 1)));
206
+ completed.set(candidate, cloned2);
207
+ return cloned2;
208
+ }
209
+ const source = record(candidate, label);
210
+ const cloned = {};
211
+ for (const key of Object.keys(source).sort((left, right) => left.localeCompare(right))) {
212
+ Object.defineProperty(cloned, key, {
213
+ value: visit(source[key], depth + 1),
214
+ enumerable: true,
215
+ configurable: false,
216
+ writable: false
217
+ });
218
+ }
219
+ const result = Object.freeze(cloned);
220
+ completed.set(candidate, result);
221
+ return result;
222
+ } finally {
223
+ ancestors.delete(candidate);
224
+ }
225
+ };
226
+ return visit(value, 0);
227
+ }
228
+ function parseRequestSource(value, index) {
229
+ const label = `Ghostget omni request.sources[${index}]`;
230
+ const source = record(value, label);
231
+ exactKeys(source, ["adapterId", "operationId", "authId"], ["input"], label);
232
+ const operationId = oneOf(source.operationId, ["messaging.list", "messaging.read"], `${label}.operationId`);
233
+ const inputValue = Object.hasOwn(source, "input") ? source.input : {};
234
+ const input = record(inputValue, `${label}.input`);
235
+ return Object.freeze({
236
+ adapterId: identifier(source.adapterId, `${label}.adapterId`),
237
+ operationId,
238
+ authId: identifier(source.authId, `${label}.authId`),
239
+ input
240
+ });
241
+ }
242
+ function prepareRequest(requestValue) {
243
+ const snapshot = snapshotJson(requestValue, "Ghostget omni request");
244
+ const request = record(snapshot, "Ghostget omni request");
245
+ exactKeys(request, ["schemaVersion", "sources"], ["filter", "page"], "Ghostget omni request");
246
+ if (request.schemaVersion !== 1)
247
+ return fail("Ghostget omni request.schemaVersion", "must be 1");
248
+ const sources = denseArray(request.sources, "Ghostget omni request.sources", OMNI_MAX_SOURCES).map(parseRequestSource).sort((left, right) => canonicalJson(left).localeCompare(canonicalJson(right)));
249
+ if (sources.length === 0)
250
+ return fail("Ghostget omni request.sources", "must not be empty");
251
+ const sourceKeys = sources.map((source) => canonicalJson(source));
252
+ if (new Set(sourceKeys).size !== sourceKeys.length) {
253
+ return fail("Ghostget omni request.sources", "must not repeat an exact source request");
254
+ }
255
+ let filter;
256
+ if (request.filter !== undefined) {
257
+ const value = record(request.filter, "Ghostget omni request.filter");
258
+ exactKeys(value, [], ["kinds", "conversationId", "unread"], "Ghostget omni request.filter");
259
+ let kinds;
260
+ if (value.kinds !== undefined) {
261
+ const parsed = denseArray(value.kinds, "Ghostget omni request.filter.kinds", 3).map((kind, index) => oneOf(kind, ["conversation", "message", "notification"], `Ghostget omni request.filter.kinds[${index}]`));
262
+ if (parsed.length === 0 || new Set(parsed).size !== parsed.length) {
263
+ return fail("Ghostget omni request.filter.kinds", "must be non-empty and unique");
264
+ }
265
+ kinds = Object.freeze([...parsed].sort());
266
+ }
267
+ const conversationId = value.conversationId === undefined ? undefined : digest(value.conversationId, "Ghostget omni request.filter.conversationId");
268
+ if (value.unread !== undefined && typeof value.unread !== "boolean") {
269
+ return fail("Ghostget omni request.filter.unread", "must be boolean");
270
+ }
271
+ filter = Object.freeze({
272
+ ...kinds === undefined ? {} : { kinds },
273
+ ...conversationId === undefined ? {} : { conversationId },
274
+ ...value.unread === undefined ? {} : { unread: value.unread }
275
+ });
276
+ }
277
+ let page;
278
+ if (request.page !== undefined) {
279
+ const value = record(request.page, "Ghostget omni request.page");
280
+ exactKeys(value, [], ["limit", "cursor"], "Ghostget omni request.page");
281
+ const limit = value.limit === undefined ? 100 : integer(value.limit, "Ghostget omni request.page.limit", 1, 500);
282
+ const cursor = value.cursor === undefined ? undefined : localCursor(value.cursor, "Ghostget omni request.page.cursor");
283
+ page = Object.freeze({
284
+ limit,
285
+ ...cursor === undefined ? {} : { cursor }
286
+ });
287
+ }
288
+ const normalized = Object.freeze({
289
+ schemaVersion: 1,
290
+ sources: Object.freeze(sources),
291
+ ...filter === undefined ? {} : { filter },
292
+ ...page === undefined ? {} : { page }
293
+ });
294
+ const json = canonicalJson(normalized);
295
+ if (Buffer.byteLength(json, "utf8") > OMNI_MAX_REQUEST_BYTES) {
296
+ return fail("Ghostget omni request", "exceeds its byte bound");
297
+ }
298
+ const cursorIndependentRequest = Object.freeze({
299
+ schemaVersion: 1,
300
+ sources: normalized.sources,
301
+ ...filter === undefined ? {} : { filter },
302
+ ...page === undefined ? {} : { page: Object.freeze({ limit: page.limit ?? 100 }) }
303
+ });
304
+ return Object.freeze({
305
+ json,
306
+ invocationDigest: sha256(json),
307
+ requestDigest: sha256(canonicalJson(cursorIndependentRequest)),
308
+ sources: Object.freeze(sources.map((source) => Object.freeze({
309
+ adapterId: source.adapterId,
310
+ operationId: source.operationId,
311
+ authId: source.authId,
312
+ requestInputHash: sha256(canonicalJson(source.input ?? {}))
313
+ }))),
314
+ filter: Object.freeze({
315
+ kinds: filter?.kinds ?? null,
316
+ conversationId: filter?.conversationId ?? null,
317
+ unread: filter?.unread ?? null
318
+ }),
319
+ pageLimit: page?.limit ?? 100
320
+ });
321
+ }
322
+ function environmentName(value) {
323
+ if (value.length === 0 || value.includes("=") || value.includes("\x00")) {
324
+ return fail("Ghostget omni client environment name", "is malformed");
325
+ }
326
+ return value;
327
+ }
328
+ function defineEnvironmentValue(environment, key, value) {
329
+ Object.defineProperty(environment, key, {
330
+ value,
331
+ enumerable: true,
332
+ configurable: true,
333
+ writable: true
334
+ });
335
+ }
336
+ function snapshotEnvironment(value) {
337
+ const result = Object.create(null);
338
+ for (const [key, environmentValue] of Object.entries(process.env)) {
339
+ if (typeof environmentValue === "string") {
340
+ defineEnvironmentValue(result, key, environmentValue);
341
+ }
342
+ }
343
+ if (value === undefined)
344
+ return Object.freeze(result);
345
+ const overrides = record(value, "Ghostget omni client environment");
346
+ for (const [key, environmentValue] of Object.entries(overrides)) {
347
+ const name = environmentName(key);
348
+ if (environmentValue === undefined) {
349
+ delete result[name];
350
+ } else if (typeof environmentValue !== "string" || environmentValue.includes("\x00")) {
351
+ return fail("Ghostget omni client environment value", "is malformed");
352
+ } else {
353
+ defineEnvironmentValue(result, name, environmentValue);
354
+ }
355
+ }
356
+ return Object.freeze(result);
357
+ }
358
+ function isBrandedAbortSignal(value) {
359
+ if (nodeTypes.isProxy(value) || typeof value !== "object" || value === null || abortSignalAbortedGetter === undefined || Object.getPrototypeOf(value) !== AbortSignal.prototype)
360
+ return false;
361
+ try {
362
+ return typeof abortSignalAbortedGetter(value) === "boolean";
363
+ } catch {
364
+ return false;
365
+ }
366
+ }
367
+ function snapshotOptions(optionsValue, revalidation) {
368
+ const options = record(optionsValue, "Ghostget omni client options");
369
+ exactKeys(options, [], revalidation ? ["environment", "headed", "signal"] : ["environment"], "Ghostget omni client options");
370
+ const headedValue = options.headed;
371
+ const signalValue = options.signal;
372
+ if (headedValue !== undefined && typeof headedValue !== "boolean") {
373
+ return fail("Ghostget omni client headed option", "is malformed");
374
+ }
375
+ if (signalValue !== undefined && !isBrandedAbortSignal(signalValue)) {
376
+ return fail("Ghostget omni client abort signal", "is malformed");
377
+ }
378
+ return Object.freeze({
379
+ cwd: process.cwd(),
380
+ environment: snapshotEnvironment(options.environment),
381
+ headed: headedValue === true,
382
+ ...signalValue === undefined ? {} : { signal: signalValue }
383
+ });
384
+ }
385
+ function signalAborted(signal) {
386
+ if (abortSignalAbortedGetter === undefined)
387
+ return true;
388
+ return abortSignalAbortedGetter(signal) === true;
389
+ }
390
+ function throwIfAborted(signal) {
391
+ if (signal === undefined || !signalAborted(signal))
392
+ return;
393
+ throw abortError(signal);
394
+ }
395
+ function abortError(signal) {
396
+ const reason = signal.reason;
397
+ if (reason instanceof Error)
398
+ return reason;
399
+ const error = new Error("Ghostget omni revalidation was aborted");
400
+ error.name = "AbortError";
401
+ return error;
402
+ }
403
+ function cliSourcePath() {
404
+ const besideSource = fileURLToPath(new URL("./cli.ts", import.meta.url));
405
+ if (existsSync(besideSource))
406
+ return besideSource;
407
+ const packagedSource = fileURLToPath(new URL("../src/cli.ts", import.meta.url));
408
+ if (existsSync(packagedSource))
409
+ return packagedSource;
410
+ throw new Error("the installed Ghostget CLI source is unavailable");
411
+ }
412
+ function requireBunRuntime() {
413
+ if (typeof process.versions.bun !== "string") {
414
+ throw new Error("@hraness/ghostget/omni requires Bun to run the installed Ghostget CLI");
415
+ }
416
+ }
417
+ function preparedCommand(request, mode, options) {
418
+ return Object.freeze({
419
+ arguments: Object.freeze([
420
+ cliSourcePath(),
421
+ "omni",
422
+ "read",
423
+ "--input",
424
+ "-",
425
+ ...mode === "cache" ? ["--cache-only"] : [],
426
+ ...mode === "identity" ? ["--identity-only"] : [],
427
+ ...mode === "live" && options.headed ? ["--headed"] : [],
428
+ "--json"
429
+ ]),
430
+ input: request.json,
431
+ request
432
+ });
433
+ }
434
+ function boundedMessage(value) {
435
+ return boundedOmniText(value, OMNI_MAX_REASON_BYTES).trim();
436
+ }
437
+ function childText(value, label) {
438
+ if (nodeTypes.isProxy(value))
439
+ return fail(label, "must not be a proxy");
440
+ if (typeof value === "string")
441
+ return value;
442
+ if (Buffer.isBuffer(value))
443
+ return Buffer.from(value).toString("utf8");
444
+ return fail(label, "must be UTF-8 text");
445
+ }
446
+ function parseJsonOutput(textValue, label) {
447
+ if (Buffer.byteLength(textValue, "utf8") > OMNI_MAX_RESPONSE_BYTES) {
448
+ return fail(label, "exceeds its byte bound");
449
+ }
450
+ try {
451
+ return JSON.parse(textValue);
452
+ } catch {
453
+ return fail(label, "is malformed JSON");
454
+ }
455
+ }
456
+ function participant(value, label) {
457
+ const source = record(value, label);
458
+ exactKeys(source, ["providerId", "displayName", "handle"], [], label);
459
+ return Object.freeze({
460
+ providerId: nullableText(source.providerId, `${label}.providerId`, 1024),
461
+ displayName: nullableText(source.displayName, `${label}.displayName`, 2048, true),
462
+ handle: nullableText(source.handle, `${label}.handle`, 512)
463
+ });
464
+ }
465
+ function attachment(value, label) {
466
+ const source = record(value, label);
467
+ exactKeys(source, ["kind", "mimeType", "name", "sizeBytes"], [], label);
468
+ return Object.freeze({
469
+ kind: oneOf(source.kind, ["audio", "document", "image", "link", "sticker", "video", "unknown"], `${label}.kind`),
470
+ mimeType: nullableText(source.mimeType, `${label}.mimeType`, 256),
471
+ name: nullableText(source.name, `${label}.name`, 2048, true),
472
+ sizeBytes: nullableInteger(source.sizeBytes, `${label}.sizeBytes`, 0, Number.MAX_SAFE_INTEGER)
473
+ });
474
+ }
475
+ function entityCommon(source, label) {
476
+ const entitySource = record(source.source, `${label}.source`);
477
+ exactKeys(entitySource, ["surfaceId", "authId", "providerId"], [], `${label}.source`);
478
+ const providerId = text(source.providerId, `${label}.providerId`, 1024);
479
+ const providerSourceId = text(entitySource.providerId, `${label}.source.providerId`, 1024);
480
+ if (providerSourceId !== providerId) {
481
+ return fail(`${label}.source.providerId`, "does not match providerId");
482
+ }
483
+ return Object.freeze({
484
+ id: digest(source.id, `${label}.id`),
485
+ providerId,
486
+ providerRevision: nullableText(source.providerRevision, `${label}.providerRevision`, 1024),
487
+ orderedAt: nullableTimestamp(source.orderedAt, `${label}.orderedAt`),
488
+ source: Object.freeze({
489
+ surfaceId: text(entitySource.surfaceId, `${label}.source.surfaceId`, 64),
490
+ authId: text(entitySource.authId, `${label}.source.authId`, 64),
491
+ providerId: providerSourceId
492
+ }),
493
+ conversationId: source.conversationId === null ? null : digest(source.conversationId, `${label}.conversationId`)
494
+ });
495
+ }
496
+ function verifyEntityRevision(semantic, revisionValue, label) {
497
+ const revision = digest(revisionValue, `${label}.revision`);
498
+ if (sha256(canonicalJson(semantic)) !== revision) {
499
+ return fail(`${label}.revision`, "does not authenticate its semantic fields");
500
+ }
501
+ return Object.freeze({ ...semantic, revision });
502
+ }
503
+ function conversationEntity(source, label) {
504
+ const hasConversationKind = Object.hasOwn(source, "conversationKind");
505
+ exactKeys(source, [
506
+ "kind",
507
+ "providerId",
508
+ "providerRevision",
509
+ "orderedAt",
510
+ ...hasConversationKind ? ["conversationKind"] : [],
511
+ "detail",
512
+ "title",
513
+ "summary",
514
+ "participants",
515
+ "unread",
516
+ "unreadCount",
517
+ "archived",
518
+ "pending",
519
+ "id",
520
+ "revision",
521
+ "source",
522
+ "conversationId"
523
+ ], [], label);
524
+ const common = entityCommon(source, label);
525
+ if (common.conversationId !== null) {
526
+ return fail(`${label}.conversationId`, "must be null for conversations");
527
+ }
528
+ const semantic = Object.freeze({
529
+ kind: "conversation",
530
+ ...hasConversationKind ? {
531
+ conversationKind: oneOf(source.conversationKind, ["single", "group", "unknown"], `${label}.conversationKind`)
532
+ } : {},
533
+ providerId: common.providerId,
534
+ providerRevision: common.providerRevision,
535
+ orderedAt: common.orderedAt,
536
+ detail: oneOf(source.detail, ["summary", "full"], `${label}.detail`),
537
+ title: nullableText(source.title, `${label}.title`, 4096, true),
538
+ summary: nullableText(source.summary, `${label}.summary`, 64 * 1024, true),
539
+ participants: Object.freeze(denseArray(source.participants, `${label}.participants`, MAX_PARTICIPANTS).map((entry, index) => participant(entry, `${label}.participants[${index}]`))),
540
+ unread: nullableBoolean(source.unread, `${label}.unread`),
541
+ unreadCount: nullableInteger(source.unreadCount, `${label}.unreadCount`, 0, Number.MAX_SAFE_INTEGER),
542
+ archived: nullableBoolean(source.archived, `${label}.archived`),
543
+ pending: nullableBoolean(source.pending, `${label}.pending`),
544
+ id: common.id,
545
+ source: common.source,
546
+ conversationId: common.conversationId
547
+ });
548
+ return verifyEntityRevision(semantic, source.revision, label);
549
+ }
550
+ function messageEntity(source, label) {
551
+ const hasBodyTruncated = Object.hasOwn(source, "bodyTruncated");
552
+ exactKeys(source, [
553
+ "kind",
554
+ "providerId",
555
+ "providerRevision",
556
+ "orderedAt",
557
+ "conversationProviderId",
558
+ "sender",
559
+ "recipients",
560
+ "direction",
561
+ "subject",
562
+ "body",
563
+ "unread",
564
+ "replyToProviderId",
565
+ "state",
566
+ "attachments",
567
+ "id",
568
+ "revision",
569
+ "source",
570
+ "conversationId"
571
+ ], hasBodyTruncated ? ["bodyTruncated"] : [], label);
572
+ const common = entityCommon(source, label);
573
+ const conversationProviderId = nullableText(source.conversationProviderId, `${label}.conversationProviderId`, 1024);
574
+ if (conversationProviderId === null !== (common.conversationId === null)) {
575
+ return fail(`${label}.conversationId`, "is inconsistent with conversationProviderId");
576
+ }
577
+ const body = nullableText(source.body, `${label}.body`, 256 * 1024, true);
578
+ const bodyTruncated = hasBodyTruncated ? boolean(source.bodyTruncated, `${label}.bodyTruncated`) : undefined;
579
+ if (body === null && bodyTruncated === true) {
580
+ return fail(`${label}.bodyTruncated`, "cannot be true when body is null");
581
+ }
582
+ const semantic = Object.freeze({
583
+ kind: "message",
584
+ providerId: common.providerId,
585
+ providerRevision: common.providerRevision,
586
+ orderedAt: common.orderedAt,
587
+ conversationProviderId,
588
+ sender: source.sender === null ? null : participant(source.sender, `${label}.sender`),
589
+ recipients: Object.freeze(denseArray(source.recipients, `${label}.recipients`, MAX_PARTICIPANTS).map((entry, index) => participant(entry, `${label}.recipients[${index}]`))),
590
+ direction: oneOf(source.direction, ["incoming", "outgoing", "unknown"], `${label}.direction`),
591
+ subject: nullableText(source.subject, `${label}.subject`, 8192, true),
592
+ body,
593
+ ...bodyTruncated === undefined ? {} : { bodyTruncated },
594
+ unread: nullableBoolean(source.unread, `${label}.unread`),
595
+ replyToProviderId: nullableText(source.replyToProviderId, `${label}.replyToProviderId`, 1024),
596
+ state: oneOf(source.state, ["active", "revoked", "deleted-for-me", "revoked-and-deleted-for-me"], `${label}.state`),
597
+ attachments: Object.freeze(denseArray(source.attachments, `${label}.attachments`, MAX_ATTACHMENTS).map((entry, index) => attachment(entry, `${label}.attachments[${index}]`))),
598
+ id: common.id,
599
+ source: common.source,
600
+ conversationId: common.conversationId
601
+ });
602
+ return verifyEntityRevision(semantic, source.revision, label);
603
+ }
604
+ function notificationEntity(source, label) {
605
+ exactKeys(source, [
606
+ "kind",
607
+ "providerId",
608
+ "providerRevision",
609
+ "orderedAt",
610
+ "actor",
611
+ "subject",
612
+ "body",
613
+ "unread",
614
+ "context",
615
+ "id",
616
+ "revision",
617
+ "source",
618
+ "conversationId"
619
+ ], [], label);
620
+ const common = entityCommon(source, label);
621
+ if (common.conversationId !== null) {
622
+ return fail(`${label}.conversationId`, "must be null for notifications");
623
+ }
624
+ const semantic = Object.freeze({
625
+ kind: "notification",
626
+ providerId: common.providerId,
627
+ providerRevision: common.providerRevision,
628
+ orderedAt: common.orderedAt,
629
+ actor: source.actor === null ? null : participant(source.actor, `${label}.actor`),
630
+ subject: nullableText(source.subject, `${label}.subject`, 8192, true),
631
+ body: nullableText(source.body, `${label}.body`, 256 * 1024, true),
632
+ unread: nullableBoolean(source.unread, `${label}.unread`),
633
+ context: nullableText(source.context, `${label}.context`, 8192),
634
+ id: common.id,
635
+ source: common.source,
636
+ conversationId: common.conversationId
637
+ });
638
+ return verifyEntityRevision(semantic, source.revision, label);
639
+ }
640
+ function entity(value, index) {
641
+ const label = `Ghostget omni view.entities[${index}]`;
642
+ const source = record(value, label);
643
+ const kind = oneOf(source.kind, ["conversation", "message", "notification"], `${label}.kind`);
644
+ if (kind === "conversation")
645
+ return conversationEntity(source, label);
646
+ if (kind === "message")
647
+ return messageEntity(source, label);
648
+ return notificationEntity(source, label);
649
+ }
650
+ function freshness(value, label) {
651
+ const source = record(value, label);
652
+ exactKeys(source, ["state", "freshForMs"], [], label);
653
+ const state = oneOf(source.state, ["fresh", "stale", "unclassified"], `${label}.state`);
654
+ const freshForMs = source.freshForMs === null ? null : integer(source.freshForMs, `${label}.freshForMs`, 0, 365 * 24 * 60 * 60 * 1000);
655
+ if (state === "unclassified" !== (freshForMs === null)) {
656
+ return fail(label, "has inconsistent classification and window");
657
+ }
658
+ return Object.freeze({ state, freshForMs });
659
+ }
660
+ function coverage(value, label) {
661
+ const source = record(value, label);
662
+ const state = oneOf(source.state, ["unavailable", "observed"], `${label}.state`);
663
+ if (state === "unavailable") {
664
+ exactKeys(source, ["state", "reason"], [], label);
665
+ return Object.freeze({
666
+ state,
667
+ reason: text(source.reason, `${label}.reason`, 8192)
668
+ });
669
+ }
670
+ exactKeys(source, ["state", "kind", "continuation", "reason"], [], label);
671
+ const kind = oneOf(source.kind, [
672
+ "complete",
673
+ "page",
674
+ "unknown",
675
+ "first-page-only",
676
+ "bounded-local",
677
+ "search-window",
678
+ "truncated"
679
+ ], `${label}.kind`);
680
+ const continuation = oneOf(source.continuation, ["none", "pending", "unavailable"], `${label}.continuation`);
681
+ if (kind === "complete" && continuation !== "none") {
682
+ return fail(label, "must not continue an authoritative complete set");
683
+ }
684
+ if (kind === "first-page-only" && continuation !== "unavailable") {
685
+ return fail(label, "must mark first-page-only continuation unavailable");
686
+ }
687
+ return Object.freeze({
688
+ state,
689
+ kind,
690
+ continuation,
691
+ reason: nullableText(source.reason, `${label}.reason`, 8192, true)
692
+ });
693
+ }
694
+ function sourceStatus(value, index) {
695
+ const label = `Ghostget omni view.sources[${index}]`;
696
+ const source = record(value, label);
697
+ exactKeys(source, [
698
+ "adapterId",
699
+ "operationId",
700
+ "authId",
701
+ "requestInputHash",
702
+ "projectionInputHash",
703
+ "normalizationDataRevision",
704
+ "surfaceId",
705
+ "exact",
706
+ "normalization",
707
+ "coverage"
708
+ ], [], label);
709
+ const exact = record(source.exact, `${label}.exact`);
710
+ exactKeys(exact, ["state", "key"], ["dataRevision", "validatedAt", "ageMs", "freshness", "reason"], `${label}.exact`);
711
+ const exactState = oneOf(exact.state, ["hit", "miss", "error"], `${label}.exact.state`);
712
+ const dataRevision = exact.dataRevision === undefined ? undefined : digest(exact.dataRevision, `${label}.exact.dataRevision`);
713
+ const validatedAt = exact.validatedAt === undefined ? undefined : timestamp(exact.validatedAt, `${label}.exact.validatedAt`);
714
+ const ageMs = exact.ageMs === undefined ? undefined : integer(exact.ageMs, `${label}.exact.ageMs`, 0, Number.MAX_SAFE_INTEGER);
715
+ const parsedFreshness = exact.freshness === undefined ? undefined : freshness(exact.freshness, `${label}.exact.freshness`);
716
+ const exactReason = exact.reason === undefined ? undefined : text(exact.reason, `${label}.exact.reason`, 8192);
717
+ if (exactState === "hit" && (dataRevision === undefined || validatedAt === undefined || ageMs === undefined || parsedFreshness === undefined || exactReason !== undefined))
718
+ return fail(`${label}.exact`, "must fully describe a hit");
719
+ if (exactState === "miss" && (dataRevision !== undefined || validatedAt !== undefined || ageMs !== undefined || parsedFreshness !== undefined || exactReason !== undefined))
720
+ return fail(`${label}.exact`, "must not attach metadata to a miss");
721
+ if (exactState === "error" && (dataRevision !== undefined || validatedAt !== undefined || ageMs !== undefined || parsedFreshness !== undefined || exactReason === undefined))
722
+ return fail(`${label}.exact`, "must describe only the exact read error");
723
+ if (exactState === "hit" && parsedFreshness.state !== "unclassified" && parsedFreshness.state === "fresh" !== ageMs <= parsedFreshness.freshForMs) {
724
+ return fail(`${label}.exact.freshness`, "does not match the returned cache age");
725
+ }
726
+ const exactKey = digest(exact.key, `${label}.exact.key`);
727
+ const exactStatus = exactState === "hit" ? Object.freeze({
728
+ state: exactState,
729
+ key: exactKey,
730
+ dataRevision,
731
+ validatedAt,
732
+ ageMs,
733
+ freshness: parsedFreshness
734
+ }) : exactState === "error" ? Object.freeze({ state: exactState, key: exactKey, reason: exactReason }) : Object.freeze({ state: exactState, key: exactKey });
735
+ const normalization = record(source.normalization, `${label}.normalization`);
736
+ exactKeys(normalization, ["state"], [
737
+ "reason",
738
+ "exactQueryKey",
739
+ "exactDataRevision",
740
+ "normalizedExactDataRevision",
741
+ "failedExactDataRevision",
742
+ "newerExactDataRevision",
743
+ "lastGoodExactDataRevision",
744
+ "lastGoodAt"
745
+ ], `${label}.normalization`);
746
+ const normalizationState = oneOf(normalization.state, [
747
+ "current",
748
+ "missing",
749
+ "unsupported",
750
+ "retained-after-drift",
751
+ "stale",
752
+ "error"
753
+ ], `${label}.normalization.state`);
754
+ const reason = normalization.reason === undefined ? undefined : text(normalization.reason, `${label}.normalization.reason`, 8192);
755
+ const exactQueryKey = normalization.exactQueryKey === undefined ? undefined : digest(normalization.exactQueryKey, `${label}.normalization.exactQueryKey`);
756
+ const exactDataRevision = normalization.exactDataRevision === undefined ? undefined : normalization.exactDataRevision === null ? null : digest(normalization.exactDataRevision, `${label}.normalization.exactDataRevision`);
757
+ const normalizedExactDataRevision = normalization.normalizedExactDataRevision === undefined ? undefined : normalization.normalizedExactDataRevision === null ? null : digest(normalization.normalizedExactDataRevision, `${label}.normalization.normalizedExactDataRevision`);
758
+ const failedExactDataRevision = normalization.failedExactDataRevision === undefined ? undefined : digest(normalization.failedExactDataRevision, `${label}.normalization.failedExactDataRevision`);
759
+ const newerExactDataRevision = normalization.newerExactDataRevision === undefined ? undefined : normalization.newerExactDataRevision === null ? null : digest(normalization.newerExactDataRevision, `${label}.normalization.newerExactDataRevision`);
760
+ const lastGoodExactDataRevision = normalization.lastGoodExactDataRevision === undefined ? undefined : normalization.lastGoodExactDataRevision === null ? null : digest(normalization.lastGoodExactDataRevision, `${label}.normalization.lastGoodExactDataRevision`);
761
+ const lastGoodAt = normalization.lastGoodAt === undefined ? undefined : nullableTimestamp(normalization.lastGoodAt, `${label}.normalization.lastGoodAt`);
762
+ let normalizationStatus;
763
+ if (normalizationState === "missing") {
764
+ if (reason !== undefined || exactQueryKey !== undefined || exactDataRevision !== undefined || normalizedExactDataRevision !== undefined || failedExactDataRevision !== undefined || newerExactDataRevision !== undefined || lastGoodExactDataRevision !== undefined || lastGoodAt !== undefined)
765
+ return fail(`${label}.normalization`, "must not attach metadata to a miss");
766
+ normalizationStatus = Object.freeze({ state: normalizationState });
767
+ } else if (normalizationState === "unsupported") {
768
+ if (reason === undefined || exactQueryKey !== undefined || exactDataRevision !== undefined || normalizedExactDataRevision !== undefined || failedExactDataRevision !== undefined || newerExactDataRevision !== undefined || lastGoodExactDataRevision !== undefined || lastGoodAt !== undefined)
769
+ return fail(`${label}.normalization`, "must describe only why it is unsupported");
770
+ normalizationStatus = Object.freeze({ state: normalizationState, reason });
771
+ } else if (normalizationState === "current") {
772
+ if (reason !== undefined || exactQueryKey === undefined || exactDataRevision === undefined || exactDataRevision === null || normalizedExactDataRevision !== undefined || failedExactDataRevision !== undefined || newerExactDataRevision !== undefined || lastGoodExactDataRevision !== undefined || lastGoodAt === undefined || lastGoodAt === null)
773
+ return fail(`${label}.normalization`, "must fully describe current normalized state");
774
+ normalizationStatus = Object.freeze({
775
+ state: normalizationState,
776
+ exactQueryKey,
777
+ exactDataRevision,
778
+ lastGoodAt
779
+ });
780
+ } else if (normalizationState === "retained-after-drift") {
781
+ if (reason === undefined || exactQueryKey === undefined || exactDataRevision !== undefined || normalizedExactDataRevision !== undefined || failedExactDataRevision === undefined || newerExactDataRevision === undefined || lastGoodExactDataRevision === undefined || lastGoodAt === undefined)
782
+ return fail(`${label}.normalization`, "must fully describe retained drift state");
783
+ if (lastGoodExactDataRevision === null !== (lastGoodAt === null)) {
784
+ return fail(`${label}.normalization`, "must pair last-good revision and observation time");
785
+ }
786
+ if (newerExactDataRevision !== null && newerExactDataRevision === failedExactDataRevision) {
787
+ return fail(`${label}.normalization.newerExactDataRevision`, "must differ from the failed exact revision");
788
+ }
789
+ normalizationStatus = Object.freeze({
790
+ state: normalizationState,
791
+ exactQueryKey,
792
+ reason,
793
+ failedExactDataRevision,
794
+ newerExactDataRevision,
795
+ lastGoodExactDataRevision,
796
+ lastGoodAt
797
+ });
798
+ } else if (normalizationState === "stale") {
799
+ if (reason === undefined || exactQueryKey === undefined || exactDataRevision === undefined || normalizedExactDataRevision === undefined || failedExactDataRevision !== undefined || newerExactDataRevision !== undefined || lastGoodExactDataRevision !== undefined || lastGoodAt === undefined)
800
+ return fail(`${label}.normalization`, "must fully describe stale normalized state");
801
+ if (normalizedExactDataRevision === null !== (lastGoodAt === null)) {
802
+ return fail(`${label}.normalization`, "must pair normalized revision and observation time");
803
+ }
804
+ normalizationStatus = Object.freeze({
805
+ state: normalizationState,
806
+ exactQueryKey,
807
+ exactDataRevision,
808
+ normalizedExactDataRevision,
809
+ lastGoodAt,
810
+ reason
811
+ });
812
+ } else {
813
+ if (reason === undefined || exactQueryKey !== undefined || exactDataRevision !== undefined || normalizedExactDataRevision !== undefined || failedExactDataRevision !== undefined || newerExactDataRevision !== undefined || lastGoodExactDataRevision !== undefined || lastGoodAt === undefined)
814
+ return fail(`${label}.normalization`, "must describe only the normalization error");
815
+ normalizationStatus = Object.freeze({
816
+ state: normalizationState,
817
+ reason,
818
+ lastGoodAt
819
+ });
820
+ }
821
+ if ("exactQueryKey" in normalizationStatus && normalizationStatus.exactQueryKey === exactStatus.key) {
822
+ let normalizationRevision;
823
+ let normalizationRevisionField;
824
+ if (normalizationStatus.state === "current") {
825
+ normalizationRevision = normalizationStatus.exactDataRevision;
826
+ normalizationRevisionField = "exactDataRevision";
827
+ } else if (normalizationStatus.state === "retained-after-drift") {
828
+ normalizationRevision = normalizationStatus.newerExactDataRevision ?? normalizationStatus.failedExactDataRevision;
829
+ normalizationRevisionField = normalizationStatus.newerExactDataRevision === null ? "failedExactDataRevision" : "newerExactDataRevision";
830
+ } else if (normalizationStatus.exactDataRevision !== null) {
831
+ normalizationRevision = normalizationStatus.exactDataRevision;
832
+ normalizationRevisionField = "exactDataRevision";
833
+ } else {
834
+ normalizationRevision = normalizationStatus.normalizedExactDataRevision;
835
+ normalizationRevisionField = "normalizedExactDataRevision";
836
+ }
837
+ if (exactStatus.state === "hit" && exactStatus.dataRevision !== normalizationRevision) {
838
+ return fail(`${label}.normalization.${normalizationRevisionField}`, "does not match the exact hit");
839
+ }
840
+ if (exactStatus.state !== "hit" && normalizationStatus.state === "current") {
841
+ return fail(`${label}.normalization`, "cannot be current without its exact hit");
842
+ }
843
+ if (exactStatus.state !== "hit" && normalizationStatus.state === "retained-after-drift" && normalizationStatus.newerExactDataRevision !== null) {
844
+ return fail(`${label}.normalization.newerExactDataRevision`, "cannot name a newer current revision without an exact hit");
845
+ }
846
+ if (exactStatus.state !== "hit" && normalizationStatus.state === "stale" && normalizationStatus.exactDataRevision !== null) {
847
+ return fail(`${label}.normalization.exactDataRevision`, "cannot name a current revision without an exact hit");
848
+ }
849
+ }
850
+ if (normalizationStatus.state === "current" && normalizationStatus.exactQueryKey !== exactStatus.key) {
851
+ return fail(`${label}.normalization.exactQueryKey`, "must name the root exact query while normalization is current");
852
+ }
853
+ const normalizationDataRevision = source.normalizationDataRevision === null ? null : digest(source.normalizationDataRevision, `${label}.normalizationDataRevision`);
854
+ if ((normalizationStatus.state === "current" || normalizationStatus.state === "stale" || normalizationStatus.state === "retained-after-drift") && normalizationDataRevision === null) {
855
+ return fail(`${label}.normalizationDataRevision`, `is required while normalization is ${normalizationStatus.state}`);
856
+ }
857
+ if (normalizationStatus.state === "unsupported" && normalizationDataRevision !== null) {
858
+ return fail(`${label}.normalizationDataRevision`, "must be null while normalization is unsupported");
859
+ }
860
+ const parsedCoverage = coverage(source.coverage, `${label}.coverage`);
861
+ if (normalizationStatus.state === "current" && parsedCoverage.state === "unavailable") {
862
+ return fail(`${label}.coverage`, "cannot be unavailable for current normalized state");
863
+ }
864
+ if (normalizationStatus.state === "current" && parsedCoverage.state === "observed" && parsedCoverage.continuation === "pending") {
865
+ return fail(`${label}.coverage.continuation`, "cannot be pending for current normalized state");
866
+ }
867
+ if ((normalizationStatus.state === "missing" || normalizationStatus.state === "unsupported" || normalizationStatus.state === "error") && parsedCoverage.state === "observed") {
868
+ return fail(`${label}.coverage`, `cannot be observed while normalization is ${normalizationStatus.state}`);
869
+ }
870
+ return Object.freeze({
871
+ adapterId: text(source.adapterId, `${label}.adapterId`, 64),
872
+ operationId: oneOf(source.operationId, ["messaging.list", "messaging.read"], `${label}.operationId`),
873
+ authId: text(source.authId, `${label}.authId`, 64),
874
+ requestInputHash: digest(source.requestInputHash, `${label}.requestInputHash`),
875
+ projectionInputHash: digest(source.projectionInputHash, `${label}.projectionInputHash`),
876
+ normalizationDataRevision,
877
+ surfaceId: text(source.surfaceId, `${label}.surfaceId`, 64),
878
+ exact: exactStatus,
879
+ normalization: normalizationStatus,
880
+ coverage: parsedCoverage
881
+ });
882
+ }
883
+ function sourceCoordinateKey(value) {
884
+ return canonicalJson({
885
+ adapterId: value.adapterId,
886
+ operationId: value.operationId,
887
+ authId: value.authId,
888
+ requestInputHash: value.requestInputHash
889
+ });
890
+ }
891
+ function compareEntities(left, right) {
892
+ const leftTime = left.orderedAt ?? "";
893
+ const rightTime = right.orderedAt ?? "";
894
+ return rightTime.localeCompare(leftTime) || left.id.localeCompare(right.id);
895
+ }
896
+ function view(value, request) {
897
+ const source = record(value, "Ghostget omni view");
898
+ exactKeys(source, ["schemaVersion", "viewRevision", "entities", "nextCursor", "sources"], [], "Ghostget omni view");
899
+ if (source.schemaVersion !== 1)
900
+ return fail("Ghostget omni view.schemaVersion", "must be 1");
901
+ const entities = Object.freeze(denseArray(source.entities, "Ghostget omni view.entities", OMNI_MAX_VIEW_ENTITIES).map(entity));
902
+ if (new Set(entities.map((entry) => entry.id)).size !== entities.length) {
903
+ return fail("Ghostget omni view.entities", "must not repeat entity identities");
904
+ }
905
+ for (let index = 1;index < entities.length; index += 1) {
906
+ const prior = entities[index - 1];
907
+ const current = entities[index];
908
+ if (prior !== undefined && current !== undefined && compareEntities(prior, current) > 0) {
909
+ return fail("Ghostget omni view.entities", "must use deterministic newest-first order");
910
+ }
911
+ }
912
+ if (entities.length > request.pageLimit) {
913
+ return fail("Ghostget omni view.entities", "exceeds the requested page limit");
914
+ }
915
+ for (const entry of entities) {
916
+ if (request.filter.kinds !== null && !request.filter.kinds.includes(entry.kind)) {
917
+ return fail("Ghostget omni view.entities", "contains a kind excluded by the request");
918
+ }
919
+ if (request.filter.conversationId !== null && entry.conversationId !== request.filter.conversationId) {
920
+ return fail("Ghostget omni view.entities", "contains an entity outside the requested conversation");
921
+ }
922
+ if (request.filter.unread !== null) {
923
+ const unread = entry.kind === "conversation" ? entry.unread ?? (entry.unreadCount === null ? null : entry.unreadCount > 0) : entry.unread;
924
+ if (unread !== request.filter.unread) {
925
+ return fail("Ghostget omni view.entities", "contains an entity outside the requested unread state");
926
+ }
927
+ }
928
+ }
929
+ const nextCursor = source.nextCursor === null ? null : localCursor(source.nextCursor, "Ghostget omni view.nextCursor");
930
+ if (nextCursor !== null && entities.length !== request.pageLimit) {
931
+ return fail("Ghostget omni view.nextCursor", "requires a full requested page");
932
+ }
933
+ const sources = Object.freeze(denseArray(source.sources, "Ghostget omni view.sources", OMNI_MAX_SOURCES).map(sourceStatus));
934
+ if (sources.length === 0)
935
+ return fail("Ghostget omni view.sources", "must not be empty");
936
+ const sourceKeys = sources.map((entry) => canonicalJson({
937
+ adapterId: entry.adapterId,
938
+ operationId: entry.operationId,
939
+ authId: entry.authId,
940
+ requestInputHash: entry.requestInputHash
941
+ }));
942
+ if (new Set(sourceKeys).size !== sourceKeys.length) {
943
+ return fail("Ghostget omni view.sources", "must not repeat source identities");
944
+ }
945
+ const expectedSourceKeys = request.sources.map(sourceCoordinateKey);
946
+ const actualSourceKeys = sources.map(sourceCoordinateKey);
947
+ if (expectedSourceKeys.length !== actualSourceKeys.length || expectedSourceKeys.some((key, index) => key !== actualSourceKeys[index])) {
948
+ return fail("Ghostget omni view.sources", "must exactly match the requested source coordinates and order");
949
+ }
950
+ const returnedEntitySources = new Set(sources.map((entry) => canonicalJson({ authId: entry.authId, surfaceId: entry.surfaceId })));
951
+ for (const entry of entities) {
952
+ const sourceKey = canonicalJson({
953
+ authId: entry.source.authId,
954
+ surfaceId: entry.source.surfaceId
955
+ });
956
+ if (!returnedEntitySources.has(sourceKey)) {
957
+ return fail("Ghostget omni view.entities", "must belong to one returned requested source");
958
+ }
959
+ }
960
+ return Object.freeze({
961
+ schemaVersion: 1,
962
+ viewRevision: digest(source.viewRevision, "Ghostget omni view.viewRevision"),
963
+ entities,
964
+ nextCursor,
965
+ sources
966
+ });
967
+ }
968
+ function identity(value) {
969
+ const source = record(value, "Ghostget omni response identity");
970
+ exactKeys(source, ["invocationDigest", "requestDigest", "sourceSetDigest"], [], "Ghostget omni response identity");
971
+ return Object.freeze({
972
+ invocationDigest: digest(source.invocationDigest, "Ghostget omni response invocation digest"),
973
+ requestDigest: digest(source.requestDigest, "Ghostget omni response request digest"),
974
+ sourceSetDigest: digest(source.sourceSetDigest, "Ghostget omni response source-set digest")
975
+ });
976
+ }
977
+ function envelope(value, request) {
978
+ const source = record(value, "Ghostget omni response");
979
+ exactKeys(source, ["ok", "schemaVersion", "source", "identity", "view"], [], "Ghostget omni response");
980
+ if (source.ok !== true)
981
+ return fail("Ghostget omni response.ok", "must be true");
982
+ if (source.schemaVersion !== 1) {
983
+ return fail("Ghostget omni response.schemaVersion", "must be 1");
984
+ }
985
+ const parsedIdentity = identity(source.identity);
986
+ if (parsedIdentity.invocationDigest !== request.invocationDigest) {
987
+ return fail("Ghostget omni response identity.invocationDigest", "does not match the exact canonical invocation");
988
+ }
989
+ if (parsedIdentity.requestDigest !== request.requestDigest) {
990
+ return fail("Ghostget omni response identity.requestDigest", "does not match the cursor-independent canonical request");
991
+ }
992
+ const parsedSource = oneOf(source.source, ["omni-cache", "omni-live", "omni-exact-cache", "omni-identity"], "Ghostget omni response.source");
993
+ if (parsedSource === "omni-identity") {
994
+ if (source.view !== null)
995
+ return fail("Ghostget omni response.view", "must be null for identity-only output");
996
+ return Object.freeze({
997
+ source: parsedSource,
998
+ identity: parsedIdentity,
999
+ view: null
1000
+ });
1001
+ }
1002
+ if (source.view === null)
1003
+ return fail("Ghostget omni response.view", "is required");
1004
+ return Object.freeze({
1005
+ source: parsedSource,
1006
+ identity: parsedIdentity,
1007
+ view: view(source.view, request)
1008
+ });
1009
+ }
1010
+ function parseEnvelopeOutput(value, label, request) {
1011
+ return envelope(parseJsonOutput(childText(value, label), label), request);
1012
+ }
1013
+ function runSynchronousCommand(command, options, label) {
1014
+ requireBunRuntime();
1015
+ const result = spawnSync(process.execPath, command.arguments, {
1016
+ cwd: options.cwd,
1017
+ env: options.environment,
1018
+ input: command.input,
1019
+ encoding: "utf8",
1020
+ maxBuffer: OMNI_MAX_RESPONSE_BYTES,
1021
+ timeout: SYNCHRONOUS_COMMAND_TIMEOUT_MS,
1022
+ killSignal: "SIGKILL"
1023
+ });
1024
+ if (result.error !== undefined)
1025
+ throw result.error;
1026
+ const code = result.status ?? 3;
1027
+ const stderr = childText(result.stderr ?? "", `${label} stderr`);
1028
+ if (code !== 0) {
1029
+ throw new Error(boundedMessage(stderr) || `${label} exited ${code}`);
1030
+ }
1031
+ const stdout = childText(result.stdout ?? "", `${label} stdout`);
1032
+ if (stdout.trim().length === 0) {
1033
+ throw new Error(boundedMessage(stderr) || `${label} returned no output`);
1034
+ }
1035
+ return parseEnvelopeOutput(stdout, `${label} response`, command.request);
1036
+ }
1037
+ function runAsynchronousCommand(command, options, label, timeoutMs) {
1038
+ requireBunRuntime();
1039
+ throwIfAborted(options.signal);
1040
+ return new Promise((resolve, reject) => {
1041
+ const child = spawn(process.execPath, command.arguments, {
1042
+ cwd: options.cwd,
1043
+ env: options.environment,
1044
+ stdio: ["pipe", "pipe", "pipe"]
1045
+ });
1046
+ const stdout = [];
1047
+ const stderr = [];
1048
+ let stdoutBytes = 0;
1049
+ let stderrBytes = 0;
1050
+ let settled = false;
1051
+ let closed = false;
1052
+ let pendingError = null;
1053
+ let terminationTimer;
1054
+ let forcedTerminationTimer;
1055
+ let controlTimer;
1056
+ const asError = (errorValue) => errorValue instanceof Error ? errorValue : new Error(String(errorValue));
1057
+ const removeAbortListener = () => {
1058
+ if (options.signal !== undefined) {
1059
+ options.signal.removeEventListener("abort", onAbort);
1060
+ }
1061
+ };
1062
+ const clearTimers = () => {
1063
+ if (terminationTimer !== undefined)
1064
+ clearTimeout(terminationTimer);
1065
+ if (forcedTerminationTimer !== undefined)
1066
+ clearTimeout(forcedTerminationTimer);
1067
+ if (controlTimer !== undefined)
1068
+ clearTimeout(controlTimer);
1069
+ };
1070
+ const settleFailure = (errorValue) => {
1071
+ if (settled)
1072
+ return;
1073
+ settled = true;
1074
+ clearTimers();
1075
+ removeAbortListener();
1076
+ reject(asError(errorValue));
1077
+ };
1078
+ const requestTermination = (errorValue) => {
1079
+ if (settled || closed)
1080
+ return;
1081
+ pendingError ??= asError(errorValue);
1082
+ if (terminationTimer !== undefined)
1083
+ return;
1084
+ try {
1085
+ child.kill("SIGTERM");
1086
+ } catch {}
1087
+ if (settled || closed)
1088
+ return;
1089
+ terminationTimer = setTimeout(() => {
1090
+ if (settled || closed)
1091
+ return;
1092
+ try {
1093
+ child.kill("SIGKILL");
1094
+ } catch {}
1095
+ if (settled || closed)
1096
+ return;
1097
+ forcedTerminationTimer = setTimeout(() => {
1098
+ if (settled || closed)
1099
+ return;
1100
+ child.stdin.destroy();
1101
+ child.stdout.destroy();
1102
+ child.stderr.destroy();
1103
+ settleFailure(new Error(`${label} child did not close after SIGKILL`, { cause: pendingError }));
1104
+ }, CHILD_FORCEFUL_TERMINATION_MS);
1105
+ forcedTerminationTimer.unref();
1106
+ }, CHILD_GRACEFUL_TERMINATION_MS);
1107
+ terminationTimer.unref();
1108
+ };
1109
+ function onAbort() {
1110
+ if (options.signal !== undefined) {
1111
+ requestTermination(abortError(options.signal));
1112
+ }
1113
+ }
1114
+ if (options.signal !== undefined) {
1115
+ options.signal.addEventListener("abort", onAbort, { once: true });
1116
+ }
1117
+ if (timeoutMs !== undefined) {
1118
+ controlTimer = setTimeout(() => {
1119
+ requestTermination(new Error(`${label} exceeded its ${timeoutMs}ms deadline`));
1120
+ }, timeoutMs);
1121
+ controlTimer.unref();
1122
+ }
1123
+ child.stdin.on("error", (error) => {
1124
+ if (pendingError === null)
1125
+ requestTermination(error);
1126
+ });
1127
+ child.stdout.on("error", requestTermination);
1128
+ child.stderr.on("error", requestTermination);
1129
+ child.stdout.on("data", (chunkValue) => {
1130
+ if (pendingError !== null)
1131
+ return;
1132
+ const chunk = Buffer.isBuffer(chunkValue) ? chunkValue : Buffer.from(chunkValue);
1133
+ stdoutBytes += chunk.byteLength;
1134
+ if (stdoutBytes > OMNI_MAX_RESPONSE_BYTES) {
1135
+ requestTermination(new Error(`${label} response exceeds its byte bound`));
1136
+ return;
1137
+ }
1138
+ stdout.push(chunk);
1139
+ });
1140
+ child.stderr.on("data", (chunkValue) => {
1141
+ if (pendingError !== null)
1142
+ return;
1143
+ const chunk = Buffer.isBuffer(chunkValue) ? chunkValue : Buffer.from(chunkValue);
1144
+ const remainingBytes = Math.max(0, OMNI_MAX_REASON_BYTES - stderrBytes);
1145
+ stderrBytes += chunk.byteLength;
1146
+ if (remainingBytes > 0)
1147
+ stderr.push(chunk.subarray(0, remainingBytes));
1148
+ });
1149
+ child.on("error", (error) => {
1150
+ if (child.pid === undefined) {
1151
+ settleFailure(error);
1152
+ return;
1153
+ }
1154
+ requestTermination(error);
1155
+ });
1156
+ child.on("close", (code, signal) => {
1157
+ if (settled)
1158
+ return;
1159
+ closed = true;
1160
+ clearTimers();
1161
+ removeAbortListener();
1162
+ if (pendingError !== null) {
1163
+ settleFailure(pendingError);
1164
+ return;
1165
+ }
1166
+ const error = boundedMessage(Buffer.concat(stderr).toString("utf8"));
1167
+ if (code !== 0) {
1168
+ settleFailure(new Error(error || `${label} exited ${code ?? signal ?? "unknown"}`));
1169
+ return;
1170
+ }
1171
+ const output = Buffer.concat(stdout).toString("utf8");
1172
+ if (output.trim().length === 0) {
1173
+ settleFailure(new Error(error || `${label} returned no output`));
1174
+ return;
1175
+ }
1176
+ let parsed;
1177
+ try {
1178
+ parsed = parseEnvelopeOutput(output, `${label} response`, command.request);
1179
+ } catch (parseError) {
1180
+ settleFailure(parseError);
1181
+ return;
1182
+ }
1183
+ settled = true;
1184
+ resolve(parsed);
1185
+ });
1186
+ child.stdin.end(command.input);
1187
+ });
1188
+ }
1189
+ function runLiveCommand(command, options) {
1190
+ return runAsynchronousCommand(command, options, "Ghostget omni live read");
1191
+ }
1192
+ function runAsynchronousControlCommand(command, options, label) {
1193
+ return runAsynchronousCommand(command, options, label, ASYNC_CONTROL_COMMAND_TIMEOUT_MS);
1194
+ }
1195
+ function requireEnvelopeSource(value, expected, label) {
1196
+ if (value.source !== expected)
1197
+ return fail(label, `must have source ${expected}`);
1198
+ return value;
1199
+ }
1200
+ function cacheResult(value) {
1201
+ const parsed = requireEnvelopeSource(value, "omni-cache", "Ghostget omni cache response");
1202
+ return Object.freeze({
1203
+ schemaVersion: 1,
1204
+ source: parsed.source,
1205
+ identity: parsed.identity,
1206
+ view: parsed.view
1207
+ });
1208
+ }
1209
+ function liveResult(value) {
1210
+ const parsed = requireEnvelopeSource(value, "omni-live", "Ghostget omni live response");
1211
+ return Object.freeze({
1212
+ schemaVersion: 1,
1213
+ source: parsed.source,
1214
+ identity: parsed.identity,
1215
+ view: parsed.view
1216
+ });
1217
+ }
1218
+ async function observeIdentity(request, options) {
1219
+ throwIfAborted(options.signal);
1220
+ return requireEnvelopeSource(await runAsynchronousControlCommand(preparedCommand(request, "identity", options), options, "Ghostget omni identity preflight"), "omni-identity", "Ghostget omni identity response").identity;
1221
+ }
1222
+ function readCachedPrepared(request, options) {
1223
+ return cacheResult(runSynchronousCommand(preparedCommand(request, "cache", options), options, "Ghostget omni cache read"));
1224
+ }
1225
+ async function readCachedPreparedAsynchronously(request, options) {
1226
+ return cacheResult(await runAsynchronousControlCommand(preparedCommand(request, "cache", options), options, "Ghostget omni cache read"));
1227
+ }
1228
+ function identitiesMatch(left, right) {
1229
+ return left.invocationDigest === right.invocationDigest && left.requestDigest === right.requestDigest && left.sourceSetDigest === right.sourceSetDigest;
1230
+ }
1231
+ function assertIdentity(expected, actual, phase) {
1232
+ if (!identitiesMatch(expected, actual)) {
1233
+ throw new Error(`Ghostget omni identity changed ${phase}; the live result was discarded`);
1234
+ }
1235
+ }
1236
+ function viewsHaveSameEntities(left, right) {
1237
+ return left.entities.length === right.entities.length && left.entities.every((entity2, index) => {
1238
+ const other = right.entities[index];
1239
+ return other !== undefined && entity2.id === other.id && entity2.revision === other.revision;
1240
+ });
1241
+ }
1242
+ function positiveSourceHeads(source) {
1243
+ const rootExact = source.exact.state === "hit" ? canonicalJson(Object.freeze({
1244
+ key: source.exact.key,
1245
+ dataRevision: source.exact.dataRevision,
1246
+ validatedAt: source.exact.validatedAt
1247
+ })) : null;
1248
+ const continuationExact = source.normalization.state === "stale" && source.normalization.exactQueryKey !== source.exact.key && source.normalization.exactDataRevision !== null ? canonicalJson(Object.freeze({
1249
+ key: source.normalization.exactQueryKey,
1250
+ dataRevision: source.normalization.exactDataRevision
1251
+ })) : source.normalization.state === "retained-after-drift" && source.normalization.exactQueryKey !== source.exact.key && source.normalization.newerExactDataRevision !== null ? canonicalJson(Object.freeze({
1252
+ key: source.normalization.exactQueryKey,
1253
+ dataRevision: source.normalization.newerExactDataRevision
1254
+ })) : null;
1255
+ return Object.freeze({
1256
+ rootExact,
1257
+ normalization: source.normalizationDataRevision,
1258
+ continuationExact
1259
+ });
1260
+ }
1261
+ function hasPositiveAdvance(after, before, duringLive) {
1262
+ return Object.keys(after).some((key) => {
1263
+ const candidate = after[key];
1264
+ return candidate !== null && candidate !== duringLive[key] && (before === null || candidate !== before[key]);
1265
+ });
1266
+ }
1267
+ function laterSourceCoordinates(cachedBefore, cachedAfter, live) {
1268
+ const beforeEvidence = new Map((cachedBefore?.view.sources ?? []).map((source) => [
1269
+ sourceCoordinateKey(source),
1270
+ positiveSourceHeads(source)
1271
+ ]));
1272
+ const liveEvidence = new Map(live.view.sources.map((source) => [
1273
+ sourceCoordinateKey(source),
1274
+ positiveSourceHeads(source)
1275
+ ]));
1276
+ const result = new Set;
1277
+ for (const source of cachedAfter.view.sources) {
1278
+ const coordinate = sourceCoordinateKey(source);
1279
+ const after = positiveSourceHeads(source);
1280
+ const before = beforeEvidence.get(coordinate) ?? null;
1281
+ const duringLive = liveEvidence.get(coordinate);
1282
+ if (duringLive !== undefined && hasPositiveAdvance(after, before, duringLive))
1283
+ result.add(coordinate);
1284
+ }
1285
+ return result;
1286
+ }
1287
+ function viewHasLaterRevision(cachedBefore, cachedAfter, live) {
1288
+ return cachedAfter.view.viewRevision !== live.view.viewRevision && (cachedBefore === null || cachedAfter.view.viewRevision !== cachedBefore.view.viewRevision);
1289
+ }
1290
+ function sourceHasUnresolvedLiveIssue(source) {
1291
+ return source.exact.state === "error" || source.normalization.state !== "current";
1292
+ }
1293
+ function selectCurrent(cachedBefore, cachedAfter, live) {
1294
+ if (cachedAfter === null)
1295
+ return live;
1296
+ const laterSources = laterSourceCoordinates(cachedBefore, cachedAfter, live);
1297
+ let useCachedView = false;
1298
+ if (viewHasLaterRevision(cachedBefore, cachedAfter, live) || laterSources.size > 0) {
1299
+ useCachedView = true;
1300
+ } else if (viewsHaveSameEntities(cachedAfter.view, live.view)) {
1301
+ return live;
1302
+ } else if (cachedBefore !== null && viewsHaveSameEntities(cachedAfter.view, cachedBefore.view)) {
1303
+ return live;
1304
+ } else {
1305
+ useCachedView = true;
1306
+ }
1307
+ if (!useCachedView)
1308
+ return live;
1309
+ const liveSources = new Map(live.view.sources.map((source) => [
1310
+ sourceCoordinateKey(source),
1311
+ source
1312
+ ]));
1313
+ let merged = false;
1314
+ const sources = Object.freeze(cachedAfter.view.sources.map((cachedSource) => {
1315
+ const coordinate = sourceCoordinateKey(cachedSource);
1316
+ const liveSource = liveSources.get(coordinate);
1317
+ if (liveSource !== undefined && sourceHasUnresolvedLiveIssue(liveSource)) {
1318
+ merged = true;
1319
+ return liveSource;
1320
+ }
1321
+ if (laterSources.has(coordinate) || liveSource === undefined || canonicalJson(liveSource) === canonicalJson(cachedSource))
1322
+ return cachedSource;
1323
+ merged = true;
1324
+ return liveSource;
1325
+ }));
1326
+ if (!merged)
1327
+ return cachedAfter;
1328
+ const result = Object.freeze({
1329
+ schemaVersion: 1,
1330
+ source: "omni-merged",
1331
+ identity: cachedAfter.identity,
1332
+ view: Object.freeze({
1333
+ ...cachedAfter.view,
1334
+ sources
1335
+ })
1336
+ });
1337
+ return result;
1338
+ }
1339
+ function scheduleRevalidation(request, options, cachedBeforeSource) {
1340
+ return Promise.resolve().then(async () => {
1341
+ throwIfAborted(options.signal);
1342
+ const identityBefore = await observeIdentity(request, options);
1343
+ let cachedBefore = cachedBeforeSource.status === "provided" ? cachedBeforeSource.value : null;
1344
+ if (cachedBeforeSource.status === "lookup") {
1345
+ try {
1346
+ cachedBefore = await readCachedPreparedAsynchronously(request, options);
1347
+ } catch {
1348
+ cachedBefore = null;
1349
+ }
1350
+ }
1351
+ if (cachedBefore !== null) {
1352
+ assertIdentity(identityBefore, cachedBefore.identity, "before revalidation started");
1353
+ }
1354
+ throwIfAborted(options.signal);
1355
+ const live = liveResult(await runLiveCommand(preparedCommand(request, "live", options), options));
1356
+ throwIfAborted(options.signal);
1357
+ const identityAfter = await observeIdentity(request, options);
1358
+ assertIdentity(identityBefore, live.identity, "while revalidation was running");
1359
+ assertIdentity(identityBefore, identityAfter, "while revalidation was running");
1360
+ let cachedAfter = null;
1361
+ try {
1362
+ cachedAfter = await readCachedPreparedAsynchronously(request, options);
1363
+ } catch {
1364
+ cachedAfter = null;
1365
+ }
1366
+ if (cachedAfter !== null) {
1367
+ assertIdentity(identityBefore, cachedAfter.identity, "before the final cache observation");
1368
+ }
1369
+ throwIfAborted(options.signal);
1370
+ return Object.freeze({
1371
+ cachedBefore,
1372
+ cachedAfter,
1373
+ live,
1374
+ current: selectCurrent(cachedBefore, cachedAfter, live)
1375
+ });
1376
+ });
1377
+ }
1378
+ function readCachedOmniView(request, options = {}) {
1379
+ const preparedOptions = snapshotOptions(options, false);
1380
+ const preparedRequest = prepareRequest(request);
1381
+ return readCachedPrepared(preparedRequest, preparedOptions);
1382
+ }
1383
+ function revalidateOmniView(request, options = {}) {
1384
+ const preparedOptions = snapshotOptions(options, true);
1385
+ const preparedRequest = prepareRequest(request);
1386
+ return scheduleRevalidation(preparedRequest, preparedOptions, { status: "lookup" });
1387
+ }
1388
+ function staleWhileRevalidateOmniView(request, options = {}) {
1389
+ const preparedOptions = snapshotOptions(options, true);
1390
+ const preparedRequest = prepareRequest(request);
1391
+ let cached = null;
1392
+ try {
1393
+ cached = readCachedPrepared(preparedRequest, preparedOptions);
1394
+ } catch {
1395
+ cached = null;
1396
+ }
1397
+ return Object.freeze({
1398
+ cached,
1399
+ revalidation: scheduleRevalidation(preparedRequest, preparedOptions, { status: "provided", value: cached })
1400
+ });
1401
+ }
1402
+ export {
1403
+ staleWhileRevalidateOmniView,
1404
+ revalidateOmniView,
1405
+ readCachedOmniView
1406
+ };