@tstdl/base 0.90.7 → 0.90.9

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/http/utils.js CHANGED
@@ -49,7 +49,7 @@ export async function readBodyAsBuffer(body, headers, options = {}) {
49
49
  uint8Array = new Uint8Array(buffer);
50
50
  }
51
51
  else if (isReadableStream(body) || isAnyIterable(body)) {
52
- uint8Array = await readBinaryStream(readBodyAsBinaryStream(body, headers, options), headers.contentLength);
52
+ uint8Array = await readBinaryStream(readBodyAsBinaryStream(body, headers, options), { length: headers.contentLength });
53
53
  }
54
54
  else {
55
55
  throw new NotSupportedError('Unsupported body type.');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tstdl/base",
3
- "version": "0.90.7",
3
+ "version": "0.90.9",
4
4
  "author": "Patrick Hein",
5
5
  "publishConfig": {
6
6
  "access": "public"
package/utils/binary.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { BinaryData } from '../types.js';
1
+ import type { BinaryData, Type } from '../types.js';
2
2
  /**
3
3
  * Get ArrayBuffer from binary data
4
4
  * @param data data to get ArrayBuffer from
@@ -14,3 +14,4 @@ export declare function toArrayBuffer(data: BinaryData, clone?: boolean): ArrayB
14
14
  export declare function toUint8Array(data: BinaryData, clone?: boolean): Uint8Array;
15
15
  export declare function concatArrayBuffers(buffers: ArrayBufferLike[]): ArrayBuffer;
16
16
  export declare function concatArrayBufferViews<T extends ArrayBufferView>(arrays: T[], totalLength?: number): T;
17
+ export declare function concatArrayBufferViews<T extends ArrayBufferView>(arrays: ArrayBufferView[], targetType: Type<T, [ArrayBufferLike, number, number]>, totalLength?: number): T;
package/utils/binary.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { supportsBuffer } from '../supports.js';
2
- import { assert, isArrayBuffer } from './type-guards.js';
2
+ import { typeExtends } from './index.js';
3
+ import { assert, isArrayBuffer, isFunction, isNumber } from './type-guards.js';
3
4
  /**
4
5
  * Get ArrayBuffer from binary data
5
6
  * @param data data to get ArrayBuffer from
@@ -36,11 +37,13 @@ export function concatArrayBuffers(buffers) {
36
37
  const bytes = concatArrayBufferViews(arrays);
37
38
  return bytes.buffer;
38
39
  }
39
- export function concatArrayBufferViews(arrays, totalLength) {
40
+ export function concatArrayBufferViews(arrays, totalLengthOrTargetType, totalLengthOrNothing) {
40
41
  assert(arrays.length > 0, 'No array provided.');
41
- const type = arrays[0].constructor;
42
- if (supportsBuffer && ((type == Buffer) || (type == Uint8Array))) {
43
- return Buffer.concat(arrays, totalLength);
42
+ const totalLength = isNumber(totalLengthOrTargetType) ? totalLengthOrTargetType : totalLengthOrNothing;
43
+ const type = isFunction(totalLengthOrTargetType) ? totalLengthOrTargetType : arrays[0].constructor;
44
+ if (supportsBuffer && typeExtends(type, Uint8Array)) {
45
+ const merged = Buffer.concat(arrays, totalLength);
46
+ return new type(merged.buffer, merged.byteOffset, merged.byteLength);
44
47
  }
45
48
  const totalBytes = totalLength ?? arrays.reduce((sum, array) => sum + array.byteLength, 0);
46
49
  const merged = new Uint8Array(totalBytes);
@@ -50,5 +53,5 @@ export function concatArrayBufferViews(arrays, totalLength) {
50
53
  merged.set(uint8Array, currentIndex);
51
54
  currentIndex += uint8Array.byteLength;
52
55
  }
53
- return new type(merged.buffer);
56
+ return new type(merged.buffer, merged.byteOffset, merged.byteLength);
54
57
  }
package/utils/index.d.ts CHANGED
@@ -49,6 +49,7 @@ export * from './timer.js';
49
49
  export * from './timing.js';
50
50
  export * from './type-guards.js';
51
51
  export * from './type-of.js';
52
+ export * from './type/index.js';
52
53
  export * from './units.js';
53
54
  export * from './url-builder.js';
54
55
  export * from './value-or-provider.js';
package/utils/index.js CHANGED
@@ -49,6 +49,7 @@ export * from './timer.js';
49
49
  export * from './timing.js';
50
50
  export * from './type-guards.js';
51
51
  export * from './type-of.js';
52
+ export * from './type/index.js';
52
53
  export * from './units.js';
53
54
  export * from './url-builder.js';
54
55
  export * from './value-or-provider.js';
@@ -4,3 +4,4 @@ export * from './readable-stream-from-promise.js';
4
4
  export * from './size-limited-stream.js';
5
5
  export * from './stream-helper-types.js';
6
6
  export * from './stream-reader.js';
7
+ export * from './to-bytes-stream.js';
@@ -4,3 +4,4 @@ export * from './readable-stream-from-promise.js';
4
4
  export * from './size-limited-stream.js';
5
5
  export * from './stream-helper-types.js';
6
6
  export * from './stream-reader.js';
7
+ export * from './to-bytes-stream.js';
@@ -14,15 +14,14 @@ export async function* getReadableStreamIterable(stream, options = {}) {
14
14
  if (options.close != false) {
15
15
  await reader.cancel();
16
16
  }
17
- reader.releaseLock();
17
+ else {
18
+ reader.releaseLock();
19
+ }
18
20
  }
19
21
  }
20
22
  export function getReadableStreamFromIterable(iterable) {
21
- let iterator;
23
+ const iterator = isAsyncIterable(iterable) ? iterable[Symbol.asyncIterator]() : iterable[Symbol.iterator]();
22
24
  return new ReadableStream({
23
- cancel: async (reason) => {
24
- await iterator.return?.(reason);
25
- },
26
25
  pull: async (controller) => {
27
26
  const result = await iterator.next();
28
27
  if (result.done == true) {
@@ -32,8 +31,8 @@ export function getReadableStreamFromIterable(iterable) {
32
31
  controller.enqueue(result.value);
33
32
  }
34
33
  },
35
- start: () => {
36
- iterator = isAsyncIterable(iterable) ? iterable[Symbol.asyncIterator]() : iterable[Symbol.iterator]();
34
+ cancel: async (reason) => {
35
+ await iterator.return?.(reason);
37
36
  }
38
37
  });
39
38
  }
@@ -1,2 +1,9 @@
1
1
  import type { AnyIterable } from '../any-iterable-iterator.js';
2
- export declare function readBinaryStream(iterableOrStream: AnyIterable<Uint8Array> | ReadableStream<Uint8Array>, length?: number): Promise<Uint8Array>;
2
+ export type ReadBinaryStreamOptions = {
3
+ length?: number;
4
+ /** Action if source stream has more bytes than provided length */
5
+ onLengthExceed?: 'error' | 'close' | 'leave-open';
6
+ /** Action if source stream has less bytes than provided length */
7
+ onLengthSubceed?: 'error' | 'close' | 'leave-open';
8
+ };
9
+ export declare function readBinaryStream(iterableOrStream: AnyIterable<ArrayBufferView> | ReadableStream<ArrayBufferView>, { length, onLengthExceed, onLengthSubceed }?: ReadBinaryStreamOptions): Promise<Uint8Array>;
@@ -1,34 +1,78 @@
1
1
  import { BadRequestError } from '../../errors/bad-request.error.js';
2
- import { isAnyIterable } from '../any-iterable-iterator.js';
2
+ import { MaxBytesExceededError } from '../../errors/max-bytes-exceeded.error.js';
3
+ import { NotSupportedError } from '../../errors/not-supported.error.js';
3
4
  import { concatArrayBufferViews } from '../binary.js';
4
- import { isDefined } from '../type-guards.js';
5
- import { getReadableStreamIterable } from './readable-stream-adapter.js';
5
+ import { isDefined, isReadableStream } from '../type-guards.js';
6
+ import { getReadableStreamFromIterable } from './readable-stream-adapter.js';
7
+ import { toBytesStream } from './to-bytes-stream.js';
6
8
  // eslint-disable-next-line max-statements
7
- export async function readBinaryStream(iterableOrStream, length) {
8
- const iterable = isAnyIterable(iterableOrStream) ? iterableOrStream : getReadableStreamIterable(iterableOrStream);
9
+ export async function readBinaryStream(iterableOrStream, { length, onLengthExceed = 'error', onLengthSubceed = 'error' } = {}) {
10
+ const stream = isReadableStream(iterableOrStream)
11
+ ? isDefined(length)
12
+ ? toBytesStream(iterableOrStream)
13
+ : iterableOrStream
14
+ : getReadableStreamFromIterable(iterableOrStream);
9
15
  if (isDefined(length)) {
10
- const array = new Uint8Array(length);
11
- let bytesWritten = 0;
12
- for await (const chunk of iterable) {
13
- if ((bytesWritten + chunk.length) > length) {
14
- throw new BadRequestError('Size of stream is greater than provided length.');
16
+ const reader = stream.getReader({ mode: 'byob' });
17
+ let buffer = new ArrayBuffer(length + 1);
18
+ let bytesRead = 0;
19
+ while (true) {
20
+ const result = await reader.read(new Uint8Array(buffer, bytesRead, buffer.byteLength - bytesRead));
21
+ buffer = result.value.buffer;
22
+ bytesRead += result.value.byteLength;
23
+ if (result.done) {
24
+ break;
25
+ }
26
+ if (bytesRead > length) {
27
+ switch (onLengthExceed) {
28
+ case 'error':
29
+ await reader.cancel();
30
+ throw new MaxBytesExceededError('Size of stream is greater than provided length.');
31
+ case 'close':
32
+ await reader.cancel();
33
+ break;
34
+ case 'leave-open':
35
+ reader.releaseLock();
36
+ break;
37
+ default:
38
+ throw new NotSupportedError(`Action ${onLengthExceed} not supported.`);
39
+ }
40
+ }
41
+ if (bytesRead == length + 1) {
42
+ break;
15
43
  }
16
- array.set(chunk, bytesWritten);
17
- bytesWritten += chunk.length;
18
44
  }
19
- if (bytesWritten != length) {
20
- throw new BadRequestError('Size of stream did not match provided length.');
45
+ if (bytesRead < length) {
46
+ switch (onLengthSubceed) {
47
+ case 'error':
48
+ await reader.cancel();
49
+ throw new BadRequestError('Size of stream was less than provided length.');
50
+ case 'close':
51
+ await reader.cancel();
52
+ break;
53
+ case 'leave-open':
54
+ reader.releaseLock();
55
+ break;
56
+ default:
57
+ throw new NotSupportedError(`Action ${onLengthSubceed} not supported.`);
58
+ }
21
59
  }
22
- return array;
60
+ await reader.cancel();
61
+ return new Uint8Array(buffer, 0, Math.min(length, bytesRead));
23
62
  }
24
63
  let totalLength = 0;
25
- const chunks = [];
26
- for await (const chunk of iterable) {
27
- chunks.push(chunk);
28
- totalLength += chunk.length;
64
+ const reader = stream.getReader();
65
+ const views = [];
66
+ while (true) {
67
+ const result = await reader.read();
68
+ if (result.done) {
69
+ break;
70
+ }
71
+ views.push(result.value);
72
+ totalLength += result.value.byteLength;
29
73
  }
30
- if (chunks.length == 0) {
74
+ if (views.length == 0) {
31
75
  return new Uint8Array(0);
32
76
  }
33
- return concatArrayBufferViews(chunks, totalLength);
77
+ return concatArrayBufferViews(views, Uint8Array, totalLength);
34
78
  }
@@ -0,0 +1,4 @@
1
+ export type ToBytesStreamOptions = {
2
+ ignoreCancel?: boolean;
3
+ };
4
+ export declare function toBytesStream(stream: ReadableStream<ArrayBufferView>, options?: ToBytesStreamOptions): ReadableStream<Uint8Array>;
@@ -0,0 +1,78 @@
1
+ import { isNotNull, isNull } from '../type-guards.js';
2
+ export function toBytesStream(stream, options) {
3
+ try { // try to use byob mode from source
4
+ let byobReader;
5
+ return new ReadableStream({
6
+ type: 'bytes',
7
+ autoAllocateChunkSize: 10240,
8
+ start() {
9
+ byobReader = stream.getReader({ mode: 'byob' });
10
+ },
11
+ async pull(controller) {
12
+ const readResult = await byobReader.read(controller.byobRequest.view);
13
+ if (readResult.done) {
14
+ controller.close();
15
+ controller.byobRequest.respond(0);
16
+ return;
17
+ }
18
+ controller.byobRequest.respondWithNewView(readResult.value);
19
+ },
20
+ async cancel(reason) {
21
+ if (options?.ignoreCancel == true) {
22
+ byobReader.releaseLock();
23
+ }
24
+ else {
25
+ await byobReader.cancel(reason);
26
+ }
27
+ }
28
+ });
29
+ }
30
+ catch { /* ignore */ }
31
+ let reader;
32
+ let buffer = null;
33
+ return new ReadableStream({
34
+ type: 'bytes',
35
+ start() {
36
+ reader = stream.getReader();
37
+ },
38
+ async pull(controller) {
39
+ const isByobRequest = isNotNull(controller.byobRequest);
40
+ const bufferIsEmpty = isNull(buffer) || (buffer.byteLength == 0);
41
+ if (!isByobRequest && !bufferIsEmpty) {
42
+ // we stil have data left in buffer from previous pull which was byob
43
+ controller.enqueue(buffer);
44
+ buffer = null;
45
+ return;
46
+ }
47
+ if (bufferIsEmpty) {
48
+ const readResult = await reader.read();
49
+ if (readResult.done) {
50
+ controller.close();
51
+ controller.byobRequest?.respond(0);
52
+ return;
53
+ }
54
+ buffer = readResult.value; // eslint-disable-line require-atomic-updates
55
+ }
56
+ if (!isByobRequest) {
57
+ controller.enqueue(buffer);
58
+ buffer = null;
59
+ return;
60
+ }
61
+ const targetView = controller.byobRequest.view;
62
+ const targetArray = new Uint8Array(targetView.buffer, targetView.byteOffset, targetView.byteLength);
63
+ const setLength = Math.min(buffer.byteLength, targetArray.byteLength);
64
+ const sourceArray = new Uint8Array(buffer.buffer, buffer.byteOffset, setLength);
65
+ targetArray.set(sourceArray);
66
+ buffer = new Uint8Array(buffer.buffer, buffer.byteOffset + setLength);
67
+ controller.byobRequest.respond(setLength);
68
+ },
69
+ async cancel(reason) {
70
+ if (options?.ignoreCancel == true) {
71
+ reader.releaseLock();
72
+ }
73
+ else {
74
+ await reader.cancel(reason);
75
+ }
76
+ }
77
+ });
78
+ }
@@ -0,0 +1,2 @@
1
+ import type { AbstractConstructor } from '../../types.js';
2
+ export declare function typeExtends(type: AbstractConstructor, base: AbstractConstructor): boolean;
@@ -0,0 +1,3 @@
1
+ export function typeExtends(type, base) {
2
+ return ((type == base) || (type.prototype instanceof base));
3
+ }
@@ -0,0 +1 @@
1
+ export * from './extends.js';
@@ -0,0 +1 @@
1
+ export * from './extends.js';