@opengeni/artifact-tool 0.2.2

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 (203) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +104 -0
  3. package/dist/bounded-zip.d.ts +28 -0
  4. package/dist/chunk-4BSENMAE.js +39 -0
  5. package/dist/chunk-4BSENMAE.js.map +1 -0
  6. package/dist/chunk-4FRFNNBM.js +276 -0
  7. package/dist/chunk-4FRFNNBM.js.map +1 -0
  8. package/dist/chunk-5XIA6KHZ.js +49 -0
  9. package/dist/chunk-5XIA6KHZ.js.map +1 -0
  10. package/dist/chunk-66CT2ZRP.js +2229 -0
  11. package/dist/chunk-66CT2ZRP.js.map +1 -0
  12. package/dist/chunk-72SYDU4O.js +319 -0
  13. package/dist/chunk-72SYDU4O.js.map +1 -0
  14. package/dist/chunk-B2ZJRZDA.js +279 -0
  15. package/dist/chunk-B2ZJRZDA.js.map +1 -0
  16. package/dist/chunk-DCTLTNPW.js +2458 -0
  17. package/dist/chunk-DCTLTNPW.js.map +1 -0
  18. package/dist/chunk-FGZM4FQ6.js +473 -0
  19. package/dist/chunk-FGZM4FQ6.js.map +1 -0
  20. package/dist/chunk-HTJBLBDE.js +591 -0
  21. package/dist/chunk-HTJBLBDE.js.map +1 -0
  22. package/dist/chunk-IYFXZQ34.js +5306 -0
  23. package/dist/chunk-IYFXZQ34.js.map +1 -0
  24. package/dist/chunk-J6N7IR44.js +172 -0
  25. package/dist/chunk-J6N7IR44.js.map +1 -0
  26. package/dist/chunk-N2IHNWBS.js +1002 -0
  27. package/dist/chunk-N2IHNWBS.js.map +1 -0
  28. package/dist/chunk-NAG44K67.js +549 -0
  29. package/dist/chunk-NAG44K67.js.map +1 -0
  30. package/dist/chunk-O24KTEGP.js +33 -0
  31. package/dist/chunk-O24KTEGP.js.map +1 -0
  32. package/dist/chunk-PI2AZVGK.js +2074 -0
  33. package/dist/chunk-PI2AZVGK.js.map +1 -0
  34. package/dist/chunk-Q2FFHEY7.js +40 -0
  35. package/dist/chunk-Q2FFHEY7.js.map +1 -0
  36. package/dist/chunk-RBGQADXA.js +57 -0
  37. package/dist/chunk-RBGQADXA.js.map +1 -0
  38. package/dist/chunk-U5G2BKM2.js +613 -0
  39. package/dist/chunk-U5G2BKM2.js.map +1 -0
  40. package/dist/chunk-VOJHMSM3.js +39 -0
  41. package/dist/chunk-VOJHMSM3.js.map +1 -0
  42. package/dist/chunk-VUNV25KB.js +16 -0
  43. package/dist/chunk-VUNV25KB.js.map +1 -0
  44. package/dist/chunk-XIFUNVBO.js +101 -0
  45. package/dist/chunk-XIFUNVBO.js.map +1 -0
  46. package/dist/chunk-ZCJYRJGB.js +489 -0
  47. package/dist/chunk-ZCJYRJGB.js.map +1 -0
  48. package/dist/chunk-ZZXPEE4S.js +2260 -0
  49. package/dist/chunk-ZZXPEE4S.js.map +1 -0
  50. package/dist/document-docx-api.d.ts +49 -0
  51. package/dist/document-docx-codec.d.ts +9 -0
  52. package/dist/document-docx-codec.js +4765 -0
  53. package/dist/document-docx-codec.js.map +1 -0
  54. package/dist/document-docx-import.d.ts +261 -0
  55. package/dist/document-docx-packer.d.ts +18 -0
  56. package/dist/document-docx-state.d.ts +12 -0
  57. package/dist/document-render.d.ts +3 -0
  58. package/dist/document-render.js +492 -0
  59. package/dist/document-render.js.map +1 -0
  60. package/dist/document.d.ts +432 -0
  61. package/dist/document.js +37 -0
  62. package/dist/document.js.map +1 -0
  63. package/dist/errors.d.ts +23 -0
  64. package/dist/file-blob.d.ts +11 -0
  65. package/dist/index.d.ts +4 -0
  66. package/dist/index.js +186 -0
  67. package/dist/index.js.map +1 -0
  68. package/dist/inspection.d.ts +12 -0
  69. package/dist/kernel.d.ts +101 -0
  70. package/dist/lib-GQLPACFK.js +9746 -0
  71. package/dist/lib-GQLPACFK.js.map +1 -0
  72. package/dist/materializer-cli-entry.d.ts +2 -0
  73. package/dist/materializer-cli-entry.js +29 -0
  74. package/dist/materializer-cli-entry.js.map +1 -0
  75. package/dist/materializer-cli.d.ts +24 -0
  76. package/dist/materializer-cli.js +24 -0
  77. package/dist/materializer-cli.js.map +1 -0
  78. package/dist/native-raster.d.ts +10 -0
  79. package/dist/native.d.ts +129 -0
  80. package/dist/native.js +31 -0
  81. package/dist/native.js.map +1 -0
  82. package/dist/office-import.d.ts +48 -0
  83. package/dist/office-import.js +154 -0
  84. package/dist/office-import.js.map +1 -0
  85. package/dist/presentation-pptx-api.d.ts +60 -0
  86. package/dist/presentation-pptx-import.d.ts +5 -0
  87. package/dist/presentation-pptx-state-digest-765CZZED.js +10 -0
  88. package/dist/presentation-pptx-state-digest-765CZZED.js.map +1 -0
  89. package/dist/presentation-pptx-state-digest.d.ts +3 -0
  90. package/dist/presentation-pptx-state.d.ts +4 -0
  91. package/dist/presentation-pptx-xml.d.ts +29 -0
  92. package/dist/presentation-pptx.d.ts +5 -0
  93. package/dist/presentation-pptx.js +2365 -0
  94. package/dist/presentation-pptx.js.map +1 -0
  95. package/dist/presentation-render.d.ts +29 -0
  96. package/dist/presentation-render.js +375 -0
  97. package/dist/presentation-render.js.map +1 -0
  98. package/dist/presentation.d.ts +565 -0
  99. package/dist/presentation.js +71 -0
  100. package/dist/presentation.js.map +1 -0
  101. package/dist/production-composite.d.ts +68 -0
  102. package/dist/production-document.d.ts +2 -0
  103. package/dist/production-document.js +176 -0
  104. package/dist/production-document.js.map +1 -0
  105. package/dist/production-native-codecs.d.ts +94 -0
  106. package/dist/production-presentation.d.ts +2 -0
  107. package/dist/production-presentation.js +176 -0
  108. package/dist/production-presentation.js.map +1 -0
  109. package/dist/production-sha256.d.ts +2 -0
  110. package/dist/production-spreadsheet-native.d.ts +12 -0
  111. package/dist/production-spreadsheet.d.ts +2 -0
  112. package/dist/production-spreadsheet.js +176 -0
  113. package/dist/production-spreadsheet.js.map +1 -0
  114. package/dist/production.d.ts +96 -0
  115. package/dist/raster-image.d.ts +34 -0
  116. package/dist/reference.d.ts +17 -0
  117. package/dist/reference.js +574 -0
  118. package/dist/reference.js.map +1 -0
  119. package/dist/runtime-cli-entry.d.ts +2 -0
  120. package/dist/runtime-cli-entry.js +21 -0
  121. package/dist/runtime-cli-entry.js.map +1 -0
  122. package/dist/runtime-cli.d.ts +76 -0
  123. package/dist/runtime-cli.js +26 -0
  124. package/dist/runtime-cli.js.map +1 -0
  125. package/dist/runtime-development.d.ts +73 -0
  126. package/dist/runtime-development.js +30 -0
  127. package/dist/runtime-development.js.map +1 -0
  128. package/dist/runtime-receipt.d.ts +19 -0
  129. package/dist/runtime.d.ts +355 -0
  130. package/dist/runtime.js +42 -0
  131. package/dist/runtime.js.map +1 -0
  132. package/dist/snapshot.d.ts +26 -0
  133. package/dist/snapshot.js +61 -0
  134. package/dist/snapshot.js.map +1 -0
  135. package/dist/spreadsheet-address.d.ts +18 -0
  136. package/dist/spreadsheet-file.d.ts +17 -0
  137. package/dist/spreadsheet-formula.d.ts +67 -0
  138. package/dist/spreadsheet-image.d.ts +17 -0
  139. package/dist/spreadsheet-render.d.ts +5 -0
  140. package/dist/spreadsheet-render.js +752 -0
  141. package/dist/spreadsheet-render.js.map +1 -0
  142. package/dist/spreadsheet-types.d.ts +155 -0
  143. package/dist/spreadsheet-xlsx-api.d.ts +88 -0
  144. package/dist/spreadsheet-xlsx-codec.d.ts +16 -0
  145. package/dist/spreadsheet-xlsx-codec.js +16 -0
  146. package/dist/spreadsheet-xlsx-codec.js.map +1 -0
  147. package/dist/spreadsheet-xlsx-state.d.ts +16 -0
  148. package/dist/spreadsheet.d.ts +724 -0
  149. package/dist/spreadsheet.js +67 -0
  150. package/dist/spreadsheet.js.map +1 -0
  151. package/package.json +129 -0
  152. package/src/bounded-zip.ts +450 -0
  153. package/src/document-docx-api.ts +64 -0
  154. package/src/document-docx-codec.ts +2253 -0
  155. package/src/document-docx-import.ts +3814 -0
  156. package/src/document-docx-packer.ts +72 -0
  157. package/src/document-docx-state.ts +59 -0
  158. package/src/document-render.ts +615 -0
  159. package/src/document.ts +2800 -0
  160. package/src/errors.ts +45 -0
  161. package/src/file-blob.ts +43 -0
  162. package/src/index.ts +4 -0
  163. package/src/inspection.ts +50 -0
  164. package/src/kernel.ts +525 -0
  165. package/src/materializer-cli-entry.ts +11 -0
  166. package/src/materializer-cli.ts +743 -0
  167. package/src/native-raster.ts +141 -0
  168. package/src/native.ts +390 -0
  169. package/src/office-import.ts +243 -0
  170. package/src/presentation-pptx-api.ts +107 -0
  171. package/src/presentation-pptx-import.ts +2065 -0
  172. package/src/presentation-pptx-state-digest.ts +46 -0
  173. package/src/presentation-pptx-state.ts +18 -0
  174. package/src/presentation-pptx-xml.ts +473 -0
  175. package/src/presentation-pptx.ts +447 -0
  176. package/src/presentation-render.ts +532 -0
  177. package/src/presentation.ts +2599 -0
  178. package/src/production-composite.ts +805 -0
  179. package/src/production-document.ts +2 -0
  180. package/src/production-native-codecs.ts +1141 -0
  181. package/src/production-presentation.ts +2 -0
  182. package/src/production-sha256.ts +150 -0
  183. package/src/production-spreadsheet-native.ts +686 -0
  184. package/src/production-spreadsheet.ts +2 -0
  185. package/src/production.ts +1022 -0
  186. package/src/raster-image.ts +586 -0
  187. package/src/reference.ts +17 -0
  188. package/src/runtime-cli-entry.ts +13 -0
  189. package/src/runtime-cli.ts +670 -0
  190. package/src/runtime-development.ts +689 -0
  191. package/src/runtime-receipt.ts +169 -0
  192. package/src/runtime.ts +1463 -0
  193. package/src/snapshot.ts +88 -0
  194. package/src/spreadsheet-address.ts +90 -0
  195. package/src/spreadsheet-file.ts +71 -0
  196. package/src/spreadsheet-formula.ts +1212 -0
  197. package/src/spreadsheet-image.ts +242 -0
  198. package/src/spreadsheet-render.ts +985 -0
  199. package/src/spreadsheet-types.ts +171 -0
  200. package/src/spreadsheet-xlsx-api.ts +123 -0
  201. package/src/spreadsheet-xlsx-codec.ts +3063 -0
  202. package/src/spreadsheet-xlsx-state.ts +191 -0
  203. package/src/spreadsheet.ts +5195 -0
@@ -0,0 +1,67 @@
1
+ import {
2
+ ChartCollection,
3
+ ChartSeriesCollection,
4
+ CommentThread,
5
+ ConditionalFormattingCollection,
6
+ DataValidationCollection,
7
+ FreezePanes,
8
+ ImageCollection,
9
+ Range,
10
+ RangeConditionalFormats,
11
+ RangeFormat,
12
+ RangeSparklines,
13
+ SPREADSHEET_RECALCULATION_LIMITS,
14
+ SPREADSHEET_SNAPSHOT_LIMITS,
15
+ ShapeCollection,
16
+ SparklineGroup,
17
+ SparklineGroupCollection,
18
+ SpreadsheetChart,
19
+ SpreadsheetChartSeries,
20
+ SpreadsheetImage,
21
+ SpreadsheetShape,
22
+ Table,
23
+ TableCollection,
24
+ Workbook,
25
+ WorkbookComments,
26
+ Worksheet,
27
+ WorksheetCollection,
28
+ validateSerializedWorkbook
29
+ } from "./chunk-IYFXZQ34.js";
30
+ import {
31
+ InvalidSpreadsheetImageError
32
+ } from "./chunk-4FRFNNBM.js";
33
+ import "./chunk-O24KTEGP.js";
34
+ import "./chunk-FGZM4FQ6.js";
35
+ import "./chunk-Q2FFHEY7.js";
36
+ import "./chunk-VUNV25KB.js";
37
+ export {
38
+ ChartCollection,
39
+ ChartSeriesCollection,
40
+ CommentThread,
41
+ ConditionalFormattingCollection,
42
+ DataValidationCollection,
43
+ FreezePanes,
44
+ ImageCollection,
45
+ InvalidSpreadsheetImageError,
46
+ Range,
47
+ RangeConditionalFormats,
48
+ RangeFormat,
49
+ RangeSparklines,
50
+ SPREADSHEET_RECALCULATION_LIMITS,
51
+ SPREADSHEET_SNAPSHOT_LIMITS,
52
+ ShapeCollection,
53
+ SparklineGroup,
54
+ SparklineGroupCollection,
55
+ SpreadsheetChart,
56
+ SpreadsheetChartSeries,
57
+ SpreadsheetImage,
58
+ SpreadsheetShape,
59
+ Table,
60
+ TableCollection,
61
+ Workbook,
62
+ WorkbookComments,
63
+ Worksheet,
64
+ WorksheetCollection,
65
+ validateSerializedWorkbook
66
+ };
67
+ //# sourceMappingURL=spreadsheet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/package.json ADDED
@@ -0,0 +1,129 @@
1
+ {
2
+ "name": "@opengeni/artifact-tool",
3
+ "version": "0.2.2",
4
+ "description": "OpenGeni-owned document, spreadsheet, and presentation engine with skill-compatible authoring APIs.",
5
+ "license": "Apache-2.0",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/Cloudgeni-ai/opengeni.git",
9
+ "directory": "packages/artifact-tool"
10
+ },
11
+ "bin": {
12
+ "opengeni-artifact-materializer": "./dist/materializer-cli-entry.js",
13
+ "opengeni-artifact-runtime": "./dist/runtime-cli-entry.js"
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "src"
18
+ ],
19
+ "type": "module",
20
+ "sideEffects": false,
21
+ "main": "./dist/index.js",
22
+ "module": "./dist/index.js",
23
+ "types": "./dist/index.d.ts",
24
+ "exports": {
25
+ ".": {
26
+ "types": "./dist/index.d.ts",
27
+ "import": "./dist/index.js"
28
+ },
29
+ "./reference": {
30
+ "types": "./dist/reference.d.ts",
31
+ "import": "./dist/reference.js"
32
+ },
33
+ "./native": {
34
+ "types": "./dist/native.d.ts",
35
+ "import": "./dist/native.js"
36
+ },
37
+ "./runtime": {
38
+ "types": "./dist/runtime.d.ts",
39
+ "import": "./dist/runtime.js"
40
+ },
41
+ "./runtime/locator": {
42
+ "types": "./dist/runtime-cli.d.ts",
43
+ "import": "./dist/runtime-cli.js"
44
+ },
45
+ "./runtime/development": {
46
+ "types": "./dist/runtime-development.d.ts",
47
+ "import": "./dist/runtime-development.js"
48
+ },
49
+ "./runtime/materializer": {
50
+ "types": "./dist/materializer-cli.d.ts",
51
+ "import": "./dist/materializer-cli.js"
52
+ },
53
+ "./office-import": {
54
+ "types": "./dist/office-import.d.ts",
55
+ "import": "./dist/office-import.js"
56
+ },
57
+ "./snapshot": {
58
+ "types": "./dist/snapshot.d.ts",
59
+ "import": "./dist/snapshot.js"
60
+ },
61
+ "./spreadsheet": {
62
+ "types": "./dist/production-spreadsheet.d.ts",
63
+ "import": "./dist/production-spreadsheet.js"
64
+ },
65
+ "./spreadsheet/render": {
66
+ "types": "./dist/spreadsheet-render.d.ts",
67
+ "import": "./dist/spreadsheet-render.js"
68
+ },
69
+ "./spreadsheet/xlsx": {
70
+ "types": "./dist/spreadsheet-xlsx-codec.d.ts",
71
+ "import": "./dist/spreadsheet-xlsx-codec.js"
72
+ },
73
+ "./document": {
74
+ "types": "./dist/production-document.d.ts",
75
+ "import": "./dist/production-document.js"
76
+ },
77
+ "./document/render": {
78
+ "types": "./dist/document-render.d.ts",
79
+ "import": "./dist/document-render.js"
80
+ },
81
+ "./document/docx": {
82
+ "types": "./dist/document-docx-codec.d.ts",
83
+ "import": "./dist/document-docx-codec.js"
84
+ },
85
+ "./presentation": {
86
+ "types": "./dist/production-presentation.d.ts",
87
+ "import": "./dist/production-presentation.js"
88
+ },
89
+ "./presentation/render": {
90
+ "types": "./dist/presentation-render.d.ts",
91
+ "import": "./dist/presentation-render.js"
92
+ },
93
+ "./presentation/pptx": {
94
+ "types": "./dist/presentation-pptx.d.ts",
95
+ "import": "./dist/presentation-pptx.js"
96
+ }
97
+ },
98
+ "publishConfig": {
99
+ "access": "public",
100
+ "provenance": true
101
+ },
102
+ "scripts": {
103
+ "build": "bun ../../scripts/build-typescript-package.ts",
104
+ "bench": "bun bench/run.ts",
105
+ "bench:deep": "bun bench/run.ts --deep",
106
+ "bench:release": "bun bench/release.ts",
107
+ "test:kernel": "cargo test --manifest-path kernel/Cargo.toml",
108
+ "test:kernel-packages": "bun ../../scripts/test-artifact-kernel-package-consumers.ts",
109
+ "test:production-native": "bun ../../scripts/run-artifact-native-tests.ts",
110
+ "bench:kernel": "cargo bench --manifest-path kernel/Cargo.toml --bench kernel",
111
+ "test:perf": "bun test --timeout 30000 test/perf",
112
+ "fuzz:check": "cargo check --manifest-path kernel/fuzz/Cargo.toml",
113
+ "fuzz:smoke": "bun kernel/fuzz/smoke.ts",
114
+ "typecheck": "tsc --noEmit",
115
+ "prepublishOnly": "bash ../../scripts/prepublish-guard"
116
+ },
117
+ "dependencies": {
118
+ "@opengeni/contracts": "^0.44.1",
119
+ "@resvg/resvg-js": "^2.6.2",
120
+ "docx": "9.7.1",
121
+ "exceljs": "^4.4.0",
122
+ "pptxgenjs": "^4.0.1",
123
+ "saxes": "5.0.1",
124
+ "sharp": "^0.34.5"
125
+ },
126
+ "engines": {
127
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
128
+ }
129
+ }
@@ -0,0 +1,450 @@
1
+ export type BoundedZipEntry = {
2
+ name: string;
3
+ flags: number;
4
+ compression: 0 | 8;
5
+ checksum: number;
6
+ compressedSize: number;
7
+ expandedSize: number;
8
+ localHeaderOffset: number;
9
+ dataStart: number;
10
+ dataEnd: number;
11
+ recordEnd: number;
12
+ directory: boolean;
13
+ };
14
+
15
+ export type BoundedZipLimits = {
16
+ entries: number;
17
+ compressedEntryBytes: number;
18
+ expandedEntryBytes: number;
19
+ expandedBytes: number;
20
+ compressionRatio: number;
21
+ compressionRatioThreshold?: number;
22
+ };
23
+
24
+ export type BoundedZipFailure = (
25
+ kind: "invalid" | "unsupported" | "encrypted" | "limit" | "platform",
26
+ message: string,
27
+ entryName?: string,
28
+ ) => never;
29
+
30
+ /** Parses ZIP metadata and validates every local record without inflating payloads. */
31
+ export function parseBoundedZip(
32
+ bytes: Uint8Array,
33
+ limits: BoundedZipLimits,
34
+ fail: BoundedZipFailure,
35
+ ): BoundedZipEntry[] {
36
+ if (bytes.byteLength < 22) fail("invalid", "ZIP end record is missing");
37
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
38
+ const end = findEndRecord(view, fail);
39
+ const disk = view.getUint16(end + 4, true);
40
+ const centralDisk = view.getUint16(end + 6, true);
41
+ const diskEntries = view.getUint16(end + 8, true);
42
+ const entryCount = view.getUint16(end + 10, true);
43
+ const centralSize = view.getUint32(end + 12, true);
44
+ const centralOffset = view.getUint32(end + 16, true);
45
+ if (disk !== 0 || centralDisk !== 0 || diskEntries !== entryCount) {
46
+ fail("unsupported", "Multi-disk ZIP archives are unsupported");
47
+ }
48
+ if (entryCount === 0xffff || centralSize === 0xffffffff || centralOffset === 0xffffffff) {
49
+ fail("unsupported", "ZIP64 archives are unsupported");
50
+ }
51
+ if (entryCount > limits.entries) fail("limit", "ZIP exceeds its entry limit");
52
+ if (centralOffset + centralSize !== end || centralOffset > bytes.byteLength) {
53
+ fail("invalid", "ZIP central-directory bounds are invalid");
54
+ }
55
+ if (end >= 20 && view.getUint32(end - 20, true) === 0x07064b50) {
56
+ fail("unsupported", "ZIP64 archives are unsupported");
57
+ }
58
+
59
+ const decoder = new TextDecoder("utf-8", { fatal: true });
60
+ const names = new Set<string>();
61
+ const entries: BoundedZipEntry[] = [];
62
+ let offset = centralOffset;
63
+ let expandedBytes = 0;
64
+ let compressedBytes = 0;
65
+ for (let index = 0; index < entryCount; index += 1) {
66
+ ensureRange(bytes, offset, 46, fail, "ZIP central-directory entry");
67
+ if (view.getUint32(offset, true) !== 0x02014b50) {
68
+ fail("invalid", "ZIP central-directory signature is invalid");
69
+ }
70
+ const flags = view.getUint16(offset + 8, true);
71
+ const compression = view.getUint16(offset + 10, true);
72
+ const checksum = view.getUint32(offset + 16, true);
73
+ const compressedSize = view.getUint32(offset + 20, true);
74
+ const expandedSize = view.getUint32(offset + 24, true);
75
+ const nameLength = view.getUint16(offset + 28, true);
76
+ const extraLength = view.getUint16(offset + 30, true);
77
+ const commentLength = view.getUint16(offset + 32, true);
78
+ const startDisk = view.getUint16(offset + 34, true);
79
+ const localHeaderOffset = view.getUint32(offset + 42, true);
80
+ const recordLength = 46 + nameLength + extraLength + commentLength;
81
+ ensureRange(bytes, offset, recordLength, fail, "ZIP central-directory entry");
82
+ if (offset + recordLength > centralOffset + centralSize) {
83
+ fail("invalid", "ZIP central-directory entry is out of bounds");
84
+ }
85
+ if (startDisk !== 0) fail("unsupported", "Multi-disk ZIP entries are unsupported");
86
+ if ((flags & 0x0041) !== 0) fail("encrypted", "Encrypted ZIP entries are unsupported");
87
+ if ((flags & ~0x080e) !== 0 || (compression === 0 && (flags & 0x0006) !== 0)) {
88
+ fail("unsupported", "ZIP entry flags are unsupported");
89
+ }
90
+ if (compression !== 0 && compression !== 8) {
91
+ fail("unsupported", `ZIP compression method ${compression} is unsupported`);
92
+ }
93
+ if (
94
+ compressedSize === 0xffffffff ||
95
+ expandedSize === 0xffffffff ||
96
+ localHeaderOffset === 0xffffffff
97
+ ) {
98
+ fail("unsupported", "ZIP64 entries are unsupported");
99
+ }
100
+ rejectZip64Extra(
101
+ bytes.subarray(offset + 46 + nameLength, offset + 46 + nameLength + extraLength),
102
+ fail,
103
+ );
104
+ let name: string;
105
+ try {
106
+ name = decoder.decode(bytes.subarray(offset + 46, offset + 46 + nameLength));
107
+ } catch {
108
+ fail("invalid", "ZIP entry name is not valid UTF-8");
109
+ }
110
+ validatePath(name, fail);
111
+ const collisionKey = name.normalize("NFC").toLowerCase();
112
+ if (names.has(collisionKey))
113
+ fail("invalid", "ZIP contains duplicate normalized entry names", name);
114
+ names.add(collisionKey);
115
+ if (compressedSize > limits.compressedEntryBytes)
116
+ fail("limit", "ZIP entry exceeds its compressed-size limit", name);
117
+ if (expandedSize > limits.expandedEntryBytes)
118
+ fail("limit", "ZIP entry exceeds its expanded-size limit", name);
119
+ if (compressedSize === 0 && expandedSize !== 0)
120
+ fail("invalid", "ZIP entry has impossible sizes", name);
121
+ if (
122
+ expandedSize > (limits.compressionRatioThreshold ?? 0) &&
123
+ expandedSize / Math.max(1, compressedSize) > limits.compressionRatio
124
+ )
125
+ fail("limit", "ZIP entry exceeds its compression-ratio limit", name);
126
+ expandedBytes += expandedSize;
127
+ compressedBytes += compressedSize;
128
+ if (expandedBytes > limits.expandedBytes) fail("limit", "ZIP exceeds its expanded-size limit");
129
+ if (
130
+ expandedBytes > (limits.compressionRatioThreshold ?? 0) &&
131
+ expandedBytes / Math.max(1, compressedBytes) > limits.compressionRatio
132
+ ) {
133
+ fail("limit", "ZIP exceeds its aggregate compression-ratio limit", name);
134
+ }
135
+
136
+ const entry = localBounds(
137
+ bytes,
138
+ view,
139
+ decoder,
140
+ {
141
+ name,
142
+ flags,
143
+ compression,
144
+ checksum,
145
+ compressedSize,
146
+ expandedSize,
147
+ localHeaderOffset,
148
+ directory: name.endsWith("/"),
149
+ },
150
+ centralOffset,
151
+ fail,
152
+ );
153
+ entries.push(entry);
154
+ offset += recordLength;
155
+ }
156
+ if (offset !== centralOffset + centralSize)
157
+ fail("invalid", "ZIP central-directory size is inconsistent");
158
+ const localOrder = [...entries].sort(
159
+ (left, right) => left.localHeaderOffset - right.localHeaderOffset,
160
+ );
161
+ for (let index = 1; index < localOrder.length; index += 1) {
162
+ const previous = localOrder[index - 1]!;
163
+ const current = localOrder[index]!;
164
+ if (previous.recordEnd > current.localHeaderOffset) {
165
+ fail("invalid", `ZIP local entries overlap: ${previous.name}`, current.name);
166
+ }
167
+ }
168
+ return entries;
169
+ }
170
+
171
+ async function consumeZipEntry(
172
+ archive: Uint8Array,
173
+ entry: BoundedZipEntry,
174
+ maximumBytes: number,
175
+ fail: BoundedZipFailure,
176
+ collect: boolean,
177
+ ): Promise<Uint8Array | undefined> {
178
+ if (entry.expandedSize > maximumBytes) {
179
+ fail("limit", "ZIP entry exceeds its configured expanded-size limit", entry.name);
180
+ }
181
+ const compressed = archive.subarray(entry.dataStart, entry.dataEnd);
182
+ if (entry.compression === 0) {
183
+ if (compressed.byteLength !== entry.expandedSize || zipCrc32(compressed) !== entry.checksum) {
184
+ fail("invalid", "ZIP entry failed size or checksum validation", entry.name);
185
+ }
186
+ return collect ? compressed.slice() : undefined;
187
+ }
188
+ if (typeof DecompressionStream === "undefined") {
189
+ return inflateWithNode(compressed, entry, fail, collect);
190
+ }
191
+ let stream: ReadableStream<Uint8Array>;
192
+ try {
193
+ stream = new Blob([Uint8Array.from(compressed)])
194
+ .stream()
195
+ .pipeThrough(new DecompressionStream("deflate-raw"));
196
+ } catch {
197
+ return inflateWithNode(compressed, entry, fail, collect);
198
+ }
199
+ const output = collect ? new Uint8Array(entry.expandedSize) : undefined;
200
+ const reader = stream.getReader();
201
+ let offset = 0;
202
+ let checksum = 0xffffffff;
203
+ try {
204
+ while (true) {
205
+ const result = await reader
206
+ .read()
207
+ .catch(() => fail("invalid", "DEFLATE stream is invalid", entry.name));
208
+ if (result.done) break;
209
+ if (offset + result.value.byteLength > entry.expandedSize) {
210
+ await reader.cancel().catch(() => undefined);
211
+ fail("limit", "Inflated ZIP entry exceeds its declared or configured size", entry.name);
212
+ }
213
+ output?.set(result.value, offset);
214
+ offset += result.value.byteLength;
215
+ checksum = zipCrc32Update(checksum, result.value);
216
+ }
217
+ } finally {
218
+ reader.releaseLock();
219
+ }
220
+ if (offset !== entry.expandedSize || (checksum ^ 0xffffffff) >>> 0 !== entry.checksum) {
221
+ fail("invalid", "ZIP entry failed size or checksum validation", entry.name);
222
+ }
223
+ return output;
224
+ }
225
+
226
+ async function inflateWithNode(
227
+ compressed: Uint8Array,
228
+ entry: BoundedZipEntry,
229
+ fail: BoundedZipFailure,
230
+ collect: boolean,
231
+ ): Promise<Uint8Array | undefined> {
232
+ if (typeof process === "undefined" || !process.versions?.node) {
233
+ fail("platform", "Raw DEFLATE is unavailable", entry.name);
234
+ }
235
+ try {
236
+ const { inflateRawSync } = await loadNodeZlib();
237
+ const output = inflateRawSync(compressed, { maxOutputLength: entry.expandedSize });
238
+ if (output.byteLength !== entry.expandedSize || zipCrc32(output) !== entry.checksum) {
239
+ fail("invalid", "ZIP entry failed size or checksum validation", entry.name);
240
+ }
241
+ return collect ? Uint8Array.from(output) : undefined;
242
+ } catch (error) {
243
+ if ((error as { code?: string }).code === "ERR_BUFFER_TOO_LARGE") {
244
+ fail("limit", "Inflated ZIP entry exceeds its declared or configured size", entry.name);
245
+ }
246
+ fail("invalid", "DEFLATE stream is invalid", entry.name);
247
+ }
248
+ }
249
+
250
+ async function loadNodeZlib(): Promise<typeof import("node:zlib")> {
251
+ // Literal dynamic imports are still resolved and polyfilled by browser
252
+ // bundlers. Keep the fallback available to Node/Bun without granting the
253
+ // browser codec graph an eager Node compatibility closure.
254
+ return importRuntimeModule<typeof import("node:zlib")>("node:zlib");
255
+ }
256
+
257
+ function importRuntimeModule<T>(specifier: string): Promise<T> {
258
+ return import(/* @vite-ignore */ specifier) as Promise<T>;
259
+ }
260
+
261
+ export async function inflateBoundedZipEntry(
262
+ archive: Uint8Array,
263
+ entry: BoundedZipEntry,
264
+ maximumBytes: number,
265
+ fail: BoundedZipFailure,
266
+ ): Promise<Uint8Array> {
267
+ return (await consumeZipEntry(archive, entry, maximumBytes, fail, true))!;
268
+ }
269
+
270
+ export async function verifyBoundedZipEntry(
271
+ archive: Uint8Array,
272
+ entry: BoundedZipEntry,
273
+ maximumBytes: number,
274
+ fail: BoundedZipFailure,
275
+ ): Promise<void> {
276
+ await consumeZipEntry(archive, entry, maximumBytes, fail, false);
277
+ }
278
+
279
+ function findEndRecord(view: DataView, fail: BoundedZipFailure): number {
280
+ const minimum = Math.max(0, view.byteLength - 65_557);
281
+ for (let offset = view.byteLength - 22; offset >= minimum; offset -= 1) {
282
+ if (
283
+ view.getUint32(offset, true) === 0x06054b50 &&
284
+ offset + 22 + view.getUint16(offset + 20, true) === view.byteLength
285
+ )
286
+ return offset;
287
+ }
288
+ return fail("invalid", "ZIP end record is missing");
289
+ }
290
+
291
+ function localBounds(
292
+ bytes: Uint8Array,
293
+ view: DataView,
294
+ decoder: TextDecoder,
295
+ entry: Omit<BoundedZipEntry, "dataStart" | "dataEnd" | "recordEnd">,
296
+ centralOffset: number,
297
+ fail: BoundedZipFailure,
298
+ ): BoundedZipEntry {
299
+ const header = entry.localHeaderOffset;
300
+ ensureRange(bytes, header, 30, fail, "ZIP local header", entry.name);
301
+ if (header + 30 > centralOffset || view.getUint32(header, true) !== 0x04034b50) {
302
+ fail("invalid", "ZIP local-file header is invalid", entry.name);
303
+ }
304
+ const flags = view.getUint16(header + 6, true);
305
+ const compression = view.getUint16(header + 8, true);
306
+ if (flags !== entry.flags || compression !== entry.compression) {
307
+ fail("invalid", "ZIP local and central entry metadata disagree", entry.name);
308
+ }
309
+ const checksum = view.getUint32(header + 14, true);
310
+ const compressedSize = view.getUint32(header + 18, true);
311
+ const expandedSize = view.getUint32(header + 22, true);
312
+ const descriptor = (entry.flags & 0x0008) !== 0;
313
+ if (
314
+ (!descriptor &&
315
+ (checksum !== entry.checksum ||
316
+ compressedSize !== entry.compressedSize ||
317
+ expandedSize !== entry.expandedSize)) ||
318
+ (descriptor &&
319
+ ((checksum !== 0 && checksum !== entry.checksum) ||
320
+ (compressedSize !== 0 && compressedSize !== entry.compressedSize) ||
321
+ (expandedSize !== 0 && expandedSize !== entry.expandedSize)))
322
+ )
323
+ fail("invalid", "ZIP local and central sizes or checksum disagree", entry.name);
324
+ const nameLength = view.getUint16(header + 26, true);
325
+ const extraLength = view.getUint16(header + 28, true);
326
+ const nameStart = header + 30;
327
+ const nameEnd = nameStart + nameLength;
328
+ ensureRange(
329
+ bytes,
330
+ header,
331
+ 30 + nameLength + extraLength + entry.compressedSize,
332
+ fail,
333
+ "ZIP local entry",
334
+ entry.name,
335
+ );
336
+ let localName: string;
337
+ try {
338
+ localName = decoder.decode(bytes.subarray(nameStart, nameEnd));
339
+ } catch {
340
+ fail("invalid", "ZIP local entry name is not valid UTF-8", entry.name);
341
+ }
342
+ if (localName !== entry.name)
343
+ fail("invalid", "ZIP local and central entry names disagree", entry.name);
344
+ const dataStart = nameEnd + extraLength;
345
+ const payloadEnd = dataStart + entry.compressedSize;
346
+ rejectZip64Extra(bytes.subarray(nameEnd, dataStart), fail, entry.name);
347
+ let recordEnd = payloadEnd;
348
+ if (descriptor) recordEnd = validateDescriptor(bytes, view, recordEnd, entry, fail);
349
+ if (dataStart < nameEnd || recordEnd > centralOffset) {
350
+ fail("invalid", "ZIP entry data is outside the local-file area", entry.name);
351
+ }
352
+ if (entry.directory && (entry.compressedSize !== 0 || entry.expandedSize !== 0)) {
353
+ fail("invalid", "ZIP directory entry contains data", entry.name);
354
+ }
355
+ return { ...entry, dataStart, dataEnd: payloadEnd, recordEnd };
356
+ }
357
+
358
+ function validateDescriptor(
359
+ bytes: Uint8Array,
360
+ view: DataView,
361
+ offset: number,
362
+ entry: Omit<BoundedZipEntry, "dataStart" | "dataEnd" | "recordEnd">,
363
+ fail: BoundedZipFailure,
364
+ ): number {
365
+ ensureRange(bytes, offset, 12, fail, "ZIP data descriptor", entry.name);
366
+ const matches = (start: number): boolean =>
367
+ start + 12 <= bytes.byteLength &&
368
+ view.getUint32(start, true) === entry.checksum &&
369
+ view.getUint32(start + 4, true) === entry.compressedSize &&
370
+ view.getUint32(start + 8, true) === entry.expandedSize;
371
+ if (matches(offset)) return offset + 12;
372
+ if (view.getUint32(offset, true) === 0x08074b50 && matches(offset + 4)) return offset + 16;
373
+ return fail("invalid", "ZIP data descriptor disagrees with central directory", entry.name);
374
+ }
375
+
376
+ function rejectZip64Extra(extra: Uint8Array, fail: BoundedZipFailure, entryName?: string): void {
377
+ const view = new DataView(extra.buffer, extra.byteOffset, extra.byteLength);
378
+ let offset = 0;
379
+ while (offset < extra.byteLength) {
380
+ if (offset + 4 > extra.byteLength) fail("invalid", "ZIP extra field is malformed", entryName);
381
+ const id = view.getUint16(offset, true);
382
+ const size = view.getUint16(offset + 2, true);
383
+ if (offset + 4 + size > extra.byteLength)
384
+ fail("invalid", "ZIP extra field is malformed", entryName);
385
+ if (id === 0x0001) fail("unsupported", "ZIP64 entries are unsupported", entryName);
386
+ offset += 4 + size;
387
+ }
388
+ }
389
+
390
+ function validatePath(name: string, fail: BoundedZipFailure): void {
391
+ if (
392
+ name.length === 0 ||
393
+ name.length > 1_024 ||
394
+ /[\u0000-\u001f\u007f]/.test(name) ||
395
+ name.includes("\\") ||
396
+ name.startsWith("/") ||
397
+ /^[A-Za-z]:/.test(name) ||
398
+ /%(?:2e|2f|5c)/i.test(name)
399
+ )
400
+ fail("invalid", "ZIP entry path is unsafe", name);
401
+ const segments = name.split("/");
402
+ if (
403
+ segments.some(
404
+ (segment, index) =>
405
+ segment === "." || segment === ".." || (segment === "" && index !== segments.length - 1),
406
+ )
407
+ ) {
408
+ fail("invalid", "ZIP entry path is unsafe", name);
409
+ }
410
+ }
411
+
412
+ function ensureRange(
413
+ bytes: Uint8Array,
414
+ offset: number,
415
+ length: number,
416
+ fail: BoundedZipFailure,
417
+ description: string,
418
+ entryName?: string,
419
+ ): void {
420
+ if (
421
+ !Number.isSafeInteger(offset) ||
422
+ !Number.isSafeInteger(length) ||
423
+ offset < 0 ||
424
+ length < 0 ||
425
+ offset + length > bytes.byteLength
426
+ ) {
427
+ fail("invalid", `${description} is out of bounds`, entryName);
428
+ }
429
+ }
430
+
431
+ let crcTable: Uint32Array | undefined;
432
+
433
+ export function zipCrc32Update(initial: number, bytes: Uint8Array): number {
434
+ if (!crcTable) {
435
+ crcTable = new Uint32Array(256);
436
+ for (let index = 0; index < 256; index += 1) {
437
+ let value = index;
438
+ for (let bit = 0; bit < 8; bit += 1)
439
+ value = (value & 1) !== 0 ? 0xedb88320 ^ (value >>> 1) : value >>> 1;
440
+ crcTable[index] = value >>> 0;
441
+ }
442
+ }
443
+ let value = initial;
444
+ for (const byte of bytes) value = (crcTable[(value ^ byte) & 0xff] ?? 0) ^ (value >>> 8);
445
+ return value >>> 0;
446
+ }
447
+
448
+ export function zipCrc32(bytes: Uint8Array): number {
449
+ return (zipCrc32Update(0xffffffff, bytes) ^ 0xffffffff) >>> 0;
450
+ }