@json-to-office/core-pptx 0.20.0 → 0.21.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.
@@ -6,13 +6,22 @@
6
6
  import PptxGenJS from 'pptxgenjs';
7
7
  import type { PresentationComponentDefinition, PptxThemeConfig, PipelineWarning } from '../types';
8
8
  import type { ServicesConfig, FontRuntimeOpts } from '@json-to-office/shared';
9
+ import { type ValidationError } from '@json-to-office/shared-pptx';
10
+ import { type PresentationPackagingOptions } from './packagePresentation';
11
+ export interface GenerationValidationOptions {
12
+ /** Validate the complete component tree before rendering. Defaults to true. */
13
+ enabled?: boolean;
14
+ /** Accept unknown props while still enforcing required fields and types. */
15
+ allowUnknownFields?: boolean;
16
+ }
9
17
  /**
10
18
  * Options for the generation pipeline
11
19
  */
12
- export interface GenerationOptions {
20
+ export interface GenerationOptions extends PresentationPackagingOptions {
13
21
  customThemes?: Record<string, PptxThemeConfig>;
14
22
  services?: ServicesConfig;
15
23
  fonts?: FontRuntimeOpts;
24
+ validation?: GenerationValidationOptions;
16
25
  }
17
26
  /**
18
27
  * Result from generateBufferWithWarnings
@@ -21,6 +30,11 @@ export interface GenerationResult {
21
30
  buffer: Buffer;
22
31
  warnings: PipelineWarning[];
23
32
  }
33
+ /** Error thrown when a presentation fails the generation validation gate. */
34
+ export declare class PresentationValidationError extends Error {
35
+ readonly errors: ValidationError[];
36
+ constructor(errors: ValidationError[]);
37
+ }
24
38
  /**
25
39
  * Type guard for presentation component
26
40
  */
@@ -44,11 +58,11 @@ export declare function generateAndSaveFromJson(jsonConfig: string | Presentatio
44
58
  /**
45
59
  * Generate from a JSON file path
46
60
  */
47
- export declare function generateFromFile(filePath: string, outputPath: string): Promise<void>;
61
+ export declare function generateFromFile(filePath: string, outputPath: string, options?: GenerationOptions): Promise<void>;
48
62
  /**
49
63
  * Save a PptxGenJS instance to file
50
64
  */
51
- export declare function savePresentation(pptx: PptxGenJS, outputPath: string): Promise<void>;
65
+ export declare function savePresentation(pptx: PptxGenJS, outputPath: string, options?: PresentationPackagingOptions): Promise<void>;
52
66
  /**
53
67
  * Export the main API
54
68
  */
@@ -1 +1 @@
1
- {"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/core/generator.ts"],"names":[],"mappings":"AAAA;;;GAGG;;AAEH,OAAO,SAAS,MAAM,WAAW,CAAC;AAGlC,OAAO,KAAK,EACV,+BAA+B,EAC/B,eAAe,EACf,eAAe,EAChB,MAAM,UAAU,CAAC;AAElB,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAQ9E;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC/C,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,KAAK,CAAC,EAAE,eAAe,CAAC;CACzB;AAID;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,eAAe,EAAE,CAAC;CAC7B;AAED;;GAEG;AACH,wBAAgB,iCAAiC,CAC/C,UAAU,EAAE,OAAO,GAClB,UAAU,IAAI,+BAA+B,CAI/C;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,+BAA+B,EACzC,OAAO,CAAC,EAAE,iBAAiB,EAC3B,QAAQ,CAAC,EAAE,eAAe,EAAE,GAC3B,OAAO,CAAC,SAAS,CAAC,CAoBpB;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CAC1C,UAAU,EAAE,MAAM,GAAG,+BAA+B,EACpD,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,MAAM,CAAC,CAGjB;AAED;;GAEG;AACH,wBAAsB,0BAA0B,CAC9C,UAAU,EAAE,MAAM,GAAG,+BAA+B,EACpD,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,gBAAgB,CAAC,CA4F3B;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,UAAU,EAAE,MAAM,GAAG,+BAA+B,EACpD,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAGf;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAIf;AAyBD;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,SAAS,EACf,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAIf;AAED;;GAEG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;CAQjC,CAAC"}
1
+ {"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/core/generator.ts"],"names":[],"mappings":"AAAA;;;GAGG;;AAEH,OAAO,SAAS,MAAM,WAAW,CAAC;AAElC,OAAO,KAAK,EACV,+BAA+B,EAC/B,eAAe,EACf,eAAe,EAChB,MAAM,UAAU,CAAC;AAElB,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAM9E,OAAO,EAIL,KAAK,eAAe,EACrB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAEL,KAAK,4BAA4B,EAClC,MAAM,uBAAuB,CAAC;AAE/B,MAAM,WAAW,2BAA2B;IAC1C,+EAA+E;IAC/E,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,4EAA4E;IAC5E,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED;;GAEG;AACH,MAAM,WAAW,iBAAkB,SAAQ,4BAA4B;IACrE,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC/C,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,UAAU,CAAC,EAAE,2BAA2B,CAAC;CAC1C;AAID;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,eAAe,EAAE,CAAC;CAC7B;AAED,6EAA6E;AAC7E,qBAAa,2BAA4B,SAAQ,KAAK;IACpD,SAAgB,MAAM,EAAE,eAAe,EAAE,CAAC;gBAE9B,MAAM,EAAE,eAAe,EAAE;CAStC;AAqBD;;GAEG;AACH,wBAAgB,iCAAiC,CAC/C,UAAU,EAAE,OAAO,GAClB,UAAU,IAAI,+BAA+B,CAI/C;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,+BAA+B,EACzC,OAAO,CAAC,EAAE,iBAAiB,EAC3B,QAAQ,CAAC,EAAE,eAAe,EAAE,GAC3B,OAAO,CAAC,SAAS,CAAC,CAsBpB;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CAC1C,UAAU,EAAE,MAAM,GAAG,+BAA+B,EACpD,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,MAAM,CAAC,CAGjB;AAED;;GAEG;AACH,wBAAsB,0BAA0B,CAC9C,UAAU,EAAE,MAAM,GAAG,+BAA+B,EACpD,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,gBAAgB,CAAC,CA8F3B;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,UAAU,EAAE,MAAM,GAAG,+BAA+B,EACpD,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAGf;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAIf;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,SAAS,EACf,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,4BAA4B,GACrC,OAAO,CAAC,IAAI,CAAC,CAIf;AAED;;GAEG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;CAQjC,CAAC"}
@@ -0,0 +1,17 @@
1
+ /// <reference types="node" />
2
+ /** Stable default shared by package entries and OOXML core metadata. */
3
+ export declare const DEFAULT_GENERATED_AT = "2000-01-01T00:00:00.000Z";
4
+ export interface PresentationPackagingOptions {
5
+ /** Normalize metadata and ZIP timestamps. Defaults to true. */
6
+ deterministic?: boolean;
7
+ /** Clock used when deterministic packaging is enabled. */
8
+ generatedAt?: Date | string;
9
+ }
10
+ /**
11
+ * Apply post-generation OOXML fixes and deterministic package metadata.
12
+ *
13
+ * PptxGenJS stamps both core.xml and ZIP entries with the wall clock. Rewriting
14
+ * both layers makes equivalent inputs byte-identical across invocations.
15
+ */
16
+ export declare function packagePresentationBuffer(buffer: Buffer, options?: PresentationPackagingOptions): Promise<Buffer>;
17
+ //# sourceMappingURL=packagePresentation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"packagePresentation.d.ts","sourceRoot":"","sources":["../../src/core/packagePresentation.ts"],"names":[],"mappings":";AAKA,wEAAwE;AACxE,eAAO,MAAM,oBAAoB,6BAA6B,CAAC;AAE/D,MAAM,WAAW,4BAA4B;IAC3C,+DAA+D;IAC/D,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,0DAA0D;IAC1D,WAAW,CAAC,EAAE,IAAI,GAAG,MAAM,CAAC;CAC7B;AA2ID;;;;;GAKG;AACH,wBAAsB,yBAAyB,CAC7C,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,4BAAiC,GACzC,OAAO,CAAC,MAAM,CAAC,CAuBjB"}
package/dist/index.d.ts CHANGED
@@ -1,12 +1,14 @@
1
1
  export declare function getPptxCoreVersion(): string;
2
- export { generatePresentation, generateBufferFromJson, generateBufferWithWarnings, generateAndSaveFromJson, generateFromFile, savePresentation, isPresentationComponentDefinition, PresentationGenerator, } from './core/generator';
3
- export type { GenerationOptions, GenerationResult } from './core/generator';
2
+ export { generatePresentation, generateBufferFromJson, generateBufferWithWarnings, generateAndSaveFromJson, generateFromFile, savePresentation, isPresentationComponentDefinition, PresentationValidationError, PresentationGenerator, } from './core/generator';
3
+ export type { GenerationOptions, GenerationResult, GenerationValidationOptions, } from './core/generator';
4
+ export { DEFAULT_GENERATED_AT, packagePresentationBuffer, } from './core/packagePresentation';
5
+ export type { PresentationPackagingOptions } from './core/packagePresentation';
4
6
  export type { PptxComponentInput, PresentationComponentDefinition, SlideComponentDefinition, ProcessedPresentation, ProcessedSlide, PptxThemeConfig, PipelineWarning, SlideContext, SlideRenderContext, } from './types';
5
7
  export { isPresentationComponent, isSlideComponent } from './types';
6
8
  export { W as WarningCodes } from './utils/warn';
7
9
  export type { WarningCode } from './utils/warn';
8
10
  export { DEFAULT_PPTX_THEME, getPptxTheme, pptxThemes } from './themes';
9
11
  export { createComponent, createVersion, createPresentationGenerator, resolveComponentVersion, validateComponentProps, validatePresentation, cleanComponentProps, ComponentValidationError, DuplicateComponentError, generatePluginPresentationSchema, exportPluginSchema, } from './plugin';
10
- export type { CustomComponent, ComponentVersion, ComponentVersionMap, RenderFunction, RenderContext, PresentationGeneratorOptions, PresentationGenerator as PluginPresentationGenerator, PresentationGeneratorBuilder, BufferGenerationResult as PluginBufferGenerationResult, FileGenerationResult as PluginFileGenerationResult, ValidationResult as PluginValidationResult, ExtractCustomComponentType, CustomComponentUnion, ExtendedPptxComponentInput, ExtendedPresentationComponent, InferBuilderComponents, InferDocumentType, InferComponentDefinition, ComponentValidationResult, ValidationError as PluginValidationError, } from './plugin';
12
+ export type { CustomComponent, ComponentVersion, ComponentVersionMap, RenderFunction, RenderContext, PresentationGeneratorOptions, PresentationGenerator as PluginPresentationGenerator, PresentationGeneratorBuilder, BufferGenerationResult as PluginBufferGenerationResult, FileGenerationResult as PluginFileGenerationResult, GenerateFileOptions as PluginGenerateFileOptions, GenerateOptions as PluginGenerateOptions, GenerationValidationOptions as PluginGenerationValidationOptions, ValidationResult as PluginValidationResult, ExtractCustomComponentType, CustomComponentUnion, ExtendedPptxComponentInput, ExtendedPresentationComponent, InferBuilderComponents, InferDocumentType, InferComponentDefinition, ComponentValidationResult, ValidationError as PluginValidationError, } from './plugin';
11
13
  export { renderTextComponent, renderImageComponent, renderShapeComponent, renderTableComponent, renderHighchartsComponent, renderComponent, } from './components';
12
14
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAGD,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,0BAA0B,EAC1B,uBAAuB,EACvB,gBAAgB,EAChB,gBAAgB,EAChB,iCAAiC,EACjC,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAE1B,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAG5E,YAAY,EACV,kBAAkB,EAClB,+BAA+B,EAC/B,wBAAwB,EACxB,qBAAqB,EACrB,cAAc,EACd,eAAe,EACf,eAAe,EACf,YAAY,EACZ,kBAAkB,GACnB,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAGpE,OAAO,EAAE,CAAC,IAAI,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAGxE,OAAO,EACL,eAAe,EACf,aAAa,EACb,2BAA2B,EAC3B,uBAAuB,EACvB,sBAAsB,EACtB,oBAAoB,EACpB,mBAAmB,EACnB,wBAAwB,EACxB,uBAAuB,EACvB,gCAAgC,EAChC,kBAAkB,GACnB,MAAM,UAAU,CAAC;AAElB,YAAY,EACV,eAAe,EACf,gBAAgB,EAChB,mBAAmB,EACnB,cAAc,EACd,aAAa,EACb,4BAA4B,EAC5B,qBAAqB,IAAI,2BAA2B,EACpD,4BAA4B,EAC5B,sBAAsB,IAAI,4BAA4B,EACtD,oBAAoB,IAAI,0BAA0B,EAClD,gBAAgB,IAAI,sBAAsB,EAC1C,0BAA0B,EAC1B,oBAAoB,EACpB,0BAA0B,EAC1B,6BAA6B,EAC7B,sBAAsB,EACtB,iBAAiB,EACjB,wBAAwB,EACxB,yBAAyB,EACzB,eAAe,IAAI,qBAAqB,GACzC,MAAM,UAAU,CAAC;AAGlB,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,oBAAoB,EACpB,oBAAoB,EACpB,yBAAyB,EACzB,eAAe,GAChB,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAGD,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,0BAA0B,EAC1B,uBAAuB,EACvB,gBAAgB,EAChB,gBAAgB,EAChB,iCAAiC,EACjC,2BAA2B,EAC3B,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAE1B,YAAY,EACV,iBAAiB,EACjB,gBAAgB,EAChB,2BAA2B,GAC5B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,oBAAoB,EACpB,yBAAyB,GAC1B,MAAM,4BAA4B,CAAC;AACpC,YAAY,EAAE,4BAA4B,EAAE,MAAM,4BAA4B,CAAC;AAG/E,YAAY,EACV,kBAAkB,EAClB,+BAA+B,EAC/B,wBAAwB,EACxB,qBAAqB,EACrB,cAAc,EACd,eAAe,EACf,eAAe,EACf,YAAY,EACZ,kBAAkB,GACnB,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAGpE,OAAO,EAAE,CAAC,IAAI,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAGxE,OAAO,EACL,eAAe,EACf,aAAa,EACb,2BAA2B,EAC3B,uBAAuB,EACvB,sBAAsB,EACtB,oBAAoB,EACpB,mBAAmB,EACnB,wBAAwB,EACxB,uBAAuB,EACvB,gCAAgC,EAChC,kBAAkB,GACnB,MAAM,UAAU,CAAC;AAElB,YAAY,EACV,eAAe,EACf,gBAAgB,EAChB,mBAAmB,EACnB,cAAc,EACd,aAAa,EACb,4BAA4B,EAC5B,qBAAqB,IAAI,2BAA2B,EACpD,4BAA4B,EAC5B,sBAAsB,IAAI,4BAA4B,EACtD,oBAAoB,IAAI,0BAA0B,EAClD,mBAAmB,IAAI,yBAAyB,EAChD,eAAe,IAAI,qBAAqB,EACxC,2BAA2B,IAAI,iCAAiC,EAChE,gBAAgB,IAAI,sBAAsB,EAC1C,0BAA0B,EAC1B,oBAAoB,EACpB,0BAA0B,EAC1B,6BAA6B,EAC7B,sBAAsB,EACtB,iBAAiB,EACjB,wBAAwB,EACxB,yBAAyB,EACzB,eAAe,IAAI,qBAAqB,GACzC,MAAM,UAAU,CAAC;AAGlB,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,oBAAoB,EACpB,oBAAoB,EACpB,yBAAyB,EACzB,eAAe,GAChB,MAAM,cAAc,CAAC"}
package/dist/index.js CHANGED
@@ -1,5 +1,4 @@
1
1
  // src/core/generator.ts
2
- import JSZip from "jszip";
3
2
  import { writeFileSync } from "fs";
4
3
 
5
4
  // src/types.ts
@@ -1612,13 +1611,161 @@ async function resolveDocumentFonts(document, theme, warnings, fonts) {
1612
1611
 
1613
1612
  // src/core/generator.ts
1614
1613
  import { applyExportMode, scopedThemeName } from "@json-to-office/shared";
1615
- import { collectImageSourceConflicts } from "@json-to-office/shared-pptx";
1614
+ import {
1615
+ collectImageSourceConflicts,
1616
+ validateJsonPresentationDocument,
1617
+ validatePresentationDocument
1618
+ } from "@json-to-office/shared-pptx";
1619
+
1620
+ // src/core/packagePresentation.ts
1621
+ import JSZip from "jszip";
1622
+ var MEDIUM_STYLE_2_ACCENT_1 = "{5C22544A-7EE6-4342-B048-85BDC9FD1C3A}";
1623
+ var NO_STYLE_NO_GRID = "{2D5ABB26-0587-4C30-8999-92F81FD0307C}";
1624
+ var DEFAULT_GENERATED_AT = "2000-01-01T00:00:00.000Z";
1625
+ function resolveGeneratedAt(value) {
1626
+ const date = value === void 0 ? new Date(DEFAULT_GENERATED_AT) : new Date(value);
1627
+ if (Number.isNaN(date.getTime())) {
1628
+ throw new Error(`Invalid generatedAt value: ${String(value)}`);
1629
+ }
1630
+ if (date.getUTCFullYear() < 1980) {
1631
+ throw new Error(
1632
+ "generatedAt must be on or after 1980-01-01 for ZIP compatibility"
1633
+ );
1634
+ }
1635
+ return date;
1636
+ }
1637
+ function replaceCoreTimestamp(xml, tag, value) {
1638
+ const expression = new RegExp(
1639
+ `(<dcterms:${tag}\\b[^>]*>)[^<]*(</dcterms:${tag}>)`,
1640
+ "g"
1641
+ );
1642
+ return xml.replace(expression, `$1${value}$2`);
1643
+ }
1644
+ var EMBEDDED_OFFICE_PACKAGE = /\.(?:docx|pptx|xlsx|xlsm)$/i;
1645
+ function remapChartReferences(value, chartIds) {
1646
+ return value.replace(/chart(\d+)\.xml/g, (match, rawId) => {
1647
+ const id = chartIds.get(Number(rawId));
1648
+ return id === void 0 ? match : `chart${id}.xml`;
1649
+ }).replace(
1650
+ /Microsoft_Excel_Worksheet(\d+)\.xlsx/g,
1651
+ (match, rawId) => {
1652
+ const id = chartIds.get(Number(rawId));
1653
+ return id === void 0 ? match : `Microsoft_Excel_Worksheet${id}.xlsx`;
1654
+ }
1655
+ );
1656
+ }
1657
+ async function canonicalizeChartIds(zip) {
1658
+ const sourceIds = Object.keys(zip.files).map((path2) => path2.match(/^ppt\/charts\/chart(\d+)\.xml$/)?.[1]).filter((value) => value !== void 0).map(Number).sort((a, b) => a - b);
1659
+ const chartIds = new Map(sourceIds.map((id, index) => [id, index + 1]));
1660
+ if (chartIds.size === 0) return;
1661
+ for (const [path2, entry] of Object.entries(zip.files)) {
1662
+ if (entry.dir || !path2.endsWith(".xml") && !path2.endsWith(".rels")) {
1663
+ continue;
1664
+ }
1665
+ const xml = await entry.async("string");
1666
+ const remapped = remapChartReferences(xml, chartIds);
1667
+ if (remapped !== xml) zip.file(path2, remapped);
1668
+ }
1669
+ const renames = [];
1670
+ for (const [path2, entry] of Object.entries(zip.files)) {
1671
+ if (entry.dir) continue;
1672
+ const remappedPath = remapChartReferences(path2, chartIds);
1673
+ if (remappedPath === path2) continue;
1674
+ renames.push({
1675
+ from: path2,
1676
+ to: remappedPath,
1677
+ data: await entry.async("nodebuffer"),
1678
+ date: entry.date
1679
+ });
1680
+ }
1681
+ for (const entry of renames) zip.remove(entry.from);
1682
+ for (const entry of renames) {
1683
+ zip.file(entry.to, entry.data, { date: entry.date });
1684
+ }
1685
+ }
1686
+ async function generateZip(zip) {
1687
+ return await zip.generateAsync({
1688
+ type: "nodebuffer",
1689
+ compression: "DEFLATE",
1690
+ compressionOptions: { level: 6 },
1691
+ platform: "DOS",
1692
+ streamFiles: false
1693
+ });
1694
+ }
1695
+ async function canonicalizePackage(zip, generatedAt, depth = 0) {
1696
+ const timestamp = generatedAt.toISOString().replace(/\.\d{3}Z$/, "Z");
1697
+ const coreEntry = zip.file("docProps/core.xml");
1698
+ if (coreEntry) {
1699
+ let coreXml = await coreEntry.async("string");
1700
+ coreXml = replaceCoreTimestamp(coreXml, "created", timestamp);
1701
+ coreXml = replaceCoreTimestamp(coreXml, "modified", timestamp);
1702
+ zip.file("docProps/core.xml", coreXml);
1703
+ }
1704
+ if (depth < 3) {
1705
+ for (const [path2, entry] of Object.entries(zip.files)) {
1706
+ if (entry.dir || !EMBEDDED_OFFICE_PACKAGE.test(path2)) continue;
1707
+ try {
1708
+ const nested = await JSZip.loadAsync(await entry.async("nodebuffer"));
1709
+ await canonicalizePackage(nested, generatedAt, depth + 1);
1710
+ zip.file(path2, await generateZip(nested));
1711
+ } catch {
1712
+ }
1713
+ }
1714
+ }
1715
+ for (const entry of Object.values(zip.files)) {
1716
+ entry.date = generatedAt;
1717
+ }
1718
+ }
1719
+ async function packagePresentationBuffer(buffer, options = {}) {
1720
+ const zip = await JSZip.loadAsync(buffer);
1721
+ let changed = false;
1722
+ for (const [path2, entry] of Object.entries(zip.files)) {
1723
+ if (!path2.match(/^ppt\/slides\/slide\d+\.xml$/)) continue;
1724
+ const xml = await entry.async("string");
1725
+ if (xml.includes(MEDIUM_STYLE_2_ACCENT_1)) {
1726
+ zip.file(path2, xml.replaceAll(MEDIUM_STYLE_2_ACCENT_1, NO_STYLE_NO_GRID));
1727
+ changed = true;
1728
+ }
1729
+ }
1730
+ if (options.deterministic !== false) {
1731
+ const generatedAt = resolveGeneratedAt(options.generatedAt);
1732
+ await canonicalizeChartIds(zip);
1733
+ await canonicalizePackage(zip, generatedAt);
1734
+ changed = true;
1735
+ }
1736
+ if (!changed) return buffer;
1737
+ return generateZip(zip);
1738
+ }
1739
+
1740
+ // src/core/generator.ts
1741
+ var PresentationValidationError = class extends Error {
1742
+ errors;
1743
+ constructor(errors) {
1744
+ super(
1745
+ `Presentation validation failed:
1746
+ ${errors.map((error) => ` - ${error.path}: ${error.message}`).join("\n")}`
1747
+ );
1748
+ this.name = "PresentationValidationError";
1749
+ this.errors = errors;
1750
+ }
1751
+ };
1752
+ function assertValidPresentation(input, validation) {
1753
+ if (validation?.enabled === false) return;
1754
+ const options = {
1755
+ allowUnknownFields: validation?.allowUnknownFields
1756
+ };
1757
+ const result = typeof input === "string" ? validateJsonPresentationDocument(input, options) : validatePresentationDocument(input, options);
1758
+ if (!result.valid) {
1759
+ throw new PresentationValidationError(result.errors);
1760
+ }
1761
+ }
1616
1762
  function isPresentationComponentDefinition(definition) {
1617
1763
  if (typeof definition !== "object" || definition === null) return false;
1618
1764
  const def = definition;
1619
1765
  return def.name === "pptx" && "props" in def;
1620
1766
  }
1621
1767
  async function generatePresentation(document, options, warnings) {
1768
+ assertValidPresentation(document, options?.validation);
1622
1769
  if (!document || document.name !== "pptx") {
1623
1770
  throw new Error("Top-level component must be a pptx component");
1624
1771
  }
@@ -1637,6 +1784,7 @@ async function generateBufferFromJson(jsonConfig, options) {
1637
1784
  return result.buffer;
1638
1785
  }
1639
1786
  async function generateBufferWithWarnings(jsonConfig, options) {
1787
+ assertValidPresentation(jsonConfig, options?.validation);
1640
1788
  let component;
1641
1789
  if (typeof jsonConfig === "string") {
1642
1790
  const parsed = JSON.parse(jsonConfig);
@@ -1705,36 +1853,21 @@ async function generateBufferWithWarnings(jsonConfig, options) {
1705
1853
  warnings
1706
1854
  );
1707
1855
  const data = await pptx.write({ outputType: "nodebuffer" });
1708
- const buffer = await neutralizeTableStyle(data);
1856
+ const buffer = await packagePresentationBuffer(data, options);
1709
1857
  return { buffer, warnings };
1710
1858
  }
1711
1859
  async function generateAndSaveFromJson(jsonConfig, outputPath, options) {
1712
1860
  const buffer = await generateBufferFromJson(jsonConfig, options);
1713
1861
  writeFileSync(outputPath, buffer);
1714
1862
  }
1715
- async function generateFromFile(filePath, outputPath) {
1863
+ async function generateFromFile(filePath, outputPath, options) {
1716
1864
  const { readFileSync } = await import("fs");
1717
1865
  const json = readFileSync(filePath, "utf-8");
1718
- await generateAndSaveFromJson(json, outputPath);
1719
- }
1720
- var MEDIUM_STYLE_2_ACCENT_1 = "{5C22544A-7EE6-4342-B048-85BDC9FD1C3A}";
1721
- var NO_STYLE_NO_GRID = "{2D5ABB26-0587-4C30-8999-92F81FD0307C}";
1722
- async function neutralizeTableStyle(buffer) {
1723
- const zip = await JSZip.loadAsync(buffer);
1724
- let changed = false;
1725
- for (const [path2, entry] of Object.entries(zip.files)) {
1726
- if (!path2.match(/^ppt\/slides\/slide\d+\.xml$/)) continue;
1727
- const xml = await entry.async("string");
1728
- if (xml.includes(MEDIUM_STYLE_2_ACCENT_1)) {
1729
- zip.file(path2, xml.replaceAll(MEDIUM_STYLE_2_ACCENT_1, NO_STYLE_NO_GRID));
1730
- changed = true;
1731
- }
1732
- }
1733
- return changed ? await zip.generateAsync({ type: "nodebuffer" }) : buffer;
1866
+ await generateAndSaveFromJson(json, outputPath, options);
1734
1867
  }
1735
- async function savePresentation(pptx, outputPath) {
1868
+ async function savePresentation(pptx, outputPath, options) {
1736
1869
  const data = await pptx.write({ outputType: "nodebuffer" });
1737
- const buffer = await neutralizeTableStyle(data);
1870
+ const buffer = await packagePresentationBuffer(data, options);
1738
1871
  writeFileSync(outputPath, buffer);
1739
1872
  }
1740
1873
  var PresentationGenerator = {
@@ -1754,7 +1887,6 @@ import {
1754
1887
  } from "@json-to-office/shared/plugin";
1755
1888
 
1756
1889
  // src/plugin/createPresentationGenerator.ts
1757
- import JSZip2 from "jszip";
1758
1890
  import {
1759
1891
  resolveComponentVersion as resolveComponentVersion2,
1760
1892
  DuplicateComponentError as DuplicateComponentError2,
@@ -1767,23 +1899,33 @@ import {
1767
1899
  validateCustomComponentProps,
1768
1900
  ComponentValidationError
1769
1901
  } from "@json-to-office/shared/plugin";
1770
- import { collectImageSourceConflicts as collectImageSourceConflicts2 } from "@json-to-office/shared-pptx";
1902
+ import { validatePresentationDocument as validatePresentationDocument2 } from "@json-to-office/shared-pptx";
1771
1903
  import {
1772
1904
  DuplicateComponentError,
1773
1905
  ComponentValidationError as ComponentValidationError2
1774
1906
  } from "@json-to-office/shared/plugin";
1775
- function validateComponentProps(schema, props, componentName) {
1907
+ function validateComponentProps(schema, props, componentName, opts) {
1776
1908
  return validateCustomComponentProps(schema.propsSchema, props, {
1777
- clean: true,
1778
- applyDefaults: true,
1909
+ // Render-time cleaning remains the default. The document-validation path
1910
+ // passes clean:false so unknown custom props are rejected when the custom
1911
+ // schema declares additionalProperties:false.
1912
+ clean: opts?.clean ?? true,
1913
+ applyDefaults: opts?.applyDefaults ?? true,
1779
1914
  componentName
1780
1915
  });
1781
1916
  }
1782
- function validatePresentation(document, customComponents) {
1783
- const errors = [];
1784
- errors.push(...collectImageSourceConflicts2(document));
1917
+ function validatePresentation(document, customComponents, options) {
1918
+ const knownCustomNames = new Set(customComponents.map((c) => c.name));
1919
+ const documentResult = validatePresentationDocument2(document, {
1920
+ knownCustomNames,
1921
+ allowUnknownFields: options?.allowUnknownFields
1922
+ });
1923
+ const errors = [...documentResult.errors];
1785
1924
  function validateComponents(components, pathPrefix = "children") {
1786
1925
  components.forEach((componentData, index) => {
1926
+ if (!componentData || typeof componentData !== "object" || Array.isArray(componentData)) {
1927
+ return;
1928
+ }
1787
1929
  const customComponent = customComponents.find(
1788
1930
  (cc) => cc.name === componentData.name
1789
1931
  );
@@ -1796,7 +1938,8 @@ function validatePresentation(document, customComponents) {
1796
1938
  const validation = validateComponentProps(
1797
1939
  versionEntry,
1798
1940
  componentData.props,
1799
- customComponent.name
1941
+ customComponent.name,
1942
+ { clean: options?.allowUnknownFields === true }
1800
1943
  );
1801
1944
  if (!validation.valid && validation.errors) {
1802
1945
  const indexedErrors = validation.errors.map(
@@ -1816,7 +1959,7 @@ function validatePresentation(document, customComponents) {
1816
1959
  }
1817
1960
  });
1818
1961
  }
1819
- if (document.children) {
1962
+ if (document && Array.isArray(document.children)) {
1820
1963
  validateComponents(document.children);
1821
1964
  }
1822
1965
  return errors.length > 0 ? { valid: false, errors } : { valid: true, errors: [] };
@@ -1866,24 +2009,9 @@ async function exportPluginSchema(customComponents, outputPath, options = {}) {
1866
2009
 
1867
2010
  // src/plugin/createPresentationGenerator.ts
1868
2011
  import { applyExportMode as applyExportMode2, scopedThemeName as scopedThemeName2 } from "@json-to-office/shared";
1869
- var MEDIUM_STYLE_2_ACCENT_12 = "{5C22544A-7EE6-4342-B048-85BDC9FD1C3A}";
1870
- var NO_STYLE_NO_GRID2 = "{2D5ABB26-0587-4C30-8999-92F81FD0307C}";
1871
- async function neutralizeTableStyle2(buffer) {
1872
- const zip = await JSZip2.loadAsync(buffer);
1873
- let changed = false;
1874
- for (const [path2, entry] of Object.entries(zip.files)) {
1875
- if (!path2.match(/^ppt\/slides\/slide\d+\.xml$/)) continue;
1876
- const xml = await entry.async("string");
1877
- if (xml.includes(MEDIUM_STYLE_2_ACCENT_12)) {
1878
- zip.file(path2, xml.replaceAll(MEDIUM_STYLE_2_ACCENT_12, NO_STYLE_NO_GRID2));
1879
- changed = true;
1880
- }
1881
- }
1882
- return changed ? await zip.generateAsync({ type: "nodebuffer" }) : buffer;
1883
- }
1884
2012
  function createBuilderImpl(state) {
1885
2013
  const componentMap = new Map(state.components.map((c) => [c.name, c]));
1886
- async function processSlideComponents(components, warningsCollector, theme, depth = 0) {
2014
+ async function processSlideComponents(components, warningsCollector, theme, validateEmitted, parentName, depth = 0) {
1887
2015
  if (depth > 20) {
1888
2016
  throw new Error(
1889
2017
  "Maximum component nesting depth exceeded (20). Check for circular component references."
@@ -1915,6 +2043,8 @@ function createBuilderImpl(state) {
1915
2043
  componentWithVersion.children,
1916
2044
  warningsCollector,
1917
2045
  theme,
2046
+ validateEmitted,
2047
+ void 0,
1918
2048
  depth + 1
1919
2049
  );
1920
2050
  }
@@ -1934,10 +2064,13 @@ function createBuilderImpl(state) {
1934
2064
  children: nestedChildren
1935
2065
  });
1936
2066
  const resultComponents = Array.isArray(result) ? result : [result];
2067
+ validateEmitted?.(resultComponents, versionLabel, parentName);
1937
2068
  const processedResult = await processSlideComponents(
1938
2069
  resultComponents,
1939
2070
  warningsCollector,
1940
2071
  theme,
2072
+ validateEmitted,
2073
+ parentName,
1941
2074
  depth + 1
1942
2075
  );
1943
2076
  processed.push(...processedResult);
@@ -1961,6 +2094,8 @@ function createBuilderImpl(state) {
1961
2094
  componentData.children,
1962
2095
  warningsCollector,
1963
2096
  theme,
2097
+ validateEmitted,
2098
+ componentData.name,
1964
2099
  depth + 1
1965
2100
  );
1966
2101
  processed.push({
@@ -1990,16 +2125,31 @@ function createBuilderImpl(state) {
1990
2125
  customThemes: state.customThemes,
1991
2126
  debug: state.debug,
1992
2127
  services: state.services,
1993
- fonts: state.fonts
2128
+ fonts: state.fonts,
2129
+ validation: state.validation,
2130
+ packaging: state.packaging
1994
2131
  };
1995
2132
  return createBuilderImpl(
1996
2133
  newState
1997
2134
  );
1998
2135
  }
1999
- async function generate(document) {
2136
+ async function generate(document, options) {
2000
2137
  try {
2001
2138
  let internalDocument = document;
2002
- if (!internalDocument || internalDocument.name !== "pptx") {
2139
+ const validationOptions = {
2140
+ ...state.validation,
2141
+ ...options?.validation
2142
+ };
2143
+ if (validationOptions.enabled !== false) {
2144
+ const result = validatePresentation(
2145
+ internalDocument,
2146
+ state.components,
2147
+ { allowUnknownFields: validationOptions.allowUnknownFields }
2148
+ );
2149
+ if (!result.valid) {
2150
+ throw new ComponentValidationError3(result.errors, internalDocument);
2151
+ }
2152
+ } else if (!internalDocument || internalDocument.name !== "pptx") {
2003
2153
  throw new Error("Top-level component must be a pptx component");
2004
2154
  }
2005
2155
  let inlineTheme;
@@ -2030,7 +2180,39 @@ function createBuilderImpl(state) {
2030
2180
  component: "fontRegistry"
2031
2181
  });
2032
2182
  }
2033
- const processedChildren = mode.doc.children ? await processAllSlides(mode.doc.children, warnings, resolvedTheme) : [];
2183
+ const validateEmitted = validationOptions.enabled === false ? void 0 : (emitted, componentLabel, parentName) => {
2184
+ let validationDocument;
2185
+ if (parentName === "pptx") {
2186
+ validationDocument = { ...mode.doc, children: emitted };
2187
+ } else if (parentName === "slide") {
2188
+ validationDocument = {
2189
+ ...mode.doc,
2190
+ children: [{ name: "slide", props: {}, children: emitted }]
2191
+ };
2192
+ } else {
2193
+ return;
2194
+ }
2195
+ const result = validatePresentation(
2196
+ validationDocument,
2197
+ state.components,
2198
+ { allowUnknownFields: validationOptions.allowUnknownFields }
2199
+ );
2200
+ if (!result.valid) {
2201
+ throw new ComponentValidationError3(
2202
+ result.errors.map((error) => ({
2203
+ ...error,
2204
+ message: `custom component '${componentLabel}' emitted invalid output \u2014 ${error.message}`
2205
+ })),
2206
+ emitted
2207
+ );
2208
+ }
2209
+ };
2210
+ const processedChildren = mode.doc.children ? await processAllSlides(
2211
+ mode.doc.children,
2212
+ warnings,
2213
+ resolvedTheme,
2214
+ validateEmitted
2215
+ ) : [];
2034
2216
  const themeName = scopedThemeName2(baseThemeName, state.fonts?.mode);
2035
2217
  const docWithScopedTheme = themeName !== baseThemeName ? {
2036
2218
  ...mode.doc,
@@ -2038,6 +2220,20 @@ function createBuilderImpl(state) {
2038
2220
  children: processedChildren
2039
2221
  } : { ...mode.doc, children: processedChildren };
2040
2222
  const processedDocument = docWithScopedTheme;
2223
+ if (validationOptions.enabled !== false) {
2224
+ const result = validatePresentation(processedDocument, [], {
2225
+ allowUnknownFields: validationOptions.allowUnknownFields
2226
+ });
2227
+ if (!result.valid) {
2228
+ throw new ComponentValidationError3(
2229
+ result.errors.map((error) => ({
2230
+ ...error,
2231
+ message: `expanded plugin output failed validation \u2014 ${error.message}`
2232
+ })),
2233
+ processedDocument
2234
+ );
2235
+ }
2236
+ }
2041
2237
  await resolveDocumentFonts(
2042
2238
  processedDocument,
2043
2239
  resolvedTheme,
@@ -2054,7 +2250,10 @@ function createBuilderImpl(state) {
2054
2250
  });
2055
2251
  const pptx = await renderPresentation(processed, warnings);
2056
2252
  const data = await pptx.write({ outputType: "nodebuffer" });
2057
- const buffer = await neutralizeTableStyle2(data);
2253
+ const buffer = await packagePresentationBuffer(data, {
2254
+ deterministic: options?.deterministic ?? state.packaging.deterministic,
2255
+ generatedAt: options?.generatedAt ?? state.packaging.generatedAt
2256
+ });
2058
2257
  return { buffer, warnings };
2059
2258
  } catch (error) {
2060
2259
  if (state.debug) {
@@ -2063,29 +2262,33 @@ function createBuilderImpl(state) {
2063
2262
  throw error;
2064
2263
  }
2065
2264
  }
2066
- async function processAllSlides(children, warnings, theme) {
2265
+ async function processAllSlides(children, warnings, theme, validateEmitted) {
2067
2266
  const result = [];
2068
2267
  for (const child of children) {
2069
2268
  if (child.name === "slide" && child.children) {
2070
2269
  const processedSlideChildren = await processSlideComponents(
2071
2270
  child.children,
2072
2271
  warnings,
2073
- theme
2272
+ theme,
2273
+ validateEmitted,
2274
+ "slide"
2074
2275
  );
2075
2276
  result.push({ ...child, children: processedSlideChildren });
2076
2277
  } else {
2077
2278
  const processedTopLevel = await processSlideComponents(
2078
2279
  [child],
2079
2280
  warnings,
2080
- theme
2281
+ theme,
2282
+ validateEmitted,
2283
+ "pptx"
2081
2284
  );
2082
2285
  result.push(...processedTopLevel);
2083
2286
  }
2084
2287
  }
2085
2288
  return result;
2086
2289
  }
2087
- async function generateFile(document, outputPath) {
2088
- const { buffer, warnings } = await generate(document);
2290
+ async function generateFile(document, outputPath, options) {
2291
+ const { buffer, warnings } = await generate(document, options);
2089
2292
  const fs = await import("fs/promises");
2090
2293
  await fs.writeFile(outputPath, new Uint8Array(buffer));
2091
2294
  return { warnings };
@@ -2162,7 +2365,12 @@ function createPresentationGenerator(options = {}) {
2162
2365
  customThemes: options.customThemes,
2163
2366
  debug: options.debug ?? false,
2164
2367
  services: options.services,
2165
- fonts: options.fonts
2368
+ fonts: options.fonts,
2369
+ validation: options.validation,
2370
+ packaging: {
2371
+ deterministic: options.deterministic,
2372
+ generatedAt: options.generatedAt
2373
+ }
2166
2374
  };
2167
2375
  return createBuilderImpl(initialState);
2168
2376
  }
@@ -2176,9 +2384,11 @@ function getPptxCoreVersion() {
2176
2384
  }
2177
2385
  export {
2178
2386
  ComponentValidationError2 as ComponentValidationError,
2387
+ DEFAULT_GENERATED_AT,
2179
2388
  DEFAULT_PPTX_THEME,
2180
2389
  DuplicateComponentError,
2181
2390
  PresentationGenerator,
2391
+ PresentationValidationError,
2182
2392
  W as WarningCodes,
2183
2393
  cleanComponentProps,
2184
2394
  createComponent,
@@ -2196,6 +2406,7 @@ export {
2196
2406
  isPresentationComponent,
2197
2407
  isPresentationComponentDefinition,
2198
2408
  isSlideComponent,
2409
+ packagePresentationBuffer,
2199
2410
  pptxThemes,
2200
2411
  renderComponent,
2201
2412
  renderHighchartsComponent,