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

ts-docx

v0.1.0

Published

Read, edit, and save existing Word (.docx) files in Node — a TypeScript port of python-docx.

Readme

ts-docx

Read, edit, and save existing Word (.docx) files in Node — a TypeScript port of python-docx.

Status: scaffold only — this folder holds this README and nothing else. The port is built in a dedicated session off this brief. Standalone and FlowDot-agnostic; the flowdot-documents engine (separate project) wraps it. This is the docx sibling of ts-pptx — same architecture, same fidelity guarantees, same conventions. Where this brief is thin, ts-pptx's shipped code is the reference (its src/xml/ lossless DOM, src/opc/ package layer, and fidelity model port directly).

The JS ecosystem has no python-docx equivalent for editing: mammoth only reads (→HTML/text), the docx package only creates. ts-docx opens an existing document, lets you read and edit its paragraphs/runs/tables/sections/styles, and saves it back — in pure TypeScript, with jszip as the only runtime dependency. Runs anywhere Node ≥ 18 runs (including the Electron main process); no native modules, no DOM.

  • Node ≥ 18 · ESM · MIT · one runtime dep (jszip)
  • Ergonomics mirror python-docx (snake_casecamelCase), so its docs and examples translate directly.

Why this exists

python-docx (MIT) is the proven design for programmatic Word editing. Port its layered architecture, not just its feature list. FlowDot's document engine needs full docx read/edit/save in pure TS so it runs identically in the Electron native app, the CLI, and the published MCP server — and so agents and users can edit real Word documents without a Python dependency or a bespoke local MCP server.

What to port (python-docx's layered architecture)

Mirror ts-pptx's layering exactly:

  1. src/xml/ — lossless XML parser / DOM / serializer. Reuse ts-pptx's (byte-span preserving; unknown elements survive verbatim). This is the backbone of the fidelity guarantee.
  2. src/opc/ — OPC package: zip open/save via jszip, [Content_Types].xml, relationships, part loading/serialization. Near-identical to ts-pptx's src/opc/.
  3. src/oxml/ — typed wrappers over WordprocessingML elements (w:document, w:body, w:p, w:r, w:rPr, w:pPr, w:tbl, w:sectPr, …).
  4. Public API (src/{text,table,section,styles,shape,parts}/), mirroring python-docx:
import { Document, Pt, Inches } from "ts-docx";

const doc = await Document.open("report.docx");     // path, Uint8Array, or ArrayBuffer

for (const p of doc.paragraphs) {
  console.log(p.style?.name, p.text);
  for (const run of p.runs) { /* run.text, run.bold, run.font.* */ }
}

// the formatting-preserving edit — change wording, keep the run's look (rPr untouched)
doc.paragraphs[3].runs[0].text = "revised sentence.";

doc.addHeading("Appendix", 1);
const p = doc.addParagraph("Body text ", "Normal");
p.addRun("bold bit").bold = true;

const table = doc.addTable(2, 3);
table.cell(0, 0).text = "Header";

await doc.save("report-edited.docx");               // or: const bytes = await doc.toBuffer();

Everything except open, save, toBuffer is synchronous (only those touch the zip container). The object model — paragraphs, runs, tables, sections — is all sync.

API surface to cover (python-docx parity + the additions below)

  • Document: Document.open(), save, toBuffer, paragraphs, tables, sections, styles, inlineShapes, coreProperties, addParagraph, addHeading, addPageBreak, addPicture, addTable, addSection.
  • Paragraph: text (get/set), runs, style, alignment (WD_ALIGN_PARAGRAPH), paragraphFormat (indents, spacing, keepWithNext, pageBreakBefore, tabs), addRun, insertParagraphBefore, clear.
  • Run: text (the formatting-preserving setter — rPr untouched, matching ts-pptx's run.text), bold/italic/underline, font (name, size, color.rgb / color.themeColor, highlightColor, strike, subscript/superscript, allCaps, smallCaps), styleId, addBreak, addTab, addPicture.
  • Tables: table.rows, columns, cell(r,c), rows[i].cells, cell.text, cell.paragraphs, cell.merge(other), addRow, addColumn, table style, alignment, autofit, column widths, cell vertical alignment/margins; read existing merged cells (cell.isMergeOrigin / span info).
  • Sections: pageWidth/pageHeight, leftMargin/right/top/bottom/gutter/header/footer, orientation, startType; headers & footers (section.header, section.footer, .isLinkedToPrevious, first-page/even-page variants) with their own paragraphs.
  • Styles: doc.styles collection — read/apply paragraph, character, table, list styles; style inheritance (basedOn).
  • Numbering / lists: apply list styles; read list level/numId (full numbering authoring can be a later milestone — flag, don't silently drop).
  • Images: addPicture(bytes|path, width?, height?) (inline), read inline shapes, replace image bytes keeping the shape (mirror ts-pptx picture.replaceImage).
  • Core properties: title/author/subject/keywords/comments/category/created/modified/lastModifiedBy/revision.

Additions beyond python-docx (high-value, python-docx lacks these)

  • Run-aware find & replacedoc.replaceText(search, replace, { all? }) that correctly handles text split across w:r runs (Word constantly splits mid-word on rsid/spellcheck boundaries). This is the #1 real docx-editing operation and the exact thing naive raw-XML regex replace (e.g. the old document-mcp-server) gets wrong. Extract run text into a logical string with an index→run map, match on the logical string, splice runs at boundaries, inherit the matched run's rPr. Ship it with a tests corpus of run-split fixtures.
  • Tracked changes & comments (display + preserve on round-trip at minimum; authoring is a flagged later milestone). Preservation is free from the fidelity model; authoring w:ins/w:del/comments is additive.
  • Reads never mutate (see below).

Units

EMU throughout for lengths, as branded number (mirror ts-pptx). Word also uses twips (page/margins) and half-points (font size) internally — expose the same unit constructors (Pt, Inches, Cm, Mm, Emu, Twips) and a Length converter; the wrappers handle the internal unit per property so callers always work in real units.

The fidelity model (identical to ts-pptx — non-negotiable)

  • Open → save with no edits is loss-free. Every untouched part is written back byte-identical (decompressed entry bytes; the zip container is re-deflated). Unknown/vendor XML survives untouched. Zip entries unreachable from the relationship graph are carried through verbatim (python-docx drops some of these — ts-docx must not).
  • An edit touches only what it must. Change one run's text and only that run's w:t changes.
  • Reads never mutate. Accessing font.color, paragraphFormat, etc. to read never writes XML (a deliberate divergence from python-docx, whose accessors can rewrite on read — required for the loss-free guarantee).

Constraints

  • Pure TypeScript. No native modules. Node ≥ 18 (Electron 28 main floor). Runs in Node and the Electron main process; no DOM.
  • Dependencies pass the FlowDot sanity test: frictionless in the native app, no security exposure, "should we build it ourselves?" answered honestly. Keep the tree at jszip only (build thin XML handling — ported from ts-pptx — rather than importing a framework).
  • License: MIT (© FlowDot LLC). All deps permissive. python-docx's MIT copyright/permission notice reproduced in LICENSE under "Third-party notices," as its license requires.
  • .docx is the open ECMA-376 (Office Open XML) standard.

Testing

  • Fixture corpus of real Word-authored documents in tests/assets/ (round-trip oracle) and fixtures/ (authored corpus): headings, styles, tables (incl. merged + nested), sections with headers/footers, inline images, lists, tracked changes, comments, run-split paragraphs.
  • Backbone test: round-trip every XML entry of every doc through parse → serialize, assert byte-equality.
  • API unit tests per surface; a dedicated run-split find-&-replace suite.
  • scripts/make-mN-gate.mjs per milestone → out/mN-*.docx for manual review in Word.
  • Vitest; tsc --strict clean; publish infra mirrors ts-pptx / ts-pdf-edit (eslint, .npmignore, verify-pack, CI publish with --provenance).

Reference

  • python-docx source + docs (MIT) — the architecture and API being ported.
  • ts-pptx shipped source — the XML/OPC layers and fidelity model to reuse verbatim.
  • ECMA-376 (WordprocessingML) for the XML specifics.
  • Consumer: flowdot-documents; keep this library FlowDot-agnostic.