1 | this.workbox = this.workbox || {};
|
2 | this.workbox.streams = (function (exports, logger_js, assert_js, Deferred_js, canConstructReadableStream_js) {
|
3 | ;
|
4 |
|
5 | try {
|
6 | self['workbox:streams:5.1.4'] && _();
|
7 | } catch (e) {}
|
8 |
|
9 | /*
|
10 | Copyright 2018 Google LLC
|
11 |
|
12 | Use of this source code is governed by an MIT-style
|
13 | license that can be found in the LICENSE file or at
|
14 | https://opensource.org/licenses/MIT.
|
15 | */
|
16 | /**
|
17 | * Takes either a Response, a ReadableStream, or a
|
18 | * [BodyInit](https://fetch.spec.whatwg.org/#bodyinit) and returns the
|
19 | * ReadableStreamReader object associated with it.
|
20 | *
|
21 | * @param {module:workbox-streams.StreamSource} source
|
22 | * @return {ReadableStreamReader}
|
23 | * @private
|
24 | */
|
25 |
|
26 | function _getReaderFromSource(source) {
|
27 | if (source instanceof Response) {
|
28 | return source.body.getReader();
|
29 | }
|
30 |
|
31 | if (source instanceof ReadableStream) {
|
32 | return source.getReader();
|
33 | }
|
34 |
|
35 | return new Response(source).body.getReader();
|
36 | }
|
37 | /**
|
38 | * Takes multiple source Promises, each of which could resolve to a Response, a
|
39 | * ReadableStream, or a [BodyInit](https://fetch.spec.whatwg.org/#bodyinit).
|
40 | *
|
41 | * Returns an object exposing a ReadableStream with each individual stream's
|
42 | * data returned in sequence, along with a Promise which signals when the
|
43 | * stream is finished (useful for passing to a FetchEvent's waitUntil()).
|
44 | *
|
45 | * @param {Array<Promise<module:workbox-streams.StreamSource>>} sourcePromises
|
46 | * @return {Object<{done: Promise, stream: ReadableStream}>}
|
47 | *
|
48 | * @memberof module:workbox-streams
|
49 | */
|
50 |
|
51 |
|
52 | function concatenate(sourcePromises) {
|
53 | {
|
54 | assert_js.assert.isArray(sourcePromises, {
|
55 | moduleName: 'workbox-streams',
|
56 | funcName: 'concatenate',
|
57 | paramName: 'sourcePromises'
|
58 | });
|
59 | }
|
60 |
|
61 | const readerPromises = sourcePromises.map(sourcePromise => {
|
62 | return Promise.resolve(sourcePromise).then(source => {
|
63 | return _getReaderFromSource(source);
|
64 | });
|
65 | });
|
66 | const streamDeferred = new Deferred_js.Deferred();
|
67 | let i = 0;
|
68 | const logMessages = [];
|
69 | const stream = new ReadableStream({
|
70 | pull(controller) {
|
71 | return readerPromises[i].then(reader => reader.read()).then(result => {
|
72 | if (result.done) {
|
73 | {
|
74 | logMessages.push(['Reached the end of source:', sourcePromises[i]]);
|
75 | }
|
76 |
|
77 | i++;
|
78 |
|
79 | if (i >= readerPromises.length) {
|
80 | // Log all the messages in the group at once in a single group.
|
81 | {
|
82 | logger_js.logger.groupCollapsed(`Concatenating ${readerPromises.length} sources.`);
|
83 |
|
84 | for (const message of logMessages) {
|
85 | if (Array.isArray(message)) {
|
86 | logger_js.logger.log(...message);
|
87 | } else {
|
88 | logger_js.logger.log(message);
|
89 | }
|
90 | }
|
91 |
|
92 | logger_js.logger.log('Finished reading all sources.');
|
93 | logger_js.logger.groupEnd();
|
94 | }
|
95 |
|
96 | controller.close();
|
97 | streamDeferred.resolve();
|
98 | return;
|
99 | } // The `pull` method is defined because we're inside it.
|
100 |
|
101 |
|
102 | return this.pull(controller);
|
103 | } else {
|
104 | controller.enqueue(result.value);
|
105 | }
|
106 | }).catch(error => {
|
107 | {
|
108 | logger_js.logger.error('An error occurred:', error);
|
109 | }
|
110 |
|
111 | streamDeferred.reject(error);
|
112 | throw error;
|
113 | });
|
114 | },
|
115 |
|
116 | cancel() {
|
117 | {
|
118 | logger_js.logger.warn('The ReadableStream was cancelled.');
|
119 | }
|
120 |
|
121 | streamDeferred.resolve();
|
122 | }
|
123 |
|
124 | });
|
125 | return {
|
126 | done: streamDeferred.promise,
|
127 | stream
|
128 | };
|
129 | }
|
130 |
|
131 | /*
|
132 | Copyright 2018 Google LLC
|
133 |
|
134 | Use of this source code is governed by an MIT-style
|
135 | license that can be found in the LICENSE file or at
|
136 | https://opensource.org/licenses/MIT.
|
137 | */
|
138 | /**
|
139 | * This is a utility method that determines whether the current browser supports
|
140 | * the features required to create streamed responses. Currently, it checks if
|
141 | * [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/ReadableStream)
|
142 | * is available.
|
143 | *
|
144 | * @private
|
145 | * @param {HeadersInit} [headersInit] If there's no `Content-Type` specified,
|
146 | * `'text/html'` will be used by default.
|
147 | * @return {boolean} `true`, if the current browser meets the requirements for
|
148 | * streaming responses, and `false` otherwise.
|
149 | *
|
150 | * @memberof module:workbox-streams
|
151 | */
|
152 |
|
153 | function createHeaders(headersInit = {}) {
|
154 | // See https://github.com/GoogleChrome/workbox/issues/1461
|
155 | const headers = new Headers(headersInit);
|
156 |
|
157 | if (!headers.has('content-type')) {
|
158 | headers.set('content-type', 'text/html');
|
159 | }
|
160 |
|
161 | return headers;
|
162 | }
|
163 |
|
164 | /*
|
165 | Copyright 2018 Google LLC
|
166 |
|
167 | Use of this source code is governed by an MIT-style
|
168 | license that can be found in the LICENSE file or at
|
169 | https://opensource.org/licenses/MIT.
|
170 | */
|
171 | /**
|
172 | * Takes multiple source Promises, each of which could resolve to a Response, a
|
173 | * ReadableStream, or a [BodyInit](https://fetch.spec.whatwg.org/#bodyinit),
|
174 | * along with a
|
175 | * [HeadersInit](https://fetch.spec.whatwg.org/#typedefdef-headersinit).
|
176 | *
|
177 | * Returns an object exposing a Response whose body consists of each individual
|
178 | * stream's data returned in sequence, along with a Promise which signals when
|
179 | * the stream is finished (useful for passing to a FetchEvent's waitUntil()).
|
180 | *
|
181 | * @param {Array<Promise<module:workbox-streams.StreamSource>>} sourcePromises
|
182 | * @param {HeadersInit} [headersInit] If there's no `Content-Type` specified,
|
183 | * `'text/html'` will be used by default.
|
184 | * @return {Object<{done: Promise, response: Response}>}
|
185 | *
|
186 | * @memberof module:workbox-streams
|
187 | */
|
188 |
|
189 | function concatenateToResponse(sourcePromises, headersInit) {
|
190 | const {
|
191 | done,
|
192 | stream
|
193 | } = concatenate(sourcePromises);
|
194 | const headers = createHeaders(headersInit);
|
195 | const response = new Response(stream, {
|
196 | headers
|
197 | });
|
198 | return {
|
199 | done,
|
200 | response
|
201 | };
|
202 | }
|
203 |
|
204 | /*
|
205 | Copyright 2018 Google LLC
|
206 |
|
207 | Use of this source code is governed by an MIT-style
|
208 | license that can be found in the LICENSE file or at
|
209 | https://opensource.org/licenses/MIT.
|
210 | */
|
211 | /**
|
212 | * This is a utility method that determines whether the current browser supports
|
213 | * the features required to create streamed responses. Currently, it checks if
|
214 | * [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/ReadableStream)
|
215 | * can be created.
|
216 | *
|
217 | * @return {boolean} `true`, if the current browser meets the requirements for
|
218 | * streaming responses, and `false` otherwise.
|
219 | *
|
220 | * @memberof module:workbox-streams
|
221 | */
|
222 |
|
223 | function isSupported() {
|
224 | return canConstructReadableStream_js.canConstructReadableStream();
|
225 | }
|
226 |
|
227 | /*
|
228 | Copyright 2018 Google LLC
|
229 |
|
230 | Use of this source code is governed by an MIT-style
|
231 | license that can be found in the LICENSE file or at
|
232 | https://opensource.org/licenses/MIT.
|
233 | */
|
234 | /**
|
235 | * A shortcut to create a strategy that could be dropped-in to Workbox's router.
|
236 | *
|
237 | * On browsers that do not support constructing new `ReadableStream`s, this
|
238 | * strategy will automatically wait for all the `sourceFunctions` to complete,
|
239 | * and create a final response that concatenates their values together.
|
240 | *
|
241 | * @param {Array<function({event, request, url, params})>} sourceFunctions
|
242 | * An array of functions similar to {@link module:workbox-routing~handlerCallback}
|
243 | * but that instead return a {@link module:workbox-streams.StreamSource} (or a
|
244 | * Promise which resolves to one).
|
245 | * @param {HeadersInit} [headersInit] If there's no `Content-Type` specified,
|
246 | * `'text/html'` will be used by default.
|
247 | * @return {module:workbox-routing~handlerCallback}
|
248 | * @memberof module:workbox-streams
|
249 | */
|
250 |
|
251 | function strategy(sourceFunctions, headersInit) {
|
252 | return async ({
|
253 | event,
|
254 | request,
|
255 | url,
|
256 | params
|
257 | }) => {
|
258 | const sourcePromises = sourceFunctions.map(fn => {
|
259 | // Ensure the return value of the function is always a promise.
|
260 | return Promise.resolve(fn({
|
261 | event,
|
262 | request,
|
263 | url,
|
264 | params
|
265 | }));
|
266 | });
|
267 |
|
268 | if (isSupported()) {
|
269 | const {
|
270 | done,
|
271 | response
|
272 | } = concatenateToResponse(sourcePromises, headersInit);
|
273 |
|
274 | if (event) {
|
275 | event.waitUntil(done);
|
276 | }
|
277 |
|
278 | return response;
|
279 | }
|
280 |
|
281 | {
|
282 | logger_js.logger.log(`The current browser doesn't support creating response ` + `streams. Falling back to non-streaming response instead.`);
|
283 | } // Fallback to waiting for everything to finish, and concatenating the
|
284 | // responses.
|
285 |
|
286 |
|
287 | const blobPartsPromises = sourcePromises.map(async sourcePromise => {
|
288 | const source = await sourcePromise;
|
289 |
|
290 | if (source instanceof Response) {
|
291 | return source.blob();
|
292 | } else {
|
293 | // Technically, a `StreamSource` object can include any valid
|
294 | // `BodyInit` type, including `FormData` and `URLSearchParams`, which
|
295 | // cannot be passed to the Blob constructor directly, so we have to
|
296 | // convert them to actual Blobs first.
|
297 | return new Response(source).blob();
|
298 | }
|
299 | });
|
300 | const blobParts = await Promise.all(blobPartsPromises);
|
301 | const headers = createHeaders(headersInit); // Constructing a new Response from a Blob source is well-supported.
|
302 | // So is constructing a new Blob from multiple source Blobs or strings.
|
303 |
|
304 | return new Response(new Blob(blobParts), {
|
305 | headers
|
306 | });
|
307 | };
|
308 | }
|
309 |
|
310 | exports.concatenate = concatenate;
|
311 | exports.concatenateToResponse = concatenateToResponse;
|
312 | exports.isSupported = isSupported;
|
313 | exports.strategy = strategy;
|
314 |
|
315 | return exports;
|
316 |
|
317 | }({}, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private));
|
318 |
|