@thoughtspot/visual-embed-sdk 1.49.2 → 1.50.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.
Files changed (153) hide show
  1. package/cjs/package.json +4 -10
  2. package/cjs/src/authToken.spec.js +5 -0
  3. package/cjs/src/authToken.spec.js.map +1 -1
  4. package/cjs/src/embed/app.d.ts +7 -1
  5. package/cjs/src/embed/app.d.ts.map +1 -1
  6. package/cjs/src/embed/app.js +6 -0
  7. package/cjs/src/embed/app.js.map +1 -1
  8. package/cjs/src/embed/app.spec.js +11 -2
  9. package/cjs/src/embed/app.spec.js.map +1 -1
  10. package/cjs/src/embed/conversation.d.ts +12 -0
  11. package/cjs/src/embed/conversation.d.ts.map +1 -1
  12. package/cjs/src/embed/conversation.js.map +1 -1
  13. package/cjs/src/embed/conversation.spec.js +30 -0
  14. package/cjs/src/embed/conversation.spec.js.map +1 -1
  15. package/cjs/src/embed/embedConfig.spec.d.ts +2 -0
  16. package/cjs/src/embed/embedConfig.spec.d.ts.map +1 -0
  17. package/cjs/src/embed/embedConfig.spec.js +74 -0
  18. package/cjs/src/embed/embedConfig.spec.js.map +1 -0
  19. package/cjs/src/embed/host-events.spec.d.ts +2 -0
  20. package/cjs/src/embed/host-events.spec.d.ts.map +1 -0
  21. package/cjs/src/embed/host-events.spec.js +1219 -0
  22. package/cjs/src/embed/host-events.spec.js.map +1 -0
  23. package/cjs/src/embed/liveboard.d.ts +6 -0
  24. package/cjs/src/embed/liveboard.d.ts.map +1 -1
  25. package/cjs/src/embed/liveboard.js.map +1 -1
  26. package/cjs/src/embed/spotter-viz-utils.d.ts +29 -0
  27. package/cjs/src/embed/spotter-viz-utils.d.ts.map +1 -1
  28. package/cjs/src/embed/spotter-viz-utils.js.map +1 -1
  29. package/cjs/src/index.d.ts +2 -2
  30. package/cjs/src/index.d.ts.map +1 -1
  31. package/cjs/src/index.js.map +1 -1
  32. package/cjs/src/mixpanel-service.spec.js +14 -0
  33. package/cjs/src/mixpanel-service.spec.js.map +1 -1
  34. package/cjs/src/types.d.ts +93 -6
  35. package/cjs/src/types.d.ts.map +1 -1
  36. package/cjs/src/types.js +88 -3
  37. package/cjs/src/types.js.map +1 -1
  38. package/cjs/src/utils/custom-actions.spec.js +26 -0
  39. package/cjs/src/utils/custom-actions.spec.js.map +1 -1
  40. package/cjs/src/utils/graphql/answerService/answerService.spec.js +15 -0
  41. package/cjs/src/utils/graphql/answerService/answerService.spec.js.map +1 -1
  42. package/cjs/src/utils/processData.spec.js +30 -0
  43. package/cjs/src/utils/processData.spec.js.map +1 -1
  44. package/cjs/src/utils/reporting.spec.js +21 -0
  45. package/cjs/src/utils/reporting.spec.js.map +1 -1
  46. package/cjs/src/utils/sessionInfoService.spec.d.ts +2 -0
  47. package/cjs/src/utils/sessionInfoService.spec.d.ts.map +1 -0
  48. package/cjs/src/utils/sessionInfoService.spec.js +130 -0
  49. package/cjs/src/utils/sessionInfoService.spec.js.map +1 -0
  50. package/cjs/src/utils.js +2 -3
  51. package/cjs/src/utils.js.map +1 -1
  52. package/cjs/src/utils.spec.js +158 -0
  53. package/cjs/src/utils.spec.js.map +1 -1
  54. package/dist/{index-CS7yJ2Jj.js → index-B5xoN6Cp.js} +303 -187
  55. package/dist/src/embed/app.d.ts +7 -1
  56. package/dist/src/embed/app.d.ts.map +1 -1
  57. package/dist/src/embed/conversation.d.ts +12 -0
  58. package/dist/src/embed/conversation.d.ts.map +1 -1
  59. package/dist/src/embed/embedConfig.spec.d.ts +2 -0
  60. package/dist/src/embed/embedConfig.spec.d.ts.map +1 -0
  61. package/dist/src/embed/host-events.spec.d.ts +2 -0
  62. package/dist/src/embed/host-events.spec.d.ts.map +1 -0
  63. package/dist/src/embed/liveboard.d.ts +6 -0
  64. package/dist/src/embed/liveboard.d.ts.map +1 -1
  65. package/dist/src/embed/spotter-viz-utils.d.ts +29 -0
  66. package/dist/src/embed/spotter-viz-utils.d.ts.map +1 -1
  67. package/dist/src/index.d.ts +2 -2
  68. package/dist/src/index.d.ts.map +1 -1
  69. package/dist/src/types.d.ts +93 -6
  70. package/dist/src/types.d.ts.map +1 -1
  71. package/dist/src/utils/sessionInfoService.spec.d.ts +2 -0
  72. package/dist/src/utils/sessionInfoService.spec.d.ts.map +1 -0
  73. package/dist/tsembed-react.es.js +126 -18
  74. package/dist/tsembed-react.js +427 -203
  75. package/dist/tsembed.es.js +126 -18
  76. package/dist/tsembed.js +427 -203
  77. package/dist/visual-embed-sdk-react-full.d.ts +147 -7
  78. package/dist/visual-embed-sdk-react.d.ts +147 -7
  79. package/dist/visual-embed-sdk.d.ts +147 -7
  80. package/lib/package.json +4 -10
  81. package/lib/src/authToken.spec.js +5 -0
  82. package/lib/src/authToken.spec.js.map +1 -1
  83. package/lib/src/embed/app.d.ts +7 -1
  84. package/lib/src/embed/app.d.ts.map +1 -1
  85. package/lib/src/embed/app.js +6 -0
  86. package/lib/src/embed/app.js.map +1 -1
  87. package/lib/src/embed/app.spec.js +11 -2
  88. package/lib/src/embed/app.spec.js.map +1 -1
  89. package/lib/src/embed/conversation.d.ts +12 -0
  90. package/lib/src/embed/conversation.d.ts.map +1 -1
  91. package/lib/src/embed/conversation.js.map +1 -1
  92. package/lib/src/embed/conversation.spec.js +30 -0
  93. package/lib/src/embed/conversation.spec.js.map +1 -1
  94. package/lib/src/embed/embedConfig.spec.d.ts +2 -0
  95. package/lib/src/embed/embedConfig.spec.d.ts.map +1 -0
  96. package/lib/src/embed/embedConfig.spec.js +72 -0
  97. package/lib/src/embed/embedConfig.spec.js.map +1 -0
  98. package/lib/src/embed/host-events.spec.d.ts +2 -0
  99. package/lib/src/embed/host-events.spec.d.ts.map +1 -0
  100. package/lib/src/embed/host-events.spec.js +1216 -0
  101. package/lib/src/embed/host-events.spec.js.map +1 -0
  102. package/lib/src/embed/liveboard.d.ts +6 -0
  103. package/lib/src/embed/liveboard.d.ts.map +1 -1
  104. package/lib/src/embed/liveboard.js.map +1 -1
  105. package/lib/src/embed/spotter-viz-utils.d.ts +29 -0
  106. package/lib/src/embed/spotter-viz-utils.d.ts.map +1 -1
  107. package/lib/src/embed/spotter-viz-utils.js.map +1 -1
  108. package/lib/src/index.d.ts +2 -2
  109. package/lib/src/index.d.ts.map +1 -1
  110. package/lib/src/index.js.map +1 -1
  111. package/lib/src/mixpanel-service.spec.js +14 -0
  112. package/lib/src/mixpanel-service.spec.js.map +1 -1
  113. package/lib/src/types.d.ts +93 -6
  114. package/lib/src/types.d.ts.map +1 -1
  115. package/lib/src/types.js +88 -3
  116. package/lib/src/types.js.map +1 -1
  117. package/lib/src/utils/custom-actions.spec.js +26 -0
  118. package/lib/src/utils/custom-actions.spec.js.map +1 -1
  119. package/lib/src/utils/graphql/answerService/answerService.spec.js +15 -0
  120. package/lib/src/utils/graphql/answerService/answerService.spec.js.map +1 -1
  121. package/lib/src/utils/processData.spec.js +30 -0
  122. package/lib/src/utils/processData.spec.js.map +1 -1
  123. package/lib/src/utils/reporting.spec.js +21 -0
  124. package/lib/src/utils/reporting.spec.js.map +1 -1
  125. package/lib/src/utils/sessionInfoService.spec.d.ts +2 -0
  126. package/lib/src/utils/sessionInfoService.spec.d.ts.map +1 -0
  127. package/lib/src/utils/sessionInfoService.spec.js +127 -0
  128. package/lib/src/utils/sessionInfoService.spec.js.map +1 -0
  129. package/lib/src/utils.js +1 -1
  130. package/lib/src/utils.js.map +1 -1
  131. package/lib/src/utils.spec.js +159 -1
  132. package/lib/src/utils.spec.js.map +1 -1
  133. package/lib/src/visual-embed-sdk.d.ts +147 -7
  134. package/package.json +4 -10
  135. package/src/authToken.spec.ts +6 -0
  136. package/src/embed/app.spec.ts +15 -2
  137. package/src/embed/app.ts +13 -1
  138. package/src/embed/conversation.spec.ts +32 -0
  139. package/src/embed/conversation.ts +12 -0
  140. package/src/embed/embedConfig.spec.ts +79 -0
  141. package/src/embed/host-events.spec.ts +1635 -0
  142. package/src/embed/liveboard.ts +6 -0
  143. package/src/embed/spotter-viz-utils.ts +30 -0
  144. package/src/index.ts +2 -1
  145. package/src/mixpanel-service.spec.ts +15 -0
  146. package/src/types.ts +96 -6
  147. package/src/utils/custom-actions.spec.ts +28 -0
  148. package/src/utils/graphql/answerService/answerService.spec.ts +18 -0
  149. package/src/utils/processData.spec.ts +32 -0
  150. package/src/utils/reporting.spec.ts +27 -0
  151. package/src/utils/sessionInfoService.spec.ts +151 -0
  152. package/src/utils.spec.ts +195 -0
  153. package/src/utils.ts +1 -1
@@ -1,4 +1,4 @@
1
- /* @thoughtspot/visual-embed-sdk version 1.49.2 */
1
+ /* @thoughtspot/visual-embed-sdk version 1.50.0 */
2
2
  'use client';
3
3
  const ALIAS = Symbol.for('yaml.alias');
4
4
  const DOC = Symbol.for('yaml.document');
@@ -475,8 +475,7 @@ function createNodeAnchors(doc, prefix) {
475
475
  return {
476
476
  onAnchor: (source) => {
477
477
  aliasObjects.push(source);
478
- if (!prevAnchors)
479
- prevAnchors = anchorNames(doc);
478
+ prevAnchors ?? (prevAnchors = anchorNames(doc));
480
479
  const anchor = findNewAnchor(prefix, prevAnchors);
481
480
  prevAnchors.add(anchor);
482
481
  return anchor;
@@ -640,23 +639,38 @@ class Alias extends NodeBase {
640
639
  * Resolve the value of this alias within `doc`, finding the last
641
640
  * instance of the `source` anchor before this node.
642
641
  */
643
- resolve(doc) {
642
+ resolve(doc, ctx) {
643
+ if (ctx?.maxAliasCount === 0)
644
+ throw new ReferenceError('Alias resolution is disabled');
645
+ let nodes;
646
+ if (ctx?.aliasResolveCache) {
647
+ nodes = ctx.aliasResolveCache;
648
+ }
649
+ else {
650
+ nodes = [];
651
+ visit$1(doc, {
652
+ Node: (_key, node) => {
653
+ if (isAlias(node) || hasAnchor(node))
654
+ nodes.push(node);
655
+ }
656
+ });
657
+ if (ctx)
658
+ ctx.aliasResolveCache = nodes;
659
+ }
644
660
  let found = undefined;
645
- visit$1(doc, {
646
- Node: (_key, node) => {
647
- if (node === this)
648
- return visit$1.BREAK;
649
- if (node.anchor === this.source)
650
- found = node;
651
- }
652
- });
661
+ for (const node of nodes) {
662
+ if (node === this)
663
+ break;
664
+ if (node.anchor === this.source)
665
+ found = node;
666
+ }
653
667
  return found;
654
668
  }
655
669
  toJSON(_arg, ctx) {
656
670
  if (!ctx)
657
671
  return { source: this.source };
658
672
  const { anchors, doc, maxAliasCount } = ctx;
659
- const source = this.resolve(doc);
673
+ const source = this.resolve(doc, ctx);
660
674
  if (!source) {
661
675
  const msg = `Unresolved alias (the anchor must be set before the alias): ${this.source}`;
662
676
  throw new ReferenceError(msg);
@@ -668,7 +682,7 @@ class Alias extends NodeBase {
668
682
  data = anchors.get(source);
669
683
  }
670
684
  /* istanbul ignore if */
671
- if (!data || data.res === undefined) {
685
+ if (data?.res === undefined) {
672
686
  const msg = 'This should not happen: Alias anchor was not resolved?';
673
687
  throw new ReferenceError(msg);
674
688
  }
@@ -775,8 +789,7 @@ function createNode(value, tagName, ctx) {
775
789
  if (aliasDuplicateObjects && value && typeof value === 'object') {
776
790
  ref = sourceObjects.get(value);
777
791
  if (ref) {
778
- if (!ref.anchor)
779
- ref.anchor = onAnchor(value);
792
+ ref.anchor ?? (ref.anchor = onAnchor(value));
780
793
  return new Alias(ref.anchor);
781
794
  }
782
795
  else {
@@ -1287,7 +1300,7 @@ function blockString({ comment, type, value }, ctx, onComment, onChompKeep) {
1287
1300
  const { blockQuote, commentString, lineWidth } = ctx.options;
1288
1301
  // 1. Block can't end in whitespace unless the last line is non-empty.
1289
1302
  // 2. Strings consisting of only whitespace are best rendered explicitly.
1290
- if (!blockQuote || /\n[\t ]+$/.test(value) || /^\s*$/.test(value)) {
1303
+ if (!blockQuote || /\n[\t ]+$/.test(value)) {
1291
1304
  return quotedString(value, ctx);
1292
1305
  }
1293
1306
  const indent = ctx.indent ||
@@ -1347,23 +1360,32 @@ function blockString({ comment, type, value }, ctx, onComment, onChompKeep) {
1347
1360
  start = start.replace(/\n+/g, `$&${indent}`);
1348
1361
  }
1349
1362
  const indentSize = indent ? '2' : '1'; // root is at -1
1350
- let header = (literal ? '|' : '>') + (startWithSpace ? indentSize : '') + chomp;
1363
+ // Leading | or > is added later
1364
+ let header = (startWithSpace ? indentSize : '') + chomp;
1351
1365
  if (comment) {
1352
1366
  header += ' ' + commentString(comment.replace(/ ?[\r\n]+/g, ' '));
1353
1367
  if (onComment)
1354
1368
  onComment();
1355
1369
  }
1356
- if (literal) {
1357
- value = value.replace(/\n+/g, `$&${indent}`);
1358
- return `${header}\n${indent}${start}${value}${end}`;
1370
+ if (!literal) {
1371
+ const foldedValue = value
1372
+ .replace(/\n+/g, '\n$&')
1373
+ .replace(/(?:^|\n)([\t ].*)(?:([\n\t ]*)\n(?![\n\t ]))?/g, '$1$2') // more-indented lines aren't folded
1374
+ // ^ more-ind. ^ empty ^ capture next empty lines only at end of indent
1375
+ .replace(/\n+/g, `$&${indent}`);
1376
+ let literalFallback = false;
1377
+ const foldOptions = getFoldOptions(ctx, true);
1378
+ if (blockQuote !== 'folded' && type !== Scalar.BLOCK_FOLDED) {
1379
+ foldOptions.onOverflow = () => {
1380
+ literalFallback = true;
1381
+ };
1382
+ }
1383
+ const body = foldFlowLines(`${start}${foldedValue}${end}`, indent, FOLD_BLOCK, foldOptions);
1384
+ if (!literalFallback)
1385
+ return `>${header}\n${indent}${body}`;
1359
1386
  }
1360
- value = value
1361
- .replace(/\n+/g, '\n$&')
1362
- .replace(/(?:^|\n)([\t ].*)(?:([\n\t ]*)\n(?![\n\t ]))?/g, '$1$2') // more-indented lines aren't folded
1363
- // ^ more-ind. ^ empty ^ capture next empty lines only at end of indent
1364
- .replace(/\n+/g, `$&${indent}`);
1365
- const body = foldFlowLines(`${start}${value}${end}`, indent, FOLD_BLOCK, getFoldOptions(ctx, true));
1366
- return `${header}\n${indent}${body}`;
1387
+ value = value.replace(/\n+/g, `$&${indent}`);
1388
+ return `|${header}\n${indent}${start}${value}${end}`;
1367
1389
  }
1368
1390
  function plainString(item, ctx, onComment, onChompKeep) {
1369
1391
  const { type, value } = item;
@@ -1372,10 +1394,9 @@ function plainString(item, ctx, onComment, onChompKeep) {
1372
1394
  (inFlow && /[[\]{},]/.test(value))) {
1373
1395
  return quotedString(value, ctx);
1374
1396
  }
1375
- if (!value ||
1376
- /^[\n\t ,[\]{}#&*!|>'"%@`]|^[?-]$|^[?-][ \t]|[\n:][ \t]|[ \t]\n|[\n\t ]#|[\n\t :]$/.test(value)) {
1397
+ if (/^[\n\t ,[\]{}#&*!|>'"%@`]|^[?-]$|^[?-][ \t]|[\n:][ \t]|[ \t]\n|[\n\t ]#|[\n\t :]$/.test(value)) {
1377
1398
  // not allowed:
1378
- // - empty string, '-' or '?'
1399
+ // - '-' or '?'
1379
1400
  // - start with an indicator character (except [?:-]) or /[?-] /
1380
1401
  // - '\n ', ': ' or ' \n' anywhere
1381
1402
  // - '#' not preceded by a non-space char
@@ -1470,6 +1491,7 @@ function createStringifyContext(doc, options) {
1470
1491
  nullStr: 'null',
1471
1492
  simpleKeys: false,
1472
1493
  singleQuote: null,
1494
+ trailingComma: false,
1473
1495
  trueStr: 'true',
1474
1496
  verifyAliasOrder: true
1475
1497
  }, doc.schema.toStringOptions, options);
@@ -1504,7 +1526,12 @@ function getTagObject(tags, item) {
1504
1526
  let obj;
1505
1527
  if (isScalar$1(item)) {
1506
1528
  obj = item.value;
1507
- const match = tags.filter(t => t.identify?.(obj));
1529
+ let match = tags.filter(t => t.identify?.(obj));
1530
+ if (match.length > 1) {
1531
+ const testMatch = match.filter(t => t.test);
1532
+ if (testMatch.length > 0)
1533
+ match = testMatch;
1534
+ }
1508
1535
  tagObj =
1509
1536
  match.find(t => t.format === item.format) ?? match.find(t => !t.format);
1510
1537
  }
@@ -1513,7 +1540,7 @@ function getTagObject(tags, item) {
1513
1540
  tagObj = tags.find(t => t.nodeClass && obj instanceof t.nodeClass);
1514
1541
  }
1515
1542
  if (!tagObj) {
1516
- const name = obj?.constructor?.name ?? typeof obj;
1543
+ const name = obj?.constructor?.name ?? (obj === null ? 'null' : typeof obj);
1517
1544
  throw new Error(`Tag not resolved for ${name} value`);
1518
1545
  }
1519
1546
  return tagObj;
@@ -1528,7 +1555,7 @@ function stringifyProps(node, tagObj, { anchors, doc }) {
1528
1555
  anchors.add(anchor);
1529
1556
  props.push(`&${anchor}`);
1530
1557
  }
1531
- const tag = node.tag ? node.tag : tagObj.default ? null : tagObj.tag;
1558
+ const tag = node.tag ?? (tagObj.default ? null : tagObj.tag);
1532
1559
  if (tag)
1533
1560
  props.push(doc.directives.tagString(tag));
1534
1561
  return props.join(' ');
@@ -1554,8 +1581,7 @@ function stringify$2(item, ctx, onComment, onChompKeep) {
1554
1581
  const node = isNode(item)
1555
1582
  ? item
1556
1583
  : ctx.doc.createNode(item, { onTagObj: o => (tagObj = o) });
1557
- if (!tagObj)
1558
- tagObj = getTagObject(ctx.doc.schema.tags, node);
1584
+ tagObj ?? (tagObj = getTagObject(ctx.doc.schema.tags, node));
1559
1585
  const props = stringifyProps(node, tagObj, ctx);
1560
1586
  if (props.length > 0)
1561
1587
  ctx.indentAtStart = (ctx.indentAtStart ?? 0) + props.length + 1;
@@ -1669,7 +1695,7 @@ function stringifyPair({ key, value }, ctx, onComment, onChompKeep) {
1669
1695
  ws += `\n${indentComment(cs, ctx.indent)}`;
1670
1696
  }
1671
1697
  if (valueStr === '' && !ctx.inFlow) {
1672
- if (ws === '\n')
1698
+ if (ws === '\n' && valueComment)
1673
1699
  ws = '\n\n';
1674
1700
  }
1675
1701
  else {
@@ -1717,54 +1743,10 @@ function stringifyPair({ key, value }, ctx, onComment, onChompKeep) {
1717
1743
 
1718
1744
  function warn(logLevel, warning) {
1719
1745
  if (logLevel === 'debug' || logLevel === 'warn') {
1720
- if (typeof process !== 'undefined' && process.emitWarning)
1721
- process.emitWarning(warning);
1722
- else
1723
- console.warn(warning);
1746
+ console.warn(warning);
1724
1747
  }
1725
1748
  }
1726
1749
 
1727
- const MERGE_KEY = '<<';
1728
- function addPairToJSMap(ctx, map, { key, value }) {
1729
- if (ctx?.doc.schema.merge && isMergeKey(key)) {
1730
- value = isAlias(value) ? value.resolve(ctx.doc) : value;
1731
- if (isSeq(value))
1732
- for (const it of value.items)
1733
- mergeToJSMap(ctx, map, it);
1734
- else if (Array.isArray(value))
1735
- for (const it of value)
1736
- mergeToJSMap(ctx, map, it);
1737
- else
1738
- mergeToJSMap(ctx, map, value);
1739
- }
1740
- else {
1741
- const jsKey = toJS(key, '', ctx);
1742
- if (map instanceof Map) {
1743
- map.set(jsKey, toJS(value, jsKey, ctx));
1744
- }
1745
- else if (map instanceof Set) {
1746
- map.add(jsKey);
1747
- }
1748
- else {
1749
- const stringKey = stringifyKey(key, jsKey, ctx);
1750
- const jsValue = toJS(value, stringKey, ctx);
1751
- if (stringKey in map)
1752
- Object.defineProperty(map, stringKey, {
1753
- value: jsValue,
1754
- writable: true,
1755
- enumerable: true,
1756
- configurable: true
1757
- });
1758
- else
1759
- map[stringKey] = jsValue;
1760
- }
1761
- }
1762
- return map;
1763
- }
1764
- const isMergeKey = (key) => key === MERGE_KEY ||
1765
- (isScalar$1(key) &&
1766
- key.value === MERGE_KEY &&
1767
- (!key.type || key.type === Scalar.PLAIN));
1768
1750
  // If the value associated with a merge key is a single mapping node, each of
1769
1751
  // its key/value pairs is inserted into the current mapping, unless the key
1770
1752
  // already exists in it. If the value associated with the merge key is a
@@ -1772,8 +1754,36 @@ const isMergeKey = (key) => key === MERGE_KEY ||
1772
1754
  // of these nodes is merged in turn according to its order in the sequence.
1773
1755
  // Keys in mapping nodes earlier in the sequence override keys specified in
1774
1756
  // later mapping nodes. -- http://yaml.org/type/merge.html
1775
- function mergeToJSMap(ctx, map, value) {
1776
- const source = ctx && isAlias(value) ? value.resolve(ctx.doc) : value;
1757
+ const MERGE_KEY = '<<';
1758
+ const merge = {
1759
+ identify: value => value === MERGE_KEY ||
1760
+ (typeof value === 'symbol' && value.description === MERGE_KEY),
1761
+ default: 'key',
1762
+ tag: 'tag:yaml.org,2002:merge',
1763
+ test: /^<<$/,
1764
+ resolve: () => Object.assign(new Scalar(Symbol(MERGE_KEY)), {
1765
+ addToJSMap: addMergeToJSMap
1766
+ }),
1767
+ stringify: () => MERGE_KEY
1768
+ };
1769
+ const isMergeKey = (ctx, key) => (merge.identify(key) ||
1770
+ (isScalar$1(key) &&
1771
+ (!key.type || key.type === Scalar.PLAIN) &&
1772
+ merge.identify(key.value))) &&
1773
+ ctx?.doc.schema.tags.some(tag => tag.tag === merge.tag && tag.default);
1774
+ function addMergeToJSMap(ctx, map, value) {
1775
+ const source = resolveAliasValue(ctx, value);
1776
+ if (isSeq(source))
1777
+ for (const it of source.items)
1778
+ mergeValue(ctx, map, it);
1779
+ else if (Array.isArray(source))
1780
+ for (const it of source)
1781
+ mergeValue(ctx, map, it);
1782
+ else
1783
+ mergeValue(ctx, map, source);
1784
+ }
1785
+ function mergeValue(ctx, map, value) {
1786
+ const source = resolveAliasValue(ctx, value);
1777
1787
  if (!isMap(source))
1778
1788
  throw new Error('Merge sources must be maps or map aliases');
1779
1789
  const srcMap = source.toJSON(null, ctx, Map);
@@ -1796,9 +1806,44 @@ function mergeToJSMap(ctx, map, value) {
1796
1806
  }
1797
1807
  return map;
1798
1808
  }
1809
+ function resolveAliasValue(ctx, value) {
1810
+ return ctx && isAlias(value) ? value.resolve(ctx.doc, ctx) : value;
1811
+ }
1812
+
1813
+ function addPairToJSMap(ctx, map, { key, value }) {
1814
+ if (isNode(key) && key.addToJSMap)
1815
+ key.addToJSMap(ctx, map, value);
1816
+ // TODO: Should drop this special case for bare << handling
1817
+ else if (isMergeKey(ctx, key))
1818
+ addMergeToJSMap(ctx, map, value);
1819
+ else {
1820
+ const jsKey = toJS(key, '', ctx);
1821
+ if (map instanceof Map) {
1822
+ map.set(jsKey, toJS(value, jsKey, ctx));
1823
+ }
1824
+ else if (map instanceof Set) {
1825
+ map.add(jsKey);
1826
+ }
1827
+ else {
1828
+ const stringKey = stringifyKey(key, jsKey, ctx);
1829
+ const jsValue = toJS(value, stringKey, ctx);
1830
+ if (stringKey in map)
1831
+ Object.defineProperty(map, stringKey, {
1832
+ value: jsValue,
1833
+ writable: true,
1834
+ enumerable: true,
1835
+ configurable: true
1836
+ });
1837
+ else
1838
+ map[stringKey] = jsValue;
1839
+ }
1840
+ }
1841
+ return map;
1842
+ }
1799
1843
  function stringifyKey(key, jsKey, ctx) {
1800
1844
  if (jsKey === null)
1801
1845
  return '';
1846
+ // eslint-disable-next-line @typescript-eslint/no-base-to-string
1802
1847
  if (typeof jsKey !== 'object')
1803
1848
  return String(jsKey);
1804
1849
  if (isNode(key) && ctx?.doc) {
@@ -1951,12 +1996,22 @@ function stringifyFlowCollection({ items }, ctx, { flowChars, itemIndent }) {
1951
1996
  if (comment)
1952
1997
  reqNewline = true;
1953
1998
  let str = stringify$2(item, itemCtx, () => (comment = null));
1954
- if (i < items.length - 1)
1999
+ reqNewline || (reqNewline = lines.length > linesAtValue || str.includes('\n'));
2000
+ if (i < items.length - 1) {
1955
2001
  str += ',';
2002
+ }
2003
+ else if (ctx.options.trailingComma) {
2004
+ if (ctx.options.lineWidth > 0) {
2005
+ reqNewline || (reqNewline = lines.reduce((sum, line) => sum + line.length + 2, 2) +
2006
+ (str.length + 2) >
2007
+ ctx.options.lineWidth);
2008
+ }
2009
+ if (reqNewline) {
2010
+ str += ',';
2011
+ }
2012
+ }
1956
2013
  if (comment)
1957
2014
  str += lineComment(str, itemIndent, commentString(comment));
1958
- if (!reqNewline && (lines.length > linesAtValue || str.includes('\n')))
1959
- reqNewline = true;
1960
2015
  lines.push(str);
1961
2016
  linesAtValue = lines.length;
1962
2017
  }
@@ -2301,11 +2356,12 @@ function stringifyNumber({ format, minFractionDigits, tag, value }) {
2301
2356
  const num = typeof value === 'number' ? value : Number(value);
2302
2357
  if (!isFinite(num))
2303
2358
  return isNaN(num) ? '.nan' : num < 0 ? '-.inf' : '.inf';
2304
- let n = JSON.stringify(value);
2359
+ let n = Object.is(value, -0) ? '-0' : JSON.stringify(value);
2305
2360
  if (!format &&
2306
2361
  minFractionDigits &&
2307
2362
  (!tag || tag === 'tag:yaml.org,2002:float') &&
2308
- /^\d/.test(n)) {
2363
+ /^-?\d/.test(n) &&
2364
+ !n.includes('e')) {
2309
2365
  let i = n.indexOf('.');
2310
2366
  if (i < 0) {
2311
2367
  i = n.length;
@@ -2431,7 +2487,7 @@ const jsonScalars = [
2431
2487
  identify: value => typeof value === 'boolean',
2432
2488
  default: true,
2433
2489
  tag: 'tag:yaml.org,2002:bool',
2434
- test: /^true|false$/,
2490
+ test: /^true$|^false$/,
2435
2491
  resolve: str => str === 'true',
2436
2492
  stringify: stringifyJSON
2437
2493
  },
@@ -2476,10 +2532,7 @@ const binary = {
2476
2532
  * document.querySelector('#photo').src = URL.createObjectURL(blob)
2477
2533
  */
2478
2534
  resolve(src, onError) {
2479
- if (typeof Buffer === 'function') {
2480
- return Buffer.from(src, 'base64');
2481
- }
2482
- else if (typeof atob === 'function') {
2535
+ if (typeof atob === 'function') {
2483
2536
  // On IE 11, atob() can't handle newlines
2484
2537
  const str = atob(src.replace(/[\n\r]/g, ''));
2485
2538
  const buffer = new Uint8Array(str.length);
@@ -2493,15 +2546,11 @@ const binary = {
2493
2546
  }
2494
2547
  },
2495
2548
  stringify({ comment, type, value }, ctx, onComment, onChompKeep) {
2549
+ if (!value)
2550
+ return '';
2496
2551
  const buf = value; // checked earlier by binary.identify()
2497
2552
  let str;
2498
- if (typeof Buffer === 'function') {
2499
- str =
2500
- buf instanceof Buffer
2501
- ? buf.toString('base64')
2502
- : Buffer.from(buf.buffer).toString('base64');
2503
- }
2504
- else if (typeof btoa === 'function') {
2553
+ if (typeof btoa === 'function') {
2505
2554
  let s = '';
2506
2555
  for (let i = 0; i < buf.length; ++i)
2507
2556
  s += String.fromCharCode(buf[i]);
@@ -2510,8 +2559,7 @@ const binary = {
2510
2559
  else {
2511
2560
  throw new Error('This environment does not support writing binary tags; either Buffer or btoa is required');
2512
2561
  }
2513
- if (!type)
2514
- type = Scalar.BLOCK_LITERAL;
2562
+ type ?? (type = Scalar.BLOCK_LITERAL);
2515
2563
  if (type !== Scalar.QUOTE_DOUBLE) {
2516
2564
  const lineWidth = Math.max(ctx.options.lineWidth - ctx.indent.length, ctx.options.minContentWidth);
2517
2565
  const n = Math.ceil(str.length / lineWidth);
@@ -2980,7 +3028,7 @@ const timestamp = {
2980
3028
  }
2981
3029
  return new Date(date);
2982
3030
  },
2983
- stringify: ({ value }) => value.toISOString().replace(/((T00:00)?:00)?\.000Z$/, '')
3031
+ stringify: ({ value }) => value?.toISOString().replace(/(T00:00:00)?\.000Z$/, '') ?? ''
2984
3032
  };
2985
3033
 
2986
3034
  const schema = [
@@ -2998,6 +3046,7 @@ const schema = [
2998
3046
  floatExp,
2999
3047
  float,
3000
3048
  binary,
3049
+ merge,
3001
3050
  omap,
3002
3051
  pairs,
3003
3052
  set,
@@ -3025,6 +3074,7 @@ const tagsByName = {
3025
3074
  intOct: intOct$1,
3026
3075
  intTime,
3027
3076
  map,
3077
+ merge,
3028
3078
  null: nullTag,
3029
3079
  omap,
3030
3080
  pairs,
@@ -3034,13 +3084,20 @@ const tagsByName = {
3034
3084
  };
3035
3085
  const coreKnownTags = {
3036
3086
  'tag:yaml.org,2002:binary': binary,
3087
+ 'tag:yaml.org,2002:merge': merge,
3037
3088
  'tag:yaml.org,2002:omap': omap,
3038
3089
  'tag:yaml.org,2002:pairs': pairs,
3039
3090
  'tag:yaml.org,2002:set': set,
3040
3091
  'tag:yaml.org,2002:timestamp': timestamp
3041
3092
  };
3042
- function getTags(customTags, schemaName) {
3043
- let tags = schemas.get(schemaName);
3093
+ function getTags(customTags, schemaName, addMergeTag) {
3094
+ const schemaTags = schemas.get(schemaName);
3095
+ if (schemaTags && !customTags) {
3096
+ return addMergeTag && !schemaTags.includes(merge)
3097
+ ? schemaTags.concat(merge)
3098
+ : schemaTags.slice();
3099
+ }
3100
+ let tags = schemaTags;
3044
3101
  if (!tags) {
3045
3102
  if (Array.isArray(customTags))
3046
3103
  tags = [];
@@ -3059,17 +3116,21 @@ function getTags(customTags, schemaName) {
3059
3116
  else if (typeof customTags === 'function') {
3060
3117
  tags = customTags(tags.slice());
3061
3118
  }
3062
- return tags.map(tag => {
3063
- if (typeof tag !== 'string')
3064
- return tag;
3065
- const tagObj = tagsByName[tag];
3066
- if (tagObj)
3067
- return tagObj;
3068
- const keys = Object.keys(tagsByName)
3069
- .map(key => JSON.stringify(key))
3070
- .join(', ');
3071
- throw new Error(`Unknown custom tag "${tag}"; use one of ${keys}`);
3072
- });
3119
+ if (addMergeTag)
3120
+ tags = tags.concat(merge);
3121
+ return tags.reduce((tags, tag) => {
3122
+ const tagObj = typeof tag === 'string' ? tagsByName[tag] : tag;
3123
+ if (!tagObj) {
3124
+ const tagName = JSON.stringify(tag);
3125
+ const keys = Object.keys(tagsByName)
3126
+ .map(key => JSON.stringify(key))
3127
+ .join(', ');
3128
+ throw new Error(`Unknown custom tag ${tagName}; use one of ${keys}`);
3129
+ }
3130
+ if (!tags.includes(tagObj))
3131
+ tags.push(tagObj);
3132
+ return tags;
3133
+ }, []);
3073
3134
  }
3074
3135
 
3075
3136
  const sortMapEntriesByKey = (a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0;
@@ -3080,10 +3141,9 @@ class Schema {
3080
3141
  : compat
3081
3142
  ? getTags(null, compat)
3082
3143
  : null;
3083
- this.merge = !!merge;
3084
3144
  this.name = (typeof schema === 'string' && schema) || 'core';
3085
3145
  this.knownTags = resolveKnownTags ? coreKnownTags : {};
3086
- this.tags = getTags(customTags, this.name);
3146
+ this.tags = getTags(customTags, this.name, merge);
3087
3147
  this.toStringOptions = toStringDefaults ?? null;
3088
3148
  Object.defineProperty(this, MAP, { value: map });
3089
3149
  Object.defineProperty(this, SCALAR$1, { value: string });
@@ -3208,6 +3268,7 @@ class Document {
3208
3268
  logLevel: 'warn',
3209
3269
  prettyErrors: true,
3210
3270
  strict: true,
3271
+ stringKeys: false,
3211
3272
  uniqueKeys: true,
3212
3273
  version: '1.2'
3213
3274
  }, options);
@@ -3431,7 +3492,7 @@ class Document {
3431
3492
  this.directives.yaml.version = '1.1';
3432
3493
  else
3433
3494
  this.directives = new Directives({ version: '1.1' });
3434
- opt = { merge: true, resolveKnownTags: false, schema: 'yaml-1.1' };
3495
+ opt = { resolveKnownTags: false, schema: 'yaml-1.1' };
3435
3496
  break;
3436
3497
  case '1.2':
3437
3498
  case 'next':
@@ -3439,7 +3500,7 @@ class Document {
3439
3500
  this.directives.yaml.version = version;
3440
3501
  else
3441
3502
  this.directives = new Directives({ version });
3442
- opt = { merge: false, resolveKnownTags: true, schema: 'core' };
3503
+ opt = { resolveKnownTags: true, schema: 'core' };
3443
3504
  break;
3444
3505
  case null:
3445
3506
  if (this.directives)
@@ -3552,7 +3613,7 @@ const prettifyError = (src, lc) => (error) => {
3552
3613
  if (/[^ ]/.test(lineStr)) {
3553
3614
  let count = 1;
3554
3615
  const end = error.linePos[1];
3555
- if (end && end.line === line && end.col > col) {
3616
+ if (end?.line === line && end.col > col) {
3556
3617
  count = Math.max(1, Math.min(end.col - col, 80 - ci));
3557
3618
  }
3558
3619
  const pointer = ' '.repeat(ci) + '^'.repeat(count);
@@ -3617,7 +3678,7 @@ function resolveProps(tokens, { flow, indicator, next, offset, onError, parentIn
3617
3678
  if (atNewline) {
3618
3679
  if (comment)
3619
3680
  comment += token.source;
3620
- else
3681
+ else if (!found || indicator !== 'seq-item-ind')
3621
3682
  spaceBefore = true;
3622
3683
  }
3623
3684
  else
@@ -3634,8 +3695,7 @@ function resolveProps(tokens, { flow, indicator, next, offset, onError, parentIn
3634
3695
  if (token.source.endsWith(':'))
3635
3696
  onError(token.offset + token.source.length - 1, 'BAD_ALIAS', 'Anchor ending in : is ambiguous', true);
3636
3697
  anchor = token;
3637
- if (start === null)
3638
- start = token.offset;
3698
+ start ?? (start = token.offset);
3639
3699
  atNewline = false;
3640
3700
  hasSpace = false;
3641
3701
  reqSpace = true;
@@ -3644,8 +3704,7 @@ function resolveProps(tokens, { flow, indicator, next, offset, onError, parentIn
3644
3704
  if (tag)
3645
3705
  onError(token, 'MULTIPLE_TAGS', 'A node can have at most one tag');
3646
3706
  tag = token;
3647
- if (start === null)
3648
- start = token.offset;
3707
+ start ?? (start = token.offset);
3649
3708
  atNewline = false;
3650
3709
  hasSpace = false;
3651
3710
  reqSpace = true;
@@ -3758,11 +3817,7 @@ function mapIncludes(ctx, items, search) {
3758
3817
  return false;
3759
3818
  const isEqual = typeof uniqueKeys === 'function'
3760
3819
  ? uniqueKeys
3761
- : (a, b) => a === b ||
3762
- (isScalar$1(a) &&
3763
- isScalar$1(b) &&
3764
- a.value === b.value &&
3765
- !(a.value === '<<' && ctx.schema.merge));
3820
+ : (a, b) => a === b || (isScalar$1(a) && isScalar$1(b) && a.value === b.value);
3766
3821
  return items.some(pair => isEqual(pair.key, search));
3767
3822
  }
3768
3823
 
@@ -3811,12 +3866,14 @@ function resolveBlockMap({ composeNode, composeEmptyNode }, ctx, bm, onError, ta
3811
3866
  onError(offset, 'BAD_INDENT', startColMsg);
3812
3867
  }
3813
3868
  // key value
3869
+ ctx.atKey = true;
3814
3870
  const keyStart = keyProps.end;
3815
3871
  const keyNode = key
3816
3872
  ? composeNode(ctx, key, keyProps, onError)
3817
3873
  : composeEmptyNode(ctx, keyStart, start, null, keyProps, onError);
3818
3874
  if (ctx.schema.compat)
3819
3875
  flowIndentCheck(bm.indent, key, onError);
3876
+ ctx.atKey = false;
3820
3877
  if (mapIncludes(ctx, map.items, keyNode))
3821
3878
  onError(keyStart, 'DUPLICATE_KEY', 'Map keys must be unique');
3822
3879
  // value properties
@@ -3876,6 +3933,8 @@ function resolveBlockSeq({ composeNode, composeEmptyNode }, ctx, bs, onError, ta
3876
3933
  const seq = new NodeClass(ctx.schema);
3877
3934
  if (ctx.atRoot)
3878
3935
  ctx.atRoot = false;
3936
+ if (ctx.atKey)
3937
+ ctx.atKey = false;
3879
3938
  let offset = bs.offset;
3880
3939
  let commentEnd = null;
3881
3940
  for (const { start, value } of bs.items) {
@@ -3889,7 +3948,7 @@ function resolveBlockSeq({ composeNode, composeEmptyNode }, ctx, bs, onError, ta
3889
3948
  });
3890
3949
  if (!props.found) {
3891
3950
  if (props.anchor || props.tag || value) {
3892
- if (value && value.type === 'block-seq')
3951
+ if (value?.type === 'block-seq')
3893
3952
  onError(props.end, 'BAD_INDENT', 'All sequence items must start at the same column');
3894
3953
  else
3895
3954
  onError(offset, 'MISSING_CHAR', 'Sequence item without - indicator');
@@ -3960,6 +4019,8 @@ function resolveFlowCollection({ composeNode, composeEmptyNode }, ctx, fc, onErr
3960
4019
  const atRoot = ctx.atRoot;
3961
4020
  if (atRoot)
3962
4021
  ctx.atRoot = false;
4022
+ if (ctx.atKey)
4023
+ ctx.atKey = false;
3963
4024
  let offset = fc.offset + fc.start.source.length;
3964
4025
  for (let i = 0; i < fc.items.length; ++i) {
3965
4026
  const collItem = fc.items[i];
@@ -4039,12 +4100,14 @@ function resolveFlowCollection({ composeNode, composeEmptyNode }, ctx, fc, onErr
4039
4100
  else {
4040
4101
  // item is a key+value pair
4041
4102
  // key value
4103
+ ctx.atKey = true;
4042
4104
  const keyStart = props.end;
4043
4105
  const keyNode = key
4044
4106
  ? composeNode(ctx, key, props, onError)
4045
4107
  : composeEmptyNode(ctx, keyStart, start, null, props, onError);
4046
4108
  if (isBlock(key))
4047
4109
  onError(keyNode.range, 'BLOCK_IN_FLOW', blockMsg);
4110
+ ctx.atKey = false;
4048
4111
  // value properties
4049
4112
  const valueProps = resolveProps(sep ?? [], {
4050
4113
  flow: fcName,
@@ -4071,7 +4134,7 @@ function resolveFlowCollection({ composeNode, composeEmptyNode }, ctx, fc, onErr
4071
4134
  }
4072
4135
  }
4073
4136
  else if (value) {
4074
- if ('source' in value && value.source && value.source[0] === ':')
4137
+ if ('source' in value && value.source?.[0] === ':')
4075
4138
  onError(value, 'MISSING_CHAR', `Missing space after : in ${fcName}`);
4076
4139
  else
4077
4140
  onError(valueProps.start, 'MISSING_CHAR', `Missing , or : between ${fcName} items`);
@@ -4115,7 +4178,7 @@ function resolveFlowCollection({ composeNode, composeEmptyNode }, ctx, fc, onErr
4115
4178
  const expectedEnd = isMap ? '}' : ']';
4116
4179
  const [ce, ...ee] = fc.end;
4117
4180
  let cePos = offset;
4118
- if (ce && ce.source === expectedEnd)
4181
+ if (ce?.source === expectedEnd)
4119
4182
  cePos = ce.offset + ce.source.length;
4120
4183
  else {
4121
4184
  const name = fcName[0].toUpperCase() + fcName.substring(1);
@@ -4195,13 +4258,13 @@ function composeCollection(CN, ctx, token, props, onError) {
4195
4258
  let tag = ctx.schema.tags.find(t => t.tag === tagName && t.collection === expType);
4196
4259
  if (!tag) {
4197
4260
  const kt = ctx.schema.knownTags[tagName];
4198
- if (kt && kt.collection === expType) {
4261
+ if (kt?.collection === expType) {
4199
4262
  ctx.schema.tags.push(Object.assign({}, kt, { default: false }));
4200
4263
  tag = kt;
4201
4264
  }
4202
4265
  else {
4203
- if (kt?.collection) {
4204
- onError(tagToken, 'BAD_COLLECTION_TYPE', `${kt.tag} used for ${expType} collection, but expects ${kt.collection}`, true);
4266
+ if (kt) {
4267
+ onError(tagToken, 'BAD_COLLECTION_TYPE', `${kt.tag} used for ${expType} collection, but expects ${kt.collection ?? 'scalar'}`, true);
4205
4268
  }
4206
4269
  else {
4207
4270
  onError(tagToken, 'TAG_RESOLVE_FAILED', `Unresolved tag: ${tagName}`, true);
@@ -4557,7 +4620,7 @@ function doubleQuotedValue(source, onError) {
4557
4620
  next = source[++i + 1];
4558
4621
  }
4559
4622
  else if (next === 'x' || next === 'u' || next === 'U') {
4560
- const length = { x: 2, u: 4, U: 8 }[next];
4623
+ const length = next === 'x' ? 2 : next === 'u' ? 4 : 8;
4561
4624
  res += parseCharCode(source, i + 1, length, onError);
4562
4625
  i += length;
4563
4626
  }
@@ -4627,12 +4690,14 @@ function parseCharCode(source, offset, length, onError) {
4627
4690
  const cc = source.substr(offset, length);
4628
4691
  const ok = cc.length === length && /^[0-9a-fA-F]+$/.test(cc);
4629
4692
  const code = ok ? parseInt(cc, 16) : NaN;
4630
- if (isNaN(code)) {
4693
+ try {
4694
+ return String.fromCodePoint(code);
4695
+ }
4696
+ catch {
4631
4697
  const raw = source.substr(offset - 2, length + 2);
4632
4698
  onError(offset - 2, 'BAD_DQ_ESCAPE', `Invalid escape sequence ${raw}`);
4633
4699
  return raw;
4634
4700
  }
4635
- return String.fromCodePoint(code);
4636
4701
  }
4637
4702
 
4638
4703
  function composeScalar(ctx, token, tagToken, onError) {
@@ -4642,11 +4707,16 @@ function composeScalar(ctx, token, tagToken, onError) {
4642
4707
  const tagName = tagToken
4643
4708
  ? ctx.directives.tagName(tagToken.source, msg => onError(tagToken, 'TAG_RESOLVE_FAILED', msg))
4644
4709
  : null;
4645
- const tag = tagToken && tagName
4646
- ? findScalarTagByName(ctx.schema, value, tagName, tagToken, onError)
4647
- : token.type === 'scalar'
4648
- ? findScalarTagByTest(ctx, value, token, onError)
4649
- : ctx.schema[SCALAR$1];
4710
+ let tag;
4711
+ if (ctx.options.stringKeys && ctx.atKey) {
4712
+ tag = ctx.schema[SCALAR$1];
4713
+ }
4714
+ else if (tagName)
4715
+ tag = findScalarTagByName(ctx.schema, value, tagName, tagToken, onError);
4716
+ else if (token.type === 'scalar')
4717
+ tag = findScalarTagByTest(ctx, value, token, onError);
4718
+ else
4719
+ tag = ctx.schema[SCALAR$1];
4650
4720
  let scalar;
4651
4721
  try {
4652
4722
  const res = tag.resolve(value, msg => onError(tagToken ?? token, 'TAG_RESOLVE_FAILED', msg), ctx.options);
@@ -4694,8 +4764,9 @@ function findScalarTagByName(schema, value, tagName, tagToken, onError) {
4694
4764
  onError(tagToken, 'TAG_RESOLVE_FAILED', `Unresolved tag: ${tagName}`, tagName !== 'tag:yaml.org,2002:str');
4695
4765
  return schema[SCALAR$1];
4696
4766
  }
4697
- function findScalarTagByTest({ directives, schema }, value, token, onError) {
4698
- const tag = schema.tags.find(tag => tag.default && tag.test?.test(value)) || schema[SCALAR$1];
4767
+ function findScalarTagByTest({ atKey, directives, schema }, value, token, onError) {
4768
+ const tag = schema.tags.find(tag => (tag.default === true || (atKey && tag.default === 'key')) &&
4769
+ tag.test?.test(value)) || schema[SCALAR$1];
4699
4770
  if (schema.compat) {
4700
4771
  const compat = schema.compat.find(tag => tag.default && tag.test?.test(value)) ??
4701
4772
  schema[SCALAR$1];
@@ -4711,8 +4782,7 @@ function findScalarTagByTest({ directives, schema }, value, token, onError) {
4711
4782
 
4712
4783
  function emptyScalarPosition(offset, before, pos) {
4713
4784
  if (before) {
4714
- if (pos === null)
4715
- pos = before.length;
4785
+ pos ?? (pos = before.length);
4716
4786
  for (let i = pos - 1; i >= 0; --i) {
4717
4787
  let st = before[i];
4718
4788
  switch (st.type) {
@@ -4737,6 +4807,7 @@ function emptyScalarPosition(offset, before, pos) {
4737
4807
 
4738
4808
  const CN = { composeNode, composeEmptyNode };
4739
4809
  function composeNode(ctx, token, props, onError) {
4810
+ const atKey = ctx.atKey;
4740
4811
  const { spaceBefore, comment, anchor, tag } = props;
4741
4812
  let node;
4742
4813
  let isSrcToken = true;
@@ -4757,21 +4828,36 @@ function composeNode(ctx, token, props, onError) {
4757
4828
  case 'block-map':
4758
4829
  case 'block-seq':
4759
4830
  case 'flow-collection':
4760
- node = composeCollection(CN, ctx, token, props, onError);
4761
- if (anchor)
4762
- node.anchor = anchor.source.substring(1);
4831
+ try {
4832
+ node = composeCollection(CN, ctx, token, props, onError);
4833
+ if (anchor)
4834
+ node.anchor = anchor.source.substring(1);
4835
+ }
4836
+ catch (error) {
4837
+ // Almost certainly here due to a stack overflow
4838
+ const message = error instanceof Error ? error.message : String(error);
4839
+ onError(token, 'RESOURCE_EXHAUSTION', message);
4840
+ }
4763
4841
  break;
4764
4842
  default: {
4765
4843
  const message = token.type === 'error'
4766
4844
  ? token.message
4767
4845
  : `Unsupported token (type: ${token.type})`;
4768
4846
  onError(token, 'UNEXPECTED_TOKEN', message);
4769
- node = composeEmptyNode(ctx, token.offset, undefined, null, props, onError);
4770
4847
  isSrcToken = false;
4771
4848
  }
4772
4849
  }
4850
+ node ?? (node = composeEmptyNode(ctx, token.offset, undefined, null, props, onError));
4773
4851
  if (anchor && node.anchor === '')
4774
4852
  onError(anchor, 'BAD_ALIAS', 'Anchor cannot be an empty string');
4853
+ if (atKey &&
4854
+ ctx.options.stringKeys &&
4855
+ (!isScalar$1(node) ||
4856
+ typeof node.value !== 'string' ||
4857
+ (node.tag && node.tag !== 'tag:yaml.org,2002:str'))) {
4858
+ const msg = 'With stringKeys, all keys must be strings';
4859
+ onError(tag ?? token, 'NON_STRING_KEY', msg);
4860
+ }
4775
4861
  if (spaceBefore)
4776
4862
  node.spaceBefore = true;
4777
4863
  if (comment) {
@@ -4824,6 +4910,7 @@ function composeDoc(options, directives, { offset, start, value, end }, onError)
4824
4910
  const opts = Object.assign({ _directives: directives }, options);
4825
4911
  const doc = new Document(undefined, opts);
4826
4912
  const ctx = {
4913
+ atKey: false,
4827
4914
  atRoot: true,
4828
4915
  directives: doc.directives,
4829
4916
  options: doc.options,
@@ -4945,8 +5032,10 @@ class Composer {
4945
5032
  }
4946
5033
  }
4947
5034
  if (afterDoc) {
4948
- Array.prototype.push.apply(doc.errors, this.errors);
4949
- Array.prototype.push.apply(doc.warnings, this.warnings);
5035
+ for (let i = 0; i < this.errors.length; ++i)
5036
+ doc.errors.push(this.errors[i]);
5037
+ for (let i = 0; i < this.warnings.length; ++i)
5038
+ doc.warnings.push(this.warnings[i]);
4950
5039
  }
4951
5040
  else {
4952
5041
  doc.errors = this.errors;
@@ -5849,7 +5938,7 @@ class Lexer {
5849
5938
  const n = (yield* this.pushCount(1)) + (yield* this.pushSpaces(true));
5850
5939
  this.indentNext = this.indentValue + 1;
5851
5940
  this.indentValue += n;
5852
- return yield* this.parseBlockStart();
5941
+ return 'block-start';
5853
5942
  }
5854
5943
  return 'doc';
5855
5944
  }
@@ -6170,32 +6259,36 @@ class Lexer {
6170
6259
  return 0;
6171
6260
  }
6172
6261
  *pushIndicators() {
6173
- switch (this.charAt(0)) {
6174
- case '!':
6175
- return ((yield* this.pushTag()) +
6176
- (yield* this.pushSpaces(true)) +
6177
- (yield* this.pushIndicators()));
6178
- case '&':
6179
- return ((yield* this.pushUntil(isNotAnchorChar)) +
6180
- (yield* this.pushSpaces(true)) +
6181
- (yield* this.pushIndicators()));
6182
- case '-': // this is an error
6183
- case '?': // this is an error outside flow collections
6184
- case ':': {
6185
- const inFlow = this.flowLevel > 0;
6186
- const ch1 = this.charAt(1);
6187
- if (isEmpty(ch1) || (inFlow && flowIndicatorChars.has(ch1))) {
6188
- if (!inFlow)
6189
- this.indentNext = this.indentValue + 1;
6190
- else if (this.flowKey)
6191
- this.flowKey = false;
6192
- return ((yield* this.pushCount(1)) +
6193
- (yield* this.pushSpaces(true)) +
6194
- (yield* this.pushIndicators()));
6262
+ let n = 0;
6263
+ loop: while (true) {
6264
+ switch (this.charAt(0)) {
6265
+ case '!':
6266
+ n += yield* this.pushTag();
6267
+ n += yield* this.pushSpaces(true);
6268
+ continue loop;
6269
+ case '&':
6270
+ n += yield* this.pushUntil(isNotAnchorChar);
6271
+ n += yield* this.pushSpaces(true);
6272
+ continue loop;
6273
+ case '-': // this is an error
6274
+ case '?': // this is an error outside flow collections
6275
+ case ':': {
6276
+ const inFlow = this.flowLevel > 0;
6277
+ const ch1 = this.charAt(1);
6278
+ if (isEmpty(ch1) || (inFlow && flowIndicatorChars.has(ch1))) {
6279
+ if (!inFlow)
6280
+ this.indentNext = this.indentValue + 1;
6281
+ else if (this.flowKey)
6282
+ this.flowKey = false;
6283
+ n += yield* this.pushCount(1);
6284
+ n += yield* this.pushSpaces(true);
6285
+ continue loop;
6286
+ }
6195
6287
  }
6196
6288
  }
6289
+ break loop;
6197
6290
  }
6198
- return 0;
6291
+ return n;
6199
6292
  }
6200
6293
  *pushTag() {
6201
6294
  if (this.charAt(1) === '<') {
@@ -6357,6 +6450,14 @@ function getFirstKeyStartProps(prev) {
6357
6450
  }
6358
6451
  return prev.splice(i, prev.length);
6359
6452
  }
6453
+ function arrayPushArray(target, source) {
6454
+ // May exhaust call stack with large `source` array
6455
+ if (source.length < 1e5)
6456
+ Array.prototype.push.apply(target, source);
6457
+ else
6458
+ for (let i = 0; i < source.length; ++i)
6459
+ target.push(source[i]);
6460
+ }
6360
6461
  function fixFlowSeqItems(fc) {
6361
6462
  if (fc.start.type === 'flow-seq-start') {
6362
6463
  for (const it of fc.items) {
@@ -6369,12 +6470,12 @@ function fixFlowSeqItems(fc) {
6369
6470
  delete it.key;
6370
6471
  if (isFlowToken(it.value)) {
6371
6472
  if (it.value.end)
6372
- Array.prototype.push.apply(it.value.end, it.sep);
6473
+ arrayPushArray(it.value.end, it.sep);
6373
6474
  else
6374
6475
  it.value.end = it.sep;
6375
6476
  }
6376
6477
  else
6377
- Array.prototype.push.apply(it.start, it.sep);
6478
+ arrayPushArray(it.start, it.sep);
6378
6479
  delete it.sep;
6379
6480
  }
6380
6481
  }
@@ -6516,7 +6617,7 @@ class Parser {
6516
6617
  }
6517
6618
  *step() {
6518
6619
  const top = this.peek(1);
6519
- if (this.type === 'doc-end' && (!top || top.type !== 'doc-end')) {
6620
+ if (this.type === 'doc-end' && top?.type !== 'doc-end') {
6520
6621
  while (this.stack.length > 0)
6521
6622
  yield* this.pop();
6522
6623
  this.stack.push({
@@ -6792,7 +6893,7 @@ class Parser {
6792
6893
  const prev = map.items[map.items.length - 2];
6793
6894
  const end = prev?.value?.end;
6794
6895
  if (Array.isArray(end)) {
6795
- Array.prototype.push.apply(end, it.start);
6896
+ arrayPushArray(end, it.start);
6796
6897
  end.push(this.sourceToken);
6797
6898
  map.items.pop();
6798
6899
  return;
@@ -6958,7 +7059,20 @@ class Parser {
6958
7059
  default: {
6959
7060
  const bv = this.startBlockValue(map);
6960
7061
  if (bv) {
6961
- if (atMapIndent && bv.type !== 'block-seq') {
7062
+ if (bv.type === 'block-seq') {
7063
+ if (!it.explicitKey &&
7064
+ it.sep &&
7065
+ !includesToken(it.sep, 'newline')) {
7066
+ yield* this.pop({
7067
+ type: 'error',
7068
+ offset: this.offset,
7069
+ message: 'Unexpected block-seq-ind on same line with key',
7070
+ source: this.source
7071
+ });
7072
+ return;
7073
+ }
7074
+ }
7075
+ else if (atMapIndent) {
6962
7076
  map.items.push({ start });
6963
7077
  }
6964
7078
  this.stack.push(bv);
@@ -6994,7 +7108,7 @@ class Parser {
6994
7108
  const prev = seq.items[seq.items.length - 2];
6995
7109
  const end = prev?.value?.end;
6996
7110
  if (Array.isArray(end)) {
6997
- Array.prototype.push.apply(end, it.start);
7111
+ arrayPushArray(end, it.start);
6998
7112
  end.push(this.sourceToken);
6999
7113
  seq.items.pop();
7000
7114
  return;
@@ -7035,7 +7149,7 @@ class Parser {
7035
7149
  do {
7036
7150
  yield* this.pop();
7037
7151
  top = this.peek(1);
7038
- } while (top && top.type === 'flow-collection');
7152
+ } while (top?.type === 'flow-collection');
7039
7153
  }
7040
7154
  else if (fc.end.length === 0) {
7041
7155
  switch (this.type) {
@@ -7329,6 +7443,8 @@ function stringify(value, replacer, options) {
7329
7443
  if (!keepUndefined)
7330
7444
  return undefined;
7331
7445
  }
7446
+ if (isDocument(value) && !_replacer)
7447
+ return value.toString(options);
7332
7448
  return new Document(value, _replacer, options).toString(options);
7333
7449
  }
7334
7450