@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.
- package/LICENSE +21 -0
- package/README.md +80 -2
- package/app/docs/App.tsx +72 -0
- package/app/docs/api/client.ts +303 -0
- package/app/docs/api/contract.ts +24 -0
- package/app/docs/api/docs.ts +38 -0
- package/app/docs/api/errors.ts +144 -0
- package/app/docs/boot.tsx +58 -0
- package/app/docs/components/Badge.tsx +65 -0
- package/app/docs/components/Button.tsx +37 -0
- package/app/docs/components/Card.tsx +41 -0
- package/app/docs/components/ConfirmDialog.tsx +173 -0
- package/app/docs/components/CopyButton.tsx +68 -0
- package/app/docs/components/Dialog.tsx +230 -0
- package/app/docs/components/EmptyState.tsx +34 -0
- package/app/docs/components/ErrorView.tsx +70 -0
- package/app/docs/components/Field.tsx +93 -0
- package/app/docs/components/JsonEditor.tsx +151 -0
- package/app/docs/components/JsonView.tsx +338 -0
- package/app/docs/components/KeyValue.tsx +63 -0
- package/app/docs/components/ProblemBanner.tsx +116 -0
- package/app/docs/components/ScreenErrorBoundary.tsx +141 -0
- package/app/docs/components/Spinner.tsx +33 -0
- package/app/docs/components/Table.tsx +33 -0
- package/app/docs/components/Tabs.tsx +226 -0
- package/app/docs/components/classNames.ts +4 -0
- package/app/docs/components/components.css +691 -0
- package/app/docs/components/copy.ts +89 -0
- package/app/docs/components/fieldContext.ts +29 -0
- package/app/docs/components/inputValues.ts +142 -0
- package/app/docs/components/inputs.tsx +387 -0
- package/app/docs/components/jsonParse.ts +338 -0
- package/app/docs/components/listKeys.ts +30 -0
- package/app/docs/config.ts +86 -0
- package/app/docs/context.ts +33 -0
- package/app/docs/embed.ts +74 -0
- package/app/docs/env.d.ts +2 -0
- package/app/docs/format.ts +9 -0
- package/app/docs/hooks/useUrlTab.ts +16 -0
- package/app/docs/main.tsx +11 -0
- package/app/docs/queryClient.ts +54 -0
- package/app/docs/router.tsx +194 -0
- package/app/docs/routing.ts +194 -0
- package/app/docs/screens/docs/document.ts +27 -0
- package/app/docs/screens/docs/http/OperationView.tsx +407 -0
- package/app/docs/screens/docs/http/Sidebar.tsx +190 -0
- package/app/docs/screens/docs/http/TryItPanel.tsx +603 -0
- package/app/docs/screens/docs/http/http.css +405 -0
- package/app/docs/screens/docs/http/index.tsx +395 -0
- package/app/docs/screens/docs/http/model.ts +604 -0
- package/app/docs/screens/docs/http/tryIt.ts +455 -0
- package/app/docs/screens/docs/permissions.ts +6 -0
- package/app/docs/screens/docs/schema/Prose.tsx +43 -0
- package/app/docs/screens/docs/schema/SchemaTree.tsx +319 -0
- package/app/docs/screens/docs/schema/index.ts +30 -0
- package/app/docs/screens/docs/schema/resolve.ts +466 -0
- package/app/docs/screens/docs/schema/schema.css +137 -0
- package/app/docs/screens/docs/schema/skeleton.ts +92 -0
- package/app/docs/screens/docs/viewer.tsx +154 -0
- package/app/docs/screens/docs/ws/InlineText.tsx +25 -0
- package/app/docs/screens/docs/ws/PayloadSchema.tsx +39 -0
- package/app/docs/screens/docs/ws/format.ts +82 -0
- package/app/docs/screens/docs/ws/index.tsx +286 -0
- package/app/docs/screens/docs/ws/items.tsx +786 -0
- package/app/docs/screens/docs/ws/model.ts +1178 -0
- package/app/docs/screens/docs/ws/panels.tsx +440 -0
- package/app/docs/screens/docs/ws/ws.css +231 -0
- package/app/docs/shortcuts.ts +94 -0
- package/app/docs/styles/base.css +847 -0
- package/app/docs/styles/tokens.css +143 -0
- package/app/docs/theme.ts +56 -0
- package/app/kit/unauthorized.ts +222 -0
- package/dist/docs/assets/main-0rchxtv5.js +18 -0
- package/dist/docs/assets/main-0rchxtv5.js.map +99 -0
- package/dist/docs/assets/main-cqgyvs86.css +1 -0
- package/dist/docs/manifest.json +26 -0
- package/dist/jobs/assets/AddFlowDialog-h6xkgx9b.js +6 -0
- package/dist/jobs/assets/AddFlowDialog-h6xkgx9b.js.map +11 -0
- package/dist/jobs/assets/AddFlowDialog-n7gtc84v.css +1 -0
- package/dist/jobs/assets/added-cbwsxv3e.js +4 -0
- package/dist/jobs/assets/added-cbwsxv3e.js.map +11 -0
- package/dist/jobs/assets/bodies-yyw5hmrp.js +6 -0
- package/dist/jobs/assets/bodies-yyw5hmrp.js.map +10 -0
- package/dist/jobs/assets/channel-tgkvqm8y.js +4 -0
- package/dist/jobs/assets/channel-tgkvqm8y.js.map +10 -0
- package/dist/jobs/assets/channelChecks-s4yz4yhw.js +4 -0
- package/dist/jobs/assets/channelChecks-s4yz4yhw.js.map +10 -0
- package/dist/jobs/assets/index-02427smp.css +1 -0
- package/dist/jobs/assets/index-09z2cjft.js +4 -0
- package/dist/jobs/assets/index-09z2cjft.js.map +16 -0
- package/dist/jobs/assets/index-18bn0gvy.css +1 -0
- package/dist/jobs/assets/index-47pxmyxn.js +4 -0
- package/dist/jobs/assets/index-47pxmyxn.js.map +10 -0
- package/dist/jobs/assets/index-4e55z986.js +4 -0
- package/dist/jobs/assets/index-4e55z986.js.map +11 -0
- package/dist/jobs/assets/index-5c933424.js +4 -0
- package/dist/jobs/assets/index-5c933424.js.map +18 -0
- package/dist/jobs/assets/index-5kbyqspm.css +1 -0
- package/dist/jobs/assets/index-5ng37hk6.css +1 -0
- package/dist/jobs/assets/index-5w8w7fvc.css +1 -0
- package/dist/jobs/assets/index-71qs5qyx.css +1 -0
- package/dist/jobs/assets/index-btpg4r8b.css +1 -0
- package/dist/jobs/assets/index-d5r5n0mm.js +4 -0
- package/dist/jobs/assets/index-d5r5n0mm.js.map +23 -0
- package/dist/jobs/assets/index-e0yy2skq.css +1 -0
- package/dist/jobs/assets/index-hs60b25v.js +10 -0
- package/dist/jobs/assets/index-hs60b25v.js.map +62 -0
- package/dist/jobs/assets/index-k2smrr70.js +4 -0
- package/dist/jobs/assets/index-k2smrr70.js.map +9 -0
- package/dist/jobs/assets/index-q24jw3w9.js +4 -0
- package/dist/jobs/assets/index-q24jw3w9.js.map +32 -0
- package/dist/jobs/assets/index-qfb80xen.css +1 -0
- package/dist/jobs/assets/index-rtynx6wj.js +4 -0
- package/dist/jobs/assets/index-rtynx6wj.js.map +10 -0
- package/dist/jobs/assets/live-6k7s6358.js +4 -0
- package/dist/jobs/assets/live-6k7s6358.js.map +10 -0
- package/dist/jobs/assets/main-0jmeh6mq.js +6 -0
- package/dist/jobs/assets/main-0jmeh6mq.js.map +12 -0
- package/dist/jobs/assets/main-3yj6bv90.js +5 -0
- package/dist/jobs/assets/main-3yj6bv90.js.map +10 -0
- package/dist/jobs/assets/main-4g69fn3x.js +5 -0
- package/dist/jobs/assets/main-4g69fn3x.js.map +10 -0
- package/dist/jobs/assets/main-4gte3eqq.js +5 -0
- package/dist/jobs/assets/main-4gte3eqq.js.map +10 -0
- package/dist/jobs/assets/main-4j1fcevh.js +5 -0
- package/dist/jobs/assets/main-4j1fcevh.js.map +10 -0
- package/dist/jobs/assets/main-54qwkz28.css +1 -0
- package/dist/jobs/assets/main-5grjvsy7.js +5 -0
- package/dist/jobs/assets/main-5grjvsy7.js.map +11 -0
- package/dist/jobs/assets/main-5zbx60gq.js +5 -0
- package/dist/jobs/assets/main-5zbx60gq.js.map +11 -0
- package/dist/jobs/assets/main-9prgjhg4.js +5 -0
- package/dist/jobs/assets/main-9prgjhg4.js.map +10 -0
- package/dist/jobs/assets/main-a250ffk7.js +17 -0
- package/dist/jobs/assets/main-a250ffk7.js.map +115 -0
- package/dist/jobs/assets/main-d6ge7w08.js +5 -0
- package/dist/jobs/assets/main-d6ge7w08.js.map +10 -0
- package/dist/jobs/assets/main-gngzht43.js +5 -0
- package/dist/jobs/assets/main-gngzht43.js.map +10 -0
- package/dist/jobs/assets/main-hg0hrg8j.js +5 -0
- package/dist/jobs/assets/main-hg0hrg8j.js.map +10 -0
- package/dist/jobs/assets/main-hm1ng3yg.js +5 -0
- package/dist/jobs/assets/main-hm1ng3yg.js.map +12 -0
- package/dist/jobs/assets/main-jkvd92z9.js +5 -0
- package/dist/jobs/assets/main-jkvd92z9.js.map +10 -0
- package/dist/jobs/assets/main-jv5xqd13.js +5 -0
- package/dist/jobs/assets/main-jv5xqd13.js.map +21 -0
- package/dist/jobs/assets/main-jxre7w93.js +5 -0
- package/dist/jobs/assets/main-jxre7w93.js.map +23 -0
- package/dist/jobs/assets/main-kwmqkjfx.js +5 -0
- package/dist/jobs/assets/main-kwmqkjfx.js.map +11 -0
- package/dist/jobs/assets/main-n9cf6bxm.js +5 -0
- package/dist/jobs/assets/main-n9cf6bxm.js.map +11 -0
- package/dist/jobs/assets/main-pegp0ygb.js +5 -0
- package/dist/jobs/assets/main-pegp0ygb.js.map +10 -0
- package/dist/jobs/assets/main-qr573853.js +5 -0
- package/dist/jobs/assets/main-qr573853.js.map +12 -0
- package/dist/jobs/assets/main-rq97rdr4.js +4 -0
- package/dist/jobs/assets/main-rq97rdr4.js.map +22 -0
- package/dist/jobs/assets/main-szjdmydc.js +5 -0
- package/dist/jobs/assets/main-szjdmydc.js.map +13 -0
- package/dist/jobs/assets/main-tnbhrm9n.js +7 -0
- package/dist/jobs/assets/main-tnbhrm9n.js.map +11 -0
- package/dist/jobs/assets/main-ts19j4n0.js +5 -0
- package/dist/jobs/assets/main-ts19j4n0.js.map +25 -0
- package/dist/jobs/assets/main-w93x1dzs.js +5 -0
- package/dist/jobs/assets/main-w93x1dzs.js.map +10 -0
- package/dist/jobs/assets/sections-0ksdxs8m.css +1 -0
- package/dist/jobs/assets/sections-0y7w4fb4.js +4 -0
- package/dist/jobs/assets/sections-0y7w4fb4.js.map +10 -0
- package/dist/jobs/assets/sse-mn0t0fh7.js +5 -0
- package/dist/jobs/assets/sse-mn0t0fh7.js.map +11 -0
- package/dist/jobs/assets/sseChecks-t7tcm7ra.js +4 -0
- package/dist/jobs/assets/sseChecks-t7tcm7ra.js.map +10 -0
- package/dist/jobs/assets/validation-ysjfy072.js +4 -0
- package/dist/jobs/assets/validation-ysjfy072.js.map +9 -0
- package/dist/jobs/manifest.json +511 -0
- package/dts/docs/assets.d.ts +144 -0
- package/dts/docs/assets.d.ts.map +1 -0
- package/dts/docs/browser/config.d.ts +61 -0
- package/dts/docs/browser/config.d.ts.map +1 -0
- package/dts/docs/browser/index.d.ts +12 -0
- package/dts/docs/browser/index.d.ts.map +1 -0
- package/dts/docs/docsUi.d.ts +42 -0
- package/dts/docs/docsUi.d.ts.map +1 -0
- package/dts/docs/hosted.d.ts +22 -0
- package/dts/docs/hosted.d.ts.map +1 -0
- package/dts/docs/index.d.ts +18 -0
- package/dts/docs/index.d.ts.map +1 -0
- package/dts/docs/shell.d.ts +57 -0
- package/dts/docs/shell.d.ts.map +1 -0
- package/dts/docs/types.d.ts +137 -0
- package/dts/docs/types.d.ts.map +1 -0
- package/dts/index.d.ts +21 -0
- package/dts/index.d.ts.map +1 -0
- package/dts/jobs/assets.d.ts +157 -0
- package/dts/jobs/assets.d.ts.map +1 -0
- package/dts/jobs/browser/config.d.ts +67 -0
- package/dts/jobs/browser/config.d.ts.map +1 -0
- package/dts/jobs/browser/index.d.ts +10 -0
- package/dts/jobs/browser/index.d.ts.map +1 -0
- package/dts/jobs/browser/unauthorized.d.ts +9 -0
- package/dts/jobs/browser/unauthorized.d.ts.map +1 -0
- package/dts/jobs/index.d.ts +24 -0
- package/dts/jobs/index.d.ts.map +1 -0
- package/dts/jobs/jobsUi.d.ts +63 -0
- package/dts/jobs/jobsUi.d.ts.map +1 -0
- package/dts/jobs/shell.d.ts +77 -0
- package/dts/jobs/shell.d.ts.map +1 -0
- package/dts/jobs/types.d.ts +146 -0
- package/dts/jobs/types.d.ts.map +1 -0
- package/dts/kit/browser/shared.d.ts +10 -0
- package/dts/kit/browser/shared.d.ts.map +1 -0
- package/dts/kit/browser/unauthorized.d.ts +74 -0
- package/dts/kit/browser/unauthorized.d.ts.map +1 -0
- package/dts/kit/unauthorized.d.ts +47 -0
- package/dts/kit/unauthorized.d.ts.map +1 -0
- package/guides/docs.md +170 -0
- package/guides/jobs.md +1120 -0
- package/lib/docs/assets.ts +427 -0
- package/lib/docs/browser/config.ts +66 -0
- package/lib/docs/browser/index.ts +24 -0
- package/lib/docs/docsUi.ts +623 -0
- package/lib/docs/hosted.ts +31 -0
- package/lib/docs/index.ts +37 -0
- package/lib/docs/shell.ts +156 -0
- package/lib/docs/types.ts +149 -0
- package/lib/index.ts +27 -0
- package/lib/jobs/assets.ts +448 -0
- package/lib/jobs/browser/config.ts +65 -0
- package/lib/jobs/browser/index.ts +9 -0
- package/lib/jobs/browser/unauthorized.ts +11 -0
- package/lib/jobs/index.ts +41 -0
- package/lib/jobs/jobsUi.ts +709 -0
- package/lib/jobs/shell.ts +205 -0
- package/lib/jobs/types.ts +162 -0
- package/lib/kit/browser/shared.ts +11 -0
- package/lib/kit/browser/unauthorized.ts +114 -0
- package/lib/kit/unauthorized.ts +222 -0
- package/package.json +198 -4
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["app/docs/screens/docs/schema/Prose.tsx", "app/docs/screens/docs/schema/resolve.ts", "app/docs/components/classNames.ts", "app/docs/screens/docs/schema/SchemaTree.tsx", "app/docs/screens/docs/schema/skeleton.ts", "lib/docs/browser/config.ts", "app/docs/screens/docs/http/model.ts", "app/docs/api/contract.ts", "app/docs/api/errors.ts", "app/docs/api/client.ts", "app/docs/screens/docs/http/tryIt.ts", "app/docs/components/Badge.tsx", "app/docs/components/Card.tsx", "app/docs/components/Button.tsx", "app/docs/components/copy.ts", "app/docs/components/CopyButton.tsx", "app/docs/components/Dialog.tsx", "app/docs/components/EmptyState.tsx", "app/docs/context.ts", "app/docs/router.tsx", "app/docs/components/fieldContext.ts", "app/docs/components/Field.tsx", "app/docs/components/inputs.tsx", "app/docs/components/ProblemBanner.tsx", "app/docs/components/ConfirmDialog.tsx", "app/docs/components/jsonParse.ts", "app/docs/components/JsonEditor.tsx", "app/docs/components/JsonView.tsx", "app/docs/components/Tabs.tsx", "app/docs/screens/docs/http/TryItPanel.tsx", "app/docs/screens/docs/http/OperationView.tsx", "app/docs/components/listKeys.ts", "app/docs/shortcuts.ts", "app/docs/screens/docs/http/Sidebar.tsx", "app/docs/screens/docs/http/index.tsx", "app/docs/screens/docs/ws/model.ts", "app/docs/format.ts", "app/docs/screens/docs/ws/format.ts", "app/docs/screens/docs/ws/InlineText.tsx", "app/docs/screens/docs/ws/PayloadSchema.tsx", "app/docs/components/KeyValue.tsx", "app/docs/components/Table.tsx", "app/docs/screens/docs/ws/panels.tsx", "app/docs/screens/docs/ws/items.tsx", "app/docs/screens/docs/ws/index.tsx", "app/jobs/api/docs.ts", "app/jobs/screens/docs/http/index.tsx", "app/jobs/screens/docs/ws/address.ts", "app/jobs/screens/docs/ws/links.ts", "app/jobs/screens/docs/ws/console.tsx", "app/jobs/screens/docs/ws/limits.ts", "app/jobs/screens/docs/ws/index.tsx", "app/jobs/screens/docs/index.tsx"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import type { ReactNode } from \"react\";\n\n/** Props of {@link Prose}. */\nexport interface ProseProps {\n /** The text: a spec's `description`, which may carry `` `code` `` spans and blank-line paragraphs. */\n text: string;\n /** Extra class names on each paragraph. */\n className?: string;\n}\n\n/** Splits one paragraph into text and `<code>` spans at backticks. */\nfunction inlineCode(text: string): ReactNode[] {\n return text.split(/(`[^`]+`)/).map((part, index) =>\n part.length > 2 && part.startsWith(\"`\") && part.endsWith(\"`\") ? (\n // eslint-disable-next-line react/no-array-index-key -- the split is fixed for a given text\n <code key={index}>{part.slice(1, -1)}</code>\n ) : (\n part\n ),\n );\n}\n\n/**\n * A spec description as paragraphs, with backtick spans as code. Nothing\n * else of Markdown is interpreted, and no HTML is ever injected: a spec is\n * data from the API, rendered as text.\n */\nexport function Prose({ text, className }: ProseProps) {\n const paragraphs = text.split(/\\n\\s*\\n/).filter((part) => part.trim() !== \"\");\n return (\n <>\n {paragraphs.map((paragraph, index) => (\n <p\n // eslint-disable-next-line react/no-array-index-key -- paragraphs of a fixed text\n key={index}\n className={className}\n >\n {inlineCode(paragraph)}\n </p>\n ))}\n </>\n );\n}\n",
|
|
6
|
+
"/**\n * Pure reading of JSON Schema (the 2020-12 dialect OpenAPI 3.1 and AsyncAPI\n * 3.0 use) for the docs viewers: `$ref` resolution against the document\n * root, a one-line type label, the constraint facts, and the children a tree\n * expands into. Nothing here renders; `SchemaTree.tsx` does.\n */\n\n/**\n * Levels below which a tree never expands, whatever the schema: a guard for\n * a pathological (non-cyclic but huge) document. Cycles stop far earlier, at\n * the first `$ref` already open above.\n */\nexport const MAX_SCHEMA_LEVEL = 24;\n\n/** A schema object; `true`/`false` are schemas too (anything / nothing). */\nexport type SchemaObject = Readonly<Record<string, unknown>>;\n\n/** A spec document, the root `$ref`s resolve against. */\nexport type SchemaRoot = Readonly<Record<string, unknown>>;\n\n/** Whether a value is a (non-array) object. */\nexport function isSchemaObject(value: unknown): value is SchemaObject {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/** Decodes one JSON Pointer token (`~1` → `/`, `~0` → `~`), after URI-decoding. */\nfunction decodeToken(token: string): string {\n let text = token;\n try {\n text = decodeURIComponent(token);\n } catch {\n // Not URI-encoded; use it as it is.\n }\n return text.replace(/~1/g, \"/\").replace(/~0/g, \"~\");\n}\n\n/**\n * The value a local `$ref` (`#/components/schemas/Job`) points at inside\n * `root`, or `undefined` when it points nowhere or is not local (a remote\n * document is never fetched).\n */\nexport function resolveRef(root: SchemaRoot, ref: string): unknown {\n if (ref === \"#\") {\n return root;\n }\n if (!ref.startsWith(\"#/\")) {\n return undefined;\n }\n let node: unknown = root;\n for (const token of ref.slice(2).split(\"/\")) {\n const key = decodeToken(token);\n if (Array.isArray(node)) {\n const index = Number(key);\n node = Number.isInteger(index) ? node[index] : undefined;\n } else if (isSchemaObject(node) && Object.hasOwn(node, key)) {\n node = node[key];\n } else {\n return undefined;\n }\n }\n return node;\n}\n\n/** The name a `$ref` is shown by: its last pointer segment (`#/components/schemas/Job` → `Job`). */\nexport function refName(ref: string): string {\n const tokens = ref.split(\"/\");\n return decodeToken(tokens[tokens.length - 1] ?? ref) || ref;\n}\n\n/** A schema's `$ref`, when it has a string one. */\nexport function refOf(schema: unknown): string | undefined {\n return isSchemaObject(schema) && typeof schema.$ref === \"string\"\n ? schema.$ref\n : undefined;\n}\n\n/** What {@link deref} found. */\nexport interface Dereffed {\n /** The schema behind the refs, or `undefined` when a ref points nowhere. */\n schema: SchemaObject | boolean | undefined;\n /** The refs followed, first to last; empty when `schema` had none. */\n refs: string[];\n /** Whether a ref in the chain points back at one already followed. */\n cycle: boolean;\n}\n\n/**\n * Follows `$ref` → `$ref` → … to a schema that is not only a reference. The\n * siblings of a `$ref` (a `description`, say) are not merged: a caller shows\n * them beside the target.\n */\nexport function deref(schema: unknown, root: SchemaRoot): Dereffed {\n const refs: string[] = [];\n let node: unknown = schema;\n for (;;) {\n const ref = refOf(node);\n if (ref === undefined) {\n break;\n }\n if (refs.includes(ref)) {\n return { schema: undefined, refs, cycle: true };\n }\n refs.push(ref);\n node = resolveRef(root, ref);\n }\n if (typeof node === \"boolean\" || isSchemaObject(node)) {\n return { schema: node, refs, cycle: false };\n }\n return { schema: undefined, refs, cycle: false };\n}\n\n/** The types a schema declares (`type` as a string or an array), or `[]`. */\nexport function declaredTypes(schema: SchemaObject): string[] {\n const { type } = schema;\n if (typeof type === \"string\") {\n return [type];\n }\n return Array.isArray(type)\n ? type.filter((item): item is string => typeof item === \"string\")\n : [];\n}\n\n/** Whether `null` is allowed: `type` includes it, OpenAPI 3.0's `nullable`, or a `const`/`enum` of null. */\nexport function allowsNull(schema: SchemaObject): boolean {\n return (\n declaredTypes(schema).includes(\"null\") ||\n schema.nullable === true ||\n (Array.isArray(schema.enum) && schema.enum.includes(null))\n );\n}\n\n/** Formats a JSON value for a label (`\"a\"`, `3`, `null`, `{…}`). */\nexport function formatValue(value: unknown): string {\n if (value === undefined) {\n return \"undefined\";\n }\n const text = JSON.stringify(value);\n return text.length > 80 ? `${text.slice(0, 77)}…` : text;\n}\n\n/** The alternatives of a composition keyword, when it has any. */\nfunction alternatives(schema: SchemaObject, key: string): unknown[] {\n const value = schema[key];\n return Array.isArray(value) ? value : [];\n}\n\n/**\n * A one-line type: `string`, `integer | null`, `array<JobState>`,\n * `object`, `anyOf(2)`, `any`, `never`; an untyped `enum`/`const` shows its\n * values (`\"asc\" | \"desc\"`), a typed one leaves them to {@link schemaFacts}.\n * A `$ref` shows its name.\n */\nexport function typeLabel(\n schema: unknown,\n root: SchemaRoot,\n level = 0,\n): string {\n if (schema === true) {\n return \"any\";\n }\n if (schema === false) {\n return \"never\";\n }\n if (!isSchemaObject(schema)) {\n return \"any\";\n }\n const ref = refOf(schema);\n if (ref !== undefined) {\n return refName(ref);\n }\n const types = declaredTypes(schema);\n if (types.length > 0) {\n const labels = types.map((type) => {\n if (type === \"array\" && level < 2) {\n const { items } = schema;\n return items === undefined\n ? \"array\"\n : `array<${typeLabel(items, root, level + 1)}>`;\n }\n return type;\n });\n if (schema.nullable === true && !labels.includes(\"null\")) {\n labels.push(\"null\");\n }\n return labels.join(\" | \");\n }\n // Untyped: the values themselves say it (`\"asc\" | \"desc\"`).\n if (Object.hasOwn(schema, \"const\")) {\n return formatValue(schema.const);\n }\n if (Array.isArray(schema.enum)) {\n const values = schema.enum.slice(0, 6).map(formatValue);\n return schema.enum.length > 6\n ? `${values.join(\" | \")} | …`\n : values.join(\" | \");\n }\n for (const key of [\"oneOf\", \"anyOf\", \"allOf\"]) {\n const options = alternatives(schema, key);\n if (options.length > 0) {\n if (key !== \"allOf\" && options.length <= 3 && level < 2) {\n return options\n .map((option) => typeLabel(option, root, level + 1))\n .join(\" | \");\n }\n return `${key}(${options.length})`;\n }\n }\n if (\n isSchemaObject(schema.properties) ||\n schema.additionalProperties !== undefined\n ) {\n return \"object\";\n }\n if (schema.items !== undefined) {\n return \"array\";\n }\n if (schema.not !== undefined) {\n return \"not\";\n }\n return \"any\";\n}\n\n/** One constraint or annotation of a schema, as a label/value pair. */\nexport interface SchemaFact {\n /** A stable key (the keyword). */\n key: string;\n /** What it is, e.g. `\"min\"`. */\n label: string;\n /** Its value, as text. */\n value: string;\n}\n\n/** Keywords shown as facts, in display order, with their labels. */\nconst FACT_KEYWORDS: readonly (readonly [string, string])[] = [\n [\"format\", \"format\"],\n [\"const\", \"const\"],\n [\"enum\", \"one of\"],\n [\"default\", \"default\"],\n [\"minimum\", \"min\"],\n [\"exclusiveMinimum\", \"above\"],\n [\"maximum\", \"max\"],\n [\"exclusiveMaximum\", \"below\"],\n [\"multipleOf\", \"multiple of\"],\n [\"minLength\", \"min length\"],\n [\"maxLength\", \"max length\"],\n [\"pattern\", \"pattern\"],\n [\"minItems\", \"min items\"],\n [\"maxItems\", \"max items\"],\n [\"uniqueItems\", \"unique items\"],\n [\"minProperties\", \"min fields\"],\n [\"maxProperties\", \"max fields\"],\n [\"contentMediaType\", \"media type\"],\n [\"contentEncoding\", \"encoding\"],\n];\n\n/**\n * The facts worth showing beside a schema's type: format, bounds, lengths,\n * pattern, enum/const, default, examples, nullable, deprecated, read/write\n * only. An untyped `enum`/`const` is left out: {@link typeLabel} shows its\n * values as the type.\n */\nexport function schemaFacts(schema: unknown): SchemaFact[] {\n if (!isSchemaObject(schema)) {\n return [];\n }\n const facts: SchemaFact[] = [];\n // An untyped enum/const is the type label itself; say it once.\n const typed = declaredTypes(schema).length > 0;\n for (const [key, label] of FACT_KEYWORDS) {\n if (!Object.hasOwn(schema, key)) {\n continue;\n }\n if ((key === \"enum\" || key === \"const\") && !typed) {\n continue;\n }\n const value = schema[key];\n if (key === \"uniqueItems\" && value !== true) {\n continue;\n }\n if (key === \"enum\" && Array.isArray(value)) {\n facts.push({ key, label, value: value.map(formatValue).join(\", \") });\n continue;\n }\n facts.push({\n key,\n label,\n value:\n typeof value === \"string\" && key !== \"default\" && key !== \"const\"\n ? value\n : formatValue(value),\n });\n }\n if (Array.isArray(schema.examples) && schema.examples.length > 0) {\n facts.push({\n key: \"examples\",\n label: \"examples\",\n value: schema.examples.map(formatValue).join(\", \"),\n });\n } else if (Object.hasOwn(schema, \"example\")) {\n facts.push({\n key: \"example\",\n label: \"example\",\n value: formatValue(schema.example),\n });\n }\n if (schema.nullable === true) {\n facts.push({ key: \"nullable\", label: \"nullable\", value: \"yes\" });\n }\n if (schema.deprecated === true) {\n facts.push({ key: \"deprecated\", label: \"deprecated\", value: \"yes\" });\n }\n if (schema.readOnly === true) {\n facts.push({ key: \"readOnly\", label: \"read only\", value: \"yes\" });\n }\n if (schema.writeOnly === true) {\n facts.push({ key: \"writeOnly\", label: \"write only\", value: \"yes\" });\n }\n return facts;\n}\n\n/**\n * What an object says about fields it does not list: `\"closed\"` for\n * `additionalProperties: false` (the API answers 400 to any other field),\n * `\"any\"` for `true`/`{}`, `\"typed\"` for a schema, `null` when it says\n * nothing (or is not an object).\n */\nexport function additionalFields(\n schema: unknown,\n): \"closed\" | \"any\" | \"typed\" | null {\n if (\n !isSchemaObject(schema) ||\n !Object.hasOwn(schema, \"additionalProperties\")\n ) {\n return null;\n }\n const extra = schema.additionalProperties;\n if (extra === false) {\n return \"closed\";\n }\n if (\n extra === true ||\n (isSchemaObject(extra) && Object.keys(extra).length === 0)\n ) {\n return \"any\";\n }\n return \"typed\";\n}\n\n/** One child of a schema node, as a tree expands it. */\nexport interface SchemaChild {\n /** A key unique among its siblings. */\n key: string;\n /** What it is. */\n kind:\n | \"property\"\n | \"items\"\n | \"prefixItems\"\n | \"additional\"\n | \"patternProperty\"\n | \"oneOf\"\n | \"anyOf\"\n | \"allOf\"\n | \"not\";\n /** The name shown: a property name, `[]`, `[0]`, `Option 1`, `*`, a pattern. */\n label: string;\n /** Its schema. */\n schema: unknown;\n /** For a property: whether the object lists it in `required`. */\n required: boolean;\n}\n\n/** The children a tree expands a (dereferenced) schema into, in reading order. */\nexport function schemaChildren(schema: unknown): SchemaChild[] {\n if (!isSchemaObject(schema)) {\n return [];\n }\n const children: SchemaChild[] = [];\n const required = new Set(\n Array.isArray(schema.required)\n ? schema.required.filter(\n (name): name is string => typeof name === \"string\",\n )\n : [],\n );\n if (isSchemaObject(schema.properties)) {\n for (const [name, child] of Object.entries(schema.properties)) {\n children.push({\n key: `p:${name}`,\n kind: \"property\",\n label: name,\n schema: child,\n required: required.has(name),\n });\n }\n }\n if (isSchemaObject(schema.patternProperties)) {\n for (const [pattern, child] of Object.entries(schema.patternProperties)) {\n children.push({\n key: `pp:${pattern}`,\n kind: \"patternProperty\",\n label: `/${pattern}/`,\n schema: child,\n required: false,\n });\n }\n }\n if (additionalFields(schema) === \"typed\") {\n children.push({\n key: \"additional\",\n kind: \"additional\",\n label: \"any other field\",\n schema: schema.additionalProperties,\n required: false,\n });\n }\n if (Array.isArray(schema.prefixItems)) {\n schema.prefixItems.forEach((child, index) => {\n children.push({\n key: `pi:${index}`,\n kind: \"prefixItems\",\n label: `[${index}]`,\n schema: child,\n required: false,\n });\n });\n }\n if (schema.items !== undefined && typeof schema.items !== \"boolean\") {\n children.push({\n key: \"items\",\n kind: \"items\",\n label: \"[]\",\n schema: schema.items,\n required: false,\n });\n }\n for (const kind of [\"oneOf\", \"anyOf\", \"allOf\"] as const) {\n alternatives(schema, kind).forEach((child, index) => {\n children.push({\n key: `${kind}:${index}`,\n kind,\n label: kind === \"allOf\" ? `Part ${index + 1}` : `Option ${index + 1}`,\n schema: child,\n required: false,\n });\n });\n }\n if (schema.not !== undefined) {\n children.push({\n key: \"not\",\n kind: \"not\",\n label: \"not\",\n schema: schema.not,\n required: false,\n });\n }\n return children;\n}\n\n/** How a composition is introduced above its alternatives. */\nexport const COMPOSITION_LABELS: Readonly<\n Record<\"oneOf\" | \"anyOf\" | \"allOf\", string>\n> = {\n oneOf: \"Exactly one of\",\n anyOf: \"Any of\",\n allOf: \"All of\",\n};\n",
|
|
7
|
+
"/** Joins class names, skipping falsy ones. */\nexport function cx(...names: (string | false | null | undefined)[]): string {\n return names.filter(Boolean).join(\" \");\n}\n",
|
|
8
|
+
"import type {\n SchemaChild,\n SchemaFact,\n SchemaObject,\n SchemaRoot,\n} from \"./resolve\";\nimport { useState } from \"react\";\nimport { cx } from \"../../../components/classNames\";\nimport { Prose } from \"./Prose\";\nimport {\n additionalFields,\n COMPOSITION_LABELS,\n deref,\n isSchemaObject,\n MAX_SCHEMA_LEVEL,\n refName,\n refOf,\n schemaChildren,\n schemaFacts,\n typeLabel,\n} from \"./resolve\";\nimport \"./schema.css\";\n\n/** Props of {@link SchemaTree}. */\nexport interface SchemaTreeProps {\n /** The schema to render: an object, `true`/`false`, or a `{ $ref }`. */\n schema: unknown;\n /** The document the schema came from; every `$ref` resolves against it (`#/components/schemas/X`). */\n root: SchemaRoot;\n /** Levels expanded on first render; `0` starts collapsed. Defaults to `1`. */\n depth?: number;\n /**\n * Called when a `$ref`'s name is activated, e.g. to open that component in\n * a side panel. When omitted the name is plain text; the reference can\n * still be expanded in place.\n */\n onRef?: (ref: string, name: string) => void;\n /** Accessible name of the tree, e.g. \"Request body schema\". Defaults to `\"Schema\"`. */\n label?: string;\n /** Extra class names on the wrapper. */\n className?: string;\n}\n\n/**\n * A JSON Schema (2020-12, as OpenAPI 3.1 and AsyncAPI 3.0 write it) as an\n * expandable tree: each node shows its name, a required marker, its type,\n * its `$ref` name, its constraints and its description; objects expand into\n * properties (noting `additionalProperties: false` as \"No other fields\"),\n * arrays into their items, and `oneOf`/`anyOf`/`allOf` into labelled\n * alternatives. A `$ref` already open above a node is shown as recursive\n * and not expanded again, so a cyclic schema renders finitely.\n */\nexport function SchemaTree({\n schema,\n root,\n depth = 1,\n onRef,\n label = \"Schema\",\n className,\n}: SchemaTreeProps) {\n return (\n <div\n className={cx(\"schema-tree\", className)}\n role=\"group\"\n aria-label={label}\n >\n <ul className=\"schema-list\">\n <SchemaNode\n child={null}\n schema={schema}\n root={root}\n level={0}\n depth={depth}\n refChain={[]}\n onRef={onRef}\n />\n </ul>\n </div>\n );\n}\n\n/** Props of {@link SchemaNode}. */\ninterface SchemaNodeProps {\n /** How the parent reached this node, or `null` at the root. */\n child: SchemaChild | null;\n /** The node's schema, possibly a `$ref`. */\n schema: unknown;\n /** The document. */\n root: SchemaRoot;\n /** Nesting level, root = 0. */\n level: number;\n /** Levels expanded on first render. */\n depth: number;\n /** The `$ref`s open above this node, for the cycle check. */\n refChain: readonly string[];\n /** See {@link SchemaTreeProps.onRef}. */\n onRef: SchemaTreeProps[\"onRef\"];\n}\n\n/** A `$ref`'s name: a button when the tree can open it, text otherwise. */\nfunction RefName({\n refValue,\n onRef,\n}: {\n /** The `$ref` string. */\n refValue: string;\n /** Opens it, when given. */\n onRef: SchemaTreeProps[\"onRef\"];\n}) {\n const name = refName(refValue);\n if (!onRef) {\n return (\n <code\n className=\"schema-ref\"\n title={refValue}\n >\n {name}\n </code>\n );\n }\n return (\n <button\n type=\"button\"\n className=\"schema-ref schema-ref-link\"\n title={`Open ${refValue}`}\n onClick={() => onRef(refValue, name)}\n >\n {name}\n </button>\n );\n}\n\n/** A reference's facts: its target's, overridden by its own siblings' (a `default` beside a `$ref`). */\nfunction mergeFacts(own: SchemaObject, target: unknown): SchemaFact[] {\n const siblings = schemaFacts({ ...own, $ref: undefined });\n const keys = new Set(siblings.map((fact) => fact.key));\n return [\n ...schemaFacts(target).filter((fact) => !keys.has(fact.key)),\n ...siblings,\n ];\n}\n\n/** One node: its row, description and notes, and its children once expanded. */\nfunction SchemaNode({\n child,\n schema,\n root,\n level,\n depth,\n refChain,\n onRef,\n}: SchemaNodeProps) {\n const ref = refOf(schema);\n const resolved = deref(schema, root);\n const target = resolved.schema;\n const recursive =\n resolved.cycle || resolved.refs.some((item) => refChain.includes(item));\n const unresolved = ref !== undefined && target === undefined && !recursive;\n const children =\n recursive || level >= MAX_SCHEMA_LEVEL ? [] : schemaChildren(target);\n const [expanded, setExpanded] = useState(level < depth);\n\n // A `$ref`'s siblings annotate it; they win over the target's.\n const own = isSchemaObject(schema) ? schema : {};\n const description =\n typeof own.description === \"string\"\n ? own.description\n : isSchemaObject(target) && typeof target.description === \"string\"\n ? target.description\n : undefined;\n const facts =\n ref === undefined ? schemaFacts(schema) : mergeFacts(own, target);\n // A reference shows its target's type beside its name, once it resolves.\n const type =\n ref === undefined\n ? typeLabel(schema, root)\n : recursive || unresolved\n ? null\n : typeLabel(target, root);\n const extra = additionalFields(target);\n const name = child?.label ?? null;\n const nextChain = [...refChain, ...resolved.refs];\n\n return (\n <li\n className=\"schema-node\"\n data-kind={child?.kind ?? \"root\"}\n >\n <div className=\"schema-row\">\n {children.length > 0 ? (\n <button\n type=\"button\"\n className=\"schema-toggle\"\n aria-expanded={expanded}\n aria-label={`${expanded ? \"Collapse\" : \"Expand\"} ${name ?? \"schema\"}`}\n onClick={() => setExpanded((open) => !open)}\n >\n <span aria-hidden=\"true\">{expanded ? \"▾\" : \"▸\"}</span>\n </button>\n ) : (\n <span\n className=\"schema-toggle-spacer\"\n aria-hidden=\"true\"\n />\n )}\n {name !== null && <span className=\"schema-name\">{name}</span>}\n {child?.required && (\n <span\n className=\"schema-required\"\n title=\"Required\"\n >\n required\n </span>\n )}\n {type !== null && <span className=\"schema-type\">{type}</span>}\n {ref !== undefined && (\n <RefName\n refValue={ref}\n onRef={onRef}\n />\n )}\n {recursive && (\n <span\n className=\"schema-note-inline\"\n title=\"This schema contains itself; it is expanded above.\"\n >\n recursive\n </span>\n )}\n {unresolved && (\n <span className=\"schema-note-inline schema-unresolved\">\n unresolved $ref\n </span>\n )}\n {facts.map((fact) => (\n <span\n key={fact.key}\n className=\"schema-fact\"\n data-fact={fact.key}\n >\n <span className=\"schema-fact-label\">{fact.label}</span>{\" \"}\n <code>{fact.value}</code>\n </span>\n ))}\n </div>\n {description !== undefined && (\n <div className=\"schema-description\">\n <Prose text={description} />\n </div>\n )}\n {extra === \"closed\" && (\n <p\n className=\"schema-closed\"\n data-testid=\"schema-closed\"\n >\n No other fields: any field not listed is refused.\n </p>\n )}\n {extra === \"any\" && (\n <p className=\"schema-open\">Other fields: any value.</p>\n )}\n {expanded && children.length > 0 && (\n <ul className=\"schema-list\">\n {children.map((item, index) => {\n const previous = children[index - 1];\n const composition =\n (item.kind === \"oneOf\" ||\n item.kind === \"anyOf\" ||\n item.kind === \"allOf\") &&\n previous?.kind !== item.kind;\n return (\n <SchemaChildItem\n key={item.key}\n item={item}\n heading={\n composition\n ? COMPOSITION_LABELS[\n item.kind as \"oneOf\" | \"anyOf\" | \"allOf\"\n ]\n : undefined\n }\n root={root}\n level={level + 1}\n depth={depth}\n refChain={nextChain}\n onRef={onRef}\n />\n );\n })}\n </ul>\n )}\n </li>\n );\n}\n\n/** A child node, preceded by its composition's heading when it opens one. */\nfunction SchemaChildItem({\n item,\n heading,\n ...rest\n}: Omit<SchemaNodeProps, \"child\" | \"schema\"> & {\n /** The child. */\n item: SchemaChild;\n /** \"Any of\" etc., before the first alternative of a composition. */\n heading: string | undefined;\n}) {\n return (\n <>\n {heading !== undefined && (\n <li className=\"schema-composition\">{heading}</li>\n )}\n <SchemaNode\n {...rest}\n child={item}\n schema={item.schema}\n />\n </>\n );\n}\n",
|
|
9
|
+
"import type { SchemaRoot } from \"./resolve\";\nimport { declaredTypes, deref, isSchemaObject } from \"./resolve\";\n\n/**\n * A starting value for a schema: an editor's prefill. Objects get only their\n * REQUIRED fields (an optional one would be a guess the reader has to\n * delete), each filled the same way; `default`, `const` and the first `enum`\n * value win over a type's zero; unions take their first non-null option.\n * A `$ref` cycle, or a schema that says nothing, gives `null`.\n */\nexport function schemaSkeleton(\n schema: unknown,\n root: SchemaRoot,\n seen: readonly string[] = [],\n): unknown {\n const { schema: node, refs, cycle } = deref(schema, root);\n if (cycle || refs.some((ref) => seen.includes(ref))) {\n return null;\n }\n if (!isSchemaObject(node)) {\n return null;\n }\n const chain = [...seen, ...refs];\n if (Object.hasOwn(node, \"default\")) {\n return structuredClone(node.default);\n }\n if (Object.hasOwn(node, \"const\")) {\n return structuredClone(node.const);\n }\n if (Array.isArray(node.enum) && node.enum.length > 0) {\n return structuredClone(node.enum.find((value) => value !== null) ?? null);\n }\n if (Array.isArray(node.allOf) && node.allOf.length > 0) {\n const merged: Record<string, unknown> = {};\n for (const part of node.allOf) {\n const value = schemaSkeleton(part, root, chain);\n if (isSchemaObject(value)) {\n Object.assign(merged, value);\n }\n }\n return merged;\n }\n for (const key of [\"oneOf\", \"anyOf\"]) {\n const options = node[key];\n if (Array.isArray(options) && options.length > 0) {\n const option =\n options.find((candidate) => {\n const target = deref(candidate, root).schema;\n return !(\n isSchemaObject(target) &&\n declaredTypes(target).length === 1 &&\n declaredTypes(target)[0] === \"null\"\n );\n }) ?? options[0];\n return schemaSkeleton(option, root, chain);\n }\n }\n const types = declaredTypes(node).filter((type) => type !== \"null\");\n const type =\n types[0] ??\n (isSchemaObject(node.properties)\n ? \"object\"\n : node.items\n ? \"array\"\n : undefined);\n switch (type) {\n case \"object\": {\n const value: Record<string, unknown> = {};\n const properties = isSchemaObject(node.properties) ? node.properties : {};\n const required = Array.isArray(node.required) ? node.required : [];\n for (const name of required) {\n if (typeof name === \"string\") {\n value[name] = schemaSkeleton(properties[name], root, chain);\n }\n }\n return value;\n }\n case \"array\": {\n const min = typeof node.minItems === \"number\" ? node.minItems : 0;\n return min > 0 ? [schemaSkeleton(node.items, root, chain)] : [];\n }\n case \"string\":\n return \"\";\n case \"integer\":\n case \"number\":\n return typeof node.minimum === \"number\" ? node.minimum : 0;\n case \"boolean\":\n return false;\n default:\n return null;\n }\n}\n",
|
|
10
|
+
"/**\n * The contract between `docsUi()` (server, `lib/docs/`) and the React viewer\n * (`app/docs/`).\n *\n * The server resolves this once per mount and inlines it into the HTML shell\n * as `<script type=\"application/json\" id=\"bun-api-docs-ui-config\" nonce=\"…\">`.\n * The viewer reads it before its first request. It must stay free of runtime\n * imports: both sides import it, and the app side is bundled for the browser.\n */\n\nimport type { UiTheme } from \"../../kit/browser/shared\";\nimport type { UiUnauthorized } from \"../../kit/browser/unauthorized\";\n\nexport type { UiTheme };\n\n/** Id of the `<script type=\"application/json\">` element carrying {@link DocsUiConfig}. */\nexport const DOCS_UI_CONFIG_ELEMENT_ID = \"bun-api-docs-ui-config\";\n\n/**\n * The specification-extension prefixes the viewers read, in priority order:\n * an extension named on an operation, channel or scheme as\n * `<prefix>-<name>` is read from the first prefix that carries it (so\n * `x-bun-common-schema` wins over `x-bun-jobs-schema` when a document has\n * both). An extension under any other prefix is ignored.\n */\nexport const DEFAULT_EXTENSION_PREFIXES = [\"x-bun-common\", \"x-bun-jobs\"];\n\n/**\n * Colour scheme the viewer starts in: the package's shared {@link UiTheme}\n * (`\"system\"` follows `prefers-color-scheme`), under the docs viewer's name.\n */\nexport type DocsUiTheme = UiTheme;\n\n/** Resolved configuration handed to the browser. Never carries secrets. */\nexport interface DocsUiConfig {\n /** Shape version of this object, bumped on a breaking change. Currently `1`. */\n version: 1;\n /** Page title. Defaults to `\"API docs\"`. */\n title: string;\n /** Absolute path the viewer is mounted at, no trailing slash (e.g. `\"/docs\"`). Client routes live under it. */\n basePath: string;\n /** Path the hashed assets are served from, e.g. `\"/docs/assets\"`. */\n assetsPath: string;\n /**\n * Where the viewer fetches the one document it shows: `<basePath>/document.json`\n * when `docsUi()` was given a thunk (the router serves it there), else the\n * URL exactly as configured (a same-origin path or an `http(s)` URL).\n */\n documentUrl: string;\n /**\n * Header the try-it panel adds on every mutation (any non-empty value), or\n * `null` when none is configured. Mirrors the documented API's CSRF rule.\n */\n csrfHeader: string | null;\n /** The extension prefixes in effect (see {@link DEFAULT_EXTENSION_PREFIXES}). */\n extensions: readonly string[];\n /** Initial colour scheme. */\n theme: DocsUiTheme;\n /**\n * Where an authorization failure sends the browser (`docsUi()`'s\n * `unauthorized`), or `null` when it is off: the viewer then shows the\n * failure, as before. A host app that embeds the viewer's screens passes\n * `null` and redirects through its own option.\n */\n unauthorized: UiUnauthorized | null;\n}\n",
|
|
11
|
+
"import type { SpecDocument } from \"../../../api/docs\";\nimport { DEFAULT_EXTENSION_PREFIXES } from \"../../../../../lib/docs/browser/config.ts\";\nimport { deref, isSchemaObject, refOf, resolveRef } from \"../schema/resolve\";\n\n/**\n * The parts of an OpenAPI 3.1 document the HTTP reference renders, read out\n * of the JSON into plain records. Everything is defensive: the document is\n * data from the API, and a field of the wrong shape is skipped rather than\n * trusted.\n *\n * The extensions are vendor-neutral: each is named on its node as\n * `<prefix>-<name>` (`x-bun-jobs-action`, `x-bun-common-csrf`, …) and read\n * from the first prefix of {@link HttpModelOptions.extensions} that carries\n * it; one under any other prefix is ignored.\n */\n\n/** Options of {@link listOperations}. */\nexport interface HttpModelOptions {\n /**\n * The extension prefixes, first hit winning. Defaults to\n * `[\"x-bun-common\", \"x-bun-jobs\"]`.\n */\n extensions?: readonly string[];\n}\n\n/** Reads the `<prefix>-<name>` extension of a node from the first prefix carrying it. */\nfunction extensionOf(\n node: Record<string, unknown>,\n name: string,\n prefixes: readonly string[],\n): unknown {\n for (const prefix of prefixes) {\n const key = `${prefix}-${name}`;\n if (Object.hasOwn(node, key)) {\n return node[key];\n }\n }\n return undefined;\n}\n\n/** Path item keys that are operations, in the order OpenAPI lists them. */\nconst OPERATION_METHODS = [\n \"get\",\n \"put\",\n \"post\",\n \"delete\",\n \"options\",\n \"head\",\n \"patch\",\n \"trace\",\n] as const;\n\n/** Where a parameter goes. */\nexport type ParameterLocation = \"path\" | \"query\" | \"header\" | \"cookie\";\n\n/** One parameter of an operation. */\nexport interface DocParameter {\n /** Its name. */\n name: string;\n /** Where it goes. */\n in: ParameterLocation;\n /** Whether it must be sent (path parameters always are). */\n required: boolean;\n /** What it means. */\n description?: string;\n /** Its schema (possibly a `$ref`). */\n schema: unknown;\n /** OpenAPI `style` (`form` for the API's array query parameters). */\n style?: string;\n /** OpenAPI `explode`: `true` sends an array as repeated keys. */\n explode?: boolean;\n /** Whether it is deprecated. */\n deprecated: boolean;\n}\n\n/** One media type of a body. */\nexport interface DocMediaType {\n /** E.g. `application/json`. */\n contentType: string;\n /** Its schema, when it declares one. */\n schema: unknown;\n}\n\n/** An operation's request body. */\nexport interface DocRequestBody {\n /** Whether a body must be sent. */\n required: boolean;\n /** A note, e.g. that `Content-Type: application/json` is required even when bodiless. */\n description?: string;\n /** The media types accepted. */\n content: DocMediaType[];\n}\n\n/** One documented response. */\nexport interface DocResponse {\n /** `\"200\"`, `\"404\"`, `\"default\"`, `\"4XX\"`. */\n status: string;\n /** What it means (for a problem: the titles of its codes). */\n description: string;\n /** Its bodies. */\n content: DocMediaType[];\n /** `<prefix>-codes`: the problem codes answered with this status. */\n codes: string[];\n /** The `$ref` it came from (`#/components/responses/Problem`), if any. */\n ref?: string;\n}\n\n/** `<prefix>-csrf`: what a mutation must carry. */\nexport interface DocCsrf {\n /** The header every mutation must carry (any non-empty value), or `null` when none is required. */\n header: string | null;\n /** Whether `Content-Type: application/json` is required, even without a body. */\n requireJson: boolean;\n}\n\n/** One operation, flattened. */\nexport interface DocOperation {\n /** `operationId` (or `METHOD path` when the document gives none). */\n id: string;\n /** Upper-case method. */\n method: string;\n /** The path template, relative to the server (`/queues/{queue}`). */\n path: string;\n /** One line. */\n summary?: string;\n /** More. */\n description?: string;\n /** Its tags. */\n tags: string[];\n /** Whether it is deprecated. */\n deprecated: boolean;\n /** Path-item and operation parameters, operation ones winning. */\n parameters: DocParameter[];\n /** The body it takes. */\n requestBody?: DocRequestBody;\n /** Its responses, in document order. */\n responses: DocResponse[];\n /** `<prefix>-action`: the permission the operation needs. */\n action?: string;\n /** `<prefix>-mutation`: whether it changes state (CSRF checks, refused when read-only). */\n mutation: boolean;\n /** `<prefix>-requires`: the driver methods it needs. */\n requires: string[];\n /** `<prefix>-csrf`, on mutations when CSRF checks are on. */\n csrf?: DocCsrf;\n}\n\n/** One tag, with the operations under it. */\nexport interface DocTagGroup {\n /** The tag's name. */\n name: string;\n /** What it covers. */\n description?: string;\n /** Its operations, in document order. */\n operations: DocOperation[];\n}\n\n/** One server entry. */\nexport interface DocServer {\n /** Its URL, often relative (`/jobs-api`). */\n url: string;\n /** A note. */\n description?: string;\n}\n\n/** The document's header. */\nexport interface DocInfo {\n /** `openapi`, e.g. `3.1.0`. */\n openapi: string;\n /** The API's title. */\n title: string;\n /** Its version. */\n version: string;\n /** What it is. */\n description?: string;\n /** Where it is served. */\n servers: DocServer[];\n}\n\n/** A string field, or `undefined`. */\nfunction text(value: unknown): string | undefined {\n return typeof value === \"string\" ? value : undefined;\n}\n\n/** A string array field, or `[]`. */\nfunction strings(value: unknown): string[] {\n return Array.isArray(value)\n ? value.filter((item): item is string => typeof item === \"string\")\n : [];\n}\n\n/** Follows a `$ref` to a component (a parameter, a response, a body), one level of indirection at a time. */\nfunction component(\n doc: SpecDocument,\n value: unknown,\n): { node: Record<string, unknown> | undefined; ref?: string } {\n const ref = refOf(value);\n if (ref === undefined) {\n return { node: isSchemaObject(value) ? value : undefined };\n }\n const target = deref(value, doc).schema;\n return { node: isSchemaObject(target) ? target : undefined, ref };\n}\n\n/** The media types of a `content` map. */\nfunction mediaTypes(value: unknown): DocMediaType[] {\n if (!isSchemaObject(value)) {\n return [];\n }\n return Object.entries(value).map(([contentType, media]) => ({\n contentType,\n schema: isSchemaObject(media) ? media.schema : undefined,\n }));\n}\n\n/** Reads one parameter. */\nfunction readParameter(\n doc: SpecDocument,\n raw: unknown,\n): DocParameter | undefined {\n const { node } = component(doc, raw);\n const name = text(node?.name);\n const location = text(node?.in);\n if (\n !node ||\n name === undefined ||\n (location !== \"path\" &&\n location !== \"query\" &&\n location !== \"header\" &&\n location !== \"cookie\")\n ) {\n return undefined;\n }\n return {\n name,\n in: location,\n required: location === \"path\" || node.required === true,\n description: text(node.description),\n schema: node.schema,\n style: text(node.style),\n explode: typeof node.explode === \"boolean\" ? node.explode : undefined,\n deprecated: node.deprecated === true,\n };\n}\n\n/** Reads the `<prefix>-csrf` extension. */\nfunction readCsrf(value: unknown): DocCsrf | undefined {\n if (!isSchemaObject(value)) {\n return undefined;\n }\n return {\n header: text(value.header) ?? null,\n requireJson: value.requireJson === true,\n };\n}\n\n/** Reads one operation of a path item. */\nfunction readOperation(\n doc: SpecDocument,\n path: string,\n method: string,\n raw: Record<string, unknown>,\n shared: unknown[],\n prefixes: readonly string[],\n): DocOperation {\n const parameters = new Map<string, DocParameter>();\n for (const item of [\n ...shared,\n ...(Array.isArray(raw.parameters) ? raw.parameters : []),\n ]) {\n const parameter = readParameter(doc, item);\n if (parameter) {\n parameters.set(`${parameter.in}:${parameter.name}`, parameter);\n }\n }\n const body = component(doc, raw.requestBody).node;\n const responses: DocResponse[] = [];\n if (isSchemaObject(raw.responses)) {\n for (const [status, value] of Object.entries(raw.responses)) {\n const { node, ref } = component(doc, value);\n responses.push({\n status,\n description: text(node?.description) ?? \"\",\n content: mediaTypes(node?.content),\n codes: strings(\n node === undefined ? undefined : extensionOf(node, \"codes\", prefixes),\n ),\n ...(ref ? { ref } : {}),\n });\n }\n }\n return {\n id: text(raw.operationId) ?? `${method.toUpperCase()} ${path}`,\n method: method.toUpperCase(),\n path,\n summary: text(raw.summary),\n description: text(raw.description),\n tags: strings(raw.tags),\n deprecated: raw.deprecated === true,\n parameters: [...parameters.values()],\n requestBody: body\n ? {\n required: body.required === true,\n description: text(body.description),\n content: mediaTypes(body.content),\n }\n : undefined,\n responses,\n action: text(extensionOf(raw, \"action\", prefixes)),\n mutation: extensionOf(raw, \"mutation\", prefixes) === true,\n requires: strings(extensionOf(raw, \"requires\", prefixes)),\n csrf: readCsrf(extensionOf(raw, \"csrf\", prefixes)),\n };\n}\n\n/** Every operation of the document, in path order and, within a path, method order. */\nexport function listOperations(\n doc: SpecDocument,\n options: HttpModelOptions = {},\n): DocOperation[] {\n const prefixes = options.extensions ?? DEFAULT_EXTENSION_PREFIXES;\n const operations: DocOperation[] = [];\n if (!isSchemaObject(doc.paths)) {\n return operations;\n }\n for (const [path, item] of Object.entries(doc.paths)) {\n const pathItem = component(doc, item).node;\n if (!pathItem) {\n continue;\n }\n const shared = Array.isArray(pathItem.parameters)\n ? pathItem.parameters\n : [];\n for (const method of OPERATION_METHODS) {\n const raw = pathItem[method];\n if (isSchemaObject(raw)) {\n operations.push(\n readOperation(doc, path, method, raw, shared, prefixes),\n );\n }\n }\n }\n return operations;\n}\n\n/** The document's header: title, version, description, servers. */\nexport function readInfo(doc: SpecDocument): DocInfo {\n const info = isSchemaObject(doc.info) ? doc.info : {};\n const servers = Array.isArray(doc.servers)\n ? doc.servers.flatMap((server): DocServer[] => {\n const url = isSchemaObject(server) ? text(server.url) : undefined;\n return url === undefined\n ? []\n : [\n {\n url,\n description: text(\n (server as Record<string, unknown>).description,\n ),\n },\n ];\n })\n : [];\n return {\n openapi: text(doc.openapi) ?? \"\",\n title: text(info.title) ?? \"API\",\n version: text(info.version) ?? \"\",\n description: text(info.description),\n servers: servers.length > 0 ? servers : [{ url: \"/\" }],\n };\n}\n\n/**\n * Operations by tag, in the document's `tags` order; tags an operation uses\n * but the list omits follow in first-use order, then `Other` for untagged\n * operations. An operation with two tags is listed under both.\n */\nexport function groupByTag(\n doc: SpecDocument,\n operations: readonly DocOperation[],\n): DocTagGroup[] {\n const groups = new Map<string, DocTagGroup>();\n if (Array.isArray(doc.tags)) {\n for (const tag of doc.tags) {\n const name = isSchemaObject(tag) ? text(tag.name) : undefined;\n if (name !== undefined && !groups.has(name)) {\n groups.set(name, {\n name,\n description: text((tag as Record<string, unknown>).description),\n operations: [],\n });\n }\n }\n }\n const other: DocOperation[] = [];\n for (const operation of operations) {\n if (operation.tags.length === 0) {\n other.push(operation);\n }\n for (const tag of operation.tags) {\n let group = groups.get(tag);\n if (!group) {\n group = { name: tag, operations: [] };\n groups.set(tag, group);\n }\n group.operations.push(operation);\n }\n }\n const result = [...groups.values()].filter(\n (group) => group.operations.length > 0,\n );\n if (other.length > 0) {\n result.push({ name: \"Other\", operations: other });\n }\n return result;\n}\n\n/** One section of the sidebar: a document's `x-tagGroups` entry, over the tags it names. */\nexport interface DocSection {\n /** The section's name. */\n name: string;\n /** Its tags with their operations, in the section's order. */\n tags: DocTagGroup[];\n}\n\n/** A section with a React key unique among its siblings, and its position. */\nexport interface KeyedSection {\n /** Its name, suffixed `#2`, `#3`… when a hand-written document repeats one. */\n key: string;\n /** Its position in the document's order (for heading ids). */\n index: number;\n /** The section. */\n section: DocSection;\n}\n\n/**\n * Keys `sections` for rendering: two sections may share a name in a\n * hand-written document, so the name alone is not a key, and a position is\n * not a stable one (`react/no-array-index-key`).\n */\nexport function keyedSections(sections: readonly DocSection[]): KeyedSection[] {\n const seen = new Map<string, number>();\n return sections.map((section, index) => {\n const count = (seen.get(section.name) ?? 0) + 1;\n seen.set(section.name, count);\n return {\n key: count === 1 ? section.name : `${section.name}#${count}`,\n index,\n section,\n };\n });\n}\n\n/** The section the tags no `x-tagGroups` entry names go in. */\nconst OTHER_SECTION = \"Other\";\n\n/**\n * The document's sections (`x-tagGroups`, Redoc's and Scalar's shape: `[{\n * name, tags }]`, the one bun-common's `generateOpenApi({ tagGroups })`\n * emits), each over its tags' groups, in the document's order; `undefined`\n * when the document declares none, so the sidebar stays one level deep. A\n * malformed entry is skipped, and a list with none well formed reads as none.\n *\n * Unlike Redoc, nothing is hidden: a group no section names (`Other`, the\n * untagged operations, among them) follows in a trailing `Other` section, or\n * in the document's section of that name when it has one. A tag in two\n * sections is listed in each; a section with no group (a search left none)\n * is dropped.\n */\nexport function groupBySection(\n doc: SpecDocument,\n groups: readonly DocTagGroup[],\n): DocSection[] | undefined {\n const raw = doc[\"x-tagGroups\"];\n if (!Array.isArray(raw)) {\n return undefined;\n }\n const declared = raw.flatMap(\n (entry: unknown): { name: string; tags: string[] }[] => {\n if (!isSchemaObject(entry)) {\n return [];\n }\n const name = text(entry.name);\n return name === undefined || !Array.isArray(entry.tags)\n ? []\n : [{ name, tags: strings(entry.tags) }];\n },\n );\n if (declared.length === 0) {\n return undefined;\n }\n const byName = new Map(groups.map((group) => [group.name, group]));\n const named = new Set<string>();\n const sections: DocSection[] = [];\n for (const { name, tags } of declared) {\n const section: DocSection = { name, tags: [] };\n for (const tag of tags) {\n const group = byName.get(tag);\n if (group && !section.tags.includes(group)) {\n section.tags.push(group);\n named.add(tag);\n }\n }\n sections.push(section);\n }\n const rest = groups.filter((group) => !named.has(group.name));\n if (rest.length > 0) {\n const other = sections.find((section) => section.name === OTHER_SECTION);\n if (other) {\n other.tags.push(...rest);\n } else {\n sections.push({ name: OTHER_SECTION, tags: rest });\n }\n }\n return sections.filter((section) => section.tags.length > 0);\n}\n\n/**\n * Whether an operation matches a search: every whitespace-separated term\n * must appear, ignoring case, in its path, id, summary, method or action.\n */\nexport function matchesSearch(operation: DocOperation, query: string): boolean {\n const terms = query.toLowerCase().split(/\\s+/).filter(Boolean);\n if (terms.length === 0) {\n return true;\n }\n const haystack = [\n operation.method,\n operation.path,\n operation.id,\n operation.summary ?? \"\",\n operation.action ?? \"\",\n ]\n .join(\" \")\n .toLowerCase();\n return terms.every((term) => haystack.includes(term));\n}\n\n/** Groups filtered by a search; groups left empty are dropped. */\nexport function filterGroups(\n groups: readonly DocTagGroup[],\n query: string,\n): DocTagGroup[] {\n return groups\n .map((group) => ({\n ...group,\n operations: group.operations.filter((operation) =>\n matchesSearch(operation, query),\n ),\n }))\n .filter((group) => group.operations.length > 0);\n}\n\n/**\n * A server URL as the reader can use it: a relative one (`/jobs-api`, the\n * API's default) is resolved against the origin the app talks to — the API\n * base's own origin when it is absolute, else the page's.\n */\nexport function resolveServerUrl(\n url: string,\n apiBase: string,\n pageOrigin: string,\n): string {\n const origin = /^https?:\\/\\//.test(apiBase)\n ? new URL(apiBase).origin\n : pageOrigin;\n try {\n return new URL(url, `${origin}/`).href.replace(/\\/$/, \"\");\n } catch {\n return url;\n }\n}\n\n/** The named component schemas, sorted as the document lists them. */\nexport function componentSchemaNames(doc: SpecDocument): string[] {\n const schemas = resolveRef(doc, \"#/components/schemas\");\n return isSchemaObject(schemas) ? Object.keys(schemas) : [];\n}\n\n/** The tone a method badge takes. */\nexport function methodTone(\n method: string,\n): \"info\" | \"success\" | \"warning\" | \"danger\" | \"neutral\" {\n switch (method) {\n case \"GET\":\n return \"info\";\n case \"POST\":\n return \"success\";\n case \"PUT\":\n case \"PATCH\":\n return \"warning\";\n case \"DELETE\":\n return \"danger\";\n default:\n return \"neutral\";\n }\n}\n\n/** The success responses (2xx): the note try-it shows when the status it received is not documented. */\nexport function successStatuses(operation: DocOperation): string[] {\n return operation.responses\n .map((response) => response.status)\n .filter((status) => /^2\\d\\d$/.test(status));\n}\n",
|
|
12
|
+
"/**\n * Constants of the viewer's own HTTP layer: which methods mutate (a try-it\n * confirms them), and which must carry `Content-Type: application/json` even\n * when bodiless. Generic HTTP, shared with no one: the documented API's own\n * constants are its own.\n */\n\n/** HTTP methods treated as mutations: every one but `GET`/`HEAD`/`OPTIONS`. */\nexport const MUTATING_METHODS: ReadonlySet<string> = new Set([\n \"POST\",\n \"PUT\",\n \"PATCH\",\n \"DELETE\",\n]);\n\n/** Methods that carry `Content-Type: application/json` even when bodiless. */\nexport const JSON_METHODS: ReadonlySet<string> = new Set([\n \"POST\",\n \"PUT\",\n \"PATCH\",\n]);\n\n/** The last instant a `Date` holds. */\nexport const MAX_DATE_MS = 8_640_000_000_000_000;\n",
|
|
13
|
+
"/**\n * Every failed request the viewer makes, whatever went wrong. An RFC 9457\n * problem response keeps its fields verbatim; anything else is mapped onto\n * the same shape so a screen has one thing to render.\n */\n\n/** A validation issue inside a problem. */\nexport interface ProblemIssueDto {\n /** Which part of the request failed. */\n target: string;\n /** Dotted path within the target, or `\"\"` for the target itself. */\n path: string;\n /** Human-readable description. */\n message: string;\n}\n\n/** An `application/problem+json` body, as far as the viewer reads it. */\nexport interface ProblemDto {\n /** The HTTP status. */\n status: number;\n /** The machine code. */\n code: string;\n /** The short title. */\n title: string;\n /** The human detail, when there is one. */\n detail?: string;\n /** The problem's `type` URI, when there is one. */\n type?: string;\n /** Validation issues. */\n issues?: ProblemIssueDto[];\n /** Safe, whitelisted context. */\n context?: Record<string, unknown>;\n /** The request path that failed. */\n instance?: string;\n}\n\n/** Where an {@link ApiError} came from. */\nexport type ApiErrorKind =\n /** The API answered with an `application/problem+json` body. */\n | \"problem\"\n /** The API (or something in front of it) answered an error that was not problem+json. */\n | \"http\"\n /** No response arrived: offline, DNS, CORS, connection reset. */\n | \"network\"\n /** A 2xx whose body was not the JSON it should have been. */\n | \"parse\";\n\n/** Fields an {@link ApiError} is built from. */\nexport interface ApiErrorInit {\n /** Where the error came from. */\n kind: ApiErrorKind;\n /** HTTP status, or `0` for a network error. */\n status: number;\n /** Machine code: the problem's `code`, or `NETWORK_ERROR` / `HTTP_<status>` / `INVALID_RESPONSE`. */\n code: string;\n /** Short title. */\n title: string;\n /** Human detail, when there is one. */\n detail?: string;\n /** The problem's `type` URI, when it was a problem. */\n type?: string;\n /** Validation issues. */\n issues?: ProblemIssueDto[];\n /** The problem's whitelisted context. */\n context?: Record<string, unknown>;\n /** The request path that failed. */\n instance?: string;\n /** The underlying error (network/parse failures). */\n cause?: unknown;\n}\n\n/** One failed request. */\nexport class ApiError extends Error {\n /** Where the error came from. */\n readonly kind: ApiErrorKind;\n /** HTTP status, or `0` for a network error. */\n readonly status: number;\n /** Machine code, e.g. `HTTP_404`. */\n readonly code: string;\n /** Short, stable title. */\n readonly title: string;\n /** Human detail for this occurrence. */\n readonly detail: string | undefined;\n /** The problem's `type` URI, when it was a problem. */\n readonly type: string | undefined;\n /** Validation issues. */\n readonly issues: ProblemIssueDto[];\n /** Whitelisted context. */\n readonly context: Record<string, unknown>;\n /** The request path that failed. */\n readonly instance: string | undefined;\n\n constructor(init: ApiErrorInit) {\n super(init.detail ?? init.title, { cause: init.cause });\n this.name = \"ApiError\";\n this.kind = init.kind;\n this.status = init.status;\n this.code = init.code;\n this.title = init.title;\n this.detail = init.detail;\n this.type = init.type;\n this.issues = init.issues ?? [];\n this.context = init.context ?? {};\n this.instance = init.instance;\n }\n\n /** Builds one from a problem body. */\n static fromProblem(problem: ProblemDto): ApiError {\n return new ApiError({\n kind: \"problem\",\n status: problem.status,\n code: problem.code,\n title: problem.title,\n detail: problem.detail,\n type: problem.type,\n issues: problem.issues,\n context: problem.context,\n instance: problem.instance,\n });\n }\n\n /** Whether the caller is not signed in or not allowed (401/403). */\n get isAuth(): boolean {\n return this.status === 401 || this.status === 403;\n }\n}\n\n/** Whether a value is an {@link ApiError}. */\nexport function isApiError(value: unknown): value is ApiError {\n return value instanceof ApiError;\n}\n\n/** Whether a parsed body has the fields a problem must carry. */\nexport function isProblem(value: unknown): value is ProblemDto {\n if (typeof value !== \"object\" || value === null) {\n return false;\n }\n const record = value as Record<string, unknown>;\n return (\n typeof record.status === \"number\" &&\n typeof record.code === \"string\" &&\n typeof record.title === \"string\"\n );\n}\n",
|
|
14
|
+
"import { JSON_METHODS, MUTATING_METHODS } from \"./contract\";\nimport { ApiError, isProblem } from \"./errors\";\n\n/**\n * The viewer's HTTP client: it fetches the document, and sends the try-it\n * panel's requests. Same-origin credentials, `Accept: application/json`\n * always, `Content-Type: application/json` on POST/PUT/PATCH (bodiless ones\n * included), and the configured CSRF header on every mutation.\n */\n\n/** An HTTP method the client sends. */\nexport type HttpMethod = \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\";\n\n/** A scalar query value. */\nexport type QueryScalar = string | number | boolean;\n\n/**\n * A query-string value: arrays become repeated keys (`state=a&state=b`), an\n * empty array becomes `key=`, and `undefined`/`null` are left out.\n */\nexport type QueryValue =\n QueryScalar | readonly QueryScalar[] | null | undefined;\n\n/** The query of one request. */\nexport type QueryParams = Readonly<Record<string, QueryValue>>;\n\n/** Options of one {@link DocsClient.request}. */\nexport interface RequestOptions {\n /** Query parameters; see {@link QueryValue}. */\n query?: QueryParams;\n /** A JSON body. `undefined` sends none (the `Content-Type` still goes on POST/PUT/PATCH). */\n body?: unknown;\n /** Aborts the request (TanStack Query passes one). An abort rejects with the fetch's own `AbortError`, never an {@link ApiError}. */\n signal?: AbortSignal;\n}\n\n/** One response as {@link DocsClient.requestRaw} resolves it. */\nexport interface RawResponse {\n /** Whether the status is 2xx. */\n ok: boolean;\n /** The HTTP status received. */\n status: number;\n /** The status text received (often `\"\"` over HTTP/2). */\n statusText: string;\n /** The response headers the caller can see, lower-cased names in alphabetical order. A browser hides `Set-Cookie`, and cross-origin ones not in `Access-Control-Expose-Headers`. */\n headers: Record<string, string>;\n /** The body: parsed JSON when the `Content-Type` is JSON and it parses, the text otherwise, `undefined` when empty. */\n body: unknown;\n /** For a non-2xx status, the {@link ApiError} `request` would have thrown (a problem's fields verbatim); `undefined` on success. */\n error: ApiError | undefined;\n /** Milliseconds from sending to the body having been read. */\n durationMs: number;\n}\n\n/** The `fetch` the client calls; injectable for tests. */\nexport type FetchLike = (input: string, init: RequestInit) => Promise<Response>;\n\n/** A client for the one server the viewer talks to. */\nexport interface DocsClient {\n /** The base URL requests are prefixed with. */\n readonly base: string;\n /** Sends one request; resolves the parsed JSON (or `undefined` for a 204), rejects with an {@link ApiError}. */\n request: <T>(\n method: HttpMethod,\n path: string,\n options?: RequestOptions,\n ) => Promise<T>;\n /**\n * Sends one request like {@link DocsClient.request}, with the same headers,\n * credentials and problem+json parsing, and resolves the whole response\n * instead of only its body. An error status **resolves** (with `ok: false`\n * and the {@link ApiError} `request` would have thrown in `error`); only a\n * network failure rejects (an {@link ApiError} of kind `network`), and an\n * abort rejects with the fetch's own `AbortError`.\n */\n requestRaw: (\n method: HttpMethod,\n path: string,\n options?: RequestOptions,\n ) => Promise<RawResponse>;\n}\n\n/** The value sent in the CSRF header. An API accepts any non-empty value. */\nexport const CSRF_HEADER_VALUE = \"1\";\n\n/** Serialises a query: repeated keys for arrays, `undefined`/`null` skipped. Returns `\"\"` or `\"?…\"`. */\nexport function serializeQuery(query: QueryParams | undefined): string {\n if (!query) {\n return \"\";\n }\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries(query)) {\n if (value === undefined || value === null) {\n continue;\n }\n if (Array.isArray(value)) {\n const items = value as readonly QueryScalar[];\n if (items.length === 0) {\n params.append(key, \"\");\n }\n for (const item of items) {\n params.append(key, String(item));\n }\n continue;\n }\n params.append(key, String(value));\n }\n const text = params.toString();\n return text ? `?${text}` : \"\";\n}\n\n/** Percent-encodes one path segment (`/` becomes `%2F`). */\nexport function segment(value: string): string {\n return encodeURIComponent(value);\n}\n\n/** Whether a `Content-Type` names JSON (`application/json` or any `+json`). */\nfunction isJsonType(contentType: string): boolean {\n const media = contentType.split(\";\")[0]?.trim().toLowerCase() ?? \"\";\n return media === \"application/json\" || media.endsWith(\"+json\");\n}\n\n/** Whether an error is the fetch's abort, which must propagate untouched. */\nfunction isAbort(error: unknown, signal: AbortSignal | undefined): boolean {\n return (\n signal?.aborted === true ||\n (error instanceof Error && error.name === \"AbortError\") ||\n (typeof DOMException !== \"undefined\" &&\n error instanceof DOMException &&\n error.name === \"AbortError\")\n );\n}\n\n/** Parses JSON text, or returns `undefined` when it is not JSON. */\nfunction tryParse(text: string): { value: unknown } | undefined {\n try {\n return { value: JSON.parse(text) };\n } catch {\n return undefined;\n }\n}\n\n/** Turns an error response into an {@link ApiError}. */\nfunction errorFromResponse(response: Response, text: string, path: string) {\n const parsed = isJsonType(response.headers.get(\"content-type\") ?? \"\")\n ? tryParse(text)\n : undefined;\n if (parsed && isProblem(parsed.value)) {\n return ApiError.fromProblem({ ...parsed.value, status: response.status });\n }\n const snippet = text.trim().slice(0, 300);\n return new ApiError({\n kind: \"http\",\n status: response.status,\n code: `HTTP_${response.status}`,\n title: response.statusText || `Request failed (${response.status})`,\n detail: snippet || undefined,\n instance: path,\n });\n}\n\n/** Options of {@link createDocsClient}. */\nexport interface DocsClientOptions {\n /** The base URL requests are prefixed with (the document's resolved server, or the document's own URL for the document fetch). */\n base: string;\n /** The CSRF header mutations carry, or `null`. */\n csrfHeader: string | null;\n /** The `fetch` to use. Defaults to `globalThis.fetch`, looked up per call so a test can replace it. */\n fetch?: FetchLike;\n}\n\n/**\n * Creates the client for one base URL. A relative `path` joins the base; a\n * `path` that is itself an absolute URL is sent as it is (a document URL the\n * operator configured).\n */\nexport function createDocsClient(options: DocsClientOptions): DocsClient {\n const base = options.base.replace(/\\/+$/, \"\");\n\n /** Builds the URL and `RequestInit` of one request: every header rule lives here. */\n function prepare(\n method: HttpMethod,\n path: string,\n { query, body, signal }: RequestOptions,\n ): { url: string; init: RequestInit } {\n const target = /^https?:\\/\\//.test(path) ? path : `${base}${path}`;\n const url = `${target}${serializeQuery(query)}`;\n const headers: Record<string, string> = { Accept: \"application/json\" };\n if (JSON_METHODS.has(method)) {\n headers[\"Content-Type\"] = \"application/json\";\n }\n if (options.csrfHeader && MUTATING_METHODS.has(method)) {\n headers[options.csrfHeader] = CSRF_HEADER_VALUE;\n }\n const init: RequestInit = {\n method,\n headers,\n credentials: \"same-origin\",\n };\n if (body !== undefined) {\n init.body = JSON.stringify(body);\n }\n if (signal) {\n init.signal = signal;\n }\n return { url, init };\n }\n\n /** Calls `fetch`; a failure other than an abort becomes a `network` {@link ApiError}. */\n async function send(\n url: string,\n init: RequestInit,\n path: string,\n ): Promise<Response> {\n const fetcher = options.fetch ?? globalThis.fetch;\n try {\n return await fetcher(url, init);\n } catch (error) {\n if (isAbort(error, init.signal ?? undefined)) {\n throw error;\n }\n throw new ApiError({\n kind: \"network\",\n status: 0,\n code: \"NETWORK_ERROR\",\n title: \"The server could not be reached\",\n detail: error instanceof Error ? error.message : String(error),\n instance: path,\n cause: error,\n });\n }\n }\n\n async function request<T>(\n method: HttpMethod,\n path: string,\n requestOptions: RequestOptions = {},\n ): Promise<T> {\n const { url, init } = prepare(method, path, requestOptions);\n const response = await send(url, init, path);\n\n if (response.status === 204 || response.status === 205) {\n return undefined as T;\n }\n const text = await response.text();\n if (!response.ok) {\n throw errorFromResponse(response, text, path);\n }\n if (text === \"\") {\n return undefined as T;\n }\n const parsed = isJsonType(response.headers.get(\"content-type\") ?? \"\")\n ? tryParse(text)\n : undefined;\n if (!parsed) {\n throw new ApiError({\n kind: \"parse\",\n status: response.status,\n code: \"INVALID_RESPONSE\",\n title: \"The server answered something other than JSON\",\n detail: text.trim().slice(0, 300) || undefined,\n instance: path,\n });\n }\n return parsed.value as T;\n }\n\n async function requestRaw(\n method: HttpMethod,\n path: string,\n requestOptions: RequestOptions = {},\n ): Promise<RawResponse> {\n const { url, init } = prepare(method, path, requestOptions);\n const started = performance.now();\n const response = await send(url, init, path);\n const text =\n response.status === 204 || response.status === 205\n ? \"\"\n : await response.text();\n const durationMs = performance.now() - started;\n const parsed =\n text !== \"\" && isJsonType(response.headers.get(\"content-type\") ?? \"\")\n ? tryParse(text)\n : undefined;\n const headers: Record<string, string> = {};\n for (const [name, value] of [...response.headers.entries()].sort(\n ([a, b]) => (a < b ? -1 : a > b ? 1 : 0),\n )) {\n headers[name.toLowerCase()] = value;\n }\n return {\n ok: response.ok,\n status: response.status,\n statusText: response.statusText,\n headers,\n body: parsed ? parsed.value : text === \"\" ? undefined : text,\n error: response.ok ? undefined : errorFromResponse(response, text, path),\n durationMs,\n };\n }\n\n return { base, request, requestRaw };\n}\n",
|
|
15
|
+
"import type { HttpMethod, QueryScalar, QueryValue } from \"../../../api/client\";\nimport type { SchemaRoot } from \"../schema/resolve\";\nimport type { DocOperation, DocParameter } from \"./model\";\nimport {\n CSRF_HEADER_VALUE,\n segment,\n serializeQuery,\n} from \"../../../api/client\";\nimport { JSON_METHODS, MUTATING_METHODS } from \"../../../api/contract\";\nimport { declaredTypes, deref, isSchemaObject } from \"../schema/resolve\";\nimport { schemaSkeleton } from \"../schema/skeleton\";\nimport { successStatuses } from \"./model\";\n\n/**\n * The try-it panel's logic: which input each parameter gets, the request a\n * filled form makes, whether the panel may send it at all, and the curl and\n * fetch() snippets of that exact request. The request goes through the\n * viewer's own client, so the headers here restate its rules (for the\n * snippets) rather than decide them.\n */\n\n/** Methods the viewer's client can send. */\nconst CLIENT_METHODS: ReadonlySet<string> = new Set([\n \"GET\",\n \"POST\",\n \"PUT\",\n \"PATCH\",\n \"DELETE\",\n]);\n\n/** Action verbs whose try-it needs a typed confirmation: they delete, discard or irreversibly end work. */\nconst DESTRUCTIVE_VERBS: ReadonlySet<string> = new Set([\n \"remove\",\n \"drain\",\n \"clean\",\n \"kill\",\n \"fail\",\n]);\n\n/** The input a parameter is edited with. */\nexport type InputKind =\n | { kind: \"text\" }\n | { kind: \"number\"; integer: boolean; min?: number; max?: number }\n | { kind: \"boolean\" }\n | { kind: \"enum\"; options: string[] }\n | { kind: \"list\"; item: \"text\" | \"integer\" | \"number\" }\n | { kind: \"enum-list\"; options: string[] };\n\n/** A form value: text for scalars and comma lists, the ticked options for an enum list. */\nexport type FieldValue = string | readonly string[];\n\n/** What the form holds, keyed by {@link paramKey}. */\nexport type TryItValues = Readonly<Record<string, FieldValue>>;\n\n/** A parameter's form key: `path:queue`, `query:state`. */\nexport function paramKey(parameter: Pick<DocParameter, \"in\" | \"name\">): string {\n return `${parameter.in}:${parameter.name}`;\n}\n\n/** The scalar item type of a schema (`$ref`s followed). */\nfunction scalarType(schema: unknown, root: SchemaRoot): string | undefined {\n const node = deref(schema, root).schema;\n if (!isSchemaObject(node)) {\n return undefined;\n }\n return declaredTypes(node).find((type) => type !== \"null\");\n}\n\n/** The string values of an enum schema, when it is one. */\nfunction enumOptions(schema: unknown, root: SchemaRoot): string[] | undefined {\n const node = deref(schema, root).schema;\n if (!isSchemaObject(node) || !Array.isArray(node.enum)) {\n return undefined;\n }\n return node.enum\n .filter((value) => value !== null)\n .map((value) => String(value));\n}\n\n/** How a parameter is edited, from its schema. */\nexport function inputKind(\n parameter: DocParameter,\n root: SchemaRoot,\n): InputKind {\n const node = deref(parameter.schema, root).schema;\n const schema = isSchemaObject(node) ? node : {};\n const type = scalarType(schema, root);\n if (type === \"array\") {\n const options = enumOptions(schema.items, root);\n if (options) {\n return { kind: \"enum-list\", options };\n }\n const item = scalarType(schema.items, root);\n return {\n kind: \"list\",\n item: item === \"integer\" || item === \"number\" ? item : \"text\",\n };\n }\n const options = enumOptions(schema, root);\n if (options) {\n return { kind: \"enum\", options };\n }\n if (type === \"boolean\") {\n return { kind: \"boolean\" };\n }\n if (type === \"integer\" || type === \"number\") {\n return {\n kind: \"number\",\n integer: type === \"integer\",\n min: typeof schema.minimum === \"number\" ? schema.minimum : undefined,\n max: typeof schema.maximum === \"number\" ? schema.maximum : undefined,\n };\n }\n return { kind: \"text\" };\n}\n\n/** The parameters the form edits: path and query. Header parameters are the client's to send. */\nexport function formParameters(operation: DocOperation): DocParameter[] {\n return operation.parameters.filter(\n (parameter) => parameter.in === \"path\" || parameter.in === \"query\",\n );\n}\n\n/** The schema of the JSON body, when the operation takes one worth editing. */\nexport function bodySchema(operation: DocOperation): unknown {\n const json = operation.requestBody?.content.find(\n (media) => media.contentType === \"application/json\",\n );\n const schema = json?.schema;\n // The API's bodiless POSTs declare a body only to require the media type;\n // its schema is a bare description (\"Ignored. Send no body, or `{}`.\").\n if (\n !isSchemaObject(schema) ||\n Object.keys(schema).every(\n (key) => key === \"description\" || key.startsWith(\"x-\"),\n )\n ) {\n return undefined;\n }\n return schema;\n}\n\n/** The editor's starting text: the body schema's required skeleton, or `\"\"`. */\nexport function initialBodyText(\n operation: DocOperation,\n root: SchemaRoot,\n): string {\n const schema = bodySchema(operation);\n return schema === undefined\n ? \"\"\n : JSON.stringify(schemaSkeleton(schema, root), null, 2);\n}\n\n/** A parameter's form value as the query takes it, or an error message. */\nfunction coerce(\n parameter: DocParameter,\n kind: InputKind,\n value: FieldValue | undefined,\n): { value: QueryValue } | { error: string } {\n if (kind.kind === \"enum-list\") {\n const picked = Array.isArray(value) ? value : [];\n return { value: picked.length > 0 ? [...picked] : undefined };\n }\n const raw = typeof value === \"string\" ? value.trim() : \"\";\n if (raw === \"\") {\n return parameter.required\n ? { error: `${parameter.name} is required` }\n : { value: undefined };\n }\n switch (kind.kind) {\n case \"number\": {\n const number = Number(raw);\n if (\n !Number.isFinite(number) ||\n (kind.integer && !Number.isInteger(number))\n ) {\n return {\n error: `${parameter.name} must be ${kind.integer ? \"an integer\" : \"a number\"}`,\n };\n }\n return { value: number };\n }\n case \"boolean\":\n return raw === \"true\" || raw === \"false\"\n ? { value: raw === \"true\" }\n : { error: `${parameter.name} must be true or false` };\n case \"list\": {\n const items = raw\n .split(\",\")\n .map((item) => item.trim())\n .filter(Boolean);\n const values: QueryScalar[] = [];\n for (const item of items) {\n if (kind.item === \"text\") {\n values.push(item);\n continue;\n }\n const number = Number(item);\n if (\n !Number.isFinite(number) ||\n (kind.item === \"integer\" && !Number.isInteger(number))\n ) {\n return {\n error: `${parameter.name}: \"${item}\" is not ${kind.item === \"integer\" ? \"an integer\" : \"a number\"}`,\n };\n }\n values.push(number);\n }\n return { value: values.length > 0 ? values : undefined };\n }\n default:\n return { value: raw };\n }\n}\n\n/** A request the panel sends, in the terms of `ApiClient.request`. */\nexport interface TryItRequest {\n /** The method. */\n method: HttpMethod;\n /** The path under the API base, path parameters percent-encoded. */\n path: string;\n /** The query; arrays go as repeated keys. */\n query: Record<string, QueryValue>;\n /** The JSON body, or `undefined` for none. */\n body: unknown;\n}\n\n/** The outcome of {@link buildRequest}. */\nexport type BuildResult =\n | { ok: true; request: TryItRequest }\n | { ok: false; errors: Record<string, string> };\n\n/**\n * The request a filled form makes: path parameters substituted (each\n * percent-encoded as one segment, so `a/b` is `a%2Fb`), query values typed\n * from their schemas, and the body as parsed. `body` is the editor's parsed\n * value, `undefined` for an empty editor.\n */\nexport function buildRequest(\n operation: DocOperation,\n root: SchemaRoot,\n values: TryItValues,\n body: unknown,\n): BuildResult {\n const errors: Record<string, string> = {};\n if (!CLIENT_METHODS.has(operation.method)) {\n return {\n ok: false,\n errors: { method: `${operation.method} cannot be sent from here` },\n };\n }\n let path = operation.path;\n const query: Record<string, QueryValue> = {};\n for (const parameter of formParameters(operation)) {\n const key = paramKey(parameter);\n const result = coerce(parameter, inputKind(parameter, root), values[key]);\n if (\"error\" in result) {\n errors[key] = result.error;\n continue;\n }\n if (parameter.in === \"path\") {\n path = path\n .split(`{${parameter.name}}`)\n .join(segment(String(result.value)));\n } else if (result.value !== undefined) {\n query[parameter.name] = result.value;\n }\n }\n if (\n body === undefined &&\n operation.requestBody?.required &&\n bodySchema(operation) !== undefined\n ) {\n errors.body = \"A body is required\";\n }\n if (Object.keys(errors).length > 0) {\n return { ok: false, errors };\n }\n return {\n ok: true,\n request: { method: operation.method as HttpMethod, path, query, body },\n };\n}\n\n/**\n * The headers the viewer's client sends for a method — restated from\n * `createDocsClient` for the snippets: `Accept` always, `Content-Type` on\n * POST/PUT/PATCH (body or not), and the CSRF header on every mutation when\n * the viewer is configured with one.\n */\nexport function clientHeaders(\n method: HttpMethod,\n csrfHeader: string | null,\n): Record<string, string> {\n const headers: Record<string, string> = { Accept: \"application/json\" };\n if (JSON_METHODS.has(method)) {\n headers[\"Content-Type\"] = \"application/json\";\n }\n if (csrfHeader && MUTATING_METHODS.has(method)) {\n headers[csrfHeader] = CSRF_HEADER_VALUE;\n }\n return headers;\n}\n\n/** Where the snippets point. */\nexport interface SnippetContext {\n /** The app's API base (`/jobs-api`, or an absolute URL). */\n apiBase: string;\n /** The page's origin, for an absolute curl URL. */\n origin: string;\n /** The UI's CSRF header, or `null`. */\n csrfHeader: string | null;\n}\n\n/** The URL the client requests: base + path + query. */\nexport function requestUrl(request: TryItRequest, apiBase: string): string {\n return `${apiBase.replace(/\\/+$/, \"\")}${request.path}${serializeQuery(request.query)}`;\n}\n\n/** Quotes a word for a POSIX shell. */\nfunction shellQuote(value: string): string {\n return `'${value.replace(/'/g, `'\\\\''`)}'`;\n}\n\n/** A `curl` command sending exactly what the panel sends. */\nexport function curlSnippet(\n request: TryItRequest,\n context: SnippetContext,\n): string {\n const relative = requestUrl(request, context.apiBase);\n const url = /^https?:\\/\\//.test(relative)\n ? relative\n : `${context.origin}${relative}`;\n const lines = [`curl -X ${request.method} ${shellQuote(url)}`];\n for (const [name, value] of Object.entries(\n clientHeaders(request.method, context.csrfHeader),\n )) {\n lines.push(` -H ${shellQuote(`${name}: ${value}`)}`);\n }\n if (request.body !== undefined) {\n lines.push(` --data ${shellQuote(JSON.stringify(request.body))}`);\n }\n return [\n \"# The browser also sends this site's cookies (credentials: same-origin).\",\n \"# If the API's authorize reads a session, add it: -b 'name=value'\",\n lines.join(\" \\\\\\n\"),\n ].join(\"\\n\");\n}\n\n/** A `fetch()` call sending exactly what the panel sends, from a page on this origin. */\nexport function fetchSnippet(\n request: TryItRequest,\n context: SnippetContext,\n): string {\n const headers = clientHeaders(request.method, context.csrfHeader);\n const headerLines = Object.entries(headers)\n .map(\n ([name, value]) =>\n ` ${JSON.stringify(name)}: ${JSON.stringify(value)},`,\n )\n .join(\"\\n\");\n const lines = [\n `const response = await fetch(${JSON.stringify(requestUrl(request, context.apiBase))}, {`,\n ` method: ${JSON.stringify(request.method)},`,\n \" headers: {\",\n headerLines,\n \" },\",\n ` credentials: \"same-origin\",`,\n ];\n if (request.body !== undefined) {\n lines.push(\n ` body: JSON.stringify(${JSON.stringify(request.body, null, 2).replace(/\\n/g, \"\\n \")}),`,\n );\n }\n lines.push(\"});\", \"const data = await response.json();\");\n return lines.join(\"\\n\");\n}\n\n/**\n * Whether sending an operation needs a TYPED confirmation: a DELETE, or an\n * action whose verb removes, discards or irreversibly ends work\n * (`jobs.remove`, `queues.drain`, `queues.clean`, `runners.kill`,\n * `repeatables.remove`, `jobs.fail`: a failed job is dead for good).\n */\nexport function isDestructive(operation: DocOperation): boolean {\n if (operation.method === \"DELETE\") {\n return true;\n }\n const verb = operation.action?.split(\".\").pop() ?? \"\";\n return DESTRUCTIVE_VERBS.has(verb);\n}\n\n/** Whether the panel may send, and why not. */\nexport type TryItGate = { enabled: true } | { enabled: false; reason: string };\n\n/** What {@link tryItGate} decides from. */\nexport interface GateInputs {\n /** `meta.readOnly`. */\n readOnly: boolean;\n /** Whether the caller holds the operation's action (untargeted permissions). */\n permitted: boolean;\n}\n\n/**\n * The panel is off for a mutation on a read-only API, and for an action the\n * caller does not hold. Advisory, like every permission marker here:\n * `authorize` may decide per target, and the API's answer stays final.\n */\nexport function tryItGate(\n operation: DocOperation,\n inputs: GateInputs,\n): TryItGate {\n if (!CLIENT_METHODS.has(operation.method)) {\n return {\n enabled: false,\n reason: `The viewer's client does not send ${operation.method}.`,\n };\n }\n // Kept although a read-only `createJobsApi` neither routes nor documents\n // its mutations: this reason shows only for a document that lists one\n // anyway, a stale or foreign OpenAPI document (a cached copy from before\n // the API went read-only, or another API's). The API would refuse the\n // send, so the panel says why instead of offering it.\n if (operation.mutation && inputs.readOnly) {\n return {\n enabled: false,\n reason: \"This API is read-only: it refuses every change.\",\n };\n }\n if (operation.action !== undefined && !inputs.permitted) {\n return {\n enabled: false,\n reason: `You do not have the ${operation.action} permission.`,\n };\n }\n return { enabled: true };\n}\n\n/**\n * The note try-it shows when the status received is not one the operation\n * documents, naming the documented success statuses (`2xx` when it documents\n * none); `undefined` when the status is documented.\n */\nexport function documentedNote(\n operation: DocOperation,\n status: number,\n): string | undefined {\n if (\n operation.responses.some((response) => response.status === String(status))\n ) {\n return undefined;\n }\n const success = successStatuses(operation);\n return `Not a documented status. Documented success: ${success.length > 0 ? success.join(\" / \") : \"2xx\"}.`;\n}\n",
|
|
16
|
+
"import type { ReactNode } from \"react\";\nimport { cx } from \"./classNames\";\n\n/** Colour of a {@link Badge}. */\nexport type BadgeTone =\n \"neutral\" | \"info\" | \"success\" | \"warning\" | \"danger\" | \"accent\";\n\n/** Props of {@link Badge}. */\nexport interface BadgeProps {\n /** Colour. Defaults to `\"neutral\"`. */\n tone?: BadgeTone;\n /** A tooltip / longer explanation. */\n title?: string;\n /** Extra class names. */\n className?: string;\n /**\n * `data-testid` for the badge, when a test or an example must find this one\n * among several — by what it is, rather than by its position.\n */\n testId?: string;\n /** The text. */\n children: ReactNode;\n}\n\n/** A small status label. */\nexport function Badge({\n tone = \"neutral\",\n title,\n className,\n testId,\n children,\n}: BadgeProps) {\n return (\n <span\n className={cx(\"badge\", `badge-${tone}`, className)}\n title={title}\n data-testid={testId}\n >\n {children}\n </span>\n );\n}\n\n/** Props of {@link Chip}. */\nexport interface ChipProps {\n /** What the value is, e.g. `\"namespace\"`. */\n label: string;\n /** The value. */\n value: ReactNode;\n /** A tooltip. */\n title?: string;\n}\n\n/** A label/value pair, e.g. `namespace orders`. */\nexport function Chip({ label, value, title }: ChipProps) {\n return (\n <span\n className=\"chip\"\n title={title}\n >\n <span className=\"chip-label\">{label}</span>\n <span className=\"chip-value\">{value}</span>\n </span>\n );\n}\n",
|
|
17
|
+
"import type { ReactNode } from \"react\";\nimport { useId } from \"react\";\nimport { cx } from \"./classNames\";\n\n/** Props of {@link Card}. */\nexport interface CardProps {\n /** Heading, rendered as an `<h2>` that labels the card's region. */\n title?: ReactNode;\n /** Controls shown beside the heading. */\n actions?: ReactNode;\n /** Extra class names. */\n className?: string;\n /** The content. */\n children: ReactNode;\n}\n\n/** A bordered surface; with a `title` it is a labelled `<section>`. */\nexport function Card({ title, actions, className, children }: CardProps) {\n const headingId = useId();\n return (\n <section\n className={cx(\"card\", className)}\n aria-labelledby={title ? headingId : undefined}\n >\n {(title || actions) && (\n <div className=\"card-header\">\n {title && (\n <h2\n id={headingId}\n className=\"card-title\"\n >\n {title}\n </h2>\n )}\n {actions && <div className=\"card-actions\">{actions}</div>}\n </div>\n )}\n <div className=\"card-body\">{children}</div>\n </section>\n );\n}\n",
|
|
18
|
+
"import type { ButtonHTMLAttributes, Ref } from \"react\";\nimport { cx } from \"./classNames\";\n\n/** Visual weight of a {@link Button}. */\nexport type ButtonVariant = \"primary\" | \"secondary\" | \"ghost\" | \"danger\";\n\n/** Props of {@link Button}. */\nexport interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n /** Visual weight. Defaults to `\"secondary\"`. */\n variant?: ButtonVariant;\n /** Size. Defaults to `\"md\"`. */\n size?: \"sm\" | \"md\";\n /** The underlying `<button>`, e.g. for a dialog's initial focus. */\n ref?: Ref<HTMLButtonElement>;\n}\n\n/** A button; `type=\"button\"` unless told otherwise, so it never submits a form by accident. */\nexport function Button({\n variant = \"secondary\",\n size = \"md\",\n type = \"button\",\n className,\n ...rest\n}: ButtonProps) {\n return (\n <button\n {...rest}\n type={type}\n className={cx(\n \"btn\",\n `btn-${variant}`,\n size === \"sm\" && \"btn-sm\",\n className,\n )}\n />\n );\n}\n",
|
|
19
|
+
"/**\n * Clipboard and JSON-serialisation helpers shared by {@link CopyButton} and\n * {@link JsonView}.\n */\n\n/** How long a {@link CopyButton}'s \"Copied\" / \"Copy failed\" feedback shows, in ms. */\nexport const COPY_FEEDBACK_MS = 1_500;\n\n/**\n * Writes `text` to the clipboard. Resolves `false` instead of throwing when\n * the Clipboard API is missing (an insecure origin, an old browser) or the\n * write is refused (no permission, the document not focused).\n */\nexport async function copyText(text: string): Promise<boolean> {\n try {\n if (typeof navigator === \"undefined\" || !navigator.clipboard) {\n return false;\n }\n await navigator.clipboard.writeText(text);\n return true;\n } catch {\n return false;\n }\n}\n\n/** What {@link toJsonSafe} puts where a reference points back at an ancestor. */\nexport const CIRCULAR_MARKER = \"[Circular]\";\n\n/**\n * A JSON-safe copy of any value: circular references become\n * {@link CIRCULAR_MARKER} (a shared, non-circular reference is kept), a\n * `bigint` becomes its decimal string, `undefined` inside an array becomes\n * `null` (as `JSON.stringify` would), functions and symbols are dropped from\n * objects, and anything with `toJSON` (a `Date`) is serialised through it.\n *\n * The guard is a `WeakSet` of the objects on the current path: added on the\n * way down, deleted on the way back up, so it flags cycles and nothing else.\n */\nexport function toJsonSafe(value: unknown): unknown {\n const path = new WeakSet<object>();\n const walk = (current: unknown): unknown => {\n if (typeof current === \"bigint\") {\n return current.toString();\n }\n if (typeof current === \"function\" || typeof current === \"symbol\") {\n return undefined;\n }\n if (typeof current !== \"object\" || current === null) {\n return current;\n }\n if (path.has(current)) {\n return CIRCULAR_MARKER;\n }\n const withJson = current as { toJSON?: unknown };\n if (typeof withJson.toJSON === \"function\") {\n return walk((withJson.toJSON as () => unknown).call(current));\n }\n path.add(current);\n try {\n if (Array.isArray(current)) {\n return current.map((item) => {\n const safe = walk(item);\n return safe === undefined ? null : safe;\n });\n }\n const out: Record<string, unknown> = {};\n for (const [key, item] of Object.entries(current)) {\n const safe = walk(item);\n if (safe !== undefined) {\n out[key] = safe;\n }\n }\n return out;\n } finally {\n path.delete(current);\n }\n };\n return walk(value);\n}\n\n/**\n * `JSON.stringify` that never throws: circular input and bigints are handled\n * by {@link toJsonSafe}, and a bare `undefined` becomes the text `undefined`.\n */\nexport function safeStringify(value: unknown, indent = 2): string {\n const text = JSON.stringify(toJsonSafe(value), null, indent) as\n string | undefined;\n return text ?? \"undefined\";\n}\n",
|
|
20
|
+
"import { useEffect, useState } from \"react\";\nimport { Button } from \"./Button\";\nimport { cx } from \"./classNames\";\nimport { COPY_FEEDBACK_MS, copyText } from \"./copy\";\n\n/** Props of {@link CopyButton}. */\nexport interface CopyButtonProps {\n /** What to copy, or a function producing it on click (so a large payload is only serialised when asked). */\n text: string | (() => string);\n /** The button's visible text. Defaults to `\"Copy\"`. */\n label?: string;\n /** Accessible name when it differs from `label`, e.g. `\"Copy job id\"`. */\n ariaLabel?: string;\n /** Size. Defaults to `\"sm\"`. */\n size?: \"sm\" | \"md\";\n /** Extra class names. */\n className?: string;\n /** Called after each attempt with whether the clipboard write succeeded. */\n onCopied?: (ok: boolean) => void;\n}\n\n/**\n * Copies text to the clipboard and says so: the label turns into \"Copied\"\n * (or \"Copy failed\") for {@link COPY_FEEDBACK_MS}, announced politely.\n */\nexport function CopyButton({\n text,\n label = \"Copy\",\n ariaLabel,\n size = \"sm\",\n className,\n onCopied,\n}: CopyButtonProps) {\n const [status, setStatus] = useState<\"idle\" | \"copied\" | \"failed\">(\"idle\");\n\n useEffect(() => {\n if (status === \"idle\") {\n return;\n }\n const timer = setTimeout(setStatus, COPY_FEEDBACK_MS, \"idle\");\n return () => clearTimeout(timer);\n }, [status]);\n\n const onClick = async () => {\n const ok = await copyText(typeof text === \"function\" ? text() : text);\n setStatus(ok ? \"copied\" : \"failed\");\n onCopied?.(ok);\n };\n\n return (\n <Button\n variant=\"ghost\"\n size={size}\n className={cx(\"copy-button\", className)}\n aria-label={ariaLabel}\n data-status={status}\n onClick={() => void onClick()}\n >\n <span aria-live=\"polite\">\n {status === \"copied\"\n ? \"Copied\"\n : status === \"failed\"\n ? \"Copy failed\"\n : label}\n </span>\n </Button>\n );\n}\n",
|
|
21
|
+
"import type {\n KeyboardEvent,\n MouseEvent,\n ReactNode,\n RefObject,\n SyntheticEvent,\n} from \"react\";\nimport { useEffect, useId, useRef } from \"react\";\nimport { Button } from \"./Button\";\nimport { cx } from \"./classNames\";\n\n/** What counts as focusable inside a dialog, for the trap and the initial focus. */\nconst FOCUSABLE = [\n \"a[href]\",\n \"button:not([disabled])\",\n \"input:not([disabled]):not([type='hidden'])\",\n \"select:not([disabled])\",\n \"textarea:not([disabled])\",\n \"summary\",\n \"[contenteditable]:not([contenteditable='false'])\",\n \"[tabindex]:not([tabindex='-1'])\",\n].join(\",\");\n\n/** The focusable elements inside `root`, in DOM order. */\nfunction focusableIn(root: HTMLElement): HTMLElement[] {\n return Array.from(root.querySelectorAll<HTMLElement>(FOCUSABLE)).filter(\n (element) => !element.hasAttribute(\"inert\") && !element.closest(\"[inert]\"),\n );\n}\n\n/** Props of {@link Dialog}. */\nexport interface DialogProps {\n /** Whether the dialog is showing. The content only mounts while open, so its state resets on each opening. */\n open: boolean;\n /** Asks to close: Escape, a backdrop click or the close button. The caller sets `open` to `false`. */\n onClose: () => void;\n /** The heading; it labels the dialog (`aria-labelledby`). */\n title: ReactNode;\n /** A sentence under the title; it describes the dialog (`aria-describedby`). */\n description?: ReactNode;\n /** The body. */\n children?: ReactNode;\n /** Actions along the bottom, usually {@link Button}s; the primary one last. */\n footer?: ReactNode;\n /** Close on Escape. Defaults to `true`; turn off while something is pending. */\n closeOnEscape?: boolean;\n /** Close on a click outside the panel. Defaults to `true`. */\n closeOnBackdrop?: boolean;\n /** Show the × button in the header. Defaults to `true`. */\n showCloseButton?: boolean;\n /** Width. Defaults to `\"md\"` (`sm` 400px, `md` 560px, `lg` 800px, each capped at the viewport). */\n size?: \"sm\" | \"md\" | \"lg\";\n /** What to focus on open. Defaults to the first focusable element in the body, then the footer, then the panel. */\n initialFocusRef?: RefObject<HTMLElement | null>;\n /** `\"alertdialog\"` for a confirmation that interrupts. Defaults to `\"dialog\"`. */\n role?: \"dialog\" | \"alertdialog\";\n /** Extra class names on the panel. */\n className?: string;\n}\n\n/**\n * A modal on the native `<dialog>` element (`showModal()`, so the page\n * behind is inert and it sits in the top layer; no portal needed).\n *\n * Tab and Shift+Tab cycle inside it; Escape and a backdrop click call\n * `onClose` when allowed; on close, focus returns to whatever had it when\n * the dialog opened.\n */\nexport function Dialog(props: DialogProps) {\n return props.open ? <OpenDialog {...props} /> : null;\n}\n\n/** The mounted dialog: mount is \"open\", unmount is \"close\". */\nfunction OpenDialog({\n onClose,\n title,\n description,\n children,\n footer,\n closeOnEscape = true,\n closeOnBackdrop = true,\n showCloseButton = true,\n size = \"md\",\n initialFocusRef,\n role = \"dialog\",\n className,\n}: DialogProps) {\n const ref = useRef<HTMLDialogElement>(null);\n const bodyRef = useRef<HTMLDivElement>(null);\n const titleId = useId();\n const descriptionId = useId();\n\n useEffect(() => {\n const dialog = ref.current;\n if (!dialog) {\n return;\n }\n const opener =\n document.activeElement instanceof HTMLElement\n ? document.activeElement\n : null;\n if (!dialog.open) {\n if (typeof dialog.showModal === \"function\") {\n dialog.showModal();\n } else {\n dialog.setAttribute(\"open\", \"\");\n }\n }\n const target =\n initialFocusRef?.current ??\n (bodyRef.current && focusableIn(bodyRef.current)[0]) ??\n focusableIn(dialog).find((element) => !element.dataset.dialogClose) ??\n dialog;\n target.focus();\n return () => {\n if (dialog.open && typeof dialog.close === \"function\") {\n dialog.close();\n }\n if (opener?.isConnected) {\n opener.focus();\n }\n };\n // Runs once per opening (the ref objects are stable).\n }, [initialFocusRef]);\n\n const onKeyDown = (event: KeyboardEvent<HTMLDialogElement>) => {\n if (event.key === \"Escape\") {\n // Stop the native `cancel` so the element's open state stays ours.\n event.preventDefault();\n event.stopPropagation();\n if (closeOnEscape) {\n onClose();\n }\n return;\n }\n if (event.key !== \"Tab\" || !ref.current) {\n return;\n }\n const focusable = focusableIn(ref.current);\n if (focusable.length === 0) {\n event.preventDefault();\n ref.current.focus();\n return;\n }\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n const active = document.activeElement;\n const inside = active instanceof Node && ref.current.contains(active);\n if (event.shiftKey && (active === first || !inside)) {\n event.preventDefault();\n last.focus();\n } else if (!event.shiftKey && (active === last || !inside)) {\n event.preventDefault();\n first.focus();\n }\n };\n\n // Escape in a real browser can arrive as `cancel` without a keydown we saw.\n const onCancel = (event: SyntheticEvent<HTMLDialogElement>) => {\n event.preventDefault();\n if (closeOnEscape) {\n onClose();\n }\n };\n\n // The panel fills the element, so a click whose target is the element\n // itself landed on the backdrop.\n const onClick = (event: MouseEvent<HTMLDialogElement>) => {\n if (closeOnBackdrop && event.target === event.currentTarget) {\n onClose();\n }\n };\n\n return (\n // The keyboard handling lives on the dialog element (Escape and the\n // focus trap); a backdrop click is a pointer-only convenience.\n <dialog\n ref={ref}\n role={role === \"alertdialog\" ? \"alertdialog\" : undefined}\n aria-modal=\"true\"\n aria-labelledby={titleId}\n aria-describedby={description ? descriptionId : undefined}\n className={cx(\"dialog\", `dialog-${size}`, className)}\n tabIndex={-1}\n onKeyDown={onKeyDown}\n onCancel={onCancel}\n onClick={onClick}\n >\n <div className=\"dialog-panel\">\n <div className=\"dialog-header\">\n <h2\n id={titleId}\n className=\"dialog-title\"\n >\n {title}\n </h2>\n {showCloseButton && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"dialog-close\"\n aria-label=\"Close\"\n data-dialog-close=\"true\"\n onClick={onClose}\n >\n <span aria-hidden=\"true\">×</span>\n </Button>\n )}\n </div>\n {description && (\n <p\n id={descriptionId}\n className=\"dialog-description\"\n >\n {description}\n </p>\n )}\n {children !== undefined && (\n <div\n ref={bodyRef}\n className=\"dialog-body\"\n >\n {children}\n </div>\n )}\n {footer && <div className=\"dialog-footer\">{footer}</div>}\n </div>\n </dialog>\n );\n}\n",
|
|
22
|
+
"import type { ReactNode } from \"react\";\n\n/** Props of {@link EmptyState}. */\nexport interface EmptyStateProps {\n /** The headline, e.g. \"No queues yet\". */\n title: string;\n /** A sentence of explanation. */\n description?: ReactNode;\n /** A follow-up control. */\n action?: ReactNode;\n /**\n * Render the headline as a heading of this level instead of a paragraph.\n * Use `1` when the empty state IS the screen (a not-found or hidden screen\n * has no other `h1`). Defaults to none (a paragraph).\n */\n headingLevel?: 1 | 2 | 3;\n}\n\n/** What a list or screen shows when there is nothing to show. */\nexport function EmptyState({\n title,\n description,\n action,\n headingLevel,\n}: EmptyStateProps) {\n const Title = headingLevel ? (`h${headingLevel}` as const) : \"p\";\n return (\n <div className=\"empty-state\">\n <Title className=\"empty-state-title\">{title}</Title>\n {description && <p className=\"empty-state-description\">{description}</p>}\n {action && <div className=\"empty-state-action\">{action}</div>}\n </div>\n );\n}\n",
|
|
23
|
+
"import type { DocsUiConfig } from \"../../lib/docs/browser/config.ts\";\nimport type { DocsClient, FetchLike } from \"./api/client\";\nimport { createContext, use } from \"react\";\n\n/** Carries the injected {@link DocsUiConfig}. */\nexport const DocsUiConfigContext = createContext<DocsUiConfig | null>(null);\n/** Carries the try-it panel's client (built on the document's resolved server). */\nexport const DocsClientContext = createContext<DocsClient | null>(null);\n/** Carries the `fetch` every client uses; tests inject one. Defaults to `globalThis.fetch`. */\nexport const DocsFetchContext = createContext<FetchLike | null>(null);\n\n/** The resolved {@link DocsUiConfig} the server injected. */\nexport function useDocsUiConfig(): DocsUiConfig {\n const config = use(DocsUiConfigContext);\n if (!config) {\n throw new Error(\"useDocsUiConfig must be used inside <DocsUiProviders>\");\n }\n return config;\n}\n\n/** The try-it panel's client. */\nexport function useDocsClient(): DocsClient {\n const client = use(DocsClientContext);\n if (!client) {\n throw new Error(\"useDocsClient must be used inside <DocsClientContext>\");\n }\n return client;\n}\n\n/** The `fetch` the clients use, or `undefined` for `globalThis.fetch`. */\nexport function useDocsFetch(): FetchLike | undefined {\n return use(DocsFetchContext) ?? undefined;\n}\n",
|
|
24
|
+
"import type { AnchorHTMLAttributes, MouseEvent, ReactNode } from \"react\";\nimport type { NavigateOptions, RouteDef } from \"./routing\";\nimport { useCallback, useEffect, useMemo, useState } from \"react\";\nimport { ScreenErrorBoundary } from \"./components/ScreenErrorBoundary\";\nimport {\n matchPath,\n normalizeBasePath,\n ParamsContext,\n readLocation,\n RouterContext,\n useLocation,\n useNavigate,\n useRouter,\n} from \"./routing\";\n\n/**\n * The router's components: the provider, route matching, redirects and\n * links. The pure path functions and the hooks live in `routing.ts`.\n */\n\n/** Props of {@link RouterProvider}. */\nexport interface RouterProviderProps {\n /** The mount path (`DocsUiConfig.basePath`). */\n basePath: string;\n /** The app. */\n children: ReactNode;\n}\n\n/** Provides routing under `basePath`, following `window.history`. */\nexport function RouterProvider({ basePath, children }: RouterProviderProps) {\n const base = normalizeBasePath(basePath);\n const [location, setLocation] = useState(() => readLocation(base));\n\n useEffect(() => {\n const onPopState = () => setLocation(readLocation(base));\n window.addEventListener(\"popstate\", onPopState);\n return () => window.removeEventListener(\"popstate\", onPopState);\n }, [base]);\n\n const href = useCallback(\n (to: string) => {\n const target = to.startsWith(\"/\") ? to : `/${to}`;\n if (base === \"\") {\n return target;\n }\n // `/` is the mount itself; keep the query attached without a slash.\n return target === \"/\" || target.startsWith(\"/?\")\n ? `${base}${target.slice(1)}`\n : `${base}${target}`;\n },\n [base],\n );\n\n const navigate = useCallback(\n (to: string, options: NavigateOptions = {}) => {\n const url = href(to);\n if (options.replace) {\n window.history.replaceState(null, \"\", url);\n } else {\n window.history.pushState(null, \"\", url);\n }\n setLocation(readLocation(base));\n },\n [base, href],\n );\n\n const value = useMemo(\n () => ({ basePath: base, location, navigate, href }),\n [base, location, navigate, href],\n );\n return <RouterContext value={value}>{children}</RouterContext>;\n}\n\n/** Props of {@link Routes}. */\nexport interface RoutesProps {\n /** Routes, tried in order; the first match wins. */\n routes: readonly RouteDef[];\n /** Rendered when nothing matches (including a URL outside `basePath`). */\n notFound: ReactNode;\n}\n\n/**\n * Renders the first route matching the current location, inside a\n * {@link ScreenErrorBoundary}: a screen that crashes while rendering shows a\n * recoverable panel in its place instead of blanking the app, and moving to\n * another location tries again.\n */\nexport function Routes({ routes, notFound }: RoutesProps) {\n const { path } = useLocation();\n return (\n <ScreenErrorBoundary resetKey={path ?? \"\"}>\n <MatchedRoute\n routes={routes}\n notFound={notFound}\n path={path}\n />\n </ScreenErrorBoundary>\n );\n}\n\n/** The first route matching `path`, or `notFound`. */\nfunction MatchedRoute({\n routes,\n notFound,\n path,\n}: RoutesProps & { path: string | null }) {\n if (path !== null) {\n for (const route of routes) {\n const params = matchPath(route.path, path);\n if (params) {\n return (\n <ParamsContext\n key={route.path}\n value={params}\n >\n {route.element}\n </ParamsContext>\n );\n }\n }\n }\n return <>{notFound}</>;\n}\n\n/** Props of {@link Navigate}. */\nexport interface NavigateProps {\n /** App path to go to. */\n to: string;\n /** Replace the history entry. Defaults to `true`: a redirect should not be a back-button stop. */\n replace?: boolean;\n}\n\n/** Redirects on render. */\nexport function Navigate({ to, replace = true }: NavigateProps) {\n const navigate = useNavigate();\n useEffect(() => {\n navigate(to, { replace });\n }, [navigate, to, replace]);\n return null;\n}\n\n/** Props of {@link Link}. */\nexport interface LinkProps extends Omit<\n AnchorHTMLAttributes<HTMLAnchorElement>,\n \"href\"\n> {\n /** App path (relative to `basePath`), may carry `?query`. */\n to: string;\n /** Mark the link `aria-current=\"page\"` when the location matches: `\"exact\"`, or `\"prefix\"` for a section. Defaults to none. */\n activeMatch?: \"exact\" | \"prefix\";\n}\n\n/** Whether a click should be left to the browser (new tab, download, modified). */\nfunction isModifiedClick(event: MouseEvent<HTMLAnchorElement>): boolean {\n return (\n event.button !== 0 ||\n event.metaKey ||\n event.altKey ||\n event.ctrlKey ||\n event.shiftKey ||\n event.defaultPrevented\n );\n}\n\n/** An `<a>` that navigates in-app on a plain left-click. */\nexport function Link({ to, activeMatch, onClick, target, ...rest }: LinkProps) {\n const { navigate, href, location } = useRouter();\n const toPath = to.split(\"?\")[0] || \"/\";\n const active =\n location.path !== null &&\n (activeMatch === \"exact\"\n ? location.path === toPath\n : activeMatch === \"prefix\"\n ? toPath === \"/\"\n ? location.path === \"/\"\n : location.path === toPath || location.path.startsWith(`${toPath}/`)\n : false);\n return (\n <a\n {...rest}\n target={target}\n href={href(to)}\n aria-current={active ? \"page\" : undefined}\n onClick={(event) => {\n onClick?.(event);\n if (isModifiedClick(event) || (target && target !== \"_self\")) {\n return;\n }\n event.preventDefault();\n navigate(to);\n }}\n />\n );\n}\n",
|
|
25
|
+
"import { createContext, use } from \"react\";\n\n/** What a {@link Field} tells the control inside it. */\nexport interface FieldControlProps {\n /** The control's `id`; the field's `<label htmlFor>` points at it. */\n id: string;\n /** Ids of the hint and error (and any extra description), space separated; `undefined` when there are none. */\n \"aria-describedby\": string | undefined;\n /** `true` while the field shows an error. */\n \"aria-invalid\": boolean | undefined;\n /** Whether the field is marked required. */\n required: boolean | undefined;\n}\n\n/** Carries the enclosing field's wiring; `null` outside a field. */\nexport const FieldContext = createContext<FieldControlProps | null>(null);\n\n/** The enclosing {@link Field}'s wiring, or `null` when the control stands alone. */\nexport function useField(): FieldControlProps | null {\n return use(FieldContext);\n}\n\n/** Joins ids for `aria-describedby`, skipping empty ones; `undefined` when none are left. */\nexport function joinIds(\n ...ids: (string | false | null | undefined)[]\n): string | undefined {\n const joined = ids.filter(Boolean).join(\" \");\n return joined === \"\" ? undefined : joined;\n}\n",
|
|
26
|
+
"import type { ReactNode } from \"react\";\nimport type { FieldControlProps } from \"./fieldContext\";\nimport { useId, useMemo } from \"react\";\nimport { cx } from \"./classNames\";\nimport { FieldContext, joinIds } from \"./fieldContext\";\n\n/** Props of {@link Field}. */\nexport interface FieldProps {\n /** The visible label, a `<label>` for the control. */\n label: ReactNode;\n /** Help under the control; it describes the control (`aria-describedby`). */\n hint?: ReactNode;\n /** An error under the control; it describes the control and sets `aria-invalid`. Falsy means valid. */\n error?: ReactNode;\n /** Show a required marker and mark the control required. Defaults to `false`. */\n required?: boolean;\n /** The control's id. Defaults to a generated one. */\n id?: string;\n /** Extra class names on the wrapper. */\n className?: string;\n /** The control: one of this kit's inputs (they read the wiring from context), or a render function receiving it for anything else. */\n children: ReactNode | ((control: FieldControlProps) => ReactNode);\n}\n\n/**\n * A labelled form row: label, control, hint and error. The kit's inputs\n * ({@link TextInput}, {@link NumberInput}, {@link Select},\n * {@link DateTimeInput}, `JsonEditor`) pick up the id, `aria-describedby`,\n * `aria-invalid` and `required` from it; a plain element can take them from\n * the render-function form of `children`.\n */\nexport function Field({\n label,\n hint,\n error,\n required = false,\n id,\n className,\n children,\n}: FieldProps) {\n const generatedId = useId();\n const controlId = id ?? generatedId;\n const hintId = `${controlId}-hint`;\n const errorId = `${controlId}-error`;\n const hasError = Boolean(error);\n const control = useMemo<FieldControlProps>(\n () => ({\n id: controlId,\n \"aria-describedby\": joinIds(hint ? hintId : null, hasError && errorId),\n \"aria-invalid\": hasError || undefined,\n required: required || undefined,\n }),\n [controlId, hint, hintId, hasError, errorId, required],\n );\n return (\n <div className={cx(\"field\", hasError && \"field-invalid\", className)}>\n <label\n className=\"field-label\"\n htmlFor={controlId}\n >\n {label}\n {required && (\n <span\n className=\"field-required\"\n aria-hidden=\"true\"\n >\n {\" \"}\n *\n </span>\n )}\n </label>\n <FieldContext value={control}>\n {typeof children === \"function\" ? children(control) : children}\n </FieldContext>\n {hint && (\n <p\n id={hintId}\n className=\"field-hint\"\n >\n {hint}\n </p>\n )}\n {hasError && (\n <p\n id={errorId}\n className=\"field-error\"\n >\n {error}\n </p>\n )}\n </div>\n );\n}\n",
|
|
27
|
+
"import type {\n ChangeEvent,\n InputHTMLAttributes,\n ReactNode,\n Ref,\n SelectHTMLAttributes,\n} from \"react\";\nimport { useEffect, useId, useRef, useState } from \"react\";\nimport { cx } from \"./classNames\";\nimport { joinIds, useField } from \"./fieldContext\";\nimport {\n localTimeZone,\n parseNumberInput,\n readDateTimeLocal,\n toDateTimeLocal,\n} from \"./inputValues\";\n\n/**\n * The kit's form controls. Inside a {@link Field} each takes its id,\n * `aria-describedby`, `aria-invalid` and `required` from it; explicit props\n * win, and an explicit `aria-describedby` is added to the field's.\n */\n\n/** Wiring props every control accepts. */\ninterface ControlWiring {\n /** The element id. Defaults to the enclosing field's. */\n id?: string;\n /** Extra description ids, added to the field's. */\n \"aria-describedby\"?: string;\n /** Invalid state. Defaults to the enclosing field's. */\n \"aria-invalid\"?: boolean;\n /** Required. Defaults to the enclosing field's. */\n required?: boolean;\n}\n\n/** Resolves a control's wiring against the enclosing field. */\nfunction useWiring(props: ControlWiring) {\n const field = useField();\n return {\n id: props.id ?? field?.id,\n \"aria-describedby\": joinIds(\n field?.[\"aria-describedby\"],\n props[\"aria-describedby\"],\n ),\n \"aria-invalid\": props[\"aria-invalid\"] ?? field?.[\"aria-invalid\"],\n required: props.required ?? field?.required,\n };\n}\n\n/** Native input props the kit's inputs pass through (value and change are typed per control). */\ntype PassThroughInputProps = Omit<\n InputHTMLAttributes<HTMLInputElement>,\n | \"value\"\n | \"defaultValue\"\n | \"onChange\"\n | \"type\"\n | \"id\"\n | \"aria-describedby\"\n | \"aria-invalid\"\n | \"required\"\n | \"min\"\n | \"max\"\n | \"step\"\n> & {\n /** The underlying `<input>`, e.g. to focus it. */\n ref?: Ref<HTMLInputElement>;\n};\n\n/** Props of {@link TextInput}. */\nexport interface TextInputProps extends PassThroughInputProps, ControlWiring {\n /** The text. */\n value: string;\n /** Called with the new text on each edit. */\n onChange: (value: string, event: ChangeEvent<HTMLInputElement>) => void;\n /** The input type. Defaults to `\"text\"`. */\n type?: \"text\" | \"search\" | \"email\" | \"url\" | \"password\" | \"tel\";\n}\n\n/** A single-line text input. */\nexport function TextInput({\n value,\n onChange,\n type = \"text\",\n className,\n ...rest\n}: TextInputProps) {\n const wiring = useWiring(rest);\n return (\n <input\n {...rest}\n {...wiring}\n type={type}\n className={cx(\"input\", className)}\n value={value}\n onChange={(event) => onChange(event.target.value, event)}\n />\n );\n}\n\n/** Props of {@link NumberInput}. */\nexport interface NumberInputProps extends PassThroughInputProps, ControlWiring {\n /** The number, or `undefined` for empty. */\n value: number | undefined;\n /** Called with the new number, or `undefined` when the input is empty (or not a number). */\n onChange: (\n value: number | undefined,\n event: ChangeEvent<HTMLInputElement>,\n ) => void;\n /** Smallest allowed value (native validation only; the value is not clamped). */\n min?: number;\n /** Largest allowed value (native validation only; the value is not clamped). */\n max?: number;\n /** Step. Defaults to `1`; pass `\"any\"` for decimals. */\n step?: number | \"any\";\n}\n\n/** A number input where empty means `undefined`, never `0`. */\nexport function NumberInput({\n value,\n onChange,\n min,\n max,\n step = 1,\n className,\n ...rest\n}: NumberInputProps) {\n const wiring = useWiring(rest);\n return (\n <input\n inputMode={step === \"any\" ? \"decimal\" : \"numeric\"}\n {...rest}\n {...wiring}\n type=\"number\"\n className={cx(\"input\", \"input-number\", className)}\n min={min}\n max={max}\n step={step}\n value={value === undefined || Number.isNaN(value) ? \"\" : value}\n onChange={(event) =>\n onChange(parseNumberInput(event.target.value), event)\n }\n />\n );\n}\n\n/** One option of a {@link Select}. */\nexport interface SelectOption<V extends string> {\n /** The value. */\n value: V;\n /** The text shown. Defaults to the value. */\n label?: string;\n /** Not selectable. */\n disabled?: boolean;\n}\n\n/** Props of {@link Select}. */\nexport interface SelectProps<V extends string>\n extends\n Omit<\n SelectHTMLAttributes<HTMLSelectElement>,\n | \"value\"\n | \"defaultValue\"\n | \"onChange\"\n | \"id\"\n | \"aria-describedby\"\n | \"aria-invalid\"\n | \"required\"\n | \"multiple\"\n >,\n ControlWiring {\n /** The choices. Include `{ value: \"\", label: \"Any\" }` (with `\"\"` in `V`) for an \"unset\" choice. */\n options: readonly SelectOption<V>[];\n /** The selected value. */\n value: V;\n /** Called with the chosen value. */\n onChange: (value: V, event: ChangeEvent<HTMLSelectElement>) => void;\n}\n\n/** A native `<select>` over a typed set of string values. */\nexport function Select<V extends string>({\n options,\n value,\n onChange,\n className,\n ...rest\n}: SelectProps<V>) {\n const wiring = useWiring(rest);\n return (\n <select\n {...rest}\n {...wiring}\n className={cx(\"input\", \"select\", className)}\n value={value}\n onChange={(event) => {\n const chosen = options.find(\n (option) => option.value === event.target.value,\n );\n if (chosen) {\n onChange(chosen.value, event);\n }\n }}\n >\n {options.map((option) => (\n <option\n key={option.value}\n value={option.value}\n disabled={option.disabled}\n >\n {option.label ?? option.value}\n </option>\n ))}\n </select>\n );\n}\n\n/** Props of {@link Checkbox}. */\nexport interface CheckboxProps extends Omit<\n PassThroughInputProps,\n \"checked\" | \"defaultChecked\"\n> {\n /** Whether it is ticked. */\n checked: boolean;\n /** Called with the new state. */\n onChange: (checked: boolean, event: ChangeEvent<HTMLInputElement>) => void;\n /** The label beside the box; clicking it toggles. */\n label: ReactNode;\n /** Help under the label; it describes the box. */\n hint?: ReactNode;\n /** The element id. Defaults to a generated one. */\n id?: string;\n}\n\n/** A checkbox with its own label (it does not need a {@link Field}). */\nexport function Checkbox({\n checked,\n onChange,\n label,\n hint,\n id,\n className,\n ...rest\n}: CheckboxProps) {\n const generatedId = useId();\n const inputId = id ?? generatedId;\n const hintId = `${inputId}-hint`;\n return (\n <div className={cx(\"checkbox\", className)}>\n <input\n {...rest}\n id={inputId}\n type=\"checkbox\"\n checked={checked}\n aria-describedby={hint ? hintId : undefined}\n onChange={(event) => onChange(event.target.checked, event)}\n />\n <label htmlFor={inputId}>{label}</label>\n {hint && (\n <p\n id={hintId}\n className=\"field-hint checkbox-hint\"\n >\n {hint}\n </p>\n )}\n </div>\n );\n}\n\n/** Props of {@link DateTimeInput}. */\nexport interface DateTimeInputProps\n extends Omit<PassThroughInputProps, \"min\" | \"max\">, ControlWiring {\n /** The instant, epoch ms, or `undefined` for empty. */\n value: number | undefined;\n /**\n * Called with the instant (epoch ms) the user picked, or `undefined` when\n * cleared, or when the field holds something unusable (then `onProblem`\n * says what).\n */\n onChange: (\n value: number | undefined,\n event: ChangeEvent<HTMLInputElement>,\n ) => void;\n /**\n * Called with a message when the field holds something unusable — a time\n * outside what the API accepts (0 to `MAX_DATE_MS`) or one the browser\n * could not read — and with `undefined` once it holds a usable time, is\n * cleared, or unmounts. Judged on each change and again on blur, since a\n * half-typed entry into an empty field fires no change. The typed text is\n * kept on screen meanwhile. Show the message as the field's error and hold\n * the form back until it clears (`useFieldProblems`, in\n * `hooks/useFieldProblems`).\n */\n onProblem?: (problem: string | undefined) => void;\n /** Earliest allowed instant, epoch ms (native validation only). */\n min?: number;\n /** Latest allowed instant, epoch ms (native validation only). */\n max?: number;\n /** Let the user pick seconds. Defaults to `false` (minute precision). */\n withSeconds?: boolean;\n /** Show the browser's time zone beside the input. Defaults to `true`. */\n showTimeZone?: boolean;\n}\n\n/**\n * A `datetime-local` input speaking epoch ms. The wall-clock value is read\n * in the browser's time zone, which is shown beside it (and part of its\n * description), so \"09:00\" is never ambiguous.\n */\nexport function DateTimeInput({\n value,\n onChange,\n onProblem,\n min,\n max,\n withSeconds = false,\n showTimeZone = true,\n className,\n onBlur,\n ...rest\n}: DateTimeInputProps) {\n const zoneId = useId();\n const wiring = useWiring(rest);\n // What the user typed, kept on screen while it is unusable (the value is\n // then `undefined`, which would otherwise blank the field).\n const [draft, setDraft] = useState<string | null>(null);\n const onProblemRef = useRef(onProblem);\n useEffect(() => {\n onProblemRef.current = onProblem;\n });\n // A field that goes away takes its problem with it.\n useEffect(() => () => onProblemRef.current?.(undefined), []);\n return (\n <span className=\"input-group\">\n <input\n {...rest}\n {...wiring}\n aria-describedby={joinIds(\n wiring[\"aria-describedby\"],\n showTimeZone && zoneId,\n )}\n type=\"datetime-local\"\n className={cx(\"input\", \"input-datetime\", className)}\n step={withSeconds ? 1 : 60}\n min={min === undefined ? undefined : toDateTimeLocal(min, withSeconds)}\n max={max === undefined ? undefined : toDateTimeLocal(max, withSeconds)}\n value={\n value === undefined\n ? (draft ?? \"\")\n : toDateTimeLocal(value, withSeconds)\n }\n onChange={(event) => {\n const reading = readDateTimeLocal(\n event.target.value,\n event.target.validity.badInput,\n );\n if (reading.kind === \"problem\") {\n setDraft(event.target.value);\n onChange(undefined, event);\n onProblem?.(reading.problem);\n return;\n }\n setDraft(null);\n onChange(reading.kind === \"time\" ? reading.ms : undefined, event);\n onProblem?.(undefined);\n }}\n // A half-typed entry into an empty field fires no change (its value\n // stays \"\"), so it is judged again on leaving the field.\n onBlur={(event) => {\n onBlur?.(event);\n const reading = readDateTimeLocal(\n event.target.value,\n event.target.validity.badInput,\n );\n onProblem?.(reading.kind === \"problem\" ? reading.problem : undefined);\n }}\n />\n {showTimeZone && (\n <span\n id={zoneId}\n className=\"input-suffix\"\n >\n {localTimeZone()}\n </span>\n )}\n </span>\n );\n}\n",
|
|
28
|
+
"import { isApiError } from \"../api/errors\";\nimport { Button } from \"./Button\";\nimport { cx } from \"./classNames\";\n\n/** Props of {@link ProblemBanner}. */\nexport interface ProblemBannerProps {\n /** What failed: an `ApiError` shows its title, code, detail, context and issues; anything else its message. */\n error: unknown;\n /** A heading override, e.g. \"Could not save limits\". Defaults to the problem's title. */\n title?: string;\n /** Offers a Retry button when given. */\n onRetry?: () => void;\n /** Offers a Dismiss button when given. */\n onDismiss?: () => void;\n /** Extra class names. */\n className?: string;\n}\n\n/** Formats one context value for the banner: strings as-is, the rest as JSON. */\nfunction contextText(value: unknown): string {\n if (typeof value === \"string\") {\n return value;\n }\n try {\n return JSON.stringify(value) ?? String(value);\n } catch {\n return String(value);\n }\n}\n\n/**\n * A compact, inline error for a panel or a dialog: one line of title and\n * code, the detail, the problem's `context` as `key=value` chips, and any\n * validation issues. {@link ErrorView} is the full-size version for a screen.\n */\nexport function ProblemBanner({\n error,\n title,\n onRetry,\n onDismiss,\n className,\n}: ProblemBannerProps) {\n const api = isApiError(error) ? error : null;\n const heading = title ?? api?.title ?? \"Something went wrong\";\n const detail =\n api?.detail ?? (error instanceof Error ? error.message : String(error));\n const context = api ? Object.entries(api.context) : [];\n return (\n <div\n className={cx(\"problem-banner\", className)}\n role=\"alert\"\n >\n <div className=\"problem-banner-main\">\n <p className=\"problem-banner-title\">\n <strong>{heading}</strong>\n {api && (\n <code className=\"problem-banner-code\">\n {api.code}\n {api.status > 0 ? ` · ${api.status}` : \"\"}\n </code>\n )}\n </p>\n {detail && detail !== heading && (\n <p className=\"problem-banner-detail\">{detail}</p>\n )}\n {context.length > 0 && (\n <ul\n className=\"problem-banner-context\"\n aria-label=\"Context\"\n >\n {context.map(([key, value]) => (\n <li key={key}>\n <span className=\"problem-banner-key\">{key}</span>=\n <code>{contextText(value)}</code>\n </li>\n ))}\n </ul>\n )}\n {api && api.issues.length > 0 && (\n <ul className=\"problem-banner-issues\">\n {api.issues.map((issue) => (\n <li key={`${issue.target}:${issue.path}:${issue.message}`}>\n <code>\n {issue.target}\n {issue.path ? `.${issue.path}` : \"\"}\n </code>{\" \"}\n {issue.message}\n </li>\n ))}\n </ul>\n )}\n </div>\n {(onRetry || onDismiss) && (\n <div className=\"problem-banner-actions\">\n {onRetry && (\n <Button\n size=\"sm\"\n onClick={onRetry}\n >\n Retry\n </Button>\n )}\n {onDismiss && (\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={onDismiss}\n >\n Dismiss\n </Button>\n )}\n </div>\n )}\n </div>\n );\n}\n",
|
|
29
|
+
"import type { FormEvent, ReactNode } from \"react\";\nimport { useRef, useState } from \"react\";\nimport { Button } from \"./Button\";\nimport { Dialog } from \"./Dialog\";\nimport { Field } from \"./Field\";\nimport { TextInput } from \"./inputs\";\nimport { ProblemBanner } from \"./ProblemBanner\";\n\n/** Props of {@link ConfirmDialog}. */\nexport interface ConfirmDialogProps {\n /** Whether it is showing. State (typed text, error) resets on each opening. */\n open: boolean;\n /** Asks to close (Cancel, Escape, backdrop, or after a successful confirm). Ignored while pending. */\n onClose: () => void;\n /** The question, e.g. \"Drain queue emails?\". */\n title: ReactNode;\n /** What will happen, in a sentence. */\n description?: ReactNode;\n /** Extra body content, e.g. options such as a \"include delayed\" checkbox. */\n children?: ReactNode;\n /**\n * The action. May be async: the dialog shows a pending state until it\n * settles, closes on success, and on a throw stays open with the error\n * shown inline (an `ApiError` as a {@link ProblemBanner}).\n */\n onConfirm: () => unknown;\n /** The confirm button's text. Defaults to `\"Confirm\"`. */\n confirmLabel?: string;\n /** The confirm button's text while pending. Defaults to `confirmLabel` + \"…\". */\n pendingLabel?: string;\n /** The cancel button's text. Defaults to `\"Cancel\"`. */\n cancelLabel?: string;\n /** `\"danger\"` for destructive actions: a red button, `alertdialog`, and Cancel focused first. Defaults to `\"default\"`. */\n variant?: \"default\" | \"danger\";\n /** When set, the user must type exactly this (e.g. the queue name) before Confirm enables. */\n confirmText?: string;\n /** The typed-confirmation label. Defaults to \"Type <confirmText> to confirm\". */\n confirmTextLabel?: ReactNode;\n /** Close after a successful `onConfirm`. Defaults to `true`. */\n closeOnSuccess?: boolean;\n /** Keeps Confirm disabled, e.g. while the body's options are invalid. */\n confirmDisabled?: boolean;\n}\n\n/**\n * A yes/no dialog for an action, built on {@link Dialog}: an optional typed\n * confirmation for destructive ones, an async `onConfirm` with a pending\n * state, and the failure shown in place so the user can retry or cancel.\n */\nexport function ConfirmDialog(props: ConfirmDialogProps) {\n return props.open ? <OpenConfirmDialog {...props} /> : null;\n}\n\n/** The mounted dialog; its state lives exactly as long as one opening. */\nfunction OpenConfirmDialog({\n onClose,\n title,\n description,\n children,\n onConfirm,\n confirmLabel = \"Confirm\",\n pendingLabel,\n cancelLabel = \"Cancel\",\n variant = \"default\",\n confirmText,\n confirmTextLabel,\n closeOnSuccess = true,\n confirmDisabled = false,\n}: ConfirmDialogProps) {\n const [typed, setTyped] = useState(\"\");\n const [pending, setPending] = useState(false);\n const [error, setError] = useState<unknown>(null);\n const cancelRef = useRef<HTMLButtonElement>(null);\n const confirmRef = useRef<HTMLButtonElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n\n const matches = confirmText === undefined || typed === confirmText;\n const canConfirm = matches && !pending && !confirmDisabled;\n const danger = variant === \"danger\";\n\n const confirm = async () => {\n if (!canConfirm) {\n return;\n }\n setPending(true);\n setError(null);\n try {\n await onConfirm();\n setPending(false);\n if (closeOnSuccess) {\n onClose();\n }\n } catch (caught) {\n setPending(false);\n setError(caught);\n }\n };\n\n const onSubmit = (event: FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n void confirm();\n };\n\n const close = () => {\n if (!pending) {\n onClose();\n }\n };\n\n return (\n <Dialog\n open\n onClose={close}\n title={title}\n description={description}\n role={danger ? \"alertdialog\" : \"dialog\"}\n size=\"sm\"\n closeOnEscape={!pending}\n closeOnBackdrop={!pending}\n initialFocusRef={\n confirmText !== undefined ? inputRef : danger ? cancelRef : confirmRef\n }\n footer={\n <>\n <Button\n ref={cancelRef}\n onClick={close}\n disabled={pending}\n >\n {cancelLabel}\n </Button>\n <Button\n ref={confirmRef}\n variant={danger ? \"danger\" : \"primary\"}\n disabled={!canConfirm}\n aria-busy={pending || undefined}\n onClick={() => void confirm()}\n >\n {pending ? (pendingLabel ?? `${confirmLabel}…`) : confirmLabel}\n </Button>\n </>\n }\n >\n <form\n className=\"confirm-dialog-body\"\n onSubmit={onSubmit}\n >\n {children}\n {confirmText !== undefined && (\n <Field\n label={\n confirmTextLabel ?? (\n <>\n Type <code>{confirmText}</code> to confirm\n </>\n )\n }\n >\n <TextInput\n ref={inputRef}\n value={typed}\n onChange={setTyped}\n autoComplete=\"off\"\n spellCheck={false}\n disabled={pending}\n />\n </Field>\n )}\n {error !== null && <ProblemBanner error={error} />}\n </form>\n </Dialog>\n );\n}\n",
|
|
30
|
+
"/**\n * JSON parsing that says *where* it failed. `JSON.parse` does the parsing;\n * only when it throws does a small scanner walk the text to find the\n * offending position, because engines disagree on whether (and how) their\n * messages carry one — JavaScriptCore's carry none at all.\n */\n\n/** Where and why a JSON text failed to parse. */\nexport interface JsonParseError {\n /** What is wrong, e.g. \"Expected ',' or '}'\". */\n message: string;\n /** 1-based line. */\n line: number;\n /** 1-based column (in UTF-16 code units). */\n column: number;\n /** 0-based offset into the text. */\n offset: number;\n}\n\n/** The outcome of {@link parseJsonText}. */\nexport type JsonParseResult =\n | {\n /** It parsed. */\n ok: true;\n /** The parsed value. */\n value: unknown;\n }\n | {\n /** It did not parse. */\n ok: false;\n /** Where and why. */\n error: JsonParseError;\n };\n\n/** Thrown inside the scanner with the failing offset. */\nclass ScanError extends Error {\n /** 0-based offset. */\n readonly offset: number;\n\n constructor(message: string, offset: number) {\n super(message);\n this.offset = offset;\n }\n}\n\n/** Describes the character at `index` for a message. */\nfunction describe(text: string, index: number): string {\n return index >= text.length\n ? \"end of input\"\n : `'${text[index] === \"\\n\" ? \"\\\\n\" : text[index]}'`;\n}\n\n/**\n * Walks `text` as JSON (RFC 8259) and throws a {@link ScanError} at the\n * first position that is not valid. Returns normally for valid JSON.\n */\nfunction scan(text: string): void {\n let index = 0;\n\n const skipWhitespace = () => {\n while (\n index < text.length &&\n (text[index] === \" \" ||\n text[index] === \"\\t\" ||\n text[index] === \"\\n\" ||\n text[index] === \"\\r\")\n ) {\n index++;\n }\n };\n\n const fail = (expected: string): never => {\n throw new ScanError(\n `Expected ${expected} but found ${describe(text, index)}`,\n index,\n );\n };\n\n const literal = (word: string) => {\n for (let i = 0; i < word.length; i++) {\n if (text[index] !== word[i]) {\n fail(`'${word}'`);\n }\n index++;\n }\n };\n\n const string = () => {\n index++; // opening quote\n while (index < text.length) {\n const char = text[index];\n if (char === '\"') {\n index++;\n return;\n }\n if (char === \"\\\\\") {\n const next = text[index + 1];\n if (next === \"u\") {\n if (!/^[0-9a-f]{4}$/i.test(text.slice(index + 2, index + 6))) {\n index += 2;\n fail(\"four hex digits after \\\\u\");\n }\n index += 6;\n continue;\n }\n if (next === undefined || !'\"\\\\/bfnrt'.includes(next)) {\n index++;\n fail(\"a valid escape\");\n }\n index += 2;\n continue;\n }\n if (char < \" \") {\n throw new ScanError(\n \"Control characters must be escaped in strings\",\n index,\n );\n }\n index++;\n }\n throw new ScanError(\"Unterminated string\", index);\n };\n\n const number = () => {\n const match = /^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:e[+-]?\\d+)?/i.exec(\n text.slice(index, index + 400),\n );\n if (!match) {\n fail(\"a number\");\n }\n index += match![0].length;\n };\n\n const value = (): void => {\n skipWhitespace();\n const char = text[index];\n if (char === \"{\") {\n index++;\n skipWhitespace();\n if (text[index] === \"}\") {\n index++;\n return;\n }\n for (;;) {\n skipWhitespace();\n if (text[index] !== '\"') {\n fail(\"a property name in double quotes\");\n }\n string();\n skipWhitespace();\n if (text[index] !== \":\") {\n fail(\"':'\");\n }\n index++;\n value();\n skipWhitespace();\n if (text[index] === \",\") {\n index++;\n continue;\n }\n if (text[index] === \"}\") {\n index++;\n return;\n }\n fail(\"',' or '}'\");\n }\n }\n if (char === \"[\") {\n index++;\n skipWhitespace();\n if (text[index] === \"]\") {\n index++;\n return;\n }\n for (;;) {\n value();\n skipWhitespace();\n if (text[index] === \",\") {\n index++;\n continue;\n }\n if (text[index] === \"]\") {\n index++;\n return;\n }\n fail(\"',' or ']'\");\n }\n }\n if (char === '\"') {\n string();\n return;\n }\n if (char === \"t\") {\n literal(\"true\");\n return;\n }\n if (char === \"f\") {\n literal(\"false\");\n return;\n }\n if (char === \"n\") {\n literal(\"null\");\n return;\n }\n if (char === \"-\" || (char !== undefined && char >= \"0\" && char <= \"9\")) {\n number();\n return;\n }\n fail(\"a value\");\n };\n\n value();\n skipWhitespace();\n if (index < text.length) {\n fail(\"end of input\");\n }\n}\n\n/** 1-based line and column of an offset. */\nexport function lineColumn(\n text: string,\n offset: number,\n): { line: number; column: number } {\n let line = 1;\n let lineStart = 0;\n const end = Math.min(offset, text.length);\n for (let i = 0; i < end; i++) {\n if (text.charCodeAt(i) === 10) {\n line++;\n lineStart = i + 1;\n }\n }\n return { line, column: end - lineStart + 1 };\n}\n\n/** Parses JSON; on failure, reports the message with its line and column. */\nexport function parseJsonText(text: string): JsonParseResult {\n try {\n return { ok: true, value: JSON.parse(text) as unknown };\n } catch (caught) {\n let message = caught instanceof Error ? caught.message : String(caught);\n let offset = 0;\n try {\n scan(text);\n // The scanner accepted what JSON.parse refused (should not happen);\n // keep the engine's message without a position.\n } catch (scanned) {\n if (scanned instanceof ScanError) {\n message = scanned.message;\n offset = scanned.offset;\n }\n }\n return {\n ok: false,\n error: { message, offset, ...lineColumn(text, offset) },\n };\n }\n}\n\n/** The UTF-8 size of a text, in bytes (what the API measures against its limits). */\nexport function byteLength(text: string): number {\n return new TextEncoder().encode(text).byteLength;\n}\n\n/** Formats a byte count: `512 B`, `1.5 KiB`, `2.0 MiB`. */\nexport function formatBytes(bytes: number): string {\n if (bytes < 1024) {\n return `${bytes} B`;\n }\n if (bytes < 1024 * 1024) {\n return `${(bytes / 1024).toFixed(1)} KiB`;\n }\n return `${(bytes / (1024 * 1024)).toFixed(1)} MiB`;\n}\n\n/** The editor's state, as `JsonEditor` reports it on every change. */\nexport interface JsonEditorState {\n /** The raw text. */\n text: string;\n /** The parsed value; `undefined` when the text does not parse (or is empty). */\n value: unknown;\n /** Whether the text parses (or is empty and `allowEmpty`) and fits `maxBytes`. The one flag a submit button needs. */\n valid: boolean;\n /** The parse error with its line and column, or `null`. */\n error: JsonParseError | null;\n /** The text's UTF-8 size. */\n bytes: number;\n /** Whether `bytes` exceeds `maxBytes`. */\n overLimit: boolean;\n}\n\n/** Options of {@link jsonEditorState}. */\nexport interface JsonEditorStateOptions {\n /** The size limit, bytes. */\n maxBytes?: number;\n /** Whether empty (whitespace-only) text is valid, as `undefined`. Defaults to `false`. */\n allowEmpty?: boolean;\n}\n\n/**\n * Works out a {@link JsonEditorState} from text, e.g. to seed a form's\n * validity before the first edit.\n */\nexport function jsonEditorState(\n text: string,\n { maxBytes, allowEmpty = false }: JsonEditorStateOptions = {},\n): JsonEditorState {\n const bytes = byteLength(text);\n const overLimit = maxBytes !== undefined && bytes > maxBytes;\n if (allowEmpty && text.trim() === \"\") {\n return {\n text,\n value: undefined,\n valid: !overLimit,\n error: null,\n bytes,\n overLimit,\n };\n }\n const parsed = parseJsonText(text);\n return parsed.ok\n ? {\n text,\n value: parsed.value,\n valid: !overLimit,\n error: null,\n bytes,\n overLimit,\n }\n : {\n text,\n value: undefined,\n valid: false,\n error: parsed.error,\n bytes,\n overLimit,\n };\n}\n",
|
|
31
|
+
"import type { JsonEditorState, JsonEditorStateOptions } from \"./jsonParse\";\nimport { useId, useMemo } from \"react\";\nimport { Button } from \"./Button\";\nimport { cx } from \"./classNames\";\nimport { joinIds, useField } from \"./fieldContext\";\nimport { formatBytes, jsonEditorState } from \"./jsonParse\";\n\n/** Props of {@link JsonEditor}. */\nexport interface JsonEditorProps extends JsonEditorStateOptions {\n /** The text (controlled). Seed it with `JSON.stringify(value, null, 2)`. */\n value: string;\n /** Called on every edit, format and minify with the new state. */\n onChange: (state: JsonEditorState) => void;\n /** Visible rows. Defaults to `10`. */\n rows?: number;\n /** Accessible name when not inside a `Field`. */\n \"aria-label\"?: string;\n /** The textarea's id. Defaults to the enclosing field's, or a generated one. */\n id?: string;\n /** Read-only: no editing, no format/minify. */\n readOnly?: boolean;\n /** Disabled. */\n disabled?: boolean;\n /** Placeholder text. */\n placeholder?: string;\n /** Extra class names on the wrapper. */\n className?: string;\n}\n\n/**\n * A plain-textarea JSON editor: the parse error with line and column, a\n * UTF-8 size meter against `maxBytes` (turning red over it), and Format /\n * Minify. Inside a `Field` it takes the field's id and description.\n */\nexport function JsonEditor({\n value: text,\n onChange,\n maxBytes,\n allowEmpty = false,\n rows = 10,\n \"aria-label\": ariaLabel,\n id,\n readOnly = false,\n disabled = false,\n placeholder,\n className,\n}: JsonEditorProps) {\n const field = useField();\n const generatedId = useId();\n const inputId = id ?? field?.id ?? generatedId;\n const errorId = `${inputId}-json-error`;\n const sizeId = `${inputId}-json-size`;\n const state = useMemo(\n () => jsonEditorState(text, { maxBytes, allowEmpty }),\n [text, maxBytes, allowEmpty],\n );\n const emit = (next: string) =>\n onChange(jsonEditorState(next, { maxBytes, allowEmpty }));\n const reformat = (indent: number | undefined) => {\n if (state.error === null && state.value !== undefined) {\n emit(JSON.stringify(state.value, null, indent));\n }\n };\n const canFormat =\n !readOnly && !disabled && state.error === null && state.value !== undefined;\n\n return (\n <div\n className={cx(\n \"json-editor\",\n !state.valid && \"json-editor-invalid\",\n className,\n )}\n >\n <textarea\n id={inputId}\n className=\"input json-editor-text\"\n rows={rows}\n value={text}\n placeholder={placeholder}\n readOnly={readOnly}\n disabled={disabled}\n spellCheck={false}\n autoCapitalize=\"off\"\n autoComplete=\"off\"\n aria-label={ariaLabel}\n aria-invalid={!state.valid || field?.[\"aria-invalid\"] || undefined}\n aria-required={field?.required}\n aria-describedby={joinIds(\n field?.[\"aria-describedby\"],\n state.error && errorId,\n sizeId,\n )}\n onChange={(event) => emit(event.target.value)}\n />\n <div className=\"json-editor-bar\">\n {state.error ? (\n <p\n id={errorId}\n className=\"json-editor-error\"\n >\n Line {state.error.line}, column {state.error.column}:{\" \"}\n {state.error.message}\n </p>\n ) : (\n <p className=\"json-editor-ok muted\">Valid JSON</p>\n )}\n <span\n id={sizeId}\n className={cx(\"json-editor-size\", state.overLimit && \"is-over\")}\n >\n {maxBytes !== undefined && (\n <meter\n className=\"json-editor-meter\"\n min={0}\n max={maxBytes}\n low={maxBytes * 0.8}\n high={maxBytes}\n optimum={0}\n value={Math.min(state.bytes, maxBytes)}\n aria-hidden=\"true\"\n />\n )}\n {maxBytes === undefined\n ? formatBytes(state.bytes)\n : state.overLimit\n ? `${formatBytes(state.bytes)} of ${formatBytes(maxBytes)}: over the limit by ${formatBytes(state.bytes - maxBytes)}`\n : `${formatBytes(state.bytes)} of ${formatBytes(maxBytes)}`}\n </span>\n {!readOnly && (\n <span className=\"json-editor-actions\">\n <Button\n size=\"sm\"\n disabled={!canFormat}\n onClick={() => reformat(2)}\n >\n Format\n </Button>\n <Button\n size=\"sm\"\n disabled={!canFormat}\n onClick={() => reformat(undefined)}\n >\n Minify\n </Button>\n </span>\n )}\n </div>\n </div>\n );\n}\n",
|
|
32
|
+
"import type { ReactNode } from \"react\";\nimport { useState } from \"react\";\nimport { cx } from \"./classNames\";\nimport { safeStringify } from \"./copy\";\nimport { CopyButton } from \"./CopyButton\";\n\n/** Limits of a {@link JsonView}. */\ninterface ViewLimits {\n /** Levels expanded on first render. */\n expandDepth: number;\n /** Characters of a string shown before \"show more\". */\n maxStringLength: number;\n /** Children rendered per expand (and per \"show more\"). */\n maxChildren: number;\n}\n\n/** Props of {@link JsonView}. */\nexport interface JsonViewProps {\n /** Any value: JSON, `undefined`, a `Date`, a `bigint`, even a circular object. */\n value: unknown;\n /** Accessible name of the tree, e.g. \"Job data\". Defaults to `\"JSON\"`. */\n label?: string;\n /** Levels expanded on first render; `0` starts collapsed. Defaults to `1`. */\n expandDepth?: number;\n /** Characters of a string shown before a \"show more\" control. Defaults to `200`. */\n maxStringLength?: number;\n /** Array items / object keys rendered per expand; \"show more\" renders the next batch. Defaults to `100`. */\n maxChildren?: number;\n /** Show a Copy button for the whole value (serialised on click, circular-safe). Defaults to `true`. */\n copyable?: boolean;\n /** Extra class names. */\n className?: string;\n}\n\n/** What a value looks like to the viewer. */\ntype Kind =\n | \"object\"\n | \"array\"\n | \"string\"\n | \"number\"\n | \"boolean\"\n | \"null\"\n | \"undefined\"\n | \"other\";\n\n/** Classifies a value for rendering. */\nfunction kindOf(value: unknown): Kind {\n if (value === null) {\n return \"null\";\n }\n if (Array.isArray(value)) {\n return \"array\";\n }\n switch (typeof value) {\n case \"object\":\n return value instanceof Date ? \"other\" : \"object\";\n case \"string\":\n return \"string\";\n case \"number\":\n case \"bigint\":\n return \"number\";\n case \"boolean\":\n return \"boolean\";\n case \"undefined\":\n return \"undefined\";\n default:\n return \"other\";\n }\n}\n\n/** A primitive's (or opaque value's) text. */\nfunction scalarText(value: unknown): string {\n if (typeof value === \"bigint\") {\n return `${value}n`;\n }\n if (value instanceof Date) {\n return Number.isNaN(value.getTime()) ? \"Invalid Date\" : value.toISOString();\n }\n if (typeof value === \"symbol\" || typeof value === \"function\") {\n return value.toString();\n }\n return String(value);\n}\n\n/** The first `limit` entries of a container, arrays keyed by index; never touches the rest. */\nfunction entriesOf(value: object, limit: number): [string, unknown][] {\n if (Array.isArray(value)) {\n return value\n .slice(0, limit)\n .map((item: unknown, index) => [String(index), item]);\n }\n const record = value as Record<string, unknown>;\n return Object.keys(record)\n .slice(0, limit)\n .map((key) => [key, record[key]]);\n}\n\n/**\n * A read-only, collapsible tree of any value. Built for job payloads of any\n * size: levels past `expandDepth` start collapsed, each expand renders at\n * most `maxChildren` children (with \"show more\"), long strings are cut at\n * `maxStringLength`, and a reference back to an ancestor renders as\n * `[Circular]` instead of recursing.\n */\nexport function JsonView({\n value,\n label = \"JSON\",\n expandDepth = 1,\n maxStringLength = 200,\n maxChildren = 100,\n copyable = true,\n className,\n}: JsonViewProps) {\n const limits: ViewLimits = { expandDepth, maxStringLength, maxChildren };\n return (\n <div className={cx(\"json-view\", className)}>\n {copyable && (\n <div className=\"json-view-toolbar\">\n <CopyButton\n text={() => safeStringify(value, 2)}\n ariaLabel={`Copy ${label}`}\n />\n </div>\n )}\n <ul\n className=\"json-tree\"\n aria-label={label}\n >\n <JsonNode\n name={null}\n value={value}\n depth={0}\n ancestors={[]}\n limits={limits}\n />\n </ul>\n </div>\n );\n}\n\n/** Props of {@link JsonNode}. */\ninterface JsonNodeProps {\n /** The key or index, or `null` at the root. */\n name: string | null;\n /** The value at this node. */\n value: unknown;\n /** Nesting level, root = 0. */\n depth: number;\n /** The containers above this node, for the circular check. */\n ancestors: readonly object[];\n /** Rendering limits. */\n limits: ViewLimits;\n}\n\n/** One key/value row, expandable when the value is a non-empty container. */\nfunction JsonNode({ name, value, depth, ancestors, limits }: JsonNodeProps) {\n const kind = kindOf(value);\n const key = name === null ? null : <span className=\"json-key\">{name}: </span>;\n\n if (kind !== \"object\" && kind !== \"array\") {\n return (\n <li className=\"json-row\">\n {key}\n <JsonScalar\n value={value}\n kind={kind}\n maxLength={limits.maxStringLength}\n />\n </li>\n );\n }\n\n const container = value as object;\n if (ancestors.includes(container)) {\n return (\n <li className=\"json-row\">\n {key}\n <span className=\"json-circular\">[Circular]</span>\n </li>\n );\n }\n return (\n <JsonContainer\n nameNode={key}\n value={container}\n isArray={kind === \"array\"}\n depth={depth}\n ancestors={ancestors}\n limits={limits}\n />\n );\n}\n\n/** Props of {@link JsonContainer}. */\ninterface JsonContainerProps {\n /** The rendered key, or `null` at the root. */\n nameNode: ReactNode;\n /** The object or array. */\n value: object;\n /** Whether it is an array. */\n isArray: boolean;\n /** Nesting level. */\n depth: number;\n /** The containers above it. */\n ancestors: readonly object[];\n /** Rendering limits. */\n limits: ViewLimits;\n}\n\n/** An object or array: a disclosure button, and its children once expanded. */\nfunction JsonContainer({\n nameNode,\n value,\n isArray,\n depth,\n ancestors,\n limits,\n}: JsonContainerProps) {\n const [expanded, setExpanded] = useState(depth < limits.expandDepth);\n const [shown, setShown] = useState(limits.maxChildren);\n // Object.keys is O(n) but cheap next to rendering; children are only\n // materialised while expanded.\n const size = isArray\n ? (value as unknown[]).length\n : Object.keys(value).length;\n const open = isArray ? \"[\" : \"{\";\n const close = isArray ? \"]\" : \"}\";\n const summary = isArray\n ? `${size} ${size === 1 ? \"item\" : \"items\"}`\n : `${size} ${size === 1 ? \"key\" : \"keys\"}`;\n\n if (size === 0) {\n return (\n <li className=\"json-row\">\n {nameNode}\n <span className=\"json-punct\">\n {open}\n {close}\n </span>\n </li>\n );\n }\n\n const children = expanded ? entriesOf(value, shown) : [];\n const nextAncestors = expanded ? [...ancestors, value] : ancestors;\n return (\n <li className=\"json-row json-container\">\n <button\n type=\"button\"\n className=\"json-toggle\"\n aria-expanded={expanded}\n onClick={() => setExpanded((current) => !current)}\n >\n <span\n className=\"json-caret\"\n aria-hidden=\"true\"\n />\n {nameNode}\n <span className=\"json-punct\">{open}</span>\n {!expanded && (\n <>\n <span className=\"json-summary\">{summary}</span>\n <span className=\"json-punct\">{close}</span>\n </>\n )}\n </button>\n {expanded && (\n <>\n <ul className=\"json-children\">\n {children.map(([key, item]) => (\n <JsonNode\n key={key}\n name={key}\n value={item}\n depth={depth + 1}\n ancestors={nextAncestors}\n limits={limits}\n />\n ))}\n {size > shown && (\n <li className=\"json-row\">\n <button\n type=\"button\"\n className=\"json-more\"\n onClick={() =>\n setShown((current) => current + limits.maxChildren)\n }\n >\n Show {Math.min(limits.maxChildren, size - shown)} more (\n {size - shown} hidden)\n </button>\n </li>\n )}\n </ul>\n <span className=\"json-punct\">{close}</span>\n </>\n )}\n </li>\n );\n}\n\n/** Props of {@link JsonScalar}. */\ninterface JsonScalarProps {\n /** The value. */\n value: unknown;\n /** Its kind. */\n kind: Kind;\n /** String length before truncation. */\n maxLength: number;\n}\n\n/** A leaf value, colour-coded; long strings truncate with \"show more\". */\nfunction JsonScalar({ value, kind, maxLength }: JsonScalarProps) {\n const [full, setFull] = useState(false);\n if (kind === \"string\") {\n const text = value as string;\n const cut = !full && text.length > maxLength;\n return (\n <>\n <span className=\"json-string\">\n "{cut ? text.slice(0, maxLength) : text}\n {cut ? \"…\" : \"\"}"\n </span>\n {text.length > maxLength && (\n <button\n type=\"button\"\n className=\"json-more\"\n aria-expanded={full}\n onClick={() => setFull((current) => !current)}\n >\n {full ? \"Show less\" : `Show more (${text.length} chars)`}\n </button>\n )}\n </>\n );\n }\n return <span className={`json-${kind}`}>{scalarText(value)}</span>;\n}\n",
|
|
33
|
+
"import type { KeyboardEvent, ReactNode } from \"react\";\nimport { useCallback, useId, useRef } from \"react\";\nimport { useUrlTab } from \"../hooks/useUrlTab\";\nimport { useRouter } from \"../routing\";\nimport { cx } from \"./classNames\";\n\n/** One tab of {@link Tabs}. */\nexport interface TabItem<V extends string> {\n /** The tab's value, e.g. a job state. */\n value: V;\n /** The text on the tab. */\n label: ReactNode;\n /** A count shown as a badge beside the label; omitted when `undefined`. */\n count?: number;\n /** Not selectable. */\n disabled?: boolean;\n /** A tooltip on the tab (its `title`), e.g. what a state's label means. Omitted when `undefined`. */\n title?: string;\n}\n\n/** Props of {@link Tabs}. */\nexport interface TabsProps<V extends string> {\n /** The tabs, in order. */\n tabs: readonly TabItem<V>[];\n /** The selected tab's value. */\n value: V;\n /** Called with the tab the user picked. */\n onChange: (value: V) => void;\n /** Accessible name of the tab list, e.g. \"Job states\". */\n label: string;\n /** The selected tab's content, rendered in the single tab panel. Omit to render only the tab list. */\n children?: ReactNode;\n /** `\"auto\"` selects on arrow-key focus; `\"manual\"` waits for Enter/Space. Defaults to `\"auto\"`. */\n activation?: \"auto\" | \"manual\";\n /** Extra class names on the wrapper. */\n className?: string;\n}\n\n/** Formats a tab count compactly: `1234` → `1.2K`. */\nconst countFormat = new Intl.NumberFormat(undefined, { notation: \"compact\" });\n\n/**\n * Controlled tabs following the WAI-ARIA tabs pattern: `tablist`/`tab`/\n * `tabpanel`, one tab in the tab order (roving `tabIndex`), Left/Right to\n * move (wrapping), Home/End to jump. Disabled tabs are skipped.\n */\nexport function Tabs<V extends string>({\n tabs,\n value,\n onChange,\n label,\n children,\n activation = \"auto\",\n className,\n}: TabsProps<V>) {\n const baseId = useId();\n const listRef = useRef<HTMLDivElement>(null);\n const tabId = (index: number) => `${baseId}-tab-${index}`;\n const panelId = `${baseId}-panel`;\n const selectedIndex = tabs.findIndex((tab) => tab.value === value);\n\n const onKeyDown = (event: KeyboardEvent<HTMLButtonElement>) => {\n const enabled = tabs\n .map((tab, index) => ({ tab, index }))\n .filter(({ tab }) => !tab.disabled);\n if (enabled.length === 0) {\n return;\n }\n const current = enabled.findIndex(\n ({ index }) => tabId(index) === event.currentTarget.id,\n );\n let next: number | null = null;\n switch (event.key) {\n case \"ArrowRight\":\n next = (current + 1) % enabled.length;\n break;\n case \"ArrowLeft\":\n next = (current - 1 + enabled.length) % enabled.length;\n break;\n case \"Home\":\n next = 0;\n break;\n case \"End\":\n next = enabled.length - 1;\n break;\n default:\n return;\n }\n event.preventDefault();\n const target = enabled[next];\n listRef.current?.ownerDocument.getElementById(tabId(target.index))?.focus();\n if (activation === \"auto\") {\n onChange(target.tab.value);\n }\n };\n\n return (\n <div className={cx(\"tabs\", className)}>\n <div\n ref={listRef}\n className=\"tabs-list\"\n role=\"tablist\"\n aria-label={label}\n >\n {tabs.map((tab, index) => {\n const selected = index === selectedIndex;\n // With nothing selected, the first enabled tab takes the tab stop.\n const tabStop =\n selected ||\n (selectedIndex === -1 &&\n index === tabs.findIndex((item) => !item.disabled));\n return (\n <button\n key={tab.value}\n id={tabId(index)}\n type=\"button\"\n role=\"tab\"\n className=\"tab\"\n aria-selected={selected}\n aria-controls={children !== undefined ? panelId : undefined}\n tabIndex={tabStop ? 0 : -1}\n disabled={tab.disabled}\n title={tab.title}\n onClick={() => onChange(tab.value)}\n onKeyDown={onKeyDown}\n >\n <span className=\"tab-label\">{tab.label}</span>\n {tab.count !== undefined && (\n <span\n className=\"tab-count\"\n title={String(tab.count)}\n >\n {countFormat.format(tab.count)}\n </span>\n )}\n </button>\n );\n })}\n </div>\n {children !== undefined && (\n <div\n id={panelId}\n className=\"tabs-panel\"\n role=\"tabpanel\"\n aria-labelledby={\n selectedIndex >= 0 ? tabId(selectedIndex) : undefined\n }\n tabIndex={0}\n >\n {children}\n </div>\n )}\n </div>\n );\n}\n\n/** Props of {@link UrlTabs}. */\nexport interface UrlTabsProps<V extends string> extends Omit<\n TabsProps<V>,\n \"value\" | \"onChange\"\n> {\n /** The query parameter holding the tab, e.g. `\"state\"`. */\n param: string;\n /** The tab when the parameter is absent or not one of `tabs`; selecting it removes the parameter. */\n defaultValue: V;\n /** Query parameters to remove when the tab changes, e.g. `[\"offset\"]` so paging restarts. */\n resetParams?: readonly string[];\n /** Called after the URL changed. */\n onChange?: (value: V) => void;\n}\n\n/**\n * {@link Tabs} whose selection lives in the query string (`?state=failed`),\n * so it survives reload and can be linked. Changes replace the history entry\n * (the router's `navigate(…, { replace: true })`), keep every other\n * parameter, and drop the `resetParams`.\n */\nexport function UrlTabs<V extends string>({\n param,\n defaultValue,\n resetParams = [],\n onChange,\n tabs,\n ...rest\n}: UrlTabsProps<V>) {\n const { location, navigate } = useRouter();\n const value = useUrlTab(param, tabs, defaultValue);\n const select = useCallback(\n (next: V) => {\n const params = new URLSearchParams(location.search);\n if (next === defaultValue) {\n params.delete(param);\n } else {\n params.set(param, next);\n }\n if (next !== value) {\n for (const key of resetParams) {\n params.delete(key);\n }\n }\n const query = params.toString();\n navigate(`${location.path ?? \"/\"}${query ? `?${query}` : \"\"}`, {\n replace: true,\n });\n onChange?.(next);\n },\n [\n location.search,\n location.path,\n defaultValue,\n param,\n value,\n resetParams,\n navigate,\n onChange,\n ],\n );\n return (\n <Tabs\n {...rest}\n tabs={tabs}\n value={value}\n onChange={select}\n />\n );\n}\n",
|
|
34
|
+
"import type { RawResponse } from \"../../../api/client\";\nimport type { JsonEditorState } from \"../../../components/jsonParse\";\nimport type { PermissionProbe } from \"../permissions\";\nimport type { SchemaRoot } from \"../schema/resolve\";\nimport type { DocOperation, DocParameter } from \"./model\";\nimport type { FieldValue, InputKind, TryItRequest, TryItValues } from \"./tryIt\";\nimport { useMemo, useState } from \"react\";\nimport { isApiError } from \"../../../api/errors\";\nimport { Badge } from \"../../../components/Badge\";\nimport { Button } from \"../../../components/Button\";\nimport { ConfirmDialog } from \"../../../components/ConfirmDialog\";\nimport { CopyButton } from \"../../../components/CopyButton\";\nimport { Field } from \"../../../components/Field\";\nimport { Checkbox, Select, TextInput } from \"../../../components/inputs\";\nimport { JsonEditor } from \"../../../components/JsonEditor\";\nimport { jsonEditorState } from \"../../../components/jsonParse\";\nimport { JsonView } from \"../../../components/JsonView\";\nimport { ProblemBanner } from \"../../../components/ProblemBanner\";\nimport { Tabs } from \"../../../components/Tabs\";\nimport { useDocsClient, useDocsUiConfig } from \"../../../context\";\nimport {\n bodySchema,\n buildRequest,\n curlSnippet,\n documentedNote,\n fetchSnippet,\n formParameters,\n initialBodyText,\n inputKind,\n isDestructive,\n paramKey,\n tryItGate,\n} from \"./tryIt\";\n\n/** What one send produced. */\ntype TryItResult =\n | {\n /** A response arrived, whatever its status. */\n ok: true;\n /** The response: real status, visible headers, body and timing. */\n response: RawResponse;\n }\n | {\n /** No response: a network failure (an `ApiError` of kind `network`) or anything else thrown. */\n ok: false;\n /** What was thrown. */\n error: unknown;\n /** Milliseconds from send to failure. */\n ms: number;\n };\n\n/** Props of {@link TryItPanel}. */\nexport interface TryItPanelProps {\n /** The operation to send. */\n operation: DocOperation;\n /** The document, for `$ref`s in parameter and body schemas. */\n root: SchemaRoot;\n /** Whether the API is read-only (the panel is off for mutations). Defaults to `false`. */\n readOnly?: boolean;\n /** Answers \"does the caller hold this permission\" (the panel is off when it is lacked). Absent: every operation may be tried. */\n probe?: PermissionProbe;\n /** After a successful mutation (the host refreshes what it shows). */\n onChanged?: () => void;\n}\n\n/** Hints per input kind. */\nfunction kindHint(\n kind: InputKind,\n parameter: DocParameter,\n): string | undefined {\n switch (kind.kind) {\n case \"list\":\n return \"Comma-separated; each value is sent as a repeated key (name=a&name=b).\";\n case \"enum-list\":\n return \"Each ticked value is sent as a repeated key.\";\n case \"number\": {\n const bounds = [\n kind.min !== undefined ? `min ${kind.min}` : undefined,\n kind.max !== undefined ? `max ${kind.max}` : undefined,\n ].filter(Boolean);\n return [kind.integer ? \"An integer\" : \"A number\", ...bounds].join(\", \");\n }\n default:\n return parameter.in === \"path\"\n ? \"Percent-encoded as one path segment.\"\n : undefined;\n }\n}\n\n/** One parameter's control. */\nfunction ParameterInput({\n parameter,\n kind,\n value,\n onChange,\n disabled,\n}: {\n /** The parameter. */\n parameter: DocParameter;\n /** Its input kind. */\n kind: InputKind;\n /** Its value. */\n value: FieldValue | undefined;\n /** Sets it. */\n onChange: (value: FieldValue) => void;\n /** Whether the panel is off. */\n disabled: boolean;\n}) {\n const text = typeof value === \"string\" ? value : \"\";\n switch (kind.kind) {\n case \"boolean\":\n case \"enum\": {\n const options =\n kind.kind === \"boolean\" ? [\"true\", \"false\"] : kind.options;\n return (\n <Select<string>\n value={text}\n disabled={disabled}\n options={[\n { value: \"\", label: parameter.required ? \"Choose…\" : \"(not sent)\" },\n ...options.map((option) => ({ value: option })),\n ]}\n onChange={onChange}\n />\n );\n }\n case \"enum-list\": {\n const picked = Array.isArray(value) ? value : [];\n return (\n <div\n className=\"http-tryit-checks\"\n role=\"group\"\n aria-label={parameter.name}\n >\n {kind.options.map((option) => (\n <Checkbox\n key={option}\n label={option}\n disabled={disabled}\n checked={picked.includes(option)}\n onChange={(checked) =>\n onChange(\n checked\n ? [...picked, option]\n : picked.filter((item) => item !== option),\n )\n }\n />\n ))}\n </div>\n );\n }\n default:\n return (\n <TextInput\n value={text}\n disabled={disabled}\n inputMode={kind.kind === \"number\" ? \"decimal\" : undefined}\n placeholder={kind.kind === \"list\" ? \"a, b, c\" : undefined}\n onChange={(next) => onChange(next)}\n />\n );\n }\n}\n\n/** The response headers the browser let the page see. */\nfunction HeadersView({\n headers,\n}: {\n /** Lower-cased names to values, in order. */\n headers: Record<string, string>;\n}) {\n const entries = Object.entries(headers);\n return (\n <details\n className=\"http-tryit-headers\"\n data-testid=\"tryit-headers\"\n >\n <summary>{`Headers (${entries.length})`}</summary>\n {entries.length === 0 ? (\n <p className=\"http-muted\">No headers visible to the page.</p>\n ) : (\n <dl>\n {entries.map(([name, value]) => (\n <div\n key={name}\n data-header={name}\n >\n <dt>\n <code>{name}</code>\n </dt>\n <dd>\n <code>{value}</code>\n </dd>\n </div>\n ))}\n </dl>\n )}\n </details>\n );\n}\n\n/** A response body: a JSON tree, text, or \"No body.\" */\nfunction BodyView({\n body,\n label,\n}: {\n /** The parsed JSON, the text, or `undefined`. */\n body: unknown;\n /** The tree's accessible label. */\n label: string;\n}) {\n if (body === undefined) {\n return <p className=\"http-muted\">No body.</p>;\n }\n if (typeof body === \"string\") {\n return (\n <pre\n className=\"http-tryit-text\"\n aria-label={label}\n >\n <code>{body}</code>\n </pre>\n );\n }\n return (\n <JsonView\n value={body}\n label={label}\n expandDepth={2}\n />\n );\n}\n\n/** The real status, timing, headers and body of the last send. */\nfunction ResultView({\n result,\n operation,\n}: {\n /** The outcome. */\n result: TryItResult;\n /** The operation, for its documented statuses. */\n operation: DocOperation;\n}) {\n const response = result.ok ? result.response : undefined;\n const error = result.ok ? result.response.error : result.error;\n const problem = isApiError(error) ? error : undefined;\n const note = response\n ? documentedNote(operation, response.status)\n : undefined;\n const ms = response ? response.durationMs : result.ok ? 0 : result.ms;\n return (\n <section\n className=\"http-tryit-result\"\n aria-label=\"Response\"\n data-testid=\"tryit-result\"\n >\n <div className=\"http-tryit-status\">\n <Badge tone={response?.ok ? \"success\" : \"danger\"}>\n <span data-testid=\"tryit-status\">\n {response\n ? String(response.status)\n : problem?.status === 0\n ? \"No response\"\n : \"Error\"}\n </span>\n </Badge>\n {response?.statusText && (\n <span className=\"http-muted\">{response.statusText}</span>\n )}\n {problem && <code>{problem.code}</code>}\n <span\n className=\"http-tryit-timing\"\n data-testid=\"tryit-timing\"\n >\n {`${Math.round(ms)} ms`}\n </span>\n </div>\n {note && (\n <p\n className=\"http-muted\"\n data-testid=\"tryit-documented\"\n >\n {note}\n </p>\n )}\n {!response && <ProblemBanner error={error} />}\n {response && (\n <>\n {!response.ok && <ProblemBanner error={error} />}\n <HeadersView headers={response.headers} />\n <BodyView\n body={response.body}\n label={\n response.ok\n ? \"Response body\"\n : problem?.kind === \"problem\"\n ? \"Problem body\"\n : \"Response body\"\n }\n />\n </>\n )}\n </section>\n );\n}\n\n/** Which snippet is showing. */\ntype SnippetTab = \"curl\" | \"fetch\";\n\n/** The curl / fetch() snippets of the request the form would send. */\nfunction Snippets({\n request,\n}: {\n /** The request, when the form is complete. */ request:\n TryItRequest | undefined;\n}) {\n const api = useDocsClient();\n const config = useDocsUiConfig();\n const [tab, setTab] = useState<SnippetTab>(\"curl\");\n const context = {\n apiBase: api.base,\n origin: typeof window === \"undefined\" ? \"\" : window.location.origin,\n csrfHeader: config.csrfHeader,\n };\n const text = request\n ? tab === \"curl\"\n ? curlSnippet(request, context)\n : fetchSnippet(request, context)\n : undefined;\n return (\n <Tabs<SnippetTab>\n label=\"Snippets\"\n value={tab}\n onChange={setTab}\n tabs={[\n { value: \"curl\", label: \"curl\" },\n { value: \"fetch\", label: \"fetch()\" },\n ]}\n className=\"http-snippets\"\n >\n {text === undefined ? (\n <p className=\"http-muted\">\n Fill in the required fields to see the snippet.\n </p>\n ) : (\n <div className=\"http-snippet\">\n <CopyButton\n text={text}\n ariaLabel={`Copy the ${tab === \"curl\" ? \"curl command\" : \"fetch() call\"}`}\n />\n <pre data-testid={`snippet-${tab}`}>\n <code>{text}</code>\n </pre>\n </div>\n )}\n </Tabs>\n );\n}\n\n/**\n * The \"try it\" panel of one operation: a form built from its path and query\n * parameters, a JSON editor for its body (prefilled with the schema's\n * required fields), sent to the document's resolved server with the\n * viewer's own client, so the CSRF header and `Content-Type` rules apply\n * exactly as the snippets promise. Mutations ask first; destructive ones\n * need the operation id typed.\n */\nexport function TryItPanel({\n operation,\n root,\n readOnly = false,\n probe,\n onChanged,\n}: TryItPanelProps) {\n const api = useDocsClient();\n const config = useDocsUiConfig();\n const parameters = formParameters(operation);\n const schema = bodySchema(operation);\n const bodyOptional = operation.requestBody?.required !== true;\n\n const [values, setValues] = useState<TryItValues>({});\n const [body, setBody] = useState<JsonEditorState>(() =>\n jsonEditorState(initialBodyText(operation, root), {\n allowEmpty: bodyOptional,\n }),\n );\n const [errors, setErrors] = useState<Record<string, string>>({});\n const [confirming, setConfirming] = useState<TryItRequest | null>(null);\n const [pending, setPending] = useState(false);\n const [result, setResult] = useState<TryItResult | null>(null);\n\n // A standalone viewer knows neither the API's read-only setting nor the\n // caller's permissions (no `probe`); the API's answer stays final either way.\n const gate = tryItGate(operation, {\n readOnly,\n permitted:\n operation.action === undefined || probe === undefined\n ? true\n : probe(operation.action) === true,\n });\n const disabled = !gate.enabled || pending;\n\n const built = useMemo(\n () =>\n buildRequest(\n operation,\n root,\n values,\n schema === undefined ? undefined : body.value,\n ),\n [operation, root, values, schema, body.value],\n );\n\n async function send(request: TryItRequest): Promise<void> {\n setPending(true);\n const started = performance.now();\n try {\n const response = await api.requestRaw(request.method, request.path, {\n query: request.query,\n body: request.body,\n });\n setResult({ ok: true, response });\n if (operation.mutation && response.ok) {\n // The host shows what this changed.\n onChanged?.();\n }\n } catch (error) {\n setResult({ ok: false, error, ms: performance.now() - started });\n } finally {\n setPending(false);\n }\n }\n\n function submit() {\n if (!gate.enabled) {\n return;\n }\n if (schema !== undefined && !body.valid) {\n setErrors({\n body: body.error\n ? `Line ${body.error.line}: ${body.error.message}`\n : \"The body is not valid JSON\",\n });\n return;\n }\n if (!built.ok) {\n setErrors(built.errors);\n return;\n }\n setErrors({});\n if (operation.mutation) {\n setConfirming(built.request);\n return;\n }\n void send(built.request);\n }\n\n const destructive = isDestructive(operation);\n const csrfParameter = operation.parameters.find(\n (parameter) =>\n parameter.in === \"header\" && parameter.name === operation.csrf?.header,\n );\n\n return (\n <section\n className=\"http-tryit\"\n aria-labelledby={`tryit-${operation.id}`}\n data-testid=\"tryit\"\n >\n <h3 id={`tryit-${operation.id}`}>Try it</h3>\n {!gate.enabled && (\n <p\n className=\"http-tryit-disabled\"\n role=\"note\"\n data-testid=\"tryit-disabled\"\n >\n {gate.reason}\n </p>\n )}\n <form\n className=\"http-tryit-form\"\n noValidate\n onSubmit={(event) => {\n event.preventDefault();\n submit();\n }}\n >\n {parameters.length === 0 && schema === undefined && (\n <p className=\"http-muted\">No parameters.</p>\n )}\n {parameters.map((parameter) => {\n const key = paramKey(parameter);\n const kind = inputKind(parameter, root);\n return (\n <Field\n key={key}\n label={\n <>\n <code>{parameter.name}</code>{\" \"}\n <span className=\"http-muted\">{parameter.in}</span>\n </>\n }\n required={parameter.required}\n hint={kindHint(kind, parameter)}\n error={errors[key]}\n >\n <ParameterInput\n parameter={parameter}\n kind={kind}\n value={values[key]}\n disabled={disabled}\n onChange={(next) =>\n setValues((current) => ({ ...current, [key]: next }))\n }\n />\n </Field>\n );\n })}\n {csrfParameter && (\n <p\n className=\"http-muted\"\n data-testid=\"tryit-csrf\"\n >\n {config.csrfHeader === csrfParameter.name\n ? `The ${csrfParameter.name} header is added by the app's client.`\n : config.csrfHeader\n ? `The API asks for ${csrfParameter.name}, but this UI sends ${config.csrfHeader}: the API will refuse this with 403 CSRF_REJECTED.`\n : `The API asks for ${csrfParameter.name}, but this UI is configured with no CSRF header: the API will refuse this with 403 CSRF_REJECTED.`}\n </p>\n )}\n {schema !== undefined && (\n <Field\n label=\"Body (application/json)\"\n required={!bodyOptional}\n hint={\n bodyOptional\n ? \"Optional: leave it empty to send none.\"\n : \"Prefilled with the schema's required fields.\"\n }\n error={errors.body}\n >\n <JsonEditor\n value={body.text}\n allowEmpty={bodyOptional}\n readOnly={disabled}\n rows={8}\n onChange={setBody}\n />\n </Field>\n )}\n <div className=\"http-tryit-actions\">\n <Button\n type=\"submit\"\n variant={\n operation.mutation\n ? destructive\n ? \"danger\"\n : \"primary\"\n : \"primary\"\n }\n disabled={disabled}\n >\n {pending ? \"Sending…\" : `Send ${operation.method}`}\n </Button>\n {result && (\n <Button\n variant=\"ghost\"\n onClick={() => setResult(null)}\n >\n Clear response\n </Button>\n )}\n </div>\n </form>\n <Snippets request={built.ok ? built.request : undefined} />\n {result && (\n <ResultView\n result={result}\n operation={operation}\n />\n )}\n <ConfirmDialog\n open={confirming !== null}\n onClose={() => setConfirming(null)}\n title={`Send ${operation.method} ${confirming?.path ?? operation.path}?`}\n description={\n destructive\n ? \"This changes the real API and cannot be undone.\"\n : \"This changes the real API, as the app's own buttons would.\"\n }\n variant={destructive ? \"danger\" : \"default\"}\n confirmLabel={`Send ${operation.method}`}\n confirmText={destructive ? operation.id : undefined}\n onConfirm={async () => {\n if (confirming) {\n await send(confirming);\n }\n }}\n />\n </section>\n );\n}\n",
|
|
35
|
+
"import type { PermissionProbe } from \"../permissions\";\nimport type { SchemaRoot } from \"../schema/resolve\";\nimport type { DocOperation, DocParameter, DocResponse } from \"./model\";\nimport { Badge } from \"../../../components/Badge\";\nimport { CopyButton } from \"../../../components/CopyButton\";\nimport { useDocsUiConfig } from \"../../../context\";\nimport { Prose } from \"../schema/Prose\";\nimport { SchemaTree } from \"../schema/SchemaTree\";\nimport { methodTone } from \"./model\";\nimport { TryItPanel } from \"./TryItPanel\";\n\n/** Props of {@link OperationView}. */\nexport interface OperationViewProps {\n /** The operation shown. */\n operation: DocOperation;\n /** The document. */\n root: SchemaRoot;\n /** Opens a component schema (`#/components/schemas/X`) in the side panel. */\n onRef: (ref: string, name: string) => void;\n /** Answers \"does the caller hold this permission\" (the have/lack badge). Absent: the permission renders plainly. */\n probe?: PermissionProbe;\n /** Whether the API is read-only (a mutation's panel and marker say so). Defaults to `false`. */\n readOnly?: boolean;\n /** After a successful mutation (the host refreshes what it shows). */\n onChanged?: () => void;\n}\n\n/** A method badge. */\nexport function MethodBadge({\n method,\n}: {\n /** Upper-case method. */ method: string;\n}) {\n return (\n <Badge\n tone={methodTone(method)}\n className=\"http-method\"\n >\n {method}\n </Badge>\n );\n}\n\n/** How an array query parameter is sent, from `style`/`explode`. */\nfunction styleNote(parameter: DocParameter): string | undefined {\n if (parameter.in !== \"query\" || parameter.style === undefined) {\n return undefined;\n }\n if (parameter.style === \"form\" && parameter.explode !== false) {\n return \"Repeat the key for each value (state=a&state=b).\";\n }\n return `style: ${parameter.style}${parameter.explode === undefined ? \"\" : `, explode: ${String(parameter.explode)}`}`;\n}\n\n/** The parameters, grouped by where they go. */\nfunction Parameters({ operation, root, onRef }: OperationViewProps) {\n if (operation.parameters.length === 0) {\n return null;\n }\n return (\n <section\n className=\"http-section\"\n aria-labelledby={`params-${operation.id}`}\n >\n <h3 id={`params-${operation.id}`}>Parameters</h3>\n <ul\n className=\"http-params\"\n data-testid=\"op-parameters\"\n >\n {operation.parameters.map((parameter) => {\n const note = styleNote(parameter);\n return (\n <li\n key={`${parameter.in}:${parameter.name}`}\n className=\"http-param\"\n data-param={parameter.name}\n >\n <div className=\"http-param-head\">\n <code className=\"http-param-name\">{parameter.name}</code>\n <Badge tone=\"neutral\">{parameter.in}</Badge>\n {parameter.required ? (\n <span className=\"schema-required\">required</span>\n ) : (\n <span className=\"http-muted\">optional</span>\n )}\n {parameter.deprecated && (\n <Badge tone=\"warning\">deprecated</Badge>\n )}\n </div>\n {parameter.description && (\n <div className=\"http-param-description\">\n <Prose text={parameter.description} />\n </div>\n )}\n {note && <p className=\"http-muted http-param-style\">{note}</p>}\n <SchemaTree\n schema={parameter.schema}\n root={root}\n depth={0}\n onRef={onRef}\n label={`Schema of ${parameter.name}`}\n />\n </li>\n );\n })}\n </ul>\n </section>\n );\n}\n\n/** The request body. */\nfunction RequestBody({ operation, root, onRef }: OperationViewProps) {\n const body = operation.requestBody;\n if (!body) {\n return null;\n }\n return (\n <section\n className=\"http-section\"\n aria-labelledby={`body-${operation.id}`}\n data-testid=\"op-body\"\n >\n <h3 id={`body-${operation.id}`}>\n Request body{\" \"}\n <span className=\"http-muted\">\n {body.required ? \"(required)\" : \"(optional)\"}\n </span>\n </h3>\n {body.description && <Prose text={body.description} />}\n {body.content.map((media) => (\n <div\n key={media.contentType}\n className=\"http-media\"\n >\n <code className=\"http-media-type\">{media.contentType}</code>\n {media.schema !== undefined && (\n <SchemaTree\n schema={media.schema}\n root={root}\n depth={2}\n onRef={onRef}\n label={`Request body schema (${media.contentType})`}\n />\n )}\n </div>\n ))}\n </section>\n );\n}\n\n/** A status badge's tone. */\nfunction statusTone(\n status: string,\n): \"success\" | \"warning\" | \"danger\" | \"neutral\" {\n if (status.startsWith(\"2\")) {\n return \"success\";\n }\n if (status.startsWith(\"4\")) {\n return \"warning\";\n }\n if (status.startsWith(\"5\")) {\n return \"danger\";\n }\n return \"neutral\";\n}\n\n/** One response. */\nfunction ResponseItem({\n response,\n root,\n onRef,\n}: {\n /** The response. */\n response: DocResponse;\n /** The document. */\n root: SchemaRoot;\n /** Opens a component. */\n onRef: OperationViewProps[\"onRef\"];\n}) {\n const success = response.status.startsWith(\"2\");\n return (\n <li\n className=\"http-response\"\n data-status={response.status}\n >\n <div className=\"http-response-head\">\n <Badge tone={statusTone(response.status)}>{response.status}</Badge>\n <span>{response.description}</span>\n </div>\n {response.codes.length > 0 && (\n <ul\n className=\"http-codes\"\n aria-label={`Problem codes for ${response.status}`}\n >\n {response.codes.map((code) => (\n <li key={code}>\n <code>{code}</code>\n </li>\n ))}\n </ul>\n )}\n {response.content.map((media) => (\n <div\n key={media.contentType}\n className=\"http-media\"\n >\n <code className=\"http-media-type\">{media.contentType}</code>\n {media.schema !== undefined && (\n <SchemaTree\n schema={media.schema}\n root={root}\n depth={success ? 1 : 0}\n onRef={onRef}\n label={`Response ${response.status} schema (${media.contentType})`}\n />\n )}\n </div>\n ))}\n </li>\n );\n}\n\n/**\n * One operation: method and path, summary and description, the permission\n * it needs, whether it is a mutation, its CSRF rules and driver requirements,\n * its parameters, body and responses (schemas as trees), and its try-it panel.\n */\nexport function OperationView({\n operation,\n root,\n onRef,\n probe,\n readOnly = false,\n onChanged,\n}: OperationViewProps) {\n const config = useDocsUiConfig();\n const held =\n operation.action === undefined || probe === undefined\n ? undefined\n : probe(operation.action);\n const csrf = operation.csrf;\n return (\n <article\n className=\"http-operation\"\n aria-labelledby={`op-${operation.id}`}\n data-testid=\"http-operation\"\n >\n <header className=\"http-operation-head\">\n <div className=\"http-operation-line\">\n <MethodBadge method={operation.method} />\n <code\n className=\"http-operation-path\"\n data-testid=\"op-path\"\n >\n {operation.path}\n </code>\n {operation.deprecated && <Badge tone=\"warning\">deprecated</Badge>}\n </div>\n <h2\n id={`op-${operation.id}`}\n className=\"http-operation-title\"\n >\n {operation.summary ?? operation.id}\n </h2>\n <p className=\"http-operation-id\">\n <span className=\"http-muted\">operationId</span>{\" \"}\n <code>{operation.id}</code>\n <CopyButton\n text={operation.id}\n ariaLabel=\"Copy the operation id\"\n />\n </p>\n </header>\n\n <div\n className=\"http-markers\"\n aria-label=\"Access\"\n role=\"group\"\n >\n {operation.action !== undefined &&\n (probe === undefined ? (\n <Badge\n tone=\"neutral\"\n title=\"The permission the API's authorize hook is asked about.\"\n >\n <span data-testid=\"op-permission\">\n Needs <code>{operation.action}</code>\n </span>\n </Badge>\n ) : (\n <Badge\n tone={held ? \"success\" : \"danger\"}\n title=\"From the caller's permissions. authorize may still decide per target.\"\n >\n <span\n data-testid=\"op-permission\"\n data-allowed={String(held ?? false)}\n >\n {held ? \"You have \" : \"You lack \"}\n <code>{operation.action}</code>\n </span>\n </Badge>\n ))}\n <Badge\n tone={operation.mutation ? \"warning\" : \"neutral\"}\n title={\n operation.mutation\n ? \"Changes state: CSRF checks apply, and a read-only API refuses it.\"\n : \"Reads only.\"\n }\n >\n <span data-testid=\"op-mutation\">\n {operation.mutation ? \"Mutation\" : \"Read\"}\n </span>\n </Badge>\n {/* Only for a stale or foreign document: a read-only API documents no mutation (see tryItGate). */}\n {operation.mutation && readOnly && (\n <Badge tone=\"danger\">Refused: the API is read-only</Badge>\n )}\n {operation.requires.length > 0 && (\n <span\n className=\"http-requires\"\n data-testid=\"op-requires\"\n >\n <span className=\"http-muted\">Needs driver support:</span>{\" \"}\n {operation.requires.map((method) => (\n <code key={method}>{method}</code>\n ))}\n </span>\n )}\n </div>\n\n {csrf && (\n <div\n className=\"http-csrf\"\n role=\"note\"\n data-testid=\"op-csrf\"\n >\n {csrf.header !== null && (\n <p>\n Every mutation must carry the <code>{csrf.header}</code> header,\n with any non-empty value.{\" \"}\n {config.csrfHeader === csrf.header\n ? \"The app sends it.\"\n : \"This UI is not configured to send it.\"}\n </p>\n )}\n {csrf.requireJson && (\n <p>\n Send <code>Content-Type: application/json</code>, even with no\n body; any other media type is refused with 415.\n </p>\n )}\n {csrf.header === null && !csrf.requireJson && (\n <p>Cross-site requests are refused (Origin / Sec-Fetch-Site).</p>\n )}\n </div>\n )}\n\n {operation.description && (\n <div className=\"http-description\">\n <Prose text={operation.description} />\n </div>\n )}\n\n <Parameters\n operation={operation}\n root={root}\n onRef={onRef}\n />\n <RequestBody\n operation={operation}\n root={root}\n onRef={onRef}\n />\n\n <section\n className=\"http-section\"\n aria-labelledby={`responses-${operation.id}`}\n >\n <h3 id={`responses-${operation.id}`}>Responses</h3>\n <ul\n className=\"http-responses\"\n data-testid=\"op-responses\"\n >\n {operation.responses.map((response) => (\n <ResponseItem\n key={response.status}\n response={response}\n root={root}\n onRef={onRef}\n />\n ))}\n </ul>\n </section>\n\n <TryItPanel\n key={operation.id}\n operation={operation}\n root={root}\n readOnly={readOnly}\n probe={probe}\n onChanged={onChanged}\n />\n </article>\n );\n}\n",
|
|
36
|
+
"/**\n * Arrow-key movement through a vertical list of links (the API docs\n * sidebars): Down/Up to the next/previous item, stopping at the ends,\n * Home/End to the first/last. Each item stays a Tab stop too, so this adds\n * speed without hiding anything from Tab.\n */\n\n/** The item a key moves focus to from `active`, or `null` when the key is not a movement key (or the list is empty). */\nexport function arrowTarget<T extends Element>(\n key: string,\n items: readonly T[],\n active: Element | null,\n): T | null {\n if (items.length === 0) {\n return null;\n }\n const index = active ? items.indexOf(active as T) : -1;\n switch (key) {\n case \"ArrowDown\":\n return items[index < 0 ? 0 : Math.min(index + 1, items.length - 1)];\n case \"ArrowUp\":\n return items[index <= 0 ? 0 : index - 1];\n case \"Home\":\n return items[0];\n case \"End\":\n return items[items.length - 1];\n default:\n return null;\n }\n}\n",
|
|
37
|
+
"/**\n * The viewer's one keyboard shortcut, as pure functions over a key event, so\n * the rules (and the \"not while typing\" guard) are testable without rendering.\n */\n\n/**\n * Marks a screen's search box for the `/` shortcut:\n * `<input {...SEARCH_SHORTCUT} />`. It also sets `aria-keyshortcuts`, so a\n * screen reader announces the shortcut on the box.\n */\nexport const SEARCH_SHORTCUT = {\n \"data-shortcut\": \"search\",\n \"aria-keyshortcuts\": \"/\",\n} as const;\n\n/** Whether the event comes from somewhere the user types: a shortcut must not steal the key. */\nexport function isTypingTarget(target: EventTarget | null): boolean {\n if (!target || typeof (target as Element).tagName !== \"string\") {\n return false;\n }\n const element = target as HTMLElement;\n const tag = element.tagName;\n if (tag === \"TEXTAREA\" || tag === \"SELECT\") {\n return true;\n }\n if (tag === \"INPUT\") {\n const type = (element as HTMLInputElement).type;\n return ![\n \"checkbox\",\n \"radio\",\n \"button\",\n \"submit\",\n \"reset\",\n \"range\",\n \"color\",\n \"file\",\n ].includes(type);\n }\n return (\n element.isContentEditable ||\n element.closest?.(\"[contenteditable]:not([contenteditable='false'])\") !==\n null\n );\n}\n\n/** The parts of a key event {@link shortcutFor} reads. */\nexport interface ShortcutKeyEvent {\n /** `KeyboardEvent.key`. */\n key: string;\n /** Where it happened. */\n target: EventTarget | null;\n /** Modifier state. */\n ctrlKey: boolean;\n /** Modifier state. */\n metaKey: boolean;\n /** Modifier state. */\n altKey: boolean;\n /** Something else handled it already. */\n defaultPrevented: boolean;\n /** IME composition in progress. */\n isComposing?: boolean;\n}\n\n/**\n * The shortcut a key press means, or `null`. Nothing fires while typing in\n * a field, with Ctrl/Meta/Alt held (the browser's own shortcuts), during\n * composition, or after another handler took the key.\n */\nexport function shortcutFor(event: ShortcutKeyEvent): \"focus-search\" | null {\n if (\n event.defaultPrevented ||\n event.isComposing ||\n event.ctrlKey ||\n event.metaKey ||\n event.altKey ||\n isTypingTarget(event.target)\n ) {\n return null;\n }\n return event.key === \"/\" ? \"focus-search\" : null;\n}\n\n/** The current screen's search box (the first enabled one marked with {@link SEARCH_SHORTCUT}), if it has one. */\nexport function findSearchBox(root: ParentNode): HTMLInputElement | null {\n const candidates = root.querySelectorAll<HTMLInputElement>(\n '[data-shortcut=\"search\"]',\n );\n for (const candidate of Array.from(candidates)) {\n if (!candidate.disabled && candidate.closest(\"[inert],[hidden]\") === null) {\n return candidate;\n }\n }\n return null;\n}\n",
|
|
38
|
+
"import type { KeyboardEvent } from \"react\";\nimport type { DocSection, DocTagGroup } from \"./model\";\nimport { useRef } from \"react\";\nimport { keyedSections } from \"./model\";\nimport { TextInput } from \"../../../components/inputs\";\nimport { arrowTarget } from \"../../../components/listKeys\";\nimport { SEARCH_SHORTCUT } from \"../../../shortcuts\";\nimport { Link } from \"../../../router\";\nimport { MethodBadge } from \"./OperationView\";\n\n/** Props of {@link Sidebar}. */\nexport interface SidebarProps {\n /** The groups to list, already filtered by the search. */\n groups: readonly DocTagGroup[];\n /**\n * The document's sections over those groups (`x-tagGroups`), already\n * filtered: each heads its tags, one level deeper. Default: none, and the\n * groups are listed at the top level.\n */\n sections?: readonly DocSection[];\n /** The search text (`?q=`). */\n query: string;\n /** Sets the search text. */\n onQuery: (query: string) => void;\n /** The link to an operation, keeping the search. */\n hrefOf: (operationId: string) => string;\n}\n\n/** The links of a sidebar, in order. */\nfunction links(root: HTMLElement | null): HTMLAnchorElement[] {\n return root\n ? Array.from(root.querySelectorAll<HTMLAnchorElement>(\"a.http-op-link\"))\n : [];\n}\n\n/** One tag's heading and its operations' links. */\nfunction TagBlock({\n group,\n headingId,\n level,\n hrefOf,\n}: {\n /** The tag and its operations. */\n group: DocTagGroup;\n /** The heading's id, unique in the sidebar (a tag can be in two sections). */\n headingId: string;\n /** The heading's level: 2 at the top, 3 inside a section. */\n level: 2 | 3;\n /** The link to an operation. */\n hrefOf: (operationId: string) => string;\n}) {\n const Heading = level === 2 ? \"h2\" : \"h3\";\n return (\n <section\n className=\"http-tag\"\n aria-labelledby={headingId}\n >\n <Heading\n id={headingId}\n className=\"http-tag-name\"\n title={group.description}\n >\n {group.name}\n </Heading>\n <ul className=\"http-tag-ops\">\n {group.operations.map((operation) => (\n <li key={operation.id}>\n <Link\n to={hrefOf(operation.id)}\n className=\"http-op-link\"\n activeMatch=\"exact\"\n data-operation={operation.id}\n >\n <MethodBadge method={operation.method} />\n <span className=\"http-op-text\">\n <span className=\"http-op-path\">{operation.path}</span>\n {operation.summary && (\n <span className=\"http-op-summary\">{operation.summary}</span>\n )}\n </span>\n </Link>\n </li>\n ))}\n </ul>\n </section>\n );\n}\n\n/**\n * The operations by tag (by section, then tag, when the document declares\n * `x-tagGroups`), with method badges, and a search over path,\n * operation id and summary. Arrow keys move between operations (Home/End to\n * the ends), and ArrowDown from the search goes to the first match.\n */\nexport function Sidebar({\n groups,\n sections,\n query,\n onQuery,\n hrefOf,\n}: SidebarProps) {\n const listRef = useRef<HTMLDivElement>(null);\n const count = groups.reduce((sum, group) => sum + group.operations.length, 0);\n\n function onListKey(event: KeyboardEvent<HTMLDivElement>) {\n const target = arrowTarget(\n event.key,\n links(listRef.current),\n document.activeElement,\n );\n if (target) {\n event.preventDefault();\n target.focus();\n }\n }\n\n return (\n <nav\n className=\"http-sidebar\"\n aria-label=\"Operations\"\n >\n <TextInput\n type=\"search\"\n value={query}\n aria-label=\"Search operations\"\n placeholder=\"Search path, id, summary\"\n {...SEARCH_SHORTCUT}\n onChange={(value) => onQuery(value)}\n onKeyDown={(event) => {\n if (event.key === \"ArrowDown\") {\n const first = links(listRef.current)[0];\n if (first) {\n event.preventDefault();\n first.focus();\n }\n }\n }}\n />\n <p\n className=\"http-muted http-sidebar-count\"\n aria-live=\"polite\"\n >\n {count === 1 ? \"1 operation\" : `${count} operations`}\n </p>\n {/* Arrow keys move between the links; each link stays a tab stop too. */}\n <div\n ref={listRef}\n className=\"http-sidebar-list\"\n onKeyDown={onListKey}\n >\n {sections === undefined\n ? groups.map((group) => (\n <TagBlock\n key={group.name}\n group={group}\n headingId={`tag-${group.name}`}\n level={2}\n hrefOf={hrefOf}\n />\n ))\n : keyedSections(sections).map(({ key, index, section }) => (\n <section\n key={key}\n className=\"http-section\"\n aria-labelledby={`section-${index}`}\n >\n <h2\n id={`section-${index}`}\n className=\"http-section-name\"\n >\n {section.name}\n </h2>\n {section.tags.map((group) => (\n <TagBlock\n key={group.name}\n group={group}\n headingId={`tag-${index}-${group.name}`}\n level={3}\n hrefOf={hrefOf}\n />\n ))}\n </section>\n ))}\n {groups.length === 0 && (\n <p className=\"http-muted\">No operation matches.</p>\n )}\n </div>\n </nav>\n );\n}\n",
|
|
39
|
+
"/** The HTTP API reference (OpenAPI 3.1): `/http` and `/http/:operationId` under the mount. */\nimport type { DocsClient } from \"../../../api/client\";\nimport type { SpecDocument } from \"../../../api/docs\";\nimport type { PermissionProbe } from \"../permissions\";\nimport type { DocInfo, DocSection, DocTagGroup } from \"./model\";\nimport { useMemo, useState } from \"react\";\nimport { createDocsClient } from \"../../../api/client\";\nimport { Badge } from \"../../../components/Badge\";\nimport { Card } from \"../../../components/Card\";\nimport { CopyButton } from \"../../../components/CopyButton\";\nimport { Dialog } from \"../../../components/Dialog\";\nimport { EmptyState } from \"../../../components/EmptyState\";\nimport {\n DocsClientContext,\n useDocsFetch,\n useDocsUiConfig,\n} from \"../../../context\";\nimport { Link } from \"../../../router\";\nimport { useParams, useQueryParam } from \"../../../routing\";\nimport { Prose } from \"../schema/Prose\";\nimport { refName } from \"../schema/resolve\";\nimport { SchemaTree } from \"../schema/SchemaTree\";\nimport {\n componentSchemaNames,\n filterGroups,\n groupBySection,\n keyedSections,\n groupByTag,\n listOperations,\n readInfo,\n resolveServerUrl,\n} from \"./model\";\nimport { OperationView } from \"./OperationView\";\nimport { Sidebar } from \"./Sidebar\";\nimport \"./http.css\";\n\n/** The app path of an operation, keeping a search; `base` is the docs section's path within the host app (\"\" for a standalone viewer). */\nfunction operationHref(operationId: string, query: string, base = \"\"): string {\n const search = query ? `?q=${encodeURIComponent(query)}` : \"\";\n return `${base}/http/${encodeURIComponent(operationId)}${search}`;\n}\n\n/** The document's title, version, description and servers. */\nfunction InfoHeader({\n info,\n serverBase,\n}: {\n /** The header. */\n info: DocInfo;\n /** The resolved URL requests are sent to. */\n serverBase: string;\n}) {\n const config = useDocsUiConfig();\n const origin = typeof window === \"undefined\" ? \"\" : window.location.origin;\n return (\n <Card className=\"http-info\">\n <div className=\"http-info-line\">\n <Badge tone=\"accent\">{`OpenAPI ${info.openapi}`}</Badge>\n {info.version && <Badge tone=\"neutral\">{`v${info.version}`}</Badge>}\n </div>\n {info.description && (\n <div className=\"http-description\">\n <Prose text={info.description} />\n </div>\n )}\n <dl\n className=\"http-servers\"\n data-testid=\"http-servers\"\n >\n {info.servers.map((server) => {\n const resolved = resolveServerUrl(\n server.url,\n config.documentUrl,\n origin,\n );\n return (\n <div key={server.url}>\n <dt>\n Server <code>{server.url}</code>\n {server.description ? ` (${server.description})` : \"\"}\n </dt>\n <dd>\n <code data-testid=\"http-server-url\">{resolved}</code>\n <CopyButton\n text={resolved}\n ariaLabel=\"Copy the server URL\"\n />\n </dd>\n </div>\n );\n })}\n <div>\n <dt>Requests are sent to</dt>\n <dd>\n <code data-testid=\"http-request-base\">{serverBase}</code>\n </dd>\n </div>\n </dl>\n <p className=\"http-muted\">Paths below are relative to the server.</p>\n </Card>\n );\n}\n\n/** One tag's description and its operations' links, on the landing view. */\nfunction OverviewTag({\n group,\n query,\n pathBase,\n}: {\n /** The tag and its operations. */\n group: DocTagGroup;\n /** The search, kept on links. */\n query: string;\n /** The docs section's path within the host app. */\n pathBase: string;\n}) {\n return (\n <>\n {group.description && <p className=\"http-muted\">{group.description}</p>}\n <ul className=\"http-overview-ops\">\n {group.operations.map((operation) => (\n <li key={operation.id}>\n <Link to={operationHref(operation.id, query, pathBase)}>\n <code>{operation.method}</code> {operation.path}\n </Link>\n {operation.summary && (\n <span className=\"http-muted\">{` ${operation.summary}`}</span>\n )}\n </li>\n ))}\n </ul>\n </>\n );\n}\n\n/** The landing view: tags (in their sections, when the document has them) with their operations, and the component schemas. */\nfunction Overview({\n doc,\n groups,\n sections,\n query,\n onRef,\n pathBase,\n}: {\n /** The document. */\n doc: SpecDocument;\n /** Every tag group (unfiltered). */\n groups: readonly DocTagGroup[];\n /** The document's sections over them (`x-tagGroups`), when it declares any: one card each, its tags inside. */\n sections: readonly DocSection[] | undefined;\n /** The search, kept on links. */\n query: string;\n /** Opens a component schema. */\n onRef: (ref: string, name: string) => void;\n /** The docs section's path within the host app. */\n pathBase: string;\n}) {\n const names = componentSchemaNames(doc);\n return (\n <div className=\"http-overview\">\n {sections === undefined\n ? groups.map((group) => (\n <Card\n key={group.name}\n title={group.name}\n >\n <OverviewTag\n group={group}\n query={query}\n pathBase={pathBase}\n />\n </Card>\n ))\n : keyedSections(sections).map(({ key, section }) => (\n <Card\n key={key}\n title={section.name}\n className=\"http-overview-section\"\n >\n {section.tags.map((group) => (\n <section\n key={group.name}\n className=\"http-overview-tag\"\n aria-label={group.name}\n >\n <h3 className=\"http-overview-tag-name\">{group.name}</h3>\n <OverviewTag\n group={group}\n query={query}\n pathBase={pathBase}\n />\n </section>\n ))}\n </Card>\n ))}\n {names.length > 0 && (\n <Card title=\"Schemas\">\n <ul\n className=\"http-components\"\n data-testid=\"http-components\"\n >\n {names.map((name) => (\n <li\n key={name}\n id={`schema-${name}`}\n >\n <button\n type=\"button\"\n className=\"schema-ref schema-ref-link\"\n onClick={() => onRef(`#/components/schemas/${name}`, name)}\n >\n {name}\n </button>\n </li>\n ))}\n </ul>\n </Card>\n )}\n </div>\n );\n}\n\n/** The side panel showing one component schema; a `$ref` inside it replaces it. */\nfunction SchemaPanel({\n doc,\n refValue,\n onClose,\n onRef,\n}: {\n /** The document. */\n doc: SpecDocument;\n /** The component shown, or `null` when closed. */\n refValue: string | null;\n /** Closes it. */\n onClose: () => void;\n /** Shows another component. */\n onRef: (ref: string, name: string) => void;\n}) {\n return (\n <Dialog\n open={refValue !== null}\n onClose={onClose}\n title={refValue === null ? \"\" : refName(refValue)}\n description={refValue ?? undefined}\n size=\"lg\"\n className=\"http-schema-panel\"\n >\n {refValue !== null && (\n <SchemaTree\n key={refValue}\n schema={{ $ref: refValue }}\n root={doc}\n depth={2}\n onRef={onRef}\n label={`Schema ${refName(refValue)}`}\n />\n )}\n </Dialog>\n );\n}\n\n/** Props of {@link HttpReference}. */\nexport interface HttpReferenceProps {\n /** The OpenAPI document. */\n doc: SpecDocument;\n /** The extension prefixes to read; defaults to the configured ones. */\n extensions?: readonly string[];\n /** The client the try-it panel sends with. Default: built on the document's resolved first server. */\n client?: DocsClient;\n /** Answers \"does the caller hold this permission\" (the have/lack badge and the try-it gate). */\n probe?: PermissionProbe;\n /** Whether the API is read-only. Defaults to `false`. */\n readOnly?: boolean;\n /** After a successful mutation (the host refreshes what it shows). */\n onChanged?: () => void;\n /** The docs section's path within the host app (\"\" for a standalone viewer). */\n pathBase?: string;\n}\n\n/** The loaded reference: sidebar, info or operation, and the schema panel. */\nexport function HttpReference({\n doc,\n extensions,\n client: clientOverride,\n probe,\n readOnly = false,\n onChanged,\n pathBase = \"\",\n}: HttpReferenceProps) {\n const config = useDocsUiConfig();\n const prefixes = extensions ?? config.extensions;\n const { operationId } = useParams<{ operationId?: string }>();\n const [query, setQuery] = useQueryParam(\"q\");\n const [panel, setPanel] = useState<string | null>(null);\n const info = useMemo(() => readInfo(doc), [doc]);\n const operations = useMemo(\n () => listOperations(doc, { extensions: prefixes }),\n [doc, prefixes],\n );\n const groups = useMemo(() => groupByTag(doc, operations), [doc, operations]);\n const visible = useMemo(() => filterGroups(groups, query), [groups, query]);\n // `undefined` without `x-tagGroups`: the sidebar is then one level, as before.\n const sections = useMemo(() => groupBySection(doc, groups), [doc, groups]);\n const visibleSections = useMemo(\n () => groupBySection(doc, visible),\n [doc, visible],\n );\n const selected =\n operationId === undefined\n ? undefined\n : operations.find((operation) => operation.id === operationId);\n const openRef = (ref: string) => setPanel(ref);\n\n // The try-it panel and the snippets send to the document's first server,\n // resolved against the origin the document was fetched from.\n const origin = typeof window === \"undefined\" ? \"\" : window.location.origin;\n const serverBase = resolveServerUrl(\n info.servers[0]?.url ?? \"/\",\n config.documentUrl,\n origin,\n );\n const fetcher = useDocsFetch();\n const client = useMemo(\n () =>\n clientOverride ??\n createDocsClient({\n base: serverBase,\n csrfHeader: config.csrfHeader,\n fetch: fetcher,\n }),\n [clientOverride, serverBase, config.csrfHeader, fetcher],\n );\n\n return (\n <div\n className=\"screen http-docs\"\n data-testid=\"http-docs\"\n >\n <div className=\"http-docs-title\">\n <h1 className=\"screen-title\">{info.title}</h1>\n </div>\n <div className=\"http-docs-layout\">\n <Sidebar\n groups={visible}\n sections={visibleSections}\n query={query}\n onQuery={(next) => setQuery(next)}\n hrefOf={(id) => operationHref(id, query, pathBase)}\n />\n <div className=\"http-docs-main\">\n {operationId === undefined ? (\n <>\n <InfoHeader\n info={info}\n serverBase={serverBase}\n />\n <Overview\n doc={doc}\n groups={groups}\n sections={sections}\n query={query}\n onRef={openRef}\n pathBase={pathBase}\n />\n </>\n ) : selected ? (\n <DocsClientContext value={client}>\n <OperationView\n key={selected.id}\n operation={selected}\n root={doc}\n onRef={openRef}\n probe={probe}\n readOnly={readOnly}\n onChanged={onChanged}\n />\n </DocsClientContext>\n ) : (\n <EmptyState\n title=\"No such operation\"\n description={`This document has no operation \"${operationId}\".`}\n action={<Link to={`${pathBase}/http`}>All operations</Link>}\n />\n )}\n </div>\n </div>\n <SchemaPanel\n doc={doc}\n refValue={panel}\n onClose={() => setPanel(null)}\n onRef={openRef}\n />\n </div>\n );\n}\n",
|
|
40
|
+
"import type { SpecDocument } from \"../../../api/docs\";\nimport { DEFAULT_EXTENSION_PREFIXES } from \"../../../../../lib/docs/browser/config.ts\";\nimport { resolveRef } from \"../schema/resolve\";\n\n/**\n * The WebSocket reference's pure half: the AsyncAPI 3.0 document read into\n * what the screen shows (header, sidebar items, per-item detail, the\n * specification extensions).\n *\n * Everything is read defensively: the document is JSON from the server, and a\n * field this reader does not find is left out rather than guessed.\n *\n * The extensions are vendor-neutral: each is named on its node as\n * `<prefix>-<name>` and read from the first prefix of\n * {@link WsModelOptions.extensions} that carries it (default\n * `[\"x-bun-common\", \"x-bun-jobs\"]`), so a document written for either\n * vocabulary — or an operator's own — renders, and an extension under any\n * other prefix is ignored.\n */\n\n/** A JSON object, loosely. */\ntype Json = Record<string, unknown>;\n\n/** Whether a value is a plain JSON object. */\nfunction isObject(value: unknown): value is Json {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/** A string field, or `undefined`. */\nfunction str(value: unknown): string | undefined {\n return typeof value === \"string\" ? value : undefined;\n}\n\n/** A number field, or `undefined`. */\nfunction num(value: unknown): number | undefined {\n return typeof value === \"number\" && Number.isFinite(value)\n ? value\n : undefined;\n}\n\n/** Options of {@link readWsDoc}. */\nexport interface WsModelOptions {\n /**\n * The extension prefixes, first hit winning. Defaults to\n * `[\"x-bun-common\", \"x-bun-jobs\"]`.\n */\n extensions?: readonly string[];\n /**\n * The subprotocol shown when the document states none, e.g. the client\n * contract's. Unset: the panel says the document does not state one.\n */\n defaultSubprotocol?: string;\n}\n\n/** One extension read: the full key it was found under, and its value. */\ninterface FoundExtension {\n /** The full key, e.g. `x-bun-jobs-limits`. */\n key: string;\n /** Its value. */\n value: unknown;\n}\n\n/** Reads the `<prefix>-<name>` extension of a node from the first prefix carrying it. */\nfunction extensionOf(\n node: Json | undefined,\n name: string,\n prefixes: readonly string[],\n): FoundExtension | undefined {\n if (node === undefined) {\n return undefined;\n }\n for (const prefix of prefixes) {\n const key = `${prefix}-${name}`;\n if (Object.hasOwn(node, key)) {\n return { key, value: node[key] };\n }\n }\n return undefined;\n}\n\n/** The last segment of a local `$ref` (`#/channels/queue` → `queue`), unescaped. */\nexport function refName(ref: unknown): string | undefined {\n const pointer = isObject(ref) ? str(ref.$ref) : undefined;\n if (pointer === undefined || !pointer.startsWith(\"#/\")) {\n return undefined;\n }\n const last = pointer.split(\"/\").at(-1);\n return last === undefined\n ? undefined\n : last.replaceAll(\"~1\", \"/\").replaceAll(\"~0\", \"~\");\n}\n\n/** The app path of an item, keeping the search; `base` is the docs section's path within the host app (\"\" for a standalone viewer). */\nexport function itemPath(slug: string, query = \"\", base = \"\"): string {\n return `${base}/ws/${encodeURIComponent(slug)}${query ? `?${new URLSearchParams({ q: query }).toString()}` : \"\"}`;\n}\n\n/** The kinds of sidebar item. */\nexport type WsItemKind =\n | \"channel\"\n | \"operation\"\n | \"message\"\n | \"limits\"\n | \"close-codes\"\n | \"upgrade-refusals\";\n\n/** The URL slug of an item: `channel-queue`, `operation-subscribe`, `message-queue.completed`, `limits`. */\nexport function itemSlug(kind: WsItemKind, key?: string): string {\n return key === undefined ? kind : `${kind}-${key}`;\n}\n\n/** A channel parameter. */\nexport interface WsParameter {\n /** Its name in the address template (`queue`, `jobId`, `runner`). */\n name: string;\n /** What it is. */\n description: string | undefined;\n /**\n * The value's JSON Schema, from the `<prefix>-schema` extension (AsyncAPI\n * 3.0's Parameter Object has no `schema` field); `undefined` when the\n * document gives none.\n */\n schema: Json | undefined;\n}\n\n/** One channel: the `connection`, or a logical family. */\nexport interface WsChannel {\n /** Its key under `channels` (`connection`, `queue`, ...). */\n key: string;\n /** The item slug. */\n slug: string;\n /** Its title, or the key. */\n title: string;\n /** The address: the socket path for `connection`, a name template otherwise. */\n address: string;\n /** Its description. */\n description: string | undefined;\n /** Address parameters, in template order. */\n parameters: WsParameter[];\n /** Message ids it carries, in document order. */\n messages: string[];\n /** The WebSocket binding's method, when declared. */\n bindingMethod: string | undefined;\n /** Whether this is the transport itself rather than a logical channel. */\n isConnection: boolean;\n}\n\n/** One operation. */\nexport interface WsOperation {\n /** Its key under `operations`. */\n key: string;\n /** The item slug. */\n slug: string;\n /** Its title, or the key. */\n title: string;\n /** Its summary. */\n summary: string | undefined;\n /** `send` (client → server) or `receive` (server → client). */\n action: \"send\" | \"receive\" | undefined;\n /** The channel key it is on. */\n channel: string | undefined;\n /** The message ids it sends or receives. */\n messages: string[];\n /** The reply, for a request/response operation. */\n reply: {\n /** The channel key the reply arrives on. */\n channel: string | undefined;\n /** The message ids the reply may be. */\n messages: string[];\n } | null;\n /** The permission it needs (the `<prefix>-action` extension). */\n permission: string | undefined;\n}\n\n/** One message. */\nexport interface WsMessage {\n /** Its key under `components.messages` (`subscribe`, `queue.completed`). */\n key: string;\n /** The item slug. */\n slug: string;\n /** Its `name`, or the key. */\n name: string;\n /** Its title. */\n title: string | undefined;\n /** Its summary. */\n summary: string | undefined;\n /** Its content type. */\n contentType: string | undefined;\n /** The payload schema as written (often a `$ref`). */\n payload: unknown;\n /** Its examples, when the document gives any. */\n examples: {\n /** The example's name. */\n name: string | undefined;\n /** Its summary. */\n summary: string | undefined;\n /** Its payload. */\n payload: unknown;\n }[];\n /**\n * For an event message — one whose key is `<kind>.<type>`\n * (`queue.completed`) — the two parts; `null` for a control message (a key\n * with no dot).\n */\n event: {\n /** The part before the first dot (`queue`). */\n kind: string;\n /** The part after it (`completed`). */\n type: string;\n } | null;\n}\n\n/** One `<prefix>-close-codes` entry. */\nexport interface WsCloseCode {\n /** The close code. */\n code: number;\n /** Its name. */\n name: string;\n /** When it is sent. */\n description: string;\n}\n\n/** One `<prefix>-upgrade-refusals` entry. */\nexport interface WsUpgradeRefusal {\n /** The HTTP status. */\n status: number;\n /** The problem's `code`. */\n code: string;\n /** When it is sent. */\n description: string;\n /** The response's content type. */\n contentType: string | undefined;\n /** Headers it carries. */\n headers: Record<string, string>;\n}\n\n/** `<prefix>-limits`, as far as it was found. */\nexport interface WsLimits {\n /** Every numeric limit, by name, in document order. */\n values: [name: string, value: number][];\n /** The replay ring: its size and age, `false` when replay is off, `undefined` when not stated. */\n replay:\n | {\n /** How many events it keeps. */\n size: number | undefined;\n /** How old an event it keeps, in ms. */\n maxAgeMs: number | undefined;\n }\n | false\n | undefined;\n}\n\n/** A security scheme, as `components.securitySchemes` declares it. */\nexport interface WsSecurityScheme {\n /** Its name. */\n name: string;\n /** Its AsyncAPI type (`http`, `httpApiKey`, `oauth2`, ...). */\n type: string | undefined;\n /** A one-line reading of it (`bearer`, `header X-Key`). */\n detail: string | undefined;\n /** Its description. */\n description: string | undefined;\n /** What a browser WebSocket cannot carry (the `<prefix>-note` extension). */\n note: string | undefined;\n}\n\n/** One requirement: every scheme in it is needed together. */\nexport type WsRequirement = {\n /** The scheme. */\n scheme: string;\n /** The scopes it needs. */\n scopes: string[];\n}[];\n\n/** The document's server. */\nexport interface WsServer {\n /** Its key under `servers`: `api`, or the first server's when there is no `api`. */\n key: string;\n /** The host, as served (the request's own when fetched over HTTP). */\n host: string;\n /** Whether the host is still the `{host}` template (the in-process document). */\n hostIsTemplate: boolean;\n /** The template's default host, when it is one. */\n hostDefault: string | undefined;\n /** The socket's path. */\n pathname: string | undefined;\n /** `ws` or `wss`. */\n protocol: string | undefined;\n /** Its description. */\n description: string | undefined;\n /** The socket URL, when host and protocol are known. */\n url: string | undefined;\n}\n\n/** The whole document, read. */\nexport interface WsDoc {\n /** `info.title`. */\n title: string;\n /** `info.version`. */\n version: string | undefined;\n /** `info.description`. */\n description: string | undefined;\n /** `servers.api` (or the first server). */\n server: WsServer | null;\n /** The subprotocol, and where it was read from. */\n subprotocol: {\n /** The subprotocol, or `undefined` when the document states none and no default was configured. */\n value: string | undefined;\n /** Whether it was read from the document rather than assumed. */\n fromDocument: boolean;\n /**\n * Where it came from: the `<prefix>-subprotocol` extension on the server\n * (`servers.api`, see {@link WsServer.key}), else on the connection\n * channel; the connection channel's description (a document that states\n * it only there); or {@link WsModelOptions.defaultSubprotocol} when the\n * document says nothing. {@link subprotocolSource} words it.\n */\n source:\n | \"server-extension\"\n | \"connection-extension\"\n | \"connection-prose\"\n | \"default\";\n };\n /** The full key the subprotocol extension was read from (e.g. `x-bun-jobs-subprotocol`), when it was one. */\n subprotocolKey?: string;\n /** The full keys the connection's extensions were found under (e.g. `x-bun-jobs-limits`), for the panels to name. */\n extensionKeys: {\n /** The key the security requirements were read from. */\n security?: string;\n /** The key the limits were read from. */\n limits?: string;\n /** The key the close codes were read from. */\n closeCodes?: string;\n /** The key the upgrade refusals were read from. */\n upgradeRefusals?: string;\n };\n /** Declared schemes that a requirement names. */\n securitySchemes: WsSecurityScheme[];\n /** Every requirement (the `<prefix>-security` extension, else the native list): any one suffices. */\n requirements: WsRequirement[];\n /** Whether some requirement could not be expressed in the native `security` list (it names several schemes). */\n conjunctive: boolean;\n /** Channels: `connection` first, then the families in document order. */\n channels: WsChannel[];\n /** Operations, in document order. */\n operations: WsOperation[];\n /** Messages: control messages, then events. */\n messages: WsMessage[];\n /** `<prefix>-limits`, or `null` when absent. */\n limits: WsLimits | null;\n /** `<prefix>-close-codes`. */\n closeCodes: WsCloseCode[];\n /** `<prefix>-upgrade-refusals`. */\n refusals: WsUpgradeRefusal[];\n}\n\n/**\n * Where the subprotocol shown was read from, in words: `from servers.api\n * (x-bun-jobs-subprotocol)`, `from the connection channel\n * (x-bun-common-subprotocol)`, `from the connection channel's description`, or\n * `not stated in the document` (plus `; the configured default` when one was).\n */\nexport function subprotocolSource(doc: WsDoc): string {\n switch (doc.subprotocol.source) {\n case \"server-extension\":\n return `from servers.${doc.server?.key ?? \"api\"} (${doc.subprotocolKey ?? \"subprotocol extension\"})`;\n case \"connection-extension\":\n return `from the connection channel (${doc.subprotocolKey ?? \"subprotocol extension\"})`;\n case \"connection-prose\":\n return \"from the connection channel's description\";\n case \"default\":\n return doc.subprotocol.value === undefined\n ? \"not stated in the document\"\n : \"not stated; the configured default\";\n }\n}\n\n/** Reads the subprotocol from the connection channel's prose (`subprotocol \\`bun-jobs.v1\\``). */\nfunction readSubprotocol(description: string | undefined): string | undefined {\n return description?.match(/subprotocol `([^`]+)`/)?.[1];\n}\n\n/** Reads a scheme's one-line detail. */\nfunction schemeDetail(scheme: Json): string | undefined {\n const type = str(scheme.type);\n switch (type) {\n case \"http\":\n return [str(scheme.scheme), str(scheme.bearerFormat)]\n .filter(Boolean)\n .join(\", \");\n case \"httpApiKey\":\n return `${str(scheme.in) ?? \"?\"} ${str(scheme.name) ?? \"?\"}`;\n case \"oauth2\": {\n const scopes = isObject(scheme.flows)\n ? Object.values(scheme.flows).flatMap((flow) =>\n isObject(flow) && isObject(flow.availableScopes)\n ? Object.keys(flow.availableScopes)\n : [],\n )\n : [];\n return scopes.length > 0\n ? `scopes: ${[...new Set(scopes)].join(\", \")}`\n : undefined;\n }\n case \"openIdConnect\":\n return str(scheme.openIdConnectUrl);\n default:\n return undefined;\n }\n}\n\n/** Reads a requirement object (`{ bearer: [], key: [\"read\"] }`). */\nfunction readRequirement(requirement: unknown): WsRequirement | null {\n if (!isObject(requirement)) {\n return null;\n }\n return Object.entries(requirement).map(([scheme, scopes]) => ({\n scheme,\n scopes: Array.isArray(scopes)\n ? scopes.filter((scope): scope is string => typeof scope === \"string\")\n : [],\n }));\n}\n\n/** Reads a native `server.security` entry: a `$ref`, or an inline scheme with `scopes`. */\nfunction readNativeRequirement(\n entry: unknown,\n schemes: Json,\n): WsRequirement | null {\n const name = refName(entry);\n if (name !== undefined) {\n return [{ scheme: name, scopes: [] }];\n }\n if (!isObject(entry)) {\n return null;\n }\n // An inline copy of a declared scheme: find it by its fields.\n const { scopes, ...rest } = entry;\n const match = Object.entries(schemes).find(\n ([, scheme]) =>\n isObject(scheme) &&\n Object.entries(rest).every(\n ([key, value]) => JSON.stringify(scheme[key]) === JSON.stringify(value),\n ),\n );\n return [\n {\n scheme: match?.[0] ?? str(rest.type) ?? \"(inline)\",\n scopes: Array.isArray(scopes)\n ? scopes.filter((scope): scope is string => typeof scope === \"string\")\n : [],\n },\n ];\n}\n\n/** The ids of a list of message `$ref`s. */\nfunction messageIds(list: unknown): string[] {\n const entries = Array.isArray(list)\n ? list\n : isObject(list)\n ? Object.values(list)\n : [];\n return entries.flatMap((entry) => {\n const name = refName(entry);\n return name === undefined ? [] : [name];\n });\n}\n\n/** Reads the event kind and type from a message key (`queue.completed`); `null` for a control message. */\nfunction eventOf(key: string): WsMessage[\"event\"] {\n const match = /^([^.]+)\\.(.+)$/.exec(key);\n return match ? { kind: match[1]!, type: match[2]! } : null;\n}\n\n/** Reads the numeric limits and the replay setting. */\nfunction readLimits(raw: unknown): WsLimits | null {\n if (!isObject(raw)) {\n return null;\n }\n const values: [string, number][] = [];\n for (const [name, value] of Object.entries(raw)) {\n const n = num(value);\n if (name !== \"replay\" && n !== undefined) {\n values.push([name, n]);\n }\n }\n const replay = raw.replay;\n return {\n values,\n replay:\n replay === false\n ? false\n : isObject(replay)\n ? { size: num(replay.size), maxAgeMs: num(replay.maxAgeMs) }\n : undefined,\n };\n}\n\n/** Reads the document into what the screen shows. */\nexport function readWsDoc(\n doc: SpecDocument,\n options: WsModelOptions = {},\n): WsDoc {\n const prefixes = options.extensions ?? DEFAULT_EXTENSION_PREFIXES;\n const info = isObject(doc.info) ? doc.info : {};\n const components = isObject(doc.components) ? doc.components : {};\n const rawSchemes = isObject(components.securitySchemes)\n ? components.securitySchemes\n : {};\n\n // The server.\n const servers = isObject(doc.servers) ? doc.servers : {};\n const serverKey = isObject(servers.api)\n ? \"api\"\n : Object.keys(servers).find((key) => isObject(servers[key]));\n const rawServer =\n serverKey === undefined ? undefined : (servers[serverKey] as Json);\n let server: WsServer | null = null;\n let requirements: WsRequirement[] = [];\n let nativeCount = 0;\n let securityKey: string | undefined;\n if (rawServer) {\n const variables = isObject(rawServer.variables) ? rawServer.variables : {};\n const hostVar = isObject(variables.host) ? variables.host : undefined;\n const rawHost = str(rawServer.host) ?? \"\";\n const hostIsTemplate = /\\{[^}]+\\}/.test(rawHost);\n const hostDefault = str(hostVar?.default);\n const pathname = str(rawServer.pathname);\n const protocol = str(rawServer.protocol);\n const host =\n hostIsTemplate && hostDefault\n ? rawHost.replace(/\\{host\\}/, hostDefault)\n : rawHost;\n server = {\n key: serverKey!,\n host: rawHost,\n hostIsTemplate,\n hostDefault,\n pathname,\n protocol,\n description: str(rawServer.description),\n url:\n host !== \"\" && protocol !== undefined\n ? `${protocol}://${host}${pathname ?? \"\"}`\n : undefined,\n };\n const native = Array.isArray(rawServer.security) ? rawServer.security : [];\n nativeCount = native.length;\n const verbatim = extensionOf(rawServer, \"security\", prefixes);\n securityKey = verbatim?.key;\n requirements = (\n verbatim !== undefined && Array.isArray(verbatim.value)\n ? verbatim.value.map(readRequirement)\n : native.map((entry) => readNativeRequirement(entry, rawSchemes))\n ).filter(\n (requirement): requirement is WsRequirement => requirement !== null,\n );\n }\n const named = new Set(\n requirements.flatMap((requirement) =>\n requirement.map((part) => part.scheme),\n ),\n );\n const securitySchemes: WsSecurityScheme[] = Object.entries(rawSchemes)\n .filter(([name]) => named.has(name) || named.size === 0)\n .flatMap(([name, scheme]) =>\n isObject(scheme)\n ? [\n {\n name,\n type: str(scheme.type),\n detail: schemeDetail(scheme) || undefined,\n description: str(scheme.description),\n note: str(extensionOf(scheme, \"note\", prefixes)?.value),\n },\n ]\n : [],\n );\n\n // Channels: connection first.\n const rawChannels = isObject(doc.channels) ? doc.channels : {};\n const channels: WsChannel[] = Object.entries(rawChannels)\n .flatMap(([key, channel]): WsChannel[] => {\n if (!isObject(channel)) {\n return [];\n }\n const address = str(channel.address) ?? key;\n const declared = isObject(channel.parameters) ? channel.parameters : {};\n const inTemplate = [...address.matchAll(/\\{([^}]+)\\}/g)].map(\n (match) => match[1],\n );\n const names = [\n ...inTemplate,\n ...Object.keys(declared).filter((name) => !inTemplate.includes(name)),\n ];\n const bindings = isObject(channel.bindings) ? channel.bindings : {};\n const ws = isObject(bindings.ws) ? bindings.ws : {};\n return [\n {\n key,\n slug: itemSlug(\"channel\", key),\n title: str(channel.title) ?? key,\n address,\n description: str(channel.description),\n parameters: names.map((name) => {\n const parameter = isObject(declared[name])\n ? declared[name]\n : undefined;\n const schema = extensionOf(parameter, \"schema\", prefixes)?.value;\n return {\n name,\n description: str(parameter?.description),\n schema: isObject(schema) ? schema : undefined,\n };\n }),\n messages: messageIds(channel.messages),\n bindingMethod: str(ws.method),\n // The transport: keyed `connection`, or (under another key) the\n // channel carrying the WebSocket binding's upgrade method, which\n // only the socket itself has.\n isConnection: key === \"connection\" || str(ws.method) !== undefined,\n },\n ];\n })\n .sort((a, b) => Number(b.isConnection) - Number(a.isConnection));\n const connection = channels.find((channel) => channel.isConnection);\n const rawConnection = connection ? (rawChannels[connection.key] as Json) : {};\n\n // Operations.\n const rawOperations = isObject(doc.operations) ? doc.operations : {};\n const operations: WsOperation[] = Object.entries(rawOperations).flatMap(\n ([key, operation]): WsOperation[] => {\n if (!isObject(operation)) {\n return [];\n }\n const action = str(operation.action);\n const reply = isObject(operation.reply) ? operation.reply : null;\n return [\n {\n key,\n slug: itemSlug(\"operation\", key),\n title: str(operation.title) ?? key,\n summary: str(operation.summary),\n action:\n action === \"send\" || action === \"receive\" ? action : undefined,\n channel: refName(operation.channel),\n messages: messageIds(operation.messages),\n reply: reply\n ? {\n channel: refName(reply.channel),\n messages: messageIds(reply.messages),\n }\n : null,\n permission: str(extensionOf(operation, \"action\", prefixes)?.value),\n },\n ];\n },\n );\n\n // Messages: control first, then events, each in document order.\n const rawMessages = isObject(components.messages) ? components.messages : {};\n const messages: WsMessage[] = Object.entries(rawMessages)\n .flatMap(([key, message]): WsMessage[] => {\n if (!isObject(message)) {\n return [];\n }\n const examples = Array.isArray(message.examples) ? message.examples : [];\n return [\n {\n key,\n slug: itemSlug(\"message\", key),\n name: str(message.name) ?? key,\n title: str(message.title),\n summary: str(message.summary),\n contentType: str(message.contentType) ?? str(doc.defaultContentType),\n payload: message.payload,\n examples: examples.filter(isObject).map((example) => ({\n name: str(example.name),\n summary: str(example.summary),\n payload: example.payload,\n })),\n event: eventOf(key),\n },\n ];\n })\n .sort((a, b) => Number(a.event !== null) - Number(b.event !== null));\n\n // The connection's extensions, on the connection channel (or wherever they\n // are): each is read from the first prefix carrying it, looking at the\n // connection channel first.\n const channelExtension = (name: string): FoundExtension | undefined => {\n for (const prefix of prefixes) {\n const key = `${prefix}-${name}`;\n if (Object.hasOwn(rawConnection, key)) {\n return { key, value: rawConnection[key] };\n }\n const carrier = Object.values(rawChannels).find(\n (channel) => isObject(channel) && Object.hasOwn(channel, key),\n );\n if (isObject(carrier)) {\n return { key, value: carrier[key] };\n }\n }\n return undefined;\n };\n const closeCodesExt = channelExtension(\"close-codes\");\n const closeCodes = (\n Array.isArray(closeCodesExt?.value) ? closeCodesExt.value : []\n ).flatMap((entry): WsCloseCode[] =>\n isObject(entry) && num(entry.code) !== undefined\n ? [\n {\n code: num(entry.code)!,\n name: str(entry.name) ?? \"\",\n description: str(entry.description) ?? \"\",\n },\n ]\n : [],\n );\n const refusalsExt = channelExtension(\"upgrade-refusals\");\n const refusals = (\n Array.isArray(refusalsExt?.value) ? refusalsExt.value : []\n ).flatMap((entry): WsUpgradeRefusal[] =>\n isObject(entry) && num(entry.status) !== undefined\n ? [\n {\n status: num(entry.status)!,\n code: str(entry.code) ?? \"\",\n description: str(entry.description) ?? \"\",\n contentType: str(entry.contentType),\n headers: isObject(entry.headers)\n ? Object.fromEntries(\n Object.entries(entry.headers).flatMap(([name, value]) =>\n typeof value === \"string\" ? [[name, value]] : [],\n ),\n )\n : {},\n },\n ]\n : [],\n );\n const limitsExt = channelExtension(\"limits\");\n\n // A non-string extension value is as good as absent (as the original\n // reader's `str(...)` at each level did): fall through to the next source.\n const subprotocolOn = (\n node: Json | undefined,\n ): FoundExtension | undefined => {\n const found = extensionOf(node, \"subprotocol\", prefixes);\n return found !== undefined && str(found.value) !== undefined\n ? found\n : undefined;\n };\n const onServer = subprotocolOn(rawServer);\n const onConnection =\n onServer === undefined ? subprotocolOn(rawConnection) : undefined;\n const prose =\n onServer === undefined && onConnection === undefined\n ? readSubprotocol(connection?.description)\n : undefined;\n const stated = str(onServer?.value) ?? str(onConnection?.value) ?? prose;\n return {\n title: str(info.title) ?? \"WebSocket API\",\n version: str(info.version),\n description: str(info.description),\n server,\n subprotocol: {\n value: stated ?? options.defaultSubprotocol,\n fromDocument: stated !== undefined,\n source:\n onServer !== undefined\n ? \"server-extension\"\n : onConnection !== undefined\n ? \"connection-extension\"\n : prose !== undefined\n ? \"connection-prose\"\n : \"default\",\n },\n subprotocolKey: onServer?.key ?? onConnection?.key,\n extensionKeys: {\n security: securityKey,\n limits: limitsExt?.key,\n closeCodes: closeCodesExt?.key,\n upgradeRefusals: refusalsExt?.key,\n },\n securitySchemes,\n requirements,\n conjunctive: requirements.length > nativeCount,\n channels,\n operations,\n messages,\n limits: readLimits(limitsExt?.value),\n closeCodes,\n refusals,\n };\n}\n\n/** One entry of the sidebar. */\nexport interface WsNavItem {\n /** The item slug. */\n slug: string;\n /** What the entry reads. */\n label: string;\n /** A second line: an address, an action, a summary. */\n hint: string | undefined;\n /** Text the search matches besides the label. */\n keywords: string;\n}\n\n/** A sidebar group. */\nexport interface WsNavGroup {\n /** The group's heading. */\n title: string;\n /** Its entries. */\n items: WsNavItem[];\n}\n\n/** The sidebar: the connection's panels, channels, operations, then messages (control, then events). */\nexport function navGroups(doc: WsDoc): WsNavGroup[] {\n const candidates: (WsNavItem | false | null)[] = [\n doc.limits && {\n slug: itemSlug(\"limits\"),\n label: \"Limits\",\n hint: doc.extensionKeys.limits,\n keywords: doc.limits.values.map(([name]) => name).join(\" \"),\n },\n doc.closeCodes.length > 0 && {\n slug: itemSlug(\"close-codes\"),\n label: \"Close codes\",\n hint: doc.extensionKeys.closeCodes,\n keywords: doc.closeCodes\n .map((entry) => `${entry.code} ${entry.name}`)\n .join(\" \"),\n },\n doc.refusals.length > 0 && {\n slug: itemSlug(\"upgrade-refusals\"),\n label: \"Upgrade refusals\",\n hint: doc.extensionKeys.upgradeRefusals,\n keywords: doc.refusals\n .map((entry) => `${entry.status} ${entry.code}`)\n .join(\" \"),\n },\n ];\n const connection = candidates.filter((item): item is WsNavItem =>\n Boolean(item),\n );\n const control = doc.messages.filter((message) => message.event === null);\n const events = doc.messages.filter((message) => message.event !== null);\n const messageItem = (message: WsMessage): WsNavItem => ({\n slug: message.slug,\n label: message.name,\n hint: message.summary,\n keywords: `${message.title ?? \"\"} ${message.summary ?? \"\"}`,\n });\n return [\n { title: \"Connection\", items: connection },\n {\n title: \"Channels\",\n items: doc.channels.map((channel) => ({\n slug: channel.slug,\n label: channel.title,\n hint: channel.address,\n keywords: `${channel.key} ${channel.address} ${channel.parameters.map((parameter) => parameter.name).join(\" \")}`,\n })),\n },\n {\n title: \"Operations\",\n items: doc.operations.map((operation) => ({\n slug: operation.slug,\n label: operation.title,\n hint: [operation.action, operation.permission]\n .filter(Boolean)\n .join(\" · \"),\n keywords: `${operation.key} ${operation.summary ?? \"\"} ${operation.permission ?? \"\"}`,\n })),\n },\n { title: \"Control messages\", items: control.map(messageItem) },\n { title: \"Event messages\", items: events.map(messageItem) },\n ].filter((group) => group.items.length > 0);\n}\n\n/** The groups keeping only entries matching `query` (case-insensitive, every word). */\nexport function filterGroups(\n groups: readonly WsNavGroup[],\n query: string,\n): WsNavGroup[] {\n const words = query.toLowerCase().split(/\\s+/).filter(Boolean);\n if (words.length === 0) {\n return [...groups];\n }\n return groups\n .map((group) => ({\n ...group,\n items: group.items.filter((item) => {\n const text =\n `${item.slug} ${item.label} ${item.hint ?? \"\"} ${item.keywords}`.toLowerCase();\n return words.every((word) => text.includes(word));\n }),\n }))\n .filter((group) => group.items.length > 0);\n}\n\n/** What a slug selects. */\nexport type WsSelection =\n | {\n /** A channel. */\n kind: \"channel\";\n /** It. */\n channel: WsChannel;\n }\n | {\n /** An operation. */\n kind: \"operation\";\n /** It. */\n operation: WsOperation;\n }\n | {\n /** A message. */\n kind: \"message\";\n /** It. */\n message: WsMessage;\n }\n | {\n /** One of the connection's extension panels. */\n kind: \"limits\" | \"close-codes\" | \"upgrade-refusals\";\n };\n\n/** The item a slug names, or `null` when the document has none by it. */\nexport function selectItem(doc: WsDoc, slug: string): WsSelection | null {\n if (slug === \"limits\" && doc.limits) {\n return { kind: \"limits\" };\n }\n if (slug === \"close-codes\" && doc.closeCodes.length > 0) {\n return { kind: \"close-codes\" };\n }\n if (slug === \"upgrade-refusals\" && doc.refusals.length > 0) {\n return { kind: \"upgrade-refusals\" };\n }\n const channel = doc.channels.find((candidate) => candidate.slug === slug);\n if (channel) {\n return { kind: \"channel\", channel };\n }\n const operation = doc.operations.find((candidate) => candidate.slug === slug);\n if (operation) {\n return { kind: \"operation\", operation };\n }\n const message = doc.messages.find((candidate) => candidate.slug === slug);\n return message ? { kind: \"message\", message } : null;\n}\n\n/** The operations on a channel. */\nexport function operationsOn(doc: WsDoc, channel: string): WsOperation[] {\n return doc.operations.filter((operation) => operation.channel === channel);\n}\n\n/** The channels carrying a message. */\nexport function channelsCarrying(doc: WsDoc, message: string): WsChannel[] {\n return doc.channels.filter((channel) => channel.messages.includes(message));\n}\n\n/** The operations sending, receiving or replying with a message. */\nexport function operationsUsing(doc: WsDoc, message: string): WsOperation[] {\n return doc.operations.filter(\n (operation) =>\n operation.messages.includes(message) ||\n (operation.reply?.messages.includes(message) ?? false),\n );\n}\n\n/** The permissions a channel's operations need, de-duplicated, in order. */\nexport function channelPermissions(doc: WsDoc, channel: string): string[] {\n return [\n ...new Set(\n operationsOn(doc, channel).flatMap((operation) =>\n operation.permission === undefined ? [] : [operation.permission],\n ),\n ),\n ];\n}\n\n/** The event types a channel carries (`completed`, ...), de-duplicated, in order. */\nexport function channelEventTypes(doc: WsDoc, channel: WsChannel): string[] {\n const types = channel.messages.flatMap((key) => {\n const message = doc.messages.find((candidate) => candidate.key === key);\n return message?.event ? [message.event.type] : [];\n });\n return [...new Set(types)];\n}\n\n/**\n * How an embedding app applies its own contract to the try-it machinery:\n * the jobs UI passes its name rule and job-id escaping; a standalone viewer\n * passes nothing (the document's own schemas decide, and values fill\n * unescaped).\n */\nexport interface WsAddressOptions {\n /**\n * Fallback validation for a parameter the document gives no\n * `<prefix>-schema`: why the value is refused, or `null` when it passes.\n * Default: anything passes.\n */\n nameRule?: (name: string, value: string) => string | null;\n /**\n * Friendlier refusal wordings for known schema patterns, by the pattern\n * string (bun-jobs words its contract's pattern). Default: `Must match\n * <pattern>.`\n */\n patternText?: Readonly<Record<string, string>>;\n /**\n * Escapes a value for a filled address (bun-jobs escapes job ids with its\n * contract's `encodeJobId`). Default: identity.\n */\n escapeValue?: (name: string, value: string) => string;\n}\n\n/** Compiles a JSON Schema `pattern` (ECMA-262, unicode), or `undefined` when it does not compile. */\nfunction compilePattern(pattern: string): RegExp | undefined {\n try {\n return new RegExp(pattern, \"u\");\n } catch {\n return undefined;\n }\n}\n\n/**\n * Why a value is refused by a parameter's `<prefix>-schema` (its `pattern`,\n * `minLength` and `maxLength`; lengths in code points, as JSON Schema counts\n * them), or `null` when it passes. A pattern that does not compile is skipped\n * rather than refusing everything.\n */\nfunction schemaProblem(\n schema: Json,\n value: string,\n options?: WsAddressOptions,\n): string | null {\n const length = [...value].length;\n const max = num(schema.maxLength);\n if (max !== undefined && length > max) {\n return `At most ${max} characters (this is ${length}).`;\n }\n const min = num(schema.minLength);\n if (min !== undefined && length < min) {\n return `At least ${min} characters.`;\n }\n const pattern = str(schema.pattern);\n const compiled = pattern === undefined ? undefined : compilePattern(pattern);\n if (compiled && !compiled.test(value)) {\n return (\n (pattern === undefined ? undefined : options?.patternText?.[pattern]) ??\n `Must match ${pattern}.`\n );\n }\n return null;\n}\n\n/**\n * Why a parameter value is refused, or `null` when it is fine (or empty).\n * The document's `<prefix>-schema` decides when it gives one; otherwise the\n * options' `nameRule` decides, when the embedding app has one.\n */\nexport function parameterProblem(\n name: string,\n value: string,\n schema?: Json,\n options?: WsAddressOptions,\n): string | null {\n if (value === \"\") {\n return null;\n }\n if (schema !== undefined) {\n return schemaProblem(schema, value, options);\n }\n return options?.nameRule?.(name, value) ?? null;\n}\n\n/** The parameters' schemas by name, for {@link fillAddress}. */\nexport function parameterSchemas(\n parameters: readonly WsParameter[],\n): Record<string, Json | undefined> {\n return Object.fromEntries(\n parameters.map((parameter) => [parameter.name, parameter.schema]),\n );\n}\n\n/**\n * An address template filled from parameter values: `queue/{queue}/job/{jobId}`\n * with each value escaped by the options' `escapeValue`. `null` while a value\n * is missing or refused by {@link parameterProblem} (under `schemas`, the\n * document's, when it gives them).\n */\nexport function fillAddress(\n address: string,\n values: Readonly<Record<string, string>>,\n schemas: Readonly<Record<string, Json | undefined>> = {},\n options?: WsAddressOptions,\n): string | null {\n let complete = true;\n const filled = address.replace(/\\{([^}]+)\\}/g, (_match, name: string) => {\n const value = values[name] ?? \"\";\n if (\n value === \"\" ||\n parameterProblem(name, value, schemas[name], options) !== null\n ) {\n complete = false;\n return \"\";\n }\n return options?.escapeValue?.(name, value) ?? value;\n });\n return complete ? filled : null;\n}\n\n/**\n * Why a channel's try-it cannot be opened yet: each empty or refused\n * parameter, in template order. Empty when it can.\n */\nexport function tryProblems(\n channel: WsChannel,\n values: Readonly<Record<string, string>>,\n options?: WsAddressOptions,\n): string[] {\n return channel.parameters.flatMap((parameter) => {\n const value = values[parameter.name] ?? \"\";\n if (value === \"\") {\n return [`${parameter.name}: fill it in.`];\n }\n const problem = parameterProblem(\n parameter.name,\n value,\n parameter.schema,\n options,\n );\n return problem === null ? [] : [`${parameter.name}: ${problem}`];\n });\n}\n\n/**\n * Whether a channel is something to subscribe to, and so has a try-it at\n * all: a logical channel name. The connection is the transport the others\n * travel over, and an address that is a socket path (`/...`) is not a\n * channel name either; neither gets a try-it, not even a disabled one.\n */\nexport function channelSubscribable(channel: WsChannel): boolean {\n return !channel.isConnection && !channel.address.startsWith(\"/\");\n}\n\n/**\n * An event message's highlighted part: the schema of `event.payload` inside\n * its envelope (resolving `$ref`s on the way), or `undefined`.\n */\nexport function eventPayloadSchema(\n payload: unknown,\n root: SpecDocument,\n): unknown {\n const deref = (value: unknown): unknown => {\n let current = value;\n for (let i = 0; i < 16 && isObject(current) && str(current.$ref); i++) {\n current = resolveRef(root, str(current.$ref)!);\n }\n return current;\n };\n const envelope = deref(payload);\n const event =\n isObject(envelope) && isObject(envelope.properties)\n ? deref(envelope.properties.event)\n : undefined;\n return isObject(event) && isObject(event.properties)\n ? event.properties.payload\n : undefined;\n}\n\n/**\n * An event example's `event.payload` (what the event says), or `undefined`\n * when the frame has none: the part of an event frame the pane shows first.\n */\nexport function exampleEventPayload(frame: unknown): unknown {\n const event = isObject(frame) ? frame.event : undefined;\n return isObject(event) ? event.payload : undefined;\n}\n",
|
|
41
|
+
"/** One shared integer formatter in the browser's locale. */\nconst integerFormat = new Intl.NumberFormat(undefined, {\n maximumFractionDigits: 0,\n});\n\n/** Formats a count, e.g. `12,345`. */\nexport function formatNumber(value: number): string {\n return integerFormat.format(value);\n}\n",
|
|
42
|
+
"import { formatNumber } from \"../../../format\";\n\n/**\n * How the limits panel reads each `<prefix>-limits` entry. A limit's unit is\n * read from its name's suffix (`…Bytes`, `…Ms`, `…PerSecond`); a viewer\n * embedding this reference can pass a {@link LimitInfo} per name for a label\n * and a meaning instead (the jobs UI does, for its own vocabulary).\n */\n\n/** A limit's unit. */\nexport type LimitUnit = \"bytes\" | \"ms\" | \"count\" | \"per-second\";\n\n/** How one limit reads. */\nexport interface LimitInfo {\n /** Its label. */\n label: string;\n /** What it means. */\n meaning: string;\n /** Its unit. */\n unit: LimitUnit;\n}\n\n/** A limit's unit from its name: `maxMessageBytes` → bytes, `heartbeatMs` → ms, `messagesPerSecond` → per-second, anything else a count. */\nexport function limitUnit(name: string): LimitUnit {\n if (/bytes$/i.test(name)) {\n return \"bytes\";\n }\n if (/ms$/i.test(name)) {\n return \"ms\";\n }\n if (/persecond$/i.test(name)) {\n return \"per-second\";\n }\n return \"count\";\n}\n\n/** `1,048,576 bytes (1 MiB)`. */\nexport function formatBytes(value: number): string {\n const units = [\"KiB\", \"MiB\", \"GiB\"];\n let scaled = value;\n let unit = \"\";\n for (const next of units) {\n if (scaled < 1024 || scaled % 1024 !== 0) {\n break;\n }\n scaled /= 1024;\n unit = next;\n }\n return unit === \"\"\n ? `${formatNumber(value)} bytes`\n : `${formatNumber(value)} bytes (${formatNumber(scaled)} ${unit})`;\n}\n\n/** `25,000 ms (25 s)`, `300,000 ms (5 min)`. */\nexport function formatMs(value: number): string {\n const plain = `${formatNumber(value)} ms`;\n if (value >= 60_000 && value % 60_000 === 0) {\n return `${plain} (${formatNumber(value / 60_000)} min)`;\n }\n if (value >= 1000 && value % 1000 === 0) {\n return `${plain} (${formatNumber(value / 1000)} s)`;\n }\n return plain;\n}\n\n/** A limit's value in its unit: `info`'s when given, else the name's suffix. */\nexport function formatLimit(\n name: string,\n value: number,\n info?: LimitInfo,\n): string {\n switch (info?.unit ?? limitUnit(name)) {\n case \"bytes\":\n return formatBytes(value);\n case \"ms\":\n return formatMs(value);\n case \"per-second\":\n return `${formatNumber(value)} / s`;\n default:\n return formatNumber(value);\n }\n}\n",
|
|
43
|
+
"/** Props of {@link InlineText}. */\nexport interface InlineTextProps {\n /** One line of spec text, whose backtick spans are code. */\n text: string;\n}\n\n/**\n * One line of spec text with its `` `code` `` spans as `<code>`: the inline\n * half of the shared `Prose`, which does not export it. No HTML is injected.\n */\nexport function InlineText({ text }: InlineTextProps) {\n const parts = text.split(/(`[^`]+`)/);\n return (\n <>\n {parts.map((part, index) =>\n part.length > 2 && part.startsWith(\"`\") && part.endsWith(\"`\") ? (\n // eslint-disable-next-line react/no-array-index-key -- the split is fixed for a given text\n <code key={index}>{part.slice(1, -1)}</code>\n ) : (\n part\n ),\n )}\n </>\n );\n}\n",
|
|
44
|
+
"import type { SpecDocument } from \"../../../api/docs\";\nimport { SchemaTree } from \"../schema\";\n\n/** Props of {@link PayloadSchema}. */\nexport interface PayloadSchemaProps {\n /** The schema, as the document writes it (often a `$ref`). */\n schema: unknown;\n /** The whole document, for resolving `$ref`s. */\n root: SpecDocument;\n /** The tree's accessible name. */\n label: string;\n /** Levels open at first. Defaults to 2. */\n depth?: number;\n}\n\n/**\n * A message's payload schema, `$ref`s resolved, drawn by the docs' shared\n * schema tree. The one place the WebSocket reference renders a schema.\n */\nexport function PayloadSchema({\n schema,\n root,\n label,\n depth = 2,\n}: PayloadSchemaProps) {\n return (\n <div\n className=\"ws-schema\"\n data-testid=\"ws-schema\"\n >\n <SchemaTree\n schema={schema}\n root={root}\n depth={depth}\n label={label}\n />\n </div>\n );\n}\n",
|
|
45
|
+
"import type { ReactNode } from \"react\";\nimport { cx } from \"./classNames\";\n\n/** One row of a {@link KeyValue}. */\nexport interface KeyValueItem {\n /** The term, e.g. \"Attempts\". */\n label: ReactNode;\n /** The value: text or any node. `null`, `undefined` and `\"\"` render the `empty` placeholder. */\n value: ReactNode;\n /** A stable React key; defaults to the label when it is a string, else the index. */\n key?: string;\n /** A short note under the value, e.g. \"of 5 allowed\". */\n hint?: ReactNode;\n}\n\n/** Props of {@link KeyValue}. */\nexport interface KeyValueProps {\n /** The rows, in order. Falsy entries are skipped, so rows can be conditional inline. */\n items: readonly (KeyValueItem | false | null | undefined)[];\n /** `\"grid\"` puts label and value side by side; `\"stacked\"` puts the label above. Defaults to `\"grid\"`. */\n layout?: \"grid\" | \"stacked\";\n /** What a missing value shows. Defaults to `\"—\"`. */\n empty?: ReactNode;\n /** Extra class names on the `<dl>`. */\n className?: string;\n}\n\n/** A summary as a definition list: label/value rows (a job's timings, a queue's settings). */\nexport function KeyValue({\n items,\n layout = \"grid\",\n empty = \"—\",\n className,\n}: KeyValueProps) {\n return (\n <dl className={cx(\"kv\", `kv-${layout}`, className)}>\n {items.map((item, index) => {\n if (!item) {\n return null;\n }\n const missing =\n item.value === null || item.value === undefined || item.value === \"\";\n // The index is only the fallback for a row with neither a key nor a\n // string label.\n const rowKey =\n item.key ??\n (typeof item.label === \"string\" ? item.label : `row-${index}`);\n return (\n <div\n key={rowKey}\n className=\"kv-row\"\n >\n <dt className=\"kv-label\">{item.label}</dt>\n <dd className={cx(\"kv-value\", missing && \"muted\")}>\n {missing ? empty : item.value}\n {item.hint && <span className=\"kv-hint\">{item.hint}</span>}\n </dd>\n </div>\n );\n })}\n </dl>\n );\n}\n",
|
|
46
|
+
"import type { ReactNode } from \"react\";\nimport { cx } from \"./classNames\";\n\n/** Props of {@link Table}. */\nexport interface TableProps {\n /** Accessible name of the table (rendered as a visually hidden caption). */\n label: string;\n /** Extra class names on the `<table>`. */\n className?: string;\n /** `<thead>` / `<tbody>`. */\n children: ReactNode;\n}\n\n/**\n * A table in a scrollable, focusable region with a sticky header. Keyboard\n * users can scroll a wide table because the region is tabbable.\n */\nexport function Table({ label, className, children }: TableProps) {\n return (\n <div\n className=\"table-wrap\"\n role=\"region\"\n aria-label={label}\n // A scrollable region must be reachable by keyboard.\n tabIndex={0}\n >\n <table className={cx(\"table\", className)}>\n <caption className=\"visually-hidden\">{label}</caption>\n {children}\n </table>\n </div>\n );\n}\n",
|
|
47
|
+
"/**\n * The WebSocket reference's panels read from the document's extensions and\n * header: the server and its security, `<prefix>-limits`,\n * `<prefix>-close-codes` and `<prefix>-upgrade-refusals`.\n */\nimport type { LimitInfo } from \"./format\";\nimport type { WsDoc, WsLimits } from \"./model\";\nimport { Badge } from \"../../../components/Badge\";\nimport { Card } from \"../../../components/Card\";\nimport { KeyValue } from \"../../../components/KeyValue\";\nimport { Table } from \"../../../components/Table\";\nimport { formatNumber } from \"../../../format\";\nimport { Prose } from \"../schema\";\nimport { formatLimit, formatMs } from \"./format\";\nimport { InlineText } from \"./InlineText\";\nimport { subprotocolSource } from \"./model\";\n\n/** The server: its URL, host, path, protocol and subprotocol. */\nexport function ServerPanel({ doc }: { doc: WsDoc }) {\n const { server, subprotocol } = doc;\n return (\n <Card title=\"Server\">\n {server === null ? (\n <p className=\"muted\">The document names no server.</p>\n ) : (\n <KeyValue\n items={[\n {\n label: \"URL\",\n value: server.url && (\n <code data-testid=\"ws-server-url\">{server.url}</code>\n ),\n },\n {\n label: \"Host\",\n value: <code>{server.host}</code>,\n hint: server.hostIsTemplate\n ? `A template: the document served over HTTP fills in the request's own host${server.hostDefault ? ` (default ${server.hostDefault})` : \"\"}.`\n : \"Taken from the request that fetched this document: the host you reached the API on.\",\n },\n {\n label: \"Path\",\n value: server.pathname && <code>{server.pathname}</code>,\n },\n {\n label: \"Protocol\",\n value: server.protocol && <code>{server.protocol}</code>,\n hint: \"wss when the API was reached over HTTPS.\",\n },\n {\n label: \"Subprotocol\",\n value:\n subprotocol.value === undefined ? (\n <span\n className=\"muted\"\n data-testid=\"ws-subprotocol\"\n >\n Not stated\n </span>\n ) : (\n <code data-testid=\"ws-subprotocol\">{subprotocol.value}</code>\n ),\n hint: (\n <>\n <span data-testid=\"ws-subprotocol-source\">\n {subprotocolSource(doc)}\n </span>\n {subprotocol.fromDocument\n ? \". Optional: offer it in Sec-WebSocket-Protocol, or offer none. Offering others without it may be refused.\"\n : \".\"}\n </>\n ),\n },\n server.description !== undefined && {\n label: \"About\",\n value: server.description,\n },\n ]}\n />\n )}\n </Card>\n );\n}\n\n/** Security: every requirement (any one suffices), and each scheme with what a browser cannot send. */\nexport function SecurityPanel({ doc }: { doc: WsDoc }) {\n const { requirements, securitySchemes, conjunctive } = doc;\n return (\n <Card title=\"Security\">\n <div data-testid=\"ws-security\">\n {requirements.length === 0 ? (\n <p className=\"muted\">\n No security schemes are declared. The host application decides, on\n the upgrade and on each subscription; a browser sends its cookies\n with the upgrade.\n </p>\n ) : (\n <>\n <p>\n {requirements.length === 1\n ? \"The connection must satisfy:\"\n : \"The connection must satisfy any one of:\"}\n </p>\n <ul\n className=\"ws-requirements\"\n aria-label=\"Security requirements\"\n >\n {requirements.map((requirement) => {\n const key = requirement\n .map((part) => `${part.scheme}:${part.scopes.join(\",\")}`)\n .join(\"+\");\n return (\n <li key={key}>\n {requirement.map((part, index) => (\n <span key={part.scheme}>\n {index > 0 && <strong> and </strong>}\n <code>{part.scheme}</code>\n {part.scopes.length > 0 && (\n <span className=\"muted\">\n {\" \"}\n (scopes: {part.scopes.join(\", \")})\n </span>\n )}\n </span>\n ))}\n </li>\n );\n })}\n </ul>\n {conjunctive && (\n <p\n className=\"ws-note\"\n data-testid=\"ws-security-conjunctive\"\n >\n AsyncAPI 3 can list only one scheme per requirement, so a\n requirement naming several is only in{\" \"}\n <code>\n {doc.extensionKeys.security ?? \"the security extension\"}\n </code>\n , which is what this list shows: every scheme within one\n requirement is needed together.\n </p>\n )}\n <Table label=\"Security schemes\">\n <thead>\n <tr>\n <th scope=\"col\">Scheme</th>\n <th scope=\"col\">Type</th>\n <th scope=\"col\">In a browser</th>\n </tr>\n </thead>\n <tbody>\n {securitySchemes.map((scheme) => (\n <tr\n key={scheme.name}\n data-testid={`ws-scheme-${scheme.name}`}\n >\n <td>\n <code>{scheme.name}</code>\n {scheme.description && (\n <div className=\"muted\">{scheme.description}</div>\n )}\n </td>\n <td>\n {scheme.type}\n {scheme.detail && (\n <div className=\"muted\">{scheme.detail}</div>\n )}\n </td>\n <td>\n {scheme.note ? (\n <span className=\"ws-note\">\n <InlineText text={scheme.note} />\n </span>\n ) : (\n <span className=\"muted\">Can be sent.</span>\n )}\n </td>\n </tr>\n ))}\n </tbody>\n </Table>\n </>\n )}\n </div>\n </Card>\n );\n}\n\n/** The replay setting in words. */\nfunction replayText(replay: WsLimits[\"replay\"]): string {\n if (replay === false) {\n return \"Off: a resume after a reconnect always reports a gap.\";\n }\n if (replay === undefined) {\n return \"Not stated.\";\n }\n const parts = [\n replay.size !== undefined && `the last ${formatNumber(replay.size)} events`,\n replay.maxAgeMs !== undefined && `at most ${formatMs(replay.maxAgeMs)} old`,\n ].filter(Boolean);\n return parts.length > 0 ? `Keeps ${parts.join(\", \")}.` : \"On.\";\n}\n\n/** `<prefix>-limits`: what a connection is held to. */\nexport function LimitsPanel({\n limits,\n extensionKey,\n limitsInfo,\n}: {\n /** The limits, read. */\n limits: WsLimits;\n /** The extension's full key (e.g. `x-bun-jobs-limits`), when one was read. */\n extensionKey?: string;\n /** Labels, meanings and units per limit name; the name's suffix decides the unit without one. */\n limitsInfo?: Readonly<Record<string, LimitInfo>>;\n}) {\n return (\n <Card title=\"Limits\">\n <p className=\"muted\">\n What one connection is held to\n {extensionKey === undefined ? (\n \"\"\n ) : (\n <>\n {\" \"}\n (<code>{extensionKey}</code>)\n </>\n )}\n .\n </p>\n <Table\n label=\"Limits\"\n className=\"ws-table\"\n >\n <thead>\n <tr>\n <th scope=\"col\">Limit</th>\n <th scope=\"col\">Value</th>\n <th scope=\"col\">Meaning</th>\n </tr>\n </thead>\n <tbody>\n {limits.values.map(([name, value]) => (\n <tr\n key={name}\n data-testid={`ws-limit-${name}`}\n >\n <td>\n {limitsInfo?.[name]?.label ?? name}\n <div className=\"muted\">\n <code>{name}</code>\n </div>\n </td>\n <td className=\"ws-value\">\n {formatLimit(name, value, limitsInfo?.[name])}\n </td>\n <td>{limitsInfo?.[name]?.meaning ?? \"\"}</td>\n </tr>\n ))}\n <tr data-testid=\"ws-limit-replay\">\n <td>\n Replay\n <div className=\"muted\">\n <code>replay</code>\n </div>\n </td>\n <td className=\"ws-value\">\n {limits.replay === false\n ? \"off\"\n : limits.replay === undefined\n ? \"—\"\n : [\n limits.replay.size !== undefined &&\n `size ${formatNumber(limits.replay.size)}`,\n limits.replay.maxAgeMs !== undefined &&\n `maxAgeMs ${formatMs(limits.replay.maxAgeMs)}`,\n ]\n .filter(Boolean)\n .join(\", \")}\n </td>\n <td>The events kept for a resume. {replayText(limits.replay)}</td>\n </tr>\n </tbody>\n </Table>\n </Card>\n );\n}\n\n/** `<prefix>-close-codes`: why an open connection is closed. */\nexport function CloseCodesPanel({ doc }: { doc: WsDoc }) {\n return (\n <Card title=\"Close codes\">\n <p className=\"muted\">\n How an open connection is closed\n {doc.extensionKeys.closeCodes === undefined ? (\n \"\"\n ) : (\n <>\n {\" \"}\n (<code>{doc.extensionKeys.closeCodes}</code>)\n </>\n )}\n .\n </p>\n <Table\n label=\"Close codes\"\n className=\"ws-table\"\n >\n <thead>\n <tr>\n <th scope=\"col\">Code</th>\n <th scope=\"col\">Name</th>\n <th scope=\"col\">When</th>\n </tr>\n </thead>\n <tbody>\n {doc.closeCodes.map((entry) => (\n <tr\n key={`${entry.code}-${entry.name}`}\n data-testid={`ws-close-${entry.code}`}\n >\n <td className=\"ws-value\">{entry.code}</td>\n <td>\n <code>{entry.name}</code>\n </td>\n <td>\n <InlineText text={entry.description} />\n </td>\n </tr>\n ))}\n </tbody>\n </Table>\n </Card>\n );\n}\n\n/** `<prefix>-upgrade-refusals`: how an upgrade is refused before a socket exists. */\nexport function RefusalsPanel({ doc }: { doc: WsDoc }) {\n return (\n <Card title=\"Upgrade refusals\">\n <p className=\"muted\">\n An upgrade can be refused before any socket exists, with an ordinary\n HTTP response whose body is a problem carrying <code>code</code>\n {doc.extensionKeys.upgradeRefusals === undefined ? (\n \"\"\n ) : (\n <>\n {\" \"}\n (<code>{doc.extensionKeys.upgradeRefusals}</code>)\n </>\n )}\n .\n </p>\n <p\n className=\"ws-note\"\n data-testid=\"ws-refusal-1006\"\n >\n A browser never sees these: its <code>WebSocket</code> exposes no status\n or body for a refused upgrade, only a close with code{\" \"}\n <strong>1006</strong> (abnormal closure). To tell them apart, make the\n same request over HTTP, or read the server's logs.\n </p>\n <Table\n label=\"Upgrade refusals\"\n className=\"ws-table\"\n >\n <thead>\n <tr>\n <th scope=\"col\">Status</th>\n <th scope=\"col\">Code</th>\n <th scope=\"col\">When</th>\n <th scope=\"col\">Content type</th>\n <th scope=\"col\">Headers</th>\n </tr>\n </thead>\n <tbody>\n {doc.refusals.map((entry) => (\n <tr\n key={`${entry.status}-${entry.code}`}\n data-testid={`ws-refusal-${entry.code}`}\n >\n <td className=\"ws-value\">\n <Badge tone={entry.status >= 500 ? \"danger\" : \"warning\"}>\n {entry.status}\n </Badge>\n </td>\n <td>\n <code>{entry.code}</code>\n </td>\n <td>\n <InlineText text={entry.description} />\n </td>\n <td>{entry.contentType && <code>{entry.contentType}</code>}</td>\n <td>\n {Object.keys(entry.headers).length === 0 ? (\n <span className=\"muted\">—</span>\n ) : (\n Object.entries(entry.headers).map(([name, value]) => (\n <div key={name}>\n <code>\n {name}: {value}\n </code>\n </div>\n ))\n )}\n </td>\n </tr>\n ))}\n </tbody>\n </Table>\n </Card>\n );\n}\n\n/** The document's header: title, version and description. */\nexport function DocHeader({ doc }: { doc: WsDoc }) {\n return (\n <header className=\"ws-header\">\n <h1 className=\"screen-title\">\n {doc.title}{\" \"}\n {doc.version && (\n <Badge\n tone=\"neutral\"\n title=\"info.version\"\n >\n v{doc.version}\n </Badge>\n )}\n </h1>\n <p className=\"muted ws-kicker\">WebSocket API (AsyncAPI 3.0)</p>\n {doc.description && (\n <details className=\"ws-description\">\n <summary>About this socket</summary>\n <Prose text={doc.description} />\n </details>\n )}\n </header>\n );\n}\n",
|
|
48
|
+
"/**\n * The WebSocket reference's main pane for one item: a channel (with its\n * try-it when a console is given), an operation, or a message (with its\n * payload and, for an event, its try-it).\n *\n * The try-it deep-links a console the host app injects as {@link WsConsole}\n * (the jobs UI passes its Events console); a standalone viewer passes none\n * and no try-it renders.\n */\nimport type { ReactNode } from \"react\";\nimport type { SpecDocument } from \"../../../api/docs\";\nimport type { PermissionProbe } from \"../permissions\";\nimport type { LimitInfo } from \"./format\";\nimport type {\n WsAddressOptions,\n WsChannel,\n WsDoc,\n WsMessage,\n WsOperation,\n} from \"./model\";\nimport { useState } from \"react\";\nimport { Badge } from \"../../../components/Badge\";\nimport { Card } from \"../../../components/Card\";\nimport { CopyButton } from \"../../../components/CopyButton\";\nimport { Field } from \"../../../components/Field\";\nimport { TextInput } from \"../../../components/inputs\";\nimport { JsonView } from \"../../../components/JsonView\";\nimport { KeyValue } from \"../../../components/KeyValue\";\nimport { Table } from \"../../../components/Table\";\nimport { Link } from \"../../../router\";\nimport { Prose, SchemaTree } from \"../schema\";\nimport { InlineText } from \"./InlineText\";\nimport {\n channelEventTypes,\n channelPermissions,\n channelsCarrying,\n channelSubscribable,\n eventPayloadSchema,\n exampleEventPayload,\n fillAddress,\n itemPath,\n operationsOn,\n operationsUsing,\n parameterProblem,\n parameterSchemas,\n tryProblems,\n} from \"./model\";\nimport { CloseCodesPanel, LimitsPanel, RefusalsPanel } from \"./panels\";\nimport { PayloadSchema } from \"./PayloadSchema\";\n\n/**\n * A console the try-it cards deep-link (the jobs UI's Events console). The\n * link builders answer `null` when the console cannot open what was asked.\n */\nexport interface WsConsole {\n /** Whether the console can be opened at all. */\n available: boolean;\n /** Its name in the UI copy (\"Events console\"). */\n label: string;\n /** Why it cannot be opened, shown when `available` is false. Defaults to a generic sentence. */\n unavailableReason?: ReactNode;\n /** Why a filled channel still cannot open, when the link builder said no. Defaults to a generic sentence. */\n noChannelReason?: (channel: string) => ReactNode;\n /** The link subscribing the console to `channel`, filtered to `types` (`[]` would be all of them). */\n channelLink: (channel: string, types: readonly string[]) => string | null;\n /** The link opening the console on an event message's channel, filtered to its type. */\n messageLink: (message: WsMessage, doc: WsDoc) => string | null;\n}\n\n/** Options of the panes, threaded from {@link WsReference}. */\nexport interface WsPaneOptions {\n /** The console the try-it cards open, when the host has one. */\n console?: WsConsole;\n /** Answers \"does the caller hold this permission\" (the have/lack markers). */\n probe?: PermissionProbe;\n /** The host's contract for the try-it's address fields. */\n address?: WsAddressOptions;\n /** The docs section's path within the host app (\"\" for a standalone viewer). */\n pathBase?: string;\n}\n\n/** A permission with a have/lack marker when a probe is given, plain otherwise. */\nexport function PermissionMarker({\n action,\n probe,\n}: {\n action: string;\n probe?: PermissionProbe;\n}) {\n if (probe === undefined) {\n return (\n <span\n className=\"ws-permission\"\n data-testid={`ws-permission-${action}`}\n >\n <code>{action}</code> <Badge tone=\"neutral\">permission</Badge>\n </span>\n );\n }\n const held = probe(action);\n return (\n <span\n className=\"ws-permission\"\n data-testid={`ws-permission-${action}`}\n data-has={held === undefined ? \"unknown\" : String(held)}\n >\n <code>{action}</code>{\" \"}\n {held === undefined ? (\n <Badge tone=\"neutral\">Not an action this UI knows</Badge>\n ) : (\n <Badge tone={held ? \"success\" : \"danger\"}>\n {held ? \"You have this\" : \"You lack this\"}\n </Badge>\n )}\n </span>\n );\n}\n\n/** Links to items by slug. */\nfunction ItemLinks({\n items,\n pathBase = \"\",\n empty = \"None.\",\n}: {\n items: readonly {\n /** The item's slug. */\n slug: string;\n /** Its label. */\n label: string;\n }[];\n /** The docs section's path within the host app. */\n pathBase?: string;\n empty?: string;\n}) {\n if (items.length === 0) {\n return <span className=\"muted\">{empty}</span>;\n }\n return (\n <ul className=\"ws-links\">\n {items.map((item) => (\n <li key={item.slug}>\n <Link to={itemPath(item.slug, \"\", pathBase)}>\n <code>{item.label}</code>\n </Link>\n </li>\n ))}\n </ul>\n );\n}\n\n/** Why \"try it\" is not offered, when the console is not available. */\nfunction ConsoleUnavailable({ console: target }: { console: WsConsole }) {\n return (\n <p\n className=\"muted\"\n data-testid=\"ws-try-unavailable\"\n >\n {target.unavailableReason ?? (\n <>The {target.label} is not available here.</>\n )}\n </p>\n );\n}\n\n/** Props of {@link ChannelTryIt}. */\ninterface ChannelTryItProps {\n /** The document. */\n doc: WsDoc;\n /** The channel family. */\n channel: WsChannel;\n /** The console to open. */\n console: WsConsole;\n /** The host's contract for the address fields. */\n address?: WsAddressOptions;\n}\n\n/** Try a channel: fill its address, then open the console subscribed to it. */\nfunction ChannelTryIt({\n doc,\n channel,\n console: target,\n address,\n}: ChannelTryItProps) {\n const [values, setValues] = useState<Record<string, string>>({});\n if (!target.available) {\n return (\n <Card title=\"Try it\">\n <ConsoleUnavailable console={target} />\n </Card>\n );\n }\n const filled = fillAddress(\n channel.address,\n values,\n parameterSchemas(channel.parameters),\n address,\n );\n const types = channelEventTypes(doc, channel);\n const link = filled === null ? null : target.channelLink(filled, types);\n const problems = tryProblems(channel, values, address);\n return (\n <Card title=\"Try it\">\n <div\n className=\"ws-try\"\n data-testid=\"ws-try-channel\"\n >\n <p className=\"muted\">\n Opens the {target.label} subscribed to this channel\n {types.length > 0\n ? `, showing the ${types.length} event types it carries`\n : \"\"}\n .\n </p>\n {channel.parameters.length > 0 && (\n <div className=\"ws-try-fields\">\n {channel.parameters.map((parameter) => {\n const value = values[parameter.name] ?? \"\";\n const problem = parameterProblem(\n parameter.name,\n value,\n parameter.schema,\n address,\n );\n return (\n <Field\n key={parameter.name}\n label={parameter.name}\n hint={\n parameter.description && (\n <InlineText text={parameter.description} />\n )\n }\n error={problem}\n >\n <TextInput\n value={value}\n onChange={(next) =>\n setValues((current) => ({\n ...current,\n [parameter.name]: next,\n }))\n }\n autoComplete=\"off\"\n spellCheck={false}\n />\n </Field>\n );\n })}\n </div>\n )}\n <p className=\"ws-try-channel\">\n Channel:{\" \"}\n {filled === null ? (\n <span className=\"muted\">fill in every parameter</span>\n ) : (\n <code data-testid=\"ws-try-address\">{filled}</code>\n )}\n </p>\n {link === null ? (\n <>\n <span\n className=\"btn btn-primary ws-disabled\"\n aria-disabled=\"true\"\n aria-describedby={`ws-try-reason-${channel.key}`}\n data-testid=\"ws-try-disabled\"\n >\n Open in the {target.label}\n </span>\n <ul\n className=\"ws-try-reason muted\"\n id={`ws-try-reason-${channel.key}`}\n data-testid=\"ws-try-reason\"\n >\n {problems.length > 0 ? (\n problems.map((problem) => <li key={problem}>{problem}</li>)\n ) : (\n <li>\n {target.noChannelReason?.(filled ?? channel.address) ?? (\n <>\n The {target.label} cannot open{\" \"}\n <code>{filled ?? channel.address}</code>.\n </>\n )}\n </li>\n )}\n </ul>\n </>\n ) : (\n <Link\n to={link}\n className=\"btn btn-primary\"\n data-testid=\"ws-try-link\"\n >\n Open in the {target.label}\n </Link>\n )}\n </div>\n </Card>\n );\n}\n\n/** Props of {@link ChannelPane}. */\ninterface ChannelPaneProps extends WsPaneOptions {\n /** The document, read. */\n doc: WsDoc;\n /** The raw document, for `$ref`s in parameter schemas. */\n root: SpecDocument;\n /** The channel. */\n channel: WsChannel;\n /** Labels and meanings for the limits, when the embedding viewer knows them. */\n limitsInfo?: Readonly<Record<string, LimitInfo>>;\n}\n\n/** A channel: address, parameters, operations, messages, permission and try-it. */\nexport function ChannelPane({\n doc,\n root,\n channel,\n limitsInfo,\n console: target,\n probe,\n address,\n pathBase,\n}: ChannelPaneProps) {\n const operations = operationsOn(doc, channel.key);\n const permissions = channelPermissions(doc, channel.key);\n const messages = channel.messages.map((key) => {\n const message = doc.messages.find((candidate) => candidate.key === key);\n return { slug: message?.slug ?? `message-${key}`, label: key };\n });\n return (\n <div\n className=\"ws-pane\"\n data-testid={`ws-pane-${channel.slug}`}\n >\n <Card\n title={\n <>\n {channel.title} <Badge tone=\"info\">channel</Badge>\n </>\n }\n >\n <KeyValue\n items={[\n {\n label: channel.isConnection ? \"Path\" : \"Address\",\n value: (\n <code data-testid=\"ws-channel-address\">{channel.address}</code>\n ),\n hint: channel.isConnection\n ? \"The socket itself: control messages travel here.\"\n : \"A logical channel: subscribe with this name over the connection.\",\n },\n channel.bindingMethod !== undefined && {\n label: \"Upgrade\",\n value: <code>{channel.bindingMethod}</code>,\n },\n permissions.length > 0 && {\n label: permissions.length === 1 ? \"Permission\" : \"Permissions\",\n value: (\n <span className=\"ws-permissions\">\n {permissions.map((action) => (\n <PermissionMarker\n key={action}\n action={action}\n probe={probe}\n />\n ))}\n </span>\n ),\n },\n ]}\n />\n {channel.description && <Prose text={channel.description} />}\n </Card>\n {channel.parameters.length > 0 && (\n <Card title=\"Parameters\">\n <Table label=\"Parameters\">\n <thead>\n <tr>\n <th scope=\"col\">Name</th>\n <th scope=\"col\">Description</th>\n <th scope=\"col\">Value</th>\n </tr>\n </thead>\n <tbody>\n {channel.parameters.map((parameter) => (\n <tr key={parameter.name}>\n <td>\n <code>{`{${parameter.name}}`}</code>\n </td>\n <td>\n {parameter.description && (\n <InlineText text={parameter.description} />\n )}\n </td>\n <td data-testid={`ws-parameter-schema-${parameter.name}`}>\n {parameter.schema ? (\n <SchemaTree\n schema={parameter.schema}\n root={root}\n label={`Schema of {${parameter.name}}`}\n />\n ) : (\n <span className=\"muted\">Any string</span>\n )}\n </td>\n </tr>\n ))}\n </tbody>\n </Table>\n </Card>\n )}\n <Card title=\"Operations\">\n <ItemLinks\n items={operations.map((operation) => ({\n slug: operation.slug,\n label: `${operation.action ?? \"?\"} ${operation.key}`,\n }))}\n pathBase={pathBase}\n />\n </Card>\n <Card title={`Messages (${messages.length})`}>\n <ItemLinks\n items={messages}\n pathBase={pathBase}\n />\n </Card>\n {/* Only a channel there is something to subscribe to, and only with a console: the connection has no try-it. */}\n {target !== undefined && channelSubscribable(channel) && (\n <ChannelTryIt\n key={channel.key}\n doc={doc}\n channel={channel}\n console={target}\n address={address}\n />\n )}\n {channel.isConnection && doc.limits && (\n <LimitsPanel\n limits={doc.limits}\n extensionKey={doc.extensionKeys.limits}\n limitsInfo={limitsInfo}\n />\n )}\n {channel.isConnection && doc.closeCodes.length > 0 && (\n <CloseCodesPanel doc={doc} />\n )}\n {channel.isConnection && doc.refusals.length > 0 && (\n <RefusalsPanel doc={doc} />\n )}\n </div>\n );\n}\n\n/** Message links for ids, labelled by id. */\nfunction messageLinks(doc: WsDoc, ids: readonly string[]) {\n return ids.map((key) => ({\n slug:\n doc.messages.find((message) => message.key === key)?.slug ??\n `message-${key}`,\n label: key,\n }));\n}\n\n/** An operation: direction, channel, messages, reply and permission. */\nexport function OperationPane({\n doc,\n operation,\n probe,\n pathBase,\n}: WsPaneOptions & {\n doc: WsDoc;\n operation: WsOperation;\n}) {\n const channel = doc.channels.find(\n (candidate) => candidate.key === operation.channel,\n );\n const replyChannel = doc.channels.find(\n (candidate) => candidate.key === operation.reply?.channel,\n );\n return (\n <div\n className=\"ws-pane\"\n data-testid={`ws-pane-${operation.slug}`}\n >\n <Card\n title={\n <>\n {operation.title} <Badge tone=\"accent\">operation</Badge>\n </>\n }\n >\n <KeyValue\n items={[\n {\n label: \"Direction\",\n value: operation.action && (\n <span data-testid=\"ws-operation-action\">\n <Badge\n tone={operation.action === \"send\" ? \"info\" : \"success\"}\n >\n {operation.action}\n </Badge>{\" \"}\n {operation.action === \"send\"\n ? \"client → server\"\n : \"server → client\"}\n </span>\n ),\n },\n {\n label: \"Channel\",\n value: channel && (\n <Link to={itemPath(channel.slug, \"\", pathBase)}>\n <code>{channel.address}</code>\n </Link>\n ),\n },\n operation.permission !== undefined && {\n label: \"Permission\",\n value: (\n <PermissionMarker\n action={operation.permission}\n probe={probe}\n />\n ),\n },\n operation.summary !== undefined && {\n label: \"Summary\",\n value: <InlineText text={operation.summary} />,\n },\n ]}\n />\n </Card>\n <Card title={operation.action === \"send\" ? \"Sends\" : \"Receives\"}>\n <ItemLinks\n items={messageLinks(doc, operation.messages)}\n pathBase={pathBase}\n />\n </Card>\n {operation.reply && (\n <Card title=\"Reply\">\n <div data-testid=\"ws-operation-reply\">\n <p className=\"muted\">\n The server answers on{\" \"}\n {replyChannel ? (\n <Link to={itemPath(replyChannel.slug, \"\", pathBase)}>\n <code>{replyChannel.address}</code>\n </Link>\n ) : (\n <code>{operation.reply.channel ?? \"?\"}</code>\n )}{\" \"}\n with one of:\n </p>\n <ItemLinks\n items={messageLinks(doc, operation.reply.messages)}\n pathBase={pathBase}\n />\n </div>\n </Card>\n )}\n </div>\n );\n}\n\n/** Props of {@link MessagePane}. */\ninterface MessagePaneProps extends WsPaneOptions {\n /** The document, read. */\n doc: WsDoc;\n /** The raw document, for `$ref`s. */\n root: SpecDocument;\n /** The message. */\n message: WsMessage;\n}\n\n/** A message: name, summary, where it travels, its payload schema, examples and (for an event) try-it. */\nexport function MessagePane({\n doc,\n root,\n message,\n console: target,\n pathBase,\n}: MessagePaneProps) {\n const channels = channelsCarrying(doc, message.key);\n const operations = operationsUsing(doc, message.key);\n const eventPayload = message.event\n ? eventPayloadSchema(message.payload, root)\n : undefined;\n const link =\n target !== undefined && message.event !== null\n ? target.messageLink(message, doc)\n : null;\n return (\n <div\n className=\"ws-pane\"\n data-testid={`ws-pane-${message.slug}`}\n >\n <Card\n title={\n <>\n {message.title ?? message.name}{\" \"}\n <Badge tone={message.event ? \"success\" : \"neutral\"}>\n {message.event\n ? `${message.event.kind} event`\n : \"control message\"}\n </Badge>\n </>\n }\n >\n <KeyValue\n items={[\n {\n label: \"Name\",\n value: <code data-testid=\"ws-message-name\">{message.name}</code>,\n },\n message.summary !== undefined && {\n label: \"Summary\",\n value: <InlineText text={message.summary} />,\n },\n message.contentType !== undefined && {\n label: \"Content type\",\n value: <code>{message.contentType}</code>,\n },\n {\n label: \"Channels\",\n value: (\n <ItemLinks\n items={channels.map((channel) => ({\n slug: channel.slug,\n label: channel.address,\n }))}\n pathBase={pathBase}\n />\n ),\n },\n {\n label: \"Operations\",\n value: (\n <ItemLinks\n items={operations.map((operation) => ({\n slug: operation.slug,\n label: operation.key,\n }))}\n pathBase={pathBase}\n />\n ),\n },\n ]}\n />\n </Card>\n {message.event && eventPayload !== undefined && (\n <Card\n title=\"Event payload\"\n className=\"ws-highlight\"\n >\n <p className=\"muted\">\n <code>event.payload</code>: what this {message.event.kind} event\n says. It arrives inside the envelope below.\n </p>\n <div data-testid=\"ws-event-payload\">\n <PayloadSchema\n schema={eventPayload}\n root={root}\n label={`Payload of ${message.name}`}\n />\n </div>\n </Card>\n )}\n <Card title={message.event ? \"Full envelope\" : \"Payload\"}>\n <div data-testid=\"ws-message-payload\">\n <PayloadSchema\n schema={message.payload}\n root={root}\n label={`Schema of ${message.name}`}\n depth={message.event ? 1 : 2}\n />\n </div>\n </Card>\n {message.examples.length > 0 && (\n <Card title={message.examples.length === 1 ? \"Example\" : \"Examples\"}>\n {message.examples.map((example, index) => (\n <MessageExample\n // eslint-disable-next-line react/no-array-index-key -- the document's examples, in its order\n key={index}\n example={example}\n index={index}\n isEvent={message.event !== null}\n />\n ))}\n </Card>\n )}\n {message.event && target !== undefined && (\n <Card title=\"Try it\">\n {!target.available ? (\n <ConsoleUnavailable console={target} />\n ) : link === null ? (\n <p className=\"muted\">No channel in this document carries it.</p>\n ) : (\n <div className=\"ws-try\">\n <p className=\"muted\">\n Opens the {target.label} on every {message.event.kind}, showing\n only <code>{message.event.type}</code> events.\n </p>\n <Link\n to={link}\n className=\"btn btn-primary\"\n data-testid=\"ws-try-link\"\n >\n Watch {message.event.type} events\n </Link>\n </div>\n )}\n </Card>\n )}\n </div>\n );\n}\n\n/** Props of {@link MessageExample}. */\ninterface MessageExampleProps {\n /** The example, as the document gives it. */\n example: WsMessage[\"examples\"][number];\n /** Its position, naming it when it has no name. */\n index: number;\n /** Whether the message is an event frame (so its `event.payload` is shown first). */\n isEvent: boolean;\n}\n\n/**\n * One message example: its name and summary, then the frame. For an event,\n * the frame's `event.payload` (what the event says) comes first, open and\n * highlighted, and the whole frame below it; the copy button copies the\n * whole frame, exactly as it travels.\n */\nfunction MessageExample({ example, index, isEvent }: MessageExampleProps) {\n const name = example.name ?? `Example ${index + 1}`;\n const eventPayload = isEvent\n ? exampleEventPayload(example.payload)\n : undefined;\n return (\n <div\n className=\"ws-example\"\n data-testid=\"ws-example\"\n >\n <div className=\"ws-example-head\">\n <strong data-testid=\"ws-example-name\">{name}</strong>\n <CopyButton\n text={() => JSON.stringify(example.payload, null, 2)}\n label=\"Copy JSON\"\n ariaLabel={`Copy example ${name} as JSON`}\n />\n </div>\n {example.summary && (\n <p\n className=\"muted\"\n data-testid=\"ws-example-summary\"\n >\n <InlineText text={example.summary} />\n </p>\n )}\n {eventPayload !== undefined && (\n <div\n className=\"ws-example-event ws-highlight\"\n data-testid=\"ws-example-event-payload\"\n >\n <p className=\"muted\">\n <code>event.payload</code>\n </p>\n <JsonView\n value={eventPayload}\n label={`event.payload of ${name}`}\n expandDepth={4}\n />\n </div>\n )}\n <div data-testid=\"ws-example-frame\">\n {eventPayload !== undefined && <p className=\"muted\">The whole frame</p>}\n <JsonView\n value={example.payload}\n label={`Example ${name}`}\n expandDepth={eventPayload !== undefined ? 1 : 2}\n />\n </div>\n </div>\n );\n}\n",
|
|
49
|
+
"/**\n * The WebSocket API reference (AsyncAPI 3.0): `/ws` and `/ws/:item` under the\n * mount. The document is loaded by the viewer (`../viewer.tsx`); the pure\n * reading of it is in `model.ts`, the per-item panes in `items.tsx`, the\n * extension panels in `panels.tsx`.\n */\nimport type { KeyboardEvent } from \"react\";\nimport type { SpecDocument } from \"../../../api/docs\";\nimport type { PermissionProbe } from \"../permissions\";\nimport type { LimitInfo } from \"./format\";\nimport type { WsConsole, WsPaneOptions } from \"./items\";\nimport type { WsAddressOptions, WsNavGroup } from \"./model\";\nimport { useMemo, useRef } from \"react\";\nimport { EmptyState } from \"../../../components/EmptyState\";\nimport { Field } from \"../../../components/Field\";\nimport { TextInput } from \"../../../components/inputs\";\nimport { arrowTarget } from \"../../../components/listKeys\";\nimport { Link } from \"../../../router\";\nimport { useParams, useQueryParam } from \"../../../routing\";\nimport { SEARCH_SHORTCUT } from \"../../../shortcuts\";\nimport { ChannelPane, MessagePane, OperationPane } from \"./items\";\nimport {\n filterGroups,\n itemPath,\n navGroups,\n readWsDoc,\n selectItem,\n} from \"./model\";\nimport {\n CloseCodesPanel,\n DocHeader,\n LimitsPanel,\n RefusalsPanel,\n SecurityPanel,\n ServerPanel,\n} from \"./panels\";\nimport \"./ws.css\";\n\n/** Props of {@link WsSidebar}. */\ninterface WsSidebarProps {\n /** The groups, before searching. */\n groups: readonly WsNavGroup[];\n /** The selected slug. */\n selected: string;\n /** The search (`?q=`). */\n query: string;\n /** Sets the search. */\n onQuery: (query: string) => void;\n /** The docs section's path within the host app (\"\" for a standalone viewer). */\n pathBase: string;\n}\n\n/** The sidebar: a search box, then the connection's panels, channels, operations and messages. */\nfunction WsSidebar({\n groups,\n selected,\n query,\n onQuery,\n pathBase,\n}: WsSidebarProps) {\n const shown = filterGroups(groups, query);\n const navRef = useRef<HTMLElement>(null);\n const links = () =>\n Array.from(\n navRef.current?.querySelectorAll<HTMLAnchorElement>(\"a.ws-nav-link\") ??\n [],\n );\n // Arrow keys move between the links (Home/End to the ends), and ArrowDown\n // from the search goes to the first; each link stays a Tab stop too.\n const onListKey = (event: KeyboardEvent<HTMLDivElement>) => {\n const target = arrowTarget(event.key, links(), document.activeElement);\n if (target) {\n event.preventDefault();\n target.focus();\n }\n };\n return (\n <nav\n ref={navRef}\n className=\"ws-sidebar\"\n aria-label=\"WebSocket reference\"\n >\n <Field label=\"Search\">\n <TextInput\n type=\"search\"\n value={query}\n onChange={onQuery}\n onKeyDown={(event) => {\n const first = links()[0];\n if (event.key === \"ArrowDown\" && first) {\n event.preventDefault();\n first.focus();\n }\n }}\n placeholder=\"Channel, message, code…\"\n {...SEARCH_SHORTCUT}\n autoComplete=\"off\"\n spellCheck={false}\n />\n </Field>\n {shown.length === 0 && (\n <p\n className=\"muted\"\n data-testid=\"ws-search-empty\"\n >\n Nothing matches “{query}”.\n </p>\n )}\n <div\n className=\"ws-sidebar-list\"\n onKeyDown={onListKey}\n >\n {shown.map((group) => (\n <section\n key={group.title}\n className=\"ws-group\"\n data-testid={`ws-group-${group.title}`}\n >\n <h2 className=\"ws-group-title\">{group.title}</h2>\n <ul>\n {group.items.map((item) => (\n <li key={item.slug}>\n <Link\n to={itemPath(item.slug, query, pathBase)}\n className=\"ws-nav-link\"\n activeMatch=\"exact\"\n data-selected={item.slug === selected ? \"true\" : undefined}\n data-testid={`ws-nav-${item.slug}`}\n >\n <span className=\"ws-nav-label\">{item.label}</span>\n {item.hint && (\n <span className=\"ws-nav-hint\">{item.hint}</span>\n )}\n </Link>\n </li>\n ))}\n </ul>\n </section>\n ))}\n </div>\n </nav>\n );\n}\n\n/** Props of {@link WsReference}. */\nexport interface WsReferenceProps {\n /** The AsyncAPI document. */\n document: SpecDocument;\n /** The extension prefixes to read; defaults to the configured ones. */\n extensions?: readonly string[];\n /** The subprotocol shown when the document states none. */\n defaultSubprotocol?: string;\n /** Labels and meanings for the limits, when the embedding viewer knows them. */\n limitsInfo?: Readonly<Record<string, LimitInfo>>;\n /** The console the try-it cards open (the jobs UI's Events console), when the host has one. */\n console?: WsConsole;\n /** Answers \"does the caller hold this permission\" (the have/lack markers). */\n probe?: PermissionProbe;\n /** The host's contract for the try-it's address fields. */\n address?: WsAddressOptions;\n /** The docs section's path within the host app (\"\" for a standalone viewer). */\n pathBase?: string;\n}\n\n/** The reference for a loaded document. */\nexport function WsReference({\n document: spec,\n extensions,\n defaultSubprotocol,\n limitsInfo,\n console: consoleOptions,\n probe,\n address,\n pathBase = \"\",\n}: WsReferenceProps) {\n const paneOptions: WsPaneOptions = {\n console: consoleOptions,\n probe,\n address,\n pathBase,\n };\n const doc = useMemo(\n () => readWsDoc(spec, { extensions, defaultSubprotocol }),\n [spec, extensions, defaultSubprotocol],\n );\n const groups = useMemo(() => navGroups(doc), [doc]);\n const { item } = useParams<{ item?: string }>();\n const [query, setQuery] = useQueryParam(\"q\");\n\n const connection = doc.channels.find((channel) => channel.isConnection);\n const slug = item ?? connection?.slug ?? doc.channels[0]?.slug ?? \"\";\n const selection = selectItem(doc, slug);\n\n let pane;\n if (selection === null) {\n pane = (\n <EmptyState\n title=\"No such item\"\n description={\n <>\n This document has nothing called <code>{slug}</code>.{\" \"}\n <Link to={`${pathBase}/ws`}>Back to the connection</Link>.\n </>\n }\n />\n );\n } else {\n switch (selection.kind) {\n case \"channel\":\n pane = (\n <ChannelPane\n doc={doc}\n root={spec}\n channel={selection.channel}\n limitsInfo={limitsInfo}\n {...paneOptions}\n />\n );\n break;\n case \"operation\":\n pane = (\n <OperationPane\n doc={doc}\n operation={selection.operation}\n {...paneOptions}\n />\n );\n break;\n case \"message\":\n pane = (\n <MessagePane\n doc={doc}\n root={spec}\n message={selection.message}\n {...paneOptions}\n />\n );\n break;\n case \"limits\":\n pane = doc.limits && (\n <LimitsPanel\n limits={doc.limits}\n extensionKey={doc.extensionKeys.limits}\n limitsInfo={limitsInfo}\n />\n );\n break;\n case \"close-codes\":\n pane = <CloseCodesPanel doc={doc} />;\n break;\n case \"upgrade-refusals\":\n pane = <RefusalsPanel doc={doc} />;\n break;\n }\n }\n\n return (\n <div\n className=\"screen ws-docs\"\n data-testid=\"ws-docs\"\n >\n <DocHeader doc={doc} />\n <div className=\"ws-overview\">\n <ServerPanel doc={doc} />\n <SecurityPanel doc={doc} />\n </div>\n <div className=\"ws-layout\">\n <WsSidebar\n groups={groups}\n selected={selection ? slug : \"\"}\n query={query}\n onQuery={setQuery}\n pathBase={pathBase}\n />\n <section\n className=\"ws-main\"\n aria-label=\"Selected item\"\n data-testid=\"ws-main\"\n data-selected={selection ? slug : \"\"}\n >\n {pane}\n </section>\n </div>\n </div>\n );\n}\n",
|
|
50
|
+
"import type { ApiClient } from \"./client\";\n\n/**\n * A spec document as JSON. The package's `OpenApiDocument`/`AsyncApiDocument`\n * types live in its root entry, which the browser project does not compile;\n * each viewer narrows the parts it renders with its own types.\n */\nexport type SpecDocument = Record<string, unknown>;\n\n/**\n * The API's own spec documents, as the docs screens read them.\n *\n * `/meta.docs` (and `JobsUiConfig.docs`) give FULL paths, which include the API's\n * `basePath`, while the client prefixes every request with `apiBase`. So the\n * path is made relative to `apiBase`'s path before it is requested: this keeps\n * the CSRF, credentials and error handling of every other API read.\n */\n\n/** The path of a full docs path relative to `apiBase` (`\"/jobs-api/openapi.json\"` → `\"/openapi.json\"`). */\nexport function docsPath(apiBase: string, fullPath: string): string {\n const basePath = /^https?:\\/\\//.test(apiBase)\n ? new URL(apiBase).pathname\n : apiBase;\n const base = basePath.replace(/\\/+$/, \"\");\n return base !== \"\" && fullPath.startsWith(`${base}/`)\n ? fullPath.slice(base.length)\n : fullPath;\n}\n\n/** Query keys of the spec documents, under `[\"docs\"]`. */\nexport const docsKeys = {\n /** Every docs read. */\n all: [\"docs\"] as const,\n /** The OpenAPI 3.1 document at a path. */\n openapi: (path: string) => [\"docs\", \"openapi\", path] as const,\n /** The AsyncAPI 3.0 document at a path. */\n asyncapi: (path: string) => [\"docs\", \"asyncapi\", path] as const,\n} satisfies Record<string, unknown>;\n\n/** `GET <docs.openapi>`: the API's OpenAPI 3.1 document. */\nexport function getOpenApiDocument(\n api: ApiClient,\n fullPath: string,\n signal?: AbortSignal,\n): Promise<SpecDocument> {\n return api.request<SpecDocument>(\"GET\", docsPath(api.base, fullPath), {\n signal,\n });\n}\n\n/** `GET <docs.asyncapi>`: the API's AsyncAPI 3.0 document. */\nexport function getAsyncApiDocument(\n api: ApiClient,\n fullPath: string,\n signal?: AbortSignal,\n): Promise<SpecDocument> {\n return api.request<SpecDocument>(\"GET\", docsPath(api.base, fullPath), {\n signal,\n });\n}\n",
|
|
51
|
+
"/**\n * The HTTP API reference (OpenAPI 3.1): `/docs/http` and\n * `/docs/http/:operationId`. The viewer is the package's\n * (`@kingsleyweb/bun-ui/docs/embed`); this wrapper fetches the document\n * with the app's client and injects the app's seams: its client for the\n * try-it (so the CSRF header and `Content-Type` rules are the app's own), the\n * permission probe, the read-only flag, and the query invalidation after a\n * mutation.\n */\nimport type { JobsApiAction } from \"../../../api/contract\";\nimport type {\n ApiError as DocsApiErrorShape,\n DocsClient,\n SpecDocument,\n} from \"@kingsleyweb/bun-ui/docs/embed\";\nimport { useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport { useMemo } from \"react\";\nimport {\n ApiError as DocsApiError,\n DocsUiConfigContext,\n HttpReference,\n} from \"@kingsleyweb/bun-ui/docs/embed\";\nimport { docsKeys, getOpenApiDocument } from \"../../../api/docs\";\nimport { EmptyState } from \"../../../components/EmptyState\";\nimport { ErrorView } from \"../../../components/ErrorView\";\nimport { Spinner } from \"../../../components/Spinner\";\nimport { useApiClient, useUiConfig } from \"../../../context\";\nimport { useCanFn, useMeta } from \"../../../meta/hooks\";\n\n/** The app's ApiError as the package's (same fields; the class is the package's). */\nfunction toDocsApiError(error: DocsApiErrorShape | undefined) {\n return error === undefined\n ? undefined\n : new DocsApiError({\n kind: error.kind,\n status: error.status,\n code: error.code,\n title: error.title,\n detail: error.detail,\n type: error.type,\n issues: error.issues,\n context: error.context,\n instance: error.instance,\n });\n}\n\n/** `/docs/http[/:operationId]`: the operations, and one of them with its try-it panel. */\nexport function HttpDocsScreen() {\n const api = useApiClient();\n const config = useUiConfig();\n const meta = useMeta();\n const can = useCanFn();\n const queryClient = useQueryClient();\n const { docs } = meta;\n const path = docs?.openapi;\n const spec = useQuery({\n queryKey: docsKeys.openapi(path ?? \"\"),\n queryFn: ({ signal }) => getOpenApiDocument(api, path!, signal),\n enabled: path !== undefined,\n staleTime: Infinity,\n });\n\n // The try-it sends through the app's own client: same CSRF header and\n // problem parsing as every other screen, with the errors re-homed to the\n // package's class (the panel's `isApiError` is the package's own).\n const client = useMemo<DocsClient>(\n () => ({\n base: api.base,\n request: (method, requestPath, options) =>\n api.request(method, requestPath, options),\n requestRaw: async (method, requestPath, options) => {\n const raw = await api.requestRaw(method, requestPath, options);\n return { ...raw, error: toDocsApiError(raw.error) };\n },\n }),\n [api],\n );\n // The page config the package's screens read: the app's CSRF header and\n // mount; the rest is the standalone defaults.\n const pageConfig = useMemo(\n () => ({\n version: 1 as const,\n title: config.title,\n basePath: config.basePath,\n assetsPath: config.assetsPath,\n documentUrl: path ?? \"\",\n csrfHeader: config.csrfHeader,\n extensions: [\"x-bun-common\", \"x-bun-jobs\"],\n theme: config.theme,\n // This app redirects through its own `unauthorized` (AppProviders):\n // the embedded screens never do, so a failure redirects once.\n unauthorized: null,\n }),\n [config, path],\n );\n\n if (path === undefined) {\n return (\n <div className=\"screen\">\n <EmptyState\n headingLevel={1}\n title=\"No HTTP documentation\"\n description=\"This API does not serve its OpenAPI document.\"\n />\n </div>\n );\n }\n if (spec.isPending) {\n return (\n <div className=\"screen\">\n <Spinner\n label=\"Loading the OpenAPI document\"\n showLabel\n />\n </div>\n );\n }\n if (spec.isError) {\n return (\n <div className=\"screen\">\n <ErrorView\n headingLevel={1}\n error={spec.error}\n title=\"Could not load the OpenAPI document\"\n onRetry={() => void spec.refetch()}\n />\n </div>\n );\n }\n return (\n <DocsUiConfigContext value={pageConfig}>\n <HttpReference\n doc={spec.data as SpecDocument}\n pathBase=\"/docs\"\n client={client}\n probe={(action) => can(action as JobsApiAction)}\n readOnly={meta.readOnly}\n onChanged={() =>\n void queryClient.invalidateQueries({\n predicate: (query) => query.queryKey[0] !== docsKeys.all[0],\n })\n }\n />\n </DocsUiConfigContext>\n );\n}\n",
|
|
52
|
+
"import type { WsAddressOptions } from \"@kingsleyweb/bun-ui/docs/embed\";\nimport {\n encodeJobId,\n MAX_NAME_LENGTH,\n NAME_PARAM_PATTERN,\n} from \"@kingsleyweb/bun-jobs/api/contract\";\n\n/**\n * The contract's own rules for the WebSocket reference's try-it, injected\n * into the package's machinery: queue/runner names follow the contract's\n * name rule, a job id is anything (it is escaped), and a job id is escaped\n * exactly as the live client names job channels (`liveChannels.job`).\n */\n\n/** How a queue or runner name must look (the contract's `NAME_PARAM_PATTERN`). */\nconst NAME_PATTERN = new RegExp(NAME_PARAM_PATTERN);\n\n/** Why a name is refused under the contract's rule. */\nconst NAME_PROBLEM = `Letters, digits, \"_\", \".\" and \"-\" only (not \".\" or \"..\"), at most ${MAX_NAME_LENGTH}.`;\n\n/** The reference's address options: the contract's rule, wording and job-id escaping. */\nexport const JOBS_ADDRESS: WsAddressOptions = {\n nameRule: (name, value) => {\n if (name === \"jobId\") {\n return null;\n }\n return value.length <= MAX_NAME_LENGTH && NAME_PATTERN.test(value)\n ? null\n : NAME_PROBLEM;\n },\n patternText: {\n [NAME_PARAM_PATTERN]:\n 'Letters, digits, \"_\", \".\" and \"-\" only (not \".\" or \"..\").',\n },\n escapeValue: (name, value) => (name === \"jobId\" ? encodeJobId(value) : value),\n};\n",
|
|
53
|
+
"import type { EventName, JobsApiMode } from \"../../../api/types\";\nimport {\n JOBS_API_ACTIONS,\n JOBS_API_WS_SUBPROTOCOL,\n} from \"@kingsleyweb/bun-jobs/api/contract\";\nimport { parseChannel, typesFor } from \"../../events/eventLog\";\n\n/**\n * The Events-console links of the WebSocket reference's try-it: the one\n * jobs-specific piece. The reading of the document and the address machinery\n * are the package's (`@kingsleyweb/bun-ui/docs/embed`); what a filled\n * channel LINKS to is the app's.\n */\n\n/** Whether a string is one of the API's actions (so a have/lack marker can be shown). */\nexport function isKnownAction(\n action: string,\n): action is (typeof JOBS_API_ACTIONS)[number] {\n return (JOBS_API_ACTIONS as readonly string[]).includes(action);\n}\n\n/** The subprotocol the client contract defaults to, for a document that states none. */\nexport const DEFAULT_SUBPROTOCOL: string = JOBS_API_WS_SUBPROTOCOL;\n\n/**\n * The Events console link subscribing to `channel` filtered to `types`, or\n * `null` when the console could not open it in `mode`. The type filter is\n * left off when it would let every type the console offers for the channel\n * through anyway (a filter of everything is no filter).\n */\nexport function eventsLink(\n channel: string,\n types: readonly EventName[],\n mode: JobsApiMode,\n): string | null {\n const choice = parseChannel(channel, mode);\n if (choice === null) {\n return null;\n }\n const allowed = typesFor(choice);\n const kept = allowed.filter((type) => types.includes(type));\n const params = new URLSearchParams({ channel });\n if (kept.length > 0 && kept.length < allowed.length) {\n params.set(\"types\", kept.join(\",\"));\n }\n return `/events?${params.toString()}`;\n}\n",
|
|
54
|
+
"import type { EventName, JobsApiMode } from \"../../../api/types\";\nimport type { WsConsole, wsModel } from \"@kingsleyweb/bun-ui/docs/embed\";\nimport { parseChannel } from \"../../events/eventLog\";\nimport { eventsLink } from \"./links\";\n\n/**\n * The console seam for the reference (`WsConsole`): `channelLink` is\n * `eventsLink` on the filled address, `messageLink` an event message's\n * kind's broad channel (`queues` / `runners` / `workers`) when the document\n * has it, else `all`, filtered to the one type.\n */\nexport function eventsConsole(\n mode: JobsApiMode,\n available: boolean,\n): WsConsole {\n return {\n available,\n label: \"Events console\",\n unavailableReason: (\n <>\n The Events console is not available here: it needs the API's\n socket, the <code>events.connect</code> permission and the manage\n screens.\n </>\n ),\n noChannelReason: (channel) => (\n <>\n The Events console has no channel <code>{channel}</code> in mode{\" \"}\n <code>{mode}</code>.\n </>\n ),\n channelLink: (channel, types) =>\n eventsLink(channel, types as readonly EventName[], mode),\n messageLink: (message, doc: wsModel.WsDoc) => {\n if (message.event === null) {\n return null;\n }\n // Each kind's broad channel. Worker events are off `all` and `queues`,\n // so `workers` is the only one that carries them.\n const broad =\n message.event.kind === \"queue\"\n ? \"queues\"\n : message.event.kind === \"worker\"\n ? \"workers\"\n : \"runners\";\n const channel = [broad, \"all\"].find((key) =>\n doc.channels.some((candidate) => candidate.key === key),\n );\n if (channel === undefined) {\n return null;\n }\n const params = new URLSearchParams({\n channel,\n types: message.event.type,\n });\n return parseChannel(channel, mode) === null\n ? null\n : `/events?${params.toString()}`;\n },\n };\n}\n",
|
|
55
|
+
"import type { LimitInfo } from \"@kingsleyweb/bun-ui/docs/embed\";\n\n/**\n * How the limits panel reads each `x-bun-jobs-limits` entry: a label, what it\n * means, and its unit. The vocabulary is this API's own; the rendering is the\n * package's (`wsFormat.formatLimit`), with this as its `limitsInfo`.\n */\n\n/** The known limits. */\nexport const LIMIT_INFO: Readonly<Record<string, LimitInfo>> = {\n maxMessageBytes: {\n label: \"Largest client frame\",\n meaning:\n \"A bigger frame is refused MESSAGE_TOO_LARGE, then the socket closes 1009.\",\n unit: \"bytes\",\n },\n messagesPerSecond: {\n label: \"Client frames per second\",\n meaning: \"The sustained rate; over it, a RATE_LIMITED error.\",\n unit: \"per-second\",\n },\n rateLimitBurst: {\n label: \"Rate-limit burst\",\n meaning: \"Frames that may be sent at once before the rate applies.\",\n unit: \"count\",\n },\n rateLimitBreachWindowMs: {\n label: \"Breach window\",\n meaning: \"A second rate-limit breach within it closes the socket 1008.\",\n unit: \"ms\",\n },\n maxSubscriptions: {\n label: \"Subscriptions per connection\",\n meaning:\n \"Channels one connection may hold; more are refused SUBSCRIPTION_LIMIT.\",\n unit: \"count\",\n },\n maxChannelsPerFrame: {\n label: \"Channels per frame\",\n meaning:\n \"Channels one subscribe or unsubscribe may list: send more in several frames.\",\n unit: \"count\",\n },\n maxConnections: {\n label: \"Connections\",\n meaning:\n \"Open connections the API accepts; the next upgrade is refused 429 CONNECTION_LIMIT.\",\n unit: \"count\",\n },\n heartbeatMs: {\n label: \"Heartbeat\",\n meaning: \"How often a heartbeat is sent to an idle connection.\",\n unit: \"ms\",\n },\n maxBufferedBytes: {\n label: \"Send buffer\",\n meaning:\n \"Unsent bytes past which a connection is behind and stops receiving events.\",\n unit: \"bytes\",\n },\n slowConsumerTimeoutMs: {\n label: \"Slow-consumer timeout\",\n meaning: \"How long a connection may stay behind before it is closed 4008.\",\n unit: \"ms\",\n },\n coalesceProgressMs: {\n label: \"Progress coalescing\",\n meaning: \"A job's progress events are merged to at most one per interval.\",\n unit: \"ms\",\n },\n};\n",
|
|
56
|
+
"/**\n * The WebSocket API reference (AsyncAPI 3.0): `/docs/ws` and `/docs/ws/:item`.\n * The viewer is the package's (`@kingsleyweb/bun-ui/docs/embed`); this\n * wrapper fetches the document with the app's client and injects the app's\n * seams: the Events console for the try-it cards, the contract's address\n * rules, the permission probe, the limits vocabulary and the subprotocol the\n * client contract defaults to.\n */\nimport { useQuery } from \"@tanstack/react-query\";\nimport { useMemo } from \"react\";\nimport { WsReference } from \"@kingsleyweb/bun-ui/docs/embed\";\nimport { docsKeys, getAsyncApiDocument } from \"../../../api/docs\";\nimport { Card } from \"../../../components/Card\";\nimport { EmptyState } from \"../../../components/EmptyState\";\nimport { ErrorView } from \"../../../components/ErrorView\";\nimport { Spinner } from \"../../../components/Spinner\";\nimport { useApiClient } from \"../../../context\";\nimport { useNav } from \"../../../layout/useNav\";\nimport { useCanFn, useMeta } from \"../../../meta/hooks\";\nimport { Link } from \"../../../router\";\nimport { JOBS_ADDRESS } from \"./address\";\nimport { eventsConsole } from \"./console\";\nimport { DEFAULT_SUBPROTOCOL, isKnownAction } from \"./links\";\nimport { LIMIT_INFO } from \"./limits\";\n\n/** `/docs/ws[/:item]`: the channels, operations and messages of the live-events socket. */\nexport function WsDocsScreen() {\n const api = useApiClient();\n const meta = useMeta();\n const can = useCanFn();\n const consoleAvailable = useNav().some((entry) => entry.id === \"events\");\n const { docs } = meta;\n const path = docs?.asyncapi;\n const document = useQuery({\n queryKey: docsKeys.asyncapi(path ?? \"\"),\n queryFn: ({ signal }) => getAsyncApiDocument(api, path!, signal),\n enabled: path !== undefined,\n staleTime: Infinity,\n });\n const console = useMemo(\n () => eventsConsole(meta.mode, consoleAvailable),\n [meta.mode, consoleAvailable],\n );\n\n let body;\n if (path === undefined) {\n body = (\n <>\n <h1 className=\"screen-title\">WebSocket API</h1>\n <EmptyState\n title=\"This API has no live-events socket\"\n description=\"There is no AsyncAPI document to show: the API was created without its WebSocket, so the screens refresh by polling over HTTP.\"\n action={<Link to=\"/docs\">Back to the API docs</Link>}\n />\n </>\n );\n } else if (document.data) {\n body = (\n <WsReference\n document={document.data}\n defaultSubprotocol={DEFAULT_SUBPROTOCOL}\n limitsInfo={LIMIT_INFO}\n console={console}\n address={JOBS_ADDRESS}\n probe={(action) => (isKnownAction(action) ? can(action) : undefined)}\n pathBase=\"/docs\"\n />\n );\n } else if (document.error) {\n body = (\n <>\n <h1 className=\"screen-title\">WebSocket API</h1>\n <ErrorView\n error={document.error}\n title=\"Could not load the AsyncAPI document\"\n onRetry={() => void document.refetch()}\n />\n </>\n );\n } else {\n body = (\n <>\n <h1 className=\"screen-title\">WebSocket API</h1>\n <Card>\n <Spinner\n label=\"Loading the AsyncAPI document\"\n showLabel\n />\n </Card>\n </>\n );\n }\n return (\n <div\n className=\"screen ws-docs\"\n data-testid=\"ws-docs\"\n >\n {body}\n </div>\n );\n}\n",
|
|
57
|
+
"/**\n * The API documentation: a landing page linking the HTTP reference (OpenAPI)\n * and the WebSocket reference (AsyncAPI), each shown only when the API serves\n * that document (`meta.docs`). Routed from `routes.tsx` under `/docs`.\n */\nimport { Card } from \"../../components/Card\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { useMeta } from \"../../meta/hooks\";\nimport { Link } from \"../../router\";\n\nexport { HttpDocsScreen } from \"./http\";\nexport { WsDocsScreen } from \"./ws\";\n\n/** `/docs`: what documentation this API serves. */\nexport function DocsHomeScreen() {\n const { docs } = useMeta();\n return (\n <div\n className=\"screen\"\n data-testid=\"docs-home\"\n >\n <h1 className=\"screen-title\">API docs</h1>\n {docs === null ? (\n <EmptyState\n title=\"No documentation\"\n description=\"This API does not serve its OpenAPI or AsyncAPI documents.\"\n />\n ) : (\n <div className=\"docs-home\">\n <Card title=\"HTTP API\">\n <p>\n Every route, its parameters, request and response schemas, the\n permission it needs, and a panel to try it.\n </p>\n <Link to=\"/docs/http\">Open the HTTP reference</Link>\n </Card>\n {docs.asyncapi !== undefined && (\n <Card title=\"WebSocket API\">\n <p>\n The live-events socket: its channels, the messages on each,\n limits and close codes.\n </p>\n <Link to=\"/docs/ws\">Open the WebSocket reference</Link>\n </Card>\n )}\n </div>\n )}\n </div>\n );\n}\n"
|
|
58
|
+
],
|
|
59
|
+
"mappings": "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",
|
|
60
|
+
"debugId": "FBF3B6313393107364756E2164756E21",
|
|
61
|
+
"names": []
|
|
62
|
+
}
|