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