cf-json-schema-viz 0.1.0

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 ADDED
@@ -0,0 +1,1195 @@
1
+ import { isRootNode, isRegularNode, SchemaCombinerName, isMirroredNode, isReferenceNode, isBooleanishNode, SchemaNodeKind, SchemaTree } from '@stoplight/json-schema-tree';
2
+ import { clsx } from 'clsx';
3
+ import { atom, useSetAtom, useAtomValue, Provider } from 'jotai';
4
+ import * as React2 from 'react';
5
+ import { isPlainObject, extractPointerFromRef, pointerToPath, getLastPathSegment } from '@stoplight/json';
6
+ import { atomFamily } from 'jotai/utils';
7
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
8
+ import { CaretRight, CaretDown, Check, Warning } from '@phosphor-icons/react';
9
+ import { Select as Select$1 } from '@base-ui/react/select';
10
+ import { Menu } from '@base-ui/react/menu';
11
+
12
+ // src/components/JsonSchemaViewer.tsx
13
+ var JSVOptionsContext = React2.createContext({
14
+ defaultExpandedDepth: 0,
15
+ viewMode: "standalone",
16
+ hideExamples: false
17
+ });
18
+ var useJSVOptionsContext = () => React2.useContext(JSVOptionsContext);
19
+ var JSVOptionsContextProvider = JSVOptionsContext.Provider;
20
+
21
+ // src/guards/isNonNullable.ts
22
+ function isNonNullable(maybeNullable) {
23
+ return maybeNullable !== void 0 && maybeNullable !== null;
24
+ }
25
+
26
+ // src/tree/utils.ts
27
+ var isNonEmptyParentNode = (node) => isRegularNode(node) && !!node.children && node.children.length > 0;
28
+ function isFlattenableNode(node) {
29
+ if (!isRegularNode(node)) return false;
30
+ if (!isArrayNode(node) && !isDictionaryNode(node) || !isNonNullable(node.children) || node.children.length === 0) {
31
+ return false;
32
+ }
33
+ return node.children.length === 1 && (isRegularNode(node.children[0]) && (!isArrayNode(node) || !isDictionaryNode(node.children[0])) || isReferenceNode(node.children[0]) && node.children[0].error !== null);
34
+ }
35
+ function isPrimitiveArray(node) {
36
+ return isFlattenableNode(node) && isArrayNode(node) && isRegularNode(node.children[0]) && node.children[0].simple;
37
+ }
38
+ function isPrimitiveDictionary(node) {
39
+ return isFlattenableNode(node) && isDictionaryNode(node) && isRegularNode(node.children[0]) && node.children[0].simple;
40
+ }
41
+ function isComplexArray(node) {
42
+ return isFlattenableNode(node) && isArrayNode(node) && isRegularNode(node.children[0]) && !node.children[0].simple;
43
+ }
44
+ function isComplexDictionary(node) {
45
+ return isFlattenableNode(node) && isDictionaryNode(node) && isRegularNode(node.children[0]) && !node.children[0].simple;
46
+ }
47
+ function isDictionaryNode(node) {
48
+ return isRegularNode(node) && node.primaryType === SchemaNodeKind.Object && isPlainObject(node.fragment.additionalProperties);
49
+ }
50
+ function isArrayNode(node) {
51
+ return isRegularNode(node) && node.primaryType === SchemaNodeKind.Array;
52
+ }
53
+ function visibleChildren(node) {
54
+ if (!isRegularNode(node) || isPrimitiveArray(node) || isPrimitiveDictionary(node)) {
55
+ return [];
56
+ }
57
+ if (isComplexArray(node) || isComplexDictionary(node)) {
58
+ return node.children[0].children ?? [];
59
+ }
60
+ return node.children ?? [];
61
+ }
62
+ function isPropertyRequired(schemaNode) {
63
+ const { parent } = schemaNode;
64
+ if (parent === null || !isRegularNode(parent) || schemaNode.subpath.length === 0) {
65
+ return false;
66
+ }
67
+ return !!parent.required?.includes(schemaNode.subpath[schemaNode.subpath.length - 1]);
68
+ }
69
+ function isValidViewMode(node, viewMode) {
70
+ const { validations } = node;
71
+ if (!!validations.writeOnly === !!validations.readOnly) {
72
+ return true;
73
+ }
74
+ return !(viewMode === "read" && !!validations.writeOnly || viewMode === "write" && !!validations.readOnly);
75
+ }
76
+ function isRenderableNode(node) {
77
+ if (node.parent === null) return true;
78
+ if (isDictionaryNode(node.parent)) {
79
+ return node.subpath.length !== 2 || node.subpath[0] !== "properties";
80
+ }
81
+ if (isArrayNode(node.parent)) {
82
+ return node.subpath[0] !== "additionalItems";
83
+ }
84
+ if (isRegularNode(node.parent) && node.parent.primaryType === SchemaNodeKind.Object && isBooleanishNode(node)) {
85
+ return !(node.subpath.length === 1 || node.subpath[0] === "additionalProperties");
86
+ }
87
+ return true;
88
+ }
89
+ function shouldNodeBeIncluded(node, viewMode = "standalone") {
90
+ return (isReferenceNode(node) || isRootNode(node) || isRenderableNode(node)) && (!isRegularNode(node) || isValidViewMode(node, viewMode));
91
+ }
92
+ var hoveredNodeAtom = atom(null);
93
+ var isNodeHoveredAtom = atomFamily(
94
+ (node) => atom((get) => node === get(hoveredNodeAtom))
95
+ );
96
+ atomFamily(
97
+ (parent) => atom((get) => {
98
+ const hoveredNode = get(hoveredNodeAtom);
99
+ if (!hoveredNode || hoveredNode === parent) return false;
100
+ return hoveredNode.parent === parent;
101
+ })
102
+ );
103
+
104
+ // src/components/PathCrumbs/state.ts
105
+ var pathCrumbsAtom = atom((get) => {
106
+ const node = get(hoveredNodeAtom);
107
+ if (!node) return [];
108
+ return propertyPathToObjectPath(node);
109
+ });
110
+ function propertyPathToObjectPath(node) {
111
+ const objectPath = [];
112
+ let currentNode = node;
113
+ while (currentNode && !isRootNode(currentNode)) {
114
+ if (isRegularNode(currentNode)) {
115
+ const pathPart = currentNode.subpath[currentNode.subpath.length - 1];
116
+ if (currentNode.primaryType === "array") {
117
+ const key = `${pathPart || ""}[]`;
118
+ if (objectPath[objectPath.length - 1]) {
119
+ objectPath[objectPath.length - 1] = key;
120
+ } else {
121
+ objectPath.push(key);
122
+ }
123
+ } else if (pathPart && (currentNode.subpath.length !== 2 || !["allOf", "oneOf", "anyOf"].includes(currentNode.subpath[0]))) {
124
+ objectPath.push(currentNode.subpath[currentNode.subpath.length - 1]);
125
+ }
126
+ }
127
+ currentNode = currentNode.parent;
128
+ }
129
+ return objectPath.reverse();
130
+ }
131
+ var SCROLL_THRESHOLD = 20;
132
+ var PathCrumbs = ({ parentCrumbs = [] }) => {
133
+ const pathCrumbs = useAtomValue(pathCrumbsAtom);
134
+ const { disableCrumbs } = useJSVOptionsContext();
135
+ const crumbsRef = React2.useRef(null);
136
+ const [style, setStyle] = React2.useState({
137
+ position: "fixed",
138
+ top: -100,
139
+ // Start off-screen
140
+ left: 0,
141
+ width: 0
142
+ });
143
+ const [hasScrolled, setHasScrolled] = React2.useState(false);
144
+ React2.useEffect(() => {
145
+ const crumbsEl = crumbsRef.current;
146
+ if (!crumbsEl) return;
147
+ const scrollContainer = crumbsEl.closest(".jsv-root");
148
+ if (!scrollContainer) return;
149
+ const updatePosition = () => {
150
+ const containerRect = scrollContainer.getBoundingClientRect();
151
+ const scrolled = scrollContainer.scrollTop > SCROLL_THRESHOLD;
152
+ setHasScrolled(scrolled);
153
+ setStyle({
154
+ position: "fixed",
155
+ top: Math.max(0, containerRect.top),
156
+ left: containerRect.left,
157
+ width: containerRect.width
158
+ });
159
+ };
160
+ updatePosition();
161
+ scrollContainer.addEventListener("scroll", updatePosition);
162
+ window.addEventListener("scroll", updatePosition);
163
+ window.addEventListener("resize", updatePosition);
164
+ return () => {
165
+ scrollContainer.removeEventListener("scroll", updatePosition);
166
+ window.removeEventListener("scroll", updatePosition);
167
+ window.removeEventListener("resize", updatePosition);
168
+ };
169
+ }, []);
170
+ if (disableCrumbs) {
171
+ return null;
172
+ }
173
+ const parentCrumbElems = [];
174
+ parentCrumbs.forEach((crumb, i) => {
175
+ parentCrumbElems.push(/* @__PURE__ */ jsx("span", { children: crumb }, i));
176
+ });
177
+ const pathCrumbElems = [];
178
+ pathCrumbs.forEach((crumb, i) => {
179
+ if (pathCrumbs[i + 1]) {
180
+ pathCrumbElems.push(/* @__PURE__ */ jsx("span", { children: crumb }, i));
181
+ } else {
182
+ pathCrumbElems.push(
183
+ /* @__PURE__ */ jsx("span", { className: "jsv-crumb-current", children: crumb }, i)
184
+ );
185
+ }
186
+ });
187
+ const hasCrumbs = parentCrumbElems.length > 0 || pathCrumbElems.length > 0;
188
+ const isVisible = hasCrumbs && hasScrolled;
189
+ return /* @__PURE__ */ jsxs("div", { ref: crumbsRef, className: "jsv-crumbs", style, "data-visible": isVisible, children: [
190
+ parentCrumbElems.map((elem, i) => /* @__PURE__ */ jsxs(React2.Fragment, { children: [
191
+ elem,
192
+ i < parentCrumbElems.length - 1 && /* @__PURE__ */ jsx("span", { children: "/" })
193
+ ] }, `parent-${i}`)),
194
+ parentCrumbElems.length > 0 && pathCrumbElems.length > 0 && /* @__PURE__ */ jsx("span", { children: "/" }),
195
+ pathCrumbElems.map((elem, i) => /* @__PURE__ */ jsxs(React2.Fragment, { children: [
196
+ elem,
197
+ i < pathCrumbElems.length - 1 && /* @__PURE__ */ jsx("span", { style: { fontWeight: 700 }, children: "." })
198
+ ] }, `path-${i}`))
199
+ ] });
200
+ };
201
+ ({
202
+ [SchemaCombinerName.AllOf]: "and",
203
+ [SchemaCombinerName.AnyOf]: "and/or",
204
+ [SchemaCombinerName.OneOf]: "or"
205
+ });
206
+ var COMMON_JSON_SCHEMA_AND_OAS_FORMATS = {
207
+ // strings are omitted because they are the default type to apply format to
208
+ number: ["byte", "int32", "int64", "float", "double"],
209
+ get integer() {
210
+ return this.number;
211
+ }
212
+ };
213
+ var COMBINER_NAME_MAP = {
214
+ allOf: "all of",
215
+ anyOf: "any of",
216
+ oneOf: "one of"
217
+ };
218
+ var SKIP_HASHING = false;
219
+ function fnv1a52(str) {
220
+ const FNV_PRIME = 16777619;
221
+ const FNV_OFFSET = 2166136261;
222
+ let hash2 = FNV_OFFSET;
223
+ for (let i = 0; i < str.length; i++) {
224
+ hash2 ^= str.charCodeAt(i);
225
+ hash2 = Math.imul(hash2, FNV_PRIME);
226
+ }
227
+ const hash32 = hash2 >>> 0;
228
+ return hash32.toString(16).padStart(8, "0");
229
+ }
230
+ var hash = (value, skipHashing = SKIP_HASHING) => {
231
+ return skipHashing ? value : fnv1a52(value);
232
+ };
233
+ function getStoplightId(fragment) {
234
+ if (typeof fragment === "boolean") return void 0;
235
+ const xStoplight = fragment["x-stoplight"];
236
+ if (isPlainObject(xStoplight)) {
237
+ const id = xStoplight.id;
238
+ return typeof id === "string" ? id : void 0;
239
+ }
240
+ return void 0;
241
+ }
242
+ var getNodeId = (node, parentId) => {
243
+ const nodeId = getStoplightId(node.fragment);
244
+ if (nodeId) return nodeId;
245
+ const key = node.path[node.path.length - 1];
246
+ return hash(["schema_property", parentId, String(key)].join("-"));
247
+ };
248
+ var getOriginalNodeId = (node, parentId) => {
249
+ const nodeId = getStoplightId(node.originalFragment);
250
+ if (nodeId) return nodeId;
251
+ const key = node.path[node.path.length - 1];
252
+ return hash(["schema_property", parentId, String(key)].join("-"));
253
+ };
254
+
255
+ // src/utils/extractVendorExtensions.ts
256
+ function extractVendorExtensions(fragment) {
257
+ if (typeof fragment === "boolean") {
258
+ return [0, {}];
259
+ }
260
+ const extensionKeys = Object.entries(fragment).filter(([key]) => key.startsWith("x-"));
261
+ let vendorExtensions = {};
262
+ for (const [key, value] of extensionKeys) {
263
+ vendorExtensions[key] = value;
264
+ }
265
+ return [extensionKeys.length, vendorExtensions];
266
+ }
267
+ var Caret = ({ isExpanded }) => /* @__PURE__ */ jsx(
268
+ "span",
269
+ {
270
+ className: clsx("jsv-caret", isExpanded && "jsv-caret-expanded"),
271
+ role: "button",
272
+ "aria-expanded": isExpanded,
273
+ children: /* @__PURE__ */ jsx(CaretRight, { size: 12, weight: "bold" })
274
+ }
275
+ );
276
+ var ChildStack = React2.memo(
277
+ ({
278
+ childNodes,
279
+ currentNestingLevel,
280
+ className,
281
+ RowComponent = SchemaRow,
282
+ parentNodeId,
283
+ parentChangeType
284
+ }) => {
285
+ const { renderRootTreeLines } = useJSVOptionsContext();
286
+ const rootLevel = renderRootTreeLines ? 0 : 1;
287
+ const isRootLevel = currentNestingLevel < rootLevel;
288
+ return /* @__PURE__ */ jsx(
289
+ "div",
290
+ {
291
+ className: clsx(
292
+ "jsv-child-stack",
293
+ !isRootLevel && "jsv-child-stack-nested",
294
+ className
295
+ ),
296
+ "data-level": currentNestingLevel,
297
+ children: childNodes.map((childNode) => /* @__PURE__ */ jsx(
298
+ RowComponent,
299
+ {
300
+ schemaNode: childNode,
301
+ nestingLevel: currentNestingLevel + 1,
302
+ parentNodeId,
303
+ parentChangeType
304
+ },
305
+ childNode.id
306
+ ))
307
+ }
308
+ );
309
+ }
310
+ );
311
+ ChildStack.displayName = "ChildStack";
312
+ var Description = ({ value }) => {
313
+ const [showAll, setShowAll] = React2.useState(false);
314
+ if (typeof value !== "string" || value.trim().length === 0) return null;
315
+ const paragraphs = value.split("\n\n");
316
+ if (paragraphs.length <= 1 || showAll) {
317
+ return /* @__PURE__ */ jsx("div", { className: "jsv-description", "data-test": "property-description", children: value });
318
+ }
319
+ const firstParagraph = paragraphs[0];
320
+ return /* @__PURE__ */ jsx("div", { className: "jsv-description", "data-test": "property-description", children: /* @__PURE__ */ jsxs("p", { children: [
321
+ /* @__PURE__ */ jsx("span", { className: "mr-1", children: firstParagraph }),
322
+ /* @__PURE__ */ jsx(
323
+ "button",
324
+ {
325
+ type: "button",
326
+ className: "text-jsv-primary cursor-pointer hover:underline",
327
+ onClick: () => setShowAll(true),
328
+ children: "Show all..."
329
+ }
330
+ )
331
+ ] }) });
332
+ };
333
+ var Divider = ({ hoveredAtom }) => {
334
+ const isHovered = useAtomValue(hoveredAtom);
335
+ return /* @__PURE__ */ jsx("span", { className: clsx("jsv-divider", isHovered && "jsv-divider-visible") });
336
+ };
337
+ function getInternalSchemaError(schemaNode) {
338
+ let errorMessage;
339
+ const fragment = schemaNode.fragment;
340
+ if (!isPlainObject(fragment)) return;
341
+ const xStoplight = fragment["x-stoplight"];
342
+ if (isPlainObject(xStoplight) && typeof xStoplight["error-message"] === "string") {
343
+ errorMessage = xStoplight["error-message"];
344
+ } else {
345
+ const fragmentErrorMessage = fragment["x-sl-error-message"];
346
+ if (typeof fragmentErrorMessage === "string") {
347
+ errorMessage = fragmentErrorMessage;
348
+ } else {
349
+ const items = fragment["items"];
350
+ if (isPlainObject(items)) {
351
+ const itemsErrorMessage = items["x-sl-error-message"];
352
+ if (typeof itemsErrorMessage === "string") {
353
+ errorMessage = itemsErrorMessage;
354
+ }
355
+ }
356
+ }
357
+ }
358
+ return errorMessage;
359
+ }
360
+ function useRefNode(schemaNode) {
361
+ return React2.useMemo(() => {
362
+ if (isReferenceNode(schemaNode)) {
363
+ return schemaNode;
364
+ }
365
+ if (isRegularNode(schemaNode) && (isFlattenableNode(schemaNode) || schemaNode.primaryType === SchemaNodeKind.Array && schemaNode.children?.length === 1)) {
366
+ return schemaNode.children?.find(isReferenceNode) ?? null;
367
+ }
368
+ return null;
369
+ }, [schemaNode]);
370
+ }
371
+ var Error = ({ schemaNode }) => {
372
+ const refNode = useRefNode(schemaNode);
373
+ const error = getInternalSchemaError(schemaNode) ?? refNode?.error;
374
+ if (typeof error !== "string") return null;
375
+ return /* @__PURE__ */ jsx("span", { className: "inline-block ml-1.5", title: error, children: /* @__PURE__ */ jsx(Warning, { className: "jsv-error", size: 16, "aria-label": error }) });
376
+ };
377
+ var NodeAnnotation = ({ change, style }) => {
378
+ if (!change) return null;
379
+ return /* @__PURE__ */ jsx(
380
+ "span",
381
+ {
382
+ className: clsx(
383
+ "jsv-annotation",
384
+ change.type === "added" && "jsv-annotation-added",
385
+ change.type === "removed" && "jsv-annotation-removed",
386
+ change.type === "modified" && "jsv-annotation-modified"
387
+ ),
388
+ style,
389
+ "aria-label": `${change.type} property`
390
+ }
391
+ );
392
+ };
393
+ var useHasProperties = ({ required, deprecated, validations: { readOnly, writeOnly } }) => {
394
+ const { viewMode } = useJSVOptionsContext();
395
+ const showVisibilityValidations = viewMode === "standalone" && !!readOnly !== !!writeOnly;
396
+ return deprecated || showVisibilityValidations || required;
397
+ };
398
+ var Properties = ({
399
+ required,
400
+ deprecated,
401
+ validations: { readOnly, writeOnly }
402
+ }) => {
403
+ const { viewMode } = useJSVOptionsContext();
404
+ const showVisibilityValidations = viewMode === "standalone" && !!readOnly !== !!writeOnly;
405
+ const visibility = showVisibilityValidations ? readOnly ? /* @__PURE__ */ jsx("span", { className: "jsv-badge jsv-badge-readonly", "data-test": "property-read-only", children: "read-only" }) : /* @__PURE__ */ jsx("span", { className: "jsv-badge jsv-badge-readonly", "data-test": "property-write-only", children: "write-only" }) : null;
406
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
407
+ deprecated ? /* @__PURE__ */ jsx("span", { className: "jsv-badge jsv-badge-deprecated", "data-test": "property-deprecated", children: "deprecated" }) : null,
408
+ visibility,
409
+ required && /* @__PURE__ */ jsx("span", { className: "jsv-badge jsv-badge-required", "data-test": "property-required", children: "required" })
410
+ ] });
411
+ };
412
+ var Select = ({
413
+ "aria-label": ariaLabel,
414
+ options,
415
+ value,
416
+ onChange,
417
+ prefix
418
+ }) => {
419
+ return /* @__PURE__ */ jsxs(
420
+ Select$1.Root,
421
+ {
422
+ value,
423
+ onValueChange: (newValue) => {
424
+ if (newValue !== null) {
425
+ onChange(newValue);
426
+ }
427
+ },
428
+ children: [
429
+ /* @__PURE__ */ jsxs(
430
+ Select$1.Trigger,
431
+ {
432
+ "aria-label": ariaLabel,
433
+ className: "jsv-select-trigger",
434
+ children: [
435
+ /* @__PURE__ */ jsx(Select$1.Value, { placeholder: "", children: (val) => {
436
+ const selectedOption = options.find((opt) => opt.value === val);
437
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
438
+ prefix,
439
+ selectedOption?.label ?? val
440
+ ] });
441
+ } }),
442
+ /* @__PURE__ */ jsx(Select$1.Icon, { children: /* @__PURE__ */ jsx(CaretDown, { size: 12 }) })
443
+ ]
444
+ }
445
+ ),
446
+ /* @__PURE__ */ jsx(Select$1.Portal, { children: /* @__PURE__ */ jsx(Select$1.Positioner, { className: "jsv-select-positioner", sideOffset: 4, children: /* @__PURE__ */ jsx(Select$1.Popup, { className: "jsv-select-popup", children: options.map((option) => /* @__PURE__ */ jsxs(
447
+ Select$1.Item,
448
+ {
449
+ value: option.value,
450
+ className: "jsv-select-item",
451
+ children: [
452
+ /* @__PURE__ */ jsx(Select$1.ItemIndicator, { className: "jsv-select-item-indicator", children: /* @__PURE__ */ jsx(Check, { size: 12, weight: "bold" }) }),
453
+ /* @__PURE__ */ jsx(Select$1.ItemText, { children: option.label })
454
+ ]
455
+ },
456
+ option.value
457
+ )) }) }) })
458
+ ]
459
+ }
460
+ );
461
+ };
462
+ function getApplicableFormats(schemaNode) {
463
+ const fragment = schemaNode.fragment;
464
+ if (isPlainObject(fragment) && fragment["contentMediaType"] === "application/octet-stream" && schemaNode.types && schemaNode.types.length > 0) {
465
+ return [schemaNode.types[0], "binary"];
466
+ }
467
+ if (schemaNode.format === null) {
468
+ return null;
469
+ }
470
+ if (schemaNode.types !== null) {
471
+ for (const type of schemaNode.types) {
472
+ if (!(type in COMMON_JSON_SCHEMA_AND_OAS_FORMATS)) continue;
473
+ if (COMMON_JSON_SCHEMA_AND_OAS_FORMATS[type].includes(schemaNode.format)) {
474
+ return [type, schemaNode.format];
475
+ }
476
+ }
477
+ }
478
+ return [SchemaNodeKind.String, schemaNode.format];
479
+ }
480
+
481
+ // src/utils/printName.ts
482
+ function upperFirst(str) {
483
+ return str.charAt(0).toUpperCase() + str.slice(1);
484
+ }
485
+ function printName(schemaNode, { shouldUseRefNameFallback = false } = {}) {
486
+ if (!isFlattenableNode(schemaNode)) {
487
+ return schemaNode.title ?? (shouldUseRefNameFallback ? getNodeNameFromOriginalRef(schemaNode) : void 0);
488
+ }
489
+ return printFlattenedName(schemaNode, { shouldUseRefNameFallback });
490
+ }
491
+ function printFlattenedName(schemaNode, { shouldUseRefNameFallback = false }) {
492
+ if (!isNonNullable(schemaNode.children) || schemaNode.children.length === 0) {
493
+ return schemaNode.title ?? (shouldUseRefNameFallback ? getNodeNameFromOriginalRef(schemaNode) : void 0);
494
+ }
495
+ if (schemaNode.children.length === 1 && isReferenceNode(schemaNode.children[0])) {
496
+ const value = `$ref(${schemaNode.children[0].value})`;
497
+ return isDictionaryNode(schemaNode) ? `dictionary[string, ${value}]` : `${value}[]`;
498
+ }
499
+ const format = isDictionaryNode(schemaNode) ? "dictionary[string, %s]" : "array[%s]";
500
+ if (isPrimitiveArray(schemaNode) || isPrimitiveDictionary(schemaNode)) {
501
+ const val = schemaNode.children?.reduce((mergedTypes, child) => {
502
+ if (mergedTypes === null) return null;
503
+ if (!isRegularNode(child)) return null;
504
+ if (child.types !== null && child.types.length > 0) {
505
+ const formats = getApplicableFormats(child);
506
+ for (const type of child.types) {
507
+ if (mergedTypes.includes(type)) continue;
508
+ if (formats !== null && formats[0] === type) {
509
+ mergedTypes.push(`${type}<${formats[1]}>`);
510
+ } else {
511
+ mergedTypes.push(type);
512
+ }
513
+ }
514
+ }
515
+ return mergedTypes;
516
+ }, []) ?? null;
517
+ if (val !== null && val.length > 0) {
518
+ return format.replace("%s", val.join(" or "));
519
+ }
520
+ return isDictionaryNode(schemaNode) ? "dictionary[string, any]" : "array";
521
+ }
522
+ if (isComplexArray(schemaNode) || isComplexDictionary(schemaNode)) {
523
+ const firstChild = schemaNode.children[0];
524
+ if (firstChild.title) {
525
+ return format.replace("%s", firstChild.title);
526
+ } else if (shouldUseRefNameFallback && getNodeNameFromOriginalRef(schemaNode)) {
527
+ return format.replace("%s", getNodeNameFromOriginalRef(schemaNode) ?? "any");
528
+ } else if (firstChild.primaryType) {
529
+ return format.replace("%s", firstChild.primaryType);
530
+ } else if (firstChild.combiners?.length) {
531
+ return format.replace("%s", firstChild.combiners.join(" "));
532
+ }
533
+ return isComplexArray(schemaNode) ? "array" : format.replace("%s", "any");
534
+ }
535
+ return void 0;
536
+ }
537
+ function getNodeNameFromOriginalRef(node) {
538
+ if (typeof node.originalFragment.$ref === "string") {
539
+ return upperFirst(getLastPathSegment(node.originalFragment.$ref));
540
+ }
541
+ return void 0;
542
+ }
543
+ function shouldRenderName(type) {
544
+ return type === SchemaNodeKind.Array || type === SchemaNodeKind.Object || type === "$ref";
545
+ }
546
+ function getTypes(schemaNode) {
547
+ return [schemaNode.types, schemaNode.combiners].reduce(
548
+ (values, value) => {
549
+ if (value === null) {
550
+ return values;
551
+ }
552
+ values.push(...value);
553
+ return values;
554
+ },
555
+ []
556
+ );
557
+ }
558
+ var Types = ({ schemaNode }) => {
559
+ if (isReferenceNode(schemaNode)) {
560
+ return /* @__PURE__ */ jsx("span", { className: "jsv-type", "data-test": "property-type-ref", children: schemaNode.value ?? "$ref" });
561
+ }
562
+ if (isBooleanishNode(schemaNode)) {
563
+ return /* @__PURE__ */ jsx("span", { className: "jsv-type", "data-test": "property-type", children: schemaNode.fragment ? "any" : "never" });
564
+ }
565
+ if (!isRegularNode(schemaNode)) {
566
+ return null;
567
+ }
568
+ const formats = getApplicableFormats(schemaNode);
569
+ const types = getTypes(schemaNode);
570
+ if (types.length === 0) {
571
+ return /* @__PURE__ */ jsx("span", { className: "jsv-type", "data-test": "property-type", children: formats === null ? "any" : `<${formats[1]}>` });
572
+ }
573
+ const rendered = types.map((type, i, { length }) => {
574
+ let printedName;
575
+ if (shouldRenderName(type)) {
576
+ printedName = printName(schemaNode);
577
+ }
578
+ printedName ?? (printedName = type + (formats === null || formats[0] !== type ? "" : `<${formats[1]}>`));
579
+ return /* @__PURE__ */ jsxs(React2.Fragment, { children: [
580
+ /* @__PURE__ */ jsx("span", { className: "jsv-type", "data-test": "property-type", children: printedName }),
581
+ i < length - 1 && /* @__PURE__ */ jsx("span", { className: "jsv-type", children: " or " }, `${i}-sep`)
582
+ ] }, type);
583
+ });
584
+ return rendered.length > 1 ? /* @__PURE__ */ jsx("span", { className: "truncate", children: rendered }) : /* @__PURE__ */ jsx(Fragment, { children: rendered });
585
+ };
586
+ Types.displayName = "JsonSchemaViewer.Types";
587
+ var numberValidationNames = [
588
+ "minimum",
589
+ "maximum",
590
+ "minLength",
591
+ "maxLength",
592
+ "minItems",
593
+ "maxItems",
594
+ "exclusiveMinimum",
595
+ "exclusiveMaximum"
596
+ ];
597
+ var exampleValidationNames = ["examples"];
598
+ var excludedValidations = ["exclusiveMinimum", "exclusiveMaximum", "readOnly", "writeOnly"];
599
+ var numberValidationFormatters = {
600
+ minimum: (value) => `>= ${value}`,
601
+ exclusiveMinimum: (value) => `> ${value}`,
602
+ minItems: (value) => `>= ${value} items`,
603
+ minLength: (value) => `>= ${value} characters`,
604
+ maximum: (value) => `<= ${value}`,
605
+ exclusiveMaximum: (value) => `< ${value}`,
606
+ maxItems: (value) => `<= ${value} items`,
607
+ maxLength: (value) => `<= ${value} characters`
608
+ };
609
+ var createStringFormatter = (nowrap) => (value) => {
610
+ return nowrap && typeof value === "string" ? value : JSON.stringify(value);
611
+ };
612
+ var createValidationsFormatter = (name, options) => (value) => {
613
+ const values = Array.isArray(value) ? value : [value];
614
+ if (values.length) {
615
+ return {
616
+ name: options?.exact ? name : values.length > 1 ? `${name}s` : `${name}`,
617
+ values: values.map(createStringFormatter(options?.nowrap))
618
+ };
619
+ }
620
+ return null;
621
+ };
622
+ var validationFormatters = {
623
+ enum: createValidationsFormatter("Allowed value", { nowrap: true }),
624
+ examples: createValidationsFormatter("Example", { nowrap: true }),
625
+ multipleOf: createValidationsFormatter("Multiple of", { exact: true }),
626
+ pattern: createValidationsFormatter("Match pattern", { exact: true, nowrap: true }),
627
+ default: createValidationsFormatter("Default", { exact: true, nowrap: true }),
628
+ style: createValidationsFormatter("Style", { exact: true, nowrap: true })
629
+ };
630
+ var oasFormats = {
631
+ int32: {
632
+ minimum: 0 - 2 ** 31,
633
+ maximum: 2 ** 31 - 1
634
+ },
635
+ int64: {
636
+ minimum: Number.MIN_SAFE_INTEGER,
637
+ maximum: Number.MAX_SAFE_INTEGER
638
+ },
639
+ float: {
640
+ minimum: 0 - 2 ** 128,
641
+ maximum: 2 ** 128 - 1
642
+ },
643
+ double: {
644
+ minimum: 0 - Number.MAX_VALUE,
645
+ maximum: Number.MAX_VALUE
646
+ },
647
+ byte: {
648
+ pattern: "^[\\w\\d+\\/=]*$"
649
+ }
650
+ };
651
+ function isOasFormat(format) {
652
+ return format in oasFormats;
653
+ }
654
+ function filterOutOasFormatValidations(format, values) {
655
+ if (!isOasFormat(format)) {
656
+ return values;
657
+ }
658
+ const newValues = { ...values };
659
+ for (const [key, value] of Object.entries(oasFormats[format])) {
660
+ if (value === newValues[key]) {
661
+ delete newValues[key];
662
+ }
663
+ }
664
+ return newValues;
665
+ }
666
+ function pick(obj, keys) {
667
+ const result = {};
668
+ for (const key of keys) {
669
+ if (key in obj) {
670
+ result[key] = obj[key];
671
+ }
672
+ }
673
+ return result;
674
+ }
675
+ function omit(obj, keys) {
676
+ const result = { ...obj };
677
+ for (const key of keys) {
678
+ delete result[key];
679
+ }
680
+ return result;
681
+ }
682
+ function capitalize(str) {
683
+ return str.charAt(0).toUpperCase() + str.slice(1);
684
+ }
685
+ function uniq(arr) {
686
+ return [...new Set(arr)];
687
+ }
688
+ var Validations = ({ validations, hideExamples }) => {
689
+ const numberValidations = pick(validations, numberValidationNames);
690
+ const keyValueValidations = omit(validations, [
691
+ ...Object.keys(numberValidations),
692
+ ...excludedValidations,
693
+ ...hideExamples ? exampleValidationNames : []
694
+ ]);
695
+ return /* @__PURE__ */ jsxs("div", { className: "jsv-validations", children: [
696
+ /* @__PURE__ */ jsx(NumberValidations, { validations: numberValidations }),
697
+ /* @__PURE__ */ jsx(KeyValueValidations, { validations: keyValueValidations })
698
+ ] });
699
+ };
700
+ var NumberValidations = ({ validations }) => {
701
+ const entries = Object.entries(validations);
702
+ if (!entries.length) {
703
+ return null;
704
+ }
705
+ return /* @__PURE__ */ jsx("div", { className: "jsv-validations-row", "data-test": "property-validation", children: entries.map(([key, value]) => numberValidationFormatters[key](value)).map((value, i) => /* @__PURE__ */ jsx(Value, { name: value }, i)) });
706
+ };
707
+ var KeyValueValidations = ({ validations }) => /* @__PURE__ */ jsx(Fragment, { children: Object.keys(validations).filter((key) => Object.keys(validationFormatters).includes(key) && validations[key] !== void 0).map((key) => {
708
+ const validation = validationFormatters[key](validations[key]);
709
+ if (validation) {
710
+ return /* @__PURE__ */ jsx(KeyValueValidation, { name: validation.name, values: validation.values }, key);
711
+ } else {
712
+ return null;
713
+ }
714
+ }) });
715
+ var KeyValueValidation = ({ name, values }) => {
716
+ return /* @__PURE__ */ jsxs("div", { className: "jsv-validation", "data-test": "property-validation", children: [
717
+ /* @__PURE__ */ jsxs("span", { className: "jsv-validation-label", children: [
718
+ capitalize(name),
719
+ ":"
720
+ ] }),
721
+ /* @__PURE__ */ jsx("div", { className: "jsv-validation-values", children: uniq(values).map((value) => /* @__PURE__ */ jsx(Value, { name: value }, value)) })
722
+ ] });
723
+ };
724
+ var Value = ({ name }) => /* @__PURE__ */ jsx("span", { className: "jsv-validation-value", children: name });
725
+ var getArrayValidations = (schemaNode) => {
726
+ if (schemaNode.children?.length === 1 && isRegularNode(schemaNode.children[0])) {
727
+ if (schemaNode.children[0].enum !== null) {
728
+ return { enum: schemaNode.children[0].enum };
729
+ } else if (schemaNode.children[0].fragment.pattern !== void 0) {
730
+ return { pattern: schemaNode.children[0].fragment.pattern };
731
+ }
732
+ }
733
+ return null;
734
+ };
735
+ function getValidationsFromSchema(schemaNode) {
736
+ return {
737
+ ...schemaNode.enum !== null ? { enum: schemaNode.enum } : schemaNode.primaryType === "array" ? (
738
+ // in case schemaNode is type: "array", check if its child has an additional validation
739
+ getArrayValidations(schemaNode)
740
+ ) : null,
741
+ ..."annotations" in schemaNode ? {
742
+ ...schemaNode.annotations.default !== void 0 ? { default: schemaNode.annotations.default } : null,
743
+ ...schemaNode.annotations.examples ? { examples: schemaNode.annotations.examples } : null
744
+ } : null,
745
+ ...getFilteredValidations(schemaNode)
746
+ };
747
+ }
748
+ function getFilteredValidations(schemaNode) {
749
+ if (schemaNode.format !== null) {
750
+ return filterOutOasFormatValidations(schemaNode.format, schemaNode.validations);
751
+ }
752
+ return schemaNode.validations;
753
+ }
754
+ function last(arr) {
755
+ return arr[arr.length - 1];
756
+ }
757
+ function calculateChoiceTitle(node, isPlural) {
758
+ const primitiveSuffix = isPlural ? "s" : "";
759
+ if (isRegularNode(node)) {
760
+ const realName = printName(node, { shouldUseRefNameFallback: true });
761
+ if (realName) {
762
+ return realName;
763
+ }
764
+ return node.primaryType !== null ? node.primaryType + primitiveSuffix : String(node.originalFragment.title || "any");
765
+ }
766
+ if (isReferenceNode(node)) {
767
+ if (node.value) {
768
+ const value = extractPointerFromRef(node.value);
769
+ const lastPiece = !node.error && value ? last(pointerToPath(value)) : null;
770
+ if (typeof lastPiece === "string") {
771
+ return lastPiece.split(".")[0];
772
+ }
773
+ }
774
+ return "$ref" + primitiveSuffix;
775
+ }
776
+ return "any";
777
+ }
778
+ function makeChoice(node) {
779
+ return {
780
+ type: node,
781
+ title: calculateChoiceTitle(node, false)
782
+ };
783
+ }
784
+ function makeArrayChoice(node, combiner) {
785
+ const itemTitle = calculateChoiceTitle(node, true);
786
+ const title = itemTitle !== "any" ? `array ${combiner ? `(${combiner})` : null} [${itemTitle}]` : "array";
787
+ return {
788
+ type: node,
789
+ title
790
+ };
791
+ }
792
+ var useChoices = (schemaNode) => {
793
+ const choices = React2.useMemo(() => {
794
+ if (isComplexArray(schemaNode) && isNonEmptyParentNode(schemaNode.children[0]) && shouldShowChildSelector(schemaNode.children[0])) {
795
+ return schemaNode.children[0].children.map(
796
+ (child) => makeArrayChoice(child, schemaNode.children[0].combiners?.[0])
797
+ );
798
+ }
799
+ if (isNonEmptyParentNode(schemaNode) && shouldShowChildSelector(schemaNode)) {
800
+ return schemaNode.children.map(makeChoice);
801
+ }
802
+ return [makeChoice(schemaNode)];
803
+ }, [schemaNode]);
804
+ const defaultChoice = choices[0];
805
+ const [selectedChoice, setSelectedChoice] = React2.useState(defaultChoice);
806
+ React2.useEffect(() => {
807
+ setSelectedChoice(defaultChoice);
808
+ }, [defaultChoice]);
809
+ const actualSelectedChoice = selectedChoice && choices.includes(selectedChoice) ? selectedChoice : defaultChoice;
810
+ return { selectedChoice: actualSelectedChoice, setSelectedChoice, choices };
811
+ };
812
+ var shouldShowChildSelector = (schemaNode) => isNonEmptyParentNode(schemaNode) && ["anyOf", "oneOf"].includes(schemaNode.combiners?.[0] ?? "");
813
+ var SchemaRow = React2.memo(
814
+ ({ schemaNode, nestingLevel, pl, parentNodeId, parentChangeType }) => {
815
+ const {
816
+ defaultExpandedDepth,
817
+ renderRowAddon,
818
+ renderExtensionAddon,
819
+ onGoToRef,
820
+ hideExamples,
821
+ renderRootTreeLines,
822
+ nodeHasChanged,
823
+ viewMode
824
+ } = useJSVOptionsContext();
825
+ const setHoveredNode = useSetAtom(hoveredNodeAtom);
826
+ const nodeId = getNodeId(schemaNode, parentNodeId);
827
+ const originalNodeId = schemaNode.originalFragment?.$ref ? getOriginalNodeId(schemaNode, parentNodeId) : nodeId;
828
+ const mode = viewMode === "standalone" ? void 0 : viewMode;
829
+ const hasChanged = nodeHasChanged?.({ nodeId: originalNodeId, mode });
830
+ const [isExpanded, setExpanded] = React2.useState(
831
+ !isMirroredNode(schemaNode) && nestingLevel <= defaultExpandedDepth
832
+ );
833
+ const { selectedChoice, setSelectedChoice, choices } = useChoices(schemaNode);
834
+ const typeToShow = selectedChoice.type;
835
+ const description = isRegularNode(typeToShow) ? typeToShow.annotations.description : null;
836
+ const rootLevel = renderRootTreeLines ? 1 : 2;
837
+ const childNodes = React2.useMemo(() => visibleChildren(typeToShow), [typeToShow]);
838
+ const combiner = isRegularNode(schemaNode) && schemaNode.combiners?.length ? schemaNode.combiners[0] : null;
839
+ const isCollapsible = childNodes.length > 0;
840
+ const isRootLevel = nestingLevel < rootLevel;
841
+ const required = isPropertyRequired(schemaNode);
842
+ const deprecated = isRegularNode(schemaNode) && schemaNode.deprecated;
843
+ const validations = isRegularNode(schemaNode) ? schemaNode.validations : {};
844
+ const hasProperties = useHasProperties({ required, deprecated, validations });
845
+ const [totalVendorExtensions, vendorExtensions] = React2.useMemo(
846
+ () => extractVendorExtensions(schemaNode.fragment),
847
+ [schemaNode.fragment]
848
+ );
849
+ const hasVendorProperties = totalVendorExtensions > 0;
850
+ const annotationRootOffset = renderRootTreeLines ? 0 : 8;
851
+ let annotationLeftOffset = -20 - annotationRootOffset;
852
+ if (nestingLevel > 1) {
853
+ annotationLeftOffset = -1 * 29 * Math.max(nestingLevel - 1, 1) - Math.min(nestingLevel, 2) * 2 - 16 - annotationRootOffset;
854
+ if (!renderRootTreeLines) {
855
+ annotationLeftOffset += 27;
856
+ }
857
+ }
858
+ if (parentChangeType === "added" && hasChanged && hasChanged.type === "removed") {
859
+ return null;
860
+ }
861
+ if (parentChangeType === "removed" && hasChanged && hasChanged.type === "added") {
862
+ return null;
863
+ }
864
+ const lastSubpath = schemaNode.subpath.length > 0 ? schemaNode.subpath[schemaNode.subpath.length - 1] : null;
865
+ const isHoveredAtom = isNodeHoveredAtom(schemaNode);
866
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
867
+ /* @__PURE__ */ jsxs(
868
+ "div",
869
+ {
870
+ className: "jsv-row",
871
+ style: { paddingLeft: pl ? `${pl * 0.25}rem` : void 0 },
872
+ "data-id": originalNodeId,
873
+ "data-test": "schema-row",
874
+ onMouseEnter: (e) => {
875
+ e.stopPropagation();
876
+ setHoveredNode(selectedChoice.type);
877
+ },
878
+ children: [
879
+ !isRootLevel && /* @__PURE__ */ jsx("span", { className: clsx("jsv-nubbin", isCollapsible && "jsv-nubbin-short") }),
880
+ parentChangeType !== "added" && parentChangeType !== "removed" ? /* @__PURE__ */ jsx(NodeAnnotation, { change: hasChanged, style: { left: annotationLeftOffset } }) : null,
881
+ /* @__PURE__ */ jsxs("div", { className: clsx("jsv-row-content", isCollapsible && !isRootLevel && "jsv-ml-2"), children: [
882
+ /* @__PURE__ */ jsxs(
883
+ "div",
884
+ {
885
+ className: clsx("jsv-row-header", isCollapsible && "jsv-cursor-pointer"),
886
+ onClick: isCollapsible ? () => setExpanded(!isExpanded) : void 0,
887
+ children: [
888
+ isCollapsible ? /* @__PURE__ */ jsx(Caret, { isExpanded }) : null,
889
+ /* @__PURE__ */ jsxs("div", { className: "jsv-types-container", children: [
890
+ schemaNode.subpath.length > 0 && shouldShowPropertyName(schemaNode) && /* @__PURE__ */ jsx(
891
+ "span",
892
+ {
893
+ className: "jsv-property-name",
894
+ "data-test": `property-name-${lastSubpath}`,
895
+ children: lastSubpath
896
+ }
897
+ ),
898
+ choices.length === 1 && /* @__PURE__ */ jsx(Types, { schemaNode: typeToShow }),
899
+ onGoToRef && isReferenceNode(schemaNode) && schemaNode.external ? /* @__PURE__ */ jsx(
900
+ "a",
901
+ {
902
+ href: "#",
903
+ className: "jsv-link",
904
+ onClick: (e) => {
905
+ e.preventDefault();
906
+ e.stopPropagation();
907
+ onGoToRef(schemaNode);
908
+ },
909
+ children: "(go to ref)"
910
+ }
911
+ ) : null,
912
+ schemaNode.subpath.length > 1 && schemaNode.subpath[0] === "patternProperties" ? /* @__PURE__ */ jsx("span", { className: "jsv-type jsv-ml-2", children: "(pattern property)" }) : null,
913
+ choices.length > 1 && /* @__PURE__ */ jsx(
914
+ Select,
915
+ {
916
+ "aria-label": "Pick a type",
917
+ prefix: combiner ? `${COMBINER_NAME_MAP[combiner]}: ` : void 0,
918
+ options: choices.map((choice, index) => ({
919
+ value: String(index),
920
+ label: choice.title
921
+ })),
922
+ value: String(choices.indexOf(selectedChoice)),
923
+ onChange: (selectedIndex) => setSelectedChoice(choices[Number(selectedIndex)])
924
+ }
925
+ )
926
+ ] }),
927
+ hasProperties && /* @__PURE__ */ jsx(Divider, { hoveredAtom: isHoveredAtom }),
928
+ /* @__PURE__ */ jsx(Properties, { required, deprecated, validations })
929
+ ]
930
+ }
931
+ ),
932
+ typeof description === "string" && (!combiner || schemaNode.parent?.fragment.description !== description) && description.length > 0 && /* @__PURE__ */ jsx(Description, { value: description }),
933
+ /* @__PURE__ */ jsx(
934
+ Validations,
935
+ {
936
+ validations: isRegularNode(schemaNode) ? getValidationsFromSchema(schemaNode) : {},
937
+ hideExamples
938
+ }
939
+ ),
940
+ hasVendorProperties && renderExtensionAddon ? /* @__PURE__ */ jsx("div", { children: renderExtensionAddon({ schemaNode, nestingLevel, vendorExtensions }) }) : null
941
+ ] }),
942
+ /* @__PURE__ */ jsx(Error, { schemaNode }),
943
+ renderRowAddon ? /* @__PURE__ */ jsx("div", { children: renderRowAddon({ schemaNode, nestingLevel }) }) : null
944
+ ]
945
+ }
946
+ ),
947
+ isCollapsible && isExpanded ? /* @__PURE__ */ jsx(
948
+ ChildStack,
949
+ {
950
+ schemaNode,
951
+ childNodes,
952
+ currentNestingLevel: nestingLevel,
953
+ parentNodeId: nodeId,
954
+ parentChangeType: parentChangeType ? parentChangeType : hasChanged ? hasChanged?.type : void 0
955
+ }
956
+ ) : null
957
+ ] });
958
+ }
959
+ );
960
+ SchemaRow.displayName = "SchemaRow";
961
+ function shouldShowPropertyName(schemaNode) {
962
+ return schemaNode.subpath.length === 2 && (schemaNode.subpath[0] === "properties" || schemaNode.subpath[0] === "patternProperties");
963
+ }
964
+ function isEmpty(obj) {
965
+ return Object.keys(obj).length === 0;
966
+ }
967
+ function getStoplightId2(fragment) {
968
+ if (typeof fragment === "boolean") return void 0;
969
+ const xStoplight = fragment["x-stoplight"];
970
+ if (isPlainObject(xStoplight)) {
971
+ const id = xStoplight.id;
972
+ return typeof id === "string" ? id : void 0;
973
+ }
974
+ return void 0;
975
+ }
976
+ var TopLevelSchemaRow = ({
977
+ schemaNode,
978
+ skipDescription
979
+ }) => {
980
+ const { renderExtensionAddon } = useJSVOptionsContext();
981
+ const { selectedChoice, setSelectedChoice, choices } = useChoices(schemaNode);
982
+ const childNodes = React2.useMemo(() => visibleChildren(selectedChoice.type), [selectedChoice.type]);
983
+ const nestingLevel = 0;
984
+ const nodeId = getStoplightId2(schemaNode.fragment);
985
+ const [totalVendorExtensions, vendorExtensions] = React2.useMemo(
986
+ () => extractVendorExtensions(schemaNode.fragment),
987
+ [schemaNode.fragment]
988
+ );
989
+ const hasVendorProperties = totalVendorExtensions > 0;
990
+ if (isRegularNode(schemaNode) && isPureObjectNode(schemaNode)) {
991
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
992
+ !skipDescription ? /* @__PURE__ */ jsx(Description, { value: schemaNode.annotations.description }) : null,
993
+ hasVendorProperties && renderExtensionAddon ? renderExtensionAddon({ schemaNode, nestingLevel, vendorExtensions }) : null,
994
+ /* @__PURE__ */ jsx(
995
+ ChildStack,
996
+ {
997
+ schemaNode,
998
+ childNodes,
999
+ currentNestingLevel: nestingLevel,
1000
+ parentNodeId: nodeId
1001
+ }
1002
+ ),
1003
+ /* @__PURE__ */ jsx(Error, { schemaNode })
1004
+ ] });
1005
+ }
1006
+ if (isRegularNode(schemaNode) && choices.length > 1) {
1007
+ const combiner = isRegularNode(schemaNode) && schemaNode.combiners?.length ? schemaNode.combiners[0] : null;
1008
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1009
+ schemaNode.annotations.description !== schemaNode.parent?.fragment.description && /* @__PURE__ */ jsx(Description, { value: schemaNode.annotations.description }),
1010
+ /* @__PURE__ */ jsxs("div", { className: "jsv-top-section", children: [
1011
+ /* @__PURE__ */ jsxs(Menu.Root, { children: [
1012
+ /* @__PURE__ */ jsxs(Menu.Trigger, { className: "jsv-select-trigger", children: [
1013
+ selectedChoice.title,
1014
+ /* @__PURE__ */ jsx(CaretDown, { size: 12 })
1015
+ ] }),
1016
+ /* @__PURE__ */ jsx(Menu.Portal, { children: /* @__PURE__ */ jsx(Menu.Positioner, { className: "jsv-select-positioner", children: /* @__PURE__ */ jsx(Menu.Popup, { className: "jsv-select-popup", children: choices.map((choice, index) => /* @__PURE__ */ jsx(
1017
+ Menu.Item,
1018
+ {
1019
+ className: "jsv-select-item",
1020
+ onClick: () => setSelectedChoice(choice),
1021
+ children: choice.title
1022
+ },
1023
+ index
1024
+ )) }) }) })
1025
+ ] }),
1026
+ combiner !== null ? /* @__PURE__ */ jsx("span", { className: "jsv-combiner-label", children: `(${COMBINER_NAME_MAP[combiner]})` }) : null
1027
+ ] }),
1028
+ childNodes.length > 0 ? /* @__PURE__ */ jsx(
1029
+ ChildStack,
1030
+ {
1031
+ schemaNode,
1032
+ childNodes,
1033
+ currentNestingLevel: nestingLevel,
1034
+ parentNodeId: nodeId
1035
+ }
1036
+ ) : combiner ? /* @__PURE__ */ jsx(SchemaRow, { schemaNode: selectedChoice.type, nestingLevel }) : null
1037
+ ] });
1038
+ }
1039
+ if (isComplexArray(schemaNode) && isPureObjectNode(schemaNode.children[0])) {
1040
+ const validations = isRegularNode(schemaNode) ? getValidationsFromSchema(schemaNode) : {};
1041
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1042
+ /* @__PURE__ */ jsx(Description, { value: schemaNode.annotations.description }),
1043
+ /* @__PURE__ */ jsx("div", { className: "jsv-top-label", children: "array of:" }),
1044
+ !isEmpty(validations) && /* @__PURE__ */ jsx("div", { className: "jsv-validations", style: { marginBottom: "0.25rem" }, children: /* @__PURE__ */ jsx(Validations, { validations }) }),
1045
+ childNodes.length > 0 ? /* @__PURE__ */ jsx(
1046
+ ChildStack,
1047
+ {
1048
+ schemaNode,
1049
+ childNodes,
1050
+ currentNestingLevel: nestingLevel,
1051
+ parentNodeId: nodeId
1052
+ }
1053
+ ) : null
1054
+ ] });
1055
+ }
1056
+ return /* @__PURE__ */ jsx(SchemaRow, { schemaNode, nestingLevel });
1057
+ };
1058
+ function isPureObjectNode(schemaNode) {
1059
+ return schemaNode.primaryType === "object" && schemaNode.types?.length === 1 && !isDictionaryNode(schemaNode);
1060
+ }
1061
+ var JsonSchemaViewer = ({
1062
+ viewMode = "standalone",
1063
+ defaultExpandedDepth = 1,
1064
+ onGoToRef,
1065
+ renderRowAddon,
1066
+ renderExtensionAddon,
1067
+ hideExamples,
1068
+ renderRootTreeLines,
1069
+ disableCrumbs,
1070
+ nodeHasChanged,
1071
+ skipTopLevelDescription,
1072
+ ...rest
1073
+ }) => {
1074
+ const options = React2.useMemo(
1075
+ () => ({
1076
+ defaultExpandedDepth,
1077
+ viewMode,
1078
+ onGoToRef,
1079
+ renderRowAddon,
1080
+ renderExtensionAddon,
1081
+ hideExamples,
1082
+ renderRootTreeLines,
1083
+ disableCrumbs,
1084
+ nodeHasChanged
1085
+ }),
1086
+ [
1087
+ defaultExpandedDepth,
1088
+ viewMode,
1089
+ onGoToRef,
1090
+ renderRowAddon,
1091
+ renderExtensionAddon,
1092
+ hideExamples,
1093
+ renderRootTreeLines,
1094
+ disableCrumbs,
1095
+ nodeHasChanged
1096
+ ]
1097
+ );
1098
+ return /* @__PURE__ */ jsx(JSVOptionsContextProvider, { value: options, children: /* @__PURE__ */ jsx(Provider, { children: /* @__PURE__ */ jsx(
1099
+ JsonSchemaViewerInner,
1100
+ {
1101
+ viewMode,
1102
+ skipTopLevelDescription,
1103
+ ...rest
1104
+ }
1105
+ ) }) });
1106
+ };
1107
+ var JsonSchemaViewerInner = ({
1108
+ schema,
1109
+ viewMode,
1110
+ className,
1111
+ resolveRef,
1112
+ maxRefDepth,
1113
+ emptyText = "No schema defined",
1114
+ onTreePopulated,
1115
+ maxHeight,
1116
+ parentCrumbs,
1117
+ skipTopLevelDescription,
1118
+ "data-theme": dataTheme
1119
+ }) => {
1120
+ const setHoveredNode = useSetAtom(hoveredNodeAtom);
1121
+ const onMouseLeave = React2.useCallback(() => {
1122
+ setHoveredNode(null);
1123
+ }, [setHoveredNode]);
1124
+ const { jsonSchemaTreeRoot, nodeCount } = React2.useMemo(() => {
1125
+ const jsonSchemaTree = new SchemaTree(schema, {
1126
+ mergeAllOf: true,
1127
+ refResolver: resolveRef,
1128
+ maxRefDepth
1129
+ });
1130
+ let nodeCount2 = 0;
1131
+ jsonSchemaTree.walker.hookInto("filter", (node) => {
1132
+ if (shouldNodeBeIncluded(node, viewMode)) {
1133
+ nodeCount2++;
1134
+ return true;
1135
+ }
1136
+ return false;
1137
+ });
1138
+ jsonSchemaTree.populate();
1139
+ return {
1140
+ jsonSchemaTreeRoot: jsonSchemaTree.root,
1141
+ nodeCount: nodeCount2
1142
+ };
1143
+ }, [schema, resolveRef, maxRefDepth, viewMode]);
1144
+ React2.useEffect(() => {
1145
+ onTreePopulated?.({
1146
+ rootNode: jsonSchemaTreeRoot,
1147
+ nodeCount
1148
+ });
1149
+ }, [jsonSchemaTreeRoot, onTreePopulated, nodeCount]);
1150
+ const isEmpty2 = React2.useMemo(
1151
+ () => jsonSchemaTreeRoot.children.every((node) => !isRegularNode(node) || node.unknown),
1152
+ [jsonSchemaTreeRoot]
1153
+ );
1154
+ if (isEmpty2) {
1155
+ return /* @__PURE__ */ jsx("div", { className: clsx("jsv-root", className), "data-test": "empty-text", "data-theme": dataTheme, children: emptyText });
1156
+ }
1157
+ return /* @__PURE__ */ jsxs(
1158
+ "div",
1159
+ {
1160
+ className: clsx("jsv-root", className),
1161
+ onMouseLeave,
1162
+ style: { maxHeight, ...maxHeight ? { overflowY: "auto" } : {} },
1163
+ "data-theme": dataTheme,
1164
+ children: [
1165
+ /* @__PURE__ */ jsx(PathCrumbs, { parentCrumbs }),
1166
+ /* @__PURE__ */ jsx("div", { className: "jsv-content", children: /* @__PURE__ */ jsx(TopLevelSchemaRow, { schemaNode: jsonSchemaTreeRoot.children[0], skipDescription: skipTopLevelDescription }) })
1167
+ ]
1168
+ }
1169
+ );
1170
+ };
1171
+ var JsonSchemaViewerErrorBoundary = class extends React2.Component {
1172
+ constructor(props) {
1173
+ super(props);
1174
+ this.state = { hasError: false, error: null };
1175
+ }
1176
+ static getDerivedStateFromError(error) {
1177
+ return { hasError: true, error };
1178
+ }
1179
+ render() {
1180
+ if (this.state.hasError) {
1181
+ if (this.props.fallback) {
1182
+ return this.props.fallback;
1183
+ }
1184
+ return /* @__PURE__ */ jsxs("div", { style: { padding: "1rem" }, children: [
1185
+ /* @__PURE__ */ jsx("strong", { style: { color: "var(--jsv-color-danger, #ef4444)" }, children: "Error" }),
1186
+ this.state.error !== null ? `: ${this.state.error.message}` : null
1187
+ ] });
1188
+ }
1189
+ return this.props.children;
1190
+ }
1191
+ };
1192
+
1193
+ export { JsonSchemaViewer, JsonSchemaViewerErrorBoundary, Validations, getValidationsFromSchema, useChoices, visibleChildren };
1194
+ //# sourceMappingURL=index.js.map
1195
+ //# sourceMappingURL=index.js.map