@loaders.gl/zip 4.1.0-alpha.1 → 4.1.0-alpha.11

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 (68) hide show
  1. package/dist/dist.dev.js +875 -64
  2. package/dist/filesystems/zip-filesystem.d.ts.map +1 -1
  3. package/dist/filesystems/zip-filesystem.js.map +1 -1
  4. package/dist/hash-file-utility.d.ts +19 -0
  5. package/dist/hash-file-utility.d.ts.map +1 -1
  6. package/dist/hash-file-utility.js +30 -0
  7. package/dist/hash-file-utility.js.map +1 -1
  8. package/dist/index.cjs +542 -56
  9. package/dist/index.d.ts +3 -2
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +3 -2
  12. package/dist/index.js.map +1 -1
  13. package/dist/lib/tar/header.d.ts.map +1 -1
  14. package/dist/lib/tar/header.js.map +1 -1
  15. package/dist/lib/tar/tar.d.ts.map +1 -1
  16. package/dist/lib/tar/tar.js.map +1 -1
  17. package/dist/lib/tar/types.d.ts.map +1 -1
  18. package/dist/lib/tar/types.js.map +1 -1
  19. package/dist/lib/tar/utils.d.ts.map +1 -1
  20. package/dist/lib/tar/utils.js.map +1 -1
  21. package/dist/parse-zip/cd-file-header.d.ts +1 -1
  22. package/dist/parse-zip/cd-file-header.d.ts.map +1 -1
  23. package/dist/parse-zip/cd-file-header.js +4 -4
  24. package/dist/parse-zip/cd-file-header.js.map +1 -1
  25. package/dist/parse-zip/end-of-central-directory.d.ts +35 -0
  26. package/dist/parse-zip/end-of-central-directory.d.ts.map +1 -1
  27. package/dist/parse-zip/end-of-central-directory.js +161 -9
  28. package/dist/parse-zip/end-of-central-directory.js.map +1 -1
  29. package/dist/parse-zip/local-file-header.d.ts +16 -0
  30. package/dist/parse-zip/local-file-header.d.ts.map +1 -1
  31. package/dist/parse-zip/local-file-header.js +73 -1
  32. package/dist/parse-zip/local-file-header.js.map +1 -1
  33. package/dist/parse-zip/search-from-the-end.d.ts.map +1 -1
  34. package/dist/parse-zip/search-from-the-end.js.map +1 -1
  35. package/dist/parse-zip/zip-composition.d.ts +38 -0
  36. package/dist/parse-zip/zip-composition.d.ts.map +1 -0
  37. package/dist/parse-zip/zip-composition.js +115 -0
  38. package/dist/parse-zip/zip-composition.js.map +1 -0
  39. package/dist/parse-zip/zip64-info-generation.d.ts +5 -8
  40. package/dist/parse-zip/zip64-info-generation.d.ts.map +1 -1
  41. package/dist/parse-zip/zip64-info-generation.js +6 -3
  42. package/dist/parse-zip/zip64-info-generation.js.map +1 -1
  43. package/dist/tar-builder.d.ts.map +1 -1
  44. package/dist/tar-builder.js.map +1 -1
  45. package/dist/zip-loader.d.ts.map +1 -1
  46. package/dist/zip-loader.js +1 -1
  47. package/dist/zip-loader.js.map +1 -1
  48. package/dist/zip-writer.d.ts +2 -2
  49. package/dist/zip-writer.d.ts.map +1 -1
  50. package/dist/zip-writer.js +22 -7
  51. package/dist/zip-writer.js.map +1 -1
  52. package/package.json +7 -7
  53. package/src/filesystems/zip-filesystem.ts +2 -1
  54. package/src/hash-file-utility.ts +84 -3
  55. package/src/index.ts +6 -3
  56. package/src/lib/tar/header.ts +2 -1
  57. package/src/lib/tar/tar.ts +2 -1
  58. package/src/lib/tar/types.ts +2 -1
  59. package/src/lib/tar/utils.ts +2 -1
  60. package/src/parse-zip/cd-file-header.ts +8 -6
  61. package/src/parse-zip/end-of-central-directory.ts +338 -10
  62. package/src/parse-zip/local-file-header.ts +128 -2
  63. package/src/parse-zip/search-from-the-end.ts +2 -1
  64. package/src/parse-zip/zip-composition.ts +235 -0
  65. package/src/parse-zip/zip64-info-generation.ts +21 -5
  66. package/src/tar-builder.ts +2 -1
  67. package/src/zip-loader.ts +2 -1
  68. package/src/zip-writer.ts +24 -10
@@ -0,0 +1,235 @@
1
+ import {
2
+ FileHandleFile,
3
+ concatenateArrayBuffers,
4
+ path,
5
+ NodeFilesystem,
6
+ NodeFile
7
+ } from '@loaders.gl/loader-utils';
8
+ import {ZipEoCDRecord, generateEoCD, parseEoCDRecord, updateEoCD} from './end-of-central-directory';
9
+ import {CRC32Hash} from '@loaders.gl/crypto';
10
+ import {generateLocalHeader} from './local-file-header';
11
+ import {generateCDHeader} from './cd-file-header';
12
+ import {fetchFile} from '@loaders.gl/core';
13
+
14
+ /**
15
+ * cut off CD and EoCD records from zip file
16
+ * @param provider zip file
17
+ * @returns tuple with three values: CD, EoCD record, EoCD information
18
+ */
19
+ async function cutTheTailOff(
20
+ provider: FileHandleFile
21
+ ): Promise<[ArrayBuffer, ArrayBuffer, ZipEoCDRecord]> {
22
+ // define where the body ends
23
+ const oldEoCDinfo = await parseEoCDRecord(provider);
24
+ const oldCDStartOffset = oldEoCDinfo.cdStartOffset;
25
+
26
+ // define cd length
27
+ const oldCDLength = Number(
28
+ oldEoCDinfo.offsets.zip64EoCDOffset
29
+ ? oldEoCDinfo.offsets.zip64EoCDOffset - oldCDStartOffset
30
+ : oldEoCDinfo.offsets.zipEoCDOffset - oldCDStartOffset
31
+ );
32
+
33
+ // cut off everything except of archieve body
34
+ const zipEnding = await provider.slice(oldCDStartOffset, provider.length);
35
+ await provider.truncate(Number(oldCDStartOffset));
36
+
37
+ // divide cd body and eocd record
38
+ const oldCDBody = zipEnding.slice(0, oldCDLength);
39
+ const eocdBody = zipEnding.slice(oldCDLength, zipEnding.byteLength);
40
+
41
+ return [oldCDBody, eocdBody, oldEoCDinfo];
42
+ }
43
+
44
+ /**
45
+ * generates CD and local headers for the file
46
+ * @param fileName name of the file
47
+ * @param fileToAdd buffer with the file
48
+ * @param localFileHeaderOffset offset of the file local header
49
+ * @returns tuple with two values: local header and file body, cd header
50
+ */
51
+ async function generateFileHeaders(
52
+ fileName: string,
53
+ fileToAdd: ArrayBuffer,
54
+ localFileHeaderOffset: bigint
55
+ ): Promise<[Uint8Array, Uint8Array]> {
56
+ // generating CRC32 of the content
57
+ const newFileCRC322 = parseInt(await new CRC32Hash().hash(fileToAdd, 'hex'), 16);
58
+
59
+ // generate local header for the file
60
+ const newFileLocalHeader = generateLocalHeader({
61
+ crc32: newFileCRC322,
62
+ fileName,
63
+ length: fileToAdd.byteLength
64
+ });
65
+
66
+ // generate hash file cd header
67
+ const newFileCDHeader = generateCDHeader({
68
+ crc32: newFileCRC322,
69
+ fileName,
70
+ offset: localFileHeaderOffset,
71
+ length: fileToAdd.byteLength
72
+ });
73
+ return [
74
+ new Uint8Array(concatenateArrayBuffers(newFileLocalHeader, fileToAdd)),
75
+ new Uint8Array(newFileCDHeader)
76
+ ];
77
+ }
78
+
79
+ /**
80
+ * adds one file in the end of the archieve
81
+ * @param zipUrl path to the file
82
+ * @param fileToAdd new file body
83
+ * @param fileName new file name
84
+ */
85
+ export async function addOneFile(zipUrl: string, fileToAdd: ArrayBuffer, fileName: string) {
86
+ // init file handler
87
+ const provider = new FileHandleFile(zipUrl, true);
88
+
89
+ const [oldCDBody, eocdBody, oldEoCDinfo] = await cutTheTailOff(provider);
90
+
91
+ // remember the new file local header start offset
92
+ const newFileOffset = provider.length;
93
+
94
+ const [localPart, cdHeaderPart] = await generateFileHeaders(fileName, fileToAdd, newFileOffset);
95
+
96
+ // write down the file local header
97
+ await provider.append(localPart);
98
+
99
+ // add the file CD header to the CD
100
+ const newCDBody = concatenateArrayBuffers(oldCDBody, cdHeaderPart);
101
+
102
+ // remember the CD start offset
103
+ const newCDStartOffset = provider.length;
104
+
105
+ // write down new CD
106
+ await provider.append(new Uint8Array(newCDBody));
107
+
108
+ // remember where eocd starts
109
+ const eocdOffset = provider.length;
110
+
111
+ await provider.append(
112
+ updateEoCD(
113
+ eocdBody,
114
+ oldEoCDinfo.offsets,
115
+ newCDStartOffset,
116
+ eocdOffset,
117
+ oldEoCDinfo.cdRecordsNumber + 1n
118
+ )
119
+ );
120
+ }
121
+
122
+ /**
123
+ * creates zip archive with no compression
124
+ * @note This is a node specific function that works on files
125
+ * @param inputPath path where files for the achive are stored
126
+ * @param outputPath path where zip archive will be placed
127
+ */
128
+ export async function createZip(
129
+ inputPath: string,
130
+ outputPath: string,
131
+ createAdditionalData?: (
132
+ fileList: {fileName: string; localHeaderOffset: bigint}[]
133
+ ) => Promise<{path: string; file: ArrayBuffer}>
134
+ ) {
135
+ const fileIterator = getFileIterator(inputPath);
136
+
137
+ const resFile = new NodeFile(outputPath, 'w');
138
+ const fileList: {fileName: string; localHeaderOffset: bigint}[] = [];
139
+
140
+ const cdArray: ArrayBuffer[] = [];
141
+ for await (const file of fileIterator) {
142
+ await addFile(file, resFile, cdArray, fileList);
143
+ }
144
+ if (createAdditionalData) {
145
+ const additionaldata = await createAdditionalData(fileList);
146
+ console.log(additionaldata);
147
+ await addFile(additionaldata, resFile, cdArray);
148
+ }
149
+ const cdOffset = (await resFile.stat()).bigsize;
150
+ const cd = concatenateArrayBuffers(...cdArray);
151
+ await resFile.append(new Uint8Array(cd));
152
+ const eoCDStart = (await resFile.stat()).bigsize;
153
+ await resFile.append(
154
+ new Uint8Array(
155
+ generateEoCD({recordsNumber: cdArray.length, cdSize: cd.byteLength, cdOffset, eoCDStart})
156
+ )
157
+ );
158
+ }
159
+
160
+ /**
161
+ * Adds file to zip parts
162
+ * @param file file to add
163
+ * @param resFile zip file body
164
+ * @param cdArray zip file central directory
165
+ * @param fileList list of file offsets
166
+ */
167
+ async function addFile(
168
+ file: {path: string; file: ArrayBuffer},
169
+ resFile: NodeFile,
170
+ cdArray: ArrayBuffer[],
171
+ fileList?: {fileName: string; localHeaderOffset: bigint}[]
172
+ ) {
173
+ const size = (await resFile.stat()).bigsize;
174
+ fileList?.push({fileName: file.path, localHeaderOffset: size});
175
+ const [localPart, cdHeaderPart] = await generateFileHeaders(file.path, file.file, size);
176
+ await resFile.append(localPart);
177
+ cdArray.push(cdHeaderPart);
178
+ }
179
+
180
+ /**
181
+ * creates iterator providing buffer with file content and path to every file in the input folder
182
+ * @param inputPath path to the input folder
183
+ * @returns iterator
184
+ */
185
+ export function getFileIterator(
186
+ inputPath: string
187
+ ): AsyncIterable<{path: string; file: ArrayBuffer}> {
188
+ async function* iterable() {
189
+ const fileList = await getAllFiles(inputPath);
190
+ for (const filePath of fileList) {
191
+ const file = await (await fetchFile(path.join(inputPath, filePath))).arrayBuffer();
192
+ yield {path: filePath, file};
193
+ }
194
+ }
195
+ return iterable();
196
+ }
197
+
198
+ /**
199
+ * creates a list of relative paths to all files in the provided folder
200
+ * @param basePath path of the root folder
201
+ * @param subfolder relative path from the root folder.
202
+ * @returns list of paths
203
+ */
204
+ export async function getAllFiles(
205
+ basePath: string,
206
+ subfolder: string = '',
207
+ fsPassed?: NodeFilesystem
208
+ ): Promise<string[]> {
209
+ const fs = fsPassed ? fsPassed : new NodeFilesystem({});
210
+ const files = await fs.readdir(pathJoin(basePath, subfolder));
211
+
212
+ const arrayOfFiles: string[] = [];
213
+
214
+ for (const file of files) {
215
+ const fullPath = pathJoin(basePath, subfolder, file);
216
+ if ((await fs.stat(fullPath)).isDirectory) {
217
+ const files = await getAllFiles(basePath, pathJoin(subfolder, file));
218
+ arrayOfFiles.push(...files);
219
+ } else {
220
+ arrayOfFiles.push(pathJoin(subfolder, file));
221
+ }
222
+ }
223
+
224
+ return arrayOfFiles;
225
+ }
226
+
227
+ /**
228
+ * removes empty parts from path array and joins it
229
+ * @param paths paths to join
230
+ * @returns joined path
231
+ */
232
+ function pathJoin(...paths: string[]): string {
233
+ const resPaths: string[] = paths.filter((val) => val.length);
234
+ return path.join(...resPaths);
235
+ }
@@ -25,7 +25,7 @@ export function createZip64Info(options: Zip64Options): ArrayBuffer {
25
25
 
26
26
  for (const field of ZIP64_FIELDS) {
27
27
  if (!optionsToUse[field.name ?? ''] && !field.default) {
28
- continue;
28
+ continue; // eslint-disable-line no-continue
29
29
  }
30
30
  const newValue = new DataView(new ArrayBuffer(field.size));
31
31
  NUMBER_SETTERS[field.size](newValue, 0, optionsToUse[field.name ?? ''] ?? field.default);
@@ -41,15 +41,31 @@ export function createZip64Info(options: Zip64Options): ArrayBuffer {
41
41
  * @param offset offset of the writing start
42
42
  * @param value value to be written
43
43
  */
44
- type NumberSetter = (header: DataView, offset: number, value: number) => void;
44
+ type NumberSetter = (header: DataView, offset: number, value: number | bigint) => void;
45
+
46
+ /**
47
+ * Writes values into buffer according to the bytes amount
48
+ * @param header header where to write the data
49
+ * @param fieldSize size of the field in bytes
50
+ * @param fieldOffset offset of the field
51
+ * @param value value to be written
52
+ */
53
+ export function setFieldToNumber(
54
+ header: DataView,
55
+ fieldSize: number,
56
+ fieldOffset: number | bigint,
57
+ value: number | bigint
58
+ ): void {
59
+ NUMBER_SETTERS[fieldSize](header, Number(fieldOffset), value);
60
+ }
45
61
 
46
62
  /** functions to write values into buffer according to the bytes amount */
47
- export const NUMBER_SETTERS: {[key: number]: NumberSetter} = {
63
+ const NUMBER_SETTERS: {[key: number]: NumberSetter} = {
48
64
  2: (header, offset, value) => {
49
- header.setUint16(offset, value, true);
65
+ header.setUint16(offset, Number(value > 0xffff ? 0xffff : value), true);
50
66
  },
51
67
  4: (header, offset, value) => {
52
- header.setUint32(offset, value, true);
68
+ header.setUint32(offset, Number(value > 0xffffffff ? 0xffffffff : value), true);
53
69
  },
54
70
  8: (header, offset, value) => {
55
71
  header.setBigUint64(offset, BigInt(value), true);
@@ -1,4 +1,5 @@
1
- // loaders.gl, MIT license
1
+ // loaders.gl
2
+ // SPDX-License-Identifier: MIT
2
3
  // Copyright (c) vis.gl contributors
3
4
 
4
5
  import Tar from './lib/tar/tar';
package/src/zip-loader.ts CHANGED
@@ -1,4 +1,5 @@
1
- // loaders.gl, MIT license
1
+ // loaders.gl
2
+ // SPDX-License-Identifier: MIT
2
3
  // Copyright (c) vis.gl contributors
3
4
 
4
5
  import type {LoaderWithParser, LoaderOptions} from '@loaders.gl/loader-utils';
package/src/zip-writer.ts CHANGED
@@ -1,9 +1,13 @@
1
- // loaders.gl, MIT license
1
+ // loaders.gl
2
+ // SPDX-License-Identifier: MIT
2
3
  // Copyright (c) vis.gl contributors
3
4
 
4
- import type {Writer, WriterOptions} from '@loaders.gl/loader-utils';
5
+ import type {WriterWithEncoder, WriterOptions} from '@loaders.gl/loader-utils';
5
6
  import JSZip, {JSZipGeneratorOptions} from 'jszip';
6
7
 
8
+ // @ts-ignore TS2304: Cannot find name '__VERSION__'.
9
+ const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'latest';
10
+
7
11
  export type ZipWriterOptions = WriterOptions & {
8
12
  zip?: {
9
13
  onUpdate?: (metadata: {percent: number}) => void;
@@ -15,19 +19,27 @@ export type ZipWriterOptions = WriterOptions & {
15
19
  /**
16
20
  * Zip exporter
17
21
  */
18
- export const ZipWriter: Writer<FileReaderEventMap, never, ZipWriterOptions> = {
22
+ export const ZipWriter: WriterWithEncoder<Record<string, ArrayBuffer>, never, ZipWriterOptions> = {
19
23
  name: 'Zip Archive',
24
+ id: 'zip',
25
+ module: 'zip',
26
+ version: VERSION,
20
27
  extensions: ['zip'],
21
28
  category: 'archive',
22
29
  mimeTypes: ['application/zip'],
23
- // @ts-ignore
30
+ options: {
31
+ zip: {
32
+ onUpdate: () => {}
33
+ },
34
+ jszip: {}
35
+ },
24
36
  encode: encodeZipAsync
25
37
  };
26
38
 
27
39
  async function encodeZipAsync(
28
40
  fileMap: Record<string, ArrayBuffer>,
29
41
  options: ZipWriterOptions = {}
30
- ) {
42
+ ): Promise<ArrayBuffer> {
31
43
  const jsZip = new JSZip();
32
44
  // add files to the zip
33
45
  for (const subFileName in fileMap) {
@@ -38,14 +50,16 @@ async function encodeZipAsync(
38
50
  jsZip.file(subFileName, subFileData, options?.jszip || {});
39
51
  }
40
52
 
41
- // always generate the full zip as an arraybuffer
42
- const jszipOptions: JSZipGeneratorOptions = {...options?.jszip, type: 'arraybuffer'};
43
- const {onUpdate = () => {}} = options;
53
+ const zipOptions = {...ZipWriter.options.zip, ...options?.zip};
54
+ const jszipOptions: JSZipGeneratorOptions = {...ZipWriter.options?.jszip, ...options.jszip};
44
55
 
45
56
  try {
46
- return await jsZip.generateAsync(jszipOptions, onUpdate);
57
+ return await jsZip.generateAsync(
58
+ {...jszipOptions, type: 'arraybuffer'}, // generate an arraybuffer
59
+ zipOptions.onUpdate
60
+ );
47
61
  } catch (error) {
48
- options.log.error(`Unable to write zip archive: ${error}`);
62
+ options.log.error(`Unable to encode zip archive: ${error}`);
49
63
  throw error;
50
64
  }
51
65
  }