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

@quario/csv

v0.9.1

Published

Tiny, dependency-free CSV render target for quario. Typed fields, and text that cannot open as a formula.

Readme

@quario/csv

The CSV render target for quario. Renders a report definition to CSV records with typed fields. A bare number stays a number's decimal, and a Date stays ISO 8601 UTC. This target neutralizes text that would open as a spreadsheet formula.

Install

npm install quario @quario/csv

The engine is a peer dependency, installed beside the target. ESM-only, Node 22+, and browser-ready through any standards-based ESM bundler. The renderer returns a string. You encode the file.

Quick start

import { writeFileSync } from "node:fs";
import { csv } from "@quario/csv";
import { quario } from "quario";

const schema = {
  data: "$.orders[*]",
  detail: {
    columns: [
      { header: "Product", value: "{{ @.product }}" },
      { header: "Price", value: "{{ @.price }}" },
    ],
  },
};

const data = {
  orders: [
    { product: "Desk", price: 250 },
    { product: "Chair", price: 120 },
    { product: "Lamp", price: 40 },
  ],
};

const report = quario().report(schema);
const records = await report.render(csv(), data);

writeFileSync("sales.csv", records);

A licensed render of that table-only report:

Product,Price
Desk,250
Chair,120
Lamp,40

Any schema the other targets render works here unchanged. A CSV file has no pages, so this target ignores page bands. Interpolate the bare value, not a formatter, when you want a typed field. {{ @.amount }} writes 1000, and {{ currency(@.amount) }} writes text.

API

csv()

The target factory takes no options and returns the target you pass to render. It refuses an argument rather than discarding one. It throws a TypeError if you give it one. report() compiles once and report.render(csv(), data) resolves the string. Compile at startup, render per request. Definition problems throw at report(), at compile time.

const report = quario().report(schema, funcs);
const records = await report.render(csv(), data);

The compiled report carries stream (the raw event generator), names, functions, and paths, like every quario report. Engine-level options (query budgets, the license key) live on the instance, and q.license settles with the verification result.

Rendering is asynchronous and returns the loop between batches, so a large report never blocks the host. Render-time failures reject with located errors.

Mapping

The banded walk flattened as records in render order: report-header items, then per group instance its header items, nested content, and footer items, then the table's header record, data records, and total records, then report-footer items. Grouping exists as that order alone. Group boundaries add no records of their own, and this target ignores break and reset. This target paginates nothing, so it ignores report-start.page band closures.

Each item takes one record with one field. A split takes one record with a field per slot, and a spanning cell writes empty fields for the columns it covers so positions hold. A report that is one table and no other bands has its column headers as record 1. Any other band makes the file ragged.

Typed fields

A cell whose value is one bare interpolation writes its pre-stringify value as text: a finite number as ECMAScript ToString, a boolean as true/false, a Date as ISO 8601 UTC. Every other cell joins to display text with text(), then the injection guard, then RFC 4180 quoting.

This target mangles display-text fields that would open as spreadsheet formulas. A leading =, +, -, @, TAB, or CR gets an apostrophe. A consumer that parses the file back does not round-trip those fields. The XLSX target does the opposite: it writes an inert string and does not mangle. See The CSV target.

A hidden cell keeps its column slot as an empty field.

Withdrawn, and the unread page bands

This target withdraws style, column width, slot width, page columns and images. A record carries no presentation, no width and no page, so there is nothing here for any of them to resolve against. An image emits no record, though an image in a slot still writes its empty field. Page bands are the one unread declaration. This target never calls their closures, so an expression inside one never runs and never raises here.

Text shape

No BOM. Records separated by LF, not CRLF. The string ends with a newline. Quoting follows RFC 4180. The line ending does not.

Unlicensed marking

An unlicensed render writes the wording from report-start.marking as one trailing line, one field, through the same quote and guard as any field. A licensed render omits it. The line's presence is normative. That a parser who stops before EOF will see it is best-effort.

Determinism

Same schema and data produce an identical string.

The full contract is The CSV target, with each declaration's fate in the support matrix.

Documentation

The quario documentation is the reference. The report schema is the normative specification of what a report may declare, and @quario/csv is this package's own API.

License

Commercial software with readable source. Evaluation is free, unlimited, and watermarked. Per-developer licenses at getquario.com. See the bundled LICENSE.

Pass your license key once, on the instance. quario verifies it offline:

const q = quario({ license: "quario_..." });
await q.license; // { licensed: true, licensee: "Acme BV", id: "1-ACME" }