@kingsleyweb/bun-ui 0.0.0-stage → 3.0.0-rc.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 (240) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +80 -2
  3. package/app/docs/App.tsx +72 -0
  4. package/app/docs/api/client.ts +303 -0
  5. package/app/docs/api/contract.ts +24 -0
  6. package/app/docs/api/docs.ts +38 -0
  7. package/app/docs/api/errors.ts +144 -0
  8. package/app/docs/boot.tsx +58 -0
  9. package/app/docs/components/Badge.tsx +65 -0
  10. package/app/docs/components/Button.tsx +37 -0
  11. package/app/docs/components/Card.tsx +41 -0
  12. package/app/docs/components/ConfirmDialog.tsx +173 -0
  13. package/app/docs/components/CopyButton.tsx +68 -0
  14. package/app/docs/components/Dialog.tsx +230 -0
  15. package/app/docs/components/EmptyState.tsx +34 -0
  16. package/app/docs/components/ErrorView.tsx +70 -0
  17. package/app/docs/components/Field.tsx +93 -0
  18. package/app/docs/components/JsonEditor.tsx +151 -0
  19. package/app/docs/components/JsonView.tsx +338 -0
  20. package/app/docs/components/KeyValue.tsx +63 -0
  21. package/app/docs/components/ProblemBanner.tsx +116 -0
  22. package/app/docs/components/ScreenErrorBoundary.tsx +141 -0
  23. package/app/docs/components/Spinner.tsx +33 -0
  24. package/app/docs/components/Table.tsx +33 -0
  25. package/app/docs/components/Tabs.tsx +226 -0
  26. package/app/docs/components/classNames.ts +4 -0
  27. package/app/docs/components/components.css +691 -0
  28. package/app/docs/components/copy.ts +89 -0
  29. package/app/docs/components/fieldContext.ts +29 -0
  30. package/app/docs/components/inputValues.ts +142 -0
  31. package/app/docs/components/inputs.tsx +387 -0
  32. package/app/docs/components/jsonParse.ts +338 -0
  33. package/app/docs/components/listKeys.ts +30 -0
  34. package/app/docs/config.ts +86 -0
  35. package/app/docs/context.ts +33 -0
  36. package/app/docs/embed.ts +74 -0
  37. package/app/docs/env.d.ts +2 -0
  38. package/app/docs/format.ts +9 -0
  39. package/app/docs/hooks/useUrlTab.ts +16 -0
  40. package/app/docs/main.tsx +11 -0
  41. package/app/docs/queryClient.ts +54 -0
  42. package/app/docs/router.tsx +194 -0
  43. package/app/docs/routing.ts +194 -0
  44. package/app/docs/screens/docs/document.ts +27 -0
  45. package/app/docs/screens/docs/http/OperationView.tsx +407 -0
  46. package/app/docs/screens/docs/http/Sidebar.tsx +190 -0
  47. package/app/docs/screens/docs/http/TryItPanel.tsx +603 -0
  48. package/app/docs/screens/docs/http/http.css +405 -0
  49. package/app/docs/screens/docs/http/index.tsx +395 -0
  50. package/app/docs/screens/docs/http/model.ts +604 -0
  51. package/app/docs/screens/docs/http/tryIt.ts +455 -0
  52. package/app/docs/screens/docs/permissions.ts +6 -0
  53. package/app/docs/screens/docs/schema/Prose.tsx +43 -0
  54. package/app/docs/screens/docs/schema/SchemaTree.tsx +319 -0
  55. package/app/docs/screens/docs/schema/index.ts +30 -0
  56. package/app/docs/screens/docs/schema/resolve.ts +466 -0
  57. package/app/docs/screens/docs/schema/schema.css +137 -0
  58. package/app/docs/screens/docs/schema/skeleton.ts +92 -0
  59. package/app/docs/screens/docs/viewer.tsx +154 -0
  60. package/app/docs/screens/docs/ws/InlineText.tsx +25 -0
  61. package/app/docs/screens/docs/ws/PayloadSchema.tsx +39 -0
  62. package/app/docs/screens/docs/ws/format.ts +82 -0
  63. package/app/docs/screens/docs/ws/index.tsx +286 -0
  64. package/app/docs/screens/docs/ws/items.tsx +786 -0
  65. package/app/docs/screens/docs/ws/model.ts +1178 -0
  66. package/app/docs/screens/docs/ws/panels.tsx +440 -0
  67. package/app/docs/screens/docs/ws/ws.css +231 -0
  68. package/app/docs/shortcuts.ts +94 -0
  69. package/app/docs/styles/base.css +847 -0
  70. package/app/docs/styles/tokens.css +143 -0
  71. package/app/docs/theme.ts +56 -0
  72. package/app/kit/unauthorized.ts +222 -0
  73. package/dist/docs/assets/main-0rchxtv5.js +18 -0
  74. package/dist/docs/assets/main-0rchxtv5.js.map +99 -0
  75. package/dist/docs/assets/main-cqgyvs86.css +1 -0
  76. package/dist/docs/manifest.json +26 -0
  77. package/dist/jobs/assets/AddFlowDialog-h6xkgx9b.js +6 -0
  78. package/dist/jobs/assets/AddFlowDialog-h6xkgx9b.js.map +11 -0
  79. package/dist/jobs/assets/AddFlowDialog-n7gtc84v.css +1 -0
  80. package/dist/jobs/assets/added-cbwsxv3e.js +4 -0
  81. package/dist/jobs/assets/added-cbwsxv3e.js.map +11 -0
  82. package/dist/jobs/assets/bodies-yyw5hmrp.js +6 -0
  83. package/dist/jobs/assets/bodies-yyw5hmrp.js.map +10 -0
  84. package/dist/jobs/assets/channel-tgkvqm8y.js +4 -0
  85. package/dist/jobs/assets/channel-tgkvqm8y.js.map +10 -0
  86. package/dist/jobs/assets/channelChecks-s4yz4yhw.js +4 -0
  87. package/dist/jobs/assets/channelChecks-s4yz4yhw.js.map +10 -0
  88. package/dist/jobs/assets/index-02427smp.css +1 -0
  89. package/dist/jobs/assets/index-09z2cjft.js +4 -0
  90. package/dist/jobs/assets/index-09z2cjft.js.map +16 -0
  91. package/dist/jobs/assets/index-18bn0gvy.css +1 -0
  92. package/dist/jobs/assets/index-47pxmyxn.js +4 -0
  93. package/dist/jobs/assets/index-47pxmyxn.js.map +10 -0
  94. package/dist/jobs/assets/index-4e55z986.js +4 -0
  95. package/dist/jobs/assets/index-4e55z986.js.map +11 -0
  96. package/dist/jobs/assets/index-5c933424.js +4 -0
  97. package/dist/jobs/assets/index-5c933424.js.map +18 -0
  98. package/dist/jobs/assets/index-5kbyqspm.css +1 -0
  99. package/dist/jobs/assets/index-5ng37hk6.css +1 -0
  100. package/dist/jobs/assets/index-5w8w7fvc.css +1 -0
  101. package/dist/jobs/assets/index-71qs5qyx.css +1 -0
  102. package/dist/jobs/assets/index-btpg4r8b.css +1 -0
  103. package/dist/jobs/assets/index-d5r5n0mm.js +4 -0
  104. package/dist/jobs/assets/index-d5r5n0mm.js.map +23 -0
  105. package/dist/jobs/assets/index-e0yy2skq.css +1 -0
  106. package/dist/jobs/assets/index-hs60b25v.js +10 -0
  107. package/dist/jobs/assets/index-hs60b25v.js.map +62 -0
  108. package/dist/jobs/assets/index-k2smrr70.js +4 -0
  109. package/dist/jobs/assets/index-k2smrr70.js.map +9 -0
  110. package/dist/jobs/assets/index-q24jw3w9.js +4 -0
  111. package/dist/jobs/assets/index-q24jw3w9.js.map +32 -0
  112. package/dist/jobs/assets/index-qfb80xen.css +1 -0
  113. package/dist/jobs/assets/index-rtynx6wj.js +4 -0
  114. package/dist/jobs/assets/index-rtynx6wj.js.map +10 -0
  115. package/dist/jobs/assets/live-6k7s6358.js +4 -0
  116. package/dist/jobs/assets/live-6k7s6358.js.map +10 -0
  117. package/dist/jobs/assets/main-0jmeh6mq.js +6 -0
  118. package/dist/jobs/assets/main-0jmeh6mq.js.map +12 -0
  119. package/dist/jobs/assets/main-3yj6bv90.js +5 -0
  120. package/dist/jobs/assets/main-3yj6bv90.js.map +10 -0
  121. package/dist/jobs/assets/main-4g69fn3x.js +5 -0
  122. package/dist/jobs/assets/main-4g69fn3x.js.map +10 -0
  123. package/dist/jobs/assets/main-4gte3eqq.js +5 -0
  124. package/dist/jobs/assets/main-4gte3eqq.js.map +10 -0
  125. package/dist/jobs/assets/main-4j1fcevh.js +5 -0
  126. package/dist/jobs/assets/main-4j1fcevh.js.map +10 -0
  127. package/dist/jobs/assets/main-54qwkz28.css +1 -0
  128. package/dist/jobs/assets/main-5grjvsy7.js +5 -0
  129. package/dist/jobs/assets/main-5grjvsy7.js.map +11 -0
  130. package/dist/jobs/assets/main-5zbx60gq.js +5 -0
  131. package/dist/jobs/assets/main-5zbx60gq.js.map +11 -0
  132. package/dist/jobs/assets/main-9prgjhg4.js +5 -0
  133. package/dist/jobs/assets/main-9prgjhg4.js.map +10 -0
  134. package/dist/jobs/assets/main-a250ffk7.js +17 -0
  135. package/dist/jobs/assets/main-a250ffk7.js.map +115 -0
  136. package/dist/jobs/assets/main-d6ge7w08.js +5 -0
  137. package/dist/jobs/assets/main-d6ge7w08.js.map +10 -0
  138. package/dist/jobs/assets/main-gngzht43.js +5 -0
  139. package/dist/jobs/assets/main-gngzht43.js.map +10 -0
  140. package/dist/jobs/assets/main-hg0hrg8j.js +5 -0
  141. package/dist/jobs/assets/main-hg0hrg8j.js.map +10 -0
  142. package/dist/jobs/assets/main-hm1ng3yg.js +5 -0
  143. package/dist/jobs/assets/main-hm1ng3yg.js.map +12 -0
  144. package/dist/jobs/assets/main-jkvd92z9.js +5 -0
  145. package/dist/jobs/assets/main-jkvd92z9.js.map +10 -0
  146. package/dist/jobs/assets/main-jv5xqd13.js +5 -0
  147. package/dist/jobs/assets/main-jv5xqd13.js.map +21 -0
  148. package/dist/jobs/assets/main-jxre7w93.js +5 -0
  149. package/dist/jobs/assets/main-jxre7w93.js.map +23 -0
  150. package/dist/jobs/assets/main-kwmqkjfx.js +5 -0
  151. package/dist/jobs/assets/main-kwmqkjfx.js.map +11 -0
  152. package/dist/jobs/assets/main-n9cf6bxm.js +5 -0
  153. package/dist/jobs/assets/main-n9cf6bxm.js.map +11 -0
  154. package/dist/jobs/assets/main-pegp0ygb.js +5 -0
  155. package/dist/jobs/assets/main-pegp0ygb.js.map +10 -0
  156. package/dist/jobs/assets/main-qr573853.js +5 -0
  157. package/dist/jobs/assets/main-qr573853.js.map +12 -0
  158. package/dist/jobs/assets/main-rq97rdr4.js +4 -0
  159. package/dist/jobs/assets/main-rq97rdr4.js.map +22 -0
  160. package/dist/jobs/assets/main-szjdmydc.js +5 -0
  161. package/dist/jobs/assets/main-szjdmydc.js.map +13 -0
  162. package/dist/jobs/assets/main-tnbhrm9n.js +7 -0
  163. package/dist/jobs/assets/main-tnbhrm9n.js.map +11 -0
  164. package/dist/jobs/assets/main-ts19j4n0.js +5 -0
  165. package/dist/jobs/assets/main-ts19j4n0.js.map +25 -0
  166. package/dist/jobs/assets/main-w93x1dzs.js +5 -0
  167. package/dist/jobs/assets/main-w93x1dzs.js.map +10 -0
  168. package/dist/jobs/assets/sections-0ksdxs8m.css +1 -0
  169. package/dist/jobs/assets/sections-0y7w4fb4.js +4 -0
  170. package/dist/jobs/assets/sections-0y7w4fb4.js.map +10 -0
  171. package/dist/jobs/assets/sse-mn0t0fh7.js +5 -0
  172. package/dist/jobs/assets/sse-mn0t0fh7.js.map +11 -0
  173. package/dist/jobs/assets/sseChecks-t7tcm7ra.js +4 -0
  174. package/dist/jobs/assets/sseChecks-t7tcm7ra.js.map +10 -0
  175. package/dist/jobs/assets/validation-ysjfy072.js +4 -0
  176. package/dist/jobs/assets/validation-ysjfy072.js.map +9 -0
  177. package/dist/jobs/manifest.json +511 -0
  178. package/dts/docs/assets.d.ts +144 -0
  179. package/dts/docs/assets.d.ts.map +1 -0
  180. package/dts/docs/browser/config.d.ts +61 -0
  181. package/dts/docs/browser/config.d.ts.map +1 -0
  182. package/dts/docs/browser/index.d.ts +12 -0
  183. package/dts/docs/browser/index.d.ts.map +1 -0
  184. package/dts/docs/docsUi.d.ts +42 -0
  185. package/dts/docs/docsUi.d.ts.map +1 -0
  186. package/dts/docs/hosted.d.ts +22 -0
  187. package/dts/docs/hosted.d.ts.map +1 -0
  188. package/dts/docs/index.d.ts +18 -0
  189. package/dts/docs/index.d.ts.map +1 -0
  190. package/dts/docs/shell.d.ts +57 -0
  191. package/dts/docs/shell.d.ts.map +1 -0
  192. package/dts/docs/types.d.ts +137 -0
  193. package/dts/docs/types.d.ts.map +1 -0
  194. package/dts/index.d.ts +21 -0
  195. package/dts/index.d.ts.map +1 -0
  196. package/dts/jobs/assets.d.ts +157 -0
  197. package/dts/jobs/assets.d.ts.map +1 -0
  198. package/dts/jobs/browser/config.d.ts +67 -0
  199. package/dts/jobs/browser/config.d.ts.map +1 -0
  200. package/dts/jobs/browser/index.d.ts +10 -0
  201. package/dts/jobs/browser/index.d.ts.map +1 -0
  202. package/dts/jobs/browser/unauthorized.d.ts +9 -0
  203. package/dts/jobs/browser/unauthorized.d.ts.map +1 -0
  204. package/dts/jobs/index.d.ts +24 -0
  205. package/dts/jobs/index.d.ts.map +1 -0
  206. package/dts/jobs/jobsUi.d.ts +63 -0
  207. package/dts/jobs/jobsUi.d.ts.map +1 -0
  208. package/dts/jobs/shell.d.ts +77 -0
  209. package/dts/jobs/shell.d.ts.map +1 -0
  210. package/dts/jobs/types.d.ts +146 -0
  211. package/dts/jobs/types.d.ts.map +1 -0
  212. package/dts/kit/browser/shared.d.ts +10 -0
  213. package/dts/kit/browser/shared.d.ts.map +1 -0
  214. package/dts/kit/browser/unauthorized.d.ts +74 -0
  215. package/dts/kit/browser/unauthorized.d.ts.map +1 -0
  216. package/dts/kit/unauthorized.d.ts +47 -0
  217. package/dts/kit/unauthorized.d.ts.map +1 -0
  218. package/guides/docs.md +170 -0
  219. package/guides/jobs.md +1120 -0
  220. package/lib/docs/assets.ts +427 -0
  221. package/lib/docs/browser/config.ts +66 -0
  222. package/lib/docs/browser/index.ts +24 -0
  223. package/lib/docs/docsUi.ts +623 -0
  224. package/lib/docs/hosted.ts +31 -0
  225. package/lib/docs/index.ts +37 -0
  226. package/lib/docs/shell.ts +156 -0
  227. package/lib/docs/types.ts +149 -0
  228. package/lib/index.ts +27 -0
  229. package/lib/jobs/assets.ts +448 -0
  230. package/lib/jobs/browser/config.ts +65 -0
  231. package/lib/jobs/browser/index.ts +9 -0
  232. package/lib/jobs/browser/unauthorized.ts +11 -0
  233. package/lib/jobs/index.ts +41 -0
  234. package/lib/jobs/jobsUi.ts +709 -0
  235. package/lib/jobs/shell.ts +205 -0
  236. package/lib/jobs/types.ts +162 -0
  237. package/lib/kit/browser/shared.ts +11 -0
  238. package/lib/kit/browser/unauthorized.ts +114 -0
  239. package/lib/kit/unauthorized.ts +222 -0
  240. package/package.json +198 -4
@@ -0,0 +1,11 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../bun-client/lib/sseParser.ts", "../bun-client/lib/sse.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * The event-stream parser `sse()` reads with (typed-client plan, `sse.md`\n * §3.1): the HTML standard's \"interpreting an event stream\" rules, written\n * for the browser (no `EventSource`: it sends no custom headers, and Bun has\n * none, S-D2).\n *\n * - Lines end in CRLF, LF or CR, also across chunk boundaries (a CR ending\n * one chunk and an LF opening the next are one line end).\n * - One leading U+FEFF (a byte order mark) is dropped.\n * - A line starting with `:` is a comment.\n * - `event:` names the event (`message` when none); `data:` lines are\n * joined with `\\n`; `id:` sets the last event ID unless its value holds a\n * NUL; `retry:` sets the reconnection time when its value is ASCII digits\n * only; any other field is ignored. One space after the colon is dropped.\n * - A blank line ends the block: the last event ID it set is committed\n * ({@link SseParser.lastEventId}), and an event is dispatched. Text after\n * the last blank line, when the stream ends, is discarded.\n *\n * **One departure, on purpose**: a block with an `event:` line and no\n * `data:` line is dispatched, its data `undefined`, where `EventSource` drops\n * it. That is how bun-common reads an event declared to carry no data\n * (`sse({ done: null })`, its `SseFramer`), and the client reads what the\n * server checks. `__tests__/sseParser.test.ts` runs this parser and\n * bun-common's `SseFramer` over one corpus, so the two cannot drift.\n *\n * @internal Not an approved name: `sse()` is the way in.\n */\n\n/** One dispatched event, as the stream framed it. */\nexport interface SseMessage {\n /** The event's name: its `event:` value, or `message`. */\n readonly name: string;\n /** Its `data:` lines joined with `\\n`; `undefined` with no `data:` line. */\n readonly data: string | undefined;\n /** The last event ID as of this event (`\"\"`: none), as `MessageEvent.lastEventId` is. */\n readonly id: string;\n}\n\n/** Character code of LF. */\nconst LF = 10;\n/** Character code of CR. */\nconst CR = 13;\n\n/**\n * Parses `text/event-stream` text, chunk by chunk, into events.\n *\n * @internal\n */\nexport class SseParser {\n /**\n * The last event ID committed by a blank line: what a reconnection sends\n * as `Last-Event-ID` (`\"\"`: none). Starts as the constructor's.\n */\n lastEventId: string;\n /** The last valid `retry:` value, in ms; `undefined` until one arrives. */\n retry: number | undefined;\n /** The `id:` value of the block being read (the standard's \"last event ID buffer\"). */\n #id: string;\n /** The line not yet ended. */\n #line = \"\";\n /** The previous text ended in CR, so an LF opening the next is part of that line end. */\n #afterCr = false;\n /** No text has been read yet, so a byte order mark may open it. */\n #start = true;\n /** The block's `event:` value. */\n #name = \"\";\n /** The block's `data:` lines. */\n #data: string[] = [];\n /** The block has an `event:` or `data:` line. */\n #isEvent = false;\n\n /**\n * @param lastEventId The last event ID to start from (a reconnection\n * carries the previous stream's). Default `\"\"`.\n */\n constructor(lastEventId = \"\") {\n this.lastEventId = lastEventId;\n this.#id = lastEventId;\n }\n\n /**\n * Feeds the next text of the stream, decoded; returns the events it\n * completed, in order.\n *\n * @param text The text (a `TextDecoder` with `ignoreBOM: true`, so this\n * parser drops the one mark the standard allows).\n */\n push(text: string): SseMessage[] {\n const out: SseMessage[] = [];\n if (text.length === 0) return out;\n let start = 0;\n if (this.#start) {\n this.#start = false;\n if (text.charCodeAt(0) === 0xfeff) start = 1;\n }\n if (this.#afterCr) {\n this.#afterCr = false;\n if (text.charCodeAt(start) === LF) start++;\n }\n for (let i = start; i < text.length; i++) {\n const c = text.charCodeAt(i);\n if (c !== LF && c !== CR) continue;\n this.#field(this.#line + text.slice(start, i), out);\n this.#line = \"\";\n if (c === CR) {\n if (i + 1 === text.length) this.#afterCr = true;\n else if (text.charCodeAt(i + 1) === LF) i++;\n }\n start = i + 1;\n }\n this.#line += text.slice(start);\n return out;\n }\n\n /** Handles one complete line. */\n #field(line: string, out: SseMessage[]): void {\n if (line.length === 0) {\n this.lastEventId = this.#id;\n if (this.#isEvent)\n out.push({\n name: this.#name === \"\" ? \"message\" : this.#name,\n data: this.#data.length === 0 ? undefined : this.#data.join(\"\\n\"),\n id: this.lastEventId,\n });\n this.#name = \"\";\n this.#data = [];\n this.#isEvent = false;\n return;\n }\n if (line.charCodeAt(0) === 58 /* \":\" */) return;\n const colon = line.indexOf(\":\");\n const field = colon === -1 ? line : line.slice(0, colon);\n let value = colon === -1 ? \"\" : line.slice(colon + 1);\n if (value.charCodeAt(0) === 32 /* \" \" */) value = value.slice(1);\n if (field === \"event\") {\n this.#name = value;\n this.#isEvent = true;\n } else if (field === \"data\") {\n this.#data.push(value);\n this.#isEvent = true;\n } else if (field === \"id\") {\n if (!value.includes(\"\\0\")) this.#id = value;\n } else if (field === \"retry\") {\n if (/^\\d+$/.test(value)) this.retry = Number(value);\n }\n }\n}\n",
6
+ "/**\n * `@kingsleyweb/bun-client/sse`: typed, validated server-sent events\n * (typed-client plan, `sse.md`, PR-S1).\n *\n * ```ts\n * const stream = sse(api, \"GET /jobs/:id/events\", { params: { id } });\n * for await (const e of stream) {\n * if (e.event === \"progress\") bar.set(e.data.percent); // data narrowed\n * }\n * stream.on(\"done\", (d) => toast(d.result));\n * ```\n *\n * - **Over `fetch`, through the client** (S-D2): each connection is the\n * client's own call (`api.request`), so its headers, auth (and refresh),\n * middleware and request validation apply, and a refreshed token reaches\n * the next reconnection. Never `EventSource`, which sends no custom\n * headers (and which Bun does not have). The parser is `./sseParser`.\n * - **Resume** (S-D3): the last event ID seen goes out as `Last-Event-ID`\n * on every reconnection (and `lastEventId` starts one).\n * - **Reconnection** (§3.2): after the server's `retry:` if it sent one,\n * else `reconnect.initialMs` (3 s), doubling on consecutive failures to\n * `reconnect.maxMs` (30 s), each wait between half of it and all of it;\n * the count starts over once a connection has stayed open 60 s. A\n * `Retry-After` header on a failed status wins. A stream the server ends\n * is reconnected too, as `EventSource` does.\n * - **Stops** on a `204`, {@link EventStream.close}, the call's `signal`, or a\n * status in `fatalStatuses` (every 4xx but 408 and 429; with reconnection\n * off, any failed status): a status the route declares (or any, when the\n * manifest lists none) ends it with the client's `HttpError`, its body\n * read and typed; any other with {@link StreamClosedError}. A success that\n * is not a `text/event-stream` is a {@link StreamClosedError} too.\n * - **Gap** (S-D4): an event named `gap` is delivered like any other, and\n * `on(\"$gap\")` fires with it.\n * - **Validation** (§3.3, TC §5.6): each event's data, by its name, under the\n * client's, the route's and the call's `validate`, as the stream's status\n * says (`response`, or `response[200]`): on in development, off in\n * production; a failure warns and delivers, `\"drop\"` discards it and fires\n * `on(\"$invalid\")`, `\"throw\"` ends the stream with the `ValidationError`.\n * The checks load lazily (`./validation`, and `./sseChecks` beside it),\n * as the core client's do.\n * - **Backpressure** (§3.4): the iterator reads the body only as fast as it\n * is consumed. Listeners (`on`) read eagerly and drop nothing; an iterator\n * beside them then buffers what it has not taken yet.\n *\n * Browser-safe; it imports nothing of the `.` entry's client (it reaches\n * the client's configuration through the client object), so an HTTP-only\n * bundle carries none of it, and this entry none of the HTTP pipeline.\n */\nimport type { Jsonify } from \"./jsonify\";\nimport type {\n CallParts,\n Client,\n ClientConfig,\n ClientFlags,\n ClientMiddleware,\n FailureMode,\n RouteMap,\n ValidationConfig,\n} from \"./types\";\nimport type { StreamEvent } from \"./sseChecks\";\nimport type { CallChecks, Checks } from \"./validation\";\nimport {\n ClientError,\n HttpError,\n NetworkError,\n TimeoutError,\n ValidationError,\n} from \"./errors\";\nimport { retryAfterMs } from \"./retry\";\nimport { SseParser } from \"./sseParser\";\n\n/** The keys of `R` that are event streams (`stream: \"sse\"`). */\ntype SseKeyOf<R> = {\n [K in keyof R]: R[K] extends { stream: \"sse\" } ? K : never;\n}[keyof R] &\n string;\n\n/** An entry's events by name: its `events`, else any name with `unknown` data. */\ntype EventsOf<E> = E extends { events: infer V extends object }\n ? V\n : Record<string, unknown>;\n\n/** The keys of `T` whose property is required. */\ntype RequiredKeys<T> = {\n [K in keyof T]-?: Record<never, never> extends Pick<T, K> ? never : K;\n}[keyof T];\n\n/**\n * One event of the event map `M` as a stream delivers it: its name, its\n * `data` after JSON, and the last event ID as of it (absent when the\n * stream has set none). A union over the names, so `event` narrows `data`.\n */\ntype EventOf<M> = {\n [N in keyof M & string]: {\n /** The event's name (`message` for one with no `event:` line). */\n readonly event: N;\n /** Its data, decoded as the route declares (JSON by default). */\n readonly data: Jsonify<M[N]>;\n /** The last event ID as of this event; absent when there is none. */\n readonly id?: string;\n };\n}[keyof M & string];\n\n/** What `on` takes: an event's name, `\"$gap\"` when the stream declares a `gap` event, or `\"$invalid\"`. */\ntype ListenerName<M> =\n (keyof M & string) | (\"gap\" extends keyof M ? \"$gap\" : never) | \"$invalid\";\n\n/** The listener for `name`: an event's `(data, event)`, or a `ValidationError` for `\"$invalid\"`. */\ntype Listener<M, N> = N extends \"$invalid\"\n ? (error: ValidationError) => void\n : N extends \"$gap\"\n ? Listener<M, \"gap\">\n : N extends keyof M & string\n ? (data: Jsonify<M[N]>, event: Extract<EventOf<M>, { event: N }>) => void\n : never;\n\n/**\n * A typed event stream (`sse.md` §3.1): what {@link sse} returns. It\n * connects when it is first read (`for await`, or a listener).\n *\n * @typeParam M The route's events by name (a generated entry's `events`),\n * each its data's wire type.\n */\nexport interface EventStream<\n M extends object = Record<string, unknown>,\n> extends AsyncIterable<EventOf<M>> {\n /**\n * Calls `listener` for every event named `name`, as it is read (reading\n * eagerly from then on); `\"$gap\"` for the declared `gap` event, as well\n * as `\"gap\"`; `\"$invalid\"` for an event `\"drop\"` discarded. Returns a\n * function that removes it. A listener that throws is logged, and the\n * stream carries on.\n */\n on: <N extends ListenerName<M>>(\n name: N,\n listener: Listener<M, N>,\n ) => () => void;\n /** What a reconnection sends as `Last-Event-ID` (`\"\"`: none): the last `id:` the stream committed. */\n readonly lastEventId: string;\n /**\n * Settles when the stream ends: fulfilled on a `204`, `close()`, or a\n * stream the server ends with reconnection off; rejected with what ended\n * it otherwise (the `signal`'s reason, an `HttpError`, a\n * {@link StreamClosedError}, a `ValidationError` under `\"throw\"`, a\n * `NetworkError` with reconnection off).\n */\n readonly closed: Promise<void>;\n /** Stops reading and reconnecting; the iterator ends and {@link closed} fulfils. */\n close: () => void;\n}\n\n/** Reconnection timing (`sse.md` §3.2). */\ninterface ReconnectConfig {\n /** The wait before the first reconnection when the server sent no `retry:`, ms. Default `3_000`. */\n initialMs?: number;\n /** The longest wait, ms (a server's `retry:` above it is still honoured). Default `30_000`. */\n maxMs?: number;\n}\n\n/**\n * Validation for a stream: the HTTP call's {@link ValidationConfig}, whose\n * `response` (or `response[status]`) switches the events' checks, plus\n * `\"drop\"` for a failed event.\n */\ntype StreamValidation = Omit<ValidationConfig, \"onFailure\"> & {\n /**\n * What a failure does: as the HTTP call's, plus, for the events,\n * `\"drop\"`: discard the event and fire `on(\"$invalid\")`. Default:\n * `{ request: \"throw\", response: \"warn\" }`.\n */\n onFailure?:\n | FailureMode\n | {\n /** For a request part. Default `\"throw\"`. */\n request?: FailureMode;\n /** For an event (and an error status's body). Default `\"warn\"`. */\n response?: FailureMode | \"drop\";\n };\n};\n\n/** A stream's options beside the route's typed parts. */\ninterface StreamConfig {\n /** Ends the stream; it rejects with the signal's reason. Default: none. */\n signal?: AbortSignal;\n /** Milliseconds each connection may take to answer (not to stream); `false` for none. Default: the client's. */\n timeout?: number | false;\n /** Middleware for the stream's requests, inside the client's and the route's. Default: none. */\n middleware?: readonly ClientMiddleware[];\n /**\n * Validation for this stream, merged per key over the client's and the\n * route's (TC §6.2); `false` turns it all off. Default: theirs.\n */\n validate?: StreamValidation | false;\n /** The `Last-Event-ID` to send on the first connection (a stream resumed across page loads). Default: none. */\n lastEventId?: string;\n /** Reconnection timing; `false`: a stream that ends stays ended. Default: see {@link ReconnectConfig}. */\n reconnect?: ReconnectConfig | false;\n /**\n * The server's heartbeat interval, ms: with no byte for twice it plus 5 s\n * (a comment counts), the connection is dropped and reconnected.\n * Default: none (no watchdog).\n */\n heartbeatMs?: number;\n /**\n * Statuses that end the stream instead of reconnecting (a `2xx` other\n * than `204` never does). Default: every 4xx but 408 and 429.\n */\n fatalStatuses?: readonly number[];\n /**\n * How each event's `data` is decoded: `\"json\"` parses it (data that does\n * not parse is delivered as its text, and is a validation issue), `\"text\"`\n * delivers it as it is; as bun-common's `sse(events, { data })`. Default:\n * the manifest's `sseData` (the generator writes it from `x-sse-data`),\n * else `\"json\"`.\n */\n data?: \"json\" | \"text\";\n}\n\n/** A stream's options for entry `E`: its typed parts plus {@link StreamConfig}. */\ntype SseOptions<E> = CallParts<E> & StreamConfig;\n\n/** The arguments after the key: the options are required when a typed part is. */\ntype SseArgs<E> = [RequiredKeys<CallParts<E>>] extends [never]\n ? [options?: SseOptions<E>]\n : [options: SseOptions<E>];\n\n/**\n * A status ended the stream that is not one the route declares (or a\n * success that is not an event stream), so there is no typed body to give:\n * `fatalStatuses` (`sse.md` §3.3). A declared one is the client's\n * `HttpError`.\n */\nexport class StreamClosedError extends ClientError {\n /** The status that ended it. */\n readonly status: number;\n /** The response, its body unread. */\n readonly response: Response;\n\n /**\n * @param key The route key.\n * @param response The response that ended it.\n * @param reason Why, for the message.\n */\n constructor(key: string, response: Response, reason: string) {\n super(`${key}: the event stream ended: ${reason}`, { key });\n this.name = \"StreamClosedError\";\n this.status = response.status;\n this.response = response;\n }\n}\n\n/** The symbol the client keeps its configuration under (`client.ts`). */\nconst CONFIG = Symbol.for(\"@kingsleyweb/bun-client\");\n\n/** Why the stream's own controller aborted on `close()`: not an error. */\nconst CLOSED = Symbol(\"closed\");\n\n/** An HTTP call's result, as the stream reads it. */\ninterface CallResult {\n /** A 2xx status. */\n readonly ok: boolean;\n /** The status. */\n readonly status: number;\n /** A failed status's body, read (and checked) by the client. */\n readonly error?: unknown;\n /** The response; an event stream's body still unread. */\n readonly response: Response;\n}\n\n/** The checks module, imported on first use and shared by every stream. */\nlet validationModule: Promise<typeof import(\"./validation\")> | undefined;\n\n/** The event checks (`./sseChecks`), imported on first use beside `./validation`. */\nlet sseChecksModule: Promise<typeof import(\"./sseChecks\")> | undefined;\n\n/** A stream's checks: the call's (for its plan) and the event checker. */\ninterface StreamChecks {\n /** The call's checks, from the client's, the route's and the call's levels. */\n readonly calls: CallChecks;\n /** Checks one event (`./sseChecks`). */\n readonly checkEvent: (typeof import(\"./sseChecks\"))[\"checkEvent\"];\n}\n\n/** Each client's checks, by its configuration, so its schemas load once. */\nconst CHECKS = new WeakMap<object, Promise<Checks>>();\n\n/** Whether a validation level could turn a check on (as `client.ts` decides). */\nfunction mayCheck(level: object | false | undefined): boolean {\n return (\n typeof level === \"object\" &&\n Object.entries(level).some(\n ([name, value]) =>\n name !== \"onFailure\" && value !== false && value !== undefined,\n )\n );\n}\n\n/**\n * The one `process` read here, `process.env.NODE_ENV`, as `client.ts`\n * reads it (a type, erased; a bundler's define replaces it).\n */\ndeclare const process: {\n /** The environment; only `NODE_ENV` is read. */\n readonly env: Readonly<Record<string, string | undefined>>;\n};\n\n/** Development unless the configuration or `NODE_ENV` says production (TC §5.5). */\nfunction isDevelopment(config: ClientConfig): boolean {\n if (config.mode !== undefined) return config.mode === \"development\";\n try {\n return process.env.NODE_ENV !== \"production\";\n } catch {\n return true;\n }\n}\n\n/** Waits `ms`, or until `signal` aborts. */\nfunction wait(ms: number, signal: AbortSignal): Promise<void> {\n return new Promise((resolve) => {\n let timer: ReturnType<typeof setTimeout> | undefined;\n const done = () => {\n clearTimeout(timer);\n signal.removeEventListener(\"abort\", done);\n resolve();\n };\n timer = setTimeout(done, ms);\n signal.addEventListener(\"abort\", done, { once: true });\n });\n}\n\n/** Decodes an event's data: JSON (undefined for none; the text when it does not parse) or text. */\nfunction decode(\n raw: string | undefined,\n format: \"json\" | \"text\",\n): { value: unknown; parsed: boolean } {\n if (format === \"text\" || raw === undefined)\n return { value: raw, parsed: true };\n if (raw === \"\") return { value: undefined, parsed: false };\n try {\n return { value: JSON.parse(raw) as unknown, parsed: true };\n } catch {\n return { value: raw, parsed: false };\n }\n}\n\n/** A drop of the connection the stream will retry (or surface, with reconnection off). */\nclass Dropped extends Error {}\n\n/** The stream behind {@link EventStream}. */\nclass Stream {\n /** {@link EventStream.lastEventId}: the `lastEventId` option, then each committed `id:`. */\n lastEventId: string;\n /** {@link EventStream.closed}. */\n readonly closed: Promise<void>;\n /** The client each connection is a call of. */\n readonly #api: Client<RouteMap>;\n /** The route key. */\n readonly #key: string;\n /** The call's options, the route's parts and the stream's own. */\n readonly #options: StreamConfig & Record<string, unknown>;\n /** The client's configuration (validation, schemas, mode, logger, manifest); `{}` for a client without one. */\n readonly #config: ClientConfig;\n /** Aborted by `close()` (with `CLOSED`) or the call's signal (with its reason): ends every wait. */\n readonly #abort = new AbortController();\n /** The listeners, by event name (and `$gap`, `$invalid`). */\n readonly #listeners = new Map<string, Set<(...args: never[]) => void>>();\n /** Events read but not yet taken by the iterator. */\n readonly #queue: StreamEvent[] = [];\n /** An iterator waiting while listeners read, woken by the next event or the end. */\n readonly #waiters: (() => void)[] = [];\n /** The connect-read-reconnect generator, started by the first read. */\n #source: AsyncGenerator<StreamEvent, void> | undefined;\n /** The read in flight: one at a time, shared by the iterator and the listeners' loop. */\n #reading: Promise<void> | undefined;\n /** The iterator was taken (it is taken once). */\n #iterating = false;\n /** A listener was added, so reading runs eagerly. */\n #eager = false;\n /** The stream ended. */\n #done = false;\n /** What ended it, for the iterator to throw once; `undefined` for a clean end. */\n #error: { value: unknown } | undefined;\n /** Settles {@link closed}: fulfilled without an error, rejected with one. */\n #settle!: (error?: { value: unknown }) => void;\n\n /**\n * @param api The client.\n * @param key The route key.\n * @param options The call's options.\n */\n constructor(\n api: Client<RouteMap>,\n key: string,\n options: StreamConfig & Record<string, unknown>,\n ) {\n this.#api = api;\n this.#key = key;\n this.#options = options;\n this.#config =\n ((api as unknown as Record<symbol, unknown>)[CONFIG] as\n ClientConfig | undefined) ?? {};\n this.lastEventId = options.lastEventId ?? \"\";\n this.closed = new Promise<void>((resolve, reject) => {\n this.#settle = (error) =>\n error === undefined ? resolve() : reject(error.value as Error);\n });\n // Rejections are the iterator's and the awaiting caller's to see.\n this.closed.catch(() => {});\n const signal = options.signal;\n if (signal?.aborted) this.#stop(signal.reason);\n else\n signal?.addEventListener(\"abort\", () => this.#stop(signal.reason), {\n once: true,\n });\n }\n\n on(name: string, listener: (...args: never[]) => void): () => void {\n let set = this.#listeners.get(name);\n if (set === undefined) this.#listeners.set(name, (set = new Set()));\n set.add(listener);\n if (!this.#eager) {\n this.#eager = true;\n void (async () => {\n while (!this.#done) await this.#read();\n })();\n }\n return () => void set.delete(listener);\n }\n\n close(): void {\n this.#stop(CLOSED);\n }\n\n [Symbol.asyncIterator](): AsyncIterator<StreamEvent> {\n if (this.#iterating)\n throw new ClientError(`${this.#key}: an event stream is iterated once`, {\n key: this.#key,\n });\n this.#iterating = true;\n return {\n next: async () => {\n for (;;) {\n const event = this.#queue.shift();\n if (event !== undefined) return { value: event, done: false };\n if (this.#done) {\n const error = this.#error;\n this.#error = undefined;\n if (error !== undefined) throw error.value as Error;\n return { value: undefined, done: true };\n }\n if (this.#eager)\n await new Promise<void>((resolve) => this.#waiters.push(resolve));\n else await this.#read();\n }\n },\n return: () => {\n this.close();\n return Promise.resolve({ value: undefined, done: true });\n },\n };\n }\n\n /** Ends everything: `CLOSED` cleanly, anything else as the error. */\n #stop(reason: unknown): void {\n if (this.#done || this.#abort.signal.aborted) return;\n this.#abort.abort(reason);\n this.#queue.length = 0;\n // An idle source (suspended at a yield, or never started) ends now; a\n // read in flight sees the abort and ends itself.\n if (this.#reading === undefined) {\n void this.#source?.return();\n this.#finish(reason === CLOSED ? undefined : { value: reason });\n }\n }\n\n /** Marks the stream ended, settles `closed` and wakes the iterator. */\n #finish(error?: { value: unknown }): void {\n if (this.#done) return;\n this.#done = true;\n this.#error = error;\n this.#settle(error);\n for (const wake of this.#waiters.splice(0)) wake();\n }\n\n /** Reads one event (or the end) from the source; one read at a time. */\n #read(): Promise<void> {\n this.#source ??= this.#run();\n return (this.#reading ??= this.#source.next().then(\n (result) => {\n this.#reading = undefined;\n if (result.done) {\n const reason = this.#abort.signal.reason as unknown;\n this.#finish(\n this.#abort.signal.aborted && reason !== CLOSED\n ? { value: reason }\n : undefined,\n );\n } else this.#deliver(result.value);\n },\n (error: unknown) => {\n this.#reading = undefined;\n this.#finish({ value: error });\n },\n ));\n }\n\n /** Hands an event to its listeners (and `$gap`'s) and to the iterator. */\n #deliver(event: StreamEvent): void {\n // An event read while close() or the signal ended the stream is dropped.\n if (this.#abort.signal.aborted) return;\n this.#emit(event.event, event.data, event);\n if (event.event === \"gap\") this.#emit(\"$gap\", event.data, event);\n if (this.#iterating) this.#queue.push(event);\n for (const wake of this.#waiters.splice(0)) wake();\n }\n\n /** Calls the listeners of `name`; one that throws is logged. */\n #emit(name: string, ...args: unknown[]): void {\n for (const listener of this.#listeners.get(name) ?? []) {\n try {\n (listener as (...values: unknown[]) => void)(...args);\n } catch (error) {\n (this.#config.logger ?? console).error(\n `${this.#key}: an event listener threw`,\n error,\n );\n }\n }\n }\n\n /** The event checks, when anything may check an event; loaded lazily. */\n async #checks(): Promise<StreamChecks | undefined> {\n const config = this.#config;\n const development = isDevelopment(config);\n const levels = [\n config.validate,\n config.routes?.[this.#key]?.validate,\n this.#options.validate as ValidationConfig | false | undefined,\n ];\n if (\n !(development && config.schemas !== undefined) &&\n !levels.some(mayCheck)\n )\n return undefined;\n let checks = CHECKS.get(config);\n if (checks === undefined) {\n checks = (validationModule ??= import(\"./validation\")).then((module) =>\n module.createChecks({\n development,\n source: config.schemas as never,\n logger: config.logger ?? console,\n errors: { ClientError, ValidationError },\n }),\n );\n CHECKS.set(config, checks);\n }\n const [calls, events] = await Promise.all([\n checks,\n (sseChecksModule ??= import(\"./sseChecks\")),\n ]);\n return { calls: calls(this.#key, levels), checkEvent: events.checkEvent };\n }\n\n /** Connects, reads, validates and reconnects: the events, in order. */\n async *#run(): AsyncGenerator<StreamEvent, void> {\n const key = this.#key;\n const signal = this.#abort.signal;\n const {\n lastEventId: _lastEventId,\n reconnect,\n heartbeatMs,\n fatalStatuses,\n data,\n validate,\n middleware,\n ...call\n } = this.#options;\n const facts = (\n this.#config.manifest as\n | Record<\n string,\n | { statuses?: readonly number[]; sseData?: \"json\" | \"text\" }\n | undefined\n >\n | undefined\n )?.[key];\n const format = data ?? facts?.sseData ?? \"json\";\n const initialMs = (reconnect || {}).initialMs ?? 3_000;\n const maxMs = (reconnect || {}).maxMs ?? 30_000;\n const fatal = (status: number) =>\n fatalStatuses?.includes(status) ??\n (status >= 400 && status < 500 && status !== 408 && status !== 429);\n // The HTTP call takes no \"drop\": an error status's body warns instead.\n const httpValidate =\n typeof validate === \"object\" &&\n typeof validate.onFailure === \"object\" &&\n validate.onFailure.response === \"drop\"\n ? {\n ...validate,\n onFailure: { ...validate.onFailure, response: \"warn\" },\n }\n : validate;\n const declared = facts?.statuses;\n const checks = await this.#checks();\n // Innermost: what the stream asks for, and where it resumes from.\n const resume: ClientMiddleware = (ctx, next) => {\n const headers = ctx.request.headers;\n if (!headers.has(\"accept\")) headers.set(\"accept\", \"text/event-stream\");\n if (this.lastEventId !== \"\")\n headers.set(\"last-event-id\", this.lastEventId);\n return next();\n };\n let retry: number | undefined;\n let failures = 0;\n\n for (;;) {\n let after: number | undefined;\n let opened = 0;\n try {\n const result = (await this.#api.request(\n key as never,\n {\n ...call,\n signal,\n validate: httpValidate,\n retry: false,\n throwOnError: false,\n undeclaredStatus: \"result\",\n middleware: [...(middleware ?? []), resume],\n } as never,\n )) as unknown as CallResult;\n const { response, status } = result;\n if (!result.ok) {\n if (fatal(status) || reconnect === false)\n throw declared === undefined || declared.includes(status)\n ? new HttpError(status, result.error, response, key)\n : new StreamClosedError(key, response, `status ${status}`);\n after = retryAfterMs(response.headers.get(\"retry-after\"));\n throw new Dropped(`status ${status}`);\n }\n if (status === 204) return;\n const type = response.headers.get(\"content-type\") ?? \"\";\n if (!/^text\\/event-stream\\b/i.test(type) || response.body === null) {\n await response.body?.cancel().catch(() => {});\n throw new StreamClosedError(\n key,\n response,\n `status ${status} is not a text/event-stream (${type || \"no content-type\"})`,\n );\n }\n\n opened = Date.now();\n // What checks this connection's events, as its status says.\n const plan = await checks?.calls.events(status);\n const reader = response.body.getReader();\n const decoder = new TextDecoder(\"utf-8\", { ignoreBOM: true });\n const parser = new SseParser(this.lastEventId);\n let stalled = false;\n let watchdog: ReturnType<typeof setTimeout> | undefined;\n const arm = () => {\n if (heartbeatMs === undefined) return;\n clearTimeout(watchdog);\n watchdog = setTimeout(\n () => {\n stalled = true;\n void reader.cancel().catch(() => {});\n },\n heartbeatMs * 2 + 5_000,\n );\n };\n const cancel = () => void reader.cancel().catch(() => {});\n signal.addEventListener(\"abort\", cancel, { once: true });\n try {\n arm();\n for (;;) {\n let chunk: Awaited<ReturnType<typeof reader.read>>;\n try {\n chunk = await reader.read();\n } catch (error) {\n if (signal.aborted) return;\n throw new Dropped(\"the event stream failed\", { cause: error });\n }\n if (signal.aborted) return;\n if (stalled)\n throw new Dropped(\n `no byte for ${heartbeatMs! * 2 + 5_000} ms (heartbeatMs ${heartbeatMs})`,\n );\n if (chunk.done) {\n // EventSource reconnects to a stream the server ended.\n if (reconnect === false) return;\n throw new Dropped(\"the server ended the stream\");\n }\n arm();\n for (const message of parser.push(\n decoder.decode(chunk.value, { stream: true }),\n )) {\n const decoded = decode(message.data, format);\n let event: StreamEvent =\n message.id === \"\"\n ? { event: message.name, data: decoded.value }\n : {\n event: message.name,\n data: decoded.value,\n id: message.id,\n };\n if (plan !== undefined) {\n const { value, issues } = await checks!.checkEvent(\n plan.route,\n status,\n plan.plan,\n event,\n { raw: message.data, parsed: decoded.parsed },\n );\n if (issues.length === 0) event = value;\n else {\n const error = new ValidationError(\n key,\n \"response\",\n issues,\n status,\n );\n if (plan.mode === \"throw\") throw error;\n if (plan.mode === \"drop\") {\n this.#emit(\"$invalid\", error);\n continue;\n }\n if (plan.mode === \"warn\")\n (this.#config.logger ?? console).warn(error.message);\n else plan.mode(error);\n }\n }\n this.lastEventId = message.id;\n yield event;\n if (signal.aborted) return;\n }\n this.lastEventId = parser.lastEventId;\n if (parser.retry !== undefined) retry = parser.retry;\n }\n } finally {\n clearTimeout(watchdog);\n signal.removeEventListener(\"abort\", cancel);\n cancel();\n }\n } catch (error) {\n if (signal.aborted) return;\n const network =\n error instanceof Dropped ||\n error instanceof NetworkError ||\n error instanceof TimeoutError;\n if (!network) throw error;\n if (reconnect === false)\n throw error instanceof Dropped\n ? new NetworkError(`${key}: ${error.message}`, {\n key,\n cause: error.cause,\n })\n : error;\n }\n // A connection that stayed open a minute starts the count over.\n if (opened !== 0 && Date.now() - opened >= 60_000) failures = 0;\n failures++;\n const base = retry ?? initialMs;\n const delay = Math.min(base * 2 ** (failures - 1), Math.max(maxMs, base));\n await wait(after ?? delay * (0.5 + Math.random() * 0.5), signal);\n if (signal.aborted) return;\n }\n }\n}\n\n/**\n * Opens a typed event stream on a route of the client's map whose entry is\n * `stream: \"sse\"` (`sse.md` §3). Nothing is sent until the stream is first\n * read.\n *\n * @param api The client (`createClient<Routes>()`): its base URL,\n * fetch, headers, auth, middleware, manifest and validation apply.\n * @param key The route key, `\"GET /jobs/:id/events\"`.\n * @param options The route's params, query, headers (and a body, for a\n * `POST` stream), and the stream's own options.\n */\nexport function sse<\n R extends RouteMap<R>,\n F extends ClientFlags,\n K extends SseKeyOf<R>,\n>(\n api: Client<R, F>,\n key: K,\n ...args: SseArgs<R[K]>\n): EventStream<EventsOf<R[K]>>;\nexport function sse(api: object, key: string, options: object = {}): unknown {\n return new Stream(\n api as Client<RouteMap>,\n key,\n options as StreamConfig & Record<string, unknown>,\n );\n}\n"
7
+ ],
8
+ "mappings": "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",
9
+ "debugId": "0EED57D7DF821B5864756E2164756E21",
10
+ "names": []
11
+ }
@@ -0,0 +1,4 @@
1
+ import{Pe,nn,Un}from"./main-jv5xqd13.js";function c(n,o){return{target:"data",key:n,message:o,path:"",source:"builtin"}}function m(n,o){if(n.events!==void 0)return n.events;let t=n.responses?.[o];return t?.kind==="sse"&&typeof t.events==="object"&&t.events!==null?t.events:void 0}async function p(n,o,t,i,u){let a=[],s=i.event,l=i,d=t.builtin&&n!==void 0?m(n,o):void 0;if(d!==void 0)if(!Object.hasOwn(d,s)){let e=Object.keys(d),r=e.slice(0,10).join(", ");a.push(c(s,`This event is not declared (${e.length>10?`${r}, …`:r})`))}else{let e=d[s];if(e===null){if(u.raw!==void 0&&u.raw!=="")a.push(c(s,"No data is declared for this event"))}else if(!u.parsed)a.push(c(s,"The event's data is not valid JSON"));else{let r=await Pe(e,i.data,"data",s,void 0,{stripProtoKeys:"nest"});if(r.ok)l={...i,data:r.value};else a.push(...nn(r.issues,!1))}}let f=i;if(t.custom!==void 0){let e=await Pe(t.custom,l,"data",s,void 0,{stripProtoKeys:"nest"});if(e.ok)f=e.value;else a.push(...Un(e.issues))}return{value:f,issues:a}}export{p as checkEvent};
2
+
3
+ //# debugId=BBE965E54CA2981A64756E2164756E21
4
+ //# sourceMappingURL=sseChecks-t7tcm7ra.js.map
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../bun-client/lib/sseChecks.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * An event stream's checks (typed-client plan, `sse.md` §3.3, PR-S1): each\n * event by the server's rules (`bun-common/lib/responseValidation.ts`),\n * then a BYO schema on the whole event.\n *\n * **Loaded lazily**, by `./sse` alone, beside `./validation` (whose plan,\n * issue marking and runner it uses), so the core client's checks chunk\n * carries none of it (`__tests__/budget.test.ts`).\n */\nimport type { StandardOutcome } from \"@kingsleyweb/bun-contract\";\nimport type { ValidationErrorIssue } from \"./errors\";\nimport type { RouteSchemas } from \"./types\";\nimport type { Checked, PartPlan } from \"./validation\";\nimport { builtinIssues, customIssues, validateStandard } from \"./validation\";\n\n/** One event of an event stream, as `sse()` delivers it (`sse.md` §3.1). */\nexport interface StreamEvent {\n /** The event's name. */\n readonly event: string;\n /** Its data, decoded. */\n readonly data: unknown;\n /** The last event ID as of it, when there is one. */\n readonly id?: string;\n}\n\n/** What `checkEvent` is told of the event's data besides the event. */\nexport interface EventData {\n /** The `data:` lines as they arrived; `undefined` with none. */\n readonly raw: string | undefined;\n /** `false` when the data was to be JSON and did not parse (it is delivered as the raw text). */\n readonly parsed: boolean;\n}\n\n/** An issue about an event's data, from the built-in rules. */\nfunction eventIssue(name: string, message: string): ValidationErrorIssue {\n return { target: \"data\", key: name, message, path: \"\", source: \"builtin\" };\n}\n\n/**\n * A route's events for status `status` (`sse.md` §3.3): its `events`, else\n * a bun-common `sse({ … })` declaration as the status's response (a\n * server's own schema module may hold one).\n */\nfunction eventsOf(route: RouteSchemas, status: number): RouteSchemas[\"events\"] {\n if (route.events !== undefined) return route.events;\n const declared = route.responses?.[status] as {\n kind?: unknown;\n events?: unknown;\n } | null;\n return declared?.kind === \"sse\" &&\n typeof declared.events === \"object\" &&\n declared.events !== null\n ? (declared.events as RouteSchemas[\"events\"])\n : undefined;\n}\n\n/**\n * Checks one event of an event stream (`sse.md` §3.3) by the server's rules\n * (`bun-common/lib/responseValidation.ts`): an undeclared name, data on an\n * event declared to carry none, data that is not JSON, then the event's\n * schema on its data; then the BYO schema, on the whole event (`{ event,\n * data, id }`, its data the built-in schema's output when that passed),\n * whose output, when it passes, is the event delivered.\n *\n * @param route The route's built-in schemas; `undefined` when it has none.\n * @param status The stream's status.\n * @param plan What runs for it.\n * @param event The event, its data decoded.\n * @param data The data as it arrived, and whether it decoded.\n */\nexport async function checkEvent(\n route: RouteSchemas | undefined,\n status: number,\n plan: PartPlan,\n event: StreamEvent,\n data: EventData,\n): Promise<Checked<StreamEvent>> {\n const issues: ValidationErrorIssue[] = [];\n const name = event.event;\n let checked = event;\n const events =\n plan.builtin && route !== undefined ? eventsOf(route, status) : undefined;\n if (events !== undefined) {\n if (!Object.hasOwn(events, name)) {\n const names = Object.keys(events);\n const shown = names.slice(0, 10).join(\", \");\n issues.push(\n eventIssue(\n name,\n `This event is not declared (${names.length > 10 ? `${shown}, …` : shown})`,\n ),\n );\n } else {\n const schema = events[name];\n if (schema === null) {\n if (data.raw !== undefined && data.raw !== \"\")\n issues.push(eventIssue(name, \"No data is declared for this event\"));\n } else if (!data.parsed) {\n issues.push(eventIssue(name, \"The event's data is not valid JSON\"));\n } else {\n const result: StandardOutcome<unknown> = await validateStandard(\n schema,\n event.data,\n \"data\",\n name,\n undefined,\n { stripProtoKeys: \"nest\" },\n );\n if (result.ok) checked = { ...event, data: result.value };\n else issues.push(...builtinIssues(result.issues, false));\n }\n }\n }\n let value = event;\n if (plan.custom !== undefined) {\n const result: StandardOutcome<unknown> = await validateStandard(\n plan.custom,\n checked,\n \"data\",\n name,\n undefined,\n { stripProtoKeys: \"nest\" },\n );\n if (result.ok) value = result.value as StreamEvent;\n else issues.push(...customIssues(result.issues));\n }\n return { value, issues };\n}\n"
6
+ ],
7
+ "mappings": "yCAkCA,SAAS,CAAU,CAAC,EAAc,EAAuC,CACvE,MAAO,CAAE,OAAQ,OAAQ,IAAK,EAAM,UAAS,KAAM,GAAI,OAAQ,SAAU,EAQ3E,SAAS,CAAQ,CAAC,EAAqB,EAAwC,CAC7E,GAAI,EAAM,SAAW,OAAW,OAAO,EAAM,OAC7C,IAAM,EAAW,EAAM,YAAY,GAInC,OAAO,GAAU,OAAS,OACxB,OAAO,EAAS,SAAW,UAC3B,EAAS,SAAW,KACjB,EAAS,OACV,OAiBC,eAAe,CAAU,CAC9B,EACA,EACA,EACA,EACA,EAC+B,CAC/B,IAAM,EAAiC,CAAC,EAClC,EAAO,EAAM,MACf,EAAU,EACR,EACJ,EAAK,SAAW,IAAU,OAAY,EAAS,EAAO,CAAM,EAAI,OAClE,GAAI,IAAW,OACb,GAAI,CAAC,OAAO,OAAO,EAAQ,CAAI,EAAG,CAChC,IAAM,EAAQ,OAAO,KAAK,CAAM,EAC1B,EAAQ,EAAM,MAAM,EAAG,EAAE,EAAE,KAAK,IAAI,EAC1C,EAAO,KACL,EACE,EACA,+BAA+B,EAAM,OAAS,GAAK,GAAG,OAAa,IACrE,CACF,EACA,IAAK,CACL,IAAM,EAAS,EAAO,GACtB,GAAI,IAAW,MACb,GAAI,EAAK,MAAQ,QAAa,EAAK,MAAQ,GACzC,EAAO,KAAK,EAAW,EAAM,oCAAoC,CAAC,EACpE,KAAK,GAAI,CAAC,EAAK,OACf,EAAO,KAAK,EAAW,EAAM,oCAAoC,CAAC,EAClE,IAAK,CACL,IAAM,EAAmC,MAAM,GAC7C,EACA,EAAM,KACN,OACA,EACA,OACA,CAAE,eAAgB,MAAO,CAC3B,EACA,GAAI,EAAO,GAAI,EAAU,IAAK,EAAO,KAAM,EAAO,KAAM,EACxD,KAAK,EAAO,KAAK,GAAG,GAAc,EAAO,OAAQ,EAAK,CAAC,GAI7D,IAAI,EAAQ,EACZ,GAAI,EAAK,SAAW,OAAW,CAC7B,IAAM,EAAmC,MAAM,GAC7C,EAAK,OACL,EACA,OACA,EACA,OACA,CAAE,eAAgB,MAAO,CAC3B,EACA,GAAI,EAAO,GAAI,EAAQ,EAAO,MAC9B,KAAK,EAAO,KAAK,GAAG,GAAa,EAAO,MAAM,CAAC,EAEjD,MAAO,CAAE,QAAO,QAAO",
8
+ "debugId": "BBE965E54CA2981A64756E2164756E21",
9
+ "names": []
10
+ }
@@ -0,0 +1,4 @@
1
+ import{Pe,ut,en,tn,Pn,nn,Un,Ms,Fs,Bs}from"./main-jv5xqd13.js";export{ut as ContractError,nn as builtinIssues,Pn as channelContract,Ms as checkRequest,Fs as checkResponse,Bs as createChecks,en as createReceiver,tn as createSender,Un as customIssues,Pe as validateStandard};
2
+
3
+ //# debugId=E1F4B095FEE835B464756E2164756E21
4
+ //# sourceMappingURL=validation-ysjfy072.js.map
@@ -0,0 +1,9 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [
5
+ ],
6
+ "mappings": "",
7
+ "debugId": "E1F4B095FEE835B464756E2164756E21",
8
+ "names": []
9
+ }