@json-to-office/core-docx 0.17.0 → 0.17.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3550,216 +3550,116 @@ function getStyleIdForLevel(level) {
3550
3550
  return styleMap[level] || "Heading1";
3551
3551
  }
3552
3552
 
3553
- // src/utils/textParser.ts
3554
- import { TextRun as TextRun2, ExternalHyperlink as ExternalHyperlink2, InternalHyperlink as InternalHyperlink2 } from "docx";
3555
-
3556
- // src/utils/placeholderProcessor.ts
3557
- import {
3558
- TextRun,
3559
- PageNumber
3560
- } from "docx";
3553
+ // src/core/render.ts
3554
+ init_styleHelpers();
3555
+ init_layoutUtils();
3561
3556
 
3562
- // src/utils/unicode.ts
3563
- function normalizeUnicodeText(text) {
3564
- return (text ?? "").normalize("NFC");
3565
- }
3557
+ // src/cache/index.ts
3558
+ var cache_exports = {};
3559
+ __export(cache_exports, {
3560
+ CacheKeyGenerator: () => CacheKeyGenerator
3561
+ });
3562
+ __reExport(cache_exports, cache_star);
3563
+ import * as cache_star from "@json-to-office/shared/cache";
3566
3564
 
3567
- // src/utils/placeholderProcessor.ts
3568
- var PlaceholderRegistry = class {
3569
- static handlers = /* @__PURE__ */ new Map();
3570
- /**
3571
- * Register a placeholder handler
3572
- */
3573
- static register(name, handler) {
3574
- this.handlers.set(name.toUpperCase(), handler);
3565
+ // src/cache/key-generator.ts
3566
+ import { createHash } from "crypto";
3567
+ var CacheKeyGenerator = class {
3568
+ version;
3569
+ constructor(version = "1.0") {
3570
+ this.version = version;
3575
3571
  }
3576
3572
  /**
3577
- * Get a placeholder handler
3573
+ * Generate cache key for a component
3578
3574
  */
3579
- static get(name) {
3580
- return this.handlers.get(name.toUpperCase());
3575
+ generateKey(component, context, options = {}) {
3576
+ const parts = [
3577
+ this.version,
3578
+ component.name,
3579
+ this.hashProps(component.props)
3580
+ ];
3581
+ if (options.includeTheme !== false) {
3582
+ parts.push(context.theme.name);
3583
+ }
3584
+ if (options.includeContext) {
3585
+ parts.push(this.hashContext(context));
3586
+ }
3587
+ if (options.additionalKeys) {
3588
+ parts.push(...options.additionalKeys);
3589
+ }
3590
+ if (options.version) {
3591
+ parts.push(options.version);
3592
+ }
3593
+ return parts.join(":");
3581
3594
  }
3582
3595
  /**
3583
- * Check if a placeholder is registered
3596
+ * Hash component props
3584
3597
  */
3585
- static has(name) {
3586
- return this.handlers.has(name.toUpperCase());
3598
+ hashProps(props) {
3599
+ if (!props) return "null";
3600
+ const normalized = this.normalizeObject(props);
3601
+ const json = JSON.stringify(normalized);
3602
+ return this.hash(json);
3587
3603
  }
3588
3604
  /**
3589
- * Get all registered placeholder names
3605
+ * Hash render context
3590
3606
  */
3591
- static getRegisteredNames() {
3592
- return Array.from(this.handlers.keys());
3607
+ hashContext(context) {
3608
+ const relevant = {
3609
+ theme: context.theme.name,
3610
+ document: context.document
3611
+ // Exclude runtime properties like sectionIndex, componentIndex
3612
+ };
3613
+ return this.hash(JSON.stringify(relevant));
3593
3614
  }
3594
3615
  /**
3595
- * Clear all registered placeholders
3616
+ * Normalize object for consistent hashing
3596
3617
  */
3597
- static clear() {
3598
- this.handlers.clear();
3599
- }
3600
- };
3601
- function processTextWithPlaceholders(text, baseStyle = {}, context = {}) {
3602
- const normalizedText = normalizeUnicodeText(text);
3603
- const combinedRegex = /(\*\*\*|___)([\s\S]*?)\1|(\*\*|__)([\s\S]*?)\3|(\*|_)([\s\S]*?)\5|\{([^}]+)\}/g;
3604
- const result = [];
3605
- let lastIndex = 0;
3606
- let match;
3607
- while ((match = combinedRegex.exec(normalizedText)) !== null) {
3608
- if (match.index > lastIndex) {
3609
- const beforeText = normalizedText.substring(lastIndex, match.index);
3610
- if (beforeText) {
3611
- result.push(...createTextRunsWithNewlines(beforeText, baseStyle));
3612
- }
3618
+ normalizeObject(obj) {
3619
+ if (obj === null || obj === void 0) return obj;
3620
+ if (Array.isArray(obj)) {
3621
+ return obj.map((item) => this.normalizeObject(item));
3613
3622
  }
3614
- if (match[7]) {
3615
- const placeholderName = match[7];
3616
- const handler = PlaceholderRegistry.get(placeholderName);
3617
- if (handler) {
3618
- const placeholderResult = handler({ ...context, style: baseStyle });
3619
- if (Array.isArray(placeholderResult)) {
3620
- result.push(...placeholderResult);
3621
- } else if (placeholderResult instanceof TextRun) {
3622
- result.push(placeholderResult);
3623
- } else if (typeof placeholderResult === "string") {
3624
- result.push(
3625
- ...createTextRunsWithNewlines(placeholderResult, baseStyle)
3626
- );
3627
- }
3628
- } else {
3629
- result.push(...createTextRunsWithNewlines(match[0], baseStyle));
3630
- }
3631
- } else {
3632
- let decoratedText;
3633
- let bold = baseStyle.bold || false;
3634
- let italics = baseStyle.italics || false;
3635
- if (match[1] === "***" || match[1] === "___") {
3636
- decoratedText = match[2];
3637
- bold = true;
3638
- italics = true;
3639
- } else if (match[3] === "**" || match[3] === "__") {
3640
- decoratedText = match[4];
3641
- bold = true;
3642
- } else if (match[5] === "*" || match[5] === "_") {
3643
- decoratedText = match[6];
3644
- italics = true;
3645
- } else {
3646
- decoratedText = match[0];
3647
- }
3648
- const decoratedTextRuns = processTextWithPlaceholders(
3649
- decoratedText,
3650
- {
3651
- ...baseStyle,
3652
- bold,
3653
- italics
3654
- },
3655
- context
3656
- );
3657
- result.push(...decoratedTextRuns);
3623
+ if (obj instanceof Date) {
3624
+ return obj.toISOString();
3658
3625
  }
3659
- lastIndex = match.index + match[0].length;
3660
- }
3661
- if (lastIndex < normalizedText.length) {
3662
- const remainingText = normalizedText.substring(lastIndex);
3663
- if (remainingText) {
3664
- result.push(...createTextRunsWithNewlines(remainingText, baseStyle));
3626
+ if (typeof obj === "object") {
3627
+ const sorted = {};
3628
+ const keys = Object.keys(obj).sort();
3629
+ for (const key of keys) {
3630
+ sorted[key] = this.normalizeObject(obj[key]);
3631
+ }
3632
+ return sorted;
3665
3633
  }
3634
+ return obj;
3666
3635
  }
3667
- if (result.length === 0 && normalizedText) {
3668
- result.push(...createTextRunsWithNewlines(normalizedText, baseStyle));
3669
- }
3670
- return result;
3671
- }
3672
- function createTextRunsWithNewlines(text, baseStyle) {
3673
- const runs = [];
3674
- const lines = text.split("\n");
3675
- for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
3676
- const line = lines[lineIndex];
3677
- const needsLineBreak = lineIndex > 0;
3678
- if (line || needsLineBreak) {
3679
- runs.push(
3680
- new TextRun({
3681
- text: line,
3682
- font: baseStyle.font,
3683
- size: baseStyle.size,
3684
- color: baseStyle.color,
3685
- bold: baseStyle.bold,
3686
- italics: baseStyle.italics,
3687
- underline: baseStyle.underline,
3688
- break: needsLineBreak ? 1 : void 0
3689
- })
3690
- );
3691
- }
3636
+ /**
3637
+ * Create hash
3638
+ */
3639
+ hash(input) {
3640
+ return createHash("sha256").update(input).digest("hex").substring(0, 16);
3692
3641
  }
3693
- return runs;
3694
- }
3695
- function initializeBuiltinPlaceholders() {
3696
- PlaceholderRegistry.register("PAGE", (context) => {
3697
- return new TextRun({
3698
- children: [PageNumber.CURRENT],
3699
- font: context?.style?.font,
3700
- size: context?.style?.size,
3701
- color: context?.style?.color,
3702
- bold: context?.style?.bold,
3703
- italics: context?.style?.italics,
3704
- underline: context?.style?.underline
3705
- });
3706
- });
3707
- PlaceholderRegistry.register("TOTAL_PAGES", (context) => {
3708
- return new TextRun({
3709
- children: [PageNumber.TOTAL_PAGES],
3710
- font: context?.style?.font,
3711
- size: context?.style?.size,
3712
- color: context?.style?.color,
3713
- bold: context?.style?.bold,
3714
- italics: context?.style?.italics,
3715
- underline: context?.style?.underline
3716
- });
3717
- });
3718
- PlaceholderRegistry.register("DATE", (context) => {
3719
- const today = /* @__PURE__ */ new Date();
3720
- const dateString = today.toLocaleDateString();
3721
- return new TextRun({
3722
- text: dateString,
3723
- font: context?.style?.font,
3724
- size: context?.style?.size,
3725
- color: context?.style?.color,
3726
- bold: context?.style?.bold,
3727
- italics: context?.style?.italics,
3728
- underline: context?.style?.underline
3729
- });
3730
- });
3731
- PlaceholderRegistry.register("DATETIME", (context) => {
3732
- const now = /* @__PURE__ */ new Date();
3733
- const dateTimeString = now.toLocaleString();
3734
- return new TextRun({
3735
- text: dateTimeString,
3736
- font: context?.style?.font,
3737
- size: context?.style?.size,
3738
- color: context?.style?.color,
3739
- bold: context?.style?.bold,
3740
- italics: context?.style?.italics,
3741
- underline: context?.style?.underline
3742
- });
3743
- });
3744
- PlaceholderRegistry.register("YEAR", (context) => {
3745
- const year = (/* @__PURE__ */ new Date()).getFullYear().toString();
3746
- return new TextRun({
3747
- text: year,
3748
- font: context?.style?.font,
3749
- size: context?.style?.size,
3750
- color: context?.style?.color,
3751
- bold: context?.style?.bold,
3752
- italics: context?.style?.italics,
3753
- underline: context?.style?.underline
3754
- });
3755
- });
3642
+ };
3643
+
3644
+ // src/utils/revisionUtils.ts
3645
+ import { TextRun as TextRun3, InsertedTextRun, DeletedTextRun } from "docx";
3646
+
3647
+ // src/utils/unicode.ts
3648
+ function normalizeUnicodeText(text) {
3649
+ return (text ?? "").normalize("NFC");
3756
3650
  }
3757
- initializeBuiltinPlaceholders();
3651
+
3652
+ // src/utils/placeholderProcessor.ts
3653
+ import {
3654
+ TextRun as TextRun2,
3655
+ PageNumber
3656
+ } from "docx";
3758
3657
 
3759
3658
  // src/utils/textParser.ts
3659
+ import { TextRun, ExternalHyperlink, InternalHyperlink } from "docx";
3760
3660
  function parseTextWithDecorators(text, baseStyle = {}, options = {}) {
3761
3661
  if (!text) {
3762
- return [new TextRun2({ text: "", ...baseStyle })];
3662
+ return [new TextRun({ text: "", ...baseStyle })];
3763
3663
  }
3764
3664
  const normalizedText = normalizeUnicodeText(text);
3765
3665
  const hasPlaceholders = /\{[^}]+\}/.test(normalizedText);
@@ -3781,7 +3681,7 @@ function parseTextWithDecorators(text, baseStyle = {}, options = {}) {
3781
3681
  if (match.index > lastIndex) {
3782
3682
  const plainText = normalizedText.substring(lastIndex, match.index);
3783
3683
  if (plainText) {
3784
- runs.push(...createTextRunsWithNewlines2(plainText, baseStyle, options));
3684
+ runs.push(...createTextRunsWithNewlines(plainText, baseStyle, options));
3785
3685
  }
3786
3686
  }
3787
3687
  let decoratedText;
@@ -3800,7 +3700,7 @@ function parseTextWithDecorators(text, baseStyle = {}, options = {}) {
3800
3700
  } else {
3801
3701
  decoratedText = match[0];
3802
3702
  }
3803
- const decoratedRuns = createTextRunsWithNewlines2(
3703
+ const decoratedRuns = createTextRunsWithNewlines(
3804
3704
  decoratedText,
3805
3705
  baseStyle,
3806
3706
  options,
@@ -3816,18 +3716,18 @@ function parseTextWithDecorators(text, baseStyle = {}, options = {}) {
3816
3716
  const remainingText = normalizedText.substring(lastIndex);
3817
3717
  if (remainingText) {
3818
3718
  runs.push(
3819
- ...createTextRunsWithNewlines2(remainingText, baseStyle, options)
3719
+ ...createTextRunsWithNewlines(remainingText, baseStyle, options)
3820
3720
  );
3821
3721
  }
3822
3722
  }
3823
3723
  if (runs.length === 0 && normalizedText) {
3824
3724
  runs.push(
3825
- ...createTextRunsWithNewlines2(normalizedText, baseStyle, options)
3725
+ ...createTextRunsWithNewlines(normalizedText, baseStyle, options)
3826
3726
  );
3827
3727
  }
3828
3728
  return runs;
3829
3729
  }
3830
- function createTextRunsWithNewlines2(text, baseStyle, options, overrideStyle) {
3730
+ function createTextRunsWithNewlines(text, baseStyle, options, overrideStyle) {
3831
3731
  const runs = [];
3832
3732
  const lines = text.split("\n");
3833
3733
  for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
@@ -3839,7 +3739,7 @@ function createTextRunsWithNewlines2(text, baseStyle, options, overrideStyle) {
3839
3739
  italics: overrideStyle?.italics ?? baseStyle.italics
3840
3740
  };
3841
3741
  runs.push(
3842
- new TextRun2({
3742
+ new TextRun({
3843
3743
  text: line,
3844
3744
  ...baseStyle.font && { font: baseStyle.font },
3845
3745
  ...baseStyle.size && { size: baseStyle.size },
@@ -3885,7 +3785,7 @@ function parseTextWithHyperlinks(text, baseStyle = {}, options = {}) {
3885
3785
  if (isInternal) {
3886
3786
  const bookmarkId = linkUrl.substring(1);
3887
3787
  runs.push(
3888
- new InternalHyperlink2({
3788
+ new InternalHyperlink({
3889
3789
  children: linkTextRuns,
3890
3790
  // Cast needed for docx types
3891
3791
  anchor: bookmarkId
@@ -3893,7 +3793,7 @@ function parseTextWithHyperlinks(text, baseStyle = {}, options = {}) {
3893
3793
  );
3894
3794
  } else {
3895
3795
  runs.push(
3896
- new ExternalHyperlink2({
3796
+ new ExternalHyperlink({
3897
3797
  children: linkTextRuns,
3898
3798
  // Cast needed for docx types
3899
3799
  link: linkUrl
@@ -3922,100 +3822,199 @@ function parseTextWithHyperlinks(text, baseStyle = {}, options = {}) {
3922
3822
  return runs;
3923
3823
  }
3924
3824
 
3925
- // src/core/render.ts
3926
- init_colorUtils();
3927
- init_styleHelpers();
3928
- init_layoutUtils();
3929
-
3930
- // src/cache/index.ts
3931
- var cache_exports = {};
3932
- __export(cache_exports, {
3933
- CacheKeyGenerator: () => CacheKeyGenerator
3934
- });
3935
- __reExport(cache_exports, cache_star);
3936
- import * as cache_star from "@json-to-office/shared/cache";
3937
-
3938
- // src/cache/key-generator.ts
3939
- import { createHash } from "crypto";
3940
- var CacheKeyGenerator = class {
3941
- version;
3942
- constructor(version = "1.0") {
3943
- this.version = version;
3825
+ // src/utils/placeholderProcessor.ts
3826
+ var PlaceholderRegistry = class {
3827
+ static handlers = /* @__PURE__ */ new Map();
3828
+ /**
3829
+ * Register a placeholder handler
3830
+ */
3831
+ static register(name, handler) {
3832
+ this.handlers.set(name.toUpperCase(), handler);
3944
3833
  }
3945
3834
  /**
3946
- * Generate cache key for a component
3835
+ * Get a placeholder handler
3947
3836
  */
3948
- generateKey(component, context, options = {}) {
3949
- const parts = [
3950
- this.version,
3951
- component.name,
3952
- this.hashProps(component.props)
3953
- ];
3954
- if (options.includeTheme !== false) {
3955
- parts.push(context.theme.name);
3956
- }
3957
- if (options.includeContext) {
3958
- parts.push(this.hashContext(context));
3959
- }
3960
- if (options.additionalKeys) {
3961
- parts.push(...options.additionalKeys);
3962
- }
3963
- if (options.version) {
3964
- parts.push(options.version);
3965
- }
3966
- return parts.join(":");
3837
+ static get(name) {
3838
+ return this.handlers.get(name.toUpperCase());
3967
3839
  }
3968
3840
  /**
3969
- * Hash component props
3841
+ * Check if a placeholder is registered
3970
3842
  */
3971
- hashProps(props) {
3972
- if (!props) return "null";
3973
- const normalized = this.normalizeObject(props);
3974
- const json = JSON.stringify(normalized);
3975
- return this.hash(json);
3843
+ static has(name) {
3844
+ return this.handlers.has(name.toUpperCase());
3976
3845
  }
3977
3846
  /**
3978
- * Hash render context
3847
+ * Get all registered placeholder names
3979
3848
  */
3980
- hashContext(context) {
3981
- const relevant = {
3982
- theme: context.theme.name,
3983
- document: context.document
3984
- // Exclude runtime properties like sectionIndex, componentIndex
3985
- };
3986
- return this.hash(JSON.stringify(relevant));
3849
+ static getRegisteredNames() {
3850
+ return Array.from(this.handlers.keys());
3987
3851
  }
3988
3852
  /**
3989
- * Normalize object for consistent hashing
3853
+ * Clear all registered placeholders
3990
3854
  */
3991
- normalizeObject(obj) {
3992
- if (obj === null || obj === void 0) return obj;
3993
- if (Array.isArray(obj)) {
3994
- return obj.map((item) => this.normalizeObject(item));
3995
- }
3996
- if (obj instanceof Date) {
3997
- return obj.toISOString();
3855
+ static clear() {
3856
+ this.handlers.clear();
3857
+ }
3858
+ };
3859
+ function processTextWithPlaceholders(text, baseStyle = {}, context = {}) {
3860
+ const normalizedText = normalizeUnicodeText(text);
3861
+ const combinedRegex = /(\*\*\*|___)([\s\S]*?)\1|(\*\*|__)([\s\S]*?)\3|(\*|_)([\s\S]*?)\5|\{([^}]+)\}/g;
3862
+ const result = [];
3863
+ let lastIndex = 0;
3864
+ let match;
3865
+ while ((match = combinedRegex.exec(normalizedText)) !== null) {
3866
+ if (match.index > lastIndex) {
3867
+ const beforeText = normalizedText.substring(lastIndex, match.index);
3868
+ if (beforeText) {
3869
+ result.push(...createTextRunsWithNewlines2(beforeText, baseStyle));
3870
+ }
3998
3871
  }
3999
- if (typeof obj === "object") {
4000
- const sorted = {};
4001
- const keys = Object.keys(obj).sort();
4002
- for (const key of keys) {
4003
- sorted[key] = this.normalizeObject(obj[key]);
3872
+ if (match[7]) {
3873
+ const placeholderName = match[7];
3874
+ const handler = PlaceholderRegistry.get(placeholderName);
3875
+ if (handler) {
3876
+ const placeholderResult = handler({ ...context, style: baseStyle });
3877
+ if (Array.isArray(placeholderResult)) {
3878
+ result.push(...placeholderResult);
3879
+ } else if (placeholderResult instanceof TextRun2) {
3880
+ result.push(placeholderResult);
3881
+ } else if (typeof placeholderResult === "string") {
3882
+ result.push(
3883
+ ...createTextRunsWithNewlines2(placeholderResult, baseStyle)
3884
+ );
3885
+ }
3886
+ } else {
3887
+ result.push(...createTextRunsWithNewlines2(match[0], baseStyle));
4004
3888
  }
4005
- return sorted;
3889
+ } else {
3890
+ let decoratedText;
3891
+ let bold = baseStyle.bold || false;
3892
+ let italics = baseStyle.italics || false;
3893
+ if (match[1] === "***" || match[1] === "___") {
3894
+ decoratedText = match[2];
3895
+ bold = true;
3896
+ italics = true;
3897
+ } else if (match[3] === "**" || match[3] === "__") {
3898
+ decoratedText = match[4];
3899
+ bold = true;
3900
+ } else if (match[5] === "*" || match[5] === "_") {
3901
+ decoratedText = match[6];
3902
+ italics = true;
3903
+ } else {
3904
+ decoratedText = match[0];
3905
+ }
3906
+ const decoratedTextRuns = processTextWithPlaceholders(
3907
+ decoratedText,
3908
+ {
3909
+ ...baseStyle,
3910
+ bold,
3911
+ italics
3912
+ },
3913
+ context
3914
+ );
3915
+ result.push(...decoratedTextRuns);
4006
3916
  }
4007
- return obj;
3917
+ lastIndex = match.index + match[0].length;
4008
3918
  }
4009
- /**
4010
- * Create hash
4011
- */
4012
- hash(input) {
4013
- return createHash("sha256").update(input).digest("hex").substring(0, 16);
3919
+ if (lastIndex < normalizedText.length) {
3920
+ const remainingText = normalizedText.substring(lastIndex);
3921
+ if (remainingText) {
3922
+ result.push(...createTextRunsWithNewlines2(remainingText, baseStyle));
3923
+ }
4014
3924
  }
4015
- };
3925
+ if (result.length === 0 && normalizedText) {
3926
+ result.push(...createTextRunsWithNewlines2(normalizedText, baseStyle));
3927
+ }
3928
+ return result;
3929
+ }
3930
+ function createTextRunsWithNewlines2(text, baseStyle) {
3931
+ const runs = [];
3932
+ const lines = text.split("\n");
3933
+ for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
3934
+ const line = lines[lineIndex];
3935
+ const needsLineBreak = lineIndex > 0;
3936
+ if (line || needsLineBreak) {
3937
+ runs.push(
3938
+ new TextRun2({
3939
+ text: line,
3940
+ font: baseStyle.font,
3941
+ size: baseStyle.size,
3942
+ color: baseStyle.color,
3943
+ bold: baseStyle.bold,
3944
+ italics: baseStyle.italics,
3945
+ underline: baseStyle.underline,
3946
+ break: needsLineBreak ? 1 : void 0
3947
+ })
3948
+ );
3949
+ }
3950
+ }
3951
+ return runs;
3952
+ }
3953
+ function initializeBuiltinPlaceholders() {
3954
+ PlaceholderRegistry.register("PAGE", (context) => {
3955
+ return new TextRun2({
3956
+ children: [PageNumber.CURRENT],
3957
+ font: context?.style?.font,
3958
+ size: context?.style?.size,
3959
+ color: context?.style?.color,
3960
+ bold: context?.style?.bold,
3961
+ italics: context?.style?.italics,
3962
+ underline: context?.style?.underline
3963
+ });
3964
+ });
3965
+ PlaceholderRegistry.register("TOTAL_PAGES", (context) => {
3966
+ return new TextRun2({
3967
+ children: [PageNumber.TOTAL_PAGES],
3968
+ font: context?.style?.font,
3969
+ size: context?.style?.size,
3970
+ color: context?.style?.color,
3971
+ bold: context?.style?.bold,
3972
+ italics: context?.style?.italics,
3973
+ underline: context?.style?.underline
3974
+ });
3975
+ });
3976
+ PlaceholderRegistry.register("DATE", (context) => {
3977
+ const today = /* @__PURE__ */ new Date();
3978
+ const dateString = today.toLocaleDateString();
3979
+ return new TextRun2({
3980
+ text: dateString,
3981
+ font: context?.style?.font,
3982
+ size: context?.style?.size,
3983
+ color: context?.style?.color,
3984
+ bold: context?.style?.bold,
3985
+ italics: context?.style?.italics,
3986
+ underline: context?.style?.underline
3987
+ });
3988
+ });
3989
+ PlaceholderRegistry.register("DATETIME", (context) => {
3990
+ const now = /* @__PURE__ */ new Date();
3991
+ const dateTimeString = now.toLocaleString();
3992
+ return new TextRun2({
3993
+ text: dateTimeString,
3994
+ font: context?.style?.font,
3995
+ size: context?.style?.size,
3996
+ color: context?.style?.color,
3997
+ bold: context?.style?.bold,
3998
+ italics: context?.style?.italics,
3999
+ underline: context?.style?.underline
4000
+ });
4001
+ });
4002
+ PlaceholderRegistry.register("YEAR", (context) => {
4003
+ const year = (/* @__PURE__ */ new Date()).getFullYear().toString();
4004
+ return new TextRun2({
4005
+ text: year,
4006
+ font: context?.style?.font,
4007
+ size: context?.style?.size,
4008
+ color: context?.style?.color,
4009
+ bold: context?.style?.bold,
4010
+ italics: context?.style?.italics,
4011
+ underline: context?.style?.underline
4012
+ });
4013
+ });
4014
+ }
4015
+ initializeBuiltinPlaceholders();
4016
4016
 
4017
4017
  // src/utils/revisionUtils.ts
4018
- import { TextRun as TextRun3, InsertedTextRun, DeletedTextRun } from "docx";
4019
4018
  var DEFAULT_REVISION_AUTHOR = "json-to-office";
4020
4019
  var DEFAULT_REVISION_DATE = "1970-01-01T00:00:00Z";
4021
4020
  var RevisionIdRegistry = class {
@@ -6772,21 +6771,26 @@ async function renderHeaderFooterComponents(components, theme, themeName, contex
6772
6771
  for (const component of activeComponents) {
6773
6772
  if (isParagraphComponent(component)) {
6774
6773
  const textComp = component;
6774
+ const font = textComp.props.font;
6775
6775
  const normalStyle = getNormalStyle(theme);
6776
- const textStyle = {
6777
- font: textComp.props.font?.family || resolveFontFamily(theme, normalStyle.font) || getThemeFonts(theme).body.family,
6778
- size: (textComp.props.font?.size ?? normalStyle.size ?? 11) * 2,
6779
- // Convert to half-points
6780
- bold: textComp.props.font?.bold ?? false,
6781
- italics: textComp.props.font?.italic ?? false,
6782
- color: textComp.props.font?.color && resolveColor(textComp.props.font.color, theme) || normalStyle.color && resolveColor(normalStyle.color, theme) || getThemeColors(theme).textPrimary
6783
- };
6784
- const textRuns = parseTextWithDecorators(textComp.props.text, textStyle);
6785
6776
  elements.push(
6786
- new Paragraph7({
6787
- children: textRuns,
6788
- alignment: textComp.props.alignment ? getAlignment3(textComp.props.alignment) : void 0,
6789
- style: "Normal"
6777
+ createText(textComp.props.text, theme, themeName, {
6778
+ style: "Normal",
6779
+ alignment: textComp.props.alignment,
6780
+ // Resolve explicit run styling against the Normal style, preserving
6781
+ // prior header/footer rendering for these properties.
6782
+ fontFamily: font?.family || resolveFontFamily(theme, normalStyle.font) || getThemeFonts(theme).body.family,
6783
+ fontSize: font?.size ?? normalStyle.size ?? 11,
6784
+ // Pass the raw color/token; createText resolves it. Fall back to the
6785
+ // Normal style color, then the theme's primary text color.
6786
+ fontColor: font?.color || normalStyle.color || "textPrimary",
6787
+ bold: font?.bold ?? false,
6788
+ italic: font?.italic ?? false,
6789
+ underline: font?.underline,
6790
+ fontWeight: font?.fontWeight,
6791
+ boldColor: textComp.props.boldColor,
6792
+ spacing: textComp.props.spacing,
6793
+ lineSpacing: font?.lineSpacing
6790
6794
  })
6791
6795
  );
6792
6796
  } else if (isImageComponent(component)) {
@@ -7133,7 +7137,7 @@ function createBuilderImpl(state) {
7133
7137
  }
7134
7138
  return getThemeWithFallback(themeName);
7135
7139
  }
7136
- async function processDocumentComponents(components, preserveSet, warningsCollector, resolvedTheme, depth = 0) {
7140
+ async function processDocumentComponents(components, preserveSet, warningsCollector, resolvedTheme, validateEmitted, depth = 0) {
7137
7141
  if (depth > 20) {
7138
7142
  throw new Error(
7139
7143
  "Maximum component nesting depth exceeded (20). Check for circular component references."
@@ -7173,6 +7177,7 @@ function createBuilderImpl(state) {
7173
7177
  preserveSet,
7174
7178
  warningsCollector,
7175
7179
  resolvedTheme,
7180
+ validateEmitted,
7176
7181
  depth + 1
7177
7182
  );
7178
7183
  nestedChildren = nested.standard;
@@ -7193,11 +7198,15 @@ function createBuilderImpl(state) {
7193
7198
  children: nestedChildren
7194
7199
  });
7195
7200
  const resultComponents = Array.isArray(result) ? result : [result];
7201
+ if (validateEmitted) {
7202
+ validateEmitted(resultComponents, versionLabel);
7203
+ }
7196
7204
  const processedResult = await processDocumentComponents(
7197
7205
  resultComponents,
7198
7206
  preserveSet,
7199
7207
  warningsCollector,
7200
7208
  resolvedTheme,
7209
+ validateEmitted,
7201
7210
  depth + 1
7202
7211
  );
7203
7212
  standardOut.push(...processedResult.standard);
@@ -7227,6 +7236,7 @@ function createBuilderImpl(state) {
7227
7236
  preserveSet,
7228
7237
  warningsCollector,
7229
7238
  resolvedTheme,
7239
+ validateEmitted,
7230
7240
  depth + 1
7231
7241
  );
7232
7242
  standardOut.push({
@@ -7307,6 +7317,22 @@ function createBuilderImpl(state) {
7307
7317
  );
7308
7318
  }
7309
7319
  }
7320
+ const validateEmitted = vOpts.enabled === false ? void 0 : (emitted, componentLabel) => {
7321
+ const result = validateDocument(
7322
+ { ...internalDocument, children: emitted },
7323
+ state.components,
7324
+ { allowUnknownFields: vOpts.allowUnknownFields }
7325
+ );
7326
+ if (!result.valid) {
7327
+ throw new ComponentValidationError2(
7328
+ (result.errors ?? []).map((e) => ({
7329
+ path: e.path ?? "",
7330
+ message: `custom component '${componentLabel}' emitted invalid output \u2014 ${e.message}`
7331
+ })),
7332
+ emitted
7333
+ );
7334
+ }
7335
+ };
7310
7336
  const baseThemeName = internalDocument.props.theme || "minimal";
7311
7337
  const docTheme = resolveDocumentTheme(baseThemeName);
7312
7338
  const warnings = [];
@@ -7329,7 +7355,8 @@ function createBuilderImpl(state) {
7329
7355
  mode.doc.children || [],
7330
7356
  preserveSet,
7331
7357
  warnings,
7332
- modedTheme
7358
+ modedTheme,
7359
+ validateEmitted
7333
7360
  );
7334
7361
  const processedDocument = {
7335
7362
  ...mode.doc,