npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ranjeetk25/schema-grid-io

v0.4.1

Published

CSV/XLSX import, export and clipboard parsing for Schema Grid.

Readme

@ranjeetk25/schema-grid-io

CSV/XLSX import and export for Schema Grid, plus the TSV clipboard format used by @ranjeetk25/schema-grid-ag-grid. The package runs in the browser (for previews and small exports) and in Node (for server import/export jobs).

Entry points

| Subpath | Runs in | Heavy deps | Exports | |---|---|---|---| | @ranjeetk25/schema-grid-io/import | browser + Node | papaparse, exceljs | parseFile, autoMapColumns, validateRows, toChangeBatches, keyOf, chunkRows, createImportJobState, recordChunkResult, buildErrorReportCsv, KEY_COLUMN_TYPES, SheetNotFoundError, ImportConfigError | | @ranjeetk25/schema-grid-io/export | browser (Blob) + Node (stream) | papaparse, exceljs | buildExport, buildExportBlob, buildExportStream, exportFileName, exportMimeType, HiddenColumnError | | @ranjeetk25/schema-grid-io/clipboard | anywhere | none | formatForClipboard, formatMatrixForClipboard, parseClipboard | | @ranjeetk25/schema-grid-io | | | everything above |

./clipboard has no runtime dependencies, so the grid can import it without pulling in exceljs or papaparse. node:stream is only loaded with a dynamic import() on the Node stream paths, so browser bundles never include it.

Import pipeline

Call these in order:

  1. parseFile(input, { type?, sheet?, headerRow?, maxRows?, tz? }) reads a File, Blob, ArrayBuffer or web ReadableStream into a ParsedTable of strings.
    • CSV: UTF-8 with the BOM removed; the delimiter (, ; tab |) is detected.
    • XLSX: numbers use the raw value (not the display text), date-only cells become YYYY-MM-DD, datetimes become UTC ISO read in tz, formulas use their saved result.
    • Pass maxRows for a preview; truncated says whether more rows exist.
  2. autoMapColumns(headers, schema, access) suggests a column for each header. Exact label/key matches get confidence 1; fuzzy or abbreviated matches score below 1. Only columns with "edit" access that aren't formulas are ever suggested.
  3. validateRows(parsed, mapping, schema, registry, { mode, keyColumnId, unknownOptions, limit?, access? }) runs every mapped cell through its field type's parse, checks required fields and keys, and applies the unknown-option policy ("create" collects labels in summary.newOptions; "reject" makes them cell errors). summary.unknownOptions lists every distinct unknown label per column under either policy, and each failed cell carries an errorKind ("unknownOption", "parse" or "required") next to its message. Mapping problems throw ImportConfigError before any row is read. In update/upsert the key column's type must be in KEY_COLUMN_TYPES (text, longText, email, phone or url).
  4. toChangeBatches(report, existingRowsByKey, { schema, registry, mode, keyColumnId }) returns an ImportPlan: flat creates (split them with chunkRows), update ChangeBatches (source: "import", at most 500 rows each), rejected rows, and unchangedSourceRows (rows identical to what is stored, which are left out).
  5. On the server, create any newOptions first, send each chunk, and feed every result to recordChunkResult starting from createImportJobState(total, plan.rejected, plan.unchangedSourceRows.length). buildErrorReportCsv(state, parsed) produces a downloadable CSV of the failed rows.

existingRowsByKey must be built with keyOf

The server job runner must build the lookup map with the exported keyOf(value, keyColumn, registry), which is the field type's format, trimmed and lowercased. The same function is used for duplicate-key detection in validateRows and for matching in toChangeBatches. If two stored rows produce the same key, the server must report them instead of letting one overwrite the other.

Export

buildExport({ columns, registry, rows, format, tz, fileName, access, sheetName? }) returns a Blob in the browser and a Node Readable in Node. buildExportBlob and buildExportStream pick a path explicitly. rows can be an array or an AsyncIterable, which the stream paths read lazily.

  • XLSX cells are typed: numbers and currency are numbers with a number format, dates and datetimes are Excel dates (datetimes shown as clock time in tz), booleans are booleans, URLs are hyperlinks. The header row is bold and frozen; widths come from ColumnDef.width.
  • CSV starts with a UTF-8 BOM and uses \r\n. Text that could run as a spreadsheet formula (starting with =, +, -, @, tab or CR/LF) gets a leading '. Numbers, dates, phone numbers and booleans are written unchanged.

Access fails closed

Every export and formatForClipboard(..., { access }) throws HiddenColumnError before writing anything if any requested column is "hidden" or missing from the access map. Pass only the columns the user can read.

Timezones

Pass the same tz to export and to parseFile. XLSX has no timezones: export writes a datetime as the clock time in tz, and import reads a clock time back as an instant in tz.

Clipboard

formatMatrixForClipboard / parseClipboard use Excel's TSV rules: cells containing a tab, newline or " are quoted, "" is a literal quote, one trailing row break is dropped, and ragged rows are padded. formatForClipboard(rows, columns, registry) formats each cell with its field type's format.

Core integration

Every core contract comes from @ranjeetk25/schema-grid-core through one shim, src/internal/core.ts (the only file allowed to import core). It imports types only, so no core code is bundled into ./clipboard.

Open item (TODO(core)): getSelectOptions reads config.options itself; replace it with a core option accessor if core exports one.

Import applies its own unknown-option policy (unknownOptions: "create" | "reject") to select, creatableSelect and multiSelect columns, independent of each column's allowCreate setting; other types' pendingOptions are honoured through the same policy.