@stoplight/elements-core 7.6.5 → 7.7.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/index.mjs CHANGED
@@ -1,13 +1,13 @@
1
1
  import { __rest, __awaiter } from 'tslib';
2
2
  import * as React from 'react';
3
3
  import React__default, { useContext, useMemo } from 'react';
4
- import { resolveInlineRef, isPlainObject as isPlainObject$1, safeParse, safeStringify } from '@stoplight/json';
4
+ import { resolveInlineRef, hasRef, isPlainObject as isPlainObject$1, safeParse, safeStringify } from '@stoplight/json';
5
5
  import isArray from 'lodash/isArray.js';
6
- import isObject from 'lodash/isObject.js';
7
6
  import isPlainObject from 'lodash/isPlainObject.js';
8
7
  import { NodeType, HttpParamStyles } from '@stoplight/types';
9
8
  import { parse } from '@stoplight/yaml';
10
- import { isArray as isArray$1, Box, Panel, CopyButton, Menu, Button, Text, Flex, Input, Icon, Select, FieldButton, Image, Link, useThemeIsDark, HStack, VStack, InvertTheme, Tooltip, Badge, LinkHeading, Tabs, TabList, Tab, TabPanels, TabPanel, Heading, useClipboard, useBreakpoints, useMosaicContext, Provider as Provider$1 } from '@stoplight/mosaic';
9
+ import { isArray as isArray$1, Box, Panel, CopyButton, Menu, Button, Text, Flex, Input, Icon, Select, FieldButton, Image, Link, useThemeIsDark, HStack, VStack, InvertTheme, Tooltip, Badge, LinkHeading as LinkHeading$1, NodeAnnotation, Tabs, TabList, Tab, TabPanels, TabPanel, Heading, useClipboard, useBreakpoints, useMosaicContext, Provider as Provider$1 } from '@stoplight/mosaic';
10
+ import isObject from 'lodash/isObject.js';
11
11
  import { withErrorBoundary } from '@stoplight/react-error-boundary';
12
12
  import { MarkdownViewer as MarkdownViewer$1, DefaultSMDComponents, MarkdownViewerProvider } from '@stoplight/markdown-viewer';
13
13
  export { DefaultSMDComponents } from '@stoplight/markdown-viewer';
@@ -37,9 +37,9 @@ import uniqBy from 'lodash/uniqBy.js';
37
37
  import formatXml from 'xml-formatter';
38
38
  import entries from 'lodash/entries.js';
39
39
  import keys from 'lodash/keys.js';
40
+ import { useLocation, BrowserRouter, MemoryRouter, HashRouter, StaticRouter, Route } from 'react-router-dom';
40
41
  import { JsonSchemaViewer } from '@stoplight/json-schema-viewer';
41
42
  import sortBy from 'lodash/sortBy.js';
42
- import { useLocation, BrowserRouter, MemoryRouter, HashRouter, StaticRouter, Route } from 'react-router-dom';
43
43
  import { HashLink } from 'react-router-hash-link';
44
44
  import { QueryClient, useQueryClient, QueryClientProvider } from 'react-query';
45
45
  import $RefParser from '@stoplight/json-schema-ref-parser';
@@ -105,7 +105,7 @@ const isResolvedObjectProxy = (someObject) => {
105
105
  const getOriginalObject = (resolvedObject) => {
106
106
  return resolvedObject[originalObjectSymbol] || resolvedObject;
107
107
  };
108
- const isReference = (value) => isObject(value) && typeof value['$ref'] === 'string';
108
+ const isReference = hasRef;
109
109
 
110
110
  const InlineRefResolverContext = React.createContext(undefined);
111
111
  InlineRefResolverContext.displayName = 'InlineRefResolverContext';
@@ -125,6 +125,15 @@ const useResolvedObject = (currentObject) => {
125
125
  return React.useMemo(() => createResolvedObject(currentObject, { contextObject: document, resolver }), [currentObject, document, resolver]);
126
126
  };
127
127
 
128
+ const DEFAULT_CONTEXT = {};
129
+ const ElementsOptionsContext = React.createContext(DEFAULT_CONTEXT);
130
+ const useOptionsCtx = () => {
131
+ return React.useContext(ElementsOptionsContext) || DEFAULT_CONTEXT;
132
+ };
133
+ function ElementsOptionsProvider({ children, nodeHasChanged }) {
134
+ return (React.createElement(ElementsOptionsContext.Provider, { value: Object.assign({}, DEFAULT_CONTEXT, { nodeHasChanged }) }, children));
135
+ }
136
+
128
137
  function isSMDASTRoot(maybeAst) {
129
138
  return isObject(maybeAst) && maybeAst['type'] === 'root' && isArray$1(maybeAst['children']);
130
139
  }
@@ -158,6 +167,7 @@ const parserMap = {
158
167
  [NodeType.TableOfContents]: parseUnknown,
159
168
  [NodeType.SpectralRuleset]: parseUnknown,
160
169
  [NodeType.Styleguide]: parseUnknown,
170
+ [NodeType.Image]: parseUnknown,
161
171
  [NodeType.Unknown]: parseUnknown,
162
172
  };
163
173
  function parseArticleData(rawData) {
@@ -271,6 +281,11 @@ var faEye = {
271
281
  iconName: 'eye',
272
282
  icon: [576, 512, [128065], "f06e", "M279.6 160.4C282.4 160.1 285.2 160 288 160C341 160 384 202.1 384 256C384 309 341 352 288 352C234.1 352 192 309 192 256C192 253.2 192.1 250.4 192.4 247.6C201.7 252.1 212.5 256 224 256C259.3 256 288 227.3 288 192C288 180.5 284.1 169.7 279.6 160.4zM480.6 112.6C527.4 156 558.7 207.1 573.5 243.7C576.8 251.6 576.8 260.4 573.5 268.3C558.7 304 527.4 355.1 480.6 399.4C433.5 443.2 368.8 480 288 480C207.2 480 142.5 443.2 95.42 399.4C48.62 355.1 17.34 304 2.461 268.3C-.8205 260.4-.8205 251.6 2.461 243.7C17.34 207.1 48.62 156 95.42 112.6C142.5 68.84 207.2 32 288 32C368.8 32 433.5 68.84 480.6 112.6V112.6zM288 112C208.5 112 144 176.5 144 256C144 335.5 208.5 400 288 400C367.5 400 432 335.5 432 256C432 176.5 367.5 112 288 112z"]
273
283
  };
284
+ var faImage = {
285
+ prefix: 'fas',
286
+ iconName: 'image',
287
+ icon: [512, 512, [], "f03e", "M447.1 32h-384C28.64 32-.0091 60.65-.0091 96v320c0 35.35 28.65 64 63.1 64h384c35.35 0 64-28.65 64-64V96C511.1 60.65 483.3 32 447.1 32zM111.1 96c26.51 0 48 21.49 48 48S138.5 192 111.1 192s-48-21.49-48-48S85.48 96 111.1 96zM446.1 407.6C443.3 412.8 437.9 416 432 416H82.01c-6.021 0-11.53-3.379-14.26-8.75c-2.73-5.367-2.215-11.81 1.334-16.68l70-96C142.1 290.4 146.9 288 152 288s9.916 2.441 12.93 6.574l32.46 44.51l93.3-139.1C293.7 194.7 298.7 192 304 192s10.35 2.672 13.31 7.125l128 192C448.6 396 448.9 402.3 446.1 407.6z"]
288
+ };
274
289
  var faServer = {
275
290
  prefix: 'fas',
276
291
  iconName: 'server',
@@ -288,6 +303,7 @@ const NodeTypeColors = {
288
303
  table_of_contents: '',
289
304
  spectral_ruleset: '',
290
305
  styleguide: '',
306
+ image: '',
291
307
  };
292
308
  const NodeTypePrettyName = {
293
309
  http_operation: 'Endpoint',
@@ -300,6 +316,7 @@ const NodeTypePrettyName = {
300
316
  table_of_contents: '',
301
317
  spectral_ruleset: '',
302
318
  styleguide: '',
319
+ image: '',
303
320
  };
304
321
  const NodeTypeIconDefs = {
305
322
  http_operation: faCrosshairs,
@@ -312,6 +329,7 @@ const NodeTypeIconDefs = {
312
329
  table_of_contents: faQuestionCircle,
313
330
  spectral_ruleset: faQuestionCircle,
314
331
  styleguide: faQuestionCircle,
332
+ image: faImage,
315
333
  };
316
334
  const HttpMethodColors = {
317
335
  get: 'success',
@@ -439,6 +457,7 @@ const getServersToDisplay = (originalServers, mockUrl) => {
439
457
  .filter(isValidServer);
440
458
  if (mockUrl) {
441
459
  servers.push({
460
+ id: 'mock',
442
461
  description: 'Mock Server',
443
462
  url: mockUrl,
444
463
  });
@@ -628,15 +647,12 @@ const requestSampleConfigs = {
628
647
  mosaicCodeViewerLanguage: 'php',
629
648
  httpSnippetLanguage: 'php',
630
649
  libraries: {
631
- 'pecl/http 1': {
632
- httpSnippetLibrary: 'http1',
633
- },
634
- 'pecl/http 2': {
635
- httpSnippetLibrary: 'http2',
636
- },
637
650
  cURL: {
638
651
  httpSnippetLibrary: 'curl',
639
652
  },
653
+ guzzle: {
654
+ httpSnippetLibrary: 'guzzle',
655
+ },
640
656
  },
641
657
  },
642
658
  Powershell: {
@@ -999,9 +1015,12 @@ function parameterSupportsFileUpload(parameter) {
999
1015
  (((_b = parameter.schema) === null || _b === void 0 ? void 0 : _b.contentEncoding) === 'base64' ||
1000
1016
  ((_c = parameter.schema) === null || _c === void 0 ? void 0 : _c.contentMediaType) === 'application/octet-stream'));
1001
1017
  }
1018
+ function stringifyValue(value) {
1019
+ return typeof value === 'object' ? JSON.stringify(value) : escapeQuotes(String(value));
1020
+ }
1002
1021
  function exampleValue(example) {
1003
1022
  const value = 'value' in example ? example.value : example.externalValue;
1004
- return escapeQuotes(String(value));
1023
+ return stringifyValue(value);
1005
1024
  }
1006
1025
  function escapeQuotes(value) {
1007
1026
  return value.replace(/"/g, '\\"');
@@ -1022,7 +1041,7 @@ const getValueForParameter = (parameter) => {
1022
1041
  var _a, _b, _c;
1023
1042
  const defaultValue = retrieveDefaultFromSchema(parameter);
1024
1043
  if (typeof defaultValue !== 'undefined') {
1025
- return { value: String(defaultValue), isDefault: true };
1044
+ return { value: stringifyValue(defaultValue), isDefault: true };
1026
1045
  }
1027
1046
  const examples = (_a = parameter.examples) !== null && _a !== void 0 ? _a : [];
1028
1047
  if (examples.length > 0) {
@@ -1030,7 +1049,7 @@ const getValueForParameter = (parameter) => {
1030
1049
  }
1031
1050
  const enums = (_c = (_b = parameter.schema) === null || _b === void 0 ? void 0 : _b.enum) !== null && _c !== void 0 ? _c : [];
1032
1051
  if (enums.length > 0) {
1033
- return { value: String(enums[0]) };
1052
+ return { value: stringifyValue(enums[0]) };
1034
1053
  }
1035
1054
  return { value: '' };
1036
1055
  };
@@ -1315,13 +1334,77 @@ const getServerUrl = ({ chosenServer, httpOperation, mockData, corsProxy, }) =>
1315
1334
  }
1316
1335
  return serverUrl;
1317
1336
  };
1337
+ const getQueryParams = ({ httpOperation, parameterValues, }) => {
1338
+ var _a;
1339
+ const query = (_a = httpOperation.request) === null || _a === void 0 ? void 0 : _a.query;
1340
+ if (!query)
1341
+ return [];
1342
+ return query.reduce((acc, param) => {
1343
+ var _a, _b, _c, _d, _e;
1344
+ const value = (_a = parameterValues[param.name]) !== null && _a !== void 0 ? _a : '';
1345
+ if (value.length === 0)
1346
+ return acc;
1347
+ const explode = (_b = param.explode) !== null && _b !== void 0 ? _b : true;
1348
+ if (((_c = param.schema) === null || _c === void 0 ? void 0 : _c.type) === 'object' && param.style === 'form' && value) {
1349
+ let nested;
1350
+ try {
1351
+ nested = JSON.parse(value);
1352
+ if (!(typeof nested === 'object' && nested !== null))
1353
+ throw Error();
1354
+ }
1355
+ catch (e) {
1356
+ throw new Error(`Cannot use param value "${value}". JSON object expected.`);
1357
+ }
1358
+ if (explode) {
1359
+ acc.push(...Object.entries(nested).map(([name, value]) => ({ name, value: value.toString() })));
1360
+ }
1361
+ else {
1362
+ acc.push({
1363
+ name: param.name,
1364
+ value: Object.entries(nested)
1365
+ .map(entry => entry.join(','))
1366
+ .join(','),
1367
+ });
1368
+ }
1369
+ }
1370
+ else if (((_d = param.schema) === null || _d === void 0 ? void 0 : _d.type) === 'array' && value) {
1371
+ let nested;
1372
+ try {
1373
+ nested = JSON.parse(value);
1374
+ if (!Array.isArray(nested))
1375
+ throw Error();
1376
+ }
1377
+ catch (e) {
1378
+ throw new Error(`Cannot use param value "${value}". JSON array expected.`);
1379
+ }
1380
+ if (explode) {
1381
+ acc.push(...nested.map(value => ({ name: param.name, value: value.toString() })));
1382
+ }
1383
+ else {
1384
+ const delimiter = {
1385
+ [HttpParamStyles.Form]: ',',
1386
+ [HttpParamStyles.SpaceDelimited]: ' ',
1387
+ [HttpParamStyles.PipeDelimited]: '|',
1388
+ };
1389
+ acc.push({
1390
+ name: param.name,
1391
+ value: nested.join((_e = delimiter[param.style]) !== null && _e !== void 0 ? _e : delimiter[HttpParamStyles.Form]),
1392
+ });
1393
+ }
1394
+ }
1395
+ else {
1396
+ acc.push({ name: param.name, value });
1397
+ }
1398
+ return acc;
1399
+ }, []);
1400
+ };
1318
1401
  function buildFetchRequest({ httpOperation, mediaTypeContent, bodyInput, parameterValues, mockData, auth, chosenServer, credentials = 'omit', corsProxy, }) {
1319
- var _a, _b, _c, _d, _e, _f;
1402
+ var _a, _b, _c;
1320
1403
  return __awaiter(this, void 0, void 0, function* () {
1321
1404
  const serverUrl = getServerUrl({ httpOperation, mockData, chosenServer, corsProxy });
1322
1405
  const shouldIncludeBody = ['PUT', 'POST', 'PATCH'].includes(httpOperation.method.toUpperCase());
1323
- const queryParams = (_c = (_b = (_a = httpOperation.request) === null || _a === void 0 ? void 0 : _a.query) === null || _b === void 0 ? void 0 : _b.map(param => { var _a; return ({ name: param.name, value: (_a = parameterValues[param.name]) !== null && _a !== void 0 ? _a : '' }); }).filter(({ value }) => value.length > 0)) !== null && _c !== void 0 ? _c : [];
1324
- const rawHeaders = filterOutAuthorizationParams((_e = (_d = httpOperation.request) === null || _d === void 0 ? void 0 : _d.headers) !== null && _e !== void 0 ? _e : [], httpOperation.security)
1406
+ const queryParams = getQueryParams({ httpOperation, parameterValues });
1407
+ const rawHeaders = filterOutAuthorizationParams((_b = (_a = httpOperation.request) === null || _a === void 0 ? void 0 : _a.headers) !== null && _b !== void 0 ? _b : [], httpOperation.security)
1325
1408
  .map(header => { var _a; return ({ name: header.name, value: (_a = parameterValues[header.name]) !== null && _a !== void 0 ? _a : '' }); })
1326
1409
  .filter(({ value }) => value.length > 0);
1327
1410
  const [queryParamsWithAuth, headersWithAuth] = runAuthRequestEhancements(auth, queryParams, rawHeaders);
@@ -1330,7 +1413,7 @@ function buildFetchRequest({ httpOperation, mediaTypeContent, bodyInput, paramet
1330
1413
  urlObject.search = new URLSearchParams(queryParamsWithAuth.map(nameAndValueObjectToPair)).toString();
1331
1414
  const body = typeof bodyInput === 'object' ? yield createRequestBody(mediaTypeContent, bodyInput) : bodyInput;
1332
1415
  const headers = Object.assign(Object.assign(Object.assign({}, ((mediaTypeContent === null || mediaTypeContent === void 0 ? void 0 : mediaTypeContent.mediaType) !== 'multipart/form-data' && {
1333
- 'Content-Type': (_f = mediaTypeContent === null || mediaTypeContent === void 0 ? void 0 : mediaTypeContent.mediaType) !== null && _f !== void 0 ? _f : 'application/json',
1416
+ 'Content-Type': (_c = mediaTypeContent === null || mediaTypeContent === void 0 ? void 0 : mediaTypeContent.mediaType) !== null && _c !== void 0 ? _c : 'application/json',
1334
1417
  })), Object.fromEntries(headersWithAuth.map(nameAndValueObjectToPair))), mockData === null || mockData === void 0 ? void 0 : mockData.header);
1335
1418
  return [
1336
1419
  urlObject.href,
@@ -1390,13 +1473,13 @@ const runAuthRequestEhancements = (auth, queryParams, headers) => {
1390
1473
  return [newQueryParams, newHeaders];
1391
1474
  };
1392
1475
  function buildHarRequest({ httpOperation, bodyInput, parameterValues, mediaTypeContent, auth, mockData, chosenServer, corsProxy, }) {
1393
- var _a, _b, _c, _d, _e, _f, _g;
1476
+ var _a, _b, _c, _d;
1394
1477
  return __awaiter(this, void 0, void 0, function* () {
1395
1478
  const serverUrl = getServerUrl({ httpOperation, mockData, chosenServer, corsProxy });
1396
1479
  const mimeType = (_a = mediaTypeContent === null || mediaTypeContent === void 0 ? void 0 : mediaTypeContent.mediaType) !== null && _a !== void 0 ? _a : 'application/json';
1397
1480
  const shouldIncludeBody = ['PUT', 'POST', 'PATCH'].includes(httpOperation.method.toUpperCase());
1398
- const queryParams = (_d = (_c = (_b = httpOperation.request) === null || _b === void 0 ? void 0 : _b.query) === null || _c === void 0 ? void 0 : _c.map(param => { var _a; return ({ name: param.name, value: (_a = parameterValues[param.name]) !== null && _a !== void 0 ? _a : '' }); }).filter(({ value }) => value.length > 0)) !== null && _d !== void 0 ? _d : [];
1399
- const headerParams = (_g = (_f = (_e = httpOperation.request) === null || _e === void 0 ? void 0 : _e.headers) === null || _f === void 0 ? void 0 : _f.map(header => { var _a; return ({ name: header.name, value: (_a = parameterValues[header.name]) !== null && _a !== void 0 ? _a : '' }); })) !== null && _g !== void 0 ? _g : [];
1481
+ const queryParams = getQueryParams({ httpOperation, parameterValues });
1482
+ const headerParams = (_d = (_c = (_b = httpOperation.request) === null || _b === void 0 ? void 0 : _b.headers) === null || _c === void 0 ? void 0 : _c.map(header => { var _a; return ({ name: header.name, value: (_a = parameterValues[header.name]) !== null && _a !== void 0 ? _a : '' }); })) !== null && _d !== void 0 ? _d : [];
1400
1483
  if (mockData === null || mockData === void 0 ? void 0 : mockData.header) {
1401
1484
  headerParams.push({ name: 'Prefer', value: mockData.header.Prefer });
1402
1485
  }
@@ -2005,6 +2088,17 @@ ${scopes.map(([key, value]) => `- \`${key}\` - ${value}`).join('\n')}`;
2005
2088
  return description;
2006
2089
  }
2007
2090
 
2091
+ const RouterTypeContext = React.createContext('history');
2092
+
2093
+ const LinkHeading = React.memo(function LinkHeading(_a) {
2094
+ var { id: _id } = _a, props = __rest(_a, ["id"]);
2095
+ const { pathname } = useLocation();
2096
+ const routerKind = React.useContext(RouterTypeContext);
2097
+ const route = pathname.split('#')[0];
2098
+ const id = routerKind === 'hash' ? `${route}#${_id}` : _id;
2099
+ return React.createElement(LinkHeading$1, Object.assign({ id: id }, props));
2100
+ });
2101
+
2008
2102
  const SectionTitle = ({ title, id, size = 2, children }) => {
2009
2103
  return (React.createElement(HStack, { spacing: 6 },
2010
2104
  React.createElement(Box, { as: LinkHeading, size: size, "aria-label": title, id: id || slugify(title) }, title),
@@ -2030,6 +2124,7 @@ const Body = ({ body, onChange }) => {
2030
2124
  var _a;
2031
2125
  const refResolver = useInlineRefResolver();
2032
2126
  const [chosenContent, setChosenContent] = React.useState(0);
2127
+ const { nodeHasChanged } = useOptionsCtx();
2033
2128
  React.useEffect(() => {
2034
2129
  onChange(chosenContent);
2035
2130
  }, [chosenContent]);
@@ -2037,11 +2132,14 @@ const Body = ({ body, onChange }) => {
2037
2132
  return null;
2038
2133
  const { contents = [], description } = body;
2039
2134
  const schema = (_a = contents[chosenContent]) === null || _a === void 0 ? void 0 : _a.schema;
2135
+ const descriptionChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: body.id, attr: 'description' });
2040
2136
  return (React.createElement(VStack, { spacing: 6 },
2041
2137
  React.createElement(SectionSubtitle, { title: "Body", id: "request-body" }, contents.length > 0 && (React.createElement(Flex, { flex: 1, justify: "end" },
2042
2138
  React.createElement(Select, { "aria-label": "Request Body Content Type", value: String(chosenContent), onChange: (value) => setChosenContent(parseInt(String(value), 10)), options: contents.map((content, index) => ({ label: content.mediaType, value: index })), size: "sm" })))),
2043
- description && React.createElement(MarkdownViewer, { markdown: description }),
2044
- isJSONSchema(schema) && (React.createElement(JsonSchemaViewer, { resolveRef: refResolver, schema: getOriginalObject(schema), viewMode: "write", renderRootTreeLines: true }))));
2139
+ description && (React.createElement(Box, { pos: "relative" },
2140
+ React.createElement(MarkdownViewer, { markdown: description }),
2141
+ React.createElement(NodeAnnotation, { change: descriptionChanged }))),
2142
+ isJSONSchema(schema) && (React.createElement(JsonSchemaViewer, { resolveRef: refResolver, schema: getOriginalObject(schema), viewMode: "write", renderRootTreeLines: true, nodeHasChanged: nodeHasChanged }))));
2045
2143
  };
2046
2144
  Body.displayName = 'HttpOperation.Body';
2047
2145
 
@@ -2065,14 +2163,16 @@ const defaultStyle = {
2065
2163
  cookie: HttpParamStyles.Form,
2066
2164
  };
2067
2165
  const Parameters = ({ parameters, parameterType }) => {
2166
+ const { nodeHasChanged } = useOptionsCtx();
2167
+ const refResolver = useInlineRefResolver();
2068
2168
  const schema = React.useMemo(() => httpOperationParamsToSchema({ parameters, parameterType }), [parameters, parameterType]);
2069
2169
  if (!schema)
2070
2170
  return null;
2071
- return React.createElement(JsonSchemaViewer, { schema: schema, disableCrumbs: true });
2171
+ return React.createElement(JsonSchemaViewer, { resolveRef: refResolver, schema: schema, disableCrumbs: true, nodeHasChanged: nodeHasChanged });
2072
2172
  };
2073
2173
  Parameters.displayName = 'HttpOperation.Parameters';
2074
2174
  const httpOperationParamsToSchema = ({ parameters, parameterType }) => {
2075
- var _a;
2175
+ var _a, _b, _c;
2076
2176
  if (!parameters || !parameters.length)
2077
2177
  return null;
2078
2178
  const schema = {
@@ -2081,8 +2181,6 @@ const httpOperationParamsToSchema = ({ parameters, parameterType }) => {
2081
2181
  };
2082
2182
  const sortedParams = sortBy(parameters, ['required', 'name']);
2083
2183
  for (const p of sortedParams) {
2084
- if (!p.schema)
2085
- continue;
2086
2184
  const { name, description, required, deprecated, examples, style } = p;
2087
2185
  const paramExamples = (examples === null || examples === void 0 ? void 0 : examples.map(example => {
2088
2186
  if (isNodeExample(example)) {
@@ -2092,10 +2190,10 @@ const httpOperationParamsToSchema = ({ parameters, parameterType }) => {
2092
2190
  })) || [];
2093
2191
  const schemaExamples = (_a = p.schema) === null || _a === void 0 ? void 0 : _a.examples;
2094
2192
  const schemaExamplesArray = Array.isArray(schemaExamples) ? schemaExamples : [];
2095
- const paramDescription = description || p.schema.description;
2193
+ const paramDescription = description || ((_b = p.schema) === null || _b === void 0 ? void 0 : _b.description);
2096
2194
  const paramDeprecated = deprecated || p.schema.deprecated;
2097
2195
  const paramStyle = style && defaultStyle[parameterType] !== style ? readableStyles[style] || style : undefined;
2098
- schema.properties[p.name] = Object.assign(Object.assign({}, p.schema), { description: paramDescription, examples: [...paramExamples, ...schemaExamplesArray], deprecated: paramDeprecated, style: paramStyle });
2196
+ schema.properties[p.name] = Object.assign(Object.assign({}, (p.schema || {})), { description: paramDescription, examples: [...paramExamples, ...schemaExamplesArray], deprecated: paramDeprecated, style: paramStyle, 'x-stoplight': Object.assign(Object.assign({}, (((_c = p.schema) === null || _c === void 0 ? void 0 : _c['x-stoplight']) || {})), { id: p.id }) });
2099
2197
  if (required) {
2100
2198
  schema.required.push(name);
2101
2199
  }
@@ -2118,7 +2216,7 @@ const Request = ({ operation: { request, request: { path: pathParams = [], heade
2118
2216
  return null;
2119
2217
  return (React.createElement(VStack, { spacing: 8 },
2120
2218
  React.createElement(SectionTitle, { title: "Request" }),
2121
- securitySchemes.length > 0 && (React.createElement(VStack, { spacing: 3 }, securitySchemes.map((scheme, i) => (React.createElement(SecurityPanel, { key: i, scheme: scheme, includeKey: shouldIncludeKey(securitySchemes, scheme.type) }))))),
2219
+ React.createElement(SecuritySchemes$1, { schemes: securitySchemes }),
2122
2220
  pathParams.length > 0 && (React.createElement(VStack, { spacing: 5 },
2123
2221
  React.createElement(SectionSubtitle, { title: "Path Parameters" }),
2124
2222
  React.createElement(Parameters, { parameterType: "path", parameters: pathParams }))),
@@ -2139,6 +2237,15 @@ const SecurityPanel = ({ scheme, includeKey }) => {
2139
2237
  const [expandedState, setExpanded] = useAtom(schemeExpandedState);
2140
2238
  return (React.createElement(SubSectionPanel, { title: `Security: ${getReadableSecurityName(scheme, includeKey)}`, defaultIsOpen: !!expandedState[scheme.key], onChange: isOpen => setExpanded(Object.assign(Object.assign({}, expandedState), { [scheme.key]: isOpen })) },
2141
2239
  React.createElement(MarkdownViewer, { style: { fontSize: 12 }, markdown: `${scheme.description || ''}\n\n` + getDefaultDescription(scheme) })));
2240
+ };
2241
+ const SecuritySchemes$1 = ({ schemes }) => {
2242
+ const { nodeHasChanged } = useOptionsCtx();
2243
+ if (!schemes.length) {
2244
+ return null;
2245
+ }
2246
+ return (React.createElement(VStack, { spacing: 3 }, schemes.map((scheme, i) => (React.createElement(Box, { pos: "relative", key: i },
2247
+ React.createElement(SecurityPanel, { scheme: scheme, includeKey: shouldIncludeKey(schemes, scheme.type) }),
2248
+ React.createElement(NodeAnnotation, { change: nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: scheme.id }) }))))));
2142
2249
  };
2143
2250
 
2144
2251
  const Responses = ({ responses: unsortedResponses, onStatusCodeChange, onMediaTypeChange }) => {
@@ -2161,13 +2268,17 @@ const Response = ({ response, onMediaTypeChange }) => {
2161
2268
  const { contents = [], headers = [], description } = response;
2162
2269
  const [chosenContent, setChosenContent] = React.useState(0);
2163
2270
  const refResolver = useInlineRefResolver();
2271
+ const { nodeHasChanged } = useOptionsCtx();
2164
2272
  const responseContent = contents[chosenContent];
2165
2273
  const schema = responseContent === null || responseContent === void 0 ? void 0 : responseContent.schema;
2166
2274
  React.useEffect(() => {
2167
2275
  responseContent && onMediaTypeChange(responseContent.mediaType);
2168
2276
  }, [responseContent]);
2277
+ const descriptionChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: response.id, attr: 'description' });
2169
2278
  return (React.createElement(VStack, { spacing: 8, pt: 8 },
2170
- description && React.createElement(MarkdownViewer, { markdown: description }),
2279
+ description && (React.createElement(Box, { pos: "relative" },
2280
+ React.createElement(MarkdownViewer, { markdown: description }),
2281
+ React.createElement(NodeAnnotation, { change: descriptionChanged }))),
2171
2282
  headers.length > 0 && (React.createElement(VStack, { spacing: 5 },
2172
2283
  React.createElement(SectionSubtitle, { title: "Headers", id: "response-headers" }),
2173
2284
  React.createElement(Parameters, { parameterType: "header", parameters: headers }))),
@@ -2175,7 +2286,7 @@ const Response = ({ response, onMediaTypeChange }) => {
2175
2286
  React.createElement(SectionSubtitle, { title: "Body", id: "response-body" },
2176
2287
  React.createElement(Flex, { flex: 1, justify: "end" },
2177
2288
  React.createElement(Select, { "aria-label": "Response Body Content Type", value: String(chosenContent), onChange: (value) => setChosenContent(parseInt(String(value), 10)), options: contents.map((content, index) => ({ label: content.mediaType, value: index })), size: "sm" }))),
2178
- schema && (React.createElement(JsonSchemaViewer, { schema: getOriginalObject(schema), resolveRef: refResolver, viewMode: "read", parentCrumbs: ['responses', response.code], renderRootTreeLines: true }))))));
2289
+ schema && (React.createElement(JsonSchemaViewer, { schema: getOriginalObject(schema), resolveRef: refResolver, viewMode: "read", parentCrumbs: ['responses', response.code], renderRootTreeLines: true, nodeHasChanged: nodeHasChanged }))))));
2179
2290
  };
2180
2291
  Response.displayName = 'HttpOperation.Response';
2181
2292
  const codeToIntentVal = (code) => {
@@ -2193,6 +2304,7 @@ const codeToIntentVal = (code) => {
2193
2304
  };
2194
2305
 
2195
2306
  const HttpOperationComponent = React.memo(({ className, data: unresolvedData, layoutOptions, tryItCredentialsPolicy, tryItCorsProxy }) => {
2307
+ const { nodeHasChanged } = useOptionsCtx();
2196
2308
  const data = useResolvedObject(unresolvedData);
2197
2309
  const mocking = React.useContext(MockingContext);
2198
2310
  const isDeprecated = !!data.deprecated;
@@ -2202,15 +2314,12 @@ const HttpOperationComponent = React.memo(({ className, data: unresolvedData, la
2202
2314
  const [requestBodyIndex, setTextRequestBodyIndex] = React.useState(0);
2203
2315
  const prettyName = (data.summary || data.iid || '').trim();
2204
2316
  const hasBadges = isDeprecated || isInternal;
2205
- const header = (!(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.noHeading) || hasBadges) && (React.createElement(VStack, { spacing: 5 },
2206
- React.createElement(HStack, { spacing: 5 },
2207
- !(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.noHeading) && prettyName ? (React.createElement(Heading, { size: 1, fontWeight: "semibold" }, prettyName)) : null,
2208
- React.createElement(HStack, { spacing: 2 },
2209
- isDeprecated && React.createElement(DeprecatedBadge, null),
2210
- isInternal && React.createElement(InternalBadge, { isHttpService: true }))),
2211
- React.createElement(MethodPath, { method: data.method, path: data.path })));
2317
+ const header = (React.createElement(OperationHeader, { id: data.id, method: data.method, path: data.path, noHeading: layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.noHeading, hasBadges: hasBadges, name: prettyName, isDeprecated: isDeprecated, isInternal: isInternal }));
2318
+ const descriptionChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: data.id, attr: 'description' });
2212
2319
  const description = (React.createElement(VStack, { spacing: 10 },
2213
- data.description && React.createElement(MarkdownViewer, { className: "HttpOperation__Description", markdown: data.description }),
2320
+ data.description && (React.createElement(Box, { pos: "relative" },
2321
+ React.createElement(MarkdownViewer, { className: "HttpOperation__Description", markdown: data.description }),
2322
+ React.createElement(NodeAnnotation, { change: descriptionChanged }))),
2214
2323
  React.createElement(Request, { onChange: setTextRequestBodyIndex, operation: data }),
2215
2324
  data.responses && (React.createElement(Responses, { responses: data.responses, onMediaTypeChange: setResponseMediaType, onStatusCodeChange: setResponseStatusCode }))));
2216
2325
  const tryItPanel = !(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.hideTryItPanel) && (React.createElement(TryItWithRequestSamples, { httpOperation: data, responseMediaType: responseMediaType, responseStatusCode: responseStatusCode, requestBodyIndex: requestBodyIndex, hideTryIt: layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.hideTryIt, tryItCredentialsPolicy: tryItCredentialsPolicy, mockUrl: mocking.hideMocking ? undefined : mocking.mockUrl, corsProxy: tryItCorsProxy }));
@@ -2241,6 +2350,25 @@ function MethodPathInner({ method, path, chosenServerUrl }) {
2241
2350
  return (React.createElement(HStack, { spacing: 3, pl: 2.5, pr: 4, py: 2, bg: "canvas-50", rounded: "lg", fontFamily: "mono", display: "inline-flex", maxW: "full", title: fullUrl },
2242
2351
  React.createElement(Box, { py: 1, px: 2.5, rounded: "lg", bg: !isDark ? HttpMethodColors[method] : 'canvas-100', color: !isDark ? 'on-primary' : 'body', fontSize: "lg", fontWeight: "semibold", textTransform: "uppercase" }, method),
2243
2352
  pathElem));
2353
+ }
2354
+ function OperationHeader({ id, noHeading, hasBadges, name, isDeprecated, isInternal, method, path, }) {
2355
+ const { nodeHasChanged } = useOptionsCtx();
2356
+ if (noHeading && !hasBadges) {
2357
+ return null;
2358
+ }
2359
+ const lineOneChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: id, attr: ['iid', 'summary', 'deprecated', 'internal'] });
2360
+ const lineTwoChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: id, attr: ['method', 'path'] });
2361
+ return (React.createElement(VStack, { spacing: 5 },
2362
+ React.createElement(Box, { pos: "relative" },
2363
+ React.createElement(HStack, { spacing: 5 },
2364
+ !noHeading && name ? (React.createElement(Heading, { size: 1, fontWeight: "semibold" }, name)) : null,
2365
+ React.createElement(HStack, { spacing: 2 },
2366
+ isDeprecated && React.createElement(DeprecatedBadge, null),
2367
+ isInternal && React.createElement(InternalBadge, { isHttpService: true }))),
2368
+ React.createElement(NodeAnnotation, { change: lineOneChanged })),
2369
+ React.createElement(Box, { pos: "relative" },
2370
+ React.createElement(MethodPath, { method: method, path: path }),
2371
+ React.createElement(NodeAnnotation, { change: lineTwoChanged }))));
2244
2372
  }
2245
2373
 
2246
2374
  const PoweredByLink = ({ source, pathname, packageType, layout = 'sidebar' }) => {
@@ -2251,7 +2379,9 @@ const PoweredByLink = ({ source, pathname, packageType, layout = 'sidebar' }) =>
2251
2379
  React.createElement("strong", null, "Stoplight"))));
2252
2380
  };
2253
2381
 
2254
- const AdditionalInfo = ({ termsOfService, contact, license }) => {
2382
+ const AdditionalInfo = ({ id, termsOfService, contact, license }) => {
2383
+ const { nodeHasChanged } = useOptionsCtx();
2384
+ const hasChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: id, attr: ['termsOfService', 'contact', 'license'] });
2255
2385
  const contactLink = (contact === null || contact === void 0 ? void 0 : contact.name) && (contact === null || contact === void 0 ? void 0 : contact.url)
2256
2386
  ? `[Contact ${contact.name}](${contact.url})`
2257
2387
  : (contact === null || contact === void 0 ? void 0 : contact.email)
@@ -2260,12 +2390,13 @@ const AdditionalInfo = ({ termsOfService, contact, license }) => {
2260
2390
  const licenseUrl = (license === null || license === void 0 ? void 0 : license.url) || `https://spdx.org/licenses/${license === null || license === void 0 ? void 0 : license.identifier}.html`;
2261
2391
  const licenseLink = (license === null || license === void 0 ? void 0 : license.name) && licenseUrl ? `[${license.name} License](${licenseUrl})` : '';
2262
2392
  const tosLink = termsOfService ? `[Terms of Service](${termsOfService})` : '';
2263
- return contactLink || licenseLink || tosLink ? (React__default.createElement(Panel, { rounded: true, isCollapsible: false },
2393
+ return contactLink || licenseLink || tosLink ? (React__default.createElement(Panel, { rounded: true, isCollapsible: false, pos: "relative" },
2264
2394
  React__default.createElement(Panel.Titlebar, { bg: "canvas-300" },
2265
2395
  React__default.createElement("span", { role: "heading" }, "Additional Information")),
2266
2396
  React__default.createElement(Panel.Content, { p: 0 },
2267
2397
  React__default.createElement(Panel.Content, null,
2268
- React__default.createElement(MarkdownViewer, { style: { fontSize: 12 }, markdown: `${contactLink}\n \n${licenseLink}\n \n ${tosLink}` }))))) : null;
2398
+ React__default.createElement(MarkdownViewer, { style: { fontSize: 12 }, markdown: `${contactLink}\n \n${licenseLink}\n \n ${tosLink}` }))),
2399
+ React__default.createElement(NodeAnnotation, { change: hasChanged }))) : null;
2269
2400
  };
2270
2401
 
2271
2402
  const ExportButton = ({ original, bundled }) => {
@@ -2287,11 +2418,14 @@ const SecuritySchemes = ({ schemes, defaultScheme, defaultCollapsed = false, })
2287
2418
  React__default.createElement(Panel.Content, { p: 0 }, sortBy(schemes, 'type').map((scheme, i) => (React__default.createElement(SecurityScheme, { key: i, scheme: scheme, defaultIsOpen: defaultScheme ? scheme.key === defaultScheme : i === 0, isCollapsible: schemes.length > 1, showSchemeKey: shouldIncludeKey(schemes, scheme.type) }))))));
2288
2419
  };
2289
2420
  const SecurityScheme = ({ scheme, defaultIsOpen, isCollapsible, showSchemeKey }) => {
2290
- return (React__default.createElement(Panel, { defaultIsOpen: defaultIsOpen, isCollapsible: isCollapsible },
2421
+ const { nodeHasChanged } = useOptionsCtx();
2422
+ const hasChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: scheme.id });
2423
+ return (React__default.createElement(Panel, { defaultIsOpen: defaultIsOpen, isCollapsible: isCollapsible, pos: "relative" },
2291
2424
  React__default.createElement(Panel.Titlebar, null,
2292
2425
  React__default.createElement(Box, { as: "span", role: "heading" }, getReadableSecurityName(scheme, showSchemeKey))),
2293
2426
  React__default.createElement(Panel.Content, null,
2294
- React__default.createElement(MarkdownViewer, { style: { fontSize: 12 }, markdown: `${scheme.description || ''}\n\n` + getDefaultDescription(scheme) }))));
2427
+ React__default.createElement(MarkdownViewer, { style: { fontSize: 12 }, markdown: `${scheme.description || ''}\n\n` + getDefaultDescription(scheme) })),
2428
+ React__default.createElement(NodeAnnotation, { change: hasChanged })));
2295
2429
  };
2296
2430
 
2297
2431
  const ServerInfo = ({ servers, mockUrl }) => {
@@ -2305,13 +2439,14 @@ const ServerInfo = ({ servers, mockUrl }) => {
2305
2439
  return (React.createElement(InvertTheme, null,
2306
2440
  React.createElement(Panel, { rounded: true, isCollapsible: false, className: "BaseURLContent", w: "full" },
2307
2441
  React.createElement(Panel.Titlebar, { whitespace: "nowrap" }, "API Base URL"),
2308
- React.createElement(Box, { overflowX: "auto" },
2309
- React.createElement(Panel.Content, { w: "full", className: "sl-flex sl-flex-col" },
2310
- React.createElement(VStack, { spacing: 1, divider: true }, serversToDisplay.map((server, index) => (React.createElement(ServerUrl, Object.assign({}, server, { key: index }))))))))));
2442
+ React.createElement(Panel.Content, { w: "full", className: "sl-flex sl-flex-col" },
2443
+ React.createElement(VStack, { spacing: 1, divider: true }, serversToDisplay.map((server, index) => (React.createElement(ServerUrl, Object.assign({}, server, { key: index })))))))));
2311
2444
  };
2312
- const ServerUrl = ({ description, url, marginBottom = true }) => {
2445
+ const ServerUrl = ({ id, description, url }) => {
2446
+ const { nodeHasChanged } = useOptionsCtx();
2313
2447
  const { onCopy, hasCopied } = useClipboard(url);
2314
- return (React.createElement(Box, { whitespace: "nowrap" },
2448
+ const hasChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: id });
2449
+ return (React.createElement(Box, { whitespace: "nowrap", pos: "relative" },
2315
2450
  React.createElement(Text, { pr: 2, fontWeight: "bold" },
2316
2451
  description,
2317
2452
  ":"),
@@ -2321,26 +2456,37 @@ const ServerUrl = ({ description, url, marginBottom = true }) => {
2321
2456
  React.createElement(Icon, { className: "sl-ml-1", icon: ['fas', 'copy'] }))),
2322
2457
  hasCopied && (React.createElement(Box, { p: 1 },
2323
2458
  "Copied Server URL ",
2324
- React.createElement(Icon, { className: "sl-ml-1", icon: ['fas', 'check'] }))))));
2459
+ React.createElement(Icon, { className: "sl-ml-1", icon: ['fas', 'check'] })))),
2460
+ React.createElement(NodeAnnotation, { change: hasChanged, additionalLeftOffset: 16 })));
2325
2461
  };
2326
2462
 
2327
- const HttpServiceComponent = React.memo(({ data, location = {}, layoutOptions, exportProps }) => {
2463
+ const HttpServiceComponent = React.memo(({ data: unresolvedData, location = {}, layoutOptions, exportProps }) => {
2328
2464
  var _a, _b, _c, _d;
2465
+ const { nodeHasChanged } = useOptionsCtx();
2466
+ const data = useResolvedObject(unresolvedData);
2329
2467
  const { search, pathname } = location;
2330
2468
  const mocking = React.useContext(MockingContext);
2331
2469
  const query = new URLSearchParams(search);
2470
+ const nameChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: data.id, attr: 'name' });
2471
+ const versionChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: data.id, attr: 'version' });
2472
+ const descriptionChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: data.id, attr: 'description' });
2332
2473
  return (React.createElement(Box, { mb: 10 },
2333
2474
  data.name && !(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.noHeading) && (React.createElement(Flex, { justifyContent: "between", alignItems: "center" },
2334
- React.createElement(Heading, { size: 1, mb: 4, fontWeight: "semibold" }, data.name),
2475
+ React.createElement(Box, { pos: "relative" },
2476
+ React.createElement(Heading, { size: 1, mb: 4, fontWeight: "semibold" }, data.name),
2477
+ React.createElement(NodeAnnotation, { change: nameChanged })),
2335
2478
  exportProps && !(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.hideExport) && React.createElement(ExportButton, Object.assign({}, exportProps)))),
2336
- data.version && (React.createElement(Box, { mb: 5 },
2337
- React.createElement(VersionBadge, { value: data.version }))),
2479
+ data.version && (React.createElement(Box, { mb: 5, pos: "relative" },
2480
+ React.createElement(VersionBadge, { value: data.version }),
2481
+ React.createElement(NodeAnnotation, { change: versionChanged }))),
2338
2482
  pathname && (layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.showPoweredByLink) && (React.createElement(PoweredByLink, { source: (_a = data.name) !== null && _a !== void 0 ? _a : 'no-title', pathname: pathname, packageType: "elements", layout: "stacked" })),
2339
2483
  React.createElement(VStack, { spacing: 6 },
2340
2484
  React.createElement(ServerInfo, { servers: (_b = data.servers) !== null && _b !== void 0 ? _b : [], mockUrl: mocking.mockUrl }),
2341
- React.createElement(Box, null, ((_c = data.securitySchemes) === null || _c === void 0 ? void 0 : _c.length) && (React.createElement(SecuritySchemes, { schemes: data.securitySchemes, defaultScheme: query.get('security') || undefined }))),
2342
- React.createElement(Box, null, (((_d = data.contact) === null || _d === void 0 ? void 0 : _d.email) || data.license || data.termsOfService) && (React.createElement(AdditionalInfo, { contact: data.contact, license: data.license, termsOfService: data.termsOfService })))),
2343
- data.description && React.createElement(MarkdownViewer, { className: "sl-my-5", markdown: data.description })));
2485
+ React.createElement(Box, null, ((_c = data.securitySchemes) === null || _c === void 0 ? void 0 : _c.length) ? (React.createElement(SecuritySchemes, { schemes: data.securitySchemes, defaultScheme: query.get('security') || undefined })) : null),
2486
+ React.createElement(Box, null, (((_d = data.contact) === null || _d === void 0 ? void 0 : _d.email) || data.license || data.termsOfService) && (React.createElement(AdditionalInfo, { id: data.id, contact: data.contact, license: data.license, termsOfService: data.termsOfService })))),
2487
+ data.description && (React.createElement(Box, { pos: "relative" },
2488
+ React.createElement(MarkdownViewer, { className: "sl-my-5", markdown: data.description }),
2489
+ React.createElement(NodeAnnotation, { change: descriptionChanged })))));
2344
2490
  });
2345
2491
  HttpServiceComponent.displayName = 'HttpService.Component';
2346
2492
  const HttpService = withErrorBoundary(HttpServiceComponent, { recoverableProps: ['data'] });
@@ -2362,23 +2508,31 @@ function useIsCompact(layoutOptions) {
2362
2508
  }
2363
2509
 
2364
2510
  const ModelComponent = ({ data: unresolvedData, className, nodeTitle, layoutOptions, exportProps, }) => {
2365
- var _a;
2511
+ var _a, _b;
2366
2512
  const resolveRef = useInlineRefResolver();
2367
2513
  const data = useResolvedObject(unresolvedData);
2514
+ const { nodeHasChanged } = useOptionsCtx();
2368
2515
  const { ref: layoutRef, isCompact } = useIsCompact(layoutOptions);
2369
- const title = (_a = data.title) !== null && _a !== void 0 ? _a : nodeTitle;
2516
+ const nodeId = (_a = data === null || data === void 0 ? void 0 : data['x-stoplight']) === null || _a === void 0 ? void 0 : _a.id;
2517
+ const title = (_b = data.title) !== null && _b !== void 0 ? _b : nodeTitle;
2370
2518
  const isInternal = !!data['x-internal'];
2371
2519
  const shouldDisplayHeader = !(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.noHeading) && (title !== undefined || (exportProps && !(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.hideExport)));
2520
+ const titleChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId, attr: ['title', 'internal'] });
2372
2521
  const header = (shouldDisplayHeader || isInternal) && (React.createElement(Flex, { justifyContent: "between", alignItems: "center" },
2373
- React.createElement(HStack, { spacing: 5 },
2374
- title && (React.createElement(Heading, { size: 1, fontWeight: "semibold" }, title)),
2375
- React.createElement(HStack, { spacing: 2 }, isInternal && React.createElement(InternalBadge, null))),
2522
+ React.createElement(Box, { pos: "relative" },
2523
+ React.createElement(HStack, { spacing: 5 },
2524
+ title && (React.createElement(Heading, { size: 1, fontWeight: "semibold" }, title)),
2525
+ React.createElement(HStack, { spacing: 2 }, isInternal && React.createElement(InternalBadge, null))),
2526
+ React.createElement(NodeAnnotation, { change: titleChanged })),
2376
2527
  exportProps && !(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.hideExport) && React.createElement(ExportButton, Object.assign({}, exportProps))));
2377
2528
  const modelExamples = !(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.hideModelExamples) && React.createElement(ModelExamples, { data: data, isCollapsible: isCompact });
2529
+ const descriptionChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId, attr: 'description' });
2378
2530
  const description = (React.createElement(VStack, { spacing: 10 },
2379
- data.description && data.type === 'object' && React.createElement(MarkdownViewer, { role: "textbox", markdown: data.description }),
2531
+ data.description && data.type === 'object' && (React.createElement(Box, { pos: "relative" },
2532
+ React.createElement(MarkdownViewer, { role: "textbox", markdown: data.description }),
2533
+ React.createElement(NodeAnnotation, { change: descriptionChanged }))),
2380
2534
  isCompact && modelExamples,
2381
- React.createElement(JsonSchemaViewer, { resolveRef: resolveRef, schema: getOriginalObject(data) })));
2535
+ React.createElement(JsonSchemaViewer, { resolveRef: resolveRef, schema: getOriginalObject(data), nodeHasChanged: nodeHasChanged, skipTopLevelDescription: true })));
2382
2536
  return (React.createElement(TwoColumnLayout, { ref: layoutRef, className: cn('Model', className), header: header, left: description, right: !isCompact && modelExamples }));
2383
2537
  };
2384
2538
  const ModelExamples = React.memo(({ data, isCollapsible = false }) => {
@@ -2400,16 +2554,16 @@ const ModelExamples = React.memo(({ data, isCollapsible = false }) => {
2400
2554
  const Model = withErrorBoundary(ModelComponent, { recoverableProps: ['data'] });
2401
2555
 
2402
2556
  const Docs = React.memo((_a) => {
2403
- var { nodeType, nodeData, useNodeForRefResolving = false, refResolver } = _a, commonProps = __rest(_a, ["nodeType", "nodeData", "useNodeForRefResolving", "refResolver"]);
2557
+ var { nodeType, nodeData, useNodeForRefResolving = false, refResolver, nodeHasChanged } = _a, commonProps = __rest(_a, ["nodeType", "nodeData", "useNodeForRefResolving", "refResolver", "nodeHasChanged"]);
2404
2558
  const parsedNode = useParsedData(nodeType, nodeData);
2405
2559
  if (!parsedNode) {
2406
2560
  return null;
2407
2561
  }
2408
- const parsedDocs = React.createElement(ParsedDocs, Object.assign({ node: parsedNode }, commonProps));
2562
+ let elem = React.createElement(ParsedDocs, Object.assign({ node: parsedNode }, commonProps));
2409
2563
  if (useNodeForRefResolving) {
2410
- return (React.createElement(InlineRefResolverProvider, { document: parsedNode.data, resolver: refResolver }, parsedDocs));
2564
+ elem = (React.createElement(InlineRefResolverProvider, { document: parsedNode.data, resolver: refResolver }, elem));
2411
2565
  }
2412
- return parsedDocs;
2566
+ return React.createElement(ElementsOptionsProvider, { nodeHasChanged: nodeHasChanged }, elem);
2413
2567
  });
2414
2568
  const ParsedDocs = (_a) => {
2415
2569
  var { node } = _a, commonProps = __rest(_a, ["node"]);
@@ -2837,16 +2991,33 @@ const useRouter = (router, basePath, staticRouterPath) => {
2837
2991
  };
2838
2992
  };
2839
2993
 
2994
+ const components = {
2995
+ a: ReactRouterMarkdownLink,
2996
+ h2: (_a) => {
2997
+ var props = __rest(_a, ["color"]);
2998
+ return React.createElement(LinkHeading, Object.assign({ size: 2 }, props));
2999
+ },
3000
+ h3: (_a) => {
3001
+ var props = __rest(_a, ["color"]);
3002
+ return React.createElement(LinkHeading, Object.assign({ size: 3 }, props));
3003
+ },
3004
+ h4: (_a) => {
3005
+ var props = __rest(_a, ["color"]);
3006
+ return React.createElement(LinkHeading, Object.assign({ size: 4 }, props));
3007
+ },
3008
+ };
2840
3009
  function withRouter(WrappedComponent) {
2841
3010
  const WithRouter = (props) => {
2842
3011
  var _a, _b, _c;
2843
3012
  const basePath = (_a = props.basePath) !== null && _a !== void 0 ? _a : '/';
2844
3013
  const staticRouterPath = (_b = props.staticRouterPath) !== null && _b !== void 0 ? _b : '';
2845
- const { Router, routerProps } = useRouter((_c = props.router) !== null && _c !== void 0 ? _c : 'history', basePath, staticRouterPath);
2846
- return (React.createElement(Router, Object.assign({}, routerProps, { key: basePath }),
2847
- React.createElement(Route, { path: "/" },
2848
- React.createElement(MarkdownComponentsProvider, { value: { a: ReactRouterMarkdownLink } },
2849
- React.createElement(WrappedComponent, Object.assign({}, props))))));
3014
+ const routerType = (_c = props.router) !== null && _c !== void 0 ? _c : 'history';
3015
+ const { Router, routerProps } = useRouter(routerType, basePath, staticRouterPath);
3016
+ return (React.createElement(RouterTypeContext.Provider, { value: routerType },
3017
+ React.createElement(Router, Object.assign({}, routerProps, { key: basePath }),
3018
+ React.createElement(Route, { path: "/" },
3019
+ React.createElement(MarkdownComponentsProvider, { value: components },
3020
+ React.createElement(WrappedComponent, Object.assign({}, props)))))));
2850
3021
  };
2851
3022
  WithRouter.displayName = `WithRouter(${getDisplayName(WrappedComponent)})`;
2852
3023
  return WithRouter;