
Company News
Socket Joins New OpenJS Program to Fund Node.js Security Work
Socket is joining the OpenJS Security Stewardship Program to fund Node.js vulnerability research, maintainer remediation, and security releases.
@uekichinos/quire
Advanced tools
Small, dependency-light .xlsx read + write for Node and the browser. Worksheets, typed cells, styles, dates, formulas, merged cells — one dependency, no legacy bloat.
A small, dependency-light .xlsx reader + writer for Node and the browser.
fflate for zip packagingDate / formula) ·
a de-duplicated style pool · merged cells · column widths · freeze panes ·
auto-filter · row heights. Deterministic Uint8Array / Blob outputnpm install @uekichinos/quire
import { createWorkbook } from '@uekichinos/quire'
const wb = createWorkbook()
const sheet = wb.addWorksheet('Sales')
const header = { font: { bold: true, color: 'FFFFFF' }, fill: '2F5597' }
sheet.addRow(
[
{ value: 'Product', style: header },
{ value: 'Revenue', style: header },
{ value: 'Updated', style: header },
],
)
sheet.addRow(['Widget', { value: 15003.4, style: { numFmt: '#,##0.00' } }, new Date(2024, 2, 1)])
sheet.addRow(['Gadget', { value: 2450, style: { numFmt: '#,##0.00' } }, new Date(2024, 2, 3)])
sheet.addRow(
['Total', { value: { formula: 'SUM(B2:B3)', result: 17453.4 } }],
{ style: { font: { bold: true }, border: { top: { style: 'thin' } } } },
)
// layout
sheet.merge('A1:C1')
sheet.setColumn(1, { width: 24 })
sheet.freeze({ ySplit: 1 })
sheet.autoFilter('A1:C1')
sheet.addRow(['tall row'], { height: 30 })
sheet.setCell('E1', 'note', { align: { wrapText: true } })
// …or up front
wb.addWorksheet('Q2', { columns: [{ width: 20 }], freeze: { ySplit: 1 }, autoFilter: 'A1:C1' })
const bytes = wb.xlsx() // Uint8Array
// Node
import { writeFileSync } from 'node:fs'
writeFileSync('sales.xlsx', bytes)
// Browser
const url = URL.createObjectURL(wb.blob())
createWorkbook(): WorkbookWorkbook | |
|---|---|
addWorksheet(name, options?) | options: { columns?, freeze?, autoFilter? }. Name: 1–31 chars, unique, no \ / ? * [ ] : |
defineName(name, sheetName, range) | workbook-scoped named range, e.g. wb.defineName('SalesRange', 'Sales', 'A1:B10') |
xlsx() → Uint8Array | deterministic |
blob() → Blob | spreadsheetml.sheet mime |
Worksheet (all chainable) | |
|---|---|
addRow(values, { style?, height? }) | values: CellInput[] — a bare value or { value, style } |
setCell(ref, value, style?) | write to any A1 reference |
setRow(i, { style?, height? }) | style/size a row even with no cells |
setColumn(i, { width?, hidden?, style? }) | 1-based; column style resolves column < row < cell |
merge(range) | 'A1:C1'; rejects single-cell / backwards / overlapping |
freeze({ xSplit?, ySplit? }) | frozen panes; freeze({}) clears |
autoFilter(range) | header filter dropdowns |
Cell values: string · number · boolean · Date · { formula, result? } · { hyperlink, text?, tooltip? } · null. Dates use the 1900 serial system
(default format yyyy-mm-dd). null / undefined cells are skipped but keep
column position. Non-finite numbers and pre-1900 dates throw.
Hyperlinks: { hyperlink: 'https://…', text?, tooltip? } as a cell value —
text (defaults to the URL itself) becomes the cell's string, hyperlink any
URI (http(s)://, mailto:, …) up to 2,079 characters. Re-setting the cell to
a non-hyperlink value drops it. sheet.setCell('A1', { hyperlink, text }, style)
combines it with a style.
Defined names: wb.defineName(name, sheetName, range) — sheetName must
already be added, range a single cell ('A1') or a range ('A1:C3'). Names
follow Excel's identifier rules (start with a letter/_/\, then letters,
digits, _ or .; can't read as a cell reference; can't use the reserved
_xlnm. prefix) and must be unique (case-insensitively). Workbook-scoped only
— sheet-scoped names aren't supported yet.
Row/column default styles: sheet.setRow(i, { style }) / setColumn(i, { style }) now also mark the row/column itself with that default style (not
just each populated cell), so empty cells in a styled row or column show the
right formatting in Excel too.
CellStyle: font (name, size, bold, italic, underline, color)
· fill ('RRGGBB' / 'AARRGGBB') · align (horizontal, vertical,
wrapText, indent) · border (top/right/bottom/left/all →
{ style, color? }) · numFmt (format code or built-in id). Every distinct
style is interned once. Cell style merges over row style over column style, one
nested level deep.
Run node examples/hello.mjs (after pnpm build) for a full example.
0.2.0)import { readWorkbook } from '@uekichinos/quire'
const wb = readWorkbook(bytes) // Uint8Array | ArrayBuffer
wb.sheetNames // ['Sales', …]
const s = wb.sheet('Sales') // by name or index
s.dimension // { rows, cols }
s.merges // ['A1:C1', …]
s.cell('B2') // { ref, row, col, type, value, formula? }
s.values() // (string|number|boolean|Date|null)[][]
wb.definedNames // [{ name, sheetName?, range?, refersTo, hidden? }]
for (const row of s.rows()) { // sparse: row[col-1]
console.log(row.map((c) => c?.value))
}
ReadCell.type is 'string' | 'number' | 'boolean' | 'date' | 'formula' | 'error' | 'empty'. Numbers with a date format become Date (opt out with
readWorkbook(bytes, { dates: false })) — formula cells get the same
treatment from their cached result. Every cell also carries .numFmt (the
resolved format code) when it has one, and .hyperlink ({ target, tooltip? })
when it carries one — external targets resolved via the worksheet's own
relationships, in-workbook links via location. Formula cells carry both
.formula (no leading =) and .value (the cached result). { sheets: [name|index] } loads a subset.
readWorkbookAsync(bytes, options?) returns the same ReadWorkbook but yields
to the event loop between sheets, so a large import doesn't block. s.values({ ragged: true }) keeps each row's own length instead of padding to the sheet
width.
Limits. readWorkbook(bytes, { limits }) caps maxTotalBytes (100 MB),
maxPartBytes (50 MB), maxCells (5 000 000) and maxSheets (256); the
archive is inflated part-by-part and aborted mid-stream when a cap trips. Over
the limit throws QuireError.
Styles on read — readWorkbook(bytes, { styles: true }) resolves a
ReadStyle onto each cell (font, fill, border, align, numFmt), mirror
of the writer's CellStyle. rgb colours resolve exactly; indexed colours use
the standard palette; theme colours are read from xl/theme (with the 0/1 swap
and an approximate tint). Off by default — most imports only need values.
Under the same option, sheet.columnStyles / sheet.rowStyles (Map<number, ReadStyle>, 1-based) expose column/row default styles — what an empty
cell (no <c> element at all) in that column/row would look like.
Defined names — wb.definedNames lists user-defined named ranges
(_xlnm._FilterDatabase and other Excel-internal names are excluded). Each
entry resolves sheetName/range when the reference is a simple single-sheet
range; anything else (a formula, a multi-area reference, a named constant)
keeps refersTo with sheetName/range left undefined.
Deliberately strict and small. The XML is parsed by a ~200-line in-house
tokenizer (not a dependency) that rejects <!DOCTYPE>, <!ENTITY>, <
Company News
Socket is joining the OpenJS Security Stewardship Program to fund Node.js vulnerability research, maintainer remediation, and security releases.

Security News
Two compromised GitHub Actions were re-enabled with malicious tags intact, exposing thousands of downstream repositories to Mini Shai-Hulud.

Research
/Security News
A malicious Firefox extension fetches its payload after installation to evade detection, steal Google session cookies, and automate account takeover.