footer-api.ts 1.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  1. import type { FooterApi, FooterEvent, RunPrompt, StreamCommit } from "../../../src/mini/types"
  2. export function createFooterApiFixture(input: { events?: FooterEvent[]; commits?: StreamCommit[] } = {}) {
  3. const prompts = new Set<(input: RunPrompt) => void>()
  4. const closes = new Set<() => void>()
  5. let ready!: () => void
  6. const promptReady = new Promise<void>((resolve) => {
  7. ready = resolve
  8. })
  9. const events = input.events ?? []
  10. const commits = input.commits ?? []
  11. const calls: Array<{ type: "event"; value: FooterEvent } | { type: "commit"; value: StreamCommit }> = []
  12. let closed = false
  13. const api: FooterApi = {
  14. get isClosed() {
  15. return closed
  16. },
  17. onPrompt(fn) {
  18. prompts.add(fn)
  19. ready()
  20. return () => prompts.delete(fn)
  21. },
  22. onClose(fn) {
  23. if (closed) {
  24. fn()
  25. return () => {}
  26. }
  27. closes.add(fn)
  28. return () => closes.delete(fn)
  29. },
  30. event(next) {
  31. events.push(next)
  32. calls.push({ type: "event", value: next })
  33. },
  34. append(next) {
  35. commits.push(next)
  36. calls.push({ type: "commit", value: next })
  37. },
  38. idle: () => Promise.resolve(),
  39. close() {
  40. if (closed) return
  41. closed = true
  42. for (const fn of [...closes]) fn()
  43. },
  44. destroy() {
  45. api.close()
  46. prompts.clear()
  47. closes.clear()
  48. },
  49. }
  50. return {
  51. api,
  52. events,
  53. commits,
  54. calls,
  55. promptReady,
  56. submit(text: string, mode?: RunPrompt["mode"], delivery?: RunPrompt["delivery"]) {
  57. if (prompts.size === 0) return false
  58. const prompt: RunPrompt = { text, parts: [], ...(mode ? { mode } : {}), ...(delivery ? { delivery } : {}) }
  59. for (const fn of [...prompts]) fn(prompt)
  60. return true
  61. },
  62. }
  63. }