radiant-docs 0.1.7 → 0.1.8
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/dist/index.js +28 -5
- package/package.json +3 -3
- package/template/astro.config.mjs +76 -3
- package/template/package-lock.json +924 -737
- package/template/package.json +7 -5
- package/template/scripts/generate-og-images.mjs +335 -0
- package/template/scripts/generate-og-metadata.mjs +173 -0
- package/template/scripts/rewrite-static-asset-host.mjs +408 -0
- package/template/scripts/stamp-image-versions.mjs +277 -0
- package/template/scripts/stamp-og-image-versions.mjs +199 -0
- package/template/scripts/stamp-pagefind-runtime-version.mjs +140 -0
- package/template/src/assets/fonts/geist-mono/cyrillic.woff2 +0 -0
- package/template/src/assets/fonts/geist-mono/latin-ext.woff2 +0 -0
- package/template/src/assets/fonts/geist-mono/latin.woff2 +0 -0
- package/template/src/assets/fonts/google-sans-flex/canadian-aboriginal.woff2 +0 -0
- package/template/src/assets/fonts/google-sans-flex/cherokee.woff2 +0 -0
- package/template/src/assets/fonts/google-sans-flex/latin-ext.woff2 +0 -0
- package/template/src/assets/fonts/google-sans-flex/latin.woff2 +0 -0
- package/template/src/assets/fonts/google-sans-flex/math.woff2 +0 -0
- package/template/src/assets/fonts/google-sans-flex/nushu.woff2 +0 -0
- package/template/src/assets/fonts/google-sans-flex/symbols.woff2 +0 -0
- package/template/src/assets/fonts/google-sans-flex/syriac.woff2 +0 -0
- package/template/src/assets/fonts/google-sans-flex/tifinagh.woff2 +0 -0
- package/template/src/assets/fonts/google-sans-flex/vietnamese.woff2 +0 -0
- package/template/src/components/Footer.astro +94 -0
- package/template/src/components/Header.astro +11 -66
- package/template/src/components/LogoLink.astro +103 -0
- package/template/src/components/MdxPage.astro +126 -11
- package/template/src/components/OpenApiPage.astro +1036 -69
- package/template/src/components/Search.astro +0 -2
- package/template/src/components/SidebarDropdown.astro +34 -14
- package/template/src/components/SidebarGroup.astro +3 -6
- package/template/src/components/SidebarLink.astro +22 -12
- package/template/src/components/SidebarMenu.astro +19 -16
- package/template/src/components/SidebarSegmented.astro +99 -0
- package/template/src/components/SidebarSubgroup.astro +12 -12
- package/template/src/components/ThemeSwitcher.astro +30 -7
- package/template/src/components/endpoint/PlaygroundBar.astro +32 -36
- package/template/src/components/endpoint/PlaygroundButton.astro +40 -4
- package/template/src/components/endpoint/PlaygroundField.astro +1068 -22
- package/template/src/components/endpoint/PlaygroundForm.astro +559 -61
- package/template/src/components/endpoint/RequestSnippets.astro +342 -193
- package/template/src/components/endpoint/ResponseDisplay.astro +161 -147
- package/template/src/components/endpoint/ResponseFieldTree.astro +134 -0
- package/template/src/components/endpoint/ResponseFields.astro +711 -68
- package/template/src/components/endpoint/ResponseSnippets.astro +299 -173
- package/template/src/components/sidebar/SidebarEndpointLink.astro +1 -1
- package/template/src/components/ui/CodeLanguageIcon.astro +19 -0
- package/template/src/components/ui/CodeTabEdge.astro +79 -0
- package/template/src/components/ui/Field.astro +103 -20
- package/template/src/components/ui/Icon.astro +32 -0
- package/template/src/components/ui/ListChevronsToggle.astro +31 -0
- package/template/src/components/ui/Tag.astro +1 -1
- package/template/src/components/user/{Accordian.astro → Accordion.astro} +6 -6
- package/template/src/components/user/Callout.astro +5 -9
- package/template/src/components/user/CodeBlock.astro +400 -0
- package/template/src/components/user/CodeGroup.astro +225 -0
- package/template/src/components/user/ComponentPreview.astro +1 -0
- package/template/src/components/user/ComponentPreviewBlock.astro +181 -0
- package/template/src/components/user/Image.astro +132 -0
- package/template/src/components/user/Steps.astro +1 -3
- package/template/src/components/user/Tabs.astro +2 -2
- package/template/src/content.config.ts +1 -0
- package/template/src/layouts/Layout.astro +109 -8
- package/template/src/lib/code/code-block.ts +546 -0
- package/template/src/lib/frontmatter-schema.ts +8 -7
- package/template/src/lib/mdx/remark-code-block-component.ts +342 -0
- package/template/src/lib/mdx/remark-demote-h1.ts +16 -0
- package/template/src/lib/pagefind.ts +19 -5
- package/template/src/lib/routes.ts +49 -31
- package/template/src/lib/utils.ts +20 -0
- package/template/src/lib/validation.ts +638 -200
- package/template/src/pages/[...slug].astro +18 -5
- package/template/src/styles/geist-mono.css +33 -0
- package/template/src/styles/global.css +89 -84
- package/template/src/styles/google-sans-flex.css +143 -0
- package/template/ec.config.mjs +0 -51
- /package/template/src/components/user/{AccordianGroup.astro → AccordionGroup.astro} +0 -0
|
@@ -7,8 +7,9 @@ import Oas from "oas";
|
|
|
7
7
|
import type { HttpMethods } from "oas/types";
|
|
8
8
|
import ResponseSnippets from "./endpoint/ResponseSnippets.astro";
|
|
9
9
|
import { renderMarkdown } from "../lib/utils";
|
|
10
|
-
import
|
|
10
|
+
import ListChevronsToggle from "./ui/ListChevronsToggle.astro";
|
|
11
11
|
import ResponseFields from "./endpoint/ResponseFields.astro";
|
|
12
|
+
import ResponseFieldTree from "./endpoint/ResponseFieldTree.astro";
|
|
12
13
|
import PlaygroundBar from "./endpoint/PlaygroundBar.astro";
|
|
13
14
|
import PlaygroundForm from "./endpoint/PlaygroundForm.astro";
|
|
14
15
|
import PlaygroundButton from "./endpoint/PlaygroundButton.astro";
|
|
@@ -28,7 +29,7 @@ const serverUrl = definition.servers?.[0]?.url;
|
|
|
28
29
|
|
|
29
30
|
const operation = api.operation(
|
|
30
31
|
route.openApiPath,
|
|
31
|
-
route.openApiMethod as HttpMethods
|
|
32
|
+
route.openApiMethod as HttpMethods,
|
|
32
33
|
);
|
|
33
34
|
|
|
34
35
|
const title = route.title;
|
|
@@ -45,12 +46,33 @@ export const headers: { [key: string]: string } = {
|
|
|
45
46
|
query: "Query Parameters",
|
|
46
47
|
body: "Body",
|
|
47
48
|
};
|
|
49
|
+
export interface FieldVariant {
|
|
50
|
+
label: string;
|
|
51
|
+
fields: Field[];
|
|
52
|
+
}
|
|
53
|
+
|
|
48
54
|
export interface Field {
|
|
49
55
|
name: string;
|
|
50
56
|
required: boolean;
|
|
51
57
|
type: string;
|
|
52
58
|
description: string;
|
|
53
|
-
enum?: string[];
|
|
59
|
+
enum?: (string | number)[];
|
|
60
|
+
minLength?: number;
|
|
61
|
+
maxLength?: number;
|
|
62
|
+
minimum?: number;
|
|
63
|
+
maximum?: number;
|
|
64
|
+
exclusiveMinimum?: number;
|
|
65
|
+
exclusiveMaximum?: number;
|
|
66
|
+
hasDefault?: boolean;
|
|
67
|
+
defaultValue?: unknown;
|
|
68
|
+
isArray?: boolean;
|
|
69
|
+
style?: string;
|
|
70
|
+
explode?: boolean;
|
|
71
|
+
nested?: Field[];
|
|
72
|
+
variants?: FieldVariant[];
|
|
73
|
+
variantType?: "oneOf" | "anyOf";
|
|
74
|
+
isAdditionalProperty?: boolean;
|
|
75
|
+
mapKnownKeys?: string[];
|
|
54
76
|
}
|
|
55
77
|
|
|
56
78
|
export interface RequestFields {
|
|
@@ -61,6 +83,13 @@ export interface RequestFields {
|
|
|
61
83
|
body: Field[];
|
|
62
84
|
}
|
|
63
85
|
|
|
86
|
+
export interface RequestSectionVariantData {
|
|
87
|
+
variants: FieldVariant[];
|
|
88
|
+
variantType: "oneOf" | "anyOf";
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
type BodyDefaultKind = "object" | "array";
|
|
92
|
+
|
|
64
93
|
const requestFields: RequestFields = {
|
|
65
94
|
header: [] as Field[],
|
|
66
95
|
cookie: [] as Field[],
|
|
@@ -68,6 +97,647 @@ const requestFields: RequestFields = {
|
|
|
68
97
|
query: [] as Field[],
|
|
69
98
|
body: [] as Field[],
|
|
70
99
|
};
|
|
100
|
+
const requestSectionVariants: Partial<
|
|
101
|
+
Record<keyof RequestFields, RequestSectionVariantData>
|
|
102
|
+
> = {};
|
|
103
|
+
let bodyDescription = "";
|
|
104
|
+
let bodyDefaultKind: BodyDefaultKind | undefined = undefined;
|
|
105
|
+
|
|
106
|
+
function getSchemaVariants(schema: any): any[] {
|
|
107
|
+
if (!schema) return [];
|
|
108
|
+
if (Array.isArray(schema.oneOf)) return schema.oneOf;
|
|
109
|
+
if (Array.isArray(schema.anyOf)) return schema.anyOf;
|
|
110
|
+
return [];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function getSchemaVariantType(schema: any): "oneOf" | "anyOf" | undefined {
|
|
114
|
+
if (Array.isArray(schema?.oneOf) && schema.oneOf.length > 0) return "oneOf";
|
|
115
|
+
if (Array.isArray(schema?.anyOf) && schema.anyOf.length > 0) return "anyOf";
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function getAdditionalPropertiesSchema(schema: any): any {
|
|
120
|
+
if (!schema || typeof schema !== "object") return undefined;
|
|
121
|
+
|
|
122
|
+
let additionalProperties = schema.additionalProperties;
|
|
123
|
+
if (Array.isArray(schema.allOf)) {
|
|
124
|
+
schema.allOf.forEach((part: any) => {
|
|
125
|
+
const partAdditional = getAdditionalPropertiesSchema(part);
|
|
126
|
+
if (partAdditional !== undefined) {
|
|
127
|
+
additionalProperties = partAdditional;
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return additionalProperties;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function getSchemaEnumValues(schema: any): (string | number)[] | undefined {
|
|
136
|
+
if (!schema) return undefined;
|
|
137
|
+
if (Array.isArray(schema.enum) && schema.enum.length > 0) {
|
|
138
|
+
const values = schema.enum.filter(
|
|
139
|
+
(value: unknown): value is string | number =>
|
|
140
|
+
typeof value === "string" || typeof value === "number",
|
|
141
|
+
);
|
|
142
|
+
return values.length > 0 ? values : undefined;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const variants = getSchemaVariants(schema);
|
|
146
|
+
if (variants.length === 0) return undefined;
|
|
147
|
+
|
|
148
|
+
const deduped = new Map<string, string | number>();
|
|
149
|
+
variants.forEach((variant) => {
|
|
150
|
+
const variantEnum = getSchemaEnumValues(variant);
|
|
151
|
+
variantEnum?.forEach((value) => {
|
|
152
|
+
deduped.set(`${typeof value}:${String(value)}`, value);
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
return deduped.size > 0 ? Array.from(deduped.values()) : undefined;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function parseFiniteNumber(value: unknown): number | undefined {
|
|
160
|
+
return typeof value === "number" && Number.isFinite(value)
|
|
161
|
+
? value
|
|
162
|
+
: undefined;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function parseNonNegativeInteger(value: unknown): number | undefined {
|
|
166
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return undefined;
|
|
167
|
+
if (!Number.isInteger(value) || value < 0) return undefined;
|
|
168
|
+
return value;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function getDirectSchemaNumericConstraints(schema: any): {
|
|
172
|
+
minimum?: number;
|
|
173
|
+
maximum?: number;
|
|
174
|
+
exclusiveMinimum?: number;
|
|
175
|
+
exclusiveMaximum?: number;
|
|
176
|
+
} {
|
|
177
|
+
const minimum = parseFiniteNumber(schema?.minimum);
|
|
178
|
+
const maximum = parseFiniteNumber(schema?.maximum);
|
|
179
|
+
const exclusiveMinimumValue = parseFiniteNumber(schema?.exclusiveMinimum);
|
|
180
|
+
const exclusiveMaximumValue = parseFiniteNumber(schema?.exclusiveMaximum);
|
|
181
|
+
|
|
182
|
+
const exclusiveMinimum =
|
|
183
|
+
exclusiveMinimumValue !== undefined
|
|
184
|
+
? exclusiveMinimumValue
|
|
185
|
+
: schema?.exclusiveMinimum === true
|
|
186
|
+
? minimum
|
|
187
|
+
: undefined;
|
|
188
|
+
const exclusiveMaximum =
|
|
189
|
+
exclusiveMaximumValue !== undefined
|
|
190
|
+
? exclusiveMaximumValue
|
|
191
|
+
: schema?.exclusiveMaximum === true
|
|
192
|
+
? maximum
|
|
193
|
+
: undefined;
|
|
194
|
+
|
|
195
|
+
const constraints: {
|
|
196
|
+
minimum?: number;
|
|
197
|
+
maximum?: number;
|
|
198
|
+
exclusiveMinimum?: number;
|
|
199
|
+
exclusiveMaximum?: number;
|
|
200
|
+
} = {};
|
|
201
|
+
|
|
202
|
+
if (exclusiveMinimum !== undefined) {
|
|
203
|
+
constraints.exclusiveMinimum = exclusiveMinimum;
|
|
204
|
+
} else if (minimum !== undefined) {
|
|
205
|
+
constraints.minimum = minimum;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (exclusiveMaximum !== undefined) {
|
|
209
|
+
constraints.exclusiveMaximum = exclusiveMaximum;
|
|
210
|
+
} else if (maximum !== undefined) {
|
|
211
|
+
constraints.maximum = maximum;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return constraints;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function mergeConjunctiveNumericConstraints(
|
|
218
|
+
base: {
|
|
219
|
+
minimum?: number;
|
|
220
|
+
maximum?: number;
|
|
221
|
+
exclusiveMinimum?: number;
|
|
222
|
+
exclusiveMaximum?: number;
|
|
223
|
+
},
|
|
224
|
+
next: {
|
|
225
|
+
minimum?: number;
|
|
226
|
+
maximum?: number;
|
|
227
|
+
exclusiveMinimum?: number;
|
|
228
|
+
exclusiveMaximum?: number;
|
|
229
|
+
},
|
|
230
|
+
): {
|
|
231
|
+
minimum?: number;
|
|
232
|
+
maximum?: number;
|
|
233
|
+
exclusiveMinimum?: number;
|
|
234
|
+
exclusiveMaximum?: number;
|
|
235
|
+
} {
|
|
236
|
+
const toLowerBound = (constraint: typeof base) => {
|
|
237
|
+
if (constraint.exclusiveMinimum !== undefined) {
|
|
238
|
+
return { value: constraint.exclusiveMinimum, exclusive: true };
|
|
239
|
+
}
|
|
240
|
+
if (constraint.minimum !== undefined) {
|
|
241
|
+
return { value: constraint.minimum, exclusive: false };
|
|
242
|
+
}
|
|
243
|
+
return undefined;
|
|
244
|
+
};
|
|
245
|
+
const toUpperBound = (constraint: typeof base) => {
|
|
246
|
+
if (constraint.exclusiveMaximum !== undefined) {
|
|
247
|
+
return { value: constraint.exclusiveMaximum, exclusive: true };
|
|
248
|
+
}
|
|
249
|
+
if (constraint.maximum !== undefined) {
|
|
250
|
+
return { value: constraint.maximum, exclusive: false };
|
|
251
|
+
}
|
|
252
|
+
return undefined;
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
const pickStricterLower = (
|
|
256
|
+
first?: { value: number; exclusive: boolean },
|
|
257
|
+
second?: { value: number; exclusive: boolean },
|
|
258
|
+
) => {
|
|
259
|
+
if (!first) return second;
|
|
260
|
+
if (!second) return first;
|
|
261
|
+
if (first.value > second.value) return first;
|
|
262
|
+
if (second.value > first.value) return second;
|
|
263
|
+
return {
|
|
264
|
+
value: first.value,
|
|
265
|
+
exclusive: first.exclusive || second.exclusive,
|
|
266
|
+
};
|
|
267
|
+
};
|
|
268
|
+
const pickStricterUpper = (
|
|
269
|
+
first?: { value: number; exclusive: boolean },
|
|
270
|
+
second?: { value: number; exclusive: boolean },
|
|
271
|
+
) => {
|
|
272
|
+
if (!first) return second;
|
|
273
|
+
if (!second) return first;
|
|
274
|
+
if (first.value < second.value) return first;
|
|
275
|
+
if (second.value < first.value) return second;
|
|
276
|
+
return {
|
|
277
|
+
value: first.value,
|
|
278
|
+
exclusive: first.exclusive || second.exclusive,
|
|
279
|
+
};
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
const lower = pickStricterLower(toLowerBound(base), toLowerBound(next));
|
|
283
|
+
const upper = pickStricterUpper(toUpperBound(base), toUpperBound(next));
|
|
284
|
+
|
|
285
|
+
const merged: {
|
|
286
|
+
minimum?: number;
|
|
287
|
+
maximum?: number;
|
|
288
|
+
exclusiveMinimum?: number;
|
|
289
|
+
exclusiveMaximum?: number;
|
|
290
|
+
} = {};
|
|
291
|
+
if (lower) {
|
|
292
|
+
if (lower.exclusive) merged.exclusiveMinimum = lower.value;
|
|
293
|
+
else merged.minimum = lower.value;
|
|
294
|
+
}
|
|
295
|
+
if (upper) {
|
|
296
|
+
if (upper.exclusive) merged.exclusiveMaximum = upper.value;
|
|
297
|
+
else merged.maximum = upper.value;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return merged;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function getSchemaNumericConstraints(
|
|
304
|
+
schema: any,
|
|
305
|
+
seen = new Set<any>(),
|
|
306
|
+
): {
|
|
307
|
+
minimum?: number;
|
|
308
|
+
maximum?: number;
|
|
309
|
+
exclusiveMinimum?: number;
|
|
310
|
+
exclusiveMaximum?: number;
|
|
311
|
+
} {
|
|
312
|
+
if (!schema || typeof schema !== "object") return {};
|
|
313
|
+
if (seen.has(schema)) return {};
|
|
314
|
+
|
|
315
|
+
const nextSeen = new Set(seen);
|
|
316
|
+
nextSeen.add(schema);
|
|
317
|
+
|
|
318
|
+
let constraints = getDirectSchemaNumericConstraints(schema);
|
|
319
|
+
|
|
320
|
+
if (Array.isArray(schema.allOf)) {
|
|
321
|
+
schema.allOf.forEach((part: any) => {
|
|
322
|
+
constraints = mergeConjunctiveNumericConstraints(
|
|
323
|
+
constraints,
|
|
324
|
+
getSchemaNumericConstraints(part, nextSeen),
|
|
325
|
+
);
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const variants = getSchemaVariants(schema);
|
|
330
|
+
if (variants.length > 0) {
|
|
331
|
+
const variantConstraints = variants.map((variant) =>
|
|
332
|
+
getSchemaNumericConstraints(variant, nextSeen),
|
|
333
|
+
);
|
|
334
|
+
const shared: {
|
|
335
|
+
minimum?: number;
|
|
336
|
+
maximum?: number;
|
|
337
|
+
exclusiveMinimum?: number;
|
|
338
|
+
exclusiveMaximum?: number;
|
|
339
|
+
} = {};
|
|
340
|
+
const keys = [
|
|
341
|
+
"minimum",
|
|
342
|
+
"maximum",
|
|
343
|
+
"exclusiveMinimum",
|
|
344
|
+
"exclusiveMaximum",
|
|
345
|
+
] as const;
|
|
346
|
+
|
|
347
|
+
keys.forEach((key) => {
|
|
348
|
+
const firstValue = variantConstraints[0]?.[key];
|
|
349
|
+
if (firstValue === undefined) return;
|
|
350
|
+
const allMatch = variantConstraints.every(
|
|
351
|
+
(variantConstraint) => variantConstraint[key] === firstValue,
|
|
352
|
+
);
|
|
353
|
+
if (allMatch) {
|
|
354
|
+
shared[key] = firstValue;
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
constraints = mergeConjunctiveNumericConstraints(constraints, shared);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
return constraints;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function getDirectSchemaStringLengthConstraints(schema: any): {
|
|
365
|
+
minLength?: number;
|
|
366
|
+
maxLength?: number;
|
|
367
|
+
} {
|
|
368
|
+
const minLength = parseNonNegativeInteger(schema?.minLength);
|
|
369
|
+
const maxLength = parseNonNegativeInteger(schema?.maxLength);
|
|
370
|
+
|
|
371
|
+
const constraints: {
|
|
372
|
+
minLength?: number;
|
|
373
|
+
maxLength?: number;
|
|
374
|
+
} = {};
|
|
375
|
+
if (minLength !== undefined) constraints.minLength = minLength;
|
|
376
|
+
if (maxLength !== undefined) constraints.maxLength = maxLength;
|
|
377
|
+
|
|
378
|
+
return constraints;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function mergeConjunctiveStringLengthConstraints(
|
|
382
|
+
base: {
|
|
383
|
+
minLength?: number;
|
|
384
|
+
maxLength?: number;
|
|
385
|
+
},
|
|
386
|
+
next: {
|
|
387
|
+
minLength?: number;
|
|
388
|
+
maxLength?: number;
|
|
389
|
+
},
|
|
390
|
+
): {
|
|
391
|
+
minLength?: number;
|
|
392
|
+
maxLength?: number;
|
|
393
|
+
} {
|
|
394
|
+
const merged: {
|
|
395
|
+
minLength?: number;
|
|
396
|
+
maxLength?: number;
|
|
397
|
+
} = {};
|
|
398
|
+
|
|
399
|
+
const minLengthCandidates = [base.minLength, next.minLength].filter(
|
|
400
|
+
(value): value is number => value !== undefined,
|
|
401
|
+
);
|
|
402
|
+
const maxLengthCandidates = [base.maxLength, next.maxLength].filter(
|
|
403
|
+
(value): value is number => value !== undefined,
|
|
404
|
+
);
|
|
405
|
+
|
|
406
|
+
if (minLengthCandidates.length > 0) {
|
|
407
|
+
merged.minLength = Math.max(...minLengthCandidates);
|
|
408
|
+
}
|
|
409
|
+
if (maxLengthCandidates.length > 0) {
|
|
410
|
+
merged.maxLength = Math.min(...maxLengthCandidates);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
return merged;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function getSchemaStringLengthConstraints(
|
|
417
|
+
schema: any,
|
|
418
|
+
seen = new Set<any>(),
|
|
419
|
+
): {
|
|
420
|
+
minLength?: number;
|
|
421
|
+
maxLength?: number;
|
|
422
|
+
} {
|
|
423
|
+
if (!schema || typeof schema !== "object") return {};
|
|
424
|
+
if (seen.has(schema)) return {};
|
|
425
|
+
|
|
426
|
+
const nextSeen = new Set(seen);
|
|
427
|
+
nextSeen.add(schema);
|
|
428
|
+
|
|
429
|
+
let constraints = getDirectSchemaStringLengthConstraints(schema);
|
|
430
|
+
|
|
431
|
+
if (Array.isArray(schema.allOf)) {
|
|
432
|
+
schema.allOf.forEach((part: any) => {
|
|
433
|
+
constraints = mergeConjunctiveStringLengthConstraints(
|
|
434
|
+
constraints,
|
|
435
|
+
getSchemaStringLengthConstraints(part, nextSeen),
|
|
436
|
+
);
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
const variants = getSchemaVariants(schema);
|
|
441
|
+
if (variants.length > 0) {
|
|
442
|
+
const variantConstraints = variants.map((variant) =>
|
|
443
|
+
getSchemaStringLengthConstraints(variant, nextSeen),
|
|
444
|
+
);
|
|
445
|
+
const shared: {
|
|
446
|
+
minLength?: number;
|
|
447
|
+
maxLength?: number;
|
|
448
|
+
} = {};
|
|
449
|
+
|
|
450
|
+
(["minLength", "maxLength"] as const).forEach((key) => {
|
|
451
|
+
const firstValue = variantConstraints[0]?.[key];
|
|
452
|
+
if (firstValue === undefined) return;
|
|
453
|
+
const allMatch = variantConstraints.every(
|
|
454
|
+
(variantConstraint) => variantConstraint[key] === firstValue,
|
|
455
|
+
);
|
|
456
|
+
if (allMatch) {
|
|
457
|
+
shared[key] = firstValue;
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
constraints = mergeConjunctiveStringLengthConstraints(constraints, shared);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
return constraints;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
function getSchemaTypeLabel(schema: any): string {
|
|
468
|
+
if (!schema) return "unknown";
|
|
469
|
+
|
|
470
|
+
const variants = getSchemaVariants(schema);
|
|
471
|
+
if (variants.length > 0) {
|
|
472
|
+
const labels = Array.from(
|
|
473
|
+
new Set(
|
|
474
|
+
variants
|
|
475
|
+
.map((variant) => getSchemaTypeLabel(variant))
|
|
476
|
+
.filter((label) => label.length > 0),
|
|
477
|
+
),
|
|
478
|
+
);
|
|
479
|
+
if (labels.length === 0) return "unknown";
|
|
480
|
+
return labels.length === 1 ? labels[0] : labels.join(" | ");
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
if (schema.type === "array") {
|
|
484
|
+
const itemSchema = schema.items;
|
|
485
|
+
if (!itemSchema) return "unknown[]";
|
|
486
|
+
const itemType = getSchemaTypeLabel(itemSchema);
|
|
487
|
+
if (itemType.includes(" | ")) return `(${itemType})[]`;
|
|
488
|
+
return `${itemType}[]`;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
if (Array.isArray(schema.enum) && schema.enum.length > 0) {
|
|
492
|
+
return `enum<${schema.type || "string"}>`;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
if (schema.format && schema.type) {
|
|
496
|
+
return `${schema.type} (${schema.format})`;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
if (schema.type) return schema.type;
|
|
500
|
+
if (schema.properties || schema.allOf || schema.oneOf || schema.anyOf) {
|
|
501
|
+
return "object";
|
|
502
|
+
}
|
|
503
|
+
return "unknown";
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
function getSchemaStructuralType(schema: any): BodyDefaultKind | undefined {
|
|
507
|
+
if (!schema || typeof schema !== "object") return undefined;
|
|
508
|
+
|
|
509
|
+
if (schema.type === "object") return "object";
|
|
510
|
+
if (schema.type === "array") return "array";
|
|
511
|
+
|
|
512
|
+
const variants = getSchemaVariants(schema);
|
|
513
|
+
if (variants.length > 0) {
|
|
514
|
+
const variantTypes = Array.from(
|
|
515
|
+
new Set(
|
|
516
|
+
variants
|
|
517
|
+
.map((variant) => getSchemaStructuralType(variant))
|
|
518
|
+
.filter(
|
|
519
|
+
(variantType): variantType is BodyDefaultKind =>
|
|
520
|
+
variantType === "object" || variantType === "array",
|
|
521
|
+
),
|
|
522
|
+
),
|
|
523
|
+
);
|
|
524
|
+
if (variantTypes.length === 1) return variantTypes[0];
|
|
525
|
+
return undefined;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
if (
|
|
529
|
+
(schema.properties && typeof schema.properties === "object") ||
|
|
530
|
+
schema.additionalProperties !== undefined
|
|
531
|
+
) {
|
|
532
|
+
return "object";
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
if (schema.items) return "array";
|
|
536
|
+
|
|
537
|
+
if (Array.isArray(schema.allOf) && schema.allOf.length > 0) {
|
|
538
|
+
const allOfTypes = Array.from(
|
|
539
|
+
new Set(
|
|
540
|
+
schema.allOf
|
|
541
|
+
.map((part: any) => getSchemaStructuralType(part))
|
|
542
|
+
.filter(
|
|
543
|
+
(partType): partType is BodyDefaultKind =>
|
|
544
|
+
partType === "object" || partType === "array",
|
|
545
|
+
),
|
|
546
|
+
),
|
|
547
|
+
);
|
|
548
|
+
if (allOfTypes.length === 1) return allOfTypes[0];
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
return undefined;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
function getTopLevelObjectShape(schema: any): {
|
|
555
|
+
properties: Record<string, any>;
|
|
556
|
+
required: Set<string>;
|
|
557
|
+
hasObjectShape: boolean;
|
|
558
|
+
} {
|
|
559
|
+
if (!schema) {
|
|
560
|
+
return {
|
|
561
|
+
properties: {},
|
|
562
|
+
required: new Set<string>(),
|
|
563
|
+
hasObjectShape: false,
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
let properties: Record<string, any> = {};
|
|
568
|
+
const required = new Set<string>(
|
|
569
|
+
Array.isArray(schema.required) ? schema.required : [],
|
|
570
|
+
);
|
|
571
|
+
let hasObjectShape = false;
|
|
572
|
+
|
|
573
|
+
if (schema.properties && typeof schema.properties === "object") {
|
|
574
|
+
properties = { ...properties, ...schema.properties };
|
|
575
|
+
hasObjectShape = true;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
if (Array.isArray(schema.allOf)) {
|
|
579
|
+
schema.allOf.forEach((part: any) => {
|
|
580
|
+
const partShape = getTopLevelObjectShape(part);
|
|
581
|
+
if (!partShape.hasObjectShape) return;
|
|
582
|
+
|
|
583
|
+
properties = { ...properties, ...partShape.properties };
|
|
584
|
+
partShape.required.forEach((name) => required.add(name));
|
|
585
|
+
hasObjectShape = true;
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
const variants = getSchemaVariants(schema);
|
|
590
|
+
if (variants.length > 0) {
|
|
591
|
+
const variantShapes = variants
|
|
592
|
+
.map((variant) => getTopLevelObjectShape(variant))
|
|
593
|
+
.filter((shape) => shape.hasObjectShape);
|
|
594
|
+
|
|
595
|
+
if (variantShapes.length > 0) {
|
|
596
|
+
variantShapes.forEach((shape) => {
|
|
597
|
+
properties = { ...properties, ...shape.properties };
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
const [firstShape, ...restShapes] = variantShapes;
|
|
601
|
+
const requiredAcrossVariants = new Set<string>(firstShape.required);
|
|
602
|
+
restShapes.forEach((shape) => {
|
|
603
|
+
Array.from(requiredAcrossVariants).forEach((name) => {
|
|
604
|
+
if (!shape.required.has(name)) {
|
|
605
|
+
requiredAcrossVariants.delete(name);
|
|
606
|
+
}
|
|
607
|
+
});
|
|
608
|
+
});
|
|
609
|
+
requiredAcrossVariants.forEach((name) => required.add(name));
|
|
610
|
+
hasObjectShape = true;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
return { properties, required, hasObjectShape };
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
function extractRequestSchemaContent(
|
|
618
|
+
schema: any,
|
|
619
|
+
seen = new Set<any>(),
|
|
620
|
+
): {
|
|
621
|
+
fields: Field[];
|
|
622
|
+
variants?: FieldVariant[];
|
|
623
|
+
variantType?: "oneOf" | "anyOf";
|
|
624
|
+
} {
|
|
625
|
+
if (!schema || typeof schema !== "object") return { fields: [] };
|
|
626
|
+
if (seen.has(schema)) return { fields: [] };
|
|
627
|
+
|
|
628
|
+
const nextSeen = new Set(seen);
|
|
629
|
+
nextSeen.add(schema);
|
|
630
|
+
|
|
631
|
+
if (schema.type === "array" && schema.items) {
|
|
632
|
+
return extractRequestSchemaContent(schema.items, nextSeen);
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
const baseSchema = { ...schema };
|
|
636
|
+
delete baseSchema.oneOf;
|
|
637
|
+
delete baseSchema.anyOf;
|
|
638
|
+
|
|
639
|
+
const shape = getTopLevelObjectShape(baseSchema);
|
|
640
|
+
|
|
641
|
+
const fields: Field[] = Object.entries(shape.properties)
|
|
642
|
+
.sort(([nameA], [nameB]) => {
|
|
643
|
+
const aRequired = shape.required.has(nameA);
|
|
644
|
+
const bRequired = shape.required.has(nameB);
|
|
645
|
+
if (aRequired && !bRequired) return -1;
|
|
646
|
+
if (!aRequired && bRequired) return 1;
|
|
647
|
+
return 0;
|
|
648
|
+
})
|
|
649
|
+
.map(([name, propertySchema]: [string, any]) => {
|
|
650
|
+
const nestedContent = extractRequestSchemaContent(
|
|
651
|
+
propertySchema,
|
|
652
|
+
nextSeen,
|
|
653
|
+
);
|
|
654
|
+
return {
|
|
655
|
+
name,
|
|
656
|
+
required: shape.required.has(name),
|
|
657
|
+
type: getSchemaTypeLabel(propertySchema),
|
|
658
|
+
description: propertySchema?.description || "",
|
|
659
|
+
enum: getSchemaEnumValues(propertySchema),
|
|
660
|
+
...getSchemaStringLengthConstraints(propertySchema, nextSeen),
|
|
661
|
+
...getSchemaNumericConstraints(propertySchema, nextSeen),
|
|
662
|
+
hasDefault: Object.prototype.hasOwnProperty.call(
|
|
663
|
+
propertySchema || {},
|
|
664
|
+
"default",
|
|
665
|
+
),
|
|
666
|
+
defaultValue: propertySchema?.default,
|
|
667
|
+
isArray: propertySchema?.type === "array",
|
|
668
|
+
nested:
|
|
669
|
+
nestedContent.fields.length > 0 ? nestedContent.fields : undefined,
|
|
670
|
+
variants: nestedContent.variants,
|
|
671
|
+
variantType: nestedContent.variantType,
|
|
672
|
+
};
|
|
673
|
+
});
|
|
674
|
+
|
|
675
|
+
const additionalProperties = getAdditionalPropertiesSchema(baseSchema);
|
|
676
|
+
const supportsAdditionalProperties =
|
|
677
|
+
additionalProperties === true ||
|
|
678
|
+
(additionalProperties &&
|
|
679
|
+
typeof additionalProperties === "object" &&
|
|
680
|
+
!Array.isArray(additionalProperties));
|
|
681
|
+
|
|
682
|
+
if (supportsAdditionalProperties) {
|
|
683
|
+
const additionalSchema =
|
|
684
|
+
additionalProperties && typeof additionalProperties === "object"
|
|
685
|
+
? additionalProperties
|
|
686
|
+
: null;
|
|
687
|
+
const additionalContent = additionalSchema
|
|
688
|
+
? extractRequestSchemaContent(additionalSchema, nextSeen)
|
|
689
|
+
: { fields: [] as Field[] };
|
|
690
|
+
|
|
691
|
+
fields.push({
|
|
692
|
+
name: "[key: string]",
|
|
693
|
+
required: false,
|
|
694
|
+
type: additionalSchema ? getSchemaTypeLabel(additionalSchema) : "any",
|
|
695
|
+
description: additionalSchema?.description || "",
|
|
696
|
+
enum: additionalSchema
|
|
697
|
+
? getSchemaEnumValues(additionalSchema)
|
|
698
|
+
: undefined,
|
|
699
|
+
...getSchemaStringLengthConstraints(additionalSchema, nextSeen),
|
|
700
|
+
...getSchemaNumericConstraints(additionalSchema, nextSeen),
|
|
701
|
+
hasDefault: Object.prototype.hasOwnProperty.call(
|
|
702
|
+
additionalSchema || {},
|
|
703
|
+
"default",
|
|
704
|
+
),
|
|
705
|
+
defaultValue: additionalSchema?.default,
|
|
706
|
+
isArray: additionalSchema?.type === "array",
|
|
707
|
+
nested:
|
|
708
|
+
additionalContent.fields.length > 0
|
|
709
|
+
? additionalContent.fields
|
|
710
|
+
: undefined,
|
|
711
|
+
variants: additionalContent.variants,
|
|
712
|
+
variantType: additionalContent.variantType,
|
|
713
|
+
isAdditionalProperty: true,
|
|
714
|
+
mapKnownKeys: Object.keys(shape.properties),
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
const variantType = getSchemaVariantType(schema);
|
|
719
|
+
const variantSchemas =
|
|
720
|
+
variantType === "oneOf"
|
|
721
|
+
? schema.oneOf || []
|
|
722
|
+
: variantType === "anyOf"
|
|
723
|
+
? schema.anyOf || []
|
|
724
|
+
: [];
|
|
725
|
+
const variants =
|
|
726
|
+
variantType && variantSchemas.length > 0
|
|
727
|
+
? variantSchemas
|
|
728
|
+
.map((variantSchema: any, index: number) => ({
|
|
729
|
+
label: `Variant ${index + 1}`,
|
|
730
|
+
fields: extractRequestSchemaContent(variantSchema, nextSeen).fields,
|
|
731
|
+
}))
|
|
732
|
+
.filter((variant: FieldVariant) => variant.fields.length > 0)
|
|
733
|
+
: [];
|
|
734
|
+
|
|
735
|
+
return {
|
|
736
|
+
fields,
|
|
737
|
+
variants: variants.length > 0 ? variants : undefined,
|
|
738
|
+
variantType: variants.length > 0 ? variantType : undefined,
|
|
739
|
+
};
|
|
740
|
+
}
|
|
71
741
|
|
|
72
742
|
// Authorization
|
|
73
743
|
const securityRequirements = operation.getSecurity();
|
|
@@ -130,82 +800,90 @@ if (securityRequirements && securityRequirements.length > 0) {
|
|
|
130
800
|
});
|
|
131
801
|
}
|
|
132
802
|
|
|
133
|
-
// --- Regular Parameters (Path
|
|
803
|
+
// --- Regular Parameters (Path, Query, Header, Cookie) ---
|
|
134
804
|
const parameters = operation.getParameters();
|
|
135
805
|
parameters.forEach((param) => {
|
|
136
806
|
const schema = param.schema as any;
|
|
807
|
+
const isArray = schema?.type === "array";
|
|
808
|
+
const schemaContent = extractRequestSchemaContent(schema);
|
|
137
809
|
|
|
138
810
|
const field = {
|
|
139
811
|
name: param.name,
|
|
140
812
|
required: param.required || false,
|
|
141
|
-
type: schema
|
|
142
|
-
? `enum<${schema?.type || "string"}>`
|
|
143
|
-
: schema?.type || "string",
|
|
813
|
+
type: getSchemaTypeLabel(schema),
|
|
144
814
|
description: param.description || "",
|
|
145
|
-
enum: schema
|
|
815
|
+
enum: getSchemaEnumValues(schema),
|
|
816
|
+
...getSchemaStringLengthConstraints(schema),
|
|
817
|
+
...getSchemaNumericConstraints(schema),
|
|
818
|
+
hasDefault: Object.prototype.hasOwnProperty.call(schema || {}, "default"),
|
|
819
|
+
defaultValue: schema?.default,
|
|
820
|
+
isArray,
|
|
821
|
+
style: param.style,
|
|
822
|
+
explode: param.explode,
|
|
823
|
+
nested: schemaContent.fields.length > 0 ? schemaContent.fields : undefined,
|
|
824
|
+
variants: schemaContent.variants,
|
|
825
|
+
variantType: schemaContent.variantType,
|
|
146
826
|
};
|
|
147
827
|
|
|
148
828
|
if (param.in === "path") requestFields.path.push(field);
|
|
149
829
|
if (param.in === "query") requestFields.query.push(field);
|
|
150
830
|
if (param.in === "header") requestFields.header.push(field);
|
|
831
|
+
if (param.in === "cookie") requestFields.cookie.push(field);
|
|
151
832
|
});
|
|
152
833
|
|
|
153
834
|
// --- 3. Body Parameters ---
|
|
154
835
|
if (operation.hasRequestBody()) {
|
|
155
836
|
const requestBody = operation.getRequestBody("application/json");
|
|
156
837
|
const bodySchema = (requestBody as any)?.schema as any;
|
|
838
|
+
bodyDescription =
|
|
839
|
+
(requestBody as any)?.description || bodySchema?.description || "";
|
|
840
|
+
bodyDefaultKind = getSchemaStructuralType(bodySchema);
|
|
841
|
+
const bodyContent = extractRequestSchemaContent(bodySchema);
|
|
842
|
+
requestFields.body.push(...bodyContent.fields);
|
|
157
843
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
bodySchema.allOf.forEach((s: any) => {
|
|
164
|
-
if (s.properties) properties = { ...properties, ...s.properties };
|
|
165
|
-
if (s.required && Array.isArray(s.required)) {
|
|
166
|
-
// Merge required arrays from allOf schemas
|
|
167
|
-
required = [...new Set([...required, ...s.required])];
|
|
168
|
-
}
|
|
169
|
-
});
|
|
844
|
+
if (bodyContent.variants && bodyContent.variantType) {
|
|
845
|
+
requestSectionVariants.body = {
|
|
846
|
+
variants: bodyContent.variants,
|
|
847
|
+
variantType: bodyContent.variantType,
|
|
848
|
+
};
|
|
170
849
|
}
|
|
171
|
-
|
|
172
|
-
Object.entries(properties)
|
|
173
|
-
.sort(([nameA], [nameB]) => {
|
|
174
|
-
const aRequired = required.includes(nameA);
|
|
175
|
-
const bRequired = required.includes(nameB);
|
|
176
|
-
// Required fields first (aRequired && !bRequired = -1, !aRequired && bRequired = 1, else 0)
|
|
177
|
-
if (aRequired && !bRequired) return -1;
|
|
178
|
-
if (!aRequired && bRequired) return 1;
|
|
179
|
-
return 0;
|
|
180
|
-
})
|
|
181
|
-
.forEach(([name, schema]: [string, any]) => {
|
|
182
|
-
requestFields.body.push({
|
|
183
|
-
name: name,
|
|
184
|
-
required: required.includes(name) || false,
|
|
185
|
-
type: schema?.enum
|
|
186
|
-
? `enum<${schema?.type || "string"}>`
|
|
187
|
-
: schema?.type || "string",
|
|
188
|
-
description: schema.description || "",
|
|
189
|
-
enum: schema?.enum,
|
|
190
|
-
});
|
|
191
|
-
});
|
|
192
850
|
}
|
|
851
|
+
|
|
852
|
+
const formattedBodyDescription = bodyDescription
|
|
853
|
+
? await renderMarkdown(bodyDescription)
|
|
854
|
+
: null;
|
|
193
855
|
---
|
|
194
856
|
|
|
195
|
-
<Layout>
|
|
857
|
+
<Layout pageTitle={title}>
|
|
196
858
|
<article>
|
|
197
859
|
<header class="mb-6">
|
|
198
|
-
<h1 class="text-
|
|
860
|
+
<h1 class="text-4xl font-semibold tracking-tight">{title}</h1>
|
|
199
861
|
</header>
|
|
200
862
|
<div class="flex flex-row-reverse justify-between gap-6 w-full">
|
|
201
|
-
<aside class="flex-1 hidden xl:block">
|
|
202
|
-
<div
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
863
|
+
<aside class="flex-1 min-w-0 hidden xl:block">
|
|
864
|
+
<div
|
|
865
|
+
data-snippet-stack-host
|
|
866
|
+
class="sticky top-[92px] max-h-[calc(100vh-92px)] w-full min-w-0 overflow-hidden"
|
|
867
|
+
>
|
|
868
|
+
<div
|
|
869
|
+
data-snippet-stack
|
|
870
|
+
class="relative flex min-h-0 w-full min-w-0 flex-col gap-6 overflow-hidden"
|
|
871
|
+
>
|
|
872
|
+
<div data-snippet-slot class="min-h-0 min-w-0 overflow-hidden">
|
|
873
|
+
<RequestSnippets
|
|
874
|
+
api={api}
|
|
875
|
+
method={route.openApiMethod}
|
|
876
|
+
path={route.openApiPath}
|
|
877
|
+
/>
|
|
878
|
+
</div>
|
|
879
|
+
{
|
|
880
|
+
responses && (
|
|
881
|
+
<div data-snippet-slot class="min-h-0 min-w-0 overflow-hidden">
|
|
882
|
+
<ResponseSnippets responses={responses} />
|
|
883
|
+
</div>
|
|
884
|
+
)
|
|
885
|
+
}
|
|
886
|
+
</div>
|
|
209
887
|
</div>
|
|
210
888
|
</aside>
|
|
211
889
|
<div class="flex-1 min-w-0">
|
|
@@ -218,6 +896,9 @@ if (operation.hasRequestBody()) {
|
|
|
218
896
|
serverUrl={serverUrl}
|
|
219
897
|
route={route}
|
|
220
898
|
requestFields={requestFields}
|
|
899
|
+
requestSectionVariants={requestSectionVariants}
|
|
900
|
+
bodyDescription={bodyDescription}
|
|
901
|
+
bodyDefaultKind={bodyDefaultKind}
|
|
221
902
|
/>
|
|
222
903
|
</PlaygroundButton>
|
|
223
904
|
)
|
|
@@ -226,7 +907,10 @@ if (operation.hasRequestBody()) {
|
|
|
226
907
|
</div>
|
|
227
908
|
{
|
|
228
909
|
formattedDescription && (
|
|
229
|
-
<div
|
|
910
|
+
<div
|
|
911
|
+
class="mb-6 prose-rules text-neutral-500 **:text-neutral-500"
|
|
912
|
+
set:html={formattedDescription}
|
|
913
|
+
/>
|
|
230
914
|
)
|
|
231
915
|
}
|
|
232
916
|
<div class="xl:hidden space-y-6 mt-6">
|
|
@@ -240,25 +924,131 @@ if (operation.hasRequestBody()) {
|
|
|
240
924
|
<div class="mt-10">
|
|
241
925
|
<!-- Request -->
|
|
242
926
|
{
|
|
243
|
-
Object.keys(requestFields).map(
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
927
|
+
Object.keys(requestFields).map((key) => {
|
|
928
|
+
const sectionFields = requestFields[key as keyof RequestFields];
|
|
929
|
+
const sectionVariantData =
|
|
930
|
+
requestSectionVariants[key as keyof RequestFields];
|
|
931
|
+
const sectionVariants = sectionVariantData?.variants || [];
|
|
932
|
+
const sectionVariantCount = sectionVariants.reduce(
|
|
933
|
+
(sum, variant) => sum + variant.fields.length,
|
|
934
|
+
0,
|
|
935
|
+
);
|
|
936
|
+
const hasCommonFields = sectionFields.length > 0;
|
|
937
|
+
const hasVariants = sectionVariantCount > 0;
|
|
938
|
+
if (!hasCommonFields && !hasVariants) return null;
|
|
939
|
+
|
|
940
|
+
const noun =
|
|
941
|
+
key === "body"
|
|
942
|
+
? sectionFields.length === 1
|
|
943
|
+
? "field"
|
|
944
|
+
: "fields"
|
|
945
|
+
: sectionFields.length === 1
|
|
946
|
+
? "parameter"
|
|
947
|
+
: "parameters";
|
|
948
|
+
const variantNoun = key === "body" ? "field" : "parameter";
|
|
949
|
+
|
|
950
|
+
return (
|
|
951
|
+
<section class="mt-10">
|
|
952
|
+
<h4 class="text-xl font-semibold">{headers[key]}</h4>
|
|
953
|
+
{key === "body" && formattedBodyDescription && (
|
|
954
|
+
<div
|
|
955
|
+
class="mt-2 prose-rules prose-sm! text-neutral-500 **:text-neutral-500"
|
|
956
|
+
set:html={formattedBodyDescription}
|
|
957
|
+
/>
|
|
958
|
+
)}
|
|
959
|
+
{hasCommonFields && (
|
|
960
|
+
<div x-data="{ expanded: false }" class="mt-4">
|
|
961
|
+
<div
|
|
962
|
+
class="w-full overflow-hidden rounded-xl border border-neutral-200 bg-white transition-colors duration-200"
|
|
963
|
+
x-bind:class="expanded ? 'border-neutral-300' : 'border-neutral-200'"
|
|
964
|
+
>
|
|
965
|
+
<button
|
|
966
|
+
type="button"
|
|
967
|
+
x-on:click="expanded = !expanded"
|
|
968
|
+
x-bind:aria-expanded="expanded"
|
|
969
|
+
class="group flex w-full items-center justify-between gap-3 px-4 py-2.5 text-left text-sm font-medium text-neutral-700 hover:bg-neutral-50 cursor-pointer transition-colors duration-200"
|
|
970
|
+
>
|
|
971
|
+
<span class="inline-flex items-center gap-2">
|
|
972
|
+
<ListChevronsToggle class="size-4 shrink-0 text-neutral-400 group-hover:text-neutral-600 transition duration-200" />
|
|
973
|
+
<span>
|
|
974
|
+
{sectionFields.length} {noun}
|
|
975
|
+
</span>
|
|
976
|
+
</span>
|
|
977
|
+
</button>
|
|
978
|
+
<div x-show="expanded" x-collapse x-cloak>
|
|
979
|
+
<div class="border-t border-neutral-100 p-4">
|
|
980
|
+
<ResponseFieldTree fields={sectionFields} />
|
|
981
|
+
</div>
|
|
982
|
+
</div>
|
|
983
|
+
</div>
|
|
984
|
+
</div>
|
|
985
|
+
)}
|
|
986
|
+
{hasVariants && (
|
|
987
|
+
<div
|
|
988
|
+
class:list={[
|
|
989
|
+
"space-y-2",
|
|
990
|
+
hasCommonFields ? "mt-3" : "mt-4",
|
|
991
|
+
]}
|
|
992
|
+
>
|
|
993
|
+
<p class="text-xs text-neutral-500">
|
|
994
|
+
{sectionVariantData?.variantType === "anyOf"
|
|
995
|
+
? "One or more variants may apply."
|
|
996
|
+
: "One of these variants applies."}
|
|
997
|
+
</p>
|
|
998
|
+
{sectionVariants.map((variant, index) => (
|
|
999
|
+
<>
|
|
1000
|
+
<div class="rounded-lg border border-neutral-200 bg-white p-3">
|
|
1001
|
+
<div class="mb-2 text-xs font-medium text-neutral-600">
|
|
1002
|
+
{variant.label}
|
|
1003
|
+
</div>
|
|
1004
|
+
<div x-data="{ expanded: false }">
|
|
1005
|
+
<div
|
|
1006
|
+
class="w-full overflow-hidden rounded-lg border border-neutral-200 bg-white transition-colors duration-200"
|
|
1007
|
+
x-bind:class="expanded ? 'border-neutral-300' : 'border-neutral-200'"
|
|
1008
|
+
>
|
|
1009
|
+
<button
|
|
1010
|
+
type="button"
|
|
1011
|
+
x-on:click="expanded = !expanded"
|
|
1012
|
+
x-bind:aria-expanded="expanded"
|
|
1013
|
+
class="group flex w-full items-center justify-between gap-3 px-3 py-2 text-left text-xs font-medium text-neutral-700 hover:bg-neutral-50 cursor-pointer transition-colors duration-200"
|
|
1014
|
+
>
|
|
1015
|
+
<span class="inline-flex items-center gap-2">
|
|
1016
|
+
<ListChevronsToggle class="size-4 shrink-0 text-neutral-400 group-hover:text-neutral-600 transition duration-200" />
|
|
1017
|
+
<span>
|
|
1018
|
+
{variant.fields.length}{" "}
|
|
1019
|
+
{variant.fields.length === 1
|
|
1020
|
+
? variantNoun
|
|
1021
|
+
: `${variantNoun}s`}
|
|
1022
|
+
</span>
|
|
1023
|
+
</span>
|
|
1024
|
+
</button>
|
|
1025
|
+
<div x-show="expanded" x-collapse x-cloak>
|
|
1026
|
+
<div class="border-t border-neutral-100 px-3 py-3">
|
|
1027
|
+
<ResponseFieldTree
|
|
1028
|
+
fields={variant.fields}
|
|
1029
|
+
/>
|
|
1030
|
+
</div>
|
|
1031
|
+
</div>
|
|
1032
|
+
</div>
|
|
1033
|
+
</div>
|
|
1034
|
+
</div>
|
|
1035
|
+
{sectionVariantData?.variantType === "oneOf" &&
|
|
1036
|
+
index < sectionVariants.length - 1 && (
|
|
1037
|
+
<div class="flex items-center gap-2 py-0">
|
|
1038
|
+
<div class="h-px flex-1 bg-neutral-200" />
|
|
1039
|
+
<span class="px-1 text-[10px] uppercase tracking-wide text-neutral-500">
|
|
1040
|
+
OR
|
|
1041
|
+
</span>
|
|
1042
|
+
<div class="h-px flex-1 bg-neutral-200" />
|
|
1043
|
+
</div>
|
|
1044
|
+
)}
|
|
1045
|
+
</>
|
|
257
1046
|
))}
|
|
258
1047
|
</div>
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
1048
|
+
)}
|
|
1049
|
+
</section>
|
|
1050
|
+
);
|
|
1051
|
+
})
|
|
262
1052
|
}
|
|
263
1053
|
|
|
264
1054
|
<!-- Response -->
|
|
@@ -268,3 +1058,180 @@ if (operation.hasRequestBody()) {
|
|
|
268
1058
|
</div>
|
|
269
1059
|
</article>
|
|
270
1060
|
</Layout>
|
|
1061
|
+
|
|
1062
|
+
<script is:inline>
|
|
1063
|
+
const stack = document.querySelector("[data-snippet-stack]");
|
|
1064
|
+
|
|
1065
|
+
if (stack instanceof HTMLElement) {
|
|
1066
|
+
const stackHost = stack.closest("[data-snippet-stack-host]");
|
|
1067
|
+
const host =
|
|
1068
|
+
stackHost instanceof HTMLElement ? stackHost : stack.parentElement;
|
|
1069
|
+
const stickyTopPx =
|
|
1070
|
+
host instanceof HTMLElement
|
|
1071
|
+
? Number.parseFloat(window.getComputedStyle(host).top || "0") || 0
|
|
1072
|
+
: 0;
|
|
1073
|
+
|
|
1074
|
+
const getViewportHeight = () => {
|
|
1075
|
+
if (window.visualViewport?.height) {
|
|
1076
|
+
return Math.floor(window.visualViewport.height);
|
|
1077
|
+
}
|
|
1078
|
+
return Math.floor(window.innerHeight);
|
|
1079
|
+
};
|
|
1080
|
+
|
|
1081
|
+
const getViewportBudget = () => {
|
|
1082
|
+
const insetTop = Math.max(0, Math.floor(stickyTopPx));
|
|
1083
|
+
return Math.max(0, getViewportHeight() - insetTop);
|
|
1084
|
+
};
|
|
1085
|
+
|
|
1086
|
+
const slots = Array.from(
|
|
1087
|
+
stack.querySelectorAll("[data-snippet-slot]"),
|
|
1088
|
+
).filter((slot) => slot instanceof HTMLElement);
|
|
1089
|
+
|
|
1090
|
+
const applySlotHeights = () => {
|
|
1091
|
+
if (!slots.length) return;
|
|
1092
|
+
|
|
1093
|
+
const viewportBudget = getViewportBudget();
|
|
1094
|
+
if (host instanceof HTMLElement && viewportBudget > 0) {
|
|
1095
|
+
host.style.maxHeight = `${viewportBudget}px`;
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
for (const slot of slots) {
|
|
1099
|
+
slot.style.height = "auto";
|
|
1100
|
+
slot.style.maxHeight = "none";
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
const getBorderY = (slot) => {
|
|
1104
|
+
const style = window.getComputedStyle(slot);
|
|
1105
|
+
const top = Number.parseFloat(style.borderTopWidth || "0") || 0;
|
|
1106
|
+
const bottom = Number.parseFloat(style.borderBottomWidth || "0") || 0;
|
|
1107
|
+
return top + bottom;
|
|
1108
|
+
};
|
|
1109
|
+
|
|
1110
|
+
const computed = window.getComputedStyle(stack);
|
|
1111
|
+
const gap =
|
|
1112
|
+
Number.parseFloat(computed.rowGap || computed.gap || "0") || 0;
|
|
1113
|
+
const hostHeight =
|
|
1114
|
+
host instanceof HTMLElement
|
|
1115
|
+
? Math.floor(host.clientHeight)
|
|
1116
|
+
: Math.floor(stack.clientHeight);
|
|
1117
|
+
const isClampedByViewport =
|
|
1118
|
+
viewportBudget > 0 && hostHeight >= viewportBudget - 1;
|
|
1119
|
+
const bottomGutter = isClampedByViewport ? 24 : 0;
|
|
1120
|
+
const available = Math.max(
|
|
1121
|
+
0,
|
|
1122
|
+
Math.floor(
|
|
1123
|
+
hostHeight - gap * Math.max(0, slots.length - 1) - bottomGutter,
|
|
1124
|
+
),
|
|
1125
|
+
);
|
|
1126
|
+
|
|
1127
|
+
const naturalHeights = slots.map((slot) =>
|
|
1128
|
+
Math.ceil(slot.scrollHeight + getBorderY(slot)),
|
|
1129
|
+
);
|
|
1130
|
+
const naturalTotal = naturalHeights.reduce(
|
|
1131
|
+
(sum, value) => sum + value,
|
|
1132
|
+
0,
|
|
1133
|
+
);
|
|
1134
|
+
|
|
1135
|
+
if (naturalTotal <= available) {
|
|
1136
|
+
for (let i = 0; i < slots.length; i += 1) {
|
|
1137
|
+
const height = naturalHeights[i];
|
|
1138
|
+
slots[i].style.height = `${height}px`;
|
|
1139
|
+
slots[i].style.maxHeight = `${height}px`;
|
|
1140
|
+
}
|
|
1141
|
+
return;
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
const roundedHeights = new Array(slots.length).fill(0);
|
|
1145
|
+
let remainingHeight = available;
|
|
1146
|
+
let remainingIndexes = slots
|
|
1147
|
+
.map((_, index) => index)
|
|
1148
|
+
.sort((a, b) => naturalHeights[a] - naturalHeights[b]);
|
|
1149
|
+
|
|
1150
|
+
while (remainingIndexes.length > 0) {
|
|
1151
|
+
const evenShare = remainingHeight / remainingIndexes.length;
|
|
1152
|
+
const smallestIndex = remainingIndexes[0];
|
|
1153
|
+
const smallestNatural = naturalHeights[smallestIndex];
|
|
1154
|
+
|
|
1155
|
+
if (smallestNatural <= evenShare) {
|
|
1156
|
+
const fixedHeight = Math.min(smallestNatural, remainingHeight);
|
|
1157
|
+
roundedHeights[smallestIndex] = Math.floor(fixedHeight);
|
|
1158
|
+
remainingHeight -= roundedHeights[smallestIndex];
|
|
1159
|
+
remainingIndexes = remainingIndexes.slice(1);
|
|
1160
|
+
continue;
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
const base = Math.floor(evenShare);
|
|
1164
|
+
const remainder = remainingHeight - base * remainingIndexes.length;
|
|
1165
|
+
for (let i = 0; i < remainingIndexes.length; i += 1) {
|
|
1166
|
+
const index = remainingIndexes[i];
|
|
1167
|
+
roundedHeights[index] = base + (i < remainder ? 1 : 0);
|
|
1168
|
+
}
|
|
1169
|
+
remainingHeight = 0;
|
|
1170
|
+
remainingIndexes = [];
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
let roundedTotal = roundedHeights.reduce((sum, value) => sum + value, 0);
|
|
1174
|
+
let delta = available - roundedTotal;
|
|
1175
|
+
|
|
1176
|
+
if (delta !== 0) {
|
|
1177
|
+
const adjustOrder = slots
|
|
1178
|
+
.map((_, index) => index)
|
|
1179
|
+
.sort((a, b) => roundedHeights[b] - roundedHeights[a]);
|
|
1180
|
+
|
|
1181
|
+
while (delta !== 0) {
|
|
1182
|
+
let changed = false;
|
|
1183
|
+
|
|
1184
|
+
for (const index of adjustOrder) {
|
|
1185
|
+
if (delta === 0) break;
|
|
1186
|
+
|
|
1187
|
+
if (delta > 0) {
|
|
1188
|
+
roundedHeights[index] += 1;
|
|
1189
|
+
delta -= 1;
|
|
1190
|
+
changed = true;
|
|
1191
|
+
continue;
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
if (roundedHeights[index] > 0) {
|
|
1195
|
+
roundedHeights[index] -= 1;
|
|
1196
|
+
delta += 1;
|
|
1197
|
+
changed = true;
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
if (!changed) break;
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
for (let i = 0; i < slots.length; i += 1) {
|
|
1206
|
+
slots[i].style.height = `${roundedHeights[i]}px`;
|
|
1207
|
+
slots[i].style.maxHeight = `${roundedHeights[i]}px`;
|
|
1208
|
+
}
|
|
1209
|
+
};
|
|
1210
|
+
|
|
1211
|
+
const scheduleApplySlotHeights = () => {
|
|
1212
|
+
window.requestAnimationFrame(() => {
|
|
1213
|
+
window.requestAnimationFrame(applySlotHeights);
|
|
1214
|
+
});
|
|
1215
|
+
};
|
|
1216
|
+
|
|
1217
|
+
const resizeObserver = new ResizeObserver(applySlotHeights);
|
|
1218
|
+
resizeObserver.observe(stack);
|
|
1219
|
+
if (host instanceof HTMLElement) {
|
|
1220
|
+
resizeObserver.observe(host);
|
|
1221
|
+
}
|
|
1222
|
+
for (const slot of slots) {
|
|
1223
|
+
resizeObserver.observe(slot);
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
window.addEventListener("resize", applySlotHeights);
|
|
1227
|
+
window.visualViewport?.addEventListener("resize", applySlotHeights);
|
|
1228
|
+
window.addEventListener("scroll", scheduleApplySlotHeights, {
|
|
1229
|
+
passive: true,
|
|
1230
|
+
});
|
|
1231
|
+
window.addEventListener(
|
|
1232
|
+
"rd:snippet-content-change",
|
|
1233
|
+
scheduleApplySlotHeights,
|
|
1234
|
+
);
|
|
1235
|
+
applySlotHeights();
|
|
1236
|
+
}
|
|
1237
|
+
</script>
|