@effected/yaml 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1155 @@
1
+ import { YamlMap, YamlPair, YamlScalar, YamlSeq } from "../../YamlNode.js";
2
+ import { checkAnchorOnAlias, getAnchorName, makeAlias, registerAnchor } from "./anchors.js";
3
+ import { enterNesting, exitNesting, hasMeta, hasNonWhitespaceBeforeOnLine, lineCol, lineIndentColumn, sameLine } from "./state.js";
4
+ import { blockMapStartsWithValueSep, classifyPlainNumeric, collectMultilineKey, collectMultilinePlainScalar, findFirstContent, findLastContent, findNextContentInList, findNextSignificantChild, findValueSepOffset, getScalarStyle, hasValueSepAfterInList, hasValueSepBetween, hasValueSepThroughPlainScalars, makeScalar, resolveScalar, shouldPreserveRaw } from "./scalars.js";
5
+
6
+ //#region src/internal/composer/block.ts
7
+ /**
8
+ * Compose a block map from its CST children, with an optional external first key.
9
+ *
10
+ * CST pattern for `a: 1, b: true`:
11
+ * `[flow-scalar("a"), block-map(children: [":"," ","1","\\n","b",":"," ","true"])]`
12
+ *
13
+ * The first key is external (sibling before block-map in document/parent).
14
+ * Subsequent keys are inside the block-map children.
15
+ */
16
+ function composeBlockMap(blockMapCst, state, externalFirstKey, meta) {
17
+ if (!enterNesting(state, blockMapCst)) return new YamlMap({
18
+ items: [],
19
+ style: "block",
20
+ offset: blockMapCst.offset,
21
+ length: blockMapCst.length
22
+ });
23
+ try {
24
+ return composeBlockMapInner(blockMapCst, state, externalFirstKey, meta);
25
+ } finally {
26
+ exitNesting(state);
27
+ }
28
+ }
29
+ function composeBlockMapInner(blockMapCst, state, externalFirstKey, meta) {
30
+ const children = blockMapCst.children ?? [];
31
+ const pairs = [];
32
+ const extKeyOffset = externalFirstKey && "offset" in externalFirstKey ? externalFirstKey.offset : void 0;
33
+ const items = flattenBlockMapChildren(children, state, extKeyOffset !== void 0 ? lineIndentColumn(state.text, extKeyOffset) : void 0, extKeyOffset);
34
+ if (externalFirstKey) items.unshift({
35
+ kind: "key",
36
+ node: externalFirstKey
37
+ });
38
+ buildPairs(items, pairs, state.text);
39
+ if (state.options.uniqueKeys) checkDuplicateKeys(pairs, state);
40
+ checkMultilineImplicitKeys(pairs, state);
41
+ const offset = externalFirstKey ? "offset" in externalFirstKey ? externalFirstKey.offset : blockMapCst.offset : blockMapCst.offset;
42
+ const map = new YamlMap({
43
+ items: pairs,
44
+ style: "block",
45
+ offset,
46
+ length: blockMapCst.offset + blockMapCst.length - offset,
47
+ ...meta?.tag !== void 0 ? { tag: meta.tag } : {},
48
+ ...meta?.anchor !== void 0 ? { anchor: meta.anchor } : {},
49
+ ...meta?.comment !== void 0 ? { comment: meta.comment } : {}
50
+ });
51
+ if (meta?.anchor) registerAnchor(map, meta.anchor, state, offset);
52
+ return map;
53
+ }
54
+ function flattenBlockMapChildren(children, state, externalKeyColumn, externalKeyOffset) {
55
+ const items = [];
56
+ let pendingMeta = {};
57
+ let outerMeta = {};
58
+ let sawNewlineSincePending = false;
59
+ let afterValueSep = false;
60
+ let lastValueSepOffset = -1;
61
+ let lastKeyColumn = externalKeyColumn ?? -1;
62
+ let lastKeyOffset = externalKeyOffset ?? -1;
63
+ const hasExternalKeyColumn = externalKeyColumn !== void 0;
64
+ let pendingExplicitKeyCol = -1;
65
+ function commitOuterIfNewlineSeen() {
66
+ if (sawNewlineSincePending && hasMeta(pendingMeta)) {
67
+ outerMeta = hasMeta(outerMeta) ? {
68
+ ...outerMeta,
69
+ ...pendingMeta
70
+ } : pendingMeta;
71
+ pendingMeta = {};
72
+ }
73
+ sawNewlineSincePending = false;
74
+ }
75
+ function combinedPending() {
76
+ if (!hasMeta(outerMeta)) return pendingMeta;
77
+ if (!hasMeta(pendingMeta)) return outerMeta;
78
+ return {
79
+ ...outerMeta,
80
+ ...pendingMeta
81
+ };
82
+ }
83
+ function resetAllMeta() {
84
+ pendingMeta = {};
85
+ outerMeta = {};
86
+ sawNewlineSincePending = false;
87
+ }
88
+ function validateKeyColumn(col, offset, length) {
89
+ if (lastKeyColumn >= 0 && col !== lastKeyColumn) state.errors.push({
90
+ code: "InvalidIndentation",
91
+ message: "Bad indentation in block mapping",
92
+ offset,
93
+ length
94
+ });
95
+ }
96
+ function pushNode(node, nodeOffset) {
97
+ if (!afterValueSep && nodeOffset !== void 0 && nodeOffset >= 0) {
98
+ lastKeyColumn = pendingExplicitKeyCol >= 0 ? pendingExplicitKeyCol : lineCol(state.text, nodeOffset).column;
99
+ lastKeyOffset = nodeOffset;
100
+ }
101
+ pendingExplicitKeyCol = -1;
102
+ items.push({
103
+ kind: "node",
104
+ node
105
+ });
106
+ afterValueSep = false;
107
+ }
108
+ for (let i = 0; i < children.length; i++) {
109
+ const child = children[i];
110
+ if (!child) continue;
111
+ if (child.type === "error") {
112
+ state.errors.push({
113
+ code: "UnexpectedToken",
114
+ message: `Unexpected content: ${child.source.trim() || "(empty)"}`,
115
+ offset: child.offset,
116
+ length: child.length
117
+ });
118
+ continue;
119
+ }
120
+ if (child.type === "newline") {
121
+ if (hasMeta(pendingMeta)) sawNewlineSincePending = true;
122
+ continue;
123
+ }
124
+ if (child.type === "whitespace") {
125
+ if (child.source === "?") {
126
+ const qCol = lineCol(state.text, child.offset).column;
127
+ pendingExplicitKeyCol = qCol;
128
+ afterValueSep = false;
129
+ const lookahead = scanExplicitKeyShape(children, i, qCol, state.text);
130
+ if (lookahead.kind === "inline-implicit-map") {
131
+ const sliceChildren = children.slice(i + 1, lookahead.endIdx);
132
+ const innerItems = flattenBlockMapChildren(sliceChildren, state);
133
+ const innerPairs = [];
134
+ buildPairs(innerItems, innerPairs, state.text);
135
+ const firstC = findFirstContent(sliceChildren);
136
+ const lastC = findLastContent(sliceChildren);
137
+ const innerOffset = firstC ? firstC.offset : child.offset;
138
+ pushNode(new YamlMap({
139
+ items: innerPairs,
140
+ style: "block",
141
+ offset: innerOffset,
142
+ length: (lastC ? lastC.offset + lastC.length : child.offset + child.length) - innerOffset
143
+ }), innerOffset);
144
+ i = lookahead.endIdx - 1;
145
+ continue;
146
+ }
147
+ continue;
148
+ }
149
+ if (child.source === ":") {
150
+ validateNoTabAfterContinuationValueSep(child, children, i, state);
151
+ const flushMeta = combinedPending();
152
+ if (hasMeta(flushMeta)) {
153
+ const scalar = new YamlScalar({
154
+ value: resolveScalar("", "plain", flushMeta.tag, state),
155
+ style: "plain",
156
+ offset: child.offset,
157
+ length: 0,
158
+ ...flushMeta.tag !== void 0 ? { tag: flushMeta.tag } : {},
159
+ ...flushMeta.anchor !== void 0 ? { anchor: flushMeta.anchor } : {}
160
+ });
161
+ if (flushMeta.anchor) registerAnchor(scalar, flushMeta.anchor, state, child.offset);
162
+ resetAllMeta();
163
+ pushNode(scalar);
164
+ }
165
+ items.push({
166
+ kind: "value-sep",
167
+ offset: child.offset
168
+ });
169
+ afterValueSep = true;
170
+ lastValueSepOffset = child.offset;
171
+ }
172
+ if (child.source === "-" && lastValueSepOffset >= 0 && sameLine(state.text, lastValueSepOffset, child.offset) && hasNonWhitespaceBeforeOnLine(state.text, lastValueSepOffset)) state.errors.push({
173
+ code: "UnexpectedToken",
174
+ message: "Sequence entry on same line as mapping value indicator",
175
+ offset: child.offset,
176
+ length: child.length
177
+ });
178
+ else if (child.source === "-" && lastValueSepOffset >= 0 && !sameLine(state.text, lastValueSepOffset, child.offset) && pendingExplicitKeyCol < 0 && !precededByExplicitKeyMarker(children, i)) state.errors.push({
179
+ code: "InvalidIndentation",
180
+ message: "Block sequence entry indicator outside any sequence",
181
+ offset: child.offset,
182
+ length: child.length
183
+ });
184
+ continue;
185
+ }
186
+ if (child.type === "comment") {
187
+ const text = child.source.startsWith("#") ? child.source.slice(1).trim() : child.source;
188
+ items.push({
189
+ kind: "comment",
190
+ comment: text
191
+ });
192
+ continue;
193
+ }
194
+ if (child.type === "anchor") {
195
+ validatePropertyContinuationColumn(child, state, afterValueSep, lastValueSepOffset, lastKeyColumn);
196
+ commitOuterIfNewlineSeen();
197
+ pendingMeta.anchor = getAnchorName(child, state.text);
198
+ continue;
199
+ }
200
+ if (child.type === "tag") {
201
+ validatePropertyContinuationColumn(child, state, afterValueSep, lastValueSepOffset, lastKeyColumn);
202
+ commitOuterIfNewlineSeen();
203
+ pendingMeta.tag = child.source;
204
+ continue;
205
+ }
206
+ if (child.type === "flow-scalar" || child.type === "block-scalar") {
207
+ commitOuterIfNewlineSeen();
208
+ validateNoDoubleAnchorOnScalar(child, children, i, outerMeta, pendingMeta, state);
209
+ const valueSepOffset = findValueSepOffset(children, i + 1);
210
+ const sepOnSameLine = valueSepOffset >= 0 && lineCol(state.text, child.offset).line === lineCol(state.text, valueSepOffset).line;
211
+ if (afterValueSep && sepOnSameLine) {
212
+ const flushMeta = combinedPending();
213
+ if (hasMeta(flushMeta)) {
214
+ const scalar = new YamlScalar({
215
+ value: resolveScalar("", "plain", flushMeta.tag, state),
216
+ style: "plain",
217
+ offset: child.offset,
218
+ length: 0,
219
+ ...flushMeta.tag !== void 0 ? { tag: flushMeta.tag } : {},
220
+ ...flushMeta.anchor !== void 0 ? { anchor: flushMeta.anchor } : {}
221
+ });
222
+ if (flushMeta.anchor) registerAnchor(scalar, flushMeta.anchor, state, child.offset);
223
+ resetAllMeta();
224
+ pushNode(scalar);
225
+ }
226
+ }
227
+ const nextValueSepOffset = findValueSepOffset(children, i + 1);
228
+ if (afterValueSep && lastValueSepOffset >= 0 && hasNonWhitespaceBeforeOnLine(state.text, lastValueSepOffset) && child.type === "flow-scalar" && nextValueSepOffset >= 0 && sameLine(state.text, lastValueSepOffset, child.offset) && sameLine(state.text, child.offset, nextValueSepOffset)) state.errors.push({
229
+ code: "UnexpectedToken",
230
+ message: "Implicit mapping key on same line as previous value indicator",
231
+ offset: child.offset,
232
+ length: child.length
233
+ });
234
+ const nextContent = findNextContentInList(children, i + 1);
235
+ if (nextContent?.node.type === "block-map" && !hasValueSepBetween(children, i + 1, nextContent.idx)) {
236
+ if (!afterValueSep && hasExternalKeyColumn) validateKeyColumn(pendingExplicitKeyCol >= 0 ? pendingExplicitKeyCol : lineCol(state.text, child.offset).column, child.offset, child.length);
237
+ const keyMeta = hasMeta(pendingMeta) ? pendingMeta : void 0;
238
+ const mapMeta = hasMeta(outerMeta) ? outerMeta : void 0;
239
+ const key = makeScalar(child, state, keyMeta);
240
+ const map = composeBlockMap(nextContent.node, state, key, mapMeta);
241
+ resetAllMeta();
242
+ pushNode(map);
243
+ i = nextContent.idx;
244
+ continue;
245
+ }
246
+ if (!afterValueSep && child.type === "flow-scalar" && getScalarStyle(child) === "plain" && !hasValueSepAfterInList(children, i + 1) && hasValueSepThroughPlainScalars(children, i + 1)) {
247
+ let isExplicitKey = false;
248
+ let explicitKeyCol = -1;
249
+ for (let p = i - 1; p >= 0; p--) {
250
+ const prev = children[p];
251
+ if (!prev) continue;
252
+ if (prev.type === "whitespace" && prev.source === "?") {
253
+ explicitKeyCol = lineCol(state.text, prev.offset).column;
254
+ isExplicitKey = true;
255
+ break;
256
+ }
257
+ if (prev.type === "whitespace" && prev.source.trim() === "") continue;
258
+ if (prev.type === "newline") continue;
259
+ break;
260
+ }
261
+ if (isExplicitKey) {
262
+ let nextScalarIndented = false;
263
+ let sawNl = false;
264
+ for (let j = i + 1; j < children.length; j++) {
265
+ const c = children[j];
266
+ if (!c) continue;
267
+ if (c.type === "newline") {
268
+ sawNl = true;
269
+ continue;
270
+ }
271
+ if (c.type === "whitespace" && c.source.trim() === "") continue;
272
+ if (sawNl && c.type === "flow-scalar") nextScalarIndented = lineCol(state.text, c.offset).column > explicitKeyCol;
273
+ break;
274
+ }
275
+ isExplicitKey = nextScalarIndented;
276
+ }
277
+ if (isExplicitKey) {
278
+ const { value: keyValue, nextIdx: keyNextIdx } = collectMultilineKey(children, i);
279
+ const keyMeta = combinedPending();
280
+ const scalar = new YamlScalar({
281
+ value: resolveScalar(keyValue, "plain", keyMeta.tag, state),
282
+ style: "plain",
283
+ offset: child.offset,
284
+ length: child.length,
285
+ ...keyMeta.tag !== void 0 ? { tag: keyMeta.tag } : {},
286
+ ...keyMeta.anchor !== void 0 ? { anchor: keyMeta.anchor } : {}
287
+ });
288
+ if (keyMeta.anchor) registerAnchor(scalar, keyMeta.anchor, state, child.offset);
289
+ resetAllMeta();
290
+ pushNode(scalar, child.offset);
291
+ i = keyNextIdx - 1;
292
+ continue;
293
+ }
294
+ }
295
+ if (child.type === "flow-scalar" && getScalarStyle(child) === "plain" && !hasValueSepAfterInList(children, i + 1)) {
296
+ const isValuePosition = afterValueSep;
297
+ if (!isValuePosition) {
298
+ let isLineStart = true;
299
+ for (let k = child.offset - 1; k >= 0; k--) {
300
+ const ch = state.text[k];
301
+ if (ch === "\n") break;
302
+ if (ch === " " || ch === " ") continue;
303
+ isLineStart = false;
304
+ break;
305
+ }
306
+ if (isLineStart) {
307
+ let precededByIndicator = false;
308
+ for (let p = i - 1; p >= 0; p--) {
309
+ const prev = children[p];
310
+ if (!prev) continue;
311
+ if (prev.type === "whitespace" && (prev.source === "-" || prev.source === "?")) {
312
+ precededByIndicator = true;
313
+ break;
314
+ }
315
+ if (prev.type === "whitespace" && prev.source.trim() === "") continue;
316
+ if (prev.type === "newline") continue;
317
+ break;
318
+ }
319
+ if (!precededByIndicator) state.errors.push({
320
+ code: "UnexpectedToken",
321
+ message: "Trailing content in block mapping",
322
+ offset: child.offset,
323
+ length: child.length
324
+ });
325
+ }
326
+ }
327
+ const minContCol = isValuePosition && lastKeyColumn >= 0 && lastKeyOffset >= 0 && lastValueSepOffset >= 0 && sameLine(state.text, lastKeyOffset, lastValueSepOffset) ? lastKeyColumn + 1 : void 0;
328
+ const { value, nextIdx, partsCount, endOffset } = collectMultilinePlainScalar(children, i, minContCol, minContCol !== void 0 ? state.text : void 0);
329
+ const plainMeta = combinedPending();
330
+ const resolved = resolveScalar(value, "plain", plainMeta.tag, state);
331
+ const needsRaw = typeof resolved !== "string" && resolved !== void 0 && shouldPreserveRaw(value, resolved);
332
+ const scalar = new YamlScalar({
333
+ value: resolved,
334
+ style: "plain",
335
+ offset: child.offset,
336
+ length: partsCount > 1 ? endOffset - child.offset : child.length,
337
+ ...plainMeta.tag !== void 0 ? { tag: plainMeta.tag } : {},
338
+ ...plainMeta.anchor !== void 0 ? { anchor: plainMeta.anchor } : {},
339
+ ...needsRaw ? { raw: value } : {}
340
+ });
341
+ if (plainMeta.anchor) registerAnchor(scalar, plainMeta.anchor, state, child.offset);
342
+ resetAllMeta();
343
+ pushNode(scalar, child.offset);
344
+ if (isValuePosition && partsCount > 1) {
345
+ const stoppedAtContent = findNextContentInList(children, nextIdx);
346
+ if (stoppedAtContent) {
347
+ const sn = stoppedAtContent.node;
348
+ const valueCol = lineCol(state.text, child.offset).column;
349
+ if (lineCol(state.text, sn.offset).column >= valueCol) {
350
+ if (sn.type === "flow-scalar" && getScalarStyle(sn) === "plain" && hasValueSepAfterInList(children, stoppedAtContent.idx + 1) || sn.type === "flow-scalar" && getScalarStyle(sn) === "plain" && (() => {
351
+ const after = findNextContentInList(children, stoppedAtContent.idx + 1);
352
+ return after !== null && after.node.type === "block-map";
353
+ })() || sn.type === "block-map") state.errors.push({
354
+ code: "UnexpectedToken",
355
+ message: "Mapping key after multiline plain scalar value",
356
+ offset: sn.offset,
357
+ length: sn.length
358
+ });
359
+ }
360
+ }
361
+ }
362
+ i = nextIdx - 1;
363
+ continue;
364
+ }
365
+ const style = getScalarStyle(child);
366
+ const isValuePosition = afterValueSep;
367
+ const scalarMeta = combinedPending();
368
+ const scalar = makeScalar(child, state, hasMeta(scalarMeta) ? scalarMeta : void 0);
369
+ resetAllMeta();
370
+ pushNode(scalar, child.offset);
371
+ if (isValuePosition && (style === "single-quoted" || style === "double-quoted")) {
372
+ checkTrailingContentOnSameLine(children, i + 1, child, state);
373
+ validateQuotedScalarContinuationIndent(child, state, lastKeyColumn);
374
+ }
375
+ continue;
376
+ }
377
+ if (child.type === "alias") {
378
+ commitOuterIfNewlineSeen();
379
+ const nextAlias = findNextContentInList(children, i + 1);
380
+ if (nextAlias?.node.type === "block-map") {
381
+ const alias = makeAlias(child, state);
382
+ const aliasMapMeta = hasMeta(outerMeta) ? outerMeta : void 0;
383
+ const map = composeBlockMap(nextAlias.node, state, alias, aliasMapMeta);
384
+ resetAllMeta();
385
+ pushNode(map);
386
+ i = nextAlias.idx;
387
+ continue;
388
+ }
389
+ checkAnchorOnAlias(combinedPending(), child, state);
390
+ const alias = makeAlias(child, state);
391
+ resetAllMeta();
392
+ pushNode(alias);
393
+ continue;
394
+ }
395
+ if (child.type === "block-map") {
396
+ commitOuterIfNewlineSeen();
397
+ const mapMeta = combinedPending();
398
+ if (hasMeta(pendingMeta) && blockMapStartsWithValueSep(child)) {
399
+ const emptyKey = new YamlScalar({
400
+ value: null,
401
+ style: "plain",
402
+ offset: child.offset,
403
+ length: 0,
404
+ ...pendingMeta.tag !== void 0 ? { tag: pendingMeta.tag } : {},
405
+ ...pendingMeta.anchor !== void 0 ? { anchor: pendingMeta.anchor } : {}
406
+ });
407
+ if (pendingMeta.anchor) registerAnchor(emptyKey, pendingMeta.anchor, state, child.offset);
408
+ const map = composeBlockMap(child, state, emptyKey, hasMeta(outerMeta) ? outerMeta : void 0);
409
+ resetAllMeta();
410
+ pushNode(map);
411
+ continue;
412
+ }
413
+ const map = composeBlockMap(child, state, void 0, hasMeta(mapMeta) ? mapMeta : void 0);
414
+ resetAllMeta();
415
+ pushNode(map);
416
+ continue;
417
+ }
418
+ if (child.type === "block-seq") {
419
+ commitOuterIfNewlineSeen();
420
+ const seqMeta = combinedPending();
421
+ if (!afterValueSep && pendingExplicitKeyCol < 0 && child.length > 0) state.errors.push({
422
+ code: "InvalidIndentation",
423
+ message: "Sequence in mapping key position",
424
+ offset: child.offset,
425
+ length: child.length
426
+ });
427
+ const seq = composeBlockSeq(child, state, hasMeta(seqMeta) ? seqMeta : void 0);
428
+ resetAllMeta();
429
+ pushNode(seq);
430
+ continue;
431
+ }
432
+ if (child.type === "flow-map") {
433
+ commitOuterIfNewlineSeen();
434
+ const isValue = afterValueSep;
435
+ const flowMapMeta = combinedPending();
436
+ const map = state.flow.composeFlowMap(child, state, hasMeta(flowMapMeta) ? flowMapMeta : void 0, lastKeyColumn);
437
+ resetAllMeta();
438
+ pushNode(map);
439
+ if (isValue) checkTrailingContentOnSameLine(children, i + 1, child, state);
440
+ continue;
441
+ }
442
+ if (child.type === "flow-seq") {
443
+ commitOuterIfNewlineSeen();
444
+ const isValue = afterValueSep;
445
+ const flowSeqMeta = combinedPending();
446
+ const seq = state.flow.composeFlowSeq(child, state, hasMeta(flowSeqMeta) ? flowSeqMeta : void 0, lastKeyColumn);
447
+ resetAllMeta();
448
+ pushNode(seq);
449
+ if (isValue) checkTrailingContentOnSameLine(children, i + 1, child, state);
450
+ }
451
+ }
452
+ const trailingMeta = combinedPending();
453
+ if (hasMeta(trailingMeta)) {
454
+ const scalar = new YamlScalar({
455
+ value: resolveScalar("", "plain", trailingMeta.tag, state),
456
+ style: "plain",
457
+ offset: 0,
458
+ length: 0,
459
+ ...trailingMeta.tag !== void 0 ? { tag: trailingMeta.tag } : {},
460
+ ...trailingMeta.anchor !== void 0 ? { anchor: trailingMeta.anchor } : {}
461
+ });
462
+ if (trailingMeta.anchor) registerAnchor(scalar, trailingMeta.anchor, state, 0);
463
+ items.push({
464
+ kind: "node",
465
+ node: scalar
466
+ });
467
+ }
468
+ return items;
469
+ }
470
+ /**
471
+ * Build pairs from a semantic item stream.
472
+ * Pattern: node, value-sep, node produces a key:value pair.
473
+ * Pattern: node, value-sep (no node) produces a key:null pair.
474
+ * Pattern: value-sep, node produces a null:value pair.
475
+ */
476
+ function buildPairs(items, pairs, text) {
477
+ let i = 0;
478
+ while (i < items.length) {
479
+ const item = items[i];
480
+ if (!item) {
481
+ i++;
482
+ continue;
483
+ }
484
+ if (item.kind === "comment") {
485
+ if (pairs.length > 0) {
486
+ const last = pairs[pairs.length - 1];
487
+ if (last) pairs[pairs.length - 1] = new YamlPair({
488
+ key: last.key,
489
+ value: last.value,
490
+ ...item.comment !== void 0 ? { comment: item.comment } : {}
491
+ });
492
+ }
493
+ i++;
494
+ continue;
495
+ }
496
+ if (item.kind === "value-sep") {
497
+ const valueSepOffset = item.offset ?? 0;
498
+ i++;
499
+ const valueNode = consumeValueNodeForNullKey(items, i, text, valueSepOffset);
500
+ if (valueNode) {
501
+ const nullKey = new YamlScalar({
502
+ value: null,
503
+ style: "plain",
504
+ offset: 0,
505
+ length: 0
506
+ });
507
+ pairs.push(new YamlPair({
508
+ key: nullKey,
509
+ value: valueNode.node ?? null
510
+ }));
511
+ i = valueNode.nextIdx;
512
+ } else {
513
+ const nullKey = new YamlScalar({
514
+ value: null,
515
+ style: "plain",
516
+ offset: 0,
517
+ length: 0
518
+ });
519
+ pairs.push(new YamlPair({
520
+ key: nullKey,
521
+ value: null
522
+ }));
523
+ }
524
+ continue;
525
+ }
526
+ if (item.kind === "node" || item.kind === "key") {
527
+ let keyNode = item.node;
528
+ i++;
529
+ if (item.kind === "key" && !keyNode) {
530
+ while (i < items.length && items[i]?.kind === "comment") i++;
531
+ if (i < items.length && items[i]?.kind === "node") {
532
+ keyNode = items[i]?.node;
533
+ i++;
534
+ }
535
+ }
536
+ while (i < items.length && items[i]?.kind === "comment") i++;
537
+ if (i < items.length && items[i]?.kind === "value-sep") {
538
+ i++;
539
+ const valueResult = consumeValueNode(items, i);
540
+ if (valueResult) {
541
+ pairs.push(new YamlPair({
542
+ key: keyNode ?? new YamlScalar({
543
+ value: null,
544
+ style: "plain",
545
+ offset: 0,
546
+ length: 0
547
+ }),
548
+ value: valueResult.node ?? null
549
+ }));
550
+ i = valueResult.nextIdx;
551
+ } else pairs.push(new YamlPair({
552
+ key: keyNode ?? new YamlScalar({
553
+ value: null,
554
+ style: "plain",
555
+ offset: 0,
556
+ length: 0
557
+ }),
558
+ value: null
559
+ }));
560
+ } else pairs.push(new YamlPair({
561
+ key: keyNode ?? new YamlScalar({
562
+ value: null,
563
+ style: "plain",
564
+ offset: 0,
565
+ length: 0
566
+ }),
567
+ value: null
568
+ }));
569
+ continue;
570
+ }
571
+ i++;
572
+ }
573
+ }
574
+ function consumeValueNode(items, startIdx) {
575
+ let i = startIdx;
576
+ while (i < items.length) {
577
+ const item = items[i];
578
+ if (!item) break;
579
+ if (item.kind === "comment") {
580
+ i++;
581
+ continue;
582
+ }
583
+ if (item.kind === "node") return {
584
+ node: item.node ?? null,
585
+ nextIdx: i + 1
586
+ };
587
+ break;
588
+ }
589
+ return i > startIdx ? {
590
+ node: null,
591
+ nextIdx: i
592
+ } : null;
593
+ }
594
+ /**
595
+ * Like consumeValueNode but for implicit null-key entries (`: value`).
596
+ * If the next non-comment node is immediately followed by a value-sep
597
+ * AND is on a different line from the null key's `:`, it's actually a
598
+ * KEY for the next pair, not our value — return null so the null key
599
+ * gets a null value. When on the same line (e.g. `a: b: c: d`), consume
600
+ * normally to preserve the original pairing (which may produce duplicate
601
+ * keys that get rejected).
602
+ */
603
+ function consumeValueNodeForNullKey(items, startIdx, text, valueSepOffset) {
604
+ let i = startIdx;
605
+ while (i < items.length) {
606
+ const item = items[i];
607
+ if (!item) break;
608
+ if (item.kind === "comment") {
609
+ i++;
610
+ continue;
611
+ }
612
+ if (item.kind === "node") {
613
+ if (i + 1 < items.length && items[i + 1]?.kind === "value-sep") {
614
+ const nodeOffset = item.node && "offset" in item.node ? item.node.offset : 0;
615
+ if (text.slice(valueSepOffset, nodeOffset).includes("\n")) break;
616
+ }
617
+ return {
618
+ node: item.node ?? null,
619
+ nextIdx: i + 1
620
+ };
621
+ }
622
+ break;
623
+ }
624
+ return i > startIdx ? {
625
+ node: null,
626
+ nextIdx: i
627
+ } : null;
628
+ }
629
+ /**
630
+ * Canonical duplicate-key identity for a scalar key. Two keys collide only if
631
+ * they are the same YAML node: same type *and* value. Resolved JS values alone
632
+ * are ambiguous for numbers — `!!int 1` and `!!float 1.0` both become the JS
633
+ * number `1` yet are distinct keys — so the number branch disambiguates int
634
+ * from float via the source form (or an explicit tag). String/bool/null keys
635
+ * are distinguished by their type prefix, so `1` (int) never collides with
636
+ * `"1"` (string) or `true`.
637
+ */
638
+ function keyIdentity(key, text) {
639
+ const v = key.value;
640
+ if (v === null) return "null";
641
+ switch (typeof v) {
642
+ case "boolean": return `b:${v}`;
643
+ case "string": return `s:${v}`;
644
+ case "bigint": return `i:${v.toString()}`;
645
+ case "number": {
646
+ let kind = classifyPlainNumeric(text.slice(key.offset, key.offset + key.length).trim());
647
+ if (key.tag !== void 0) {
648
+ if (key.tag.includes("float")) kind = "float";
649
+ else if (key.tag.includes("int")) kind = "int";
650
+ }
651
+ return `${kind === "float" ? "f" : "i"}:${v}`;
652
+ }
653
+ default: return `o:${String(v)}`;
654
+ }
655
+ }
656
+ function checkDuplicateKeys(pairs, state) {
657
+ const seen = /* @__PURE__ */ new Set();
658
+ for (const pair of pairs) if (pair.key instanceof YamlScalar) {
659
+ const id = keyIdentity(pair.key, state.text);
660
+ if (seen.has(id)) state.warnings.push({
661
+ code: "DuplicateKey",
662
+ message: `Duplicate key: ${String(pair.key.value)}`,
663
+ offset: pair.key.offset,
664
+ length: pair.key.length
665
+ });
666
+ seen.add(id);
667
+ }
668
+ }
669
+ /**
670
+ * Inspect the slice of children after a `?` indicator to decide how the
671
+ * explicit key should be composed.
672
+ *
673
+ * - `terminated` — found a matching `:` at `qCol`; the key is the slice
674
+ * between `?` and that `:`. Existing per-node logic handles this.
675
+ * - `inline-implicit-map` — no matching `:` at `qCol`, but the slice
676
+ * contains a `:` at a deeper column. The whole slice is a compact
677
+ * inline implicit-map key (M2N8/00, M2N8/01).
678
+ * - `simple` — no internal `:` at all; the next content node is the
679
+ * single key (KK5P, M5DY block-seq keys; plain scalar keys).
680
+ */
681
+ function scanExplicitKeyShape(children, qIdx, qCol, text) {
682
+ const qChild = children[qIdx];
683
+ const qLine = qChild ? lineCol(text, qChild.offset).line : -1;
684
+ let inlineColonOnQLine = false;
685
+ let endIdx = children.length;
686
+ for (let j = qIdx + 1; j < children.length; j++) {
687
+ const c = children[j];
688
+ if (!c) continue;
689
+ if (c.type === "whitespace" && c.source === ":") {
690
+ if (lineCol(text, c.offset).column === qCol) return {
691
+ kind: "terminated",
692
+ matchIdx: j
693
+ };
694
+ if (lineCol(text, c.offset).line === qLine) inlineColonOnQLine = true;
695
+ }
696
+ if (c.type === "whitespace" && c.source === "?") {
697
+ if (lineCol(text, c.offset).column === qCol) {
698
+ endIdx = j;
699
+ break;
700
+ }
701
+ }
702
+ }
703
+ if (inlineColonOnQLine) return {
704
+ kind: "inline-implicit-map",
705
+ endIdx
706
+ };
707
+ return { kind: "simple" };
708
+ }
709
+ /**
710
+ * Returns true if a value at the given offset was introduced by a `?`
711
+ * explicit-key indicator. Scans backward through whitespace and newlines
712
+ * looking for a `?` at the start of a line (not part of a scalar).
713
+ *
714
+ * Named verbosely to avoid colliding with the local `isExplicitKey`
715
+ * boolean in `flattenBlockMapChildren` (used for multi-line key
716
+ * continuation detection — different concept).
717
+ */
718
+ function wasIntroducedByExplicitKeyIndicator(text, offset) {
719
+ let i = offset - 1;
720
+ while (i >= 0) {
721
+ const ch = text[i];
722
+ if (ch === " " || ch === " " || ch === "\n" || ch === "\r") {
723
+ i--;
724
+ continue;
725
+ }
726
+ if (ch === "?") {
727
+ if (i === 0) return true;
728
+ const prev = text[i - 1];
729
+ return prev === " " || prev === " " || prev === "\n" || prev === "\r";
730
+ }
731
+ return false;
732
+ }
733
+ return false;
734
+ }
735
+ /**
736
+ * Validate that implicit mapping keys do not span multiple lines.
737
+ * YAML 1.2 §7.4.2 requires implicit keys to fit on a single line.
738
+ */
739
+ function checkMultilineImplicitKeys(pairs, state, items) {
740
+ for (const pair of pairs) {
741
+ const key = pair.key;
742
+ if (key.length === 0) continue;
743
+ if (key._tag === "YamlScalar") {
744
+ const s = key.style;
745
+ if (s !== "single-quoted" && s !== "double-quoted") continue;
746
+ const keySource = state.text.slice(key.offset, key.offset + key.length);
747
+ if (keySource.includes("\n") || keySource.includes("\r")) state.errors.push({
748
+ code: "UnexpectedToken",
749
+ message: "Implicit mapping key must not span multiple lines",
750
+ offset: key.offset,
751
+ length: key.length
752
+ });
753
+ continue;
754
+ }
755
+ if (key._tag === "YamlMap" || key._tag === "YamlSeq") {
756
+ if (key.style !== "flow") continue;
757
+ if (wasIntroducedByExplicitKeyIndicator(state.text, key.offset)) continue;
758
+ const keySource = state.text.slice(key.offset, key.offset + key.length);
759
+ if (keySource.includes("\n") || keySource.includes("\r")) state.errors.push({
760
+ code: "UnexpectedToken",
761
+ message: "Implicit mapping key must not span multiple lines",
762
+ offset: key.offset,
763
+ length: key.length
764
+ });
765
+ }
766
+ }
767
+ if (!items) return;
768
+ for (let i = 0; i < items.length; i++) {
769
+ const item = items[i];
770
+ if (!item) continue;
771
+ if (item.kind !== "node" && item.kind !== "key") continue;
772
+ const node = item.node;
773
+ if (node?._tag !== "YamlScalar" || node.length === 0) continue;
774
+ let j = i + 1;
775
+ while (j < items.length && items[j]?.kind === "comment") j++;
776
+ const next = items[j];
777
+ if (next?.kind !== "value-sep" || next.offset === void 0) continue;
778
+ if (lineCol(state.text, node.offset + node.length - 1).line !== lineCol(state.text, next.offset).line) state.errors.push({
779
+ code: "UnexpectedToken",
780
+ message: "Implicit mapping key and value indicator must be on the same line",
781
+ offset: node.offset,
782
+ length: node.length
783
+ });
784
+ }
785
+ }
786
+ /**
787
+ * Check for non-trivial CST content on the same line after a completed value node.
788
+ * Used to detect trailing content after quoted scalars and flow collections.
789
+ * Skips if the next non-trivia content is a ":" (value-sep), since that means
790
+ * this node is actually a key, not a value.
791
+ */
792
+ function checkTrailingContentOnSameLine(children, startIdx, valueNode, state) {
793
+ const valueEnd = valueNode.offset + valueNode.length;
794
+ for (let j = startIdx; j < children.length; j++) {
795
+ const next = children[j];
796
+ if (!next) continue;
797
+ if (next.type === "newline") break;
798
+ if (next.type === "comment") break;
799
+ if (next.type === "whitespace") {
800
+ if (next.source === ":") break;
801
+ if (next.source.trim() === "") continue;
802
+ }
803
+ if (sameLine(state.text, valueEnd - 1, next.offset)) state.errors.push({
804
+ code: "UnexpectedToken",
805
+ message: "Trailing content after value on same line",
806
+ offset: next.offset,
807
+ length: next.length
808
+ });
809
+ break;
810
+ }
811
+ }
812
+ /**
813
+ * QB6E: continuation lines of a multi-line quoted scalar in value position
814
+ * must be indented past the parent key column.
815
+ */
816
+ function validateQuotedScalarContinuationIndent(scalar, state, parentKeyColumn) {
817
+ if (parentKeyColumn < 0) return;
818
+ const text = state.text;
819
+ const start = scalar.offset;
820
+ const end = scalar.offset + scalar.length;
821
+ let i = start;
822
+ let inLineStart = false;
823
+ let lineStart = -1;
824
+ while (i < end && i < text.length) {
825
+ const ch = text[i];
826
+ if (ch === "\n") {
827
+ inLineStart = true;
828
+ lineStart = i + 1;
829
+ i++;
830
+ continue;
831
+ }
832
+ if (inLineStart) {
833
+ if (ch === " " || ch === " ") {
834
+ i++;
835
+ continue;
836
+ }
837
+ if (i - lineStart <= parentKeyColumn) {
838
+ state.errors.push({
839
+ code: "InvalidIndentation",
840
+ message: "Multi-line quoted scalar continuation must be indented past the parent key",
841
+ offset: i,
842
+ length: 1
843
+ });
844
+ return;
845
+ }
846
+ inLineStart = false;
847
+ }
848
+ i++;
849
+ }
850
+ }
851
+ /**
852
+ * 4JVG: a single scalar cannot have two anchor declarations. When the
853
+ * outer-meta slot and the pending-meta slot both carry an anchor, AND the
854
+ * scalar is being consumed as a plain value (not as a key for a nested
855
+ * block-map, not followed by `:`), both anchors would collapse onto the
856
+ * scalar — that's invalid YAML.
857
+ */
858
+ function validateNoDoubleAnchorOnScalar(scalar, children, idx, outerMeta, pendingMeta, state) {
859
+ if (outerMeta.anchor === void 0 || pendingMeta.anchor === void 0) return;
860
+ if (hasValueSepAfterInList(children, idx + 1)) return;
861
+ const nextContent = findNextContentInList(children, idx + 1);
862
+ if (nextContent?.node.type === "block-map" && !hasValueSepBetween(children, idx + 1, nextContent.idx)) return;
863
+ state.errors.push({
864
+ code: "UnexpectedToken",
865
+ message: "Scalar cannot have two anchor declarations",
866
+ offset: scalar.offset,
867
+ length: scalar.length
868
+ });
869
+ }
870
+ /**
871
+ * Y79Y/009: when a `:` value-sep is at the start of a line (continuation
872
+ * line, not on the same line as a key) and is immediately followed by a
873
+ * tab and same-line content, the tab is being used as block indentation
874
+ * for the upcoming content — invalid per YAML 1.2 §6.1.
875
+ */
876
+ function validateNoTabAfterContinuationValueSep(colonChild, children, idx, state) {
877
+ if (lineCol(state.text, colonChild.offset).column !== lineIndentColumn(state.text, colonChild.offset)) return;
878
+ let sawTab = false;
879
+ for (let j = idx + 1; j < children.length; j++) {
880
+ const c = children[j];
881
+ if (!c) continue;
882
+ if (c.type === "newline") return;
883
+ if (c.type === "whitespace") {
884
+ if (c.source.includes(" ")) sawTab = true;
885
+ continue;
886
+ }
887
+ if (sawTab && sameLine(state.text, colonChild.offset, c.offset)) state.errors.push({
888
+ code: "TabIndentation",
889
+ message: "Tab character cannot be used as indentation after a value indicator",
890
+ offset: colonChild.offset,
891
+ length: colonChild.length
892
+ });
893
+ return;
894
+ }
895
+ }
896
+ /**
897
+ * Returns true when the upcoming "-" indicator is preceded (after only
898
+ * whitespace/newlines) by either an empty block-seq placeholder or a
899
+ * `?`-only block-map. The parser uses both shapes to mark "explicit key
900
+ * with a sequence as the key" (KK5P fixture).
901
+ */
902
+ function precededByExplicitKeyMarker(children, idx) {
903
+ for (let j = idx - 1; j >= 0; j--) {
904
+ const c = children[j];
905
+ if (!c) continue;
906
+ if (c.type === "whitespace" || c.type === "newline" || c.type === "comment") continue;
907
+ if (c.type === "block-seq" && c.length === 0) return true;
908
+ if (c.type === "block-map" && c.source.trimEnd() === "?") return true;
909
+ return false;
910
+ }
911
+ return false;
912
+ }
913
+ /**
914
+ * G9HC, H7J7: anchor/tag in value position on a continuation line (not on
915
+ * the same line as `:`) must be at a column strictly greater than the
916
+ * parent key's column. Per YAML 1.2 §8.1.2, properties before a block
917
+ * collection must be at indent n+1, where n is the parent key column.
918
+ */
919
+ function validatePropertyContinuationColumn(property, state, afterValueSep, lastValueSepOffset, parentKeyColumn) {
920
+ if (!afterValueSep) return;
921
+ if (parentKeyColumn < 0) return;
922
+ if (lastValueSepOffset >= 0 && sameLine(state.text, lastValueSepOffset, property.offset)) return;
923
+ if (lineCol(state.text, property.offset).column <= parentKeyColumn) state.errors.push({
924
+ code: "InvalidIndentation",
925
+ message: "Property (anchor or tag) must be indented past the parent key",
926
+ offset: property.offset,
927
+ length: property.length
928
+ });
929
+ }
930
+ function composeBlockSeq(cst, state, meta) {
931
+ if (!enterNesting(state, cst)) return new YamlSeq({
932
+ items: [],
933
+ style: "block",
934
+ offset: cst.offset,
935
+ length: cst.length
936
+ });
937
+ try {
938
+ return composeBlockSeqInner(cst, state, meta);
939
+ } finally {
940
+ exitNesting(state);
941
+ }
942
+ }
943
+ function composeBlockSeqInner(cst, state, meta) {
944
+ const children = cst.children ?? [];
945
+ const items = [];
946
+ let pendingMeta = {};
947
+ let sawEntry = false;
948
+ const seqIndent = lineIndentColumn(state.text, cst.offset);
949
+ let sawNewlineSincePending = false;
950
+ for (let ci = 0; ci < children.length; ci++) {
951
+ const child = children[ci];
952
+ if (!child) continue;
953
+ if (child.type === "newline") {
954
+ if (hasMeta(pendingMeta)) sawNewlineSincePending = true;
955
+ continue;
956
+ }
957
+ if (child.type === "comment") continue;
958
+ if (child.type === "whitespace") {
959
+ if (child.source.trim() === "-") {
960
+ if (sawEntry) {
961
+ const emptyScalar = new YamlScalar({
962
+ value: null,
963
+ style: "plain",
964
+ offset: child.offset,
965
+ length: 0,
966
+ ...pendingMeta.tag !== void 0 ? { tag: pendingMeta.tag } : {},
967
+ ...pendingMeta.anchor !== void 0 ? { anchor: pendingMeta.anchor } : {}
968
+ });
969
+ if (pendingMeta.anchor) registerAnchor(emptyScalar, pendingMeta.anchor, state, child.offset);
970
+ pendingMeta = {};
971
+ items.push(emptyScalar);
972
+ }
973
+ sawEntry = true;
974
+ }
975
+ continue;
976
+ }
977
+ if (child.type === "error") {
978
+ state.errors.push({
979
+ code: "UnexpectedToken",
980
+ message: `Unexpected content: ${child.source.trim() || "(empty)"}`,
981
+ offset: child.offset,
982
+ length: child.length
983
+ });
984
+ continue;
985
+ }
986
+ if (child.type === "anchor") {
987
+ pendingMeta.anchor = getAnchorName(child, state.text);
988
+ continue;
989
+ }
990
+ if (child.type === "tag") {
991
+ pendingMeta.tag = child.source;
992
+ continue;
993
+ }
994
+ if (child.type === "flow-scalar" || child.type === "block-scalar") {
995
+ const nextSig = findNextSignificantChild(children, ci + 1, true);
996
+ const nextSigChild = nextSig !== null ? children[nextSig] : void 0;
997
+ if (nextSig !== null && nextSigChild && nextSigChild.type === "block-map") {
998
+ let keyMeta = hasMeta(pendingMeta) ? pendingMeta : void 0;
999
+ let mapMeta;
1000
+ if (sawNewlineSincePending && hasMeta(pendingMeta)) {
1001
+ mapMeta = pendingMeta;
1002
+ keyMeta = void 0;
1003
+ }
1004
+ const map = composeBlockMap(nextSigChild, state, makeScalar(child, state, keyMeta), mapMeta);
1005
+ pendingMeta = {};
1006
+ sawNewlineSincePending = false;
1007
+ sawEntry = false;
1008
+ items.push(map);
1009
+ ci = nextSig;
1010
+ continue;
1011
+ }
1012
+ if (child.type === "flow-scalar" && getScalarStyle(child) === "plain") {
1013
+ const { value: merged, nextIdx: mergeEnd, partsCount, endOffset } = collectMultilinePlainScalar(children, ci, void 0, state.text);
1014
+ if (partsCount > 1) {
1015
+ const scalar = new YamlScalar({
1016
+ value: resolveScalar(merged, "plain", pendingMeta.tag, state),
1017
+ style: "plain",
1018
+ offset: child.offset,
1019
+ length: endOffset - child.offset,
1020
+ ...pendingMeta.tag !== void 0 ? { tag: pendingMeta.tag } : {},
1021
+ ...pendingMeta.anchor !== void 0 ? { anchor: pendingMeta.anchor } : {}
1022
+ });
1023
+ if (pendingMeta.anchor) registerAnchor(scalar, pendingMeta.anchor, state, child.offset);
1024
+ pendingMeta = {};
1025
+ sawEntry = false;
1026
+ items.push(scalar);
1027
+ ci = mergeEnd - 1;
1028
+ continue;
1029
+ }
1030
+ }
1031
+ const scalar = makeScalar(child, state, hasMeta(pendingMeta) ? pendingMeta : void 0);
1032
+ pendingMeta = {};
1033
+ sawNewlineSincePending = false;
1034
+ sawEntry = false;
1035
+ items.push(scalar);
1036
+ continue;
1037
+ }
1038
+ if (child.type === "alias") {
1039
+ checkAnchorOnAlias(pendingMeta, child, state);
1040
+ const alias = makeAlias(child, state);
1041
+ pendingMeta = {};
1042
+ sawNewlineSincePending = false;
1043
+ sawEntry = false;
1044
+ items.push(alias);
1045
+ continue;
1046
+ }
1047
+ if (child.type === "block-map") {
1048
+ if (hasMeta(pendingMeta) && blockMapStartsWithValueSep(child)) {
1049
+ const emptyKey = new YamlScalar({
1050
+ value: null,
1051
+ style: "plain",
1052
+ offset: child.offset,
1053
+ length: 0,
1054
+ ...pendingMeta.tag !== void 0 ? { tag: pendingMeta.tag } : {},
1055
+ ...pendingMeta.anchor !== void 0 ? { anchor: pendingMeta.anchor } : {}
1056
+ });
1057
+ if (pendingMeta.anchor) registerAnchor(emptyKey, pendingMeta.anchor, state, child.offset);
1058
+ pendingMeta = {};
1059
+ sawNewlineSincePending = false;
1060
+ const map = composeBlockMap(child, state, emptyKey, void 0);
1061
+ sawEntry = false;
1062
+ items.push(map);
1063
+ continue;
1064
+ }
1065
+ const map = composeBlockMap(child, state, void 0, hasMeta(pendingMeta) ? pendingMeta : void 0);
1066
+ pendingMeta = {};
1067
+ sawNewlineSincePending = false;
1068
+ sawEntry = false;
1069
+ items.push(map);
1070
+ continue;
1071
+ }
1072
+ if (child.type === "block-seq") {
1073
+ const seq = composeBlockSeq(child, state, hasMeta(pendingMeta) ? pendingMeta : void 0);
1074
+ pendingMeta = {};
1075
+ sawNewlineSincePending = false;
1076
+ sawEntry = false;
1077
+ items.push(seq);
1078
+ continue;
1079
+ }
1080
+ if (child.type === "flow-map") {
1081
+ const map = state.flow.composeFlowMap(child, state, hasMeta(pendingMeta) ? pendingMeta : void 0, seqIndent);
1082
+ pendingMeta = {};
1083
+ sawEntry = false;
1084
+ items.push(map);
1085
+ checkTrailingContentOnSameLine(children, ci + 1, child, state);
1086
+ continue;
1087
+ }
1088
+ if (child.type === "flow-seq") {
1089
+ const seq = state.flow.composeFlowSeq(child, state, hasMeta(pendingMeta) ? pendingMeta : void 0, seqIndent);
1090
+ pendingMeta = {};
1091
+ sawEntry = false;
1092
+ items.push(seq);
1093
+ checkTrailingContentOnSameLine(children, ci + 1, child, state);
1094
+ }
1095
+ }
1096
+ if (sawEntry && !hasMeta(pendingMeta)) items.push(new YamlScalar({
1097
+ value: null,
1098
+ style: "plain",
1099
+ offset: cst.offset + cst.length,
1100
+ length: 0
1101
+ }));
1102
+ if (hasMeta(pendingMeta)) {
1103
+ const scalar = new YamlScalar({
1104
+ value: resolveScalar("", "plain", pendingMeta.tag, state),
1105
+ style: "plain",
1106
+ offset: 0,
1107
+ length: 0,
1108
+ ...pendingMeta.tag !== void 0 ? { tag: pendingMeta.tag } : {},
1109
+ ...pendingMeta.anchor !== void 0 ? { anchor: pendingMeta.anchor } : {}
1110
+ });
1111
+ if (pendingMeta.anchor) registerAnchor(scalar, pendingMeta.anchor, state, 0);
1112
+ items.push(scalar);
1113
+ }
1114
+ const seq = new YamlSeq({
1115
+ items,
1116
+ style: "block",
1117
+ offset: cst.offset,
1118
+ length: cst.length,
1119
+ ...meta?.tag !== void 0 ? { tag: meta.tag } : {},
1120
+ ...meta?.anchor !== void 0 ? { anchor: meta.anchor } : {},
1121
+ ...meta?.comment !== void 0 ? { comment: meta.comment } : {}
1122
+ });
1123
+ if (meta?.anchor) registerAnchor(seq, meta.anchor, state, cst.offset);
1124
+ return seq;
1125
+ }
1126
+ /**
1127
+ * Compose a block map from flat document children (no block-map wrapper node).
1128
+ * This happens in multi-document scenarios where the parser doesn't create a block-map node.
1129
+ */
1130
+ function composeFlatBlockMap(children, startIdx, parentCst, state, externalFirstKey, meta) {
1131
+ const items = flattenBlockMapChildren(children.slice(startIdx), state);
1132
+ items.unshift({
1133
+ kind: "key",
1134
+ node: externalFirstKey
1135
+ });
1136
+ const pairs = [];
1137
+ buildPairs(items, pairs, state.text);
1138
+ if (state.options.uniqueKeys) checkDuplicateKeys(pairs, state);
1139
+ checkMultilineImplicitKeys(pairs, state);
1140
+ const offset = "offset" in externalFirstKey ? externalFirstKey.offset : parentCst.offset;
1141
+ const map = new YamlMap({
1142
+ items: pairs,
1143
+ style: "block",
1144
+ offset,
1145
+ length: parentCst.offset + parentCst.length - offset,
1146
+ ...meta?.tag !== void 0 ? { tag: meta.tag } : {},
1147
+ ...meta?.anchor !== void 0 ? { anchor: meta.anchor } : {},
1148
+ ...meta?.comment !== void 0 ? { comment: meta.comment } : {}
1149
+ });
1150
+ if (meta?.anchor) registerAnchor(map, meta.anchor, state, offset);
1151
+ return map;
1152
+ }
1153
+
1154
+ //#endregion
1155
+ export { buildPairs, checkDuplicateKeys, checkMultilineImplicitKeys, checkTrailingContentOnSameLine, composeBlockMap, composeBlockSeq, composeFlatBlockMap, flattenBlockMapChildren };