docxodus 5.5.4 → 6.1.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.
Files changed (37) hide show
  1. package/dist/docxodus.worker.js +154 -1
  2. package/dist/docxodus.worker.js.map +1 -1
  3. package/dist/index.d.ts +43 -4
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +79 -2
  6. package/dist/index.js.map +1 -1
  7. package/dist/session.d.ts +302 -0
  8. package/dist/session.d.ts.map +1 -0
  9. package/dist/session.js +505 -0
  10. package/dist/session.js.map +1 -0
  11. package/dist/types.d.ts +731 -3
  12. package/dist/types.d.ts.map +1 -1
  13. package/dist/types.js +148 -0
  14. package/dist/types.js.map +1 -1
  15. package/dist/wasm/_framework/DocumentFormat.OpenXml.Framework.wasm +0 -0
  16. package/dist/wasm/_framework/DocumentFormat.OpenXml.wasm +0 -0
  17. package/dist/wasm/_framework/Docxodus.wasm +0 -0
  18. package/dist/wasm/_framework/DocxodusWasm.wasm +0 -0
  19. package/dist/wasm/_framework/System.Collections.Concurrent.wasm +0 -0
  20. package/dist/wasm/_framework/System.Collections.wasm +0 -0
  21. package/dist/wasm/_framework/System.Linq.wasm +0 -0
  22. package/dist/wasm/_framework/System.Memory.wasm +0 -0
  23. package/dist/wasm/_framework/System.Net.Http.wasm +0 -0
  24. package/dist/wasm/_framework/System.Private.CoreLib.wasm +0 -0
  25. package/dist/wasm/_framework/System.Runtime.wasm +0 -0
  26. package/dist/wasm/_framework/System.Security.Cryptography.wasm +0 -0
  27. package/dist/wasm/_framework/System.Text.Json.wasm +0 -0
  28. package/dist/wasm/_framework/System.Text.RegularExpressions.wasm +0 -0
  29. package/dist/wasm/_framework/System.Threading.wasm +0 -0
  30. package/dist/wasm/_framework/blazor.boot.json +17 -17
  31. package/dist/wasm/_framework/dotnet.native.wasm +0 -0
  32. package/dist/worker-proxy.bundle.js +81 -0
  33. package/dist/worker-proxy.d.ts +57 -1
  34. package/dist/worker-proxy.d.ts.map +1 -1
  35. package/dist/worker-proxy.js +76 -0
  36. package/dist/worker-proxy.js.map +1 -1
  37. package/package.json +1 -1
package/dist/types.d.ts CHANGED
@@ -1,3 +1,153 @@
1
+ /**
2
+ * Which parts of the OOXML package to include in the markdown projection.
3
+ * Bitflags mirroring the .NET `Docxodus.ProjectionScopes` enum.
4
+ */
5
+ export declare enum ProjectionScopes {
6
+ Body = 1,
7
+ Headers = 2,
8
+ Footers = 4,
9
+ Footnotes = 8,
10
+ Endnotes = 16,
11
+ Comments = 32,
12
+ All = 63
13
+ }
14
+ /**
15
+ * How anchor markers are rendered in the markdown projection.
16
+ * Mirrors the .NET `Docxodus.AnchorRenderMode` enum.
17
+ */
18
+ export declare enum AnchorRenderMode {
19
+ /** Anchor appears on its own line before each block element (default). */
20
+ Block = 0,
21
+ /** Block anchors plus inline `{#…}` markers for spans (comments, hyperlinks). */
22
+ BlockAndInline = 1,
23
+ /** No anchor markers in the output (projection only, no addressing). */
24
+ None = 2
25
+ }
26
+ /**
27
+ * Strategy for rendering `w:tbl` elements that don't fit GFM pipe-table constraints.
28
+ * Mirrors the .NET `Docxodus.TableRenderMode` enum.
29
+ */
30
+ export declare enum TableRenderMode {
31
+ /** Emit GFM pipe tables when possible, opaque anchor blocks otherwise (default). */
32
+ GfmWithOpaqueFallback = 0,
33
+ /** Always emit GFM pipe tables, flattening complex structure with possible loss. */
34
+ AlwaysGfm = 1,
35
+ /** Always emit opaque anchor blocks. */
36
+ AlwaysOpaque = 2
37
+ }
38
+ /**
39
+ * How tracked changes are handled in the markdown projection.
40
+ * Mirrors the .NET `Docxodus.TrackedChangeMode` enum.
41
+ */
42
+ export declare enum TrackedChangeMode {
43
+ /** Accept all revisions before conversion (default). */
44
+ Accept = 0,
45
+ /** Render insertions and deletions inline as `{+ins+}` / `{-del-}`. */
46
+ RenderInline = 1,
47
+ /** Accept insertions, drop deletions. */
48
+ StripDeletions = 2
49
+ }
50
+ /**
51
+ * How empty paragraphs are rendered. Mirrors the .NET `Docxodus.EmptyParagraphMode` enum.
52
+ */
53
+ export declare enum EmptyParagraphMode {
54
+ /** Default: emit the anchor on its own line (`{#p:body:UNID}\n`). */
55
+ AnchorOnly = 0,
56
+ /** Tag the empty paragraph visibly so agents can pattern-match (`{#p:body:UNID} ∅`). */
57
+ MarkedEmpty = 1,
58
+ /** Skip empty paragraphs entirely — they don't appear in the markdown or the anchor index. */
59
+ Suppress = 2
60
+ }
61
+ /**
62
+ * How anchor ids are rendered inside `{#…}` tokens (and keyed in
63
+ * {@link MarkdownProjection.anchorIndex}). Mirrors the .NET
64
+ * `Docxodus.AnchorIdRendering` enum.
65
+ *
66
+ * `Anchor.token` (and any `AnchorRef` returned by `DocxSession` mutations)
67
+ * always carries the full Unid regardless of rendering — the choice only
68
+ * affects the markdown text. The returned `anchorIndex` is dual-keyed so
69
+ * lookups by either the rendered id or the full Unid work for the
70
+ * `Abbreviated`/`Sequential` modes.
71
+ */
72
+ export declare enum AnchorIdRendering {
73
+ /** Full 32-char hex Unid (default; e.g. `{#h:body:a1b2c3d4e5f6789012345678901234ab}`). */
74
+ FullUnid = 0,
75
+ /** Shortest unique prefix per (kind, scope) bucket, 4-char floor (e.g. `{#h:body:a1b2}`).
76
+ * Saves 5-10% of projection-token budget for LLM consumption. */
77
+ Abbreviated = 1,
78
+ /** Sequential numeric ids per (kind, scope) bucket in document order (e.g. `{#h:body:1}`).
79
+ * Maximally token-efficient for one-shot LLM contexts. NOT stable across
80
+ * `project()` calls and must NOT be persisted. */
81
+ Sequential = 2
82
+ }
83
+ /**
84
+ * How far below the target anchor to include in
85
+ * {@link DocxSession.projectAnchor}. Mirrors the .NET
86
+ * `Docxodus.ProjectionDepth` enum.
87
+ */
88
+ export declare enum ProjectionDepth {
89
+ /** Just the target block itself (its anchor + its own text). For headings,
90
+ * returns only the heading paragraph, not the section under it. */
91
+ SelfOnly = 0,
92
+ /** Self + descendants. Most useful for `tbl` anchors (returns the whole table);
93
+ * for paragraphs it's the same as `SelfOnly`. */
94
+ Subtree = 1,
95
+ /** Self + descendants + following siblings up to (but not including) the next
96
+ * sibling at the same or higher heading level. For non-heading anchors,
97
+ * equivalent to `Subtree`. Dominant "give me this section" case; the default. */
98
+ SubtreeAndFollowingSiblings = 2
99
+ }
100
+ /**
101
+ * Settings controlling the markdown projection. Mirrors the .NET
102
+ * `WmlToMarkdownConverterSettings` class — see `docs/architecture/markdown_projection.md`.
103
+ */
104
+ export interface MarkdownProjectionSettings {
105
+ scopes?: ProjectionScopes;
106
+ headingLevelOffset?: number;
107
+ anchorMode?: AnchorRenderMode;
108
+ tableMode?: TableRenderMode;
109
+ tableInlineCellMax?: number;
110
+ trackedChanges?: TrackedChangeMode;
111
+ resolveNumbering?: boolean;
112
+ emptyParagraphs?: EmptyParagraphMode;
113
+ /**
114
+ * How anchor ids are rendered in markdown output. Default `FullUnid`.
115
+ * Set to `Abbreviated` for terse LLM-friendly ids; `Sequential` for
116
+ * 1-based per-scope counters (best for replay logs / human review).
117
+ * `Anchor.token` (and any `AnchorRef`) always reflects the full Unid
118
+ * regardless; this only affects the markdown text. Use the returned
119
+ * {@link MarkdownProjection.anchorIndex} (dual-keyed for Abbreviated /
120
+ * Sequential) to translate rendered ids back to full Unids.
121
+ */
122
+ anchorIdRendering?: AnchorIdRendering;
123
+ }
124
+ /**
125
+ * Resolved location of an anchor in the underlying OOXML package — sufficient to walk
126
+ * back to the source element via the .NET API.
127
+ */
128
+ export interface MarkdownAnchorTarget {
129
+ id: string;
130
+ kind: string;
131
+ scope: string;
132
+ unid: string;
133
+ partUri: string;
134
+ /** First ~80 characters of the element's flat text — for previewing/picking anchors. */
135
+ textPreview: string;
136
+ /** Resolved auto-numbering prefix (e.g. "1.", "First") for paragraphs/headings/list
137
+ * items whose style or `w:numPr` produces numbering. Absent when the element has
138
+ * no numbering. The prefix is NOT included in {@link textPreview} because
139
+ * textPreview reflects only the run text; this field gives callers the value
140
+ * Word actually renders before the element's text. */
141
+ autoNumberPrefix?: string;
142
+ }
143
+ /**
144
+ * Output of the markdown projection: rendered text plus the anchor index mapping
145
+ * each `{#…}` token back to a location in the OOXML package.
146
+ */
147
+ export interface MarkdownProjection {
148
+ markdown: string;
149
+ anchorIndex: Record<string, MarkdownAnchorTarget>;
150
+ }
1
151
  /**
2
152
  * Revision type enum matching the .NET WmlComparerRevisionType
3
153
  */
@@ -351,6 +501,7 @@ export interface DocxodusWasmExports {
351
501
  GetDocumentStructure: (bytes: Uint8Array) => string;
352
502
  GetDocumentMetadata: (bytes: Uint8Array) => string;
353
503
  ExportToOpenContract: (bytes: Uint8Array) => string;
504
+ ConvertWmlToMarkdown: (bytes: Uint8Array, settingsJson: string) => string;
354
505
  GetVersion: () => string;
355
506
  ConvertDocxToHtmlProfiled: (bytes: Uint8Array) => string;
356
507
  ProfileConversionDetailed: (bytes: Uint8Array) => string;
@@ -376,6 +527,503 @@ export interface DocxodusWasmExports {
376
527
  CompareDocumentsWithLog: (originalBytes: Uint8Array, modifiedBytes: Uint8Array, authorName: string, detailThreshold: number, caseInsensitive: boolean) => string;
377
528
  CompareDocumentsToHtmlWithLog: (originalBytes: Uint8Array, modifiedBytes: Uint8Array, authorName: string, detailThreshold: number, caseInsensitive: boolean, renderTrackedChanges: boolean) => string;
378
529
  };
530
+ DocxSessionBridge: {
531
+ OpenSession: (bytes: Uint8Array, settingsJson: string) => number;
532
+ CloseSession: (handle: number) => void;
533
+ Project: (handle: number) => string;
534
+ ProjectAnchor: (handle: number, anchorId: string, depth: number) => string;
535
+ ReplaceText: (handle: number, anchor: string, md: string) => string;
536
+ DeleteBlock: (handle: number, anchor: string) => string;
537
+ DeleteRange: (handle: number, fromAnchorId: string, toAnchorIdExclusive: string) => string;
538
+ DeleteSection: (handle: number, headingAnchorId: string) => string;
539
+ InsertParagraph: (handle: number, anchor: string, pos: string, md: string) => string;
540
+ SplitParagraph: (handle: number, anchor: string, offset: number) => string;
541
+ MergeParagraphs: (handle: number, first: string, second: string) => string;
542
+ ApplyFormat: (handle: number, anchor: string, spanJson: string, opJson: string) => string;
543
+ ApplyFormatBySubstring: (handle: number, anchor: string, substring: string, opJson: string) => string;
544
+ SetParagraphStyle: (handle: number, anchor: string, styleId: string) => string;
545
+ SetListLevel: (handle: number, anchor: string, delta: number) => string;
546
+ RemoveListMembership: (handle: number, anchor: string) => string;
547
+ ReplaceCellContent: (handle: number, anchor: string, md: string) => string;
548
+ RawGetXml: (handle: number, anchor: string) => string;
549
+ RawInsertXml: (handle: number, anchor: string, pos: string, xml: string) => string;
550
+ RawReplaceXml: (handle: number, anchor: string, xml: string) => string;
551
+ Grep: (handle: number, pattern: string, optionsJson: string) => string;
552
+ GrepCrossBlock: (handle: number, pattern: string, optionsJson: string) => string;
553
+ ReplaceTextRange: (handle: number, anchor: string, find: string, replace: string, optionsJson: string) => string;
554
+ ReplaceTextAtSpan: (handle: number, anchor: string, spanStart: number, spanLength: number, replace: string) => string;
555
+ ReplaceInner: (handle: number, matchText: string, anchor: string, spanStart: number, spanLength: number, newInner: string) => string;
556
+ FindPlaceholders: (handle: number, kinds: number, scope: number, contextChars: number, boundary: number) => string;
557
+ GetEditSummary: (handle: number) => string;
558
+ RemainingPlaceholders: (handle: number, kinds: number) => string;
559
+ GetDiff: (handle: number, format: number) => string;
560
+ FindByAnnotation: (handle: number, annotationId: string) => string;
561
+ FindByLabel: (handle: number, labelId: string) => string;
562
+ FindByBookmark: (handle: number, bookmarkName: string) => string;
563
+ GetAnchorInfo: (handle: number, anchorId: string) => string;
564
+ GetAnchorInfos: (handle: number, anchorIdsJson: string) => string;
565
+ GetBlockMetadata: (handle: number, anchorId: string) => string;
566
+ GetBlockMetadatas: (handle: number, anchorIdsJson: string) => string;
567
+ GetListMembership: (handle: number, anchorId: string) => string;
568
+ GetSectionInfo: (handle: number, anchorId: string) => string;
569
+ ListAnnotations: (handle: number) => string;
570
+ AddAnnotation: (handle: number, anchorId: string, spanJson: string, annotationJson: string) => string;
571
+ SessionRemoveAnnotation: (handle: number, annotationId: string) => string;
572
+ UpdateAnnotation: (handle: number, annotationId: string, updateJson: string) => string;
573
+ MoveAnnotation: (handle: number, annotationId: string, newAnchorId: string, newSpanJson: string) => string;
574
+ Undo: (handle: number) => boolean;
575
+ Redo: (handle: number) => boolean;
576
+ Save: (handle: number) => Uint8Array;
577
+ };
578
+ }
579
+ export type EditErrorCode = "anchor_not_found" | "anchor_wrong_kind" | "anchors_not_adjacent" | "session_disposed" | "malformed_markdown" | "unsupported_markdown_syntax" | "table_insert_not_supported" | "footnote_ref_not_supported" | "comment_marker_not_supported" | "image_insert_not_supported" | "anchor_token_in_payload" | "offset_out_of_range" | "invalid_position" | "unknown_style" | "invalid_list_level" | "malformed_xml" | "disallowed_namespace" | "incompatible_element_type" | "validation_failed" | "nothing_to_undo" | "nothing_to_redo" | "internal_error";
580
+ export interface AnchorRef {
581
+ id: string;
582
+ kind: string;
583
+ scope: string;
584
+ unid: string;
585
+ }
586
+ export interface EditError {
587
+ code: EditErrorCode;
588
+ message: string;
589
+ anchorId?: string;
590
+ }
591
+ export interface MarkdownPatch {
592
+ scopeAnchorId: string;
593
+ markdown: string;
594
+ }
595
+ export interface EditResult {
596
+ success: boolean;
597
+ error?: EditError;
598
+ created: AnchorRef[];
599
+ removed: AnchorRef[];
600
+ modified: AnchorRef[];
601
+ patch?: MarkdownPatch;
602
+ }
603
+ export interface CharSpan {
604
+ start: number;
605
+ length: number;
606
+ }
607
+ export interface FormatOp {
608
+ bold?: boolean;
609
+ italic?: boolean;
610
+ underline?: boolean;
611
+ strike?: boolean;
612
+ code?: boolean;
613
+ color?: string;
614
+ runStyle?: string;
615
+ }
616
+ export interface DocxSessionSettings {
617
+ undoDepth?: number;
618
+ validateRawOps?: boolean;
619
+ trackedChanges?: "accept" | "render_inline" | "strip_deletions";
620
+ revisionAuthor?: string;
621
+ /**
622
+ * When false (default), `save()` strips the projector's internal `PtOpenXml:Unid`
623
+ * attributes before serializing — they aren't OOXML schema, and persisting them
624
+ * bloats large documents significantly (~700 KB on a 100-page DOCX). Set to true
625
+ * only when anchor ids must survive a save/reopen round trip.
626
+ */
627
+ persistAnchorIds?: boolean;
628
+ /**
629
+ * When true, ReplaceText / ReplaceTextRange / ReplaceMatch payloads have ASCII
630
+ * `"` and `'` converted to typographic curly quotes (`“ ” ‘ ’`) based on
631
+ * context — open at start / after whitespace / after open-bracket, close
632
+ * elsewhere. Avoids the cosmetic regression where a replacement lands as
633
+ * straight-quoted text adjacent to surrounding already-curly text. Default false.
634
+ */
635
+ smartQuotes?: boolean;
636
+ /**
637
+ * When `true` (default), the session projects the document at construction
638
+ * time so {@link DocxSession.getDiff} can compare initial vs. current.
639
+ * Set to `false` to skip the ~200ms upfront cost if you don't plan to diff.
640
+ */
641
+ captureInitialProjection?: boolean;
642
+ }
643
+ export interface DocxSessionProjection {
644
+ markdown: string;
645
+ anchorIndex: Record<string, {
646
+ partUri: string;
647
+ unid: string;
648
+ kind: string;
649
+ scope: string;
650
+ textPreview: string;
651
+ }>;
652
+ }
653
+ /**
654
+ * Per-fragment visible formatting reported by {@link DocxSession.grep}.
655
+ */
656
+ export interface RunFormatting {
657
+ bold: boolean;
658
+ italic: boolean;
659
+ underline: boolean;
660
+ strike: boolean;
661
+ code: boolean;
662
+ color?: string;
663
+ hyperlinkUrl?: string;
664
+ runStyle?: string;
665
+ }
666
+ /**
667
+ * One piece of a {@link TextMatch} that came from a single `<w:r>` run.
668
+ */
669
+ export interface RunFragment {
670
+ /** PtOpenXml:Unid of the `w:r` element this fragment came from. */
671
+ unid: string;
672
+ /** The text from this run that participates in the match. */
673
+ text: string;
674
+ /** Character offset + length of this fragment inside the run's flat text. */
675
+ spanInElement: CharSpan;
676
+ /** Visible formatting of the run this fragment came from. */
677
+ formatting: RunFormatting;
678
+ }
679
+ /**
680
+ * A single match returned by {@link DocxSession.grep}. The match always lives
681
+ * within one block-level element.
682
+ */
683
+ export interface TextMatch {
684
+ text: string;
685
+ enclosingAnchor: AnchorRef;
686
+ span: CharSpan;
687
+ fragments: RunFragment[];
688
+ contextBefore: string;
689
+ contextAfter: string;
690
+ /** Regex capture groups; index 0 is always the whole match. */
691
+ groups: string[];
692
+ }
693
+ /**
694
+ * One block's contribution to a {@link CrossBlockMatch}. The slice's `fragments`
695
+ * list is empty when the match touches an empty paragraph — the slice is still
696
+ * recorded so callers can see the match crossed the empty block.
697
+ */
698
+ export interface BlockSlice {
699
+ anchor: AnchorRef;
700
+ /** Character offset + length of the slice within the block's own flat text. */
701
+ spanInBlock: CharSpan;
702
+ /** Run fragments contributing to this slice, in document order. */
703
+ fragments: RunFragment[];
704
+ }
705
+ /**
706
+ * A single match returned by {@link DocxSession.grepCrossBlock}. The match may
707
+ * span multiple adjacent block-level elements (paragraphs/headings/list items)
708
+ * under the same parent container. `slices` is the per-block breakdown;
709
+ * `enclosingAnchors` lists every block the match touches, in document order.
710
+ *
711
+ * Block boundaries appear in `text` / `contextBefore` / `contextAfter` as
712
+ * single `\n` characters.
713
+ */
714
+ export interface CrossBlockMatch {
715
+ text: string;
716
+ enclosingAnchors: AnchorRef[];
717
+ slices: BlockSlice[];
718
+ contextBefore: string;
719
+ contextAfter: string;
720
+ /** Regex capture groups; index 0 is always the whole match. */
721
+ groups: string[];
722
+ }
723
+ /**
724
+ * Options for {@link DocxSession.replaceTextRange}.
725
+ */
726
+ export interface ReplaceOptions {
727
+ /** Case-insensitive matching for the literal `find` needle. */
728
+ ignoreCase?: boolean;
729
+ /** Cap the number of replacements; omitted = unlimited. */
730
+ maxReplacements?: number;
731
+ }
732
+ /**
733
+ * Categories of bracketed placeholders {@link DocxSession.findPlaceholders} recognizes.
734
+ *
735
+ * - `blank_fill` — `[___]` or `$[___]` value slots
736
+ * - `alternative_clause` — `[entire clause text]`
737
+ * - `instruction` — `[insert X]`, `[specify Y]`, `[*italicized hint*]`
738
+ */
739
+ export type PlaceholderKind = "blank_fill" | "alternative_clause" | "instruction";
740
+ /**
741
+ * Numeric flag layout matching the .NET `PlaceholderKinds` enum. Combine with bitwise OR.
742
+ */
743
+ export declare const PlaceholderKinds: {
744
+ readonly BlankFill: 1;
745
+ readonly AlternativeClause: 2;
746
+ readonly Instruction: 4;
747
+ readonly All: 7;
748
+ };
749
+ /**
750
+ * Numeric flag layout matching the .NET `DiffFormat` enum. Use with
751
+ * {@link DocxSession.getDiff}.
752
+ *
753
+ * - `Json` (default) — anchor-keyed structured diff. Returns a `DiffEntry[]`.
754
+ * - `Unified` — `patch(1)`-compatible unified diff over the markdown projection.
755
+ * Returns a single string (`""` when nothing changed).
756
+ * - `SideBySide` — two-column human-review diff (`diff -y` style) over the
757
+ * markdown projection. Returns a single string.
758
+ */
759
+ export declare const DiffFormat: {
760
+ readonly Json: 0;
761
+ readonly Unified: 1;
762
+ readonly SideBySide: 2;
763
+ };
764
+ /**
765
+ * A single anchor-keyed change in the diff between an initial and current projection.
766
+ */
767
+ export interface DiffEntry {
768
+ op: "delete" | "insert" | "modify";
769
+ anchorId: string;
770
+ /** Pre-change text content for delete/modify; absent for insert. */
771
+ before?: string;
772
+ /** Post-change text content for insert/modify; absent for delete. */
773
+ after?: string;
774
+ }
775
+ /**
776
+ * Aggregate snapshot of edit-state introspection signals returned by
777
+ * {@link DocxSession.getEditSummary}.
778
+ */
779
+ export interface EditSummary {
780
+ totalAnchors: number;
781
+ remainingPlaceholders: TemplatePlaceholder[];
782
+ bareUnderscoreRuns: TextMatch[];
783
+ footnoteCount: number;
784
+ inlineFootnoteRefCount: number;
785
+ commentCount: number;
786
+ }
787
+ /**
788
+ * Numeric flag layout matching the .NET `ContextBoundary` enum. Controls
789
+ * how `Grep` / `GrepCrossBlock` / `FindPlaceholders` decide where to stop
790
+ * walking outward when computing `TextMatch.contextBefore` / `contextAfter`.
791
+ *
792
+ * - `Char` (default) — truncate at `contextChars`. Matches legacy behavior.
793
+ * - `Bracket` — stop at `[` or `]`. Use for template fills: each placeholder's
794
+ * context is unambiguously its own even when multiple placeholders crowd
795
+ * into one sentence.
796
+ * - `Sentence` — stop at `.`, `!`, `?`, `:`, `;`.
797
+ * - `Comma` — stop at `,`. For matches inside enumerations.
798
+ */
799
+ export declare const ContextBoundary: {
800
+ readonly Char: 0;
801
+ readonly Bracket: 1;
802
+ readonly Sentence: 2;
803
+ readonly Comma: 3;
804
+ };
805
+ export interface TemplatePlaceholder {
806
+ kind: PlaceholderKind;
807
+ /** For `instruction` placeholders: the inner text with surrounding brackets/asterisks stripped. */
808
+ hint?: string;
809
+ match: TextMatch;
810
+ /**
811
+ * Additional plausible classifications when the primary `kind` is borderline.
812
+ * Empty by default. The classic case is a long bracketed clause that happens
813
+ * to contain a `_______` blank: primary `kind` stays `"blank_fill"`
814
+ * (back-compat) and `alternativeKinds` contains `"alternative_clause"`.
815
+ */
816
+ alternativeKinds: PlaceholderKind[];
817
+ }
818
+ /**
819
+ * Options for {@link DocxSession.fillPlaceholders}.
820
+ */
821
+ export interface FillOptions {
822
+ /** Which placeholder kinds to fill. Defaults to `PlaceholderKinds.All` so the
823
+ * picker is invoked for every kind in the doc. Narrow with e.g.
824
+ * `PlaceholderKinds.BlankFill | PlaceholderKinds.Instruction` to ignore
825
+ * bracketed alternative clauses. */
826
+ kinds?: number;
827
+ /** Which package parts to scan. Defaults to body (1). */
828
+ scope?: number;
829
+ /** Max iteration passes for multi-pass nested-bracket scenarios. Default 8. */
830
+ maxPasses?: number;
831
+ /** When the match starts with `$` and the picker's return value doesn't,
832
+ * preserve the `$` by prepending it. Default true. */
833
+ preserveDollarPrefix?: boolean;
834
+ /** Cap on `contextBefore` / `contextAfter` length on each side. Default 80. */
835
+ contextChars?: number;
836
+ /** Where to stop walking outward when computing context. Numeric layout
837
+ * matching {@link ContextBoundary}. Default `Char` (0). */
838
+ boundary?: number;
839
+ /** When the picker returns an empty string (and after `$`-prefix preservation
840
+ * has been applied), look at the chars immediately adjacent to the placeholder
841
+ * span and absorb surrounding whitespace / leading-space-before-punctuation /
842
+ * matched-brackets so the dropped placeholder doesn't leave cosmetic
843
+ * artifacts. Default `false` (preserve the literal-delete behavior).
844
+ *
845
+ * Rules: whitespace on both sides collapses to one space; whitespace before
846
+ * and clause-terminating punctuation (`. , ; : ! ?`) after drops the leading
847
+ * space; matched open/close brackets (`()` `[]` `{}`) on either side drop
848
+ * both. NBSP / narrow NBSP / thin space are treated as whitespace.
849
+ *
850
+ * Caveat: `$`-prefix preservation runs first, so a picker returning `""` for
851
+ * `$[xxx]` with `preserveDollarPrefix: true` (default) ends up replacing with
852
+ * `"$"` and coalescing is skipped. Set `preserveDollarPrefix: false` when you
853
+ * want the `$` to drop along with the brackets. */
854
+ coalesceWhitespaceAroundEmptyFill?: boolean;
855
+ }
856
+ /**
857
+ * Aggregate result returned by {@link DocxSession.fillPlaceholders}.
858
+ *
859
+ * `skipped` counts placeholders the picker returned null for in the first pass
860
+ * that saw them — it stays > 0 even if later passes finished those same
861
+ * placeholders. Use `stillPresent` (post-loop document state) for the
862
+ * trustworthy "is the template done?" check; `skipped > 0 && stillPresent === 0`
863
+ * means "picker said no the first time but later passes resolved it."
864
+ */
865
+ export interface BulkEditResult {
866
+ filled: number;
867
+ skipped: number;
868
+ /** Number of placeholders matching `options.kinds` in `options.scope` that
869
+ * remain in the document after the final pass. `0` means the template is
870
+ * fully filled for the requested kinds/scope. */
871
+ stillPresent: number;
872
+ passes: number;
873
+ unfilled: TemplatePlaceholder[];
874
+ errors: EditError[];
875
+ }
876
+ /**
877
+ * Options for {@link DocxSession.grep}.
878
+ *
879
+ * `regexOptions` and `scope` use the numeric flag layouts of the .NET
880
+ * `System.Text.RegularExpressions.RegexOptions` and `ProjectionScopes` enums.
881
+ * Common values:
882
+ * - `RegexOptions.IgnoreCase = 1`
883
+ * - `RegexOptions.Multiline = 2`
884
+ * - `ProjectionScopes.Body = 1`, `Headers = 2`, `Footers = 4`,
885
+ * `Footnotes = 8`, `Endnotes = 16`, `Comments = 32`, `All = 63`.
886
+ */
887
+ export interface GrepOptions {
888
+ regexOptions?: number;
889
+ scope?: number;
890
+ contextChars?: number;
891
+ /**
892
+ * Whitespace handling. Numeric layout matching the .NET `WhitespaceMode` enum:
893
+ * - 0 = Preserve (default; match against the document's original characters)
894
+ * - 1 = Normalize (fold NBSP / narrow-NBSP / thin-space to ASCII space before matching)
895
+ */
896
+ whitespace?: number;
897
+ /**
898
+ * Where to stop walking outward when computing `TextMatch.contextBefore` /
899
+ * `contextAfter`. Numeric layout matching the .NET `ContextBoundary` enum;
900
+ * use the {@link ContextBoundary} const. Default `Char` (0) — truncate at
901
+ * `contextChars`.
902
+ */
903
+ boundary?: number;
904
+ }
905
+ /**
906
+ * Resolved location of an anchor — what {@link DocxSession.findByAnnotation} and
907
+ * the other discovery helpers return. The shape is {@link AnchorRef} plus the
908
+ * `partUri` of the package part the element lives in (useful for callers that
909
+ * walk the underlying OOXML directly).
910
+ */
911
+ export interface AnchorTargetRef extends AnchorRef {
912
+ partUri: string;
913
+ /** First ~80 characters of the element's flat text — for previewing/picking anchors. */
914
+ textPreview: string;
915
+ /** Resolved auto-numbering prefix (e.g. "1.", "First") when the element carries
916
+ * numbering. Absent otherwise. See {@link MarkdownAnchorTarget.autoNumberPrefix}. */
917
+ autoNumberPrefix?: string;
918
+ }
919
+ /**
920
+ * The shape returned by {@link DocxSession.getAnchorInfo}.
921
+ * Use {@link MarkdownAnchorTarget} when iterating a full projection — it
922
+ * includes the same fields plus `unid` and `partUri`.
923
+ */
924
+ export interface AnchorInfo {
925
+ id: string;
926
+ kind: string;
927
+ scope: string;
928
+ textPreview: string;
929
+ /** Resolved auto-numbering prefix (e.g. "1.", "First") when the element carries
930
+ * numbering. Absent for un-numbered paragraphs or non-paragraph kinds. */
931
+ autoNumberPrefix?: string;
932
+ }
933
+ /** Six list formats supported by the list write surface (decimal, upperLetter,
934
+ * lowerLetter, upperRoman, lowerRoman, bullet). Surfaced on
935
+ * {@link ListMembership.format} as a string union (mirrors the JSON wire format). */
936
+ export type NumberFormat = "decimal" | "upperLetter" | "lowerLetter" | "upperRoman" | "lowerRoman" | "bullet";
937
+ /** Numbering facts for a list-item paragraph. Returned by
938
+ * {@link DocxSession.getListMembership} and surfaced as {@link BlockMetadata.list}. */
939
+ export interface ListMembership {
940
+ /** The w:numId the paragraph belongs to (the w:num instance). */
941
+ numId: number;
942
+ /** The w:abstractNumId the paragraph's w:num points at. */
943
+ abstractNumId: number;
944
+ /** The paragraph's level (w:ilvl), 0-8. */
945
+ level: number;
946
+ /** Resolved format for this level. */
947
+ format: NumberFormat;
948
+ /** Always true for a paragraph carrying w:numPr (inline or via style). */
949
+ isAutoNumbered: boolean;
950
+ /** True when the w:numPr is inherited from the paragraph's style chain. */
951
+ fromStyle: boolean;
952
+ /** Start-override from w:lvlOverride/w:startOverride for this level, if any. */
953
+ startOverride?: number;
954
+ /** Resolved label (e.g. "1.", "(a)") — same value surfaced via AnchorInfo.autoNumberPrefix. */
955
+ generatedLabel?: string;
956
+ }
957
+ /** Block-level structural metadata. Returned by {@link DocxSession.getBlockMetadata}. */
958
+ export interface BlockMetadata {
959
+ anchorId: string;
960
+ kind: string;
961
+ scope: string;
962
+ styleId?: string;
963
+ styleName?: string;
964
+ /** 0-based outline level (Word convention). */
965
+ outlineLevel?: number;
966
+ list?: ListMembership;
967
+ /** True when any descendant w:r carries a non-empty w:rPr. */
968
+ hasInlineFormatting: boolean;
969
+ }
970
+ /** Page-layout snapshot for the w:sectPr that governs an anchor.
971
+ * Returned by {@link DocxSession.getSectionInfo}. */
972
+ export interface SectionInfo {
973
+ sectionUnid: string;
974
+ pageWidthTwips: number;
975
+ pageHeightTwips: number;
976
+ landscape: boolean;
977
+ marginTopTwips: number;
978
+ marginBottomTwips: number;
979
+ marginLeftTwips: number;
980
+ marginRightTwips: number;
981
+ columns: number;
982
+ headerPartUris: string[];
983
+ footerPartUris: string[];
984
+ }
985
+ /**
986
+ * A custom annotation persisted in the document via Docxodus' annotation system.
987
+ * Returned by {@link DocxSession.listAnnotations}; mirrors the wire-relevant
988
+ * fields of the .NET `DocumentAnnotation` type. The page-info cache fields
989
+ * (`startPage`/`endPage`/`pageInfoStale`/`pageInfoComputedAt`) are omitted to
990
+ * keep the JSON payload compact — callers that need them can use the .NET API
991
+ * directly. The `metadata` bag is emitted only when non-empty.
992
+ *
993
+ * See `docs/architecture/custom_annotations.md` for the persistence design.
994
+ */
995
+ export interface DocumentAnnotation {
996
+ /** Unique annotation identifier (caller-supplied at add time). */
997
+ id: string;
998
+ /** Label category/type (e.g. `"INDEMNIFICATION"`, `"CLAUSE_TYPE_A"`). */
999
+ labelId: string;
1000
+ /** Human-readable label text displayed in the UI. */
1001
+ label: string;
1002
+ /** Highlight color in hex (e.g. `"#FFEB3B"`). */
1003
+ color: string;
1004
+ /** Internal bookmark name in the DOCX (`_Docxodus_Ann_{id}` for managed annotations). */
1005
+ bookmarkName: string;
1006
+ /** Author who created the annotation, if recorded. */
1007
+ author?: string;
1008
+ /** Creation timestamp in ISO-8601 (round-trip) format, if recorded. */
1009
+ created?: string;
1010
+ /** The text content covered by the annotation's bookmark, populated when reading. */
1011
+ annotatedText?: string;
1012
+ /** Arbitrary string→string metadata bag persisted with the annotation. */
1013
+ metadata?: Record<string, string>;
1014
+ }
1015
+ /**
1016
+ * Partial-update payload for {@link DocxSession.updateAnnotation}.
1017
+ * Null/missing fields leave the existing value unchanged. `metadataPatch`
1018
+ * is a per-key merge: a non-null value sets the key, an explicit `null`
1019
+ * removes it, a missing key leaves it unchanged.
1020
+ */
1021
+ export interface AnnotationUpdate {
1022
+ labelId?: string;
1023
+ label?: string;
1024
+ color?: string;
1025
+ author?: string;
1026
+ metadataPatch?: Record<string, string | null>;
379
1027
  }
380
1028
  /**
381
1029
  * Severity level for comparison log entries.
@@ -923,7 +1571,7 @@ export interface DocumentMetadata {
923
1571
  /**
924
1572
  * Message types sent from main thread to worker.
925
1573
  */
926
- export type WorkerRequestType = "init" | "convertDocxToHtml" | "compareDocuments" | "compareDocumentsToHtml" | "getRevisions" | "getDocumentMetadata" | "getVersion";
1574
+ export type WorkerRequestType = "init" | "convertDocxToHtml" | "compareDocuments" | "compareDocumentsToHtml" | "getRevisions" | "getDocumentMetadata" | "getVersion" | "sessionOpen" | "sessionClose" | "sessionAddAnnotation" | "sessionRemoveAnnotation" | "sessionUpdateAnnotation" | "sessionMoveAnnotation";
927
1575
  /**
928
1576
  * Base structure for worker requests.
929
1577
  */
@@ -999,10 +1647,67 @@ export interface WorkerGetDocumentMetadataRequest extends WorkerRequestBase {
999
1647
  export interface WorkerGetVersionRequest extends WorkerRequestBase {
1000
1648
  type: "getVersion";
1001
1649
  }
1650
+ /**
1651
+ * Open a DocxSession in the worker.
1652
+ */
1653
+ export interface WorkerSessionOpenRequest extends WorkerRequestBase {
1654
+ type: "sessionOpen";
1655
+ /** Document bytes transferred to the worker */
1656
+ documentBytes: Uint8Array;
1657
+ /** Session settings as JSON */
1658
+ settingsJson?: string;
1659
+ }
1660
+ /**
1661
+ * Close a worker DocxSession.
1662
+ */
1663
+ export interface WorkerSessionCloseRequest extends WorkerRequestBase {
1664
+ type: "sessionClose";
1665
+ /** Session handle returned by sessionOpen */
1666
+ handle: number;
1667
+ }
1668
+ /**
1669
+ * Add an annotation via a worker DocxSession.
1670
+ */
1671
+ export interface WorkerSessionAddAnnotationRequest extends WorkerRequestBase {
1672
+ type: "sessionAddAnnotation";
1673
+ handle: number;
1674
+ anchorId: string;
1675
+ /** CharSpan as JSON, or empty string for block-level */
1676
+ spanJson: string;
1677
+ annotationJson: string;
1678
+ }
1679
+ /**
1680
+ * Remove an annotation via a worker DocxSession.
1681
+ */
1682
+ export interface WorkerSessionRemoveAnnotationRequest extends WorkerRequestBase {
1683
+ type: "sessionRemoveAnnotation";
1684
+ handle: number;
1685
+ annotationId: string;
1686
+ }
1687
+ /**
1688
+ * Update an annotation via a worker DocxSession.
1689
+ */
1690
+ export interface WorkerSessionUpdateAnnotationRequest extends WorkerRequestBase {
1691
+ type: "sessionUpdateAnnotation";
1692
+ handle: number;
1693
+ annotationId: string;
1694
+ updateJson: string;
1695
+ }
1696
+ /**
1697
+ * Move an annotation via a worker DocxSession.
1698
+ */
1699
+ export interface WorkerSessionMoveAnnotationRequest extends WorkerRequestBase {
1700
+ type: "sessionMoveAnnotation";
1701
+ handle: number;
1702
+ annotationId: string;
1703
+ newAnchorId: string;
1704
+ /** CharSpan as JSON, or empty string for block-level */
1705
+ newSpanJson: string;
1706
+ }
1002
1707
  /**
1003
1708
  * Union type of all possible worker requests.
1004
1709
  */
1005
- export type WorkerRequest = WorkerInitRequest | WorkerConvertRequest | WorkerCompareRequest | WorkerCompareToHtmlRequest | WorkerGetRevisionsRequest | WorkerGetDocumentMetadataRequest | WorkerGetVersionRequest;
1710
+ export type WorkerRequest = WorkerInitRequest | WorkerConvertRequest | WorkerCompareRequest | WorkerCompareToHtmlRequest | WorkerGetRevisionsRequest | WorkerGetDocumentMetadataRequest | WorkerGetVersionRequest | WorkerSessionOpenRequest | WorkerSessionCloseRequest | WorkerSessionAddAnnotationRequest | WorkerSessionRemoveAnnotationRequest | WorkerSessionUpdateAnnotationRequest | WorkerSessionMoveAnnotationRequest;
1006
1711
  /**
1007
1712
  * Base structure for worker responses.
1008
1713
  */
@@ -1068,10 +1773,33 @@ export interface WorkerGetVersionResponse extends WorkerResponseBase {
1068
1773
  /** Version information */
1069
1774
  version?: VersionInfo;
1070
1775
  }
1776
+ /**
1777
+ * Response from sessionOpen request.
1778
+ */
1779
+ export interface WorkerSessionOpenResponse extends WorkerResponseBase {
1780
+ type: "sessionOpen";
1781
+ /** Integer handle identifying the session in the worker */
1782
+ handle?: number;
1783
+ }
1784
+ /**
1785
+ * Response from sessionClose request.
1786
+ */
1787
+ export interface WorkerSessionCloseResponse extends WorkerResponseBase {
1788
+ type: "sessionClose";
1789
+ }
1790
+ /**
1791
+ * Response from session annotation write operations.
1792
+ * The `result` field is the serialised EditResult from the WASM bridge.
1793
+ */
1794
+ export interface WorkerSessionEditResponse extends WorkerResponseBase {
1795
+ type: "sessionAddAnnotation" | "sessionRemoveAnnotation" | "sessionUpdateAnnotation" | "sessionMoveAnnotation";
1796
+ /** EditResult returned by the session operation */
1797
+ result?: EditResult;
1798
+ }
1071
1799
  /**
1072
1800
  * Union type of all possible worker responses.
1073
1801
  */
1074
- export type WorkerResponse = WorkerInitResponse | WorkerConvertResponse | WorkerCompareResponse | WorkerCompareToHtmlResponse | WorkerGetRevisionsResponse | WorkerGetDocumentMetadataResponse | WorkerGetVersionResponse;
1802
+ export type WorkerResponse = WorkerInitResponse | WorkerConvertResponse | WorkerCompareResponse | WorkerCompareToHtmlResponse | WorkerGetRevisionsResponse | WorkerGetDocumentMetadataResponse | WorkerGetVersionResponse | WorkerSessionOpenResponse | WorkerSessionCloseResponse | WorkerSessionEditResponse;
1075
1803
  /**
1076
1804
  * Options for creating a worker-based Docxodus instance.
1077
1805
  */