Skip to content

Commit eafa310

Browse files
chore(internal): refactor utils
1 parent 678516c commit eafa310

File tree

5 files changed

+56
-67
lines changed

5 files changed

+56
-67
lines changed

src/client.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -700,12 +700,12 @@ export class Gitpod {
700700
fetchOptions.method = method.toUpperCase();
701701
}
702702

703-
return (
703+
try {
704704
// use undefined this binding; fetch errors if bound to something else in browser/cloudflare
705-
this.fetch.call(undefined, url, fetchOptions).finally(() => {
706-
clearTimeout(timeout);
707-
})
708-
);
705+
return await this.fetch.call(undefined, url, fetchOptions);
706+
} finally {
707+
clearTimeout(timeout);
708+
}
709709
}
710710

711711
private shouldRetry(response: Response): boolean {

src/internal/decoders/line.ts

+11-56
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { GitpodError } from '../../core/error';
1+
import { concatBytes, decodeUTF8, encodeUTF8 } from '../utils/bytes';
22

33
export type Bytes = string | ArrayBuffer | Uint8Array | null | undefined;
44

@@ -13,16 +13,11 @@ export class LineDecoder {
1313
static NEWLINE_CHARS = new Set(['\n', '\r']);
1414
static NEWLINE_REGEXP = /\r\n|[\n\r]/g;
1515

16-
buffer: Uint8Array;
16+
#buffer: Uint8Array;
1717
#carriageReturnIndex: number | null;
18-
textDecoder:
19-
| undefined
20-
| {
21-
decode(buffer: Uint8Array | ArrayBuffer): string;
22-
};
2318

2419
constructor() {
25-
this.buffer = new Uint8Array();
20+
this.#buffer = new Uint8Array();
2621
this.#carriageReturnIndex = null;
2722
}
2823

@@ -33,17 +28,14 @@ export class LineDecoder {
3328

3429
const binaryChunk =
3530
chunk instanceof ArrayBuffer ? new Uint8Array(chunk)
36-
: typeof chunk === 'string' ? new TextEncoder().encode(chunk)
31+
: typeof chunk === 'string' ? encodeUTF8(chunk)
3732
: chunk;
3833

39-
let newData = new Uint8Array(this.buffer.length + binaryChunk.length);
40-
newData.set(this.buffer);
41-
newData.set(binaryChunk, this.buffer.length);
42-
this.buffer = newData;
34+
this.#buffer = concatBytes([this.#buffer, binaryChunk]);
4335

4436
const lines: string[] = [];
4537
let patternIndex;
46-
while ((patternIndex = findNewlineIndex(this.buffer, this.#carriageReturnIndex)) != null) {
38+
while ((patternIndex = findNewlineIndex(this.#buffer, this.#carriageReturnIndex)) != null) {
4739
if (patternIndex.carriage && this.#carriageReturnIndex == null) {
4840
// skip until we either get a corresponding `\n`, a new `\r` or nothing
4941
this.#carriageReturnIndex = patternIndex.index;
@@ -55,64 +47,27 @@ export class LineDecoder {
5547
this.#carriageReturnIndex != null &&
5648
(patternIndex.index !== this.#carriageReturnIndex + 1 || patternIndex.carriage)
5749
) {
58-
lines.push(this.decodeText(this.buffer.slice(0, this.#carriageReturnIndex - 1)));
59-
this.buffer = this.buffer.slice(this.#carriageReturnIndex);
50+
lines.push(decodeUTF8(this.#buffer.subarray(0, this.#carriageReturnIndex - 1)));
51+
this.#buffer = this.#buffer.subarray(this.#carriageReturnIndex);
6052
this.#carriageReturnIndex = null;
6153
continue;
6254
}
6355

6456
const endIndex =
6557
this.#carriageReturnIndex !== null ? patternIndex.preceding - 1 : patternIndex.preceding;
6658

67-
const line = this.decodeText(this.buffer.slice(0, endIndex));
59+
const line = decodeUTF8(this.#buffer.subarray(0, endIndex));
6860
lines.push(line);
6961

70-
this.buffer = this.buffer.slice(patternIndex.index);
62+
this.#buffer = this.#buffer.subarray(patternIndex.index);
7163
this.#carriageReturnIndex = null;
7264
}
7365

7466
return lines;
7567
}
7668

77-
decodeText(bytes: Bytes): string {
78-
if (bytes == null) return '';
79-
if (typeof bytes === 'string') return bytes;
80-
81-
// Node:
82-
if (typeof (globalThis as any).Buffer !== 'undefined') {
83-
if (bytes instanceof (globalThis as any).Buffer) {
84-
return bytes.toString();
85-
}
86-
if (bytes instanceof Uint8Array) {
87-
return (globalThis as any).Buffer.from(bytes).toString();
88-
}
89-
90-
throw new GitpodError(
91-
`Unexpected: received non-Uint8Array (${bytes.constructor.name}) stream chunk in an environment with a global "Buffer" defined, which this library assumes to be Node. Please report this error.`,
92-
);
93-
}
94-
95-
// Browser
96-
if (typeof (globalThis as any).TextDecoder !== 'undefined') {
97-
if (bytes instanceof Uint8Array || bytes instanceof ArrayBuffer) {
98-
this.textDecoder ??= new (globalThis as any).TextDecoder('utf8');
99-
return this.textDecoder!.decode(bytes);
100-
}
101-
102-
throw new GitpodError(
103-
`Unexpected: received non-Uint8Array/ArrayBuffer (${
104-
(bytes as any).constructor.name
105-
}) in a web platform. Please report this error.`,
106-
);
107-
}
108-
109-
throw new GitpodError(
110-
`Unexpected: neither Buffer nor TextDecoder are available as globals. Please report this error.`,
111-
);
112-
}
113-
11469
flush(): string[] {
115-
if (!this.buffer.length) {
70+
if (!this.#buffer.length) {
11671
return [];
11772
}
11873
return this.decode('\n');

src/internal/headers.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
type HeaderValue = string | undefined | null;
44
export type HeadersLike =
55
| Headers
6-
| readonly [string, HeaderValue][]
6+
| readonly HeaderValue[][]
77
| Record<string, HeaderValue | readonly HeaderValue[]>
88
| undefined
99
| null
@@ -40,7 +40,7 @@ function* iterateHeaders(headers: HeadersLike): IterableIterator<readonly [strin
4040
}
4141

4242
let shouldClear = false;
43-
let iter: Iterable<readonly [string, HeaderValue | readonly HeaderValue[]]>;
43+
let iter: Iterable<readonly (HeaderValue | readonly HeaderValue[])[]>;
4444
if (headers instanceof Headers) {
4545
iter = headers.entries();
4646
} else if (isArray(headers)) {
@@ -51,6 +51,7 @@ function* iterateHeaders(headers: HeadersLike): IterableIterator<readonly [strin
5151
}
5252
for (let row of iter) {
5353
const name = row[0];
54+
if (typeof name !== 'string') throw new TypeError('expected header name to be a string');
5455
const values = isArray(row[1]) ? row[1] : [row[1]];
5556
let didClear = false;
5657
for (const value of values) {

src/internal/utils/base64.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
22

33
import { GitpodError } from '../../core/error';
4+
import { encodeUTF8 } from './bytes';
45

56
export const toBase64 = (data: string | Uint8Array | null | undefined): string => {
67
if (!data) return '';
78

8-
if (typeof data === 'string') {
9-
data = new (globalThis as any).TextEncoder().encode(data);
10-
}
11-
129
if (typeof (globalThis as any).Buffer !== 'undefined') {
1310
return (globalThis as any).Buffer.from(data).toString('base64');
1411
}
1512

13+
if (typeof data === 'string') {
14+
data = encodeUTF8(data);
15+
}
16+
1617
if (typeof btoa !== 'undefined') {
1718
return btoa(String.fromCharCode.apply(null, data as any));
1819
}

src/internal/utils/bytes.ts

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
export function concatBytes(buffers: Uint8Array[]): Uint8Array {
2+
let length = 0;
3+
for (const buffer of buffers) {
4+
length += buffer.length;
5+
}
6+
const output = new Uint8Array(length);
7+
let index = 0;
8+
for (const buffer of buffers) {
9+
output.set(buffer, index);
10+
index += buffer.length;
11+
}
12+
13+
return output;
14+
}
15+
16+
let encodeUTF8_: (str: string) => Uint8Array;
17+
export function encodeUTF8(str: string) {
18+
let encoder;
19+
return (
20+
encodeUTF8_ ??
21+
((encoder = new (globalThis as any).TextEncoder()), (encodeUTF8_ = encoder.encode.bind(encoder)))
22+
)(str);
23+
}
24+
25+
let decodeUTF8_: (bytes: Uint8Array) => string;
26+
export function decodeUTF8(bytes: Uint8Array) {
27+
let decoder;
28+
return (
29+
decodeUTF8_ ??
30+
((decoder = new (globalThis as any).TextDecoder()), (decodeUTF8_ = decoder.decode.bind(decoder)))
31+
)(bytes);
32+
}

0 commit comments

Comments
 (0)