@effected/yaml 0.10.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.d.ts CHANGED
@@ -175,6 +175,33 @@ declare const YamlStringifyOptions_base: Schema.Class<YamlStringifyOptions, Sche
175
175
  * `"double-quoted"` is unaffected.
176
176
  */
177
177
  readonly quoteStyle: Schema.optionalKey<Schema.Literals<readonly ["single", "double"]>>;
178
+ /**
179
+ * Additionally quote plain scalars a foreign resolution dialect would
180
+ * coerce to a non-string. Absent (the default) adds no quoting beyond the
181
+ * YAML 1.2 Core Schema rules — byte-identical to the released output.
182
+ *
183
+ * `"yaml-1.1"` quotes every plain scalar a YAML 1.1 parser (js-yaml,
184
+ * PyYAML, libyaml, and the `yaml` npm package's YAML 1.1 schema, whose
185
+ * lenient resolvers set the outer bound) would implicitly resolve to a
186
+ * non-string: the extended
187
+ * boolean spellings (`y`/`yes`/`on`/`off` and case variants — the "Norway
188
+ * problem"), timestamps (`2024-01-15`, `2001-12-15T02:59:43.1Z`, the
189
+ * space-separated 1.1 forms), sexagesimal numbers (`1:30`,
190
+ * `190:20:30.15`), underscore-separated numbers (`1_000`) and the
191
+ * base-2/8/16 integer forms (`0b1010_0111`, `0777`, `0x_FF`). Use it when
192
+ * emitted output is parsed downstream by a YAML 1.1 consumer and blanket
193
+ * `defaultScalarStyle` quoting is too noisy.
194
+ *
195
+ * Strictly additive: it never un-quotes anything the 1.2 rules require
196
+ * quoted, strings no 1.1 parser coerces stay plain, and the quote
197
+ * character stays governed by `quoteStyle`. Scalars carrying an explicit
198
+ * tag are exempt, exactly like the 1.2 type-conflict check. Applies
199
+ * wherever the plain fallback renders — values, mapping keys, flow items —
200
+ * on both the value path ({@link Yaml.stringify},
201
+ * {@link Yaml.stringifyResult}) and the node path (`YamlDocument#stringify`
202
+ * and the `YamlFormat` helpers).
203
+ */
204
+ readonly quoteCompat: Schema.optionalKey<Schema.Literals<readonly ["yaml-1.1"]>>;
178
205
  readonly finalNewline: Schema.optionalKey<Schema.Boolean>;
179
206
  readonly forceDefaultStyles: Schema.optionalKey<Schema.Boolean>;
180
207
  }>, {}>;
@@ -182,8 +209,9 @@ declare const YamlStringifyOptions_base: Schema.Class<YamlStringifyOptions, Sche
182
209
  * Options controlling stringify behavior. All fields are omissible; absent
183
210
  * fields resolve to `indent` `2`, `lineWidth` `0`, `defaultScalarStyle`
184
211
  * `"plain"`, `defaultCollectionStyle` `"block"`, `sortKeys` `false`,
185
- * `indentSequences` `false`, `quoteStyle` `"single"`, `finalNewline` `true`
186
- * and `forceDefaultStyles` `false`.
212
+ * `indentSequences` `false`, `quoteStyle` `"single"`, `quoteCompat` absent
213
+ * (no dialect-compat quoting), `finalNewline` `true` and `forceDefaultStyles`
214
+ * `false`.
187
215
  *
188
216
  * `lineWidth` controls column-based scalar folding. The default `0` (and any
189
217
  * value `<= 0`) never wraps, emitting byte-identical output to the historic
@@ -641,6 +669,27 @@ declare const QuoteStyle: Schema.Literals<readonly ["single", "double"]>;
641
669
  * @public
642
670
  */
643
671
  type QuoteStyle = typeof QuoteStyle.Type;
672
+ /**
673
+ * Foreign resolution dialects the stringifier's plain-scalar fallback can
674
+ * defend against: setting the `quoteCompat` field of `YamlStringifyOptions`
675
+ * to `"yaml-1.1"` additionally quotes every plain scalar a YAML 1.1 parser
676
+ * (js-yaml, PyYAML, libyaml, and the `yaml` npm package's YAML 1.1 schema,
677
+ * whose lenient resolvers set the outer bound) would implicitly resolve to a
678
+ * non-string —
679
+ * `yes`/`no`/`on`/`off` booleans, ISO 8601 and space-separated timestamps,
680
+ * sexagesimal `1:30`, underscored `1_000` and base-2/8/16 numbers. Like
681
+ * `QuoteStyle` it is a stringify-option vocabulary, never a property of a
682
+ * composed node.
683
+ *
684
+ * @public
685
+ */
686
+ declare const QuoteCompat: Schema.Literals<readonly ["yaml-1.1"]>;
687
+ /**
688
+ * The union of all quote-compat dialect string literals.
689
+ *
690
+ * @public
691
+ */
692
+ type QuoteCompat = typeof QuoteCompat.Type;
644
693
  /**
645
694
  * Block-scalar chomping indicators (`-` strip, default clip, `+` keep).
646
695
  * Referenced by the {@link YamlScalar} `chomp` field schema.
@@ -1005,18 +1054,36 @@ declare const YamlFormattingOptions_base: Schema.Class<YamlFormattingOptions, Sc
1005
1054
  readonly sortKeys: Schema.optionalKey<Schema.Boolean>;
1006
1055
  readonly indentSequences: Schema.optionalKey<Schema.Boolean>;
1007
1056
  readonly quoteStyle: Schema.optionalKey<Schema.Literals<readonly ["single", "double"]>>;
1057
+ readonly quoteCompat: Schema.optionalKey<Schema.Literals<readonly ["yaml-1.1"]>>;
1008
1058
  readonly finalNewline: Schema.optionalKey<Schema.Boolean>;
1009
1059
  readonly forceDefaultStyles: Schema.optionalKey<Schema.Boolean>;
1010
1060
  readonly preserveComments: Schema.optionalKey<Schema.Boolean>;
1011
1061
  readonly range: Schema.optionalKey<typeof YamlRange>;
1062
+ readonly requoteScalars: Schema.optionalKey<Schema.Boolean>;
1012
1063
  }>, {}>;
1013
1064
  /**
1014
1065
  * Options controlling formatting behavior: every {@link YamlStringifyOptions}
1015
- * field (derived, not hand-duplicated — including `indentSequences` and
1016
- * `quoteStyle`) plus
1017
- * `preserveComments` (default `true`) and `range` (restrict edits to a
1066
+ * field (derived, not hand-duplicated — including `indentSequences`,
1067
+ * `quoteStyle` and `quoteCompat`) plus
1068
+ * `preserveComments` (default `true`), `range` (restrict edits to a
1018
1069
  * region; see the module-level remarks on the `range` parameter vs. this
1019
- * field).
1070
+ * field) and `requoteScalars` (default `false`).
1071
+ *
1072
+ * `requoteScalars` makes `quoteStyle` apply to scalars **already quoted in
1073
+ * the source** on the format path — by default formatting preserves an
1074
+ * existing scalar's own quote style, and `quoteStyle` governs only quotes the
1075
+ * stringifier introduces. When enabled, a re-quote happens only when it
1076
+ * provably preserves the parsed value: single→double applies proper
1077
+ * double-quote escaping, double→single is skipped whenever the value carries
1078
+ * characters single quotes cannot express (newlines, tabs, control and other
1079
+ * non-printable characters — single-quoted style can escape nothing but
1080
+ * `'`). Plain scalars stay plain, block scalars stay block, and scalars
1081
+ * carrying a tag or anchor — or spanning multiple source lines — are left
1082
+ * untouched. The option exists only where a source quote exists to re-quote:
1083
+ * it is read by {@link YamlFormat.format} / {@link YamlFormat.formatToString}
1084
+ * and is deliberately absent from `Yaml.stringify` (which serializes plain
1085
+ * values) and {@link YamlFormat.modify} (which takes a bare
1086
+ * {@link YamlStringifyOptions}).
1020
1087
  *
1021
1088
  * Construct with the validated `YamlFormattingOptions.make({ ... })` static —
1022
1089
  * the kit convention (never `new`). Call sites that take a
@@ -1122,6 +1189,12 @@ declare class YamlFormat {
1122
1189
  * `{ offset, length }` object as well as a {@link YamlRange} instance, so
1123
1190
  * callers do not need `YamlRange.make(...)` for the common case.
1124
1191
  *
1192
+ * Formatting preserves an existing scalar's own quote style by default —
1193
+ * `quoteStyle` governs only quotes the stringifier introduces. The opt-in
1194
+ * `options.requoteScalars` makes `quoteStyle` apply to already-quoted
1195
+ * source scalars too, re-quoting only where the parsed value is provably
1196
+ * preserved; see {@link YamlFormattingOptions} for the exact skip rules.
1197
+ *
1125
1198
  * A plain `<<` mapping key is preserved unquoted, keeping its merge-key
1126
1199
  * meaning (`tag:yaml.org,2002:merge`) — quoting it to `'<<'` would produce
1127
1200
  * an ordinary string key that merges nothing, changing what the document
@@ -1337,6 +1410,60 @@ interface LintContext {
1337
1410
  readonly tokens: ReadonlyArray<YamlToken>;
1338
1411
  readonly document: YamlDocument;
1339
1412
  }
1413
+ declare const StyleVote_base: Schema.Class<StyleVote, Schema.TaggedStruct<"StyleVote", {
1414
+ readonly dimension: Schema.String;
1415
+ readonly value: Schema.Union<readonly [Schema.String, Schema.Number, Schema.Boolean]>;
1416
+ readonly offset: Schema.Number;
1417
+ readonly length: Schema.Number;
1418
+ readonly line: Schema.Number;
1419
+ readonly character: Schema.Number;
1420
+ }>, {}>;
1421
+ /**
1422
+ * One categorical style observation (#345): a single occurrence of a style
1423
+ * choice in the source, voting a `value` for an inference `dimension`.
1424
+ *
1425
+ * The `dimension` IS the rule's option key and the `value` IS that option's
1426
+ * value (`"double"` for `quoteType`, `2` for `spaces`, `false` for
1427
+ * `present`), so the inference resolvers can turn unanimous or dominant
1428
+ * votes into a config entry with no per-rule knowledge — which is what lets
1429
+ * custom rules participate in inference for free. The position points at the
1430
+ * occurrence that voted, so a strict-resolution conflict can name where each
1431
+ * spelling was seen.
1432
+ *
1433
+ * The `_tag` literal is the RUNTIME discriminator between the two
1434
+ * observation kinds: class instances are structurally assignable, so a
1435
+ * custom rule may yield a plain object shaped like a vote, and the evidence
1436
+ * builder must still sort it into the right tally without `instanceof`.
1437
+ * `.make` defaults it — construction sites never pass `_tag`.
1438
+ *
1439
+ * @public
1440
+ */
1441
+ declare class StyleVote extends StyleVote_base {}
1442
+ declare const StyleFloor_base: Schema.Class<StyleFloor, Schema.TaggedStruct<"StyleFloor", {
1443
+ readonly dimension: Schema.String;
1444
+ readonly value: Schema.Number;
1445
+ }>, {}>;
1446
+ /**
1447
+ * One measured style floor (#345): a value the source PROVES is at least
1448
+ * `value`, without proving what the configured limit should be — the longest
1449
+ * observed line proves `line-length.max` is at least that long, not what it
1450
+ * is. Floors are carried in the evidence for callers that want them and are
1451
+ * never resolved into config options; fabricating a max from the largest
1452
+ * value one happened to see would be lying with a straight face.
1453
+ *
1454
+ * The `_tag` literal discriminates a floor from a {@link StyleVote} at
1455
+ * runtime (see there); `.make` defaults it.
1456
+ *
1457
+ * @public
1458
+ */
1459
+ declare class StyleFloor extends StyleFloor_base {}
1460
+ /**
1461
+ * What a rule's `infer` hook yields per occurrence: a categorical
1462
+ * {@link StyleVote} or a measured {@link StyleFloor}.
1463
+ *
1464
+ * @public
1465
+ */
1466
+ type StyleObservation = StyleVote | StyleFloor;
1340
1467
  /**
1341
1468
  * The public rule interface — built-ins and custom rules are the same
1342
1469
  * shape, and config references either by `id`; there is no privileged
@@ -1347,11 +1474,19 @@ interface LintContext {
1347
1474
  * rules receive options already validated against their exported options
1348
1475
  * schema, custom rules validate their own.
1349
1476
  *
1477
+ * `infer` is the optional config-inference hook (#345): it reports the style
1478
+ * the source already follows as per-occurrence {@link StyleObservation}s, so
1479
+ * detection logic lives beside the check logic that polices the same
1480
+ * dimension (and shares its fixtures). Rules with no detectable style — the
1481
+ * policy rules, whose only evidence is violations — simply omit the hook and
1482
+ * stay default-driven under every resolver.
1483
+ *
1350
1484
  * @public
1351
1485
  */
1352
1486
  interface YamlRule {
1353
1487
  readonly id: string;
1354
1488
  readonly check: (ctx: LintContext, options: unknown) => Iterable<YamlLintDiagnostic>;
1489
+ readonly infer?: (ctx: LintContext) => Iterable<StyleObservation>;
1355
1490
  }
1356
1491
  //#endregion
1357
1492
  //#region src/YamlLint.d.ts
@@ -1399,6 +1534,118 @@ declare class YamlLintConfig extends YamlLintConfig_base {
1399
1534
  /** The relaxed preset: style rules demoted to warnings. */
1400
1535
  static readonly relaxed: YamlLintConfig;
1401
1536
  }
1537
+ declare const StyleVoteTally_base: Schema.Class<StyleVoteTally, Schema.Struct<{
1538
+ readonly rule: Schema.String;
1539
+ readonly dimension: Schema.String;
1540
+ readonly value: Schema.Union<readonly [Schema.String, Schema.Number, Schema.Boolean]>;
1541
+ readonly count: Schema.Number;
1542
+ readonly offset: Schema.Number;
1543
+ readonly length: Schema.Number;
1544
+ readonly line: Schema.Number;
1545
+ readonly character: Schema.Number;
1546
+ }>, {}>;
1547
+ /**
1548
+ * An accumulated tally of one {@link StyleVote} spelling: how many times a
1549
+ * `value` was voted for a `(rule, dimension)` pair, and the position of the
1550
+ * FIRST occurrence seen (merging keeps the left operand's position, so on a
1551
+ * multi-file merge the first file that exhibited the spelling names it).
1552
+ *
1553
+ * @public
1554
+ */
1555
+ declare class StyleVoteTally extends StyleVoteTally_base {}
1556
+ declare const StyleFloorTally_base: Schema.Class<StyleFloorTally, Schema.Struct<{
1557
+ readonly rule: Schema.String;
1558
+ readonly dimension: Schema.String;
1559
+ readonly value: Schema.Number;
1560
+ }>, {}>;
1561
+ /**
1562
+ * An accumulated {@link StyleFloor} for a `(rule, dimension)` pair: the
1563
+ * largest value the observed sources prove the limit is AT LEAST. Merging
1564
+ * takes the maximum. Floors are informational — never resolved into config
1565
+ * options (see {@link StyleFloor}).
1566
+ *
1567
+ * @public
1568
+ */
1569
+ declare class StyleFloorTally extends StyleFloorTally_base {}
1570
+ declare const StyleEvidence_base: Schema.Class<StyleEvidence, Schema.Struct<{
1571
+ readonly votes: Schema.$Array<typeof StyleVoteTally>;
1572
+ readonly floors: Schema.$Array<typeof StyleFloorTally>;
1573
+ }>, {}>;
1574
+ /**
1575
+ * Per-dimension style evidence (#345): what the observed sources say about
1576
+ * each inferable `(rule, dimension)` — vote histograms with first-seen
1577
+ * positions, and measured floors.
1578
+ *
1579
+ * Evidence is a MONOID: {@link StyleEvidence.empty} is the identity and
1580
+ * {@link StyleEvidence.combine} is associative, so multi-file inference is
1581
+ * observe-per-file, merge, resolve — and the N-file loop stays the caller's
1582
+ * (no IO enters the package). `combine` normalizes: counts of the same
1583
+ * `(rule, dimension, value)` add, the left operand's first-seen position
1584
+ * wins, floors take the maximum, and the result is canonically sorted — so
1585
+ * every value `observe`/`combine`/`fromObservations` produces is canonical
1586
+ * and the monoid laws hold structurally over them.
1587
+ *
1588
+ * @public
1589
+ */
1590
+ declare class StyleEvidence extends StyleEvidence_base {
1591
+ /** The monoid identity: no observations. */
1592
+ static readonly empty: StyleEvidence;
1593
+ /**
1594
+ * Merge two bodies of evidence (associative; {@link StyleEvidence.empty}
1595
+ * is the identity). Vote counts add, the left operand's first-seen
1596
+ * position wins per spelling, floors take the maximum.
1597
+ */
1598
+ static combine(a: StyleEvidence, b: StyleEvidence): StyleEvidence;
1599
+ /**
1600
+ * Build canonical evidence from one rule's raw observations — the
1601
+ * homomorphism from observation lists into the monoid
1602
+ * (`fromObservations(r, [...a, ...b])` equals
1603
+ * `combine(fromObservations(r, a), fromObservations(r, b))`). `observe`
1604
+ * uses it per rule; it is public so custom tooling can construct evidence
1605
+ * without a {@link LintContext}.
1606
+ */
1607
+ static fromObservations(rule: string, observations: Iterable<StyleObservation>): StyleEvidence;
1608
+ }
1609
+ declare const StyleConflict_base: Schema.Class<StyleConflict, Schema.Struct<{
1610
+ readonly rule: Schema.String;
1611
+ readonly dimension: Schema.String;
1612
+ readonly candidates: Schema.$Array<typeof StyleVoteTally>;
1613
+ }>, {}>;
1614
+ /**
1615
+ * One strict-resolution conflict: a `(rule, dimension)` whose observed
1616
+ * spellings disagree. `candidates` carries every spelling with its count and
1617
+ * first-seen position, ordered by count descending (dominant first).
1618
+ *
1619
+ * @public
1620
+ */
1621
+ declare class StyleConflict extends StyleConflict_base {}
1622
+ declare const YamlStyleConflictError_base: Schema.Class<YamlStyleConflictError, Schema.TaggedStruct<"YamlStyleConflictError", {
1623
+ readonly conflicts: Schema.$Array<typeof StyleConflict>;
1624
+ }>, import("effect/Cause").YieldableError>;
1625
+ /**
1626
+ * Raised by strict config inference when observed evidence is not unanimous:
1627
+ * carries every conflicting `(rule, dimension)` as a structured
1628
+ * {@link StyleConflict} — dimension, all spellings, counts and positions —
1629
+ * never a collapsed `reason` string (the structure-preserving-errors house
1630
+ * rule). Unobserved dimensions never conflict: they fall back to the base
1631
+ * config's defaults.
1632
+ *
1633
+ * @public
1634
+ */
1635
+ declare class YamlStyleConflictError extends YamlStyleConflictError_base {
1636
+ get message(): string;
1637
+ }
1638
+ /**
1639
+ * A lenient inference report: the inferred config plus the residual — the
1640
+ * diagnostics that config still produces on the observed text ("here is your
1641
+ * config, and the places that do not match it").
1642
+ *
1643
+ * @public
1644
+ */
1645
+ interface YamlLintInference {
1646
+ readonly config: YamlLintConfig;
1647
+ readonly residual: ReadonlyArray<YamlLintDiagnostic>;
1648
+ }
1402
1649
  /**
1403
1650
  * Linting statics. Not instantiable.
1404
1651
  *
@@ -1447,6 +1694,52 @@ declare class YamlLint {
1447
1694
  * is dropped (its diagnostic remains reported by {@link YamlLint.run}).
1448
1695
  */
1449
1696
  static fix(text: string, rules: ReadonlyArray<YamlRule>, config: YamlLintConfig): Result.Result<string, YamlParseError>;
1697
+ /**
1698
+ * Observe the style `text` already follows (#345): run every rule's
1699
+ * optional `infer` hook over one eagerly-built context and merge the
1700
+ * observations into canonical {@link StyleEvidence}. Pure and total —
1701
+ * strings in, evidence out; observing N files is N `observe` calls merged
1702
+ * with {@link StyleEvidence.combine}, and the N-file loop stays the
1703
+ * caller's (no IO enters the package).
1704
+ */
1705
+ static observe(text: string, rules: ReadonlyArray<YamlRule>): StyleEvidence;
1706
+ /**
1707
+ * Strict resolution: every OBSERVED dimension must be unanimous, and the
1708
+ * unanimous picks overlay `base` (default {@link YamlLintConfig.default})
1709
+ * into an exact config. Conflicting evidence fails with
1710
+ * {@link YamlStyleConflictError} naming every conflicting dimension, all
1711
+ * spellings, counts and first-seen positions. Unobserved is NOT
1712
+ * conflicting: a corpus with no comments says nothing about
1713
+ * `comments-spacing`, so that dimension falls back to `base` rather than
1714
+ * failing. A rule `base` sets to `"off"` stays off — an explicit disable
1715
+ * outranks inference.
1716
+ */
1717
+ static resolveStrict(evidence: StyleEvidence, base?: YamlLintConfig): Result.Result<YamlLintConfig, YamlStyleConflictError>;
1718
+ /**
1719
+ * Lenient resolution: the dominant (plurality) spelling per observed
1720
+ * dimension overlays `base` (default {@link YamlLintConfig.default}); a
1721
+ * tie breaks deterministically to the first candidate in canonical
1722
+ * value order. Total — lenient resolution cannot fail; the places that do
1723
+ * not match the inferred config surface as the residual report (run the
1724
+ * lint with the inferred config, or use {@link YamlLint.inferLenient}).
1725
+ */
1726
+ static resolveLenient(evidence: StyleEvidence, base?: YamlLintConfig): YamlLintConfig;
1727
+ /**
1728
+ * Single-text strict inference: `observe` then {@link YamlLint.resolveStrict}
1729
+ * in one step. Multi-file callers observe each file and merge with
1730
+ * {@link StyleEvidence.combine} before resolving.
1731
+ */
1732
+ static inferStrict(text: string, rules: ReadonlyArray<YamlRule>, base?: YamlLintConfig): Result.Result<YamlLintConfig, YamlStyleConflictError>;
1733
+ /**
1734
+ * Single-text lenient inference: `observe`, resolve dominant picks over
1735
+ * `base`, then run the lint with the inferred config — returning the
1736
+ * config AND the residual diagnostics it still produces ("here is your
1737
+ * config, and the places that do not match it"). One context serves both
1738
+ * the observation and the residual run. Multi-file callers compose the
1739
+ * primitives instead: observe each file, {@link StyleEvidence.combine},
1740
+ * {@link YamlLint.resolveLenient}, then {@link YamlLint.run} per file.
1741
+ */
1742
+ static inferLenient(text: string, rules: ReadonlyArray<YamlRule>, base?: YamlLintConfig): YamlLintInference;
1450
1743
  }
1451
1744
  //#endregion
1452
1745
  //#region src/YamlVisitor.d.ts
@@ -2051,5 +2344,5 @@ declare class YamlVisitor {
2051
2344
  static visit(text: string, options?: YamlParseOptions): Stream.Stream<YamlVisitorEvent>;
2052
2345
  }
2053
2346
  //#endregion
2054
- export { CollectionStyle, type LintContext, type LintLine, QuoteStyle, ScalarChomp, ScalarStyle, Yaml, YamlAlias, type YamlAliasEncoded, type YamlBoundCodec, YamlComposerErrorCode, YamlDiagnostic, YamlDirective, YamlDocument, YamlEdit, YamlErrorCode, YamlFormat, YamlFormattingOptions, YamlLexErrorCode, YamlLint, YamlLintConfig, YamlLintDiagnostic, YamlLintRuleSetting, YamlLintSeverity, YamlMap, type YamlMapEncoded, YamlModificationError, YamlModifyErrorCode, YamlNode, YamlPair, YamlParseError, YamlParseErrorCode, YamlParseOptions, type YamlPath, YamlRange, type YamlRangeLike, type YamlRule, YamlScalar, type YamlScalarEncoded, type YamlSegment, YamlSeq, type YamlSeqEncoded, YamlStringifyError, YamlStringifyErrorCode, YamlStringifyOptions, YamlToken, YamlTokenKind, YamlTokens, YamlVisitor, YamlVisitorEvent };
2347
+ export { CollectionStyle, type LintContext, type LintLine, QuoteCompat, QuoteStyle, ScalarChomp, ScalarStyle, StyleConflict, StyleEvidence, StyleFloor, StyleFloorTally, type StyleObservation, StyleVote, StyleVoteTally, Yaml, YamlAlias, type YamlAliasEncoded, type YamlBoundCodec, YamlComposerErrorCode, YamlDiagnostic, YamlDirective, YamlDocument, YamlEdit, YamlErrorCode, YamlFormat, YamlFormattingOptions, YamlLexErrorCode, YamlLint, YamlLintConfig, YamlLintDiagnostic, type YamlLintInference, YamlLintRuleSetting, YamlLintSeverity, YamlMap, type YamlMapEncoded, YamlModificationError, YamlModifyErrorCode, YamlNode, YamlPair, YamlParseError, YamlParseErrorCode, YamlParseOptions, type YamlPath, YamlRange, type YamlRangeLike, type YamlRule, YamlScalar, type YamlScalarEncoded, type YamlSegment, YamlSeq, type YamlSeqEncoded, YamlStringifyError, YamlStringifyErrorCode, YamlStringifyOptions, YamlStyleConflictError, YamlToken, YamlTokenKind, YamlTokens, YamlVisitor, YamlVisitorEvent };
2055
2348
  //# sourceMappingURL=index.d.ts.map
package/index.js CHANGED
@@ -1,12 +1,12 @@
1
- import { CollectionStyle, QuoteStyle, ScalarChomp, ScalarStyle, YamlAlias, YamlMap, YamlNode, YamlPair, YamlScalar, YamlSeq } from "./YamlNode.js";
1
+ import { CollectionStyle, QuoteCompat, QuoteStyle, ScalarChomp, ScalarStyle, YamlAlias, YamlMap, YamlNode, YamlPair, YamlScalar, YamlSeq } from "./YamlNode.js";
2
2
  import { YamlComposerErrorCode, YamlDiagnostic, YamlErrorCode, YamlLexErrorCode, YamlModifyErrorCode, YamlParseErrorCode, YamlStringifyErrorCode } from "./YamlDiagnostic.js";
3
3
  import { Yaml, YamlParseError, YamlParseOptions, YamlStringifyError, YamlStringifyOptions } from "./Yaml.js";
4
4
  import { YamlDirective, YamlDocument } from "./YamlDocument.js";
5
5
  import { YamlEdit, YamlRange } from "./YamlEdit.js";
6
6
  import { YamlFormat, YamlFormattingOptions, YamlModificationError } from "./YamlFormat.js";
7
- import { YamlLintDiagnostic, YamlLintSeverity } from "./YamlLintRule.js";
7
+ import { StyleFloor, StyleVote, YamlLintDiagnostic, YamlLintSeverity } from "./YamlLintRule.js";
8
8
  import { YamlToken, YamlTokenKind, YamlTokens } from "./YamlToken.js";
9
- import { YamlLint, YamlLintConfig, YamlLintRuleSetting } from "./YamlLint.js";
9
+ import { StyleConflict, StyleEvidence, StyleFloorTally, StyleVoteTally, YamlLint, YamlLintConfig, YamlLintRuleSetting, YamlStyleConflictError } from "./YamlLint.js";
10
10
  import { YamlVisitor, YamlVisitorEvent } from "./YamlVisitor.js";
11
11
 
12
- export { CollectionStyle, QuoteStyle, ScalarChomp, ScalarStyle, Yaml, YamlAlias, YamlComposerErrorCode, YamlDiagnostic, YamlDirective, YamlDocument, YamlEdit, YamlErrorCode, YamlFormat, YamlFormattingOptions, YamlLexErrorCode, YamlLint, YamlLintConfig, YamlLintDiagnostic, YamlLintRuleSetting, YamlLintSeverity, YamlMap, YamlModificationError, YamlModifyErrorCode, YamlNode, YamlPair, YamlParseError, YamlParseErrorCode, YamlParseOptions, YamlRange, YamlScalar, YamlSeq, YamlStringifyError, YamlStringifyErrorCode, YamlStringifyOptions, YamlToken, YamlTokenKind, YamlTokens, YamlVisitor, YamlVisitorEvent };
12
+ export { CollectionStyle, QuoteCompat, QuoteStyle, ScalarChomp, ScalarStyle, StyleConflict, StyleEvidence, StyleFloor, StyleFloorTally, StyleVote, StyleVoteTally, Yaml, YamlAlias, YamlComposerErrorCode, YamlDiagnostic, YamlDirective, YamlDocument, YamlEdit, YamlErrorCode, YamlFormat, YamlFormattingOptions, YamlLexErrorCode, YamlLint, YamlLintConfig, YamlLintDiagnostic, YamlLintRuleSetting, YamlLintSeverity, YamlMap, YamlModificationError, YamlModifyErrorCode, YamlNode, YamlPair, YamlParseError, YamlParseErrorCode, YamlParseOptions, YamlRange, YamlScalar, YamlSeq, YamlStringifyError, YamlStringifyErrorCode, YamlStringifyOptions, YamlStyleConflictError, YamlToken, YamlTokenKind, YamlTokens, YamlVisitor, YamlVisitorEvent };
@@ -0,0 +1,48 @@
1
+ import { isControlChar } from "./fold.js";
2
+ import { renderDoubleQuoted, renderSingleQuoted } from "./stringifier.js";
3
+
4
+ //#region src/internal/requote.ts
5
+ /**
6
+ * True when `value` can be carried by a single-quoted flow scalar on one
7
+ * line. Single-quoted style has exactly one escape (`''`), so any character
8
+ * double-quote escape sequences exist to encode — newline, carriage return,
9
+ * tab, C0 controls, DEL and the C1 range — has no single-quoted spelling and
10
+ * makes the conversion impossible rather than lossy-but-tempting.
11
+ */
12
+ function isSingleQuotable(value) {
13
+ for (let i = 0; i < value.length; i++) {
14
+ const code = value.charCodeAt(i);
15
+ if (code === 9 || code === 10 || code === 13) return false;
16
+ if (isControlChar(code)) return false;
17
+ if (code === 127 || code >= 128 && code <= 159) return false;
18
+ }
19
+ return true;
20
+ }
21
+ /**
22
+ * The replacement raw text that re-quotes `scalar` with `quote`, or
23
+ * `undefined` when no value-preserving replacement exists under `mode`
24
+ * (skipping is always correct; corrupting never is).
25
+ */
26
+ function requoteScalarText(text, scalar, quote, mode) {
27
+ if (scalar.tag !== void 0 || scalar.anchor !== void 0) return void 0;
28
+ if (typeof scalar.value !== "string") return void 0;
29
+ const raw = text.slice(scalar.offset, scalar.offset + scalar.length);
30
+ if (raw.includes("\n") || raw.includes("\r")) return void 0;
31
+ if (mode === "conservative") {
32
+ const inner = scalar.style === "plain" ? raw : raw.slice(1, -1);
33
+ if (inner !== scalar.value) return void 0;
34
+ if (inner.includes(quote)) return void 0;
35
+ if (quote === "\"" && inner.includes("\\")) return void 0;
36
+ return `${quote}${inner}${quote}`;
37
+ }
38
+ if (quote === "\"") {
39
+ if (scalar.style !== "single-quoted") return void 0;
40
+ return renderDoubleQuoted(scalar.value);
41
+ }
42
+ if (scalar.style !== "double-quoted") return void 0;
43
+ if (!isSingleQuotable(scalar.value)) return void 0;
44
+ return renderSingleQuoted(scalar.value);
45
+ }
46
+
47
+ //#endregion
48
+ export { requoteScalarText };
@@ -1,5 +1,5 @@
1
1
  import { YamlEdit } from "../../YamlEdit.js";
2
- import { YamlLintDiagnostic, YamlLintSeverity } from "../../YamlLintRule.js";
2
+ import { StyleVote, YamlLintDiagnostic, YamlLintSeverity } from "../../YamlLintRule.js";
3
3
  import { nonNegativeIntegerOption } from "./util.js";
4
4
  import { Schema } from "effect";
5
5
 
@@ -14,6 +14,25 @@ const commentsSpacingOptions = Schema.Struct({
14
14
  minSpacesBefore: Schema.optionalKey(nonNegativeIntegerOption),
15
15
  requireSpaceAfter: Schema.optionalKey(Schema.Boolean)
16
16
  });
17
+ /**
18
+ * The horizontal whitespace run directly before a comment token, and whether
19
+ * line content precedes it (a TRAILING comment) — shared by the check and
20
+ * the inference hook so the two cannot disagree about what "before" means.
21
+ */
22
+ const spacingBefore = (ctx, token) => {
23
+ let i = token.offset - 1;
24
+ let spaces = 0;
25
+ while (i >= 0 && (ctx.text[i] === " " || ctx.text[i] === " ")) {
26
+ spaces++;
27
+ i--;
28
+ }
29
+ return {
30
+ spaces,
31
+ hasContentBefore: i >= 0 && ctx.text[i] !== "\n" && ctx.text[i] !== "\r"
32
+ };
33
+ };
34
+ /** Shebang exemption: `#!` at the very start of the stream. */
35
+ const isShebang = (token) => token.offset === 0 && token.text.startsWith("#!");
17
36
  /** Comment spacing: after the `#`, and before a trailing comment's `#`. */
18
37
  const commentsSpacing = {
19
38
  id: "comments-spacing",
@@ -24,7 +43,7 @@ const commentsSpacing = {
24
43
  const out = [];
25
44
  for (const token of ctx.tokens) {
26
45
  if (token.kind !== "comment") continue;
27
- if (token.offset === 0 && token.text.startsWith("#!")) continue;
46
+ if (isShebang(token)) continue;
28
47
  if (requireAfter && token.text.length > 1 && !/[ \t]/.test(token.text[1])) out.push(new YamlLintDiagnostic({
29
48
  rule: "comments-spacing",
30
49
  severity: "error",
@@ -39,13 +58,8 @@ const commentsSpacing = {
39
58
  content: " "
40
59
  })
41
60
  }));
42
- let i = token.offset - 1;
43
- let spaces = 0;
44
- while (i >= 0 && (ctx.text[i] === " " || ctx.text[i] === " ")) {
45
- spaces++;
46
- i--;
47
- }
48
- if (i >= 0 && ctx.text[i] !== "\n" && ctx.text[i] !== "\r" && spaces < minBefore) out.push(new YamlLintDiagnostic({
61
+ const { spaces, hasContentBefore } = spacingBefore(ctx, token);
62
+ if (hasContentBefore && spaces < minBefore) out.push(new YamlLintDiagnostic({
49
63
  rule: "comments-spacing",
50
64
  severity: "error",
51
65
  message: `Too few spaces before comment (${spaces} < ${minBefore})`,
@@ -61,6 +75,31 @@ const commentsSpacing = {
61
75
  }));
62
76
  }
63
77
  return out;
78
+ },
79
+ infer: (ctx) => {
80
+ const out = [];
81
+ for (const token of ctx.tokens) {
82
+ if (token.kind !== "comment") continue;
83
+ if (isShebang(token)) continue;
84
+ if (token.text.length > 1) out.push(StyleVote.make({
85
+ dimension: "requireSpaceAfter",
86
+ value: /[ \t]/.test(token.text[1]),
87
+ offset: token.offset,
88
+ length: token.length,
89
+ line: token.line,
90
+ character: token.character
91
+ }));
92
+ const { spaces, hasContentBefore } = spacingBefore(ctx, token);
93
+ if (hasContentBefore) out.push(StyleVote.make({
94
+ dimension: "minSpacesBefore",
95
+ value: spaces,
96
+ offset: token.offset - spaces,
97
+ length: spaces,
98
+ line: token.line,
99
+ character: token.character - spaces
100
+ }));
101
+ }
102
+ return out;
64
103
  }
65
104
  };
66
105
 
@@ -1,5 +1,5 @@
1
1
  import { YamlEdit } from "../../YamlEdit.js";
2
- import { YamlLintDiagnostic, YamlLintSeverity } from "../../YamlLintRule.js";
2
+ import { StyleVote, YamlLintDiagnostic, YamlLintSeverity } from "../../YamlLintRule.js";
3
3
  import { Schema } from "effect";
4
4
 
5
5
  //#region src/internal/rules/document-end.ts
@@ -14,31 +14,43 @@ const TRIVIA = /* @__PURE__ */ new Set([
14
14
  "comment",
15
15
  "byte-order-mark"
16
16
  ]);
17
+ /** The last non-trivia token: it decides whether the stream ends with `...`. */
18
+ const tailToken = (ctx) => [...ctx.tokens].reverse().find((t) => !TRIVIA.has(t.kind));
19
+ /**
20
+ * The end-of-stream position, shared by the missing-marker diagnostic and
21
+ * the absent-marker vote so the two cannot drift apart. `line`/`character`
22
+ * must agree with `offset` (= text length): when the text ends with a
23
+ * newline, that offset sits at the head of the line AFTER the last content
24
+ * line `buildLines` kept.
25
+ */
26
+ const endOfStreamPosition = (ctx) => {
27
+ const lastLine = ctx.lines[ctx.lines.length - 1];
28
+ const trailingNewline = ctx.text.endsWith("\n");
29
+ return {
30
+ offset: ctx.text.length,
31
+ length: 0,
32
+ line: (lastLine?.number ?? 0) + (trailingNewline ? 1 : 0),
33
+ character: trailingNewline ? 0 : lastLine?.text.length ?? 0
34
+ };
35
+ };
17
36
  /** The `...` marker at the tail of the stream. */
18
37
  const documentEnd = {
19
38
  id: "document-end",
20
39
  check: (ctx, options) => {
21
40
  const present = options?.present ?? true;
22
- const tail = [...ctx.tokens].reverse().find((t) => !TRIVIA.has(t.kind));
41
+ const tail = tailToken(ctx);
23
42
  const ended = tail?.kind === "document-end";
24
- if (present && !ended && ctx.text.trim() !== "") {
25
- const lastLine = ctx.lines[ctx.lines.length - 1];
26
- const trailingNewline = ctx.text.endsWith("\n");
27
- return [new YamlLintDiagnostic({
28
- rule: "document-end",
29
- severity: "error",
30
- message: "Missing \"...\" document end marker",
43
+ if (present && !ended && ctx.text.trim() !== "") return [new YamlLintDiagnostic({
44
+ rule: "document-end",
45
+ severity: "error",
46
+ message: "Missing \"...\" document end marker",
47
+ ...endOfStreamPosition(ctx),
48
+ fix: YamlEdit.make({
31
49
  offset: ctx.text.length,
32
50
  length: 0,
33
- line: (lastLine?.number ?? 0) + (trailingNewline ? 1 : 0),
34
- character: trailingNewline ? 0 : lastLine?.text.length ?? 0,
35
- fix: YamlEdit.make({
36
- offset: ctx.text.length,
37
- length: 0,
38
- content: ctx.text.endsWith("\n") ? "...\n" : "\n...\n"
39
- })
40
- })];
41
- }
51
+ content: ctx.text.endsWith("\n") ? "...\n" : "\n...\n"
52
+ })
53
+ })];
42
54
  if (!present && tail !== void 0 && tail.kind === "document-end") {
43
55
  const alone = (ctx.lines[tail.line]?.text ?? "").trim() === "...";
44
56
  const terminator = ctx.text.startsWith("\r\n", tail.offset + tail.length) ? 2 : 1;
@@ -58,6 +70,24 @@ const documentEnd = {
58
70
  })];
59
71
  }
60
72
  return [];
73
+ },
74
+ infer: (ctx) => {
75
+ if (ctx.text.trim() === "") return [];
76
+ const tail = tailToken(ctx);
77
+ if (tail === void 0) return [];
78
+ if (tail.kind === "document-end") return [StyleVote.make({
79
+ dimension: "present",
80
+ value: true,
81
+ offset: tail.offset,
82
+ length: tail.length,
83
+ line: tail.line,
84
+ character: tail.character
85
+ })];
86
+ return [StyleVote.make({
87
+ dimension: "present",
88
+ value: false,
89
+ ...endOfStreamPosition(ctx)
90
+ })];
61
91
  }
62
92
  };
63
93