@loaders.gl/zip 4.2.0-alpha.4 → 4.2.0-alpha.6

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 (56) hide show
  1. package/dist/dist.dev.js +919 -523
  2. package/dist/dist.min.js +25 -0
  3. package/dist/filesystems/IndexedArchive.js +26 -12
  4. package/dist/filesystems/zip-filesystem.d.ts +2 -2
  5. package/dist/filesystems/zip-filesystem.d.ts.map +1 -1
  6. package/dist/filesystems/zip-filesystem.js +125 -88
  7. package/dist/hash-file-utility.d.ts +1 -1
  8. package/dist/hash-file-utility.d.ts.map +1 -1
  9. package/dist/hash-file-utility.js +85 -42
  10. package/dist/index.cjs +64 -128
  11. package/dist/index.cjs.map +7 -0
  12. package/dist/index.d.ts +12 -12
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +4 -1
  15. package/dist/lib/tar/header.d.ts +1 -1
  16. package/dist/lib/tar/header.d.ts.map +1 -1
  17. package/dist/lib/tar/header.js +69 -33
  18. package/dist/lib/tar/tar.d.ts +1 -1
  19. package/dist/lib/tar/tar.d.ts.map +1 -1
  20. package/dist/lib/tar/tar.js +124 -106
  21. package/dist/lib/tar/types.js +3 -1
  22. package/dist/lib/tar/utils.js +45 -18
  23. package/dist/parse-zip/cd-file-header.d.ts +1 -1
  24. package/dist/parse-zip/cd-file-header.d.ts.map +1 -1
  25. package/dist/parse-zip/cd-file-header.js +239 -177
  26. package/dist/parse-zip/end-of-central-directory.js +247 -158
  27. package/dist/parse-zip/local-file-header.d.ts +1 -1
  28. package/dist/parse-zip/local-file-header.d.ts.map +1 -1
  29. package/dist/parse-zip/local-file-header.js +143 -102
  30. package/dist/parse-zip/search-from-the-end.js +27 -13
  31. package/dist/parse-zip/zip-composition.js +142 -92
  32. package/dist/parse-zip/zip64-info-generation.js +64 -41
  33. package/dist/tar-builder.d.ts +1 -1
  34. package/dist/tar-builder.d.ts.map +1 -1
  35. package/dist/tar-builder.js +32 -29
  36. package/dist/zip-loader.js +52 -41
  37. package/dist/zip-writer.js +40 -40
  38. package/package.json +11 -7
  39. package/src/filesystems/zip-filesystem.ts +4 -0
  40. package/dist/filesystems/IndexedArchive.js.map +0 -1
  41. package/dist/filesystems/zip-filesystem.js.map +0 -1
  42. package/dist/hash-file-utility.js.map +0 -1
  43. package/dist/index.js.map +0 -1
  44. package/dist/lib/tar/header.js.map +0 -1
  45. package/dist/lib/tar/tar.js.map +0 -1
  46. package/dist/lib/tar/types.js.map +0 -1
  47. package/dist/lib/tar/utils.js.map +0 -1
  48. package/dist/parse-zip/cd-file-header.js.map +0 -1
  49. package/dist/parse-zip/end-of-central-directory.js.map +0 -1
  50. package/dist/parse-zip/local-file-header.js.map +0 -1
  51. package/dist/parse-zip/search-from-the-end.js.map +0 -1
  52. package/dist/parse-zip/zip-composition.js.map +0 -1
  53. package/dist/parse-zip/zip64-info-generation.js.map +0 -1
  54. package/dist/tar-builder.js.map +0 -1
  55. package/dist/zip-loader.js.map +0 -1
  56. package/dist/zip-writer.js.map +0 -1
@@ -1,3 +1,6 @@
1
+ // loaders.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
1
4
  import { isBrowser } from '@loaders.gl/loader-utils';
2
5
  import { isFileProvider } from '@loaders.gl/loader-utils';
3
6
  import { FileHandleFile } from '@loaders.gl/loader-utils';
@@ -5,101 +8,135 @@ import { makeZipCDHeaderIterator } from "../parse-zip/cd-file-header.js";
5
8
  import { parseZipLocalFileHeader } from "../parse-zip/local-file-header.js";
6
9
  import { DeflateCompression } from '@loaders.gl/compression';
7
10
  import { IndexedArchive } from "./IndexedArchive.js";
11
+ /** Handling different compression types in zip */
8
12
  export const ZIP_COMPRESSION_HANDLERS = {
9
- 0: async compressedFile => compressedFile,
10
- 8: async compressedFile => {
11
- const compression = new DeflateCompression({
12
- raw: true
13
- });
14
- const decompressedData = await compression.decompress(compressedFile);
15
- return decompressedData;
16
- }
13
+ /** No compression */
14
+ 0: async (compressedFile) => compressedFile,
15
+ /** Deflation */
16
+ 8: async (compressedFile) => {
17
+ const compression = new DeflateCompression({ raw: true });
18
+ const decompressedData = await compression.decompress(compressedFile);
19
+ return decompressedData;
20
+ }
17
21
  };
22
+ /**
23
+ * FileSystem adapter for a ZIP file
24
+ * Holds FileProvider object that provides random access to archived files
25
+ */
18
26
  export class ZipFileSystem {
19
- constructor(file) {
20
- this.fileProvider = null;
21
- this.fileName = void 0;
22
- this.archive = null;
23
- if (typeof file === 'string') {
24
- this.fileName = file;
25
- if (!isBrowser) {
26
- this.fileProvider = new FileHandleFile(file);
27
- } else {
28
- throw new Error('Cannot open file for random access in a WEB browser');
29
- }
30
- } else if (file instanceof IndexedArchive) {
31
- this.fileProvider = file.fileProvider;
32
- this.archive = file;
33
- this.fileName = file.fileName;
34
- } else if (isFileProvider(file)) {
35
- this.fileProvider = file;
36
- }
37
- }
38
- async destroy() {
39
- if (this.fileProvider) {
40
- await this.fileProvider.destroy();
41
- }
42
- }
43
- async readdir() {
44
- if (!this.fileProvider) {
45
- throw new Error('No data detected in the zip archive');
27
+ /** FileProvider instance promise */
28
+ fileProvider = null;
29
+ fileName;
30
+ archive = null;
31
+ /**
32
+ * Constructor
33
+ * @param file - instance of FileProvider or file path string
34
+ */
35
+ constructor(file) {
36
+ // Try to open file in NodeJS
37
+ if (typeof file === 'string') {
38
+ this.fileName = file;
39
+ if (!isBrowser) {
40
+ this.fileProvider = new FileHandleFile(file);
41
+ }
42
+ else {
43
+ throw new Error('Cannot open file for random access in a WEB browser');
44
+ }
45
+ }
46
+ else if (file instanceof IndexedArchive) {
47
+ this.fileProvider = file.fileProvider;
48
+ this.archive = file;
49
+ this.fileName = file.fileName;
50
+ }
51
+ else if (isFileProvider(file)) {
52
+ this.fileProvider = file;
53
+ }
46
54
  }
47
- const fileNames = [];
48
- const zipCDIterator = makeZipCDHeaderIterator(this.fileProvider);
49
- for await (const cdHeader of zipCDIterator) {
50
- fileNames.push(cdHeader.fileName);
55
+ /** Clean up resources */
56
+ async destroy() {
57
+ if (this.fileProvider) {
58
+ await this.fileProvider.destroy();
59
+ }
51
60
  }
52
- return fileNames;
53
- }
54
- async stat(filename) {
55
- const cdFileHeader = await this.getCDFileHeader(filename);
56
- return {
57
- ...cdFileHeader,
58
- size: Number(cdFileHeader.uncompressedSize)
59
- };
60
- }
61
- async fetch(filename) {
62
- let uncompressedFile;
63
- if (this.archive) {
64
- uncompressedFile = await this.archive.getFile(filename, 'http');
65
- } else {
66
- if (!this.fileProvider) {
67
- throw new Error('No data detected in the zip archive');
68
- }
69
- const cdFileHeader = await this.getCDFileHeader(filename);
70
- const localFileHeader = await parseZipLocalFileHeader(cdFileHeader.localHeaderOffset, this.fileProvider);
71
- if (!localFileHeader) {
72
- throw new Error('Local file header has not been found in the zip archive`');
73
- }
74
- const compressionHandler = ZIP_COMPRESSION_HANDLERS[localFileHeader.compressionMethod.toString()];
75
- if (!compressionHandler) {
76
- throw Error('Only Deflation compression is supported');
77
- }
78
- const compressedFile = await this.fileProvider.slice(localFileHeader.fileDataOffset, localFileHeader.fileDataOffset + localFileHeader.compressedSize);
79
- uncompressedFile = await compressionHandler(compressedFile);
61
+ /**
62
+ * Get file names list from zip archive
63
+ * @returns array of file names
64
+ */
65
+ async readdir() {
66
+ if (!this.fileProvider) {
67
+ throw new Error('No data detected in the zip archive');
68
+ }
69
+ const fileNames = [];
70
+ const zipCDIterator = makeZipCDHeaderIterator(this.fileProvider);
71
+ for await (const cdHeader of zipCDIterator) {
72
+ fileNames.push(cdHeader.fileName);
73
+ }
74
+ return fileNames;
80
75
  }
81
- const response = new Response(uncompressedFile);
82
- Object.defineProperty(response, 'url', {
83
- value: filename ? `${this.fileName || ''}/${filename}` : this.fileName || ''
84
- });
85
- return response;
86
- }
87
- async getCDFileHeader(filename) {
88
- if (!this.fileProvider) {
89
- throw new Error('No data detected in the zip archive');
76
+ /**
77
+ * Get file metadata
78
+ * @param filename - name of a file
79
+ * @returns central directory data
80
+ */
81
+ async stat(filename) {
82
+ const cdFileHeader = await this.getCDFileHeader(filename);
83
+ return { ...cdFileHeader, size: Number(cdFileHeader.uncompressedSize) };
90
84
  }
91
- const zipCDIterator = makeZipCDHeaderIterator(this.fileProvider);
92
- let result = null;
93
- for await (const cdHeader of zipCDIterator) {
94
- if (cdHeader.fileName === filename) {
95
- result = cdHeader;
96
- break;
97
- }
85
+ /**
86
+ * Implementation of fetch against this file system
87
+ * @param filename - name of a file
88
+ * @returns - Response with file data
89
+ */
90
+ async fetch(filename) {
91
+ if (this.fileName && filename.indexOf(this.fileName) === 0) {
92
+ filename = filename.substring(this.fileName.length + 1);
93
+ }
94
+ let uncompressedFile;
95
+ if (this.archive) {
96
+ uncompressedFile = await this.archive.getFile(filename, 'http');
97
+ }
98
+ else {
99
+ if (!this.fileProvider) {
100
+ throw new Error('No data detected in the zip archive');
101
+ }
102
+ const cdFileHeader = await this.getCDFileHeader(filename);
103
+ const localFileHeader = await parseZipLocalFileHeader(cdFileHeader.localHeaderOffset, this.fileProvider);
104
+ if (!localFileHeader) {
105
+ throw new Error('Local file header has not been found in the zip archive`');
106
+ }
107
+ const compressionHandler = ZIP_COMPRESSION_HANDLERS[localFileHeader.compressionMethod.toString()];
108
+ if (!compressionHandler) {
109
+ throw Error('Only Deflation compression is supported');
110
+ }
111
+ const compressedFile = await this.fileProvider.slice(localFileHeader.fileDataOffset, localFileHeader.fileDataOffset + localFileHeader.compressedSize);
112
+ uncompressedFile = await compressionHandler(compressedFile);
113
+ }
114
+ const response = new Response(uncompressedFile);
115
+ Object.defineProperty(response, 'url', {
116
+ value: filename ? `${this.fileName || ''}/${filename}` : this.fileName || ''
117
+ });
118
+ return response;
98
119
  }
99
- if (!result) {
100
- throw new Error('File has not been found in the zip archive');
120
+ /**
121
+ * Get central directory file header
122
+ * @param filename - name of a file
123
+ * @returns central directory file header
124
+ */
125
+ async getCDFileHeader(filename) {
126
+ if (!this.fileProvider) {
127
+ throw new Error('No data detected in the zip archive');
128
+ }
129
+ const zipCDIterator = makeZipCDHeaderIterator(this.fileProvider);
130
+ let result = null;
131
+ for await (const cdHeader of zipCDIterator) {
132
+ if (cdHeader.fileName === filename) {
133
+ result = cdHeader;
134
+ break;
135
+ }
136
+ }
137
+ if (!result) {
138
+ throw new Error('File has not been found in the zip archive');
139
+ }
140
+ return result;
101
141
  }
102
- return result;
103
- }
104
142
  }
105
- //# sourceMappingURL=zip-filesystem.js.map
@@ -1,5 +1,5 @@
1
1
  import { FileProvider } from '@loaders.gl/loader-utils';
2
- import { ZipCDFileHeader } from './parse-zip/cd-file-header';
2
+ import { ZipCDFileHeader } from "./parse-zip/cd-file-header.js";
3
3
  /**
4
4
  * Reads hash file from buffer and returns it in ready-to-use form
5
5
  * @param arrayBuffer - buffer containing hash file
@@ -1 +1 @@
1
- {"version":3,"file":"hash-file-utility.d.ts","sourceRoot":"","sources":["../src/hash-file-utility.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,YAAY,EAGb,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAC,eAAe,EAA0B,MAAM,4BAA4B,CAAC;AAEpF;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAY/E;AASD;;;;GAIG;AACH,wBAAsB,2BAA2B,CAC/C,YAAY,EAAE,YAAY,GACzB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAGjC;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAChC,aAAa,EAAE,aAAa,CAAC,eAAe,CAAC,GAC5C,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAcjC;AAED,mCAAmC;AACnC,KAAK,YAAY,GAAG;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,aAAa,EAAE,aAAa,CAAC,YAAY,CAAC,GAAG,QAAQ,CAAC,YAAY,CAAC,GAClE,OAAO,CAAC,WAAW,CAAC,CAwBtB"}
1
+ {"version":3,"file":"hash-file-utility.d.ts","sourceRoot":"","sources":["../src/hash-file-utility.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,YAAY,EAGb,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAC,eAAe,EAA0B,sCAAmC;AAEpF;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAY/E;AASD;;;;GAIG;AACH,wBAAsB,2BAA2B,CAC/C,YAAY,EAAE,YAAY,GACzB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAGjC;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAChC,aAAa,EAAE,aAAa,CAAC,eAAe,CAAC,GAC5C,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAcjC;AAED,mCAAmC;AACnC,KAAK,YAAY,GAAG;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,aAAa,EAAE,aAAa,CAAC,YAAY,CAAC,GAAG,QAAQ,CAAC,YAAY,CAAC,GAClE,OAAO,CAAC,WAAW,CAAC,CAwBtB"}
@@ -1,62 +1,105 @@
1
+ // loaders.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
1
4
  import { MD5Hash } from '@loaders.gl/crypto';
2
5
  import { concatenateArrayBuffers, concatenateArrayBuffersFromArray } from '@loaders.gl/loader-utils';
3
6
  import { makeZipCDHeaderIterator } from "./parse-zip/cd-file-header.js";
7
+ /**
8
+ * Reads hash file from buffer and returns it in ready-to-use form
9
+ * @param arrayBuffer - buffer containing hash file
10
+ * @returns Map containing hash and offset
11
+ */
4
12
  export function parseHashTable(arrayBuffer) {
5
- const dataView = new DataView(arrayBuffer);
6
- const hashMap = {};
7
- for (let i = 0; i < arrayBuffer.byteLength; i = i + 24) {
8
- const offset = dataView.getBigUint64(i + 16, true);
9
- const hash = bufferToHex(arrayBuffer, i, 16);
10
- hashMap[hash] = offset;
11
- }
12
- return hashMap;
13
+ const dataView = new DataView(arrayBuffer);
14
+ const hashMap = {};
15
+ for (let i = 0; i < arrayBuffer.byteLength; i = i + 24) {
16
+ const offset = dataView.getBigUint64(i + 16, true);
17
+ const hash = bufferToHex(arrayBuffer, i, 16);
18
+ hashMap[hash] = offset;
19
+ }
20
+ return hashMap;
13
21
  }
14
22
  function bufferToHex(buffer, start, length) {
15
- return [...new Uint8Array(buffer, start, length)].map(x => x.toString(16).padStart(2, '0')).join('');
23
+ // buffer is an ArrayBuffer
24
+ return [...new Uint8Array(buffer, start, length)]
25
+ .map((x) => x.toString(16).padStart(2, '0'))
26
+ .join('');
16
27
  }
28
+ /**
29
+ * generates hash info from zip files "central directory"
30
+ * @param fileProvider - provider of the archive
31
+ * @returns ready to use hash info
32
+ */
17
33
  export async function makeHashTableFromZipHeaders(fileProvider) {
18
- const zipCDIterator = makeZipCDHeaderIterator(fileProvider);
19
- return getHashTable(zipCDIterator);
34
+ const zipCDIterator = makeZipCDHeaderIterator(fileProvider);
35
+ return getHashTable(zipCDIterator);
20
36
  }
37
+ /**
38
+ * creates hash table from file offset iterator
39
+ * @param zipCDIterator iterator to use
40
+ * @returns hash table
41
+ */
21
42
  export async function getHashTable(zipCDIterator) {
22
- const md5Hash = new MD5Hash();
23
- const textEncoder = new TextEncoder();
24
- const hashTable = {};
25
- for await (const cdHeader of zipCDIterator) {
26
- const filename = cdHeader.fileName.split('\\').join('/').toLocaleLowerCase();
27
- const arrayBuffer = textEncoder.encode(filename).buffer;
28
- const md5 = await md5Hash.hash(arrayBuffer, 'hex');
29
- hashTable[md5] = cdHeader.localHeaderOffset;
30
- }
31
- return hashTable;
43
+ const md5Hash = new MD5Hash();
44
+ const textEncoder = new TextEncoder();
45
+ const hashTable = {};
46
+ for await (const cdHeader of zipCDIterator) {
47
+ const filename = cdHeader.fileName.split('\\').join('/').toLocaleLowerCase();
48
+ const arrayBuffer = textEncoder.encode(filename).buffer;
49
+ const md5 = await md5Hash.hash(arrayBuffer, 'hex');
50
+ hashTable[md5] = cdHeader.localHeaderOffset;
51
+ }
52
+ return hashTable;
32
53
  }
54
+ /**
55
+ * creates hash file that later can be added to the SLPK archive
56
+ * @param zipCDIterator iterator to use
57
+ * @returns ArrayBuffer containing hash file
58
+ */
33
59
  export async function composeHashFile(zipCDIterator) {
34
- const md5Hash = new MD5Hash();
35
- const textEncoder = new TextEncoder();
36
- const hashArray = [];
37
- for await (const cdHeader of zipCDIterator) {
38
- let filename = cdHeader.fileName.split('\\').join('/');
39
- if (filename !== '3dSceneLayer.json.gz') {
40
- filename = filename.toLocaleLowerCase();
60
+ const md5Hash = new MD5Hash();
61
+ const textEncoder = new TextEncoder();
62
+ const hashArray = [];
63
+ for await (const cdHeader of zipCDIterator) {
64
+ let filename = cdHeader.fileName.split('\\').join('/');
65
+ // I3S edge case. All files should be lower case by spec. However, ArcGIS
66
+ // and official i3s_converter https://github.com/Esri/i3s-spec/blob/master/i3s_converter/i3s_converter_ReadMe.md
67
+ // expect `3dSceneLayer.json.gz` in camel case
68
+ if (filename !== '3dSceneLayer.json.gz') {
69
+ filename = filename.toLocaleLowerCase();
70
+ }
71
+ const arrayBuffer = textEncoder.encode(filename).buffer;
72
+ const md5 = await md5Hash.hash(arrayBuffer, 'hex');
73
+ hashArray.push(concatenateArrayBuffers(hexStringToBuffer(md5), bigintToBuffer(cdHeader.localHeaderOffset)));
41
74
  }
42
- const arrayBuffer = textEncoder.encode(filename).buffer;
43
- const md5 = await md5Hash.hash(arrayBuffer, 'hex');
44
- hashArray.push(concatenateArrayBuffers(hexStringToBuffer(md5), bigintToBuffer(cdHeader.localHeaderOffset)));
45
- }
46
- const bufferArray = hashArray.sort(compareHashes);
47
- return concatenateArrayBuffersFromArray(bufferArray);
75
+ const bufferArray = hashArray.sort(compareHashes);
76
+ return concatenateArrayBuffersFromArray(bufferArray);
48
77
  }
78
+ /**
79
+ * Function to compare md5 hashes according to https://github.com/Esri/i3s-spec/blob/master/docs/2.0/slpk_hashtable.pcsl.md
80
+ * @param arrA first hash to compare
81
+ * @param arrB second hash to compare
82
+ * @returns 0 if equal, negative number if a<b, pozitive if a>b
83
+ */
49
84
  function compareHashes(arrA, arrB) {
50
- const a = new BigUint64Array(arrA);
51
- const b = new BigUint64Array(arrB);
52
- return Number(a[0] === b[0] ? a[1] - b[1] : a[0] - b[0]);
85
+ const a = new BigUint64Array(arrA);
86
+ const b = new BigUint64Array(arrB);
87
+ return Number(a[0] === b[0] ? a[1] - b[1] : a[0] - b[0]);
53
88
  }
89
+ /**
90
+ * converts hex string to buffer
91
+ * @param str hex string to convert
92
+ * @returns conversion result
93
+ */
54
94
  function hexStringToBuffer(str) {
55
- var _str$match;
56
- const byteArray = (_str$match = str.match(/../g)) === null || _str$match === void 0 ? void 0 : _str$match.map(h => parseInt(h, 16));
57
- return new Uint8Array(byteArray !== null && byteArray !== void 0 ? byteArray : new Array(16)).buffer;
95
+ const byteArray = str.match(/../g)?.map((h) => parseInt(h, 16));
96
+ return new Uint8Array(byteArray ?? new Array(16)).buffer;
58
97
  }
98
+ /**
99
+ * converts bigint to buffer
100
+ * @param n bigint to convert
101
+ * @returns convertion result
102
+ */
59
103
  function bigintToBuffer(n) {
60
- return new BigUint64Array([n]).buffer;
104
+ return new BigUint64Array([n]).buffer;
61
105
  }
62
- //# sourceMappingURL=hash-file-utility.js.map