@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.js CHANGED
@@ -6,11 +6,11 @@ var tslib = require('tslib');
6
6
  var React = require('react');
7
7
  var json = require('@stoplight/json');
8
8
  var isArray = require('lodash/isArray.js');
9
- var isObject = require('lodash/isObject.js');
10
9
  var isPlainObject = require('lodash/isPlainObject.js');
11
10
  var types = require('@stoplight/types');
12
11
  var yaml = require('@stoplight/yaml');
13
12
  var mosaic = require('@stoplight/mosaic');
13
+ var isObject = require('lodash/isObject.js');
14
14
  var reactErrorBoundary = require('@stoplight/react-error-boundary');
15
15
  var markdownViewer = require('@stoplight/markdown-viewer');
16
16
  var cn = require('classnames');
@@ -39,9 +39,9 @@ var uniqBy = require('lodash/uniqBy.js');
39
39
  var formatXml = require('xml-formatter');
40
40
  var entries = require('lodash/entries.js');
41
41
  var keys = require('lodash/keys.js');
42
+ var reactRouterDom = require('react-router-dom');
42
43
  var jsonSchemaViewer = require('@stoplight/json-schema-viewer');
43
44
  var sortBy = require('lodash/sortBy.js');
44
- var reactRouterDom = require('react-router-dom');
45
45
  var reactRouterHashLink = require('react-router-hash-link');
46
46
  var reactQuery = require('react-query');
47
47
  var $RefParser = require('@stoplight/json-schema-ref-parser');
@@ -72,8 +72,8 @@ function _interopNamespace(e) {
72
72
  var React__namespace = /*#__PURE__*/_interopNamespace(React);
73
73
  var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
74
74
  var isArray__default = /*#__PURE__*/_interopDefaultLegacy(isArray);
75
- var isObject__default = /*#__PURE__*/_interopDefaultLegacy(isObject);
76
75
  var isPlainObject__default = /*#__PURE__*/_interopDefaultLegacy(isPlainObject);
76
+ var isObject__default = /*#__PURE__*/_interopDefaultLegacy(isObject);
77
77
  var cn__default = /*#__PURE__*/_interopDefaultLegacy(cn);
78
78
  var URI__default = /*#__PURE__*/_interopDefaultLegacy(URI);
79
79
  var HTTPSnippet__default = /*#__PURE__*/_interopDefaultLegacy(HTTPSnippet);
@@ -159,7 +159,7 @@ const isResolvedObjectProxy = (someObject) => {
159
159
  const getOriginalObject = (resolvedObject) => {
160
160
  return resolvedObject[originalObjectSymbol] || resolvedObject;
161
161
  };
162
- const isReference = (value) => isObject__default["default"](value) && typeof value['$ref'] === 'string';
162
+ const isReference = json.hasRef;
163
163
 
164
164
  const InlineRefResolverContext = React__namespace.createContext(undefined);
165
165
  InlineRefResolverContext.displayName = 'InlineRefResolverContext';
@@ -179,6 +179,15 @@ const useResolvedObject = (currentObject) => {
179
179
  return React__namespace.useMemo(() => createResolvedObject(currentObject, { contextObject: document, resolver }), [currentObject, document, resolver]);
180
180
  };
181
181
 
182
+ const DEFAULT_CONTEXT = {};
183
+ const ElementsOptionsContext = React__namespace.createContext(DEFAULT_CONTEXT);
184
+ const useOptionsCtx = () => {
185
+ return React__namespace.useContext(ElementsOptionsContext) || DEFAULT_CONTEXT;
186
+ };
187
+ function ElementsOptionsProvider({ children, nodeHasChanged }) {
188
+ return (React__namespace.createElement(ElementsOptionsContext.Provider, { value: Object.assign({}, DEFAULT_CONTEXT, { nodeHasChanged }) }, children));
189
+ }
190
+
182
191
  function isSMDASTRoot(maybeAst) {
183
192
  return isObject__default["default"](maybeAst) && maybeAst['type'] === 'root' && mosaic.isArray(maybeAst['children']);
184
193
  }
@@ -212,6 +221,7 @@ const parserMap = {
212
221
  [types.NodeType.TableOfContents]: parseUnknown,
213
222
  [types.NodeType.SpectralRuleset]: parseUnknown,
214
223
  [types.NodeType.Styleguide]: parseUnknown,
224
+ [types.NodeType.Image]: parseUnknown,
215
225
  [types.NodeType.Unknown]: parseUnknown,
216
226
  };
217
227
  function parseArticleData(rawData) {
@@ -325,6 +335,11 @@ var faEye = {
325
335
  iconName: 'eye',
326
336
  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"]
327
337
  };
338
+ var faImage = {
339
+ prefix: 'fas',
340
+ iconName: 'image',
341
+ 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"]
342
+ };
328
343
  var faServer = {
329
344
  prefix: 'fas',
330
345
  iconName: 'server',
@@ -342,6 +357,7 @@ const NodeTypeColors = {
342
357
  table_of_contents: '',
343
358
  spectral_ruleset: '',
344
359
  styleguide: '',
360
+ image: '',
345
361
  };
346
362
  const NodeTypePrettyName = {
347
363
  http_operation: 'Endpoint',
@@ -354,6 +370,7 @@ const NodeTypePrettyName = {
354
370
  table_of_contents: '',
355
371
  spectral_ruleset: '',
356
372
  styleguide: '',
373
+ image: '',
357
374
  };
358
375
  const NodeTypeIconDefs = {
359
376
  http_operation: faCrosshairs,
@@ -366,6 +383,7 @@ const NodeTypeIconDefs = {
366
383
  table_of_contents: faQuestionCircle,
367
384
  spectral_ruleset: faQuestionCircle,
368
385
  styleguide: faQuestionCircle,
386
+ image: faImage,
369
387
  };
370
388
  const HttpMethodColors = {
371
389
  get: 'success',
@@ -493,6 +511,7 @@ const getServersToDisplay = (originalServers, mockUrl) => {
493
511
  .filter(isValidServer);
494
512
  if (mockUrl) {
495
513
  servers.push({
514
+ id: 'mock',
496
515
  description: 'Mock Server',
497
516
  url: mockUrl,
498
517
  });
@@ -682,15 +701,12 @@ const requestSampleConfigs = {
682
701
  mosaicCodeViewerLanguage: 'php',
683
702
  httpSnippetLanguage: 'php',
684
703
  libraries: {
685
- 'pecl/http 1': {
686
- httpSnippetLibrary: 'http1',
687
- },
688
- 'pecl/http 2': {
689
- httpSnippetLibrary: 'http2',
690
- },
691
704
  cURL: {
692
705
  httpSnippetLibrary: 'curl',
693
706
  },
707
+ guzzle: {
708
+ httpSnippetLibrary: 'guzzle',
709
+ },
694
710
  },
695
711
  },
696
712
  Powershell: {
@@ -1053,9 +1069,12 @@ function parameterSupportsFileUpload(parameter) {
1053
1069
  (((_b = parameter.schema) === null || _b === void 0 ? void 0 : _b.contentEncoding) === 'base64' ||
1054
1070
  ((_c = parameter.schema) === null || _c === void 0 ? void 0 : _c.contentMediaType) === 'application/octet-stream'));
1055
1071
  }
1072
+ function stringifyValue(value) {
1073
+ return typeof value === 'object' ? JSON.stringify(value) : escapeQuotes(String(value));
1074
+ }
1056
1075
  function exampleValue(example) {
1057
1076
  const value = 'value' in example ? example.value : example.externalValue;
1058
- return escapeQuotes(String(value));
1077
+ return stringifyValue(value);
1059
1078
  }
1060
1079
  function escapeQuotes(value) {
1061
1080
  return value.replace(/"/g, '\\"');
@@ -1076,7 +1095,7 @@ const getValueForParameter = (parameter) => {
1076
1095
  var _a, _b, _c;
1077
1096
  const defaultValue = retrieveDefaultFromSchema(parameter);
1078
1097
  if (typeof defaultValue !== 'undefined') {
1079
- return { value: String(defaultValue), isDefault: true };
1098
+ return { value: stringifyValue(defaultValue), isDefault: true };
1080
1099
  }
1081
1100
  const examples = (_a = parameter.examples) !== null && _a !== void 0 ? _a : [];
1082
1101
  if (examples.length > 0) {
@@ -1084,7 +1103,7 @@ const getValueForParameter = (parameter) => {
1084
1103
  }
1085
1104
  const enums = (_c = (_b = parameter.schema) === null || _b === void 0 ? void 0 : _b.enum) !== null && _c !== void 0 ? _c : [];
1086
1105
  if (enums.length > 0) {
1087
- return { value: String(enums[0]) };
1106
+ return { value: stringifyValue(enums[0]) };
1088
1107
  }
1089
1108
  return { value: '' };
1090
1109
  };
@@ -1369,13 +1388,77 @@ const getServerUrl = ({ chosenServer, httpOperation, mockData, corsProxy, }) =>
1369
1388
  }
1370
1389
  return serverUrl;
1371
1390
  };
1391
+ const getQueryParams = ({ httpOperation, parameterValues, }) => {
1392
+ var _a;
1393
+ const query = (_a = httpOperation.request) === null || _a === void 0 ? void 0 : _a.query;
1394
+ if (!query)
1395
+ return [];
1396
+ return query.reduce((acc, param) => {
1397
+ var _a, _b, _c, _d, _e;
1398
+ const value = (_a = parameterValues[param.name]) !== null && _a !== void 0 ? _a : '';
1399
+ if (value.length === 0)
1400
+ return acc;
1401
+ const explode = (_b = param.explode) !== null && _b !== void 0 ? _b : true;
1402
+ if (((_c = param.schema) === null || _c === void 0 ? void 0 : _c.type) === 'object' && param.style === 'form' && value) {
1403
+ let nested;
1404
+ try {
1405
+ nested = JSON.parse(value);
1406
+ if (!(typeof nested === 'object' && nested !== null))
1407
+ throw Error();
1408
+ }
1409
+ catch (e) {
1410
+ throw new Error(`Cannot use param value "${value}". JSON object expected.`);
1411
+ }
1412
+ if (explode) {
1413
+ acc.push(...Object.entries(nested).map(([name, value]) => ({ name, value: value.toString() })));
1414
+ }
1415
+ else {
1416
+ acc.push({
1417
+ name: param.name,
1418
+ value: Object.entries(nested)
1419
+ .map(entry => entry.join(','))
1420
+ .join(','),
1421
+ });
1422
+ }
1423
+ }
1424
+ else if (((_d = param.schema) === null || _d === void 0 ? void 0 : _d.type) === 'array' && value) {
1425
+ let nested;
1426
+ try {
1427
+ nested = JSON.parse(value);
1428
+ if (!Array.isArray(nested))
1429
+ throw Error();
1430
+ }
1431
+ catch (e) {
1432
+ throw new Error(`Cannot use param value "${value}". JSON array expected.`);
1433
+ }
1434
+ if (explode) {
1435
+ acc.push(...nested.map(value => ({ name: param.name, value: value.toString() })));
1436
+ }
1437
+ else {
1438
+ const delimiter = {
1439
+ [types.HttpParamStyles.Form]: ',',
1440
+ [types.HttpParamStyles.SpaceDelimited]: ' ',
1441
+ [types.HttpParamStyles.PipeDelimited]: '|',
1442
+ };
1443
+ acc.push({
1444
+ name: param.name,
1445
+ value: nested.join((_e = delimiter[param.style]) !== null && _e !== void 0 ? _e : delimiter[types.HttpParamStyles.Form]),
1446
+ });
1447
+ }
1448
+ }
1449
+ else {
1450
+ acc.push({ name: param.name, value });
1451
+ }
1452
+ return acc;
1453
+ }, []);
1454
+ };
1372
1455
  function buildFetchRequest({ httpOperation, mediaTypeContent, bodyInput, parameterValues, mockData, auth, chosenServer, credentials = 'omit', corsProxy, }) {
1373
- var _a, _b, _c, _d, _e, _f;
1456
+ var _a, _b, _c;
1374
1457
  return tslib.__awaiter(this, void 0, void 0, function* () {
1375
1458
  const serverUrl = getServerUrl({ httpOperation, mockData, chosenServer, corsProxy });
1376
1459
  const shouldIncludeBody = ['PUT', 'POST', 'PATCH'].includes(httpOperation.method.toUpperCase());
1377
- 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 : [];
1378
- const rawHeaders = filterOutAuthorizationParams((_e = (_d = httpOperation.request) === null || _d === void 0 ? void 0 : _d.headers) !== null && _e !== void 0 ? _e : [], httpOperation.security)
1460
+ const queryParams = getQueryParams({ httpOperation, parameterValues });
1461
+ const rawHeaders = filterOutAuthorizationParams((_b = (_a = httpOperation.request) === null || _a === void 0 ? void 0 : _a.headers) !== null && _b !== void 0 ? _b : [], httpOperation.security)
1379
1462
  .map(header => { var _a; return ({ name: header.name, value: (_a = parameterValues[header.name]) !== null && _a !== void 0 ? _a : '' }); })
1380
1463
  .filter(({ value }) => value.length > 0);
1381
1464
  const [queryParamsWithAuth, headersWithAuth] = runAuthRequestEhancements(auth, queryParams, rawHeaders);
@@ -1384,7 +1467,7 @@ function buildFetchRequest({ httpOperation, mediaTypeContent, bodyInput, paramet
1384
1467
  urlObject.search = new URLSearchParams(queryParamsWithAuth.map(nameAndValueObjectToPair)).toString();
1385
1468
  const body = typeof bodyInput === 'object' ? yield createRequestBody(mediaTypeContent, bodyInput) : bodyInput;
1386
1469
  const headers = Object.assign(Object.assign(Object.assign({}, ((mediaTypeContent === null || mediaTypeContent === void 0 ? void 0 : mediaTypeContent.mediaType) !== 'multipart/form-data' && {
1387
- 'Content-Type': (_f = mediaTypeContent === null || mediaTypeContent === void 0 ? void 0 : mediaTypeContent.mediaType) !== null && _f !== void 0 ? _f : 'application/json',
1470
+ 'Content-Type': (_c = mediaTypeContent === null || mediaTypeContent === void 0 ? void 0 : mediaTypeContent.mediaType) !== null && _c !== void 0 ? _c : 'application/json',
1388
1471
  })), Object.fromEntries(headersWithAuth.map(nameAndValueObjectToPair))), mockData === null || mockData === void 0 ? void 0 : mockData.header);
1389
1472
  return [
1390
1473
  urlObject.href,
@@ -1444,13 +1527,13 @@ const runAuthRequestEhancements = (auth, queryParams, headers) => {
1444
1527
  return [newQueryParams, newHeaders];
1445
1528
  };
1446
1529
  function buildHarRequest({ httpOperation, bodyInput, parameterValues, mediaTypeContent, auth, mockData, chosenServer, corsProxy, }) {
1447
- var _a, _b, _c, _d, _e, _f, _g;
1530
+ var _a, _b, _c, _d;
1448
1531
  return tslib.__awaiter(this, void 0, void 0, function* () {
1449
1532
  const serverUrl = getServerUrl({ httpOperation, mockData, chosenServer, corsProxy });
1450
1533
  const mimeType = (_a = mediaTypeContent === null || mediaTypeContent === void 0 ? void 0 : mediaTypeContent.mediaType) !== null && _a !== void 0 ? _a : 'application/json';
1451
1534
  const shouldIncludeBody = ['PUT', 'POST', 'PATCH'].includes(httpOperation.method.toUpperCase());
1452
- 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 : [];
1453
- 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 : [];
1535
+ const queryParams = getQueryParams({ httpOperation, parameterValues });
1536
+ 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 : [];
1454
1537
  if (mockData === null || mockData === void 0 ? void 0 : mockData.header) {
1455
1538
  headerParams.push({ name: 'Prefer', value: mockData.header.Prefer });
1456
1539
  }
@@ -2059,9 +2142,20 @@ ${scopes.map(([key, value]) => `- \`${key}\` - ${value}`).join('\n')}`;
2059
2142
  return description;
2060
2143
  }
2061
2144
 
2145
+ const RouterTypeContext = React__namespace.createContext('history');
2146
+
2147
+ const LinkHeading = React__namespace.memo(function LinkHeading(_a) {
2148
+ var { id: _id } = _a, props = tslib.__rest(_a, ["id"]);
2149
+ const { pathname } = reactRouterDom.useLocation();
2150
+ const routerKind = React__namespace.useContext(RouterTypeContext);
2151
+ const route = pathname.split('#')[0];
2152
+ const id = routerKind === 'hash' ? `${route}#${_id}` : _id;
2153
+ return React__namespace.createElement(mosaic.LinkHeading, Object.assign({ id: id }, props));
2154
+ });
2155
+
2062
2156
  const SectionTitle = ({ title, id, size = 2, children }) => {
2063
2157
  return (React__namespace.createElement(mosaic.HStack, { spacing: 6 },
2064
- React__namespace.createElement(mosaic.Box, { as: mosaic.LinkHeading, size: size, "aria-label": title, id: id || slugify(title) }, title),
2158
+ React__namespace.createElement(mosaic.Box, { as: LinkHeading, size: size, "aria-label": title, id: id || slugify(title) }, title),
2065
2159
  children));
2066
2160
  };
2067
2161
  const SectionSubtitle = props => {
@@ -2084,6 +2178,7 @@ const Body = ({ body, onChange }) => {
2084
2178
  var _a;
2085
2179
  const refResolver = useInlineRefResolver();
2086
2180
  const [chosenContent, setChosenContent] = React__namespace.useState(0);
2181
+ const { nodeHasChanged } = useOptionsCtx();
2087
2182
  React__namespace.useEffect(() => {
2088
2183
  onChange(chosenContent);
2089
2184
  }, [chosenContent]);
@@ -2091,11 +2186,14 @@ const Body = ({ body, onChange }) => {
2091
2186
  return null;
2092
2187
  const { contents = [], description } = body;
2093
2188
  const schema = (_a = contents[chosenContent]) === null || _a === void 0 ? void 0 : _a.schema;
2189
+ const descriptionChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: body.id, attr: 'description' });
2094
2190
  return (React__namespace.createElement(mosaic.VStack, { spacing: 6 },
2095
2191
  React__namespace.createElement(SectionSubtitle, { title: "Body", id: "request-body" }, contents.length > 0 && (React__namespace.createElement(mosaic.Flex, { flex: 1, justify: "end" },
2096
2192
  React__namespace.createElement(mosaic.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" })))),
2097
- description && React__namespace.createElement(MarkdownViewer, { markdown: description }),
2098
- isJSONSchema(schema) && (React__namespace.createElement(jsonSchemaViewer.JsonSchemaViewer, { resolveRef: refResolver, schema: getOriginalObject(schema), viewMode: "write", renderRootTreeLines: true }))));
2193
+ description && (React__namespace.createElement(mosaic.Box, { pos: "relative" },
2194
+ React__namespace.createElement(MarkdownViewer, { markdown: description }),
2195
+ React__namespace.createElement(mosaic.NodeAnnotation, { change: descriptionChanged }))),
2196
+ isJSONSchema(schema) && (React__namespace.createElement(jsonSchemaViewer.JsonSchemaViewer, { resolveRef: refResolver, schema: getOriginalObject(schema), viewMode: "write", renderRootTreeLines: true, nodeHasChanged: nodeHasChanged }))));
2099
2197
  };
2100
2198
  Body.displayName = 'HttpOperation.Body';
2101
2199
 
@@ -2119,14 +2217,16 @@ const defaultStyle = {
2119
2217
  cookie: types.HttpParamStyles.Form,
2120
2218
  };
2121
2219
  const Parameters = ({ parameters, parameterType }) => {
2220
+ const { nodeHasChanged } = useOptionsCtx();
2221
+ const refResolver = useInlineRefResolver();
2122
2222
  const schema = React__namespace.useMemo(() => httpOperationParamsToSchema({ parameters, parameterType }), [parameters, parameterType]);
2123
2223
  if (!schema)
2124
2224
  return null;
2125
- return React__namespace.createElement(jsonSchemaViewer.JsonSchemaViewer, { schema: schema, disableCrumbs: true });
2225
+ return React__namespace.createElement(jsonSchemaViewer.JsonSchemaViewer, { resolveRef: refResolver, schema: schema, disableCrumbs: true, nodeHasChanged: nodeHasChanged });
2126
2226
  };
2127
2227
  Parameters.displayName = 'HttpOperation.Parameters';
2128
2228
  const httpOperationParamsToSchema = ({ parameters, parameterType }) => {
2129
- var _a;
2229
+ var _a, _b, _c;
2130
2230
  if (!parameters || !parameters.length)
2131
2231
  return null;
2132
2232
  const schema = {
@@ -2135,8 +2235,6 @@ const httpOperationParamsToSchema = ({ parameters, parameterType }) => {
2135
2235
  };
2136
2236
  const sortedParams = sortBy__default["default"](parameters, ['required', 'name']);
2137
2237
  for (const p of sortedParams) {
2138
- if (!p.schema)
2139
- continue;
2140
2238
  const { name, description, required, deprecated, examples, style } = p;
2141
2239
  const paramExamples = (examples === null || examples === void 0 ? void 0 : examples.map(example => {
2142
2240
  if (isNodeExample(example)) {
@@ -2146,10 +2244,10 @@ const httpOperationParamsToSchema = ({ parameters, parameterType }) => {
2146
2244
  })) || [];
2147
2245
  const schemaExamples = (_a = p.schema) === null || _a === void 0 ? void 0 : _a.examples;
2148
2246
  const schemaExamplesArray = Array.isArray(schemaExamples) ? schemaExamples : [];
2149
- const paramDescription = description || p.schema.description;
2247
+ const paramDescription = description || ((_b = p.schema) === null || _b === void 0 ? void 0 : _b.description);
2150
2248
  const paramDeprecated = deprecated || p.schema.deprecated;
2151
2249
  const paramStyle = style && defaultStyle[parameterType] !== style ? readableStyles[style] || style : undefined;
2152
- schema.properties[p.name] = Object.assign(Object.assign({}, p.schema), { description: paramDescription, examples: [...paramExamples, ...schemaExamplesArray], deprecated: paramDeprecated, style: paramStyle });
2250
+ 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 }) });
2153
2251
  if (required) {
2154
2252
  schema.required.push(name);
2155
2253
  }
@@ -2172,7 +2270,7 @@ const Request = ({ operation: { request, request: { path: pathParams = [], heade
2172
2270
  return null;
2173
2271
  return (React__namespace.createElement(mosaic.VStack, { spacing: 8 },
2174
2272
  React__namespace.createElement(SectionTitle, { title: "Request" }),
2175
- securitySchemes.length > 0 && (React__namespace.createElement(mosaic.VStack, { spacing: 3 }, securitySchemes.map((scheme, i) => (React__namespace.createElement(SecurityPanel, { key: i, scheme: scheme, includeKey: shouldIncludeKey(securitySchemes, scheme.type) }))))),
2273
+ React__namespace.createElement(SecuritySchemes$1, { schemes: securitySchemes }),
2176
2274
  pathParams.length > 0 && (React__namespace.createElement(mosaic.VStack, { spacing: 5 },
2177
2275
  React__namespace.createElement(SectionSubtitle, { title: "Path Parameters" }),
2178
2276
  React__namespace.createElement(Parameters, { parameterType: "path", parameters: pathParams }))),
@@ -2193,6 +2291,15 @@ const SecurityPanel = ({ scheme, includeKey }) => {
2193
2291
  const [expandedState, setExpanded] = jotai.useAtom(schemeExpandedState);
2194
2292
  return (React__namespace.createElement(SubSectionPanel, { title: `Security: ${getReadableSecurityName(scheme, includeKey)}`, defaultIsOpen: !!expandedState[scheme.key], onChange: isOpen => setExpanded(Object.assign(Object.assign({}, expandedState), { [scheme.key]: isOpen })) },
2195
2293
  React__namespace.createElement(MarkdownViewer, { style: { fontSize: 12 }, markdown: `${scheme.description || ''}\n\n` + getDefaultDescription(scheme) })));
2294
+ };
2295
+ const SecuritySchemes$1 = ({ schemes }) => {
2296
+ const { nodeHasChanged } = useOptionsCtx();
2297
+ if (!schemes.length) {
2298
+ return null;
2299
+ }
2300
+ return (React__namespace.createElement(mosaic.VStack, { spacing: 3 }, schemes.map((scheme, i) => (React__namespace.createElement(mosaic.Box, { pos: "relative", key: i },
2301
+ React__namespace.createElement(SecurityPanel, { scheme: scheme, includeKey: shouldIncludeKey(schemes, scheme.type) }),
2302
+ React__namespace.createElement(mosaic.NodeAnnotation, { change: nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: scheme.id }) }))))));
2196
2303
  };
2197
2304
 
2198
2305
  const Responses = ({ responses: unsortedResponses, onStatusCodeChange, onMediaTypeChange }) => {
@@ -2215,13 +2322,17 @@ const Response = ({ response, onMediaTypeChange }) => {
2215
2322
  const { contents = [], headers = [], description } = response;
2216
2323
  const [chosenContent, setChosenContent] = React__namespace.useState(0);
2217
2324
  const refResolver = useInlineRefResolver();
2325
+ const { nodeHasChanged } = useOptionsCtx();
2218
2326
  const responseContent = contents[chosenContent];
2219
2327
  const schema = responseContent === null || responseContent === void 0 ? void 0 : responseContent.schema;
2220
2328
  React__namespace.useEffect(() => {
2221
2329
  responseContent && onMediaTypeChange(responseContent.mediaType);
2222
2330
  }, [responseContent]);
2331
+ const descriptionChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: response.id, attr: 'description' });
2223
2332
  return (React__namespace.createElement(mosaic.VStack, { spacing: 8, pt: 8 },
2224
- description && React__namespace.createElement(MarkdownViewer, { markdown: description }),
2333
+ description && (React__namespace.createElement(mosaic.Box, { pos: "relative" },
2334
+ React__namespace.createElement(MarkdownViewer, { markdown: description }),
2335
+ React__namespace.createElement(mosaic.NodeAnnotation, { change: descriptionChanged }))),
2225
2336
  headers.length > 0 && (React__namespace.createElement(mosaic.VStack, { spacing: 5 },
2226
2337
  React__namespace.createElement(SectionSubtitle, { title: "Headers", id: "response-headers" }),
2227
2338
  React__namespace.createElement(Parameters, { parameterType: "header", parameters: headers }))),
@@ -2229,7 +2340,7 @@ const Response = ({ response, onMediaTypeChange }) => {
2229
2340
  React__namespace.createElement(SectionSubtitle, { title: "Body", id: "response-body" },
2230
2341
  React__namespace.createElement(mosaic.Flex, { flex: 1, justify: "end" },
2231
2342
  React__namespace.createElement(mosaic.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" }))),
2232
- schema && (React__namespace.createElement(jsonSchemaViewer.JsonSchemaViewer, { schema: getOriginalObject(schema), resolveRef: refResolver, viewMode: "read", parentCrumbs: ['responses', response.code], renderRootTreeLines: true }))))));
2343
+ schema && (React__namespace.createElement(jsonSchemaViewer.JsonSchemaViewer, { schema: getOriginalObject(schema), resolveRef: refResolver, viewMode: "read", parentCrumbs: ['responses', response.code], renderRootTreeLines: true, nodeHasChanged: nodeHasChanged }))))));
2233
2344
  };
2234
2345
  Response.displayName = 'HttpOperation.Response';
2235
2346
  const codeToIntentVal = (code) => {
@@ -2247,6 +2358,7 @@ const codeToIntentVal = (code) => {
2247
2358
  };
2248
2359
 
2249
2360
  const HttpOperationComponent = React__namespace.memo(({ className, data: unresolvedData, layoutOptions, tryItCredentialsPolicy, tryItCorsProxy }) => {
2361
+ const { nodeHasChanged } = useOptionsCtx();
2250
2362
  const data = useResolvedObject(unresolvedData);
2251
2363
  const mocking = React__namespace.useContext(MockingContext);
2252
2364
  const isDeprecated = !!data.deprecated;
@@ -2256,15 +2368,12 @@ const HttpOperationComponent = React__namespace.memo(({ className, data: unresol
2256
2368
  const [requestBodyIndex, setTextRequestBodyIndex] = React__namespace.useState(0);
2257
2369
  const prettyName = (data.summary || data.iid || '').trim();
2258
2370
  const hasBadges = isDeprecated || isInternal;
2259
- const header = (!(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.noHeading) || hasBadges) && (React__namespace.createElement(mosaic.VStack, { spacing: 5 },
2260
- React__namespace.createElement(mosaic.HStack, { spacing: 5 },
2261
- !(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.noHeading) && prettyName ? (React__namespace.createElement(mosaic.Heading, { size: 1, fontWeight: "semibold" }, prettyName)) : null,
2262
- React__namespace.createElement(mosaic.HStack, { spacing: 2 },
2263
- isDeprecated && React__namespace.createElement(DeprecatedBadge, null),
2264
- isInternal && React__namespace.createElement(InternalBadge, { isHttpService: true }))),
2265
- React__namespace.createElement(MethodPath, { method: data.method, path: data.path })));
2371
+ const header = (React__namespace.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 }));
2372
+ const descriptionChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: data.id, attr: 'description' });
2266
2373
  const description = (React__namespace.createElement(mosaic.VStack, { spacing: 10 },
2267
- data.description && React__namespace.createElement(MarkdownViewer, { className: "HttpOperation__Description", markdown: data.description }),
2374
+ data.description && (React__namespace.createElement(mosaic.Box, { pos: "relative" },
2375
+ React__namespace.createElement(MarkdownViewer, { className: "HttpOperation__Description", markdown: data.description }),
2376
+ React__namespace.createElement(mosaic.NodeAnnotation, { change: descriptionChanged }))),
2268
2377
  React__namespace.createElement(Request, { onChange: setTextRequestBodyIndex, operation: data }),
2269
2378
  data.responses && (React__namespace.createElement(Responses, { responses: data.responses, onMediaTypeChange: setResponseMediaType, onStatusCodeChange: setResponseStatusCode }))));
2270
2379
  const tryItPanel = !(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.hideTryItPanel) && (React__namespace.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 }));
@@ -2295,6 +2404,25 @@ function MethodPathInner({ method, path, chosenServerUrl }) {
2295
2404
  return (React__namespace.createElement(mosaic.HStack, { spacing: 3, pl: 2.5, pr: 4, py: 2, bg: "canvas-50", rounded: "lg", fontFamily: "mono", display: "inline-flex", maxW: "full", title: fullUrl },
2296
2405
  React__namespace.createElement(mosaic.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),
2297
2406
  pathElem));
2407
+ }
2408
+ function OperationHeader({ id, noHeading, hasBadges, name, isDeprecated, isInternal, method, path, }) {
2409
+ const { nodeHasChanged } = useOptionsCtx();
2410
+ if (noHeading && !hasBadges) {
2411
+ return null;
2412
+ }
2413
+ const lineOneChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: id, attr: ['iid', 'summary', 'deprecated', 'internal'] });
2414
+ const lineTwoChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: id, attr: ['method', 'path'] });
2415
+ return (React__namespace.createElement(mosaic.VStack, { spacing: 5 },
2416
+ React__namespace.createElement(mosaic.Box, { pos: "relative" },
2417
+ React__namespace.createElement(mosaic.HStack, { spacing: 5 },
2418
+ !noHeading && name ? (React__namespace.createElement(mosaic.Heading, { size: 1, fontWeight: "semibold" }, name)) : null,
2419
+ React__namespace.createElement(mosaic.HStack, { spacing: 2 },
2420
+ isDeprecated && React__namespace.createElement(DeprecatedBadge, null),
2421
+ isInternal && React__namespace.createElement(InternalBadge, { isHttpService: true }))),
2422
+ React__namespace.createElement(mosaic.NodeAnnotation, { change: lineOneChanged })),
2423
+ React__namespace.createElement(mosaic.Box, { pos: "relative" },
2424
+ React__namespace.createElement(MethodPath, { method: method, path: path }),
2425
+ React__namespace.createElement(mosaic.NodeAnnotation, { change: lineTwoChanged }))));
2298
2426
  }
2299
2427
 
2300
2428
  const PoweredByLink = ({ source, pathname, packageType, layout = 'sidebar' }) => {
@@ -2305,7 +2433,9 @@ const PoweredByLink = ({ source, pathname, packageType, layout = 'sidebar' }) =>
2305
2433
  React__namespace.createElement("strong", null, "Stoplight"))));
2306
2434
  };
2307
2435
 
2308
- const AdditionalInfo = ({ termsOfService, contact, license }) => {
2436
+ const AdditionalInfo = ({ id, termsOfService, contact, license }) => {
2437
+ const { nodeHasChanged } = useOptionsCtx();
2438
+ const hasChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: id, attr: ['termsOfService', 'contact', 'license'] });
2309
2439
  const contactLink = (contact === null || contact === void 0 ? void 0 : contact.name) && (contact === null || contact === void 0 ? void 0 : contact.url)
2310
2440
  ? `[Contact ${contact.name}](${contact.url})`
2311
2441
  : (contact === null || contact === void 0 ? void 0 : contact.email)
@@ -2314,12 +2444,13 @@ const AdditionalInfo = ({ termsOfService, contact, license }) => {
2314
2444
  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`;
2315
2445
  const licenseLink = (license === null || license === void 0 ? void 0 : license.name) && licenseUrl ? `[${license.name} License](${licenseUrl})` : '';
2316
2446
  const tosLink = termsOfService ? `[Terms of Service](${termsOfService})` : '';
2317
- return contactLink || licenseLink || tosLink ? (React__default["default"].createElement(mosaic.Panel, { rounded: true, isCollapsible: false },
2447
+ return contactLink || licenseLink || tosLink ? (React__default["default"].createElement(mosaic.Panel, { rounded: true, isCollapsible: false, pos: "relative" },
2318
2448
  React__default["default"].createElement(mosaic.Panel.Titlebar, { bg: "canvas-300" },
2319
2449
  React__default["default"].createElement("span", { role: "heading" }, "Additional Information")),
2320
2450
  React__default["default"].createElement(mosaic.Panel.Content, { p: 0 },
2321
2451
  React__default["default"].createElement(mosaic.Panel.Content, null,
2322
- React__default["default"].createElement(MarkdownViewer, { style: { fontSize: 12 }, markdown: `${contactLink}\n \n${licenseLink}\n \n ${tosLink}` }))))) : null;
2452
+ React__default["default"].createElement(MarkdownViewer, { style: { fontSize: 12 }, markdown: `${contactLink}\n \n${licenseLink}\n \n ${tosLink}` }))),
2453
+ React__default["default"].createElement(mosaic.NodeAnnotation, { change: hasChanged }))) : null;
2323
2454
  };
2324
2455
 
2325
2456
  const ExportButton = ({ original, bundled }) => {
@@ -2341,11 +2472,14 @@ const SecuritySchemes = ({ schemes, defaultScheme, defaultCollapsed = false, })
2341
2472
  React__default["default"].createElement(mosaic.Panel.Content, { p: 0 }, sortBy__default["default"](schemes, 'type').map((scheme, i) => (React__default["default"].createElement(SecurityScheme, { key: i, scheme: scheme, defaultIsOpen: defaultScheme ? scheme.key === defaultScheme : i === 0, isCollapsible: schemes.length > 1, showSchemeKey: shouldIncludeKey(schemes, scheme.type) }))))));
2342
2473
  };
2343
2474
  const SecurityScheme = ({ scheme, defaultIsOpen, isCollapsible, showSchemeKey }) => {
2344
- return (React__default["default"].createElement(mosaic.Panel, { defaultIsOpen: defaultIsOpen, isCollapsible: isCollapsible },
2475
+ const { nodeHasChanged } = useOptionsCtx();
2476
+ const hasChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: scheme.id });
2477
+ return (React__default["default"].createElement(mosaic.Panel, { defaultIsOpen: defaultIsOpen, isCollapsible: isCollapsible, pos: "relative" },
2345
2478
  React__default["default"].createElement(mosaic.Panel.Titlebar, null,
2346
2479
  React__default["default"].createElement(mosaic.Box, { as: "span", role: "heading" }, getReadableSecurityName(scheme, showSchemeKey))),
2347
2480
  React__default["default"].createElement(mosaic.Panel.Content, null,
2348
- React__default["default"].createElement(MarkdownViewer, { style: { fontSize: 12 }, markdown: `${scheme.description || ''}\n\n` + getDefaultDescription(scheme) }))));
2481
+ React__default["default"].createElement(MarkdownViewer, { style: { fontSize: 12 }, markdown: `${scheme.description || ''}\n\n` + getDefaultDescription(scheme) })),
2482
+ React__default["default"].createElement(mosaic.NodeAnnotation, { change: hasChanged })));
2349
2483
  };
2350
2484
 
2351
2485
  const ServerInfo = ({ servers, mockUrl }) => {
@@ -2359,13 +2493,14 @@ const ServerInfo = ({ servers, mockUrl }) => {
2359
2493
  return (React__namespace.createElement(mosaic.InvertTheme, null,
2360
2494
  React__namespace.createElement(mosaic.Panel, { rounded: true, isCollapsible: false, className: "BaseURLContent", w: "full" },
2361
2495
  React__namespace.createElement(mosaic.Panel.Titlebar, { whitespace: "nowrap" }, "API Base URL"),
2362
- React__namespace.createElement(mosaic.Box, { overflowX: "auto" },
2363
- React__namespace.createElement(mosaic.Panel.Content, { w: "full", className: "sl-flex sl-flex-col" },
2364
- React__namespace.createElement(mosaic.VStack, { spacing: 1, divider: true }, serversToDisplay.map((server, index) => (React__namespace.createElement(ServerUrl, Object.assign({}, server, { key: index }))))))))));
2496
+ React__namespace.createElement(mosaic.Panel.Content, { w: "full", className: "sl-flex sl-flex-col" },
2497
+ React__namespace.createElement(mosaic.VStack, { spacing: 1, divider: true }, serversToDisplay.map((server, index) => (React__namespace.createElement(ServerUrl, Object.assign({}, server, { key: index })))))))));
2365
2498
  };
2366
- const ServerUrl = ({ description, url, marginBottom = true }) => {
2499
+ const ServerUrl = ({ id, description, url }) => {
2500
+ const { nodeHasChanged } = useOptionsCtx();
2367
2501
  const { onCopy, hasCopied } = mosaic.useClipboard(url);
2368
- return (React__namespace.createElement(mosaic.Box, { whitespace: "nowrap" },
2502
+ const hasChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: id });
2503
+ return (React__namespace.createElement(mosaic.Box, { whitespace: "nowrap", pos: "relative" },
2369
2504
  React__namespace.createElement(mosaic.Text, { pr: 2, fontWeight: "bold" },
2370
2505
  description,
2371
2506
  ":"),
@@ -2375,26 +2510,37 @@ const ServerUrl = ({ description, url, marginBottom = true }) => {
2375
2510
  React__namespace.createElement(mosaic.Icon, { className: "sl-ml-1", icon: ['fas', 'copy'] }))),
2376
2511
  hasCopied && (React__namespace.createElement(mosaic.Box, { p: 1 },
2377
2512
  "Copied Server URL ",
2378
- React__namespace.createElement(mosaic.Icon, { className: "sl-ml-1", icon: ['fas', 'check'] }))))));
2513
+ React__namespace.createElement(mosaic.Icon, { className: "sl-ml-1", icon: ['fas', 'check'] })))),
2514
+ React__namespace.createElement(mosaic.NodeAnnotation, { change: hasChanged, additionalLeftOffset: 16 })));
2379
2515
  };
2380
2516
 
2381
- const HttpServiceComponent = React__namespace.memo(({ data, location = {}, layoutOptions, exportProps }) => {
2517
+ const HttpServiceComponent = React__namespace.memo(({ data: unresolvedData, location = {}, layoutOptions, exportProps }) => {
2382
2518
  var _a, _b, _c, _d;
2519
+ const { nodeHasChanged } = useOptionsCtx();
2520
+ const data = useResolvedObject(unresolvedData);
2383
2521
  const { search, pathname } = location;
2384
2522
  const mocking = React__namespace.useContext(MockingContext);
2385
2523
  const query = new URLSearchParams(search);
2524
+ const nameChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: data.id, attr: 'name' });
2525
+ const versionChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: data.id, attr: 'version' });
2526
+ const descriptionChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: data.id, attr: 'description' });
2386
2527
  return (React__namespace.createElement(mosaic.Box, { mb: 10 },
2387
2528
  data.name && !(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.noHeading) && (React__namespace.createElement(mosaic.Flex, { justifyContent: "between", alignItems: "center" },
2388
- React__namespace.createElement(mosaic.Heading, { size: 1, mb: 4, fontWeight: "semibold" }, data.name),
2529
+ React__namespace.createElement(mosaic.Box, { pos: "relative" },
2530
+ React__namespace.createElement(mosaic.Heading, { size: 1, mb: 4, fontWeight: "semibold" }, data.name),
2531
+ React__namespace.createElement(mosaic.NodeAnnotation, { change: nameChanged })),
2389
2532
  exportProps && !(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.hideExport) && React__namespace.createElement(ExportButton, Object.assign({}, exportProps)))),
2390
- data.version && (React__namespace.createElement(mosaic.Box, { mb: 5 },
2391
- React__namespace.createElement(VersionBadge, { value: data.version }))),
2533
+ data.version && (React__namespace.createElement(mosaic.Box, { mb: 5, pos: "relative" },
2534
+ React__namespace.createElement(VersionBadge, { value: data.version }),
2535
+ React__namespace.createElement(mosaic.NodeAnnotation, { change: versionChanged }))),
2392
2536
  pathname && (layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.showPoweredByLink) && (React__namespace.createElement(PoweredByLink, { source: (_a = data.name) !== null && _a !== void 0 ? _a : 'no-title', pathname: pathname, packageType: "elements", layout: "stacked" })),
2393
2537
  React__namespace.createElement(mosaic.VStack, { spacing: 6 },
2394
2538
  React__namespace.createElement(ServerInfo, { servers: (_b = data.servers) !== null && _b !== void 0 ? _b : [], mockUrl: mocking.mockUrl }),
2395
- React__namespace.createElement(mosaic.Box, null, ((_c = data.securitySchemes) === null || _c === void 0 ? void 0 : _c.length) && (React__namespace.createElement(SecuritySchemes, { schemes: data.securitySchemes, defaultScheme: query.get('security') || undefined }))),
2396
- React__namespace.createElement(mosaic.Box, null, (((_d = data.contact) === null || _d === void 0 ? void 0 : _d.email) || data.license || data.termsOfService) && (React__namespace.createElement(AdditionalInfo, { contact: data.contact, license: data.license, termsOfService: data.termsOfService })))),
2397
- data.description && React__namespace.createElement(MarkdownViewer, { className: "sl-my-5", markdown: data.description })));
2539
+ React__namespace.createElement(mosaic.Box, null, ((_c = data.securitySchemes) === null || _c === void 0 ? void 0 : _c.length) ? (React__namespace.createElement(SecuritySchemes, { schemes: data.securitySchemes, defaultScheme: query.get('security') || undefined })) : null),
2540
+ React__namespace.createElement(mosaic.Box, null, (((_d = data.contact) === null || _d === void 0 ? void 0 : _d.email) || data.license || data.termsOfService) && (React__namespace.createElement(AdditionalInfo, { id: data.id, contact: data.contact, license: data.license, termsOfService: data.termsOfService })))),
2541
+ data.description && (React__namespace.createElement(mosaic.Box, { pos: "relative" },
2542
+ React__namespace.createElement(MarkdownViewer, { className: "sl-my-5", markdown: data.description }),
2543
+ React__namespace.createElement(mosaic.NodeAnnotation, { change: descriptionChanged })))));
2398
2544
  });
2399
2545
  HttpServiceComponent.displayName = 'HttpService.Component';
2400
2546
  const HttpService = reactErrorBoundary.withErrorBoundary(HttpServiceComponent, { recoverableProps: ['data'] });
@@ -2416,23 +2562,31 @@ function useIsCompact(layoutOptions) {
2416
2562
  }
2417
2563
 
2418
2564
  const ModelComponent = ({ data: unresolvedData, className, nodeTitle, layoutOptions, exportProps, }) => {
2419
- var _a;
2565
+ var _a, _b;
2420
2566
  const resolveRef = useInlineRefResolver();
2421
2567
  const data = useResolvedObject(unresolvedData);
2568
+ const { nodeHasChanged } = useOptionsCtx();
2422
2569
  const { ref: layoutRef, isCompact } = useIsCompact(layoutOptions);
2423
- const title = (_a = data.title) !== null && _a !== void 0 ? _a : nodeTitle;
2570
+ const nodeId = (_a = data === null || data === void 0 ? void 0 : data['x-stoplight']) === null || _a === void 0 ? void 0 : _a.id;
2571
+ const title = (_b = data.title) !== null && _b !== void 0 ? _b : nodeTitle;
2424
2572
  const isInternal = !!data['x-internal'];
2425
2573
  const shouldDisplayHeader = !(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.noHeading) && (title !== undefined || (exportProps && !(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.hideExport)));
2574
+ const titleChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId, attr: ['title', 'internal'] });
2426
2575
  const header = (shouldDisplayHeader || isInternal) && (React__namespace.createElement(mosaic.Flex, { justifyContent: "between", alignItems: "center" },
2427
- React__namespace.createElement(mosaic.HStack, { spacing: 5 },
2428
- title && (React__namespace.createElement(mosaic.Heading, { size: 1, fontWeight: "semibold" }, title)),
2429
- React__namespace.createElement(mosaic.HStack, { spacing: 2 }, isInternal && React__namespace.createElement(InternalBadge, null))),
2576
+ React__namespace.createElement(mosaic.Box, { pos: "relative" },
2577
+ React__namespace.createElement(mosaic.HStack, { spacing: 5 },
2578
+ title && (React__namespace.createElement(mosaic.Heading, { size: 1, fontWeight: "semibold" }, title)),
2579
+ React__namespace.createElement(mosaic.HStack, { spacing: 2 }, isInternal && React__namespace.createElement(InternalBadge, null))),
2580
+ React__namespace.createElement(mosaic.NodeAnnotation, { change: titleChanged })),
2430
2581
  exportProps && !(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.hideExport) && React__namespace.createElement(ExportButton, Object.assign({}, exportProps))));
2431
2582
  const modelExamples = !(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.hideModelExamples) && React__namespace.createElement(ModelExamples, { data: data, isCollapsible: isCompact });
2583
+ const descriptionChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId, attr: 'description' });
2432
2584
  const description = (React__namespace.createElement(mosaic.VStack, { spacing: 10 },
2433
- data.description && data.type === 'object' && React__namespace.createElement(MarkdownViewer, { role: "textbox", markdown: data.description }),
2585
+ data.description && data.type === 'object' && (React__namespace.createElement(mosaic.Box, { pos: "relative" },
2586
+ React__namespace.createElement(MarkdownViewer, { role: "textbox", markdown: data.description }),
2587
+ React__namespace.createElement(mosaic.NodeAnnotation, { change: descriptionChanged }))),
2434
2588
  isCompact && modelExamples,
2435
- React__namespace.createElement(jsonSchemaViewer.JsonSchemaViewer, { resolveRef: resolveRef, schema: getOriginalObject(data) })));
2589
+ React__namespace.createElement(jsonSchemaViewer.JsonSchemaViewer, { resolveRef: resolveRef, schema: getOriginalObject(data), nodeHasChanged: nodeHasChanged, skipTopLevelDescription: true })));
2436
2590
  return (React__namespace.createElement(TwoColumnLayout, { ref: layoutRef, className: cn__default["default"]('Model', className), header: header, left: description, right: !isCompact && modelExamples }));
2437
2591
  };
2438
2592
  const ModelExamples = React__namespace.memo(({ data, isCollapsible = false }) => {
@@ -2454,16 +2608,16 @@ const ModelExamples = React__namespace.memo(({ data, isCollapsible = false }) =>
2454
2608
  const Model = reactErrorBoundary.withErrorBoundary(ModelComponent, { recoverableProps: ['data'] });
2455
2609
 
2456
2610
  const Docs = React__namespace.memo((_a) => {
2457
- var { nodeType, nodeData, useNodeForRefResolving = false, refResolver } = _a, commonProps = tslib.__rest(_a, ["nodeType", "nodeData", "useNodeForRefResolving", "refResolver"]);
2611
+ var { nodeType, nodeData, useNodeForRefResolving = false, refResolver, nodeHasChanged } = _a, commonProps = tslib.__rest(_a, ["nodeType", "nodeData", "useNodeForRefResolving", "refResolver", "nodeHasChanged"]);
2458
2612
  const parsedNode = useParsedData(nodeType, nodeData);
2459
2613
  if (!parsedNode) {
2460
2614
  return null;
2461
2615
  }
2462
- const parsedDocs = React__namespace.createElement(ParsedDocs, Object.assign({ node: parsedNode }, commonProps));
2616
+ let elem = React__namespace.createElement(ParsedDocs, Object.assign({ node: parsedNode }, commonProps));
2463
2617
  if (useNodeForRefResolving) {
2464
- return (React__namespace.createElement(InlineRefResolverProvider, { document: parsedNode.data, resolver: refResolver }, parsedDocs));
2618
+ elem = (React__namespace.createElement(InlineRefResolverProvider, { document: parsedNode.data, resolver: refResolver }, elem));
2465
2619
  }
2466
- return parsedDocs;
2620
+ return React__namespace.createElement(ElementsOptionsProvider, { nodeHasChanged: nodeHasChanged }, elem);
2467
2621
  });
2468
2622
  const ParsedDocs = (_a) => {
2469
2623
  var { node } = _a, commonProps = tslib.__rest(_a, ["node"]);
@@ -2891,16 +3045,33 @@ const useRouter = (router, basePath, staticRouterPath) => {
2891
3045
  };
2892
3046
  };
2893
3047
 
3048
+ const components = {
3049
+ a: ReactRouterMarkdownLink,
3050
+ h2: (_a) => {
3051
+ var props = tslib.__rest(_a, ["color"]);
3052
+ return React__namespace.createElement(LinkHeading, Object.assign({ size: 2 }, props));
3053
+ },
3054
+ h3: (_a) => {
3055
+ var props = tslib.__rest(_a, ["color"]);
3056
+ return React__namespace.createElement(LinkHeading, Object.assign({ size: 3 }, props));
3057
+ },
3058
+ h4: (_a) => {
3059
+ var props = tslib.__rest(_a, ["color"]);
3060
+ return React__namespace.createElement(LinkHeading, Object.assign({ size: 4 }, props));
3061
+ },
3062
+ };
2894
3063
  function withRouter(WrappedComponent) {
2895
3064
  const WithRouter = (props) => {
2896
3065
  var _a, _b, _c;
2897
3066
  const basePath = (_a = props.basePath) !== null && _a !== void 0 ? _a : '/';
2898
3067
  const staticRouterPath = (_b = props.staticRouterPath) !== null && _b !== void 0 ? _b : '';
2899
- const { Router, routerProps } = useRouter((_c = props.router) !== null && _c !== void 0 ? _c : 'history', basePath, staticRouterPath);
2900
- return (React__namespace.createElement(Router, Object.assign({}, routerProps, { key: basePath }),
2901
- React__namespace.createElement(reactRouterDom.Route, { path: "/" },
2902
- React__namespace.createElement(MarkdownComponentsProvider, { value: { a: ReactRouterMarkdownLink } },
2903
- React__namespace.createElement(WrappedComponent, Object.assign({}, props))))));
3068
+ const routerType = (_c = props.router) !== null && _c !== void 0 ? _c : 'history';
3069
+ const { Router, routerProps } = useRouter(routerType, basePath, staticRouterPath);
3070
+ return (React__namespace.createElement(RouterTypeContext.Provider, { value: routerType },
3071
+ React__namespace.createElement(Router, Object.assign({}, routerProps, { key: basePath }),
3072
+ React__namespace.createElement(reactRouterDom.Route, { path: "/" },
3073
+ React__namespace.createElement(MarkdownComponentsProvider, { value: components },
3074
+ React__namespace.createElement(WrappedComponent, Object.assign({}, props)))))));
2904
3075
  };
2905
3076
  WithRouter.displayName = `WithRouter(${getDisplayName(WrappedComponent)})`;
2906
3077
  return WithRouter;