bun-docx 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -7
- package/dist/index.js +30763 -4108
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -42,7 +42,7 @@ The repo includes a Common Paper Mutual NDA template at `tests/fixtures/mnda.doc
|
|
|
42
42
|
cp tests/fixtures/mnda.docx mnda-filled.docx
|
|
43
43
|
|
|
44
44
|
# Read the cover-page table so the agent knows what placeholders exist
|
|
45
|
-
docx read mnda-filled.docx --
|
|
45
|
+
docx read mnda-filled.docx --to t1
|
|
46
46
|
|
|
47
47
|
# Fill the yellow-highlighted bracketed placeholders
|
|
48
48
|
docx replace mnda-filled.docx "Fill in: today’s date" "May 6, 2026"
|
|
@@ -72,16 +72,26 @@ Open `mnda-filled.docx` in Word: tracked changes and comments appear in the revi
|
|
|
72
72
|
|
|
73
73
|
```sh
|
|
74
74
|
docx create FILE [--title T] [--author A] [--text "..."]
|
|
75
|
-
docx read FILE [--
|
|
75
|
+
docx read FILE [--from pN] [--to pN] [--accepted | --baseline | --current] [--comments]
|
|
76
|
+
docx read FILE --ast # JSON-AST opt-in (every other read flag is markdown-only)
|
|
76
77
|
docx insert FILE --after p3 --text "..." [--style HeadingN] [--color HEX] [--bold] [--italic] [--url URL]
|
|
77
78
|
docx insert FILE --after p3 --runs '[{"type":"text","text":"X","bold":true}]'
|
|
78
79
|
docx insert FILE --after p3 --page-break | --column-break
|
|
79
80
|
docx insert FILE --after p3 --section [--columns N] [--type continuous|nextPage|evenPage|oddPage|nextColumn]
|
|
81
|
+
docx insert FILE --after p3 --table --rows N --cols N [--widths "A,B,C"] [--table-width 100%] [--borders single|none|double] [--layout autofit|fixed]
|
|
82
|
+
docx insert FILE --after p3 --image SRC [--alt TEXT] [--width INCHES] [--height INCHES] # SRC = path, data: URI, or http(s) URL
|
|
83
|
+
docx insert FILE --after p3 --code "..." [--language LANG] # one CodeBlock paragraph per \n; --language → syntax highlight
|
|
84
|
+
docx insert FILE --after p3 --code-file PATH [--language LANG] | --code-file - # same, from file or stdin
|
|
80
85
|
docx edit FILE --at p3 --text "..." [--no-formatting] # word-level diff preserves bold/italic on unchanged words
|
|
81
86
|
docx edit FILE --at p3 --runs '[...]'
|
|
82
|
-
docx edit FILE --at
|
|
87
|
+
docx edit FILE --at p3 --code "..." [--language LANG] # replace paragraph with a code block (expands to N lines)
|
|
88
|
+
docx edit FILE --at p3 --code-file PATH [--language LANG]
|
|
89
|
+
docx edit FILE --at p2-p5 --text "..." # range replace: collapse paragraph range to one paragraph
|
|
90
|
+
docx edit FILE --at p2-p5 --code-file new.go --language go # range replace with a fresh code block
|
|
91
|
+
docx edit FILE --at s0 [--columns N] [--type T] # mutate section properties
|
|
83
92
|
docx delete FILE --at p3
|
|
84
|
-
docx delete FILE --at
|
|
93
|
+
docx delete FILE --at p2-p5 # range delete: drop a contiguous paragraph span
|
|
94
|
+
docx delete FILE --at s0 # strip an inline sectPr
|
|
85
95
|
|
|
86
96
|
docx find FILE QUERY [--regex] [--ignore-case] [--all] [--nth N] [--current | --baseline] [--exact]
|
|
87
97
|
docx replace FILE PATTERN REPLACEMENT [--regex] [--ignore-case] [--all] [--limit N] [--current | --baseline] [--exact] [--dry-run]
|
|
@@ -101,15 +111,34 @@ docx comments delete FILE --id c1 --id c3 # repeatable
|
|
|
101
111
|
docx comments delete FILE --batch removals.jsonl
|
|
102
112
|
docx comments list FILE [--include-resolved] [--thread c0]
|
|
103
113
|
|
|
114
|
+
docx footnotes add FILE --at pN[:offset] --text "..."
|
|
115
|
+
docx footnotes edit FILE --id fnN --text "..."
|
|
116
|
+
docx footnotes delete FILE --id fnN
|
|
117
|
+
docx footnotes list FILE
|
|
118
|
+
docx endnotes add FILE --at pN[:offset] --text "..."
|
|
119
|
+
docx endnotes edit FILE --id enN --text "..."
|
|
120
|
+
docx endnotes delete FILE --id enN
|
|
121
|
+
docx endnotes list FILE
|
|
122
|
+
|
|
104
123
|
docx images list FILE
|
|
105
124
|
docx images extract FILE --to ./media [--id imgN]
|
|
106
125
|
docx images replace FILE --at imgN --with ./new.png
|
|
126
|
+
docx images delete FILE --at imgN
|
|
107
127
|
|
|
108
128
|
docx hyperlinks list FILE
|
|
109
129
|
docx hyperlinks add FILE --at pN:S-E --url URL
|
|
110
130
|
docx hyperlinks replace FILE --at linkN --with URL
|
|
111
131
|
docx hyperlinks delete FILE --at linkN
|
|
112
132
|
|
|
133
|
+
docx tables insert-row FILE --at tN [--position INDEX] [--cells "a,b,c"]
|
|
134
|
+
docx tables delete-row FILE --at tN:rR
|
|
135
|
+
docx tables insert-column FILE --at tN [--position INDEX] [--width TWIPS]
|
|
136
|
+
docx tables delete-column FILE --at tN:cC
|
|
137
|
+
docx tables set-widths FILE --at tN --widths "25%,25%,50%" | "1440,..." | auto
|
|
138
|
+
docx tables merge FILE --at tN:rR1cC1-rR2cC2
|
|
139
|
+
docx tables unmerge FILE --at tN:rRcC
|
|
140
|
+
docx tables borders FILE --at tN [--style single|double|none] [--size N] [--color HEX]
|
|
141
|
+
|
|
113
142
|
docx track-changes FILE on|off
|
|
114
143
|
docx track-changes list FILE
|
|
115
144
|
docx track-changes accept FILE (--at tcN [--at tcM ...] | --all)
|
|
@@ -120,11 +149,11 @@ docx info locators [--json]
|
|
|
120
149
|
|
|
121
150
|
Every command has `--help`. Mutating commands accept `--dry-run`, `-o/--output PATH` (write to a parallel file instead of overwriting `FILE`), and `-v/--verbose` (print the JSON ack — see "Quiet by default" below).
|
|
122
151
|
|
|
123
|
-
**Quiet by default.** Mutators (`create`, `insert`, `edit`, `delete`, `replace`, `comments add/reply/resolve/delete`, `images replace`, `hyperlinks add/replace/delete`, `track-changes` toggle/accept/reject) print nothing on success and exit 0. Errors always print as `{ok: false, code, error, hint}`. Pass `-v`/`--verbose` to get the full JSON ack. Read commands (`read`, `find`, `wc`, `outline`, `info *`, `*-list`) print their data unconditionally. Batch operations that mint new ids — `comments add --batch`, `comments delete --batch`, `comments resolve --batch`, and `comments delete/resolve` with multiple `--id` — always print the affected ids since the agent can't reconstruct them otherwise.
|
|
152
|
+
**Quiet by default.** Mutators (`create`, `insert`, `edit`, `delete`, `replace`, `comments add/reply/resolve/delete`, `footnotes add/edit/delete`, `endnotes add/edit/delete`, `images replace/delete`, `hyperlinks add/replace/delete`, `tables *`, `track-changes` toggle/accept/reject) print nothing on success and exit 0. Errors always print as `{ok: false, code, error, hint}`. Pass `-v`/`--verbose` to get the full JSON ack. Read commands (`read`, `find`, `wc`, `outline`, `info *`, `*-list`) print their data unconditionally. Batch operations that mint new ids — `comments add --batch`, `comments delete --batch`, `comments resolve --batch`, and `comments delete/resolve` with multiple `--id` — always print the affected ids since the agent can't reconstruct them otherwise.
|
|
124
153
|
|
|
125
154
|
### Markdown rendering
|
|
126
155
|
|
|
127
|
-
`docx read FILE
|
|
156
|
+
`docx read FILE` renders the document body as GitHub-flavored Markdown — the default since v0.9. Useful when an LLM wants to skim a doc without parsing the AST. Each rendered paragraph is followed by an HTML comment with its locator (`<!-- p3 -->`) so the markdown is invisible-pinned: humans see clean prose in a renderer, agents parse the locators from raw text. Pass `--ast` for the structured JSON when programmatic walks need the typed tree (`docx read FILE --ast | jq '.blocks[] | select(.type == "paragraph")'`).
|
|
128
157
|
|
|
129
158
|
`--from LOC` and `--to LOC` slice by top-level block (both inclusive). Accepts paragraph, table, cell, span, and range locators; cell/span/range collapse to their enclosing top-level block.
|
|
130
159
|
|
|
@@ -160,8 +189,9 @@ The same `--current`/`--baseline` flags apply to `find`, `replace`, `wc`, and `c
|
|
|
160
189
|
|
|
161
190
|
```
|
|
162
191
|
pN paragraph N (e.g., p3)
|
|
192
|
+
pN-pM paragraph range (whole paragraphs pN..pM as a unit)
|
|
163
193
|
pN:S-E characters S..E within paragraph N
|
|
164
|
-
pN:S-pM:E cross-paragraph range
|
|
194
|
+
pN:S-pM:E cross-paragraph character range
|
|
165
195
|
tN table N; tN:rRcC for cell at row R, col C
|
|
166
196
|
sN section break N — column count, type (continuous /
|
|
167
197
|
nextPage / nextColumn / evenPage / oddPage)
|
|
@@ -182,12 +212,21 @@ Run `docx info locators` for the full reference.
|
|
|
182
212
|
|
|
183
213
|
**Cross-format image replacement.** `images replace --at img0 --with new.png` detects the new MIME type via `Bun.file().type`, renames the part (`word/media/image1.jpeg` → `word/media/image1.png`), rewrites the relationship `Target`, and ensures `[Content_Types].xml` has a `<Default>` for the new extension.
|
|
184
214
|
|
|
215
|
+
**Code blocks + inline code.** `insert --code TEXT` (or `--code-file PATH`, `-` for stdin) splits content on `\n` and emits one `<w:p>` per source line, all styled `CodeBlock` (Courier New, indent, adjacent-paragraph spacing collapse) with runs styled `Code` (monospace character style — defensive in case Word doesn't cascade the paragraph font). Both styles get provisioned in `styles.xml` automatically. `--language LANG` syntax-highlights via [lowlight](https://github.com/wooorm/lowlight) (highlight.js); 37 common languages are bundled — `bash`, `c`, `cpp`, `csharp`, `css`, `diff`, `go`, `graphql`, `ini`, `java`, `javascript`, `json`, `kotlin`, `less`, `lua`, `makefile`, `markdown`, `objectivec`, `perl`, `php`, `php-template`, `plaintext`, `python`, `python-repl`, `r`, `ruby`, `rust`, `scss`, `shell`, `sql`, `swift`, `typescript`, `vbnet`, `wasm`, `xml`, `yaml`. Unknown languages degrade silently to uncolored runs. The palette is GitHub-light inspired (keywords red, strings dark-blue, comments gray, …) and unmapped highlight.js classes fall through with no color. For an inline ``code`` span inside a normal paragraph, use `--runs` JSON with `runStyle: "Code"` (the S8 markdown walker will make this ergonomic via the `\`code\`` shorthand). On `docx read`, consecutive `CodeBlock` paragraphs collapse into one GFM fenced block (`` ``` `` … `` ``` ``); inline `runStyle: "Code"` runs render with backticks.
|
|
216
|
+
|
|
217
|
+
**Image insertion.** `insert --image SRC` resolves SRC from a file path, a `data:` URI, or an `http(s)` URL (bounded fetch: 10s timeout, 25 MB cap streamed per-chunk so the limit holds even if `Content-Length` lies), writes the bytes to `word/media/imageN.ext`, mints an `image` relationship, and registers the extension's content-type `<Default>`. Pixel dimensions are read from the PNG/JPEG/GIF header and converted to EMU (1px = 9525 EMU at 96 dpi) for `<wp:extent>`; `--width`/`--height` override in inches, and supplying one alone scales the other to preserve aspect. The drawing is a standard inline `<w:drawing><wp:inline>` picture (`a:`/`pic:` namespaces declared on the subtree). Under track-changes the inserted run is wrapped in `<w:ins>` like any other inserted content. **HEIC/HEIF** input (common from iPhones) is transcoded to JPEG before embedding — Word can't render HEIC — so students can drop a `.heic` straight in; detection is by file header, not just extension. **SVG input is sanitized** before embedding (`<script>`, `on*` handlers, `<foreignObject>`, animation events, external `href`/`xlink:href`, and `data:image/svg+xml` self-references are stripped; XXE is rejected at parse time by `fast-xml-parser`) so an attacker-controlled SVG can't smuggle active content into the doc. **Remote fetches block non-public addresses** — private, loopback, link-local, and cloud-metadata ranges are refused, and HTTP redirects are followed manually with the same check at every hop, so an agent steered into `http://169.254.169.254/...` or `http://10.0.0.1/admin` is short-circuited.
|
|
218
|
+
|
|
219
|
+
**Image deletion.** `images delete --at imgN` removes the inline drawing and its run, pruning the media part and relationship when nothing else references them. Under track-changes it wraps the run in a real `<w:del>` instead (accept removes, reject restores), keeping the part until the change is accepted.
|
|
220
|
+
|
|
185
221
|
**Hyperlink CRUD.** `hyperlinks list` enumerates `<w:hyperlink>` elements with positional `linkN` ids; `hyperlinks add --at p3:5-20 --url URL` wraps an existing span (splitting runs at offsets); `hyperlinks replace --at link0 --with URL` updates the rels `Target`, allocating a new rId if the existing one is shared so siblings stay pointed at the original URL; `hyperlinks delete --at link0` unwraps the link (text survives) and prunes the rels entry when no longer referenced.
|
|
186
222
|
|
|
223
|
+
**Table restructuring.** The `tables` verbs operate on a merge-aware logical grid: `gridSpan` (horizontal) and `vMerge` (vertical) cells map to physical `<w:tc>` elements so row/column locators (`tN:rR`, `tN:cC`, region `tN:rR1cC1-rR2cC2`) resolve correctly. `merge`/`unmerge` reshape `gridSpan`/`vMerge`; `set-widths` rewrites `<w:tblGrid>` plus per-cell `<w:tcW>`; structural edits refuse to bisect or orphan an existing merge (with a hint to `unmerge` first). Under track-changes, the tracked representation was verified against Microsoft Word (accept/reject), since Word — not the ECMA-376 schema — decides what actually round-trips. Row insert/delete emit native `<w:trPr><w:ins>`/`<w:del>`; column insert/delete emit per-cell `<w:tcPr><w:cellIns>`/`<w:cellDel>` (paired with a `<w:tblGridChange>` on insert); `set-widths` emits `<w:tblGridChange>` plus a per-cell `<w:tcPrChange>` (which is what Word's reject actually reverts) — all addressable as `tcN` and resolvable via `track-changes accept`/`reject` (which resyncs the grid). Cell merges and border changes are *not* tracked by Word (it warns "this action won't be marked as a change" and applies them immediately), so `merge`/`unmerge`/`borders` match that — applied in place with a `[docx-cli]` audit comment (mirroring hyperlink/image edits).
|
|
224
|
+
|
|
187
225
|
## Stack
|
|
188
226
|
|
|
189
227
|
- **Runtime**: Bun (`node:util` parseArgs, JSX with custom factory, native zlib)
|
|
190
228
|
- **Parser**: [`jszip`](https://www.npmjs.com/package/jszip) + [`fast-xml-parser`](https://www.npmjs.com/package/fast-xml-parser) + [`fast-xml-builder`](https://www.npmjs.com/package/fast-xml-builder)
|
|
229
|
+
- **Images**: [`heic-convert`](https://www.npmjs.com/package/heic-convert) (wasm libheif) transcodes HEIC/HEIF input to JPEG on insert
|
|
191
230
|
- **Quality**: Biome + Knip + tsc; LibreOffice headless for round-trip integration tests
|
|
192
231
|
- **Standard**: ECMA-376 Part 1 §17 (WordprocessingML), Transitional profile
|
|
193
232
|
|