@limaformat/lima 0.3.1 → 0.5.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.
@@ -1,12 +1,43 @@
1
1
  /** Reference tokens recorded while the annotated Core tree is being built. */
2
- import type { StringSourceSpan } from './builder.js';
3
2
  export type ReferenceToken2 = {
4
3
  token: string;
4
+ /**
5
+ * Index of the token within the decoded scalar value — the splice point
6
+ * for interpolation. A UTF-16 offset (JS `String.slice` units); stays
7
+ * implementation-local, unlike `line`/`offset`.
8
+ */
5
9
  index: number;
6
- offset: number;
10
+ /** Physical 1-based source line of the token (References 2.0 §2.4). */
7
11
  line: number;
12
+ /**
13
+ * 0-based **codepoint** offset of the token within its source line — the
14
+ * `column - 1` a diagnostic reports, and the tie-break key for §5 error
15
+ * ordering. Identical across implementations.
16
+ */
17
+ offset: number;
8
18
  documentPath?: string;
9
19
  partialPath?: string;
10
20
  };
21
+ /**
22
+ * Where a scalar begins in the source: physical 1-based `line` and 0-based
23
+ * codepoint column `col` of its first character. A token's position is this
24
+ * anchor plus its codepoint offset within the value.
25
+ *
26
+ * `raw` is the value text a token's position is *read from*, before comment
27
+ * stripping and `\#` collapse (so `x: a\#b ${t}` reports `${t}` at its real
28
+ * column) — for a block scalar it is the original body lines joined with
29
+ * `\n` (§2.4 forbids reconstructing a token's line from the `^^`-merged
30
+ * string). Omitted only when it equals the decoded value.
31
+ *
32
+ * `tabAdjust[i]` is the column count Core §3 added to physical line
33
+ * `line + i` by leading-tab expansion; it is subtracted so the reported
34
+ * column is the *original*-source column (§2.4).
35
+ */
36
+ export type ReferenceSource = {
37
+ raw?: string;
38
+ line: number;
39
+ col: number;
40
+ tabAdjust?: number[];
41
+ };
11
42
  export declare const PURE_REFERENCE_2: RegExp;
12
- export declare const scanReferenceTokens2: (value: string, firstLine: number, sourceSpans?: StringSourceSpan[]) => ReferenceToken2[];
43
+ export declare const scanReferenceTokens2: (value: string, source: ReferenceSource | undefined) => ReferenceToken2[];
@@ -1,3 +1,4 @@
1
+ /** Reference tokens recorded while the annotated Core tree is being built. */
1
2
  const SEGMENT = '[a-zA-Z0-9_][a-zA-Z0-9_:-]*';
2
3
  const DOC_PATH = `${SEGMENT}(?:\\.${SEGMENT})*`;
3
4
  const PARTIAL_NAME = '[a-zA-Z0-9_][a-zA-Z0-9_:/-]*';
@@ -5,33 +6,65 @@ const PARTIAL_PATH = `${PARTIAL_NAME}(?:\\.${SEGMENT})*`;
5
6
  export const PURE_REFERENCE_2 = new RegExp(`^(?:\\$\\{(${DOC_PATH})\\}|\\$\\((${PARTIAL_PATH})\\))$`);
6
7
  const REFERENCE_2 = new RegExp(`\\$\\{(${DOC_PATH})\\}|\\$\\((${PARTIAL_PATH})\\)`, 'g');
7
8
  const NO_TOKENS = [];
8
- export const scanReferenceTokens2 = (value, firstLine, sourceSpans) => {
9
- if (!value.includes('${') && !value.includes('$('))
10
- return NO_TOKENS;
9
+ /** Codepoint length of the UTF-16 slice `[a, b)` of `s`. */
10
+ const codepointLen = (s, a, b) => {
11
+ let n = 0;
12
+ for (let i = a; i < b;) {
13
+ i += s.codePointAt(i) > 0xffff ? 2 : 1;
14
+ n++;
15
+ }
16
+ return n;
17
+ };
18
+ /** Splice points (decoded value order): token text, UTF-16 index, path. */
19
+ const scanDecoded = (value) => {
11
20
  REFERENCE_2.lastIndex = 0;
12
- const result = [];
13
- let line = firstLine;
14
- let lineStart = 0;
21
+ const out = [];
22
+ for (const m of value.matchAll(REFERENCE_2)) {
23
+ out.push({
24
+ token: m[0], index: m.index ?? 0,
25
+ ...(m[1] !== undefined ? { documentPath: m[1] } : { partialPath: m[2] }),
26
+ });
27
+ }
28
+ return out;
29
+ };
30
+ /** Physical `(line, offset)` of each token, in source order, from the raw text. */
31
+ const scanPhysical = (raw, firstLine, firstCol, tabAdjust) => {
32
+ REFERENCE_2.lastIndex = 0;
33
+ const out = [];
15
34
  let scanned = 0;
16
- let spanIndex = 0;
17
- for (const match of value.matchAll(REFERENCE_2)) {
18
- const index = match.index ?? 0;
19
- while (scanned < index) {
20
- if (value.charCodeAt(scanned) === 10) {
35
+ let line = firstLine;
36
+ let col = firstCol;
37
+ let lineIndex = 0;
38
+ for (const m of raw.matchAll(REFERENCE_2)) {
39
+ const idx = m.index ?? 0;
40
+ while (scanned < idx) {
41
+ const cp = raw.codePointAt(scanned);
42
+ if (cp === 10) {
21
43
  line++;
22
- lineStart = scanned + 1;
44
+ col = 0;
45
+ lineIndex++;
23
46
  }
24
- scanned++;
47
+ else
48
+ col++;
49
+ scanned += cp > 0xffff ? 2 : 1;
25
50
  }
26
- while (sourceSpans && spanIndex + 1 < sourceSpans.length && sourceSpans[spanIndex + 1].start <= index)
27
- spanIndex++;
28
- const source = sourceSpans?.[spanIndex];
29
- result.push({
30
- token: match[0], index,
31
- line: source?.line ?? line,
32
- offset: source ? source.sourceOffset + index - source.start : index - lineStart,
33
- ...(match[1] !== undefined ? { documentPath: match[1] } : { partialPath: match[2] }),
34
- });
51
+ out.push({ line, offset: col - (tabAdjust?.[lineIndex] ?? 0) });
52
+ }
53
+ return out;
54
+ };
55
+ export const scanReferenceTokens2 = (value, source) => {
56
+ if (!value.includes('${') && !value.includes('$('))
57
+ return NO_TOKENS;
58
+ const decoded = scanDecoded(value);
59
+ if (decoded.length === 0)
60
+ return NO_TOKENS;
61
+ // The raw and decoded scans see the same tokens in the same order —
62
+ // `\#` collapse and `^^` merge never add or remove a `${…}` / `$(…)`.
63
+ const physical = source === undefined
64
+ ? scanPhysical(value, 1, 0)
65
+ : scanPhysical(source.raw ?? value, source.line, source.col, source.tabAdjust);
66
+ if (physical.length !== decoded.length) {
67
+ throw new Error(`Lima internal: reference token count mismatch (raw ${physical.length}, decoded ${decoded.length})`);
35
68
  }
36
- return result;
69
+ return decoded.map((d, i) => ({ ...d, line: physical[i].line, offset: physical[i].offset }));
37
70
  };
@@ -8,6 +8,8 @@ export type ParseOptions = {
8
8
  onWarning?: (diagnostic: Diagnostic) => void;
9
9
  };
10
10
  export declare const parse: <T extends Record<string, unknown> = Meta>(frontMatter: string, options?: ParseOptions) => T;
11
+ /** Test oracle: exercises the resolver without its dependency-result cache. */
12
+ export declare const __parseWithoutResolveCacheForTest: <T extends Record<string, unknown> = Meta>(frontMatter: string, options?: ParseOptions) => T;
11
13
  /** @deprecated Use parse(). */
12
14
  export declare const parseReferences: typeof parse;
13
15
  export {};
@@ -3,11 +3,19 @@ import { canonicalString, codepointLength, countNodes, ingestPartialValue, PARTI
3
3
  import { NESTING_DEPTH_LIMIT, parseCore, parseCoreWithPositions, toPlainValue, } from './core.js';
4
4
  import { hasActiveReferences2 } from './scalars.js';
5
5
  import { LimaError } from './errors.js';
6
- import { collectAllParticipants, deepCopyPositioned, earliestParticipant, finalizePositioned, partialToPositioned, } from './references.js';
7
- const emptyMapping = () => Object.create(null);
6
+ import { collectAllParticipants, deepCopyPositioned, earliestParticipant, emptyMapping, finalizePositioned, partialToPositioned, } from './positioned-tree.js';
8
7
  const PARTIAL_NAME = '[a-zA-Z0-9_][a-zA-Z0-9_:/-]*';
9
8
  const PARTIAL_NAME_RE = new RegExp(`^${PARTIAL_NAME}$`);
10
9
  const MAX_EDGES = 3;
10
+ /** Copies a pure-reference target and stamps the new insertion without
11
+ * discarding provenance already attached to the copied root (R-112). */
12
+ const copyWithInsertion = (target, insertedAt) => {
13
+ const copy = deepCopyPositioned(target);
14
+ const priorInsertions = copy.insertedAt === undefined
15
+ ? copy.priorInsertions
16
+ : [...(copy.priorInsertions ?? []), copy.insertedAt];
17
+ return { ...copy, insertedAt, priorInsertions };
18
+ };
11
19
  const addDiagnostic = (ctx, diagnostic, offset = 0) => {
12
20
  ctx.diagnostics.push({ ...diagnostic, offset });
13
21
  };
@@ -75,9 +83,9 @@ const resolveNodeUncached = (node, document, partials, ctx, remainingEdges, stac
75
83
  const match = tokenMatches(item)[0];
76
84
  const token = match?.token ?? item.value;
77
85
  addDiagnostic(ctx, {
78
- code: 'INVALID_REFERENCE_SHAPE', line: item.line, token,
79
- message: `Lima: reference "${token}" resolves to an array, which cannot be inserted as a sequence item at line ${item.line}`,
80
- });
86
+ code: 'INVALID_REFERENCE_SHAPE', line: match?.line ?? item.line, token,
87
+ message: `Lima: reference "${token}" resolves to an array, which cannot be inserted as a sequence item at line ${match?.line ?? item.line}`,
88
+ }, match?.offset ?? 0);
81
89
  return item;
82
90
  }
83
91
  return result.value;
@@ -120,8 +128,10 @@ const resolveNodeUncached = (node, document, partials, ctx, remainingEdges, stac
120
128
  if (target === undefined || stack.has(target))
121
129
  return { value: node, complete: false };
122
130
  if (pure.partialPath !== undefined) {
131
+ // `partialToPositioned` never annotates partial targets: partials are
132
+ // inert and have no source token before this document insertion.
123
133
  return {
124
- value: { ...deepCopyPositioned(target), insertedAt: { line: pure.line, token: pure.token } },
134
+ value: copyWithInsertion(target, { line: pure.line, offset: pure.offset, token: pure.token }),
125
135
  complete: true,
126
136
  };
127
137
  }
@@ -134,7 +144,9 @@ const resolveNodeUncached = (node, document, partials, ctx, remainingEdges, stac
134
144
  if (selected === undefined)
135
145
  return { value: node, complete: false };
136
146
  return {
137
- value: { ...deepCopyPositioned(selected), insertedAt: { line: pure.line, token: pure.token } },
147
+ // `selected` may itself be the root inserted by an earlier reference;
148
+ // both it and this outer copy participate in final-structure errors.
149
+ value: copyWithInsertion(selected, { line: pure.line, offset: pure.offset, token: pure.token }),
138
150
  complete: true,
139
151
  };
140
152
  }
@@ -182,13 +194,24 @@ const resolveNodeUncached = (node, document, partials, ctx, remainingEdges, stac
182
194
  }
183
195
  return { value: { ...node, value: output, references2: unresolvedTokens }, complete };
184
196
  };
185
- /** Successful and failed dependency results are immutable and budget-specific. */
197
+ /**
198
+ * The cache key is complete without `stack`: for a fixed node and edge
199
+ * budget, its outgoing targets and traversal order are fixed by the parsed
200
+ * document. If one of those targets is already on the current stack, that
201
+ * target is on a dependency cycle reachable from this node; entering from a
202
+ * different ancestor cannot make the cycle complete, and the monotonically
203
+ * decreasing edge budget fixes where an overlong path becomes unresolved.
204
+ * Thus `stack` only detects an already-inevitable incomplete dependency; it
205
+ * does not select a different successful value. Top-level calls are excluded
206
+ * (`stack.size > 1`) and cached Resolution trees are immutable, so sharing a
207
+ * successful or incomplete result at the same remaining budget is safe.
208
+ */
186
209
  const resolveNode = (node, document, partials, ctx, remainingEdges, stack) => {
187
- const cacheable = stack.size > 1 && (node.kind === 'array' || node.kind === 'mapping' ||
188
- (isActiveString(node) && tokenMatches(node).length > 0));
189
- if (!cacheable)
210
+ const cache = ctx.cache;
211
+ if (cache === undefined || stack.size <= 1 || !(node.kind === 'array' || node.kind === 'mapping' ||
212
+ (isActiveString(node) && tokenMatches(node).length > 0)))
190
213
  return resolveNodeUncached(node, document, partials, ctx, remainingEdges, stack);
191
- const byBudget = ctx.cache.get(node);
214
+ const byBudget = cache.get(node);
192
215
  const cached = byBudget?.get(remainingEdges);
193
216
  if (cached !== undefined)
194
217
  return cached;
@@ -196,7 +219,7 @@ const resolveNode = (node, document, partials, ctx, remainingEdges, stack) => {
196
219
  if (byBudget !== undefined)
197
220
  byBudget.set(remainingEdges, result);
198
221
  else
199
- ctx.cache.set(node, new Map([[remainingEdges, result]]));
222
+ cache.set(node, new Map([[remainingEdges, result]]));
200
223
  return result;
201
224
  };
202
225
  const validatePartials = (raw) => {
@@ -243,7 +266,7 @@ const scanUnresolved = (node, ctx) => {
243
266
  for (const child of node.entries.values())
244
267
  scanUnresolved(child, ctx);
245
268
  };
246
- export const parse = (frontMatter, options) => {
269
+ const parseInternal = (frontMatter, options, useResolveCache) => {
247
270
  if (options?.mode !== undefined && options.mode !== 'core' && options.mode !== 'references') {
248
271
  throw new TypeError(`Lima: invalid parse mode "${String(options.mode)}"`);
249
272
  }
@@ -260,7 +283,7 @@ export const parse = (frontMatter, options) => {
260
283
  return parseCore(frontMatter, options);
261
284
  }
262
285
  const document = parseCoreWithPositions(frontMatter, { strict: options?.strict ?? false, onWarning: options?.onWarning });
263
- const ctx = { diagnostics: [], cache: new WeakMap() };
286
+ const ctx = { diagnostics: [], ...(useResolveCache ? { cache: new WeakMap() } : {}) };
264
287
  const resolved = new Map();
265
288
  for (const [key, value] of document) {
266
289
  const stack = new Set([value]);
@@ -271,7 +294,10 @@ export const parse = (frontMatter, options) => {
271
294
  scanUnresolved(value, ctx);
272
295
  if (ctx.diagnostics.length > 0) {
273
296
  ctx.diagnostics.sort((a, b) => a.line - b.line || a.offset - b.offset);
274
- throw new LimaError(ctx.diagnostics[0]);
297
+ const winner = ctx.diagnostics[0];
298
+ // §2.4: expose the token's physical position (1-based) — Rust and Go
299
+ // already do; `offset` was internal-only here.
300
+ throw new LimaError({ ...winner, column: winner.offset + 1 });
275
301
  }
276
302
  const finalized = [...resolved].map(([key, value]) => [key, finalizePositioned(value)]);
277
303
  const depth = finalized.length === 0 ? 0 : Math.max(...finalized.map(([, value]) => value.depth));
@@ -280,6 +306,7 @@ export const parse = (frontMatter, options) => {
280
306
  const winner = earliestParticipant(participants);
281
307
  throw new LimaError({
282
308
  code: 'RESOURCE_LIMIT', line: winner?.line ?? 1, token: winner?.token,
309
+ ...(winner?.offset !== undefined ? { column: winner.offset + 1 } : {}),
283
310
  message: `Lima: nesting depth exceeds maximum of ${NESTING_DEPTH_LIMIT} at line ${winner?.line ?? 1}`,
284
311
  });
285
312
  }
@@ -293,6 +320,7 @@ export const parse = (frontMatter, options) => {
293
320
  const winner = earliestParticipant(participants);
294
321
  throw new LimaError({
295
322
  code: 'RESOURCE_LIMIT', line: winner?.line ?? 1, token: winner?.token,
323
+ ...(winner?.offset !== undefined ? { column: winner.offset + 1 } : {}),
296
324
  message: `Lima: result exceeds maximum size of ${RESULT_NODE_LIMIT} total nodes at line ${winner?.line ?? 1}`,
297
325
  });
298
326
  }
@@ -301,5 +329,8 @@ export const parse = (frontMatter, options) => {
301
329
  out[key] = value.native;
302
330
  return out;
303
331
  };
332
+ export const parse = (frontMatter, options) => parseInternal(frontMatter, options, true);
333
+ /** Test oracle: exercises the resolver without its dependency-result cache. */
334
+ export const __parseWithoutResolveCacheForTest = (frontMatter, options) => parseInternal(frontMatter, options, false);
304
335
  /** @deprecated Use parse(). */
305
336
  export const parseReferences = parse;
package/dist/scalars.d.ts CHANGED
@@ -7,40 +7,47 @@
7
7
  import { type LimaValue } from './value.js';
8
8
  import { type ParseContext } from './normalize.js';
9
9
  import type { ValueBuilder } from './builder.js';
10
- import { type ReferenceToken2 } from './reference-tokens2.js';
10
+ import { type ReferenceSource, type ReferenceToken2 } from './reference-tokens2.js';
11
11
  /**
12
12
  * `insertedAt` is never set by Core — it's a References-only annotation
13
13
  * (see references.ts's `resolveTree`), stamped on the root of a value
14
14
  * copied in by a successful pure-reference resolution, with the source
15
- * token and line that caused the insertion. It powers References §5's
16
- * global-error attribution (R-112): when a final-result limit (nesting
17
- * depth, total node count) is violated, the lowest-line `insertedAt` among
18
- * the participating nodes identifies which reference token to blame — the
19
- * spec requires the error message to include both the token and the line.
15
+ * token, line, and character offset that caused the insertion. It powers
16
+ * References §5's global-error attribution (R-112): when a final-result
17
+ * limit (nesting depth, total node count) is violated, the lowest source
18
+ * position (`line`, then `offset`) among the participating nodes identifies
19
+ * which reference token to blame — the spec requires the error message to
20
+ * include both the token and the line. `priorInsertions` retains older
21
+ * root provenance when a resolved pure-reference result is copied again.
20
22
  */
21
23
  export type InsertedAt = {
22
24
  line: number;
23
25
  token: string;
26
+ offset?: number;
24
27
  };
25
28
  export type PositionedValue = {
26
29
  kind: 'null';
27
30
  line: number;
28
31
  insertedAt?: InsertedAt;
32
+ priorInsertions?: InsertedAt[];
29
33
  } | {
30
34
  kind: 'bool';
31
35
  value: boolean;
32
36
  line: number;
33
37
  insertedAt?: InsertedAt;
38
+ priorInsertions?: InsertedAt[];
34
39
  } | {
35
40
  kind: 'int';
36
41
  value: number;
37
42
  line: number;
38
43
  insertedAt?: InsertedAt;
44
+ priorInsertions?: InsertedAt[];
39
45
  } | {
40
46
  kind: 'float';
41
47
  value: number;
42
48
  line: number;
43
49
  insertedAt?: InsertedAt;
50
+ priorInsertions?: InsertedAt[];
44
51
  } | {
45
52
  kind: 'string';
46
53
  value: string;
@@ -48,23 +55,27 @@ export type PositionedValue = {
48
55
  quoted: boolean;
49
56
  references2?: ReferenceToken2[];
50
57
  insertedAt?: InsertedAt;
58
+ priorInsertions?: InsertedAt[];
51
59
  } | {
52
60
  kind: 'instant';
53
61
  value: Date;
54
62
  line: number;
55
63
  insertedAt?: InsertedAt;
64
+ priorInsertions?: InsertedAt[];
56
65
  } | {
57
66
  kind: 'array';
58
67
  items: PositionedValue[];
59
68
  line: number;
60
69
  references2Active?: boolean;
61
70
  insertedAt?: InsertedAt;
71
+ priorInsertions?: InsertedAt[];
62
72
  } | {
63
73
  kind: 'mapping';
64
74
  entries: Map<string, PositionedValue>;
65
75
  line: number;
66
76
  references2Active?: boolean;
67
77
  insertedAt?: InsertedAt;
78
+ priorInsertions?: InsertedAt[];
68
79
  };
69
80
  export declare const hasActiveReferences2: (value: PositionedValue) => boolean;
70
81
  /** The `ValueBuilder<PositionedValue>` — reconstructs today's annotated tree exactly, for References. */
@@ -78,17 +89,76 @@ export declare const NO_SPAN_VALUE: unique symbol;
78
89
  * handled by the full string-based scalar/flow grammar.
79
90
  */
80
91
  export declare const parseSimpleScalarSpan: <V, M>(source: string, start: number, end: number, line: number, strict: boolean, builder: ValueBuilder<V, M>) => V | typeof NO_SPAN_VALUE;
92
+ /**
93
+ * Index of the quote that closes a quoted scalar opening at `s[0]`, or -1
94
+ * if it is never closed. Escape-aware, so `"a\""` closes at its last
95
+ * character rather than the escaped inner `"`:
96
+ * - double quotes: a backslash escapes the next character (§6.1.2);
97
+ * - single-quoted *values* (§6.1.3 "Backslash pairing"): scanning left
98
+ * to right, `\\` (two literal backslashes) and `\'` (an escaped quote)
99
+ * are each consumed as a two-character unit, so a lone `\` is literal
100
+ * and a closing `'` after an even backslash run closes the string.
101
+ * Pass `singleQuoteEscape = false` for single-quoted *keys*, which are
102
+ * fully literal (§5.2), where the first `'` closes.
103
+ * `s[0]` is assumed to be `'` or `"`.
104
+ */
105
+ export declare const closingQuoteIndex: (s: string, singleQuoteEscape?: boolean) => number;
106
+ /**
107
+ * Whether `raw` (a key candidate as written, before quote stripping) is a
108
+ * usable Lima key:
109
+ * - a quoted string that is properly closed at its final character, or
110
+ * - an unquoted token matching the Core §5.1 grammar.
111
+ *
112
+ * §5.1 is a closed pattern: a key with a space, a dot, a slash, a paren, or
113
+ * any other character outside `[a-zA-Z0-9_:\-]` (a surviving NBSP included)
114
+ * is not an unquoted key. Per §4, such a line/item is unrecognised and
115
+ * skipped in *both* modes — §10's strict error list stays closed. The three
116
+ * mapping contexts (top level, block-nested, flow) now share this one
117
+ * check; `docs/decisions/structural-indentation-unicode-whitespace.md`
118
+ * governs only whether Unicode whitespace counts as *indentation*, not
119
+ * whether it may appear in a key.
120
+ */
121
+ export declare const isValidKey: (raw: string) => boolean;
81
122
  export declare const unescapeDQ: (s: string, strict?: boolean, line?: number) => string;
123
+ /**
124
+ * The value text with a trailing `#` comment removed, but with `\#`
125
+ * escapes left as written. This is the *physical* form a References 2.0
126
+ * token's column is measured against (§2.4): the comment is not part of
127
+ * the value (so its `${…}`-shaped text must not be scanned as a token),
128
+ * but a `\#` before a token still occupies its two source columns.
129
+ *
130
+ * `trimEnd()` is exactly the project whitespace set (ECMAScript WhiteSpace
131
+ * + LineTerminator = `isTrimWhitespace`) — a U+00A0 / FEFF between the
132
+ * value and the `#` is not part of the value. The Rust and Go ports must
133
+ * use `is_trim_whitespace` here, not a host `trim_end` or `" \t"`.
134
+ */
135
+ export declare const stripCommentKeepEscapes: (val: string) => string;
136
+ /**
137
+ * The value text with a trailing `#` comment removed and each `\#` *outside*
138
+ * a quoted string collapsed to `#` (Core §6.1.4 — the escaped-hash rule is
139
+ * defined for unquoted values only). Inside `"..."` / `'...'` the backslash
140
+ * is left intact: `\#` there is an unknown double-quoted escape (§6.1.2:
141
+ * strict throws, non-strict keeps it) or a literal backslash in a
142
+ * single-quoted string (§6.1.3). Finds the same comment boundary as
143
+ * `stripCommentKeepEscapes` — the two must agree, they annotate the same
144
+ * value.
145
+ */
82
146
  export declare const stripComment: (val: string) => string;
83
- /** Strips a key's surrounding quotes (unescaping double-quoted keys), or returns it unchanged. */
84
- export declare const stripKeyQuotes: (s: string) => string;
147
+ /**
148
+ * Strips a key's surrounding quotes — unescaping a double-quoted key
149
+ * (§6.1.2 escapes, strict-checked), taking a single-quoted key literally
150
+ * (§5.2). Returns `s` unchanged when it is not a properly-closed quoted
151
+ * string. Callers gate on `isValidKey` first, so an unterminated or
152
+ * trailing-content key never reaches here as a real key.
153
+ */
154
+ export declare const stripKeyQuotes: (s: string, strict?: boolean, line?: number) => string;
85
155
  /**
86
156
  * Quoted-or-typed scalar, shared by every value position (top-level inline
87
157
  * values, flow-sequence/flow-mapping items, block-array scalar items).
88
- * `topLevel` gates two checks that only apply at the outermost resolveValue
89
- * call site in the legacy parser and are deliberately not extended to flow
90
- * items here, to keep this a faithful behavioral port: the "unclosed flow
91
- * bracket" throw and the "non-whitespace after closing quote" strict throw.
158
+ * §10.1's two quoted-string strict checks — "unterminated quoted string"
159
+ * and "non-whitespace content after closing quote in an inline value" —
160
+ * apply in every one of those positions, so they are enforced here rather
161
+ * than gated to the top level.
92
162
  */
93
- export declare const parseQuotedOrTyped: <V, M>(raw: string, ctx: ParseContext, line: number, topLevel: boolean, builder: ValueBuilder<V, M>) => V;
94
- export declare const parseScalarValue: <V, M>(raw: string, ctx: ParseContext, line: number, builder: ValueBuilder<V, M>) => V;
163
+ export declare const parseQuotedOrTyped: <V, M>(raw: string, ctx: ParseContext, line: number, builder: ValueBuilder<V, M>, source?: ReferenceSource) => V;
164
+ export declare const parseScalarValue: <V, M>(raw: string, ctx: ParseContext, line: number, builder: ValueBuilder<V, M>, source?: ReferenceSource) => V;