@json-to-office/shared 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{capabilities-DrHJ6_4G.d.ts → capabilities-DtPF3aBj.d.ts} +47 -4
- package/dist/{chunk-BJNBJSQG.js → chunk-QLZNOXT5.js} +50 -7
- package/dist/chunk-QLZNOXT5.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +13 -11
- package/dist/index.js.map +1 -1
- package/dist/rendering/index.d.ts +1 -1
- package/dist/rendering/index.js +3 -1
- package/dist/schemas/slide-content.d.ts +5 -1
- package/dist/schemas/slide-content.js +6 -1
- package/dist/schemas/slide-content.js.map +1 -1
- package/package.json +4 -4
- package/dist/chunk-BJNBJSQG.js.map +0 -1
|
@@ -173,6 +173,25 @@ declare function diagnoseUnsupportedFeatures<TFeature extends string>(required:
|
|
|
173
173
|
* Call this after compiling to IR and before handing the IR to an adapter.
|
|
174
174
|
*/
|
|
175
175
|
declare function assertRendererSupports<TFeature extends string>(required: readonly FeatureRequirement<TFeature>[], renderer: Pick<OfficeRenderer<unknown, TFeature, string>, 'id' | 'format' | 'capabilities'>): void;
|
|
176
|
+
/**
|
|
177
|
+
* Whether a registered renderer can actually run on this host.
|
|
178
|
+
*
|
|
179
|
+
* Registration says a renderer exists; it says nothing about whether its
|
|
180
|
+
* backend is installed, because the factory is only invoked on selection. A
|
|
181
|
+
* discovery surface that reports the ids alone therefore advertises renderers
|
|
182
|
+
* that fail at the first render — which is what `jto_info` and `jto_discover`
|
|
183
|
+
* used to do for `office-open` on a host that never installed it.
|
|
184
|
+
*/
|
|
185
|
+
interface RendererStatus<TId extends string = string> {
|
|
186
|
+
id: TId;
|
|
187
|
+
/** The one used when a caller passes no id. */
|
|
188
|
+
default: boolean;
|
|
189
|
+
available: boolean;
|
|
190
|
+
/** Why not, when not — the message the load failure carried. */
|
|
191
|
+
reason?: string;
|
|
192
|
+
/** The command that would make it available. */
|
|
193
|
+
installHint?: string;
|
|
194
|
+
}
|
|
176
195
|
/**
|
|
177
196
|
* A registry of renderers for a single format.
|
|
178
197
|
*
|
|
@@ -187,8 +206,8 @@ declare class RendererRegistry<TIR, TFeature extends string, TId extends string>
|
|
|
187
206
|
/**
|
|
188
207
|
* Register a lazily-constructed renderer.
|
|
189
208
|
*
|
|
190
|
-
* The factory is async and only invoked on selection, so
|
|
191
|
-
*
|
|
209
|
+
* The factory is async and only invoked on selection, so choosing one
|
|
210
|
+
* renderer never imports another one's backend.
|
|
192
211
|
*/
|
|
193
212
|
register(id: TId, factory: () => Promise<OfficeRenderer<TIR, TFeature, TId>>): void;
|
|
194
213
|
/** Renderer ids registered for this format, in registration order. */
|
|
@@ -201,10 +220,34 @@ declare class RendererRegistry<TIR, TFeature extends string, TId extends string>
|
|
|
201
220
|
*
|
|
202
221
|
* An unknown id is `UnknownRendererError`, which carries the id asked for and
|
|
203
222
|
* the ones that exist, so a caller boundary can answer "bad request" rather
|
|
204
|
-
* than "the server broke". A
|
|
223
|
+
* than "the server broke". A backend that will not load is re-thrown with an
|
|
205
224
|
* actionable install hint.
|
|
206
225
|
*/
|
|
207
226
|
resolve(id?: TId): Promise<OfficeRenderer<TIR, TFeature, TId>>;
|
|
227
|
+
/**
|
|
228
|
+
* Every registered renderer, with whether it can actually be loaded here.
|
|
229
|
+
*
|
|
230
|
+
* Answers the question by loading each one, which is the only answer that
|
|
231
|
+
* cannot be wrong — a resolver check would still miss a backend that
|
|
232
|
+
* resolves and then throws on import.
|
|
233
|
+
*
|
|
234
|
+
* Memoized, and the promise rather than its value, so concurrent callers
|
|
235
|
+
* share one probe instead of racing several. Nothing installs a package into
|
|
236
|
+
* a running process, so the answer cannot go stale within one; without this
|
|
237
|
+
* `jto_validate` would pay a package import on every call, which is the tool
|
|
238
|
+
* an agent uses after every edit.
|
|
239
|
+
*/
|
|
240
|
+
statuses(): Promise<RendererStatus<TId>[]>;
|
|
241
|
+
private statusCache?;
|
|
242
|
+
private probeStatuses;
|
|
208
243
|
}
|
|
244
|
+
/**
|
|
245
|
+
* `Error.name` marking a renderer whose optional backend is not installed.
|
|
246
|
+
*
|
|
247
|
+
* A name rather than a subclass: the error crosses a package boundary and an
|
|
248
|
+
* `instanceof` there would depend on both sides loading the same copy of this
|
|
249
|
+
* module, which under a workspace layout is not something to rely on.
|
|
250
|
+
*/
|
|
251
|
+
declare const RENDERER_DEPENDENCY_MISSING = "RendererDependencyMissingError";
|
|
209
252
|
|
|
210
|
-
export { type FeatureRequirement as F, type OfficeFormat as O,
|
|
253
|
+
export { type FeatureRequirement as F, type OfficeFormat as O, RENDERER_DEPENDENCY_MISSING as R, UnknownRendererError as U, FeatureRequirementCollector as a, type OfficeRenderer as b, type RenderOptions as c, type RendererDiagnostic as d, type RendererDiagnosticSeverity as e, RendererRegistry as f, type RendererStatus as g, UnsupportedRendererFeatureError as h, type UnsupportedRendererFeatureErrorInit as i, assertNever as j, assertRendererSupports as k, diagnoseUnsupportedFeatures as l, rendererWarning as m, partitionDiagnostics as p, rendererError as r };
|
|
@@ -166,11 +166,12 @@ var RendererRegistry = class {
|
|
|
166
166
|
/**
|
|
167
167
|
* Register a lazily-constructed renderer.
|
|
168
168
|
*
|
|
169
|
-
* The factory is async and only invoked on selection, so
|
|
170
|
-
*
|
|
169
|
+
* The factory is async and only invoked on selection, so choosing one
|
|
170
|
+
* renderer never imports another one's backend.
|
|
171
171
|
*/
|
|
172
172
|
register(id, factory) {
|
|
173
173
|
this.renderers.set(id, factory);
|
|
174
|
+
this.statusCache = void 0;
|
|
174
175
|
}
|
|
175
176
|
/** Renderer ids registered for this format, in registration order. */
|
|
176
177
|
ids() {
|
|
@@ -188,7 +189,7 @@ var RendererRegistry = class {
|
|
|
188
189
|
*
|
|
189
190
|
* An unknown id is `UnknownRendererError`, which carries the id asked for and
|
|
190
191
|
* the ones that exist, so a caller boundary can answer "bad request" rather
|
|
191
|
-
* than "the server broke". A
|
|
192
|
+
* than "the server broke". A backend that will not load is re-thrown with an
|
|
192
193
|
* actionable install hint.
|
|
193
194
|
*/
|
|
194
195
|
async resolve(id) {
|
|
@@ -203,6 +204,44 @@ var RendererRegistry = class {
|
|
|
203
204
|
throw enrichLoadFailure(error, this.format, selected);
|
|
204
205
|
}
|
|
205
206
|
}
|
|
207
|
+
/**
|
|
208
|
+
* Every registered renderer, with whether it can actually be loaded here.
|
|
209
|
+
*
|
|
210
|
+
* Answers the question by loading each one, which is the only answer that
|
|
211
|
+
* cannot be wrong — a resolver check would still miss a backend that
|
|
212
|
+
* resolves and then throws on import.
|
|
213
|
+
*
|
|
214
|
+
* Memoized, and the promise rather than its value, so concurrent callers
|
|
215
|
+
* share one probe instead of racing several. Nothing installs a package into
|
|
216
|
+
* a running process, so the answer cannot go stale within one; without this
|
|
217
|
+
* `jto_validate` would pay a package import on every call, which is the tool
|
|
218
|
+
* an agent uses after every edit.
|
|
219
|
+
*/
|
|
220
|
+
statuses() {
|
|
221
|
+
this.statusCache ??= this.probeStatuses();
|
|
222
|
+
return this.statusCache;
|
|
223
|
+
}
|
|
224
|
+
statusCache;
|
|
225
|
+
async probeStatuses() {
|
|
226
|
+
return Promise.all(
|
|
227
|
+
this.ids().map(async (id) => {
|
|
228
|
+
const base = { id, default: id === this.defaultId };
|
|
229
|
+
try {
|
|
230
|
+
await this.resolve(id);
|
|
231
|
+
return { ...base, available: true };
|
|
232
|
+
} catch (error) {
|
|
233
|
+
const missing = error instanceof Error && error.name === RENDERER_DEPENDENCY_MISSING;
|
|
234
|
+
const pkg = missing && error instanceof Error ? error.packageName : void 0;
|
|
235
|
+
return {
|
|
236
|
+
...base,
|
|
237
|
+
available: false,
|
|
238
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
239
|
+
...pkg ? { installHint: `pnpm add ${pkg}` } : {}
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
})
|
|
243
|
+
);
|
|
244
|
+
}
|
|
206
245
|
};
|
|
207
246
|
function enrichLoadFailure(error, format, rendererId) {
|
|
208
247
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -212,14 +251,17 @@ function enrichLoadFailure(error, format, rendererId) {
|
|
|
212
251
|
if (!isMissingModule) {
|
|
213
252
|
return error instanceof Error ? error : new Error(message);
|
|
214
253
|
}
|
|
215
|
-
const pkg = missingPackageName(message)
|
|
254
|
+
const pkg = missingPackageName(message);
|
|
255
|
+
const named = pkg ?? `the "${rendererId}" backend`;
|
|
216
256
|
const enriched = new Error(
|
|
217
|
-
`The "${rendererId}" ${format} renderer requires ${
|
|
257
|
+
`The "${rendererId}" ${format} renderer requires ${named}, which is not installed. Install it with: pnpm add ${named}
|
|
218
258
|
Original error: ${message}`
|
|
219
259
|
);
|
|
220
|
-
enriched.name =
|
|
260
|
+
enriched.name = RENDERER_DEPENDENCY_MISSING;
|
|
261
|
+
if (pkg) enriched.packageName = pkg;
|
|
221
262
|
return enriched;
|
|
222
263
|
}
|
|
264
|
+
var RENDERER_DEPENDENCY_MISSING = "RendererDependencyMissingError";
|
|
223
265
|
function missingPackageName(message) {
|
|
224
266
|
const match = /Cannot find (?:module|package) ['"]([^'"]+)['"]/.exec(message) ?? /Failed to resolve (?:module|import)[: ]+['"]?([^'"\s]+)/.exec(message);
|
|
225
267
|
return match?.[1];
|
|
@@ -674,6 +716,7 @@ export {
|
|
|
674
716
|
diagnoseUnsupportedFeatures,
|
|
675
717
|
assertRendererSupports,
|
|
676
718
|
RendererRegistry,
|
|
719
|
+
RENDERER_DEPENDENCY_MISSING,
|
|
677
720
|
CHART_WORKBOOK_SHEET_NAME,
|
|
678
721
|
CHART_PACKAGE_RELATIONSHIP,
|
|
679
722
|
CHART_WORKBOOK_CONTENT_TYPE,
|
|
@@ -688,4 +731,4 @@ export {
|
|
|
688
731
|
chartInputSignature,
|
|
689
732
|
matchChartParts
|
|
690
733
|
};
|
|
691
|
-
//# sourceMappingURL=chunk-
|
|
734
|
+
//# sourceMappingURL=chunk-QLZNOXT5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/rendering/types.ts","../src/rendering/diagnostics.ts","../src/rendering/capabilities.ts","../src/rendering/chart-parts.ts"],"sourcesContent":["/**\n * Format-independent renderer contracts.\n *\n * This module deliberately knows nothing about DOCX or PPTX semantics. Each\n * format owns its own intermediate representation (`DocxIR`, `PptxIR`) and its\n * own feature union; the only thing shared between them is the shape of the\n * contract a backend adapter must satisfy.\n *\n * Do not add format-specific feature names, IR nodes or units here.\n */\n\n/** The Office formats this repository can produce. */\nexport type OfficeFormat = 'docx' | 'pptx';\n\n/**\n * Options every renderer accepts.\n *\n * `deterministic` asks the adapter (and the packaging step after it) to make\n * output byte-stable across runs: fixed zip entry timestamps, fixed core\n * metadata timestamps, no random identifiers.\n *\n * `generatedAt` pins the timestamp written into package metadata. Callers that\n * want reproducible bytes pass both.\n */\nexport interface RenderOptions {\n deterministic?: boolean;\n generatedAt?: Date;\n}\n\n/**\n * A backend that turns a format-specific IR into package bytes.\n *\n * @typeParam TIR - the format's intermediate representation (plain data)\n * @typeParam TFeature - the format's feature union (see `capabilities.ts`)\n * @typeParam TId - the string-literal union of renderer ids for the format\n */\nexport interface OfficeRenderer<\n TIR,\n TFeature extends string,\n TId extends string,\n> {\n readonly id: TId;\n readonly format: OfficeFormat;\n readonly capabilities: ReadonlySet<TFeature>;\n\n render(document: TIR, options?: RenderOptions): Promise<Uint8Array>;\n}\n\n/**\n * Exhaustiveness guard for discriminated-union switches.\n *\n * Reaching this at runtime means an IR node kind was added without a matching\n * `case`, so it throws rather than silently dropping content.\n */\nexport function assertNever(value: never, context?: string): never {\n const described = describeUnhandled(value);\n throw new Error(\n context\n ? `Unhandled variant in ${context}: ${described}`\n : `Unhandled variant: ${described}`\n );\n}\n\nfunction describeUnhandled(value: unknown): string {\n if (value === null || typeof value !== 'object') {\n return String(value);\n }\n const kind = (value as { kind?: unknown }).kind;\n const type = (value as { type?: unknown }).type;\n if (typeof kind === 'string') return `kind=\"${kind}\"`;\n if (typeof type === 'string') return `type=\"${type}\"`;\n try {\n return JSON.stringify(value);\n } catch {\n return Object.prototype.toString.call(value);\n }\n}\n","import type { OfficeFormat } from './types';\n\n/**\n * Diagnostics raised when an IR asks a renderer for something it cannot do.\n *\n * These are distinct from `GenerationWarning` (see `../types/warnings`), which\n * describes authoring problems found while building the document. A renderer\n * diagnostic describes a *backend* limitation: the document is fine, this\n * particular adapter just cannot express part of it.\n */\n\nexport type RendererDiagnosticSeverity = 'error' | 'warning';\n\n/**\n * One unsupported (or degraded) feature at one place in the IR.\n *\n * `path` is an IR path such as `slides[2].elements[0].fill` — not an author-JSON\n * path — because the check runs against compiled IR. Compilers record the\n * authoring path alongside where it is useful for the message text.\n */\nexport interface RendererDiagnostic<TFeature extends string = string> {\n feature: TFeature;\n path: string;\n severity: RendererDiagnosticSeverity;\n message: string;\n}\n\nexport interface UnsupportedRendererFeatureErrorInit<\n TFeature extends string = string,\n> {\n format: OfficeFormat;\n rendererId: string;\n diagnostics: readonly RendererDiagnostic<TFeature>[];\n}\n\n/**\n * Aggregated failure thrown *before* rendering starts.\n *\n * One error carries every unsupported feature found in the IR so a caller sees\n * the whole gap at once instead of fixing them one render at a time.\n */\nexport class UnsupportedRendererFeatureError<\n TFeature extends string = string,\n> extends Error {\n public readonly code = 'UNSUPPORTED_RENDERER_FEATURE';\n public readonly format: OfficeFormat;\n public readonly rendererId: string;\n /** Distinct unsupported features, in first-seen order. */\n public readonly features: readonly TFeature[];\n /** Distinct IR paths that required them, in first-seen order. */\n public readonly paths: readonly string[];\n /** Every error-severity diagnostic that produced this failure. */\n public readonly diagnostics: readonly RendererDiagnostic<TFeature>[];\n\n constructor(init: UnsupportedRendererFeatureErrorInit<TFeature>) {\n const { format, rendererId, diagnostics } = init;\n const features = distinct(diagnostics.map((d) => d.feature));\n const paths = distinct(diagnostics.map((d) => d.path));\n\n super(formatMessage(format, rendererId, diagnostics, features));\n\n this.name = 'UnsupportedRendererFeatureError';\n this.format = format;\n this.rendererId = rendererId;\n this.features = features;\n this.paths = paths;\n this.diagnostics = [...diagnostics];\n\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, UnsupportedRendererFeatureError);\n }\n }\n}\n\n/**\n * A renderer id that is not registered for the format asked for.\n *\n * Caller input, not an infrastructure failure — which is the whole reason it is\n * a class with a `code` rather than a bare `Error`. A server matching on the\n * message text could only answer `500`, so an unknown id looked like the\n * service falling over, and a retry looked worth attempting (#263).\n */\nexport class UnknownRendererError extends Error {\n public readonly code = 'UNKNOWN_RENDERER';\n public readonly format: OfficeFormat;\n /** What the caller asked for. */\n public readonly rendererId: string;\n /** Every id registered for this format, in registration order. */\n public readonly availableIds: readonly string[];\n\n constructor(\n format: OfficeFormat,\n rendererId: string,\n availableIds: readonly string[]\n ) {\n const known = availableIds.map((id) => `\"${id}\"`).join(', ');\n super(\n `Unknown ${format} renderer \"${rendererId}\". Available renderers: ${known}.`\n );\n\n this.name = 'UnknownRendererError';\n this.format = format;\n this.rendererId = rendererId;\n this.availableIds = [...availableIds];\n\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, UnknownRendererError);\n }\n }\n}\n\nfunction distinct<T>(values: readonly T[]): T[] {\n return [...new Set(values)];\n}\n\nfunction formatMessage<TFeature extends string>(\n format: OfficeFormat,\n rendererId: string,\n diagnostics: readonly RendererDiagnostic<TFeature>[],\n features: readonly TFeature[]\n): string {\n const featureList = features.map((f) => `\"${f}\"`).join(', ');\n const lines = diagnostics.map(\n (d) => ` - ${d.feature} at ${d.path}: ${d.message}`\n );\n return (\n `The \"${rendererId}\" ${format} renderer does not support ${features.length} ` +\n `required feature(s): ${featureList}.\\n${lines.join('\\n')}`\n );\n}\n\n/** Build a `RendererDiagnostic` with `severity: 'error'`. */\nexport function rendererError<TFeature extends string>(\n feature: TFeature,\n path: string,\n message: string\n): RendererDiagnostic<TFeature> {\n return { feature, path, severity: 'error', message };\n}\n\n/** Build a `RendererDiagnostic` with `severity: 'warning'`. */\nexport function rendererWarning<TFeature extends string>(\n feature: TFeature,\n path: string,\n message: string\n): RendererDiagnostic<TFeature> {\n return { feature, path, severity: 'warning', message };\n}\n\n/** Split diagnostics into blocking errors and non-blocking warnings. */\nexport function partitionDiagnostics<TFeature extends string>(\n diagnostics: readonly RendererDiagnostic<TFeature>[]\n): {\n errors: RendererDiagnostic<TFeature>[];\n warnings: RendererDiagnostic<TFeature>[];\n} {\n const errors: RendererDiagnostic<TFeature>[] = [];\n const warnings: RendererDiagnostic<TFeature>[] = [];\n for (const diagnostic of diagnostics) {\n if (diagnostic.severity === 'error') errors.push(diagnostic);\n else warnings.push(diagnostic);\n }\n return { errors, warnings };\n}\n","import {\n UnknownRendererError,\n UnsupportedRendererFeatureError,\n rendererError,\n type RendererDiagnostic,\n} from './diagnostics';\nimport type { OfficeFormat, OfficeRenderer } from './types';\n\n/**\n * Capability checking: what an IR *requires* versus what an adapter *provides*.\n *\n * A compiler records one `FeatureRequirement` each time it emits an IR node that\n * needs a backend capability. Before rendering, `assertRendererSupports` diffs\n * those requirements against the adapter's `capabilities` set and throws a\n * single aggregated `UnsupportedRendererFeatureError` if anything is missing.\n *\n * The point is that nothing is dropped silently: a feature either appears in the\n * adapter's capability set and is rendered, or it fails loudly before bytes are\n * produced.\n */\n\n/** One capability an IR node needs, and where in the IR it was needed. */\nexport interface FeatureRequirement<TFeature extends string = string> {\n feature: TFeature;\n /** IR path, e.g. `sections[0].children[3].image`. */\n path: string;\n /** Optional detail folded into the failure message. */\n detail?: string;\n}\n\n/**\n * Accumulates feature requirements during compilation.\n *\n * Deliberately per-compilation (never module-global) so concurrent generations\n * never share state.\n */\nexport class FeatureRequirementCollector<TFeature extends string> {\n private readonly requirements: FeatureRequirement<TFeature>[] = [];\n private readonly seen = new Set<string>();\n\n /**\n * Record that `feature` is needed at `path`.\n *\n * Duplicate (feature, path) pairs collapse, so a compiler can call this\n * unconditionally inside a loop without inflating the diagnostics.\n */\n require(feature: TFeature, path: string, detail?: string): void {\n const key = `${feature}\\u0000${path}`;\n if (this.seen.has(key)) return;\n this.seen.add(key);\n this.requirements.push(\n detail === undefined ? { feature, path } : { feature, path, detail }\n );\n }\n\n /** Every recorded requirement, in first-seen order. */\n list(): readonly FeatureRequirement<TFeature>[] {\n return this.requirements;\n }\n\n /** Distinct required features, in first-seen order. */\n features(): readonly TFeature[] {\n return [...new Set(this.requirements.map((r) => r.feature))];\n }\n\n /** True when nothing has been required yet. */\n isEmpty(): boolean {\n return this.requirements.length === 0;\n }\n}\n\n/**\n * Diff required features against a capability set.\n *\n * Returns one error-severity diagnostic per unsupported requirement. An empty\n * array means the renderer can render the IR.\n */\nexport function diagnoseUnsupportedFeatures<TFeature extends string>(\n required: readonly FeatureRequirement<TFeature>[],\n capabilities: ReadonlySet<TFeature>,\n rendererId: string\n): RendererDiagnostic<TFeature>[] {\n const diagnostics: RendererDiagnostic<TFeature>[] = [];\n for (const requirement of required) {\n if (capabilities.has(requirement.feature)) continue;\n diagnostics.push(\n rendererError(\n requirement.feature,\n requirement.path,\n buildMessage(requirement, rendererId)\n )\n );\n }\n return diagnostics;\n}\n\nfunction buildMessage<TFeature extends string>(\n requirement: FeatureRequirement<TFeature>,\n rendererId: string\n): string {\n const base = `the \"${rendererId}\" renderer cannot express \"${requirement.feature}\"`;\n return requirement.detail ? `${base} (${requirement.detail})` : base;\n}\n\n/**\n * Throw one aggregated error if the renderer is missing any required feature.\n *\n * Call this after compiling to IR and before handing the IR to an adapter.\n */\nexport function assertRendererSupports<TFeature extends string>(\n required: readonly FeatureRequirement<TFeature>[],\n renderer: Pick<\n OfficeRenderer<unknown, TFeature, string>,\n 'id' | 'format' | 'capabilities'\n >\n): void {\n const diagnostics = diagnoseUnsupportedFeatures(\n required,\n renderer.capabilities,\n renderer.id\n );\n if (diagnostics.length === 0) return;\n throw new UnsupportedRendererFeatureError<TFeature>({\n format: renderer.format,\n rendererId: renderer.id,\n diagnostics,\n });\n}\n\n/**\n * Whether a registered renderer can actually run on this host.\n *\n * Registration says a renderer exists; it says nothing about whether its\n * backend is installed, because the factory is only invoked on selection. A\n * discovery surface that reports the ids alone therefore advertises renderers\n * that fail at the first render — which is what `jto_info` and `jto_discover`\n * used to do for `office-open` on a host that never installed it.\n */\nexport interface RendererStatus<TId extends string = string> {\n id: TId;\n /** The one used when a caller passes no id. */\n default: boolean;\n available: boolean;\n /** Why not, when not — the message the load failure carried. */\n reason?: string;\n /** The command that would make it available. */\n installHint?: string;\n}\n\n/**\n * A registry of renderers for a single format.\n *\n * Instances are created per format module, not per generation, and hold only\n * immutable adapter descriptors — never per-document state.\n */\nexport class RendererRegistry<\n TIR,\n TFeature extends string,\n TId extends string,\n> {\n private readonly renderers = new Map<\n TId,\n () => Promise<OfficeRenderer<TIR, TFeature, TId>>\n >();\n\n constructor(\n private readonly format: OfficeFormat,\n private readonly defaultId: TId\n ) {}\n\n /**\n * Register a lazily-constructed renderer.\n *\n * The factory is async and only invoked on selection, so choosing one\n * renderer never imports another one's backend.\n */\n register(\n id: TId,\n factory: () => Promise<OfficeRenderer<TIR, TFeature, TId>>\n ): void {\n this.renderers.set(id, factory);\n // The probe below is memoized over the registered set, so registering into\n // an already-probed registry has to drop it — otherwise `statuses()` keeps\n // answering with a list this renderer is missing from, forever. The\n // bundled registries register everything at module load and never reach\n // this, but the class is exported and a caller registering late should not\n // have to know that.\n this.statusCache = undefined;\n }\n\n /** Renderer ids registered for this format, in registration order. */\n ids(): readonly TId[] {\n return [...this.renderers.keys()];\n }\n\n /** The id used when a caller does not pass one. */\n getDefaultId(): TId {\n return this.defaultId;\n }\n\n has(id: string): id is TId {\n return this.renderers.has(id as TId);\n }\n\n /**\n * Resolve a renderer, defaulting when `id` is omitted.\n *\n * An unknown id is `UnknownRendererError`, which carries the id asked for and\n * the ones that exist, so a caller boundary can answer \"bad request\" rather\n * than \"the server broke\". A backend that will not load is re-thrown with an\n * actionable install hint.\n */\n async resolve(id?: TId): Promise<OfficeRenderer<TIR, TFeature, TId>> {\n const selected = id ?? this.defaultId;\n const factory = this.renderers.get(selected);\n if (!factory) {\n throw new UnknownRendererError(this.format, selected, this.ids());\n }\n try {\n return await factory();\n } catch (error) {\n throw enrichLoadFailure(error, this.format, selected);\n }\n }\n\n /**\n * Every registered renderer, with whether it can actually be loaded here.\n *\n * Answers the question by loading each one, which is the only answer that\n * cannot be wrong — a resolver check would still miss a backend that\n * resolves and then throws on import.\n *\n * Memoized, and the promise rather than its value, so concurrent callers\n * share one probe instead of racing several. Nothing installs a package into\n * a running process, so the answer cannot go stale within one; without this\n * `jto_validate` would pay a package import on every call, which is the tool\n * an agent uses after every edit.\n */\n statuses(): Promise<RendererStatus<TId>[]> {\n this.statusCache ??= this.probeStatuses();\n return this.statusCache;\n }\n\n private statusCache?: Promise<RendererStatus<TId>[]>;\n\n private async probeStatuses(): Promise<RendererStatus<TId>[]> {\n return Promise.all(\n this.ids().map(async (id) => {\n const base = { id, default: id === this.defaultId };\n try {\n await this.resolve(id);\n return { ...base, available: true };\n } catch (error) {\n const missing =\n error instanceof Error &&\n error.name === RENDERER_DEPENDENCY_MISSING;\n const pkg =\n missing && error instanceof Error\n ? (error as { packageName?: string }).packageName\n : undefined;\n return {\n ...base,\n available: false,\n reason: error instanceof Error ? error.message : String(error),\n ...(pkg ? { installHint: `pnpm add ${pkg}` } : {}),\n };\n }\n })\n );\n }\n}\n\n/**\n * Turn a bare module-resolution failure into something a user can act on.\n *\n * Optional backends are not installed by default, so the common failure here is\n * a missing package rather than a bug.\n */\nfunction enrichLoadFailure(\n error: unknown,\n format: OfficeFormat,\n rendererId: string\n): Error {\n const message = error instanceof Error ? error.message : String(error);\n const isMissingModule =\n /Cannot find (?:module|package)|ERR_MODULE_NOT_FOUND|Failed to resolve/i.test(\n message\n );\n if (!isMissingModule) {\n return error instanceof Error ? error : new Error(message);\n }\n const pkg = missingPackageName(message);\n const named = pkg ?? `the \"${rendererId}\" backend`;\n const enriched = new Error(\n `The \"${rendererId}\" ${format} renderer requires ${named}, which is not installed. ` +\n `Install it with: pnpm add ${named}\\nOriginal error: ${message}`\n );\n enriched.name = RENDERER_DEPENDENCY_MISSING;\n // Carried separately from the message so a caller can build its own install\n // line rather than parsing one back out of English.\n if (pkg) (enriched as Error & { packageName?: string }).packageName = pkg;\n return enriched;\n}\n\n/**\n * `Error.name` marking a renderer whose optional backend is not installed.\n *\n * A name rather than a subclass: the error crosses a package boundary and an\n * `instanceof` there would depend on both sides loading the same copy of this\n * module, which under a workspace layout is not something to rely on.\n */\nexport const RENDERER_DEPENDENCY_MISSING = 'RendererDependencyMissingError';\n\nfunction missingPackageName(message: string): string | undefined {\n const match =\n /Cannot find (?:module|package) ['\"]([^'\"]+)['\"]/.exec(message) ??\n /Failed to resolve (?:module|import)[: ]+['\"]?([^'\"\\s]+)/.exec(message);\n return match?.[1];\n}\n","/**\n * The half of a native chart `@office-open` does not write.\n *\n * Both `@office-open/docx` and `@office-open/pptx` build their chart XML with\n * the same `chartSpaceDesc` out of `@office-open/core`, and both forward only a\n * subset of `ChartSpaceOptions` from their chart element. Verified against the\n * packages rather than their types, because `ChartOptions extends\n * ChartSpaceOptions` promises far more than either adapter reads. What gets\n * dropped is identical in both formats, and all of it is visible to whoever\n * opens the file:\n *\n * - **No `c:externalData`.** Neither backend writes one, and every `<c:f>`\n * comes out empty, so the chart caches its values with no source for them and\n * \"Edit Data\" fails. This is the exact defect the pptx adapter refused native\n * charts over.\n * - **No series colours.** Neither `ChartSeriesCommon` nor `DataPointOptions`\n * carries a fill, and `colorMappingOverride` is not forwarded, so every\n * series draws in the reader's default palette and ignores the theme.\n * - **No axis titles.** Neither backend writes one: docx drops the `axes`\n * option, and pptx accepts it but cannot be given one without inventing the\n * axis ids its plot area references.\n * - **No legend position**, on docx only — pptx forwards it.\n * - **No grouping.** `ChartSpaceOptions` has no field for it and\n * `chartSpaceDesc` writes `clustered` unconditionally, so a stacked chart\n * came out side by side.\n *\n * So this module writes them, as pure string transforms over the emitted chart\n * part plus the XML of the workbook it points at. Editing another library's\n * serialisation is not free and is chosen deliberately: the alternative is a\n * chart that draws and then fails on the first double-click.\n *\n * Format-neutral on purpose. A `c:chartSpace` is DrawingML, identical in a\n * .docx and a .pptx; only the *packaging* differs — part paths, relationship\n * files, content types, and which ZIP library the core happens to use. Those\n * stay in each core; everything here is shared, which is what keeps the two\n * formats from drifting into two different answers to the same problem.\n *\n * Nothing here touches a ZIP, a clock or a counter, so this package needs no\n * new dependency and the same series always produce the same bytes.\n */\n\n/** The sheet a chart's cell references name. */\nexport const CHART_WORKBOOK_SHEET_NAME = 'Sheet1';\n\n/** The relationship type an embedded workbook is attached by. */\nexport const CHART_PACKAGE_RELATIONSHIP =\n 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/package';\n\n/** The content type of an embedded chart workbook. */\nexport const CHART_WORKBOOK_CONTENT_TYPE =\n 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';\n\n/** One resolved series, as both formats' IR carries it by the time it gets here. */\nexport interface ChartPartSeries {\n name?: string;\n labels: readonly string[];\n values: readonly number[];\n}\n\n/**\n * Everything the splice needs, in neither format's vocabulary.\n *\n * Each core adapts its own IR node to this rather than this module learning\n * about `DocxIrChartRun` and `PptxIrChartElement`, which would make a shared\n * module depend on both cores it exists to serve.\n */\n/**\n * What an authored axis asks for, in neither format's vocabulary.\n *\n * Every field here is one a backend drops: `AxisOptions` cannot be passed to\n * `@office-open` at all — supplying `axes` replaces the default pair and needs\n * `id`/`crossAxisId` values an adapter cannot safely allocate — so an authored\n * axis is applied by rewriting the axis the backend built.\n */\n/** Font family, size, weight and colour on one piece of chart text. */\nexport interface ChartTextStyle {\n fontFamily?: string;\n /** Points. */\n fontSize?: number;\n bold?: boolean;\n /** 6-digit hex, no `#`. */\n color?: string;\n}\n\nexport interface ChartAxisEdits {\n title?: string;\n /** The font of this axis' tick labels. */\n labelFont?: ChartTextStyle;\n /** `c:delete`: an axis hidden entirely. */\n hidden?: boolean;\n /** `false` draws no axis line, leaving its labels. */\n lineVisible?: boolean;\n /** Label rotation, in degrees. */\n labelRotation?: number;\n gridLine?: { style?: string; size?: number; color?: string };\n /** Value-axis bounds; ignored on a category axis, which has no scale. */\n min?: number;\n max?: number;\n majorUnit?: number;\n /** A number format code, e.g. `#,##0`. */\n numberFormat?: string;\n}\n\nexport interface ChartPartInput {\n /** The chart type, in `@office-open`'s spelling. Decides fill vs stroke. */\n chartType: string;\n series: readonly ChartPartSeries[];\n /** Resolved series colours, uppercase 6-digit hex without `#`. May be empty. */\n colors: readonly string[];\n categoryAxis?: ChartAxisEdits;\n valueAxis?: ChartAxisEdits;\n /** Series line width in points. Only meaningful where the series is a line. */\n lineWidthPoints?: number;\n /** An outline on filled data elements: bars, areas and slices. */\n dataBorder?: { widthPoints: number; color: string };\n /** `standard`, `marker` or `filled`; a backend may hardcode the first. */\n radarStyle?: string;\n titleFont?: ChartTextStyle;\n legendFont?: ChartTextStyle;\n dataLabelFont?: ChartTextStyle;\n legendPosition?: string;\n /**\n * `clustered` | `stacked` | `percentStacked`.\n *\n * Spliced rather than passed: `ChartSpaceOptions` has no grouping field at\n * all, and `chartSpaceDesc` writes `clustered` unconditionally. A chart\n * authored as \"% of total\" therefore came out as side-by-side bars summing\n * to nothing — the one dropped option that misrepresents the data rather\n * than restyling it.\n */\n barGrouping?: string;\n}\n\nfunction escapeXml(value: string): string {\n return value\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"');\n}\n\n/* ------------------------------------------------------------------ *\n * The workbook\n * ------------------------------------------------------------------ */\n\n/**\n * A spreadsheet column letter: A, B, … Z, AA, AB, …\n *\n * One-based, because a spreadsheet is. Written out rather than assumed to stay\n * under 26 — a chart with 27 series is unusual, not impossible, and the failure\n * would be a corrupt sheet rather than an error.\n */\nexport function columnLetter(index: number): string {\n let remaining = index;\n let letters = '';\n while (remaining > 0) {\n const rest = (remaining - 1) % 26;\n letters = String.fromCharCode(65 + rest) + letters;\n remaining = Math.floor((remaining - 1) / 26);\n }\n return letters;\n}\n\n/** A number a spreadsheet will accept: finite, never exponential shorthand. */\nfunction cellNumber(value: number): string {\n return Number.isFinite(value) ? String(value) : '0';\n}\n\nfunction inlineStringCell(reference: string, text: string): string {\n return `<c r=\"${reference}\" t=\"inlineStr\"><is><t>${escapeXml(text)}</t></is></c>`;\n}\n\nfunction numberCell(reference: string, value: number): string {\n return `<c r=\"${reference}\"><v>${cellNumber(value)}</v></c>`;\n}\n\n/**\n * The sheet holding the chart's data.\n *\n * Laid out the way every Office chart workbook is, because the chart's own cell\n * references assume it: row 1 is the series names with A1 left blank, column A\n * is the category labels, and the values fill the rectangle between them.\n */\nfunction sheetXml(series: readonly ChartPartSeries[]): string {\n // The category column is as long as the first series' labels; a value column\n // is as long as *that* series' values. They can differ: the pptx compiler\n // accepts a ragged chart (only the docx one refuses it), and writing a zero\n // to square the rectangle would put a data point in the file that the author\n // never wrote — and that the chart's own cached values do not contain.\n const rowCount = Math.max(\n series[0]?.labels.length ?? 0,\n ...series.map((entry) => entry.values.length)\n );\n const lastColumn = columnLetter(series.length + 1);\n const rows: string[] = [];\n\n const header = [\n `<c r=\"A1\"/>`,\n ...series.map((entry, index) =>\n inlineStringCell(\n `${columnLetter(index + 2)}1`,\n entry.name ?? `Series ${index + 1}`\n )\n ),\n ];\n rows.push(`<row r=\"1\">${header.join('')}</row>`);\n\n for (let row = 0; row < rowCount; row++) {\n const reference = row + 2;\n const label = series[0]?.labels[row];\n const cells = [\n ...(label !== undefined\n ? [inlineStringCell(`A${reference}`, label)]\n : []),\n ...series.flatMap((entry, index) =>\n row < entry.values.length\n ? [\n numberCell(\n `${columnLetter(index + 2)}${reference}`,\n entry.values[row]\n ),\n ]\n : []\n ),\n ];\n if (cells.length === 0) continue;\n rows.push(`<row r=\"${reference}\">${cells.join('')}</row>`);\n }\n\n return (\n `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>` +\n `<worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" ` +\n `xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">` +\n `<dimension ref=\"A1:${lastColumn}${Math.max(rowCount + 1, 1)}\"/>` +\n `<sheetData>${rows.join('')}</sheetData>` +\n `</worksheet>`\n );\n}\n\nconst WORKBOOK_XML =\n `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>` +\n `<workbook xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" ` +\n `xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">` +\n `<sheets><sheet name=\"${CHART_WORKBOOK_SHEET_NAME}\" sheetId=\"1\" r:id=\"rId1\"/></sheets>` +\n `</workbook>`;\n\nconst WORKBOOK_RELS =\n `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>` +\n `<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">` +\n `<Relationship Id=\"rId1\" ` +\n `Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet\" ` +\n `Target=\"worksheets/sheet1.xml\"/>` +\n `</Relationships>`;\n\nconst ROOT_RELS =\n `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>` +\n `<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">` +\n `<Relationship Id=\"rId1\" ` +\n `Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument\" ` +\n `Target=\"xl/workbook.xml\"/>` +\n `</Relationships>`;\n\nconst CONTENT_TYPES =\n `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>` +\n `<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\">` +\n `<Default Extension=\"rels\" ContentType=\"application/vnd.openxmlformats-package.relationships+xml\"/>` +\n `<Default Extension=\"xml\" ContentType=\"application/xml\"/>` +\n `<Override PartName=\"/xl/workbook.xml\" ` +\n `ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml\"/>` +\n `<Override PartName=\"/xl/worksheets/sheet1.xml\" ` +\n `ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml\"/>` +\n `</Types>`;\n\n/**\n * The parts of the xlsx a chart's `c:externalData` points at, in ZIP order.\n *\n * Returned as XML rather than as a packaged archive: `core-docx` zips with\n * adm-zip and `core-pptx` with jszip, and a shared module that picked one would\n * force a second ZIP library into whichever core did not use it. Order is fixed\n * rather than incidental, so the central directory is a function of the data.\n *\n * Deliberately minimal — five parts, one sheet, inline strings rather than a\n * shared-string table. A chart workbook is written once and read by one\n * consumer, so the compression a shared-string table buys is not worth a part\n * whose indices are one more thing to keep in step with the cells.\n */\nexport function chartWorkbookParts(\n series: readonly ChartPartSeries[]\n): ReadonlyArray<readonly [path: string, xml: string]> {\n return [\n ['[Content_Types].xml', CONTENT_TYPES],\n ['_rels/.rels', ROOT_RELS],\n ['xl/workbook.xml', WORKBOOK_XML],\n ['xl/_rels/workbook.xml.rels', WORKBOOK_RELS],\n ['xl/worksheets/sheet1.xml', sheetXml(series)],\n ];\n}\n\n/**\n * The cell range one series' values occupy, as a chart reference.\n *\n * The chart XML and the sheet have to agree on this exactly; deriving both from\n * one function is what keeps them from drifting apart.\n */\nexport function seriesValueReference(\n seriesIndex: number,\n pointCount: number\n): string {\n const column = columnLetter(seriesIndex + 2);\n return `${CHART_WORKBOOK_SHEET_NAME}!$${column}$2:$${column}$${pointCount + 1}`;\n}\n\n/** The cell range the category labels occupy. */\nexport function categoryReference(pointCount: number): string {\n return `${CHART_WORKBOOK_SHEET_NAME}!$A$2:$A$${pointCount + 1}`;\n}\n\n/** The single cell holding one series' name. */\nexport function seriesNameReference(seriesIndex: number): string {\n return `${CHART_WORKBOOK_SHEET_NAME}!$${columnLetter(seriesIndex + 2)}$1`;\n}\n\n/** The relationship part attaching one workbook to one chart. */\nexport function chartWorkbookRelsXml(workbookName: string): string {\n return (\n `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>` +\n `<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">` +\n `<Relationship Id=\"rId1\" Type=\"${CHART_PACKAGE_RELATIONSHIP}\" ` +\n `Target=\"../embeddings/${escapeXml(workbookName)}\"/>` +\n `</Relationships>`\n );\n}\n\n/* ------------------------------------------------------------------ *\n * The splice\n * ------------------------------------------------------------------ */\n\n/**\n * Replace each empty `<c:f/>` in one `<c:ser>` with the range it caches.\n *\n * Order is the schema's, not a guess: within a series `c:tx` comes before\n * `c:cat`, which comes before `c:val`, so the three empty formulas appear in\n * that order and are filled in that order.\n */\nfunction fillSeriesFormulas(\n seriesXml: string,\n seriesIndex: number,\n categoryCount: number,\n valueCount: number\n): string {\n // A range whose end row is above its start — `$A$2:$A$1` — is not a range a\n // reader accepts, so a series with no points states no reference at all\n // rather than an impossible one. The chart has nothing to plot either way.\n if (categoryCount === 0 || valueCount === 0) return seriesXml;\n\n const references = [\n seriesNameReference(seriesIndex),\n categoryReference(categoryCount),\n // This series' own length, not the chart's: a range longer than the cells\n // behind it claims data the workbook does not hold, and disagrees with the\n // `c:ptCount` the backend already cached.\n seriesValueReference(seriesIndex, valueCount),\n ];\n let next = 0;\n return seriesXml.replace(/<c:f\\/>/g, () => {\n const reference = references[next++];\n return reference === undefined\n ? '<c:f/>'\n : `<c:f>${escapeXml(reference)}</c:f>`;\n });\n}\n\n/**\n * Chart types whose series colour is a stroke, not a fill.\n *\n * A line series has no area to fill: an `a:solidFill` on one is accepted, drawn\n * nowhere, and the line stays the reader's default colour — which is what a\n * LibreOffice render showed, a blue line under an `accent` palette. The colour\n * has to go on `a:ln`, and on the marker with it, or the points keep the\n * default too.\n */\nconst STROKE_COLORED: ReadonlySet<string> = new Set([\n 'line',\n 'scatter',\n 'radar',\n]);\n\n/**\n * Chart types coloured per data point rather than per series.\n *\n * A pie has one series whose slices are its data points, so a series-level fill\n * paints every slice the same colour and the rest of the palette is never\n * written. PowerPoint's own charts carry one `c:dPt` per slice; so must these,\n * or a themed pie renders monochrome while the same document on the other\n * backend renders normally.\n */\nconst POINT_COLORED: ReadonlySet<string> = new Set(['pie', 'doughnut']);\n\n/** One `c:dPt`, giving slice `index` its own fill. */\nfunction dataPoint(\n index: number,\n hex: string,\n border?: ChartPartInput['dataBorder']\n): string {\n return (\n `<c:dPt><c:idx val=\"${index}\"/><c:bubble3D val=\"0\"/>` +\n `<c:spPr><a:solidFill><a:srgbClr val=\"${hex}\"/></a:solidFill>` +\n (border ? outline(border.widthPoints, border.color) : '') +\n `</c:spPr></c:dPt>`\n );\n}\n\n/** Paint one series, leaving the empty `<c:spPr/>` alone when there is no colour. */\nfunction paintSeries(\n seriesXml: string,\n color: string | undefined,\n chartType: string,\n palette: readonly string[],\n pointCount: number,\n chart: ChartPartInput\n): string {\n const fillFor = (hex: string) =>\n `<a:solidFill><a:srgbClr val=\"${hex.toUpperCase()}\"/></a:solidFill>`;\n\n // `lineSize` and `dataBorder` reach the same `a:ln`, and never at the same\n // time: one is the width of a series that *is* a line, the other an outline\n // on a series that is a filled shape. Verified against pptxgenjs, which on a\n // bar chart writes the border's width and colour and on a line chart writes\n // `lineSize` with the series colour.\n const stroke = STROKE_COLORED.has(chartType);\n const border = stroke ? undefined : chart.dataBorder;\n\n // A pie's colours belong to its slices. CT_PieSer orders `dPt` before\n // `dLbls`, and `dLbls` before `cat` — so anchoring on `c:cat` alone put the\n // slices after the data labels as soon as any were authored.\n if (POINT_COLORED.has(chartType)) {\n if (palette.length === 0 || pointCount === 0) return seriesXml;\n const points = Array.from({ length: pointCount }, (_, index) =>\n dataPoint(index, palette[index % palette.length].toUpperCase(), border)\n ).join('');\n for (const anchor of ['<c:dLbls>', '<c:cat>', '<c:val>']) {\n if (seriesXml.includes(anchor)) {\n return seriesXml.replace(anchor, `${points}${anchor}`);\n }\n }\n return seriesXml;\n }\n\n const parts: string[] = [];\n if (!stroke) {\n if (color) parts.push(fillFor(color));\n if (border) parts.push(outline(border.widthPoints, border.color));\n if (parts.length === 0) return seriesXml;\n return seriesXml.replace('<c:spPr/>', `<c:spPr>${parts.join('')}</c:spPr>`);\n }\n\n if (!color && chart.lineWidthPoints === undefined) return seriesXml;\n\n // `c:marker` follows `c:spPr` in CT_LineSer, and there may be only one of\n // it. The backend writes its own as soon as `lineDataSymbol` or\n // `lineDataSymbolSize` is authored, so adding a second here put two sibling\n // markers in one series — which PowerPoint answers with a repair prompt and\n // LibreOffice drew without a word. Colour the existing one when there is\n // one, and write a whole marker only when there is not.\n const fill = color ? fillFor(color) : '';\n const line = outline(chart.lineWidthPoints, color);\n const painted = seriesXml.replace('<c:spPr/>', `<c:spPr>${line}</c:spPr>`);\n if (!color) return painted;\n\n const markerSpPr = `<c:spPr>${fill}<a:ln>${fill}</a:ln></c:spPr>`;\n const existing = painted.match(/<c:marker>[\\s\\S]*?<\\/c:marker>/);\n if (!existing) {\n return painted.replace(\n `<c:spPr>${line}</c:spPr>`,\n `<c:spPr>${line}</c:spPr><c:marker>${markerSpPr}</c:marker>`\n );\n }\n // CT_Marker orders symbol, size, spPr — so the fill goes last, and only if\n // the backend did not already give the marker one.\n if (existing[0].includes('<c:spPr>')) return painted;\n return painted.replace(\n existing[0],\n existing[0].replace('</c:marker>', `${markerSpPr}</c:marker>`)\n );\n}\n\n/** A `c:title` block holding one line of text, as an axis wants it. */\nfunction axisTitle(text: string): string {\n return (\n `<c:title><c:tx><c:rich><a:bodyPr/><a:lstStyle/><a:p><a:r>` +\n `<a:t>${escapeXml(text)}</a:t>` +\n `</a:r></a:p></c:rich></c:tx><c:overlay val=\"0\"/></c:title>`\n );\n}\n\n/** Points to EMU, the unit a line width is written in. */\nconst POINTS_TO_EMU = 12700;\n\n/** How the authored dash names spell out in DrawingML. */\nconst DASH_STYLES: Readonly<Record<string, string>> = {\n solid: 'solid',\n dash: 'dash',\n dot: 'sysDot',\n};\n\n/** An `a:ln` of a given width, optionally coloured. */\nfunction outline(widthPoints: number | undefined, hex?: string): string {\n const width =\n widthPoints !== undefined\n ? ` w=\"${Math.round(widthPoints * POINTS_TO_EMU)}\"`\n : '';\n const fill = hex\n ? `<a:solidFill><a:srgbClr val=\"${hex.toUpperCase()}\"/></a:solidFill>`\n : '';\n return `<a:ln${width}>${fill}</a:ln>`;\n}\n\n/** `c:majorGridlines`, styled if the author said how. */\nfunction gridLinesElement(\n gridLine: NonNullable<ChartAxisEdits['gridLine']>\n): string {\n if (gridLine.style === 'none') return '';\n const parts: string[] = [];\n if (gridLine.color) {\n parts.push(\n `<a:solidFill><a:srgbClr val=\"${gridLine.color.toUpperCase()}\"/></a:solidFill>`\n );\n }\n const dash = gridLine.style ? DASH_STYLES[gridLine.style] : undefined;\n if (dash) parts.push(`<a:prstDash val=\"${dash}\"/>`);\n if (parts.length === 0 && gridLine.size === undefined) {\n return '<c:majorGridlines/>';\n }\n const width =\n gridLine.size !== undefined\n ? ` w=\"${Math.round(gridLine.size * POINTS_TO_EMU)}\"`\n : '';\n return `<c:majorGridlines><c:spPr><a:ln${width}>${parts.join('')}</a:ln></c:spPr></c:majorGridlines>`;\n}\n\n/**\n * `a:defRPr`, the run properties a piece of chart text defaults to.\n *\n * CT_TextCharacterProperties fixes the child order — fill before `a:latin` —\n * and `sz` is in hundredths of a point, not points.\n */\nfunction defaultRunProperties(font: ChartTextStyle | undefined): string {\n if (!font) return '<a:defRPr/>';\n const attrs =\n (font.fontSize !== undefined\n ? ` sz=\"${Math.round(font.fontSize * 100)}\"`\n : '') + (font.bold !== undefined ? ` b=\"${font.bold ? 1 : 0}\"` : '');\n const children =\n (font.color\n ? `<a:solidFill><a:srgbClr val=\"${font.color.toUpperCase()}\"/></a:solidFill>`\n : '') +\n (font.fontFamily\n ? `<a:latin typeface=\"${escapeXml(font.fontFamily)}\"/>`\n : '');\n return children\n ? `<a:defRPr${attrs}>${children}</a:defRPr>`\n : `<a:defRPr${attrs}/>`;\n}\n\n/** Whether a text style asks for anything at all. */\nfunction hasTextStyle(font: ChartTextStyle | undefined): boolean {\n return !!font && Object.keys(font).length > 0;\n}\n\n/**\n * `c:txPr`, carrying a rotation, a font, or both.\n *\n * Both go in one element: they are two properties of the same text, and an\n * axis that wrote a second `c:txPr` for the font would be a repair prompt\n * rather than a differently-styled label.\n */\nfunction textProperties(\n rotation: number | undefined,\n font: ChartTextStyle | undefined\n): string {\n // `rot` is in 60000ths of a degree, and negative turns clockwise — the same\n // direction the authored value means.\n const bodyPr =\n rotation !== undefined\n ? `<a:bodyPr rot=\"${Math.round(rotation * 60000)}\" spcFirstLastPara=\"1\" vertOverflow=\"ellipsis\" vert=\"horz\" wrap=\"square\" anchorCtr=\"1\"/>`\n : '<a:bodyPr/>';\n return (\n `<c:txPr>${bodyPr}<a:lstStyle/><a:p><a:pPr>` +\n defaultRunProperties(font) +\n `</a:pPr><a:endParaRPr lang=\"en-US\"/></a:p></c:txPr>`\n );\n}\n\n/**\n * Apply an authored axis to the axis the backend built.\n *\n * Rebuilt rather than patched in place, because CT_CatAx and CT_ValAx fix the\n * order of their children and a reader enforces it: `majorGridlines` before\n * `title` before `numFmt` before `spPr` before `txPr`, all of them between\n * `axPos` and `crossAx`. Inserting each edit at its own anchor put whichever\n * landed last in front of the others, which is a repair prompt rather than a\n * mis-drawn axis. Splitting the element at the two fixed points and writing the\n * middle out in order is the only way this stays correct as edits are added.\n *\n * Anything already present that this does not replace is preserved, and that\n * has to be exhaustive rather than best-effort: the region is replaced\n * wholesale, so a child this function does not capture is a child it deletes.\n * The two backends emit different amounts, so the same rewrite has to be safe\n * on both.\n */\nfunction rewriteAxis(axisXml: string, edits: ChartAxisEdits): string {\n const axPos = axisXml.match(/<c:axPos[^>]*\\/>/);\n const crossAxAt = axisXml.indexOf('<c:crossAx');\n if (!axPos || crossAxAt < 0) return axisXml;\n\n const headEnd = axisXml.indexOf(axPos[0]) + axPos[0].length;\n let head = axisXml.slice(0, headEnd);\n const middle = axisXml.slice(headEnd, crossAxAt);\n let tail = axisXml.slice(crossAxAt);\n\n // `c:delete` and `c:scaling` both live in the head, in that fixed order.\n if (edits.hidden !== undefined) {\n head = head.replace(\n /<c:delete val=\"[^\"]*\"\\/>/,\n `<c:delete val=\"${edits.hidden ? 1 : 0}\"/>`\n );\n }\n if (edits.max !== undefined || edits.min !== undefined) {\n // CT_Scaling orders logBase, orientation, max, min.\n const bounds =\n (edits.max !== undefined ? `<c:max val=\"${edits.max}\"/>` : '') +\n (edits.min !== undefined ? `<c:min val=\"${edits.min}\"/>` : '');\n head = head.replace('</c:scaling>', `${bounds}</c:scaling>`);\n }\n\n // Rebuild the middle in schema order, keeping what was already there.\n //\n // Every child CT_CatAx and CT_ValAx allow in this region is captured, not\n // just the ones this function writes: the region is *replaced*, so a child\n // left out is a child deleted. Authoring a title alone used to drop the\n // backend's gridlines, tick marks and tick-label position on the floor.\n const existingMajorGrid = middle.match(\n /<c:majorGridlines(?:\\/>|>[\\s\\S]*?<\\/c:majorGridlines>)/\n )?.[0];\n const existingMinorGrid = middle.match(\n /<c:minorGridlines(?:\\/>|>[\\s\\S]*?<\\/c:minorGridlines>)/\n )?.[0];\n const existingTitle = middle.match(/<c:title>[\\s\\S]*?<\\/c:title>/)?.[0];\n const existingNumFmt = middle.match(/<c:numFmt[^>]*\\/>/)?.[0];\n const existingMajorTick = middle.match(/<c:majorTickMark[^>]*\\/>/)?.[0];\n const existingMinorTick = middle.match(/<c:minorTickMark[^>]*\\/>/)?.[0];\n const existingTickLblPos = middle.match(/<c:tickLblPos[^>]*\\/>/)?.[0];\n const existingSpPr = middle.match(/<c:spPr>[\\s\\S]*?<\\/c:spPr>/)?.[0];\n const existingTxPr = middle.match(/<c:txPr>[\\s\\S]*?<\\/c:txPr>/)?.[0];\n\n const rebuilt = [\n edits.gridLine ? gridLinesElement(edits.gridLine) : existingMajorGrid ?? '',\n existingMinorGrid ?? '',\n // An axis that already carries a title keeps it: writing a second one is a\n // repair prompt, not a duplicated label.\n existingTitle ?? (edits.title ? axisTitle(edits.title) : ''),\n edits.numberFormat !== undefined\n ? `<c:numFmt formatCode=\"${escapeXml(edits.numberFormat)}\" sourceLinked=\"0\"/>`\n : existingNumFmt ?? '',\n existingMajorTick ?? '',\n existingMinorTick ?? '',\n existingTickLblPos ?? '',\n edits.lineVisible === false\n ? '<c:spPr><a:ln><a:noFill/></a:ln></c:spPr>'\n : existingSpPr ?? '',\n edits.labelRotation !== undefined || hasTextStyle(edits.labelFont)\n ? textProperties(edits.labelRotation, edits.labelFont)\n : existingTxPr ?? '',\n ].join('');\n\n // `majorUnit` follows crossAx/crosses/crossBetween in CT_ValAx.\n if (edits.majorUnit !== undefined && !tail.includes('<c:majorUnit')) {\n const crosses = tail.match(/<c:cross(?:es|esAt|Between)[^>]*\\/>/g);\n const anchor = crosses?.[crosses.length - 1];\n if (anchor) {\n const at = tail.lastIndexOf(anchor) + anchor.length;\n tail =\n tail.slice(0, at) +\n `<c:majorUnit val=\"${edits.majorUnit}\"/>` +\n tail.slice(at);\n }\n }\n\n return head + rebuilt + tail;\n}\n\n/**\n * Apply an authored axis to the Nth element with this tag.\n *\n * `occurrence` exists for scatter, whose axes are both `c:valAx`.\n */\nfunction editAxis(\n chartXml: string,\n tag: string,\n edits: ChartAxisEdits | undefined,\n occurrence = 0\n): string {\n if (!edits || Object.keys(edits).length === 0) return chartXml;\n const open = `<c:${tag}>`;\n let start = -1;\n for (let seen = 0; seen <= occurrence; seen++) {\n start = chartXml.indexOf(open, start + 1);\n if (start < 0) return chartXml;\n }\n const end = chartXml.indexOf(`</c:${tag}>`, start);\n if (end < 0) return chartXml;\n\n return (\n chartXml.slice(0, start) +\n rewriteAxis(chartXml.slice(start, end), edits) +\n chartXml.slice(end)\n );\n}\n\n/**\n * Say explicitly that colours do not vary by data point.\n *\n * `c:varyColors` is a `CT_Boolean`, so an *absent* one means **true** — the\n * same default that made `<c:showVal/>` alone switch on every other data label.\n * `@office-open` writes it for `ofPie` and for nothing else, so every other\n * chart inherited \"vary by point\". On a chart with one series that is plainly\n * visible: PowerPoint colours each point separately and gives the legend one\n * entry per *category*, so a line chart of four quarters had a legend reading\n * `Q1 Q2 Q3 Q4` instead of its series name. A chart with two or more series\n * hides the problem, because the setting only applies to single-series charts —\n * which is why the bar chart beside it looked right.\n *\n * A pie is the exception the default was written for: its slices *should* vary,\n * and the per-point colours written into `c:dPt` agree with that.\n *\n * `varyColors` is the last child before `c:ser` in every plot type that has it,\n * so the first `c:ser` is the anchor.\n */\nfunction setVaryColors(chartXml: string, chartType: string): string {\n if (POINT_COLORED.has(chartType)) return chartXml;\n if (chartXml.includes('<c:varyColors')) return chartXml;\n return chartXml.replace('<c:ser>', '<c:varyColors val=\"0\"/><c:ser>');\n}\n\n/**\n * Set the grouping, and the overlap that has to go with it.\n *\n * `c:grouping` is written `clustered` unconditionally by the backend, and\n * stacked bars that do not overlap are drawn side by side — they look clustered\n * whatever the grouping says. So `c:overlap val=\"100\"` goes with it.\n *\n * The two cannot be written together, though, and doing so produced invalid\n * XML three ways. CT_BarChart fixes the order as `barDir`, `grouping`,\n * `varyColors`, `ser`, `dLbls`, `gapWidth`, `overlap`, `serLines`, `axId`, so an\n * overlap written beside the grouping lands before `c:ser`. `c:grouping` is\n * also a child of `c:lineChart` and `c:areaChart`, neither of which allows\n * `c:overlap` at all. And an author who set `barOverlapPct` already has one\n * from the backend, in the right place — a second is a duplicate. Word and\n * PowerPoint answer all three with a repair prompt; LibreOffice drew them\n * without complaint, which is why the tests did not notice.\n */\nfunction setBarGrouping(chartXml: string, grouping: string): string {\n const start = chartXml.indexOf('<c:barChart>');\n // Only a bar chart has an overlap. A line or area chart takes the grouping\n // and nothing else.\n if (start < 0) {\n return chartXml.replace(\n /<c:grouping val=\"[^\"]*\"\\/>/,\n `<c:grouping val=\"${escapeXml(grouping)}\"/>`\n );\n }\n const end = chartXml.indexOf('</c:barChart>', start);\n if (end < 0) return chartXml;\n\n let plot = chartXml\n .slice(start, end)\n .replace(\n /<c:grouping val=\"[^\"]*\"\\/>/,\n `<c:grouping val=\"${escapeXml(grouping)}\"/>`\n );\n\n if (!plot.includes('<c:overlap')) {\n // After `c:gapWidth` when the author set one, otherwise immediately before\n // the axis ids that close the plot — both are the same legal slot.\n const gapWidth = plot.match(/<c:gapWidth val=\"[^\"]*\"\\/>/)?.[0];\n if (gapWidth) {\n const at = plot.indexOf(gapWidth) + gapWidth.length;\n plot = plot.slice(0, at) + '<c:overlap val=\"100\"/>' + plot.slice(at);\n } else {\n const axId = plot.indexOf('<c:axId');\n if (axId >= 0) {\n plot =\n plot.slice(0, axId) + '<c:overlap val=\"100\"/>' + plot.slice(axId);\n }\n }\n }\n\n return chartXml.slice(0, start) + plot + chartXml.slice(end);\n}\n\n/**\n * Style the chart's own title.\n *\n * Scoped to the region before `c:plotArea`, because an axis title is a\n * `c:title` too and styling the first one found would put the chart title's\n * font on an axis whenever the chart had no title of its own.\n */\nfunction styleChartTitle(\n chartXml: string,\n font: ChartTextStyle | undefined\n): string {\n if (!hasTextStyle(font)) return chartXml;\n const plotAreaAt = chartXml.indexOf('<c:plotArea>');\n if (plotAreaAt < 0) return chartXml;\n const head = chartXml.slice(0, plotAreaAt);\n if (!head.includes('<c:title>')) return chartXml;\n\n // `a:pPr` precedes the runs it sets defaults for.\n const styled = head.replace(\n '<a:p><a:r>',\n `<a:p><a:pPr>${defaultRunProperties(font)}</a:pPr><a:r>`\n );\n return styled + chartXml.slice(plotAreaAt);\n}\n\n/**\n * Style the legend, whose `c:txPr` the backend already writes.\n *\n * Filling in the empty `a:defRPr` it leaves rather than adding a second\n * `c:txPr`, which a reader offers to repair.\n */\nfunction styleLegend(\n chartXml: string,\n font: ChartTextStyle | undefined\n): string {\n if (!hasTextStyle(font)) return chartXml;\n const start = chartXml.indexOf('<c:legend>');\n if (start < 0) return chartXml;\n const end = chartXml.indexOf('</c:legend>', start);\n if (end < 0) return chartXml;\n\n const legend = chartXml\n .slice(start, end)\n .replace('<a:defRPr/>', defaultRunProperties(font));\n return chartXml.slice(0, start) + legend + chartXml.slice(end);\n}\n\n/**\n * Style every series' data labels.\n *\n * CT_DLbls orders `numFmt`, `spPr`, `txPr`, `dLblPos` and only then the `show*`\n * flags, so the text properties go immediately after the opening tag — which is\n * also before the `c:dLblPos` the backend writes first.\n */\nfunction styleDataLabels(\n chartXml: string,\n font: ChartTextStyle | undefined\n): string {\n if (!hasTextStyle(font)) return chartXml;\n return chartXml.replace(\n /<c:dLbls>(?!<c:txPr>)/g,\n `<c:dLbls>${textProperties(undefined, font)}`\n );\n}\n\n/**\n * Rewrite one emitted `chartN.xml` with everything the backend omitted.\n *\n * Every repair is guarded on what the XML actually lacks, because the two\n * backends omit different amounts. `@office-open/pptx` hands its whole options\n * object to `chartSpaceDesc`, so the legend position survives;\n * `@office-open/docx` forwards eight named fields and loses it. Everything else\n * here — the cell references behind `<c:f/>`, the series fill, the axis titles,\n * the grouping and `c:externalData` — is missing from both.\n *\n * Detecting rather than assuming is also what keeps this honest if a backend\n * starts emitting more: the repair simply stops firing, instead of writing a\n * second copy of an element a reader would offer to repair.\n *\n * `relationshipId` names the workbook relationship in the chart part's own\n * rels file, which each core writes alongside.\n */\nexport function spliceChartXml(\n chartXml: string,\n chart: ChartPartInput,\n relationshipId = 'rId1'\n): string {\n const pointCount = chart.series[0]?.labels.length ?? 0;\n\n // Walk the series in document order so the Nth `<c:ser>` gets the Nth\n // series' references and colour. A regex over the whole part would fill the\n // formulas of every series from the first one's ranges.\n let seriesIndex = 0;\n let result = chartXml.replace(/<c:ser>[\\s\\S]*?<\\/c:ser>/g, (seriesXml) => {\n const index = seriesIndex++;\n const withFormulas = fillSeriesFormulas(\n seriesXml,\n index,\n pointCount,\n chart.series[index]?.values.length ?? pointCount\n );\n // A palette shorter than the series list wraps, exactly as the implicit\n // theme palette does everywhere else in the project.\n const color =\n chart.colors.length > 0\n ? chart.colors[index % chart.colors.length]\n : undefined;\n return paintSeries(\n withFormulas,\n color,\n chart.chartType,\n chart.colors,\n pointCount,\n chart\n );\n });\n\n // A scatter chart has no category axis: both of its axes are `c:valAx`, X\n // first. Titling by tag alone dropped the category title and put the value\n // title on X — a mislabelled chart rather than an invalid one, so nothing\n // complained.\n if (chart.chartType === 'scatter') {\n result = editAxis(result, 'valAx', chart.categoryAxis, 0);\n result = editAxis(result, 'valAx', chart.valueAxis, 1);\n } else {\n result = editAxis(result, 'catAx', chart.categoryAxis);\n result = editAxis(result, 'valAx', chart.valueAxis);\n }\n\n result = styleChartTitle(result, chart.titleFont);\n result = styleLegend(result, chart.legendFont);\n result = styleDataLabels(result, chart.dataLabelFont);\n\n // `chartSpaceDesc` writes `<c:radarStyle val=\"standard\"/>` from a literal —\n // there is no option behind it at all, so `marker` and `filled` had nowhere\n // to go and became `standard` without a word.\n if (chart.radarStyle) {\n result = result.replace(\n /<c:radarStyle val=\"[^\"]*\"\\/>/,\n `<c:radarStyle val=\"${escapeXml(chart.radarStyle)}\"/>`\n );\n }\n\n // `legendPosition` is not among the fields either backend forwards, so every\n // legend came out at the default whatever the author asked for.\n if (chart.legendPosition) {\n result = result.replace(\n /<c:legendPos val=\"[^\"]*\"\\/>/,\n `<c:legendPos val=\"${escapeXml(chart.legendPosition)}\"/>`\n );\n }\n\n if (chart.barGrouping && chart.barGrouping !== 'clustered') {\n result = setBarGrouping(result, chart.barGrouping);\n }\n\n result = setVaryColors(result, chart.chartType);\n\n // `c:externalData` is the last child of `c:chartSpace`: after `c:chart`,\n // `c:spPr` and `c:txPr`, before nothing. Only written when the backend did\n // not — pptx forwards it, docx drops it.\n if (result.includes('<c:externalData')) return result;\n return result.replace(\n '</c:chartSpace>',\n `<c:externalData r:id=\"${escapeXml(relationshipId)}\">` +\n `<c:autoUpdate val=\"0\"/></c:externalData></c:chartSpace>`\n );\n}\n\n/* ------------------------------------------------------------------ *\n * Matching parts to the nodes they came from\n * ------------------------------------------------------------------ */\n\n/**\n * Every `<c:v>` in a chart part, per series, in document order.\n *\n * The identity of a chart part, for the purpose of matching it to the IR node\n * it came from. Position cannot do that job: an emitter fills its array while\n * *building* the backend's options object, and the backend numbers its parts\n * while *stringifying* that object, and the two walks disagree the moment a\n * chart sits somewhere other than the main body — a docx header or footer, a\n * pptx master or layout. Pairing by position handed charts another chart's\n * workbook, so a recipient choosing \"Edit Data\" saw a different chart's\n * numbers.\n *\n * Content is stable under either walk. A `<c:v>` holds a series name, a\n * category label or a cached value, all of which came from the IR node and none\n * of which the splice has written yet.\n */\n// Control characters, so a signature cannot be forged by a label that happens\n// to contain the separator: joining on '' would make ['ab','c'] and ['a','bc']\n// the same chart, and the wrong workbook would follow.\nconst VALUE_SEPARATOR = '\\u0001';\nconst SERIES_SEPARATOR = '\\u0002';\n\nconst NAMED_ENTITIES: Readonly<Record<string, string>> = {\n amp: '&',\n lt: '<',\n gt: '>',\n quot: '\"',\n apos: \"'\",\n};\n\n/**\n * Undo the backend's XML escaping, so a part's text compares against the IR's.\n *\n * The part signature is read out of `<c:v>` elements, where `&`, `<`, `>`, `\"`\n * and `'` have all been escaped; the input signature is built from the raw IR\n * strings. Comparing the two directly made every chart whose series name or\n * category label contained one of those characters fail to match — and an\n * unmatched part was skipped, so the chart shipped with no workbook, no\n * `c:externalData` and empty `<c:f/>` references, with nothing said about it.\n *\n * Decoding rather than escaping, because this side has to undo whatever the\n * backend did: it emits `'`, which the escaper here does not produce, so\n * escaping the other side would leave the same mismatch one character over.\n */\nfunction decodeXmlEntities(value: string): string {\n return value.replace(\n /&(#x[0-9a-fA-F]+|#[0-9]+|[a-zA-Z]+);/g,\n (match, body: string) => {\n if (body.startsWith('#x') || body.startsWith('#X')) {\n return String.fromCodePoint(Number.parseInt(body.slice(2), 16));\n }\n if (body.startsWith('#')) {\n return String.fromCodePoint(Number.parseInt(body.slice(1), 10));\n }\n return NAMED_ENTITIES[body] ?? match;\n }\n );\n}\n\nexport function chartPartSignature(chartXml: string): string {\n return (chartXml.match(/<c:ser>[\\s\\S]*?<\\/c:ser>/g) ?? [])\n .map((series) =>\n [...series.matchAll(/<c:v>([\\s\\S]*?)<\\/c:v>/g)]\n .map((match) => decodeXmlEntities(match[1]))\n .join(VALUE_SEPARATOR)\n )\n .join(SERIES_SEPARATOR);\n}\n\n/** The same signature, computed from the IR node the part was emitted from. */\nexport function chartInputSignature(chart: ChartPartInput): string {\n const categories = chart.series[0]?.labels ?? [];\n return chart.series\n .map((series, index) =>\n [\n series.name ?? `Series ${index + 1}`,\n ...categories,\n ...series.values.map((value) => String(value)),\n ].join(VALUE_SEPARATOR)\n )\n .join(SERIES_SEPARATOR);\n}\n\n/**\n * Pair emitted chart parts with the inputs they came from, by content.\n *\n * Returns one entry per part that matched, in part order. A part with no match\n * is left out rather than guessed at: the package holds a chart this pass did\n * not emit, and repairing it from the wrong node is exactly the defect the\n * matching exists to prevent. Two charts identical in every cached value match\n * interchangeably, which is harmless — identical data yields an identical\n * workbook, and an author who wrote two identical charts did not distinguish\n * their palettes either.\n */\nexport function matchChartParts<T extends ChartPartInput>(\n parts: ReadonlyArray<readonly [ordinal: number, xml: string]>,\n charts: readonly T[]\n): Array<{ ordinal: number; xml: string; chart: T }> {\n const unmatched = new Set(charts.keys());\n const matched: Array<{ ordinal: number; xml: string; chart: T }> = [];\n\n for (const [ordinal, xml] of parts) {\n const signature = chartPartSignature(xml);\n const index = [...unmatched].find(\n (candidate) => chartInputSignature(charts[candidate]) === signature\n );\n if (index === undefined) continue;\n unmatched.delete(index);\n matched.push({ ordinal, xml, chart: charts[index] });\n }\n\n // A chart the emitter produced but no part matched would ship with no\n // workbook, no `c:externalData` and empty `<c:f/>` references — a chart that\n // draws and then fails on the first double-click, which is the exact defect\n // this whole pass exists to prevent. Silence made an escaping mismatch look\n // like a working document, so an unmatched chart is loud.\n if (unmatched.size > 0) {\n const names = [...unmatched]\n .map((index) => charts[index].series[0]?.name ?? `chart ${index + 1}`)\n .join(', ');\n throw new Error(\n `Could not match ${unmatched.size} chart(s) to an emitted chart part ` +\n `(${names}). The package would ship a chart without its workbook.`\n );\n }\n\n return matched;\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export { C as ComponentDefinition, a as ComponentSchemaConfig, c as convertToJso
|
|
|
2
2
|
export { AddWarningFunction, GenerationWarning } from './types/warnings.js';
|
|
3
3
|
import { F as FontRegistryEntry, c as FontRuntimeOpts, R as ResolvedFontSource, b as ResolvedFont } from './types-kcQwhOlf.js';
|
|
4
4
|
export { D as DEFAULT_VISUAL_DPI, d as FontFamilyNameSchema, e as FontRegistryDefinition, f as FontRegistryEntrySchema, g as FontRegistrySchema, h as FontSource, i as FontSourceSchema, H as HighchartsHeaders, j as HighchartsHeadersResolver, k as HighchartsServiceConfig, M as MAX_RASTERIZE_BATCH_SLIDES, l as MAX_RASTERIZE_FONTS, m as MAX_RASTERIZE_FONT_BYTES, n as MAX_VISUAL_DPI, o as MIN_VISUAL_DPI, P as PptxBatchRasterizer, p as PptxRasterizeBatchRequest, q as PptxRasterizeBatchResult, r as PptxRasterizeBatchSlide, s as PptxRasterizeBatchSlideResult, t as PptxRasterizeFailureStage, u as PptxRasterizeRequest, v as PptxRasterizeResult, w as PptxRasterizer, x as PptxServiceConfig, y as PptxServiceHeaders, z as PptxServiceHeadersResolver, a as RasterizeFontFace, S as SAFE_FONTS, A as SafeFontName, B as ServicesConfig, C as clampVisualDpi, E as isSafeFont } from './types-kcQwhOlf.js';
|
|
5
|
-
export { F as FeatureRequirement, a as FeatureRequirementCollector, O as OfficeFormat, b as OfficeRenderer, R as
|
|
5
|
+
export { F as FeatureRequirement, a as FeatureRequirementCollector, O as OfficeFormat, b as OfficeRenderer, R as RENDERER_DEPENDENCY_MISSING, c as RenderOptions, d as RendererDiagnostic, e as RendererDiagnosticSeverity, f as RendererRegistry, g as RendererStatus, h as UnsupportedRendererFeatureError, i as UnsupportedRendererFeatureErrorInit, j as assertNever, k as assertRendererSupports, l as diagnoseUnsupportedFeatures, p as partitionDiagnostics, r as rendererError, m as rendererWarning } from './capabilities-DtPF3aBj.js';
|
|
6
6
|
export { DEFAULT_ERROR_CONFIG, ERROR_EMOJIS, ErrorFormatterConfig, calculatePosition, clearComponentNamesCache, createErrorConfig, createJsonParseError, extractStandardComponentNames, formatErrorMessage, formatErrorSummary, getLiteralValue, getObjectSchemaPropertyNames, getSchemaMetadata, groupErrorsByPath, isLiteralSchema, isObjectSchema, isUnionSchema, transformValueError, transformValueErrors } from './validation/unified/index.js';
|
|
7
7
|
export { T as TransformedError, V as ValidationError, a as ValidationResult } from './types-BWFZ7OaO.js';
|
|
8
8
|
export { ComponentValidationError, ComponentValidationResult, ComponentVersion, ComponentVersionMap, CustomComponent, DuplicateComponentError, PluginValidationOptions, PluginValidationResult, RenderContext, RenderFunction, UnknownPreservedComponentError, createComponent, createVersion, getValidationSummary, isValidationSuccess, resolveComponentVersion, validateCustomComponentProps } from './plugin/index.js';
|
package/dist/index.js
CHANGED
|
@@ -34,17 +34,6 @@ import {
|
|
|
34
34
|
transformValueError,
|
|
35
35
|
transformValueErrors
|
|
36
36
|
} from "./chunk-ZKD5BAMU.js";
|
|
37
|
-
import {
|
|
38
|
-
FeatureRequirementCollector,
|
|
39
|
-
RendererRegistry,
|
|
40
|
-
UnsupportedRendererFeatureError,
|
|
41
|
-
assertNever,
|
|
42
|
-
assertRendererSupports,
|
|
43
|
-
diagnoseUnsupportedFeatures,
|
|
44
|
-
partitionDiagnostics,
|
|
45
|
-
rendererError,
|
|
46
|
-
rendererWarning
|
|
47
|
-
} from "./chunk-BJNBJSQG.js";
|
|
48
37
|
import {
|
|
49
38
|
convertToJsonSchema,
|
|
50
39
|
createComponentSchema,
|
|
@@ -62,6 +51,18 @@ import {
|
|
|
62
51
|
SAFE_FONTS,
|
|
63
52
|
isSafeFont
|
|
64
53
|
} from "./chunk-6KUQYVPT.js";
|
|
54
|
+
import {
|
|
55
|
+
FeatureRequirementCollector,
|
|
56
|
+
RENDERER_DEPENDENCY_MISSING,
|
|
57
|
+
RendererRegistry,
|
|
58
|
+
UnsupportedRendererFeatureError,
|
|
59
|
+
assertNever,
|
|
60
|
+
assertRendererSupports,
|
|
61
|
+
diagnoseUnsupportedFeatures,
|
|
62
|
+
partitionDiagnostics,
|
|
63
|
+
rendererError,
|
|
64
|
+
rendererWarning
|
|
65
|
+
} from "./chunk-QLZNOXT5.js";
|
|
65
66
|
import {
|
|
66
67
|
compareSemver,
|
|
67
68
|
isValidSemver,
|
|
@@ -1493,6 +1494,7 @@ export {
|
|
|
1493
1494
|
MAX_VISUAL_DPI,
|
|
1494
1495
|
MIN_VISUAL_DPI,
|
|
1495
1496
|
POPULAR_GOOGLE_FONTS,
|
|
1497
|
+
RENDERER_DEPENDENCY_MISSING,
|
|
1496
1498
|
RendererRegistry,
|
|
1497
1499
|
SAFE_FONTS,
|
|
1498
1500
|
UPSTREAM_OVERRIDES,
|