@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
package/guides/docs.md ADDED
@@ -0,0 +1,170 @@
1
+ # The docs viewer: `@kingsleyweb/bun-ui/docs`
2
+
3
+ A self-hosted viewer for one **OpenAPI 3.1** or **AsyncAPI 3.0** document,
4
+ served by a `docsUi()` handler you mount beside your API. The viewer is a
5
+ bundled React app the package builds itself (React 19 and TanStack Query are
6
+ bundled into it — the host installs neither), served with hash-named immutable
7
+ assets, computed Subresource Integrity and a strict self-only
8
+ Content-Security-Policy (`default-src 'self'; script-src 'self' 'nonce-…';
9
+ style-src 'self'`). Nothing comes from a CDN, so it works offline.
10
+
11
+ Requires Bun ≥ 1.4.2 and `bun add @kingsleyweb/bun-ui`. `/docs` needs no peer:
12
+ not `@kingsleyweb/bun-jobs`, which only the package's `/jobs` entry uses. The
13
+ browser-safe half (`DocsUiConfig`, `DOCS_UI_CONFIG_ELEMENT_ID`,
14
+ `DEFAULT_EXTENSION_PREFIXES` and the `unauthorized` helpers) is
15
+ `@kingsleyweb/bun-ui/docs/browser`; what bun-common's `docsRouter`
16
+ `"self-hosted"` viewer loads is `@kingsleyweb/bun-ui/docs/hosted`.
17
+
18
+ ## Usage
19
+
20
+ ```ts
21
+ import { BunRouter } from "@kingsleyweb/bun-common";
22
+ import { docsUi } from "@kingsleyweb/bun-ui/docs";
23
+
24
+ const app = new BunRouter();
25
+
26
+ // Point at a document your API already serves…
27
+ const ui = docsUi({ document: "/jobs-api/openapi.json", basePath: "/docs" });
28
+
29
+ // …or hand it a thunk, and the router serves the document itself
30
+ // at <basePath>/document.json:
31
+ const ui = docsUi({
32
+ document: () => buildAsyncApiDocument(),
33
+ title: "Events API",
34
+ });
35
+
36
+ app.use(ui.basePath, ui.router);
37
+ ```
38
+
39
+ Which reference the viewer shows is read from the document itself: an
40
+ `openapi` field gets the HTTP reference (operations by tag — and, when the
41
+ document declares `x-tagGroups`, the tags nested under those sections, with
42
+ any tag no section names in a trailing `Other` one; schemas as
43
+ expandable trees, a try-it panel per operation), an `asyncapi` field gets the
44
+ WebSocket reference (channels, operations, messages, limits, close codes).
45
+
46
+ The whole page is client-routed under `basePath`, so every URL under it
47
+ (`<basePath>/http/<operationId>`, `<basePath>/ws/<item>`) is a deep link the
48
+ router answers with the shell.
49
+
50
+ ## Options
51
+
52
+ | Option | Default | What it does |
53
+ | --- | --- | --- |
54
+ | `document` | — (required) | Same-origin path or `http(s)` URL the viewer fetches, or a `() => document` thunk the router serves at `<basePath>/document.json`. |
55
+ | `basePath` | `"/docs"` | Mount path. Not `"/"`, no trailing slash, simple segments only. |
56
+ | `title` | `"API docs"` | Page title. |
57
+ | `theme` | `"system"` | Initial colour scheme: `"system"`, `"light"` or `"dark"`. |
58
+ | `extensions` | `["x-bun-common", "x-bun-jobs"]` | Specification-extension prefixes the viewers read, first hit winning: a `<prefix>-<name>` extension is read from the first prefix carrying it; anything under another prefix is ignored. |
59
+ | `csrfHeader` | none | Header the try-it panel adds on every mutation, or `false`. |
60
+ | `authorize` | none | Guards the shell, the bundle and a thunk's document (not the documented API itself). |
61
+ | `unauthorized` | off | `{ redirectTo, returnParam?, statuses? }`: sends the browser to a sign-in page (or any page) when the caller is not authorized, instead of showing the failure. See [Sending the browser to sign in](#sending-the-browser-to-sign-in). |
62
+ | `middleware` | none | Runs before every route. |
63
+ | `dev` | auto | `true` always builds the bundle in memory; `false` requires the prebuilt `dist/docs/` (a `ConfigError` names the fix without it). |
64
+ | `logger` | console | Anything `resolveLogger` accepts. |
65
+
66
+ ### Sending the browser to sign in
67
+
68
+ By default an authorization failure stays where it happens: the shell
69
+ answers a 401 problem, and a refused document read shows "Could not load the
70
+ API document". With `unauthorized`, a failure sends the browser to a page you
71
+ choose. The jobs UI's `jobsUi()` takes the same option, and both run the same
72
+ code (`lib/kit/unauthorized.ts`, `./docs/embed/unauthorized`).
73
+
74
+ ```ts
75
+ const ui = docsUi({
76
+ document: () => openApiDocument(),
77
+ authorize: (req) => (session(req) ? true : { allow: false, status: 401 }),
78
+ unauthorized: { redirectTo: "/login" },
79
+ });
80
+ // A signed-out visit to /docs/http/listQueues
81
+ // → 303 to /login?returnTo=%2Fdocs%2Fhttp%2FlistQueues
82
+ ```
83
+
84
+ | Field | Default | Notes |
85
+ |---|---|---|
86
+ | `redirectTo` | — (required) | A path starting with one `/` (`"/login"`), or an absolute `http:`/`https:` URL. A path inside the viewer's own `basePath` is a `ConfigError` (a redirect loop). `javascript:`, `data:`, `//host`, a `\`, whitespace, control characters and credentials in a URL are refused too. |
87
+ | `returnParam` | `"returnTo"` | The query parameter that carries the page the user was on (path, query and, in the browser, fragment), always a relative URL starting with one `/`. `false` adds nothing. Letters, digits, `_`, `.`, `~` and `-` only. |
88
+ | `statuses` | `[401]` | Which failures redirect: `401`, `403` or both. |
89
+
90
+ What redirects, for a status in `statuses`:
91
+
92
+ - **The shell.** A page navigation that `authorize` denies answers
93
+ `303 See Other` with `Location: <redirectTo>?returnTo=<path and query>`. A
94
+ navigation is a `GET` or `HEAD` with `Sec-Fetch-Mode: navigate`, or, from a
95
+ client that sends no `Sec-Fetch-Mode`, an `Accept` naming `text/html`. A
96
+ bundle file, `document.json` and a script's `fetch` keep the 401 or 403
97
+ problem. A denial that your `middleware` answers itself is not redirected.
98
+ - **The viewer.** A document read that fails with such a status sends the
99
+ browser to `redirectTo` with `location.assign`, once per page load. The
100
+ try-it panel never does: a 401 there is the documented API's answer to the
101
+ request you tried, not the viewer's.
102
+
103
+ The target is fixed by the option, never read from the request; the sign-in
104
+ page must still check `returnTo` before sending the user back to it.
105
+
106
+ ## The bundle
107
+
108
+ `prepack` builds `app/docs/main.tsx` into `dist/docs/` (`bun scripts/build.ts`): hashed
109
+ file names, a `manifest.json` with every file's SRI digest, type-checked
110
+ first. Without a `dist/docs/` (a source checkout), the first request builds the
111
+ same bundle in memory, once per process, and logs one line saying so.
112
+
113
+ The WebSocket reference's "try it" links and permission markers of
114
+ the jobs UI's Events console are jobs-specific and are not part
115
+ of this standalone viewer.
116
+
117
+ ## Embedding the screens (`./docs/embed`)
118
+
119
+ `docsUi()` needs nothing beyond this package: React and TanStack Query are
120
+ bundled into `dist/docs/`. The `./docs/embed` entries are different. They export the
121
+ viewer's screens as source, from `app/docs/` and `app/kit/`, for a React app that renders them
122
+ inside its own UI (the package's own jobs UI does, by this name), and that app's bundler
123
+ builds them with its own copies of React and TanStack Query. This package
124
+ does not declare them, so install them yourself:
125
+
126
+ ```bash
127
+ bun add react@^19.2.0 @tanstack/react-query@^5.90.0
128
+ bun add -d @types/react@^19.2.0 # for TypeScript
129
+ ```
130
+
131
+ | Entry | Imports |
132
+ |---|---|
133
+ | `./docs/embed`, `./docs/embed/router` | `react`, `@tanstack/react-query` |
134
+ | `./docs/embed/routing`, `./docs/embed/context` | `react` |
135
+ | `./docs/embed/unauthorized` | `react`, and `@tanstack/react-query`'s types |
136
+ | `./docs/embed/permissions`, `./docs/embed/client`, `./docs/embed/errors` | nothing outside this package |
137
+
138
+ The screens also import their `.css` files, which your bundler has to
139
+ handle (`Bun.build` does).
140
+
141
+ The screens never redirect on their own. A host app that embeds them passes
142
+ `unauthorized: null` in the `DocsUiConfig` it provides, and sends the browser
143
+ to sign in through its own app, once: `./docs/embed/unauthorized`'s
144
+ `createUnauthorizedRedirect` and `watchAuthFailures` over the host's query
145
+ client, as the jobs UI does.
146
+
147
+ Because the entries are source, your TypeScript checks them with your
148
+ `tsconfig.json`, which needs two things (`skipLibCheck` does not cover `.ts`
149
+ files):
150
+
151
+ - `"allowImportingTsExtensions": true` (two modules import a `.ts` path;
152
+ `bun init`'s config already sets it), and
153
+ - a declaration for the stylesheets, in any `.d.ts` your project includes,
154
+ since TypeScript 6 checks side-effect imports:
155
+
156
+ ```ts
157
+ declare module "*.css";
158
+ ```
159
+
160
+ ## Development
161
+
162
+ ```bash
163
+ bun run check && bun run lint && bun run format:check && bun run test
164
+ bun scripts/build.ts --ui docs # app/docs/main.tsx → dist/docs/
165
+ ```
166
+
167
+ The golden AsyncAPI fixtures under `__tests__/docs/app/docs/ws/fixtures/` are what
168
+ a real `@kingsleyweb/bun-jobs` API serves (a dev-only dependency, used by the
169
+ fixture test); `UPDATE_WS_FIXTURES=1 bun test __tests__/docs/server/docs-ws.integration.test.ts`
170
+ rewrites them.