@jsenv/server 15.0.1 → 15.0.3

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.
Files changed (53) hide show
  1. package/README.md +23 -23
  2. package/package.json +4 -4
  3. package/src/content_negotiation/pick_accepted_content.js +13 -13
  4. package/src/content_negotiation/pick_content_encoding.js +25 -25
  5. package/src/content_negotiation/pick_content_language.js +35 -34
  6. package/src/content_negotiation/pick_content_type.js +29 -29
  7. package/src/fetch_filesystem.js +110 -107
  8. package/src/from_fetch_response.js +6 -6
  9. package/src/interfacing_with_node/body.js +22 -22
  10. package/src/interfacing_with_node/observable.js +80 -80
  11. package/src/interfacing_with_node/observable_from_node_stream.js +35 -35
  12. package/src/interfacing_with_node/request_factory.js +26 -26
  13. package/src/interfacing_with_node/write_node_response.js +82 -82
  14. package/src/internal/colorizeResponseStatus.js +26 -26
  15. package/src/internal/convertFileSystemErrorToResponseProperties.js +10 -10
  16. package/src/internal/dns_resolution.js +7 -7
  17. package/src/internal/etag.js +11 -11
  18. package/src/internal/filesystem.js +19 -19
  19. package/src/internal/headersFromObject.js +8 -8
  20. package/src/internal/headersFromString.js +11 -11
  21. package/src/internal/headersToObject.js +5 -5
  22. package/src/internal/headersToString.js +7 -7
  23. package/src/internal/headers_composition.js +9 -9
  24. package/src/internal/hostname_parser.js +9 -9
  25. package/src/internal/listen.js +47 -47
  26. package/src/internal/listenClientError.js +10 -10
  27. package/src/internal/listenEvent.js +5 -5
  28. package/src/internal/listenRequest.js +8 -8
  29. package/src/internal/listenServerConnectionError.js +19 -19
  30. package/src/internal/multiple-header.js +50 -50
  31. package/src/internal/normalizeHeaderName.js +4 -4
  32. package/src/internal/normalizeHeaderValue.js +2 -2
  33. package/src/internal/object_composition.js +41 -39
  34. package/src/internal/response_composition.js +6 -6
  35. package/src/internal/server-polyglot.js +42 -42
  36. package/src/internal/server_ips.js +20 -20
  37. package/src/internal/trackServerPendingConnections.js +23 -23
  38. package/src/internal/trackServerPendingRequests.js +28 -28
  39. package/src/main.js +18 -18
  40. package/src/readRequestBody.js +15 -15
  41. package/src/serve_directory.js +16 -16
  42. package/src/server_timing/timing_header.js +24 -24
  43. package/src/server_timing/timing_measure.js +14 -14
  44. package/src/service_composition/path_to_regexp.js +173 -168
  45. package/src/service_composition/routing.js +12 -12
  46. package/src/service_controller.js +77 -77
  47. package/src/services/cors/jsenv_service_cors.js +29 -29
  48. package/src/services/error_handler/jsenv_service_error_handler.js +18 -18
  49. package/src/services/request_aliases/jsenv_service_request_aliases.js +20 -20
  50. package/src/services/response_acceptance_check/jsenv_service_response_acceptance_check.js +16 -16
  51. package/src/sse/sse_room.js +117 -115
  52. package/src/start_server.js +328 -328
  53. package/src/stopReasons.js +9 -9
@@ -4,16 +4,19 @@
4
4
  * It is meant to be used inside "requestToResponse"
5
5
  */
6
6
 
7
- import { createReadStream, statSync, readFile } from "node:fs"
8
- import { CONTENT_TYPE } from "@jsenv/utils/src/content_type/content_type.js"
9
-
10
- import { isFileSystemPath, fileSystemPathToUrl } from "./internal/filesystem.js"
11
- import { bufferToEtag } from "./internal/etag.js"
12
- import { composeTwoResponses } from "./internal/response_composition.js"
13
- import { convertFileSystemErrorToResponseProperties } from "./internal/convertFileSystemErrorToResponseProperties.js"
14
- import { timeFunction } from "./server_timing/timing_measure.js"
15
- import { pickContentEncoding } from "./content_negotiation/pick_content_encoding.js"
16
- import { serveDirectory } from "./serve_directory.js"
7
+ import { createReadStream, statSync, readFile } from "node:fs";
8
+ import { CONTENT_TYPE } from "@jsenv/utils/src/content_type/content_type.js";
9
+
10
+ import {
11
+ isFileSystemPath,
12
+ fileSystemPathToUrl,
13
+ } from "./internal/filesystem.js";
14
+ import { bufferToEtag } from "./internal/etag.js";
15
+ import { composeTwoResponses } from "./internal/response_composition.js";
16
+ import { convertFileSystemErrorToResponseProperties } from "./internal/convertFileSystemErrorToResponseProperties.js";
17
+ import { timeFunction } from "./server_timing/timing_measure.js";
18
+ import { pickContentEncoding } from "./content_negotiation/pick_content_encoding.js";
19
+ import { serveDirectory } from "./serve_directory.js";
17
20
 
18
21
  export const fetchFileSystem = async (
19
22
  filesystemUrl,
@@ -34,33 +37,33 @@ export const fetchFileSystem = async (
34
37
  rootDirectoryUrl, // = `${pathToFileURL(process.cwd())}/`,
35
38
  } = {},
36
39
  ) => {
37
- const urlString = asUrlString(filesystemUrl)
40
+ const urlString = asUrlString(filesystemUrl);
38
41
  if (!urlString) {
39
42
  return create500Response(
40
43
  `fetchFileSystem first parameter must be a file url, got ${filesystemUrl}`,
41
- )
44
+ );
42
45
  }
43
46
  if (!urlString.startsWith("file://")) {
44
47
  return create500Response(
45
48
  `fetchFileSystem url must use "file://" scheme, got ${filesystemUrl}`,
46
- )
49
+ );
47
50
  }
48
51
  if (rootDirectoryUrl) {
49
- let rootDirectoryUrlString = asUrlString(rootDirectoryUrl)
52
+ let rootDirectoryUrlString = asUrlString(rootDirectoryUrl);
50
53
  if (!rootDirectoryUrlString) {
51
54
  return create500Response(
52
55
  `rootDirectoryUrl must be a string or an url, got ${rootDirectoryUrl}`,
53
- )
56
+ );
54
57
  }
55
58
  if (!rootDirectoryUrlString.endsWith("/")) {
56
- rootDirectoryUrlString = `${rootDirectoryUrlString}/`
59
+ rootDirectoryUrlString = `${rootDirectoryUrlString}/`;
57
60
  }
58
61
  if (!urlString.startsWith(rootDirectoryUrlString)) {
59
62
  return create500Response(
60
63
  `fetchFileSystem url must be inside root directory, got ${urlString}`,
61
- )
64
+ );
62
65
  }
63
- rootDirectoryUrl = rootDirectoryUrlString
66
+ rootDirectoryUrl = rootDirectoryUrlString;
64
67
  }
65
68
 
66
69
  // here you might be tempted to add || cacheControl === 'no-cache'
@@ -68,52 +71,52 @@ export const fetchFileSystem = async (
68
71
  // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#Cacheability
69
72
  if (cacheControl === "no-store") {
70
73
  if (etagEnabled) {
71
- console.warn(`cannot enable etag when cache-control is ${cacheControl}`)
72
- etagEnabled = false
74
+ console.warn(`cannot enable etag when cache-control is ${cacheControl}`);
75
+ etagEnabled = false;
73
76
  }
74
77
  if (mtimeEnabled) {
75
- console.warn(`cannot enable mtime when cache-control is ${cacheControl}`)
76
- mtimeEnabled = false
78
+ console.warn(`cannot enable mtime when cache-control is ${cacheControl}`);
79
+ mtimeEnabled = false;
77
80
  }
78
81
  }
79
82
  if (etagEnabled && mtimeEnabled) {
80
83
  console.warn(
81
84
  `cannot enable both etag and mtime, mtime disabled in favor of etag.`,
82
- )
83
- mtimeEnabled = false
85
+ );
86
+ mtimeEnabled = false;
84
87
  }
85
88
 
86
89
  if (method !== "GET" && method !== "HEAD") {
87
90
  return {
88
91
  status: 501,
89
- }
92
+ };
90
93
  }
91
94
 
92
- const sourceUrl = `file://${new URL(urlString).pathname}`
95
+ const sourceUrl = `file://${new URL(urlString).pathname}`;
93
96
  try {
94
97
  const [readStatTiming, sourceStat] = await timeFunction(
95
98
  "file service>read file stat",
96
99
  () => statSync(new URL(sourceUrl)),
97
- )
100
+ );
98
101
  if (sourceStat.isDirectory()) {
99
102
  if (canReadDirectory) {
100
103
  return serveDirectory(urlString, {
101
104
  headers,
102
105
  canReadDirectory,
103
106
  rootDirectoryUrl,
104
- })
107
+ });
105
108
  }
106
109
  return {
107
110
  status: 403,
108
111
  statusText: "not allowed to read directory",
109
- }
112
+ };
110
113
  }
111
114
  // not a file, give up
112
115
  if (!sourceStat.isFile()) {
113
116
  return {
114
117
  status: 404,
115
118
  timing: readStatTiming,
116
- }
119
+ };
117
120
  }
118
121
 
119
122
  const clientCacheResponse = await getClientCacheResponse({
@@ -124,7 +127,7 @@ export const fetchFileSystem = async (
124
127
  mtimeEnabled,
125
128
  sourceStat,
126
129
  sourceUrl,
127
- })
130
+ });
128
131
 
129
132
  // send 304 (redirect response to client cache)
130
133
  // because the response body does not have to be transmitted
@@ -137,24 +140,24 @@ export const fetchFileSystem = async (
137
140
  },
138
141
  },
139
142
  clientCacheResponse,
140
- )
143
+ );
141
144
  }
142
145
 
143
- let response
146
+ let response;
144
147
  if (compressionEnabled && sourceStat.size >= compressionSizeThreshold) {
145
148
  const compressedResponse = await getCompressedResponse({
146
149
  headers,
147
150
  sourceUrl,
148
- })
151
+ });
149
152
  if (compressedResponse) {
150
- response = compressedResponse
153
+ response = compressedResponse;
151
154
  }
152
155
  }
153
156
  if (!response) {
154
157
  response = await getRawResponse({
155
158
  sourceStat,
156
159
  sourceUrl,
157
- })
160
+ });
158
161
  }
159
162
 
160
163
  const intermediateResponse = composeTwoResponses(
@@ -171,8 +174,8 @@ export const fetchFileSystem = async (
171
174
  },
172
175
  },
173
176
  response,
174
- )
175
- return composeTwoResponses(intermediateResponse, clientCacheResponse)
177
+ );
178
+ return composeTwoResponses(intermediateResponse, clientCacheResponse);
176
179
  } catch (e) {
177
180
  return composeTwoResponses(
178
181
  {
@@ -181,9 +184,9 @@ export const fetchFileSystem = async (
181
184
  },
182
185
  },
183
186
  convertFileSystemErrorToResponseProperties(e) || {},
184
- )
187
+ );
185
188
  }
186
- }
189
+ };
187
190
 
188
191
  const create500Response = (message) => {
189
192
  return {
@@ -193,8 +196,8 @@ const create500Response = (message) => {
193
196
  "content-length": Buffer.byteLength(message),
194
197
  },
195
198
  body: message,
196
- }
197
- }
199
+ };
200
+ };
198
201
 
199
202
  const getClientCacheResponse = async ({
200
203
  headers,
@@ -214,7 +217,7 @@ const getClientCacheResponse = async ({
214
217
  // let's disable it on no-cache too
215
218
  headers["cache-control"] === "no-cache"
216
219
  ) {
217
- return { status: 200 }
220
+ return { status: 200 };
218
221
  }
219
222
 
220
223
  if (etagEnabled) {
@@ -224,18 +227,18 @@ const getClientCacheResponse = async ({
224
227
  etagMemoryMaxSize,
225
228
  sourceStat,
226
229
  sourceUrl,
227
- })
230
+ });
228
231
  }
229
232
 
230
233
  if (mtimeEnabled) {
231
234
  return getMtimeResponse({
232
235
  headers,
233
236
  sourceStat,
234
- })
237
+ });
235
238
  }
236
239
 
237
- return { status: 200 }
238
- }
240
+ return { status: 200 };
241
+ };
239
242
 
240
243
  const getEtagResponse = async ({
241
244
  headers,
@@ -253,9 +256,9 @@ const getEtagResponse = async ({
253
256
  sourceUrl,
254
257
  sourceStat,
255
258
  }),
256
- )
259
+ );
257
260
 
258
- const requestHasIfNoneMatchHeader = "if-none-match" in headers
261
+ const requestHasIfNoneMatchHeader = "if-none-match" in headers;
259
262
  if (
260
263
  requestHasIfNoneMatchHeader &&
261
264
  headers["if-none-match"] === fileContentEtag
@@ -263,7 +266,7 @@ const getEtagResponse = async ({
263
266
  return {
264
267
  status: 304,
265
268
  timing: computeEtagTiming,
266
- }
269
+ };
267
270
  }
268
271
 
269
272
  return {
@@ -272,10 +275,10 @@ const getEtagResponse = async ({
272
275
  etag: fileContentEtag,
273
276
  },
274
277
  timing: computeEtagTiming,
275
- }
276
- }
278
+ };
279
+ };
277
280
 
278
- const ETAG_MEMORY_MAP = new Map()
281
+ const ETAG_MEMORY_MAP = new Map();
279
282
  const computeEtag = async ({
280
283
  etagMemory,
281
284
  etagMemoryMaxSize,
@@ -283,42 +286,42 @@ const computeEtag = async ({
283
286
  sourceStat,
284
287
  }) => {
285
288
  if (etagMemory) {
286
- const etagMemoryEntry = ETAG_MEMORY_MAP.get(sourceUrl)
289
+ const etagMemoryEntry = ETAG_MEMORY_MAP.get(sourceUrl);
287
290
  if (
288
291
  etagMemoryEntry &&
289
292
  fileStatAreTheSame(etagMemoryEntry.sourceStat, sourceStat)
290
293
  ) {
291
- return etagMemoryEntry.eTag
294
+ return etagMemoryEntry.eTag;
292
295
  }
293
296
  }
294
297
  const fileContentAsBuffer = await new Promise((resolve, reject) => {
295
298
  readFile(new URL(sourceUrl), (error, buffer) => {
296
299
  if (error) {
297
- reject(error)
300
+ reject(error);
298
301
  } else {
299
- resolve(buffer)
302
+ resolve(buffer);
300
303
  }
301
- })
302
- })
303
- const eTag = bufferToEtag(fileContentAsBuffer)
304
+ });
305
+ });
306
+ const eTag = bufferToEtag(fileContentAsBuffer);
304
307
  if (etagMemory) {
305
308
  if (ETAG_MEMORY_MAP.size >= etagMemoryMaxSize) {
306
- const firstKey = Array.from(ETAG_MEMORY_MAP.keys())[0]
307
- ETAG_MEMORY_MAP.delete(firstKey)
309
+ const firstKey = Array.from(ETAG_MEMORY_MAP.keys())[0];
310
+ ETAG_MEMORY_MAP.delete(firstKey);
308
311
  }
309
- ETAG_MEMORY_MAP.set(sourceUrl, { sourceStat, eTag })
312
+ ETAG_MEMORY_MAP.set(sourceUrl, { sourceStat, eTag });
310
313
  }
311
- return eTag
312
- }
314
+ return eTag;
315
+ };
313
316
 
314
317
  // https://nodejs.org/api/fs.html#fs_class_fs_stats
315
318
  const fileStatAreTheSame = (leftFileStat, rightFileStat) => {
316
319
  return fileStatKeysToCompare.every((keyToCompare) => {
317
- const leftValue = leftFileStat[keyToCompare]
318
- const rightValue = rightFileStat[keyToCompare]
319
- return leftValue === rightValue
320
- })
321
- }
320
+ const leftValue = leftFileStat[keyToCompare];
321
+ const rightValue = rightFileStat[keyToCompare];
322
+ return leftValue === rightValue;
323
+ });
324
+ };
322
325
  const fileStatKeysToCompare = [
323
326
  // mtime the the most likely to change, check it first
324
327
  "mtimeMs",
@@ -329,25 +332,25 @@ const fileStatKeysToCompare = [
329
332
  "uid",
330
333
  "gid",
331
334
  "blksize",
332
- ]
335
+ ];
333
336
 
334
337
  const getMtimeResponse = async ({ headers, sourceStat }) => {
335
338
  if ("if-modified-since" in headers) {
336
- let cachedModificationDate
339
+ let cachedModificationDate;
337
340
  try {
338
- cachedModificationDate = new Date(headers["if-modified-since"])
341
+ cachedModificationDate = new Date(headers["if-modified-since"]);
339
342
  } catch (e) {
340
343
  return {
341
344
  status: 400,
342
345
  statusText: "if-modified-since header is not a valid date",
343
- }
346
+ };
344
347
  }
345
348
 
346
- const actualModificationDate = dateToSecondsPrecision(sourceStat.mtime)
349
+ const actualModificationDate = dateToSecondsPrecision(sourceStat.mtime);
347
350
  if (Number(cachedModificationDate) >= Number(actualModificationDate)) {
348
351
  return {
349
352
  status: 304,
350
- }
353
+ };
351
354
  }
352
355
  }
353
356
 
@@ -356,22 +359,22 @@ const getMtimeResponse = async ({ headers, sourceStat }) => {
356
359
  headers: {
357
360
  "last-modified": dateToUTCString(sourceStat.mtime),
358
361
  },
359
- }
360
- }
362
+ };
363
+ };
361
364
 
362
365
  const getCompressedResponse = async ({ sourceUrl, headers }) => {
363
366
  const acceptedCompressionFormat = pickContentEncoding(
364
367
  { headers },
365
368
  Object.keys(availableCompressionFormats),
366
- )
369
+ );
367
370
  if (!acceptedCompressionFormat) {
368
- return null
371
+ return null;
369
372
  }
370
373
 
371
- const fileReadableStream = fileUrlToReadableStream(sourceUrl)
374
+ const fileReadableStream = fileUrlToReadableStream(sourceUrl);
372
375
  const body = await availableCompressionFormats[acceptedCompressionFormat](
373
376
  fileReadableStream,
374
- )
377
+ );
375
378
 
376
379
  return {
377
380
  status: 200,
@@ -381,30 +384,30 @@ const getCompressedResponse = async ({ sourceUrl, headers }) => {
381
384
  "vary": "accept-encoding",
382
385
  },
383
386
  body,
384
- }
385
- }
387
+ };
388
+ };
386
389
 
387
390
  const fileUrlToReadableStream = (fileUrl) => {
388
391
  return createReadStream(new URL(fileUrl), {
389
392
  emitClose: true,
390
393
  autoClose: true,
391
- })
392
- }
394
+ });
395
+ };
393
396
 
394
397
  const availableCompressionFormats = {
395
398
  br: async (fileReadableStream) => {
396
- const { createBrotliCompress } = await import("node:zlib")
397
- return fileReadableStream.pipe(createBrotliCompress())
399
+ const { createBrotliCompress } = await import("node:zlib");
400
+ return fileReadableStream.pipe(createBrotliCompress());
398
401
  },
399
402
  deflate: async (fileReadableStream) => {
400
- const { createDeflate } = await import("node:zlib")
401
- return fileReadableStream.pipe(createDeflate())
403
+ const { createDeflate } = await import("node:zlib");
404
+ return fileReadableStream.pipe(createDeflate());
402
405
  },
403
406
  gzip: async (fileReadableStream) => {
404
- const { createGzip } = await import("node:zlib")
405
- return fileReadableStream.pipe(createGzip())
407
+ const { createGzip } = await import("node:zlib");
408
+ return fileReadableStream.pipe(createGzip());
406
409
  },
407
- }
410
+ };
408
411
 
409
412
  const getRawResponse = async ({ sourceUrl, sourceStat }) => {
410
413
  return {
@@ -414,32 +417,32 @@ const getRawResponse = async ({ sourceUrl, sourceStat }) => {
414
417
  "content-length": sourceStat.size,
415
418
  },
416
419
  body: fileUrlToReadableStream(sourceUrl),
417
- }
418
- }
420
+ };
421
+ };
419
422
 
420
423
  // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toUTCString
421
- const dateToUTCString = (date) => date.toUTCString()
424
+ const dateToUTCString = (date) => date.toUTCString();
422
425
 
423
426
  const dateToSecondsPrecision = (date) => {
424
- const dateWithSecondsPrecision = new Date(date)
425
- dateWithSecondsPrecision.setMilliseconds(0)
426
- return dateWithSecondsPrecision
427
- }
427
+ const dateWithSecondsPrecision = new Date(date);
428
+ dateWithSecondsPrecision.setMilliseconds(0);
429
+ return dateWithSecondsPrecision;
430
+ };
428
431
 
429
432
  const asUrlString = (value) => {
430
433
  if (value instanceof URL) {
431
- return value.href
434
+ return value.href;
432
435
  }
433
436
  if (typeof value === "string") {
434
437
  if (isFileSystemPath(value)) {
435
- return fileSystemPathToUrl(value)
438
+ return fileSystemPathToUrl(value);
436
439
  }
437
440
  try {
438
- const urlObject = new URL(value)
439
- return String(urlObject)
441
+ const urlObject = new URL(value);
442
+ return String(urlObject);
440
443
  } catch (e) {
441
- return null
444
+ return null;
442
445
  }
443
446
  }
444
- return null
445
- }
447
+ return null;
448
+ };
@@ -1,15 +1,15 @@
1
1
  export const fromFetchResponse = (fetchResponse) => {
2
- const responseHeaders = {}
3
- const headersToIgnore = ["connection"]
2
+ const responseHeaders = {};
3
+ const headersToIgnore = ["connection"];
4
4
  fetchResponse.headers.forEach((value, name) => {
5
5
  if (!headersToIgnore.includes(name)) {
6
- responseHeaders[name] = value
6
+ responseHeaders[name] = value;
7
7
  }
8
- })
8
+ });
9
9
  return {
10
10
  status: fetchResponse.status,
11
11
  statusText: fetchResponse.statusText,
12
12
  headers: responseHeaders,
13
13
  body: fetchResponse.body, // node-fetch assumed
14
- }
15
- }
14
+ };
15
+ };
@@ -1,44 +1,44 @@
1
- import { Stream, Writable, Readable } from "node:stream"
2
- import { createReadStream } from "node:fs"
1
+ import { Stream, Writable, Readable } from "node:stream";
2
+ import { createReadStream } from "node:fs";
3
3
 
4
- import { isObservable, observableFromValue } from "./observable.js"
5
- import { observableFromNodeStream } from "./observable_from_node_stream.js"
4
+ import { isObservable, observableFromValue } from "./observable.js";
5
+ import { observableFromNodeStream } from "./observable_from_node_stream.js";
6
6
 
7
7
  export const normalizeBodyMethods = (body) => {
8
8
  if (isObservable(body)) {
9
9
  return {
10
10
  asObservable: () => body,
11
11
  destroy: () => {},
12
- }
12
+ };
13
13
  }
14
14
 
15
15
  if (isFileHandle(body)) {
16
16
  return {
17
17
  asObservable: () => fileHandleToObservable(body),
18
18
  destroy: () => {
19
- body.close()
19
+ body.close();
20
20
  },
21
- }
21
+ };
22
22
  }
23
23
 
24
24
  if (isNodeStream(body)) {
25
25
  return {
26
26
  asObservable: () => observableFromNodeStream(body),
27
27
  destroy: () => {
28
- body.destroy()
28
+ body.destroy();
29
29
  },
30
- }
30
+ };
31
31
  }
32
32
 
33
33
  return {
34
34
  asObservable: () => observableFromValue(body),
35
35
  destroy: () => {},
36
- }
37
- }
36
+ };
37
+ };
38
38
 
39
39
  export const isFileHandle = (value) => {
40
- return value && value.constructor && value.constructor.name === "FileHandle"
41
- }
40
+ return value && value.constructor && value.constructor.name === "FileHandle";
41
+ };
42
42
 
43
43
  export const fileHandleToReadableStream = (fileHandle) => {
44
44
  const fileReadableStream =
@@ -51,22 +51,22 @@ export const fileHandleToReadableStream = (fileHandle) => {
51
51
  emitClose: true,
52
52
  // autoClose: true
53
53
  },
54
- )
54
+ );
55
55
  // I suppose it's required only when doing fs.createReadStream()
56
56
  // and not fileHandle.createReadStream()
57
57
  // fileReadableStream.on("end", () => {
58
58
  // fileHandle.close()
59
59
  // })
60
- return fileReadableStream
61
- }
60
+ return fileReadableStream;
61
+ };
62
62
 
63
63
  const fileHandleToObservable = (fileHandle) => {
64
- return observableFromNodeStream(fileHandleToReadableStream(fileHandle))
65
- }
64
+ return observableFromNodeStream(fileHandleToReadableStream(fileHandle));
65
+ };
66
66
 
67
67
  const isNodeStream = (value) => {
68
68
  if (value === undefined) {
69
- return false
69
+ return false;
70
70
  }
71
71
 
72
72
  if (
@@ -74,8 +74,8 @@ const isNodeStream = (value) => {
74
74
  value instanceof Writable ||
75
75
  value instanceof Readable
76
76
  ) {
77
- return true
77
+ return true;
78
78
  }
79
79
 
80
- return false
81
- }
80
+ return false;
81
+ };