workbox-streams.dev.js 10 KB

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