@json-to-office/shared 1.2.0 → 1.3.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.
@@ -1,210 +1,206 @@
1
+ export { F as FeatureRequirement, a as FeatureRequirementCollector, O as OfficeFormat, b as OfficeRenderer, R as RenderOptions, c as RendererDiagnostic, d as RendererDiagnosticSeverity, e as RendererRegistry, U as UnknownRendererError, f as UnsupportedRendererFeatureError, g as UnsupportedRendererFeatureErrorInit, h as assertNever, i as assertRendererSupports, j as diagnoseUnsupportedFeatures, p as partitionDiagnostics, r as rendererError, k as rendererWarning } from '../capabilities-DrHJ6_4G.js';
2
+
1
3
  /**
2
- * Format-independent renderer contracts.
3
- *
4
- * This module deliberately knows nothing about DOCX or PPTX semantics. Each
5
- * format owns its own intermediate representation (`DocxIR`, `PptxIR`) and its
6
- * own feature union; the only thing shared between them is the shape of the
7
- * contract a backend adapter must satisfy.
8
- *
9
- * Do not add format-specific feature names, IR nodes or units here.
10
- */
11
- /** The Office formats this repository can produce. */
12
- type OfficeFormat = 'docx' | 'pptx';
13
- /**
14
- * Options every renderer accepts.
15
- *
16
- * `deterministic` asks the adapter (and the packaging step after it) to make
17
- * output byte-stable across runs: fixed zip entry timestamps, fixed core
18
- * metadata timestamps, no random identifiers.
19
- *
20
- * `generatedAt` pins the timestamp written into package metadata. Callers that
21
- * want reproducible bytes pass both.
22
- */
23
- interface RenderOptions {
24
- deterministic?: boolean;
25
- generatedAt?: Date;
26
- }
27
- /**
28
- * A backend that turns a format-specific IR into package bytes.
29
- *
30
- * @typeParam TIR - the format's intermediate representation (plain data)
31
- * @typeParam TFeature - the format's feature union (see `capabilities.ts`)
32
- * @typeParam TId - the string-literal union of renderer ids for the format
4
+ * The half of a native chart `@office-open` does not write.
5
+ *
6
+ * Both `@office-open/docx` and `@office-open/pptx` build their chart XML with
7
+ * the same `chartSpaceDesc` out of `@office-open/core`, and both forward only a
8
+ * subset of `ChartSpaceOptions` from their chart element. Verified against the
9
+ * packages rather than their types, because `ChartOptions extends
10
+ * ChartSpaceOptions` promises far more than either adapter reads. What gets
11
+ * dropped is identical in both formats, and all of it is visible to whoever
12
+ * opens the file:
13
+ *
14
+ * - **No `c:externalData`.** Neither backend writes one, and every `<c:f>`
15
+ * comes out empty, so the chart caches its values with no source for them and
16
+ * "Edit Data" fails. This is the exact defect the pptx adapter refused native
17
+ * charts over.
18
+ * - **No series colours.** Neither `ChartSeriesCommon` nor `DataPointOptions`
19
+ * carries a fill, and `colorMappingOverride` is not forwarded, so every
20
+ * series draws in the reader's default palette and ignores the theme.
21
+ * - **No axis titles.** Neither backend writes one: docx drops the `axes`
22
+ * option, and pptx accepts it but cannot be given one without inventing the
23
+ * axis ids its plot area references.
24
+ * - **No legend position**, on docx only — pptx forwards it.
25
+ * - **No grouping.** `ChartSpaceOptions` has no field for it and
26
+ * `chartSpaceDesc` writes `clustered` unconditionally, so a stacked chart
27
+ * came out side by side.
28
+ *
29
+ * So this module writes them, as pure string transforms over the emitted chart
30
+ * part plus the XML of the workbook it points at. Editing another library's
31
+ * serialisation is not free and is chosen deliberately: the alternative is a
32
+ * chart that draws and then fails on the first double-click.
33
+ *
34
+ * Format-neutral on purpose. A `c:chartSpace` is DrawingML, identical in a
35
+ * .docx and a .pptx; only the *packaging* differs — part paths, relationship
36
+ * files, content types, and which ZIP library the core happens to use. Those
37
+ * stay in each core; everything here is shared, which is what keeps the two
38
+ * formats from drifting into two different answers to the same problem.
39
+ *
40
+ * Nothing here touches a ZIP, a clock or a counter, so this package needs no
41
+ * new dependency and the same series always produce the same bytes.
33
42
  */
34
- interface OfficeRenderer<TIR, TFeature extends string, TId extends string> {
35
- readonly id: TId;
36
- readonly format: OfficeFormat;
37
- readonly capabilities: ReadonlySet<TFeature>;
38
- render(document: TIR, options?: RenderOptions): Promise<Uint8Array>;
43
+ /** The sheet a chart's cell references name. */
44
+ declare const CHART_WORKBOOK_SHEET_NAME = "Sheet1";
45
+ /** The relationship type an embedded workbook is attached by. */
46
+ declare const CHART_PACKAGE_RELATIONSHIP = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/package";
47
+ /** The content type of an embedded chart workbook. */
48
+ declare const CHART_WORKBOOK_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
49
+ /** One resolved series, as both formats' IR carries it by the time it gets here. */
50
+ interface ChartPartSeries {
51
+ name?: string;
52
+ labels: readonly string[];
53
+ values: readonly number[];
39
54
  }
40
55
  /**
41
- * Exhaustiveness guard for discriminated-union switches.
42
- *
43
- * Reaching this at runtime means an IR node kind was added without a matching
44
- * `case`, so it throws rather than silently dropping content.
45
- */
46
- declare function assertNever(value: never, context?: string): never;
47
-
48
- /**
49
- * Diagnostics raised when an IR asks a renderer for something it cannot do.
56
+ * Everything the splice needs, in neither format's vocabulary.
50
57
  *
51
- * These are distinct from `GenerationWarning` (see `../types/warnings`), which
52
- * describes authoring problems found while building the document. A renderer
53
- * diagnostic describes a *backend* limitation: the document is fine, this
54
- * particular adapter just cannot express part of it.
58
+ * Each core adapts its own IR node to this rather than this module learning
59
+ * about `DocxIrChartRun` and `PptxIrChartElement`, which would make a shared
60
+ * module depend on both cores it exists to serve.
55
61
  */
56
- type RendererDiagnosticSeverity = 'error' | 'warning';
57
62
  /**
58
- * One unsupported (or degraded) feature at one place in the IR.
63
+ * What an authored axis asks for, in neither format's vocabulary.
59
64
  *
60
- * `path` is an IR path such as `slides[2].elements[0].fill` not an author-JSON
61
- * path because the check runs against compiled IR. Compilers record the
62
- * authoring path alongside where it is useful for the message text.
65
+ * Every field here is one a backend drops: `AxisOptions` cannot be passed to
66
+ * `@office-open` at all supplying `axes` replaces the default pair and needs
67
+ * `id`/`crossAxisId` values an adapter cannot safely allocate so an authored
68
+ * axis is applied by rewriting the axis the backend built.
63
69
  */
64
- interface RendererDiagnostic<TFeature extends string = string> {
65
- feature: TFeature;
66
- path: string;
67
- severity: RendererDiagnosticSeverity;
68
- message: string;
70
+ /** Font family, size, weight and colour on one piece of chart text. */
71
+ interface ChartTextStyle {
72
+ fontFamily?: string;
73
+ /** Points. */
74
+ fontSize?: number;
75
+ bold?: boolean;
76
+ /** 6-digit hex, no `#`. */
77
+ color?: string;
69
78
  }
70
- interface UnsupportedRendererFeatureErrorInit<TFeature extends string = string> {
71
- format: OfficeFormat;
72
- rendererId: string;
73
- diagnostics: readonly RendererDiagnostic<TFeature>[];
79
+ interface ChartAxisEdits {
80
+ title?: string;
81
+ /** The font of this axis' tick labels. */
82
+ labelFont?: ChartTextStyle;
83
+ /** `c:delete`: an axis hidden entirely. */
84
+ hidden?: boolean;
85
+ /** `false` draws no axis line, leaving its labels. */
86
+ lineVisible?: boolean;
87
+ /** Label rotation, in degrees. */
88
+ labelRotation?: number;
89
+ gridLine?: {
90
+ style?: string;
91
+ size?: number;
92
+ color?: string;
93
+ };
94
+ /** Value-axis bounds; ignored on a category axis, which has no scale. */
95
+ min?: number;
96
+ max?: number;
97
+ majorUnit?: number;
98
+ /** A number format code, e.g. `#,##0`. */
99
+ numberFormat?: string;
74
100
  }
75
- /**
76
- * Aggregated failure thrown *before* rendering starts.
77
- *
78
- * One error carries every unsupported feature found in the IR so a caller sees
79
- * the whole gap at once instead of fixing them one render at a time.
80
- */
81
- declare class UnsupportedRendererFeatureError<TFeature extends string = string> extends Error {
82
- readonly code = "UNSUPPORTED_RENDERER_FEATURE";
83
- readonly format: OfficeFormat;
84
- readonly rendererId: string;
85
- /** Distinct unsupported features, in first-seen order. */
86
- readonly features: readonly TFeature[];
87
- /** Distinct IR paths that required them, in first-seen order. */
88
- readonly paths: readonly string[];
89
- /** Every error-severity diagnostic that produced this failure. */
90
- readonly diagnostics: readonly RendererDiagnostic<TFeature>[];
91
- constructor(init: UnsupportedRendererFeatureErrorInit<TFeature>);
101
+ interface ChartPartInput {
102
+ /** The chart type, in `@office-open`'s spelling. Decides fill vs stroke. */
103
+ chartType: string;
104
+ series: readonly ChartPartSeries[];
105
+ /** Resolved series colours, uppercase 6-digit hex without `#`. May be empty. */
106
+ colors: readonly string[];
107
+ categoryAxis?: ChartAxisEdits;
108
+ valueAxis?: ChartAxisEdits;
109
+ /** Series line width in points. Only meaningful where the series is a line. */
110
+ lineWidthPoints?: number;
111
+ /** An outline on filled data elements: bars, areas and slices. */
112
+ dataBorder?: {
113
+ widthPoints: number;
114
+ color: string;
115
+ };
116
+ /** `standard`, `marker` or `filled`; a backend may hardcode the first. */
117
+ radarStyle?: string;
118
+ titleFont?: ChartTextStyle;
119
+ legendFont?: ChartTextStyle;
120
+ dataLabelFont?: ChartTextStyle;
121
+ legendPosition?: string;
122
+ /**
123
+ * `clustered` | `stacked` | `percentStacked`.
124
+ *
125
+ * Spliced rather than passed: `ChartSpaceOptions` has no grouping field at
126
+ * all, and `chartSpaceDesc` writes `clustered` unconditionally. A chart
127
+ * authored as "% of total" therefore came out as side-by-side bars summing
128
+ * to nothing — the one dropped option that misrepresents the data rather
129
+ * than restyling it.
130
+ */
131
+ barGrouping?: string;
92
132
  }
93
133
  /**
94
- * A renderer id that is not registered for the format asked for.
134
+ * A spreadsheet column letter: A, B, Z, AA, AB,
95
135
  *
96
- * Caller input, not an infrastructure failure which is the whole reason it is
97
- * a class with a `code` rather than a bare `Error`. A server matching on the
98
- * message text could only answer `500`, so an unknown id looked like the
99
- * service falling over, and a retry looked worth attempting (#263).
136
+ * One-based, because a spreadsheet is. Written out rather than assumed to stay
137
+ * under 26 a chart with 27 series is unusual, not impossible, and the failure
138
+ * would be a corrupt sheet rather than an error.
100
139
  */
101
- declare class UnknownRendererError extends Error {
102
- readonly code = "UNKNOWN_RENDERER";
103
- readonly format: OfficeFormat;
104
- /** What the caller asked for. */
105
- readonly rendererId: string;
106
- /** Every id registered for this format, in registration order. */
107
- readonly availableIds: readonly string[];
108
- constructor(format: OfficeFormat, rendererId: string, availableIds: readonly string[]);
109
- }
110
- /** Build a `RendererDiagnostic` with `severity: 'error'`. */
111
- declare function rendererError<TFeature extends string>(feature: TFeature, path: string, message: string): RendererDiagnostic<TFeature>;
112
- /** Build a `RendererDiagnostic` with `severity: 'warning'`. */
113
- declare function rendererWarning<TFeature extends string>(feature: TFeature, path: string, message: string): RendererDiagnostic<TFeature>;
114
- /** Split diagnostics into blocking errors and non-blocking warnings. */
115
- declare function partitionDiagnostics<TFeature extends string>(diagnostics: readonly RendererDiagnostic<TFeature>[]): {
116
- errors: RendererDiagnostic<TFeature>[];
117
- warnings: RendererDiagnostic<TFeature>[];
118
- };
119
-
140
+ declare function columnLetter(index: number): string;
120
141
  /**
121
- * Capability checking: what an IR *requires* versus what an adapter *provides*.
142
+ * The parts of the xlsx a chart's `c:externalData` points at, in ZIP order.
122
143
  *
123
- * A compiler records one `FeatureRequirement` each time it emits an IR node that
124
- * needs a backend capability. Before rendering, `assertRendererSupports` diffs
125
- * those requirements against the adapter's `capabilities` set and throws a
126
- * single aggregated `UnsupportedRendererFeatureError` if anything is missing.
144
+ * Returned as XML rather than as a packaged archive: `core-docx` zips with
145
+ * adm-zip and `core-pptx` with jszip, and a shared module that picked one would
146
+ * force a second ZIP library into whichever core did not use it. Order is fixed
147
+ * rather than incidental, so the central directory is a function of the data.
127
148
  *
128
- * The point is that nothing is dropped silently: a feature either appears in the
129
- * adapter's capability set and is rendered, or it fails loudly before bytes are
130
- * produced.
149
+ * Deliberately minimal five parts, one sheet, inline strings rather than a
150
+ * shared-string table. A chart workbook is written once and read by one
151
+ * consumer, so the compression a shared-string table buys is not worth a part
152
+ * whose indices are one more thing to keep in step with the cells.
131
153
  */
132
- /** One capability an IR node needs, and where in the IR it was needed. */
133
- interface FeatureRequirement<TFeature extends string = string> {
134
- feature: TFeature;
135
- /** IR path, e.g. `sections[0].children[3].image`. */
136
- path: string;
137
- /** Optional detail folded into the failure message. */
138
- detail?: string;
139
- }
154
+ declare function chartWorkbookParts(series: readonly ChartPartSeries[]): ReadonlyArray<readonly [path: string, xml: string]>;
140
155
  /**
141
- * Accumulates feature requirements during compilation.
156
+ * The cell range one series' values occupy, as a chart reference.
142
157
  *
143
- * Deliberately per-compilation (never module-global) so concurrent generations
144
- * never share state.
158
+ * The chart XML and the sheet have to agree on this exactly; deriving both from
159
+ * one function is what keeps them from drifting apart.
145
160
  */
146
- declare class FeatureRequirementCollector<TFeature extends string> {
147
- private readonly requirements;
148
- private readonly seen;
149
- /**
150
- * Record that `feature` is needed at `path`.
151
- *
152
- * Duplicate (feature, path) pairs collapse, so a compiler can call this
153
- * unconditionally inside a loop without inflating the diagnostics.
154
- */
155
- require(feature: TFeature, path: string, detail?: string): void;
156
- /** Every recorded requirement, in first-seen order. */
157
- list(): readonly FeatureRequirement<TFeature>[];
158
- /** Distinct required features, in first-seen order. */
159
- features(): readonly TFeature[];
160
- /** True when nothing has been required yet. */
161
- isEmpty(): boolean;
162
- }
161
+ declare function seriesValueReference(seriesIndex: number, pointCount: number): string;
162
+ /** The cell range the category labels occupy. */
163
+ declare function categoryReference(pointCount: number): string;
164
+ /** The single cell holding one series' name. */
165
+ declare function seriesNameReference(seriesIndex: number): string;
166
+ /** The relationship part attaching one workbook to one chart. */
167
+ declare function chartWorkbookRelsXml(workbookName: string): string;
163
168
  /**
164
- * Diff required features against a capability set.
169
+ * Rewrite one emitted `chartN.xml` with everything the backend omitted.
165
170
  *
166
- * Returns one error-severity diagnostic per unsupported requirement. An empty
167
- * array means the renderer can render the IR.
168
- */
169
- declare function diagnoseUnsupportedFeatures<TFeature extends string>(required: readonly FeatureRequirement<TFeature>[], capabilities: ReadonlySet<TFeature>, rendererId: string): RendererDiagnostic<TFeature>[];
170
- /**
171
- * Throw one aggregated error if the renderer is missing any required feature.
171
+ * Every repair is guarded on what the XML actually lacks, because the two
172
+ * backends omit different amounts. `@office-open/pptx` hands its whole options
173
+ * object to `chartSpaceDesc`, so the legend position survives;
174
+ * `@office-open/docx` forwards eight named fields and loses it. Everything else
175
+ * here — the cell references behind `<c:f/>`, the series fill, the axis titles,
176
+ * the grouping and `c:externalData` is missing from both.
172
177
  *
173
- * Call this after compiling to IR and before handing the IR to an adapter.
178
+ * Detecting rather than assuming is also what keeps this honest if a backend
179
+ * starts emitting more: the repair simply stops firing, instead of writing a
180
+ * second copy of an element a reader would offer to repair.
181
+ *
182
+ * `relationshipId` names the workbook relationship in the chart part's own
183
+ * rels file, which each core writes alongside.
174
184
  */
175
- declare function assertRendererSupports<TFeature extends string>(required: readonly FeatureRequirement<TFeature>[], renderer: Pick<OfficeRenderer<unknown, TFeature, string>, 'id' | 'format' | 'capabilities'>): void;
185
+ declare function spliceChartXml(chartXml: string, chart: ChartPartInput, relationshipId?: string): string;
186
+ declare function chartPartSignature(chartXml: string): string;
187
+ /** The same signature, computed from the IR node the part was emitted from. */
188
+ declare function chartInputSignature(chart: ChartPartInput): string;
176
189
  /**
177
- * A registry of renderers for a single format.
178
- *
179
- * Instances are created per format module, not per generation, and hold only
180
- * immutable adapter descriptors never per-document state.
190
+ * Pair emitted chart parts with the inputs they came from, by content.
191
+ *
192
+ * Returns one entry per part that matched, in part order. A part with no match
193
+ * is left out rather than guessed at: the package holds a chart this pass did
194
+ * not emit, and repairing it from the wrong node is exactly the defect the
195
+ * matching exists to prevent. Two charts identical in every cached value match
196
+ * interchangeably, which is harmless — identical data yields an identical
197
+ * workbook, and an author who wrote two identical charts did not distinguish
198
+ * their palettes either.
181
199
  */
182
- declare class RendererRegistry<TIR, TFeature extends string, TId extends string> {
183
- private readonly format;
184
- private readonly defaultId;
185
- private readonly renderers;
186
- constructor(format: OfficeFormat, defaultId: TId);
187
- /**
188
- * Register a lazily-constructed renderer.
189
- *
190
- * The factory is async and only invoked on selection, so an adapter whose
191
- * backend is an optional dependency is never imported unless it is chosen.
192
- */
193
- register(id: TId, factory: () => Promise<OfficeRenderer<TIR, TFeature, TId>>): void;
194
- /** Renderer ids registered for this format, in registration order. */
195
- ids(): readonly TId[];
196
- /** The id used when a caller does not pass one. */
197
- getDefaultId(): TId;
198
- has(id: string): id is TId;
199
- /**
200
- * Resolve a renderer, defaulting when `id` is omitted.
201
- *
202
- * An unknown id is `UnknownRendererError`, which carries the id asked for and
203
- * the ones that exist, so a caller boundary can answer "bad request" rather
204
- * than "the server broke". A missing optional dependency is re-thrown with an
205
- * actionable install hint.
206
- */
207
- resolve(id?: TId): Promise<OfficeRenderer<TIR, TFeature, TId>>;
208
- }
200
+ declare function matchChartParts<T extends ChartPartInput>(parts: ReadonlyArray<readonly [ordinal: number, xml: string]>, charts: readonly T[]): Array<{
201
+ ordinal: number;
202
+ xml: string;
203
+ chart: T;
204
+ }>;
209
205
 
210
- export { type FeatureRequirement, FeatureRequirementCollector, type OfficeFormat, type OfficeRenderer, type RenderOptions, type RendererDiagnostic, type RendererDiagnosticSeverity, RendererRegistry, UnknownRendererError, UnsupportedRendererFeatureError, type UnsupportedRendererFeatureErrorInit, assertNever, assertRendererSupports, diagnoseUnsupportedFeatures, partitionDiagnostics, rendererError, rendererWarning };
206
+ export { CHART_PACKAGE_RELATIONSHIP, CHART_WORKBOOK_CONTENT_TYPE, CHART_WORKBOOK_SHEET_NAME, type ChartAxisEdits, type ChartPartInput, type ChartPartSeries, type ChartTextStyle, categoryReference, chartInputSignature, chartPartSignature, chartWorkbookParts, chartWorkbookRelsXml, columnLetter, matchChartParts, seriesNameReference, seriesValueReference, spliceChartXml };
@@ -1,25 +1,51 @@
1
1
  import {
2
+ CHART_PACKAGE_RELATIONSHIP,
3
+ CHART_WORKBOOK_CONTENT_TYPE,
4
+ CHART_WORKBOOK_SHEET_NAME,
2
5
  FeatureRequirementCollector,
3
6
  RendererRegistry,
4
7
  UnknownRendererError,
5
8
  UnsupportedRendererFeatureError,
6
9
  assertNever,
7
10
  assertRendererSupports,
11
+ categoryReference,
12
+ chartInputSignature,
13
+ chartPartSignature,
14
+ chartWorkbookParts,
15
+ chartWorkbookRelsXml,
16
+ columnLetter,
8
17
  diagnoseUnsupportedFeatures,
18
+ matchChartParts,
9
19
  partitionDiagnostics,
10
20
  rendererError,
11
- rendererWarning
12
- } from "../chunk-JM5KTMNL.js";
21
+ rendererWarning,
22
+ seriesNameReference,
23
+ seriesValueReference,
24
+ spliceChartXml
25
+ } from "../chunk-BJNBJSQG.js";
13
26
  export {
27
+ CHART_PACKAGE_RELATIONSHIP,
28
+ CHART_WORKBOOK_CONTENT_TYPE,
29
+ CHART_WORKBOOK_SHEET_NAME,
14
30
  FeatureRequirementCollector,
15
31
  RendererRegistry,
16
32
  UnknownRendererError,
17
33
  UnsupportedRendererFeatureError,
18
34
  assertNever,
19
35
  assertRendererSupports,
36
+ categoryReference,
37
+ chartInputSignature,
38
+ chartPartSignature,
39
+ chartWorkbookParts,
40
+ chartWorkbookRelsXml,
41
+ columnLetter,
20
42
  diagnoseUnsupportedFeatures,
43
+ matchChartParts,
21
44
  partitionDiagnostics,
22
45
  rendererError,
23
- rendererWarning
46
+ rendererWarning,
47
+ seriesNameReference,
48
+ seriesValueReference,
49
+ spliceChartXml
24
50
  };
25
51
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@json-to-office/shared",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Format-agnostic shared types, schemas and validation utilities",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",