@json-to-office/core-docx 0.19.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.
Files changed (36) hide show
  1. package/dist/components/highcharts.d.ts.map +1 -1
  2. package/dist/components/section.d.ts.map +1 -1
  3. package/dist/core/cached-render.d.ts.map +1 -1
  4. package/dist/core/content.d.ts.map +1 -1
  5. package/dist/core/generator.d.ts +6 -2
  6. package/dist/core/generator.d.ts.map +1 -1
  7. package/dist/core/render.d.ts +8 -6
  8. package/dist/core/render.d.ts.map +1 -1
  9. package/dist/core/structure.d.ts +2 -2
  10. package/dist/core/structure.d.ts.map +1 -1
  11. package/dist/index.js +414 -345
  12. package/dist/index.js.map +1 -1
  13. package/dist/plugin/createDocumentGenerator.d.ts +4 -0
  14. package/dist/plugin/createDocumentGenerator.d.ts.map +1 -1
  15. package/dist/plugin/example/index.js +394 -287
  16. package/dist/plugin/example/index.js.map +1 -1
  17. package/dist/plugin/types.d.ts +4 -0
  18. package/dist/plugin/types.d.ts.map +1 -1
  19. package/dist/tsconfig.tsbuildinfo +1 -1
  20. package/dist/utils/bookmarkRegistry.d.ts +5 -2
  21. package/dist/utils/bookmarkRegistry.d.ts.map +1 -1
  22. package/dist/utils/fixFloatingImageIds.d.ts +3 -0
  23. package/dist/utils/fixFloatingImageIds.d.ts.map +1 -1
  24. package/dist/utils/generationContext.d.ts +5 -0
  25. package/dist/utils/generationContext.d.ts.map +1 -0
  26. package/dist/utils/numberingConfig.d.ts +5 -2
  27. package/dist/utils/numberingConfig.d.ts.map +1 -1
  28. package/dist/utils/packageDocument.d.ts +20 -0
  29. package/dist/utils/packageDocument.d.ts.map +1 -0
  30. package/dist/utils/placeholderProcessor.d.ts +5 -5
  31. package/dist/utils/placeholderProcessor.d.ts.map +1 -1
  32. package/dist/utils/revisionUtils.d.ts +5 -5
  33. package/dist/utils/revisionUtils.d.ts.map +1 -1
  34. package/dist/utils/textParser.d.ts +3 -3
  35. package/dist/utils/textParser.d.ts.map +1 -1
  36. package/package.json +3 -3
@@ -1613,219 +1613,11 @@ var init_docxImagePositioning = __esm({
1613
1613
  }
1614
1614
  });
1615
1615
 
1616
- // src/utils/numberingConfig.ts
1617
- var numberingConfig_exports = {};
1618
- __export(numberingConfig_exports, {
1619
- NumberingRegistry: () => NumberingRegistry,
1620
- createBulletListConfig: () => createBulletListConfig,
1621
- createNumberedListConfig: () => createNumberedListConfig,
1622
- createNumberingConfig: () => createNumberingConfig,
1623
- globalNumberingRegistry: () => globalNumberingRegistry
1624
- });
1625
- import { AlignmentType as AlignmentType3, convertInchesToTwip, LevelFormat } from "docx";
1626
- function getLevelFormat(format2) {
1627
- if (!format2) return LevelFormat.BULLET;
1628
- return LEVEL_FORMAT_MAP[format2] || LevelFormat.BULLET;
1629
- }
1630
- function getAlignment2(alignment) {
1631
- if (!alignment) return AlignmentType3.LEFT;
1632
- return ALIGNMENT_MAP[alignment] || AlignmentType3.LEFT;
1633
- }
1634
- function createDefaultLevel(level, format2 = LevelFormat.BULLET, text) {
1635
- const baseIndent = 0.5 * (level + 1);
1636
- const hangingIndent = 0.25;
1637
- return {
1638
- level,
1639
- format: format2,
1640
- text: text || (format2 === LevelFormat.BULLET ? "\u2022" : "%1."),
1641
- alignment: AlignmentType3.LEFT,
1642
- style: {
1643
- paragraph: {
1644
- indent: {
1645
- left: convertInchesToTwip(baseIndent),
1646
- hanging: convertInchesToTwip(hangingIndent)
1647
- }
1648
- }
1649
- }
1650
- };
1651
- }
1652
- function createNumberingConfig(config) {
1653
- const levels = [];
1654
- for (const levelConfig of config.levels) {
1655
- const format2 = getLevelFormat(levelConfig.format);
1656
- const alignment = getAlignment2(levelConfig.alignment);
1657
- const text = levelConfig.text || (format2 === LevelFormat.BULLET ? "\u2022" : `%${levelConfig.level + 1}.`);
1658
- const baseIndent = levelConfig.indent?.left !== void 0 ? levelConfig.indent.left / 72 : 0.5 * (levelConfig.level + 1);
1659
- const hangingIndent = levelConfig.indent?.hanging !== void 0 ? levelConfig.indent.hanging / 72 : 0.25;
1660
- const level = {
1661
- level: levelConfig.level,
1662
- format: format2,
1663
- text,
1664
- alignment,
1665
- style: {
1666
- paragraph: {
1667
- indent: {
1668
- left: convertInchesToTwip(baseIndent),
1669
- hanging: convertInchesToTwip(hangingIndent)
1670
- }
1671
- }
1672
- },
1673
- // Add start number if specified
1674
- ...levelConfig.start !== void 0 && { start: levelConfig.start }
1675
- };
1676
- levels.push(level);
1677
- }
1678
- return {
1679
- reference: config.reference,
1680
- levels
1681
- };
1682
- }
1683
- function createBulletListConfig(reference, bullet = "\u2022") {
1684
- return {
1685
- reference,
1686
- levels: [
1687
- createDefaultLevel(0, LevelFormat.BULLET, bullet),
1688
- createDefaultLevel(1, LevelFormat.BULLET, "\u25E6"),
1689
- createDefaultLevel(2, LevelFormat.BULLET, "\u25AA")
1690
- ]
1691
- };
1692
- }
1693
- function createNumberedListConfig(reference, start = 1) {
1694
- return {
1695
- reference,
1696
- levels: [
1697
- { ...createDefaultLevel(0, LevelFormat.DECIMAL, "%1."), start },
1698
- createDefaultLevel(1, LevelFormat.LOWER_LETTER, "%2."),
1699
- createDefaultLevel(2, LevelFormat.LOWER_ROMAN, "%3.")
1700
- ]
1701
- };
1702
- }
1703
- var LEVEL_FORMAT_MAP, ALIGNMENT_MAP, NumberingRegistry, globalNumberingRegistry;
1704
- var init_numberingConfig = __esm({
1705
- "src/utils/numberingConfig.ts"() {
1706
- "use strict";
1707
- LEVEL_FORMAT_MAP = {
1708
- decimal: LevelFormat.DECIMAL,
1709
- upperRoman: LevelFormat.UPPER_ROMAN,
1710
- lowerRoman: LevelFormat.LOWER_ROMAN,
1711
- upperLetter: LevelFormat.UPPER_LETTER,
1712
- lowerLetter: LevelFormat.LOWER_LETTER,
1713
- bullet: LevelFormat.BULLET,
1714
- ordinal: LevelFormat.ORDINAL,
1715
- cardinalText: LevelFormat.CARDINAL_TEXT,
1716
- ordinalText: LevelFormat.ORDINAL_TEXT,
1717
- hex: LevelFormat.HEX,
1718
- chicago: LevelFormat.CHICAGO,
1719
- ideographDigital: LevelFormat.IDEOGRAPH__DIGITAL,
1720
- japaneseCounting: LevelFormat.JAPANESE_COUNTING,
1721
- aiueo: LevelFormat.AIUEO,
1722
- iroha: LevelFormat.IROHA,
1723
- decimalFullWidth: LevelFormat.DECIMAL_FULL_WIDTH,
1724
- decimalHalfWidth: LevelFormat.DECIMAL_HALF_WIDTH,
1725
- japaneseLegal: LevelFormat.JAPANESE_LEGAL,
1726
- japaneseDigitalTenThousand: LevelFormat.JAPANESE_DIGITAL_TEN_THOUSAND,
1727
- decimalEnclosedCircle: LevelFormat.DECIMAL_ENCLOSED_CIRCLE,
1728
- decimalFullWidth2: LevelFormat.DECIMAL_FULL_WIDTH2,
1729
- aiueoFullWidth: LevelFormat.AIUEO_FULL_WIDTH,
1730
- irohaFullWidth: LevelFormat.IROHA_FULL_WIDTH,
1731
- decimalZero: LevelFormat.DECIMAL_ZERO,
1732
- ganada: LevelFormat.GANADA,
1733
- chosung: LevelFormat.CHOSUNG,
1734
- decimalEnclosedFullstop: LevelFormat.DECIMAL_ENCLOSED_FULLSTOP,
1735
- decimalEnclosedParen: LevelFormat.DECIMAL_ENCLOSED_PARENTHESES,
1736
- decimalEnclosedCircleChinese: LevelFormat.DECIMAL_ENCLOSED_CIRCLE_CHINESE,
1737
- ideographEnclosedCircle: LevelFormat.IDEOGRAPH_ENCLOSED_CIRCLE,
1738
- ideographTraditional: LevelFormat.IDEOGRAPH_TRADITIONAL,
1739
- ideographZodiac: LevelFormat.IDEOGRAPH_ZODIAC,
1740
- ideographZodiacTraditional: LevelFormat.IDEOGRAPH_ZODIAC_TRADITIONAL,
1741
- taiwaneseCounting: LevelFormat.TAIWANESE_COUNTING,
1742
- ideographLegalTraditional: LevelFormat.IDEOGRAPH_LEGAL_TRADITIONAL,
1743
- taiwaneseCountingThousand: LevelFormat.TAIWANESE_COUNTING_THOUSAND,
1744
- taiwaneseDigital: LevelFormat.TAIWANESE_DIGITAL,
1745
- chineseCounting: LevelFormat.CHINESE_COUNTING,
1746
- chineseLegalSimplified: LevelFormat.CHINESE_LEGAL_SIMPLIFIED,
1747
- chineseCountingThousand: LevelFormat.CHINESE_COUNTING_THOUSAND,
1748
- koreanDigital: LevelFormat.KOREAN_DIGITAL,
1749
- koreanCounting: LevelFormat.KOREAN_COUNTING,
1750
- koreanLegal: LevelFormat.KOREAN_LEGAL,
1751
- koreanDigital2: LevelFormat.KOREAN_DIGITAL2,
1752
- vietnameseCounting: LevelFormat.VIETNAMESE_COUNTING,
1753
- russianLower: LevelFormat.RUSSIAN_LOWER,
1754
- russianUpper: LevelFormat.RUSSIAN_UPPER,
1755
- none: LevelFormat.NONE,
1756
- numberInDash: LevelFormat.NUMBER_IN_DASH,
1757
- hebrew1: LevelFormat.HEBREW1,
1758
- hebrew2: LevelFormat.HEBREW2,
1759
- arabicAlpha: LevelFormat.ARABIC_ALPHA,
1760
- arabicAbjad: LevelFormat.ARABIC_ABJAD,
1761
- hindiVowels: LevelFormat.HINDI_VOWELS,
1762
- hindiConsonants: LevelFormat.HINDI_CONSONANTS,
1763
- hindiNumbers: LevelFormat.HINDI_NUMBERS,
1764
- hindiCounting: LevelFormat.HINDI_COUNTING,
1765
- thaiLetters: LevelFormat.THAI_LETTERS,
1766
- thaiNumbers: LevelFormat.THAI_NUMBERS,
1767
- thaiCounting: LevelFormat.THAI_COUNTING
1768
- };
1769
- ALIGNMENT_MAP = {
1770
- start: AlignmentType3.START,
1771
- end: AlignmentType3.END,
1772
- left: AlignmentType3.LEFT,
1773
- right: AlignmentType3.RIGHT,
1774
- center: AlignmentType3.CENTER
1775
- };
1776
- NumberingRegistry = class {
1777
- configs = /* @__PURE__ */ new Map();
1778
- counter = 0;
1779
- /**
1780
- * Register a numbering configuration
1781
- */
1782
- register(config) {
1783
- const reference = config.reference;
1784
- this.configs.set(reference, config);
1785
- return reference;
1786
- }
1787
- /**
1788
- * Generate a unique reference ID
1789
- */
1790
- generateReference(prefix = "list") {
1791
- return `${prefix}-${++this.counter}`;
1792
- }
1793
- /**
1794
- * Get all registered configurations as an array suitable for INumberingOptions
1795
- */
1796
- getAll() {
1797
- return Array.from(this.configs.values());
1798
- }
1799
- /**
1800
- * Clear all configurations
1801
- */
1802
- clear() {
1803
- this.configs.clear();
1804
- this.counter = 0;
1805
- }
1806
- /**
1807
- * Check if a reference exists
1808
- */
1809
- has(reference) {
1810
- return this.configs.has(reference);
1811
- }
1812
- /**
1813
- * Get a configuration by reference
1814
- */
1815
- get(reference) {
1816
- return this.configs.get(reference);
1817
- }
1818
- };
1819
- globalNumberingRegistry = new NumberingRegistry();
1820
- }
1821
- });
1822
-
1823
1616
  // src/plugin/example/index.ts
1824
1617
  import { Packer as Packer2 } from "docx";
1825
1618
 
1826
1619
  // src/plugin/createDocumentGenerator.ts
1827
1620
  init_styles();
1828
- import { Packer } from "docx";
1829
1621
  import { applyExportMode, scopedThemeName } from "@json-to-office/shared";
1830
1622
 
1831
1623
  // src/core/fontResolution.ts
@@ -2192,8 +1984,8 @@ function resolveComponentTree(components, theme) {
2192
1984
  }
2193
1985
 
2194
1986
  // src/core/structure.ts
2195
- async function processDocument(document, theme, themeName) {
2196
- const metadata = createDocumentMetadata(document.props);
1987
+ async function processDocument(document, theme, themeName, generationDate) {
1988
+ const metadata = createDocumentMetadata(document.props, generationDate);
2197
1989
  const docDefaults = document.props.componentDefaults;
2198
1990
  const mergedComponentDefaults = docDefaults ? mergeWithDefaults2(docDefaults, theme.componentDefaults || {}) : void 0;
2199
1991
  const docNoProofWords = document.props.noProofWords;
@@ -2231,13 +2023,13 @@ async function processDocument(document, theme, themeName) {
2231
2023
  language: document.props.language
2232
2024
  };
2233
2025
  }
2234
- function createDocumentMetadata(props) {
2026
+ function createDocumentMetadata(props, generationDate = /* @__PURE__ */ new Date()) {
2235
2027
  return {
2236
2028
  title: props.metadata?.title,
2237
2029
  subtitle: props.metadata?.subtitle,
2238
2030
  author: props.metadata?.author,
2239
2031
  company: props.metadata?.company,
2240
- date: props.metadata?.date ? new Date(props.metadata.date) : /* @__PURE__ */ new Date()
2032
+ date: props.metadata?.date ? new Date(props.metadata.date) : generationDate
2241
2033
  };
2242
2034
  }
2243
2035
  async function extractSections(components, context) {
@@ -3900,7 +3692,39 @@ function parseTextWithHyperlinks(text, baseStyle = {}, options = {}) {
3900
3692
  return runs;
3901
3693
  }
3902
3694
 
3695
+ // src/utils/generationContext.ts
3696
+ import { AsyncLocalStorage } from "async_hooks";
3697
+ var generationDateStorage = new AsyncLocalStorage();
3698
+ function runWithGenerationDate(date, callback) {
3699
+ return generationDateStorage.run(date, callback);
3700
+ }
3701
+ function getGenerationDate() {
3702
+ return generationDateStorage.getStore() ?? /* @__PURE__ */ new Date();
3703
+ }
3704
+
3903
3705
  // src/utils/placeholderProcessor.ts
3706
+ function styledPlaceholderText(text, context) {
3707
+ return new TextRun2({
3708
+ text,
3709
+ font: context?.style?.font,
3710
+ size: context?.style?.size,
3711
+ color: context?.style?.color,
3712
+ bold: context?.style?.bold,
3713
+ italics: context?.style?.italics,
3714
+ underline: context?.style?.underline,
3715
+ language: context?.style?.language,
3716
+ noProof: context?.style?.noProof
3717
+ });
3718
+ }
3719
+ function placeholderDate(context) {
3720
+ return context?.date ?? getGenerationDate();
3721
+ }
3722
+ function isoDate(date) {
3723
+ return date.toISOString().slice(0, 10);
3724
+ }
3725
+ function isoDateTime(date) {
3726
+ return date.toISOString().replace("T", " ").replace(/\.\d{3}Z$/, "Z");
3727
+ }
3904
3728
  var PlaceholderRegistry = class {
3905
3729
  static handlers = /* @__PURE__ */ new Map();
3906
3730
  /**
@@ -3956,8 +3780,6 @@ function processTextWithPlaceholders(text, baseStyle = {}, context = {}, noProof
3956
3780
  const placeholderResult = handler({ ...context, style: baseStyle });
3957
3781
  if (Array.isArray(placeholderResult)) {
3958
3782
  result.push(...placeholderResult);
3959
- } else if (placeholderResult instanceof TextRun2) {
3960
- result.push(placeholderResult);
3961
3783
  } else if (typeof placeholderResult === "string") {
3962
3784
  result.push(
3963
3785
  ...createTextRunsWithNewlines2(
@@ -3966,6 +3788,8 @@ function processTextWithPlaceholders(text, baseStyle = {}, context = {}, noProof
3966
3788
  noProofWords
3967
3789
  )
3968
3790
  );
3791
+ } else {
3792
+ result.push(placeholderResult);
3969
3793
  }
3970
3794
  } else {
3971
3795
  result.push(
@@ -4080,59 +3904,51 @@ function initializeBuiltinPlaceholders() {
4080
3904
  underline: context?.style?.underline
4081
3905
  });
4082
3906
  });
4083
- PlaceholderRegistry.register("DATE", (context) => {
4084
- const today = /* @__PURE__ */ new Date();
4085
- const dateString = today.toLocaleDateString();
4086
- return new TextRun2({
4087
- text: dateString,
4088
- font: context?.style?.font,
4089
- size: context?.style?.size,
4090
- color: context?.style?.color,
4091
- bold: context?.style?.bold,
4092
- italics: context?.style?.italics,
4093
- underline: context?.style?.underline
4094
- });
4095
- });
4096
- PlaceholderRegistry.register("DATETIME", (context) => {
4097
- const now = /* @__PURE__ */ new Date();
4098
- const dateTimeString = now.toLocaleString();
4099
- return new TextRun2({
4100
- text: dateTimeString,
4101
- font: context?.style?.font,
4102
- size: context?.style?.size,
4103
- color: context?.style?.color,
4104
- bold: context?.style?.bold,
4105
- italics: context?.style?.italics,
4106
- underline: context?.style?.underline
4107
- });
4108
- });
4109
- PlaceholderRegistry.register("YEAR", (context) => {
4110
- const year = (/* @__PURE__ */ new Date()).getFullYear().toString();
4111
- return new TextRun2({
4112
- text: year,
4113
- font: context?.style?.font,
4114
- size: context?.style?.size,
4115
- color: context?.style?.color,
4116
- bold: context?.style?.bold,
4117
- italics: context?.style?.italics,
4118
- underline: context?.style?.underline
4119
- });
4120
- });
3907
+ PlaceholderRegistry.register(
3908
+ "DATE",
3909
+ (context) => styledPlaceholderText(isoDate(placeholderDate(context)), context)
3910
+ );
3911
+ PlaceholderRegistry.register(
3912
+ "DATETIME",
3913
+ (context) => styledPlaceholderText(isoDateTime(placeholderDate(context)), context)
3914
+ );
3915
+ PlaceholderRegistry.register(
3916
+ "YEAR",
3917
+ (context) => styledPlaceholderText(
3918
+ String(placeholderDate(context).getUTCFullYear()),
3919
+ context
3920
+ )
3921
+ );
4121
3922
  }
4122
3923
  initializeBuiltinPlaceholders();
4123
3924
 
4124
3925
  // src/utils/revisionUtils.ts
3926
+ import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
4125
3927
  var DEFAULT_REVISION_AUTHOR = "json-to-office";
4126
3928
  var DEFAULT_REVISION_DATE = "1970-01-01T00:00:00Z";
4127
3929
  var RevisionIdRegistry = class {
4128
- counter = 0;
3930
+ fallbackCounter = 0;
3931
+ scopes = new AsyncLocalStorage2();
3932
+ runScoped(callback) {
3933
+ return this.scopes.run({ counter: 0 }, callback);
3934
+ }
4129
3935
  next() {
4130
- this.counter += 1;
4131
- return this.counter;
3936
+ const state = this.scopes.getStore();
3937
+ if (state) {
3938
+ state.counter += 1;
3939
+ return state.counter;
3940
+ }
3941
+ this.fallbackCounter += 1;
3942
+ return this.fallbackCounter;
4132
3943
  }
4133
3944
  /** Test-only: deterministic ids for snapshot assertions. */
4134
3945
  clear() {
4135
- this.counter = 0;
3946
+ const state = this.scopes.getStore();
3947
+ if (state) {
3948
+ state.counter = 0;
3949
+ } else {
3950
+ this.fallbackCounter = 0;
3951
+ }
4136
3952
  }
4137
3953
  };
4138
3954
  var globalRevisionIdRegistry = new RevisionIdRegistry();
@@ -4252,7 +4068,10 @@ function initializeComponentCache(cache) {
4252
4068
  }
4253
4069
  }
4254
4070
  async function renderComponentWithCache(component, theme, themeName, context, bypassCache = false) {
4255
- const forceBypassForType = component.name === "toc" || component.name === "section" || component.name === "visual" || componentHasRevision(component);
4071
+ const forceBypassForType = component.name === "toc" || component.name === "section" || component.name === "visual" || component.name === "heading" || // Both explicit lists and markdown-list paragraphs register numbering in
4072
+ // the current document scope. Cached paragraphs can otherwise reference
4073
+ // definitions that only existed in a previous render.
4074
+ component.name === "list" || component.name === "paragraph" || "id" in component || componentHasRevision(component);
4256
4075
  if (!componentCache) {
4257
4076
  initializeComponentCache();
4258
4077
  }
@@ -4262,8 +4081,9 @@ async function renderComponentWithCache(component, theme, themeName, context, by
4262
4081
  const componentProps = JSON.stringify(component.props || {});
4263
4082
  const themeHash = createThemeHash(theme);
4264
4083
  const contextKey = context?.section ? `${context.section.currentLayout}:${context.section.columnCount}` : "no-section";
4084
+ const generationDateKey = getGenerationDate().toISOString();
4265
4085
  const childrenKey = "children" in component && component.children ? `:children:${JSON.stringify(component.children)}` : "";
4266
- const cacheKey = `component:${component.name}:${themeHash}:${contextKey}:${componentProps}${childrenKey}`;
4086
+ const cacheKey = `component:${component.name}:${themeHash}:${contextKey}:${generationDateKey}:${componentProps}${childrenKey}`;
4267
4087
  const cached = await componentCache.get(cacheKey);
4268
4088
  if (cached) {
4269
4089
  return cached.result;
@@ -4311,19 +4131,27 @@ init_styles();
4311
4131
  init_defaults();
4312
4132
 
4313
4133
  // src/utils/bookmarkRegistry.ts
4134
+ import { AsyncLocalStorage as AsyncLocalStorage3 } from "async_hooks";
4314
4135
  var BookmarkRegistry = class {
4315
- bookmarks = /* @__PURE__ */ new Map();
4316
- counter = 0;
4136
+ fallback = { bookmarks: /* @__PURE__ */ new Map() };
4137
+ scopes = new AsyncLocalStorage3();
4138
+ get state() {
4139
+ return this.scopes.getStore() ?? this.fallback;
4140
+ }
4141
+ /** Run work with an isolated registry that follows its async call chain. */
4142
+ runScoped(callback) {
4143
+ return this.scopes.run({ bookmarks: /* @__PURE__ */ new Map() }, callback);
4144
+ }
4317
4145
  /**
4318
4146
  * Register a bookmark
4319
4147
  */
4320
4148
  register(id, title, type) {
4321
- if (this.bookmarks.has(id)) {
4149
+ if (this.state.bookmarks.has(id)) {
4322
4150
  console.warn(
4323
4151
  `Duplicate bookmark ID: ${id}. Using the latest registration.`
4324
4152
  );
4325
4153
  }
4326
- this.bookmarks.set(id, { id, title, type });
4154
+ this.state.bookmarks.set(id, { id, title, type });
4327
4155
  }
4328
4156
  /**
4329
4157
  * Generate a unique bookmark ID from text
@@ -4333,7 +4161,7 @@ var BookmarkRegistry = class {
4333
4161
  const baseId = text.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "").substring(0, 40);
4334
4162
  let id = baseId;
4335
4163
  let attempt = 0;
4336
- while (this.bookmarks.has(id) && attempt < 100) {
4164
+ while (this.state.bookmarks.has(id) && attempt < 100) {
4337
4165
  id = `${baseId}-${++attempt}`;
4338
4166
  }
4339
4167
  return id;
@@ -4342,26 +4170,25 @@ var BookmarkRegistry = class {
4342
4170
  * Check if a bookmark exists
4343
4171
  */
4344
4172
  exists(id) {
4345
- return this.bookmarks.has(id);
4173
+ return this.state.bookmarks.has(id);
4346
4174
  }
4347
4175
  /**
4348
4176
  * Get bookmark info by ID
4349
4177
  */
4350
4178
  get(id) {
4351
- return this.bookmarks.get(id);
4179
+ return this.state.bookmarks.get(id);
4352
4180
  }
4353
4181
  /**
4354
4182
  * Get all registered bookmarks
4355
4183
  */
4356
4184
  getAll() {
4357
- return Array.from(this.bookmarks.values());
4185
+ return Array.from(this.state.bookmarks.values());
4358
4186
  }
4359
4187
  /**
4360
4188
  * Clear all bookmarks
4361
4189
  */
4362
4190
  clear() {
4363
- this.bookmarks.clear();
4364
- this.counter = 0;
4191
+ this.state.bookmarks.clear();
4365
4192
  }
4366
4193
  /**
4367
4194
  * Validate that all internal hyperlink references exist
@@ -5649,8 +5476,173 @@ function renderHeadingComponent(component, theme, themeName) {
5649
5476
  return [header];
5650
5477
  }
5651
5478
 
5479
+ // src/utils/numberingConfig.ts
5480
+ import { AlignmentType as AlignmentType3, convertInchesToTwip, LevelFormat } from "docx";
5481
+ import { AsyncLocalStorage as AsyncLocalStorage4 } from "async_hooks";
5482
+ var LEVEL_FORMAT_MAP = {
5483
+ decimal: LevelFormat.DECIMAL,
5484
+ upperRoman: LevelFormat.UPPER_ROMAN,
5485
+ lowerRoman: LevelFormat.LOWER_ROMAN,
5486
+ upperLetter: LevelFormat.UPPER_LETTER,
5487
+ lowerLetter: LevelFormat.LOWER_LETTER,
5488
+ bullet: LevelFormat.BULLET,
5489
+ ordinal: LevelFormat.ORDINAL,
5490
+ cardinalText: LevelFormat.CARDINAL_TEXT,
5491
+ ordinalText: LevelFormat.ORDINAL_TEXT,
5492
+ hex: LevelFormat.HEX,
5493
+ chicago: LevelFormat.CHICAGO,
5494
+ ideographDigital: LevelFormat.IDEOGRAPH__DIGITAL,
5495
+ japaneseCounting: LevelFormat.JAPANESE_COUNTING,
5496
+ aiueo: LevelFormat.AIUEO,
5497
+ iroha: LevelFormat.IROHA,
5498
+ decimalFullWidth: LevelFormat.DECIMAL_FULL_WIDTH,
5499
+ decimalHalfWidth: LevelFormat.DECIMAL_HALF_WIDTH,
5500
+ japaneseLegal: LevelFormat.JAPANESE_LEGAL,
5501
+ japaneseDigitalTenThousand: LevelFormat.JAPANESE_DIGITAL_TEN_THOUSAND,
5502
+ decimalEnclosedCircle: LevelFormat.DECIMAL_ENCLOSED_CIRCLE,
5503
+ decimalFullWidth2: LevelFormat.DECIMAL_FULL_WIDTH2,
5504
+ aiueoFullWidth: LevelFormat.AIUEO_FULL_WIDTH,
5505
+ irohaFullWidth: LevelFormat.IROHA_FULL_WIDTH,
5506
+ decimalZero: LevelFormat.DECIMAL_ZERO,
5507
+ ganada: LevelFormat.GANADA,
5508
+ chosung: LevelFormat.CHOSUNG,
5509
+ decimalEnclosedFullstop: LevelFormat.DECIMAL_ENCLOSED_FULLSTOP,
5510
+ decimalEnclosedParen: LevelFormat.DECIMAL_ENCLOSED_PARENTHESES,
5511
+ decimalEnclosedCircleChinese: LevelFormat.DECIMAL_ENCLOSED_CIRCLE_CHINESE,
5512
+ ideographEnclosedCircle: LevelFormat.IDEOGRAPH_ENCLOSED_CIRCLE,
5513
+ ideographTraditional: LevelFormat.IDEOGRAPH_TRADITIONAL,
5514
+ ideographZodiac: LevelFormat.IDEOGRAPH_ZODIAC,
5515
+ ideographZodiacTraditional: LevelFormat.IDEOGRAPH_ZODIAC_TRADITIONAL,
5516
+ taiwaneseCounting: LevelFormat.TAIWANESE_COUNTING,
5517
+ ideographLegalTraditional: LevelFormat.IDEOGRAPH_LEGAL_TRADITIONAL,
5518
+ taiwaneseCountingThousand: LevelFormat.TAIWANESE_COUNTING_THOUSAND,
5519
+ taiwaneseDigital: LevelFormat.TAIWANESE_DIGITAL,
5520
+ chineseCounting: LevelFormat.CHINESE_COUNTING,
5521
+ chineseLegalSimplified: LevelFormat.CHINESE_LEGAL_SIMPLIFIED,
5522
+ chineseCountingThousand: LevelFormat.CHINESE_COUNTING_THOUSAND,
5523
+ koreanDigital: LevelFormat.KOREAN_DIGITAL,
5524
+ koreanCounting: LevelFormat.KOREAN_COUNTING,
5525
+ koreanLegal: LevelFormat.KOREAN_LEGAL,
5526
+ koreanDigital2: LevelFormat.KOREAN_DIGITAL2,
5527
+ vietnameseCounting: LevelFormat.VIETNAMESE_COUNTING,
5528
+ russianLower: LevelFormat.RUSSIAN_LOWER,
5529
+ russianUpper: LevelFormat.RUSSIAN_UPPER,
5530
+ none: LevelFormat.NONE,
5531
+ numberInDash: LevelFormat.NUMBER_IN_DASH,
5532
+ hebrew1: LevelFormat.HEBREW1,
5533
+ hebrew2: LevelFormat.HEBREW2,
5534
+ arabicAlpha: LevelFormat.ARABIC_ALPHA,
5535
+ arabicAbjad: LevelFormat.ARABIC_ABJAD,
5536
+ hindiVowels: LevelFormat.HINDI_VOWELS,
5537
+ hindiConsonants: LevelFormat.HINDI_CONSONANTS,
5538
+ hindiNumbers: LevelFormat.HINDI_NUMBERS,
5539
+ hindiCounting: LevelFormat.HINDI_COUNTING,
5540
+ thaiLetters: LevelFormat.THAI_LETTERS,
5541
+ thaiNumbers: LevelFormat.THAI_NUMBERS,
5542
+ thaiCounting: LevelFormat.THAI_COUNTING
5543
+ };
5544
+ var ALIGNMENT_MAP = {
5545
+ start: AlignmentType3.START,
5546
+ end: AlignmentType3.END,
5547
+ left: AlignmentType3.LEFT,
5548
+ right: AlignmentType3.RIGHT,
5549
+ center: AlignmentType3.CENTER
5550
+ };
5551
+ function getLevelFormat(format2) {
5552
+ if (!format2) return LevelFormat.BULLET;
5553
+ return LEVEL_FORMAT_MAP[format2] || LevelFormat.BULLET;
5554
+ }
5555
+ function getAlignment2(alignment) {
5556
+ if (!alignment) return AlignmentType3.LEFT;
5557
+ return ALIGNMENT_MAP[alignment] || AlignmentType3.LEFT;
5558
+ }
5559
+ function createNumberingConfig(config) {
5560
+ const levels = [];
5561
+ for (const levelConfig of config.levels) {
5562
+ const format2 = getLevelFormat(levelConfig.format);
5563
+ const alignment = getAlignment2(levelConfig.alignment);
5564
+ const text = levelConfig.text || (format2 === LevelFormat.BULLET ? "\u2022" : `%${levelConfig.level + 1}.`);
5565
+ const baseIndent = levelConfig.indent?.left !== void 0 ? levelConfig.indent.left / 72 : 0.5 * (levelConfig.level + 1);
5566
+ const hangingIndent = levelConfig.indent?.hanging !== void 0 ? levelConfig.indent.hanging / 72 : 0.25;
5567
+ const level = {
5568
+ level: levelConfig.level,
5569
+ format: format2,
5570
+ text,
5571
+ alignment,
5572
+ style: {
5573
+ paragraph: {
5574
+ indent: {
5575
+ left: convertInchesToTwip(baseIndent),
5576
+ hanging: convertInchesToTwip(hangingIndent)
5577
+ }
5578
+ }
5579
+ },
5580
+ // Add start number if specified
5581
+ ...levelConfig.start !== void 0 && { start: levelConfig.start }
5582
+ };
5583
+ levels.push(level);
5584
+ }
5585
+ return {
5586
+ reference: config.reference,
5587
+ levels
5588
+ };
5589
+ }
5590
+ var NumberingRegistry = class {
5591
+ fallback = {
5592
+ configs: /* @__PURE__ */ new Map(),
5593
+ counter: 0
5594
+ };
5595
+ scopes = new AsyncLocalStorage4();
5596
+ get state() {
5597
+ return this.scopes.getStore() ?? this.fallback;
5598
+ }
5599
+ /** Run work with an isolated registry that follows its async call chain. */
5600
+ runScoped(callback) {
5601
+ return this.scopes.run({ configs: /* @__PURE__ */ new Map(), counter: 0 }, callback);
5602
+ }
5603
+ /**
5604
+ * Register a numbering configuration
5605
+ */
5606
+ register(config) {
5607
+ const reference = config.reference;
5608
+ this.state.configs.set(reference, config);
5609
+ return reference;
5610
+ }
5611
+ /**
5612
+ * Generate a unique reference ID
5613
+ */
5614
+ generateReference(prefix = "list") {
5615
+ return `${prefix}-${++this.state.counter}`;
5616
+ }
5617
+ /**
5618
+ * Get all registered configurations as an array suitable for INumberingOptions
5619
+ */
5620
+ getAll() {
5621
+ return Array.from(this.state.configs.values());
5622
+ }
5623
+ /**
5624
+ * Clear all configurations
5625
+ */
5626
+ clear() {
5627
+ this.state.configs.clear();
5628
+ this.state.counter = 0;
5629
+ }
5630
+ /**
5631
+ * Check if a reference exists
5632
+ */
5633
+ has(reference) {
5634
+ return this.state.configs.has(reference);
5635
+ }
5636
+ /**
5637
+ * Get a configuration by reference
5638
+ */
5639
+ get(reference) {
5640
+ return this.state.configs.get(reference);
5641
+ }
5642
+ };
5643
+ var globalNumberingRegistry = new NumberingRegistry();
5644
+
5652
5645
  // src/components/paragraph.ts
5653
- init_numberingConfig();
5654
5646
  function parseMarkdownList(text) {
5655
5647
  const lines = text.split("\n");
5656
5648
  const items = [];
@@ -5776,7 +5768,6 @@ function renderParagraphComponent(component, theme, themeName) {
5776
5768
  }
5777
5769
 
5778
5770
  // src/components/list.ts
5779
- init_numberingConfig();
5780
5771
  function createLevelsFromSimplifiedProps(props) {
5781
5772
  const levels = [];
5782
5773
  let format2;
@@ -6200,17 +6191,22 @@ async function renderTableComponent(component, theme, themeName) {
6200
6191
 
6201
6192
  // src/components/section.ts
6202
6193
  import { Paragraph as Paragraph4, BookmarkStart, BookmarkEnd } from "docx";
6203
- function generateSectionBookmarkId() {
6204
- const linkId = Math.floor(Math.random() * 1e6);
6194
+ function generateSectionBookmarkId(context) {
6195
+ const custom = context.custom ??= {};
6196
+ const state = custom.sectionBookmarks ??= {
6197
+ next: 1
6198
+ };
6199
+ const ordinal = state.next++;
6200
+ const linkId = 1e6 + ordinal;
6205
6201
  return {
6206
- id: `_Section_${linkId}_${Date.now()}`,
6202
+ id: `_NestedSection_${ordinal}`,
6207
6203
  linkId
6208
6204
  };
6209
6205
  }
6210
6206
  async function renderSectionComponent(component, theme, themeName, context) {
6211
6207
  if (!isSectionComponent(component)) return [];
6212
6208
  const elements = [];
6213
- const { id: sectionBookmarkId, linkId: bookmarkLinkId } = generateSectionBookmarkId();
6209
+ const { id: sectionBookmarkId, linkId: bookmarkLinkId } = generateSectionBookmarkId(context);
6214
6210
  elements.push(
6215
6211
  new Paragraph4({
6216
6212
  children: [new BookmarkStart(sectionBookmarkId, bookmarkLinkId)],
@@ -6619,9 +6615,23 @@ Cause: ${cause}`
6619
6615
  height
6620
6616
  };
6621
6617
  }
6618
+ function withThemeColors(config, theme) {
6619
+ const options = config.options;
6620
+ if (!options || options.colors || !theme?.colors) return config;
6621
+ const palette = [
6622
+ theme.colors.primary,
6623
+ theme.colors.secondary,
6624
+ theme.colors.accent
6625
+ ].filter((c) => typeof c === "string" && c.length > 0).map((c) => c.startsWith("#") ? c : `#${c}`);
6626
+ if (palette.length === 0) return config;
6627
+ return {
6628
+ ...config,
6629
+ options: { ...config.options, colors: palette }
6630
+ };
6631
+ }
6622
6632
  async function renderHighchartsComponent(component, theme, themeName, context) {
6623
6633
  if (!isHighchartsComponent(component)) return [];
6624
- const config = component.props;
6634
+ const config = withThemeColors(component.props, theme);
6625
6635
  const chartResult = await generateChart(
6626
6636
  config,
6627
6637
  context?.services?.highcharts
@@ -6811,13 +6821,23 @@ function getAlignment3(alignment) {
6811
6821
  }
6812
6822
  }
6813
6823
  async function renderDocument(structure, layout, options) {
6824
+ return runWithGenerationDate(
6825
+ structure.metadata.date,
6826
+ () => globalBookmarkRegistry.runScoped(
6827
+ () => globalRevisionIdRegistry.runScoped(
6828
+ () => globalNumberingRegistry.runScoped(
6829
+ () => renderDocumentScoped(structure, layout, options)
6830
+ )
6831
+ )
6832
+ )
6833
+ );
6834
+ }
6835
+ async function renderDocumentScoped(structure, layout, options) {
6814
6836
  if (options?.cache) {
6815
6837
  initializeComponentCache(options.cache);
6816
6838
  } else if (!options?.bypassCache) {
6817
6839
  initializeComponentCache();
6818
6840
  }
6819
- const { globalNumberingRegistry: globalNumberingRegistry2 } = await Promise.resolve().then(() => (init_numberingConfig(), numberingConfig_exports));
6820
- globalNumberingRegistry2.clear();
6821
6841
  const sections = [];
6822
6842
  const context = createRenderContext(
6823
6843
  structure,
@@ -6874,7 +6894,8 @@ async function renderDocument(structure, layout, options) {
6874
6894
  structure.themeName,
6875
6895
  context,
6876
6896
  sectionOrdinal,
6877
- closeBookmark
6897
+ closeBookmark,
6898
+ options?.bypassCache === true
6878
6899
  );
6879
6900
  if (layoutSection.isUserSection) {
6880
6901
  sectionBookmarkCounter++;
@@ -6883,7 +6904,7 @@ async function renderDocument(structure, layout, options) {
6883
6904
  sections.push(rendered);
6884
6905
  }
6885
6906
  }
6886
- const numberingConfigs = globalNumberingRegistry2.getAll();
6907
+ const numberingConfigs = globalNumberingRegistry.getAll();
6887
6908
  return new Document({
6888
6909
  styles: createWordStyles(structure.theme, structure.language),
6889
6910
  sections,
@@ -7051,7 +7072,7 @@ async function renderHeaderFooterComponents(components, theme, themeName, contex
7051
7072
  }
7052
7073
  return elements;
7053
7074
  }
7054
- async function renderSection(section, theme, themeName, context, sectionOrdinal, closeBookmark) {
7075
+ async function renderSection(section, theme, themeName, context, sectionOrdinal, closeBookmark, bypassCache = false) {
7055
7076
  const elements = [];
7056
7077
  const isFirstLayoutOfUserSection = section.isUserSection;
7057
7078
  const sharedLinkId = section.belongsToUserSection && sectionOrdinal ? sectionOrdinal : void 0;
@@ -7089,8 +7110,7 @@ async function renderSection(section, theme, themeName, context, sectionOrdinal,
7089
7110
  theme,
7090
7111
  themeName,
7091
7112
  sectionContext,
7092
- false
7093
- // Don't bypass cache
7113
+ bypassCache
7094
7114
  );
7095
7115
  elements.push(...rendered);
7096
7116
  }
@@ -7259,6 +7279,75 @@ function normalizeDocument(document) {
7259
7279
  return [normalized];
7260
7280
  }
7261
7281
 
7282
+ // src/utils/packageDocument.ts
7283
+ import AdmZip2 from "adm-zip";
7284
+ import { Packer } from "docx";
7285
+
7286
+ // src/utils/fixFloatingImageIds.ts
7287
+ import AdmZip from "adm-zip";
7288
+ function fixFloatingImageIdsInBuffer(buffer) {
7289
+ const zip = new AdmZip(buffer);
7290
+ const documentEntry = zip.getEntry("word/document.xml");
7291
+ if (!documentEntry) {
7292
+ throw new Error("document.xml not found in DOCX");
7293
+ }
7294
+ let idCounter = 1;
7295
+ const documentXml = documentEntry.getData().toString("utf8").replace(/<wp:docPr\s+id="(\d+)"/g, () => {
7296
+ const newId = idCounter++;
7297
+ return `<wp:docPr id="${newId}"`;
7298
+ });
7299
+ zip.updateFile(documentEntry, Buffer.from(documentXml, "utf8"));
7300
+ return zip.toBuffer();
7301
+ }
7302
+
7303
+ // src/utils/packageDocument.ts
7304
+ var DEFAULT_GENERATION_DATE = /* @__PURE__ */ new Date("2000-01-01T00:00:00.000Z");
7305
+ function resolveGenerationDate(options) {
7306
+ if (options?.generatedAt !== void 0) {
7307
+ const date = new Date(options.generatedAt);
7308
+ if (Number.isNaN(date.getTime())) {
7309
+ throw new RangeError("generatedAt must be a valid date");
7310
+ }
7311
+ if (date.getUTCFullYear() < 1980) {
7312
+ throw new RangeError(
7313
+ "generatedAt must be on or after 1980-01-01 for ZIP compatibility"
7314
+ );
7315
+ }
7316
+ return date;
7317
+ }
7318
+ return options?.deterministic === false ? /* @__PURE__ */ new Date() : new Date(DEFAULT_GENERATION_DATE);
7319
+ }
7320
+ function toDosTime(date) {
7321
+ const dosDate = (date.getUTCFullYear() - 1980 & 127) << 9 | date.getUTCMonth() + 1 << 5 | date.getUTCDate();
7322
+ const dosTime = date.getUTCHours() << 11 | date.getUTCMinutes() << 5 | date.getUTCSeconds() >> 1;
7323
+ return (dosDate << 16 | dosTime) >>> 0;
7324
+ }
7325
+ function canonicalizeDocxBuffer(buffer, generatedAt = DEFAULT_GENERATION_DATE) {
7326
+ const zip = new AdmZip2(buffer);
7327
+ const isoTimestamp = generatedAt.toISOString();
7328
+ const coreProperties = zip.getEntry("docProps/core.xml");
7329
+ if (coreProperties) {
7330
+ const normalized = coreProperties.getData().toString("utf8").replace(
7331
+ /(<dcterms:(?:created|modified)\b[^>]*>)[^<]*(<\/dcterms:(?:created|modified)>)/g,
7332
+ `$1${isoTimestamp}$2`
7333
+ );
7334
+ zip.updateFile(coreProperties, Buffer.from(normalized, "utf8"));
7335
+ }
7336
+ const zipTimestamp = toDosTime(generatedAt);
7337
+ for (const entry of zip.getEntries()) {
7338
+ entry.header.timeval = zipTimestamp;
7339
+ }
7340
+ return zip.toBuffer();
7341
+ }
7342
+ async function packageDocument(document, options) {
7343
+ const packed = await Packer.toBuffer(document);
7344
+ const fixed = fixFloatingImageIdsInBuffer(packed);
7345
+ if (options?.deterministic === false) {
7346
+ return fixed;
7347
+ }
7348
+ return canonicalizeDocxBuffer(fixed, resolveGenerationDate(options));
7349
+ }
7350
+
7262
7351
  // src/plugin/createDocumentGenerator.ts
7263
7352
  function createBuilderImpl(state) {
7264
7353
  const componentMap = new Map(state.components.map((c) => [c.name, c]));
@@ -7414,7 +7503,10 @@ function createBuilderImpl(state) {
7414
7503
  debug: state.debug,
7415
7504
  enableCache: state.enableCache,
7416
7505
  services: state.services,
7417
- fonts: state.fonts
7506
+ fonts: state.fonts,
7507
+ validation: state.validation,
7508
+ deterministic: state.deterministic,
7509
+ generatedAt: state.generatedAt
7418
7510
  };
7419
7511
  return createBuilderImpl(
7420
7512
  newState
@@ -7506,10 +7598,20 @@ function createBuilderImpl(state) {
7506
7598
  };
7507
7599
  const [modedDoc] = normalizeDocument(processedDocument);
7508
7600
  await resolveDocumentFonts(modedDoc, modedTheme, state.fonts, warnings);
7509
- const structure = await processDocument(modedDoc, modedTheme, themeName);
7601
+ const packageOptions = {
7602
+ deterministic: options?.deterministic ?? state.deterministic,
7603
+ generatedAt: options?.generatedAt ?? state.generatedAt
7604
+ };
7605
+ const structure = await processDocument(
7606
+ modedDoc,
7607
+ modedTheme,
7608
+ themeName,
7609
+ resolveGenerationDate(packageOptions)
7610
+ );
7510
7611
  const layout = applyLayout(structure.sections, modedTheme, themeName);
7511
7612
  const generatedDocument = await renderDocument(structure, layout, {
7512
- services: state.services
7613
+ services: state.services,
7614
+ bypassCache: !state.enableCache
7513
7615
  });
7514
7616
  const preservedDefinition = preserveSet ? {
7515
7617
  ...mode.doc,
@@ -7535,7 +7637,10 @@ function createBuilderImpl(state) {
7535
7637
  standardDefinition,
7536
7638
  preservedDefinition
7537
7639
  } = await generate(document, options);
7538
- const buffer = await Packer.toBuffer(doc);
7640
+ const buffer = await packageDocument(doc, {
7641
+ deterministic: options?.deterministic ?? state.deterministic,
7642
+ generatedAt: options?.generatedAt ?? state.generatedAt
7643
+ });
7539
7644
  return { buffer, warnings, standardDefinition, preservedDefinition };
7540
7645
  }
7541
7646
  async function generateFile(document, outputPath, options) {
@@ -7634,7 +7739,9 @@ function createDocumentGenerator(options) {
7634
7739
  enableCache: options.enableCache ?? false,
7635
7740
  services: options.services,
7636
7741
  fonts: options.fonts,
7637
- validation: options.validation
7742
+ validation: options.validation,
7743
+ deterministic: options.deterministic ?? true,
7744
+ generatedAt: options.generatedAt
7638
7745
  };
7639
7746
  return createBuilderImpl(initialState);
7640
7747
  }