@docx4j/core-ts 0.5.0 → 0.7.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 (207) hide show
  1. package/NOTICE +4 -0
  2. package/README.md +92 -4
  3. package/dist/html/convert.mjs +2 -1
  4. package/dist/html/elements.mjs +2 -1
  5. package/dist/model/color/ColorMath.d.mts +29 -0
  6. package/dist/model/color/ColorMath.mjs +146 -0
  7. package/dist/model/color/ColorTables.d.mts +6 -0
  8. package/dist/model/color/ColorTables.mjs +83 -0
  9. package/dist/model/color/DrawingMLColors.d.mts +52 -0
  10. package/dist/model/color/DrawingMLColors.mjs +374 -0
  11. package/dist/model/color/ResolvedColor.d.mts +36 -0
  12. package/dist/model/color/ResolvedColor.mjs +65 -0
  13. package/dist/model/color/SpreadsheetColors.d.mts +36 -0
  14. package/dist/model/color/SpreadsheetColors.mjs +84 -0
  15. package/dist/model/color/StyleFill.d.mts +28 -0
  16. package/dist/model/color/StyleFill.mjs +21 -0
  17. package/dist/model/color/ThemeColors.d.mts +86 -0
  18. package/dist/model/color/ThemeColors.mjs +304 -0
  19. package/dist/model/color/WordColors.d.mts +71 -0
  20. package/dist/model/color/WordColors.mjs +121 -0
  21. package/dist/model/color/index.d.mts +6 -0
  22. package/dist/model/color/index.mjs +30 -0
  23. package/dist/model/content/Body.d.mts +145 -23
  24. package/dist/model/content/Body.mjs +507 -150
  25. package/dist/model/content/Comment.d.mts +23 -5
  26. package/dist/model/content/Comment.mjs +207 -26
  27. package/dist/model/content/ContentControl.d.mts +198 -19
  28. package/dist/model/content/ContentControl.mjs +672 -99
  29. package/dist/model/content/Font.d.mts +70 -17
  30. package/dist/model/content/Font.mjs +119 -36
  31. package/dist/model/content/InlinePicture.d.mts +11 -0
  32. package/dist/model/content/InlinePicture.mjs +21 -1
  33. package/dist/model/content/List.d.mts +6 -3
  34. package/dist/model/content/List.mjs +18 -8
  35. package/dist/model/content/Paragraph.d.mts +200 -14
  36. package/dist/model/content/Paragraph.mjs +898 -82
  37. package/dist/model/content/Range.d.mts +227 -12
  38. package/dist/model/content/Range.mjs +785 -59
  39. package/dist/model/content/ResolvedView.d.mts +104 -0
  40. package/dist/model/content/ResolvedView.mjs +596 -0
  41. package/dist/model/content/Table.d.mts +29 -3
  42. package/dist/model/content/Table.mjs +135 -49
  43. package/dist/model/content/TrackedChange.d.mts +174 -15
  44. package/dist/model/content/TrackedChange.mjs +734 -170
  45. package/dist/model/content/comments.d.mts +20 -6
  46. package/dist/model/content/comments.mjs +67 -57
  47. package/dist/model/content/generalException.d.mts +18 -0
  48. package/dist/model/content/generalException.mjs +24 -0
  49. package/dist/model/content/index.d.mts +10 -3
  50. package/dist/model/content/index.mjs +10 -2
  51. package/dist/model/content/mathText.d.mts +21 -0
  52. package/dist/model/content/mathText.mjs +267 -0
  53. package/dist/model/content/officeText.d.mts +27 -0
  54. package/dist/model/content/officeText.mjs +74 -0
  55. package/dist/model/content/ooxml.mjs +12 -1
  56. package/dist/model/content/outline.d.mts +5 -0
  57. package/dist/model/content/outline.mjs +13 -0
  58. package/dist/model/content/paraIds.d.mts +5 -0
  59. package/dist/model/content/paraIds.mjs +54 -0
  60. package/dist/model/content/placeholders.d.mts +110 -0
  61. package/dist/model/content/placeholders.mjs +253 -0
  62. package/dist/model/content/search.d.mts +74 -0
  63. package/dist/model/content/search.mjs +255 -9
  64. package/dist/model/content/spanEdits.d.mts +159 -0
  65. package/dist/model/content/spanEdits.mjs +728 -0
  66. package/dist/model/content/story.d.mts +47 -0
  67. package/dist/model/content/story.mjs +95 -0
  68. package/dist/model/content/tableColumns.d.mts +1 -1
  69. package/dist/model/content/tableColumns.mjs +108 -80
  70. package/dist/model/content/tableRevisions.d.mts +11 -3
  71. package/dist/model/content/tableRevisions.mjs +72 -42
  72. package/dist/model/content/tableRules.d.mts +30 -0
  73. package/dist/model/content/tableRules.mjs +102 -0
  74. package/dist/model/content/textBoxes.d.mts +18 -0
  75. package/dist/model/content/textBoxes.mjs +105 -0
  76. package/dist/model/content/trackedChangeFilter.d.mts +39 -0
  77. package/dist/model/content/trackedChangeFilter.mjs +81 -0
  78. package/dist/model/content/tracking.d.mts +63 -6
  79. package/dist/model/content/tracking.mjs +242 -22
  80. package/dist/model/content/tree.d.mts +172 -24
  81. package/dist/model/content/tree.mjs +328 -54
  82. package/dist/model/content/wordRange.d.mts +48 -0
  83. package/dist/model/content/wordRange.mjs +97 -0
  84. package/dist/model/customxml/CustomXmlPartCollection.d.mts +30 -1
  85. package/dist/model/customxml/CustomXmlPartCollection.mjs +81 -12
  86. package/dist/model/customxml/XmlMapping.d.mts +32 -8
  87. package/dist/model/customxml/XmlMapping.mjs +64 -10
  88. package/dist/model/customxml/bindings.d.mts +33 -4
  89. package/dist/model/customxml/bindings.mjs +171 -45
  90. package/dist/model/customxml/index.d.mts +1 -0
  91. package/dist/model/customxml/index.mjs +1 -0
  92. package/dist/model/customxml/kinds.d.mts +8 -3
  93. package/dist/model/customxml/kinds.mjs +26 -5
  94. package/dist/model/customxml/opendope.d.mts +4 -1
  95. package/dist/model/customxml/opendope.mjs +9 -6
  96. package/dist/model/customxml/picturePlaceholder.d.mts +17 -0
  97. package/dist/model/customxml/picturePlaceholder.mjs +32 -0
  98. package/dist/model/customxml/pictures.d.mts +4 -3
  99. package/dist/model/customxml/pictures.mjs +22 -8
  100. package/dist/model/customxml/storeItems.d.mts +57 -0
  101. package/dist/model/customxml/storeItems.mjs +122 -0
  102. package/dist/model/customxml/xhtml.mjs +23 -22
  103. package/dist/model/dateZone.d.mts +25 -0
  104. package/dist/model/dateZone.mjs +57 -0
  105. package/dist/model/index.d.mts +3 -0
  106. package/dist/model/index.mjs +5 -0
  107. package/dist/model/listnumbering/Emulator.d.mts +37 -5
  108. package/dist/model/listnumbering/Emulator.mjs +63 -9
  109. package/dist/model/properties/PropertyResolver.d.mts +74 -2
  110. package/dist/model/properties/PropertyResolver.mjs +133 -5
  111. package/dist/model/properties/catalogue.d.mts +19 -5
  112. package/dist/model/properties/catalogue.mjs +41 -15
  113. package/dist/model/properties/index.d.mts +1 -1
  114. package/dist/model/properties/log.d.mts +1 -1
  115. package/dist/model/properties/log.mjs +5 -5
  116. package/dist/model/properties/styleUtil.d.mts +3 -1
  117. package/dist/model/properties/styleUtil.mjs +20 -24
  118. package/dist/model/properties/table.mjs +4 -2
  119. package/dist/model/text/CaseMapping.d.mts +38 -0
  120. package/dist/model/text/CaseMapping.mjs +86 -0
  121. package/dist/model/text/Extractor.d.mts +349 -0
  122. package/dist/model/text/Extractor.mjs +2406 -0
  123. package/dist/model/text/LinkTargets.d.mts +45 -0
  124. package/dist/model/text/LinkTargets.mjs +170 -0
  125. package/dist/model/text/MarkdownExporter.d.mts +140 -0
  126. package/dist/model/text/MarkdownExporter.mjs +255 -0
  127. package/dist/model/text/PlainText.d.mts +29 -0
  128. package/dist/model/text/PlainText.mjs +542 -0
  129. package/dist/model/text/TextContext.d.mts +256 -0
  130. package/dist/model/text/TextContext.mjs +600 -0
  131. package/dist/model/text/TextOptions.d.mts +233 -0
  132. package/dist/model/text/TextOptions.mjs +419 -0
  133. package/dist/model/text/TextResult.d.mts +621 -0
  134. package/dist/model/text/TextResult.mjs +629 -0
  135. package/dist/model/text/Texts.d.mts +99 -0
  136. package/dist/model/text/Texts.mjs +187 -0
  137. package/dist/model/text/WmlToMarkdown.d.mts +120 -0
  138. package/dist/model/text/WmlToMarkdown.mjs +936 -0
  139. package/dist/model/text/caseMapping.generated.d.mts +7 -0
  140. package/dist/model/text/caseMapping.generated.mjs +214 -0
  141. package/dist/model/text/commonmark.d.mts +121 -0
  142. package/dist/model/text/commonmark.mjs +655 -0
  143. package/dist/model/text/index.d.mts +8 -0
  144. package/dist/model/text/index.mjs +9 -0
  145. package/dist/model/text/normalize.d.mts +8 -0
  146. package/dist/model/text/normalize.mjs +13 -0
  147. package/dist/model/text/outline.d.mts +4 -0
  148. package/dist/model/text/outline.mjs +71 -0
  149. package/dist/model/wordIntegers.d.mts +29 -0
  150. package/dist/model/wordIntegers.mjs +78 -0
  151. package/dist/office-js/document.mjs +15 -1
  152. package/dist/office-js/fragment.d.mts +7 -5
  153. package/dist/office-js/fragment.mjs +63 -14
  154. package/dist/office-js/proxy.d.mts +8 -0
  155. package/dist/office-js/proxy.mjs +29 -0
  156. package/dist/office-js/run.mjs +2 -0
  157. package/dist/office-js/supported.generated.mjs +16 -0
  158. package/dist/office-js/toApiScript.mjs +29 -12
  159. package/dist/opc/DirectoryPartStore.d.mts +6 -2
  160. package/dist/opc/DirectoryPartStore.mjs +11 -4
  161. package/dist/opc/FlatOpcPartStore.d.mts +4 -2
  162. package/dist/opc/FlatOpcPartStore.mjs +11 -8
  163. package/dist/opc/Load.d.mts +32 -0
  164. package/dist/opc/Load.mjs +115 -5
  165. package/dist/opc/PartStore.d.mts +34 -1
  166. package/dist/opc/PartStore.mjs +71 -8
  167. package/dist/opc/Save.d.mts +32 -0
  168. package/dist/opc/Save.mjs +110 -4
  169. package/dist/opc/ZipPartStore.d.mts +4 -2
  170. package/dist/opc/ZipPartStore.mjs +12 -7
  171. package/dist/opc/exceptions.d.mts +140 -0
  172. package/dist/opc/exceptions.mjs +108 -0
  173. package/dist/opc/index.d.mts +8 -5
  174. package/dist/opc/index.mjs +5 -3
  175. package/dist/opc/mce/preprocessor.d.mts +141 -5
  176. package/dist/opc/mce/preprocessor.mjs +119 -18
  177. package/dist/opc/mce/understood.d.mts +3 -11
  178. package/dist/opc/mce/understood.mjs +27 -23
  179. package/dist/opc/repair/breaks.d.mts +23 -0
  180. package/dist/opc/repair/breaks.mjs +288 -0
  181. package/dist/opc/repair/hosts.d.mts +12 -0
  182. package/dist/opc/repair/hosts.mjs +33 -0
  183. package/dist/opc/repair/mainRoot.d.mts +19 -0
  184. package/dist/opc/repair/mainRoot.mjs +168 -0
  185. package/dist/opc/repair/unknownElements.d.mts +8 -0
  186. package/dist/opc/repair/unknownElements.mjs +180 -0
  187. package/dist/opc/repair/wmlNames.generated.d.mts +2 -0
  188. package/dist/opc/repair/wmlNames.generated.mjs +80 -0
  189. package/dist/opc/skipped.d.mts +50 -0
  190. package/dist/opc/skipped.mjs +140 -0
  191. package/dist/packages/OpcPackage.d.mts +110 -4
  192. package/dist/packages/OpcPackage.mjs +123 -7
  193. package/dist/packages/WordprocessingMLPackage.d.mts +45 -8
  194. package/dist/packages/WordprocessingMLPackage.mjs +158 -21
  195. package/dist/packages/index.d.mts +1 -1
  196. package/dist/parts/DefaultXmlPart.mjs +2 -2
  197. package/dist/parts/Part.d.mts +14 -2
  198. package/dist/parts/RelationshipsPart.d.mts +15 -0
  199. package/dist/parts/RelationshipsPart.mjs +66 -1
  200. package/dist/parts/XmlPart.d.mts +63 -32
  201. package/dist/parts/XmlPart.mjs +240 -96
  202. package/dist/parts/wml/comments.mjs +30 -0
  203. package/dist/parts/wml/index.d.mts +12 -1
  204. package/dist/parts/wml/index.mjs +37 -1
  205. package/dist/xml/dom.d.mts +7 -0
  206. package/dist/xml/dom.mjs +18 -0
  207. package/package.json +2 -2
@@ -0,0 +1,4 @@
1
+ import type { Outline } from '../content/Body.mjs';
2
+ import type { TextBlock } from './TextResult.mjs';
3
+ /** The outline of blocks: each paragraph with its address, paraId, style and text, each table with its size; a cell's blocks after its table. */
4
+ export declare function outlineOfBlocks(blocks: readonly TextBlock[]): Outline;
@@ -0,0 +1,71 @@
1
+ // CR-012 D3: Body.outline() and pkg.outline() read the text model. The paragraph text is the model's (hidden
2
+ // text left out, a deleted mark joined, the view's), each text box's paragraphs follow their anchor at
3
+ // `textbox:` addresses, flagged, and the options are the model's; the shape is the outline's own.
4
+ import { setOutlineReader } from '../content/outline.mjs';
5
+ import { Extractor } from './Extractor.mjs';
6
+ import { PackageTrees, TextContext } from './TextContext.mjs';
7
+ import { contextFor, optionsOf, isPackage } from './Texts.mjs';
8
+ /** The story a body's blocks are in, from its address prefix. */
9
+ function storyOf(prefix) {
10
+ const kind = prefix.split(':')[0];
11
+ switch (kind) {
12
+ case 'header': return 'HEADER';
13
+ case 'footer': return 'FOOTER';
14
+ case 'footnote': return 'FOOTNOTE';
15
+ case 'endnote': return 'ENDNOTE';
16
+ case 'comment': return 'COMMENT';
17
+ default: return 'MAIN';
18
+ }
19
+ }
20
+ /** The outline of blocks: each paragraph with its address, paraId, style and text, each table with its size; a cell's blocks after its table. */
21
+ export function outlineOfBlocks(blocks) {
22
+ const paragraphs = [];
23
+ const tables = [];
24
+ const visit = (list) => {
25
+ var _a, _b, _c;
26
+ for (const block of list) {
27
+ if (block.kind === 'PARAGRAPH') {
28
+ const entry = { address: block.address, style: (_a = block.style) !== null && _a !== void 0 ? _a : 'Normal', text: block.text };
29
+ if (block.paraId)
30
+ entry.paraId = block.paraId;
31
+ if (block.textbox)
32
+ entry.textbox = true;
33
+ if (block.joined.length > 0)
34
+ entry.joined = [...block.joined];
35
+ paragraphs.push(entry);
36
+ }
37
+ else if (block.kind === 'TABLE') {
38
+ tables.push({ address: block.address, rows: block.rows.length, cols: (_c = (_b = block.rows[0]) === null || _b === void 0 ? void 0 : _b.cells.length) !== null && _c !== void 0 ? _c : 0 });
39
+ for (const row of block.rows)
40
+ for (const cell of row.cells)
41
+ visit(cell.blocks);
42
+ }
43
+ }
44
+ };
45
+ visit(blocks);
46
+ return { paragraphs, tables };
47
+ }
48
+ setOutlineReader(async (body, options, context, blocks) => {
49
+ const o = optionsOf(options);
50
+ const pkg = body.package_;
51
+ const kind = storyOf(body.prefix);
52
+ if (!isPackage(pkg)) {
53
+ // content with no package: read as it stands, as Texts.of(content) does
54
+ const none = PackageTrees.none();
55
+ const x = new Extractor(none, o, 'planless', new TextContext(none));
56
+ if (blocks === undefined)
57
+ return outlineOfBlocks(x.extractContent(body.container, kind, body.prefix, undefined).blocks);
58
+ const starts = Extractor.fieldsAtBlocks(none, o, 'planless', new TextContext(none), body.container, kind, body.prefix, undefined);
59
+ return outlineOfBlocks(x.extractBlocks(body.container, kind, body.prefix, undefined, blocks, starts).blocks);
60
+ }
61
+ const ctx = await contextFor(pkg, context, o);
62
+ const part = body.part;
63
+ if (blocks === undefined)
64
+ return outlineOfBlocks(new Extractor(ctx.trees, o, 'view', ctx).extractContent(body.container, kind, body.prefix, part).blocks);
65
+ // some blocks (section 17.13): the plan made in the context itself, so that the siblings share it, and the
66
+ // reading under a sibling, so that it numbers no note and counts no list in the caller's context
67
+ if (o.view !== 'markup')
68
+ ctx.plan(o.view === 'accepted');
69
+ const starts = ctx.blockStarts(body.container, JSON.stringify(o.toMap()), () => Extractor.fieldsAtBlocks(ctx.trees, o, 'view', ctx, body.container, kind, body.prefix, part));
70
+ return outlineOfBlocks(new Extractor(ctx.trees, o, 'view', ctx.sibling()).extractBlocks(body.container, kind, body.prefix, part, blocks, starts).blocks);
71
+ });
@@ -0,0 +1,29 @@
1
+ import { type WordIntegerReading, type WordIntegerStorage } from '@docx4j/generated-objects-ts/helpers/wml';
2
+ /** `[min, max]` in the attribute's own unit: every value in it Word reads as written, and none outside. */
3
+ export type KeptRange = readonly [number, number];
4
+ /** The certain range of an attribute the table has but no round measured: outside it every measured attribute of its type changes. */
5
+ export declare const UNMEASURED_RANGE: KeptRange;
6
+ /** Whether a reading is of an attribute no round measured (`storage` says so), as against one Word reads to no number. */
7
+ export declare const isUnmeasured: (reading: WordIntegerReading) => boolean;
8
+ /**
9
+ * The range Word 365 keeps as written in an attribute of the table (CR-017 E1's `range`; CR-002 section 65.3's bounds):
10
+ * - a signed field `-2^(bits-1)` to `2^(bits-1) - 1`, an unsigned one `0` to `2^bits - 1`;
11
+ * - `w:ind`'s `firstLine` and `hanging`, one signed field, `0` to `32767` and `0` to `32768`;
12
+ * - a line or a row height whose sign is the rule: under `exact` `1` to `32768`, under `atLeast` `0` to `32767`, a line
13
+ * under `auto` the plain signed field; a row height under `hRule="auto"` is ignored, so null;
14
+ * - a frame's height `0` to `32767` (15 bits);
15
+ * - then the clamps (a tab stop, the page size), and a column's width or space, whose negative Word replaces (a
16
+ * computed width; -1, which it also keeps as written, its own sentinel).
17
+ * The rule is the `lineRule` or `hRule` beside the attribute, where it has one. Undefined for an attribute not in the
18
+ * table. 65535 in an attribute Word's save writes as -1 (`w:spacing`'s `before` and `after`, the unsigned page margins)
19
+ * is in the range, and Word reads a -1 there as 65535 too, its own spelling.
20
+ */
21
+ export declare function keptRange(typeName: string, property: string, rule?: string): KeptRange | null | undefined;
22
+ /** `keptRange` from the table's entry; `property` for the shared field, `rule` for a line or a height. */
23
+ export declare function rangeOf(entry: WordIntegerStorage, property: string, rule?: string): KeptRange | null;
24
+ /**
25
+ * Word's reading, for a message: the value, in the shared field's other attribute where the sign moved it (`25536 in
26
+ * w:hanging`), with the rule where the sign carries one (`atLeast 1`, `exact 32768`); `not known` for an unmeasured
27
+ * attribute.
28
+ */
29
+ export declare function describeReading(reading: WordIntegerReading, attribute: string): string;
@@ -0,0 +1,78 @@
1
+ // CR-017: what Word 365 keeps of an integer attribute, from the objects package's table (its CR-016, docx4j CR-066's
2
+ // rounds 5 and 6, run by Jason 2026-10-26). Word stores each attribute in a field of its binary format's width, 16 bits
3
+ // for most, and reads a value past it wrapped, sometimes then clamped, with no error or repair. The table gives the
4
+ // width, the sign and the rules per attribute; this module turns an entry into the range of values Word keeps as
5
+ // written, which the setters' guards (CR-002 section 65.3, `wordRange.mts`) and the out-of-range warnings (CR-017 P2
6
+ // and P3, `XmlPart.valueOutOfRange`) both read, so there is one source for both. A report names its entry (`typeName`,
7
+ // `property`, `storage` and the `rule` beside the value, objects 0.7.0), so `rangeOf` takes it straight.
8
+ import { WORD_INTEGER_STORAGE } from '@docx4j/generated-objects-ts/helpers/wml';
9
+ /** The certain range of an attribute the table has but no round measured: outside it every measured attribute of its type changes. */
10
+ export const UNMEASURED_RANGE = [-32768, 65535];
11
+ /** Whether a reading is of an attribute no round measured (`storage` says so), as against one Word reads to no number. */
12
+ export const isUnmeasured = (reading) => reading.storage.startsWith('unmeasured');
13
+ /**
14
+ * The range Word 365 keeps as written in an attribute of the table (CR-017 E1's `range`; CR-002 section 65.3's bounds):
15
+ * - a signed field `-2^(bits-1)` to `2^(bits-1) - 1`, an unsigned one `0` to `2^bits - 1`;
16
+ * - `w:ind`'s `firstLine` and `hanging`, one signed field, `0` to `32767` and `0` to `32768`;
17
+ * - a line or a row height whose sign is the rule: under `exact` `1` to `32768`, under `atLeast` `0` to `32767`, a line
18
+ * under `auto` the plain signed field; a row height under `hRule="auto"` is ignored, so null;
19
+ * - a frame's height `0` to `32767` (15 bits);
20
+ * - then the clamps (a tab stop, the page size), and a column's width or space, whose negative Word replaces (a
21
+ * computed width; -1, which it also keeps as written, its own sentinel).
22
+ * The rule is the `lineRule` or `hRule` beside the attribute, where it has one. Undefined for an attribute not in the
23
+ * table. 65535 in an attribute Word's save writes as -1 (`w:spacing`'s `before` and `after`, the unsigned page margins)
24
+ * is in the range, and Word reads a -1 there as 65535 too, its own spelling.
25
+ */
26
+ export function keptRange(typeName, property, rule) {
27
+ var _a;
28
+ const entry = (_a = WORD_INTEGER_STORAGE[typeName]) === null || _a === void 0 ? void 0 : _a[property];
29
+ return entry === undefined ? undefined : rangeOf(entry, property, rule);
30
+ }
31
+ /** `keptRange` from the table's entry; `property` for the shared field, `rule` for a line or a height. */
32
+ export function rangeOf(entry, property, rule) {
33
+ if (!entry.measured)
34
+ return UNMEASURED_RANGE;
35
+ const half = 2 ** (entry.bits - 1);
36
+ let min;
37
+ let max;
38
+ if (entry.heightFlag)
39
+ [min, max] = [0, half - 1];
40
+ else if (entry.pair)
41
+ [min, max] = property === entry.pair.negative ? [0, half] : [0, half - 1];
42
+ else if (entry.signRule) {
43
+ const read = rule !== null && rule !== void 0 ? rule : (entry.signRule === 'hRule' ? 'atLeast' : 'auto');
44
+ if (entry.signRule === 'hRule' && read === 'auto')
45
+ return null;
46
+ if (read === 'exact')
47
+ [min, max] = [1, half];
48
+ else if (read === 'atLeast')
49
+ [min, max] = [0, half - 1];
50
+ else
51
+ [min, max] = [-half, half - 1];
52
+ }
53
+ else if (entry.signed)
54
+ [min, max] = [-half, half - 1];
55
+ else
56
+ [min, max] = [0, 2 ** entry.bits - 1];
57
+ if (entry.negative !== undefined)
58
+ min = Math.max(min, 0);
59
+ if (entry.min !== undefined)
60
+ min = Math.max(min, entry.min);
61
+ if (entry.max !== undefined)
62
+ max = Math.min(max, entry.max);
63
+ return [min, max];
64
+ }
65
+ /** Whether a reading's storage string names a rule beside the value (a line's `lineRule`, a height's `hRule`). */
66
+ const dependsOnRule = (storage) => /\brule\b|clearing exact/.test(storage);
67
+ /**
68
+ * Word's reading, for a message: the value, in the shared field's other attribute where the sign moved it (`25536 in
69
+ * w:hanging`), with the rule where the sign carries one (`atLeast 1`, `exact 32768`); `not known` for an unmeasured
70
+ * attribute.
71
+ */
72
+ export function describeReading(reading, attribute) {
73
+ if (reading.value === null)
74
+ return reading.storage.includes('computed') ? 'a width Word computes' : 'not known';
75
+ const where = reading.attribute !== undefined && reading.attribute !== attribute ? ` in w:${reading.attribute}` : '';
76
+ const rule = reading.rule !== undefined && dependsOnRule(reading.storage) ? `${reading.rule} ` : '';
77
+ return `${rule}${reading.value}${where}`;
78
+ }
@@ -15,10 +15,24 @@ function literal(text) {
15
15
  function w3cdtf(date) {
16
16
  return { TYPE_NAME: 'org_docx4j_docProps_core_dc_terms.W3CDTF', content: [date.toISOString().replace(/\.\d{3}Z$/, 'Z')] };
17
17
  }
18
+ /**
19
+ * A `dcterms:created` or `dcterms:modified` value as Word 365 reads it (docx4j CR-067 section 12.2, Jason's probe of
20
+ * 2026-10-28; CR-018 D6): a date alone (`2024-02-03`) and a time with no offset (`2025-06-07T08:09:10`) are UTC, as a `Z`
21
+ * value is (Word showed 11:00 and 18:09 at UTC+11 and UTC+10), so they are read here as UTC fields, where ECMAScript's
22
+ * `Date` would read the offset-free time as local. A `Z` or an offset is honoured as before. The seconds are kept: Word
23
+ * shows the value to the minute, but this reads the stored instant.
24
+ */
18
25
  function dateOf(value) {
19
- const text = literalText(value);
26
+ const text = literalText(value).trim();
20
27
  if (text === '')
21
28
  return undefined;
29
+ const bare = /^(\d{4})-(\d{2})-(\d{2})(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d+))?)?)?$/.exec(text);
30
+ if (bare) {
31
+ const [year, month, day, hour, minute, second, fraction] = bare.slice(1);
32
+ const ms = fraction === undefined ? 0 : Math.round(Number(`0.${fraction}`) * 1000);
33
+ const date = new Date(Date.UTC(Number(year), Number(month) - 1, Number(day), Number(hour !== null && hour !== void 0 ? hour : 0), Number(minute !== null && minute !== void 0 ? minute : 0), Number(second !== null && second !== void 0 ? second : 0), ms));
34
+ return Number.isNaN(date.getTime()) ? undefined : date;
35
+ }
22
36
  const date = new Date(text);
23
37
  return Number.isNaN(date.getTime()) ? undefined : date;
24
38
  }
@@ -1,10 +1,12 @@
1
- import type * as wml from '@docx4j/generated-objects-ts/modules/org_docx4j_wml';
2
1
  import type { Range } from '../model/content/Range.mjs';
3
2
  import type { Element } from '../model/content/tree.mjs';
4
3
  /**
5
- * A detached `w:p` holding exactly the range's text, with the runs and their formatting. The
6
- * paragraph is copied first, so the document is untouched.
4
+ * The blocks a range holds, as detached copies: within one paragraph, that paragraph trimmed to the range; across
5
+ * paragraphs, the first paragraph from the range's start, every block between whole (a table, a content control),
6
+ * and the last paragraph to the range's end, left out where the range ends at its start. The blocks are those
7
+ * `delete()` takes (`extentOf`), so a range starting or ending inside a table or a content control it doesn't hold
8
+ * whole is refused: a fragment of part of one isn't written.
7
9
  */
8
- export declare function paragraphElementOfRange(range: Range): Element<wml.P>;
9
- /** The range as a `w:p` fragment (extension; Office JS wraps a range in a pkg:package). */
10
+ export declare function blocksOfRange(range: Range): Element[];
11
+ /** The range as a fragment: its blocks' XML, one after another (extension; Office JS wraps a range in a pkg:package). */
10
12
  export declare function xmlOfRange(range: Range): Promise<string>;
@@ -1,23 +1,72 @@
1
1
  // Marshalling a view back to a fragment: what `getOoxml()` and `toApiScript`'s fallback need.
2
- // A `w:p` copy holding only a range's text is the one piece the views do not offer (Range has no
3
- // element of its own: it is a span of the paragraph's text).
2
+ // Copies of the blocks a range holds, its paragraphs trimmed to it, are the one piece the views do
3
+ // not offer (Range has no element of its own: it is a span of its paragraphs' text).
4
4
  import { deepCopy, marshalString } from '@docx4j/generated-objects-ts';
5
+ import { Body } from '../model/content/Body.mjs';
5
6
  import { Paragraph } from '../model/content/Paragraph.mjs';
7
+ import { extentOf } from '../model/content/spanEdits.mjs';
8
+ import { GeneralException } from '../model/content/generalException.mjs';
9
+ import { NotSupportedError } from './errors.mjs';
6
10
  /**
7
- * A detached `w:p` holding exactly the range's text, with the runs and their formatting. The
8
- * paragraph is copied first, so the document is untouched.
11
+ * The body a detached copy is trimmed through. It reads as the paragraph's own body does (its package's styles
12
+ * decide which runs are hidden, and so the offsets), and writes untracked: trimming a copy is no edit of the
13
+ * document, and under tracking the trim would otherwise mark the rest of the copy deleted. What the trim queues on
14
+ * the package (a picture's relationship, a comment whose reference the copy lost) is settled at save against the
15
+ * document as it stands, which still holds them.
9
16
  */
10
- export function paragraphElementOfRange(range) {
11
- const copy = deepCopy(range.paragraph.element);
12
- const view = new Paragraph(copy, [copy], range.paragraph.parentBody);
17
+ class CopyBody extends Body {
18
+ get changeTracker() {
19
+ return undefined;
20
+ }
21
+ }
22
+ /** A detached copy of `paragraph` holding only its text from `start` to `end`, with the runs and their formatting. */
23
+ function trimmed(paragraph, start, end) {
24
+ const copy = deepCopy(paragraph.element);
25
+ const own = paragraph.parentBody;
26
+ const view = new Paragraph(copy, [copy], new CopyBody(own.part, { content: [copy] }, own.prefix, own.package_));
13
27
  const length = view.text.length;
14
- if (range.end < length)
15
- view.splice(range.end, length, '');
16
- if (range.start > 0)
17
- view.splice(0, range.start, '');
28
+ if (end < length)
29
+ view.splice(end, length, '');
30
+ if (start > 0)
31
+ view.splice(0, start, '');
18
32
  return copy;
19
33
  }
20
- /** The range as a `w:p` fragment (extension; Office JS wraps a range in a pkg:package). */
21
- export function xmlOfRange(range) {
22
- return marshalString(paragraphElementOfRange(range));
34
+ /**
35
+ * The blocks a range holds, as detached copies: within one paragraph, that paragraph trimmed to the range; across
36
+ * paragraphs, the first paragraph from the range's start, every block between whole (a table, a content control),
37
+ * and the last paragraph to the range's end, left out where the range ends at its start. The blocks are those
38
+ * `delete()` takes (`extentOf`), so a range starting or ending inside a table or a content control it doesn't hold
39
+ * whole is refused: a fragment of part of one isn't written.
40
+ */
41
+ export function blocksOfRange(range) {
42
+ if (!range.spansParagraphs)
43
+ return [trimmed(range.paragraph, range.start, range.end)];
44
+ let extent;
45
+ try {
46
+ extent = extentOf(range, 'getOoxml');
47
+ }
48
+ catch (error) {
49
+ if (!(error instanceof GeneralException) && !(error instanceof NotSupportedError))
50
+ throw error;
51
+ throw new NotSupportedError('Range', 'getOoxml', "this range starts or ends inside a table or a content control it doesn't hold whole, "
52
+ + "and a fragment of part of one isn't written; take the whole table or control, or a range inside one cell");
53
+ }
54
+ const out = [];
55
+ for (let k = extent.first; k <= extent.last; k++) {
56
+ const block = extent.list[k];
57
+ if (k === extent.first && extent.head)
58
+ out.push(trimmed(extent.head, range.start, extent.head.text.length));
59
+ else if (k === extent.last && extent.tail) {
60
+ if (range.end > 0)
61
+ out.push(trimmed(extent.tail, 0, range.end));
62
+ }
63
+ else
64
+ out.push(deepCopy(block));
65
+ }
66
+ return out;
67
+ }
68
+ /** The range as a fragment: its blocks' XML, one after another (extension; Office JS wraps a range in a pkg:package). */
69
+ export async function xmlOfRange(range) {
70
+ const xml = await Promise.all(blocksOfRange(range).map((block) => marshalString(block)));
71
+ return xml.join('');
23
72
  }
@@ -23,6 +23,14 @@ export declare class ClientResult<T = unknown> {
23
23
  resolveWith(value: T): void;
24
24
  /** True once `context.sync()` has filled it. */
25
25
  get isLoaded(): boolean;
26
+ /**
27
+ * Office JS's `load` on what an asynchronous member returned: nothing to load here, so the result itself.
28
+ * With `items`, a collection a member returns asynchronously (`range.getComments()`, the comment parts
29
+ * being read first) reads as Office JS code expects: `load`, `context.sync()`, then `items` (CR-014 phase 1).
30
+ */
31
+ load(): this;
32
+ /** The collection's items, once `context.sync()` has filled the result: an asynchronous collection's. */
33
+ get items(): T extends readonly (infer I)[] ? I[] : never;
26
34
  toJSON(): {
27
35
  value: T | undefined;
28
36
  };
@@ -47,6 +47,21 @@ export class ClientResult {
47
47
  get isLoaded() {
48
48
  return this.loaded;
49
49
  }
50
+ /**
51
+ * Office JS's `load` on what an asynchronous member returned: nothing to load here, so the result itself.
52
+ * With `items`, a collection a member returns asynchronously (`range.getComments()`, the comment parts
53
+ * being read first) reads as Office JS code expects: `load`, `context.sync()`, then `items` (CR-014 phase 1).
54
+ */
55
+ load() {
56
+ return this;
57
+ }
58
+ /** The collection's items, once `context.sync()` has filled the result: an asynchronous collection's. */
59
+ get items() {
60
+ const value = this.value;
61
+ if (!Array.isArray(value))
62
+ throw new NotSupportedError('ClientResult', 'items', `the result of ${this.member}() is not a collection`);
63
+ return value;
64
+ }
50
65
  toJSON() {
51
66
  return { value: this.loaded ? this.result : undefined };
52
67
  }
@@ -219,6 +234,9 @@ export class Wrapper {
219
234
  return wrapped;
220
235
  });
221
236
  result.attach(settled);
237
+ // the next sync() reports a failure; code that throws before it (a member of the result called at once, as
238
+ // Office JS code chains `insertComment(...).getRange()`) leaves it unawaited, which must not end the process
239
+ settled.catch(() => { });
222
240
  this.context.enqueue(settled);
223
241
  return result;
224
242
  }
@@ -253,6 +271,12 @@ export class Wrapper {
253
271
  result.resolveWith(wrapped.length);
254
272
  return result;
255
273
  });
274
+ if (itemClass === 'ContentControl') {
275
+ // Office JS's ContentControlCollection lookups: the controls with the tag or title, as a collection
276
+ const matching = (key, value) => this.collection(items.filter((i) => unwrap(i)[key] === value), 'contentControls');
277
+ define('getByTag', (tag) => matching('tag', tag));
278
+ define('getByTitle', (title) => matching('title', title));
279
+ }
256
280
  define('load', () => collectionProxy);
257
281
  define('track', () => collectionProxy);
258
282
  define('untrack', () => collectionProxy);
@@ -268,6 +292,11 @@ export class Wrapper {
268
292
  if (!(prop in obj))
269
293
  throw new NotSupportedError(className, prop);
270
294
  const member2 = Reflect.get(obj, prop, obj);
295
+ // the array's own methods (map, filter, forEach) give what they give: an Office JS collection has
296
+ // none, and a map's empty result made into a collection wouldn't serialize (CR-014 phase 1)
297
+ if (typeof member2 === 'function' && Object.prototype.hasOwnProperty.call(Array.prototype, prop)) {
298
+ return member2.bind(obj);
299
+ }
271
300
  if (typeof member2 === 'function' && !/^\d+$/.test(prop)) {
272
301
  return (...args) => wrapperSelf.result(member2.apply(obj, args.map(unwrap)), prop);
273
302
  }
@@ -43,6 +43,8 @@ export class RequestContext {
43
43
  async function prepare(pkg, options) {
44
44
  const main = pkg.getMainDocumentPart();
45
45
  await main.getContents();
46
+ // a control's placeholderText reads the glossary's entries, read privately (CR-002 section 63)
47
+ await pkg.glossaryEntries.load(main.glossaryDocumentPart);
46
48
  if (options.unmarshalSideParts === false)
47
49
  return;
48
50
  const sideParts = [pkg.docPropsCorePart, pkg.docPropsExtendedPart, main.documentSettingsPart];
@@ -5,6 +5,8 @@ export const SUBSET_MEMBERS = [
5
5
  'Body.clear',
6
6
  'Body.contentControls',
7
7
  'Body.getComments',
8
+ 'Body.getContentControls',
9
+ 'Body.getRange',
8
10
  'Body.getTrackedChanges',
9
11
  'Body.inlinePictures',
10
12
  'Body.insertBreak',
@@ -23,6 +25,7 @@ export const SUBSET_MEMBERS = [
23
25
  'Comment.content',
24
26
  'Comment.creationDate',
25
27
  'Comment.delete',
28
+ 'Comment.getRange',
26
29
  'Comment.replies',
27
30
  'Comment.reply',
28
31
  'Comment.resolved',
@@ -36,6 +39,7 @@ export const SUBSET_MEMBERS = [
36
39
  'ContentControl.datePickerContentControl',
37
40
  'ContentControl.delete',
38
41
  'ContentControl.dropDownListContentControl',
42
+ 'ContentControl.getContentControls',
39
43
  'ContentControl.getRange',
40
44
  'ContentControl.groupContentControl',
41
45
  'ContentControl.id',
@@ -47,6 +51,7 @@ export const SUBSET_MEMBERS = [
47
51
  'ContentControl.removeWhenEdited',
48
52
  'ContentControl.repeatingSectionContentControl',
49
53
  'ContentControl.search',
54
+ 'ContentControl.showingPlaceholderText',
50
55
  'ContentControl.tables',
51
56
  'ContentControl.tag',
52
57
  'ContentControl.text',
@@ -57,6 +62,7 @@ export const SUBSET_MEMBERS = [
57
62
  'ContentControlListItem.displayText',
58
63
  'ContentControlListItem.index',
59
64
  'ContentControlListItem.value',
65
+ 'ContentControlOptions.types',
60
66
  'CustomXmlNode.attributes',
61
67
  'CustomXmlNode.baseName',
62
68
  'CustomXmlNode.childNodes',
@@ -151,6 +157,7 @@ export const SUBSET_MEMBERS = [
151
157
  'Paragraph.firstLineIndent',
152
158
  'Paragraph.font',
153
159
  'Paragraph.getComments',
160
+ 'Paragraph.getContentControls',
154
161
  'Paragraph.getRange',
155
162
  'Paragraph.getTrackedChanges',
156
163
  'Paragraph.insertBreak',
@@ -172,19 +179,27 @@ export const SUBSET_MEMBERS = [
172
179
  'Paragraph.startNewList',
173
180
  'Paragraph.style',
174
181
  'Paragraph.styleBuiltIn',
182
+ 'Paragraph.tableNestingLevel',
175
183
  'Paragraph.text',
176
184
  'PictureContentControl.inlinePicture',
185
+ 'Range.contentControls',
177
186
  'Range.delete',
187
+ 'Range.expandTo',
178
188
  'Range.font',
179
189
  'Range.getComments',
190
+ 'Range.getContentControls',
180
191
  'Range.getRange',
181
192
  'Range.getTrackedChanges',
182
193
  'Range.hyperlink',
183
194
  'Range.insertComment',
184
195
  'Range.insertParagraph',
185
196
  'Range.insertText',
197
+ 'Range.isEmpty',
198
+ 'Range.paragraphs',
186
199
  'Range.search',
187
200
  'Range.style',
201
+ 'Range.styleBuiltIn',
202
+ 'Range.tables',
188
203
  'Range.text',
189
204
  'RepeatingSectionContentControl.allowInsertDeleteSection',
190
205
  'RepeatingSectionContentControl.items',
@@ -196,6 +211,7 @@ export const SUBSET_MEMBERS = [
196
211
  'Table.delete',
197
212
  'Table.deleteRows',
198
213
  'Table.getCell',
214
+ 'Table.getRange',
199
215
  'Table.headerRowCount',
200
216
  'Table.rowCount',
201
217
  'Table.rows',
@@ -9,8 +9,8 @@ import { Docx4JException } from '../opc/exceptions.mjs';
9
9
  import { Body } from '../model/content/Body.mjs';
10
10
  import { Paragraph } from '../model/content/Paragraph.mjs';
11
11
  import { Range } from '../model/content/Range.mjs';
12
- import { isElement, typeNameOf, textOf } from '../model/content/tree.mjs';
13
- import { paragraphElementOfRange } from './fragment.mjs';
12
+ import { isElement, typeNameOf, textOf, selectedBranchOf, ALTERNATE_CONTENT } from '../model/content/tree.mjs';
13
+ import { blocksOfRange } from './fragment.mjs';
14
14
  import { builtInOf, displayNameOf } from '../model/content/styles.mjs';
15
15
  const TWIPS_PER_POINT = 20;
16
16
  /** Paragraph properties the verbs express; anything else sends the paragraph to `insertXml`. */
@@ -97,13 +97,23 @@ class Emitter {
97
97
  line(text) {
98
98
  this.lines.push(this.indent + text);
99
99
  }
100
- /** A block-level element: a paragraph, a table, or anything else through `insertXml`. */
100
+ /**
101
+ * A block-level element: a paragraph, a table, or anything else through `insertXml`. A kept
102
+ * `mc:AlternateContent` is its selected branch's content, as Word reads it and as its save writes it:
103
+ * `insertXml` refuses the element itself (CR-013 phase 3).
104
+ */
101
105
  async element(element) {
106
+ var _a;
102
107
  const typeName = typeNameOf(element);
103
108
  if (typeName === 'org_docx4j_wml.P')
104
109
  await this.paragraph(element);
105
110
  else if (typeName === 'org_docx4j_wml.Tbl')
106
111
  await this.table(element);
112
+ else if (typeName === ALTERNATE_CONTENT) {
113
+ this.line('// an mc:AlternateContent: its selected branch, as Word reads it');
114
+ for (const child of (_a = selectedBranchOf(element.value)) !== null && _a !== void 0 ? _a : [])
115
+ await this.element(child);
116
+ }
107
117
  else
108
118
  await this.fallback(element);
109
119
  }
@@ -117,9 +127,10 @@ class Emitter {
117
127
  await this.fallback(element, error.message);
118
128
  }
119
129
  }
120
- /** A range: its text and run formatting, as one paragraph. */
130
+ /** A range: its text and run formatting, as one paragraph; across paragraphs, each block it holds. */
121
131
  async range(range) {
122
- await this.paragraph(paragraphElementOfRange(range));
132
+ for (const block of blocksOfRange(range))
133
+ await this.element(block);
123
134
  }
124
135
  emitParagraph(p) {
125
136
  const properties = this.paragraphProperties(p);
@@ -173,7 +184,7 @@ class Emitter {
173
184
  * the generated document (CR-001 Phase B step 2; the same holds for `runFormat` below).
174
185
  */
175
186
  paragraphProperties(p) {
176
- var _a, _b, _c, _d, _e, _f, _g;
187
+ var _a, _b, _c, _d, _e, _f;
177
188
  const pPr = p.pPr;
178
189
  if (!pPr)
179
190
  return [];
@@ -212,13 +223,14 @@ class Emitter {
212
223
  if (spacing.after !== undefined)
213
224
  out.push(['spaceAfter', num(spacing.after / TWIPS_PER_POINT)]);
214
225
  if (spacing.line !== undefined) {
215
- // Paragraph.lineSpacing writes w:lineRule="exact", so only an exact rule round-trips.
216
- if (spacing.lineRule !== 'exact')
217
- throw new Unexpressible(`w:spacing w:lineRule="${(_c = spacing.lineRule) !== null && _c !== void 0 ? _c : 'auto'}"`);
226
+ // Paragraph.lineSpacing writes w:lineRule="auto", as Word's setter does (CR-002 section 65.9), so only an auto rule,
227
+ // stated or the default, round-trips.
228
+ if (spacing.lineRule !== undefined && spacing.lineRule !== 'auto')
229
+ throw new Unexpressible(`w:spacing w:lineRule="${spacing.lineRule}"`);
218
230
  out.push(['lineSpacing', num(spacing.line / TWIPS_PER_POINT)]);
219
231
  }
220
232
  }
221
- const outlineLvl = (_d = pPr.outlineLvl) === null || _d === void 0 ? void 0 : _d.val;
233
+ const outlineLvl = (_c = pPr.outlineLvl) === null || _c === void 0 ? void 0 : _c.val;
222
234
  if (outlineLvl !== undefined)
223
235
  out.push(['outlineLevel', num(outlineLvl + 1)]);
224
236
  const numPr = pPr.numPr;
@@ -229,10 +241,10 @@ class Emitter {
229
241
  // to know whether the list is a bullet one (CR-002 section 17).
230
242
  if (!within(numPr, NUMPR_KEYS))
231
243
  throw new Unexpressible(`w:numPr (${keysOf(numPr).filter((k) => !NUMPR_KEYS.has(k)).join(', ')})`);
232
- const numId = (_e = numPr.numId) === null || _e === void 0 ? void 0 : _e.val;
244
+ const numId = (_d = numPr.numId) === null || _d === void 0 ? void 0 : _d.val;
233
245
  if (numId === undefined)
234
246
  throw new Unexpressible('w:numPr with no w:numId');
235
- out.push([CALL, numId === 0 ? 'detachFromList()' : `attachToList(${numId}, ${(_g = (_f = numPr.ilvl) === null || _f === void 0 ? void 0 : _f.val) !== null && _g !== void 0 ? _g : 0})`]);
247
+ out.push([CALL, numId === 0 ? 'detachFromList()' : `attachToList(${numId}, ${(_f = (_e = numPr.ilvl) === null || _e === void 0 ? void 0 : _e.val) !== null && _f !== void 0 ? _f : 0})`]);
236
248
  }
237
249
  return out;
238
250
  }
@@ -326,10 +338,15 @@ function tableGrid(tbl) {
326
338
  const typeName = typeNameOf(item);
327
339
  if (typeName === 'org_docx4j_wml.CTTblGridCol' || typeName === 'org_docx4j_wml.TblGrid')
328
340
  continue;
341
+ // a row- or cell-level mc:AlternateContent is no plain grid: the table goes whole through insertXml (CR-013 phase 3)
342
+ if (typeName === ALTERNATE_CONTENT)
343
+ return undefined;
329
344
  if (typeName !== 'org_docx4j_wml.Tr')
330
345
  continue;
331
346
  const row = [];
332
347
  for (const cell of ((_b = item.value.content) !== null && _b !== void 0 ? _b : [])) {
348
+ if (typeNameOf(cell) === ALTERNATE_CONTENT)
349
+ return undefined;
333
350
  if (typeNameOf(cell) !== 'org_docx4j_wml.Tc')
334
351
  continue;
335
352
  const tc = cell.value;
@@ -1,4 +1,4 @@
1
- import type { PartStore, PartSink, PutOptions } from './PartStore.mjs';
1
+ import { type PartStore, type PartSink, type PutOptions } from './PartStore.mjs';
2
2
  /**
3
3
  * A package spread over a directory: `word/document.xml` is the file `<dir>/word/document.xml`,
4
4
  * `[Content_Types].xml` is read from the directory like any other entry, and a part's size comes
@@ -14,7 +14,10 @@ import type { PartStore, PartSink, PutOptions } from './PartStore.mjs';
14
14
  */
15
15
  export declare class DirectoryPartStore implements PartStore {
16
16
  readonly dir: string;
17
- /** Store name (no leading '/') to [absolute file path, size]. */
17
+ /**
18
+ * Store name (no leading '/') to [absolute file path, size], looked up without case; of two files whose names differ only
19
+ * in case (a case-sensitive file system), the first in the walk is read and the rest counted (CR-024).
20
+ */
18
21
  private readonly entries;
19
22
  private constructor();
20
23
  /** Scans a directory and returns a store over it. */
@@ -22,6 +25,7 @@ export declare class DirectoryPartStore implements PartStore {
22
25
  private scan;
23
26
  partNames(): Iterable<string>;
24
27
  has(partName: string): boolean;
28
+ duplicates(): ReadonlyMap<string, number>;
25
29
  load(partName: string): Promise<Uint8Array>;
26
30
  size(partName: string): number | undefined;
27
31
  }