@node-projects/jszip 4.0.0

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.
@@ -0,0 +1,21 @@
1
+ import { GenericWorker } from "./GenericWorker.js";
2
+ import crc32 from "../crc32.js";
3
+
4
+ export class Crc32Probe extends GenericWorker {
5
+ /**
6
+ * A worker which calculate the crc32 of the data flowing through.
7
+ * @constructor
8
+ */
9
+ constructor() {
10
+ super("Crc32Probe");
11
+ this.withStreamInfo("crc32", 0);
12
+ }
13
+
14
+ /**
15
+ * @see GenericWorker.processChunk
16
+ */
17
+ processChunk(chunk) {
18
+ this.streamInfo.crc32 = crc32(chunk.data, this.streamInfo.crc32 || 0);
19
+ this.push(chunk);
20
+ }
21
+ }
@@ -0,0 +1,25 @@
1
+ import { GenericWorker } from "./GenericWorker.js";
2
+
3
+ export class DataLengthProbe extends GenericWorker {
4
+ /**
5
+ * A worker which calculate the total length of the data flowing through.
6
+ * @constructor
7
+ * @param {String} propName the name used to expose the length
8
+ */
9
+ constructor(propName) {
10
+ super("DataLengthProbe for " + propName);
11
+ this.propName = propName;
12
+ this.withStreamInfo(propName, 0);
13
+ }
14
+
15
+ /**
16
+ * @see GenericWorker.processChunk
17
+ */
18
+ processChunk(chunk) {
19
+ if (chunk) {
20
+ var length = this.streamInfo[this.propName] || 0;
21
+ this.streamInfo[this.propName] = length + chunk.data.length;
22
+ }
23
+ GenericWorker.prototype.processChunk.call(this, chunk);
24
+ };
25
+ }
@@ -0,0 +1,112 @@
1
+ import utils from "../utils.js";
2
+ import { GenericWorker } from "./GenericWorker.js";
3
+
4
+ // the size of the generated chunks
5
+ // TODO expose this as a public variable
6
+ const DEFAULT_BLOCK_SIZE = 16 * 1024;
7
+
8
+ export class DataWorker extends GenericWorker {
9
+ /**
10
+ * A worker that reads a content and emits chunks.
11
+ * @constructor
12
+ * @param {Promise} dataP the promise of the data to split
13
+ */
14
+ constructor(dataP) {
15
+ super("DataWorker");
16
+ var self = this;
17
+ this.dataIsReady = false;
18
+ this.index = 0;
19
+ this.max = 0;
20
+ this.data = null;
21
+ this.type = "";
22
+
23
+ this._tickScheduled = false;
24
+
25
+ dataP.then(function (data) {
26
+ self.dataIsReady = true;
27
+ self.data = data;
28
+ self.max = data && data.length || 0;
29
+ self.type = utils.getTypeOf(data);
30
+ if (!self.isPaused) {
31
+ self._tickAndRepeat();
32
+ }
33
+ }, function (e) {
34
+ self.error(e);
35
+ });
36
+ }
37
+
38
+ /**
39
+ * @see GenericWorker.cleanUp
40
+ */
41
+ cleanUp() {
42
+ GenericWorker.prototype.cleanUp.call(this);
43
+ this.data = null;
44
+ };
45
+
46
+ /**
47
+ * @see GenericWorker.resume
48
+ */
49
+ resume() {
50
+ if (!GenericWorker.prototype.resume.call(this)) {
51
+ return false;
52
+ }
53
+
54
+ if (!this._tickScheduled && this.dataIsReady) {
55
+ this._tickScheduled = true;
56
+ utils.delay(this._tickAndRepeat, [], this);
57
+ }
58
+ return true;
59
+ };
60
+
61
+ /**
62
+ * Trigger a tick a schedule an other call to this function.
63
+ */
64
+ _tickAndRepeat() {
65
+ this._tickScheduled = false;
66
+ if (this.isPaused || this.isFinished) {
67
+ return;
68
+ }
69
+ this._tick();
70
+ if (!this.isFinished) {
71
+ utils.delay(this._tickAndRepeat, [], this);
72
+ this._tickScheduled = true;
73
+ }
74
+ };
75
+
76
+ /**
77
+ * Read and push a chunk.
78
+ */
79
+ _tick() {
80
+
81
+ if (this.isPaused || this.isFinished) {
82
+ return false;
83
+ }
84
+
85
+ var size = DEFAULT_BLOCK_SIZE;
86
+ var data = null, nextIndex = Math.min(this.max, this.index + size);
87
+ if (this.index >= this.max) {
88
+ // EOF
89
+ return this.end();
90
+ } else {
91
+ switch (this.type) {
92
+ case "string":
93
+ data = this.data.substring(this.index, nextIndex);
94
+ break;
95
+ case "uint8array":
96
+ data = this.data.subarray(this.index, nextIndex);
97
+ break;
98
+ case "array":
99
+ case "nodebuffer":
100
+ data = this.data.slice(this.index, nextIndex);
101
+ break;
102
+ }
103
+ this.index = nextIndex;
104
+ return this.push({
105
+ data: data,
106
+ meta: {
107
+ percent: this.max ? this.index / this.max * 100 : 0
108
+ }
109
+ });
110
+ }
111
+ };
112
+ }
@@ -0,0 +1,273 @@
1
+ /**
2
+ * A worker that does nothing but passing chunks to the next one. This is like
3
+ * a nodejs stream but with some differences. On the good side :
4
+ * - it works on IE 6-9 without any issue / polyfill
5
+ * - it weights less than the full dependencies bundled with browserify
6
+ * - it forwards errors (no need to declare an error handler EVERYWHERE)
7
+ *
8
+ * A chunk is an object with 2 attributes : `meta` and `data`. The former is an
9
+ * object containing anything (`percent` for example), see each worker for more
10
+ * details. The latter is the real data (String, Uint8Array, etc).
11
+ *
12
+ * @constructor
13
+ * @param {String} name the name of the stream (mainly used for debugging purposes)
14
+ */
15
+ export class GenericWorker {
16
+
17
+ constructor(name) {
18
+ // the name of the worker
19
+ this.name = name || "default";
20
+ // an object containing metadata about the workers chain
21
+ this.streamInfo = {};
22
+ // an error which happened when the worker was paused
23
+ this.generatedError = null;
24
+ // an object containing metadata to be merged by this worker into the general metadata
25
+ this.extraStreamInfo = {};
26
+ // true if the stream is paused (and should not do anything), false otherwise
27
+ this.isPaused = true;
28
+ // true if the stream is finished (and should not do anything), false otherwise
29
+ this.isFinished = false;
30
+ // true if the stream is locked to prevent further structure updates (pipe), false otherwise
31
+ this.isLocked = false;
32
+ // the event listeners
33
+ this._listeners = {
34
+ "data": [],
35
+ "end": [],
36
+ "error": []
37
+ };
38
+ // the previous worker, if any
39
+ this.previous = null;
40
+ }
41
+
42
+ /**
43
+ * Push a chunk to the next workers.
44
+ * @param {Object} chunk the chunk to push
45
+ */
46
+ push(chunk) {
47
+ this.emit("data", chunk);
48
+ }
49
+
50
+ /**
51
+ * End the stream.
52
+ * @return {Boolean} true if this call ended the worker, false otherwise.
53
+ */
54
+ end() {
55
+ if (this.isFinished) {
56
+ return false;
57
+ }
58
+
59
+ this.flush();
60
+ try {
61
+ this.emit("end");
62
+ this.cleanUp();
63
+ this.isFinished = true;
64
+ } catch (e) {
65
+ this.emit("error", e);
66
+ }
67
+ return true;
68
+ }
69
+
70
+ /**
71
+ * End the stream with an error.
72
+ * @param {Error} e the error which caused the premature end.
73
+ * @return {Boolean} true if this call ended the worker with an error, false otherwise.
74
+ */
75
+ error(e) {
76
+ if (this.isFinished) {
77
+ return false;
78
+ }
79
+
80
+ if (this.isPaused) {
81
+ this.generatedError = e;
82
+ } else {
83
+ this.isFinished = true;
84
+
85
+ this.emit("error", e);
86
+
87
+ // in the workers chain exploded in the middle of the chain,
88
+ // the error event will go downward but we also need to notify
89
+ // workers upward that there has been an error.
90
+ if (this.previous) {
91
+ this.previous.error(e);
92
+ }
93
+
94
+ this.cleanUp();
95
+ }
96
+ return true;
97
+ }
98
+
99
+ /**
100
+ * Add a callback on an event.
101
+ * @param {String} name the name of the event (data, end, error)
102
+ * @param {Function} listener the function to call when the event is triggered
103
+ * @return {GenericWorker} the current object for chainability
104
+ */
105
+ on(name, listener) {
106
+ this._listeners[name].push(listener);
107
+ return this;
108
+ }
109
+
110
+ /**
111
+ * Clean any references when a worker is ending.
112
+ */
113
+ cleanUp() {
114
+ this.streamInfo = this.generatedError = this.extraStreamInfo = null;
115
+ this._listeners = [];
116
+ }
117
+
118
+ /**
119
+ * Trigger an event. This will call registered callback with the provided arg.
120
+ * @param {String} name the name of the event (data, end, error)
121
+ * @param {Object} arg the argument to call the callback with.
122
+ */
123
+ emit(name, arg) {
124
+ if (this._listeners[name]) {
125
+ for (var i = 0; i < this._listeners[name].length; i++) {
126
+ this._listeners[name][i].call(this, arg);
127
+ }
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Chain a worker with an other.
133
+ * @param {Worker} next the worker receiving events from the current one.
134
+ * @return {worker} the next worker for chainability
135
+ */
136
+ pipe(next) {
137
+ return next.registerPrevious(this);
138
+ }
139
+
140
+ /**
141
+ * Same as `pipe` in the other direction.
142
+ * Using an API with `pipe(next)` is very easy.
143
+ * Implementing the API with the point of view of the next one registering
144
+ * a source is easier, see the ZipFileWorker.
145
+ * @param {Worker} previous the previous worker, sending events to this one
146
+ * @return {Worker} the current worker for chainability
147
+ */
148
+ registerPrevious(previous) {
149
+ if (this.isLocked) {
150
+ throw new Error("The stream '" + this + "' has already been used.");
151
+ }
152
+
153
+ // sharing the streamInfo...
154
+ this.streamInfo = previous.streamInfo;
155
+ // ... and adding our own bits
156
+ this.mergeStreamInfo();
157
+ this.previous = previous;
158
+ var self = this;
159
+ previous.on("data", function (chunk) {
160
+ self.processChunk(chunk);
161
+ });
162
+ previous.on("end", function () {
163
+ self.end();
164
+ });
165
+ previous.on("error", function (e) {
166
+ self.error(e);
167
+ });
168
+ return this;
169
+ }
170
+
171
+ /**
172
+ * Pause the stream so it doesn't send events anymore.
173
+ * @return {Boolean} true if this call paused the worker, false otherwise.
174
+ */
175
+ pause() {
176
+ if (this.isPaused || this.isFinished) {
177
+ return false;
178
+ }
179
+ this.isPaused = true;
180
+
181
+ if (this.previous) {
182
+ this.previous.pause();
183
+ }
184
+ return true;
185
+ }
186
+
187
+ /**
188
+ * Resume a paused stream.
189
+ * @return {Boolean} true if this call resumed the worker, false otherwise.
190
+ */
191
+ resume() {
192
+ if (!this.isPaused || this.isFinished) {
193
+ return false;
194
+ }
195
+ this.isPaused = false;
196
+
197
+ // if true, the worker tried to resume but failed
198
+ var withError = false;
199
+ if (this.generatedError) {
200
+ this.error(this.generatedError);
201
+ withError = true;
202
+ }
203
+ if (this.previous) {
204
+ this.previous.resume();
205
+ }
206
+
207
+ return !withError;
208
+ }
209
+
210
+ /**
211
+ * Flush any remaining bytes as the stream is ending.
212
+ */
213
+ flush() { }
214
+
215
+ /**
216
+ * Process a chunk. This is usually the method overridden.
217
+ * @param {Object} chunk the chunk to process.
218
+ */
219
+ processChunk(chunk) {
220
+ this.push(chunk);
221
+ }
222
+
223
+ /**
224
+ * Add a key/value to be added in the workers chain streamInfo once activated.
225
+ * @param {String} key the key to use
226
+ * @param {Object} value the associated value
227
+ * @return {Worker} the current worker for chainability
228
+ */
229
+ withStreamInfo(key, value) {
230
+ this.extraStreamInfo[key] = value;
231
+ this.mergeStreamInfo();
232
+ return this;
233
+ }
234
+
235
+ /**
236
+ * Merge this worker's streamInfo into the chain's streamInfo.
237
+ */
238
+ mergeStreamInfo() {
239
+ for (var key in this.extraStreamInfo) {
240
+ if (!Object.prototype.hasOwnProperty.call(this.extraStreamInfo, key)) {
241
+ continue;
242
+ }
243
+ this.streamInfo[key] = this.extraStreamInfo[key];
244
+ }
245
+ }
246
+
247
+ /**
248
+ * Lock the stream to prevent further updates on the workers chain.
249
+ * After calling this method, all calls to pipe will fail.
250
+ */
251
+ lock() {
252
+ if (this.isLocked) {
253
+ throw new Error("The stream '" + this + "' has already been used.");
254
+ }
255
+ this.isLocked = true;
256
+ if (this.previous) {
257
+ this.previous.lock();
258
+ }
259
+ }
260
+
261
+ /**
262
+ *
263
+ * Pretty print the workers chain.
264
+ */
265
+ toString() {
266
+ var me = "Worker " + this.name;
267
+ if (this.previous) {
268
+ return this.previous + " -> " + me;
269
+ } else {
270
+ return me;
271
+ }
272
+ }
273
+ }
@@ -0,0 +1,214 @@
1
+ import utils from "../utils.js";
2
+ import { ConvertWorker } from "./ConvertWorker.js";
3
+ import { GenericWorker } from "./GenericWorker.js";
4
+ import base64 from "../base64.js";
5
+ import support from "../support.js";
6
+
7
+ /*
8
+ var NodejsStreamOutputAdapter = null;
9
+ if (support.nodestream) {
10
+ try {
11
+ NodejsStreamOutputAdapter = require("../nodejs/NodejsStreamOutputAdapter");
12
+ } catch (e) {
13
+ // ignore
14
+ }
15
+ }
16
+ */
17
+
18
+ /**
19
+ * Apply the final transformation of the data. If the user wants a Blob for
20
+ * example, it's easier to work with an U8intArray and finally do the
21
+ * ArrayBuffer/Blob conversion.
22
+ * @param {String} type the name of the final type
23
+ * @param {String|Uint8Array|Buffer} content the content to transform
24
+ * @param {String} mimeType the mime type of the content, if applicable.
25
+ * @return {String|Uint8Array|ArrayBuffer|Buffer|Blob} the content in the right format.
26
+ */
27
+ function transformZipOutput(type, content, mimeType) {
28
+ switch (type) {
29
+ case "blob":
30
+ return utils.newBlob(utils.transformTo("arraybuffer", content), mimeType);
31
+ case "base64":
32
+ return base64.encode(content);
33
+ default:
34
+ return utils.transformTo(type, content);
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Concatenate an array of data of the given type.
40
+ * @param {String} type the type of the data in the given array.
41
+ * @param {Array} dataArray the array containing the data chunks to concatenate
42
+ * @return {String|Uint8Array|Buffer} the concatenated data
43
+ * @throws Error if the asked type is unsupported
44
+ */
45
+ function concat(type, dataArray) {
46
+ var i, index = 0, res = null, totalLength = 0;
47
+ for (i = 0; i < dataArray.length; i++) {
48
+ totalLength += dataArray[i].length;
49
+ }
50
+ switch (type) {
51
+ case "string":
52
+ return dataArray.join("");
53
+ case "array":
54
+ return Array.prototype.concat.apply([], dataArray);
55
+ case "uint8array":
56
+ res = new Uint8Array(totalLength);
57
+ for (i = 0; i < dataArray.length; i++) {
58
+ res.set(dataArray[i], index);
59
+ index += dataArray[i].length;
60
+ }
61
+ return res;
62
+ case "nodebuffer":
63
+ return Buffer.concat(dataArray);
64
+ default:
65
+ throw new Error("concat : unsupported type '" + type + "'");
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Listen a StreamHelper, accumulate its content and concatenate it into a
71
+ * complete block.
72
+ * @param {StreamHelper} helper the helper to use.
73
+ * @param {Function} updateCallback a callback called on each update. Called
74
+ * with one arg :
75
+ * - the metadata linked to the update received.
76
+ * @return Promise the promise for the accumulation.
77
+ */
78
+ function accumulate(helper, updateCallback) {
79
+ return new Promise(function (resolve, reject) {
80
+ var dataArray = [];
81
+ var chunkType = helper._internalType,
82
+ resultType = helper._outputType,
83
+ mimeType = helper._mimeType;
84
+ helper
85
+ .on("data", function (data, meta) {
86
+ dataArray.push(data);
87
+ if (updateCallback) {
88
+ updateCallback(meta);
89
+ }
90
+ })
91
+ .on("error", function (err) {
92
+ dataArray = [];
93
+ reject(err);
94
+ })
95
+ .on("end", function () {
96
+ try {
97
+ var result = transformZipOutput(resultType, concat(chunkType, dataArray), mimeType);
98
+ resolve(result);
99
+ } catch (e) {
100
+ reject(e);
101
+ }
102
+ dataArray = [];
103
+ })
104
+ .resume();
105
+ });
106
+ }
107
+
108
+ export class StreamHelper {
109
+ /**
110
+ * An helper to easily use workers outside of JSZip.
111
+ * @constructor
112
+ * @param {Worker} worker the worker to wrap
113
+ * @param {String} outputType the type of data expected by the use
114
+ * @param {String} mimeType the mime type of the content, if applicable.
115
+ */
116
+ constructor(worker, outputType, mimeType) {
117
+ var internalType = outputType;
118
+ switch (outputType) {
119
+ case "blob":
120
+ case "arraybuffer":
121
+ internalType = "uint8array";
122
+ break;
123
+ case "base64":
124
+ internalType = "string";
125
+ break;
126
+ }
127
+
128
+ try {
129
+ // the type used internally
130
+ this._internalType = internalType;
131
+ // the type used to output results
132
+ this._outputType = outputType;
133
+ // the mime type
134
+ this._mimeType = mimeType;
135
+ utils.checkSupport(internalType);
136
+ this._worker = worker.pipe(new ConvertWorker(internalType));
137
+ // the last workers can be rewired without issues but we need to
138
+ // prevent any updates on previous workers.
139
+ worker.lock();
140
+ } catch (e) {
141
+ this._worker = new GenericWorker("error");
142
+ this._worker.error(e);
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Listen a StreamHelper, accumulate its content and concatenate it into a
148
+ * complete block.
149
+ * @param {Function} updateCb the update callback.
150
+ * @return Promise the promise for the accumulation.
151
+ */
152
+ accumulate(updateCb) {
153
+ return accumulate(this, updateCb);
154
+ }
155
+
156
+ /**
157
+ * Add a listener on an event triggered on a stream.
158
+ * @param {String} evt the name of the event
159
+ * @param {Function} fn the listener
160
+ * @return {StreamHelper} the current helper.
161
+ */
162
+ on(evt, fn) {
163
+ var self = this;
164
+
165
+ if (evt === "data") {
166
+ this._worker.on(evt, function (chunk) {
167
+ fn.call(self, chunk.data, chunk.meta);
168
+ });
169
+ } else {
170
+ this._worker.on(evt, function () {
171
+ utils.delay(fn, arguments, self);
172
+ });
173
+ }
174
+ return this;
175
+ }
176
+
177
+ /**
178
+ * Resume the flow of chunks.
179
+ * @return {StreamHelper} the current helper.
180
+ */
181
+ resume() {
182
+ utils.delay(this._worker.resume, [], this._worker);
183
+ return this;
184
+ }
185
+
186
+ /**
187
+ * Pause the flow of chunks.
188
+ * @return {StreamHelper} the current helper.
189
+ */
190
+ pause() {
191
+ this._worker.pause();
192
+ return this;
193
+ }
194
+
195
+ /**
196
+ * Return a nodejs stream for this helper.
197
+ * @param {Function} updateCb the update callback.
198
+ * @return {NodejsStreamOutputAdapter} the nodejs stream.
199
+ */
200
+ toNodejsStream(updateCb) {
201
+ utils.checkSupport("nodestream");
202
+ if (this._outputType !== "nodebuffer") {
203
+ // an object stream containing blob/arraybuffer/uint8array/string
204
+ // is strange and I don't know if it would be useful.
205
+ // I you find this comment and have a good usecase, please open a
206
+ // bug report !
207
+ throw new Error(this._outputType + " is not supported by this method");
208
+ }
209
+
210
+ return new NodejsStreamOutputAdapter(this, {
211
+ objectMode: this._outputType !== "nodebuffer"
212
+ }, updateCb);
213
+ }
214
+ }
package/lib/support.js ADDED
@@ -0,0 +1,42 @@
1
+ export const base64 = true;
2
+ export const array = true;
3
+ export const string = true;
4
+ export const arraybuffer = typeof ArrayBuffer !== "undefined" && typeof Uint8Array !== "undefined";
5
+ export const nodebuffer = typeof Buffer !== "undefined";
6
+ // contains true if JSZip can read/generate Uint8Array, false otherwise.
7
+ export const uint8array = typeof Uint8Array !== "undefined";
8
+
9
+ export let blob = false;
10
+ if (typeof ArrayBuffer === "undefined") {
11
+ blob = false;
12
+ }
13
+ else {
14
+ var buffer = new ArrayBuffer(0);
15
+ try {
16
+ blob = new Blob([buffer], {
17
+ type: "application/zip"
18
+ }).size === 0;
19
+ }
20
+ catch (e) {
21
+ try {
22
+ var Builder = self.BlobBuilder || self.WebKitBlobBuilder || self.MozBlobBuilder || self.MSBlobBuilder;
23
+ var builder = new Builder();
24
+ builder.append(buffer);
25
+ blob = builder.getBlob("application/zip").size === 0;
26
+ }
27
+ catch (e) {
28
+ blob = false;
29
+ }
30
+ }
31
+ }
32
+
33
+ export let nodestream = false;
34
+ try {
35
+ nodestream = !!require("readable-stream").Readable;
36
+ } catch(e) {
37
+ nodestream = false;
38
+ }
39
+
40
+ export default {
41
+ base64, array, string, arraybuffer, nodebuffer, uint8array, blob, nodestream
42
+ }