feat: add csv/tsv as a first-class DocumentFormat mapped to the spreadsheet variant - #613
Merged
Conversation
…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.
Mearman
force-pushed
the
feat/csv-format
branch
from
August 17, 2026 17:33
6844d7f to
d7b0b14
Compare
Code Coverage OverviewLanguages: TypeScript TypeScript / unitThe overall coverage in commit d7b0b14 in the Show a code coverage summary of the most impacted files.
|
Contributor
|
🎉 This PR is included in version 2.1.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
csv becomes the eleventh
DocumentFormatmember, sharing the spreadsheet variant with xlsx and ods: reading produces a one-sheet spreadsheetContentDocument, writing emits one sheet back out as RFC 4180 records via each cell'sdisplayText. 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;quoteCsvFieldis shared with the.odbcsv 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 sameinferCellValueheuristicpdfToOdsapplies, with declines keeping the plain string andonCellTypeInferencefiring per decision.write.ts— one sheet out; a multi-sheet source refuses withCsvSheetNotSpecifiedErrornaming 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 takescsvToMarkdown/markdownToCsvas the PDF-composed last resort.csvPdfCodec,odsCsvCodec,xlsxCsvCodec, andcsvMarkdownCodecexpose the same pairs asz.codec()forms. The composition engine now routes 91 (source, target) pairs.Metadata is honest about the format:
CsvBytesSchemavalidates well-formed UTF-8 only (plain text has no magic bytes),readDocumentMetadata('csv', …)answers an emptyLayoutMetadata, andsetDocumentMetadatarejects csv in both directions, since RFC 4180 text has no metadata container.Closes #80
Generated by Claude Code