Skip to content

feat: add csv/tsv as a first-class DocumentFormat mapped to the spreadsheet variant - #613

Merged
Mearman merged 1 commit into
mainfrom
feat/csv-format
Aug 17, 2026
Merged

feat: add csv/tsv as a first-class DocumentFormat mapped to the spreadsheet variant#613
Mearman merged 1 commit into
mainfrom
feat/csv-format

Conversation

@Mearman

@Mearman Mearman commented Aug 17, 2026

Copy link
Copy Markdown
Member

csv becomes the eleventh DocumentFormat member, sharing the spreadsheet variant with xlsx and ods: reading produces a one-sheet spreadsheet ContentDocument, writing emits one sheet back out as RFC 4180 records via each cell's displayText. TSV is the same format with { delimiter: '\t' } on either side, since a delimiter is a parse option, not a different document format.

The adapter family in src/csv/ follows the established stage pattern:

  • records.ts — RFC 4180 record parser/writer; quoteCsvField is shared with the .odb csv exporter rather than duplicated.
  • text.ts — byte⇄text boundary that rejects malformed UTF-8 (CsvInvalidUtf8Error) instead of producing U+FFFD.
  • read.ts — first record becomes a verbatim string header (never re-typed, even when it looks like data); data cells re-type through the same inferCellValue heuristic pdfToOds applies, with declines keeping the plain string and onCellTypeInference firing per decision.
  • write.ts — one sheet out; a multi-sheet source refuses with CsvSheetNotSpecifiedError naming every sheet until { sheet } selects one.

Routing: csv bridges one hop to its spreadsheet siblings (csvToOds/odsToCsv, csvToXlsx/xlsxToCsv — same-variant, PDF-bypassing), reaches PDF through the ods layout pair (csvToPdf/pdfToCsv), and takes csvToMarkdown/markdownToCsv as the PDF-composed last resort. csvPdfCodec, odsCsvCodec, xlsxCsvCodec, and csvMarkdownCodec expose the same pairs as z.codec() forms. The composition engine now routes 91 (source, target) pairs.

Metadata is honest about the format: CsvBytesSchema validates well-formed UTF-8 only (plain text has no magic bytes), readDocumentMetadata('csv', …) answers an empty LayoutMetadata, and setDocumentMetadata rejects csv in both directions, since RFC 4180 text has no metadata container.

Closes #80

Generated by Claude Code

@Mearman Mearman closed this Aug 17, 2026
@Mearman Mearman reopened this Aug 17, 2026
…dsheet variant

csv joins the eleven DocumentFormat members as a spreadsheet-variant format:
reading is a one-sheet spreadsheet ContentDocument, writing emits one sheet
back out as RFC 4180 records via each cell's displayText. TSV is the same
format with { delimiter: '\t' } on either side, since a delimiter is a parse
option, not a different document format.

The adapter family lives in src/csv/: records.ts (RFC 4180 record
parser/writer, its quoteCsvField shared with the .odb csv exporter),
text.ts (byte<->text boundary rejecting malformed UTF-8), read.ts (first
record as a verbatim string header, data cells re-typed through the same
inferCellValue heuristic pdfToOds applies, declines keeping the plain
string), and write.ts (CsvSheetNotSpecifiedError names every sheet when a
multi-sheet source reaches a one-table target).

The composition engine now routes 91 (source, target) pairs: csv bridges
one hop to its spreadsheet siblings (csvToOds/odsToCsv, csvToXlsx/
xlsxToCsv -- same-variant, PDF-bypassing), reaches PDF through the ods
layout pair (csvToPdf/pdfToCsv), and takes csvToMarkdown/markdownToCsv as
the PDF-composed last resort. csvPdfCodec, odsCsvCodec, xlsxCsvCodec,
and csvMarkdownCodec expose the same pairs as z.codec() forms.

CsvBytesSchema validates well-formed UTF-8 only (plain text has no magic
bytes); readDocumentMetadata answers an empty LayoutMetadata and
setDocumentMetadata rejects csv in both directions, since RFC 4180 text
has no metadata container. The round-trip matrix sweep now covers every
routable pair including csv, selecting a sheet when a multi-sheet
intermediate meets the one-table target.
@github-code-quality

Copy link
Copy Markdown

Code Coverage Overview

Languages: TypeScript

TypeScript / unit

The overall coverage in commit d7b0b14 in the feat/csv-format branch remains at 91%, unchanged from commit 5617ef0 in the main branch.

Show a code coverage summary of the most impacted files.
File main 5617ef0 feat/csv-format d7b0b14 +/-
src/odb/csv.ts 93% 92% -1%
src/convert/composition.ts 93% 93% 0%
src/convert/convert.ts 96% 96% 0%
src/metadata/write.ts 94% 94% 0%
src/codecs/registry.ts 94% 94% 0%
src/convert/codec.ts 86% 89% +3%
src/csv/write.ts 0% 98% +98%
src/csv/records.ts 0% 100% +100%
src/csv/read.ts 0% 100% +100%
src/csv/text.ts 0% 100% +100%

@Mearman
Mearman merged commit 75180a1 into main Aug 17, 2026
12 checks passed
@Mearman
Mearman deleted the feat/csv-format branch August 17, 2026 17:35
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 2.1.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add CSV/TSV as a first-class DocumentFormat

1 participant