• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

box / box-typescript-sdk-gen / 11817965999

13 Nov 2024 01:10PM UTC coverage: 41.715% (+0.03%) from 41.685%
11817965999

Pull #411

github

web-flow
Merge 0d287c38e into e48500756
Pull Request #411: chore: add codegen diff job (box/box-codegen#598)

4058 of 16837 branches covered (24.1%)

Branch coverage included in aggregate %.

2 of 7 new or added lines in 1 file covered. (28.57%)

6 existing lines in 1 file now uncovered.

13372 of 24947 relevant lines covered (53.6%)

76.7 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

63.97
/src/internal/utils.ts
1
import { Buffer } from 'buffer';
140✔
2
import type { Readable } from 'stream';
3
import { v4 as uuidv4 } from 'uuid';
140✔
4
import { SignJWT, importPKCS8 } from 'jose';
140✔
5

6
export function isBrowser() {
140✔
7
  return (
23,884✔
8
    typeof window === 'object' && typeof document === 'object' && window.crypto
23,884!
9
  );
10
}
11

12
export function getUuid() {
140✔
13
  return uuidv4();
522✔
14
}
15

16
export function decodeBase64(value: string) {
140✔
17
  return Buffer.from(value, 'base64').toString('utf8');
244✔
18
}
19

20
export function hexToBase64(data: string): string {
140✔
21
  return Buffer.from(data, 'hex').toString('base64');
12✔
22
}
23

24
export { Buffer, Readable as ByteStream };
140✔
25
export type { CancellationToken };
26
export type Iterator<T = any> = AsyncIterator<T>;
27
export type AgentOptions = any;
28
export type Agent = any;
29

30
// using wrappers for date/datetime because of inability to export built-in Date types
31
class DateWrapper {
32
  constructor(public readonly value: Date) {}
10✔
33
}
34

35
class DateTimeWrapper {
36
  constructor(public readonly value: Date) {}
5,096✔
37
}
38

39
export { DateWrapper as Date, DateTimeWrapper as DateTime };
140✔
40

41
export function dateFromString(value: string): DateWrapper {
140✔
42
  return new DateWrapper(new Date(value));
10✔
43
}
44

45
export function dateToString(date: DateWrapper): string {
140✔
46
  return date.value.toISOString().match(/^\d{4}-\d{2}-\d{2}/)![0];
4✔
47
}
48

49
export function dateTimeFromString(value: string): DateTimeWrapper {
140✔
50
  return new DateTimeWrapper(new Date(value));
5,096✔
51
}
52

53
export function dateTimeToString(dateTime: DateTimeWrapper): string {
140✔
54
  return (
36✔
55
    dateTime.value
56
      .toISOString()
57
      .match(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/)![0] + '+00:00'
58
  );
59
}
60

61
export {
62
  dateToString as serializeDate,
140✔
63
  dateFromString as deserializeDate,
140✔
64
  dateTimeToString as serializeDateTime,
140✔
65
  dateTimeFromString as deserializeDateTime,
140✔
66
};
67

68
// Function to convert a hexadecimal string to base64
69
export function hexStrToBase64(hex: string) {
140✔
UNCOV
70
  const hexString = hex.toString(); // Ensure the input is a string
×
UNCOV
71
  const hexBytes = new Uint8Array(hexString.length / 2);
×
72

73
  // Convert the hexadecimal string to bytes
UNCOV
74
  for (let i = 0; i < hexString.length; i += 2) {
×
UNCOV
75
    hexBytes[i / 2] = parseInt(hexString.substr(i, 2), 16);
×
76
  }
77

78
  // Encode the bytes as base64
UNCOV
79
  const base64 = btoa(String.fromCharCode.apply(null, Array.from(hexBytes)));
×
80

UNCOV
81
  return base64;
×
82
}
83

84
export type HashName = 'sha1';
85

86
export class Hash {
140✔
87
  #hash: any;
18✔
88
  #chunks: Uint8Array; // In browser environment, we need to buffer the chunks until we get the hash object
18✔
89
  algorithm: HashName;
90

91
  constructor({ algorithm }: { algorithm: HashName }) {
92
    this.algorithm = algorithm;
18✔
93
    this.#chunks = new Uint8Array();
18✔
94
    if (isBrowser()) {
18!
95
      this.#hash = undefined;
×
96
    } else {
97
      this.#hash = eval('require')('crypto').createHash(algorithm);
18✔
98
    }
99
  }
100

101
  updateHash(data: Buffer) {
102
    if (isBrowser()) {
24!
103
      let dataBuffer =
104
        typeof data === 'string' ? new TextEncoder().encode(data) : data;
×
105
      let newChunks = new Uint8Array(this.#chunks.length + dataBuffer.length);
×
106
      newChunks.set(this.#chunks);
×
107
      newChunks.set(dataBuffer, this.#chunks.length);
×
108
      this.#chunks = newChunks;
×
109
      return;
×
110
    }
111
    this.#hash.update(data);
24✔
112
  }
113

114
  async digestHash(encoding: 'base64'): Promise<string> {
115
    if (isBrowser()) {
18!
116
      this.#hash = await window.crypto.subtle.digest(
×
117
        this.algorithm,
118
        this.#chunks,
119
      );
120
      const hashArray = Array.from(new Uint8Array(this.#hash));
×
121
      const hashHex = hashArray
×
122
        .map((b) => b.toString(16).padStart(2, '0'))
×
123
        .join('');
124
      if (encoding === 'base64') {
×
125
        return hexStrToBase64(hashHex);
×
126
      }
127
      return hashHex;
×
128
    }
129
    return this.#hash.digest(encoding);
18✔
130
  }
131
}
132

133
export function getEnvVar(name: string) {
140✔
134
  return process.env[name] || '';
398!
135
}
136

137
export function generateByteBuffer(size: number): Buffer {
140✔
138
  if (isBrowser()) {
102!
139
    const buffer = new Uint8Array(size);
×
140
    window.crypto.getRandomValues(buffer);
×
141
    return Buffer.from(buffer);
×
142
  }
143
  const crypto = eval('require')('crypto');
102✔
144
  return crypto.randomBytes(size);
102✔
145
}
146

147
export function generateByteStreamFromBuffer(
140✔
148
  buffer: Buffer | ArrayBuffer,
149
): Readable {
150
  return isBrowser()
1,600!
151
    ? new ReadableStream<Uint8Array>({
152
        start(controller) {
153
          controller.enqueue(new Uint8Array(buffer));
×
154
          controller.close();
×
155
        },
156
      })
157
    : eval('require')('stream').Readable.from(Buffer.from(buffer));
158
}
159

160
export function generateByteStream(size: number): Readable {
140✔
161
  return generateByteStreamFromBuffer(generateByteBuffer(size));
92✔
162
}
163

164
export function bufferEquals(buffer1: Buffer, buffer2: Buffer): boolean {
140✔
165
  return Buffer.compare(buffer1, buffer2) === 0;
12✔
166
}
167

168
export function bufferLength(buffer: Buffer): number {
140✔
169
  return buffer.length;
12✔
170
}
171

172
export function decodeBase64ByteStream(data: string): Readable {
140✔
173
  return isBrowser()
2!
174
    ? new ReadableStream<Uint8Array>({
175
        start(controller) {
176
          const decodedStr = atob(data);
×
177
          const buffer = new ArrayBuffer(decodedStr.length);
×
178
          const array = new Uint8Array(buffer);
×
179
          for (let i = 0; i < decodedStr.length; i++) {
×
180
            array[i] = decodedStr.charCodeAt(i);
×
181
          }
182
          controller.enqueue(array);
×
183
          controller.close();
×
184
        },
185
      })
186
    : eval('require')('stream').Readable.from(Buffer.from(data, 'base64'));
187
}
188

189
export function stringToByteStream(data: string): Readable {
140✔
190
  return isBrowser()
6!
191
    ? new ReadableStream<Uint8Array>({
192
        start(controller) {
193
          const buffer = new ArrayBuffer(data.length);
×
194
          const array = new Uint8Array(buffer);
×
195
          for (let i = 0; i < data.length; i++) {
×
196
            array[i] = data.charCodeAt(i);
×
197
          }
198
          controller.enqueue(array);
×
199
          controller.close();
×
200
        },
201
      })
202
    : eval('require')('stream').Readable.from(Buffer.from(data, 'ascii'));
203
}
204

205
export async function readByteStream(byteStream: Readable): Promise<Buffer> {
140✔
206
  const buffers: Buffer[] = [];
136✔
207
  for await (const data of byteStream) {
268✔
208
    buffers.push(data);
134✔
209
  }
210
  return Buffer.concat(buffers);
136✔
211
}
212

213
export async function* iterateChunks(
140✔
214
  stream: Readable,
215
  chunkSize: number,
216
  fileSize: number,
217
): Iterator<Readable> {
218
  let buffers: Buffer[] = [];
6✔
219
  let totalSize = 0;
6✔
220
  let consumedSize = 0;
6✔
221
  while (consumedSize < fileSize && !stream.readableEnded) {
6✔
222
    for await (const data of stream) {
18✔
223
      if (!Buffer.isBuffer(data)) {
6!
224
        throw new Error('Expecting a chunk of stream to be a Buffer');
×
225
      }
226
      consumedSize += data.length;
6✔
227
      buffers.push(data);
6✔
228
      totalSize += data.length;
6✔
229

230
      if (totalSize < chunkSize) {
6!
231
        continue;
×
232
      }
233

234
      const buffer = Buffer.concat(buffers);
6✔
235

236
      let start = 0;
6✔
237
      while (totalSize >= chunkSize) {
6✔
238
        yield generateByteStreamFromBuffer(
6✔
239
          buffer.subarray(start, start + chunkSize),
240
        );
241
        start += chunkSize;
6✔
242
        totalSize -= chunkSize;
6✔
243
      }
244

245
      buffers = totalSize > 0 ? [buffer.subarray(start)] : [];
6!
246
    }
247
  }
248

249
  if (consumedSize !== fileSize) {
6!
250
    throw new Error(
×
251
      `Stream size ${consumedSize} does not match expected file size ${fileSize}`,
252
    );
253
  }
254
  if (totalSize > 0) {
6✔
255
    yield generateByteStreamFromBuffer(Buffer.concat(buffers));
6✔
256
  }
257
}
258

259
export async function reduceIterator<T, U>(
140✔
260
  iterator: Iterator<T>,
261
  reducer: (accumulator: U, current: T) => Promise<U>,
262
  initialValue: U,
263
): Promise<U> {
264
  let result = initialValue;
6✔
265
  let iteration = await iterator.next();
6✔
266

267
  while (!iteration.done) {
6✔
268
    result = await reducer(result, iteration.value);
12✔
269
    iteration = await iterator.next();
12✔
270
  }
271

272
  return result;
6✔
273
}
274

275
export function prepareParams(map: {
140✔
276
  readonly [key: string]: undefined | string;
277
}): { readonly [key: string]: string } {
278
  if (!map || typeof map !== 'object') {
2,082!
279
    throw new Error('Expecting obj to be an object in prepareParams');
×
280
  }
281
  return Object.fromEntries(
2,082✔
282
    Object.entries(map).filter<[string, string]>(
283
      (entry): entry is [string, string] => typeof entry[1] === 'string',
1,624✔
284
    ),
285
  );
286
}
287

288
export function toString(value: any): string {
140✔
289
  if (typeof value === 'string' || value == null) {
2,230✔
290
    return value;
2,160✔
291
  }
292
  return String(value);
70✔
293
}
294

295
type CancellationToken = AbortSignal;
296

297
/**
298
 * Creates a cancellation token that will be cancelled after a given delay in ms.
299
 *
300
 * @param {number} delay Delay in ms.
301
 * @returns {CancellationToken} Cancellation token.
302
 */
303
export function createTokenAndCancelAfter(delay: number): CancellationToken {
140✔
304
  return AbortSignal.timeout(delay);
2✔
305
}
306

307
export type JwtKey = {
308
  key: string;
309
  passphrase: string;
310
};
311

312
export type JwtAlgorithm =
313
  | 'HS256'
314
  | 'HS384'
315
  | 'HS512'
316
  | 'RS256'
317
  | 'RS384'
318
  | 'RS512'
319
  | 'ES256'
320
  | 'ES384'
321
  | 'ES512'
322
  | 'PS256'
323
  | 'PS384'
324
  | 'PS512'
325
  | 'none';
326

327
export type JwtSignOptions = {
328
  algorithm?: JwtAlgorithm;
329
  keyid?: string | undefined;
330
  expiresIn?: string | number | undefined;
331
  notBefore?: string | number | undefined;
332
  audience?: string | string[] | undefined;
333
  subject?: string | undefined;
334
  issuer?: string | undefined;
335
  jwtid?: string | undefined;
336
};
337

338
/**
339
 * Creates a JWT assertion.
340
 *
341
 * @param claims
342
 * @param key
343
 * @param options
344
 * @returns
345
 */
346
export async function createJwtAssertion(
140✔
347
  claims: {
348
    readonly [key: string]: any;
349
  },
350
  key: JwtKey,
351
  options: JwtSignOptions,
352
): Promise<string> {
353
  const crypto = eval('require')('crypto');
240✔
354
  const privateKey = crypto.createPrivateKey({
240✔
355
    key: key.key,
356
    format: 'pem',
357
    type: 'pkcs8',
358
    passphrase: key.passphrase,
359
  });
360
  const pem = privateKey.export({ type: 'pkcs8', format: 'pem' }).toString();
240✔
361
  const pkcs8 = await importPKCS8(pem, options.algorithm || 'RS256');
240!
362
  let signer = new SignJWT(claims);
240✔
363
  signer = options.audience ? signer.setAudience(options.audience) : signer;
240!
364
  signer = options.expiresIn
240!
365
    ? signer.setExpirationTime(options.expiresIn)
366
    : signer;
367
  signer = options.issuer ? signer.setIssuer(options.issuer) : signer;
240!
368
  signer = options.jwtid ? signer.setJti(options.jwtid) : signer;
240!
369
  signer = options.notBefore ? signer.setNotBefore(options.notBefore) : signer;
240!
370
  signer = options.subject ? signer.setSubject(options.subject) : signer;
240!
371
  signer = options.algorithm
240!
372
    ? signer.setProtectedHeader({ alg: options.algorithm })
373
    : signer;
374
  signer = signer.setIssuedAt();
240✔
375
  return await signer.sign(pkcs8);
240✔
376
}
377

378
/**
379
 * Reads a text file and returns its content.
380
 */
381
export function readTextFromFile(filepath: string): string {
140✔
382
  return eval('require')('fs').readFileSync(filepath, 'utf8');
×
383
}
384

385
/**
386
 * Get current epoch time in seconds.
387
 */
388
export function getEpochTimeInSeconds(): number {
140✔
389
  return Math.floor(Date.now() / 1000);
240✔
390
}
391

392
/**
393
 * Create web agent from proxy agent options.
394
 */
395
export function createAgent(options?: AgentOptions, proxyConfig?: any): Agent {
140✔
396
  if (isBrowser()) {
21,202!
397
    return undefined;
×
398
  }
399
  const ProxyAgent = eval('require')('proxy-agent').ProxyAgent;
21,202✔
400
  let agentOptions = options;
21,202✔
401

402
  if (proxyConfig && proxyConfig.url) {
21,202!
403
    if (!proxyConfig.url.startsWith('http')) {
×
404
      throw new Error('Invalid proxy URL');
×
405
    }
406

407
    const proxyHost = proxyConfig.url.split('//')[1];
×
408
    const proxyAuth =
409
      proxyConfig.username && proxyConfig.password
×
410
        ? `${proxyConfig.username}:${proxyConfig.password}@`
411
        : '';
412
    const proxyUrl = `http://${proxyAuth}${proxyHost}`;
×
413
    agentOptions = Object.assign(
×
414
      { getProxyForUrl: (url: string) => proxyUrl },
×
415
      options || {},
×
416
    );
417
  }
418

419
  return agentOptions ? new ProxyAgent(agentOptions) : new ProxyAgent();
21,202!
420
}
421

422
export async function delayInSeconds(seconds: number): Promise<void> {
140✔
423
  return await new Promise((resolve) => setTimeout(resolve, seconds * 1000));
6✔
424
}
425

426
/**
427
 * Get value from object raw data.
428
 *
429
 * @param obj Object with raw data from which to get the value.
430
 * @param key Key of the value to get.
431
 * @returns Value from object raw data.
432
 */
433
export function getValueFromObjectRawData(obj: any, key: string): any {
140✔
434
  if (obj && typeof obj === 'object' && obj.rawData) {
20✔
435
    return obj.rawData[key];
20✔
436
  }
437
  return undefined;
×
438
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc