JavaScript API

A fluent API that reads like English. Every edit is tracked by default. Author is set once and applied to everything.

const docex = require('./src/docex');
const doc = docex("paper.docx");
doc.author("Alex Chen");

Opening and saving

docex(path)

Open a .docx file. Returns a document object.

const doc = docex("paper.docx");
doc.author(name)

Set the author name for all subsequent operations. Applied to tracked changes and comments.

doc.author("Alex Chen");
await doc.save(path?)

Save the document. Overwrites the original by default. All queued operations are applied in a single zip cycle. Auto-verifies after save.

await doc.save();                      // overwrite original
await doc.save("revised.docx");        // save to new file
await doc.save({ dryRun: true });      // preview without saving
await docex.create(path)

Create an empty .docx file from scratch.

await docex.create("new-paper.docx");
doc.untracked()

Disable tracked changes for subsequent operations. Edits will be applied directly.

doc.untracked();
doc.replace("old", "new");  // no tracked change record

Position selectors

Target any location in the document. Like CSS selectors for documents.

doc.at(text)

Select the position at a phrase. Used for comments, formatting, and inline operations.

doc.at("digital wellbeing").comment("Needs citation");
doc.at("measurement validity").bold();
doc.at("12,847").highlight("yellow");
doc.after(text)

Select the position after a heading or text. Used for inserting new content.

doc.after("Methods").insert("We used a mixed-methods approach.");
doc.after("Results").figure("fig03.png", "Figure 3. Rates.");
doc.after("Results").table([["A","B"],["1","2"]]);
doc.before(text)

Select the position before a heading or text.

doc.before("Conclusion").insert("Final remarks before conclusion.");
doc.id(paraId)

Select a paragraph by its stable ID. IDs survive other edits, making them reliable anchors.

doc.id("3A7F2B1C").replace("old text", "new text");

Text operations

doc.replace(old, new)

Replace text. Shows as a tracked change (strikethrough + insertion) in Word.

doc.replace("measurement validity", "regulatory gap");
doc.replaceAll(old, new)

Replace all occurrences of a string throughout the document.

doc.replaceAll("GDPR", "GDPR/DPA");
doc.delete(text)

Delete text (tracked). Shows as strikethrough in Word.

doc.delete("redundant sentence here");
doc.after(anchor).insert(text, opts?)

Insert a new paragraph after a heading or text.

doc.after("Methods").insert("We used a mixed-methods approach.");
await doc.find(text)

Find all occurrences of text in the document.

const results = await doc.find("sleep");
// [{paragraph: 3, text: "...the research dataset..."}]

Formatting

doc.at(text).bold() / .italic() / .underline()

Apply basic formatting to selected text.

doc.at("significant findings").bold();
doc.at("p < 0.05").italic();
doc.at("critical note").underline();
doc.at(text).highlight(color)

Highlight text with a Word highlight color.

doc.at("needs review").highlight("yellow");
doc.at(text).color(color)

Set text color.

doc.at("warning").color("red");
doc.at(text).strikethrough() / .superscript() / .subscript() / .smallCaps() / .code()

Additional formatting options.

doc.at("removed text").strikethrough();
doc.at("2").superscript();     // x2 becomes x^2
doc.at("i").subscript();       // xi becomes x_i
doc.at("Author Name").smallCaps();
doc.at("var x = 1").code();
doc.at(text).footnote(text)

Add a footnote anchored to text.

doc.at("finding").footnote("See appendix B for full results.");

Comments

doc.at(anchor).comment(text, opts?)

Add a comment anchored to a phrase.

doc.at("digital wellbeing").comment("Needs citation", { by: "Reviewer 2" });
doc.at(anchor).reply(text, opts?)

Reply to an existing comment (threaded reply in Word).

doc.at("digital wellbeing").reply("Added Johnson 2022", { by: "Alex Chen" });
await doc.comments()

List all comments in the document.

const comments = await doc.comments();
// [{id: 1, author: "Reviewer 2", text: "Needs citation", anchor: "digital wellbeing"}]
await doc.exportComments(format)

Export all comments to CSV or JSON.

const csv = await doc.exportComments("csv");

Figures and tables

doc.after(anchor).figure(imagePath, caption, opts?)

Insert a figure after a heading or paragraph. Auto-detects image dimensions.

doc.after("Results").figure("fig03.png", "Figure 3. Effect sizes by study.");
doc.after(anchor).table(data, opts?)

Insert a table. Supports booktabs styling and captions.

doc.after("Results").table(
  [["Study", "N", "Effect Size"],
   ["Smith 2023", "342", "d = 0.45"],
   ["Lee 2022", "289", "d = 0.38"]],
  { style: "booktabs", caption: "Table 1. Effect sizes by study." }
);
doc.after(anchor).bulletList(items) / .numberedList(items)

Insert a bullet or numbered list.

doc.after("Key findings").bulletList([
  "Screen time >4h/day associated with 23% reduction in sleep quality",
  "Only 14.2% of studies used objective sleep measures",
  "Average effect size: d = 0.42"
]);
await doc.figures()

List all figures in the document.

const figs = await doc.figures();
// [{rId: "rId5", src: "word/media/image1.png", width: "5486400emu"}]

Tracked changes

await doc.revisions()

List all tracked changes.

const revs = await doc.revisions();
// [{id: 17, type: "del", author: "Research Assistant", text: "12,847 records"}]
await doc.accept(id?) / await doc.reject(id?)

Accept or reject tracked changes. Pass an ID for a specific change, or omit to apply to all.

await doc.accept();      // accept all
await doc.accept(5);     // accept only change #5
await doc.reject();      // reject all
await doc.cleanCopy()

Create a clean copy with all tracked changes accepted and markup removed.

await doc.cleanCopy();
await doc.diff(otherPath)

Compare two documents and produce a .docx with tracked changes showing the differences.

await doc.diff("paper-v1.docx");

.dex format

DexDecompiler.decompile(workspace)

Convert a .docx workspace into .dex plain text format.

const { DexDecompiler } = require('./src/dex-decompiler');
const dex = DexDecompiler.decompile("paper.docx");
// Returns the full .dex string
DexCompiler.compile(dexString)

Convert a .dex string back into a .docx file.

const { DexCompiler } = require('./src/dex-compiler');
await DexCompiler.compile(dexString, "output.docx");

Journal styles

await doc.style(preset)

Apply a journal formatting preset. Sets fonts, margins, spacing, headers, and page layout.

await doc.style("polcomm");   // Political Communication
await doc.style("apa7");     // APA 7th Edition
await doc.style("jcmc");     // Journal of Computer-Mediated Communication
await doc.style("joc");      // Journal of Communication
await doc.style("academic"); // Generic academic
await doc.verify(preset)

Validate the document against journal requirements.

const result = await doc.verify("polcomm");
// { pass: false, issues: ["Word count 8200 exceeds limit 8000"] }

Document health

await doc.validate()

Run the document doctor. Checks for corrupt zip structure, orphaned images, broken relationships, duplicate paragraph IDs, and heading hierarchy problems.

const health = await doc.validate();
// { issues: [], healthy: true }
await doc.wordCount()

Get word counts broken down by section.

const counts = await doc.wordCount();
// { body: 6800, headings: 45, abstract: 250, captions: 120, footnotes: 85, total: 7300 }
await doc.metadata()

Get document metadata (title, author, creation date, etc.).

const meta = await doc.metadata();
// { title: "The Effects of Social Media on Sleep Quality", creator: "Alex Chen", created: "2026-02-15" }
await doc.headings() / await doc.paragraphs()

List all headings or paragraphs with their IDs.

const headings = await doc.headings();
// [{level: 1, text: "Introduction", index: 5}]

const paras = await doc.paragraphs();
// [{id: "3A7F2B1C", text: "We analyzed 12,847...", index: 12}]
await doc.anonymize()

Anonymize for blind peer review. Removes author names, affiliations, and identifying information.

await doc.anonymize();
doc.preview()

Preview all pending operations without applying them.

doc.replace("old", "new");
doc.at("text").comment("note");
const ops = doc.preview();
// [{type: "replace", old: "old", new: "new"}, {type: "comment", anchor: "text"}]

Batch operations

docex.batch(files)

Apply operations to multiple documents at once.

docex.batch(["paper1.docx", "paper2.docx"]).forEach(doc => {
  doc.style("apa7");
  doc.save();
});
await docex.fromTemplate(opts)

Create a new document from a journal template.

await docex.fromTemplate({
  title: "My Paper",
  journal: "polcomm",
  sections: ["Introduction", "Methods", "Results", "Discussion"]
});
await docex.responseLetter(opts)

Generate a response-to-reviewers letter from a JSON spec.

await docex.responseLetter({
  manuscript: "paper.docx",
  responses: "responses.json",
  output: "response-letter.docx"
});

Export

await doc.toLatex() / await doc.toHtml() / await doc.toMarkdown()

Export the document to other formats.

const latex = await doc.toLatex();
const html = await doc.toHtml();
const md = await doc.toMarkdown();