docxmlater 11.0.9 → 12.0.1

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 (354) hide show
  1. package/README.md +51 -13
  2. package/dist/core/Document.d.ts +28 -2
  3. package/dist/core/Document.d.ts.map +1 -1
  4. package/dist/core/Document.js +725 -170
  5. package/dist/core/Document.js.map +1 -1
  6. package/dist/core/DocumentGenerator.d.ts +1 -0
  7. package/dist/core/DocumentGenerator.d.ts.map +1 -1
  8. package/dist/core/DocumentGenerator.js +77 -25
  9. package/dist/core/DocumentGenerator.js.map +1 -1
  10. package/dist/core/DocumentParser.d.ts +7 -3
  11. package/dist/core/DocumentParser.d.ts.map +1 -1
  12. package/dist/core/DocumentParser.js +431 -85
  13. package/dist/core/DocumentParser.js.map +1 -1
  14. package/dist/core/RelationshipManager.d.ts.map +1 -1
  15. package/dist/core/RelationshipManager.js +2 -2
  16. package/dist/core/RelationshipManager.js.map +1 -1
  17. package/dist/elements/Comment.d.ts +15 -1
  18. package/dist/elements/Comment.d.ts.map +1 -1
  19. package/dist/elements/Comment.js +67 -12
  20. package/dist/elements/Comment.js.map +1 -1
  21. package/dist/elements/CommentManager.d.ts +4 -0
  22. package/dist/elements/CommentManager.d.ts.map +1 -1
  23. package/dist/elements/CommentManager.js +74 -16
  24. package/dist/elements/CommentManager.js.map +1 -1
  25. package/dist/elements/EndnoteManager.d.ts.map +1 -1
  26. package/dist/elements/EndnoteManager.js.map +1 -1
  27. package/dist/elements/Field.d.ts +10 -1
  28. package/dist/elements/Field.d.ts.map +1 -1
  29. package/dist/elements/Field.js +76 -34
  30. package/dist/elements/Field.js.map +1 -1
  31. package/dist/elements/FieldHelpers.d.ts +1 -0
  32. package/dist/elements/FieldHelpers.d.ts.map +1 -1
  33. package/dist/elements/FieldHelpers.js +8 -4
  34. package/dist/elements/FieldHelpers.js.map +1 -1
  35. package/dist/elements/Footer.d.ts +3 -1
  36. package/dist/elements/Footer.d.ts.map +1 -1
  37. package/dist/elements/Footer.js +39 -53
  38. package/dist/elements/Footer.js.map +1 -1
  39. package/dist/elements/FootnoteManager.d.ts.map +1 -1
  40. package/dist/elements/FootnoteManager.js.map +1 -1
  41. package/dist/elements/Header.d.ts +3 -1
  42. package/dist/elements/Header.d.ts.map +1 -1
  43. package/dist/elements/Header.js +39 -53
  44. package/dist/elements/Header.js.map +1 -1
  45. package/dist/elements/HeaderFooterManager.d.ts +4 -2
  46. package/dist/elements/HeaderFooterManager.d.ts.map +1 -1
  47. package/dist/elements/HeaderFooterManager.js +36 -6
  48. package/dist/elements/HeaderFooterManager.js.map +1 -1
  49. package/dist/elements/Hyperlink.d.ts +6 -1
  50. package/dist/elements/Hyperlink.d.ts.map +1 -1
  51. package/dist/elements/Hyperlink.js +69 -44
  52. package/dist/elements/Hyperlink.js.map +1 -1
  53. package/dist/elements/Image.d.ts.map +1 -1
  54. package/dist/elements/Image.js +33 -6
  55. package/dist/elements/Image.js.map +1 -1
  56. package/dist/elements/ImageManager.d.ts +1 -0
  57. package/dist/elements/ImageManager.d.ts.map +1 -1
  58. package/dist/elements/ImageManager.js +11 -0
  59. package/dist/elements/ImageManager.js.map +1 -1
  60. package/dist/elements/Paragraph.d.ts +3 -0
  61. package/dist/elements/Paragraph.d.ts.map +1 -1
  62. package/dist/elements/Paragraph.js +91 -26
  63. package/dist/elements/Paragraph.js.map +1 -1
  64. package/dist/elements/Revision.d.ts +4 -0
  65. package/dist/elements/Revision.d.ts.map +1 -1
  66. package/dist/elements/Revision.js +369 -29
  67. package/dist/elements/Revision.js.map +1 -1
  68. package/dist/elements/Run.d.ts +1 -0
  69. package/dist/elements/Run.d.ts.map +1 -1
  70. package/dist/elements/Run.js +24 -4
  71. package/dist/elements/Run.js.map +1 -1
  72. package/dist/elements/Section.d.ts.map +1 -1
  73. package/dist/elements/Section.js +17 -7
  74. package/dist/elements/Section.js.map +1 -1
  75. package/dist/elements/Shape.d.ts.map +1 -1
  76. package/dist/elements/Shape.js +50 -42
  77. package/dist/elements/Shape.js.map +1 -1
  78. package/dist/elements/StructuredDocumentTag.d.ts +9 -1
  79. package/dist/elements/StructuredDocumentTag.d.ts.map +1 -1
  80. package/dist/elements/StructuredDocumentTag.js +46 -6
  81. package/dist/elements/StructuredDocumentTag.js.map +1 -1
  82. package/dist/elements/Table.d.ts +2 -0
  83. package/dist/elements/Table.d.ts.map +1 -1
  84. package/dist/elements/Table.js +90 -42
  85. package/dist/elements/Table.js.map +1 -1
  86. package/dist/elements/TableCell.d.ts +1 -0
  87. package/dist/elements/TableCell.d.ts.map +1 -1
  88. package/dist/elements/TableCell.js +32 -13
  89. package/dist/elements/TableCell.js.map +1 -1
  90. package/dist/elements/TableOfContents.d.ts.map +1 -1
  91. package/dist/elements/TableOfContents.js +5 -7
  92. package/dist/elements/TableOfContents.js.map +1 -1
  93. package/dist/elements/TableRow.d.ts +1 -0
  94. package/dist/elements/TableRow.d.ts.map +1 -1
  95. package/dist/elements/TableRow.js +16 -1
  96. package/dist/elements/TableRow.js.map +1 -1
  97. package/dist/elements/TextBox.d.ts.map +1 -1
  98. package/dist/elements/TextBox.js +55 -42
  99. package/dist/elements/TextBox.js.map +1 -1
  100. package/dist/esm/core/Document.js +726 -171
  101. package/dist/esm/core/Document.js.map +1 -1
  102. package/dist/esm/core/DocumentGenerator.js +77 -25
  103. package/dist/esm/core/DocumentGenerator.js.map +1 -1
  104. package/dist/esm/core/DocumentParser.js +431 -85
  105. package/dist/esm/core/DocumentParser.js.map +1 -1
  106. package/dist/esm/core/RelationshipManager.js +2 -2
  107. package/dist/esm/core/RelationshipManager.js.map +1 -1
  108. package/dist/esm/elements/Comment.js +67 -12
  109. package/dist/esm/elements/Comment.js.map +1 -1
  110. package/dist/esm/elements/CommentManager.js +74 -16
  111. package/dist/esm/elements/CommentManager.js.map +1 -1
  112. package/dist/esm/elements/EndnoteManager.js.map +1 -1
  113. package/dist/esm/elements/Field.js +77 -35
  114. package/dist/esm/elements/Field.js.map +1 -1
  115. package/dist/esm/elements/FieldHelpers.js +7 -4
  116. package/dist/esm/elements/FieldHelpers.js.map +1 -1
  117. package/dist/esm/elements/Footer.js +39 -53
  118. package/dist/esm/elements/Footer.js.map +1 -1
  119. package/dist/esm/elements/FootnoteManager.js.map +1 -1
  120. package/dist/esm/elements/Header.js +39 -53
  121. package/dist/esm/elements/Header.js.map +1 -1
  122. package/dist/esm/elements/HeaderFooterManager.js +36 -6
  123. package/dist/esm/elements/HeaderFooterManager.js.map +1 -1
  124. package/dist/esm/elements/Hyperlink.js +69 -44
  125. package/dist/esm/elements/Hyperlink.js.map +1 -1
  126. package/dist/esm/elements/Image.js +33 -6
  127. package/dist/esm/elements/Image.js.map +1 -1
  128. package/dist/esm/elements/ImageManager.js +11 -0
  129. package/dist/esm/elements/ImageManager.js.map +1 -1
  130. package/dist/esm/elements/Paragraph.js +91 -26
  131. package/dist/esm/elements/Paragraph.js.map +1 -1
  132. package/dist/esm/elements/Revision.js +369 -29
  133. package/dist/esm/elements/Revision.js.map +1 -1
  134. package/dist/esm/elements/Run.js +24 -4
  135. package/dist/esm/elements/Run.js.map +1 -1
  136. package/dist/esm/elements/Section.js +17 -7
  137. package/dist/esm/elements/Section.js.map +1 -1
  138. package/dist/esm/elements/Shape.js +50 -42
  139. package/dist/esm/elements/Shape.js.map +1 -1
  140. package/dist/esm/elements/StructuredDocumentTag.js +46 -6
  141. package/dist/esm/elements/StructuredDocumentTag.js.map +1 -1
  142. package/dist/esm/elements/Table.js +90 -42
  143. package/dist/esm/elements/Table.js.map +1 -1
  144. package/dist/esm/elements/TableCell.js +32 -13
  145. package/dist/esm/elements/TableCell.js.map +1 -1
  146. package/dist/esm/elements/TableOfContents.js +5 -7
  147. package/dist/esm/elements/TableOfContents.js.map +1 -1
  148. package/dist/esm/elements/TableRow.js +16 -1
  149. package/dist/esm/elements/TableRow.js.map +1 -1
  150. package/dist/esm/elements/TextBox.js +55 -42
  151. package/dist/esm/elements/TextBox.js.map +1 -1
  152. package/dist/esm/formatting/AbstractNumbering.js +1 -1
  153. package/dist/esm/formatting/AbstractNumbering.js.map +1 -1
  154. package/dist/esm/formatting/NumberingLevel.js +8 -6
  155. package/dist/esm/formatting/NumberingLevel.js.map +1 -1
  156. package/dist/esm/formatting/NumberingManager.js +16 -1
  157. package/dist/esm/formatting/NumberingManager.js.map +1 -1
  158. package/dist/esm/formatting/Style.js +14 -0
  159. package/dist/esm/formatting/Style.js.map +1 -1
  160. package/dist/esm/formatting/StylesManager.js +14 -2
  161. package/dist/esm/formatting/StylesManager.js.map +1 -1
  162. package/dist/esm/helpers/CleanupHelper.js +103 -26
  163. package/dist/esm/helpers/CleanupHelper.js.map +1 -1
  164. package/dist/esm/images/ImageOptimizer.js +52 -4
  165. package/dist/esm/images/ImageOptimizer.js.map +1 -1
  166. package/dist/esm/index.js +1 -0
  167. package/dist/esm/index.js.map +1 -1
  168. package/dist/esm/internal.js +1 -1
  169. package/dist/esm/internal.js.map +1 -1
  170. package/dist/esm/processors/CompatibilityUpgrader.js +7 -1
  171. package/dist/esm/processors/CompatibilityUpgrader.js.map +1 -1
  172. package/dist/esm/processors/InMemoryRevisionAcceptor.js +134 -13
  173. package/dist/esm/processors/InMemoryRevisionAcceptor.js.map +1 -1
  174. package/dist/esm/processors/RevisionWalker.js +58 -8
  175. package/dist/esm/processors/RevisionWalker.js.map +1 -1
  176. package/dist/esm/processors/SelectiveRevisionAcceptor.js +116 -17
  177. package/dist/esm/processors/SelectiveRevisionAcceptor.js.map +1 -1
  178. package/dist/esm/processors/cnfStyleDecoder.js +7 -7
  179. package/dist/esm/processors/cnfStyleDecoder.js.map +1 -1
  180. package/dist/esm/processors/stripTrackedChanges.js +16 -5
  181. package/dist/esm/processors/stripTrackedChanges.js.map +1 -1
  182. package/dist/esm/utils/deepClone.js +24 -11
  183. package/dist/esm/utils/deepClone.js.map +1 -1
  184. package/dist/esm/utils/units.js +1 -1
  185. package/dist/esm/utils/units.js.map +1 -1
  186. package/dist/esm/utils/validation.js +11 -23
  187. package/dist/esm/utils/validation.js.map +1 -1
  188. package/dist/esm/utils/xmlSanitization.js +131 -7
  189. package/dist/esm/utils/xmlSanitization.js.map +1 -1
  190. package/dist/esm/validation/RevisionAutoFixer.js +69 -55
  191. package/dist/esm/validation/RevisionAutoFixer.js.map +1 -1
  192. package/dist/esm/validation/RevisionValidator.js +24 -20
  193. package/dist/esm/validation/RevisionValidator.js.map +1 -1
  194. package/dist/esm/xml/XMLBuilder.js +52 -20
  195. package/dist/esm/xml/XMLBuilder.js.map +1 -1
  196. package/dist/esm/xml/XMLParser.js +55 -13
  197. package/dist/esm/xml/XMLParser.js.map +1 -1
  198. package/dist/esm/zip/ZipHandler.js +37 -13
  199. package/dist/esm/zip/ZipHandler.js.map +1 -1
  200. package/dist/esm/zip/ZipReader.js +53 -6
  201. package/dist/esm/zip/ZipReader.js.map +1 -1
  202. package/dist/esm/zip/errors.js +7 -0
  203. package/dist/esm/zip/errors.js.map +1 -1
  204. package/dist/esm/zip/types.js +4 -0
  205. package/dist/esm/zip/types.js.map +1 -1
  206. package/dist/formatting/AbstractNumbering.d.ts.map +1 -1
  207. package/dist/formatting/AbstractNumbering.js +1 -1
  208. package/dist/formatting/AbstractNumbering.js.map +1 -1
  209. package/dist/formatting/NumberingLevel.d.ts +1 -1
  210. package/dist/formatting/NumberingLevel.d.ts.map +1 -1
  211. package/dist/formatting/NumberingLevel.js +8 -6
  212. package/dist/formatting/NumberingLevel.js.map +1 -1
  213. package/dist/formatting/NumberingManager.d.ts.map +1 -1
  214. package/dist/formatting/NumberingManager.js +16 -1
  215. package/dist/formatting/NumberingManager.js.map +1 -1
  216. package/dist/formatting/Style.d.ts.map +1 -1
  217. package/dist/formatting/Style.js +14 -0
  218. package/dist/formatting/Style.js.map +1 -1
  219. package/dist/formatting/StylesManager.d.ts +2 -0
  220. package/dist/formatting/StylesManager.d.ts.map +1 -1
  221. package/dist/formatting/StylesManager.js +14 -2
  222. package/dist/formatting/StylesManager.js.map +1 -1
  223. package/dist/helpers/CleanupHelper.d.ts.map +1 -1
  224. package/dist/helpers/CleanupHelper.js +103 -26
  225. package/dist/helpers/CleanupHelper.js.map +1 -1
  226. package/dist/images/ImageOptimizer.d.ts.map +1 -1
  227. package/dist/images/ImageOptimizer.js +52 -4
  228. package/dist/images/ImageOptimizer.js.map +1 -1
  229. package/dist/index.d.ts +3 -1
  230. package/dist/index.d.ts.map +1 -1
  231. package/dist/index.js +4 -2
  232. package/dist/index.js.map +1 -1
  233. package/dist/internal.d.ts +1 -1
  234. package/dist/internal.d.ts.map +1 -1
  235. package/dist/internal.js +2 -1
  236. package/dist/internal.js.map +1 -1
  237. package/dist/processors/CompatibilityUpgrader.d.ts.map +1 -1
  238. package/dist/processors/CompatibilityUpgrader.js +7 -1
  239. package/dist/processors/CompatibilityUpgrader.js.map +1 -1
  240. package/dist/processors/InMemoryRevisionAcceptor.d.ts +1 -0
  241. package/dist/processors/InMemoryRevisionAcceptor.d.ts.map +1 -1
  242. package/dist/processors/InMemoryRevisionAcceptor.js +135 -13
  243. package/dist/processors/InMemoryRevisionAcceptor.js.map +1 -1
  244. package/dist/processors/RevisionWalker.d.ts +2 -0
  245. package/dist/processors/RevisionWalker.d.ts.map +1 -1
  246. package/dist/processors/RevisionWalker.js +58 -8
  247. package/dist/processors/RevisionWalker.js.map +1 -1
  248. package/dist/processors/SelectiveRevisionAcceptor.d.ts.map +1 -1
  249. package/dist/processors/SelectiveRevisionAcceptor.js +116 -17
  250. package/dist/processors/SelectiveRevisionAcceptor.js.map +1 -1
  251. package/dist/processors/cnfStyleDecoder.d.ts.map +1 -1
  252. package/dist/processors/cnfStyleDecoder.js +7 -7
  253. package/dist/processors/cnfStyleDecoder.js.map +1 -1
  254. package/dist/processors/stripTrackedChanges.d.ts.map +1 -1
  255. package/dist/processors/stripTrackedChanges.js +16 -5
  256. package/dist/processors/stripTrackedChanges.js.map +1 -1
  257. package/dist/utils/deepClone.d.ts.map +1 -1
  258. package/dist/utils/deepClone.js +24 -11
  259. package/dist/utils/deepClone.js.map +1 -1
  260. package/dist/utils/units.d.ts.map +1 -1
  261. package/dist/utils/units.js +1 -1
  262. package/dist/utils/units.js.map +1 -1
  263. package/dist/utils/validation.d.ts.map +1 -1
  264. package/dist/utils/validation.js +11 -23
  265. package/dist/utils/validation.js.map +1 -1
  266. package/dist/utils/xmlSanitization.d.ts +2 -0
  267. package/dist/utils/xmlSanitization.d.ts.map +1 -1
  268. package/dist/utils/xmlSanitization.js +133 -7
  269. package/dist/utils/xmlSanitization.js.map +1 -1
  270. package/dist/validation/RevisionAutoFixer.d.ts +6 -2
  271. package/dist/validation/RevisionAutoFixer.d.ts.map +1 -1
  272. package/dist/validation/RevisionAutoFixer.js +68 -54
  273. package/dist/validation/RevisionAutoFixer.js.map +1 -1
  274. package/dist/validation/RevisionValidator.d.ts +2 -0
  275. package/dist/validation/RevisionValidator.d.ts.map +1 -1
  276. package/dist/validation/RevisionValidator.js +26 -21
  277. package/dist/validation/RevisionValidator.js.map +1 -1
  278. package/dist/xml/XMLBuilder.d.ts +1 -0
  279. package/dist/xml/XMLBuilder.d.ts.map +1 -1
  280. package/dist/xml/XMLBuilder.js +52 -20
  281. package/dist/xml/XMLBuilder.js.map +1 -1
  282. package/dist/xml/XMLParser.d.ts +1 -0
  283. package/dist/xml/XMLParser.d.ts.map +1 -1
  284. package/dist/xml/XMLParser.js +55 -13
  285. package/dist/xml/XMLParser.js.map +1 -1
  286. package/dist/zip/ZipHandler.d.ts.map +1 -1
  287. package/dist/zip/ZipHandler.js +37 -13
  288. package/dist/zip/ZipHandler.js.map +1 -1
  289. package/dist/zip/ZipReader.d.ts.map +1 -1
  290. package/dist/zip/ZipReader.js +52 -5
  291. package/dist/zip/ZipReader.js.map +1 -1
  292. package/dist/zip/errors.d.ts +3 -0
  293. package/dist/zip/errors.d.ts.map +1 -1
  294. package/dist/zip/errors.js +9 -1
  295. package/dist/zip/errors.js.map +1 -1
  296. package/dist/zip/types.d.ts +4 -0
  297. package/dist/zip/types.d.ts.map +1 -1
  298. package/dist/zip/types.js +4 -0
  299. package/dist/zip/types.js.map +1 -1
  300. package/package.json +2 -2
  301. package/src/core/Document.ts +1159 -240
  302. package/src/core/DocumentGenerator.ts +106 -26
  303. package/src/core/DocumentParser.ts +692 -120
  304. package/src/core/RelationshipManager.ts +446 -442
  305. package/src/elements/Comment.ts +123 -18
  306. package/src/elements/CommentManager.ts +121 -22
  307. package/src/elements/EndnoteManager.ts +4 -1
  308. package/src/elements/Field.ts +134 -43
  309. package/src/elements/FieldHelpers.ts +20 -4
  310. package/src/elements/Footer.ts +62 -67
  311. package/src/elements/FootnoteManager.ts +4 -1
  312. package/src/elements/Header.ts +62 -67
  313. package/src/elements/HeaderFooterManager.ts +56 -8
  314. package/src/elements/Hyperlink.ts +150 -55
  315. package/src/elements/Image.ts +51 -7
  316. package/src/elements/ImageManager.ts +24 -0
  317. package/src/elements/Paragraph.ts +161 -40
  318. package/src/elements/Revision.ts +446 -47
  319. package/src/elements/Run.ts +44 -6
  320. package/src/elements/Section.ts +25 -9
  321. package/src/elements/Shape.ts +52 -44
  322. package/src/elements/StructuredDocumentTag.ts +83 -8
  323. package/src/elements/Table.ts +151 -50
  324. package/src/elements/TableCell.ts +57 -17
  325. package/src/elements/TableOfContents.ts +10 -9
  326. package/src/elements/TableRow.ts +35 -3
  327. package/src/elements/TextBox.ts +58 -43
  328. package/src/formatting/AbstractNumbering.ts +4 -2
  329. package/src/formatting/NumberingLevel.ts +50 -12
  330. package/src/formatting/NumberingManager.ts +32 -3
  331. package/src/formatting/Style.ts +14 -0
  332. package/src/formatting/StylesManager.ts +29 -2
  333. package/src/helpers/CleanupHelper.ts +122 -30
  334. package/src/images/ImageOptimizer.ts +64 -5
  335. package/src/index.ts +7 -0
  336. package/src/internal.ts +1 -0
  337. package/src/processors/CompatibilityUpgrader.ts +15 -2
  338. package/src/processors/InMemoryRevisionAcceptor.ts +276 -17
  339. package/src/processors/RevisionWalker.ts +83 -7
  340. package/src/processors/SelectiveRevisionAcceptor.ts +164 -28
  341. package/src/processors/cnfStyleDecoder.ts +18 -12
  342. package/src/processors/stripTrackedChanges.ts +47 -10
  343. package/src/utils/deepClone.ts +48 -14
  344. package/src/utils/units.ts +3 -1
  345. package/src/utils/validation.ts +30 -32
  346. package/src/utils/xmlSanitization.ts +218 -22
  347. package/src/validation/RevisionAutoFixer.ts +109 -65
  348. package/src/validation/RevisionValidator.ts +48 -23
  349. package/src/xml/XMLBuilder.ts +82 -22
  350. package/src/xml/XMLParser.ts +87 -20
  351. package/src/zip/ZipHandler.ts +61 -21
  352. package/src/zip/ZipReader.ts +135 -13
  353. package/src/zip/errors.ts +15 -0
  354. package/src/zip/types.ts +44 -2
@@ -1 +1 @@
1
- {"version":3,"file":"ZipHandler.js","sourceRoot":"","sources":["../../src/zip/ZipHandler.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * ZipHandler - Main facade for ZIP archive operations\n * Provides a unified interface for reading and writing DOCX files\n */\n\nimport { ZipReader } from './ZipReader.js';\nimport { ZipWriter } from './ZipWriter.js';\nimport {\n ZipFile,\n FileMap,\n LoadOptions,\n SaveOptions,\n AddFileOptions,\n SizeLimitOptions,\n DEFAULT_SIZE_LIMITS,\n} from './types.js';\nimport { getGlobalLogger, createScopedLogger, ILogger } from '../utils/logger.js';\n\n// Create scoped logger for ZipHandler operations\nfunction getLogger(): ILogger {\n return createScopedLogger(getGlobalLogger(), 'ZipHandler');\n}\n\n/**\n * Main class for handling ZIP archives (DOCX files)\n * Combines reading and writing operations into a single interface\n */\nexport class ZipHandler {\n private reader: ZipReader;\n private writer: ZipWriter;\n private mode: 'read' | 'write' | 'modify' = 'write';\n\n constructor() {\n this.reader = new ZipReader();\n this.writer = new ZipWriter();\n }\n\n // ==================== SIZE VALIDATION ====================\n\n /**\n * Validates document size against configured limits\n * @param sizeMB - Size in megabytes\n * @param limits - Size limit options (merged with defaults)\n * @throws Error if size exceeds maximum\n */\n private validateDocumentSize(sizeMB: number, limits: Required<SizeLimitOptions>): void {\n const logger = getLogger();\n const { warningSizeMB, maxSizeMB } = limits;\n\n // Check maximum size (if enabled)\n if (maxSizeMB > 0 && sizeMB > maxSizeMB) {\n logger.error('Document exceeds maximum size', { sizeMB: sizeMB.toFixed(1), maxSizeMB });\n throw new Error(\n `Document size (${sizeMB.toFixed(1)}MB) exceeds maximum supported size (${maxSizeMB}MB). ` +\n `This would likely cause out-of-memory errors. Consider:\\n` +\n `- Compressing/optimizing images\\n` +\n `- Splitting into multiple documents\\n` +\n `- Processing on a machine with more memory\\n` +\n `- Increasing maxSizeMB via LoadOptions.sizeLimits (not recommended)`\n );\n }\n\n // Warn on large files (if enabled)\n if (warningSizeMB > 0 && sizeMB > warningSizeMB) {\n logger.warn('Large document detected', { sizeMB: sizeMB.toFixed(1), warningSizeMB });\n }\n }\n\n /**\n * Gets merged size limits from options and defaults\n * @param options - Load options that may contain size limits\n * @returns Merged size limits with all values defined\n */\n private getSizeLimits(options: LoadOptions): Required<SizeLimitOptions> {\n return {\n ...DEFAULT_SIZE_LIMITS,\n ...options.sizeLimits,\n };\n }\n\n // ==================== LOADING ====================\n\n /**\n * Loads a DOCX file from the filesystem\n * @param filePath - Path to the DOCX file\n * @param options - Load options (including configurable size limits)\n */\n async load(filePath: string, options: LoadOptions = {}): Promise<void> {\n const logger = getLogger();\n logger.info('Loading DOCX file', { path: filePath });\n\n // Check file size before loading\n const { promises: fs } = await import('fs');\n const stats = await fs.stat(filePath);\n const sizeMB = stats.size / (1024 * 1024);\n\n // Validate against configurable limits\n const limits = this.getSizeLimits(options);\n this.validateDocumentSize(sizeMB, limits);\n\n await this.reader.loadFromFile(filePath, options);\n\n // Copy all files from reader to writer for modification\n const files = this.reader.getAllFiles();\n this.writer.clear();\n this.writer.addFiles(files);\n\n this.mode = 'modify';\n logger.info('DOCX file loaded', { fileCount: files.size, sizeMB: sizeMB.toFixed(2) });\n }\n\n /**\n * Loads a DOCX file from a buffer\n * @param buffer - Buffer containing the DOCX data\n * @param options - Load options (including configurable size limits)\n */\n async loadFromBuffer(buffer: Buffer, options: LoadOptions = {}): Promise<void> {\n const logger = getLogger();\n logger.info('Loading DOCX from buffer', { bufferSize: buffer.length });\n\n // Check buffer size before loading\n const sizeMB = buffer.length / (1024 * 1024);\n\n // Validate against configurable limits\n const limits = this.getSizeLimits(options);\n this.validateDocumentSize(sizeMB, limits);\n\n await this.reader.loadFromBuffer(buffer, options);\n\n // Copy all files from reader to writer for modification\n const files = this.reader.getAllFiles();\n this.writer.clear();\n this.writer.addFiles(files);\n\n this.mode = 'modify';\n logger.info('DOCX buffer loaded', { fileCount: files.size, sizeMB: sizeMB.toFixed(2) });\n }\n\n // ==================== FILE OPERATIONS ====================\n\n /**\n * Adds a file to the archive\n * @param filePath - Path where the file will be stored in the archive\n * @param content - File content (string or Buffer)\n * @param options - Options for adding the file\n */\n addFile(filePath: string, content: string | Buffer, options: AddFileOptions = {}): void {\n this.writer.addFile(filePath, content, options);\n }\n\n /**\n * Adds multiple files to the archive\n * @param files - Map of file paths to contents\n * @param options - Options for adding files\n */\n addFiles(files: FileMap, options: AddFileOptions = {}): void {\n this.writer.addFiles(files, options);\n }\n\n /**\n * Updates an existing file in the archive\n * @param filePath - Path to the file to update\n * @param content - New content\n * @param options - Options for updating the file\n * @returns True if the file was updated, false if it didn't exist\n */\n updateFile(filePath: string, content: string | Buffer, options: AddFileOptions = {}): boolean {\n if (!this.hasFile(filePath)) {\n return false;\n }\n this.addFile(filePath, content, options);\n return true;\n }\n\n /**\n * Removes a file from the archive\n * @param filePath - Path to the file to remove\n * @returns True if the file was removed, false if it didn't exist\n */\n removeFile(filePath: string): boolean {\n return this.writer.removeFile(filePath);\n }\n\n /**\n * Gets a specific file from the archive\n * @param filePath - Path to the file within the archive\n * @returns The file data, or undefined if not found\n */\n getFile(filePath: string): ZipFile | undefined {\n return this.writer.getFile(filePath);\n }\n\n /**\n * Gets the content of a specific file as a string\n * @param filePath - Path to the file within the archive\n * @returns The file content as a UTF-8 string, or undefined if not found\n * @throws Error if attempting to convert a binary file to string (Issue #12 fix)\n *\n * **Encoding Note:**\n * - Returns UTF-8 decoded string content for text files\n * - All text content in DOCX files must be UTF-8 per OpenXML specification\n * - For binary files (images, fonts), throws an error to prevent garbage output\n * - Use getFileAsBuffer() for binary files instead\n */\n getFileAsString(filePath: string): string | undefined {\n const file = this.getFile(filePath);\n if (!file) {\n return undefined;\n }\n\n // Issue #12 fix: Prevent converting binary files to UTF-8 strings\n // This produces garbage output for images, fonts, etc.\n if (file.isBinary) {\n throw new Error(\n `Cannot convert binary file \"${filePath}\" to string. ` +\n `Binary files (images, fonts, etc.) cannot be safely converted to UTF-8 strings. ` +\n `Use getFileAsBuffer() instead to access the raw bytes.`\n );\n }\n\n // After ZipWriter fix (Issue #4), check actual content type instead of flag\n // Content from ZipWriter is always Buffer, content from ZipReader may be string\n if (Buffer.isBuffer(file.content)) {\n // Convert buffer to UTF-8 string\n return file.content.toString('utf8');\n }\n\n return file.content;\n }\n\n /**\n * Gets the content of a specific file as a buffer\n * @param filePath - Path to the file within the archive\n * @returns The file content as a Buffer, or undefined if not found\n *\n * **Encoding Note:**\n * - Returns Buffer with UTF-8 encoded content for text files\n * - For binary files, returns raw bytes unchanged\n * - All text content is guaranteed to be UTF-8 encoded\n */\n getFileAsBuffer(filePath: string): Buffer | undefined {\n const file = this.getFile(filePath);\n if (!file) {\n return undefined;\n }\n\n // After ZipWriter fix (Issue #4), check actual content type instead of flag\n // Content from ZipWriter is always Buffer, content from ZipReader may be string\n if (Buffer.isBuffer(file.content)) {\n return file.content;\n }\n\n // Encode string content as UTF-8 Buffer (for content from ZipReader)\n return Buffer.from(file.content, 'utf8');\n }\n\n /**\n * Gets all files from the archive\n * @returns Map of file paths to file data\n */\n getAllFiles(): FileMap {\n return this.writer.getAllFiles();\n }\n\n /**\n * Gets a list of all file paths in the archive\n * @returns Array of file paths\n */\n getFilePaths(): string[] {\n return this.writer.getFilePaths();\n }\n\n /**\n * Checks if a file exists in the archive\n * @param filePath - Path to check\n * @returns True if the file exists\n */\n hasFile(filePath: string): boolean {\n return this.writer.hasFile(filePath);\n }\n\n /**\n * Gets the number of files in the archive\n * @returns Number of files\n */\n getFileCount(): number {\n return this.writer.getFileCount();\n }\n\n // ==================== HELPER METHODS ====================\n\n /**\n * Renames a file in the archive\n * @param oldPath - Current path of the file\n * @param newPath - New path for the file\n * @returns True if the file was renamed, false if it didn't exist\n */\n renameFile(oldPath: string, newPath: string): boolean {\n const file = this.getFile(oldPath);\n if (!file) {\n return false;\n }\n this.addFile(newPath, file.content, {\n binary: file.isBinary,\n date: file.date,\n });\n this.removeFile(oldPath);\n return true;\n }\n\n /**\n * Copies a file within the archive\n * @param srcPath - Source file path\n * @param destPath - Destination file path\n * @returns True if the file was copied, false if source didn't exist\n */\n copyFile(srcPath: string, destPath: string): boolean {\n const file = this.getFile(srcPath);\n if (!file) {\n return false;\n }\n this.addFile(destPath, file.content, {\n binary: file.isBinary,\n date: file.date,\n });\n return true;\n }\n\n /**\n * Moves a file within the archive (copy and delete)\n * @param srcPath - Source file path\n * @param destPath - Destination file path\n * @returns True if the file was moved, false if source didn't exist\n */\n moveFile(srcPath: string, destPath: string): boolean {\n if (!this.copyFile(srcPath, destPath)) {\n return false;\n }\n this.removeFile(srcPath);\n return true;\n }\n\n /**\n * Checks if a file exists, throws if it doesn't\n * @param filePath - Path to check\n * @throws {Error} If file doesn't exist\n */\n existsOrThrow(filePath: string): void {\n if (!this.hasFile(filePath)) {\n throw new Error(`File not found in archive: ${filePath}`);\n }\n }\n\n /**\n * Removes multiple files from the archive\n * @param filePaths - Array of file paths to remove\n * @returns Number of files successfully removed\n */\n removeFiles(filePaths: string[]): number {\n let count = 0;\n for (const filePath of filePaths) {\n if (this.removeFile(filePath)) {\n count++;\n }\n }\n return count;\n }\n\n /**\n * Gets all files with a specific extension\n * @param extension - File extension (with or without leading dot)\n * @returns Array of files with the specified extension\n */\n getFilesByExtension(extension: string): ZipFile[] {\n const ext = extension.startsWith('.') ? extension : `.${extension}`;\n const files: ZipFile[] = [];\n for (const [path, file] of this.getAllFiles()) {\n if (path.toLowerCase().endsWith(ext.toLowerCase())) {\n files.push(file);\n }\n }\n return files;\n }\n\n /**\n * Gets the total uncompressed size of all files in the archive\n * @returns Total size in bytes\n */\n getTotalSize(): number {\n let totalSize = 0;\n for (const file of this.getAllFiles().values()) {\n totalSize += file.size;\n }\n return totalSize;\n }\n\n /**\n * Gets comprehensive statistics about the archive\n * @returns Statistics object\n */\n getStats(): {\n fileCount: number;\n totalSize: number;\n textFileCount: number;\n binaryFileCount: number;\n avgFileSize: number;\n } {\n let textCount = 0;\n let binaryCount = 0;\n const totalSize = this.getTotalSize();\n const fileCount = this.getFileCount();\n\n for (const file of this.getAllFiles().values()) {\n if (file.isBinary) {\n binaryCount++;\n } else {\n textCount++;\n }\n }\n\n return {\n fileCount,\n totalSize,\n textFileCount: textCount,\n binaryFileCount: binaryCount,\n avgFileSize: fileCount > 0 ? Math.round(totalSize / fileCount) : 0,\n };\n }\n\n /**\n * Checks if the archive is empty\n * @returns True if the archive has no files\n */\n isEmpty(): boolean {\n return this.getFileCount() === 0;\n }\n\n /**\n * Gets all text (non-binary) files from the archive\n * @returns Array of text files\n */\n getTextFiles(): ZipFile[] {\n const files: ZipFile[] = [];\n for (const file of this.getAllFiles().values()) {\n if (!file.isBinary) {\n files.push(file);\n }\n }\n return files;\n }\n\n /**\n * Gets all binary files from the archive\n * @returns Array of binary files\n */\n getBinaryFiles(): ZipFile[] {\n const files: ZipFile[] = [];\n for (const file of this.getAllFiles().values()) {\n if (file.isBinary) {\n files.push(file);\n }\n }\n return files;\n }\n\n /**\n * Gets all media files from the word/media/ directory\n * @returns Array of media files\n */\n getMediaFiles(): ZipFile[] {\n const files: ZipFile[] = [];\n for (const [path, file] of this.getAllFiles()) {\n if (path.startsWith('word/media/')) {\n files.push(file);\n }\n }\n return files;\n }\n\n /**\n * Exports a file from the archive to the filesystem\n * @param internalPath - Path of the file within the archive\n * @param outputPath - Path where the file will be saved\n */\n async exportFile(internalPath: string, outputPath: string): Promise<void> {\n const { promises: fs } = await import('fs');\n const content = this.getFileAsBuffer(internalPath);\n if (!content) {\n throw new Error(`File not found in archive: ${internalPath}`);\n }\n await fs.writeFile(outputPath, content);\n }\n\n /**\n * Imports a file from the filesystem into the archive\n * @param sourcePath - Path to the file on filesystem\n * @param internalPath - Path where the file will be stored in archive\n * @param options - Options for adding the file\n */\n async importFile(\n sourcePath: string,\n internalPath: string,\n options: AddFileOptions = {}\n ): Promise<void> {\n const { promises: fs } = await import('fs');\n const content = await fs.readFile(sourcePath);\n this.addFile(internalPath, content, {\n ...options,\n binary: options.binary !== undefined ? options.binary : true,\n });\n }\n\n // ==================== SAVING ====================\n\n /**\n * Saves the archive to a file\n * @param filePath - Path where the file will be saved\n * @param options - Save options\n */\n async save(filePath: string, options: SaveOptions = {}): Promise<void> {\n const logger = getLogger();\n logger.info('Saving DOCX file', { path: filePath, fileCount: this.getFileCount() });\n await this.writer.saveToFile(filePath, options);\n logger.info('DOCX file saved', { path: filePath });\n }\n\n /**\n * Generates the ZIP archive as a buffer\n * @param options - Save options\n * @returns Buffer containing the ZIP archive\n */\n async toBuffer(options: SaveOptions = {}): Promise<Buffer> {\n const logger = getLogger();\n logger.info('Generating DOCX buffer', { fileCount: this.getFileCount() });\n const buffer = await this.writer.toBuffer(options);\n logger.info('DOCX buffer generated', { bufferSize: buffer.length });\n return buffer;\n }\n\n // ==================== VALIDATION ====================\n\n /**\n * Validates the DOCX structure\n * @throws {MissingRequiredFileError} If required files are missing\n */\n validate(): void {\n const logger = getLogger();\n logger.info('Validating DOCX structure');\n this.writer.validate();\n logger.info('DOCX structure valid');\n }\n\n // ==================== UTILITY ====================\n\n /**\n * Creates a new empty archive\n */\n clear(): void {\n this.reader.clear();\n this.writer.clear();\n this.mode = 'write';\n }\n\n /**\n * Gets the current mode of the handler\n * @returns Current mode ('read', 'write', or 'modify')\n */\n getMode(): 'read' | 'write' | 'modify' {\n return this.mode;\n }\n\n /**\n * Checks if the archive has been loaded\n * @returns True if loaded\n */\n isLoaded(): boolean {\n return this.reader.isLoaded();\n }\n\n /**\n * Creates a clone of this handler with all its files\n * @returns A new ZipHandler instance with the same files\n */\n clone(): ZipHandler {\n const newHandler = new ZipHandler();\n newHandler.writer = this.writer.clone();\n newHandler.mode = this.mode;\n return newHandler;\n }\n\n // ==================== CONVENIENCE METHODS ====================\n\n /**\n * Reads a DOCX file, modifies it, and saves it back\n * @param inputPath - Path to the input DOCX file\n * @param outputPath - Path to save the modified file\n * @param modifier - Function that modifies the handler\n * @param loadOptions - Options for loading\n * @param saveOptions - Options for saving\n */\n static async modify(\n inputPath: string,\n outputPath: string,\n modifier: (handler: ZipHandler) => void | Promise<void>,\n loadOptions: LoadOptions = {},\n saveOptions: SaveOptions = {}\n ): Promise<void> {\n const handler = new ZipHandler();\n await handler.load(inputPath, loadOptions);\n await modifier(handler);\n await handler.save(outputPath, saveOptions);\n }\n\n /**\n * Creates a new DOCX file with the provided files\n * @param outputPath - Path to save the new file\n * @param files - Map of file paths to contents\n * @param saveOptions - Options for saving\n */\n static async create(\n outputPath: string,\n files: FileMap,\n saveOptions: SaveOptions = {}\n ): Promise<void> {\n const handler = new ZipHandler();\n handler.addFiles(files);\n await handler.save(outputPath, saveOptions);\n }\n}\n"]}
1
+ {"version":3,"file":"ZipHandler.js","sourceRoot":"","sources":["../../src/zip/ZipHandler.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * ZipHandler - Main facade for ZIP archive operations\n * Provides a unified interface for reading and writing DOCX files\n */\n\nimport { ZipReader } from './ZipReader.js';\nimport { ZipWriter } from './ZipWriter.js';\nimport {\n ZipFile,\n FileMap,\n LoadOptions,\n SaveOptions,\n AddFileOptions,\n SizeLimitOptions,\n DEFAULT_SIZE_LIMITS,\n} from './types.js';\nimport { ResourceLimitError } from './errors.js';\nimport { getGlobalLogger, createScopedLogger, ILogger } from '../utils/logger.js';\nimport { normalizePath } from '../utils/validation.js';\n\n// Create scoped logger for ZipHandler operations\nfunction getLogger(): ILogger {\n return createScopedLogger(getGlobalLogger(), 'ZipHandler');\n}\n\n/**\n * Main class for handling ZIP archives (DOCX files)\n * Combines reading and writing operations into a single interface\n */\nexport class ZipHandler {\n private reader: ZipReader;\n private writer: ZipWriter;\n private mode: 'read' | 'write' | 'modify' = 'write';\n\n constructor() {\n this.reader = new ZipReader();\n this.writer = new ZipWriter();\n }\n\n // ==================== SIZE VALIDATION ====================\n\n /**\n * Validates document size against configured limits\n * @param sizeMB - Size in megabytes\n * @param limits - Size limit options (merged with defaults)\n * @throws {ResourceLimitError} If the compressed size exceeds maxSizeMB\n */\n private validateDocumentSize(sizeMB: number, limits: Required<SizeLimitOptions>): void {\n const logger = getLogger();\n const { warningSizeMB, maxSizeMB } = limits;\n\n // Check maximum size (if enabled). Throw the typed ResourceLimitError so this\n // compressed-size guard is catchable by the same type as the uncompressed/ratio/\n // entry-count guards enforced downstream in ZipReader — callers can distinguish a\n // hostile/oversized payload from a genuinely corrupt archive.\n if (maxSizeMB > 0 && sizeMB > maxSizeMB) {\n logger.error('Document exceeds maximum size', { sizeMB: sizeMB.toFixed(1), maxSizeMB });\n throw new ResourceLimitError(\n `Document size (${sizeMB.toFixed(1)}MB) exceeds maximum supported size (${maxSizeMB}MB). ` +\n `This would likely cause out-of-memory errors. Consider:\\n` +\n `- Compressing/optimizing images\\n` +\n `- Splitting into multiple documents\\n` +\n `- Processing on a machine with more memory\\n` +\n `- Increasing maxSizeMB via LoadOptions.sizeLimits (not recommended)`\n );\n }\n\n // Warn on large files (if enabled)\n if (warningSizeMB > 0 && sizeMB > warningSizeMB) {\n logger.warn('Large document detected', { sizeMB: sizeMB.toFixed(1), warningSizeMB });\n }\n }\n\n /**\n * Gets merged size limits from options and defaults\n * @param options - Load options that may contain size limits\n * @returns Merged size limits with all values defined\n */\n private getSizeLimits(options: LoadOptions): Required<SizeLimitOptions> {\n return {\n ...DEFAULT_SIZE_LIMITS,\n ...options.sizeLimits,\n };\n }\n\n // ==================== LOADING ====================\n\n /**\n * Loads a DOCX file from the filesystem\n * @param filePath - Path to the DOCX file\n * @param options - Load options (including configurable size limits)\n */\n async load(filePath: string, options: LoadOptions = {}): Promise<void> {\n const logger = getLogger();\n logger.info('Loading DOCX file', { path: filePath });\n\n // Check file size before loading\n const { promises: fs } = await import('fs');\n const stats = await fs.stat(filePath);\n const sizeMB = stats.size / (1024 * 1024);\n\n // Validate against configurable limits\n const limits = this.getSizeLimits(options);\n this.validateDocumentSize(sizeMB, limits);\n\n try {\n await this.reader.loadFromFile(filePath, options);\n\n // Copy all files from reader to writer for modification\n const files = this.reader.getAllFiles();\n this.writer.clear();\n this.writer.addFiles(files);\n\n this.mode = 'modify';\n logger.info('DOCX file loaded', { fileCount: files.size, sizeMB: sizeMB.toFixed(2) });\n } catch (error) {\n // A failed reload must not leave the handler serving the previously loaded\n // archive. Reset to a deterministically empty state so getFile/save/etc.\n // cannot silently return stale content. A fresh reader discards any\n // partially-extracted files and the stale `loaded` flag.\n this.writer.clear();\n this.reader = new ZipReader();\n this.mode = 'write';\n throw error;\n }\n }\n\n /**\n * Loads a DOCX file from a buffer\n * @param buffer - Buffer containing the DOCX data\n * @param options - Load options (including configurable size limits)\n */\n async loadFromBuffer(buffer: Buffer, options: LoadOptions = {}): Promise<void> {\n const logger = getLogger();\n logger.info('Loading DOCX from buffer', { bufferSize: buffer.length });\n\n // Check buffer size before loading\n const sizeMB = buffer.length / (1024 * 1024);\n\n // Validate against configurable limits\n const limits = this.getSizeLimits(options);\n this.validateDocumentSize(sizeMB, limits);\n\n try {\n await this.reader.loadFromBuffer(buffer, options);\n\n // Copy all files from reader to writer for modification\n const files = this.reader.getAllFiles();\n this.writer.clear();\n this.writer.addFiles(files);\n\n this.mode = 'modify';\n logger.info('DOCX buffer loaded', { fileCount: files.size, sizeMB: sizeMB.toFixed(2) });\n } catch (error) {\n // A failed reload must not leave the handler serving the previously loaded\n // archive. Reset to a deterministically empty state so getFile/save/etc.\n // cannot silently return stale content. A fresh reader discards any\n // partially-extracted files and the stale `loaded` flag.\n this.writer.clear();\n this.reader = new ZipReader();\n this.mode = 'write';\n throw error;\n }\n }\n\n // ==================== FILE OPERATIONS ====================\n\n /**\n * Adds a file to the archive\n * @param filePath - Path where the file will be stored in the archive\n * @param content - File content (string or Buffer)\n * @param options - Options for adding the file\n */\n addFile(filePath: string, content: string | Buffer, options: AddFileOptions = {}): void {\n this.writer.addFile(filePath, content, options);\n }\n\n /**\n * Adds multiple files to the archive\n * @param files - Map of file paths to contents\n * @param options - Options for adding files\n */\n addFiles(files: FileMap, options: AddFileOptions = {}): void {\n this.writer.addFiles(files, options);\n }\n\n /**\n * Updates an existing file in the archive\n * @param filePath - Path to the file to update\n * @param content - New content\n * @param options - Options for updating the file\n * @returns True if the file was updated, false if it didn't exist\n */\n updateFile(filePath: string, content: string | Buffer, options: AddFileOptions = {}): boolean {\n if (!this.hasFile(filePath)) {\n return false;\n }\n this.addFile(filePath, content, options);\n return true;\n }\n\n /**\n * Removes a file from the archive\n * @param filePath - Path to the file to remove\n * @returns True if the file was removed, false if it didn't exist\n */\n removeFile(filePath: string): boolean {\n return this.writer.removeFile(filePath);\n }\n\n /**\n * Gets a specific file from the archive\n * @param filePath - Path to the file within the archive\n * @returns The file data, or undefined if not found\n */\n getFile(filePath: string): ZipFile | undefined {\n return this.writer.getFile(filePath);\n }\n\n /**\n * Gets the content of a specific file as a string\n * @param filePath - Path to the file within the archive\n * @returns The file content as a UTF-8 string, or undefined if not found\n * @throws Error if attempting to convert a binary file to string (Issue #12 fix)\n *\n * **Encoding Note:**\n * - Returns UTF-8 decoded string content for text files\n * - All text content in DOCX files must be UTF-8 per OpenXML specification\n * - For binary files (images, fonts), throws an error to prevent garbage output\n * - Use getFileAsBuffer() for binary files instead\n */\n getFileAsString(filePath: string): string | undefined {\n const file = this.getFile(filePath);\n if (!file) {\n return undefined;\n }\n\n // Issue #12 fix: Prevent converting binary files to UTF-8 strings\n // This produces garbage output for images, fonts, etc.\n if (file.isBinary) {\n throw new Error(\n `Cannot convert binary file \"${filePath}\" to string. ` +\n `Binary files (images, fonts, etc.) cannot be safely converted to UTF-8 strings. ` +\n `Use getFileAsBuffer() instead to access the raw bytes.`\n );\n }\n\n // After ZipWriter fix (Issue #4), check actual content type instead of flag\n // Content from ZipWriter is always Buffer, content from ZipReader may be string\n if (Buffer.isBuffer(file.content)) {\n // Convert buffer to UTF-8 string\n return file.content.toString('utf8');\n }\n\n return file.content;\n }\n\n /**\n * Gets the content of a specific file as a buffer\n * @param filePath - Path to the file within the archive\n * @returns The file content as a Buffer, or undefined if not found\n *\n * **Encoding Note:**\n * - Returns Buffer with UTF-8 encoded content for text files\n * - For binary files, returns raw bytes unchanged\n * - All text content is guaranteed to be UTF-8 encoded\n */\n getFileAsBuffer(filePath: string): Buffer | undefined {\n const file = this.getFile(filePath);\n if (!file) {\n return undefined;\n }\n\n // After ZipWriter fix (Issue #4), check actual content type instead of flag\n // Content from ZipWriter is always Buffer, content from ZipReader may be string\n if (Buffer.isBuffer(file.content)) {\n return file.content;\n }\n\n // Encode string content as UTF-8 Buffer (for content from ZipReader)\n return Buffer.from(file.content, 'utf8');\n }\n\n /**\n * Gets all files from the archive\n * @returns Map of file paths to file data\n */\n getAllFiles(): FileMap {\n return this.writer.getAllFiles();\n }\n\n /**\n * Gets a list of all file paths in the archive\n * @returns Array of file paths\n */\n getFilePaths(): string[] {\n return this.writer.getFilePaths();\n }\n\n /**\n * Checks if a file exists in the archive\n * @param filePath - Path to check\n * @returns True if the file exists\n */\n hasFile(filePath: string): boolean {\n return this.writer.hasFile(filePath);\n }\n\n /**\n * Gets the number of files in the archive\n * @returns Number of files\n */\n getFileCount(): number {\n return this.writer.getFileCount();\n }\n\n // ==================== HELPER METHODS ====================\n\n /**\n * Renames a file in the archive\n * @param oldPath - Current path of the file\n * @param newPath - New path for the file\n * @returns True if the file was renamed, false if it didn't exist\n */\n renameFile(oldPath: string, newPath: string): boolean {\n const file = this.getFile(oldPath);\n if (!file) {\n return false;\n }\n // Guard against degenerate rename where source and destination collapse to\n // the same archive key (identical strings or backslash/forward-slash variants).\n // Without this, addFile would overwrite the single entry and the subsequent\n // removeFile would delete it, silently destroying the file.\n if (normalizePath(oldPath) === normalizePath(newPath)) {\n return true;\n }\n this.addFile(newPath, file.content, {\n binary: file.isBinary,\n date: file.date,\n });\n this.removeFile(oldPath);\n return true;\n }\n\n /**\n * Copies a file within the archive\n * @param srcPath - Source file path\n * @param destPath - Destination file path\n * @returns True if the file was copied, false if source didn't exist\n */\n copyFile(srcPath: string, destPath: string): boolean {\n const file = this.getFile(srcPath);\n if (!file) {\n return false;\n }\n this.addFile(destPath, file.content, {\n binary: file.isBinary,\n date: file.date,\n });\n return true;\n }\n\n /**\n * Moves a file within the archive (copy and delete)\n * @param srcPath - Source file path\n * @param destPath - Destination file path\n * @returns True if the file was moved, false if source didn't exist\n */\n moveFile(srcPath: string, destPath: string): boolean {\n if (!this.copyFile(srcPath, destPath)) {\n return false;\n }\n // Guard against degenerate move where source and destination collapse to the\n // same archive key. copyFile already re-added the (identical) entry, so the\n // removeFile below would delete it, silently destroying the file.\n if (normalizePath(srcPath) === normalizePath(destPath)) {\n return true;\n }\n this.removeFile(srcPath);\n return true;\n }\n\n /**\n * Checks if a file exists, throws if it doesn't\n * @param filePath - Path to check\n * @throws {Error} If file doesn't exist\n */\n existsOrThrow(filePath: string): void {\n if (!this.hasFile(filePath)) {\n throw new Error(`File not found in archive: ${filePath}`);\n }\n }\n\n /**\n * Removes multiple files from the archive\n * @param filePaths - Array of file paths to remove\n * @returns Number of files successfully removed\n */\n removeFiles(filePaths: string[]): number {\n let count = 0;\n for (const filePath of filePaths) {\n if (this.removeFile(filePath)) {\n count++;\n }\n }\n return count;\n }\n\n /**\n * Gets all files with a specific extension\n * @param extension - File extension (with or without leading dot)\n * @returns Array of files with the specified extension\n */\n getFilesByExtension(extension: string): ZipFile[] {\n const ext = extension.startsWith('.') ? extension : `.${extension}`;\n const files: ZipFile[] = [];\n for (const [path, file] of this.getAllFiles()) {\n if (path.toLowerCase().endsWith(ext.toLowerCase())) {\n files.push(file);\n }\n }\n return files;\n }\n\n /**\n * Gets the total uncompressed size of all files in the archive\n * @returns Total size in bytes\n */\n getTotalSize(): number {\n let totalSize = 0;\n for (const file of this.getAllFiles().values()) {\n totalSize += file.size;\n }\n return totalSize;\n }\n\n /**\n * Gets comprehensive statistics about the archive\n * @returns Statistics object\n */\n getStats(): {\n fileCount: number;\n totalSize: number;\n textFileCount: number;\n binaryFileCount: number;\n avgFileSize: number;\n } {\n let textCount = 0;\n let binaryCount = 0;\n const totalSize = this.getTotalSize();\n const fileCount = this.getFileCount();\n\n for (const file of this.getAllFiles().values()) {\n if (file.isBinary) {\n binaryCount++;\n } else {\n textCount++;\n }\n }\n\n return {\n fileCount,\n totalSize,\n textFileCount: textCount,\n binaryFileCount: binaryCount,\n avgFileSize: fileCount > 0 ? Math.round(totalSize / fileCount) : 0,\n };\n }\n\n /**\n * Checks if the archive is empty\n * @returns True if the archive has no files\n */\n isEmpty(): boolean {\n return this.getFileCount() === 0;\n }\n\n /**\n * Gets all text (non-binary) files from the archive\n * @returns Array of text files\n */\n getTextFiles(): ZipFile[] {\n const files: ZipFile[] = [];\n for (const file of this.getAllFiles().values()) {\n if (!file.isBinary) {\n files.push(file);\n }\n }\n return files;\n }\n\n /**\n * Gets all binary files from the archive\n * @returns Array of binary files\n */\n getBinaryFiles(): ZipFile[] {\n const files: ZipFile[] = [];\n for (const file of this.getAllFiles().values()) {\n if (file.isBinary) {\n files.push(file);\n }\n }\n return files;\n }\n\n /**\n * Gets all media files from the word/media/ directory\n * @returns Array of media files\n */\n getMediaFiles(): ZipFile[] {\n const files: ZipFile[] = [];\n for (const [path, file] of this.getAllFiles()) {\n if (path.startsWith('word/media/')) {\n files.push(file);\n }\n }\n return files;\n }\n\n /**\n * Exports a file from the archive to the filesystem\n * @param internalPath - Path of the file within the archive\n * @param outputPath - Path where the file will be saved\n */\n async exportFile(internalPath: string, outputPath: string): Promise<void> {\n const { promises: fs } = await import('fs');\n const content = this.getFileAsBuffer(internalPath);\n if (!content) {\n throw new Error(`File not found in archive: ${internalPath}`);\n }\n await fs.writeFile(outputPath, content);\n }\n\n /**\n * Imports a file from the filesystem into the archive\n * @param sourcePath - Path to the file on filesystem\n * @param internalPath - Path where the file will be stored in archive\n * @param options - Options for adding the file\n */\n async importFile(\n sourcePath: string,\n internalPath: string,\n options: AddFileOptions = {}\n ): Promise<void> {\n const { promises: fs } = await import('fs');\n const content = await fs.readFile(sourcePath);\n this.addFile(internalPath, content, {\n ...options,\n binary: options.binary !== undefined ? options.binary : true,\n });\n }\n\n // ==================== SAVING ====================\n\n /**\n * Saves the archive to a file\n * @param filePath - Path where the file will be saved\n * @param options - Save options\n */\n async save(filePath: string, options: SaveOptions = {}): Promise<void> {\n const logger = getLogger();\n logger.info('Saving DOCX file', { path: filePath, fileCount: this.getFileCount() });\n await this.writer.saveToFile(filePath, options);\n logger.info('DOCX file saved', { path: filePath });\n }\n\n /**\n * Generates the ZIP archive as a buffer\n * @param options - Save options\n * @returns Buffer containing the ZIP archive\n */\n async toBuffer(options: SaveOptions = {}): Promise<Buffer> {\n const logger = getLogger();\n logger.info('Generating DOCX buffer', { fileCount: this.getFileCount() });\n const buffer = await this.writer.toBuffer(options);\n logger.info('DOCX buffer generated', { bufferSize: buffer.length });\n return buffer;\n }\n\n // ==================== VALIDATION ====================\n\n /**\n * Validates the DOCX structure\n * @throws {MissingRequiredFileError} If required files are missing\n */\n validate(): void {\n const logger = getLogger();\n logger.info('Validating DOCX structure');\n this.writer.validate();\n logger.info('DOCX structure valid');\n }\n\n // ==================== UTILITY ====================\n\n /**\n * Creates a new empty archive\n */\n clear(): void {\n this.reader.clear();\n this.writer.clear();\n this.mode = 'write';\n }\n\n /**\n * Gets the current mode of the handler\n * @returns Current mode ('read', 'write', or 'modify')\n */\n getMode(): 'read' | 'write' | 'modify' {\n return this.mode;\n }\n\n /**\n * Checks if the archive has been loaded\n * @returns True if loaded\n */\n isLoaded(): boolean {\n return this.reader.isLoaded();\n }\n\n /**\n * Creates a clone of this handler with all its files\n * @returns A new ZipHandler instance with the same files\n */\n clone(): ZipHandler {\n const newHandler = new ZipHandler();\n newHandler.writer = this.writer.clone();\n newHandler.mode = this.mode;\n return newHandler;\n }\n\n // ==================== CONVENIENCE METHODS ====================\n\n /**\n * Reads a DOCX file, modifies it, and saves it back\n * @param inputPath - Path to the input DOCX file\n * @param outputPath - Path to save the modified file\n * @param modifier - Function that modifies the handler\n * @param loadOptions - Options for loading\n * @param saveOptions - Options for saving\n */\n static async modify(\n inputPath: string,\n outputPath: string,\n modifier: (handler: ZipHandler) => void | Promise<void>,\n loadOptions: LoadOptions = {},\n saveOptions: SaveOptions = {}\n ): Promise<void> {\n const handler = new ZipHandler();\n await handler.load(inputPath, loadOptions);\n await modifier(handler);\n await handler.save(outputPath, saveOptions);\n }\n\n /**\n * Creates a new DOCX file with the provided files\n * @param outputPath - Path to save the new file\n * @param files - Map of file paths to contents\n * @param saveOptions - Options for saving\n */\n static async create(\n outputPath: string,\n files: FileMap,\n saveOptions: SaveOptions = {}\n ): Promise<void> {\n const handler = new ZipHandler();\n handler.addFiles(files);\n await handler.save(outputPath, saveOptions);\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"ZipReader.d.ts","sourceRoot":"","sources":["../../src/zip/ZipReader.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAiB3D,qBAAa,SAAS;IACpB,OAAO,CAAC,GAAG,CAAsB;IACjC,OAAO,CAAC,KAAK,CAAsB;IACnC,OAAO,CAAC,MAAM,CAAS;IAOjB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IA0BxE,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;YAuChE,YAAY;IAmD1B,OAAO,CAAC,QAAQ;IAUhB,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS;IAgB9C,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IA0BrD,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAoBrD,WAAW,IAAI,OAAO;IAStB,YAAY,IAAI,MAAM,EAAE;IAUxB,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAWlC,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,EAAE;IAqB7C,OAAO,CAAC,YAAY;IAUpB,QAAQ,IAAI,OAAO;IAOnB,KAAK,IAAI,IAAI;CAKd"}
1
+ {"version":3,"file":"ZipReader.d.ts","sourceRoot":"","sources":["../../src/zip/ZipReader.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,EAAyC,MAAM,YAAY,CAAC;AAqClG,qBAAa,SAAS;IACpB,OAAO,CAAC,GAAG,CAAsB;IACjC,OAAO,CAAC,KAAK,CAAsB;IACnC,OAAO,CAAC,MAAM,CAAS;IAOjB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAiCxE,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;YA+DhE,YAAY;IA0H1B,OAAO,CAAC,QAAQ;IAUhB,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS;IAgB9C,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IA0BrD,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAoBrD,WAAW,IAAI,OAAO;IAStB,YAAY,IAAI,MAAM,EAAE;IAUxB,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAWlC,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,EAAE;IAqB7C,OAAO,CAAC,YAAY;IAUpB,QAAQ,IAAI,OAAO;IAOnB,KAAK,IAAI,IAAI;CAKd"}
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.ZipReader = void 0;
7
7
  const jszip_1 = __importDefault(require("jszip"));
8
8
  const fs_1 = require("fs");
9
+ const types_js_1 = require("./types.js");
9
10
  const errors_js_1 = require("./errors.js");
10
11
  const validation_js_1 = require("../utils/validation.js");
11
12
  class ZipReader {
@@ -24,7 +25,10 @@ class ZipReader {
24
25
  await this.loadFromBuffer(buffer, options);
25
26
  }
26
27
  catch (error) {
27
- if (error instanceof errors_js_1.DocxNotFoundError) {
28
+ if (error instanceof errors_js_1.DocxNotFoundError ||
29
+ error instanceof errors_js_1.ResourceLimitError ||
30
+ error instanceof errors_js_1.InvalidDocxError ||
31
+ error instanceof errors_js_1.CorruptedArchiveError) {
28
32
  throw error;
29
33
  }
30
34
  const message = error instanceof Error ? error.message : String(error);
@@ -33,37 +37,57 @@ class ZipReader {
33
37
  }
34
38
  async loadFromBuffer(buffer, options = {}) {
35
39
  const { validate = true } = options;
40
+ const limits = { ...types_js_1.DEFAULT_SIZE_LIMITS, ...options.sizeLimits };
36
41
  try {
37
42
  if (!(0, validation_js_1.isValidZipBuffer)(buffer)) {
38
43
  throw new errors_js_1.InvalidDocxError('File is not a valid ZIP archive');
39
44
  }
40
45
  this.zip = await jszip_1.default.loadAsync(buffer);
41
- await this.extractFiles();
46
+ const filePaths = Object.keys(this.zip.files).filter((path) => !this.zip.files[path].dir);
47
+ if (limits.maxEntryCount > 0 && filePaths.length > limits.maxEntryCount) {
48
+ throw new errors_js_1.ResourceLimitError(`archive entry count (${filePaths.length}) exceeds maxEntryCount (${limits.maxEntryCount})`);
49
+ }
50
+ await this.extractFiles(limits, filePaths);
42
51
  if (validate) {
43
52
  this.validate();
44
53
  }
45
54
  this.loaded = true;
46
55
  }
47
56
  catch (error) {
48
- if (error instanceof errors_js_1.InvalidDocxError) {
57
+ if (error instanceof errors_js_1.InvalidDocxError || error instanceof errors_js_1.ResourceLimitError) {
49
58
  throw error;
50
59
  }
51
60
  const message = error instanceof Error ? error.message : String(error);
52
61
  throw new errors_js_1.CorruptedArchiveError(message);
53
62
  }
54
63
  }
55
- async extractFiles() {
64
+ async extractFiles(limits, filePaths) {
56
65
  if (!this.zip) {
57
66
  throw new Error('ZIP archive not loaded');
58
67
  }
59
68
  this.files.clear();
60
- const filePaths = Object.keys(this.zip.files).filter((path) => !this.zip.files[path].dir);
69
+ const maxEntryBytes = limits.maxEntryUncompressedMB > 0 ? limits.maxEntryUncompressedMB * 1024 * 1024 : 0;
70
+ const maxTotalBytes = limits.maxTotalUncompressedMB > 0 ? limits.maxTotalUncompressedMB * 1024 * 1024 : 0;
71
+ const ratioFloorBytes = 1024 * 1024;
72
+ let totalBytes = 0;
61
73
  for (const filePath of filePaths) {
62
74
  const normalizedPath = (0, validation_js_1.normalizePath)(filePath);
63
75
  const zipObject = this.zip.files[filePath];
64
76
  if (!zipObject) {
65
77
  continue;
66
78
  }
79
+ const internalData = zipObject._data;
80
+ const declaredSize = internalData?.uncompressedSize;
81
+ if (typeof declaredSize === 'number' && declaredSize >= 0) {
82
+ if (maxEntryBytes > 0 && declaredSize > maxEntryBytes) {
83
+ throw new errors_js_1.ResourceLimitError(`entry "${normalizedPath}" uncompressed size (${declaredSize} bytes) exceeds ` +
84
+ `maxEntryUncompressedMB (${limits.maxEntryUncompressedMB}MB)`);
85
+ }
86
+ if (maxTotalBytes > 0 && totalBytes + declaredSize > maxTotalBytes) {
87
+ throw new errors_js_1.ResourceLimitError(`total uncompressed size would exceed maxTotalUncompressedMB ` +
88
+ `(${limits.maxTotalUncompressedMB}MB)`);
89
+ }
90
+ }
67
91
  const isBinary = (0, validation_js_1.isBinaryFile)(normalizedPath);
68
92
  let content;
69
93
  if (isBinary) {
@@ -72,6 +96,29 @@ class ZipReader {
72
96
  else {
73
97
  content = await zipObject.async('string');
74
98
  }
99
+ const entryBytes = Buffer.isBuffer(content)
100
+ ? content.length
101
+ : Buffer.byteLength(content, 'utf8');
102
+ if (maxEntryBytes > 0 && entryBytes > maxEntryBytes) {
103
+ throw new errors_js_1.ResourceLimitError(`entry "${normalizedPath}" uncompressed size (${entryBytes} bytes) exceeds ` +
104
+ `maxEntryUncompressedMB (${limits.maxEntryUncompressedMB}MB)`);
105
+ }
106
+ totalBytes += entryBytes;
107
+ if (maxTotalBytes > 0 && totalBytes > maxTotalBytes) {
108
+ throw new errors_js_1.ResourceLimitError(`total uncompressed size (${totalBytes} bytes) exceeds maxTotalUncompressedMB ` +
109
+ `(${limits.maxTotalUncompressedMB}MB)`);
110
+ }
111
+ const compressedSize = internalData?.compressedSize;
112
+ if (limits.maxCompressionRatio > 0 &&
113
+ entryBytes > ratioFloorBytes &&
114
+ typeof compressedSize === 'number' &&
115
+ compressedSize > 0) {
116
+ const ratio = entryBytes / compressedSize;
117
+ if (ratio > limits.maxCompressionRatio) {
118
+ throw new errors_js_1.ResourceLimitError(`entry "${normalizedPath}" compression ratio (${ratio.toFixed(1)}:1) exceeds ` +
119
+ `maxCompressionRatio (${limits.maxCompressionRatio}:1)`);
120
+ }
121
+ }
75
122
  const date = zipObject.date;
76
123
  this.files.set(normalizedPath, {
77
124
  path: normalizedPath,
@@ -1 +1 @@
1
- {"version":3,"file":"ZipReader.js","sourceRoot":"","sources":["../../src/zip/ZipReader.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * ZipReader - Handles reading ZIP archives (DOCX files)\n */\n\nimport JSZip from 'jszip';\nimport { promises as fs } from 'fs';\nimport { ZipFile, FileMap, LoadOptions } from './types.js';\nimport {\n DocxNotFoundError,\n InvalidDocxError,\n CorruptedArchiveError,\n FileOperationError,\n} from './errors.js';\nimport {\n validateDocxStructure,\n isBinaryFile,\n normalizePath,\n isValidZipBuffer,\n} from '../utils/validation.js';\n\n/**\n * Handles reading operations on ZIP archives\n */\nexport class ZipReader {\n private zip: JSZip | null = null;\n private files: FileMap = new Map();\n private loaded = false;\n\n /**\n * Loads a DOCX file from the filesystem\n * @param filePath - Path to the DOCX file\n * @param options - Load options\n */\n async loadFromFile(filePath: string, options: LoadOptions = {}): Promise<void> {\n try {\n // Check if file exists\n try {\n await fs.access(filePath);\n } catch {\n throw new DocxNotFoundError(filePath);\n }\n\n // Read file as buffer\n const buffer = await fs.readFile(filePath);\n await this.loadFromBuffer(buffer, options);\n } catch (error: unknown) {\n if (error instanceof DocxNotFoundError) {\n throw error;\n }\n const message = error instanceof Error ? error.message : String(error);\n throw new FileOperationError('read', message);\n }\n }\n\n /**\n * Loads a DOCX file from a buffer\n * @param buffer - Buffer containing the DOCX data\n * @param options - Load options\n */\n async loadFromBuffer(buffer: Buffer, options: LoadOptions = {}): Promise<void> {\n const { validate = true } = options;\n\n try {\n // Validate ZIP signature\n if (!isValidZipBuffer(buffer)) {\n throw new InvalidDocxError('File is not a valid ZIP archive');\n }\n\n // Load ZIP archive\n this.zip = await JSZip.loadAsync(buffer);\n\n // Extract all files\n await this.extractFiles();\n\n // Validate DOCX structure if requested\n if (validate) {\n this.validate();\n }\n\n this.loaded = true;\n } catch (error: unknown) {\n if (error instanceof InvalidDocxError) {\n throw error;\n }\n const message = error instanceof Error ? error.message : String(error);\n throw new CorruptedArchiveError(message);\n }\n }\n\n /**\n * Extracts all files from the ZIP archive into memory\n *\n * **Encoding Note:**\n * - Text files (XML, etc.) are extracted as UTF-8 strings using `async('string')`\n * - JSZip automatically decodes UTF-8 when extracting as 'string'\n * - Binary files are extracted as Buffers to preserve exact content\n * - All text content is guaranteed to be valid UTF-8\n */\n private async extractFiles(): Promise<void> {\n if (!this.zip) {\n throw new Error('ZIP archive not loaded');\n }\n\n this.files.clear();\n\n // Get all file paths\n const filePaths = Object.keys(this.zip.files).filter((path) => !this.zip!.files[path]!.dir);\n\n // Extract each file\n for (const filePath of filePaths) {\n const normalizedPath = normalizePath(filePath);\n const zipObject = this.zip.files[filePath];\n\n if (!zipObject) {\n continue;\n }\n\n const isBinary = isBinaryFile(normalizedPath);\n\n // Extract content based on type\n // For text files: JSZip's async('string') automatically uses UTF-8 decoding\n // For binary files: async('nodebuffer') preserves exact bytes\n let content;\n if (isBinary) {\n content = await zipObject.async('nodebuffer');\n } else {\n // Text files are extracted as UTF-8 strings\n // JSZip automatically handles UTF-8 decoding for 'string' type\n content = await zipObject.async('string');\n }\n\n // Get file metadata\n const date = zipObject.date;\n\n // Store file information\n this.files.set(normalizedPath, {\n path: normalizedPath,\n content,\n isBinary,\n size: isBinary ? (content as Buffer).length : (content as string).length,\n date,\n });\n }\n }\n\n /**\n * Validates the DOCX structure\n * @throws {MissingRequiredFileError} If required files are missing\n */\n private validate(): void {\n const filePaths = Array.from(this.files.keys());\n validateDocxStructure(filePaths);\n }\n\n /**\n * Gets a specific file from the archive\n * @param filePath - Path to the file within the archive\n * @returns The file data, or undefined if not found\n */\n getFile(filePath: string): ZipFile | undefined {\n this.ensureLoaded();\n const normalizedPath = normalizePath(filePath);\n return this.files.get(normalizedPath);\n }\n\n /**\n * Gets the content of a specific file as a string\n * @param filePath - Path to the file within the archive\n * @returns The file content as a UTF-8 string, or undefined if not found\n *\n * **Encoding Note:**\n * - Returns UTF-8 decoded string content\n * - For binary files, converts the Buffer to UTF-8 string\n * - Assumes all text content is UTF-8 encoded (per OpenXML standard)\n */\n getFileAsString(filePath: string): string | undefined {\n const file = this.getFile(filePath);\n if (!file) {\n return undefined;\n }\n\n // Check actual content type instead of flag (Issue #4)\n // Content is Buffer for binary files, string for text files\n if (Buffer.isBuffer(file.content)) {\n // Convert binary buffer to UTF-8 string\n return file.content.toString('utf8');\n }\n\n return file.content;\n }\n\n /**\n * Gets the content of a specific file as a buffer\n * @param filePath - Path to the file within the archive\n * @returns The file content as a Buffer, or undefined if not found\n *\n * **Encoding Note:**\n * - Returns Buffer with UTF-8 encoded content for text files\n * - For binary files, returns raw bytes\n * - String content is explicitly encoded as UTF-8\n */\n getFileAsBuffer(filePath: string): Buffer | undefined {\n const file = this.getFile(filePath);\n if (!file) {\n return undefined;\n }\n\n // Check actual content type instead of flag (Issue #4)\n // Content is Buffer for binary files, string for text files\n if (Buffer.isBuffer(file.content)) {\n return file.content;\n }\n\n // Encode string content as UTF-8 Buffer\n return Buffer.from(file.content, 'utf8');\n }\n\n /**\n * Gets all files from the archive\n * @returns Map of file paths to file data\n */\n getAllFiles(): FileMap {\n this.ensureLoaded();\n return new Map(this.files);\n }\n\n /**\n * Gets a list of all file paths in the archive\n * @returns Array of file paths\n */\n getFilePaths(): string[] {\n this.ensureLoaded();\n return Array.from(this.files.keys());\n }\n\n /**\n * Checks if a file exists in the archive\n * @param filePath - Path to check\n * @returns True if the file exists\n */\n hasFile(filePath: string): boolean {\n this.ensureLoaded();\n const normalizedPath = normalizePath(filePath);\n return this.files.has(normalizedPath);\n }\n\n /**\n * Gets files matching a pattern (simple glob)\n * @param pattern - Pattern to match (supports * wildcard)\n * @returns Array of matching files\n */\n getFilesByPattern(pattern: string): ZipFile[] {\n this.ensureLoaded();\n\n // Convert simple glob pattern to regex\n const regexPattern = pattern.replace(/\\*/g, '.*').replace(/\\?/g, '.');\n const regex = new RegExp(`^${regexPattern}$`);\n\n const matchingFiles: ZipFile[] = [];\n for (const [path, file] of this.files) {\n if (regex.test(path)) {\n matchingFiles.push(file);\n }\n }\n\n return matchingFiles;\n }\n\n /**\n * Ensures the archive is loaded before operations\n * @throws {Error} If archive is not loaded\n */\n private ensureLoaded(): void {\n if (!this.loaded) {\n throw new Error('Archive not loaded. Call loadFromFile() or loadFromBuffer() first.');\n }\n }\n\n /**\n * Checks if the archive is loaded\n * @returns True if loaded\n */\n isLoaded(): boolean {\n return this.loaded;\n }\n\n /**\n * Clears all loaded data\n */\n clear(): void {\n this.zip = null;\n this.files.clear();\n this.loaded = false;\n }\n}\n"]}
1
+ {"version":3,"file":"ZipReader.js","sourceRoot":"","sources":["../../src/zip/ZipReader.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * ZipReader - Handles reading ZIP archives (DOCX files)\n */\n\nimport JSZip from 'jszip';\nimport { promises as fs } from 'fs';\nimport { ZipFile, FileMap, LoadOptions, SizeLimitOptions, DEFAULT_SIZE_LIMITS } from './types.js';\nimport {\n DocxNotFoundError,\n InvalidDocxError,\n CorruptedArchiveError,\n FileOperationError,\n ResourceLimitError,\n} from './errors.js';\nimport {\n validateDocxStructure,\n isBinaryFile,\n normalizePath,\n isValidZipBuffer,\n} from '../utils/validation.js';\n\n/**\n * Shape of JSZip's undocumented per-entry `_data` field, consulted only as an\n * early-reject hint — never as the authoritative size guard (measured decompressed\n * bytes are). Mirrors the commented-out `CompressedObject` interface in jszip's own\n * `node_modules/jszip/index.d.ts`.\n *\n * [TS-5] Verified against jszip 3.10.x (package.json range `^3.10.1`). A minor jszip\n * bump could rename or remove this field; if so the early-reject/ratio hints silently\n * degrade to no-ops while the measured-byte accounting in {@link extractFiles} keeps\n * enforcing the budgets. `tests/zip/JSZipInternalContract.test.ts` canaries the field\n * so such a regression surfaces on upgrade.\n */\ninterface JSZipObjectPrivate {\n _data?: {\n compressedSize?: number;\n uncompressedSize?: number;\n };\n}\n\n/**\n * Handles reading operations on ZIP archives\n */\nexport class ZipReader {\n private zip: JSZip | null = null;\n private files: FileMap = new Map();\n private loaded = false;\n\n /**\n * Loads a DOCX file from the filesystem\n * @param filePath - Path to the DOCX file\n * @param options - Load options\n */\n async loadFromFile(filePath: string, options: LoadOptions = {}): Promise<void> {\n try {\n // Check if file exists\n try {\n await fs.access(filePath);\n } catch {\n throw new DocxNotFoundError(filePath);\n }\n\n // Read file as buffer\n const buffer = await fs.readFile(filePath);\n await this.loadFromBuffer(buffer, options);\n } catch (error: unknown) {\n // Surface load-classification errors verbatim so callers can distinguish a\n // missing/oversized/hostile archive from a generic read failure.\n if (\n error instanceof DocxNotFoundError ||\n error instanceof ResourceLimitError ||\n error instanceof InvalidDocxError ||\n error instanceof CorruptedArchiveError\n ) {\n throw error;\n }\n const message = error instanceof Error ? error.message : String(error);\n throw new FileOperationError('read', message);\n }\n }\n\n /**\n * Loads a DOCX file from a buffer\n * @param buffer - Buffer containing the DOCX data\n * @param options - Load options\n */\n async loadFromBuffer(buffer: Buffer, options: LoadOptions = {}): Promise<void> {\n const { validate = true } = options;\n const limits: Required<SizeLimitOptions> = { ...DEFAULT_SIZE_LIMITS, ...options.sizeLimits };\n\n try {\n // Validate ZIP signature\n if (!isValidZipBuffer(buffer)) {\n throw new InvalidDocxError('File is not a valid ZIP archive');\n }\n\n // Load ZIP archive\n this.zip = await JSZip.loadAsync(buffer);\n\n // Enumerate non-directory entries once; reused for the count guard below and\n // for extraction, so the file list is not filtered twice.\n const filePaths = Object.keys(this.zip.files).filter((path) => !this.zip!.files[path]!.dir);\n\n // Reject entry-count amplification before decompressing anything.\n if (limits.maxEntryCount > 0 && filePaths.length > limits.maxEntryCount) {\n throw new ResourceLimitError(\n `archive entry count (${filePaths.length}) exceeds maxEntryCount (${limits.maxEntryCount})`\n );\n }\n\n // Extract all files (enforces uncompressed/ratio budgets while decompressing)\n await this.extractFiles(limits, filePaths);\n\n // Validate DOCX structure if requested\n if (validate) {\n this.validate();\n }\n\n this.loaded = true;\n } catch (error: unknown) {\n // A resource-limit breach must not be masked as a generic corruption error;\n // the cause (which budget was exceeded) is actionable for the caller.\n if (error instanceof InvalidDocxError || error instanceof ResourceLimitError) {\n throw error;\n }\n const message = error instanceof Error ? error.message : String(error);\n throw new CorruptedArchiveError(message);\n }\n }\n\n /**\n * Extracts all files from the ZIP archive into memory\n *\n * **Encoding Note:**\n * - Known-text parts (.xml, .rels, etc.) are extracted as UTF-8 strings using `async('string')`\n * - JSZip automatically decodes UTF-8 when extracting as 'string'\n * - Everything else is extracted as a Buffer to preserve exact bytes — a\n * string decode is lossy (invalid UTF-8 becomes U+FFFD), which would\n * corrupt embedded binary parts (OLE packages, fonts, metafiles)\n * - All text content is guaranteed to be valid UTF-8\n *\n * **Resource limits:** the running total of *actual* decompressed bytes is the\n * primary defense against high-ratio archives. JSZip's internal `_data` metadata\n * is consulted only as an early-reject hint (it is undocumented and may be absent),\n * never as the sole guard — every breach is re-checked against measured bytes.\n *\n * @param limits - Fully-resolved size limits to enforce while extracting\n * @param filePaths - Pre-filtered non-directory entry paths (computed by the caller)\n */\n private async extractFiles(\n limits: Required<SizeLimitOptions>,\n filePaths: string[]\n ): Promise<void> {\n if (!this.zip) {\n throw new Error('ZIP archive not loaded');\n }\n\n this.files.clear();\n\n const maxEntryBytes =\n limits.maxEntryUncompressedMB > 0 ? limits.maxEntryUncompressedMB * 1024 * 1024 : 0;\n const maxTotalBytes =\n limits.maxTotalUncompressedMB > 0 ? limits.maxTotalUncompressedMB * 1024 * 1024 : 0;\n // Only enforce the compression ratio on entries large enough for the ratio to be\n // meaningful, so a small but highly-compressible part (e.g. tiny repetitive XML)\n // cannot trip a false positive.\n const ratioFloorBytes = 1024 * 1024;\n\n let totalBytes = 0;\n\n // Extract each file\n for (const filePath of filePaths) {\n const normalizedPath = normalizePath(filePath);\n const zipObject = this.zip.files[filePath];\n\n if (!zipObject) {\n continue;\n }\n\n // Early-reject hint: JSZip exposes declared uncompressed/compressed sizes on a\n // private `_data` field. Cast once per entry (see {@link JSZipObjectPrivate}) and\n // use it only to avoid decompressing an obviously oversized entry; the\n // authoritative check below uses the measured byte count.\n const internalData = (zipObject as unknown as JSZipObjectPrivate)._data;\n const declaredSize = internalData?.uncompressedSize;\n if (typeof declaredSize === 'number' && declaredSize >= 0) {\n if (maxEntryBytes > 0 && declaredSize > maxEntryBytes) {\n throw new ResourceLimitError(\n `entry \"${normalizedPath}\" uncompressed size (${declaredSize} bytes) exceeds ` +\n `maxEntryUncompressedMB (${limits.maxEntryUncompressedMB}MB)`\n );\n }\n if (maxTotalBytes > 0 && totalBytes + declaredSize > maxTotalBytes) {\n throw new ResourceLimitError(\n `total uncompressed size would exceed maxTotalUncompressedMB ` +\n `(${limits.maxTotalUncompressedMB}MB)`\n );\n }\n }\n\n const isBinary = isBinaryFile(normalizedPath);\n\n // Extract content based on type\n // For known-text files: JSZip's async('string') automatically uses UTF-8 decoding\n // For everything else: async('nodebuffer') preserves exact bytes\n let content;\n if (isBinary) {\n content = await zipObject.async('nodebuffer');\n } else {\n // Known-text files are extracted as UTF-8 strings\n // JSZip automatically handles UTF-8 decoding for 'string' type\n content = await zipObject.async('string');\n }\n\n // Measure the *actual* decompressed byte count (UTF-8 for strings) — this is the\n // primary, authoritative resource accounting that does not trust archive metadata.\n const entryBytes = Buffer.isBuffer(content)\n ? content.length\n : Buffer.byteLength(content, 'utf8');\n\n if (maxEntryBytes > 0 && entryBytes > maxEntryBytes) {\n throw new ResourceLimitError(\n `entry \"${normalizedPath}\" uncompressed size (${entryBytes} bytes) exceeds ` +\n `maxEntryUncompressedMB (${limits.maxEntryUncompressedMB}MB)`\n );\n }\n\n totalBytes += entryBytes;\n if (maxTotalBytes > 0 && totalBytes > maxTotalBytes) {\n throw new ResourceLimitError(\n `total uncompressed size (${totalBytes} bytes) exceeds maxTotalUncompressedMB ` +\n `(${limits.maxTotalUncompressedMB}MB)`\n );\n }\n\n // Compression-ratio guard for sizable entries, when the archive reports a\n // compressed size we can divide by (reusing the single `_data` read above).\n const compressedSize = internalData?.compressedSize;\n if (\n limits.maxCompressionRatio > 0 &&\n entryBytes > ratioFloorBytes &&\n typeof compressedSize === 'number' &&\n compressedSize > 0\n ) {\n const ratio = entryBytes / compressedSize;\n if (ratio > limits.maxCompressionRatio) {\n throw new ResourceLimitError(\n `entry \"${normalizedPath}\" compression ratio (${ratio.toFixed(1)}:1) exceeds ` +\n `maxCompressionRatio (${limits.maxCompressionRatio}:1)`\n );\n }\n }\n\n // Get file metadata\n const date = zipObject.date;\n\n // Store file information\n this.files.set(normalizedPath, {\n path: normalizedPath,\n content,\n isBinary,\n size: isBinary ? (content as Buffer).length : (content as string).length,\n date,\n });\n }\n }\n\n /**\n * Validates the DOCX structure\n * @throws {MissingRequiredFileError} If required files are missing\n */\n private validate(): void {\n const filePaths = Array.from(this.files.keys());\n validateDocxStructure(filePaths);\n }\n\n /**\n * Gets a specific file from the archive\n * @param filePath - Path to the file within the archive\n * @returns The file data, or undefined if not found\n */\n getFile(filePath: string): ZipFile | undefined {\n this.ensureLoaded();\n const normalizedPath = normalizePath(filePath);\n return this.files.get(normalizedPath);\n }\n\n /**\n * Gets the content of a specific file as a string\n * @param filePath - Path to the file within the archive\n * @returns The file content as a UTF-8 string, or undefined if not found\n *\n * **Encoding Note:**\n * - Returns UTF-8 decoded string content\n * - For binary files, converts the Buffer to UTF-8 string\n * - Assumes all text content is UTF-8 encoded (per OpenXML standard)\n */\n getFileAsString(filePath: string): string | undefined {\n const file = this.getFile(filePath);\n if (!file) {\n return undefined;\n }\n\n // Check actual content type instead of flag (Issue #4)\n // Content is Buffer for binary files, string for text files\n if (Buffer.isBuffer(file.content)) {\n // Convert binary buffer to UTF-8 string\n return file.content.toString('utf8');\n }\n\n return file.content;\n }\n\n /**\n * Gets the content of a specific file as a buffer\n * @param filePath - Path to the file within the archive\n * @returns The file content as a Buffer, or undefined if not found\n *\n * **Encoding Note:**\n * - Returns Buffer with UTF-8 encoded content for text files\n * - For binary files, returns raw bytes\n * - String content is explicitly encoded as UTF-8\n */\n getFileAsBuffer(filePath: string): Buffer | undefined {\n const file = this.getFile(filePath);\n if (!file) {\n return undefined;\n }\n\n // Check actual content type instead of flag (Issue #4)\n // Content is Buffer for binary files, string for text files\n if (Buffer.isBuffer(file.content)) {\n return file.content;\n }\n\n // Encode string content as UTF-8 Buffer\n return Buffer.from(file.content, 'utf8');\n }\n\n /**\n * Gets all files from the archive\n * @returns Map of file paths to file data\n */\n getAllFiles(): FileMap {\n this.ensureLoaded();\n return new Map(this.files);\n }\n\n /**\n * Gets a list of all file paths in the archive\n * @returns Array of file paths\n */\n getFilePaths(): string[] {\n this.ensureLoaded();\n return Array.from(this.files.keys());\n }\n\n /**\n * Checks if a file exists in the archive\n * @param filePath - Path to check\n * @returns True if the file exists\n */\n hasFile(filePath: string): boolean {\n this.ensureLoaded();\n const normalizedPath = normalizePath(filePath);\n return this.files.has(normalizedPath);\n }\n\n /**\n * Gets files matching a pattern (simple glob)\n * @param pattern - Pattern to match (supports * wildcard)\n * @returns Array of matching files\n */\n getFilesByPattern(pattern: string): ZipFile[] {\n this.ensureLoaded();\n\n // Convert simple glob pattern to regex\n const regexPattern = pattern.replace(/\\*/g, '.*').replace(/\\?/g, '.');\n const regex = new RegExp(`^${regexPattern}$`);\n\n const matchingFiles: ZipFile[] = [];\n for (const [path, file] of this.files) {\n if (regex.test(path)) {\n matchingFiles.push(file);\n }\n }\n\n return matchingFiles;\n }\n\n /**\n * Ensures the archive is loaded before operations\n * @throws {Error} If archive is not loaded\n */\n private ensureLoaded(): void {\n if (!this.loaded) {\n throw new Error('Archive not loaded. Call loadFromFile() or loadFromBuffer() first.');\n }\n }\n\n /**\n * Checks if the archive is loaded\n * @returns True if loaded\n */\n isLoaded(): boolean {\n return this.loaded;\n }\n\n /**\n * Clears all loaded data\n */\n clear(): void {\n this.zip = null;\n this.files.clear();\n this.loaded = false;\n }\n}\n"]}
@@ -10,6 +10,9 @@ export declare class InvalidDocxError extends DocxError {
10
10
  export declare class CorruptedArchiveError extends DocxError {
11
11
  constructor(message: string);
12
12
  }
13
+ export declare class ResourceLimitError extends DocxError {
14
+ constructor(message: string);
15
+ }
13
16
  export declare class MissingRequiredFileError extends DocxError {
14
17
  constructor(fileName: string);
15
18
  }
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/zip/errors.ts"],"names":[],"mappings":"AAOA,qBAAa,SAAU,SAAQ,KAAK;gBACtB,OAAO,EAAE,MAAM;CAK5B;AAKD,qBAAa,iBAAkB,SAAQ,SAAS;gBAClC,QAAQ,EAAE,MAAM;CAK7B;AAKD,qBAAa,gBAAiB,SAAQ,SAAS;gBACjC,OAAO,EAAE,MAAM;CAK5B;AAKD,qBAAa,qBAAsB,SAAQ,SAAS;gBACtC,OAAO,EAAE,MAAM;CAK5B;AAKD,qBAAa,wBAAyB,SAAQ,SAAS;gBACzC,QAAQ,EAAE,MAAM;CAK7B;AAKD,qBAAa,kBAAmB,SAAQ,SAAS;gBACnC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAK/C"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/zip/errors.ts"],"names":[],"mappings":"AAOA,qBAAa,SAAU,SAAQ,KAAK;gBACtB,OAAO,EAAE,MAAM;CAK5B;AAKD,qBAAa,iBAAkB,SAAQ,SAAS;gBAClC,QAAQ,EAAE,MAAM;CAK7B;AAKD,qBAAa,gBAAiB,SAAQ,SAAS;gBACjC,OAAO,EAAE,MAAM;CAK5B;AAKD,qBAAa,qBAAsB,SAAQ,SAAS;gBACtC,OAAO,EAAE,MAAM;CAK5B;AASD,qBAAa,kBAAmB,SAAQ,SAAS;gBACnC,OAAO,EAAE,MAAM;CAK5B;AAKD,qBAAa,wBAAyB,SAAQ,SAAS;gBACzC,QAAQ,EAAE,MAAM;CAK7B;AAKD,qBAAa,kBAAmB,SAAQ,SAAS;gBACnC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAK/C"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FileOperationError = exports.MissingRequiredFileError = exports.CorruptedArchiveError = exports.InvalidDocxError = exports.DocxNotFoundError = exports.DocxError = void 0;
3
+ exports.FileOperationError = exports.MissingRequiredFileError = exports.ResourceLimitError = exports.CorruptedArchiveError = exports.InvalidDocxError = exports.DocxNotFoundError = exports.DocxError = void 0;
4
4
  class DocxError extends Error {
5
5
  constructor(message) {
6
6
  super(message);
@@ -33,6 +33,14 @@ class CorruptedArchiveError extends DocxError {
33
33
  }
34
34
  }
35
35
  exports.CorruptedArchiveError = CorruptedArchiveError;
36
+ class ResourceLimitError extends DocxError {
37
+ constructor(message) {
38
+ super(`Resource limit exceeded: ${message}`);
39
+ this.name = 'ResourceLimitError';
40
+ Object.setPrototypeOf(this, ResourceLimitError.prototype);
41
+ }
42
+ }
43
+ exports.ResourceLimitError = ResourceLimitError;
36
44
  class MissingRequiredFileError extends DocxError {
37
45
  constructor(fileName) {
38
46
  super(`Missing required file: ${fileName}`);
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/zip/errors.ts"],"names":[],"mappings":";;;AAOA,MAAa,SAAU,SAAQ,KAAK;IAClC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;QACxB,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACnD,CAAC;CACF;AAND,8BAMC;AAKD,MAAa,iBAAkB,SAAQ,SAAS;IAC9C,YAAY,QAAgB;QAC1B,KAAK,CAAC,wBAAwB,QAAQ,EAAE,CAAC,CAAC;QAC1C,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;QAChC,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC3D,CAAC;CACF;AAND,8CAMC;AAKD,MAAa,gBAAiB,SAAQ,SAAS;IAC7C,YAAY,OAAe;QACzB,KAAK,CAAC,sBAAsB,OAAO,EAAE,CAAC,CAAC;QACvC,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;QAC/B,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,gBAAgB,CAAC,SAAS,CAAC,CAAC;IAC1D,CAAC;CACF;AAND,4CAMC;AAKD,MAAa,qBAAsB,SAAQ,SAAS;IAClD,YAAY,OAAe;QACzB,KAAK,CAAC,sBAAsB,OAAO,EAAE,CAAC,CAAC;QACvC,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;QACpC,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,qBAAqB,CAAC,SAAS,CAAC,CAAC;IAC/D,CAAC;CACF;AAND,sDAMC;AAKD,MAAa,wBAAyB,SAAQ,SAAS;IACrD,YAAY,QAAgB;QAC1B,KAAK,CAAC,0BAA0B,QAAQ,EAAE,CAAC,CAAC;QAC5C,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;QACvC,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,wBAAwB,CAAC,SAAS,CAAC,CAAC;IAClE,CAAC;CACF;AAND,4DAMC;AAKD,MAAa,kBAAmB,SAAQ,SAAS;IAC/C,YAAY,SAAiB,EAAE,OAAe;QAC5C,KAAK,CAAC,mBAAmB,SAAS,aAAa,OAAO,EAAE,CAAC,CAAC;QAC1D,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;QACjC,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,kBAAkB,CAAC,SAAS,CAAC,CAAC;IAC5D,CAAC;CACF;AAND,gDAMC","sourcesContent":["/**\r\n * Custom error classes for ZIP and DOCX operations\r\n */\r\n\r\n/**\r\n * Base error class for all DOCX-related errors\r\n */\r\nexport class DocxError extends Error {\r\n constructor(message: string) {\r\n super(message);\r\n this.name = 'DocxError';\r\n Object.setPrototypeOf(this, DocxError.prototype);\r\n }\r\n}\r\n\r\n/**\r\n * Error thrown when a DOCX file is not found\r\n */\r\nexport class DocxNotFoundError extends DocxError {\r\n constructor(filePath: string) {\r\n super(`DOCX file not found: ${filePath}`);\r\n this.name = 'DocxNotFoundError';\r\n Object.setPrototypeOf(this, DocxNotFoundError.prototype);\r\n }\r\n}\r\n\r\n/**\r\n * Error thrown when a file is not a valid DOCX/ZIP archive\r\n */\r\nexport class InvalidDocxError extends DocxError {\r\n constructor(message: string) {\r\n super(`Invalid DOCX file: ${message}`);\r\n this.name = 'InvalidDocxError';\r\n Object.setPrototypeOf(this, InvalidDocxError.prototype);\r\n }\r\n}\r\n\r\n/**\r\n * Error thrown when a ZIP archive is corrupted\r\n */\r\nexport class CorruptedArchiveError extends DocxError {\r\n constructor(message: string) {\r\n super(`Corrupted archive: ${message}`);\r\n this.name = 'CorruptedArchiveError';\r\n Object.setPrototypeOf(this, CorruptedArchiveError.prototype);\r\n }\r\n}\r\n\r\n/**\r\n * Error thrown when a required DOCX file is missing\r\n */\r\nexport class MissingRequiredFileError extends DocxError {\r\n constructor(fileName: string) {\r\n super(`Missing required file: ${fileName}`);\r\n this.name = 'MissingRequiredFileError';\r\n Object.setPrototypeOf(this, MissingRequiredFileError.prototype);\r\n }\r\n}\r\n\r\n/**\r\n * Error thrown when a file operation fails\r\n */\r\nexport class FileOperationError extends DocxError {\r\n constructor(operation: string, message: string) {\r\n super(`File operation '${operation}' failed: ${message}`);\r\n this.name = 'FileOperationError';\r\n Object.setPrototypeOf(this, FileOperationError.prototype);\r\n }\r\n}\r\n"]}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/zip/errors.ts"],"names":[],"mappings":";;;AAOA,MAAa,SAAU,SAAQ,KAAK;IAClC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;QACxB,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACnD,CAAC;CACF;AAND,8BAMC;AAKD,MAAa,iBAAkB,SAAQ,SAAS;IAC9C,YAAY,QAAgB;QAC1B,KAAK,CAAC,wBAAwB,QAAQ,EAAE,CAAC,CAAC;QAC1C,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;QAChC,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC3D,CAAC;CACF;AAND,8CAMC;AAKD,MAAa,gBAAiB,SAAQ,SAAS;IAC7C,YAAY,OAAe;QACzB,KAAK,CAAC,sBAAsB,OAAO,EAAE,CAAC,CAAC;QACvC,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;QAC/B,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,gBAAgB,CAAC,SAAS,CAAC,CAAC;IAC1D,CAAC;CACF;AAND,4CAMC;AAKD,MAAa,qBAAsB,SAAQ,SAAS;IAClD,YAAY,OAAe;QACzB,KAAK,CAAC,sBAAsB,OAAO,EAAE,CAAC,CAAC;QACvC,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;QACpC,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,qBAAqB,CAAC,SAAS,CAAC,CAAC;IAC/D,CAAC;CACF;AAND,sDAMC;AASD,MAAa,kBAAmB,SAAQ,SAAS;IAC/C,YAAY,OAAe;QACzB,KAAK,CAAC,4BAA4B,OAAO,EAAE,CAAC,CAAC;QAC7C,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;QACjC,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,kBAAkB,CAAC,SAAS,CAAC,CAAC;IAC5D,CAAC;CACF;AAND,gDAMC;AAKD,MAAa,wBAAyB,SAAQ,SAAS;IACrD,YAAY,QAAgB;QAC1B,KAAK,CAAC,0BAA0B,QAAQ,EAAE,CAAC,CAAC;QAC5C,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;QACvC,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,wBAAwB,CAAC,SAAS,CAAC,CAAC;IAClE,CAAC;CACF;AAND,4DAMC;AAKD,MAAa,kBAAmB,SAAQ,SAAS;IAC/C,YAAY,SAAiB,EAAE,OAAe;QAC5C,KAAK,CAAC,mBAAmB,SAAS,aAAa,OAAO,EAAE,CAAC,CAAC;QAC1D,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;QACjC,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,kBAAkB,CAAC,SAAS,CAAC,CAAC;IAC5D,CAAC;CACF;AAND,gDAMC","sourcesContent":["/**\r\n * Custom error classes for ZIP and DOCX operations\r\n */\r\n\r\n/**\r\n * Base error class for all DOCX-related errors\r\n */\r\nexport class DocxError extends Error {\r\n constructor(message: string) {\r\n super(message);\r\n this.name = 'DocxError';\r\n Object.setPrototypeOf(this, DocxError.prototype);\r\n }\r\n}\r\n\r\n/**\r\n * Error thrown when a DOCX file is not found\r\n */\r\nexport class DocxNotFoundError extends DocxError {\r\n constructor(filePath: string) {\r\n super(`DOCX file not found: ${filePath}`);\r\n this.name = 'DocxNotFoundError';\r\n Object.setPrototypeOf(this, DocxNotFoundError.prototype);\r\n }\r\n}\r\n\r\n/**\r\n * Error thrown when a file is not a valid DOCX/ZIP archive\r\n */\r\nexport class InvalidDocxError extends DocxError {\r\n constructor(message: string) {\r\n super(`Invalid DOCX file: ${message}`);\r\n this.name = 'InvalidDocxError';\r\n Object.setPrototypeOf(this, InvalidDocxError.prototype);\r\n }\r\n}\r\n\r\n/**\r\n * Error thrown when a ZIP archive is corrupted\r\n */\r\nexport class CorruptedArchiveError extends DocxError {\r\n constructor(message: string) {\r\n super(`Corrupted archive: ${message}`);\r\n this.name = 'CorruptedArchiveError';\r\n Object.setPrototypeOf(this, CorruptedArchiveError.prototype);\r\n }\r\n}\r\n\r\n/**\r\n * Error thrown when an archive breaches a configured resource limit.\r\n *\r\n * Distinct from {@link CorruptedArchiveError} so callers can tell a hostile/oversized\r\n * input (a \"zip bomb\" style payload) apart from a genuinely malformed archive. The\r\n * message names the specific limit that was exceeded so it is actionable.\r\n */\r\nexport class ResourceLimitError extends DocxError {\r\n constructor(message: string) {\r\n super(`Resource limit exceeded: ${message}`);\r\n this.name = 'ResourceLimitError';\r\n Object.setPrototypeOf(this, ResourceLimitError.prototype);\r\n }\r\n}\r\n\r\n/**\r\n * Error thrown when a required DOCX file is missing\r\n */\r\nexport class MissingRequiredFileError extends DocxError {\r\n constructor(fileName: string) {\r\n super(`Missing required file: ${fileName}`);\r\n this.name = 'MissingRequiredFileError';\r\n Object.setPrototypeOf(this, MissingRequiredFileError.prototype);\r\n }\r\n}\r\n\r\n/**\r\n * Error thrown when a file operation fails\r\n */\r\nexport class FileOperationError extends DocxError {\r\n constructor(operation: string, message: string) {\r\n super(`File operation '${operation}' failed: ${message}`);\r\n this.name = 'FileOperationError';\r\n Object.setPrototypeOf(this, FileOperationError.prototype);\r\n }\r\n}\r\n"]}
@@ -8,6 +8,10 @@ export interface ZipFile {
8
8
  export interface SizeLimitOptions {
9
9
  warningSizeMB?: number;
10
10
  maxSizeMB?: number;
11
+ maxTotalUncompressedMB?: number;
12
+ maxEntryUncompressedMB?: number;
13
+ maxEntryCount?: number;
14
+ maxCompressionRatio?: number;
11
15
  }
12
16
  export declare const DEFAULT_SIZE_LIMITS: Required<SizeLimitOptions>;
13
17
  export interface LoadOptions {
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/zip/types.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,OAAO;IAEtB,IAAI,EAAE,MAAM,CAAC;IAEb,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;IAEzB,QAAQ,EAAE,OAAO,CAAC;IAElB,IAAI,EAAE,MAAM,CAAC;IAEb,IAAI,CAAC,EAAE,IAAI,CAAC;CACb;AAKD,MAAM,WAAW,gBAAgB;IAK/B,aAAa,CAAC,EAAE,MAAM,CAAC;IAKvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAKD,eAAO,MAAM,mBAAmB,EAAE,QAAQ,CAAC,gBAAgB,CAG1D,CAAC;AAKF,MAAM,WAAW,WAAW;IAE1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf,UAAU,CAAC,EAAE,gBAAgB,CAAC;CAC/B;AAKD,MAAM,WAAW,WAAW;IAE1B,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAKD,MAAM,WAAW,cAAc;IAE7B,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,IAAI,CAAC,EAAE,IAAI,CAAC;CACb;AAKD,MAAM,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAK3C,eAAO,MAAM,mBAAmB,sEAItB,CAAC;AAKX,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;CAoBb,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/zip/types.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,OAAO;IAEtB,IAAI,EAAE,MAAM,CAAC;IAEb,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;IAEzB,QAAQ,EAAE,OAAO,CAAC;IAElB,IAAI,EAAE,MAAM,CAAC;IAEb,IAAI,CAAC,EAAE,IAAI,CAAC;CACb;AAKD,MAAM,WAAW,gBAAgB;IAK/B,aAAa,CAAC,EAAE,MAAM,CAAC;IAKvB,SAAS,CAAC,EAAE,MAAM,CAAC;IAanB,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAKhC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAKhC,aAAa,CAAC,EAAE,MAAM,CAAC;IAYvB,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAQD,eAAO,MAAM,mBAAmB,EAAE,QAAQ,CAAC,gBAAgB,CAO1D,CAAC;AAKF,MAAM,WAAW,WAAW;IAE1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf,UAAU,CAAC,EAAE,gBAAgB,CAAC;CAC/B;AAKD,MAAM,WAAW,WAAW;IAE1B,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAKD,MAAM,WAAW,cAAc;IAE7B,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,IAAI,CAAC,EAAE,IAAI,CAAC;CACb;AAKD,MAAM,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAK3C,eAAO,MAAM,mBAAmB,sEAItB,CAAC;AAKX,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;CAoBb,CAAC"}
package/dist/zip/types.js CHANGED
@@ -4,6 +4,10 @@ exports.DOCX_PATHS = exports.REQUIRED_DOCX_FILES = exports.DEFAULT_SIZE_LIMITS =
4
4
  exports.DEFAULT_SIZE_LIMITS = {
5
5
  warningSizeMB: 50,
6
6
  maxSizeMB: 150,
7
+ maxTotalUncompressedMB: 300,
8
+ maxEntryUncompressedMB: 150,
9
+ maxEntryCount: 2000,
10
+ maxCompressionRatio: 200,
7
11
  };
8
12
  exports.REQUIRED_DOCX_FILES = [
9
13
  '[Content_Types].xml',
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/zip/types.ts"],"names":[],"mappings":";;;AAuCa,QAAA,mBAAmB,GAA+B;IAC7D,aAAa,EAAE,EAAE;IACjB,SAAS,EAAE,GAAG;CACf,CAAC;AA4CW,QAAA,mBAAmB,GAAG;IACjC,qBAAqB;IACrB,aAAa;IACb,mBAAmB;CACX,CAAC;AAKE,QAAA,UAAU,GAAG;IACxB,aAAa,EAAE,qBAAqB;IACpC,IAAI,EAAE,aAAa;IACnB,QAAQ,EAAE,mBAAmB;IAC7B,aAAa,EAAE,8BAA8B;IAC7C,MAAM,EAAE,iBAAiB;IACzB,SAAS,EAAE,oBAAoB;IAC/B,cAAc,EAAE,+BAA+B;IAC/C,QAAQ,EAAE,mBAAmB;IAC7B,UAAU,EAAE,oBAAoB;IAChC,KAAK,EAAE,uBAAuB;IAC9B,UAAU,EAAE,mBAAmB;IAC/B,SAAS,EAAE,kBAAkB;IAC7B,SAAS,EAAE,aAAa;IACxB,SAAS,EAAE,oBAAoB;IAC/B,QAAQ,EAAE,mBAAmB;IAC7B,iBAAiB,EAAE,2BAA2B;IAC9C,YAAY,EAAE,sBAAsB;IACpC,mBAAmB,EAAE,6BAA6B;IAClD,YAAY,EAAE,sBAAsB;CAC5B,CAAC","sourcesContent":["/**\n * Types and interfaces for ZIP archive handling\n */\n\n/**\n * Represents a file within a ZIP archive\n */\nexport interface ZipFile {\n /** The path of the file within the archive */\n path: string;\n /** The content of the file (string for text, Buffer for binary) */\n content: string | Buffer;\n /** Whether the content is binary */\n isBinary: boolean;\n /** File size in bytes */\n size: number;\n /** Last modified date */\n date?: Date;\n}\n\n/**\n * Size limit configuration for document loading\n */\nexport interface SizeLimitOptions {\n /**\n * Size in MB at which to show a warning (default: 50 MB)\n * Set to 0 to disable warnings\n */\n warningSizeMB?: number;\n /**\n * Maximum size in MB to load (default: 150 MB)\n * Set to 0 to disable the limit (not recommended)\n */\n maxSizeMB?: number;\n}\n\n/**\n * Default size limits for document loading\n */\nexport const DEFAULT_SIZE_LIMITS: Required<SizeLimitOptions> = {\n warningSizeMB: 50,\n maxSizeMB: 150,\n};\n\n/**\n * Options for loading a DOCX file\n */\nexport interface LoadOptions {\n /** Whether to validate the DOCX structure on load */\n validate?: boolean;\n /** Whether to load files lazily */\n lazy?: boolean;\n /** Size limit configuration (warning and max size thresholds) */\n sizeLimits?: SizeLimitOptions;\n}\n\n/**\n * Options for saving a DOCX file\n */\nexport interface SaveOptions {\n /** Compression level (0-9, where 0 is no compression and 9 is maximum) */\n compression?: number;\n /** Whether to validate the DOCX structure before save */\n validate?: boolean;\n}\n\n/**\n * Options for adding files to the archive\n */\nexport interface AddFileOptions {\n /** Whether the content is binary */\n binary?: boolean;\n /** Compression level for this specific file */\n compression?: number;\n /** Last modified date */\n date?: Date;\n}\n\n/**\n * Map of file paths to their contents\n */\nexport type FileMap = Map<string, ZipFile>;\n\n/**\n * Required files for a valid DOCX structure\n */\nexport const REQUIRED_DOCX_FILES = [\n '[Content_Types].xml',\n '_rels/.rels',\n 'word/document.xml',\n] as const;\n\n/**\n * Common DOCX file paths\n */\nexport const DOCX_PATHS = {\n CONTENT_TYPES: '[Content_Types].xml',\n RELS: '_rels/.rels',\n DOCUMENT: 'word/document.xml',\n DOCUMENT_RELS: 'word/_rels/document.xml.rels',\n STYLES: 'word/styles.xml',\n NUMBERING: 'word/numbering.xml',\n NUMBERING_RELS: 'word/_rels/numbering.xml.rels',\n SETTINGS: 'word/settings.xml',\n FONT_TABLE: 'word/fontTable.xml',\n THEME: 'word/theme/theme1.xml',\n CORE_PROPS: 'docProps/core.xml',\n APP_PROPS: 'docProps/app.xml',\n MEDIA_DIR: 'word/media/',\n FOOTNOTES: 'word/footnotes.xml',\n ENDNOTES: 'word/endnotes.xml',\n COMMENTS_EXTENDED: 'word/commentsExtended.xml',\n COMMENTS_IDS: 'word/commentsIds.xml',\n COMMENTS_EXTENSIBLE: 'word/commentsExtensible.xml',\n WEB_SETTINGS: 'word/webSettings.xml',\n} as const;\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/zip/types.ts"],"names":[],"mappings":";;;AA6Ea,QAAA,mBAAmB,GAA+B;IAC7D,aAAa,EAAE,EAAE;IACjB,SAAS,EAAE,GAAG;IACd,sBAAsB,EAAE,GAAG;IAC3B,sBAAsB,EAAE,GAAG;IAC3B,aAAa,EAAE,IAAI;IACnB,mBAAmB,EAAE,GAAG;CACzB,CAAC;AA4CW,QAAA,mBAAmB,GAAG;IACjC,qBAAqB;IACrB,aAAa;IACb,mBAAmB;CACX,CAAC;AAKE,QAAA,UAAU,GAAG;IACxB,aAAa,EAAE,qBAAqB;IACpC,IAAI,EAAE,aAAa;IACnB,QAAQ,EAAE,mBAAmB;IAC7B,aAAa,EAAE,8BAA8B;IAC7C,MAAM,EAAE,iBAAiB;IACzB,SAAS,EAAE,oBAAoB;IAC/B,cAAc,EAAE,+BAA+B;IAC/C,QAAQ,EAAE,mBAAmB;IAC7B,UAAU,EAAE,oBAAoB;IAChC,KAAK,EAAE,uBAAuB;IAC9B,UAAU,EAAE,mBAAmB;IAC/B,SAAS,EAAE,kBAAkB;IAC7B,SAAS,EAAE,aAAa;IACxB,SAAS,EAAE,oBAAoB;IAC/B,QAAQ,EAAE,mBAAmB;IAC7B,iBAAiB,EAAE,2BAA2B;IAC9C,YAAY,EAAE,sBAAsB;IACpC,mBAAmB,EAAE,6BAA6B;IAClD,YAAY,EAAE,sBAAsB;CAC5B,CAAC","sourcesContent":["/**\n * Types and interfaces for ZIP archive handling\n */\n\n/**\n * Represents a file within a ZIP archive\n */\nexport interface ZipFile {\n /** The path of the file within the archive */\n path: string;\n /** The content of the file (string for text, Buffer for binary) */\n content: string | Buffer;\n /** Whether the content is binary */\n isBinary: boolean;\n /** File size in bytes */\n size: number;\n /** Last modified date */\n date?: Date;\n}\n\n/**\n * Size limit configuration for document loading\n */\nexport interface SizeLimitOptions {\n /**\n * Size in MB at which to show a warning (default: 50 MB)\n * Set to 0 to disable warnings\n */\n warningSizeMB?: number;\n /**\n * Maximum *compressed* archive size in MB to load (default: 150 MB)\n * Set to 0 to disable the limit (not recommended)\n */\n maxSizeMB?: number;\n /**\n * Maximum total *uncompressed* size in MB across all archive entries\n * (default: 300 MB). This is the primary, authoritative defense against\n * high-ratio \"zip bomb\" payloads whose compressed size is small: it is\n * enforced against measured decompressed bytes, never archive-reported\n * metadata. Set to 0 to disable.\n *\n * Note: decompression is non-streaming (JSZip buffers each entry fully), so\n * peak transient heap can approach `maxTotalUncompressedMB + maxEntryUncompressedMB`\n * (the already-accumulated total plus the entry being decoded) before a limit\n * fires. Size the limits with that headroom in mind for memory-constrained hosts.\n */\n maxTotalUncompressedMB?: number;\n /**\n * Maximum *uncompressed* size in MB for any single archive entry\n * (default: 150 MB). Set to 0 to disable.\n */\n maxEntryUncompressedMB?: number;\n /**\n * Maximum number of entries (files) allowed in the archive\n * (default: 2000). Guards against entry-count amplification. Set to 0 to disable.\n */\n maxEntryCount?: number;\n /**\n * Maximum allowed per-entry compression ratio (uncompressed / compressed)\n * (default: 200). Only enforced for sizable entries where the ratio is\n * meaningful and the compressed size is reported by the archive.\n *\n * Best-effort: it relies on a compressed size that JSZip exposes only on an\n * undocumented internal field, so it is silently skipped for any entry where\n * that size is absent. `maxTotalUncompressedMB` is the authoritative guard\n * against high-ratio payloads; treat this ratio limit as a defense-in-depth\n * early reject, not a sole defense. Set to 0 to disable.\n */\n maxCompressionRatio?: number;\n}\n\n/**\n * Default size limits for document loading.\n *\n * Defaults are intentionally generous so legitimate large documents load\n * unchanged; tighten them via {@link LoadOptions.sizeLimits} for untrusted input.\n */\nexport const DEFAULT_SIZE_LIMITS: Required<SizeLimitOptions> = {\n warningSizeMB: 50,\n maxSizeMB: 150,\n maxTotalUncompressedMB: 300,\n maxEntryUncompressedMB: 150,\n maxEntryCount: 2000,\n maxCompressionRatio: 200,\n};\n\n/**\n * Options for loading a DOCX file\n */\nexport interface LoadOptions {\n /** Whether to validate the DOCX structure on load */\n validate?: boolean;\n /** Whether to load files lazily */\n lazy?: boolean;\n /** Size limit configuration (warning and max size thresholds) */\n sizeLimits?: SizeLimitOptions;\n}\n\n/**\n * Options for saving a DOCX file\n */\nexport interface SaveOptions {\n /** Compression level (0-9, where 0 is no compression and 9 is maximum) */\n compression?: number;\n /** Whether to validate the DOCX structure before save */\n validate?: boolean;\n}\n\n/**\n * Options for adding files to the archive\n */\nexport interface AddFileOptions {\n /** Whether the content is binary */\n binary?: boolean;\n /** Compression level for this specific file */\n compression?: number;\n /** Last modified date */\n date?: Date;\n}\n\n/**\n * Map of file paths to their contents\n */\nexport type FileMap = Map<string, ZipFile>;\n\n/**\n * Required files for a valid DOCX structure\n */\nexport const REQUIRED_DOCX_FILES = [\n '[Content_Types].xml',\n '_rels/.rels',\n 'word/document.xml',\n] as const;\n\n/**\n * Common DOCX file paths\n */\nexport const DOCX_PATHS = {\n CONTENT_TYPES: '[Content_Types].xml',\n RELS: '_rels/.rels',\n DOCUMENT: 'word/document.xml',\n DOCUMENT_RELS: 'word/_rels/document.xml.rels',\n STYLES: 'word/styles.xml',\n NUMBERING: 'word/numbering.xml',\n NUMBERING_RELS: 'word/_rels/numbering.xml.rels',\n SETTINGS: 'word/settings.xml',\n FONT_TABLE: 'word/fontTable.xml',\n THEME: 'word/theme/theme1.xml',\n CORE_PROPS: 'docProps/core.xml',\n APP_PROPS: 'docProps/app.xml',\n MEDIA_DIR: 'word/media/',\n FOOTNOTES: 'word/footnotes.xml',\n ENDNOTES: 'word/endnotes.xml',\n COMMENTS_EXTENDED: 'word/commentsExtended.xml',\n COMMENTS_IDS: 'word/commentsIds.xml',\n COMMENTS_EXTENSIBLE: 'word/commentsExtensible.xml',\n WEB_SETTINGS: 'word/webSettings.xml',\n} as const;\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "docxmlater",
3
- "version": "11.0.9",
3
+ "version": "12.0.1",
4
4
  "description": "TypeScript library for editing existing Word documents with full tracked-changes, comment, and bookmark fidelity. Round-trip safe DOCX modification.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -29,7 +29,7 @@
29
29
  "test:coverage": "jest --coverage",
30
30
  "dev": "ts-node src/index.ts",
31
31
  "clean": "rimraf dist",
32
- "prepublishOnly": "npm run clean && npm run build",
32
+ "prepublishOnly": "npm run clean && npm run build && npm run typecheck && npm test",
33
33
  "docs:metrics": "echo 'Source Files:' && find src -name '*.ts' | wc -l && echo 'Test Files:' && find tests -name '*.test.ts' | wc -l && echo 'Lines of Code:' && find src -name '*.ts' -exec wc -l {} + | tail -1",
34
34
  "docs:check": "echo 'Checking documentation...' && ls -1 README.md CHANGELOG.md 2>/dev/null || echo 'Missing documentation files'",
35
35
  "test-list-arch": "tsx examples/test-list-formatting-architecture.ts",