@serwist/streams 8.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2018 Google LLC, 2019 ShadowWalker w@weiw.io https://weiw.io, 2023 Serwist
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1 @@
1
+ This module's documentation can be found at https://developers.google.com/web/tools/workbox/reference-docs/latest/module-workbox-streams
@@ -0,0 +1,2 @@
1
+ export type StreamSource = Response | ReadableStream | BodyInit;
2
+ //# sourceMappingURL=_types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_types.d.ts","sourceRoot":"","sources":["../src/_types.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,cAAc,GAAG,QAAQ,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { StreamSource } from "./_types.js";
2
+ /**
3
+ * Takes multiple source Promises, each of which could resolve to a Response, a
4
+ * ReadableStream, or a [BodyInit](https://fetch.spec.whatwg.org/#bodyinit).
5
+ *
6
+ * Returns an object exposing a ReadableStream with each individual stream's
7
+ * data returned in sequence, along with a Promise which signals when the
8
+ * stream is finished (useful for passing to a FetchEvent's waitUntil()).
9
+ *
10
+ * @param sourcePromises
11
+ * @returns
12
+ */
13
+ declare function concatenate(sourcePromises: Promise<StreamSource>[]): {
14
+ done: Promise<void>;
15
+ stream: ReadableStream;
16
+ };
17
+ export { concatenate };
18
+ //# sourceMappingURL=concatenate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"concatenate.d.ts","sourceRoot":"","sources":["../src/concatenate.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAyBhD;;;;;;;;;;GAUG;AACH,iBAAS,WAAW,CAAC,cAAc,EAAE,OAAO,CAAC,YAAY,CAAC,EAAE,GAAG;IAC7D,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACpB,MAAM,EAAE,cAAc,CAAC;CACxB,CAiFA;AAED,OAAO,EAAE,WAAW,EAAE,CAAC"}
@@ -0,0 +1,21 @@
1
+ import type { StreamSource } from "./_types.js";
2
+ /**
3
+ * Takes multiple source Promises, each of which could resolve to a Response, a
4
+ * ReadableStream, or a [BodyInit](https://fetch.spec.whatwg.org/#bodyinit),
5
+ * along with a
6
+ * [HeadersInit](https://fetch.spec.whatwg.org/#typedefdef-headersinit).
7
+ *
8
+ * Returns an object exposing a Response whose body consists of each individual
9
+ * stream's data returned in sequence, along with a Promise which signals when
10
+ * the stream is finished (useful for passing to a FetchEvent's waitUntil()).
11
+ *
12
+ * @param sourcePromises
13
+ * @param headersInit If there's no `Content-Type` specified,`'text/html'` will be used by default.
14
+ * @returns
15
+ */
16
+ declare function concatenateToResponse(sourcePromises: Promise<StreamSource>[], headersInit: HeadersInit): {
17
+ done: Promise<void>;
18
+ response: Response;
19
+ };
20
+ export { concatenateToResponse };
21
+ //# sourceMappingURL=concatenateToResponse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"concatenateToResponse.d.ts","sourceRoot":"","sources":["../src/concatenateToResponse.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAIhD;;;;;;;;;;;;;GAaG;AACH,iBAAS,qBAAqB,CAAC,cAAc,EAAE,OAAO,CAAC,YAAY,CAAC,EAAE,EAAE,WAAW,EAAE,WAAW,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,CAO7I;AAED,OAAO,EAAE,qBAAqB,EAAE,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { concatenate } from "./concatenate.js";
2
+ import { concatenateToResponse } from "./concatenateToResponse.js";
3
+ import { isSupported } from "./isSupported.js";
4
+ import type { StreamsHandlerCallback } from "./strategy.js";
5
+ import { strategy } from "./strategy.js";
6
+ export { concatenate, concatenateToResponse, isSupported, strategy };
7
+ export type { StreamsHandlerCallback };
8
+ export * from "./_types.js";
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,OAAO,EAAE,WAAW,EAAE,qBAAqB,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC;AAErE,YAAY,EAAE,sBAAsB,EAAE,CAAC;AAEvC,cAAc,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,231 @@
1
+ import { assert, Deferred, logger, SerwistError, canConstructReadableStream } from '@serwist/core/internal';
2
+
3
+ /**
4
+ * Takes either a Response, a ReadableStream, or a
5
+ * [BodyInit](https://fetch.spec.whatwg.org/#bodyinit) and returns the
6
+ * ReadableStreamReader object associated with it.
7
+ *
8
+ * @param source
9
+ * @returns
10
+ * @private
11
+ */ function _getReaderFromSource(source) {
12
+ if (source instanceof Response) {
13
+ // See https://github.com/GoogleChrome/workbox/issues/2998
14
+ if (source.body) {
15
+ return source.body.getReader();
16
+ }
17
+ throw new SerwistError("opaque-streams-source", {
18
+ type: source.type
19
+ });
20
+ }
21
+ if (source instanceof ReadableStream) {
22
+ return source.getReader();
23
+ }
24
+ return new Response(source).body.getReader();
25
+ }
26
+ /**
27
+ * Takes multiple source Promises, each of which could resolve to a Response, a
28
+ * ReadableStream, or a [BodyInit](https://fetch.spec.whatwg.org/#bodyinit).
29
+ *
30
+ * Returns an object exposing a ReadableStream with each individual stream's
31
+ * data returned in sequence, along with a Promise which signals when the
32
+ * stream is finished (useful for passing to a FetchEvent's waitUntil()).
33
+ *
34
+ * @param sourcePromises
35
+ * @returns
36
+ */ function concatenate(sourcePromises) {
37
+ if (process.env.NODE_ENV !== "production") {
38
+ assert.isArray(sourcePromises, {
39
+ moduleName: "@serwist/streams",
40
+ funcName: "concatenate",
41
+ paramName: "sourcePromises"
42
+ });
43
+ }
44
+ const readerPromises = sourcePromises.map((sourcePromise)=>{
45
+ return Promise.resolve(sourcePromise).then((source)=>{
46
+ return _getReaderFromSource(source);
47
+ });
48
+ });
49
+ const streamDeferred = new Deferred();
50
+ let i = 0;
51
+ const logMessages = [];
52
+ const stream = new ReadableStream({
53
+ pull (controller) {
54
+ return readerPromises[i].then((reader)=>{
55
+ if (reader instanceof ReadableStreamDefaultReader) {
56
+ return reader.read();
57
+ } else {
58
+ return;
59
+ }
60
+ }).then((result)=>{
61
+ if (result?.done) {
62
+ if (process.env.NODE_ENV !== "production") {
63
+ logMessages.push([
64
+ "Reached the end of source:",
65
+ sourcePromises[i]
66
+ ]);
67
+ }
68
+ i++;
69
+ if (i >= readerPromises.length) {
70
+ // Log all the messages in the group at once in a single group.
71
+ if (process.env.NODE_ENV !== "production") {
72
+ logger.groupCollapsed(`Concatenating ${readerPromises.length} sources.`);
73
+ for (const message of logMessages){
74
+ if (Array.isArray(message)) {
75
+ logger.log(...message);
76
+ } else {
77
+ logger.log(message);
78
+ }
79
+ }
80
+ logger.log("Finished reading all sources.");
81
+ logger.groupEnd();
82
+ }
83
+ controller.close();
84
+ streamDeferred.resolve();
85
+ return;
86
+ }
87
+ // The `pull` method is defined because we're inside it.
88
+ return this.pull(controller);
89
+ } else {
90
+ controller.enqueue(result?.value);
91
+ }
92
+ }).catch((error)=>{
93
+ if (process.env.NODE_ENV !== "production") {
94
+ logger.error("An error occurred:", error);
95
+ }
96
+ streamDeferred.reject(error);
97
+ throw error;
98
+ });
99
+ },
100
+ cancel () {
101
+ if (process.env.NODE_ENV !== "production") {
102
+ logger.warn("The ReadableStream was cancelled.");
103
+ }
104
+ streamDeferred.resolve();
105
+ }
106
+ });
107
+ return {
108
+ done: streamDeferred.promise,
109
+ stream
110
+ };
111
+ }
112
+
113
+ /*
114
+ Copyright 2018 Google LLC
115
+
116
+ Use of this source code is governed by an MIT-style
117
+ license that can be found in the LICENSE file or at
118
+ https://opensource.org/licenses/MIT.
119
+ */ /**
120
+ * This is a utility method that determines whether the current browser supports
121
+ * the features required to create streamed responses. Currently, it checks if
122
+ * [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/ReadableStream)
123
+ * is available.
124
+ *
125
+ * @private
126
+ * @param headersInit If there's no `Content-Type` specified, `'text/html'` will be used by default.
127
+ * @returns `true`, if the current browser meets the requirements for
128
+ * streaming responses, and `false` otherwise.
129
+ */ function createHeaders(headersInit = {}) {
130
+ // See https://github.com/GoogleChrome/workbox/issues/1461
131
+ const headers = new Headers(headersInit);
132
+ if (!headers.has("content-type")) {
133
+ headers.set("content-type", "text/html");
134
+ }
135
+ return headers;
136
+ }
137
+
138
+ /**
139
+ * Takes multiple source Promises, each of which could resolve to a Response, a
140
+ * ReadableStream, or a [BodyInit](https://fetch.spec.whatwg.org/#bodyinit),
141
+ * along with a
142
+ * [HeadersInit](https://fetch.spec.whatwg.org/#typedefdef-headersinit).
143
+ *
144
+ * Returns an object exposing a Response whose body consists of each individual
145
+ * stream's data returned in sequence, along with a Promise which signals when
146
+ * the stream is finished (useful for passing to a FetchEvent's waitUntil()).
147
+ *
148
+ * @param sourcePromises
149
+ * @param headersInit If there's no `Content-Type` specified,`'text/html'` will be used by default.
150
+ * @returns
151
+ */ function concatenateToResponse(sourcePromises, headersInit) {
152
+ const { done, stream } = concatenate(sourcePromises);
153
+ const headers = createHeaders(headersInit);
154
+ const response = new Response(stream, {
155
+ headers
156
+ });
157
+ return {
158
+ done,
159
+ response
160
+ };
161
+ }
162
+
163
+ /**
164
+ * This is a utility method that determines whether the current browser supports
165
+ * the features required to create streamed responses. Currently, it checks if
166
+ * [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/ReadableStream)
167
+ * can be created.
168
+ *
169
+ * @returns `true`, if the current browser meets the requirements for
170
+ * streaming responses, and `false` otherwise.
171
+ */ function isSupported() {
172
+ return canConstructReadableStream();
173
+ }
174
+
175
+ /**
176
+ * A shortcut to create a strategy that could be dropped-in to Serwist's router.
177
+ *
178
+ * On browsers that do not support constructing new `ReadableStream`s, this
179
+ * strategy will automatically wait for all the `sourceFunctions` to complete,
180
+ * and create a final response that concatenates their values together.
181
+ *
182
+ * @param sourceFunctions An array of functions similar to `@serwist/routing.handlerCallback`
183
+ * but that instead return a `@serwist/streams.StreamSource` (or a Promise which resolves to one).
184
+ * @param headersInit If there's no `Content-Type` specified, `'text/html'` will be used by default.
185
+ * @returns
186
+ */ function strategy(sourceFunctions, headersInit) {
187
+ return async ({ event, request, url, params })=>{
188
+ const sourcePromises = sourceFunctions.map((fn)=>{
189
+ // Ensure the return value of the function is always a promise.
190
+ return Promise.resolve(fn({
191
+ event,
192
+ request,
193
+ url,
194
+ params
195
+ }));
196
+ });
197
+ if (isSupported()) {
198
+ const { done, response } = concatenateToResponse(sourcePromises, headersInit);
199
+ if (event) {
200
+ event.waitUntil(done);
201
+ }
202
+ return response;
203
+ }
204
+ if (process.env.NODE_ENV !== "production") {
205
+ logger.log(`The current browser doesn't support creating response ` + `streams. Falling back to non-streaming response instead.`);
206
+ }
207
+ // Fallback to waiting for everything to finish, and concatenating the
208
+ // responses.
209
+ const blobPartsPromises = sourcePromises.map(async (sourcePromise)=>{
210
+ const source = await sourcePromise;
211
+ if (source instanceof Response) {
212
+ return source.blob();
213
+ } else {
214
+ // Technically, a `StreamSource` object can include any valid
215
+ // `BodyInit` type, including `FormData` and `URLSearchParams`, which
216
+ // cannot be passed to the Blob constructor directly, so we have to
217
+ // convert them to actual Blobs first.
218
+ return new Response(source).blob();
219
+ }
220
+ });
221
+ const blobParts = await Promise.all(blobPartsPromises);
222
+ const headers = createHeaders(headersInit);
223
+ // Constructing a new Response from a Blob source is well-supported.
224
+ // So is constructing a new Blob from multiple source Blobs or strings.
225
+ return new Response(new Blob(blobParts), {
226
+ headers
227
+ });
228
+ };
229
+ }
230
+
231
+ export { concatenate, concatenateToResponse, isSupported, strategy };
@@ -0,0 +1,236 @@
1
+ 'use strict';
2
+
3
+ var internal = require('@serwist/core/internal');
4
+
5
+ /**
6
+ * Takes either a Response, a ReadableStream, or a
7
+ * [BodyInit](https://fetch.spec.whatwg.org/#bodyinit) and returns the
8
+ * ReadableStreamReader object associated with it.
9
+ *
10
+ * @param source
11
+ * @returns
12
+ * @private
13
+ */ function _getReaderFromSource(source) {
14
+ if (source instanceof Response) {
15
+ // See https://github.com/GoogleChrome/workbox/issues/2998
16
+ if (source.body) {
17
+ return source.body.getReader();
18
+ }
19
+ throw new internal.SerwistError("opaque-streams-source", {
20
+ type: source.type
21
+ });
22
+ }
23
+ if (source instanceof ReadableStream) {
24
+ return source.getReader();
25
+ }
26
+ return new Response(source).body.getReader();
27
+ }
28
+ /**
29
+ * Takes multiple source Promises, each of which could resolve to a Response, a
30
+ * ReadableStream, or a [BodyInit](https://fetch.spec.whatwg.org/#bodyinit).
31
+ *
32
+ * Returns an object exposing a ReadableStream with each individual stream's
33
+ * data returned in sequence, along with a Promise which signals when the
34
+ * stream is finished (useful for passing to a FetchEvent's waitUntil()).
35
+ *
36
+ * @param sourcePromises
37
+ * @returns
38
+ */ function concatenate(sourcePromises) {
39
+ if (process.env.NODE_ENV !== "production") {
40
+ internal.assert.isArray(sourcePromises, {
41
+ moduleName: "@serwist/streams",
42
+ funcName: "concatenate",
43
+ paramName: "sourcePromises"
44
+ });
45
+ }
46
+ const readerPromises = sourcePromises.map((sourcePromise)=>{
47
+ return Promise.resolve(sourcePromise).then((source)=>{
48
+ return _getReaderFromSource(source);
49
+ });
50
+ });
51
+ const streamDeferred = new internal.Deferred();
52
+ let i = 0;
53
+ const logMessages = [];
54
+ const stream = new ReadableStream({
55
+ pull (controller) {
56
+ return readerPromises[i].then((reader)=>{
57
+ if (reader instanceof ReadableStreamDefaultReader) {
58
+ return reader.read();
59
+ } else {
60
+ return;
61
+ }
62
+ }).then((result)=>{
63
+ if (result?.done) {
64
+ if (process.env.NODE_ENV !== "production") {
65
+ logMessages.push([
66
+ "Reached the end of source:",
67
+ sourcePromises[i]
68
+ ]);
69
+ }
70
+ i++;
71
+ if (i >= readerPromises.length) {
72
+ // Log all the messages in the group at once in a single group.
73
+ if (process.env.NODE_ENV !== "production") {
74
+ internal.logger.groupCollapsed(`Concatenating ${readerPromises.length} sources.`);
75
+ for (const message of logMessages){
76
+ if (Array.isArray(message)) {
77
+ internal.logger.log(...message);
78
+ } else {
79
+ internal.logger.log(message);
80
+ }
81
+ }
82
+ internal.logger.log("Finished reading all sources.");
83
+ internal.logger.groupEnd();
84
+ }
85
+ controller.close();
86
+ streamDeferred.resolve();
87
+ return;
88
+ }
89
+ // The `pull` method is defined because we're inside it.
90
+ return this.pull(controller);
91
+ } else {
92
+ controller.enqueue(result?.value);
93
+ }
94
+ }).catch((error)=>{
95
+ if (process.env.NODE_ENV !== "production") {
96
+ internal.logger.error("An error occurred:", error);
97
+ }
98
+ streamDeferred.reject(error);
99
+ throw error;
100
+ });
101
+ },
102
+ cancel () {
103
+ if (process.env.NODE_ENV !== "production") {
104
+ internal.logger.warn("The ReadableStream was cancelled.");
105
+ }
106
+ streamDeferred.resolve();
107
+ }
108
+ });
109
+ return {
110
+ done: streamDeferred.promise,
111
+ stream
112
+ };
113
+ }
114
+
115
+ /*
116
+ Copyright 2018 Google LLC
117
+
118
+ Use of this source code is governed by an MIT-style
119
+ license that can be found in the LICENSE file or at
120
+ https://opensource.org/licenses/MIT.
121
+ */ /**
122
+ * This is a utility method that determines whether the current browser supports
123
+ * the features required to create streamed responses. Currently, it checks if
124
+ * [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/ReadableStream)
125
+ * is available.
126
+ *
127
+ * @private
128
+ * @param headersInit If there's no `Content-Type` specified, `'text/html'` will be used by default.
129
+ * @returns `true`, if the current browser meets the requirements for
130
+ * streaming responses, and `false` otherwise.
131
+ */ function createHeaders(headersInit = {}) {
132
+ // See https://github.com/GoogleChrome/workbox/issues/1461
133
+ const headers = new Headers(headersInit);
134
+ if (!headers.has("content-type")) {
135
+ headers.set("content-type", "text/html");
136
+ }
137
+ return headers;
138
+ }
139
+
140
+ /**
141
+ * Takes multiple source Promises, each of which could resolve to a Response, a
142
+ * ReadableStream, or a [BodyInit](https://fetch.spec.whatwg.org/#bodyinit),
143
+ * along with a
144
+ * [HeadersInit](https://fetch.spec.whatwg.org/#typedefdef-headersinit).
145
+ *
146
+ * Returns an object exposing a Response whose body consists of each individual
147
+ * stream's data returned in sequence, along with a Promise which signals when
148
+ * the stream is finished (useful for passing to a FetchEvent's waitUntil()).
149
+ *
150
+ * @param sourcePromises
151
+ * @param headersInit If there's no `Content-Type` specified,`'text/html'` will be used by default.
152
+ * @returns
153
+ */ function concatenateToResponse(sourcePromises, headersInit) {
154
+ const { done, stream } = concatenate(sourcePromises);
155
+ const headers = createHeaders(headersInit);
156
+ const response = new Response(stream, {
157
+ headers
158
+ });
159
+ return {
160
+ done,
161
+ response
162
+ };
163
+ }
164
+
165
+ /**
166
+ * This is a utility method that determines whether the current browser supports
167
+ * the features required to create streamed responses. Currently, it checks if
168
+ * [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/ReadableStream)
169
+ * can be created.
170
+ *
171
+ * @returns `true`, if the current browser meets the requirements for
172
+ * streaming responses, and `false` otherwise.
173
+ */ function isSupported() {
174
+ return internal.canConstructReadableStream();
175
+ }
176
+
177
+ /**
178
+ * A shortcut to create a strategy that could be dropped-in to Serwist's router.
179
+ *
180
+ * On browsers that do not support constructing new `ReadableStream`s, this
181
+ * strategy will automatically wait for all the `sourceFunctions` to complete,
182
+ * and create a final response that concatenates their values together.
183
+ *
184
+ * @param sourceFunctions An array of functions similar to `@serwist/routing.handlerCallback`
185
+ * but that instead return a `@serwist/streams.StreamSource` (or a Promise which resolves to one).
186
+ * @param headersInit If there's no `Content-Type` specified, `'text/html'` will be used by default.
187
+ * @returns
188
+ */ function strategy(sourceFunctions, headersInit) {
189
+ return async ({ event, request, url, params })=>{
190
+ const sourcePromises = sourceFunctions.map((fn)=>{
191
+ // Ensure the return value of the function is always a promise.
192
+ return Promise.resolve(fn({
193
+ event,
194
+ request,
195
+ url,
196
+ params
197
+ }));
198
+ });
199
+ if (isSupported()) {
200
+ const { done, response } = concatenateToResponse(sourcePromises, headersInit);
201
+ if (event) {
202
+ event.waitUntil(done);
203
+ }
204
+ return response;
205
+ }
206
+ if (process.env.NODE_ENV !== "production") {
207
+ internal.logger.log(`The current browser doesn't support creating response ` + `streams. Falling back to non-streaming response instead.`);
208
+ }
209
+ // Fallback to waiting for everything to finish, and concatenating the
210
+ // responses.
211
+ const blobPartsPromises = sourcePromises.map(async (sourcePromise)=>{
212
+ const source = await sourcePromise;
213
+ if (source instanceof Response) {
214
+ return source.blob();
215
+ } else {
216
+ // Technically, a `StreamSource` object can include any valid
217
+ // `BodyInit` type, including `FormData` and `URLSearchParams`, which
218
+ // cannot be passed to the Blob constructor directly, so we have to
219
+ // convert them to actual Blobs first.
220
+ return new Response(source).blob();
221
+ }
222
+ });
223
+ const blobParts = await Promise.all(blobPartsPromises);
224
+ const headers = createHeaders(headersInit);
225
+ // Constructing a new Response from a Blob source is well-supported.
226
+ // So is constructing a new Blob from multiple source Blobs or strings.
227
+ return new Response(new Blob(blobParts), {
228
+ headers
229
+ });
230
+ };
231
+ }
232
+
233
+ exports.concatenate = concatenate;
234
+ exports.concatenateToResponse = concatenateToResponse;
235
+ exports.isSupported = isSupported;
236
+ exports.strategy = strategy;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * This is a utility method that determines whether the current browser supports
3
+ * the features required to create streamed responses. Currently, it checks if
4
+ * [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/ReadableStream)
5
+ * can be created.
6
+ *
7
+ * @returns `true`, if the current browser meets the requirements for
8
+ * streaming responses, and `false` otherwise.
9
+ */
10
+ declare function isSupported(): boolean;
11
+ export { isSupported };
12
+ //# sourceMappingURL=isSupported.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isSupported.d.ts","sourceRoot":"","sources":["../src/isSupported.ts"],"names":[],"mappings":"AAUA;;;;;;;;GAQG;AACH,iBAAS,WAAW,IAAI,OAAO,CAE9B;AAED,OAAO,EAAE,WAAW,EAAE,CAAC"}
@@ -0,0 +1,20 @@
1
+ import type { RouteHandlerCallback, RouteHandlerCallbackOptions } from "@serwist/core";
2
+ import type { StreamSource } from "./_types.js";
3
+ export interface StreamsHandlerCallback {
4
+ ({ url, request, event, params }: RouteHandlerCallbackOptions): Promise<StreamSource> | StreamSource;
5
+ }
6
+ /**
7
+ * A shortcut to create a strategy that could be dropped-in to Serwist's router.
8
+ *
9
+ * On browsers that do not support constructing new `ReadableStream`s, this
10
+ * strategy will automatically wait for all the `sourceFunctions` to complete,
11
+ * and create a final response that concatenates their values together.
12
+ *
13
+ * @param sourceFunctions An array of functions similar to `@serwist/routing.handlerCallback`
14
+ * but that instead return a `@serwist/streams.StreamSource` (or a Promise which resolves to one).
15
+ * @param headersInit If there's no `Content-Type` specified, `'text/html'` will be used by default.
16
+ * @returns
17
+ */
18
+ declare function strategy(sourceFunctions: StreamsHandlerCallback[], headersInit: HeadersInit): RouteHandlerCallback;
19
+ export { strategy };
20
+ //# sourceMappingURL=strategy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"strategy.d.ts","sourceRoot":"","sources":["../src/strategy.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,oBAAoB,EAAE,2BAA2B,EAAE,MAAM,eAAe,CAAC;AAGvF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAKhD,MAAM,WAAW,sBAAsB;IACrC,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,2BAA2B,GAAG,OAAO,CAAC,YAAY,CAAC,GAAG,YAAY,CAAC;CACtG;AAED;;;;;;;;;;;GAWG;AACH,iBAAS,QAAQ,CAAC,eAAe,EAAE,sBAAsB,EAAE,EAAE,WAAW,EAAE,WAAW,GAAG,oBAAoB,CAyC3G;AAED,OAAO,EAAE,QAAQ,EAAE,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * This is a utility method that determines whether the current browser supports
3
+ * the features required to create streamed responses. Currently, it checks if
4
+ * [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/ReadableStream)
5
+ * is available.
6
+ *
7
+ * @private
8
+ * @param headersInit If there's no `Content-Type` specified, `'text/html'` will be used by default.
9
+ * @returns `true`, if the current browser meets the requirements for
10
+ * streaming responses, and `false` otherwise.
11
+ */
12
+ declare function createHeaders(headersInit?: {}): Headers;
13
+ export { createHeaders };
14
+ //# sourceMappingURL=createHeaders.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createHeaders.d.ts","sourceRoot":"","sources":["../../src/utils/createHeaders.ts"],"names":[],"mappings":"AAQA;;;;;;;;;;GAUG;AACH,iBAAS,aAAa,CAAC,WAAW,KAAK,GAAG,OAAO,CAOhD;AAED,OAAO,EAAE,aAAa,EAAE,CAAC"}
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@serwist/streams",
3
+ "version": "8.0.0",
4
+ "type": "module",
5
+ "description": "A library that makes it easier to work with Streams in the browser.",
6
+ "files": [
7
+ "dist",
8
+ "!dist/**/dts"
9
+ ],
10
+ "keywords": [
11
+ "serwist",
12
+ "serwistjs",
13
+ "service worker",
14
+ "sw",
15
+ "streams",
16
+ "readablestream"
17
+ ],
18
+ "author": "Google's Web DevRel Team, Serwist's Team",
19
+ "license": "MIT",
20
+ "repository": "serwist/serwist",
21
+ "bugs": "https://github.com/serwist/serwist/issues",
22
+ "homepage": "https://ducanh-next-pwa.vercel.app",
23
+ "module": "./dist/index.js",
24
+ "main": "./dist/index.old.cjs",
25
+ "types": "./dist/index.d.ts",
26
+ "exports": {
27
+ ".": {
28
+ "import": "./dist/index.js",
29
+ "require": "./dist/index.old.cjs",
30
+ "types": "./dist/index.d.ts"
31
+ },
32
+ "./package.json": "./package.json"
33
+ },
34
+ "dependencies": {
35
+ "@serwist/core": "8.0.0",
36
+ "@serwist/routing": "8.0.0"
37
+ },
38
+ "devDependencies": {
39
+ "rollup": "3.28.1",
40
+ "@serwist/constants": "8.0.0"
41
+ },
42
+ "scripts": {
43
+ "build": "rimraf dist && cross-env NODE_ENV=production rollup --config rollup.config.js",
44
+ "lint": "eslint src --ext ts,tsx,js,jsx,cjs,mjs",
45
+ "typecheck": "tsc"
46
+ }
47
+ }