DocxSax.Tool
0.1.0-rc.1
dotnet tool install --global DocxSax.Tool --version 0.1.0-rc.1
dotnet new tool-manifest
dotnet tool install --local DocxSax.Tool --version 0.1.0-rc.1
#tool dotnet:?package=DocxSax.Tool&version=0.1.0-rc.1&prerelease
nuke :add-package DocxSax.Tool --version 0.1.0-rc.1
docx-sax
docx-sax is a standalone developer tool for parsing .docx/OpenXML packages as a typed, low-level event stream.
The core is a .NET 8 library built on the Microsoft Open XML SDK. It exposes OpenXML-oriented events such as package, part, relationship, element, text, and diagnostic records. It deliberately preserves low-level details: part URIs, relationship types, XML names/namespaces, attributes, depth, paths, and event ordinals.
For adapter-selection guidance written for humans and LLM/agent consumers, see LLMS.md. The Next.js demo also serves the same file at /LLMS.md when deployed.
Current status
This repository currently contains the core .NET library scaffold, a minimal forward-only reader for simple Word documents, a CLI JSONL adapter over the typed event stream, an initial Linux x64 Node N-API wrapper backed by a .NET Native AOT shared library, and a browser/WASM bridge spike, and namespaced npm runtime plugin packages.
JavaScript API model
The public JavaScript wrappers share one event model and differ only by input transport:
@docx-sax/nodeaccepts file paths withparseFile(path, options)andparseFileBatches(path, options)and loads the@docx-sax/native-linux-x64payload on Linux x64.@docx-sax/browseraccepts bytes/blob inputs withparseBytes(input, options)andparseBytesBatches(input, options)and owns the browser/WASM adapter.
All four functions return async iterables over the same DocxSaxEvent object union. The non-batched helpers yield one event at a time; the batched helpers yield DocxSaxEvent[] chunks and accept the shared { batchSize } option. Transport-specific options stay transport-specific (nativeLibraryPath for Node, dotnetModuleUrl for browser/WASM).
The package ships TypeScript declarations for the common event union: package/part lifecycle events, relationships, XML element starts/ends, text events, diagnostics, attributes, ordinals, paths, namespaces, and part URIs. The wrappers intentionally do not expose a whole-document array API; consumers should iterate the stream or batches.
Non-goals
- No JSON-first API in the core library.
- No application-level semantic events such as paragraphs, headings, comments, or product-specific concepts.
- No authoring workflow or document transformation API.
- No product-layer concepts, this is a walker-style SAX parser
Repository layout
DocxSax.sln
src/DocxSax/ # .NET 8 typed event reader library
src/DocxSax.Tool/ # .NET 8 CLI/global-tool JSONL adapter
src/DocxSax.Native/ # .NET 8 Native AOT C ABI bridge for Node/native hosts
src/DocxSax.Browser/ # .NET 8 browser-wasm JSExport bridge spike
packages/node/ # @docx-sax/node user-facing Node adapter
packages/native-linux-x64/ # @docx-sax/native-linux-x64 Linux x64 native runtime payload
packages/native-darwin-arm64/ # @docx-sax/native-darwin-arm64 placeholder payload
packages/native-darwin-x64/ # @docx-sax/native-darwin-x64 placeholder payload
packages/native-win32-x64/ # @docx-sax/native-win32-x64 placeholder payload
packages/browser-wasm/ # @docx-sax/browser browser adapter + WASM assets
demos/nextjs-wasm/ # Next.js browser/WASM demo
test/DocxSax.Tests/ # generated DOCX fixtures, golden JSONL, and tests
docs/ # design notes as the project grows
Stack plan
- Core .NET typed event API.
- CLI JSONL adapter over the typed API (this layer).
- Node N-API bindings (initial Linux x64 v0 is present).
- WASM package.
- Next.js demo using the public adapters.
Build, test, and validation
dotnet restore
dotnet build --configuration Release
dotnet test --configuration Release
dotnet format --verify-no-changes --verbosity minimal
dotnet pack --configuration Release --output artifacts/packages
npm install
npm run build --workspace docx-sax
npm run test --workspace docx-sax
npm run build --workspace @docx-sax/native-linux-x64
npm run test --workspace @docx-sax/native-linux-x64
npm run build --workspace @docx-sax/browser
npx playwright install chromium
npm run test --workspace @docx-sax/browser
CI runs these checks on Ubuntu, Windows, and macOS with .NET 8. It also uploads Cobertura coverage artifacts, validates that CLI JSONL output parses line-by-line as JSON, installs the packed DocxSax.Tool from the local package output, and performs a Native AOT publish check for the CLI on each runner RID. The Linux leg additionally builds/tests the Node N-API wrapper and runs the browser/WASM Vite + Playwright smoke.
NuGet package IDs:
DocxSaxfor the core library.DocxSax.Toolfor thedocx-sax.NET global/local tool command.
Version strategy
DocxSax and DocxSax.Tool use one repo-wide SemVer, centralized in Directory.Build.props.
Early public packages start at 0.1.0-alpha.1. The project stays in 0.x while the typed event contract and JSONL schema are evolving. Use prerelease versions for all early packages, and publish only the top merged artifact from a stacked PR series rather than every intermediate branch.
Version bump guidance:
- Patch/prerelease increments (
0.1.0-alpha.N) for validation, fixtures, docs, CI, and compatible implementation changes. - Minor prerelease increments (
0.2.0-alpha.N, etc.) for meaningful event-model or schema changes. 1.0.0only after typed event names/properties are stable, JSONL compatibility is documented, and Node/WASM adapters have proven the core API shape.
CLI usage
dotnet run --project src/DocxSax.Tool -- parse document.docx --jsonl
The docx-sax global tool command shape is:
docx-sax parse input.docx --jsonl
JSONL is an adapter format, not the core primitive. Each line is one typed event serialized deterministically to stdout. Human diagnostics and parse failures are written to stderr; invalid or corrupt input exits nonzero.
Event type values:
packagewithphase: "start" | "end"partwithphase: "start" | "end", part URI, content type, and relationship typerelationshipwith source URI, relationship id/type, target URI, and external flagelementfor XML element starts, including names, namespace, depth, path, empty-element flag, and attributestextfor XML text-like nodes, including text, depth, path, and whitespace flagendfor XML element endsdiagnosticfor non-fatal reader diagnostics
Node wrapper usage
import { parseFile, parseFileBatches } from '@docx-sax/node';
for await (const event of parseFile('document.docx')) {
console.log(event.type, event.ordinal);
}
for await (const batch of parseFileBatches('document.docx', { batchSize: 256 })) {
// batch is DocxSaxEvent[] from the same model used by @docx-sax/browser
}
See docs/native-node.md for local validation and current transport caveats. The v0 native plugin validates on Linux x64 first and is published as @docx-sax/native-linux-x64; @docx-sax/node is the ergonomic Node import path.
Browser/WASM usage
import { parseBytes, parseBytesBatches } from '@docx-sax/browser';
const response = await fetch('/document.docx');
const bytes = new Uint8Array(await response.arrayBuffer());
for await (const batch of parseBytesBatches(bytes, { batchSize: 256 })) {
// batch is DocxSaxEvent[] from the same model used by @docx-sax/node
}
for await (const event of parseBytes(bytes)) {
console.log(event.type, event.ordinal);
}
See docs/browser-wasm.md for local validation, artifact-size measurements, and current caveats. The spike validates that OpenXML SDK can parse a small generated DOCX fixture in browser WASM, but the published runtime is still large and remains an alpha plugin package (@docx-sax/browser).
Next.js WASM demo
demos/nextjs-wasm is an isolated Next.js app that loads @docx-sax/browser in a client component, accepts a .docx upload, parses it through the browser WASM bridge, and renders a simple text preview from text events grouped by paragraph ends. It also exposes the safe public smoke fixture at /fixtures/simple.docx.
cd demos/nextjs-wasm
npm install
npm run build:wasm # publishes packages/browser-wasm WASM assets, then copies _framework/ into public/docx-sax/
npm run build # regular Next/Vercel build; intentionally does not run dotnet publish
npm run test:e2e # uploads public/fixtures/simple.docx and verifies WASM parse + preview render
For a single local validation command, run npm test. Vercel should use the plain npm run build path after WASM assets have been prepared by CI or a package/artifact step; generic Vercel builds should not republish the heavy .NET browser workload.
Minimal library usage
await using var stream = File.OpenRead("document.docx");
var reader = new DocxSaxReader();
foreach (DocxEvent docxEvent in reader.Read(stream))
{
// Pattern-match PackageEvent, PartEvent, RelationshipEvent,
// ElementStartEvent, ElementEndEvent, TextEvent, DiagnosticEvent.
}
License
MIT
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
This package has no dependencies.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.1.0-rc.1 | 82 | 5/10/2026 |
| 0.1.0-rc.0 | 69 | 5/10/2026 |