@stoplight/elements-core 7.6.4 → 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',
@@ -464,6 +482,21 @@ function createNamedContext(name, defaultValue) {
464
482
  return context;
465
483
  }
466
484
 
485
+ const ALPHANUMERIC = /[^A-Za-z0-9]+$/;
486
+ function useChosenServerUrl(chosenServerUrl) {
487
+ const match = ALPHANUMERIC.exec(chosenServerUrl);
488
+ if (match === null) {
489
+ return {
490
+ leading: chosenServerUrl,
491
+ trailing: null,
492
+ };
493
+ }
494
+ return {
495
+ leading: chosenServerUrl.substring(0, match.index),
496
+ trailing: chosenServerUrl.substring(match.index),
497
+ };
498
+ }
499
+
467
500
  const chosenServerAtom = jotai.atom(undefined);
468
501
 
469
502
  function isValidServer(server) {
@@ -478,6 +511,7 @@ const getServersToDisplay = (originalServers, mockUrl) => {
478
511
  .filter(isValidServer);
479
512
  if (mockUrl) {
480
513
  servers.push({
514
+ id: 'mock',
481
515
  description: 'Mock Server',
482
516
  url: mockUrl,
483
517
  });
@@ -667,15 +701,12 @@ const requestSampleConfigs = {
667
701
  mosaicCodeViewerLanguage: 'php',
668
702
  httpSnippetLanguage: 'php',
669
703
  libraries: {
670
- 'pecl/http 1': {
671
- httpSnippetLibrary: 'http1',
672
- },
673
- 'pecl/http 2': {
674
- httpSnippetLibrary: 'http2',
675
- },
676
704
  cURL: {
677
705
  httpSnippetLibrary: 'curl',
678
706
  },
707
+ guzzle: {
708
+ httpSnippetLibrary: 'guzzle',
709
+ },
679
710
  },
680
711
  },
681
712
  Powershell: {
@@ -1038,9 +1069,12 @@ function parameterSupportsFileUpload(parameter) {
1038
1069
  (((_b = parameter.schema) === null || _b === void 0 ? void 0 : _b.contentEncoding) === 'base64' ||
1039
1070
  ((_c = parameter.schema) === null || _c === void 0 ? void 0 : _c.contentMediaType) === 'application/octet-stream'));
1040
1071
  }
1072
+ function stringifyValue(value) {
1073
+ return typeof value === 'object' ? JSON.stringify(value) : escapeQuotes(String(value));
1074
+ }
1041
1075
  function exampleValue(example) {
1042
1076
  const value = 'value' in example ? example.value : example.externalValue;
1043
- return escapeQuotes(String(value));
1077
+ return stringifyValue(value);
1044
1078
  }
1045
1079
  function escapeQuotes(value) {
1046
1080
  return value.replace(/"/g, '\\"');
@@ -1061,7 +1095,7 @@ const getValueForParameter = (parameter) => {
1061
1095
  var _a, _b, _c;
1062
1096
  const defaultValue = retrieveDefaultFromSchema(parameter);
1063
1097
  if (typeof defaultValue !== 'undefined') {
1064
- return { value: String(defaultValue), isDefault: true };
1098
+ return { value: stringifyValue(defaultValue), isDefault: true };
1065
1099
  }
1066
1100
  const examples = (_a = parameter.examples) !== null && _a !== void 0 ? _a : [];
1067
1101
  if (examples.length > 0) {
@@ -1069,7 +1103,7 @@ const getValueForParameter = (parameter) => {
1069
1103
  }
1070
1104
  const enums = (_c = (_b = parameter.schema) === null || _b === void 0 ? void 0 : _b.enum) !== null && _c !== void 0 ? _c : [];
1071
1105
  if (enums.length > 0) {
1072
- return { value: String(enums[0]) };
1106
+ return { value: stringifyValue(enums[0]) };
1073
1107
  }
1074
1108
  return { value: '' };
1075
1109
  };
@@ -1354,13 +1388,77 @@ const getServerUrl = ({ chosenServer, httpOperation, mockData, corsProxy, }) =>
1354
1388
  }
1355
1389
  return serverUrl;
1356
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
+ };
1357
1455
  function buildFetchRequest({ httpOperation, mediaTypeContent, bodyInput, parameterValues, mockData, auth, chosenServer, credentials = 'omit', corsProxy, }) {
1358
- var _a, _b, _c, _d, _e, _f;
1456
+ var _a, _b, _c;
1359
1457
  return tslib.__awaiter(this, void 0, void 0, function* () {
1360
1458
  const serverUrl = getServerUrl({ httpOperation, mockData, chosenServer, corsProxy });
1361
1459
  const shouldIncludeBody = ['PUT', 'POST', 'PATCH'].includes(httpOperation.method.toUpperCase());
1362
- 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 : [];
1363
- 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)
1364
1462
  .map(header => { var _a; return ({ name: header.name, value: (_a = parameterValues[header.name]) !== null && _a !== void 0 ? _a : '' }); })
1365
1463
  .filter(({ value }) => value.length > 0);
1366
1464
  const [queryParamsWithAuth, headersWithAuth] = runAuthRequestEhancements(auth, queryParams, rawHeaders);
@@ -1369,7 +1467,7 @@ function buildFetchRequest({ httpOperation, mediaTypeContent, bodyInput, paramet
1369
1467
  urlObject.search = new URLSearchParams(queryParamsWithAuth.map(nameAndValueObjectToPair)).toString();
1370
1468
  const body = typeof bodyInput === 'object' ? yield createRequestBody(mediaTypeContent, bodyInput) : bodyInput;
1371
1469
  const headers = Object.assign(Object.assign(Object.assign({}, ((mediaTypeContent === null || mediaTypeContent === void 0 ? void 0 : mediaTypeContent.mediaType) !== 'multipart/form-data' && {
1372
- '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',
1373
1471
  })), Object.fromEntries(headersWithAuth.map(nameAndValueObjectToPair))), mockData === null || mockData === void 0 ? void 0 : mockData.header);
1374
1472
  return [
1375
1473
  urlObject.href,
@@ -1429,13 +1527,13 @@ const runAuthRequestEhancements = (auth, queryParams, headers) => {
1429
1527
  return [newQueryParams, newHeaders];
1430
1528
  };
1431
1529
  function buildHarRequest({ httpOperation, bodyInput, parameterValues, mediaTypeContent, auth, mockData, chosenServer, corsProxy, }) {
1432
- var _a, _b, _c, _d, _e, _f, _g;
1530
+ var _a, _b, _c, _d;
1433
1531
  return tslib.__awaiter(this, void 0, void 0, function* () {
1434
1532
  const serverUrl = getServerUrl({ httpOperation, mockData, chosenServer, corsProxy });
1435
1533
  const mimeType = (_a = mediaTypeContent === null || mediaTypeContent === void 0 ? void 0 : mediaTypeContent.mediaType) !== null && _a !== void 0 ? _a : 'application/json';
1436
1534
  const shouldIncludeBody = ['PUT', 'POST', 'PATCH'].includes(httpOperation.method.toUpperCase());
1437
- 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 : [];
1438
- 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 : [];
1439
1537
  if (mockData === null || mockData === void 0 ? void 0 : mockData.header) {
1440
1538
  headerParams.push({ name: 'Prefer', value: mockData.header.Prefer });
1441
1539
  }
@@ -2044,9 +2142,20 @@ ${scopes.map(([key, value]) => `- \`${key}\` - ${value}`).join('\n')}`;
2044
2142
  return description;
2045
2143
  }
2046
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
+
2047
2156
  const SectionTitle = ({ title, id, size = 2, children }) => {
2048
2157
  return (React__namespace.createElement(mosaic.HStack, { spacing: 6 },
2049
- 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),
2050
2159
  children));
2051
2160
  };
2052
2161
  const SectionSubtitle = props => {
@@ -2069,6 +2178,7 @@ const Body = ({ body, onChange }) => {
2069
2178
  var _a;
2070
2179
  const refResolver = useInlineRefResolver();
2071
2180
  const [chosenContent, setChosenContent] = React__namespace.useState(0);
2181
+ const { nodeHasChanged } = useOptionsCtx();
2072
2182
  React__namespace.useEffect(() => {
2073
2183
  onChange(chosenContent);
2074
2184
  }, [chosenContent]);
@@ -2076,11 +2186,14 @@ const Body = ({ body, onChange }) => {
2076
2186
  return null;
2077
2187
  const { contents = [], description } = body;
2078
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' });
2079
2190
  return (React__namespace.createElement(mosaic.VStack, { spacing: 6 },
2080
2191
  React__namespace.createElement(SectionSubtitle, { title: "Body", id: "request-body" }, contents.length > 0 && (React__namespace.createElement(mosaic.Flex, { flex: 1, justify: "end" },
2081
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" })))),
2082
- description && React__namespace.createElement(MarkdownViewer, { markdown: description }),
2083
- 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 }))));
2084
2197
  };
2085
2198
  Body.displayName = 'HttpOperation.Body';
2086
2199
 
@@ -2104,14 +2217,16 @@ const defaultStyle = {
2104
2217
  cookie: types.HttpParamStyles.Form,
2105
2218
  };
2106
2219
  const Parameters = ({ parameters, parameterType }) => {
2220
+ const { nodeHasChanged } = useOptionsCtx();
2221
+ const refResolver = useInlineRefResolver();
2107
2222
  const schema = React__namespace.useMemo(() => httpOperationParamsToSchema({ parameters, parameterType }), [parameters, parameterType]);
2108
2223
  if (!schema)
2109
2224
  return null;
2110
- 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 });
2111
2226
  };
2112
2227
  Parameters.displayName = 'HttpOperation.Parameters';
2113
2228
  const httpOperationParamsToSchema = ({ parameters, parameterType }) => {
2114
- var _a;
2229
+ var _a, _b, _c;
2115
2230
  if (!parameters || !parameters.length)
2116
2231
  return null;
2117
2232
  const schema = {
@@ -2120,8 +2235,6 @@ const httpOperationParamsToSchema = ({ parameters, parameterType }) => {
2120
2235
  };
2121
2236
  const sortedParams = sortBy__default["default"](parameters, ['required', 'name']);
2122
2237
  for (const p of sortedParams) {
2123
- if (!p.schema)
2124
- continue;
2125
2238
  const { name, description, required, deprecated, examples, style } = p;
2126
2239
  const paramExamples = (examples === null || examples === void 0 ? void 0 : examples.map(example => {
2127
2240
  if (isNodeExample(example)) {
@@ -2131,10 +2244,10 @@ const httpOperationParamsToSchema = ({ parameters, parameterType }) => {
2131
2244
  })) || [];
2132
2245
  const schemaExamples = (_a = p.schema) === null || _a === void 0 ? void 0 : _a.examples;
2133
2246
  const schemaExamplesArray = Array.isArray(schemaExamples) ? schemaExamples : [];
2134
- const paramDescription = description || p.schema.description;
2247
+ const paramDescription = description || ((_b = p.schema) === null || _b === void 0 ? void 0 : _b.description);
2135
2248
  const paramDeprecated = deprecated || p.schema.deprecated;
2136
2249
  const paramStyle = style && defaultStyle[parameterType] !== style ? readableStyles[style] || style : undefined;
2137
- 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 }) });
2138
2251
  if (required) {
2139
2252
  schema.required.push(name);
2140
2253
  }
@@ -2157,7 +2270,7 @@ const Request = ({ operation: { request, request: { path: pathParams = [], heade
2157
2270
  return null;
2158
2271
  return (React__namespace.createElement(mosaic.VStack, { spacing: 8 },
2159
2272
  React__namespace.createElement(SectionTitle, { title: "Request" }),
2160
- 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 }),
2161
2274
  pathParams.length > 0 && (React__namespace.createElement(mosaic.VStack, { spacing: 5 },
2162
2275
  React__namespace.createElement(SectionSubtitle, { title: "Path Parameters" }),
2163
2276
  React__namespace.createElement(Parameters, { parameterType: "path", parameters: pathParams }))),
@@ -2178,6 +2291,15 @@ const SecurityPanel = ({ scheme, includeKey }) => {
2178
2291
  const [expandedState, setExpanded] = jotai.useAtom(schemeExpandedState);
2179
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 })) },
2180
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 }) }))))));
2181
2303
  };
2182
2304
 
2183
2305
  const Responses = ({ responses: unsortedResponses, onStatusCodeChange, onMediaTypeChange }) => {
@@ -2200,13 +2322,17 @@ const Response = ({ response, onMediaTypeChange }) => {
2200
2322
  const { contents = [], headers = [], description } = response;
2201
2323
  const [chosenContent, setChosenContent] = React__namespace.useState(0);
2202
2324
  const refResolver = useInlineRefResolver();
2325
+ const { nodeHasChanged } = useOptionsCtx();
2203
2326
  const responseContent = contents[chosenContent];
2204
2327
  const schema = responseContent === null || responseContent === void 0 ? void 0 : responseContent.schema;
2205
2328
  React__namespace.useEffect(() => {
2206
2329
  responseContent && onMediaTypeChange(responseContent.mediaType);
2207
2330
  }, [responseContent]);
2331
+ const descriptionChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: response.id, attr: 'description' });
2208
2332
  return (React__namespace.createElement(mosaic.VStack, { spacing: 8, pt: 8 },
2209
- 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 }))),
2210
2336
  headers.length > 0 && (React__namespace.createElement(mosaic.VStack, { spacing: 5 },
2211
2337
  React__namespace.createElement(SectionSubtitle, { title: "Headers", id: "response-headers" }),
2212
2338
  React__namespace.createElement(Parameters, { parameterType: "header", parameters: headers }))),
@@ -2214,7 +2340,7 @@ const Response = ({ response, onMediaTypeChange }) => {
2214
2340
  React__namespace.createElement(SectionSubtitle, { title: "Body", id: "response-body" },
2215
2341
  React__namespace.createElement(mosaic.Flex, { flex: 1, justify: "end" },
2216
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" }))),
2217
- 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 }))))));
2218
2344
  };
2219
2345
  Response.displayName = 'HttpOperation.Response';
2220
2346
  const codeToIntentVal = (code) => {
@@ -2232,6 +2358,7 @@ const codeToIntentVal = (code) => {
2232
2358
  };
2233
2359
 
2234
2360
  const HttpOperationComponent = React__namespace.memo(({ className, data: unresolvedData, layoutOptions, tryItCredentialsPolicy, tryItCorsProxy }) => {
2361
+ const { nodeHasChanged } = useOptionsCtx();
2235
2362
  const data = useResolvedObject(unresolvedData);
2236
2363
  const mocking = React__namespace.useContext(MockingContext);
2237
2364
  const isDeprecated = !!data.deprecated;
@@ -2241,15 +2368,12 @@ const HttpOperationComponent = React__namespace.memo(({ className, data: unresol
2241
2368
  const [requestBodyIndex, setTextRequestBodyIndex] = React__namespace.useState(0);
2242
2369
  const prettyName = (data.summary || data.iid || '').trim();
2243
2370
  const hasBadges = isDeprecated || isInternal;
2244
- const header = (!(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.noHeading) || hasBadges) && (React__namespace.createElement(mosaic.VStack, { spacing: 5 },
2245
- React__namespace.createElement(mosaic.HStack, { spacing: 5 },
2246
- !(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.noHeading) && prettyName ? (React__namespace.createElement(mosaic.Heading, { size: 1, fontWeight: "semibold" }, prettyName)) : null,
2247
- React__namespace.createElement(mosaic.HStack, { spacing: 2 },
2248
- isDeprecated && React__namespace.createElement(DeprecatedBadge, null),
2249
- isInternal && React__namespace.createElement(InternalBadge, { isHttpService: true }))),
2250
- 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' });
2251
2373
  const description = (React__namespace.createElement(mosaic.VStack, { spacing: 10 },
2252
- 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 }))),
2253
2377
  React__namespace.createElement(Request, { onChange: setTextRequestBodyIndex, operation: data }),
2254
2378
  data.responses && (React__namespace.createElement(Responses, { responses: data.responses, onMediaTypeChange: setResponseMediaType, onStatusCodeChange: setResponseStatusCode }))));
2255
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 }));
@@ -2271,12 +2395,34 @@ function MethodPath({ method, path }) {
2271
2395
  function MethodPathInner({ method, path, chosenServerUrl }) {
2272
2396
  const isDark = mosaic.useThemeIsDark();
2273
2397
  const fullUrl = `${chosenServerUrl}${path}`;
2274
- const pathElem = (React__namespace.createElement(mosaic.Flex, { overflowX: "hidden" },
2275
- chosenServerUrl ? (React__namespace.createElement(mosaic.Box, { dir: "rtl", color: "muted", fontSize: "lg", textOverflow: "truncate", overflowX: "hidden" }, chosenServerUrl)) : null,
2276
- React__namespace.createElement(mosaic.Box, { fontSize: "lg", fontWeight: "semibold", flex: 1 }, path)));
2398
+ const { leading, trailing } = useChosenServerUrl(chosenServerUrl);
2399
+ const pathElem = (React__namespace.createElement(mosaic.Flex, { overflowX: "hidden", fontSize: "lg", userSelect: "all" },
2400
+ React__namespace.createElement(mosaic.Box, { dir: "rtl", color: "muted", textOverflow: "truncate", overflowX: "hidden" },
2401
+ leading,
2402
+ trailing !== null && (React__namespace.createElement(mosaic.Box, { as: "span", dir: "ltr", style: { unicodeBidi: 'bidi-override' } }, trailing))),
2403
+ React__namespace.createElement(mosaic.Box, { fontWeight: "semibold", flex: 1 }, path)));
2277
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 },
2278
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),
2279
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 }))));
2280
2426
  }
2281
2427
 
2282
2428
  const PoweredByLink = ({ source, pathname, packageType, layout = 'sidebar' }) => {
@@ -2287,7 +2433,9 @@ const PoweredByLink = ({ source, pathname, packageType, layout = 'sidebar' }) =>
2287
2433
  React__namespace.createElement("strong", null, "Stoplight"))));
2288
2434
  };
2289
2435
 
2290
- 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'] });
2291
2439
  const contactLink = (contact === null || contact === void 0 ? void 0 : contact.name) && (contact === null || contact === void 0 ? void 0 : contact.url)
2292
2440
  ? `[Contact ${contact.name}](${contact.url})`
2293
2441
  : (contact === null || contact === void 0 ? void 0 : contact.email)
@@ -2296,12 +2444,13 @@ const AdditionalInfo = ({ termsOfService, contact, license }) => {
2296
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`;
2297
2445
  const licenseLink = (license === null || license === void 0 ? void 0 : license.name) && licenseUrl ? `[${license.name} License](${licenseUrl})` : '';
2298
2446
  const tosLink = termsOfService ? `[Terms of Service](${termsOfService})` : '';
2299
- 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" },
2300
2448
  React__default["default"].createElement(mosaic.Panel.Titlebar, { bg: "canvas-300" },
2301
2449
  React__default["default"].createElement("span", { role: "heading" }, "Additional Information")),
2302
2450
  React__default["default"].createElement(mosaic.Panel.Content, { p: 0 },
2303
2451
  React__default["default"].createElement(mosaic.Panel.Content, null,
2304
- 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;
2305
2454
  };
2306
2455
 
2307
2456
  const ExportButton = ({ original, bundled }) => {
@@ -2323,11 +2472,14 @@ const SecuritySchemes = ({ schemes, defaultScheme, defaultCollapsed = false, })
2323
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) }))))));
2324
2473
  };
2325
2474
  const SecurityScheme = ({ scheme, defaultIsOpen, isCollapsible, showSchemeKey }) => {
2326
- 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" },
2327
2478
  React__default["default"].createElement(mosaic.Panel.Titlebar, null,
2328
2479
  React__default["default"].createElement(mosaic.Box, { as: "span", role: "heading" }, getReadableSecurityName(scheme, showSchemeKey))),
2329
2480
  React__default["default"].createElement(mosaic.Panel.Content, null,
2330
- 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 })));
2331
2483
  };
2332
2484
 
2333
2485
  const ServerInfo = ({ servers, mockUrl }) => {
@@ -2341,13 +2493,14 @@ const ServerInfo = ({ servers, mockUrl }) => {
2341
2493
  return (React__namespace.createElement(mosaic.InvertTheme, null,
2342
2494
  React__namespace.createElement(mosaic.Panel, { rounded: true, isCollapsible: false, className: "BaseURLContent", w: "full" },
2343
2495
  React__namespace.createElement(mosaic.Panel.Titlebar, { whitespace: "nowrap" }, "API Base URL"),
2344
- React__namespace.createElement(mosaic.Box, { overflowX: "auto" },
2345
- React__namespace.createElement(mosaic.Panel.Content, { w: "full", className: "sl-flex sl-flex-col" },
2346
- 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 })))))))));
2347
2498
  };
2348
- const ServerUrl = ({ description, url, marginBottom = true }) => {
2499
+ const ServerUrl = ({ id, description, url }) => {
2500
+ const { nodeHasChanged } = useOptionsCtx();
2349
2501
  const { onCopy, hasCopied } = mosaic.useClipboard(url);
2350
- 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" },
2351
2504
  React__namespace.createElement(mosaic.Text, { pr: 2, fontWeight: "bold" },
2352
2505
  description,
2353
2506
  ":"),
@@ -2357,26 +2510,37 @@ const ServerUrl = ({ description, url, marginBottom = true }) => {
2357
2510
  React__namespace.createElement(mosaic.Icon, { className: "sl-ml-1", icon: ['fas', 'copy'] }))),
2358
2511
  hasCopied && (React__namespace.createElement(mosaic.Box, { p: 1 },
2359
2512
  "Copied Server URL ",
2360
- 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 })));
2361
2515
  };
2362
2516
 
2363
- const HttpServiceComponent = React__namespace.memo(({ data, location = {}, layoutOptions, exportProps }) => {
2517
+ const HttpServiceComponent = React__namespace.memo(({ data: unresolvedData, location = {}, layoutOptions, exportProps }) => {
2364
2518
  var _a, _b, _c, _d;
2519
+ const { nodeHasChanged } = useOptionsCtx();
2520
+ const data = useResolvedObject(unresolvedData);
2365
2521
  const { search, pathname } = location;
2366
2522
  const mocking = React__namespace.useContext(MockingContext);
2367
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' });
2368
2527
  return (React__namespace.createElement(mosaic.Box, { mb: 10 },
2369
2528
  data.name && !(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.noHeading) && (React__namespace.createElement(mosaic.Flex, { justifyContent: "between", alignItems: "center" },
2370
- 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 })),
2371
2532
  exportProps && !(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.hideExport) && React__namespace.createElement(ExportButton, Object.assign({}, exportProps)))),
2372
- data.version && (React__namespace.createElement(mosaic.Box, { mb: 5 },
2373
- 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 }))),
2374
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" })),
2375
2537
  React__namespace.createElement(mosaic.VStack, { spacing: 6 },
2376
2538
  React__namespace.createElement(ServerInfo, { servers: (_b = data.servers) !== null && _b !== void 0 ? _b : [], mockUrl: mocking.mockUrl }),
2377
- 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 }))),
2378
- 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 })))),
2379
- 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 })))));
2380
2544
  });
2381
2545
  HttpServiceComponent.displayName = 'HttpService.Component';
2382
2546
  const HttpService = reactErrorBoundary.withErrorBoundary(HttpServiceComponent, { recoverableProps: ['data'] });
@@ -2398,23 +2562,31 @@ function useIsCompact(layoutOptions) {
2398
2562
  }
2399
2563
 
2400
2564
  const ModelComponent = ({ data: unresolvedData, className, nodeTitle, layoutOptions, exportProps, }) => {
2401
- var _a;
2565
+ var _a, _b;
2402
2566
  const resolveRef = useInlineRefResolver();
2403
2567
  const data = useResolvedObject(unresolvedData);
2568
+ const { nodeHasChanged } = useOptionsCtx();
2404
2569
  const { ref: layoutRef, isCompact } = useIsCompact(layoutOptions);
2405
- 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;
2406
2572
  const isInternal = !!data['x-internal'];
2407
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'] });
2408
2575
  const header = (shouldDisplayHeader || isInternal) && (React__namespace.createElement(mosaic.Flex, { justifyContent: "between", alignItems: "center" },
2409
- React__namespace.createElement(mosaic.HStack, { spacing: 5 },
2410
- title && (React__namespace.createElement(mosaic.Heading, { size: 1, fontWeight: "semibold" }, title)),
2411
- 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 })),
2412
2581
  exportProps && !(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.hideExport) && React__namespace.createElement(ExportButton, Object.assign({}, exportProps))));
2413
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' });
2414
2584
  const description = (React__namespace.createElement(mosaic.VStack, { spacing: 10 },
2415
- 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 }))),
2416
2588
  isCompact && modelExamples,
2417
- 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 })));
2418
2590
  return (React__namespace.createElement(TwoColumnLayout, { ref: layoutRef, className: cn__default["default"]('Model', className), header: header, left: description, right: !isCompact && modelExamples }));
2419
2591
  };
2420
2592
  const ModelExamples = React__namespace.memo(({ data, isCollapsible = false }) => {
@@ -2436,16 +2608,16 @@ const ModelExamples = React__namespace.memo(({ data, isCollapsible = false }) =>
2436
2608
  const Model = reactErrorBoundary.withErrorBoundary(ModelComponent, { recoverableProps: ['data'] });
2437
2609
 
2438
2610
  const Docs = React__namespace.memo((_a) => {
2439
- 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"]);
2440
2612
  const parsedNode = useParsedData(nodeType, nodeData);
2441
2613
  if (!parsedNode) {
2442
2614
  return null;
2443
2615
  }
2444
- const parsedDocs = React__namespace.createElement(ParsedDocs, Object.assign({ node: parsedNode }, commonProps));
2616
+ let elem = React__namespace.createElement(ParsedDocs, Object.assign({ node: parsedNode }, commonProps));
2445
2617
  if (useNodeForRefResolving) {
2446
- return (React__namespace.createElement(InlineRefResolverProvider, { document: parsedNode.data, resolver: refResolver }, parsedDocs));
2618
+ elem = (React__namespace.createElement(InlineRefResolverProvider, { document: parsedNode.data, resolver: refResolver }, elem));
2447
2619
  }
2448
- return parsedDocs;
2620
+ return React__namespace.createElement(ElementsOptionsProvider, { nodeHasChanged: nodeHasChanged }, elem);
2449
2621
  });
2450
2622
  const ParsedDocs = (_a) => {
2451
2623
  var { node } = _a, commonProps = tslib.__rest(_a, ["node"]);
@@ -2873,16 +3045,33 @@ const useRouter = (router, basePath, staticRouterPath) => {
2873
3045
  };
2874
3046
  };
2875
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
+ };
2876
3063
  function withRouter(WrappedComponent) {
2877
3064
  const WithRouter = (props) => {
2878
3065
  var _a, _b, _c;
2879
3066
  const basePath = (_a = props.basePath) !== null && _a !== void 0 ? _a : '/';
2880
3067
  const staticRouterPath = (_b = props.staticRouterPath) !== null && _b !== void 0 ? _b : '';
2881
- const { Router, routerProps } = useRouter((_c = props.router) !== null && _c !== void 0 ? _c : 'history', basePath, staticRouterPath);
2882
- return (React__namespace.createElement(Router, Object.assign({}, routerProps, { key: basePath }),
2883
- React__namespace.createElement(reactRouterDom.Route, { path: "/" },
2884
- React__namespace.createElement(MarkdownComponentsProvider, { value: { a: ReactRouterMarkdownLink } },
2885
- 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)))))));
2886
3075
  };
2887
3076
  WithRouter.displayName = `WithRouter(${getDisplayName(WrappedComponent)})`;
2888
3077
  return WithRouter;