paired-markdown-derivatives.ts 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263
  1. /**
  2. * Separate byte-identical Chinese Markdown code blocks from the primary checks
  3. * performed on their unsuffixed English siblings. The bilingual pairing gate
  4. * owns cross-language identity; source-oriented gates consume one copy.
  5. */
  6. /** The result of separating canonical blocks from paired Chinese derivatives. */
  7. export interface MarkdownDerivativePartition<T> {
  8. /** Blocks that still require the caller's owning check. */
  9. primary: T[]
  10. /** Chinese blocks covered by the byte-identical unsuffixed sequence. */
  11. derivatives: T[]
  12. }
  13. /** Return the unsuffixed sibling of a Chinese Markdown path. */
  14. function unsuffixedSibling(doc: string): string | null {
  15. return doc.endsWith('.zh.md') ? `${doc.slice(0, -'.zh.md'.length)}.md` : null
  16. }
  17. /**
  18. * Partition complete byte-identical `.zh.md` block sequences from primary
  19. * blocks. A partial or reordered match stays primary so the caller fails
  20. * closed; the translation-pairing gate reports the cross-language mismatch.
  21. *
  22. * @param blocks - Blocks in repository scan order.
  23. * @param docOf - Repository-relative Markdown path owning a block.
  24. * @param fingerprintOf - Block kind/info string plus byte-exact body.
  25. * @returns Primary blocks and paired Chinese derivatives, preserving order.
  26. */
  27. export function partitionPairedMarkdownDerivatives<T>(
  28. blocks: readonly T[],
  29. docOf: (block: T) => string,
  30. fingerprintOf: (block: T) => string,
  31. ): MarkdownDerivativePartition<T> {
  32. const byDoc = new Map<string, T[]>()
  33. for (const block of blocks) {
  34. const doc = docOf(block)
  35. const group = byDoc.get(doc)
  36. if (group) group.push(block)
  37. else byDoc.set(doc, [block])
  38. }
  39. const derivativeDocs = new Set<string>()
  40. for (const [doc, candidates] of byDoc) {
  41. const sibling = unsuffixedSibling(doc)
  42. if (sibling === null) continue
  43. const originals = byDoc.get(sibling)
  44. if (originals === undefined || originals.length !== candidates.length) continue
  45. if (candidates.every((candidate, index) => {
  46. const original = originals[index]
  47. return original !== undefined && fingerprintOf(candidate) === fingerprintOf(original)
  48. })) {
  49. derivativeDocs.add(doc)
  50. }
  51. }
  52. const primary: T[] = []
  53. const derivatives: T[] = []
  54. for (const block of blocks) {
  55. (derivativeDocs.has(docOf(block)) ? derivatives : primary).push(block)
  56. }
  57. return { primary, derivatives }
  58. }