Overview
Office.js-compatible editing API: an object model that batches its work, running on a server over bytes or in a page against an editor already open.
@docx-editor.dev/editor-api is an Office.js-compatible editing API for DOCX. It implements the same object model (context.document.body.paragraphs, load() then sync(), search(), getFirstOrNullObject()) so code written for a Word add-in compiles and runs here.
You describe work against objects (paragraphs, ranges, comments, revisions, content controls) and one sync() sends it as a single ordered batch that either applies whole or not at all. The same code runs in two places: on a server over DOCX bytes, or in a page against a document a reader already has open.
npm install @docx-editor.dev/editor-apiOn a server
Runs headless: it takes bytes and returns bytes.
import { readFile, writeFile } from 'node:fs/promises';
import { DocxEditor } from '@docx-editor.dev/editor-api';
const runtime = await DocxEditor.createServer(await readFile('contract.docx'), {
author: 'Review bot',
});
try {
await runtime.run(async (context) => {
const matches = context.document.body.search('$50k');
matches.load();
await context.sync(); // one round trip: now you know what was found
for (const match of matches.items) match.insertText('$500k', 'Replace');
await context.sync(); // one atomic batch: all of the writes, or none
});
await writeFile('contract.filled.docx', await runtime.save());
} finally {
runtime.dispose();
}In the browser
The browser entry takes an editor the host already created (from @docx-editor.dev/react or a plain page) and drives it in place. Edits land in the open document with the reader's undo stack intact, so there is no save(): the host saves the way it already did.
import { DocxEditor } from '@docx-editor.dev/editor-api/browser';
const runtime = DocxEditor.createBrowser(editor);
await runtime.run(async (context) => {
const heading = context.document.body.paragraphs.getFirstOrNullObject();
heading.load('text');
await context.sync();
if (!heading.isNullObject) heading.font.bold = true;
await context.sync();
});The /browser entry includes integration with the painted engine. Import the root entry on servers
to keep that browser code out of the bundle.
Programming model
- Reading a property you did not
load()throws. This catches typos before they affect later writes. sync()is the only round trip. Everything queued between two syncs is one ordered transaction. If any operation in it is refused, none of them happened.- Objects are proxies into a document the runtime owns. They survive
sync()calls within onerun. To carry one into a laterrun, track it and pass it toruntime.run(object, callback)for adoption. No proxy survivesdispose(). getFirstOrNullObjectandgetLastOrNullObjectanswer an object whoseisNullObjectistrueafter the sync, which is the difference between "no such heading" and a crash.
Capabilities
runtime.capabilities reports host differences. save is false in the browser; selection,
scrolling and layout are false on a server. Branch on these values because they remain frozen
for the life of the runtime.
Entries
| Entry | Use when |
|---|---|
@docx-editor.dev/editor-api | Servers, workers, build scripts: bytes in, bytes out |
@docx-editor.dev/editor-api/browser | A page, driving an editor the host already created |
Both export the same vocabulary (the lifecycle types, the object model, the error type) so consumer code compiles against either. They differ by one member: createBrowser.
Building an AI feature on this
This package ships no model integration, tool catalog or chat UI. The application that owns the
model defines which operations to expose, how to describe them and how to handle refusals. A tool
such as add_comment performs its document work inside a run block. Keep its chat UI with the
application's other chrome.
License
This package is not Apache 2.0 like the editor packages. It is licensed under the EigenPal Pro Evaluation License 1.0, which lets you read, run and modify it internally, free of charge, to evaluate it. Production use (a live or customer-facing environment, live or business-operational data, or this package inside something you offer to others) requires a written commercial agreement, and so does redistribution. Commercial licensing: licensing@eigenpal.com.