polytypo 1.6.2 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/README.md +27 -0
  2. package/dist/{chunk-CW3GAIYQ.js → chunk-25XZKUNP.js} +1 -1
  3. package/dist/{chunk-CW3GAIYQ.js.map → chunk-25XZKUNP.js.map} +1 -1
  4. package/dist/chunk-33SLJJSZ.cjs +33 -0
  5. package/dist/{chunk-7SX4K4MI.cjs.map → chunk-33SLJJSZ.cjs.map} +1 -1
  6. package/dist/{chunk-J32K2ENU.cjs → chunk-54TSHDNC.cjs} +68 -20
  7. package/dist/chunk-54TSHDNC.cjs.map +1 -0
  8. package/dist/chunk-74I62TZ2.js +35 -0
  9. package/dist/chunk-74I62TZ2.js.map +1 -0
  10. package/dist/{chunk-HBAWMBV7.js → chunk-DAIPI5CX.js} +2 -2
  11. package/dist/{chunk-T6DOGDDS.cjs → chunk-FMU74VA6.cjs} +1 -1
  12. package/dist/{chunk-T6DOGDDS.cjs.map → chunk-FMU74VA6.cjs.map} +1 -1
  13. package/dist/{chunk-TIYWNQJT.js → chunk-GUTUSN4K.js} +4 -4
  14. package/dist/{chunk-V5MA5FVX.js → chunk-HBIIWBB5.js} +23 -27
  15. package/dist/chunk-HBIIWBB5.js.map +1 -0
  16. package/dist/chunk-HTCGXDEG.cjs +31 -0
  17. package/dist/{chunk-F2TYEIR6.cjs.map → chunk-HTCGXDEG.cjs.map} +1 -1
  18. package/dist/{chunk-XRBVOVKA.cjs → chunk-I2QPXPHX.cjs} +3 -3
  19. package/dist/{chunk-XRBVOVKA.cjs.map → chunk-I2QPXPHX.cjs.map} +1 -1
  20. package/dist/{chunk-XO74OCDD.cjs → chunk-IQHYZG2K.cjs} +23 -27
  21. package/dist/chunk-IQHYZG2K.cjs.map +1 -0
  22. package/dist/{chunk-YF4HZDOF.js → chunk-ITDTGCLJ.js} +2 -2
  23. package/dist/{chunk-XSQWMQXM.js → chunk-JE2MK4CC.js} +59 -11
  24. package/dist/chunk-JE2MK4CC.js.map +1 -0
  25. package/dist/{chunk-EU6OXPQ4.js → chunk-JVBK3LG3.js} +2 -2
  26. package/dist/{chunk-XKLNVHLQ.cjs → chunk-MMXKQUAS.cjs} +4 -4
  27. package/dist/{chunk-XKLNVHLQ.cjs.map → chunk-MMXKQUAS.cjs.map} +1 -1
  28. package/dist/{chunk-UNG66DF3.js → chunk-U5Y7C2L4.js} +21 -8
  29. package/dist/{chunk-UNG66DF3.js.map → chunk-U5Y7C2L4.js.map} +1 -1
  30. package/dist/{chunk-XEWXDNYN.cjs → chunk-VDLHT5CW.cjs} +35 -22
  31. package/dist/chunk-VDLHT5CW.cjs.map +1 -0
  32. package/dist/chunk-X6CI7GWV.cjs +35 -0
  33. package/dist/chunk-X6CI7GWV.cjs.map +1 -0
  34. package/dist/{errors-C1_sHGgx.d.cts → errors-DeHxbXRn.d.cts} +8 -0
  35. package/dist/{errors-C1_sHGgx.d.ts → errors-DeHxbXRn.d.ts} +8 -0
  36. package/dist/html.cjs +10 -10
  37. package/dist/html.cjs.map +1 -1
  38. package/dist/html.d.cts +3 -3
  39. package/dist/html.d.ts +3 -3
  40. package/dist/html.js +5 -5
  41. package/dist/html.js.map +1 -1
  42. package/dist/index.cjs +18 -17
  43. package/dist/index.cjs.map +1 -1
  44. package/dist/index.d.cts +2 -2
  45. package/dist/index.d.ts +2 -2
  46. package/dist/index.js +8 -7
  47. package/dist/index.js.map +1 -1
  48. package/dist/markdown.cjs +11 -10
  49. package/dist/markdown.cjs.map +1 -1
  50. package/dist/markdown.d.cts +2 -2
  51. package/dist/markdown.d.ts +2 -2
  52. package/dist/markdown.js +6 -5
  53. package/dist/markdown.js.map +1 -1
  54. package/dist/text.cjs +8 -8
  55. package/dist/text.cjs.map +1 -1
  56. package/dist/text.d.cts +3 -3
  57. package/dist/text.d.ts +3 -3
  58. package/dist/text.js +3 -3
  59. package/dist/text.js.map +1 -1
  60. package/dist/yaml.cjs +10 -9
  61. package/dist/yaml.cjs.map +1 -1
  62. package/dist/yaml.d.cts +3 -3
  63. package/dist/yaml.d.ts +3 -3
  64. package/dist/yaml.js +5 -4
  65. package/dist/yaml.js.map +1 -1
  66. package/package.json +1 -1
  67. package/dist/chunk-7SX4K4MI.cjs +0 -33
  68. package/dist/chunk-F2TYEIR6.cjs +0 -31
  69. package/dist/chunk-J32K2ENU.cjs.map +0 -1
  70. package/dist/chunk-V5MA5FVX.js.map +0 -1
  71. package/dist/chunk-XEWXDNYN.cjs.map +0 -1
  72. package/dist/chunk-XO74OCDD.cjs.map +0 -1
  73. package/dist/chunk-XSQWMQXM.js.map +0 -1
  74. /package/dist/{chunk-HBAWMBV7.js.map → chunk-DAIPI5CX.js.map} +0 -0
  75. /package/dist/{chunk-TIYWNQJT.js.map → chunk-GUTUSN4K.js.map} +0 -0
  76. /package/dist/{chunk-YF4HZDOF.js.map → chunk-ITDTGCLJ.js.map} +0 -0
  77. /package/dist/{chunk-EU6OXPQ4.js.map → chunk-JVBK3LG3.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/polytypo-js/polytypo-js/dist/chunk-XKLNVHLQ.cjs","../src/modes/html.ts","../src/modes/parse-error.ts"],"names":[],"mappings":"AAAA;AACE;AACF,wDAA6B;AAC7B;AACA;ACJA,gCAAqC;ADMrC;AACA;AEAA,SAAS,QAAA,CAAS,KAAA,EAAwB;AACxC,EAAA,GAAA,CAAI,OAAO,MAAA,IAAU,SAAA,GAAY,MAAA,IAAU,IAAA,EAAM,OAAO,MAAA,CAAO,KAAK,CAAA;AACpE,EAAA,MAAM,WAAA,EAAa,KAAA;AACnB,EAAA,MAAM,OAAA,EACJ,OAAO,UAAA,CAAW,OAAA,IAAW,SAAA,EAAW,UAAA,CAAW,OAAA,EAAU,KAAA,CAAgB,OAAA;AAC/E,EAAA,MAAM,MAAA,EAAQ,UAAA,CAAW,KAAA;AACzB,EAAA,GAAA,CACE,MAAA,IAAU,KAAA,GACV,MAAA,IAAU,KAAA,EAAA,GACV,OAAO,KAAA,CAAM,KAAA,IAAS,SAAA,GACtB,OAAO,KAAA,CAAM,OAAA,IAAW,QAAA,EACxB;AACA,IAAA,OAAO,CAAA,EAAA;AACT,EAAA;AACO,EAAA;AACT;AAcgB;AACV,EAAA;AACK,IAAA;AACA,EAAA;AACH,IAAA;AACE,IAAA;AACJ,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AACF;AFjBY;AACA;ACfN;AACJ,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACD;AAEe;AACP,EAAA;AACT;AAEM;AACA;AACO;AACP;AACA;AAGA;AAEG;AACA,EAAA;AACT;AAES;AACA,EAAA;AACT;AAES;AACA,EAAA;AACT;AAQS;AACH,EAAA;AACI,EAAA;AACC,EAAA;AAEL,EAAA;AACG,IAAA;AACC,IAAA;AACA,IAAA;AACG,IAAA;AACH,IAAA;AAEJ,IAAA;AAGK,MAAA;AACP,IAAA;AACI,IAAA;AACG,IAAA;AACT,EAAA;AAEM,EAAA;AAEJ,EAAA;AAIK,IAAA;AACP,EAAA;AACU,EAAA;AACH,EAAA;AACT;AAOgB;AACR,EAAA;AACF,EAAA;AACI,EAAA;AACD,EAAA;AACD,IAAA;AACI,MAAA;AACF,MAAA;AACE,QAAA;AACJ,QAAA;AACI,QAAA;AACJ,QAAA;AACF,MAAA;AACF,IAAA;AACK,IAAA;AACP,EAAA;AACU,EAAA;AACH,EAAA;AACT;AAES;AACA,EAAA;AACT;AAES;AACE,EAAA;AACD,IAAA;AACF,IAAA;AACJ,IAAA;AACQ,MAAA;AACR,IAAA;AACA,IAAA;AACF,EAAA;AAGS,EAAA;AAEL,EAAA;AAGK,EAAA;AACC,IAAA;AACR,IAAA;AACF,EAAA;AAEK,EAAA;AACL,EAAA;AACF;AAQgB;AACR,EAAA;AACA,EAAA;AACE,EAAA;AACD,EAAA;AACT;AAGgB;AACR,EAAA;AAA4B,IAAA;AAChC,IAAA;AACF,EAAA;AACM,EAAA;AACE,EAAA;AACD,EAAA;AAGT;AD9BY;AACA;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/polytypo-js/polytypo-js/dist/chunk-XKLNVHLQ.cjs","sourcesContent":[null,"import { parse, parseFragment } from \"parse5\";\nimport type { DefaultTreeAdapterMap } from \"parse5\";\nimport { wrapParserErrors } from \"./parse-error.js\";\nimport type { Span } from \"./spans.js\";\n\ntype Node = DefaultTreeAdapterMap[\"node\"];\ntype ParentNode = DefaultTreeAdapterMap[\"parentNode\"];\n\n/**\n * spec/rules/modes.md 3.6, exhaustive and **closed**: extending it is a spec change, not an\n * implementation decision. `svg` and `math` are here because in MathML a quotation mark, a\n * hyphen and a prime are operators and identifiers — substituting a curly glyph changes what\n * the expression means. Everything not listed is processable, **including unknown and custom\n * elements**: guessing from a tag name is exactly the heuristic that diverges across runtimes.\n */\nconst SKIPPED_ELEMENTS: ReadonlySet<string> = new Set([\n \"code\",\n \"pre\",\n \"kbd\",\n \"samp\",\n \"var\",\n \"script\",\n \"style\",\n \"textarea\",\n \"svg\",\n \"math\",\n]);\n\nexport function isSkippedElement(tagName: string): boolean {\n return SKIPPED_ELEMENTS.has(tagName);\n}\n\nconst AMPERSAND = 0x26;\nconst SEMICOLON = 0x3b;\nconst HASH = 0x23;\nconst LOWER_X = 0x78;\nconst UPPER_X = 0x58;\n\n/** Longest named reference in the WHATWG table is 32 characters; the guard is a bound, not a rule. */\nconst MAX_REFERENCE_BODY = 34;\n\nfunction isAsciiDigit(unit: number): boolean {\n return unit >= 0x30 && unit <= 0x39;\n}\n\nfunction isAsciiHexDigit(unit: number): boolean {\n return isAsciiDigit(unit) || (unit >= 0x61 && unit <= 0x66) || (unit >= 0x41 && unit <= 0x46);\n}\n\nfunction isAsciiAlphanumeric(unit: number): boolean {\n return isAsciiDigit(unit) || (unit >= 0x61 && unit <= 0x7a) || (unit >= 0x41 && unit <= 0x5a);\n}\n\n/**\n * The end offset of a well-formed character reference starting at `at`, or -1. A bare `&` that a\n * parser would repair is deliberately **not** treated as a reference: it stays inside the span,\n * where no rule can emit or delete it, rather than manufacturing a boundary that suppresses\n * conversions around it.\n */\nfunction characterReferenceEnd(source: string, at: number, limit: number): number {\n if (source.charCodeAt(at) !== AMPERSAND) return -1;\n let i = at + 1;\n if (i >= limit) return -1;\n\n if (source.charCodeAt(i) === HASH) {\n i += 1;\n const unit = i < limit ? source.charCodeAt(i) : -1;\n const hex = unit === LOWER_X || unit === UPPER_X;\n if (hex) i += 1;\n const digitsAt = i;\n while (\n i < limit &&\n (hex ? isAsciiHexDigit(source.charCodeAt(i)) : isAsciiDigit(source.charCodeAt(i)))\n ) {\n i += 1;\n }\n if (i === digitsAt) return -1;\n return i < limit && source.charCodeAt(i) === SEMICOLON ? i + 1 : -1;\n }\n\n const nameAt = i;\n while (\n i < limit &&\n i - nameAt < MAX_REFERENCE_BODY &&\n isAsciiAlphanumeric(source.charCodeAt(i))\n ) {\n i += 1;\n }\n if (i === nameAt) return -1;\n return i < limit && source.charCodeAt(i) === SEMICOLON ? i + 1 : -1;\n}\n\n/**\n * modes.md 3.6: every character reference is skipped as an **opaque unit**, and a text node\n * containing one is split into spans around it. That is the only way `&nbsp;` survives as\n * `&nbsp;` instead of becoming a literal U+00A0 on the way out.\n */\nexport function splitCharacterReferences(source: string, start: number, end: number): Span[] {\n const spans: Span[] = [];\n let cursor = start;\n let i = start;\n while (i < end) {\n if (source.charCodeAt(i) === AMPERSAND) {\n const stop = characterReferenceEnd(source, i, end);\n if (stop > 0) {\n if (i > cursor) spans.push({ start: cursor, end: i });\n cursor = stop;\n i = stop;\n continue;\n }\n }\n i += 1;\n }\n if (end > cursor) spans.push({ start: cursor, end });\n return spans;\n}\n\nfunction isParent(node: Node): node is ParentNode {\n return \"childNodes\" in node;\n}\n\nfunction collect(node: Node, source: string, spans: Span[]): void {\n if (node.nodeName === \"#text\") {\n const location = node.sourceCodeLocation;\n if (location === undefined || location === null) return;\n for (const span of splitCharacterReferences(source, location.startOffset, location.endOffset)) {\n spans.push(span);\n }\n return;\n }\n\n // Comments, doctype and processing instructions never contribute a span (modes.md 3.6).\n if (node.nodeName === \"#comment\" || node.nodeName === \"#documentType\") return;\n\n if (\"tagName\" in node && isSkippedElement(node.tagName)) return;\n\n // A `template`'s children live in a separate document fragment; `template` is not skipped.\n if (node.nodeName === \"template\" && \"content\" in node) {\n collect(node.content, source, spans);\n return;\n }\n\n if (!isParent(node)) return;\n for (const child of node.childNodes) collect(child, source, spans);\n}\n\n/**\n * Locate the processable spans of an HTML document. The tree is used only to find offsets and is\n * then discarded — modes.md 4 forbids reserialisation, which is what makes attribute quoting,\n * self-closing forms, entity spelling, tag case and malformed-input recovery preserved by\n * construction rather than by effort.\n */\nexport function htmlSpans(source: string): Span[] {\n const document = wrapParserErrors(\"HTML\", () => parse(source, { sourceCodeLocationInfo: true }));\n const spans: Span[] = [];\n collect(document, source, spans);\n return spans;\n}\n\n/** The same, for a fragment: used for HTML blocks embedded in markdown (modes.md 3.7). */\nexport function htmlFragmentSpans(source: string, offset: number): Span[] {\n const fragment = wrapParserErrors(\"HTML\", () =>\n parseFragment(source, { sourceCodeLocationInfo: true }),\n );\n const spans: Span[] = [];\n collect(fragment, source, spans);\n return offset === 0\n ? spans\n : spans.map((span) => ({ start: span.start + offset, end: span.end + offset }));\n}\n","import { PolytypoError } from \"../errors.js\";\n\ninterface PositionedError {\n readonly reason?: unknown;\n readonly place?: { readonly line?: unknown; readonly column?: unknown } | null;\n}\n\nfunction describe(error: unknown): string {\n if (typeof error !== \"object\" || error === null) return String(error);\n const positioned = error as PositionedError;\n const reason =\n typeof positioned.reason === \"string\" ? positioned.reason : (error as Error).message;\n const place = positioned.place;\n if (\n place !== null &&\n place !== undefined &&\n typeof place.line === \"number\" &&\n typeof place.column === \"number\"\n ) {\n return `${reason} (line ${place.line}, column ${place.column})`;\n }\n return reason;\n}\n\n/**\n * spec/rules/modes.md 3.7.2. **The parser's own error type must never escape.** A\n * `VFileMessage`, a `Nokogiri::SyntaxError` or a Python exception on the public surface puts a\n * dependency's type into the contract and is unreproducible in the other four runtimes; only the\n * code is contractual (ARCHITECTURE.md 4.6), while the message and the source position are\n * useful and are not.\n *\n * Reachable in exactly one place: `dialect: \"mdx\"`, because MDX embeds JavaScript. HTML parsing\n * is specified with total error recovery and every byte sequence is valid CommonMark, so neither\n * of the other two languages can fail. The wrapper is applied to all of them anyway — the\n * guarantee is that no parser type escapes, not that this particular parser is trusted.\n */\nexport function wrapParserErrors<T>(what: string, run: () => T): T {\n try {\n return run();\n } catch (error) {\n if (error instanceof PolytypoError) throw error;\n throw new PolytypoError(\n \"POLYTYPO_MALFORMED_INPUT\",\n `Input does not parse as ${what}: ${describe(error)}`,\n );\n }\n}\n"]}
1
+ {"version":3,"sources":["/home/runner/work/polytypo-js/polytypo-js/dist/chunk-MMXKQUAS.cjs","../src/modes/html.ts","../src/modes/parse-error.ts"],"names":[],"mappings":"AAAA;AACE;AACF,wDAA6B;AAC7B;AACA;ACJA,gCAAqC;ADMrC;AACA;AEAA,SAAS,QAAA,CAAS,KAAA,EAAwB;AACxC,EAAA,GAAA,CAAI,OAAO,MAAA,IAAU,SAAA,GAAY,MAAA,IAAU,IAAA,EAAM,OAAO,MAAA,CAAO,KAAK,CAAA;AACpE,EAAA,MAAM,WAAA,EAAa,KAAA;AACnB,EAAA,MAAM,OAAA,EACJ,OAAO,UAAA,CAAW,OAAA,IAAW,SAAA,EAAW,UAAA,CAAW,OAAA,EAAU,KAAA,CAAgB,OAAA;AAC/E,EAAA,MAAM,MAAA,EAAQ,UAAA,CAAW,KAAA;AACzB,EAAA,GAAA,CACE,MAAA,IAAU,KAAA,GACV,MAAA,IAAU,KAAA,EAAA,GACV,OAAO,KAAA,CAAM,KAAA,IAAS,SAAA,GACtB,OAAO,KAAA,CAAM,OAAA,IAAW,QAAA,EACxB;AACA,IAAA,OAAO,CAAA,EAAA;AACT,EAAA;AACO,EAAA;AACT;AAcgB;AACV,EAAA;AACK,IAAA;AACA,EAAA;AACH,IAAA;AACE,IAAA;AACJ,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AACF;AFjBY;AACA;ACfN;AACJ,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACD;AAEe;AACP,EAAA;AACT;AAEM;AACA;AACO;AACP;AACA;AAGA;AAEG;AACA,EAAA;AACT;AAES;AACA,EAAA;AACT;AAES;AACA,EAAA;AACT;AAQS;AACH,EAAA;AACI,EAAA;AACC,EAAA;AAEL,EAAA;AACG,IAAA;AACC,IAAA;AACA,IAAA;AACG,IAAA;AACH,IAAA;AAEJ,IAAA;AAGK,MAAA;AACP,IAAA;AACI,IAAA;AACG,IAAA;AACT,EAAA;AAEM,EAAA;AAEJ,EAAA;AAIK,IAAA;AACP,EAAA;AACU,EAAA;AACH,EAAA;AACT;AAOgB;AACR,EAAA;AACF,EAAA;AACI,EAAA;AACD,EAAA;AACD,IAAA;AACI,MAAA;AACF,MAAA;AACE,QAAA;AACJ,QAAA;AACI,QAAA;AACJ,QAAA;AACF,MAAA;AACF,IAAA;AACK,IAAA;AACP,EAAA;AACU,EAAA;AACH,EAAA;AACT;AAES;AACA,EAAA;AACT;AAES;AACE,EAAA;AACD,IAAA;AACF,IAAA;AACJ,IAAA;AACQ,MAAA;AACR,IAAA;AACA,IAAA;AACF,EAAA;AAGS,EAAA;AAEL,EAAA;AAGK,EAAA;AACC,IAAA;AACR,IAAA;AACF,EAAA;AAEK,EAAA;AACL,EAAA;AACF;AAQgB;AACR,EAAA;AACA,EAAA;AACE,EAAA;AACD,EAAA;AACT;AAGgB;AACR,EAAA;AAA4B,IAAA;AAChC,IAAA;AACF,EAAA;AACM,EAAA;AACE,EAAA;AACD,EAAA;AAGT;AD9BY;AACA;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/polytypo-js/polytypo-js/dist/chunk-MMXKQUAS.cjs","sourcesContent":[null,"import { parse, parseFragment } from \"parse5\";\nimport type { DefaultTreeAdapterMap } from \"parse5\";\nimport { wrapParserErrors } from \"./parse-error.js\";\nimport type { Span } from \"./spans.js\";\n\ntype Node = DefaultTreeAdapterMap[\"node\"];\ntype ParentNode = DefaultTreeAdapterMap[\"parentNode\"];\n\n/**\n * spec/rules/modes.md 3.6, exhaustive and **closed**: extending it is a spec change, not an\n * implementation decision. `svg` and `math` are here because in MathML a quotation mark, a\n * hyphen and a prime are operators and identifiers — substituting a curly glyph changes what\n * the expression means. Everything not listed is processable, **including unknown and custom\n * elements**: guessing from a tag name is exactly the heuristic that diverges across runtimes.\n */\nconst SKIPPED_ELEMENTS: ReadonlySet<string> = new Set([\n \"code\",\n \"pre\",\n \"kbd\",\n \"samp\",\n \"var\",\n \"script\",\n \"style\",\n \"textarea\",\n \"svg\",\n \"math\",\n]);\n\nexport function isSkippedElement(tagName: string): boolean {\n return SKIPPED_ELEMENTS.has(tagName);\n}\n\nconst AMPERSAND = 0x26;\nconst SEMICOLON = 0x3b;\nconst HASH = 0x23;\nconst LOWER_X = 0x78;\nconst UPPER_X = 0x58;\n\n/** Longest named reference in the WHATWG table is 32 characters; the guard is a bound, not a rule. */\nconst MAX_REFERENCE_BODY = 34;\n\nfunction isAsciiDigit(unit: number): boolean {\n return unit >= 0x30 && unit <= 0x39;\n}\n\nfunction isAsciiHexDigit(unit: number): boolean {\n return isAsciiDigit(unit) || (unit >= 0x61 && unit <= 0x66) || (unit >= 0x41 && unit <= 0x46);\n}\n\nfunction isAsciiAlphanumeric(unit: number): boolean {\n return isAsciiDigit(unit) || (unit >= 0x61 && unit <= 0x7a) || (unit >= 0x41 && unit <= 0x5a);\n}\n\n/**\n * The end offset of a well-formed character reference starting at `at`, or -1. A bare `&` that a\n * parser would repair is deliberately **not** treated as a reference: it stays inside the span,\n * where no rule can emit or delete it, rather than manufacturing a boundary that suppresses\n * conversions around it.\n */\nfunction characterReferenceEnd(source: string, at: number, limit: number): number {\n if (source.charCodeAt(at) !== AMPERSAND) return -1;\n let i = at + 1;\n if (i >= limit) return -1;\n\n if (source.charCodeAt(i) === HASH) {\n i += 1;\n const unit = i < limit ? source.charCodeAt(i) : -1;\n const hex = unit === LOWER_X || unit === UPPER_X;\n if (hex) i += 1;\n const digitsAt = i;\n while (\n i < limit &&\n (hex ? isAsciiHexDigit(source.charCodeAt(i)) : isAsciiDigit(source.charCodeAt(i)))\n ) {\n i += 1;\n }\n if (i === digitsAt) return -1;\n return i < limit && source.charCodeAt(i) === SEMICOLON ? i + 1 : -1;\n }\n\n const nameAt = i;\n while (\n i < limit &&\n i - nameAt < MAX_REFERENCE_BODY &&\n isAsciiAlphanumeric(source.charCodeAt(i))\n ) {\n i += 1;\n }\n if (i === nameAt) return -1;\n return i < limit && source.charCodeAt(i) === SEMICOLON ? i + 1 : -1;\n}\n\n/**\n * modes.md 3.6: every character reference is skipped as an **opaque unit**, and a text node\n * containing one is split into spans around it. That is the only way `&nbsp;` survives as\n * `&nbsp;` instead of becoming a literal U+00A0 on the way out.\n */\nexport function splitCharacterReferences(source: string, start: number, end: number): Span[] {\n const spans: Span[] = [];\n let cursor = start;\n let i = start;\n while (i < end) {\n if (source.charCodeAt(i) === AMPERSAND) {\n const stop = characterReferenceEnd(source, i, end);\n if (stop > 0) {\n if (i > cursor) spans.push({ start: cursor, end: i });\n cursor = stop;\n i = stop;\n continue;\n }\n }\n i += 1;\n }\n if (end > cursor) spans.push({ start: cursor, end });\n return spans;\n}\n\nfunction isParent(node: Node): node is ParentNode {\n return \"childNodes\" in node;\n}\n\nfunction collect(node: Node, source: string, spans: Span[]): void {\n if (node.nodeName === \"#text\") {\n const location = node.sourceCodeLocation;\n if (location === undefined || location === null) return;\n for (const span of splitCharacterReferences(source, location.startOffset, location.endOffset)) {\n spans.push(span);\n }\n return;\n }\n\n // Comments, doctype and processing instructions never contribute a span (modes.md 3.6).\n if (node.nodeName === \"#comment\" || node.nodeName === \"#documentType\") return;\n\n if (\"tagName\" in node && isSkippedElement(node.tagName)) return;\n\n // A `template`'s children live in a separate document fragment; `template` is not skipped.\n if (node.nodeName === \"template\" && \"content\" in node) {\n collect(node.content, source, spans);\n return;\n }\n\n if (!isParent(node)) return;\n for (const child of node.childNodes) collect(child, source, spans);\n}\n\n/**\n * Locate the processable spans of an HTML document. The tree is used only to find offsets and is\n * then discarded — modes.md 4 forbids reserialisation, which is what makes attribute quoting,\n * self-closing forms, entity spelling, tag case and malformed-input recovery preserved by\n * construction rather than by effort.\n */\nexport function htmlSpans(source: string): Span[] {\n const document = wrapParserErrors(\"HTML\", () => parse(source, { sourceCodeLocationInfo: true }));\n const spans: Span[] = [];\n collect(document, source, spans);\n return spans;\n}\n\n/** The same, for a fragment: used for HTML blocks embedded in markdown (modes.md 3.7). */\nexport function htmlFragmentSpans(source: string, offset: number): Span[] {\n const fragment = wrapParserErrors(\"HTML\", () =>\n parseFragment(source, { sourceCodeLocationInfo: true }),\n );\n const spans: Span[] = [];\n collect(fragment, source, spans);\n return offset === 0\n ? spans\n : spans.map((span) => ({ start: span.start + offset, end: span.end + offset }));\n}\n","import { PolytypoError } from \"../errors.js\";\n\ninterface PositionedError {\n readonly reason?: unknown;\n readonly place?: { readonly line?: unknown; readonly column?: unknown } | null;\n}\n\nfunction describe(error: unknown): string {\n if (typeof error !== \"object\" || error === null) return String(error);\n const positioned = error as PositionedError;\n const reason =\n typeof positioned.reason === \"string\" ? positioned.reason : (error as Error).message;\n const place = positioned.place;\n if (\n place !== null &&\n place !== undefined &&\n typeof place.line === \"number\" &&\n typeof place.column === \"number\"\n ) {\n return `${reason} (line ${place.line}, column ${place.column})`;\n }\n return reason;\n}\n\n/**\n * spec/rules/modes.md 3.7.2. **The parser's own error type must never escape.** A\n * `VFileMessage`, a `Nokogiri::SyntaxError` or a Python exception on the public surface puts a\n * dependency's type into the contract and is unreproducible in the other four runtimes; only the\n * code is contractual (ARCHITECTURE.md 4.6), while the message and the source position are\n * useful and are not.\n *\n * Reachable in exactly one place: `dialect: \"mdx\"`, because MDX embeds JavaScript. HTML parsing\n * is specified with total error recovery and every byte sequence is valid CommonMark, so neither\n * of the other two languages can fail. The wrapper is applied to all of them anyway — the\n * guarantee is that no parser type escapes, not that this particular parser is trusted.\n */\nexport function wrapParserErrors<T>(what: string, run: () => T): T {\n try {\n return run();\n } catch (error) {\n if (error instanceof PolytypoError) throw error;\n throw new PolytypoError(\n \"POLYTYPO_MALFORMED_INPUT\",\n `Input does not parse as ${what}: ${describe(error)}`,\n );\n }\n}\n"]}
@@ -9,7 +9,7 @@ import {
9
9
  isMarker,
10
10
  runRulesRecording,
11
11
  toCodePoints
12
- } from "./chunk-CW3GAIYQ.js";
12
+ } from "./chunk-25XZKUNP.js";
13
13
 
14
14
  // src/modes/spans.ts
15
15
  var SPACE = 32;
@@ -160,9 +160,9 @@ function runRulesOverSpans(cp, planned, locale, mode, narrowTarget) {
160
160
  }
161
161
  return current;
162
162
  }
163
- function runOverSpans(source, spans, planned, locale, mode, narrowTarget) {
163
+ function replacementsOfUnit(source, spans, planned, locale, mode, narrowTarget) {
164
164
  const normalized = normalizeSpans(spans);
165
- if (normalized.length === 0) return source;
165
+ if (normalized.length === 0) return [];
166
166
  const transformed = runRulesOverSpans(
167
167
  concatenateSpans(source, normalized),
168
168
  planned,
@@ -171,18 +171,29 @@ function runOverSpans(source, spans, planned, locale, mode, narrowTarget) {
171
171
  narrowTarget
172
172
  );
173
173
  const pieces = splitOnMarker(transformed, normalized.length);
174
+ return normalized.map((span, i) => ({ span, text: fromCodePoints(pieces[i]) }));
175
+ }
176
+ function emit(source, replacements) {
174
177
  let out = "";
175
178
  let cursor = 0;
176
- for (let i = 0; i < normalized.length; i += 1) {
177
- const span = normalized[i];
178
- const replacement = fromCodePoints(pieces[i]);
179
+ for (const { span, text } of replacements) {
179
180
  const original = source.slice(span.start, span.end);
180
181
  out += source.slice(cursor, span.start);
181
- out += replacement === original ? original : replacement;
182
+ out += text === original ? original : text;
182
183
  cursor = span.end;
183
184
  }
184
185
  return out + source.slice(cursor);
185
186
  }
187
+ function runOverSpans(source, spans, planned, locale, mode, narrowTarget) {
188
+ return emit(source, replacementsOfUnit(source, spans, planned, locale, mode, narrowTarget));
189
+ }
190
+ function runOverUnits(source, units, planned, locale, mode, narrowTarget) {
191
+ const replacements = units.flatMap((spans) => replacementsOfUnit(source, spans, planned, locale, mode, narrowTarget)).sort((a, b) => a.span.start - b.span.start);
192
+ return emit(source, replacements);
193
+ }
194
+ function analyzeOverUnits(source, units, planned, locale, mode, narrowTarget) {
195
+ return units.flatMap((spans) => analyzeOverSpans(source, spans, planned, locale, mode, narrowTarget)).sort((a, b) => a.start - b.start);
196
+ }
186
197
  function analyzeOverSpans(source, spans, planned, locale, mode, narrowTarget) {
187
198
  const normalized = normalizeSpans(spans);
188
199
  if (normalized.length === 0) return [];
@@ -200,6 +211,8 @@ function analyzeOverSpans(source, spans, planned, locale, mode, narrowTarget) {
200
211
 
201
212
  export {
202
213
  runOverSpans,
214
+ runOverUnits,
215
+ analyzeOverUnits,
203
216
  analyzeOverSpans
204
217
  };
205
- //# sourceMappingURL=chunk-UNG66DF3.js.map
218
+ //# sourceMappingURL=chunk-U5Y7C2L4.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/modes/spans.ts","../src/engine/span-runner.ts"],"sourcesContent":["import { toCodePoints } from \"../engine/codepoints.js\";\nimport { isMarker, LINE_MARKER, MARKER } from \"../engine/sentinels.js\";\nimport { PolytypoError } from \"../errors.js\";\nimport type { Edit } from \"../types.js\";\nimport { NO_ORIGIN } from \"../engine/origin.js\";\n\n/**\n * The boundary markers are defined in the engine (src/engine/sentinels.ts), which is where all\n * three non-code-point sentinels live and where their disjointness is maintained. They are\n * re-exported here because the mode layer is what writes them into the array.\n *\n * Which marker separates two spans is decided by the **raw source bytes of the gap between\n * them**, so it is decidable without asking the parser anything and is identical in five\n * runtimes: a gap containing a line terminator gives `LINE_MARKER`, anything else `MARKER`.\n */\nexport { LINE_MARKER, MARKER, isMarker } from \"../engine/sentinels.js\";\n\n/** `BREAK` as the rules define it (`spaces.md` 3.1), tested against the gap's raw source. */\n/** U+0020, the one emitted code point whose meaning is positional (modes.md 3.4, 5 item 2). */\nconst SPACE = 0x20;\n\nconst LINE_TERMINATORS: ReadonlySet<number> = new Set([\n 0x0a, 0x0d, 0x0b, 0x0c, 0x85, 0x2028, 0x2029,\n]);\n\nfunction gapIsLineBoundary(source: string, from: number, to: number): boolean {\n for (let i = from; i < to; i += 1) {\n if (LINE_TERMINATORS.has(source.charCodeAt(i))) return true;\n }\n return false;\n}\n\n/**\n * A processable span, identified by its offsets in the **original source**. Offsets are UTF-16\n * indices into the JS source string, which is what every JS parser reports; they are an\n * implementation detail of this runtime and never reach the rules, which index code points.\n */\nexport interface Span {\n readonly start: number;\n readonly end: number;\n}\n\n/** A span's extent in the concatenated code-point array: `s₀` and `s₁` of modes.md 3.4. */\nexport interface SpanRange {\n readonly first: number;\n readonly last: number;\n}\n\n/**\n * Sort, drop empties, and coalesce spans separated by nothing in the source (modes.md 7.5:\n * a parser that reports one text run as two adjacent nodes must not manufacture a boundary).\n * Overlapping spans are an extractor bug and are rejected rather than silently merged.\n */\nexport function normalizeSpans(spans: readonly Span[]): Span[] {\n const sorted = spans.filter((s) => s.end > s.start).sort((a, b) => a.start - b.start);\n const out: Span[] = [];\n for (const span of sorted) {\n const last = out[out.length - 1];\n if (last === undefined) {\n out.push(span);\n continue;\n }\n if (span.start < last.end) {\n throw new PolytypoError(\n \"POLYTYPO_RULE_CONTRACT\",\n `mode extractor produced overlapping spans (${last.start}, ${last.end}) and (${span.start}, ${span.end})`,\n );\n }\n if (span.start === last.end) {\n out[out.length - 1] = { start: last.start, end: span.end };\n continue;\n }\n out.push(span);\n }\n return out;\n}\n\n/** `S₁ ⌢ [marker] ⌢ S₂ ⌢ … ⌢ Sₘ` (modes.md 3.5 step 2). */\nexport function concatenateSpans(source: string, spans: readonly Span[]): number[] {\n const cp: number[] = [];\n let previous: Span | undefined;\n for (const span of spans) {\n if (previous !== undefined) {\n cp.push(gapIsLineBoundary(source, previous.end, span.start) ? LINE_MARKER : MARKER);\n }\n for (const value of toCodePoints(source.slice(span.start, span.end))) cp.push(value);\n previous = span;\n }\n return cp;\n}\n\n/**\n * The span extents of the array as it stands. Recomputed after every rule, because applying\n * edits shifts every index after the first one — the markers themselves are guaranteed to\n * survive, since no edit may contain one.\n */\nexport function spanRangesOf(cp: readonly number[]): SpanRange[] {\n const ranges: SpanRange[] = [];\n let first = 0;\n for (let i = 0; i < cp.length; i += 1) {\n if (isMarker(cp[i] as number)) {\n ranges.push({ first, last: i - 1 });\n first = i + 1;\n }\n }\n ranges.push({ first, last: cp.length - 1 });\n return ranges;\n}\n\nfunction spanContaining(ranges: readonly SpanRange[], p: number): SpanRange | undefined {\n for (const range of ranges) {\n if (p >= range.first && p <= range.last + 1) return range;\n }\n return undefined;\n}\n\n/**\n * modes.md 3.4, two safety nets, both pure functions of `(p, q, r, s₀, s₁)` — so the verdict is\n * identical on every run, which is what makes redistribution deterministic (modes.md 5, point 3).\n *\n * 1. **No edit may contain a marker.** No rule can produce one; one that does is a bug, and the\n * edit is discarded rather than treated as a redistribution question.\n * 2. **The edge-growth rule.** An edit is discarded if it would place code points at an\n * extremity of its span that were not there before: with `d = q - p + 1` the replaced length\n * and `r` the replacement length, discard when `p = s₀ and r > d`, or `q = s₁ and r > d`.\n *\n * The length test is the whole rule and needs no knowledge of Markdown or HTML syntax. It\n * separates exactly the cases that matter: `\"` → `“` at an edge is 1 → 1 and applies; `--` →\n * `␣–␣` at an edge is 2 → 3 and is discarded, while the same edit interior to a span applies;\n * `(c)` → `©` is 3 → 1 and applies, because shrinking is always safe. An insertion has `d = 0`,\n * so it is discarded exactly when its position coincides with a span edge — the rule this one\n * generalises.\n *\n * **Deletion at an edge is not restricted here**, and must not be: `r > d` is false for a\n * deletion, so this filter never sees one. Deletion at an edge is governed by modes.md 3.3's\n * *Edge tests* clause instead — where a rule asks \"am I at the edge of the text I am allowed to\n * modify\" rather than \"what character is here\", the marker behaves as `NONE`. That clause is the\n * one place a rule may treat a span edge as the end of the text, it exists because `spaces` is\n * the only rule that deletes, and 3.3 states the division: **a rule that deletes must treat a\n * span edge as the end of the text; a rule that replaces or inserts must not, and is governed by\n * this filter.** The single test that claims the clause is `spaces.md` 3.2 step 4.\n */\nexport function filterBoundaryEdits(\n cp: readonly number[],\n edits: readonly Edit[],\n ranges: readonly SpanRange[],\n): Edit[] {\n const out: Edit[] = [];\n for (const edit of edits) {\n let containsMarker = false;\n for (let i = edit.start; i < edit.end; i += 1) {\n if (isMarker(cp[i] as number)) {\n containsMarker = true;\n break;\n }\n }\n if (containsMarker) continue;\n\n // `[start, end)` in the engine's half-open form is `cp[p … q]` with p = start, q = end - 1;\n // an insertion is `q = p - 1`, which falls out of the same expression.\n const p = edit.start;\n const q = edit.end - 1;\n const d = edit.end - edit.start;\n const r = edit.replacement.length;\n const span = spanContaining(ranges, p);\n if (span !== undefined && (p === span.first || q === span.last)) {\n if (r > d) continue;\n // The character clause. `r > d` is not the rule, only a formalisation of it that misses\n // `r === d`: `dashes` P3 admits a run of THREE dashes, so `---` -> `␣–␣` is 3 -> 3 and the\n // length test sees nothing while U+0020 lands on both extremities anyway. Testing the\n // character catches it, and only U+0020 needs testing — it is the one code point any rule\n // emits whose meaning comes from its position rather than from itself (modes.md 5 item 2).\n const first = edit.replacement[0];\n const last = edit.replacement[r - 1];\n if (r > 0 && p === span.first && first === SPACE && cp[p] !== SPACE) continue;\n if (r > 0 && q === span.last && last === SPACE && cp[q] !== SPACE) continue;\n }\n\n out.push(edit);\n }\n return out;\n}\n\n/** Redistribute the transformed array back to one piece per span (modes.md 3.5 step 4). */\nexport function splitOnMarker(cp: readonly number[], expected: number): number[][] {\n const pieces: number[][] = [[]];\n for (const value of cp) {\n if (isMarker(value)) {\n pieces.push([]);\n continue;\n }\n (pieces[pieces.length - 1] as number[]).push(value);\n }\n if (pieces.length !== expected) {\n throw new PolytypoError(\n \"POLYTYPO_RULE_CONTRACT\",\n `boundary markers did not survive the pipeline: expected ${expected} spans, found ${pieces.length}`,\n );\n }\n return pieces;\n}\n\n/**\n * The origin map for `concatenateSpans` (analyze.md §2): for every code point of the joined\n * array, the code-point offset of the character it came from IN THE DOCUMENT, and NO_ORIGIN for\n * the markers, which came from nowhere. `Span` bounds are native string indices, so the\n * conversion to code-point offsets happens here and not in the caller — this is the one place\n * that knows both coordinate systems.\n */\nexport function originOfSpans(source: string, spans: readonly Span[]): number[] {\n const codePointIndexOf = new Array<number>(source.length + 1);\n let cpIndex = 0;\n for (let i = 0; i < source.length;) {\n codePointIndexOf[i] = cpIndex;\n const code = source.codePointAt(i) as number;\n const width = code > 0xffff ? 2 : 1;\n if (width === 2) codePointIndexOf[i + 1] = cpIndex;\n i += width;\n cpIndex += 1;\n }\n codePointIndexOf[source.length] = cpIndex;\n\n const origin: number[] = [];\n let previous: Span | undefined;\n for (const span of spans) {\n if (previous !== undefined) origin.push(NO_ORIGIN);\n const base = codePointIndexOf[span.start] as number;\n const length = toCodePoints(source.slice(span.start, span.end)).length;\n for (let k = 0; k < length; k += 1) origin.push(base + k);\n previous = span;\n }\n return origin;\n}\n","import {\n concatenateSpans,\n filterBoundaryEdits,\n normalizeSpans,\n originOfSpans,\n spanRangesOf,\n splitOnMarker,\n type Span,\n} from \"../modes/spans.js\";\nimport { RULES } from \"../rules/registry.js\";\nimport type { LocaleData, Mode, RuleId } from \"../types.js\";\nimport { applyEdits } from \"./edits.js\";\nimport { fromCodePoints, toCodePoints } from \"./codepoints.js\";\nimport type { Change } from \"./origin.js\";\nimport { runRulesRecording } from \"./rule-runner.js\";\n\n/**\n * The same sequence as `runRules` (`./rule-runner.js`), with the two boundary filters of\n * modes.md 3.4 interposed. The span extents are recomputed after every rule, because applying\n * an edit shifts every index after it; the markers themselves always survive, since no edit may\n * contain one.\n */\nfunction runRulesOverSpans(\n cp: readonly number[],\n planned: readonly RuleId[],\n locale: LocaleData,\n mode: Mode,\n narrowTarget: number,\n): readonly number[] {\n let current = cp;\n for (const id of planned) {\n const rule = RULES[id];\n if (rule === undefined) continue;\n const edits = rule.apply({ cp: current, locale, mode, narrowTarget });\n current = applyEdits(current, filterBoundaryEdits(current, edits, spanRangesOf(current)), id);\n }\n return current;\n}\n\n/**\n * modes.md 3.5. The pipeline runs **once**, over the marker-separated concatenation of every\n * processable span — not per span, which would pair quotation marks in isolation, and not over a\n * naive concatenation, which would manufacture adjacencies the document does not have.\n *\n * The output is the input with a set of disjoint substring replacements applied and nothing else\n * (modes.md 4). A span whose content the rules did not change contributes no replacement, so a\n * document needing no changes comes back byte-identical; the parser located the spans and was\n * then discarded, and the document is never serialised.\n */\nexport function runOverSpans(\n source: string,\n spans: readonly Span[],\n planned: readonly RuleId[],\n locale: LocaleData,\n mode: Mode,\n narrowTarget: number,\n): string {\n const normalized = normalizeSpans(spans);\n if (normalized.length === 0) return source;\n\n const transformed = runRulesOverSpans(\n concatenateSpans(source, normalized),\n planned,\n locale,\n mode,\n narrowTarget,\n );\n const pieces = splitOnMarker(transformed, normalized.length);\n\n let out = \"\";\n let cursor = 0;\n for (let i = 0; i < normalized.length; i += 1) {\n const span = normalized[i] as Span;\n const replacement = fromCodePoints(pieces[i] as number[]);\n const original = source.slice(span.start, span.end);\n out += source.slice(cursor, span.start);\n out += replacement === original ? original : replacement;\n cursor = span.end;\n }\n return out + source.slice(cursor);\n}\n\n/**\n * `runOverSpans`, reporting instead of applying (analyze.md §1). The span table supplies the\n * origin map, so every change comes back in DOCUMENT coordinates — analyze.md §6 names a\n * runtime that reports span-local offsets here as the mistake that passes every text-mode test.\n */\nexport function analyzeOverSpans(\n source: string,\n spans: readonly Span[],\n planned: readonly RuleId[],\n locale: LocaleData,\n mode: Mode,\n narrowTarget: number,\n): Change[] {\n const normalized = normalizeSpans(spans);\n if (normalized.length === 0) return [];\n return runRulesRecording(\n concatenateSpans(source, normalized),\n planned,\n locale,\n mode,\n narrowTarget,\n originOfSpans(source, normalized),\n toCodePoints(source).length,\n (current, edits) => filterBoundaryEdits(current, edits, spanRangesOf(current)),\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/modes/spans.ts","../src/engine/span-runner.ts"],"sourcesContent":["import { toCodePoints } from \"../engine/codepoints.js\";\nimport { isMarker, LINE_MARKER, MARKER } from \"../engine/sentinels.js\";\nimport { PolytypoError } from \"../errors.js\";\nimport type { Edit } from \"../types.js\";\nimport { NO_ORIGIN } from \"../engine/origin.js\";\n\n/**\n * The boundary markers are defined in the engine (src/engine/sentinels.ts), which is where all\n * three non-code-point sentinels live and where their disjointness is maintained. They are\n * re-exported here because the mode layer is what writes them into the array.\n *\n * Which marker separates two spans is decided by the **raw source bytes of the gap between\n * them**, so it is decidable without asking the parser anything and is identical in five\n * runtimes: a gap containing a line terminator gives `LINE_MARKER`, anything else `MARKER`.\n */\nexport { LINE_MARKER, MARKER, isMarker } from \"../engine/sentinels.js\";\n\n/** `BREAK` as the rules define it (`spaces.md` 3.1), tested against the gap's raw source. */\n/** U+0020, the one emitted code point whose meaning is positional (modes.md 3.4, 5 item 2). */\nconst SPACE = 0x20;\n\nconst LINE_TERMINATORS: ReadonlySet<number> = new Set([\n 0x0a, 0x0d, 0x0b, 0x0c, 0x85, 0x2028, 0x2029,\n]);\n\nfunction gapIsLineBoundary(source: string, from: number, to: number): boolean {\n for (let i = from; i < to; i += 1) {\n if (LINE_TERMINATORS.has(source.charCodeAt(i))) return true;\n }\n return false;\n}\n\n/**\n * A processable span, identified by its offsets in the **original source**. Offsets are UTF-16\n * indices into the JS source string, which is what every JS parser reports; they are an\n * implementation detail of this runtime and never reach the rules, which index code points.\n */\nexport interface Span {\n readonly start: number;\n readonly end: number;\n}\n\n/** A span's extent in the concatenated code-point array: `s₀` and `s₁` of modes.md 3.4. */\nexport interface SpanRange {\n readonly first: number;\n readonly last: number;\n}\n\n/**\n * Sort, drop empties, and coalesce spans separated by nothing in the source (modes.md 7.5:\n * a parser that reports one text run as two adjacent nodes must not manufacture a boundary).\n * Overlapping spans are an extractor bug and are rejected rather than silently merged.\n */\nexport function normalizeSpans(spans: readonly Span[]): Span[] {\n const sorted = spans.filter((s) => s.end > s.start).sort((a, b) => a.start - b.start);\n const out: Span[] = [];\n for (const span of sorted) {\n const last = out[out.length - 1];\n if (last === undefined) {\n out.push(span);\n continue;\n }\n if (span.start < last.end) {\n throw new PolytypoError(\n \"POLYTYPO_RULE_CONTRACT\",\n `mode extractor produced overlapping spans (${last.start}, ${last.end}) and (${span.start}, ${span.end})`,\n );\n }\n if (span.start === last.end) {\n out[out.length - 1] = { start: last.start, end: span.end };\n continue;\n }\n out.push(span);\n }\n return out;\n}\n\n/** `S₁ ⌢ [marker] ⌢ S₂ ⌢ … ⌢ Sₘ` (modes.md 3.5 step 2). */\nexport function concatenateSpans(source: string, spans: readonly Span[]): number[] {\n const cp: number[] = [];\n let previous: Span | undefined;\n for (const span of spans) {\n if (previous !== undefined) {\n cp.push(gapIsLineBoundary(source, previous.end, span.start) ? LINE_MARKER : MARKER);\n }\n for (const value of toCodePoints(source.slice(span.start, span.end))) cp.push(value);\n previous = span;\n }\n return cp;\n}\n\n/**\n * The span extents of the array as it stands. Recomputed after every rule, because applying\n * edits shifts every index after the first one — the markers themselves are guaranteed to\n * survive, since no edit may contain one.\n */\nexport function spanRangesOf(cp: readonly number[]): SpanRange[] {\n const ranges: SpanRange[] = [];\n let first = 0;\n for (let i = 0; i < cp.length; i += 1) {\n if (isMarker(cp[i] as number)) {\n ranges.push({ first, last: i - 1 });\n first = i + 1;\n }\n }\n ranges.push({ first, last: cp.length - 1 });\n return ranges;\n}\n\nfunction spanContaining(ranges: readonly SpanRange[], p: number): SpanRange | undefined {\n for (const range of ranges) {\n if (p >= range.first && p <= range.last + 1) return range;\n }\n return undefined;\n}\n\n/**\n * modes.md 3.4, two safety nets, both pure functions of `(p, q, r, s₀, s₁)` — so the verdict is\n * identical on every run, which is what makes redistribution deterministic (modes.md 5, point 3).\n *\n * 1. **No edit may contain a marker.** No rule can produce one; one that does is a bug, and the\n * edit is discarded rather than treated as a redistribution question.\n * 2. **The edge-growth rule.** An edit is discarded if it would place code points at an\n * extremity of its span that were not there before: with `d = q - p + 1` the replaced length\n * and `r` the replacement length, discard when `p = s₀ and r > d`, or `q = s₁ and r > d`.\n *\n * The length test is the whole rule and needs no knowledge of Markdown or HTML syntax. It\n * separates exactly the cases that matter: `\"` → `“` at an edge is 1 → 1 and applies; `--` →\n * `␣–␣` at an edge is 2 → 3 and is discarded, while the same edit interior to a span applies;\n * `(c)` → `©` is 3 → 1 and applies, because shrinking is always safe. An insertion has `d = 0`,\n * so it is discarded exactly when its position coincides with a span edge — the rule this one\n * generalises.\n *\n * **Deletion at an edge is not restricted here**, and must not be: `r > d` is false for a\n * deletion, so this filter never sees one. Deletion at an edge is governed by modes.md 3.3's\n * *Edge tests* clause instead — where a rule asks \"am I at the edge of the text I am allowed to\n * modify\" rather than \"what character is here\", the marker behaves as `NONE`. That clause is the\n * one place a rule may treat a span edge as the end of the text, it exists because `spaces` is\n * the only rule that deletes, and 3.3 states the division: **a rule that deletes must treat a\n * span edge as the end of the text; a rule that replaces or inserts must not, and is governed by\n * this filter.** The single test that claims the clause is `spaces.md` 3.2 step 4.\n */\nexport function filterBoundaryEdits(\n cp: readonly number[],\n edits: readonly Edit[],\n ranges: readonly SpanRange[],\n): Edit[] {\n const out: Edit[] = [];\n for (const edit of edits) {\n let containsMarker = false;\n for (let i = edit.start; i < edit.end; i += 1) {\n if (isMarker(cp[i] as number)) {\n containsMarker = true;\n break;\n }\n }\n if (containsMarker) continue;\n\n // `[start, end)` in the engine's half-open form is `cp[p … q]` with p = start, q = end - 1;\n // an insertion is `q = p - 1`, which falls out of the same expression.\n const p = edit.start;\n const q = edit.end - 1;\n const d = edit.end - edit.start;\n const r = edit.replacement.length;\n const span = spanContaining(ranges, p);\n if (span !== undefined && (p === span.first || q === span.last)) {\n if (r > d) continue;\n // The character clause. `r > d` is not the rule, only a formalisation of it that misses\n // `r === d`: `dashes` P3 admits a run of THREE dashes, so `---` -> `␣–␣` is 3 -> 3 and the\n // length test sees nothing while U+0020 lands on both extremities anyway. Testing the\n // character catches it, and only U+0020 needs testing — it is the one code point any rule\n // emits whose meaning comes from its position rather than from itself (modes.md 5 item 2).\n const first = edit.replacement[0];\n const last = edit.replacement[r - 1];\n if (r > 0 && p === span.first && first === SPACE && cp[p] !== SPACE) continue;\n if (r > 0 && q === span.last && last === SPACE && cp[q] !== SPACE) continue;\n }\n\n out.push(edit);\n }\n return out;\n}\n\n/** Redistribute the transformed array back to one piece per span (modes.md 3.5 step 4). */\nexport function splitOnMarker(cp: readonly number[], expected: number): number[][] {\n const pieces: number[][] = [[]];\n for (const value of cp) {\n if (isMarker(value)) {\n pieces.push([]);\n continue;\n }\n (pieces[pieces.length - 1] as number[]).push(value);\n }\n if (pieces.length !== expected) {\n throw new PolytypoError(\n \"POLYTYPO_RULE_CONTRACT\",\n `boundary markers did not survive the pipeline: expected ${expected} spans, found ${pieces.length}`,\n );\n }\n return pieces;\n}\n\n/**\n * The origin map for `concatenateSpans` (analyze.md §2): for every code point of the joined\n * array, the code-point offset of the character it came from IN THE DOCUMENT, and NO_ORIGIN for\n * the markers, which came from nowhere. `Span` bounds are native string indices, so the\n * conversion to code-point offsets happens here and not in the caller — this is the one place\n * that knows both coordinate systems.\n */\nexport function originOfSpans(source: string, spans: readonly Span[]): number[] {\n const codePointIndexOf = new Array<number>(source.length + 1);\n let cpIndex = 0;\n for (let i = 0; i < source.length;) {\n codePointIndexOf[i] = cpIndex;\n const code = source.codePointAt(i) as number;\n const width = code > 0xffff ? 2 : 1;\n if (width === 2) codePointIndexOf[i + 1] = cpIndex;\n i += width;\n cpIndex += 1;\n }\n codePointIndexOf[source.length] = cpIndex;\n\n const origin: number[] = [];\n let previous: Span | undefined;\n for (const span of spans) {\n if (previous !== undefined) origin.push(NO_ORIGIN);\n const base = codePointIndexOf[span.start] as number;\n const length = toCodePoints(source.slice(span.start, span.end)).length;\n for (let k = 0; k < length; k += 1) origin.push(base + k);\n previous = span;\n }\n return origin;\n}\n","import {\n concatenateSpans,\n filterBoundaryEdits,\n normalizeSpans,\n originOfSpans,\n spanRangesOf,\n splitOnMarker,\n type Span,\n} from \"../modes/spans.js\";\nimport { RULES } from \"../rules/registry.js\";\nimport type { LocaleData, Mode, RuleId } from \"../types.js\";\nimport { applyEdits } from \"./edits.js\";\nimport { fromCodePoints, toCodePoints } from \"./codepoints.js\";\nimport type { Change } from \"./origin.js\";\nimport { runRulesRecording } from \"./rule-runner.js\";\n\n/**\n * The same sequence as `runRules` (`./rule-runner.js`), with the two boundary filters of\n * modes.md 3.4 interposed. The span extents are recomputed after every rule, because applying\n * an edit shifts every index after it; the markers themselves always survive, since no edit may\n * contain one.\n */\nfunction runRulesOverSpans(\n cp: readonly number[],\n planned: readonly RuleId[],\n locale: LocaleData,\n mode: Mode,\n narrowTarget: number,\n): readonly number[] {\n let current = cp;\n for (const id of planned) {\n const rule = RULES[id];\n if (rule === undefined) continue;\n const edits = rule.apply({ cp: current, locale, mode, narrowTarget });\n current = applyEdits(current, filterBoundaryEdits(current, edits, spanRangesOf(current)), id);\n }\n return current;\n}\n\n/**\n * modes.md 3.5. The pipeline runs **once**, over the marker-separated concatenation of every\n * processable span — not per span, which would pair quotation marks in isolation, and not over a\n * naive concatenation, which would manufacture adjacencies the document does not have.\n *\n * The output is the input with a set of disjoint substring replacements applied and nothing else\n * (modes.md 4). A span whose content the rules did not change contributes no replacement, so a\n * document needing no changes comes back byte-identical; the parser located the spans and was\n * then discarded, and the document is never serialised.\n */\ninterface Replacement {\n readonly span: Span;\n readonly text: string;\n}\n\n/** One text unit: the marker-separated concatenation, the pipeline, and the pieces it produced. */\nfunction replacementsOfUnit(\n source: string,\n spans: readonly Span[],\n planned: readonly RuleId[],\n locale: LocaleData,\n mode: Mode,\n narrowTarget: number,\n): Replacement[] {\n const normalized = normalizeSpans(spans);\n if (normalized.length === 0) return [];\n const transformed = runRulesOverSpans(\n concatenateSpans(source, normalized),\n planned,\n locale,\n mode,\n narrowTarget,\n );\n const pieces = splitOnMarker(transformed, normalized.length);\n return normalized.map((span, i) => ({ span, text: fromCodePoints(pieces[i] as number[]) }));\n}\n\n/** modes.md 4: the source with disjoint replacements applied at recorded offsets, nothing else. */\nfunction emit(source: string, replacements: readonly Replacement[]): string {\n let out = \"\";\n let cursor = 0;\n for (const { span, text } of replacements) {\n const original = source.slice(span.start, span.end);\n out += source.slice(cursor, span.start);\n out += text === original ? original : text;\n cursor = span.end;\n }\n return out + source.slice(cursor);\n}\n\nexport function runOverSpans(\n source: string,\n spans: readonly Span[],\n planned: readonly RuleId[],\n locale: LocaleData,\n mode: Mode,\n narrowTarget: number,\n): string {\n return emit(source, replacementsOfUnit(source, spans, planned, locale, mode, narrowTarget));\n}\n\n/**\n * modes.md 3.1 and 3.5 step 3 (spec 1.7.0). A document has one text unit, except in `markdown`\n * with `frontmatterKeys`, where the frontmatter block's spans form a unit of their own. The\n * pipeline runs once per unit, and the two edit sets are disjoint because no span of one unit lies\n * inside the other — which is exactly what `markdownSpans` skipping the block guarantees.\n *\n * Only step 5 is shared: the source is emitted once, with every unit's replacements in document\n * order.\n */\nexport function runOverUnits(\n source: string,\n units: readonly (readonly Span[])[],\n planned: readonly RuleId[],\n locale: LocaleData,\n mode: Mode,\n narrowTarget: number,\n): string {\n const replacements = units\n .flatMap((spans) => replacementsOfUnit(source, spans, planned, locale, mode, narrowTarget))\n .sort((a, b) => a.span.start - b.span.start);\n return emit(source, replacements);\n}\n\n/**\n * `runOverSpans`, reporting instead of applying (analyze.md §1). The span table supplies the\n * origin map, so every change comes back in DOCUMENT coordinates — analyze.md §6 names a\n * runtime that reports span-local offsets here as the mistake that passes every text-mode test.\n */\n/** `analyzeOverSpans` per text unit (modes.md 3.1), reported in document order. */\nexport function analyzeOverUnits(\n source: string,\n units: readonly (readonly Span[])[],\n planned: readonly RuleId[],\n locale: LocaleData,\n mode: Mode,\n narrowTarget: number,\n): Change[] {\n return units\n .flatMap((spans) => analyzeOverSpans(source, spans, planned, locale, mode, narrowTarget))\n .sort((a, b) => a.start - b.start);\n}\n\nexport function analyzeOverSpans(\n source: string,\n spans: readonly Span[],\n planned: readonly RuleId[],\n locale: LocaleData,\n mode: Mode,\n narrowTarget: number,\n): Change[] {\n const normalized = normalizeSpans(spans);\n if (normalized.length === 0) return [];\n return runRulesRecording(\n concatenateSpans(source, normalized),\n planned,\n locale,\n mode,\n narrowTarget,\n originOfSpans(source, normalized),\n toCodePoints(source).length,\n (current, edits) => filterBoundaryEdits(current, edits, spanRangesOf(current)),\n );\n}\n"],"mappings":";;;;;;;;;;;;;;AAmBA,IAAM,QAAQ;AAEd,IAAM,mBAAwC,oBAAI,IAAI;AAAA,EACpD;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAQ;AACxC,CAAC;AAED,SAAS,kBAAkB,QAAgB,MAAc,IAAqB;AAC5E,WAAS,IAAI,MAAM,IAAI,IAAI,KAAK,GAAG;AACjC,QAAI,iBAAiB,IAAI,OAAO,WAAW,CAAC,CAAC,EAAG,QAAO;AAAA,EACzD;AACA,SAAO;AACT;AAuBO,SAAS,eAAe,OAAgC;AAC7D,QAAM,SAAS,MAAM,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;AACpF,QAAM,MAAc,CAAC;AACrB,aAAW,QAAQ,QAAQ;AACzB,UAAM,OAAO,IAAI,IAAI,SAAS,CAAC;AAC/B,QAAI,SAAS,QAAW;AACtB,UAAI,KAAK,IAAI;AACb;AAAA,IACF;AACA,QAAI,KAAK,QAAQ,KAAK,KAAK;AACzB,YAAM,IAAI;AAAA,QACR;AAAA,QACA,8CAA8C,KAAK,KAAK,KAAK,KAAK,GAAG,UAAU,KAAK,KAAK,KAAK,KAAK,GAAG;AAAA,MACxG;AAAA,IACF;AACA,QAAI,KAAK,UAAU,KAAK,KAAK;AAC3B,UAAI,IAAI,SAAS,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,KAAK,KAAK,IAAI;AACzD;AAAA,IACF;AACA,QAAI,KAAK,IAAI;AAAA,EACf;AACA,SAAO;AACT;AAGO,SAAS,iBAAiB,QAAgB,OAAkC;AACjF,QAAM,KAAe,CAAC;AACtB,MAAI;AACJ,aAAW,QAAQ,OAAO;AACxB,QAAI,aAAa,QAAW;AAC1B,SAAG,KAAK,kBAAkB,QAAQ,SAAS,KAAK,KAAK,KAAK,IAAI,cAAc,MAAM;AAAA,IACpF;AACA,eAAW,SAAS,aAAa,OAAO,MAAM,KAAK,OAAO,KAAK,GAAG,CAAC,EAAG,IAAG,KAAK,KAAK;AACnF,eAAW;AAAA,EACb;AACA,SAAO;AACT;AAOO,SAAS,aAAa,IAAoC;AAC/D,QAAM,SAAsB,CAAC;AAC7B,MAAI,QAAQ;AACZ,WAAS,IAAI,GAAG,IAAI,GAAG,QAAQ,KAAK,GAAG;AACrC,QAAI,SAAS,GAAG,CAAC,CAAW,GAAG;AAC7B,aAAO,KAAK,EAAE,OAAO,MAAM,IAAI,EAAE,CAAC;AAClC,cAAQ,IAAI;AAAA,IACd;AAAA,EACF;AACA,SAAO,KAAK,EAAE,OAAO,MAAM,GAAG,SAAS,EAAE,CAAC;AAC1C,SAAO;AACT;AAEA,SAAS,eAAe,QAA8B,GAAkC;AACtF,aAAW,SAAS,QAAQ;AAC1B,QAAI,KAAK,MAAM,SAAS,KAAK,MAAM,OAAO,EAAG,QAAO;AAAA,EACtD;AACA,SAAO;AACT;AA4BO,SAAS,oBACd,IACA,OACA,QACQ;AACR,QAAM,MAAc,CAAC;AACrB,aAAW,QAAQ,OAAO;AACxB,QAAI,iBAAiB;AACrB,aAAS,IAAI,KAAK,OAAO,IAAI,KAAK,KAAK,KAAK,GAAG;AAC7C,UAAI,SAAS,GAAG,CAAC,CAAW,GAAG;AAC7B,yBAAiB;AACjB;AAAA,MACF;AAAA,IACF;AACA,QAAI,eAAgB;AAIpB,UAAM,IAAI,KAAK;AACf,UAAM,IAAI,KAAK,MAAM;AACrB,UAAM,IAAI,KAAK,MAAM,KAAK;AAC1B,UAAM,IAAI,KAAK,YAAY;AAC3B,UAAM,OAAO,eAAe,QAAQ,CAAC;AACrC,QAAI,SAAS,WAAc,MAAM,KAAK,SAAS,MAAM,KAAK,OAAO;AAC/D,UAAI,IAAI,EAAG;AAMX,YAAM,QAAQ,KAAK,YAAY,CAAC;AAChC,YAAM,OAAO,KAAK,YAAY,IAAI,CAAC;AACnC,UAAI,IAAI,KAAK,MAAM,KAAK,SAAS,UAAU,SAAS,GAAG,CAAC,MAAM,MAAO;AACrE,UAAI,IAAI,KAAK,MAAM,KAAK,QAAQ,SAAS,SAAS,GAAG,CAAC,MAAM,MAAO;AAAA,IACrE;AAEA,QAAI,KAAK,IAAI;AAAA,EACf;AACA,SAAO;AACT;AAGO,SAAS,cAAc,IAAuB,UAA8B;AACjF,QAAM,SAAqB,CAAC,CAAC,CAAC;AAC9B,aAAW,SAAS,IAAI;AACtB,QAAI,SAAS,KAAK,GAAG;AACnB,aAAO,KAAK,CAAC,CAAC;AACd;AAAA,IACF;AACA,IAAC,OAAO,OAAO,SAAS,CAAC,EAAe,KAAK,KAAK;AAAA,EACpD;AACA,MAAI,OAAO,WAAW,UAAU;AAC9B,UAAM,IAAI;AAAA,MACR;AAAA,MACA,2DAA2D,QAAQ,iBAAiB,OAAO,MAAM;AAAA,IACnG;AAAA,EACF;AACA,SAAO;AACT;AASO,SAAS,cAAc,QAAgB,OAAkC;AAC9E,QAAM,mBAAmB,IAAI,MAAc,OAAO,SAAS,CAAC;AAC5D,MAAI,UAAU;AACd,WAAS,IAAI,GAAG,IAAI,OAAO,UAAS;AAClC,qBAAiB,CAAC,IAAI;AACtB,UAAM,OAAO,OAAO,YAAY,CAAC;AACjC,UAAM,QAAQ,OAAO,QAAS,IAAI;AAClC,QAAI,UAAU,EAAG,kBAAiB,IAAI,CAAC,IAAI;AAC3C,SAAK;AACL,eAAW;AAAA,EACb;AACA,mBAAiB,OAAO,MAAM,IAAI;AAElC,QAAM,SAAmB,CAAC;AAC1B,MAAI;AACJ,aAAW,QAAQ,OAAO;AACxB,QAAI,aAAa,OAAW,QAAO,KAAK,SAAS;AACjD,UAAM,OAAO,iBAAiB,KAAK,KAAK;AACxC,UAAM,SAAS,aAAa,OAAO,MAAM,KAAK,OAAO,KAAK,GAAG,CAAC,EAAE;AAChE,aAAS,IAAI,GAAG,IAAI,QAAQ,KAAK,EAAG,QAAO,KAAK,OAAO,CAAC;AACxD,eAAW;AAAA,EACb;AACA,SAAO;AACT;;;AClNA,SAAS,kBACP,IACA,SACA,QACA,MACA,cACmB;AACnB,MAAI,UAAU;AACd,aAAW,MAAM,SAAS;AACxB,UAAM,OAAO,MAAM,EAAE;AACrB,QAAI,SAAS,OAAW;AACxB,UAAM,QAAQ,KAAK,MAAM,EAAE,IAAI,SAAS,QAAQ,MAAM,aAAa,CAAC;AACpE,cAAU,WAAW,SAAS,oBAAoB,SAAS,OAAO,aAAa,OAAO,CAAC,GAAG,EAAE;AAAA,EAC9F;AACA,SAAO;AACT;AAkBA,SAAS,mBACP,QACA,OACA,SACA,QACA,MACA,cACe;AACf,QAAM,aAAa,eAAe,KAAK;AACvC,MAAI,WAAW,WAAW,EAAG,QAAO,CAAC;AACrC,QAAM,cAAc;AAAA,IAClB,iBAAiB,QAAQ,UAAU;AAAA,IACnC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,QAAM,SAAS,cAAc,aAAa,WAAW,MAAM;AAC3D,SAAO,WAAW,IAAI,CAAC,MAAM,OAAO,EAAE,MAAM,MAAM,eAAe,OAAO,CAAC,CAAa,EAAE,EAAE;AAC5F;AAGA,SAAS,KAAK,QAAgB,cAA8C;AAC1E,MAAI,MAAM;AACV,MAAI,SAAS;AACb,aAAW,EAAE,MAAM,KAAK,KAAK,cAAc;AACzC,UAAM,WAAW,OAAO,MAAM,KAAK,OAAO,KAAK,GAAG;AAClD,WAAO,OAAO,MAAM,QAAQ,KAAK,KAAK;AACtC,WAAO,SAAS,WAAW,WAAW;AACtC,aAAS,KAAK;AAAA,EAChB;AACA,SAAO,MAAM,OAAO,MAAM,MAAM;AAClC;AAEO,SAAS,aACd,QACA,OACA,SACA,QACA,MACA,cACQ;AACR,SAAO,KAAK,QAAQ,mBAAmB,QAAQ,OAAO,SAAS,QAAQ,MAAM,YAAY,CAAC;AAC5F;AAWO,SAAS,aACd,QACA,OACA,SACA,QACA,MACA,cACQ;AACR,QAAM,eAAe,MAClB,QAAQ,CAAC,UAAU,mBAAmB,QAAQ,OAAO,SAAS,QAAQ,MAAM,YAAY,CAAC,EACzF,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,QAAQ,EAAE,KAAK,KAAK;AAC7C,SAAO,KAAK,QAAQ,YAAY;AAClC;AAQO,SAAS,iBACd,QACA,OACA,SACA,QACA,MACA,cACU;AACV,SAAO,MACJ,QAAQ,CAAC,UAAU,iBAAiB,QAAQ,OAAO,SAAS,QAAQ,MAAM,YAAY,CAAC,EACvF,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;AACrC;AAEO,SAAS,iBACd,QACA,OACA,SACA,QACA,MACA,cACU;AACV,QAAM,aAAa,eAAe,KAAK;AACvC,MAAI,WAAW,WAAW,EAAG,QAAO,CAAC;AACrC,SAAO;AAAA,IACL,iBAAiB,QAAQ,UAAU;AAAA,IACnC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,cAAc,QAAQ,UAAU;AAAA,IAChC,aAAa,MAAM,EAAE;AAAA,IACrB,CAAC,SAAS,UAAU,oBAAoB,SAAS,OAAO,aAAa,OAAO,CAAC;AAAA,EAC/E;AACF;","names":[]}
@@ -9,7 +9,7 @@
9
9
 
10
10
 
11
11
 
12
- var _chunkT6DOGDDScjs = require('./chunk-T6DOGDDS.cjs');
12
+ var _chunkFMU74VA6cjs = require('./chunk-FMU74VA6.cjs');
13
13
 
14
14
  // src/modes/spans.ts
15
15
  var SPACE = 32;
@@ -38,7 +38,7 @@ function normalizeSpans(spans) {
38
38
  continue;
39
39
  }
40
40
  if (span.start < last.end) {
41
- throw new (0, _chunkT6DOGDDScjs.PolytypoError)(
41
+ throw new (0, _chunkFMU74VA6cjs.PolytypoError)(
42
42
  "POLYTYPO_RULE_CONTRACT",
43
43
  `mode extractor produced overlapping spans (${last.start}, ${last.end}) and (${span.start}, ${span.end})`
44
44
  );
@@ -56,9 +56,9 @@ function concatenateSpans(source, spans) {
56
56
  let previous;
57
57
  for (const span of spans) {
58
58
  if (previous !== void 0) {
59
- cp.push(gapIsLineBoundary(source, previous.end, span.start) ? _chunkT6DOGDDScjs.LINE_MARKER : _chunkT6DOGDDScjs.MARKER);
59
+ cp.push(gapIsLineBoundary(source, previous.end, span.start) ? _chunkFMU74VA6cjs.LINE_MARKER : _chunkFMU74VA6cjs.MARKER);
60
60
  }
61
- for (const value of _chunkT6DOGDDScjs.toCodePoints.call(void 0, source.slice(span.start, span.end))) cp.push(value);
61
+ for (const value of _chunkFMU74VA6cjs.toCodePoints.call(void 0, source.slice(span.start, span.end))) cp.push(value);
62
62
  previous = span;
63
63
  }
64
64
  return cp;
@@ -67,7 +67,7 @@ function spanRangesOf(cp) {
67
67
  const ranges = [];
68
68
  let first = 0;
69
69
  for (let i = 0; i < cp.length; i += 1) {
70
- if (_chunkT6DOGDDScjs.isMarker.call(void 0, cp[i])) {
70
+ if (_chunkFMU74VA6cjs.isMarker.call(void 0, cp[i])) {
71
71
  ranges.push({ first, last: i - 1 });
72
72
  first = i + 1;
73
73
  }
@@ -86,7 +86,7 @@ function filterBoundaryEdits(cp, edits, ranges) {
86
86
  for (const edit of edits) {
87
87
  let containsMarker = false;
88
88
  for (let i = edit.start; i < edit.end; i += 1) {
89
- if (_chunkT6DOGDDScjs.isMarker.call(void 0, cp[i])) {
89
+ if (_chunkFMU74VA6cjs.isMarker.call(void 0, cp[i])) {
90
90
  containsMarker = true;
91
91
  break;
92
92
  }
@@ -111,14 +111,14 @@ function filterBoundaryEdits(cp, edits, ranges) {
111
111
  function splitOnMarker(cp, expected) {
112
112
  const pieces = [[]];
113
113
  for (const value of cp) {
114
- if (_chunkT6DOGDDScjs.isMarker.call(void 0, value)) {
114
+ if (_chunkFMU74VA6cjs.isMarker.call(void 0, value)) {
115
115
  pieces.push([]);
116
116
  continue;
117
117
  }
118
118
  pieces[pieces.length - 1].push(value);
119
119
  }
120
120
  if (pieces.length !== expected) {
121
- throw new (0, _chunkT6DOGDDScjs.PolytypoError)(
121
+ throw new (0, _chunkFMU74VA6cjs.PolytypoError)(
122
122
  "POLYTYPO_RULE_CONTRACT",
123
123
  `boundary markers did not survive the pipeline: expected ${expected} spans, found ${pieces.length}`
124
124
  );
@@ -140,9 +140,9 @@ function originOfSpans(source, spans) {
140
140
  const origin = [];
141
141
  let previous;
142
142
  for (const span of spans) {
143
- if (previous !== void 0) origin.push(_chunkT6DOGDDScjs.NO_ORIGIN);
143
+ if (previous !== void 0) origin.push(_chunkFMU74VA6cjs.NO_ORIGIN);
144
144
  const base = codePointIndexOf[span.start];
145
- const length = _chunkT6DOGDDScjs.toCodePoints.call(void 0, source.slice(span.start, span.end)).length;
145
+ const length = _chunkFMU74VA6cjs.toCodePoints.call(void 0, source.slice(span.start, span.end)).length;
146
146
  for (let k = 0; k < length; k += 1) origin.push(base + k);
147
147
  previous = span;
148
148
  }
@@ -153,16 +153,16 @@ function originOfSpans(source, spans) {
153
153
  function runRulesOverSpans(cp, planned, locale, mode, narrowTarget) {
154
154
  let current = cp;
155
155
  for (const id of planned) {
156
- const rule = _chunkT6DOGDDScjs.RULES[id];
156
+ const rule = _chunkFMU74VA6cjs.RULES[id];
157
157
  if (rule === void 0) continue;
158
158
  const edits = rule.apply({ cp: current, locale, mode, narrowTarget });
159
- current = _chunkT6DOGDDScjs.applyEdits.call(void 0, current, filterBoundaryEdits(current, edits, spanRangesOf(current)), id);
159
+ current = _chunkFMU74VA6cjs.applyEdits.call(void 0, current, filterBoundaryEdits(current, edits, spanRangesOf(current)), id);
160
160
  }
161
161
  return current;
162
162
  }
163
- function runOverSpans(source, spans, planned, locale, mode, narrowTarget) {
163
+ function replacementsOfUnit(source, spans, planned, locale, mode, narrowTarget) {
164
164
  const normalized = normalizeSpans(spans);
165
- if (normalized.length === 0) return source;
165
+ if (normalized.length === 0) return [];
166
166
  const transformed = runRulesOverSpans(
167
167
  concatenateSpans(source, normalized),
168
168
  planned,
@@ -171,29 +171,40 @@ function runOverSpans(source, spans, planned, locale, mode, narrowTarget) {
171
171
  narrowTarget
172
172
  );
173
173
  const pieces = splitOnMarker(transformed, normalized.length);
174
+ return normalized.map((span, i) => ({ span, text: _chunkFMU74VA6cjs.fromCodePoints.call(void 0, pieces[i]) }));
175
+ }
176
+ function emit(source, replacements) {
174
177
  let out = "";
175
178
  let cursor = 0;
176
- for (let i = 0; i < normalized.length; i += 1) {
177
- const span = normalized[i];
178
- const replacement = _chunkT6DOGDDScjs.fromCodePoints.call(void 0, pieces[i]);
179
+ for (const { span, text } of replacements) {
179
180
  const original = source.slice(span.start, span.end);
180
181
  out += source.slice(cursor, span.start);
181
- out += replacement === original ? original : replacement;
182
+ out += text === original ? original : text;
182
183
  cursor = span.end;
183
184
  }
184
185
  return out + source.slice(cursor);
185
186
  }
187
+ function runOverSpans(source, spans, planned, locale, mode, narrowTarget) {
188
+ return emit(source, replacementsOfUnit(source, spans, planned, locale, mode, narrowTarget));
189
+ }
190
+ function runOverUnits(source, units, planned, locale, mode, narrowTarget) {
191
+ const replacements = units.flatMap((spans) => replacementsOfUnit(source, spans, planned, locale, mode, narrowTarget)).sort((a, b) => a.span.start - b.span.start);
192
+ return emit(source, replacements);
193
+ }
194
+ function analyzeOverUnits(source, units, planned, locale, mode, narrowTarget) {
195
+ return units.flatMap((spans) => analyzeOverSpans(source, spans, planned, locale, mode, narrowTarget)).sort((a, b) => a.start - b.start);
196
+ }
186
197
  function analyzeOverSpans(source, spans, planned, locale, mode, narrowTarget) {
187
198
  const normalized = normalizeSpans(spans);
188
199
  if (normalized.length === 0) return [];
189
- return _chunkT6DOGDDScjs.runRulesRecording.call(void 0,
200
+ return _chunkFMU74VA6cjs.runRulesRecording.call(void 0,
190
201
  concatenateSpans(source, normalized),
191
202
  planned,
192
203
  locale,
193
204
  mode,
194
205
  narrowTarget,
195
206
  originOfSpans(source, normalized),
196
- _chunkT6DOGDDScjs.toCodePoints.call(void 0, source).length,
207
+ _chunkFMU74VA6cjs.toCodePoints.call(void 0, source).length,
197
208
  (current, edits) => filterBoundaryEdits(current, edits, spanRangesOf(current))
198
209
  );
199
210
  }
@@ -201,5 +212,7 @@ function analyzeOverSpans(source, spans, planned, locale, mode, narrowTarget) {
201
212
 
202
213
 
203
214
 
204
- exports.runOverSpans = runOverSpans; exports.analyzeOverSpans = analyzeOverSpans;
205
- //# sourceMappingURL=chunk-XEWXDNYN.cjs.map
215
+
216
+
217
+ exports.runOverSpans = runOverSpans; exports.runOverUnits = runOverUnits; exports.analyzeOverUnits = analyzeOverUnits; exports.analyzeOverSpans = analyzeOverSpans;
218
+ //# sourceMappingURL=chunk-VDLHT5CW.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/polytypo-js/polytypo-js/dist/chunk-VDLHT5CW.cjs","../src/modes/spans.ts","../src/engine/span-runner.ts"],"names":[],"mappings":"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","file":"/home/runner/work/polytypo-js/polytypo-js/dist/chunk-VDLHT5CW.cjs","sourcesContent":[null,"import { toCodePoints } from \"../engine/codepoints.js\";\nimport { isMarker, LINE_MARKER, MARKER } from \"../engine/sentinels.js\";\nimport { PolytypoError } from \"../errors.js\";\nimport type { Edit } from \"../types.js\";\nimport { NO_ORIGIN } from \"../engine/origin.js\";\n\n/**\n * The boundary markers are defined in the engine (src/engine/sentinels.ts), which is where all\n * three non-code-point sentinels live and where their disjointness is maintained. They are\n * re-exported here because the mode layer is what writes them into the array.\n *\n * Which marker separates two spans is decided by the **raw source bytes of the gap between\n * them**, so it is decidable without asking the parser anything and is identical in five\n * runtimes: a gap containing a line terminator gives `LINE_MARKER`, anything else `MARKER`.\n */\nexport { LINE_MARKER, MARKER, isMarker } from \"../engine/sentinels.js\";\n\n/** `BREAK` as the rules define it (`spaces.md` 3.1), tested against the gap's raw source. */\n/** U+0020, the one emitted code point whose meaning is positional (modes.md 3.4, 5 item 2). */\nconst SPACE = 0x20;\n\nconst LINE_TERMINATORS: ReadonlySet<number> = new Set([\n 0x0a, 0x0d, 0x0b, 0x0c, 0x85, 0x2028, 0x2029,\n]);\n\nfunction gapIsLineBoundary(source: string, from: number, to: number): boolean {\n for (let i = from; i < to; i += 1) {\n if (LINE_TERMINATORS.has(source.charCodeAt(i))) return true;\n }\n return false;\n}\n\n/**\n * A processable span, identified by its offsets in the **original source**. Offsets are UTF-16\n * indices into the JS source string, which is what every JS parser reports; they are an\n * implementation detail of this runtime and never reach the rules, which index code points.\n */\nexport interface Span {\n readonly start: number;\n readonly end: number;\n}\n\n/** A span's extent in the concatenated code-point array: `s₀` and `s₁` of modes.md 3.4. */\nexport interface SpanRange {\n readonly first: number;\n readonly last: number;\n}\n\n/**\n * Sort, drop empties, and coalesce spans separated by nothing in the source (modes.md 7.5:\n * a parser that reports one text run as two adjacent nodes must not manufacture a boundary).\n * Overlapping spans are an extractor bug and are rejected rather than silently merged.\n */\nexport function normalizeSpans(spans: readonly Span[]): Span[] {\n const sorted = spans.filter((s) => s.end > s.start).sort((a, b) => a.start - b.start);\n const out: Span[] = [];\n for (const span of sorted) {\n const last = out[out.length - 1];\n if (last === undefined) {\n out.push(span);\n continue;\n }\n if (span.start < last.end) {\n throw new PolytypoError(\n \"POLYTYPO_RULE_CONTRACT\",\n `mode extractor produced overlapping spans (${last.start}, ${last.end}) and (${span.start}, ${span.end})`,\n );\n }\n if (span.start === last.end) {\n out[out.length - 1] = { start: last.start, end: span.end };\n continue;\n }\n out.push(span);\n }\n return out;\n}\n\n/** `S₁ ⌢ [marker] ⌢ S₂ ⌢ … ⌢ Sₘ` (modes.md 3.5 step 2). */\nexport function concatenateSpans(source: string, spans: readonly Span[]): number[] {\n const cp: number[] = [];\n let previous: Span | undefined;\n for (const span of spans) {\n if (previous !== undefined) {\n cp.push(gapIsLineBoundary(source, previous.end, span.start) ? LINE_MARKER : MARKER);\n }\n for (const value of toCodePoints(source.slice(span.start, span.end))) cp.push(value);\n previous = span;\n }\n return cp;\n}\n\n/**\n * The span extents of the array as it stands. Recomputed after every rule, because applying\n * edits shifts every index after the first one — the markers themselves are guaranteed to\n * survive, since no edit may contain one.\n */\nexport function spanRangesOf(cp: readonly number[]): SpanRange[] {\n const ranges: SpanRange[] = [];\n let first = 0;\n for (let i = 0; i < cp.length; i += 1) {\n if (isMarker(cp[i] as number)) {\n ranges.push({ first, last: i - 1 });\n first = i + 1;\n }\n }\n ranges.push({ first, last: cp.length - 1 });\n return ranges;\n}\n\nfunction spanContaining(ranges: readonly SpanRange[], p: number): SpanRange | undefined {\n for (const range of ranges) {\n if (p >= range.first && p <= range.last + 1) return range;\n }\n return undefined;\n}\n\n/**\n * modes.md 3.4, two safety nets, both pure functions of `(p, q, r, s₀, s₁)` — so the verdict is\n * identical on every run, which is what makes redistribution deterministic (modes.md 5, point 3).\n *\n * 1. **No edit may contain a marker.** No rule can produce one; one that does is a bug, and the\n * edit is discarded rather than treated as a redistribution question.\n * 2. **The edge-growth rule.** An edit is discarded if it would place code points at an\n * extremity of its span that were not there before: with `d = q - p + 1` the replaced length\n * and `r` the replacement length, discard when `p = s₀ and r > d`, or `q = s₁ and r > d`.\n *\n * The length test is the whole rule and needs no knowledge of Markdown or HTML syntax. It\n * separates exactly the cases that matter: `\"` → `“` at an edge is 1 → 1 and applies; `--` →\n * `␣–␣` at an edge is 2 → 3 and is discarded, while the same edit interior to a span applies;\n * `(c)` → `©` is 3 → 1 and applies, because shrinking is always safe. An insertion has `d = 0`,\n * so it is discarded exactly when its position coincides with a span edge — the rule this one\n * generalises.\n *\n * **Deletion at an edge is not restricted here**, and must not be: `r > d` is false for a\n * deletion, so this filter never sees one. Deletion at an edge is governed by modes.md 3.3's\n * *Edge tests* clause instead — where a rule asks \"am I at the edge of the text I am allowed to\n * modify\" rather than \"what character is here\", the marker behaves as `NONE`. That clause is the\n * one place a rule may treat a span edge as the end of the text, it exists because `spaces` is\n * the only rule that deletes, and 3.3 states the division: **a rule that deletes must treat a\n * span edge as the end of the text; a rule that replaces or inserts must not, and is governed by\n * this filter.** The single test that claims the clause is `spaces.md` 3.2 step 4.\n */\nexport function filterBoundaryEdits(\n cp: readonly number[],\n edits: readonly Edit[],\n ranges: readonly SpanRange[],\n): Edit[] {\n const out: Edit[] = [];\n for (const edit of edits) {\n let containsMarker = false;\n for (let i = edit.start; i < edit.end; i += 1) {\n if (isMarker(cp[i] as number)) {\n containsMarker = true;\n break;\n }\n }\n if (containsMarker) continue;\n\n // `[start, end)` in the engine's half-open form is `cp[p … q]` with p = start, q = end - 1;\n // an insertion is `q = p - 1`, which falls out of the same expression.\n const p = edit.start;\n const q = edit.end - 1;\n const d = edit.end - edit.start;\n const r = edit.replacement.length;\n const span = spanContaining(ranges, p);\n if (span !== undefined && (p === span.first || q === span.last)) {\n if (r > d) continue;\n // The character clause. `r > d` is not the rule, only a formalisation of it that misses\n // `r === d`: `dashes` P3 admits a run of THREE dashes, so `---` -> `␣–␣` is 3 -> 3 and the\n // length test sees nothing while U+0020 lands on both extremities anyway. Testing the\n // character catches it, and only U+0020 needs testing — it is the one code point any rule\n // emits whose meaning comes from its position rather than from itself (modes.md 5 item 2).\n const first = edit.replacement[0];\n const last = edit.replacement[r - 1];\n if (r > 0 && p === span.first && first === SPACE && cp[p] !== SPACE) continue;\n if (r > 0 && q === span.last && last === SPACE && cp[q] !== SPACE) continue;\n }\n\n out.push(edit);\n }\n return out;\n}\n\n/** Redistribute the transformed array back to one piece per span (modes.md 3.5 step 4). */\nexport function splitOnMarker(cp: readonly number[], expected: number): number[][] {\n const pieces: number[][] = [[]];\n for (const value of cp) {\n if (isMarker(value)) {\n pieces.push([]);\n continue;\n }\n (pieces[pieces.length - 1] as number[]).push(value);\n }\n if (pieces.length !== expected) {\n throw new PolytypoError(\n \"POLYTYPO_RULE_CONTRACT\",\n `boundary markers did not survive the pipeline: expected ${expected} spans, found ${pieces.length}`,\n );\n }\n return pieces;\n}\n\n/**\n * The origin map for `concatenateSpans` (analyze.md §2): for every code point of the joined\n * array, the code-point offset of the character it came from IN THE DOCUMENT, and NO_ORIGIN for\n * the markers, which came from nowhere. `Span` bounds are native string indices, so the\n * conversion to code-point offsets happens here and not in the caller — this is the one place\n * that knows both coordinate systems.\n */\nexport function originOfSpans(source: string, spans: readonly Span[]): number[] {\n const codePointIndexOf = new Array<number>(source.length + 1);\n let cpIndex = 0;\n for (let i = 0; i < source.length;) {\n codePointIndexOf[i] = cpIndex;\n const code = source.codePointAt(i) as number;\n const width = code > 0xffff ? 2 : 1;\n if (width === 2) codePointIndexOf[i + 1] = cpIndex;\n i += width;\n cpIndex += 1;\n }\n codePointIndexOf[source.length] = cpIndex;\n\n const origin: number[] = [];\n let previous: Span | undefined;\n for (const span of spans) {\n if (previous !== undefined) origin.push(NO_ORIGIN);\n const base = codePointIndexOf[span.start] as number;\n const length = toCodePoints(source.slice(span.start, span.end)).length;\n for (let k = 0; k < length; k += 1) origin.push(base + k);\n previous = span;\n }\n return origin;\n}\n","import {\n concatenateSpans,\n filterBoundaryEdits,\n normalizeSpans,\n originOfSpans,\n spanRangesOf,\n splitOnMarker,\n type Span,\n} from \"../modes/spans.js\";\nimport { RULES } from \"../rules/registry.js\";\nimport type { LocaleData, Mode, RuleId } from \"../types.js\";\nimport { applyEdits } from \"./edits.js\";\nimport { fromCodePoints, toCodePoints } from \"./codepoints.js\";\nimport type { Change } from \"./origin.js\";\nimport { runRulesRecording } from \"./rule-runner.js\";\n\n/**\n * The same sequence as `runRules` (`./rule-runner.js`), with the two boundary filters of\n * modes.md 3.4 interposed. The span extents are recomputed after every rule, because applying\n * an edit shifts every index after it; the markers themselves always survive, since no edit may\n * contain one.\n */\nfunction runRulesOverSpans(\n cp: readonly number[],\n planned: readonly RuleId[],\n locale: LocaleData,\n mode: Mode,\n narrowTarget: number,\n): readonly number[] {\n let current = cp;\n for (const id of planned) {\n const rule = RULES[id];\n if (rule === undefined) continue;\n const edits = rule.apply({ cp: current, locale, mode, narrowTarget });\n current = applyEdits(current, filterBoundaryEdits(current, edits, spanRangesOf(current)), id);\n }\n return current;\n}\n\n/**\n * modes.md 3.5. The pipeline runs **once**, over the marker-separated concatenation of every\n * processable span — not per span, which would pair quotation marks in isolation, and not over a\n * naive concatenation, which would manufacture adjacencies the document does not have.\n *\n * The output is the input with a set of disjoint substring replacements applied and nothing else\n * (modes.md 4). A span whose content the rules did not change contributes no replacement, so a\n * document needing no changes comes back byte-identical; the parser located the spans and was\n * then discarded, and the document is never serialised.\n */\ninterface Replacement {\n readonly span: Span;\n readonly text: string;\n}\n\n/** One text unit: the marker-separated concatenation, the pipeline, and the pieces it produced. */\nfunction replacementsOfUnit(\n source: string,\n spans: readonly Span[],\n planned: readonly RuleId[],\n locale: LocaleData,\n mode: Mode,\n narrowTarget: number,\n): Replacement[] {\n const normalized = normalizeSpans(spans);\n if (normalized.length === 0) return [];\n const transformed = runRulesOverSpans(\n concatenateSpans(source, normalized),\n planned,\n locale,\n mode,\n narrowTarget,\n );\n const pieces = splitOnMarker(transformed, normalized.length);\n return normalized.map((span, i) => ({ span, text: fromCodePoints(pieces[i] as number[]) }));\n}\n\n/** modes.md 4: the source with disjoint replacements applied at recorded offsets, nothing else. */\nfunction emit(source: string, replacements: readonly Replacement[]): string {\n let out = \"\";\n let cursor = 0;\n for (const { span, text } of replacements) {\n const original = source.slice(span.start, span.end);\n out += source.slice(cursor, span.start);\n out += text === original ? original : text;\n cursor = span.end;\n }\n return out + source.slice(cursor);\n}\n\nexport function runOverSpans(\n source: string,\n spans: readonly Span[],\n planned: readonly RuleId[],\n locale: LocaleData,\n mode: Mode,\n narrowTarget: number,\n): string {\n return emit(source, replacementsOfUnit(source, spans, planned, locale, mode, narrowTarget));\n}\n\n/**\n * modes.md 3.1 and 3.5 step 3 (spec 1.7.0). A document has one text unit, except in `markdown`\n * with `frontmatterKeys`, where the frontmatter block's spans form a unit of their own. The\n * pipeline runs once per unit, and the two edit sets are disjoint because no span of one unit lies\n * inside the other — which is exactly what `markdownSpans` skipping the block guarantees.\n *\n * Only step 5 is shared: the source is emitted once, with every unit's replacements in document\n * order.\n */\nexport function runOverUnits(\n source: string,\n units: readonly (readonly Span[])[],\n planned: readonly RuleId[],\n locale: LocaleData,\n mode: Mode,\n narrowTarget: number,\n): string {\n const replacements = units\n .flatMap((spans) => replacementsOfUnit(source, spans, planned, locale, mode, narrowTarget))\n .sort((a, b) => a.span.start - b.span.start);\n return emit(source, replacements);\n}\n\n/**\n * `runOverSpans`, reporting instead of applying (analyze.md §1). The span table supplies the\n * origin map, so every change comes back in DOCUMENT coordinates — analyze.md §6 names a\n * runtime that reports span-local offsets here as the mistake that passes every text-mode test.\n */\n/** `analyzeOverSpans` per text unit (modes.md 3.1), reported in document order. */\nexport function analyzeOverUnits(\n source: string,\n units: readonly (readonly Span[])[],\n planned: readonly RuleId[],\n locale: LocaleData,\n mode: Mode,\n narrowTarget: number,\n): Change[] {\n return units\n .flatMap((spans) => analyzeOverSpans(source, spans, planned, locale, mode, narrowTarget))\n .sort((a, b) => a.start - b.start);\n}\n\nexport function analyzeOverSpans(\n source: string,\n spans: readonly Span[],\n planned: readonly RuleId[],\n locale: LocaleData,\n mode: Mode,\n narrowTarget: number,\n): Change[] {\n const normalized = normalizeSpans(spans);\n if (normalized.length === 0) return [];\n return runRulesRecording(\n concatenateSpans(source, normalized),\n planned,\n locale,\n mode,\n narrowTarget,\n originOfSpans(source, normalized),\n toCodePoints(source).length,\n (current, edits) => filterBoundaryEdits(current, edits, spanRangesOf(current)),\n );\n}\n"]}
@@ -0,0 +1,35 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
+
3
+
4
+ var _chunkIQHYZG2Kcjs = require('./chunk-IQHYZG2K.cjs');
5
+
6
+
7
+
8
+ var _chunkVDLHT5CWcjs = require('./chunk-VDLHT5CW.cjs');
9
+
10
+
11
+
12
+
13
+ var _chunkFMU74VA6cjs = require('./chunk-FMU74VA6.cjs');
14
+
15
+ // src/engine/yaml-pipeline.ts
16
+ function runYamlPipeline(input, options) {
17
+ const narrowTarget = _chunkFMU74VA6cjs.resolveNarrowTarget.call(void 0, options.narrowNbsp);
18
+ const planned = _chunkFMU74VA6cjs.planRules.call(void 0, options.rules);
19
+ const locale = _chunkFMU74VA6cjs.getLocaleData.call(void 0, options.locale);
20
+ const keys = _chunkIQHYZG2Kcjs.resolveYamlKeys.call(void 0, options.keys);
21
+ return _chunkVDLHT5CWcjs.runOverSpans.call(void 0, input, _chunkIQHYZG2Kcjs.yamlSpans.call(void 0, input, keys), planned, locale, "yaml", narrowTarget);
22
+ }
23
+ function analyzeYamlPipeline(input, options) {
24
+ const narrowTarget = _chunkFMU74VA6cjs.resolveNarrowTarget.call(void 0, options.narrowNbsp);
25
+ const planned = _chunkFMU74VA6cjs.planRules.call(void 0, options.rules);
26
+ const locale = _chunkFMU74VA6cjs.getLocaleData.call(void 0, options.locale);
27
+ const keys = _chunkIQHYZG2Kcjs.resolveYamlKeys.call(void 0, options.keys);
28
+ return _chunkVDLHT5CWcjs.analyzeOverSpans.call(void 0, input, _chunkIQHYZG2Kcjs.yamlSpans.call(void 0, input, keys), planned, locale, "yaml", narrowTarget);
29
+ }
30
+
31
+
32
+
33
+
34
+ exports.runYamlPipeline = runYamlPipeline; exports.analyzeYamlPipeline = analyzeYamlPipeline;
35
+ //# sourceMappingURL=chunk-X6CI7GWV.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/polytypo-js/polytypo-js/dist/chunk-X6CI7GWV.cjs","../src/engine/yaml-pipeline.ts"],"names":[],"mappings":"AAAA;AACE;AACA;AACF,wDAA6B;AAC7B;AACE;AACA;AACF,wDAA6B;AAC7B;AACE;AACA;AACA;AACF,wDAA6B;AAC7B;AACA;ACEO,SAAS,eAAA,CAAgB,KAAA,EAAe,OAAA,EAAmC;AAChF,EAAA,MAAM,aAAA,EAAe,mDAAA,OAAoB,CAAQ,UAAU,CAAA;AAC3D,EAAA,MAAM,QAAA,EAAU,yCAAA,OAAU,CAAQ,KAAK,CAAA;AACvC,EAAA,MAAM,OAAA,EAAS,6CAAA,OAAc,CAAQ,MAAM,CAAA;AAC3C,EAAA,MAAM,KAAA,EAAO,+CAAA,OAAgB,CAAQ,IAAI,CAAA;AACzC,EAAA,OAAO,4CAAA,KAAa,EAAO,yCAAA,KAAU,EAAO,IAAI,CAAA,EAAG,OAAA,EAAS,MAAA,EAAQ,MAAA,EAAQ,YAAY,CAAA;AAC1F;AAGO,SAAS,mBAAA,CAAoB,KAAA,EAAe,OAAA,EAAqC;AACtF,EAAA,MAAM,aAAA,EAAe,mDAAA,OAAoB,CAAQ,UAAU,CAAA;AAC3D,EAAA,MAAM,QAAA,EAAU,yCAAA,OAAU,CAAQ,KAAK,CAAA;AACvC,EAAA,MAAM,OAAA,EAAS,6CAAA,OAAc,CAAQ,MAAM,CAAA;AAC3C,EAAA,MAAM,KAAA,EAAO,+CAAA,OAAgB,CAAQ,IAAI,CAAA;AACzC,EAAA,OAAO,gDAAA,KAAiB,EAAO,yCAAA,KAAU,EAAO,IAAI,CAAA,EAAG,OAAA,EAAS,MAAA,EAAQ,MAAA,EAAQ,YAAY,CAAA;AAC9F;ADFA;AACA;AACE;AACA;AACF,6FAAC","file":"/home/runner/work/polytypo-js/polytypo-js/dist/chunk-X6CI7GWV.cjs","sourcesContent":[null,"import { yamlSpans } from \"../modes/yaml.js\";\nimport type { Options } from \"../types.js\";\nimport { getLocaleData } from \"./locale.js\";\nimport { resolveNarrowTarget } from \"./narrow-target.js\";\nimport { resolveYamlKeys } from \"./yaml-keys.js\";\nimport { planRules } from \"./rule-runner.js\";\nimport { analyzeOverSpans, runOverSpans } from \"./span-runner.js\";\nimport type { Change } from \"./origin.js\";\n\n/**\n * `yaml` mode only, and the only mode pipeline with **no parser dependency at all** — span\n * selection is the specified scan of modes.md 3.8, not a library. There is likewise no\n * `POLYTYPO_MALFORMED_INPUT` counterpart here: with no declared grammar to violate, a file that\n * is not YAML yields few spans or none and comes back byte for byte (modes.md 3.8.3). The only\n * throw this mode adds is `keys`, which is about the call and not the input.\n */\nexport function runYamlPipeline(input: string, options: Partial<Options>): string {\n const narrowTarget = resolveNarrowTarget(options.narrowNbsp);\n const planned = planRules(options.rules);\n const locale = getLocaleData(options.locale);\n const keys = resolveYamlKeys(options.keys);\n return runOverSpans(input, yamlSpans(input, keys), planned, locale, \"yaml\", narrowTarget);\n}\n\n/** analyze.md §1, `yaml` mode: offsets are into the document, not into a span (analyze.md §6). */\nexport function analyzeYamlPipeline(input: string, options: Partial<Options>): Change[] {\n const narrowTarget = resolveNarrowTarget(options.narrowNbsp);\n const planned = planRules(options.rules);\n const locale = getLocaleData(options.locale);\n const keys = resolveYamlKeys(options.keys);\n return analyzeOverSpans(input, yamlSpans(input, keys), planned, locale, \"yaml\", narrowTarget);\n}\n"]}
@@ -20,6 +20,14 @@ interface Options {
20
20
  * guesses. An empty list is legal and processes nothing.
21
21
  */
22
22
  keys?: readonly string[];
23
+ /**
24
+ * Optional, and only meaningful when `mode` is `"markdown"` (spec/rules/modes.md §3.7.4, spec
25
+ * 1.7.0). The keys in the document's YAML frontmatter block whose scalar values are
26
+ * processable, scanned by the same scan `yaml` mode uses. Absent means the block is skipped
27
+ * whole, as it was before 1.7.0; an empty list is legal and yields no spans. The block is its
28
+ * own text unit, so this option can never change a byte outside it.
29
+ */
30
+ frontmatterKeys?: readonly string[];
23
31
  /**
24
32
  * Per-rule override, keyed by `RuleId`. For a default-on rule (every rule except `ranges`),
25
33
  * `false` disables it and `true` is a no-op. For `ranges` — off by default (spec 0.5.0) —
@@ -20,6 +20,14 @@ interface Options {
20
20
  * guesses. An empty list is legal and processes nothing.
21
21
  */
22
22
  keys?: readonly string[];
23
+ /**
24
+ * Optional, and only meaningful when `mode` is `"markdown"` (spec/rules/modes.md §3.7.4, spec
25
+ * 1.7.0). The keys in the document's YAML frontmatter block whose scalar values are
26
+ * processable, scanned by the same scan `yaml` mode uses. Absent means the block is skipped
27
+ * whole, as it was before 1.7.0; an empty list is legal and yields no spans. The block is its
28
+ * own text unit, so this option can never change a byte outside it.
29
+ */
30
+ frontmatterKeys?: readonly string[];
23
31
  /**
24
32
  * Per-rule override, keyed by `RuleId`. For a default-on rule (every rule except `ranges`),
25
33
  * `false` disables it and `true` is a no-op. For `ranges` — off by default (spec 0.5.0) —
package/dist/html.cjs CHANGED
@@ -1,30 +1,30 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }
2
2
 
3
3
 
4
- var _chunk7SX4K4MIcjs = require('./chunk-7SX4K4MI.cjs');
5
- require('./chunk-XKLNVHLQ.cjs');
6
- require('./chunk-XEWXDNYN.cjs');
4
+ var _chunk33SLJJSZcjs = require('./chunk-33SLJJSZ.cjs');
5
+ require('./chunk-MMXKQUAS.cjs');
6
+ require('./chunk-VDLHT5CW.cjs');
7
7
 
8
8
 
9
- var _chunkXRBVOVKAcjs = require('./chunk-XRBVOVKA.cjs');
9
+ var _chunkI2QPXPHXcjs = require('./chunk-I2QPXPHX.cjs');
10
10
 
11
11
 
12
- var _chunkT6DOGDDScjs = require('./chunk-T6DOGDDS.cjs');
12
+ var _chunkFMU74VA6cjs = require('./chunk-FMU74VA6.cjs');
13
13
 
14
14
  // src/index.html.ts
15
15
  function transform(input, options) {
16
16
  const given = _nullishCoalesce(options, () => ( {}));
17
- _chunkXRBVOVKAcjs.assertFixedMode.call(void 0, given.mode, "html", "polytypo/html");
18
- return _chunk7SX4K4MIcjs.runHtmlPipeline.call(void 0, input, given);
17
+ _chunkI2QPXPHXcjs.assertFixedMode.call(void 0, given.mode, "html", "polytypo/html");
18
+ return _chunk33SLJJSZcjs.runHtmlPipeline.call(void 0, input, given);
19
19
  }
20
20
  function analyze(input, options) {
21
21
  const given = _nullishCoalesce(options, () => ( {}));
22
- _chunkXRBVOVKAcjs.assertFixedMode.call(void 0, given.mode, "html", "polytypo/html");
23
- return _chunk7SX4K4MIcjs.analyzeHtmlPipeline.call(void 0, input, given);
22
+ _chunkI2QPXPHXcjs.assertFixedMode.call(void 0, given.mode, "html", "polytypo/html");
23
+ return _chunk33SLJJSZcjs.analyzeHtmlPipeline.call(void 0, input, given);
24
24
  }
25
25
 
26
26
 
27
27
 
28
28
 
29
- exports.PolytypoError = _chunkT6DOGDDScjs.PolytypoError; exports.analyze = analyze; exports.transform = transform;
29
+ exports.PolytypoError = _chunkFMU74VA6cjs.PolytypoError; exports.analyze = analyze; exports.transform = transform;
30
30
  //# sourceMappingURL=html.cjs.map
package/dist/html.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/polytypo-js/polytypo-js/dist/html.cjs","../src/index.html.ts"],"names":[],"mappings":"AAAA;AACE;AACA;AACF,wDAA6B;AAC7B,gCAA6B;AAC7B,gCAA6B;AAC7B;AACE;AACF,wDAA6B;AAC7B;AACE;AACF,wDAA6B;AAC7B;AACA;ACGO,SAAS,SAAA,CAAU,KAAA,EAAe,OAAA,EAA8B;AACrE,EAAA,MAAM,MAAA,mBAAS,OAAA,UAAW,CAAC,GAAA;AAC3B,EAAA,+CAAA,KAAgB,CAAM,IAAA,EAAM,MAAA,EAAQ,eAAe,CAAA;AACnD,EAAA,OAAO,+CAAA,KAAgB,EAAO,KAAK,CAAA;AACrC;AAOO,SAAS,OAAA,CAAQ,KAAA,EAAe,OAAA,EAAgC;AACrE,EAAA,MAAM,MAAA,mBAAS,OAAA,UAAW,CAAC,GAAA;AAC3B,EAAA,+CAAA,KAAgB,CAAM,IAAA,EAAM,MAAA,EAAQ,eAAe,CAAA;AACnD,EAAA,OAAO,mDAAA,KAAoB,EAAO,KAAK,CAAA;AACzC;ADPA;AACE;AACA;AACA;AACF,kHAAC","file":"/home/runner/work/polytypo-js/polytypo-js/dist/html.cjs","sourcesContent":[null,"import { assertFixedMode } from \"./engine/assert-fixed-mode.js\";\nimport { analyzeHtmlPipeline, runHtmlPipeline } from \"./engine/html-pipeline.js\";\nimport type { Change } from \"./engine/origin.js\";\nimport type { Options } from \"./types.js\";\n\n/**\n * `polytypo/html` — HTML-only entry point. Its module graph includes `parse5` but excludes the\n * Micromark/MDX stack (AUDIT_REMEDIATION_AND_RELEASE_PLAN.md 5.1): it imports\n * `./engine/html-pipeline.js` directly and never `./engine/pipeline.js` or `./modes/markdown.js`.\n *\n * `mode` is absent from `HtmlOptions`, since this entry only ever runs `html` mode. `dialect` is\n * likewise absent — it has no effect in `html` mode even in the aggregate entry (types.ts), so\n * there is nothing for it to conflict with here.\n */\nexport type HtmlOptions = Omit<Options, \"mode\" | \"dialect\">;\n\nexport function transform(input: string, options: HtmlOptions): string {\n const given = (options ?? {}) as Partial<Options>;\n assertFixedMode(given.mode, \"html\", \"polytypo/html\");\n return runHtmlPipeline(input, given);\n}\n\n/**\n * The same pipeline as this entry's `transform`, reporting instead of applying\n * (spec/rules/analyze.md). Offsets are code-point offsets into `input` — into the document,\n * not into a span (analyze.md §6).\n */\nexport function analyze(input: string, options: HtmlOptions): Change[] {\n const given = (options ?? {}) as Partial<Options>;\n assertFixedMode(given.mode, \"html\", \"polytypo/html\");\n return analyzeHtmlPipeline(input, given);\n}\n\nexport { PolytypoError } from \"./errors.js\";\nexport type { PolytypoErrorCode } from \"./errors.js\";\nexport type { Change } from \"./engine/origin.js\";\nexport type { NarrowNbsp } from \"./types.js\";\nexport type { LocaleData, LocaleSource, QuotePair, Rule, RuleContext, RuleId } from \"./types.js\";\n"]}
1
+ {"version":3,"sources":["/home/runner/work/polytypo-js/polytypo-js/dist/html.cjs","../src/index.html.ts"],"names":[],"mappings":"AAAA;AACE;AACA;AACF,wDAA6B;AAC7B,gCAA6B;AAC7B,gCAA6B;AAC7B;AACE;AACF,wDAA6B;AAC7B;AACE;AACF,wDAA6B;AAC7B;AACA;ACGO,SAAS,SAAA,CAAU,KAAA,EAAe,OAAA,EAA8B;AACrE,EAAA,MAAM,MAAA,mBAAS,OAAA,UAAW,CAAC,GAAA;AAC3B,EAAA,+CAAA,KAAgB,CAAM,IAAA,EAAM,MAAA,EAAQ,eAAe,CAAA;AACnD,EAAA,OAAO,+CAAA,KAAgB,EAAO,KAAK,CAAA;AACrC;AAOO,SAAS,OAAA,CAAQ,KAAA,EAAe,OAAA,EAAgC;AACrE,EAAA,MAAM,MAAA,mBAAS,OAAA,UAAW,CAAC,GAAA;AAC3B,EAAA,+CAAA,KAAgB,CAAM,IAAA,EAAM,MAAA,EAAQ,eAAe,CAAA;AACnD,EAAA,OAAO,mDAAA,KAAoB,EAAO,KAAK,CAAA;AACzC;ADPA;AACE;AACA;AACA;AACF,kHAAC","file":"/home/runner/work/polytypo-js/polytypo-js/dist/html.cjs","sourcesContent":[null,"import { assertFixedMode } from \"./engine/assert-fixed-mode.js\";\nimport { analyzeHtmlPipeline, runHtmlPipeline } from \"./engine/html-pipeline.js\";\nimport type { Change } from \"./engine/origin.js\";\nimport type { Options } from \"./types.js\";\n\n/**\n * `polytypo/html` — HTML-only entry point. Its module graph includes `parse5` but excludes the\n * Micromark/MDX stack (AUDIT_REMEDIATION_AND_RELEASE_PLAN.md 5.1): it imports\n * `./engine/html-pipeline.js` directly and never `./engine/pipeline.js` or `./modes/markdown.js`.\n *\n * `mode` is absent from `HtmlOptions`, since this entry only ever runs `html` mode. `dialect` is\n * likewise absent — it has no effect in `html` mode even in the aggregate entry (types.ts), so\n * there is nothing for it to conflict with here.\n */\nexport type HtmlOptions = Omit<Options, \"mode\" | \"dialect\" | \"frontmatterKeys\">;\n\nexport function transform(input: string, options: HtmlOptions): string {\n const given = (options ?? {}) as Partial<Options>;\n assertFixedMode(given.mode, \"html\", \"polytypo/html\");\n return runHtmlPipeline(input, given);\n}\n\n/**\n * The same pipeline as this entry's `transform`, reporting instead of applying\n * (spec/rules/analyze.md). Offsets are code-point offsets into `input` — into the document,\n * not into a span (analyze.md §6).\n */\nexport function analyze(input: string, options: HtmlOptions): Change[] {\n const given = (options ?? {}) as Partial<Options>;\n assertFixedMode(given.mode, \"html\", \"polytypo/html\");\n return analyzeHtmlPipeline(input, given);\n}\n\nexport { PolytypoError } from \"./errors.js\";\nexport type { PolytypoErrorCode } from \"./errors.js\";\nexport type { Change } from \"./engine/origin.js\";\nexport type { NarrowNbsp } from \"./types.js\";\nexport type { LocaleData, LocaleSource, QuotePair, Rule, RuleContext, RuleId } from \"./types.js\";\n"]}
package/dist/html.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { O as Options, C as Change } from './errors-C1_sHGgx.cjs';
2
- export { L as LocaleData, a as LocaleSource, N as NarrowNbsp, P as PolytypoError, b as PolytypoErrorCode, Q as QuotePair, R as Rule, c as RuleContext, d as RuleId } from './errors-C1_sHGgx.cjs';
1
+ import { O as Options, C as Change } from './errors-DeHxbXRn.cjs';
2
+ export { L as LocaleData, a as LocaleSource, N as NarrowNbsp, P as PolytypoError, b as PolytypoErrorCode, Q as QuotePair, R as Rule, c as RuleContext, d as RuleId } from './errors-DeHxbXRn.cjs';
3
3
 
4
4
  /**
5
5
  * `polytypo/html` — HTML-only entry point. Its module graph includes `parse5` but excludes the
@@ -10,7 +10,7 @@ export { L as LocaleData, a as LocaleSource, N as NarrowNbsp, P as PolytypoError
10
10
  * likewise absent — it has no effect in `html` mode even in the aggregate entry (types.ts), so
11
11
  * there is nothing for it to conflict with here.
12
12
  */
13
- type HtmlOptions = Omit<Options, "mode" | "dialect">;
13
+ type HtmlOptions = Omit<Options, "mode" | "dialect" | "frontmatterKeys">;
14
14
  declare function transform(input: string, options: HtmlOptions): string;
15
15
  /**
16
16
  * The same pipeline as this entry's `transform`, reporting instead of applying
package/dist/html.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { O as Options, C as Change } from './errors-C1_sHGgx.js';
2
- export { L as LocaleData, a as LocaleSource, N as NarrowNbsp, P as PolytypoError, b as PolytypoErrorCode, Q as QuotePair, R as Rule, c as RuleContext, d as RuleId } from './errors-C1_sHGgx.js';
1
+ import { O as Options, C as Change } from './errors-DeHxbXRn.js';
2
+ export { L as LocaleData, a as LocaleSource, N as NarrowNbsp, P as PolytypoError, b as PolytypoErrorCode, Q as QuotePair, R as Rule, c as RuleContext, d as RuleId } from './errors-DeHxbXRn.js';
3
3
 
4
4
  /**
5
5
  * `polytypo/html` — HTML-only entry point. Its module graph includes `parse5` but excludes the
@@ -10,7 +10,7 @@ export { L as LocaleData, a as LocaleSource, N as NarrowNbsp, P as PolytypoError
10
10
  * likewise absent — it has no effect in `html` mode even in the aggregate entry (types.ts), so
11
11
  * there is nothing for it to conflict with here.
12
12
  */
13
- type HtmlOptions = Omit<Options, "mode" | "dialect">;
13
+ type HtmlOptions = Omit<Options, "mode" | "dialect" | "frontmatterKeys">;
14
14
  declare function transform(input: string, options: HtmlOptions): string;
15
15
  /**
16
16
  * The same pipeline as this entry's `transform`, reporting instead of applying
package/dist/html.js CHANGED
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  analyzeHtmlPipeline,
3
3
  runHtmlPipeline
4
- } from "./chunk-TIYWNQJT.js";
5
- import "./chunk-YF4HZDOF.js";
6
- import "./chunk-UNG66DF3.js";
4
+ } from "./chunk-GUTUSN4K.js";
5
+ import "./chunk-ITDTGCLJ.js";
6
+ import "./chunk-U5Y7C2L4.js";
7
7
  import {
8
8
  assertFixedMode
9
- } from "./chunk-EU6OXPQ4.js";
9
+ } from "./chunk-JVBK3LG3.js";
10
10
  import {
11
11
  PolytypoError
12
- } from "./chunk-CW3GAIYQ.js";
12
+ } from "./chunk-25XZKUNP.js";
13
13
 
14
14
  // src/index.html.ts
15
15
  function transform(input, options) {