@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,466 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure reading of JSON Schema (the 2020-12 dialect OpenAPI 3.1 and AsyncAPI
|
|
3
|
+
* 3.0 use) for the docs viewers: `$ref` resolution against the document
|
|
4
|
+
* root, a one-line type label, the constraint facts, and the children a tree
|
|
5
|
+
* expands into. Nothing here renders; `SchemaTree.tsx` does.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Levels below which a tree never expands, whatever the schema: a guard for
|
|
10
|
+
* a pathological (non-cyclic but huge) document. Cycles stop far earlier, at
|
|
11
|
+
* the first `$ref` already open above.
|
|
12
|
+
*/
|
|
13
|
+
export const MAX_SCHEMA_LEVEL = 24;
|
|
14
|
+
|
|
15
|
+
/** A schema object; `true`/`false` are schemas too (anything / nothing). */
|
|
16
|
+
export type SchemaObject = Readonly<Record<string, unknown>>;
|
|
17
|
+
|
|
18
|
+
/** A spec document, the root `$ref`s resolve against. */
|
|
19
|
+
export type SchemaRoot = Readonly<Record<string, unknown>>;
|
|
20
|
+
|
|
21
|
+
/** Whether a value is a (non-array) object. */
|
|
22
|
+
export function isSchemaObject(value: unknown): value is SchemaObject {
|
|
23
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Decodes one JSON Pointer token (`~1` → `/`, `~0` → `~`), after URI-decoding. */
|
|
27
|
+
function decodeToken(token: string): string {
|
|
28
|
+
let text = token;
|
|
29
|
+
try {
|
|
30
|
+
text = decodeURIComponent(token);
|
|
31
|
+
} catch {
|
|
32
|
+
// Not URI-encoded; use it as it is.
|
|
33
|
+
}
|
|
34
|
+
return text.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The value a local `$ref` (`#/components/schemas/Job`) points at inside
|
|
39
|
+
* `root`, or `undefined` when it points nowhere or is not local (a remote
|
|
40
|
+
* document is never fetched).
|
|
41
|
+
*/
|
|
42
|
+
export function resolveRef(root: SchemaRoot, ref: string): unknown {
|
|
43
|
+
if (ref === "#") {
|
|
44
|
+
return root;
|
|
45
|
+
}
|
|
46
|
+
if (!ref.startsWith("#/")) {
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
let node: unknown = root;
|
|
50
|
+
for (const token of ref.slice(2).split("/")) {
|
|
51
|
+
const key = decodeToken(token);
|
|
52
|
+
if (Array.isArray(node)) {
|
|
53
|
+
const index = Number(key);
|
|
54
|
+
node = Number.isInteger(index) ? node[index] : undefined;
|
|
55
|
+
} else if (isSchemaObject(node) && Object.hasOwn(node, key)) {
|
|
56
|
+
node = node[key];
|
|
57
|
+
} else {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return node;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** The name a `$ref` is shown by: its last pointer segment (`#/components/schemas/Job` → `Job`). */
|
|
65
|
+
export function refName(ref: string): string {
|
|
66
|
+
const tokens = ref.split("/");
|
|
67
|
+
return decodeToken(tokens[tokens.length - 1] ?? ref) || ref;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** A schema's `$ref`, when it has a string one. */
|
|
71
|
+
export function refOf(schema: unknown): string | undefined {
|
|
72
|
+
return isSchemaObject(schema) && typeof schema.$ref === "string"
|
|
73
|
+
? schema.$ref
|
|
74
|
+
: undefined;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** What {@link deref} found. */
|
|
78
|
+
export interface Dereffed {
|
|
79
|
+
/** The schema behind the refs, or `undefined` when a ref points nowhere. */
|
|
80
|
+
schema: SchemaObject | boolean | undefined;
|
|
81
|
+
/** The refs followed, first to last; empty when `schema` had none. */
|
|
82
|
+
refs: string[];
|
|
83
|
+
/** Whether a ref in the chain points back at one already followed. */
|
|
84
|
+
cycle: boolean;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Follows `$ref` → `$ref` → … to a schema that is not only a reference. The
|
|
89
|
+
* siblings of a `$ref` (a `description`, say) are not merged: a caller shows
|
|
90
|
+
* them beside the target.
|
|
91
|
+
*/
|
|
92
|
+
export function deref(schema: unknown, root: SchemaRoot): Dereffed {
|
|
93
|
+
const refs: string[] = [];
|
|
94
|
+
let node: unknown = schema;
|
|
95
|
+
for (;;) {
|
|
96
|
+
const ref = refOf(node);
|
|
97
|
+
if (ref === undefined) {
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
if (refs.includes(ref)) {
|
|
101
|
+
return { schema: undefined, refs, cycle: true };
|
|
102
|
+
}
|
|
103
|
+
refs.push(ref);
|
|
104
|
+
node = resolveRef(root, ref);
|
|
105
|
+
}
|
|
106
|
+
if (typeof node === "boolean" || isSchemaObject(node)) {
|
|
107
|
+
return { schema: node, refs, cycle: false };
|
|
108
|
+
}
|
|
109
|
+
return { schema: undefined, refs, cycle: false };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** The types a schema declares (`type` as a string or an array), or `[]`. */
|
|
113
|
+
export function declaredTypes(schema: SchemaObject): string[] {
|
|
114
|
+
const { type } = schema;
|
|
115
|
+
if (typeof type === "string") {
|
|
116
|
+
return [type];
|
|
117
|
+
}
|
|
118
|
+
return Array.isArray(type)
|
|
119
|
+
? type.filter((item): item is string => typeof item === "string")
|
|
120
|
+
: [];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Whether `null` is allowed: `type` includes it, OpenAPI 3.0's `nullable`, or a `const`/`enum` of null. */
|
|
124
|
+
export function allowsNull(schema: SchemaObject): boolean {
|
|
125
|
+
return (
|
|
126
|
+
declaredTypes(schema).includes("null") ||
|
|
127
|
+
schema.nullable === true ||
|
|
128
|
+
(Array.isArray(schema.enum) && schema.enum.includes(null))
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Formats a JSON value for a label (`"a"`, `3`, `null`, `{…}`). */
|
|
133
|
+
export function formatValue(value: unknown): string {
|
|
134
|
+
if (value === undefined) {
|
|
135
|
+
return "undefined";
|
|
136
|
+
}
|
|
137
|
+
const text = JSON.stringify(value);
|
|
138
|
+
return text.length > 80 ? `${text.slice(0, 77)}…` : text;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** The alternatives of a composition keyword, when it has any. */
|
|
142
|
+
function alternatives(schema: SchemaObject, key: string): unknown[] {
|
|
143
|
+
const value = schema[key];
|
|
144
|
+
return Array.isArray(value) ? value : [];
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* A one-line type: `string`, `integer | null`, `array<JobState>`,
|
|
149
|
+
* `object`, `anyOf(2)`, `any`, `never`; an untyped `enum`/`const` shows its
|
|
150
|
+
* values (`"asc" | "desc"`), a typed one leaves them to {@link schemaFacts}.
|
|
151
|
+
* A `$ref` shows its name.
|
|
152
|
+
*/
|
|
153
|
+
export function typeLabel(
|
|
154
|
+
schema: unknown,
|
|
155
|
+
root: SchemaRoot,
|
|
156
|
+
level = 0,
|
|
157
|
+
): string {
|
|
158
|
+
if (schema === true) {
|
|
159
|
+
return "any";
|
|
160
|
+
}
|
|
161
|
+
if (schema === false) {
|
|
162
|
+
return "never";
|
|
163
|
+
}
|
|
164
|
+
if (!isSchemaObject(schema)) {
|
|
165
|
+
return "any";
|
|
166
|
+
}
|
|
167
|
+
const ref = refOf(schema);
|
|
168
|
+
if (ref !== undefined) {
|
|
169
|
+
return refName(ref);
|
|
170
|
+
}
|
|
171
|
+
const types = declaredTypes(schema);
|
|
172
|
+
if (types.length > 0) {
|
|
173
|
+
const labels = types.map((type) => {
|
|
174
|
+
if (type === "array" && level < 2) {
|
|
175
|
+
const { items } = schema;
|
|
176
|
+
return items === undefined
|
|
177
|
+
? "array"
|
|
178
|
+
: `array<${typeLabel(items, root, level + 1)}>`;
|
|
179
|
+
}
|
|
180
|
+
return type;
|
|
181
|
+
});
|
|
182
|
+
if (schema.nullable === true && !labels.includes("null")) {
|
|
183
|
+
labels.push("null");
|
|
184
|
+
}
|
|
185
|
+
return labels.join(" | ");
|
|
186
|
+
}
|
|
187
|
+
// Untyped: the values themselves say it (`"asc" | "desc"`).
|
|
188
|
+
if (Object.hasOwn(schema, "const")) {
|
|
189
|
+
return formatValue(schema.const);
|
|
190
|
+
}
|
|
191
|
+
if (Array.isArray(schema.enum)) {
|
|
192
|
+
const values = schema.enum.slice(0, 6).map(formatValue);
|
|
193
|
+
return schema.enum.length > 6
|
|
194
|
+
? `${values.join(" | ")} | …`
|
|
195
|
+
: values.join(" | ");
|
|
196
|
+
}
|
|
197
|
+
for (const key of ["oneOf", "anyOf", "allOf"]) {
|
|
198
|
+
const options = alternatives(schema, key);
|
|
199
|
+
if (options.length > 0) {
|
|
200
|
+
if (key !== "allOf" && options.length <= 3 && level < 2) {
|
|
201
|
+
return options
|
|
202
|
+
.map((option) => typeLabel(option, root, level + 1))
|
|
203
|
+
.join(" | ");
|
|
204
|
+
}
|
|
205
|
+
return `${key}(${options.length})`;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
if (
|
|
209
|
+
isSchemaObject(schema.properties) ||
|
|
210
|
+
schema.additionalProperties !== undefined
|
|
211
|
+
) {
|
|
212
|
+
return "object";
|
|
213
|
+
}
|
|
214
|
+
if (schema.items !== undefined) {
|
|
215
|
+
return "array";
|
|
216
|
+
}
|
|
217
|
+
if (schema.not !== undefined) {
|
|
218
|
+
return "not";
|
|
219
|
+
}
|
|
220
|
+
return "any";
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/** One constraint or annotation of a schema, as a label/value pair. */
|
|
224
|
+
export interface SchemaFact {
|
|
225
|
+
/** A stable key (the keyword). */
|
|
226
|
+
key: string;
|
|
227
|
+
/** What it is, e.g. `"min"`. */
|
|
228
|
+
label: string;
|
|
229
|
+
/** Its value, as text. */
|
|
230
|
+
value: string;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/** Keywords shown as facts, in display order, with their labels. */
|
|
234
|
+
const FACT_KEYWORDS: readonly (readonly [string, string])[] = [
|
|
235
|
+
["format", "format"],
|
|
236
|
+
["const", "const"],
|
|
237
|
+
["enum", "one of"],
|
|
238
|
+
["default", "default"],
|
|
239
|
+
["minimum", "min"],
|
|
240
|
+
["exclusiveMinimum", "above"],
|
|
241
|
+
["maximum", "max"],
|
|
242
|
+
["exclusiveMaximum", "below"],
|
|
243
|
+
["multipleOf", "multiple of"],
|
|
244
|
+
["minLength", "min length"],
|
|
245
|
+
["maxLength", "max length"],
|
|
246
|
+
["pattern", "pattern"],
|
|
247
|
+
["minItems", "min items"],
|
|
248
|
+
["maxItems", "max items"],
|
|
249
|
+
["uniqueItems", "unique items"],
|
|
250
|
+
["minProperties", "min fields"],
|
|
251
|
+
["maxProperties", "max fields"],
|
|
252
|
+
["contentMediaType", "media type"],
|
|
253
|
+
["contentEncoding", "encoding"],
|
|
254
|
+
];
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* The facts worth showing beside a schema's type: format, bounds, lengths,
|
|
258
|
+
* pattern, enum/const, default, examples, nullable, deprecated, read/write
|
|
259
|
+
* only. An untyped `enum`/`const` is left out: {@link typeLabel} shows its
|
|
260
|
+
* values as the type.
|
|
261
|
+
*/
|
|
262
|
+
export function schemaFacts(schema: unknown): SchemaFact[] {
|
|
263
|
+
if (!isSchemaObject(schema)) {
|
|
264
|
+
return [];
|
|
265
|
+
}
|
|
266
|
+
const facts: SchemaFact[] = [];
|
|
267
|
+
// An untyped enum/const is the type label itself; say it once.
|
|
268
|
+
const typed = declaredTypes(schema).length > 0;
|
|
269
|
+
for (const [key, label] of FACT_KEYWORDS) {
|
|
270
|
+
if (!Object.hasOwn(schema, key)) {
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
if ((key === "enum" || key === "const") && !typed) {
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
const value = schema[key];
|
|
277
|
+
if (key === "uniqueItems" && value !== true) {
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
if (key === "enum" && Array.isArray(value)) {
|
|
281
|
+
facts.push({ key, label, value: value.map(formatValue).join(", ") });
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
284
|
+
facts.push({
|
|
285
|
+
key,
|
|
286
|
+
label,
|
|
287
|
+
value:
|
|
288
|
+
typeof value === "string" && key !== "default" && key !== "const"
|
|
289
|
+
? value
|
|
290
|
+
: formatValue(value),
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
if (Array.isArray(schema.examples) && schema.examples.length > 0) {
|
|
294
|
+
facts.push({
|
|
295
|
+
key: "examples",
|
|
296
|
+
label: "examples",
|
|
297
|
+
value: schema.examples.map(formatValue).join(", "),
|
|
298
|
+
});
|
|
299
|
+
} else if (Object.hasOwn(schema, "example")) {
|
|
300
|
+
facts.push({
|
|
301
|
+
key: "example",
|
|
302
|
+
label: "example",
|
|
303
|
+
value: formatValue(schema.example),
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
if (schema.nullable === true) {
|
|
307
|
+
facts.push({ key: "nullable", label: "nullable", value: "yes" });
|
|
308
|
+
}
|
|
309
|
+
if (schema.deprecated === true) {
|
|
310
|
+
facts.push({ key: "deprecated", label: "deprecated", value: "yes" });
|
|
311
|
+
}
|
|
312
|
+
if (schema.readOnly === true) {
|
|
313
|
+
facts.push({ key: "readOnly", label: "read only", value: "yes" });
|
|
314
|
+
}
|
|
315
|
+
if (schema.writeOnly === true) {
|
|
316
|
+
facts.push({ key: "writeOnly", label: "write only", value: "yes" });
|
|
317
|
+
}
|
|
318
|
+
return facts;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* What an object says about fields it does not list: `"closed"` for
|
|
323
|
+
* `additionalProperties: false` (the API answers 400 to any other field),
|
|
324
|
+
* `"any"` for `true`/`{}`, `"typed"` for a schema, `null` when it says
|
|
325
|
+
* nothing (or is not an object).
|
|
326
|
+
*/
|
|
327
|
+
export function additionalFields(
|
|
328
|
+
schema: unknown,
|
|
329
|
+
): "closed" | "any" | "typed" | null {
|
|
330
|
+
if (
|
|
331
|
+
!isSchemaObject(schema) ||
|
|
332
|
+
!Object.hasOwn(schema, "additionalProperties")
|
|
333
|
+
) {
|
|
334
|
+
return null;
|
|
335
|
+
}
|
|
336
|
+
const extra = schema.additionalProperties;
|
|
337
|
+
if (extra === false) {
|
|
338
|
+
return "closed";
|
|
339
|
+
}
|
|
340
|
+
if (
|
|
341
|
+
extra === true ||
|
|
342
|
+
(isSchemaObject(extra) && Object.keys(extra).length === 0)
|
|
343
|
+
) {
|
|
344
|
+
return "any";
|
|
345
|
+
}
|
|
346
|
+
return "typed";
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/** One child of a schema node, as a tree expands it. */
|
|
350
|
+
export interface SchemaChild {
|
|
351
|
+
/** A key unique among its siblings. */
|
|
352
|
+
key: string;
|
|
353
|
+
/** What it is. */
|
|
354
|
+
kind:
|
|
355
|
+
| "property"
|
|
356
|
+
| "items"
|
|
357
|
+
| "prefixItems"
|
|
358
|
+
| "additional"
|
|
359
|
+
| "patternProperty"
|
|
360
|
+
| "oneOf"
|
|
361
|
+
| "anyOf"
|
|
362
|
+
| "allOf"
|
|
363
|
+
| "not";
|
|
364
|
+
/** The name shown: a property name, `[]`, `[0]`, `Option 1`, `*`, a pattern. */
|
|
365
|
+
label: string;
|
|
366
|
+
/** Its schema. */
|
|
367
|
+
schema: unknown;
|
|
368
|
+
/** For a property: whether the object lists it in `required`. */
|
|
369
|
+
required: boolean;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/** The children a tree expands a (dereferenced) schema into, in reading order. */
|
|
373
|
+
export function schemaChildren(schema: unknown): SchemaChild[] {
|
|
374
|
+
if (!isSchemaObject(schema)) {
|
|
375
|
+
return [];
|
|
376
|
+
}
|
|
377
|
+
const children: SchemaChild[] = [];
|
|
378
|
+
const required = new Set(
|
|
379
|
+
Array.isArray(schema.required)
|
|
380
|
+
? schema.required.filter(
|
|
381
|
+
(name): name is string => typeof name === "string",
|
|
382
|
+
)
|
|
383
|
+
: [],
|
|
384
|
+
);
|
|
385
|
+
if (isSchemaObject(schema.properties)) {
|
|
386
|
+
for (const [name, child] of Object.entries(schema.properties)) {
|
|
387
|
+
children.push({
|
|
388
|
+
key: `p:${name}`,
|
|
389
|
+
kind: "property",
|
|
390
|
+
label: name,
|
|
391
|
+
schema: child,
|
|
392
|
+
required: required.has(name),
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
if (isSchemaObject(schema.patternProperties)) {
|
|
397
|
+
for (const [pattern, child] of Object.entries(schema.patternProperties)) {
|
|
398
|
+
children.push({
|
|
399
|
+
key: `pp:${pattern}`,
|
|
400
|
+
kind: "patternProperty",
|
|
401
|
+
label: `/${pattern}/`,
|
|
402
|
+
schema: child,
|
|
403
|
+
required: false,
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
if (additionalFields(schema) === "typed") {
|
|
408
|
+
children.push({
|
|
409
|
+
key: "additional",
|
|
410
|
+
kind: "additional",
|
|
411
|
+
label: "any other field",
|
|
412
|
+
schema: schema.additionalProperties,
|
|
413
|
+
required: false,
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
if (Array.isArray(schema.prefixItems)) {
|
|
417
|
+
schema.prefixItems.forEach((child, index) => {
|
|
418
|
+
children.push({
|
|
419
|
+
key: `pi:${index}`,
|
|
420
|
+
kind: "prefixItems",
|
|
421
|
+
label: `[${index}]`,
|
|
422
|
+
schema: child,
|
|
423
|
+
required: false,
|
|
424
|
+
});
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
if (schema.items !== undefined && typeof schema.items !== "boolean") {
|
|
428
|
+
children.push({
|
|
429
|
+
key: "items",
|
|
430
|
+
kind: "items",
|
|
431
|
+
label: "[]",
|
|
432
|
+
schema: schema.items,
|
|
433
|
+
required: false,
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
for (const kind of ["oneOf", "anyOf", "allOf"] as const) {
|
|
437
|
+
alternatives(schema, kind).forEach((child, index) => {
|
|
438
|
+
children.push({
|
|
439
|
+
key: `${kind}:${index}`,
|
|
440
|
+
kind,
|
|
441
|
+
label: kind === "allOf" ? `Part ${index + 1}` : `Option ${index + 1}`,
|
|
442
|
+
schema: child,
|
|
443
|
+
required: false,
|
|
444
|
+
});
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
if (schema.not !== undefined) {
|
|
448
|
+
children.push({
|
|
449
|
+
key: "not",
|
|
450
|
+
kind: "not",
|
|
451
|
+
label: "not",
|
|
452
|
+
schema: schema.not,
|
|
453
|
+
required: false,
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
return children;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/** How a composition is introduced above its alternatives. */
|
|
460
|
+
export const COMPOSITION_LABELS: Readonly<
|
|
461
|
+
Record<"oneOf" | "anyOf" | "allOf", string>
|
|
462
|
+
> = {
|
|
463
|
+
oneOf: "Exactly one of",
|
|
464
|
+
anyOf: "Any of",
|
|
465
|
+
allOf: "All of",
|
|
466
|
+
};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/* The shared JSON Schema tree of the docs viewers. */
|
|
2
|
+
|
|
3
|
+
.schema-tree {
|
|
4
|
+
font-size: var(--text-sm);
|
|
5
|
+
overflow-x: auto;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.schema-list {
|
|
9
|
+
list-style: none;
|
|
10
|
+
margin: 0;
|
|
11
|
+
padding: 0;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.schema-list .schema-list {
|
|
15
|
+
margin-left: var(--space-3);
|
|
16
|
+
padding-left: var(--space-3);
|
|
17
|
+
border-left: 1px solid var(--border);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.schema-node {
|
|
21
|
+
padding: var(--space-1) 0;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.schema-row {
|
|
25
|
+
display: flex;
|
|
26
|
+
flex-wrap: wrap;
|
|
27
|
+
align-items: baseline;
|
|
28
|
+
gap: var(--space-1) var(--space-2);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.schema-toggle,
|
|
32
|
+
.schema-toggle-spacer {
|
|
33
|
+
flex: none;
|
|
34
|
+
width: 1.25rem;
|
|
35
|
+
text-align: center;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.schema-toggle {
|
|
39
|
+
padding: 0;
|
|
40
|
+
border: 0;
|
|
41
|
+
background: none;
|
|
42
|
+
color: var(--text-muted);
|
|
43
|
+
cursor: pointer;
|
|
44
|
+
font: inherit;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.schema-toggle:focus-visible,
|
|
48
|
+
.schema-ref-link:focus-visible {
|
|
49
|
+
outline: 2px solid var(--accent);
|
|
50
|
+
outline-offset: 1px;
|
|
51
|
+
border-radius: var(--radius-sm);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.schema-name {
|
|
55
|
+
font-family: var(--font-mono);
|
|
56
|
+
font-weight: 600;
|
|
57
|
+
overflow-wrap: anywhere;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.schema-required {
|
|
61
|
+
color: var(--danger-text);
|
|
62
|
+
font-size: var(--text-xs);
|
|
63
|
+
text-transform: uppercase;
|
|
64
|
+
letter-spacing: 0.03em;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.schema-type {
|
|
68
|
+
font-family: var(--font-mono);
|
|
69
|
+
color: var(--accent);
|
|
70
|
+
overflow-wrap: anywhere;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.schema-ref {
|
|
74
|
+
font-family: var(--font-mono);
|
|
75
|
+
font-size: var(--text-sm);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.schema-ref-link {
|
|
79
|
+
padding: 0;
|
|
80
|
+
border: 0;
|
|
81
|
+
background: none;
|
|
82
|
+
color: var(--accent);
|
|
83
|
+
text-decoration: underline;
|
|
84
|
+
cursor: pointer;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.schema-note-inline {
|
|
88
|
+
font-size: var(--text-xs);
|
|
89
|
+
color: var(--text-muted);
|
|
90
|
+
font-style: italic;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.schema-unresolved {
|
|
94
|
+
color: var(--danger-text);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.schema-fact {
|
|
98
|
+
padding: 0 var(--space-1);
|
|
99
|
+
border-radius: var(--radius-sm);
|
|
100
|
+
background: var(--surface-2);
|
|
101
|
+
font-size: var(--text-xs);
|
|
102
|
+
overflow-wrap: anywhere;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.schema-fact-label {
|
|
106
|
+
color: var(--text-muted);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.schema-description,
|
|
110
|
+
.schema-closed,
|
|
111
|
+
.schema-open {
|
|
112
|
+
margin: 0 0 0 calc(1.25rem + var(--space-2));
|
|
113
|
+
color: var(--text-muted);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.schema-description p {
|
|
117
|
+
margin: var(--space-1) 0 0;
|
|
118
|
+
overflow-wrap: anywhere;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.schema-closed,
|
|
122
|
+
.schema-open {
|
|
123
|
+
font-size: var(--text-xs);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.schema-closed {
|
|
127
|
+
color: var(--warning-text);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.schema-composition {
|
|
131
|
+
padding-top: var(--space-1);
|
|
132
|
+
color: var(--text-muted);
|
|
133
|
+
font-size: var(--text-xs);
|
|
134
|
+
font-weight: 600;
|
|
135
|
+
text-transform: uppercase;
|
|
136
|
+
letter-spacing: 0.03em;
|
|
137
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { SchemaRoot } from "./resolve";
|
|
2
|
+
import { declaredTypes, deref, isSchemaObject } from "./resolve";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A starting value for a schema: an editor's prefill. Objects get only their
|
|
6
|
+
* REQUIRED fields (an optional one would be a guess the reader has to
|
|
7
|
+
* delete), each filled the same way; `default`, `const` and the first `enum`
|
|
8
|
+
* value win over a type's zero; unions take their first non-null option.
|
|
9
|
+
* A `$ref` cycle, or a schema that says nothing, gives `null`.
|
|
10
|
+
*/
|
|
11
|
+
export function schemaSkeleton(
|
|
12
|
+
schema: unknown,
|
|
13
|
+
root: SchemaRoot,
|
|
14
|
+
seen: readonly string[] = [],
|
|
15
|
+
): unknown {
|
|
16
|
+
const { schema: node, refs, cycle } = deref(schema, root);
|
|
17
|
+
if (cycle || refs.some((ref) => seen.includes(ref))) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
if (!isSchemaObject(node)) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
const chain = [...seen, ...refs];
|
|
24
|
+
if (Object.hasOwn(node, "default")) {
|
|
25
|
+
return structuredClone(node.default);
|
|
26
|
+
}
|
|
27
|
+
if (Object.hasOwn(node, "const")) {
|
|
28
|
+
return structuredClone(node.const);
|
|
29
|
+
}
|
|
30
|
+
if (Array.isArray(node.enum) && node.enum.length > 0) {
|
|
31
|
+
return structuredClone(node.enum.find((value) => value !== null) ?? null);
|
|
32
|
+
}
|
|
33
|
+
if (Array.isArray(node.allOf) && node.allOf.length > 0) {
|
|
34
|
+
const merged: Record<string, unknown> = {};
|
|
35
|
+
for (const part of node.allOf) {
|
|
36
|
+
const value = schemaSkeleton(part, root, chain);
|
|
37
|
+
if (isSchemaObject(value)) {
|
|
38
|
+
Object.assign(merged, value);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return merged;
|
|
42
|
+
}
|
|
43
|
+
for (const key of ["oneOf", "anyOf"]) {
|
|
44
|
+
const options = node[key];
|
|
45
|
+
if (Array.isArray(options) && options.length > 0) {
|
|
46
|
+
const option =
|
|
47
|
+
options.find((candidate) => {
|
|
48
|
+
const target = deref(candidate, root).schema;
|
|
49
|
+
return !(
|
|
50
|
+
isSchemaObject(target) &&
|
|
51
|
+
declaredTypes(target).length === 1 &&
|
|
52
|
+
declaredTypes(target)[0] === "null"
|
|
53
|
+
);
|
|
54
|
+
}) ?? options[0];
|
|
55
|
+
return schemaSkeleton(option, root, chain);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const types = declaredTypes(node).filter((type) => type !== "null");
|
|
59
|
+
const type =
|
|
60
|
+
types[0] ??
|
|
61
|
+
(isSchemaObject(node.properties)
|
|
62
|
+
? "object"
|
|
63
|
+
: node.items
|
|
64
|
+
? "array"
|
|
65
|
+
: undefined);
|
|
66
|
+
switch (type) {
|
|
67
|
+
case "object": {
|
|
68
|
+
const value: Record<string, unknown> = {};
|
|
69
|
+
const properties = isSchemaObject(node.properties) ? node.properties : {};
|
|
70
|
+
const required = Array.isArray(node.required) ? node.required : [];
|
|
71
|
+
for (const name of required) {
|
|
72
|
+
if (typeof name === "string") {
|
|
73
|
+
value[name] = schemaSkeleton(properties[name], root, chain);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return value;
|
|
77
|
+
}
|
|
78
|
+
case "array": {
|
|
79
|
+
const min = typeof node.minItems === "number" ? node.minItems : 0;
|
|
80
|
+
return min > 0 ? [schemaSkeleton(node.items, root, chain)] : [];
|
|
81
|
+
}
|
|
82
|
+
case "string":
|
|
83
|
+
return "";
|
|
84
|
+
case "integer":
|
|
85
|
+
case "number":
|
|
86
|
+
return typeof node.minimum === "number" ? node.minimum : 0;
|
|
87
|
+
case "boolean":
|
|
88
|
+
return false;
|
|
89
|
+
default:
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}
|