@serwist/streams 8.4.4 → 9.0.0-preview.1

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/dist/_types.d.ts CHANGED
@@ -1 +1,2 @@
1
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"}
@@ -15,3 +15,4 @@ declare function concatenate(sourcePromises: Promise<StreamSource>[]): {
15
15
  stream: ReadableStream;
16
16
  };
17
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,CA+EA;AAED,OAAO,EAAE,WAAW,EAAE,CAAC"}
@@ -18,3 +18,4 @@ declare function concatenateToResponse(sourcePromises: Promise<StreamSource>[],
18
18
  response: Response;
19
19
  };
20
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"}
package/dist/index.d.ts CHANGED
@@ -6,3 +6,4 @@ import { strategy } from "./strategy.js";
6
6
  export { concatenate, concatenateToResponse, isSupported, strategy };
7
7
  export type { StreamsHandlerCallback };
8
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 CHANGED
@@ -1,16 +1,7 @@
1
1
  import { assert, Deferred, logger, SerwistError, canConstructReadableStream } from '@serwist/core/internal';
2
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) {
3
+ function _getReaderFromSource(source) {
12
4
  if (source instanceof Response) {
13
- // See https://github.com/GoogleChrome/workbox/issues/2998
14
5
  if (source.body) {
15
6
  return source.body.getReader();
16
7
  }
@@ -23,17 +14,7 @@ import { assert, Deferred, logger, SerwistError, canConstructReadableStream } fr
23
14
  }
24
15
  return new Response(source).body.getReader();
25
16
  }
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) {
17
+ function concatenate(sourcePromises) {
37
18
  if (process.env.NODE_ENV !== "production") {
38
19
  assert.isArray(sourcePromises, {
39
20
  moduleName: "@serwist/streams",
@@ -66,7 +47,6 @@ import { assert, Deferred, logger, SerwistError, canConstructReadableStream } fr
66
47
  }
67
48
  i++;
68
49
  if (i >= readerPromises.length) {
69
- // Log all the messages in the group at once in a single group.
70
50
  if (process.env.NODE_ENV !== "production") {
71
51
  logger.groupCollapsed(`Concatenating ${readerPromises.length} sources.`);
72
52
  for (const message of logMessages){
@@ -83,7 +63,6 @@ import { assert, Deferred, logger, SerwistError, canConstructReadableStream } fr
83
63
  streamDeferred.resolve();
84
64
  return;
85
65
  }
86
- // The `pull` method is defined because we're inside it.
87
66
  return this.pull(controller);
88
67
  }
89
68
  controller.enqueue(result?.value);
@@ -108,24 +87,7 @@ import { assert, Deferred, logger, SerwistError, canConstructReadableStream } fr
108
87
  };
109
88
  }
110
89
 
111
- /*
112
- Copyright 2018 Google LLC
113
-
114
- Use of this source code is governed by an MIT-style
115
- license that can be found in the LICENSE file or at
116
- https://opensource.org/licenses/MIT.
117
- */ /**
118
- * This is a utility method that determines whether the current browser supports
119
- * the features required to create streamed responses. Currently, it checks if
120
- * [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/ReadableStream)
121
- * is available.
122
- *
123
- * @private
124
- * @param headersInit If there's no `Content-Type` specified, `'text/html'` will be used by default.
125
- * @returns `true`, if the current browser meets the requirements for
126
- * streaming responses, and `false` otherwise.
127
- */ function createHeaders(headersInit = {}) {
128
- // See https://github.com/GoogleChrome/workbox/issues/1461
90
+ function createHeaders(headersInit = {}) {
129
91
  const headers = new Headers(headersInit);
130
92
  if (!headers.has("content-type")) {
131
93
  headers.set("content-type", "text/html");
@@ -133,20 +95,7 @@ import { assert, Deferred, logger, SerwistError, canConstructReadableStream } fr
133
95
  return headers;
134
96
  }
135
97
 
136
- /**
137
- * Takes multiple source Promises, each of which could resolve to a Response, a
138
- * ReadableStream, or a [BodyInit](https://fetch.spec.whatwg.org/#bodyinit),
139
- * along with a
140
- * [HeadersInit](https://fetch.spec.whatwg.org/#typedefdef-headersinit).
141
- *
142
- * Returns an object exposing a Response whose body consists of each individual
143
- * stream's data returned in sequence, along with a Promise which signals when
144
- * the stream is finished (useful for passing to a FetchEvent's waitUntil()).
145
- *
146
- * @param sourcePromises
147
- * @param headersInit If there's no `Content-Type` specified,`'text/html'` will be used by default.
148
- * @returns
149
- */ function concatenateToResponse(sourcePromises, headersInit) {
98
+ function concatenateToResponse(sourcePromises, headersInit) {
150
99
  const { done, stream } = concatenate(sourcePromises);
151
100
  const headers = createHeaders(headersInit);
152
101
  const response = new Response(stream, {
@@ -158,33 +107,13 @@ import { assert, Deferred, logger, SerwistError, canConstructReadableStream } fr
158
107
  };
159
108
  }
160
109
 
161
- /**
162
- * This is a utility method that determines whether the current browser supports
163
- * the features required to create streamed responses. Currently, it checks if
164
- * [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/ReadableStream)
165
- * can be created.
166
- *
167
- * @returns `true`, if the current browser meets the requirements for
168
- * streaming responses, and `false` otherwise.
169
- */ function isSupported() {
110
+ function isSupported() {
170
111
  return canConstructReadableStream();
171
112
  }
172
113
 
173
- /**
174
- * A shortcut to create a strategy that could be dropped-in to Serwist's router.
175
- *
176
- * On browsers that do not support constructing new `ReadableStream`s, this
177
- * strategy will automatically wait for all the `sourceFunctions` to complete,
178
- * and create a final response that concatenates their values together.
179
- *
180
- * @param sourceFunctions An array of functions similar to `@serwist/routing.handlerCallback`
181
- * but that instead return a `@serwist/streams.StreamSource` (or a Promise which resolves to one).
182
- * @param headersInit If there's no `Content-Type` specified, `'text/html'` will be used by default.
183
- * @returns
184
- */ function strategy(sourceFunctions, headersInit) {
114
+ function strategy(sourceFunctions, headersInit) {
185
115
  return async ({ event, request, url, params })=>{
186
116
  const sourcePromises = sourceFunctions.map((fn)=>{
187
- // Ensure the return value of the function is always a promise.
188
117
  return Promise.resolve(fn({
189
118
  event,
190
119
  request,
@@ -202,23 +131,15 @@ import { assert, Deferred, logger, SerwistError, canConstructReadableStream } fr
202
131
  if (process.env.NODE_ENV !== "production") {
203
132
  logger.log(`The current browser doesn't support creating response ` + "streams. Falling back to non-streaming response instead.");
204
133
  }
205
- // Fallback to waiting for everything to finish, and concatenating the
206
- // responses.
207
134
  const blobPartsPromises = sourcePromises.map(async (sourcePromise)=>{
208
135
  const source = await sourcePromise;
209
136
  if (source instanceof Response) {
210
137
  return source.blob();
211
138
  }
212
- // Technically, a `StreamSource` object can include any valid
213
- // `BodyInit` type, including `FormData` and `URLSearchParams`, which
214
- // cannot be passed to the Blob constructor directly, so we have to
215
- // convert them to actual Blobs first.
216
139
  return new Response(source).blob();
217
140
  });
218
141
  const blobParts = await Promise.all(blobPartsPromises);
219
142
  const headers = createHeaders(headersInit);
220
- // Constructing a new Response from a Blob source is well-supported.
221
- // So is constructing a new Blob from multiple source Blobs or strings.
222
143
  return new Response(new Blob(blobParts), {
223
144
  headers
224
145
  });
@@ -9,3 +9,4 @@
9
9
  */
10
10
  declare function isSupported(): boolean;
11
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"}
@@ -17,3 +17,4 @@ export interface StreamsHandlerCallback {
17
17
  */
18
18
  declare function strategy(sourceFunctions: StreamsHandlerCallback[], headersInit: HeadersInit): RouteHandlerCallback;
19
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,CAwC3G;AAED,OAAO,EAAE,QAAQ,EAAE,CAAC"}
@@ -11,3 +11,4 @@
11
11
  */
12
12
  declare function createHeaders(headersInit?: {}): Headers;
13
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 CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@serwist/streams",
3
- "version": "8.4.4",
3
+ "version": "9.0.0-preview.1",
4
4
  "type": "module",
5
5
  "description": "A library that makes it easier to work with Streams in the browser.",
6
6
  "files": [
7
- "dist",
8
- "!dist/**/dts"
7
+ "src",
8
+ "dist"
9
9
  ],
10
10
  "keywords": [
11
11
  "serwist",
@@ -20,29 +20,31 @@
20
20
  "repository": "serwist/serwist",
21
21
  "bugs": "https://github.com/serwist/serwist/issues",
22
22
  "homepage": "https://serwist.pages.dev",
23
- "module": "./dist/index.js",
24
- "main": "./dist/index.cjs",
23
+ "main": "./dist/index.js",
25
24
  "types": "./dist/index.d.ts",
26
25
  "exports": {
27
26
  ".": {
28
- "import": {
29
- "types": "./dist/index.d.ts",
30
- "default": "./dist/index.js"
31
- },
32
- "require": {
33
- "types": "./dist/index.d.cts",
34
- "default": "./dist/index.cjs"
35
- }
27
+ "types": "./dist/index.d.ts",
28
+ "default": "./dist/index.js"
36
29
  },
37
30
  "./package.json": "./package.json"
38
31
  },
39
32
  "dependencies": {
40
- "@serwist/core": "8.4.4",
41
- "@serwist/routing": "8.4.4"
33
+ "@serwist/core": "9.0.0-preview.1",
34
+ "@serwist/routing": "9.0.0-preview.1"
42
35
  },
43
36
  "devDependencies": {
44
- "rollup": "4.9.1",
45
- "@serwist/constants": "8.4.4"
37
+ "rollup": "4.9.6",
38
+ "typescript": "5.4.0-dev.20240203",
39
+ "@serwist/constants": "9.0.0-preview.1"
40
+ },
41
+ "peerDependencies": {
42
+ "typescript": ">=5.0.0"
43
+ },
44
+ "peerDependenciesMeta": {
45
+ "typescript": {
46
+ "optional": true
47
+ }
46
48
  },
47
49
  "scripts": {
48
50
  "build": "rimraf dist && cross-env NODE_ENV=production rollup --config rollup.config.js",
package/src/_types.ts ADDED
@@ -0,0 +1,9 @@
1
+ /*
2
+ Copyright 2018 Google LLC
3
+
4
+ Use of this source code is governed by an MIT-style
5
+ license that can be found in the LICENSE file or at
6
+ https://opensource.org/licenses/MIT.
7
+ */
8
+
9
+ export type StreamSource = Response | ReadableStream | BodyInit;
@@ -0,0 +1,131 @@
1
+ /*
2
+ Copyright 2018 Google LLC
3
+
4
+ Use of this source code is governed by an MIT-style
5
+ license that can be found in the LICENSE file or at
6
+ https://opensource.org/licenses/MIT.
7
+ */
8
+
9
+ import { assert, Deferred, SerwistError, logger } from "@serwist/core/internal";
10
+
11
+ import type { StreamSource } from "./_types.js";
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 source
19
+ * @returns
20
+ * @private
21
+ */
22
+ function _getReaderFromSource(source: StreamSource): ReadableStreamReader<unknown> {
23
+ if (source instanceof Response) {
24
+ // See https://github.com/GoogleChrome/workbox/issues/2998
25
+ if (source.body) {
26
+ return source.body.getReader();
27
+ }
28
+ throw new SerwistError("opaque-streams-source", { type: source.type });
29
+ }
30
+ if (source instanceof ReadableStream) {
31
+ return source.getReader();
32
+ }
33
+ return new Response(source as BodyInit).body!.getReader();
34
+ }
35
+
36
+ /**
37
+ * Takes multiple source Promises, each of which could resolve to a Response, a
38
+ * ReadableStream, or a [BodyInit](https://fetch.spec.whatwg.org/#bodyinit).
39
+ *
40
+ * Returns an object exposing a ReadableStream with each individual stream's
41
+ * data returned in sequence, along with a Promise which signals when the
42
+ * stream is finished (useful for passing to a FetchEvent's waitUntil()).
43
+ *
44
+ * @param sourcePromises
45
+ * @returns
46
+ */
47
+ function concatenate(sourcePromises: Promise<StreamSource>[]): {
48
+ done: Promise<void>;
49
+ stream: ReadableStream;
50
+ } {
51
+ if (process.env.NODE_ENV !== "production") {
52
+ assert!.isArray(sourcePromises, {
53
+ moduleName: "@serwist/streams",
54
+ funcName: "concatenate",
55
+ paramName: "sourcePromises",
56
+ });
57
+ }
58
+
59
+ const readerPromises = sourcePromises.map((sourcePromise) => {
60
+ return Promise.resolve(sourcePromise).then((source) => {
61
+ return _getReaderFromSource(source);
62
+ });
63
+ });
64
+
65
+ const streamDeferred: Deferred<void> = new Deferred();
66
+
67
+ let i = 0;
68
+ const logMessages: any[] = [];
69
+ const stream = new ReadableStream({
70
+ pull(controller: ReadableStreamDefaultController<any>) {
71
+ return readerPromises[i]
72
+ .then((reader) => {
73
+ if (reader instanceof ReadableStreamDefaultReader) {
74
+ return reader.read();
75
+ }
76
+ return;
77
+ })
78
+ .then((result) => {
79
+ if (result?.done) {
80
+ if (process.env.NODE_ENV !== "production") {
81
+ logMessages.push(["Reached the end of source:", sourcePromises[i]]);
82
+ }
83
+
84
+ i++;
85
+ if (i >= readerPromises.length) {
86
+ // Log all the messages in the group at once in a single group.
87
+ if (process.env.NODE_ENV !== "production") {
88
+ logger.groupCollapsed(`Concatenating ${readerPromises.length} sources.`);
89
+ for (const message of logMessages) {
90
+ if (Array.isArray(message)) {
91
+ logger.log(...message);
92
+ } else {
93
+ logger.log(message);
94
+ }
95
+ }
96
+ logger.log("Finished reading all sources.");
97
+ logger.groupEnd();
98
+ }
99
+
100
+ controller.close();
101
+ streamDeferred.resolve();
102
+ return;
103
+ }
104
+
105
+ // The `pull` method is defined because we're inside it.
106
+ return this.pull!(controller);
107
+ }
108
+ controller.enqueue(result?.value);
109
+ })
110
+ .catch((error) => {
111
+ if (process.env.NODE_ENV !== "production") {
112
+ logger.error("An error occurred:", error);
113
+ }
114
+ streamDeferred.reject(error);
115
+ throw error;
116
+ });
117
+ },
118
+
119
+ cancel() {
120
+ if (process.env.NODE_ENV !== "production") {
121
+ logger.warn("The ReadableStream was cancelled.");
122
+ }
123
+
124
+ streamDeferred.resolve();
125
+ },
126
+ });
127
+
128
+ return { done: streamDeferred.promise, stream };
129
+ }
130
+
131
+ export { concatenate };
@@ -0,0 +1,36 @@
1
+ /*
2
+ Copyright 2018 Google LLC
3
+
4
+ Use of this source code is governed by an MIT-style
5
+ license that can be found in the LICENSE file or at
6
+ https://opensource.org/licenses/MIT.
7
+ */
8
+
9
+ import type { StreamSource } from "./_types.js";
10
+ import { concatenate } from "./concatenate.js";
11
+ import { createHeaders } from "./utils/createHeaders.js";
12
+
13
+ /**
14
+ * Takes multiple source Promises, each of which could resolve to a Response, a
15
+ * ReadableStream, or a [BodyInit](https://fetch.spec.whatwg.org/#bodyinit),
16
+ * along with a
17
+ * [HeadersInit](https://fetch.spec.whatwg.org/#typedefdef-headersinit).
18
+ *
19
+ * Returns an object exposing a Response whose body consists of each individual
20
+ * stream's data returned in sequence, along with a Promise which signals when
21
+ * the stream is finished (useful for passing to a FetchEvent's waitUntil()).
22
+ *
23
+ * @param sourcePromises
24
+ * @param headersInit If there's no `Content-Type` specified,`'text/html'` will be used by default.
25
+ * @returns
26
+ */
27
+ function concatenateToResponse(sourcePromises: Promise<StreamSource>[], headersInit: HeadersInit): { done: Promise<void>; response: Response } {
28
+ const { done, stream } = concatenate(sourcePromises);
29
+
30
+ const headers = createHeaders(headersInit);
31
+ const response = new Response(stream, { headers });
32
+
33
+ return { done, response };
34
+ }
35
+
36
+ export { concatenateToResponse };
@@ -1,8 +1,19 @@
1
+ /*
2
+ Copyright 2018 Google LLC
3
+
4
+ Use of this source code is governed by an MIT-style
5
+ license that can be found in the LICENSE file or at
6
+ https://opensource.org/licenses/MIT.
7
+ */
8
+
1
9
  import { concatenate } from "./concatenate.js";
2
10
  import { concatenateToResponse } from "./concatenateToResponse.js";
3
11
  import { isSupported } from "./isSupported.js";
4
12
  import type { StreamsHandlerCallback } from "./strategy.js";
5
13
  import { strategy } from "./strategy.js";
14
+
6
15
  export { concatenate, concatenateToResponse, isSupported, strategy };
16
+
7
17
  export type { StreamsHandlerCallback };
18
+
8
19
  export * from "./_types.js";
@@ -0,0 +1,24 @@
1
+ /*
2
+ Copyright 2018 Google LLC
3
+
4
+ Use of this source code is governed by an MIT-style
5
+ license that can be found in the LICENSE file or at
6
+ https://opensource.org/licenses/MIT.
7
+ */
8
+
9
+ import { canConstructReadableStream } from "@serwist/core/internal";
10
+
11
+ /**
12
+ * This is a utility method that determines whether the current browser supports
13
+ * the features required to create streamed responses. Currently, it checks if
14
+ * [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/ReadableStream)
15
+ * can be created.
16
+ *
17
+ * @returns `true`, if the current browser meets the requirements for
18
+ * streaming responses, and `false` otherwise.
19
+ */
20
+ function isSupported(): boolean {
21
+ return canConstructReadableStream();
22
+ }
23
+
24
+ export { isSupported };
@@ -0,0 +1,75 @@
1
+ /*
2
+ Copyright 2018 Google LLC
3
+
4
+ Use of this source code is governed by an MIT-style
5
+ license that can be found in the LICENSE file or at
6
+ https://opensource.org/licenses/MIT.
7
+ */
8
+
9
+ import type { RouteHandlerCallback, RouteHandlerCallbackOptions } from "@serwist/core";
10
+ import { logger } from "@serwist/core/internal";
11
+
12
+ import type { StreamSource } from "./_types.js";
13
+ import { concatenateToResponse } from "./concatenateToResponse.js";
14
+ import { isSupported } from "./isSupported.js";
15
+ import { createHeaders } from "./utils/createHeaders.js";
16
+
17
+ export interface StreamsHandlerCallback {
18
+ ({ url, request, event, params }: RouteHandlerCallbackOptions): Promise<StreamSource> | StreamSource;
19
+ }
20
+
21
+ /**
22
+ * A shortcut to create a strategy that could be dropped-in to Serwist's router.
23
+ *
24
+ * On browsers that do not support constructing new `ReadableStream`s, this
25
+ * strategy will automatically wait for all the `sourceFunctions` to complete,
26
+ * and create a final response that concatenates their values together.
27
+ *
28
+ * @param sourceFunctions An array of functions similar to `@serwist/routing.handlerCallback`
29
+ * but that instead return a `@serwist/streams.StreamSource` (or a Promise which resolves to one).
30
+ * @param headersInit If there's no `Content-Type` specified, `'text/html'` will be used by default.
31
+ * @returns
32
+ */
33
+ function strategy(sourceFunctions: StreamsHandlerCallback[], headersInit: HeadersInit): RouteHandlerCallback {
34
+ return async ({ event, request, url, params }: RouteHandlerCallbackOptions) => {
35
+ const sourcePromises = sourceFunctions.map((fn) => {
36
+ // Ensure the return value of the function is always a promise.
37
+ return Promise.resolve(fn({ event, request, url, params }));
38
+ });
39
+
40
+ if (isSupported()) {
41
+ const { done, response } = concatenateToResponse(sourcePromises, headersInit);
42
+
43
+ if (event) {
44
+ event.waitUntil(done);
45
+ }
46
+ return response;
47
+ }
48
+
49
+ if (process.env.NODE_ENV !== "production") {
50
+ logger.log(`The current browser doesn't support creating response ` + "streams. Falling back to non-streaming response instead.");
51
+ }
52
+
53
+ // Fallback to waiting for everything to finish, and concatenating the
54
+ // responses.
55
+ const blobPartsPromises = sourcePromises.map(async (sourcePromise) => {
56
+ const source = await sourcePromise;
57
+ if (source instanceof Response) {
58
+ return source.blob();
59
+ }
60
+ // Technically, a `StreamSource` object can include any valid
61
+ // `BodyInit` type, including `FormData` and `URLSearchParams`, which
62
+ // cannot be passed to the Blob constructor directly, so we have to
63
+ // convert them to actual Blobs first.
64
+ return new Response(source).blob();
65
+ });
66
+ const blobParts = await Promise.all(blobPartsPromises);
67
+ const headers = createHeaders(headersInit);
68
+
69
+ // Constructing a new Response from a Blob source is well-supported.
70
+ // So is constructing a new Blob from multiple source Blobs or strings.
71
+ return new Response(new Blob(blobParts), { headers });
72
+ };
73
+ }
74
+
75
+ export { strategy };
@@ -0,0 +1,29 @@
1
+ /*
2
+ Copyright 2018 Google LLC
3
+
4
+ Use of this source code is governed by an MIT-style
5
+ license that can be found in the LICENSE file or at
6
+ https://opensource.org/licenses/MIT.
7
+ */
8
+
9
+ /**
10
+ * This is a utility method that determines whether the current browser supports
11
+ * the features required to create streamed responses. Currently, it checks if
12
+ * [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/ReadableStream)
13
+ * is available.
14
+ *
15
+ * @private
16
+ * @param headersInit If there's no `Content-Type` specified, `'text/html'` will be used by default.
17
+ * @returns `true`, if the current browser meets the requirements for
18
+ * streaming responses, and `false` otherwise.
19
+ */
20
+ function createHeaders(headersInit = {}): Headers {
21
+ // See https://github.com/GoogleChrome/workbox/issues/1461
22
+ const headers = new Headers(headersInit);
23
+ if (!headers.has("content-type")) {
24
+ headers.set("content-type", "text/html");
25
+ }
26
+ return headers;
27
+ }
28
+
29
+ export { createHeaders };
package/dist/index.cjs DELETED
@@ -1,233 +0,0 @@
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
- }
60
- return;
61
- }).then((result)=>{
62
- if (result?.done) {
63
- if (process.env.NODE_ENV !== "production") {
64
- logMessages.push([
65
- "Reached the end of source:",
66
- sourcePromises[i]
67
- ]);
68
- }
69
- i++;
70
- if (i >= readerPromises.length) {
71
- // Log all the messages in the group at once in a single group.
72
- if (process.env.NODE_ENV !== "production") {
73
- internal.logger.groupCollapsed(`Concatenating ${readerPromises.length} sources.`);
74
- for (const message of logMessages){
75
- if (Array.isArray(message)) {
76
- internal.logger.log(...message);
77
- } else {
78
- internal.logger.log(message);
79
- }
80
- }
81
- internal.logger.log("Finished reading all sources.");
82
- internal.logger.groupEnd();
83
- }
84
- controller.close();
85
- streamDeferred.resolve();
86
- return;
87
- }
88
- // The `pull` method is defined because we're inside it.
89
- return this.pull(controller);
90
- }
91
- controller.enqueue(result?.value);
92
- }).catch((error)=>{
93
- if (process.env.NODE_ENV !== "production") {
94
- internal.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
- internal.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 internal.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
- internal.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
- }
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
- const blobParts = await Promise.all(blobPartsPromises);
221
- const headers = createHeaders(headersInit);
222
- // Constructing a new Response from a Blob source is well-supported.
223
- // So is constructing a new Blob from multiple source Blobs or strings.
224
- return new Response(new Blob(blobParts), {
225
- headers
226
- });
227
- };
228
- }
229
-
230
- exports.concatenate = concatenate;
231
- exports.concatenateToResponse = concatenateToResponse;
232
- exports.isSupported = isSupported;
233
- exports.strategy = strategy;