@php-wasm/stream-compression 0.0.1 → 0.9.16
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.
- package/LICENSE +339 -0
- package/index.cjs +1 -0
- package/index.d.ts +143 -0
- package/index.js +546 -0
- package/package.json +36 -29
- package/{src/test/vitest-setup-file.ts → test/vitest-setup-file.d.ts} +0 -1
- package/utils/append-bytes.d.ts +7 -0
- package/utils/collect-bytes.d.ts +8 -0
- package/utils/collect-file.d.ts +8 -0
- package/utils/collect-string.d.ts +8 -0
- package/utils/concat-bytes.d.ts +9 -0
- package/utils/concat-string.d.ts +6 -0
- package/utils/concat-uint8-array.d.ts +7 -0
- package/utils/filter-stream.d.ts +7 -0
- package/utils/iterable-stream-polyfill.d.ts +1 -0
- package/utils/iterator-to-stream.d.ts +8 -0
- package/utils/limit-bytes.d.ts +8 -0
- package/utils/prepend-bytes.d.ts +7 -0
- package/utils/skip-first-bytes.d.ts +7 -0
- package/utils/skip-last-bytes.d.ts +7 -0
- package/utils/streamed-file.d.ts +39 -0
- package/zip/decode-remote-zip.d.ts +14 -0
- package/zip/decode-zip.d.ts +82 -0
- package/zip/encode-zip.d.ts +7 -0
- package/{src/zip/index.ts → zip/index.d.ts} +0 -2
- package/zip/types.d.ts +66 -0
- package/.eslintrc.json +0 -18
- package/project.json +0 -34
- package/src/index.ts +0 -7
- package/src/test/append-bytes.spec.ts +0 -25
- package/src/test/decode-zip.spec.ts +0 -22
- package/src/test/encode-zip.spec.ts +0 -47
- package/src/test/fixtures/hello-dolly.zip +0 -0
- package/src/test/prepend-bytes.spec.ts +0 -25
- package/src/test/skip-first-bytes.spec.ts +0 -41
- package/src/test/skip-last-bytes.spec.ts +0 -27
- package/src/utils/append-bytes.ts +0 -16
- package/src/utils/collect-bytes.ts +0 -24
- package/src/utils/collect-file.ts +0 -16
- package/src/utils/collect-string.ts +0 -25
- package/src/utils/concat-bytes.ts +0 -38
- package/src/utils/concat-string.ts +0 -17
- package/src/utils/concat-uint8-array.ts +0 -17
- package/src/utils/filter-stream.ts +0 -15
- package/src/utils/iterable-stream-polyfill.ts +0 -35
- package/src/utils/iterator-to-stream.ts +0 -39
- package/src/utils/limit-bytes.ts +0 -40
- package/src/utils/prepend-bytes.ts +0 -18
- package/src/utils/skip-first-bytes.ts +0 -21
- package/src/utils/skip-last-bytes.ts +0 -24
- package/src/utils/streamed-file.ts +0 -58
- package/src/zip/decode-remote-zip.ts +0 -409
- package/src/zip/decode-zip.ts +0 -349
- package/src/zip/encode-zip.ts +0 -278
- package/src/zip/types.ts +0 -76
- package/tsconfig.json +0 -23
- package/tsconfig.lib.json +0 -14
- package/tsconfig.spec.json +0 -25
- package/vite.config.ts +0 -55
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { IterableReadableStream } from './iterable-stream-polyfill';
|
|
2
|
+
/**
|
|
3
|
+
* Converts an iterator or iterable to a stream.
|
|
4
|
+
*
|
|
5
|
+
* @param iteratorOrIterable The iterator or iterable to convert.
|
|
6
|
+
* @returns A stream that will yield the values from the iterator or iterable.
|
|
7
|
+
*/
|
|
8
|
+
export declare function iteratorToStream<T>(iteratorOrIterable: AsyncIterator<T> | Iterator<T> | AsyncIterable<T> | Iterable<T>): IterableReadableStream<T>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Limit the number of bytes read from a stream.
|
|
3
|
+
*
|
|
4
|
+
* @param stream The stream to limit.
|
|
5
|
+
* @param bytes The number of bytes to read from the stream.
|
|
6
|
+
* @returns A new stream that will read at most `bytes` bytes from `stream`.
|
|
7
|
+
*/
|
|
8
|
+
export declare function limitBytes(stream: ReadableStream<Uint8Array>, bytes: number): ReadableStream<any>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skips the first `length` bytes of a stream.
|
|
3
|
+
*
|
|
4
|
+
* @param length The number of bytes to skip.
|
|
5
|
+
* @returns A transform stream that will skip the specified number of bytes.
|
|
6
|
+
*/
|
|
7
|
+
export declare function skipFirstBytes(length: number): TransformStream<Uint8Array, Uint8Array>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skips the first `length` bytes of a stream.
|
|
3
|
+
*
|
|
4
|
+
* @param length The number of bytes to skip.
|
|
5
|
+
* @returns A transform stream that will skip the specified number of bytes.
|
|
6
|
+
*/
|
|
7
|
+
export declare function skipLastBytes(skip: number, streamLength: number): TransformStream<any, any>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents a file that is streamed and not fully
|
|
3
|
+
* loaded into memory.
|
|
4
|
+
*/
|
|
5
|
+
export declare class StreamedFile extends File {
|
|
6
|
+
private readableStream;
|
|
7
|
+
/**
|
|
8
|
+
* Creates a new StreamedFile instance.
|
|
9
|
+
*
|
|
10
|
+
* @param readableStream The readable stream containing the file data.
|
|
11
|
+
* @param name The name of the file.
|
|
12
|
+
* @param type The MIME type of the file.
|
|
13
|
+
*/
|
|
14
|
+
constructor(readableStream: ReadableStream<Uint8Array>, name: string, type?: string);
|
|
15
|
+
/**
|
|
16
|
+
* Overrides the slice() method of the File class.
|
|
17
|
+
*
|
|
18
|
+
* @returns A Blob representing a portion of the file.
|
|
19
|
+
*/
|
|
20
|
+
slice(): Blob;
|
|
21
|
+
/**
|
|
22
|
+
* Returns the readable stream associated with the file.
|
|
23
|
+
*
|
|
24
|
+
* @returns The readable stream.
|
|
25
|
+
*/
|
|
26
|
+
stream(): ReadableStream<Uint8Array>;
|
|
27
|
+
/**
|
|
28
|
+
* Loads the file data into memory and then returns it as a string.
|
|
29
|
+
*
|
|
30
|
+
* @returns File data as text.
|
|
31
|
+
*/
|
|
32
|
+
text(): Promise<string>;
|
|
33
|
+
/**
|
|
34
|
+
* Loads the file data into memory and then returns it as an ArrayBuffer.
|
|
35
|
+
*
|
|
36
|
+
* @returns File data as an ArrayBuffer.
|
|
37
|
+
*/
|
|
38
|
+
arrayBuffer(): Promise<Uint8Array>;
|
|
39
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { CentralDirectoryEntry, FileEntry } from './types';
|
|
2
|
+
import { IterableReadableStream } from '../utils/iterable-stream-polyfill';
|
|
3
|
+
/**
|
|
4
|
+
* Streams the contents of a remote zip file.
|
|
5
|
+
*
|
|
6
|
+
* If the zip is large and the predicate is filtering the zip contents,
|
|
7
|
+
* only the matching files will be downloaded using the Range header
|
|
8
|
+
* (if supported by the server).
|
|
9
|
+
*
|
|
10
|
+
* @param url The URL of the zip file.
|
|
11
|
+
* @param predicate Optional. A function that returns true if the file should be downloaded.
|
|
12
|
+
* @returns A stream of zip entries.
|
|
13
|
+
*/
|
|
14
|
+
export declare function decodeRemoteZip(url: string, predicate?: (dirEntry: CentralDirectoryEntry | FileEntry) => boolean): Promise<IterableReadableStream<FileEntry> | IterableReadableStream<File>>;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reads files from a stream of zip file bytes.
|
|
3
|
+
*/
|
|
4
|
+
import { IterableReadableStream } from '../utils/iterable-stream-polyfill';
|
|
5
|
+
import { CentralDirectoryEntry, FileEntry } from './types';
|
|
6
|
+
/**
|
|
7
|
+
* Unzips a stream of zip file bytes.
|
|
8
|
+
*
|
|
9
|
+
* @param stream A stream of zip file bytes.
|
|
10
|
+
* @param predicate Optional. A function that returns true if the file should be downloaded.
|
|
11
|
+
* @returns An iterable stream of File objects.
|
|
12
|
+
*/
|
|
13
|
+
export declare function decodeZip(stream: ReadableStream<Uint8Array>, predicate?: () => boolean): IterableReadableStream<File>;
|
|
14
|
+
/**
|
|
15
|
+
* Parses a stream of zipped bytes into FileEntry informations.
|
|
16
|
+
*
|
|
17
|
+
* @param stream A stream of zip file bytes.
|
|
18
|
+
* @param predicate Optional. A function that returns true if the file should be downloaded.
|
|
19
|
+
* @returns An iterable stream of FileEntry objects.
|
|
20
|
+
*/
|
|
21
|
+
export declare function streamZippedFileEntries(stream: ReadableStream<Uint8Array>, predicate?: (dirEntry: CentralDirectoryEntry | FileEntry) => boolean): IterableReadableStream<FileEntry>;
|
|
22
|
+
/**
|
|
23
|
+
* Reads a file entry from a zip file.
|
|
24
|
+
*
|
|
25
|
+
* The file entry is structured as follows:
|
|
26
|
+
*
|
|
27
|
+
* ```
|
|
28
|
+
* Offset Bytes Description
|
|
29
|
+
* 0 4 Local file header signature = 0x04034b50 (PK♥♦ or "PK\3\4")
|
|
30
|
+
* 4 2 Version needed to extract (minimum)
|
|
31
|
+
* 6 2 General purpose bit flag
|
|
32
|
+
* 8 2 Compression method; e.g. none = 0, DEFLATE = 8 (or "\0x08\0x00")
|
|
33
|
+
* 10 2 File last modification time
|
|
34
|
+
* 12 2 File last modification date
|
|
35
|
+
* 14 4 CRC-32 of uncompressed data
|
|
36
|
+
* 18 4 Compressed size (or 0xffffffff for ZIP64)
|
|
37
|
+
* 22 4 Uncompressed size (or 0xffffffff for ZIP64)
|
|
38
|
+
* 26 2 File name length (n)
|
|
39
|
+
* 28 2 Extra field length (m)
|
|
40
|
+
* 30 n File name
|
|
41
|
+
* 30+n m Extra field
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* @param stream
|
|
45
|
+
* @param skipSignature Do not consume the signature from the stream.
|
|
46
|
+
* @returns
|
|
47
|
+
*/
|
|
48
|
+
export declare function readFileEntry(stream: ReadableStream<Uint8Array>, skipSignature?: boolean): Promise<FileEntry | null>;
|
|
49
|
+
/**
|
|
50
|
+
* Reads a central directory entry from a zip file.
|
|
51
|
+
*
|
|
52
|
+
* The central directory entry is structured as follows:
|
|
53
|
+
*
|
|
54
|
+
* ```
|
|
55
|
+
* Offset Bytes Description
|
|
56
|
+
* 0 4 Central directory file header signature = 0x02014b50
|
|
57
|
+
* 4 2 Version made by
|
|
58
|
+
* 6 2 Version needed to extract (minimum)
|
|
59
|
+
* 8 2 General purpose bit flag
|
|
60
|
+
* 10 2 Compression method
|
|
61
|
+
* 12 2 File last modification time
|
|
62
|
+
* 14 2 File last modification date
|
|
63
|
+
* 16 4 CRC-32 of uncompressed data
|
|
64
|
+
* 20 4 Compressed size (or 0xffffffff for ZIP64)
|
|
65
|
+
* 24 4 Uncompressed size (or 0xffffffff for ZIP64)
|
|
66
|
+
* 28 2 File name length (n)
|
|
67
|
+
* 30 2 Extra field length (m)
|
|
68
|
+
* 32 2 File comment length (k)
|
|
69
|
+
* 34 2 Disk number where file starts (or 0xffff for ZIP64)
|
|
70
|
+
* 36 2 Internal file attributes
|
|
71
|
+
* 38 4 External file attributes
|
|
72
|
+
* 42 4 Relative offset of local file header (or 0xffffffff for ZIP64). This is the number of bytes between the start of the first disk on which the file occurs, and the start of the local file header. This allows software reading the central directory to locate the position of the file inside the ZIP file.
|
|
73
|
+
* 46 n File name
|
|
74
|
+
* 46+n m Extra field
|
|
75
|
+
* 46+n+m k File comment
|
|
76
|
+
* ```
|
|
77
|
+
*
|
|
78
|
+
* @param stream
|
|
79
|
+
* @param skipSignature
|
|
80
|
+
* @returns
|
|
81
|
+
*/
|
|
82
|
+
export declare function readCentralDirectoryEntry(stream: ReadableStream<Uint8Array>, skipSignature?: boolean): Promise<CentralDirectoryEntry | null>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compresses the given files into a ZIP archive.
|
|
3
|
+
*
|
|
4
|
+
* @param files - An async or sync iterable of files to be compressed.
|
|
5
|
+
* @returns A readable stream of the compressed ZIP archive as Uint8Array chunks.
|
|
6
|
+
*/
|
|
7
|
+
export declare function encodeZip(files: AsyncIterable<File> | Iterable<File>): ReadableStream<Uint8Array>;
|
package/zip/types.d.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export declare const FILE_HEADER_SIZE = 32;
|
|
2
|
+
export declare const SIGNATURE_FILE: 67324752;
|
|
3
|
+
export declare const SIGNATURE_CENTRAL_DIRECTORY: 33639248;
|
|
4
|
+
export declare const SIGNATURE_CENTRAL_DIRECTORY_END: 101010256;
|
|
5
|
+
export declare const SIGNATURE_DATA_DESCRIPTOR: 134695760;
|
|
6
|
+
export declare const COMPRESSION_NONE: 0;
|
|
7
|
+
export declare const COMPRESSION_DEFLATE: 8;
|
|
8
|
+
export type CompressionMethod = typeof COMPRESSION_NONE | typeof COMPRESSION_DEFLATE;
|
|
9
|
+
export type ZipEntry = FileEntry | CentralDirectoryEntry | CentralDirectoryEndEntry;
|
|
10
|
+
/**
|
|
11
|
+
* Data of the file entry header encoded in a ".zip" file.
|
|
12
|
+
*/
|
|
13
|
+
export interface FileHeader {
|
|
14
|
+
signature: typeof SIGNATURE_FILE;
|
|
15
|
+
version: number;
|
|
16
|
+
generalPurpose: number;
|
|
17
|
+
compressionMethod: CompressionMethod;
|
|
18
|
+
lastModifiedTime: number;
|
|
19
|
+
lastModifiedDate: number;
|
|
20
|
+
crc: number;
|
|
21
|
+
compressedSize: number;
|
|
22
|
+
uncompressedSize: number;
|
|
23
|
+
path: Uint8Array;
|
|
24
|
+
extra: Uint8Array;
|
|
25
|
+
}
|
|
26
|
+
export interface FileEntry extends FileHeader {
|
|
27
|
+
isDirectory: boolean;
|
|
28
|
+
bytes: Uint8Array;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Data of the central directory entry encoded in a ".zip" file.
|
|
32
|
+
*/
|
|
33
|
+
export interface CentralDirectoryEntry {
|
|
34
|
+
signature: typeof SIGNATURE_CENTRAL_DIRECTORY;
|
|
35
|
+
versionCreated: number;
|
|
36
|
+
versionNeeded: number;
|
|
37
|
+
generalPurpose: number;
|
|
38
|
+
compressionMethod: CompressionMethod;
|
|
39
|
+
lastModifiedTime: number;
|
|
40
|
+
lastModifiedDate: number;
|
|
41
|
+
crc: number;
|
|
42
|
+
compressedSize: number;
|
|
43
|
+
uncompressedSize: number;
|
|
44
|
+
diskNumber: number;
|
|
45
|
+
internalAttributes: number;
|
|
46
|
+
externalAttributes: number;
|
|
47
|
+
firstByteAt: number;
|
|
48
|
+
lastByteAt: number;
|
|
49
|
+
path: Uint8Array;
|
|
50
|
+
extra: Uint8Array;
|
|
51
|
+
fileComment: Uint8Array;
|
|
52
|
+
isDirectory: boolean;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Data of the central directory end entry encoded in a ".zip" file.
|
|
56
|
+
*/
|
|
57
|
+
export interface CentralDirectoryEndEntry {
|
|
58
|
+
signature: typeof SIGNATURE_CENTRAL_DIRECTORY_END;
|
|
59
|
+
numberOfDisks: number;
|
|
60
|
+
centralDirectoryStartDisk: number;
|
|
61
|
+
numberCentralDirectoryRecordsOnThisDisk: number;
|
|
62
|
+
numberCentralDirectoryRecords: number;
|
|
63
|
+
centralDirectorySize: number;
|
|
64
|
+
centralDirectoryOffset: number;
|
|
65
|
+
comment: Uint8Array;
|
|
66
|
+
}
|
package/.eslintrc.json
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": ["../../../.eslintrc.json"],
|
|
3
|
-
"ignorePatterns": ["!**/*"],
|
|
4
|
-
"overrides": [
|
|
5
|
-
{
|
|
6
|
-
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
|
7
|
-
"rules": {}
|
|
8
|
-
},
|
|
9
|
-
{
|
|
10
|
-
"files": ["*.ts", "*.tsx"],
|
|
11
|
-
"rules": {}
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
"files": ["*.js", "*.jsx"],
|
|
15
|
-
"rules": {}
|
|
16
|
-
}
|
|
17
|
-
]
|
|
18
|
-
}
|
package/project.json
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "php-wasm-stream-compression",
|
|
3
|
-
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
|
|
4
|
-
"sourceRoot": "packages/php-wasm/stream-compression/src",
|
|
5
|
-
"projectType": "library",
|
|
6
|
-
"targets": {
|
|
7
|
-
"build": {
|
|
8
|
-
"executor": "@nx/vite:build",
|
|
9
|
-
"outputs": ["{options.outputPath}"],
|
|
10
|
-
"options": {
|
|
11
|
-
"outputPath": "dist/packages/php-wasm/stream-compression"
|
|
12
|
-
}
|
|
13
|
-
},
|
|
14
|
-
"test": {
|
|
15
|
-
"executor": "@nx/vite:test",
|
|
16
|
-
"outputs": ["{options.reportsDirectory}"],
|
|
17
|
-
"options": {
|
|
18
|
-
"passWithNoTests": true,
|
|
19
|
-
"reportsDirectory": "../../../coverage/packages/php-wasm/stream-compression"
|
|
20
|
-
}
|
|
21
|
-
},
|
|
22
|
-
"lint": {
|
|
23
|
-
"executor": "@nx/linter:eslint",
|
|
24
|
-
"outputs": ["{options.outputFile}"],
|
|
25
|
-
"options": {
|
|
26
|
-
"lintFilePatterns": [
|
|
27
|
-
"packages/php-wasm/stream-compression/**/*.ts",
|
|
28
|
-
"packages/php-wasm/stream-compression/package.json"
|
|
29
|
-
]
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
},
|
|
33
|
-
"tags": ["scope:independent-from-php-binaries"]
|
|
34
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import '@php-wasm/node-polyfills';
|
|
2
|
-
|
|
3
|
-
export { collectBytes } from './utils/collect-bytes';
|
|
4
|
-
export { collectFile } from './utils/collect-file';
|
|
5
|
-
export { iteratorToStream } from './utils/iterator-to-stream';
|
|
6
|
-
export { StreamedFile } from './utils/streamed-file';
|
|
7
|
-
export { encodeZip, decodeZip, decodeRemoteZip } from './zip';
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { appendBytes } from '../utils/append-bytes';
|
|
2
|
-
|
|
3
|
-
describe('appendBytes', () => {
|
|
4
|
-
it('Should append the specified number of bytes', async () => {
|
|
5
|
-
const stream = new ReadableStream<Uint8Array>({
|
|
6
|
-
type: 'bytes',
|
|
7
|
-
start(controller) {
|
|
8
|
-
controller.enqueue(new Uint8Array([1, 2, 3]));
|
|
9
|
-
controller.close();
|
|
10
|
-
},
|
|
11
|
-
}).pipeThrough(appendBytes(new Uint8Array([4, 5])));
|
|
12
|
-
|
|
13
|
-
const reader = stream.getReader();
|
|
14
|
-
const result1 = await reader.read();
|
|
15
|
-
expect(result1.value).toEqual(new Uint8Array([1, 2, 3]));
|
|
16
|
-
expect(result1.done).toBe(false);
|
|
17
|
-
|
|
18
|
-
const result2 = await reader.read();
|
|
19
|
-
expect(result2.value).toEqual(new Uint8Array([4, 5]));
|
|
20
|
-
expect(result2.done).toBe(false);
|
|
21
|
-
|
|
22
|
-
const result3 = await reader.read();
|
|
23
|
-
expect(result3.done).toBe(true);
|
|
24
|
-
});
|
|
25
|
-
});
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { decodeZip } from '../zip/decode-zip';
|
|
2
|
-
import { readFile } from 'fs/promises';
|
|
3
|
-
|
|
4
|
-
describe('decodeZip', () => {
|
|
5
|
-
it('Should uncompress compress files', async () => {
|
|
6
|
-
const zipBytes = await readFile(
|
|
7
|
-
__dirname + '/fixtures/hello-dolly.zip'
|
|
8
|
-
);
|
|
9
|
-
const zipStream = decodeZip(new Blob([zipBytes]).stream());
|
|
10
|
-
|
|
11
|
-
const files = [];
|
|
12
|
-
for await (const file of zipStream) {
|
|
13
|
-
files.push(file);
|
|
14
|
-
}
|
|
15
|
-
expect(files.length).toBe(3);
|
|
16
|
-
expect(files[0].name).toBe('hello-dolly/');
|
|
17
|
-
expect(files[1].name).toBe('hello-dolly/hello.php');
|
|
18
|
-
expect(files[1].size).toBe(2593);
|
|
19
|
-
expect(files[2].name).toBe('hello-dolly/readme.txt');
|
|
20
|
-
expect(files[2].size).toBe(624);
|
|
21
|
-
});
|
|
22
|
-
});
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { collectBytes } from '../utils/collect-bytes';
|
|
2
|
-
import { encodeZip } from '../zip/encode-zip';
|
|
3
|
-
import { decodeZip } from '../zip/decode-zip';
|
|
4
|
-
|
|
5
|
-
describe('compressFiles', () => {
|
|
6
|
-
it('Should compress files into a zip archive', async () => {
|
|
7
|
-
const files: File[] = [
|
|
8
|
-
new File(
|
|
9
|
-
[new Uint8Array([1, 2, 3, 4, 5])],
|
|
10
|
-
'wp-content/plugins/hello.php'
|
|
11
|
-
),
|
|
12
|
-
new File(
|
|
13
|
-
[new Uint8Array([1, 2, 3, 4, 5])],
|
|
14
|
-
'wp-content/plugins/hello/hello.php'
|
|
15
|
-
),
|
|
16
|
-
new File(
|
|
17
|
-
[new Uint8Array([1, 2, 3, 4, 5])],
|
|
18
|
-
'wp-content/plugins/hello/hello2.php'
|
|
19
|
-
),
|
|
20
|
-
new File(
|
|
21
|
-
[new Uint8Array([1, 2, 3, 4, 5])],
|
|
22
|
-
'wp-content/plugins/hello/hello3.php'
|
|
23
|
-
),
|
|
24
|
-
];
|
|
25
|
-
|
|
26
|
-
const zipBytes = await collectBytes(
|
|
27
|
-
encodeZip(files[Symbol.iterator]())
|
|
28
|
-
);
|
|
29
|
-
const zipStream = decodeZip(new Blob([zipBytes!]).stream());
|
|
30
|
-
|
|
31
|
-
const reader = zipStream.getReader();
|
|
32
|
-
let i = 0;
|
|
33
|
-
for (i = 0; i < files.length; i++) {
|
|
34
|
-
const { value: receivedFile, done } = await reader.read();
|
|
35
|
-
const receivedBytes = new Uint8Array(
|
|
36
|
-
await receivedFile!.arrayBuffer()
|
|
37
|
-
);
|
|
38
|
-
const expectedBytes = new Uint8Array(await files[i].arrayBuffer());
|
|
39
|
-
expect(receivedBytes).toEqual(expectedBytes);
|
|
40
|
-
expect(done).toBe(false);
|
|
41
|
-
}
|
|
42
|
-
expect(i).toBe(files.length);
|
|
43
|
-
|
|
44
|
-
const { done } = await reader.read();
|
|
45
|
-
expect(done).toBe(true);
|
|
46
|
-
});
|
|
47
|
-
});
|
|
Binary file
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { prependBytes } from '../utils/prepend-bytes';
|
|
2
|
-
|
|
3
|
-
describe('prependBytes', () => {
|
|
4
|
-
it('Should prepend the specified number of bytes', async () => {
|
|
5
|
-
const stream = new ReadableStream<Uint8Array>({
|
|
6
|
-
type: 'bytes',
|
|
7
|
-
start(controller) {
|
|
8
|
-
controller.enqueue(new Uint8Array([4, 5]));
|
|
9
|
-
controller.close();
|
|
10
|
-
},
|
|
11
|
-
}).pipeThrough(prependBytes(new Uint8Array([1, 2, 3])));
|
|
12
|
-
|
|
13
|
-
const reader = stream.getReader();
|
|
14
|
-
const result1 = await reader.read();
|
|
15
|
-
expect(result1.value).toEqual(new Uint8Array([1, 2, 3]));
|
|
16
|
-
expect(result1.done).toBe(false);
|
|
17
|
-
|
|
18
|
-
const result2 = await reader.read();
|
|
19
|
-
expect(result2.value).toEqual(new Uint8Array([4, 5]));
|
|
20
|
-
expect(result2.done).toBe(false);
|
|
21
|
-
|
|
22
|
-
const result3 = await reader.read();
|
|
23
|
-
expect(result3.done).toBe(true);
|
|
24
|
-
});
|
|
25
|
-
});
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { skipFirstBytes } from '../utils/skip-first-bytes';
|
|
2
|
-
|
|
3
|
-
describe('skipFirstBytes', () => {
|
|
4
|
-
it('Should skip the specified number of bytes', async () => {
|
|
5
|
-
const stream = new ReadableStream<Uint8Array>({
|
|
6
|
-
type: 'bytes',
|
|
7
|
-
start(controller) {
|
|
8
|
-
controller.enqueue(new Uint8Array([1, 2, 3, 4, 5]));
|
|
9
|
-
controller.close();
|
|
10
|
-
},
|
|
11
|
-
}).pipeThrough(skipFirstBytes(3));
|
|
12
|
-
|
|
13
|
-
const reader = stream.getReader();
|
|
14
|
-
const result1 = await reader.read();
|
|
15
|
-
expect(result1.value).toEqual(new Uint8Array([4, 5]));
|
|
16
|
-
expect(result1.done).toBe(false);
|
|
17
|
-
|
|
18
|
-
const result2 = await reader.read();
|
|
19
|
-
expect(result2.done).toBe(true);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it('Should skip the specified number of bytes across multiple pulls', async () => {
|
|
23
|
-
const stream = new ReadableStream<Uint8Array>({
|
|
24
|
-
type: 'bytes',
|
|
25
|
-
start(controller) {
|
|
26
|
-
controller.enqueue(new Uint8Array([1]));
|
|
27
|
-
controller.enqueue(new Uint8Array([2, 3]));
|
|
28
|
-
controller.enqueue(new Uint8Array([4, 5, 6]));
|
|
29
|
-
controller.close();
|
|
30
|
-
},
|
|
31
|
-
}).pipeThrough(skipFirstBytes(4));
|
|
32
|
-
|
|
33
|
-
const reader = stream.getReader();
|
|
34
|
-
const result1 = await reader.read();
|
|
35
|
-
expect(result1.value).toEqual(new Uint8Array([5, 6]));
|
|
36
|
-
expect(result1.done).toBe(false);
|
|
37
|
-
|
|
38
|
-
const result2 = await reader.read();
|
|
39
|
-
expect(result2.done).toBe(true);
|
|
40
|
-
});
|
|
41
|
-
});
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { skipLastBytes } from '../utils/skip-last-bytes';
|
|
2
|
-
|
|
3
|
-
describe('skipLastBytes', () => {
|
|
4
|
-
it('Should skip the specified number of bytes', async () => {
|
|
5
|
-
const stream = new ReadableStream<Uint8Array>({
|
|
6
|
-
type: 'bytes',
|
|
7
|
-
start(controller) {
|
|
8
|
-
controller.enqueue(new Uint8Array([1, 2, 3, 4, 5]));
|
|
9
|
-
controller.enqueue(new Uint8Array([6, 7]));
|
|
10
|
-
controller.enqueue(new Uint8Array([8, 9]));
|
|
11
|
-
controller.close();
|
|
12
|
-
},
|
|
13
|
-
}).pipeThrough(skipLastBytes(3, 9));
|
|
14
|
-
|
|
15
|
-
const reader = stream.getReader();
|
|
16
|
-
const result1 = await reader.read();
|
|
17
|
-
expect(result1.value).toEqual(new Uint8Array([1, 2, 3, 4, 5]));
|
|
18
|
-
expect(result1.done).toBe(false);
|
|
19
|
-
|
|
20
|
-
const result2 = await reader.read();
|
|
21
|
-
expect(result2.value).toEqual(new Uint8Array([6]));
|
|
22
|
-
expect(result2.done).toBe(false);
|
|
23
|
-
|
|
24
|
-
const result3 = await reader.read();
|
|
25
|
-
expect(result3.done).toBe(true);
|
|
26
|
-
});
|
|
27
|
-
});
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Appends bytes to a stream.
|
|
3
|
-
*
|
|
4
|
-
* @param bytes The bytes to append.
|
|
5
|
-
* @returns A transform stream that will append the specified bytes.
|
|
6
|
-
*/
|
|
7
|
-
export function appendBytes(bytes: Uint8Array) {
|
|
8
|
-
return new TransformStream<Uint8Array, Uint8Array>({
|
|
9
|
-
async transform(chunk, controller) {
|
|
10
|
-
controller.enqueue(chunk);
|
|
11
|
-
},
|
|
12
|
-
async flush(controller) {
|
|
13
|
-
controller.enqueue(bytes);
|
|
14
|
-
},
|
|
15
|
-
});
|
|
16
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { concatBytes } from './concat-bytes';
|
|
2
|
-
import { limitBytes } from './limit-bytes';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Collects the contents of the entire stream into a single Uint8Array.
|
|
6
|
-
*
|
|
7
|
-
* @param stream The stream to collect.
|
|
8
|
-
* @param bytes Optional. The number of bytes to read from the stream.
|
|
9
|
-
* @returns The string contents of the stream.
|
|
10
|
-
*/
|
|
11
|
-
export async function collectBytes(
|
|
12
|
-
stream: ReadableStream<Uint8Array>,
|
|
13
|
-
bytes?: number
|
|
14
|
-
) {
|
|
15
|
-
if (bytes !== undefined) {
|
|
16
|
-
stream = limitBytes(stream, bytes);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
return await stream
|
|
20
|
-
.pipeThrough(concatBytes(bytes))
|
|
21
|
-
.getReader()
|
|
22
|
-
.read()
|
|
23
|
-
.then(({ value }) => value!);
|
|
24
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { collectBytes } from './collect-bytes';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Collects the contents of the entire stream into a single File object.
|
|
5
|
-
*
|
|
6
|
-
* @param stream The stream to collect.
|
|
7
|
-
* @param fileName The name of the file
|
|
8
|
-
* @returns The string contents of the stream.
|
|
9
|
-
*/
|
|
10
|
-
export async function collectFile(
|
|
11
|
-
fileName: string,
|
|
12
|
-
stream: ReadableStream<Uint8Array>
|
|
13
|
-
) {
|
|
14
|
-
// @TODO: use StreamingFile
|
|
15
|
-
return new File([await collectBytes(stream)], fileName);
|
|
16
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { concatString } from './concat-string';
|
|
2
|
-
import { limitBytes } from './limit-bytes';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Collects the contents of the entire stream into a single string.
|
|
6
|
-
*
|
|
7
|
-
* @param stream The stream to collect.
|
|
8
|
-
* @param bytes Optional. The number of bytes to read from the stream.
|
|
9
|
-
* @returns The string contents of the stream.
|
|
10
|
-
*/
|
|
11
|
-
export async function collectString(
|
|
12
|
-
stream: ReadableStream<Uint8Array>,
|
|
13
|
-
bytes?: number
|
|
14
|
-
) {
|
|
15
|
-
if (bytes !== undefined) {
|
|
16
|
-
stream = limitBytes(stream, bytes);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
return await stream
|
|
20
|
-
.pipeThrough(new TextDecoderStream())
|
|
21
|
-
.pipeThrough(concatString())
|
|
22
|
-
.getReader()
|
|
23
|
-
.read()
|
|
24
|
-
.then(({ value }) => value);
|
|
25
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { concatUint8Array } from './concat-uint8-array';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Concatenates the contents of the stream into a single Uint8Array.
|
|
5
|
-
*
|
|
6
|
-
* @param totalBytes Optional. The number of bytes to concatenate. Used to
|
|
7
|
-
* pre-allocate the buffer. If not provided, the buffer will
|
|
8
|
-
* be dynamically resized as needed.
|
|
9
|
-
* @returns A stream that will emit a single UInt8Array entry before closing.
|
|
10
|
-
*/
|
|
11
|
-
export function concatBytes(totalBytes?: number) {
|
|
12
|
-
if (totalBytes === undefined) {
|
|
13
|
-
let acc = new Uint8Array();
|
|
14
|
-
return new TransformStream<Uint8Array, Uint8Array>({
|
|
15
|
-
transform(chunk) {
|
|
16
|
-
acc = concatUint8Array(acc, chunk);
|
|
17
|
-
},
|
|
18
|
-
|
|
19
|
-
flush(controller) {
|
|
20
|
-
controller.enqueue(acc);
|
|
21
|
-
},
|
|
22
|
-
});
|
|
23
|
-
} else {
|
|
24
|
-
const buffer = new ArrayBuffer(totalBytes || 0);
|
|
25
|
-
let offset = 0;
|
|
26
|
-
return new TransformStream<Uint8Array, Uint8Array>({
|
|
27
|
-
transform(chunk) {
|
|
28
|
-
const view = new Uint8Array(buffer);
|
|
29
|
-
view.set(chunk, offset);
|
|
30
|
-
offset += chunk.byteLength;
|
|
31
|
-
},
|
|
32
|
-
|
|
33
|
-
flush(controller) {
|
|
34
|
-
controller.enqueue(new Uint8Array(buffer));
|
|
35
|
-
},
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Concatenate all chunks into a single string.
|
|
3
|
-
*
|
|
4
|
-
* @returns A stream that will emit a single string entry before closing.
|
|
5
|
-
*/
|
|
6
|
-
export function concatString() {
|
|
7
|
-
const chunks: string[] = [];
|
|
8
|
-
return new TransformStream<string, string>({
|
|
9
|
-
transform(chunk) {
|
|
10
|
-
chunks.push(chunk);
|
|
11
|
-
},
|
|
12
|
-
|
|
13
|
-
flush(controller) {
|
|
14
|
-
controller.enqueue(chunks.join(''));
|
|
15
|
-
},
|
|
16
|
-
});
|
|
17
|
-
}
|