@docx4j/core-ts 0.1.1 → 0.1.3

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.
@@ -1,4 +1,4 @@
1
- export { type XPathEngine, type XPathValue, DefaultXPathEngine, parsePrefixMappings, formatPrefixMappings, canonicalXPathOf, xmlOf, type CanonicalXPath } from './xpath.mjs';
1
+ export { type XPathEngine, type XPathValue, type BooleanConversionMode, booleanValue, DefaultXPathEngine, parsePrefixMappings, formatPrefixMappings, canonicalXPathOf, xmlOf, type CanonicalXPath } from './xpath.mjs';
2
2
  export { CustomXmlPart, CustomXmlNode, CustomXmlPrefixMappingCollection, type CustomXmlNodeType, type CustomXmlPrefixMapping, type CustomXmlPartOwner } from './CustomXmlPart.mjs';
3
3
  export { CustomXmlPartCollection, type CustomXmlHost } from './CustomXmlPartCollection.mjs';
4
4
  export { XmlMapping, type CustomXmlPartLookup } from './XmlMapping.mjs';
@@ -1,6 +1,6 @@
1
1
  // CR-002 phase E: custom XML parts, XML mapping, the XPath engine and the typed content controls
2
2
  // (CR-002 sections 3.5 and 3.6). Exported from '.' and './model', as the content API is.
3
- export { DefaultXPathEngine, parsePrefixMappings, formatPrefixMappings, canonicalXPathOf, xmlOf } from './xpath.mjs';
3
+ export { booleanValue, DefaultXPathEngine, parsePrefixMappings, formatPrefixMappings, canonicalXPathOf, xmlOf } from './xpath.mjs';
4
4
  export { CustomXmlPart, CustomXmlNode, CustomXmlPrefixMappingCollection } from './CustomXmlPart.mjs';
5
5
  export { CustomXmlPartCollection } from './CustomXmlPartCollection.mjs';
6
6
  export { XmlMapping } from './XmlMapping.mjs';
@@ -0,0 +1,42 @@
1
+ import type { BooleanConversionMode, XPathEngine, XPathValue } from './xpath.mjs';
2
+ /**
3
+ * XPath 3.1 through FontoXPath, for the `xpath2` boolean conversion mode and for expressions
4
+ * written in XPath 2.0 syntax. `select`, `selectValue` and the `java` and `xpath1` modes answer as
5
+ * `DefaultXPathEngine` does; `xpath2` is the mode this engine exists for.
6
+ */
7
+ export declare class FontoXPathEngine implements XPathEngine {
8
+ private module;
9
+ private loading;
10
+ get isReady(): boolean;
11
+ ready(): Promise<void>;
12
+ select(expression: string, context: Node, namespaces?: Record<string, string>): Node[];
13
+ selectValue(expression: string, context: Node, namespaces?: Record<string, string>): XPathValue;
14
+ /**
15
+ * Table 7's three modes. `java` and `xpath1` are the rules the specification states, evaluated
16
+ * here rather than through the `booleanValue` function so that one engine answers all three.
17
+ * `xpath2` is the effective boolean value **with the specification's string rule**: see
18
+ * {@link xpath2BooleanValue}.
19
+ */
20
+ booleanValue(expression: string, context: Node, namespaces: Record<string, string>, mode: BooleanConversionMode): boolean;
21
+ /**
22
+ * The `xpath2` mode of table 7, **as docx4j does it**: the expression is wrapped in
23
+ * `xs:boolean(...)` and the engine performs the cast, which is `XmlPart.cachedXPathGetBoolean`
24
+ * with `opendope.conditions.Xpathref.XPathBoolean=cast2` (measured against Saxon-HE 9.9.0-2 by
25
+ * the docx4j session, 2026-09-25, and matched here case for case). Doing what docx4j does,
26
+ * rather than casting by hand, is what keeps the two from drifting: the lexical space, the
27
+ * whitespace collapse, the multi-item rule and the empty sequence all come from the engine.
28
+ *
29
+ * So: `true`, `1` are true; `false`, `0` are false; leading and trailing space is collapsed
30
+ * first, so `" false "` is false; an expression selecting **nothing** is false; and anything
31
+ * else raises - `"yes"`, `"True"` (`xs:boolean`'s lexical space is case-sensitive where the
32
+ * `java` mode ignores case), `""` from an empty element, and a sequence of more than one node.
33
+ *
34
+ * A raise is not a false. docx4j turns it into an `InputIntegrityException` and the whole bind
35
+ * fails; here it is a `Docx4JException` from this call, and what a processor does with it is
36
+ * the processor's business (CR-005 phase B). That is the one place the two behaviours are not
37
+ * yet the same thing, and the specification does not say which is right.
38
+ */
39
+ private xpath2BooleanValue;
40
+ private options;
41
+ private fonto;
42
+ }
@@ -0,0 +1,123 @@
1
+ // CR-005 phase A: an XPath 2.0 (in fact 3.1) `XPathEngine`, for the OpenDoPE specification's
2
+ // `xpath2` boolean conversion mode (its section 7.2, table 7).
3
+ //
4
+ // `DefaultXPathEngine` is XPath 1.0, which is what `document.evaluate` and the `xpath` package
5
+ // give. The specification's `xpath2` mode needs a 2.0 evaluator, and expressions written for it
6
+ // may use 2.0 syntax; docx4j uses Saxon. [FontoXPath](https://github.com/FontoXML/fontoxpath) is
7
+ // XPath 3.1 in JavaScript (MIT), over DOM nodes in browsers and in Node, and XPath 3.1 is a
8
+ // superset of 2.0.
9
+ //
10
+ // It is an **optional peer dependency** and this module is its own entry point
11
+ // (`@docx4j/core-ts/xpath-fonto`), so nothing that imports the package pays its ~665 KB: only a
12
+ // consumer who needs the `xpath2` mode installs it and imports this.
13
+ //
14
+ // import { FontoXPathEngine } from '@docx4j/core-ts/xpath-fonto';
15
+ // pkg.xpathEngine = new FontoXPathEngine();
16
+ // await pkg.customXmlParts.load();
17
+ import { Docx4JException } from '../../opc/exceptions.mjs';
18
+ /** `evaluateXPath`'s ANY_TYPE: the value in its natural type, which is what `selectValue` reports. */
19
+ const ANY_TYPE = 0;
20
+ /**
21
+ * XPath 3.1 through FontoXPath, for the `xpath2` boolean conversion mode and for expressions
22
+ * written in XPath 2.0 syntax. `select`, `selectValue` and the `java` and `xpath1` modes answer as
23
+ * `DefaultXPathEngine` does; `xpath2` is the mode this engine exists for.
24
+ */
25
+ export class FontoXPathEngine {
26
+ get isReady() {
27
+ return this.module !== undefined;
28
+ }
29
+ async ready() {
30
+ var _a;
31
+ if (this.isReady)
32
+ return;
33
+ (_a = this.loading) !== null && _a !== void 0 ? _a : (this.loading = (async () => {
34
+ var _a;
35
+ const specifier = 'fontoxpath';
36
+ try {
37
+ // The published ESM build has no named exports under Node's interop, only a default.
38
+ const loaded = await import(/* @vite-ignore */ specifier);
39
+ this.module = (_a = loaded.default) !== null && _a !== void 0 ? _a : loaded;
40
+ }
41
+ catch (e) {
42
+ throw new Docx4JException("FontoXPathEngine needs the optional peer dependency 'fontoxpath' (npm install fontoxpath)", { cause: e });
43
+ }
44
+ })());
45
+ await this.loading;
46
+ }
47
+ select(expression, context, namespaces = {}) {
48
+ return this.fonto().evaluateXPathToNodes(expression, context, null, null, this.options(namespaces));
49
+ }
50
+ selectValue(expression, context, namespaces = {}) {
51
+ const value = this.fonto().evaluateXPath(expression, context, null, null, ANY_TYPE, this.options(namespaces));
52
+ return valueOf(value);
53
+ }
54
+ /**
55
+ * Table 7's three modes. `java` and `xpath1` are the rules the specification states, evaluated
56
+ * here rather than through the `booleanValue` function so that one engine answers all three.
57
+ * `xpath2` is the effective boolean value **with the specification's string rule**: see
58
+ * {@link xpath2BooleanValue}.
59
+ */
60
+ booleanValue(expression, context, namespaces, mode) {
61
+ switch (mode) {
62
+ case 'java': {
63
+ const value = this.selectValue(expression, context, namespaces);
64
+ return typeof value === 'boolean' ? value : String(value !== null && value !== void 0 ? value : '').toLowerCase() === 'true';
65
+ }
66
+ case 'xpath1':
67
+ // XPath 1.0's boolean() rules, asked for in that language so that 3.1's do not apply.
68
+ return this.fonto().evaluateXPathToBoolean(`boolean(${expression})`, context, null, null, { ...this.options(namespaces), language: 'XPath3.1' });
69
+ default:
70
+ return this.xpath2BooleanValue(expression, context, namespaces);
71
+ }
72
+ }
73
+ /**
74
+ * The `xpath2` mode of table 7, **as docx4j does it**: the expression is wrapped in
75
+ * `xs:boolean(...)` and the engine performs the cast, which is `XmlPart.cachedXPathGetBoolean`
76
+ * with `opendope.conditions.Xpathref.XPathBoolean=cast2` (measured against Saxon-HE 9.9.0-2 by
77
+ * the docx4j session, 2026-09-25, and matched here case for case). Doing what docx4j does,
78
+ * rather than casting by hand, is what keeps the two from drifting: the lexical space, the
79
+ * whitespace collapse, the multi-item rule and the empty sequence all come from the engine.
80
+ *
81
+ * So: `true`, `1` are true; `false`, `0` are false; leading and trailing space is collapsed
82
+ * first, so `" false "` is false; an expression selecting **nothing** is false; and anything
83
+ * else raises - `"yes"`, `"True"` (`xs:boolean`'s lexical space is case-sensitive where the
84
+ * `java` mode ignores case), `""` from an empty element, and a sequence of more than one node.
85
+ *
86
+ * A raise is not a false. docx4j turns it into an `InputIntegrityException` and the whole bind
87
+ * fails; here it is a `Docx4JException` from this call, and what a processor does with it is
88
+ * the processor's business (CR-005 phase B). That is the one place the two behaviours are not
89
+ * yet the same thing, and the specification does not say which is right.
90
+ */
91
+ xpath2BooleanValue(expression, context, namespaces) {
92
+ try {
93
+ return this.fonto().evaluateXPathToBoolean(`xs:boolean(${expression})`, context, null, null, this.options(namespaces));
94
+ }
95
+ catch (e) {
96
+ throw new Docx4JException(`The xpath2 boolean conversion mode casts to xs:boolean, which accepts only "true", "false", "1" and "0", `
97
+ + `and one value at a time: ${expression} gives none of them`, { cause: e });
98
+ }
99
+ }
100
+ options(namespaces) {
101
+ return { namespaceResolver: (prefix) => { var _a; return (_a = namespaces[prefix]) !== null && _a !== void 0 ? _a : null; } };
102
+ }
103
+ fonto() {
104
+ if (!this.module) {
105
+ throw new Docx4JException('The XPath engine is not ready yet: await pkg.customXmlParts.load() (or pkg.xpathEngine.ready()) once before selecting nodes');
106
+ }
107
+ return this.module;
108
+ }
109
+ }
110
+ /** A FontoXPath result in the four-type union `selectValue` reports. */
111
+ function valueOf(value) {
112
+ var _a;
113
+ if (value === null || value === undefined)
114
+ return undefined;
115
+ if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean')
116
+ return value;
117
+ if (Array.isArray(value))
118
+ return value.length === 0 ? undefined : valueOf(value[0]);
119
+ if (typeof value === 'object' && typeof value.nodeType === 'number') {
120
+ return (_a = value.textContent) !== null && _a !== void 0 ? _a : '';
121
+ }
122
+ return String(value);
123
+ }
@@ -1,5 +1,19 @@
1
1
  /** The XPath result kinds `selectValue` can report. */
2
2
  export type XPathValue = string | number | boolean | undefined;
3
+ /**
4
+ * How an XPaths entry becomes a boolean (OpenDoPE specification v3 section 7.2, table 7, declared
5
+ * in the XPaths part's `booleanConversion` attribute; CR-005 phase A):
6
+ *
7
+ * - `java`: the string value is true only if it equals `true`, ignoring case. `"1"` and `"yes"`
8
+ * are false. docx4j's `Boolean.parseBoolean`, and the default for a template that declares no
9
+ * mode.
10
+ * - `xpath1`: XPath 1.0 `boolean()`. A node-set is true if non-empty, a string if non-empty - so
11
+ * `"false"` is **true**.
12
+ * - `xpath2`: the effective boolean value, a string cast to `xs:boolean` accepting only `true`,
13
+ * `false`, `1` and `0` - so `"false"` is false and `"yes"` is an **error**. Needs an XPath 2.0
14
+ * engine: `FontoXPathEngine` from `@docx4j/core-ts/xpath-fonto`.
15
+ */
16
+ export type BooleanConversionMode = 'java' | 'xpath1' | 'xpath2';
3
17
  /**
4
18
  * XPath 1.0 over a DOM (CR-002 section 3.6). `select` answers a node set; `selectValue` answers a
5
19
  * string, number or boolean result. `namespaces` maps prefixes used in the expression to URIs.
@@ -11,7 +25,25 @@ export interface XPathEngine {
11
25
  readonly isReady: boolean;
12
26
  select(expression: string, context: Node, namespaces?: Record<string, string>): Node[];
13
27
  selectValue(expression: string, context: Node, namespaces?: Record<string, string>): XPathValue;
28
+ /**
29
+ * The expression as a boolean in one conversion mode, for an engine that can do more than the
30
+ * function {@link booleanValue} can do over `selectValue` - in practice `xpath2`, which needs an
31
+ * XPath 2.0 evaluator. Optional: `XPathEngine` is public and a consumer may implement it, so a
32
+ * required member would break every implementation that predates this. Callers go through
33
+ * {@link booleanValue}, never here.
34
+ */
35
+ booleanValue?(expression: string, context: Node, namespaces: Record<string, string>, mode: BooleanConversionMode): boolean;
14
36
  }
37
+ /**
38
+ * The expression as a boolean in a declared conversion mode (table 7 above).
39
+ *
40
+ * `java` and `xpath1` need nothing an `XPathEngine` does not already have, so they work over any
41
+ * engine: `java` is the string value compared with `true`, `xpath1` is XPath 1.0 `boolean()` of the
42
+ * expression. `xpath2` is delegated to the engine's own `booleanValue` when it has one, and
43
+ * otherwise throws naming the engine to install - which is the specification's REQ-032, a processor
44
+ * evaluating in the declared mode or refusing to process the template.
45
+ */
46
+ export declare function booleanValue(engine: XPathEngine, expression: string, context: Node, namespaces?: Record<string, string>, mode?: BooleanConversionMode): boolean;
15
47
  /**
16
48
  * The default engine: `document.evaluate` where the runtime has it, else the `xpath` package,
17
49
  * imported on `ready()`. The import specifier is a variable so that a bundler does not follow it
@@ -12,6 +12,34 @@
12
12
  // afterwards is synchronous. A call before the engine is ready throws, naming the fix.
13
13
  import { Docx4JException } from '../../opc/exceptions.mjs';
14
14
  import { serializeXml } from '../../xml/dom.mjs';
15
+ /**
16
+ * The expression as a boolean in a declared conversion mode (table 7 above).
17
+ *
18
+ * `java` and `xpath1` need nothing an `XPathEngine` does not already have, so they work over any
19
+ * engine: `java` is the string value compared with `true`, `xpath1` is XPath 1.0 `boolean()` of the
20
+ * expression. `xpath2` is delegated to the engine's own `booleanValue` when it has one, and
21
+ * otherwise throws naming the engine to install - which is the specification's REQ-032, a processor
22
+ * evaluating in the declared mode or refusing to process the template.
23
+ */
24
+ export function booleanValue(engine, expression, context, namespaces = {}, mode = 'java') {
25
+ if (engine.booleanValue)
26
+ return engine.booleanValue(expression, context, namespaces, mode);
27
+ switch (mode) {
28
+ case 'java': {
29
+ const value = engine.selectValue(expression, context, namespaces);
30
+ return typeof value === 'boolean' ? value : String(value !== null && value !== void 0 ? value : '').toLowerCase() === 'true';
31
+ }
32
+ case 'xpath1': {
33
+ // boolean() is XPath 1.0's own conversion, so the engine applies the rules rather than this.
34
+ const value = engine.selectValue(`boolean(${expression})`, context, namespaces);
35
+ return value === true;
36
+ }
37
+ default:
38
+ throw new Docx4JException(`The boolean conversion mode "${mode}" needs an XPath 2.0 engine: `
39
+ + "import { FontoXPathEngine } from '@docx4j/core-ts/xpath-fonto' (npm install fontoxpath) "
40
+ + 'and set pkg.xpathEngine to it');
41
+ }
42
+ }
15
43
  // XPathResult constants, spelled out because `XPathResult` is not a global in Node.
16
44
  const ANY_TYPE = 0;
17
45
  const NUMBER_TYPE = 1;
@@ -92,6 +92,16 @@ export declare const ContentTypes: {
92
92
  readonly SPREADSHEETML_COMMENTS: "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml";
93
93
  readonly SPREADSHEETML_CONNECTIONS: "application/vnd.openxmlformats-officedocument.spreadsheetml.connections+xml";
94
94
  readonly SPREADSHEETML_EXTERNAL_LINK: "application/vnd.openxmlformats-officedocument.spreadsheetml.externalLink+xml";
95
+ readonly SPREADSHEETML_SLICER_CACHE: "application/vnd.ms-excel.slicerCache+xml";
96
+ readonly SPREADSHEETML_SLICERS: "application/vnd.ms-excel.slicer+xml";
97
+ readonly SPREADSHEETML_TIMELINE_CACHE: "application/vnd.ms-excel.timelineCache+xml";
98
+ readonly SPREADSHEETML_TIMELINES: "application/vnd.ms-excel.timeline+xml";
99
+ readonly SPREADSHEETML_CONTROL_PROPERTIES: "application/vnd.ms-excel.controlproperties+xml";
100
+ readonly SPREADSHEETML_CUSTOM_DATA_PROPERTIES: "application/vnd.openxmlformats-officedocument.customDataProperties+xml";
101
+ /** Generic, so `CustomDataPart` is registered by relationship type instead. */
102
+ readonly SPREADSHEETML_CUSTOM_DATA: "application/binary";
103
+ readonly SPREADSHEETML_SURVEY: "application/vnd.ms-excel.Survey+xml";
104
+ readonly SPREADSHEETML_DATA_MODEL: "application/vnd.openxmlformats-officedocument.model+data";
95
105
  readonly DIGITAL_SIGNATURE_ORIGIN_PART: "application/vnd.openxmlformats-package.digital-signature-origin";
96
106
  readonly DIGITAL_SIGNATURE_XML_SIGNATURE_PART: "application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml";
97
107
  readonly INK_ML: "application/inkml+xml";
@@ -92,6 +92,19 @@ export const ContentTypes = {
92
92
  SPREADSHEETML_COMMENTS: 'application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml',
93
93
  SPREADSHEETML_CONNECTIONS: 'application/vnd.openxmlformats-officedocument.spreadsheetml.connections+xml',
94
94
  SPREADSHEETML_EXTERNAL_LINK: 'application/vnd.openxmlformats-officedocument.spreadsheetml.externalLink+xml',
95
+ // The Excel 2010 and 2013 extension parts ([MS-XLSX] 2.1; docx4j 17.2.0 CR-022, CR-004 here).
96
+ // The strings are Excel 365's as docx4j measured them: the timeline pair is lowercase, and
97
+ // the survey's capital S is Excel's too.
98
+ SPREADSHEETML_SLICER_CACHE: 'application/vnd.ms-excel.slicerCache+xml',
99
+ SPREADSHEETML_SLICERS: 'application/vnd.ms-excel.slicer+xml',
100
+ SPREADSHEETML_TIMELINE_CACHE: 'application/vnd.ms-excel.timelineCache+xml',
101
+ SPREADSHEETML_TIMELINES: 'application/vnd.ms-excel.timeline+xml',
102
+ SPREADSHEETML_CONTROL_PROPERTIES: 'application/vnd.ms-excel.controlproperties+xml',
103
+ SPREADSHEETML_CUSTOM_DATA_PROPERTIES: 'application/vnd.openxmlformats-officedocument.customDataProperties+xml',
104
+ /** Generic, so `CustomDataPart` is registered by relationship type instead. */
105
+ SPREADSHEETML_CUSTOM_DATA: 'application/binary',
106
+ SPREADSHEETML_SURVEY: 'application/vnd.ms-excel.Survey+xml',
107
+ SPREADSHEETML_DATA_MODEL: 'application/vnd.openxmlformats-officedocument.model+data',
95
108
  DIGITAL_SIGNATURE_ORIGIN_PART: 'application/vnd.openxmlformats-package.digital-signature-origin',
96
109
  DIGITAL_SIGNATURE_XML_SIGNATURE_PART: 'application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml',
97
110
  INK_ML: 'application/inkml+xml',
@@ -91,6 +91,24 @@ export declare const Namespaces: {
91
91
  readonly SPREADSHEETML_PIVOT_CACHE_DEFINITION: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheDefinition";
92
92
  readonly SPREADSHEETML_PIVOT_CACHE_RECORDS: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheRecords";
93
93
  readonly SPREADSHEETML_EXTERNAL_LINK: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/externalLink";
94
+ /** `/xl/slicerCaches/slicerCache1.xml`, from the workbook. */
95
+ readonly SPREADSHEETML_SLICER_CACHE: "http://schemas.microsoft.com/office/2007/relationships/slicerCache";
96
+ /** `/xl/slicers/slicer1.xml`, from a worksheet. */
97
+ readonly SPREADSHEETML_SLICERS: "http://schemas.microsoft.com/office/2007/relationships/slicer";
98
+ /** `/xl/timelineCaches/timelineCache1.xml`, from the workbook. */
99
+ readonly SPREADSHEETML_TIMELINE_CACHE: "http://schemas.microsoft.com/office/2011/relationships/timelineCache";
100
+ /** `/xl/timelines/timeline1.xml`, from a worksheet. */
101
+ readonly SPREADSHEETML_TIMELINES: "http://schemas.microsoft.com/office/2011/relationships/timeline";
102
+ /** `/xl/ctrlProps/ctrlProp1.xml`, from a worksheet, for one of its form controls. */
103
+ readonly SPREADSHEETML_CONTROL_PROPERTIES: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/ctrlProp";
104
+ /** `/xl/customDataProps/customDataProps1.xml`, from the workbook. */
105
+ readonly SPREADSHEETML_CUSTOM_DATA_PROPERTIES: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/customDataProps";
106
+ /** `/xl/customData/customData1.dat`, from its properties part. */
107
+ readonly SPREADSHEETML_CUSTOM_DATA: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/customData";
108
+ /** `/xl/surveys/survey1.xml`, from the workbook. */
109
+ readonly SPREADSHEETML_SURVEY: "http://schemas.microsoft.com/office/2010/relationships/Survey";
110
+ /** `/xl/model/item.data` ([MS-XLDM]), from the workbook. */
111
+ readonly SPREADSHEETML_DATA_MODEL: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/powerPivotData";
94
112
  readonly WEB_EXTENSION_TASKPANES: "http://schemas.microsoft.com/office/webextensions/taskpanes/2010/11";
95
113
  readonly WEB_EXTENSION_WEBEXTENSION: "http://schemas.microsoft.com/office/webextensions/webextension/2010/11";
96
114
  readonly XML_EVENTS: "http://www.w3.org/2001/xml-events";
@@ -91,6 +91,26 @@ export const Namespaces = {
91
91
  SPREADSHEETML_PIVOT_CACHE_DEFINITION: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheDefinition',
92
92
  SPREADSHEETML_PIVOT_CACHE_RECORDS: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheRecords',
93
93
  SPREADSHEETML_EXTERNAL_LINK: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/externalLink',
94
+ // The Excel 2010 and 2013 extension parts (CR-004): slicers and their caches are 2007,
95
+ // timelines 2011, the survey 2010; the rest are the officeDocument ones.
96
+ /** `/xl/slicerCaches/slicerCache1.xml`, from the workbook. */
97
+ SPREADSHEETML_SLICER_CACHE: 'http://schemas.microsoft.com/office/2007/relationships/slicerCache',
98
+ /** `/xl/slicers/slicer1.xml`, from a worksheet. */
99
+ SPREADSHEETML_SLICERS: 'http://schemas.microsoft.com/office/2007/relationships/slicer',
100
+ /** `/xl/timelineCaches/timelineCache1.xml`, from the workbook. */
101
+ SPREADSHEETML_TIMELINE_CACHE: 'http://schemas.microsoft.com/office/2011/relationships/timelineCache',
102
+ /** `/xl/timelines/timeline1.xml`, from a worksheet. */
103
+ SPREADSHEETML_TIMELINES: 'http://schemas.microsoft.com/office/2011/relationships/timeline',
104
+ /** `/xl/ctrlProps/ctrlProp1.xml`, from a worksheet, for one of its form controls. */
105
+ SPREADSHEETML_CONTROL_PROPERTIES: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/ctrlProp',
106
+ /** `/xl/customDataProps/customDataProps1.xml`, from the workbook. */
107
+ SPREADSHEETML_CUSTOM_DATA_PROPERTIES: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/customDataProps',
108
+ /** `/xl/customData/customData1.dat`, from its properties part. */
109
+ SPREADSHEETML_CUSTOM_DATA: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/customData',
110
+ /** `/xl/surveys/survey1.xml`, from the workbook. */
111
+ SPREADSHEETML_SURVEY: 'http://schemas.microsoft.com/office/2010/relationships/Survey',
112
+ /** `/xl/model/item.data` ([MS-XLDM]), from the workbook. */
113
+ SPREADSHEETML_DATA_MODEL: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/powerPivotData',
94
114
  WEB_EXTENSION_TASKPANES: 'http://schemas.microsoft.com/office/webextensions/taskpanes/2010/11',
95
115
  WEB_EXTENSION_WEBEXTENSION: 'http://schemas.microsoft.com/office/webextensions/webextension/2010/11',
96
116
  XML_EVENTS: 'http://www.w3.org/2001/xml-events',
@@ -39,6 +39,12 @@ export declare abstract class Part implements RelationshipSource {
39
39
  * its content type in the content type manager, and a relationship in this part's relationships.
40
40
  */
41
41
  addTargetPart(target: Part, mode?: AddPartBehaviour, proposedRelId?: string): Relationship;
42
+ /**
43
+ * This part's targets of one relationship type that are instances of a class, in relationship
44
+ * order: what a shortcut for a collection of parts (a sheet's slicers, a workbook's slicer
45
+ * caches) is made of.
46
+ */
47
+ protected partsByRelationshipType<T extends Part>(relationshipType: string, cls: abstract new (...args: never[]) => T): T[];
42
48
  /** Subclasses keep typed references to well-known targets (styles, numbering, ...); returns true when one was set. */
43
49
  setPartShortcut(_part: Part, _relationshipType: string): boolean;
44
50
  /** The bytes this part was loaded from, if it came from a container. Undefined for new parts. */
@@ -47,6 +47,19 @@ export class Part {
47
47
  this.setPartShortcut(target, target.relationshipType);
48
48
  return rel;
49
49
  }
50
+ /**
51
+ * This part's targets of one relationship type that are instances of a class, in relationship
52
+ * order: what a shortcut for a collection of parts (a sheet's slicers, a workbook's slicer
53
+ * caches) is made of.
54
+ */
55
+ partsByRelationshipType(relationshipType, cls) {
56
+ const rp = this.relationshipsPart;
57
+ if (!rp)
58
+ return [];
59
+ return rp.getRelationshipsByType(relationshipType)
60
+ .map((r) => rp.getPart(r))
61
+ .filter((p) => p instanceof cls);
62
+ }
50
63
  /** Subclasses keep typed references to well-known targets (styles, numbering, ...); returns true when one was set. */
51
64
  setPartShortcut(_part, _relationshipType) {
52
65
  return false;
@@ -61,6 +61,7 @@ export const defaultPartRegistry = new PartRegistry()
61
61
  .registerForRelationship(Namespaces.EMBEDDED_PKG, (n, ct) => new EmbeddedPackagePart(n, ct))
62
62
  .registerForRelationship(Namespaces.OLE_OBJECT, (n, ct) => new OleObjectBinaryPart(n, ct))
63
63
  .registerForRelationship(Namespaces.CUSTOM_XML_DATA_STORAGE, (n, ct) => new CustomXmlDataStoragePart(n, ct))
64
+ .registerForRelationship(Namespaces.SPREADSHEETML_CUSTOM_DATA, (n, ct) => new sml.CustomDataPart(n, ct))
64
65
  .register(ContentTypes.WORDPROCESSINGML_DOCUMENT, (n, ct) => new wml.MainDocumentPart(n, ct))
65
66
  .register(ContentTypes.WORDPROCESSINGML_DOCUMENT_MACROENABLED, (n, ct) => new wml.MainDocumentPart(n, ct))
66
67
  .register(ContentTypes.WORDPROCESSINGML_TEMPLATE, (n, ct) => new wml.MainDocumentPart(n, ct))
@@ -140,4 +141,14 @@ export const defaultPartRegistry = new PartRegistry()
140
141
  .register(ContentTypes.SPREADSHEETML_QUERY_TABLE, (n) => new sml.QueryTablePart(n))
141
142
  .register(ContentTypes.SPREADSHEETML_CONNECTIONS, (n) => new sml.ConnectionsPart(n))
142
143
  .register(ContentTypes.SPREADSHEETML_EXTERNAL_LINK, (n) => new sml.ExternalLinkPart(n))
143
- .register(ContentTypes.SPREADSHEETML_PRINTER_SETTINGS, (n) => new sml.PrinterSettingsPart(n));
144
+ .register(ContentTypes.SPREADSHEETML_PRINTER_SETTINGS, (n) => new sml.PrinterSettingsPart(n))
145
+ // The Excel 2010 and 2013 extension parts (CR-004). `CustomDataPart`'s content type is the
146
+ // generic `application/binary`, so it is registered by relationship type above instead.
147
+ .register(ContentTypes.SPREADSHEETML_SLICER_CACHE, (n) => new sml.SlicerCachePart(n))
148
+ .register(ContentTypes.SPREADSHEETML_SLICERS, (n) => new sml.SlicersPart(n))
149
+ .register(ContentTypes.SPREADSHEETML_TIMELINE_CACHE, (n) => new sml.TimelineCachePart(n))
150
+ .register(ContentTypes.SPREADSHEETML_TIMELINES, (n) => new sml.TimelinesPart(n))
151
+ .register(ContentTypes.SPREADSHEETML_CONTROL_PROPERTIES, (n) => new sml.ControlPropertiesPart(n))
152
+ .register(ContentTypes.SPREADSHEETML_CUSTOM_DATA_PROPERTIES, (n) => new sml.CustomDataPropertiesPart(n))
153
+ .register(ContentTypes.SPREADSHEETML_SURVEY, (n) => new sml.SurveyPart(n))
154
+ .register(ContentTypes.SPREADSHEETML_DATA_MODEL, (n) => new sml.DataModelPart(n));
@@ -1,5 +1,6 @@
1
1
  import type * as sml from '@docx4j/generated-objects-ts/modules/org_xlsx4j_sml';
2
2
  import { XmlPart } from '../XmlPart.mjs';
3
+ import { DefaultXmlPart } from '../DefaultXmlPart.mjs';
3
4
  import { BinaryPart } from '../BinaryPart.mjs';
4
5
  import { Part } from '../Part.mjs';
5
6
  import { PartName } from '../../opc/PartName.mjs';
@@ -9,6 +10,10 @@ export declare class WorkbookPart extends XmlPart<sml.Workbook> {
9
10
  stylesPart: StylesPart | undefined;
10
11
  themePart: ThemePart | undefined;
11
12
  calcChainPart: CalcChainPart | undefined;
13
+ /** The workbook's survey part, if it has one (CR-004). */
14
+ surveyPart: SurveyPart | undefined;
15
+ /** The Power Pivot data model (`/xl/model/item.data`), if the workbook has one (CR-004). */
16
+ dataModelPart: DataModelPart | undefined;
12
17
  constructor(partName?: PartName | string, contentType?: string);
13
18
  setPartShortcut(part: Part, relationshipType: string): boolean;
14
19
  /**
@@ -20,6 +25,12 @@ export declare class WorkbookPart extends XmlPart<sml.Workbook> {
20
25
  getWorksheetParts(): Promise<WorksheetPart[]>;
21
26
  /** The worksheet at an index of `sheets` (docx4j `WorkbookPart.getWorksheet(int)`). */
22
27
  getWorksheet(index: number): WorksheetPart;
28
+ /** The workbook's slicer caches (`/xl/slicerCaches/slicerCacheN.xml`), in relationship order. */
29
+ get slicerCacheParts(): SlicerCachePart[];
30
+ /** The workbook's timeline caches (`/xl/timelineCaches/timelineCacheN.xml`), in relationship order. */
31
+ get timelineCacheParts(): TimelineCachePart[];
32
+ /** The workbook's custom data properties parts, in relationship order; each names a `CustomDataPart`. */
33
+ get customDataPropertiesParts(): CustomDataPropertiesPart[];
23
34
  /** docx4j `WorkbookPart.isDate1904()`. */
24
35
  get isDate1904(): boolean;
25
36
  }
@@ -32,6 +43,16 @@ export declare class WorksheetPart extends XmlPart<sml.Worksheet> {
32
43
  setPartShortcut(part: Part, relationshipType: string): boolean;
33
44
  /** The sheet's table parts (`xl/tables/tableN.xml`), in relationship order. */
34
45
  get tableParts(): TablePart[];
46
+ /** The sheet's slicers parts (`/xl/slicers/slicerN.xml`), in relationship order (CR-004). */
47
+ get slicersParts(): SlicersPart[];
48
+ /** The sheet's timelines parts (`/xl/timelines/timelineN.xml`), in relationship order (CR-004). */
49
+ get timelinesParts(): TimelinesPart[];
50
+ /**
51
+ * The sheet's form-control properties parts (`/xl/ctrlProps/ctrlPropN.xml`), in relationship
52
+ * order: one per `x:control`, whose `r:id` names it from inside the sheet's
53
+ * `mc:AlternateContent` (CR-004).
54
+ */
55
+ get controlPropertiesParts(): ControlPropertiesPart[];
35
56
  /** The workbook this sheet belongs to, through its source relationship. */
36
57
  get workbookPart(): WorkbookPart | undefined;
37
58
  }
@@ -77,3 +98,43 @@ export declare class ExternalLinkPart extends XmlPart<sml.CTExternalLink> {
77
98
  export declare class PrinterSettingsPart extends BinaryPart {
78
99
  constructor(partName: PartName | string);
79
100
  }
101
+ /** A slicer cache (`x14:slicerCacheDefinition`), from the workbook. docx4j `SlicerCachePart`. */
102
+ export declare class SlicerCachePart extends DefaultXmlPart {
103
+ constructor(partName?: PartName | string);
104
+ }
105
+ /** A sheet's slicers (`x14:slicers`). docx4j `SlicersPart`. */
106
+ export declare class SlicersPart extends DefaultXmlPart {
107
+ constructor(partName?: PartName | string);
108
+ }
109
+ /** A timeline cache (`x15:timelineCacheDefinition`), from the workbook. docx4j `TimelineCachePart`. */
110
+ export declare class TimelineCachePart extends DefaultXmlPart {
111
+ constructor(partName?: PartName | string);
112
+ }
113
+ /** A sheet's timelines (`x15:timelines`). docx4j `TimelinesPart`. */
114
+ export declare class TimelinesPart extends DefaultXmlPart {
115
+ constructor(partName?: PartName | string);
116
+ }
117
+ /** A form control's properties (`x14:formControlPr`), from a worksheet. docx4j `ControlPropertiesPart`. */
118
+ export declare class ControlPropertiesPart extends DefaultXmlPart {
119
+ constructor(partName?: PartName | string);
120
+ }
121
+ /** The properties of a custom data part (`x14:datastoreItem`). docx4j `CustomDataPropertiesPart`. */
122
+ export declare class CustomDataPropertiesPart extends DefaultXmlPart {
123
+ constructor(partName?: PartName | string);
124
+ }
125
+ /** A survey (`x15:survey`), from the workbook. docx4j `SurveyPart`. */
126
+ export declare class SurveyPart extends DefaultXmlPart {
127
+ constructor(partName?: PartName | string);
128
+ }
129
+ /**
130
+ * Custom data, whatever an add-in put there (docx4j `CustomDataPart`). Its content type is the
131
+ * generic `application/binary`, so the registry chooses it by relationship type, as it does for
132
+ * altChunk, embedded packages, OLE objects and custom XML (CR-001 section 5.4).
133
+ */
134
+ export declare class CustomDataPart extends BinaryPart {
135
+ constructor(partName?: PartName | string, contentType?: string);
136
+ }
137
+ /** The Power Pivot data model (`/xl/model/item.data`, [MS-XLDM]). docx4j `DataModelPart`. */
138
+ export declare class DataModelPart extends BinaryPart {
139
+ constructor(partName?: PartName | string);
140
+ }
@@ -1,4 +1,5 @@
1
1
  import { XmlPart } from '../XmlPart.mjs';
2
+ import { DefaultXmlPart } from '../DefaultXmlPart.mjs';
2
3
  import { BinaryPart } from '../BinaryPart.mjs';
3
4
  import { ContentTypes } from '../../opc/ContentTypes.mjs';
4
5
  import { Namespaces } from '../Namespaces.mjs';
@@ -22,6 +23,12 @@ export class WorkbookPart extends XmlPart {
22
23
  case Namespaces.SPREADSHEETML_CALC_CHAIN:
23
24
  this.calcChainPart = part;
24
25
  return true;
26
+ case Namespaces.SPREADSHEETML_SURVEY:
27
+ this.surveyPart = part;
28
+ return true;
29
+ case Namespaces.SPREADSHEETML_DATA_MODEL:
30
+ this.dataModelPart = part;
31
+ return true;
25
32
  default: return false;
26
33
  }
27
34
  }
@@ -61,6 +68,18 @@ export class WorkbookPart extends XmlPart {
61
68
  throw new Xlsx4jException(`No sheet at index ${index}. (There are ${sheets.length} sheets) `);
62
69
  return sheet;
63
70
  }
71
+ /** The workbook's slicer caches (`/xl/slicerCaches/slicerCacheN.xml`), in relationship order. */
72
+ get slicerCacheParts() {
73
+ return this.partsByRelationshipType(Namespaces.SPREADSHEETML_SLICER_CACHE, SlicerCachePart);
74
+ }
75
+ /** The workbook's timeline caches (`/xl/timelineCaches/timelineCacheN.xml`), in relationship order. */
76
+ get timelineCacheParts() {
77
+ return this.partsByRelationshipType(Namespaces.SPREADSHEETML_TIMELINE_CACHE, TimelineCachePart);
78
+ }
79
+ /** The workbook's custom data properties parts, in relationship order; each names a `CustomDataPart`. */
80
+ get customDataPropertiesParts() {
81
+ return this.partsByRelationshipType(Namespaces.SPREADSHEETML_CUSTOM_DATA_PROPERTIES, CustomDataPropertiesPart);
82
+ }
64
83
  /** docx4j `WorkbookPart.isDate1904()`. */
65
84
  get isDate1904() {
66
85
  var _a;
@@ -84,10 +103,23 @@ export class WorksheetPart extends XmlPart {
84
103
  }
85
104
  /** The sheet's table parts (`xl/tables/tableN.xml`), in relationship order. */
86
105
  get tableParts() {
87
- const rp = this.relationshipsPart;
88
- if (!rp)
89
- return [];
90
- return rp.getRelationshipsByType(Namespaces.SPREADSHEETML_TABLE).map((r) => rp.getPart(r)).filter((p) => p instanceof TablePart);
106
+ return this.partsByRelationshipType(Namespaces.SPREADSHEETML_TABLE, TablePart);
107
+ }
108
+ /** The sheet's slicers parts (`/xl/slicers/slicerN.xml`), in relationship order (CR-004). */
109
+ get slicersParts() {
110
+ return this.partsByRelationshipType(Namespaces.SPREADSHEETML_SLICERS, SlicersPart);
111
+ }
112
+ /** The sheet's timelines parts (`/xl/timelines/timelineN.xml`), in relationship order (CR-004). */
113
+ get timelinesParts() {
114
+ return this.partsByRelationshipType(Namespaces.SPREADSHEETML_TIMELINES, TimelinesPart);
115
+ }
116
+ /**
117
+ * The sheet's form-control properties parts (`/xl/ctrlProps/ctrlPropN.xml`), in relationship
118
+ * order: one per `x:control`, whose `r:id` names it from inside the sheet's
119
+ * `mc:AlternateContent` (CR-004).
120
+ */
121
+ get controlPropertiesParts() {
122
+ return this.partsByRelationshipType(Namespaces.SPREADSHEETML_CONTROL_PROPERTIES, ControlPropertiesPart);
91
123
  }
92
124
  /** The workbook this sheet belongs to, through its source relationship. */
93
125
  get workbookPart() {
@@ -164,3 +196,65 @@ export class PrinterSettingsPart extends BinaryPart {
164
196
  super(partName, ContentTypes.SPREADSHEETML_PRINTER_SETTINGS, Namespaces.SPREADSHEETML_PRINTER_SETTINGS);
165
197
  }
166
198
  }
199
+ // The Excel 2010 and 2013 extension parts (CR-004 phase A; docx4j
200
+ // org.docx4j.openpackaging.parts.SpreadsheetML). The seven XML ones are `DefaultXmlPart`s for
201
+ // now: their DOM round-trips byte for byte, and nothing re-marshals them, so an `mc:Ignorable`
202
+ // they carry is untouched. Phase B makes them `XmlPart`s over the `x14` and `x15` roots.
203
+ /** A slicer cache (`x14:slicerCacheDefinition`), from the workbook. docx4j `SlicerCachePart`. */
204
+ export class SlicerCachePart extends DefaultXmlPart {
205
+ constructor(partName = '/xl/slicerCaches/slicerCache1.xml') {
206
+ super(partName, ContentTypes.SPREADSHEETML_SLICER_CACHE, Namespaces.SPREADSHEETML_SLICER_CACHE);
207
+ }
208
+ }
209
+ /** A sheet's slicers (`x14:slicers`). docx4j `SlicersPart`. */
210
+ export class SlicersPart extends DefaultXmlPart {
211
+ constructor(partName = '/xl/slicers/slicer1.xml') {
212
+ super(partName, ContentTypes.SPREADSHEETML_SLICERS, Namespaces.SPREADSHEETML_SLICERS);
213
+ }
214
+ }
215
+ /** A timeline cache (`x15:timelineCacheDefinition`), from the workbook. docx4j `TimelineCachePart`. */
216
+ export class TimelineCachePart extends DefaultXmlPart {
217
+ constructor(partName = '/xl/timelineCaches/timelineCache1.xml') {
218
+ super(partName, ContentTypes.SPREADSHEETML_TIMELINE_CACHE, Namespaces.SPREADSHEETML_TIMELINE_CACHE);
219
+ }
220
+ }
221
+ /** A sheet's timelines (`x15:timelines`). docx4j `TimelinesPart`. */
222
+ export class TimelinesPart extends DefaultXmlPart {
223
+ constructor(partName = '/xl/timelines/timeline1.xml') {
224
+ super(partName, ContentTypes.SPREADSHEETML_TIMELINES, Namespaces.SPREADSHEETML_TIMELINES);
225
+ }
226
+ }
227
+ /** A form control's properties (`x14:formControlPr`), from a worksheet. docx4j `ControlPropertiesPart`. */
228
+ export class ControlPropertiesPart extends DefaultXmlPart {
229
+ constructor(partName = '/xl/ctrlProps/ctrlProp1.xml') {
230
+ super(partName, ContentTypes.SPREADSHEETML_CONTROL_PROPERTIES, Namespaces.SPREADSHEETML_CONTROL_PROPERTIES);
231
+ }
232
+ }
233
+ /** The properties of a custom data part (`x14:datastoreItem`). docx4j `CustomDataPropertiesPart`. */
234
+ export class CustomDataPropertiesPart extends DefaultXmlPart {
235
+ constructor(partName = '/xl/customDataProps/customDataProps1.xml') {
236
+ super(partName, ContentTypes.SPREADSHEETML_CUSTOM_DATA_PROPERTIES, Namespaces.SPREADSHEETML_CUSTOM_DATA_PROPERTIES);
237
+ }
238
+ }
239
+ /** A survey (`x15:survey`), from the workbook. docx4j `SurveyPart`. */
240
+ export class SurveyPart extends DefaultXmlPart {
241
+ constructor(partName = '/xl/surveys/survey1.xml') {
242
+ super(partName, ContentTypes.SPREADSHEETML_SURVEY, Namespaces.SPREADSHEETML_SURVEY);
243
+ }
244
+ }
245
+ /**
246
+ * Custom data, whatever an add-in put there (docx4j `CustomDataPart`). Its content type is the
247
+ * generic `application/binary`, so the registry chooses it by relationship type, as it does for
248
+ * altChunk, embedded packages, OLE objects and custom XML (CR-001 section 5.4).
249
+ */
250
+ export class CustomDataPart extends BinaryPart {
251
+ constructor(partName = '/xl/customData/customData1.dat', contentType = ContentTypes.SPREADSHEETML_CUSTOM_DATA) {
252
+ super(partName, contentType, Namespaces.SPREADSHEETML_CUSTOM_DATA);
253
+ }
254
+ }
255
+ /** The Power Pivot data model (`/xl/model/item.data`, [MS-XLDM]). docx4j `DataModelPart`. */
256
+ export class DataModelPart extends BinaryPart {
257
+ constructor(partName = '/xl/model/item.data') {
258
+ super(partName, ContentTypes.SPREADSHEETML_DATA_MODEL, Namespaces.SPREADSHEETML_DATA_MODEL);
259
+ }
260
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@docx4j/core-ts",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "docx4j-core for TypeScript: Open Packaging (docx/pptx/xlsx zip and flat OPC), typed parts and relationships, style, numbering and font resolution over @docx4j/generated-objects-ts. Node, browsers, Word add-ins.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.mjs",
@@ -33,6 +33,10 @@
33
33
  "./office-js": {
34
34
  "types": "./dist/office-js/index.d.mts",
35
35
  "import": "./dist/office-js/index.mjs"
36
+ },
37
+ "./xpath-fonto": {
38
+ "types": "./dist/model/customxml/xpath-fonto.d.mts",
39
+ "import": "./dist/model/customxml/xpath-fonto.mjs"
36
40
  }
37
41
  },
38
42
  "files": [
@@ -75,17 +79,22 @@
75
79
  },
76
80
  "license": "Apache-2.0",
77
81
  "dependencies": {
78
- "@docx4j/generated-objects-ts": "^0.1.6",
82
+ "@docx4j/generated-objects-ts": "^0.2.0",
79
83
  "fflate": "^0.8.2"
80
84
  },
81
85
  "devDependencies": {
86
+ "fontoxpath": "^3.34.0",
82
87
  "typescript": "~5.6.3",
83
88
  "xpath": "^0.0.34"
84
89
  },
85
90
  "peerDependencies": {
91
+ "fontoxpath": "^3.30.0",
86
92
  "xpath": "^0.0.34"
87
93
  },
88
94
  "peerDependenciesMeta": {
95
+ "fontoxpath": {
96
+ "optional": true
97
+ },
89
98
  "xpath": {
90
99
  "optional": true
91
100
  }