@copilotkit/aimock 1.42.1 → 1.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (442) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +65 -0
  4. package/README.md +108 -16
  5. package/dist/agui-handler.d.ts.map +1 -1
  6. package/dist/agui-mock.cjs +1 -1
  7. package/dist/agui-mock.cjs.map +1 -1
  8. package/dist/agui-mock.d.cts.map +1 -1
  9. package/dist/agui-mock.d.ts.map +1 -1
  10. package/dist/agui-mock.js +1 -1
  11. package/dist/agui-mock.js.map +1 -1
  12. package/dist/agui-recorder.cjs +33 -11
  13. package/dist/agui-recorder.cjs.map +1 -1
  14. package/dist/agui-recorder.d.cts +10 -1
  15. package/dist/agui-recorder.d.cts.map +1 -1
  16. package/dist/agui-recorder.d.ts +10 -1
  17. package/dist/agui-recorder.d.ts.map +1 -1
  18. package/dist/agui-recorder.js +33 -11
  19. package/dist/agui-recorder.js.map +1 -1
  20. package/dist/agui-types.d.cts +1 -0
  21. package/dist/agui-types.d.cts.map +1 -1
  22. package/dist/agui-types.d.ts +1 -0
  23. package/dist/agui-types.d.ts.map +1 -1
  24. package/dist/aimock-cli.cjs +38 -10
  25. package/dist/aimock-cli.cjs.map +1 -1
  26. package/dist/aimock-cli.d.cts +5 -0
  27. package/dist/aimock-cli.d.cts.map +1 -1
  28. package/dist/aimock-cli.d.ts +5 -0
  29. package/dist/aimock-cli.d.ts.map +1 -1
  30. package/dist/aimock-cli.js +38 -10
  31. package/dist/aimock-cli.js.map +1 -1
  32. package/dist/batches.cjs +458 -0
  33. package/dist/batches.cjs.map +1 -0
  34. package/dist/batches.d.cts +57 -0
  35. package/dist/batches.d.cts.map +1 -0
  36. package/dist/batches.d.ts +57 -0
  37. package/dist/batches.d.ts.map +1 -0
  38. package/dist/batches.js +453 -0
  39. package/dist/batches.js.map +1 -0
  40. package/dist/bedrock-converse.cjs +44 -12
  41. package/dist/bedrock-converse.cjs.map +1 -1
  42. package/dist/bedrock-converse.d.cts.map +1 -1
  43. package/dist/bedrock-converse.d.ts.map +1 -1
  44. package/dist/bedrock-converse.js +46 -14
  45. package/dist/bedrock-converse.js.map +1 -1
  46. package/dist/bedrock.cjs +10 -8
  47. package/dist/bedrock.cjs.map +1 -1
  48. package/dist/bedrock.d.cts.map +1 -1
  49. package/dist/bedrock.d.ts.map +1 -1
  50. package/dist/bedrock.js +12 -10
  51. package/dist/bedrock.js.map +1 -1
  52. package/dist/byteplus-video.cjs +3 -3
  53. package/dist/byteplus-video.cjs.map +1 -1
  54. package/dist/byteplus-video.js +4 -4
  55. package/dist/byteplus-video.js.map +1 -1
  56. package/dist/chaos.cjs +625 -72
  57. package/dist/chaos.cjs.map +1 -1
  58. package/dist/chaos.d.cts +164 -7
  59. package/dist/chaos.d.cts.map +1 -1
  60. package/dist/chaos.d.ts +164 -7
  61. package/dist/chaos.d.ts.map +1 -1
  62. package/dist/chaos.js +616 -74
  63. package/dist/chaos.js.map +1 -1
  64. package/dist/cli.cjs +131 -30
  65. package/dist/cli.cjs.map +1 -1
  66. package/dist/cli.js +132 -31
  67. package/dist/cli.js.map +1 -1
  68. package/dist/cohere.cjs +37 -9
  69. package/dist/cohere.cjs.map +1 -1
  70. package/dist/cohere.d.cts.map +1 -1
  71. package/dist/cohere.d.ts.map +1 -1
  72. package/dist/cohere.js +39 -11
  73. package/dist/cohere.js.map +1 -1
  74. package/dist/config-loader.cjs +1 -1
  75. package/dist/config-loader.js +1 -1
  76. package/dist/constants.cjs +8 -0
  77. package/dist/constants.cjs.map +1 -1
  78. package/dist/constants.d.cts +6 -2
  79. package/dist/constants.d.ts +6 -2
  80. package/dist/constants.js +8 -1
  81. package/dist/constants.js.map +1 -1
  82. package/dist/convert.cjs +1 -1
  83. package/dist/convert.cjs.map +1 -1
  84. package/dist/convert.js +1 -1
  85. package/dist/convert.js.map +1 -1
  86. package/dist/echo-text.cjs +65 -0
  87. package/dist/echo-text.cjs.map +1 -0
  88. package/dist/echo-text.d.cts +22 -0
  89. package/dist/echo-text.d.cts.map +1 -0
  90. package/dist/echo-text.d.ts +22 -0
  91. package/dist/echo-text.d.ts.map +1 -0
  92. package/dist/echo-text.js +65 -0
  93. package/dist/echo-text.js.map +1 -0
  94. package/dist/elevenlabs-audio.cjs +3 -3
  95. package/dist/elevenlabs-audio.cjs.map +1 -1
  96. package/dist/elevenlabs-audio.js +4 -4
  97. package/dist/elevenlabs-audio.js.map +1 -1
  98. package/dist/elevenlabs-voice.cjs +1053 -0
  99. package/dist/elevenlabs-voice.cjs.map +1 -0
  100. package/dist/elevenlabs-voice.d.cts +72 -0
  101. package/dist/elevenlabs-voice.d.cts.map +1 -0
  102. package/dist/elevenlabs-voice.d.ts +72 -0
  103. package/dist/elevenlabs-voice.d.ts.map +1 -0
  104. package/dist/elevenlabs-voice.js +1048 -0
  105. package/dist/elevenlabs-voice.js.map +1 -0
  106. package/dist/embeddings.cjs +26 -5
  107. package/dist/embeddings.cjs.map +1 -1
  108. package/dist/embeddings.d.cts +1 -1
  109. package/dist/embeddings.d.cts.map +1 -1
  110. package/dist/embeddings.d.ts +1 -1
  111. package/dist/embeddings.d.ts.map +1 -1
  112. package/dist/embeddings.js +27 -6
  113. package/dist/embeddings.js.map +1 -1
  114. package/dist/fal-audio.cjs +3 -3
  115. package/dist/fal-audio.cjs.map +1 -1
  116. package/dist/fal-audio.js +4 -4
  117. package/dist/fal-audio.js.map +1 -1
  118. package/dist/fal.cjs +28 -4
  119. package/dist/fal.cjs.map +1 -1
  120. package/dist/fal.d.cts +16 -1
  121. package/dist/fal.d.cts.map +1 -1
  122. package/dist/fal.d.ts +16 -1
  123. package/dist/fal.d.ts.map +1 -1
  124. package/dist/fal.js +28 -5
  125. package/dist/fal.js.map +1 -1
  126. package/dist/files.cjs +1069 -0
  127. package/dist/files.cjs.map +1 -0
  128. package/dist/files.d.cts +68 -0
  129. package/dist/files.d.cts.map +1 -0
  130. package/dist/files.d.ts +68 -0
  131. package/dist/files.d.ts.map +1 -0
  132. package/dist/files.js +1056 -0
  133. package/dist/files.js.map +1 -0
  134. package/dist/fine-tuning.cjs +1033 -0
  135. package/dist/fine-tuning.cjs.map +1 -0
  136. package/dist/fine-tuning.d.cts +96 -0
  137. package/dist/fine-tuning.d.cts.map +1 -0
  138. package/dist/fine-tuning.d.ts +96 -0
  139. package/dist/fine-tuning.d.ts.map +1 -0
  140. package/dist/fine-tuning.js +1026 -0
  141. package/dist/fine-tuning.js.map +1 -0
  142. package/dist/fixture-loader-services.cjs +116 -0
  143. package/dist/fixture-loader-services.cjs.map +1 -0
  144. package/dist/fixture-loader-services.d.cts +48 -0
  145. package/dist/fixture-loader-services.d.cts.map +1 -0
  146. package/dist/fixture-loader-services.d.ts +48 -0
  147. package/dist/fixture-loader-services.d.ts.map +1 -0
  148. package/dist/fixture-loader-services.js +114 -0
  149. package/dist/fixture-loader-services.js.map +1 -0
  150. package/dist/fixture-loader.cjs +0 -0
  151. package/dist/fixture-loader.cjs.map +1 -1
  152. package/dist/fixture-loader.d.cts +131 -5
  153. package/dist/fixture-loader.d.cts.map +1 -1
  154. package/dist/fixture-loader.d.ts +131 -5
  155. package/dist/fixture-loader.d.ts.map +1 -1
  156. package/dist/fixture-loader.js +0 -0
  157. package/dist/fixture-loader.js.map +1 -1
  158. package/dist/gemini-embeddings.cjs +44 -4
  159. package/dist/gemini-embeddings.cjs.map +1 -1
  160. package/dist/gemini-embeddings.js +46 -6
  161. package/dist/gemini-embeddings.js.map +1 -1
  162. package/dist/gemini-interactions.cjs +51 -7
  163. package/dist/gemini-interactions.cjs.map +1 -1
  164. package/dist/gemini-interactions.d.cts.map +1 -1
  165. package/dist/gemini-interactions.d.ts.map +1 -1
  166. package/dist/gemini-interactions.js +53 -9
  167. package/dist/gemini-interactions.js.map +1 -1
  168. package/dist/gemini.cjs +19 -8
  169. package/dist/gemini.cjs.map +1 -1
  170. package/dist/gemini.d.cts.map +1 -1
  171. package/dist/gemini.d.ts.map +1 -1
  172. package/dist/gemini.js +21 -10
  173. package/dist/gemini.js.map +1 -1
  174. package/dist/grok-video.cjs +14 -14
  175. package/dist/grok-video.cjs.map +1 -1
  176. package/dist/grok-video.js +15 -15
  177. package/dist/grok-video.js.map +1 -1
  178. package/dist/helpers.cjs +497 -10
  179. package/dist/helpers.cjs.map +1 -1
  180. package/dist/helpers.d.cts +38 -2
  181. package/dist/helpers.d.cts.map +1 -1
  182. package/dist/helpers.d.ts +38 -2
  183. package/dist/helpers.d.ts.map +1 -1
  184. package/dist/helpers.js +487 -12
  185. package/dist/helpers.js.map +1 -1
  186. package/dist/images.cjs +57 -125
  187. package/dist/images.cjs.map +1 -1
  188. package/dist/images.d.cts.map +1 -1
  189. package/dist/images.d.ts.map +1 -1
  190. package/dist/images.js +58 -126
  191. package/dist/images.js.map +1 -1
  192. package/dist/index.cjs +71 -9
  193. package/dist/index.d.cts +17 -7
  194. package/dist/index.d.ts +17 -7
  195. package/dist/index.js +21 -11
  196. package/dist/jest.cjs +24 -12
  197. package/dist/jest.cjs.map +1 -1
  198. package/dist/jest.d.cts +6 -3
  199. package/dist/jest.d.cts.map +1 -1
  200. package/dist/jest.d.ts +6 -3
  201. package/dist/jest.d.ts.map +1 -1
  202. package/dist/jest.js +25 -13
  203. package/dist/jest.js.map +1 -1
  204. package/dist/journal.cjs +61 -4
  205. package/dist/journal.cjs.map +1 -1
  206. package/dist/journal.d.cts +60 -3
  207. package/dist/journal.d.cts.map +1 -1
  208. package/dist/journal.d.ts +60 -3
  209. package/dist/journal.d.ts.map +1 -1
  210. package/dist/journal.js +61 -5
  211. package/dist/journal.js.map +1 -1
  212. package/dist/live-fixture.cjs +1192 -0
  213. package/dist/live-fixture.cjs.map +1 -0
  214. package/dist/live-fixture.js +1187 -0
  215. package/dist/live-fixture.js.map +1 -0
  216. package/dist/live-recorder.cjs +256 -0
  217. package/dist/live-recorder.cjs.map +1 -0
  218. package/dist/live-recorder.js +254 -0
  219. package/dist/live-recorder.js.map +1 -0
  220. package/dist/live-replay.cjs +286 -0
  221. package/dist/live-replay.cjs.map +1 -0
  222. package/dist/live-replay.js +285 -0
  223. package/dist/live-replay.js.map +1 -0
  224. package/dist/live-sanitize.cjs +196 -0
  225. package/dist/live-sanitize.cjs.map +1 -0
  226. package/dist/live-sanitize.js +196 -0
  227. package/dist/live-sanitize.js.map +1 -0
  228. package/dist/live-types.d.cts +71 -0
  229. package/dist/live-types.d.cts.map +1 -0
  230. package/dist/live-types.d.ts +71 -0
  231. package/dist/live-types.d.ts.map +1 -0
  232. package/dist/llmock.cjs +132 -49
  233. package/dist/llmock.cjs.map +1 -1
  234. package/dist/llmock.d.cts +51 -5
  235. package/dist/llmock.d.cts.map +1 -1
  236. package/dist/llmock.d.ts +51 -5
  237. package/dist/llmock.d.ts.map +1 -1
  238. package/dist/llmock.js +133 -50
  239. package/dist/llmock.js.map +1 -1
  240. package/dist/mcp-fakes-mount.cjs +216 -0
  241. package/dist/mcp-fakes-mount.cjs.map +1 -0
  242. package/dist/mcp-fakes-mount.js +210 -0
  243. package/dist/mcp-fakes-mount.js.map +1 -0
  244. package/dist/mcp-fakes.cjs +1619 -0
  245. package/dist/mcp-fakes.cjs.map +1 -0
  246. package/dist/mcp-fakes.d.cts +101 -0
  247. package/dist/mcp-fakes.d.cts.map +1 -0
  248. package/dist/mcp-fakes.d.ts +101 -0
  249. package/dist/mcp-fakes.d.ts.map +1 -0
  250. package/dist/mcp-fakes.js +1614 -0
  251. package/dist/mcp-fakes.js.map +1 -0
  252. package/dist/mcp-handler.cjs +331 -17
  253. package/dist/mcp-handler.cjs.map +1 -1
  254. package/dist/mcp-handler.js +330 -18
  255. package/dist/mcp-handler.js.map +1 -1
  256. package/dist/mcp-mock.cjs +217 -49
  257. package/dist/mcp-mock.cjs.map +1 -1
  258. package/dist/mcp-mock.d.cts +66 -1
  259. package/dist/mcp-mock.d.cts.map +1 -1
  260. package/dist/mcp-mock.d.ts +66 -1
  261. package/dist/mcp-mock.d.ts.map +1 -1
  262. package/dist/mcp-mock.js +218 -50
  263. package/dist/mcp-mock.js.map +1 -1
  264. package/dist/mcp-stub.cjs +5 -1
  265. package/dist/mcp-stub.d.cts +3 -1
  266. package/dist/mcp-stub.d.ts +3 -1
  267. package/dist/mcp-stub.js +3 -1
  268. package/dist/mcp-types.d.cts +6 -0
  269. package/dist/mcp-types.d.ts +6 -0
  270. package/dist/message-text.cjs +192 -0
  271. package/dist/message-text.cjs.map +1 -0
  272. package/dist/message-text.d.cts +31 -0
  273. package/dist/message-text.d.cts.map +1 -0
  274. package/dist/message-text.d.ts +31 -0
  275. package/dist/message-text.d.ts.map +1 -0
  276. package/dist/message-text.js +184 -0
  277. package/dist/message-text.js.map +1 -0
  278. package/dist/messages.cjs +49 -7
  279. package/dist/messages.cjs.map +1 -1
  280. package/dist/messages.d.cts.map +1 -1
  281. package/dist/messages.d.ts.map +1 -1
  282. package/dist/messages.js +51 -9
  283. package/dist/messages.js.map +1 -1
  284. package/dist/metrics.cjs +278 -45
  285. package/dist/metrics.cjs.map +1 -1
  286. package/dist/metrics.d.cts +17 -7
  287. package/dist/metrics.d.cts.map +1 -1
  288. package/dist/metrics.d.ts +17 -7
  289. package/dist/metrics.d.ts.map +1 -1
  290. package/dist/metrics.js +270 -29
  291. package/dist/metrics.js.map +1 -1
  292. package/dist/moderation.cjs +10 -1
  293. package/dist/moderation.cjs.map +1 -1
  294. package/dist/moderation.d.cts.map +1 -1
  295. package/dist/moderation.d.ts.map +1 -1
  296. package/dist/moderation.js +10 -1
  297. package/dist/moderation.js.map +1 -1
  298. package/dist/ollama.cjs +10 -6
  299. package/dist/ollama.cjs.map +1 -1
  300. package/dist/ollama.d.cts.map +1 -1
  301. package/dist/ollama.d.ts.map +1 -1
  302. package/dist/ollama.js +11 -7
  303. package/dist/ollama.js.map +1 -1
  304. package/dist/openapi.cjs +3405 -0
  305. package/dist/openapi.cjs.map +1 -0
  306. package/dist/openapi.d.cts +30 -0
  307. package/dist/openapi.d.cts.map +1 -0
  308. package/dist/openapi.d.ts +30 -0
  309. package/dist/openapi.d.ts.map +1 -0
  310. package/dist/openapi.js +3404 -0
  311. package/dist/openapi.js.map +1 -0
  312. package/dist/openrouter-video.cjs +9 -9
  313. package/dist/openrouter-video.cjs.map +1 -1
  314. package/dist/openrouter-video.d.cts.map +1 -1
  315. package/dist/openrouter-video.d.ts.map +1 -1
  316. package/dist/openrouter-video.js +10 -10
  317. package/dist/openrouter-video.js.map +1 -1
  318. package/dist/recorder.cjs +54 -10
  319. package/dist/recorder.cjs.map +1 -1
  320. package/dist/recorder.d.cts.map +1 -1
  321. package/dist/recorder.d.ts.map +1 -1
  322. package/dist/recorder.js +52 -12
  323. package/dist/recorder.js.map +1 -1
  324. package/dist/responses.cjs +47 -7
  325. package/dist/responses.cjs.map +1 -1
  326. package/dist/responses.d.cts.map +1 -1
  327. package/dist/responses.d.ts.map +1 -1
  328. package/dist/responses.js +48 -9
  329. package/dist/responses.js.map +1 -1
  330. package/dist/route-registry.cjs +951 -0
  331. package/dist/route-registry.cjs.map +1 -0
  332. package/dist/route-registry.d.cts +33 -0
  333. package/dist/route-registry.d.cts.map +1 -0
  334. package/dist/route-registry.d.ts +33 -0
  335. package/dist/route-registry.d.ts.map +1 -0
  336. package/dist/route-registry.js +866 -0
  337. package/dist/route-registry.js.map +1 -0
  338. package/dist/router.cjs +21 -34
  339. package/dist/router.cjs.map +1 -1
  340. package/dist/router.d.cts +4 -1
  341. package/dist/router.d.cts.map +1 -1
  342. package/dist/router.d.ts +4 -1
  343. package/dist/router.d.ts.map +1 -1
  344. package/dist/router.js +21 -35
  345. package/dist/router.js.map +1 -1
  346. package/dist/server.cjs +1252 -777
  347. package/dist/server.cjs.map +1 -1
  348. package/dist/server.d.cts +5 -1
  349. package/dist/server.d.cts.map +1 -1
  350. package/dist/server.d.ts +5 -1
  351. package/dist/server.d.ts.map +1 -1
  352. package/dist/server.js +1185 -710
  353. package/dist/server.js.map +1 -1
  354. package/dist/speech.cjs +6 -4
  355. package/dist/speech.cjs.map +1 -1
  356. package/dist/speech.js +7 -5
  357. package/dist/speech.js.map +1 -1
  358. package/dist/suite.cjs +1 -1
  359. package/dist/suite.js +1 -1
  360. package/dist/transcription.cjs +6 -6
  361. package/dist/transcription.cjs.map +1 -1
  362. package/dist/transcription.d.cts.map +1 -1
  363. package/dist/transcription.d.ts.map +1 -1
  364. package/dist/transcription.js +7 -7
  365. package/dist/transcription.js.map +1 -1
  366. package/dist/types.cjs +18 -0
  367. package/dist/types.cjs.map +1 -0
  368. package/dist/types.d.cts +320 -17
  369. package/dist/types.d.cts.map +1 -1
  370. package/dist/types.d.ts +320 -17
  371. package/dist/types.d.ts.map +1 -1
  372. package/dist/types.js +17 -0
  373. package/dist/types.js.map +1 -0
  374. package/dist/validate-cli.cjs +983 -0
  375. package/dist/validate-cli.cjs.map +1 -0
  376. package/dist/validate-cli.js +982 -0
  377. package/dist/validate-cli.js.map +1 -0
  378. package/dist/vector-stores.cjs +1180 -0
  379. package/dist/vector-stores.cjs.map +1 -0
  380. package/dist/vector-stores.d.cts +106 -0
  381. package/dist/vector-stores.d.cts.map +1 -0
  382. package/dist/vector-stores.d.ts +106 -0
  383. package/dist/vector-stores.d.ts.map +1 -0
  384. package/dist/vector-stores.js +1164 -0
  385. package/dist/vector-stores.js.map +1 -0
  386. package/dist/vector-types.d.cts.map +1 -1
  387. package/dist/vector-types.d.ts.map +1 -1
  388. package/dist/veo-video.cjs +13 -13
  389. package/dist/veo-video.cjs.map +1 -1
  390. package/dist/veo-video.js +14 -14
  391. package/dist/veo-video.js.map +1 -1
  392. package/dist/video.cjs +23 -4
  393. package/dist/video.cjs.map +1 -1
  394. package/dist/video.d.cts +1 -1
  395. package/dist/video.d.cts.map +1 -1
  396. package/dist/video.d.ts +1 -1
  397. package/dist/video.d.ts.map +1 -1
  398. package/dist/video.js +24 -5
  399. package/dist/video.js.map +1 -1
  400. package/dist/vitest.cjs +23 -11
  401. package/dist/vitest.cjs.map +1 -1
  402. package/dist/vitest.d.cts +5 -2
  403. package/dist/vitest.d.cts.map +1 -1
  404. package/dist/vitest.d.ts +5 -2
  405. package/dist/vitest.d.ts.map +1 -1
  406. package/dist/vitest.js +25 -13
  407. package/dist/vitest.js.map +1 -1
  408. package/dist/watcher.cjs +2 -1
  409. package/dist/watcher.cjs.map +1 -1
  410. package/dist/watcher.js +2 -1
  411. package/dist/watcher.js.map +1 -1
  412. package/dist/ws-framing.cjs +150 -35
  413. package/dist/ws-framing.cjs.map +1 -1
  414. package/dist/ws-framing.d.cts +18 -3
  415. package/dist/ws-framing.d.cts.map +1 -1
  416. package/dist/ws-framing.d.ts +18 -3
  417. package/dist/ws-framing.d.ts.map +1 -1
  418. package/dist/ws-framing.js +151 -36
  419. package/dist/ws-framing.js.map +1 -1
  420. package/dist/ws-gemini-live.cjs +2 -2
  421. package/dist/ws-gemini-live.js +2 -2
  422. package/dist/ws-live.cjs +401 -0
  423. package/dist/ws-live.cjs.map +1 -0
  424. package/dist/ws-live.js +400 -0
  425. package/dist/ws-live.js.map +1 -0
  426. package/dist/ws-realtime.cjs +46 -2
  427. package/dist/ws-realtime.cjs.map +1 -1
  428. package/dist/ws-realtime.d.cts.map +1 -1
  429. package/dist/ws-realtime.d.ts.map +1 -1
  430. package/dist/ws-realtime.js +46 -2
  431. package/dist/ws-realtime.js.map +1 -1
  432. package/dist/ws-responses.cjs +16 -4
  433. package/dist/ws-responses.cjs.map +1 -1
  434. package/dist/ws-responses.js +16 -4
  435. package/dist/ws-responses.js.map +1 -1
  436. package/dist/ws-upstream.cjs +118 -0
  437. package/dist/ws-upstream.cjs.map +1 -0
  438. package/dist/ws-upstream.js +115 -0
  439. package/dist/ws-upstream.js.map +1 -0
  440. package/fixtures/openai-live-client.json +390 -0
  441. package/fixtures/openai-live-managed.json +709 -0
  442. package/package.json +4 -1
@@ -0,0 +1,216 @@
1
+ const require_message_text = require('./message-text.cjs');
2
+ const require_fixture_loader = require('./fixture-loader.cjs');
3
+ const require_mcp_fakes = require('./mcp-fakes.cjs');
4
+ const require_mcp_mock = require('./mcp-mock.cjs');
5
+
6
+ //#region src/mcp-fakes-mount.ts
7
+ /**
8
+ * The control-API prefix. The server hands every request whose path starts
9
+ * with it to the control API, before any mount, so no mount under it is
10
+ * reachable.
11
+ */
12
+ const CONTROL_PREFIX = "/__aimock";
13
+ /** The handlers an auto-mount created (W3, W6), whichever path created them. */
14
+ const autoMounts = /* @__PURE__ */ new WeakSet();
15
+ /** `handler` was auto-mounted for MCP fakes (at start, after start or by the control API). */
16
+ function isAutoMount(handler) {
17
+ return autoMounts.has(handler);
18
+ }
19
+ /** A mount "is an MCPMock" for fakes if and only if it implements `addMcpFakes`. */
20
+ function isFakeMount(handler) {
21
+ return typeof handler.addMcpFakes === "function";
22
+ }
23
+ /**
24
+ * The mount that serves `path`, in dispatch order: an MCPMock at exactly
25
+ * `path`; a conflict when the path is under the control prefix, when a mount
26
+ * that is not an MCPMock serves it first (it would shadow the fakes), or when
27
+ * an MCPMock at `path` without its trailing "/" answers it as its root; none
28
+ * when no mount serves it. An MCPMock mounted at a prefix of `path` lets any
29
+ * other sub-path fall through, so it is skipped. `pending` are the paths an
30
+ * add will auto-mount before this one, in order (they follow `mounts`).
31
+ */
32
+ function findFakeMount(mounts, path, pending = []) {
33
+ if (path.startsWith(CONTROL_PREFIX)) return {
34
+ kind: "conflict",
35
+ mountPath: CONTROL_PREFIX,
36
+ reason: "control"
37
+ };
38
+ for (const { path: mountPath, handler } of mounts) {
39
+ if (path !== mountPath && !path.startsWith(mountPath + "/")) continue;
40
+ if (!isFakeMount(handler)) return {
41
+ kind: "conflict",
42
+ mountPath,
43
+ reason: "not-mcp"
44
+ };
45
+ if (mountPath === path) return {
46
+ kind: "mcp",
47
+ handler
48
+ };
49
+ if (path === mountPath + "/") return {
50
+ kind: "conflict",
51
+ mountPath,
52
+ reason: "mcp-root"
53
+ };
54
+ }
55
+ for (const mountPath of pending) if (path === mountPath + "/") return {
56
+ kind: "conflict",
57
+ mountPath,
58
+ reason: "mcp-root"
59
+ };
60
+ return { kind: "none" };
61
+ }
62
+ /** Why the fakes for `path` cannot be served there (no location prefix). */
63
+ function mountConflictDetail(path, target) {
64
+ switch (target.reason) {
65
+ case "control": return require_message_text.msg`mount ${require_message_text.quote(path)} is under the control API prefix ${require_message_text.quote(target.mountPath)}, which takes its requests first, so its fakes cannot be served`;
66
+ case "mcp-root": return require_message_text.msg`mount ${require_message_text.quote(path)} is answered by the MCP mock at ${require_message_text.quote(target.mountPath)} as its root, so its fakes cannot be served`;
67
+ case "not-mcp": return require_message_text.msg`mount ${require_message_text.quote(path)} is served by ${require_message_text.quote(target.mountPath)}, which is not an MCP mock, so its fakes cannot be served`;
68
+ }
69
+ }
70
+ /** Wire, push and log (L4, L5) an auto-mounted MCPMock. */
71
+ function pushAutoMount(mounts, path, handler, wiring, logger) {
72
+ if (wiring.journal) handler.setJournal(wiring.journal);
73
+ if (wiring.registry) handler.setRegistry(wiring.registry);
74
+ if (wiring.logger) handler.setLogger(wiring.logger);
75
+ const others = mounts.filter((m) => m.path !== path && isFakeMount(m.handler));
76
+ mounts.push({
77
+ path,
78
+ handler
79
+ });
80
+ autoMounts.add(handler);
81
+ logger.info(require_message_text.build(require_message_text.msg`MCP fakes: auto-mounted an MCP mock at ${require_message_text.quote(path)}`));
82
+ for (const other of others) logger.warn(require_message_text.build(require_message_text.msg`MCP fakes: auto-mounted an MCP mock at ${require_message_text.quote(path)} while an MCP mock is mounted at ${require_message_text.quote(other.path)}; fakes for one path do not reach the other`));
83
+ }
84
+ /**
85
+ * The MCPMock for `path`: the one already mounted there, or a new one pushed
86
+ * onto `mounts` (auto-mount, logs L4 and L5). A conflict is returned, not
87
+ * thrown, so the caller can name the offending block.
88
+ */
89
+ function ensureFakeMount(mounts, path, wiring, logger) {
90
+ const target = findFakeMount(mounts, path);
91
+ if (target.kind === "conflict") return { conflict: target.mountPath };
92
+ if (target.kind === "mcp") return {
93
+ handler: target.handler,
94
+ created: false
95
+ };
96
+ const handler = new require_mcp_mock.MCPMock();
97
+ pushAutoMount(mounts, path, handler, wiring, logger);
98
+ return {
99
+ handler,
100
+ created: true
101
+ };
102
+ }
103
+ /** A block's `mount` path, read without running getters; the add validates it. */
104
+ function mountOf(raw) {
105
+ if (typeof raw !== "object" || raw === null) return require_mcp_fakes.MCP_FAKES_DEFAULT_MOUNT;
106
+ const value = Object.getOwnPropertyDescriptor(raw, "mount")?.value;
107
+ return typeof value === "string" ? value : require_mcp_fakes.MCP_FAKES_DEFAULT_MOUNT;
108
+ }
109
+ /** The mount-conflict error for one file-origin block. */
110
+ function mountConflictError(block, path, target) {
111
+ return new require_fixture_loader.FixtureLoadError({
112
+ rule: "mcp-fakes/mount-conflict",
113
+ file: block.source,
114
+ blockId: require_mcp_fakes.blockIdOf(block.source, "", block.blockIndex),
115
+ entryId: null,
116
+ detail: mountConflictDetail(path, target)
117
+ });
118
+ }
119
+ /**
120
+ * Check file-origin `blocks` against `mounts` without changing anything:
121
+ * group them by `mount` path (default `/mcp`), find each group's mount (a
122
+ * mount conflict is one error per block), and add each group to a throwaway
123
+ * store (bad blocks and id collisions inside the input). Throws one
124
+ * `McpFakesAddError` holding every error. With `checked`, only the groups
125
+ * that hold a checked block are checked, and only checked blocks get a
126
+ * mount-conflict error (the others were checked before).
127
+ */
128
+ function planFileFakes(mounts, blocks, checked) {
129
+ const groups = /* @__PURE__ */ new Map();
130
+ for (const block of blocks) {
131
+ const path = mountOf(block.raw);
132
+ const group = groups.get(path);
133
+ if (group) group.push(block);
134
+ else groups.set(path, [block]);
135
+ }
136
+ const errors = [];
137
+ const warnings = [];
138
+ const plan = {
139
+ onExisting: [],
140
+ onNew: [],
141
+ warnings
142
+ };
143
+ for (const [path, group] of groups) {
144
+ if (checked && !group.some((block) => checked.has(block))) continue;
145
+ const target = findFakeMount(mounts, path, plan.onNew.map((g) => g.path));
146
+ if (target.kind === "conflict") {
147
+ for (const block of group) if (!checked || checked.has(block)) errors.push(mountConflictError(block, path, target));
148
+ continue;
149
+ }
150
+ try {
151
+ warnings.push(...new require_mcp_fakes.McpFakeStore().add(group, { kind: "file" }).warnings);
152
+ } catch (err) {
153
+ if (!(err instanceof require_mcp_fakes.McpFakesAddError)) throw err;
154
+ errors.push(...err.errors);
155
+ warnings.push(...err.warnings);
156
+ continue;
157
+ }
158
+ if (target.kind === "mcp") plan.onExisting.push({
159
+ handler: target.handler,
160
+ group
161
+ });
162
+ else plan.onNew.push({
163
+ path,
164
+ group
165
+ });
166
+ }
167
+ if (errors.length > 0) throw new require_mcp_fakes.McpFakesAddError(errors, warnings);
168
+ return plan;
169
+ }
170
+ /**
171
+ * Start-time hand-off (F2): check every block first (`planFileFakes`; on any
172
+ * error, a bad block, an id collision or a mount conflict, throws one
173
+ * `McpFakesAddError` holding every error and changes nothing). Then the
174
+ * groups for paths no mount serves are added to new MCPMocks, pushed onto
175
+ * `mounts` (auto-mount, L4, L5) so the mount loop wires them. The groups for
176
+ * an MCPMock already mounted are added by `commit`, once the server listens,
177
+ * so a start that fails after the hand-off leaves those mounts untouched;
178
+ * `undo` removes the auto-mounts. The add warnings (L8) are logged at `warn`.
179
+ */
180
+ function handOffMcpFakes(mounts, blocks, logger) {
181
+ const plan = planFileFakes(mounts, blocks);
182
+ const created = [];
183
+ for (const { path, group } of plan.onNew) {
184
+ const handler = new require_mcp_mock.MCPMock();
185
+ for (const warning of handler.addMcpFakes(group, { kind: "file" }).warnings) logger.warn(warning.message);
186
+ created.push({
187
+ path,
188
+ handler
189
+ });
190
+ }
191
+ for (const { path, handler } of created) pushAutoMount(mounts, path, handler, {}, logger);
192
+ return {
193
+ commit() {
194
+ for (const { handler, group } of plan.onExisting) {
195
+ const result = handler.addMcpFakes?.(group, { kind: "file" });
196
+ for (const warning of result?.warnings ?? []) logger.warn(warning.message);
197
+ }
198
+ },
199
+ undo() {
200
+ for (const { handler } of created) {
201
+ const at = mounts.findIndex((m) => m.handler === handler);
202
+ if (at !== -1) mounts.splice(at, 1);
203
+ }
204
+ }
205
+ };
206
+ }
207
+
208
+ //#endregion
209
+ exports.ensureFakeMount = ensureFakeMount;
210
+ exports.findFakeMount = findFakeMount;
211
+ exports.handOffMcpFakes = handOffMcpFakes;
212
+ exports.isAutoMount = isAutoMount;
213
+ exports.isFakeMount = isFakeMount;
214
+ exports.mountConflictDetail = mountConflictDetail;
215
+ exports.planFileFakes = planFileFakes;
216
+ //# sourceMappingURL=mcp-fakes-mount.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-fakes-mount.cjs","names":["quote","build","MCPMock","MCP_FAKES_DEFAULT_MOUNT","FixtureLoadError","blockIdOf","McpFakeStore","McpFakesAddError"],"sources":["../src/mcp-fakes-mount.ts"],"sourcesContent":["/**\n * Where MCP fakes go on a server (spec 5.2): find the mount that serves a\n * block's `mount` path, auto-mount an `MCPMock` when none does (W3, W6), and\n * the start-time hand-off of `ServiceFixtures.mcpFakes` (F2).\n */\nimport type { Journal } from \"./journal.js\";\nimport type { Logger } from \"./logger.js\";\nimport type { MetricsRegistry } from \"./metrics.js\";\nimport type { McpFakeSource, Mountable } from \"./types.js\";\nimport { FixtureLoadError } from \"./fixture-loader.js\";\nimport { MCPMock } from \"./mcp-mock.js\";\nimport {\n MCP_FAKES_DEFAULT_MOUNT,\n McpFakeStore,\n McpFakesAddError,\n blockIdOf,\n type McpFakeIssue,\n} from \"./mcp-fakes.js\";\nimport { build, msg, quote, type Message } from \"./message-text.js\";\n\nexport type MountList = Array<{ path: string; handler: Mountable }>;\n\n/** Dependencies an auto-mount gets when it is created (after start: W4 wiring). */\nexport interface FakeMountWiring {\n journal?: Journal;\n registry?: MetricsRegistry;\n logger?: Logger;\n}\n\n/**\n * The control-API prefix. The server hands every request whose path starts\n * with it to the control API, before any mount, so no mount under it is\n * reachable.\n */\nexport const CONTROL_PREFIX = \"/__aimock\";\n\n/**\n * What serves a block's `mount` path. A conflict is a path no fake there can\n * be served at: a mount that is not an MCPMock serves it first (`not-mcp`), an\n * MCPMock mounted at `mountPath` answers it as its root (`mcp-root`, the path\n * is `mountPath` plus a trailing \"/\"), or it is under the control prefix\n * (`control`).\n */\nexport type FakeMountTarget =\n | { kind: \"mcp\"; handler: Mountable }\n | { kind: \"conflict\"; mountPath: string; reason: \"not-mcp\" | \"mcp-root\" | \"control\" }\n | { kind: \"none\" };\n\n/** The handlers an auto-mount created (W3, W6), whichever path created them. */\nconst autoMounts = new WeakSet<Mountable>();\n\n/** `handler` was auto-mounted for MCP fakes (at start, after start or by the control API). */\nexport function isAutoMount(handler: Mountable): boolean {\n return autoMounts.has(handler);\n}\n\n/** A mount \"is an MCPMock\" for fakes if and only if it implements `addMcpFakes`. */\nexport function isFakeMount(handler: Mountable): boolean {\n return typeof handler.addMcpFakes === \"function\";\n}\n\n/**\n * The mount that serves `path`, in dispatch order: an MCPMock at exactly\n * `path`; a conflict when the path is under the control prefix, when a mount\n * that is not an MCPMock serves it first (it would shadow the fakes), or when\n * an MCPMock at `path` without its trailing \"/\" answers it as its root; none\n * when no mount serves it. An MCPMock mounted at a prefix of `path` lets any\n * other sub-path fall through, so it is skipped. `pending` are the paths an\n * add will auto-mount before this one, in order (they follow `mounts`).\n */\nexport function findFakeMount(\n mounts: MountList,\n path: string,\n pending: readonly string[] = [],\n): FakeMountTarget {\n if (path.startsWith(CONTROL_PREFIX)) {\n return { kind: \"conflict\", mountPath: CONTROL_PREFIX, reason: \"control\" };\n }\n for (const { path: mountPath, handler } of mounts) {\n if (path !== mountPath && !path.startsWith(mountPath + \"/\")) continue;\n if (!isFakeMount(handler)) return { kind: \"conflict\", mountPath, reason: \"not-mcp\" };\n if (mountPath === path) return { kind: \"mcp\", handler };\n if (path === mountPath + \"/\") return { kind: \"conflict\", mountPath, reason: \"mcp-root\" };\n }\n for (const mountPath of pending) {\n if (path === mountPath + \"/\") return { kind: \"conflict\", mountPath, reason: \"mcp-root\" };\n }\n return { kind: \"none\" };\n}\n\n/** Why the fakes for `path` cannot be served there (no location prefix). */\nexport function mountConflictDetail(\n path: string,\n target: Extract<FakeMountTarget, { kind: \"conflict\" }>,\n): Message {\n switch (target.reason) {\n case \"control\":\n return msg`mount ${quote(path)} is under the control API prefix ${quote(target.mountPath)}, which takes its requests first, so its fakes cannot be served`;\n case \"mcp-root\":\n return msg`mount ${quote(path)} is answered by the MCP mock at ${quote(target.mountPath)} as its root, so its fakes cannot be served`;\n case \"not-mcp\":\n return msg`mount ${quote(path)} is served by ${quote(target.mountPath)}, which is not an MCP mock, so its fakes cannot be served`;\n }\n}\n\n/** Wire, push and log (L4, L5) an auto-mounted MCPMock. */\nfunction pushAutoMount(\n mounts: MountList,\n path: string,\n handler: MCPMock,\n wiring: FakeMountWiring,\n logger: Logger,\n): void {\n if (wiring.journal) handler.setJournal(wiring.journal);\n if (wiring.registry) handler.setRegistry(wiring.registry);\n if (wiring.logger) handler.setLogger(wiring.logger);\n const others = mounts.filter((m) => m.path !== path && isFakeMount(m.handler));\n mounts.push({ path, handler });\n autoMounts.add(handler);\n logger.info(build(msg`MCP fakes: auto-mounted an MCP mock at ${quote(path)}`));\n for (const other of others) {\n logger.warn(\n build(\n msg`MCP fakes: auto-mounted an MCP mock at ${quote(path)} while an MCP mock is mounted at ${quote(other.path)}; fakes for one path do not reach the other`,\n ),\n );\n }\n}\n\n/**\n * The MCPMock for `path`: the one already mounted there, or a new one pushed\n * onto `mounts` (auto-mount, logs L4 and L5). A conflict is returned, not\n * thrown, so the caller can name the offending block.\n */\nexport function ensureFakeMount(\n mounts: MountList,\n path: string,\n wiring: FakeMountWiring,\n logger: Logger,\n): { handler: Mountable; created: boolean } | { conflict: string } {\n const target = findFakeMount(mounts, path);\n if (target.kind === \"conflict\") return { conflict: target.mountPath };\n if (target.kind === \"mcp\") return { handler: target.handler, created: false };\n const handler = new MCPMock();\n pushAutoMount(mounts, path, handler, wiring, logger);\n return { handler, created: true };\n}\n\n/** A block's `mount` path, read without running getters; the add validates it. */\nfunction mountOf(raw: unknown): string {\n if (typeof raw !== \"object\" || raw === null) return MCP_FAKES_DEFAULT_MOUNT;\n const value: unknown = Object.getOwnPropertyDescriptor(raw, \"mount\")?.value;\n return typeof value === \"string\" ? value : MCP_FAKES_DEFAULT_MOUNT;\n}\n\n/** The mount-conflict error for one file-origin block. */\nexport function mountConflictError(\n block: McpFakeSource,\n path: string,\n target: Extract<FakeMountTarget, { kind: \"conflict\" }>,\n): FixtureLoadError {\n return new FixtureLoadError({\n rule: \"mcp-fakes/mount-conflict\",\n file: block.source,\n blockId: blockIdOf(block.source, \"\", block.blockIndex),\n entryId: null,\n detail: mountConflictDetail(path, target),\n });\n}\n\n/** Where a checked add of file blocks goes: each group by its `mount` path. */\nexport interface FileFakesPlan {\n /** Groups for a path an MCPMock already serves, with that mount. */\n onExisting: Array<{ handler: Mountable; group: McpFakeSource[] }>;\n /** Groups for a path no mount serves, in order: each gets an auto-mount. */\n onNew: Array<{ path: string; group: McpFakeSource[] }>;\n /** The add warnings (L8), as a throwaway store reports them. */\n warnings: McpFakeIssue[];\n}\n\n/**\n * Check file-origin `blocks` against `mounts` without changing anything:\n * group them by `mount` path (default `/mcp`), find each group's mount (a\n * mount conflict is one error per block), and add each group to a throwaway\n * store (bad blocks and id collisions inside the input). Throws one\n * `McpFakesAddError` holding every error. With `checked`, only the groups\n * that hold a checked block are checked, and only checked blocks get a\n * mount-conflict error (the others were checked before).\n */\nexport function planFileFakes(\n mounts: MountList,\n blocks: readonly McpFakeSource[],\n checked?: ReadonlySet<McpFakeSource>,\n): FileFakesPlan {\n const groups = new Map<string, McpFakeSource[]>();\n for (const block of blocks) {\n const path = mountOf(block.raw);\n const group = groups.get(path);\n if (group) group.push(block);\n else groups.set(path, [block]);\n }\n\n const errors: FixtureLoadError[] = [];\n const warnings: McpFakeIssue[] = [];\n const plan: FileFakesPlan = { onExisting: [], onNew: [], warnings };\n for (const [path, group] of groups) {\n if (checked && !group.some((block) => checked.has(block))) continue;\n const target = findFakeMount(\n mounts,\n path,\n plan.onNew.map((g) => g.path),\n );\n if (target.kind === \"conflict\") {\n for (const block of group) {\n if (!checked || checked.has(block)) errors.push(mountConflictError(block, path, target));\n }\n continue;\n }\n try {\n warnings.push(...new McpFakeStore().add(group, { kind: \"file\" }).warnings);\n } catch (err) {\n if (!(err instanceof McpFakesAddError)) throw err;\n errors.push(...err.errors);\n warnings.push(...err.warnings);\n continue;\n }\n if (target.kind === \"mcp\") plan.onExisting.push({ handler: target.handler, group });\n else plan.onNew.push({ path, group });\n }\n if (errors.length > 0) throw new McpFakesAddError(errors, warnings);\n return plan;\n}\n\n/** A start-time hand-off: the auto-mounts are in place, the rest waits for `commit`. */\nexport interface McpFakesHandOff {\n /** Add the groups for existing MCPMocks (after the server listens). */\n commit(): void;\n /** Take the auto-mounts back off the mounts array (start failed). */\n undo(): void;\n}\n\n/**\n * Start-time hand-off (F2): check every block first (`planFileFakes`; on any\n * error, a bad block, an id collision or a mount conflict, throws one\n * `McpFakesAddError` holding every error and changes nothing). Then the\n * groups for paths no mount serves are added to new MCPMocks, pushed onto\n * `mounts` (auto-mount, L4, L5) so the mount loop wires them. The groups for\n * an MCPMock already mounted are added by `commit`, once the server listens,\n * so a start that fails after the hand-off leaves those mounts untouched;\n * `undo` removes the auto-mounts. The add warnings (L8) are logged at `warn`.\n */\nexport function handOffMcpFakes(\n mounts: MountList,\n blocks: McpFakeSource[],\n logger: Logger,\n): McpFakesHandOff {\n const plan = planFileFakes(mounts, blocks);\n const created: Array<{ path: string; handler: MCPMock }> = [];\n for (const { path, group } of plan.onNew) {\n const handler = new MCPMock();\n for (const warning of handler.addMcpFakes(group, { kind: \"file\" }).warnings) {\n logger.warn(warning.message);\n }\n created.push({ path, handler });\n }\n for (const { path, handler } of created) pushAutoMount(mounts, path, handler, {}, logger);\n return {\n commit(): void {\n for (const { handler, group } of plan.onExisting) {\n const result = handler.addMcpFakes?.(group, { kind: \"file\" });\n for (const warning of result?.warnings ?? []) logger.warn(warning.message);\n }\n },\n undo(): void {\n for (const { handler } of created) {\n const at = mounts.findIndex((m) => m.handler === handler);\n if (at !== -1) mounts.splice(at, 1);\n }\n },\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,210 @@
1
+ import { build, msg, quote } from "./message-text.js";
2
+ import { FixtureLoadError } from "./fixture-loader.js";
3
+ import { MCP_FAKES_DEFAULT_MOUNT, McpFakeStore, McpFakesAddError, blockIdOf } from "./mcp-fakes.js";
4
+ import { MCPMock } from "./mcp-mock.js";
5
+
6
+ //#region src/mcp-fakes-mount.ts
7
+ /**
8
+ * The control-API prefix. The server hands every request whose path starts
9
+ * with it to the control API, before any mount, so no mount under it is
10
+ * reachable.
11
+ */
12
+ const CONTROL_PREFIX = "/__aimock";
13
+ /** The handlers an auto-mount created (W3, W6), whichever path created them. */
14
+ const autoMounts = /* @__PURE__ */ new WeakSet();
15
+ /** `handler` was auto-mounted for MCP fakes (at start, after start or by the control API). */
16
+ function isAutoMount(handler) {
17
+ return autoMounts.has(handler);
18
+ }
19
+ /** A mount "is an MCPMock" for fakes if and only if it implements `addMcpFakes`. */
20
+ function isFakeMount(handler) {
21
+ return typeof handler.addMcpFakes === "function";
22
+ }
23
+ /**
24
+ * The mount that serves `path`, in dispatch order: an MCPMock at exactly
25
+ * `path`; a conflict when the path is under the control prefix, when a mount
26
+ * that is not an MCPMock serves it first (it would shadow the fakes), or when
27
+ * an MCPMock at `path` without its trailing "/" answers it as its root; none
28
+ * when no mount serves it. An MCPMock mounted at a prefix of `path` lets any
29
+ * other sub-path fall through, so it is skipped. `pending` are the paths an
30
+ * add will auto-mount before this one, in order (they follow `mounts`).
31
+ */
32
+ function findFakeMount(mounts, path, pending = []) {
33
+ if (path.startsWith(CONTROL_PREFIX)) return {
34
+ kind: "conflict",
35
+ mountPath: CONTROL_PREFIX,
36
+ reason: "control"
37
+ };
38
+ for (const { path: mountPath, handler } of mounts) {
39
+ if (path !== mountPath && !path.startsWith(mountPath + "/")) continue;
40
+ if (!isFakeMount(handler)) return {
41
+ kind: "conflict",
42
+ mountPath,
43
+ reason: "not-mcp"
44
+ };
45
+ if (mountPath === path) return {
46
+ kind: "mcp",
47
+ handler
48
+ };
49
+ if (path === mountPath + "/") return {
50
+ kind: "conflict",
51
+ mountPath,
52
+ reason: "mcp-root"
53
+ };
54
+ }
55
+ for (const mountPath of pending) if (path === mountPath + "/") return {
56
+ kind: "conflict",
57
+ mountPath,
58
+ reason: "mcp-root"
59
+ };
60
+ return { kind: "none" };
61
+ }
62
+ /** Why the fakes for `path` cannot be served there (no location prefix). */
63
+ function mountConflictDetail(path, target) {
64
+ switch (target.reason) {
65
+ case "control": return msg`mount ${quote(path)} is under the control API prefix ${quote(target.mountPath)}, which takes its requests first, so its fakes cannot be served`;
66
+ case "mcp-root": return msg`mount ${quote(path)} is answered by the MCP mock at ${quote(target.mountPath)} as its root, so its fakes cannot be served`;
67
+ case "not-mcp": return msg`mount ${quote(path)} is served by ${quote(target.mountPath)}, which is not an MCP mock, so its fakes cannot be served`;
68
+ }
69
+ }
70
+ /** Wire, push and log (L4, L5) an auto-mounted MCPMock. */
71
+ function pushAutoMount(mounts, path, handler, wiring, logger) {
72
+ if (wiring.journal) handler.setJournal(wiring.journal);
73
+ if (wiring.registry) handler.setRegistry(wiring.registry);
74
+ if (wiring.logger) handler.setLogger(wiring.logger);
75
+ const others = mounts.filter((m) => m.path !== path && isFakeMount(m.handler));
76
+ mounts.push({
77
+ path,
78
+ handler
79
+ });
80
+ autoMounts.add(handler);
81
+ logger.info(build(msg`MCP fakes: auto-mounted an MCP mock at ${quote(path)}`));
82
+ for (const other of others) logger.warn(build(msg`MCP fakes: auto-mounted an MCP mock at ${quote(path)} while an MCP mock is mounted at ${quote(other.path)}; fakes for one path do not reach the other`));
83
+ }
84
+ /**
85
+ * The MCPMock for `path`: the one already mounted there, or a new one pushed
86
+ * onto `mounts` (auto-mount, logs L4 and L5). A conflict is returned, not
87
+ * thrown, so the caller can name the offending block.
88
+ */
89
+ function ensureFakeMount(mounts, path, wiring, logger) {
90
+ const target = findFakeMount(mounts, path);
91
+ if (target.kind === "conflict") return { conflict: target.mountPath };
92
+ if (target.kind === "mcp") return {
93
+ handler: target.handler,
94
+ created: false
95
+ };
96
+ const handler = new MCPMock();
97
+ pushAutoMount(mounts, path, handler, wiring, logger);
98
+ return {
99
+ handler,
100
+ created: true
101
+ };
102
+ }
103
+ /** A block's `mount` path, read without running getters; the add validates it. */
104
+ function mountOf(raw) {
105
+ if (typeof raw !== "object" || raw === null) return MCP_FAKES_DEFAULT_MOUNT;
106
+ const value = Object.getOwnPropertyDescriptor(raw, "mount")?.value;
107
+ return typeof value === "string" ? value : MCP_FAKES_DEFAULT_MOUNT;
108
+ }
109
+ /** The mount-conflict error for one file-origin block. */
110
+ function mountConflictError(block, path, target) {
111
+ return new FixtureLoadError({
112
+ rule: "mcp-fakes/mount-conflict",
113
+ file: block.source,
114
+ blockId: blockIdOf(block.source, "", block.blockIndex),
115
+ entryId: null,
116
+ detail: mountConflictDetail(path, target)
117
+ });
118
+ }
119
+ /**
120
+ * Check file-origin `blocks` against `mounts` without changing anything:
121
+ * group them by `mount` path (default `/mcp`), find each group's mount (a
122
+ * mount conflict is one error per block), and add each group to a throwaway
123
+ * store (bad blocks and id collisions inside the input). Throws one
124
+ * `McpFakesAddError` holding every error. With `checked`, only the groups
125
+ * that hold a checked block are checked, and only checked blocks get a
126
+ * mount-conflict error (the others were checked before).
127
+ */
128
+ function planFileFakes(mounts, blocks, checked) {
129
+ const groups = /* @__PURE__ */ new Map();
130
+ for (const block of blocks) {
131
+ const path = mountOf(block.raw);
132
+ const group = groups.get(path);
133
+ if (group) group.push(block);
134
+ else groups.set(path, [block]);
135
+ }
136
+ const errors = [];
137
+ const warnings = [];
138
+ const plan = {
139
+ onExisting: [],
140
+ onNew: [],
141
+ warnings
142
+ };
143
+ for (const [path, group] of groups) {
144
+ if (checked && !group.some((block) => checked.has(block))) continue;
145
+ const target = findFakeMount(mounts, path, plan.onNew.map((g) => g.path));
146
+ if (target.kind === "conflict") {
147
+ for (const block of group) if (!checked || checked.has(block)) errors.push(mountConflictError(block, path, target));
148
+ continue;
149
+ }
150
+ try {
151
+ warnings.push(...new McpFakeStore().add(group, { kind: "file" }).warnings);
152
+ } catch (err) {
153
+ if (!(err instanceof McpFakesAddError)) throw err;
154
+ errors.push(...err.errors);
155
+ warnings.push(...err.warnings);
156
+ continue;
157
+ }
158
+ if (target.kind === "mcp") plan.onExisting.push({
159
+ handler: target.handler,
160
+ group
161
+ });
162
+ else plan.onNew.push({
163
+ path,
164
+ group
165
+ });
166
+ }
167
+ if (errors.length > 0) throw new McpFakesAddError(errors, warnings);
168
+ return plan;
169
+ }
170
+ /**
171
+ * Start-time hand-off (F2): check every block first (`planFileFakes`; on any
172
+ * error, a bad block, an id collision or a mount conflict, throws one
173
+ * `McpFakesAddError` holding every error and changes nothing). Then the
174
+ * groups for paths no mount serves are added to new MCPMocks, pushed onto
175
+ * `mounts` (auto-mount, L4, L5) so the mount loop wires them. The groups for
176
+ * an MCPMock already mounted are added by `commit`, once the server listens,
177
+ * so a start that fails after the hand-off leaves those mounts untouched;
178
+ * `undo` removes the auto-mounts. The add warnings (L8) are logged at `warn`.
179
+ */
180
+ function handOffMcpFakes(mounts, blocks, logger) {
181
+ const plan = planFileFakes(mounts, blocks);
182
+ const created = [];
183
+ for (const { path, group } of plan.onNew) {
184
+ const handler = new MCPMock();
185
+ for (const warning of handler.addMcpFakes(group, { kind: "file" }).warnings) logger.warn(warning.message);
186
+ created.push({
187
+ path,
188
+ handler
189
+ });
190
+ }
191
+ for (const { path, handler } of created) pushAutoMount(mounts, path, handler, {}, logger);
192
+ return {
193
+ commit() {
194
+ for (const { handler, group } of plan.onExisting) {
195
+ const result = handler.addMcpFakes?.(group, { kind: "file" });
196
+ for (const warning of result?.warnings ?? []) logger.warn(warning.message);
197
+ }
198
+ },
199
+ undo() {
200
+ for (const { handler } of created) {
201
+ const at = mounts.findIndex((m) => m.handler === handler);
202
+ if (at !== -1) mounts.splice(at, 1);
203
+ }
204
+ }
205
+ };
206
+ }
207
+
208
+ //#endregion
209
+ export { ensureFakeMount, findFakeMount, handOffMcpFakes, isAutoMount, isFakeMount, mountConflictDetail, planFileFakes };
210
+ //# sourceMappingURL=mcp-fakes-mount.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-fakes-mount.js","names":[],"sources":["../src/mcp-fakes-mount.ts"],"sourcesContent":["/**\n * Where MCP fakes go on a server (spec 5.2): find the mount that serves a\n * block's `mount` path, auto-mount an `MCPMock` when none does (W3, W6), and\n * the start-time hand-off of `ServiceFixtures.mcpFakes` (F2).\n */\nimport type { Journal } from \"./journal.js\";\nimport type { Logger } from \"./logger.js\";\nimport type { MetricsRegistry } from \"./metrics.js\";\nimport type { McpFakeSource, Mountable } from \"./types.js\";\nimport { FixtureLoadError } from \"./fixture-loader.js\";\nimport { MCPMock } from \"./mcp-mock.js\";\nimport {\n MCP_FAKES_DEFAULT_MOUNT,\n McpFakeStore,\n McpFakesAddError,\n blockIdOf,\n type McpFakeIssue,\n} from \"./mcp-fakes.js\";\nimport { build, msg, quote, type Message } from \"./message-text.js\";\n\nexport type MountList = Array<{ path: string; handler: Mountable }>;\n\n/** Dependencies an auto-mount gets when it is created (after start: W4 wiring). */\nexport interface FakeMountWiring {\n journal?: Journal;\n registry?: MetricsRegistry;\n logger?: Logger;\n}\n\n/**\n * The control-API prefix. The server hands every request whose path starts\n * with it to the control API, before any mount, so no mount under it is\n * reachable.\n */\nexport const CONTROL_PREFIX = \"/__aimock\";\n\n/**\n * What serves a block's `mount` path. A conflict is a path no fake there can\n * be served at: a mount that is not an MCPMock serves it first (`not-mcp`), an\n * MCPMock mounted at `mountPath` answers it as its root (`mcp-root`, the path\n * is `mountPath` plus a trailing \"/\"), or it is under the control prefix\n * (`control`).\n */\nexport type FakeMountTarget =\n | { kind: \"mcp\"; handler: Mountable }\n | { kind: \"conflict\"; mountPath: string; reason: \"not-mcp\" | \"mcp-root\" | \"control\" }\n | { kind: \"none\" };\n\n/** The handlers an auto-mount created (W3, W6), whichever path created them. */\nconst autoMounts = new WeakSet<Mountable>();\n\n/** `handler` was auto-mounted for MCP fakes (at start, after start or by the control API). */\nexport function isAutoMount(handler: Mountable): boolean {\n return autoMounts.has(handler);\n}\n\n/** A mount \"is an MCPMock\" for fakes if and only if it implements `addMcpFakes`. */\nexport function isFakeMount(handler: Mountable): boolean {\n return typeof handler.addMcpFakes === \"function\";\n}\n\n/**\n * The mount that serves `path`, in dispatch order: an MCPMock at exactly\n * `path`; a conflict when the path is under the control prefix, when a mount\n * that is not an MCPMock serves it first (it would shadow the fakes), or when\n * an MCPMock at `path` without its trailing \"/\" answers it as its root; none\n * when no mount serves it. An MCPMock mounted at a prefix of `path` lets any\n * other sub-path fall through, so it is skipped. `pending` are the paths an\n * add will auto-mount before this one, in order (they follow `mounts`).\n */\nexport function findFakeMount(\n mounts: MountList,\n path: string,\n pending: readonly string[] = [],\n): FakeMountTarget {\n if (path.startsWith(CONTROL_PREFIX)) {\n return { kind: \"conflict\", mountPath: CONTROL_PREFIX, reason: \"control\" };\n }\n for (const { path: mountPath, handler } of mounts) {\n if (path !== mountPath && !path.startsWith(mountPath + \"/\")) continue;\n if (!isFakeMount(handler)) return { kind: \"conflict\", mountPath, reason: \"not-mcp\" };\n if (mountPath === path) return { kind: \"mcp\", handler };\n if (path === mountPath + \"/\") return { kind: \"conflict\", mountPath, reason: \"mcp-root\" };\n }\n for (const mountPath of pending) {\n if (path === mountPath + \"/\") return { kind: \"conflict\", mountPath, reason: \"mcp-root\" };\n }\n return { kind: \"none\" };\n}\n\n/** Why the fakes for `path` cannot be served there (no location prefix). */\nexport function mountConflictDetail(\n path: string,\n target: Extract<FakeMountTarget, { kind: \"conflict\" }>,\n): Message {\n switch (target.reason) {\n case \"control\":\n return msg`mount ${quote(path)} is under the control API prefix ${quote(target.mountPath)}, which takes its requests first, so its fakes cannot be served`;\n case \"mcp-root\":\n return msg`mount ${quote(path)} is answered by the MCP mock at ${quote(target.mountPath)} as its root, so its fakes cannot be served`;\n case \"not-mcp\":\n return msg`mount ${quote(path)} is served by ${quote(target.mountPath)}, which is not an MCP mock, so its fakes cannot be served`;\n }\n}\n\n/** Wire, push and log (L4, L5) an auto-mounted MCPMock. */\nfunction pushAutoMount(\n mounts: MountList,\n path: string,\n handler: MCPMock,\n wiring: FakeMountWiring,\n logger: Logger,\n): void {\n if (wiring.journal) handler.setJournal(wiring.journal);\n if (wiring.registry) handler.setRegistry(wiring.registry);\n if (wiring.logger) handler.setLogger(wiring.logger);\n const others = mounts.filter((m) => m.path !== path && isFakeMount(m.handler));\n mounts.push({ path, handler });\n autoMounts.add(handler);\n logger.info(build(msg`MCP fakes: auto-mounted an MCP mock at ${quote(path)}`));\n for (const other of others) {\n logger.warn(\n build(\n msg`MCP fakes: auto-mounted an MCP mock at ${quote(path)} while an MCP mock is mounted at ${quote(other.path)}; fakes for one path do not reach the other`,\n ),\n );\n }\n}\n\n/**\n * The MCPMock for `path`: the one already mounted there, or a new one pushed\n * onto `mounts` (auto-mount, logs L4 and L5). A conflict is returned, not\n * thrown, so the caller can name the offending block.\n */\nexport function ensureFakeMount(\n mounts: MountList,\n path: string,\n wiring: FakeMountWiring,\n logger: Logger,\n): { handler: Mountable; created: boolean } | { conflict: string } {\n const target = findFakeMount(mounts, path);\n if (target.kind === \"conflict\") return { conflict: target.mountPath };\n if (target.kind === \"mcp\") return { handler: target.handler, created: false };\n const handler = new MCPMock();\n pushAutoMount(mounts, path, handler, wiring, logger);\n return { handler, created: true };\n}\n\n/** A block's `mount` path, read without running getters; the add validates it. */\nfunction mountOf(raw: unknown): string {\n if (typeof raw !== \"object\" || raw === null) return MCP_FAKES_DEFAULT_MOUNT;\n const value: unknown = Object.getOwnPropertyDescriptor(raw, \"mount\")?.value;\n return typeof value === \"string\" ? value : MCP_FAKES_DEFAULT_MOUNT;\n}\n\n/** The mount-conflict error for one file-origin block. */\nexport function mountConflictError(\n block: McpFakeSource,\n path: string,\n target: Extract<FakeMountTarget, { kind: \"conflict\" }>,\n): FixtureLoadError {\n return new FixtureLoadError({\n rule: \"mcp-fakes/mount-conflict\",\n file: block.source,\n blockId: blockIdOf(block.source, \"\", block.blockIndex),\n entryId: null,\n detail: mountConflictDetail(path, target),\n });\n}\n\n/** Where a checked add of file blocks goes: each group by its `mount` path. */\nexport interface FileFakesPlan {\n /** Groups for a path an MCPMock already serves, with that mount. */\n onExisting: Array<{ handler: Mountable; group: McpFakeSource[] }>;\n /** Groups for a path no mount serves, in order: each gets an auto-mount. */\n onNew: Array<{ path: string; group: McpFakeSource[] }>;\n /** The add warnings (L8), as a throwaway store reports them. */\n warnings: McpFakeIssue[];\n}\n\n/**\n * Check file-origin `blocks` against `mounts` without changing anything:\n * group them by `mount` path (default `/mcp`), find each group's mount (a\n * mount conflict is one error per block), and add each group to a throwaway\n * store (bad blocks and id collisions inside the input). Throws one\n * `McpFakesAddError` holding every error. With `checked`, only the groups\n * that hold a checked block are checked, and only checked blocks get a\n * mount-conflict error (the others were checked before).\n */\nexport function planFileFakes(\n mounts: MountList,\n blocks: readonly McpFakeSource[],\n checked?: ReadonlySet<McpFakeSource>,\n): FileFakesPlan {\n const groups = new Map<string, McpFakeSource[]>();\n for (const block of blocks) {\n const path = mountOf(block.raw);\n const group = groups.get(path);\n if (group) group.push(block);\n else groups.set(path, [block]);\n }\n\n const errors: FixtureLoadError[] = [];\n const warnings: McpFakeIssue[] = [];\n const plan: FileFakesPlan = { onExisting: [], onNew: [], warnings };\n for (const [path, group] of groups) {\n if (checked && !group.some((block) => checked.has(block))) continue;\n const target = findFakeMount(\n mounts,\n path,\n plan.onNew.map((g) => g.path),\n );\n if (target.kind === \"conflict\") {\n for (const block of group) {\n if (!checked || checked.has(block)) errors.push(mountConflictError(block, path, target));\n }\n continue;\n }\n try {\n warnings.push(...new McpFakeStore().add(group, { kind: \"file\" }).warnings);\n } catch (err) {\n if (!(err instanceof McpFakesAddError)) throw err;\n errors.push(...err.errors);\n warnings.push(...err.warnings);\n continue;\n }\n if (target.kind === \"mcp\") plan.onExisting.push({ handler: target.handler, group });\n else plan.onNew.push({ path, group });\n }\n if (errors.length > 0) throw new McpFakesAddError(errors, warnings);\n return plan;\n}\n\n/** A start-time hand-off: the auto-mounts are in place, the rest waits for `commit`. */\nexport interface McpFakesHandOff {\n /** Add the groups for existing MCPMocks (after the server listens). */\n commit(): void;\n /** Take the auto-mounts back off the mounts array (start failed). */\n undo(): void;\n}\n\n/**\n * Start-time hand-off (F2): check every block first (`planFileFakes`; on any\n * error, a bad block, an id collision or a mount conflict, throws one\n * `McpFakesAddError` holding every error and changes nothing). Then the\n * groups for paths no mount serves are added to new MCPMocks, pushed onto\n * `mounts` (auto-mount, L4, L5) so the mount loop wires them. The groups for\n * an MCPMock already mounted are added by `commit`, once the server listens,\n * so a start that fails after the hand-off leaves those mounts untouched;\n * `undo` removes the auto-mounts. The add warnings (L8) are logged at `warn`.\n */\nexport function handOffMcpFakes(\n mounts: MountList,\n blocks: McpFakeSource[],\n logger: Logger,\n): McpFakesHandOff {\n const plan = planFileFakes(mounts, blocks);\n const created: Array<{ path: string; handler: MCPMock }> = [];\n for (const { path, group } of plan.onNew) {\n const handler = new MCPMock();\n for (const warning of handler.addMcpFakes(group, { kind: \"file\" }).warnings) {\n logger.warn(warning.message);\n }\n created.push({ path, handler });\n }\n for (const { path, handler } of created) pushAutoMount(mounts, path, handler, {}, logger);\n return {\n commit(): void {\n for (const { handler, group } of plan.onExisting) {\n const result = handler.addMcpFakes?.(group, { kind: \"file\" });\n for (const warning of result?.warnings ?? []) logger.warn(warning.message);\n }\n },\n undo(): void {\n for (const { handler } of created) {\n const at = mounts.findIndex((m) => m.handler === handler);\n if (at !== -1) mounts.splice(at, 1);\n }\n },\n };\n}\n"],"mappings":"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"}