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

box / box-typescript-sdk-gen / 14452166668

14 Apr 2025 05:43PM UTC coverage: 42.556%. First build
14452166668

Pull #585

github

web-flow
Merge e90c1fb25 into a7dcaf0f3
Pull Request #585: fix: Modify utils functions for browser (box/box-codegen#686)

4150 of 16757 branches covered (24.77%)

Branch coverage included in aggregate %.

26 of 37 new or added lines in 4 files covered. (70.27%)

15046 of 28351 relevant lines covered (53.07%)

139.77 hits per line

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

68.13
/src/internal/utilsNode.ts
1
import { Buffer } from 'buffer';
225✔
2
import { Readable } from 'stream';
225✔
3
import { SignJWT, importPKCS8 } from 'jose';
225✔
4
import { default as crypto } from 'crypto';
225✔
5
import { default as fs } from 'fs';
225✔
6
import { ProxyAgent } from 'proxy-agent';
225✔
7
import { default as FormData } from 'form-data';
225✔
8
import util from 'util';
225✔
9

10
export { Buffer, Readable as ByteStream, FormData, util as utilLib };
678✔
11
export type AgentOptions = any;
12
export type Agent = any;
13
export type HashName = 'sha1';
14
export type DigestHashType = 'base64';
15

16
export class Hash {
225✔
17
  #hash: any;
36✔
18
  algorithm: HashName;
19

20
  constructor({ algorithm }: { algorithm: HashName }) {
21
    this.algorithm = algorithm;
36✔
22
    this.#hash = crypto.createHash(algorithm);
36✔
23
  }
24

25
  async updateHash(data: Buffer) {
26
    this.#hash.update(data);
54✔
27
  }
28

29
  async digestHash(encoding: DigestHashType = 'base64'): Promise<string> {
36!
30
    return this.#hash.digest(encoding);
36✔
31
  }
32
}
33

34
export function generateByteBuffer(size: number): Buffer {
225✔
35
  return crypto.randomBytes(size);
186✔
36
}
37

38
export function generateReadableStreamFromFile(
225✔
39
  file: any,
40
  chunkSize: number = 1024 * 1024,
×
41
): ReadableStream {
42
  throw new Error('This function is only supported in the browser');
×
43
}
44

45
export function generateByteStreamFromBuffer(
225✔
46
  buffer: Buffer | ArrayBuffer,
47
): Readable {
48
  return Readable.from(Buffer.from(buffer));
2,967✔
49
}
50

51
export function decodeBase64ByteStream(data: string): Readable {
225✔
52
  return Readable.from(Buffer.from(data, 'base64'));
3✔
53
}
54

55
export function stringToByteStream(data: string): Readable {
225✔
56
  return Readable.from(Buffer.from(data, 'ascii'));
9✔
57
}
58

59
export async function readByteStream(byteStream: Readable): Promise<Buffer> {
225✔
60
  const buffers: Buffer[] = [];
435✔
61
  for await (const data of byteStream) {
870✔
62
    buffers.push(data);
435✔
63
  }
64
  return Buffer.concat(buffers);
435✔
65
}
66

67
export async function* iterateChunks(
225✔
68
  stream: Readable,
69
  chunkSize: number,
70
  fileSize: number,
71
): AsyncGenerator<Readable> {
72
  let buffers: Buffer[] = [];
9✔
73
  let totalSize = 0;
9✔
74
  let consumedSize = 0;
9✔
75
  while (consumedSize < fileSize && !stream.readableEnded) {
9✔
76
    for await (const chunk of stream) {
27✔
77
      const data = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
9!
78
      if (!Buffer.isBuffer(data)) {
9!
79
        throw new Error('Expecting a chunk of stream to be a Buffer');
×
80
      }
81
      consumedSize += data.length;
9✔
82
      buffers.push(data);
9✔
83
      totalSize += data.length;
9✔
84

85
      if (totalSize < chunkSize) {
9!
86
        continue;
×
87
      }
88

89
      const buffer = Buffer.concat(buffers);
9✔
90

91
      let start = 0;
9✔
92
      while (totalSize >= chunkSize) {
9✔
93
        yield generateByteStreamFromBuffer(
18✔
94
          buffer.subarray(start, start + chunkSize),
95
        );
96
        start += chunkSize;
18✔
97
        totalSize -= chunkSize;
18✔
98
      }
99

100
      buffers = totalSize > 0 ? [buffer.subarray(start)] : [];
9!
101
    }
102
  }
103

104
  if (consumedSize !== fileSize) {
9!
105
    throw new Error(
×
106
      `Stream size ${consumedSize} does not match expected file size ${fileSize}`,
107
    );
108
  }
109
  if (totalSize > 0) {
9✔
110
    yield generateByteStreamFromBuffer(Buffer.concat(buffers));
9✔
111
  }
112
}
113

114
export type JwtKey = {
115
  key: string;
116
  passphrase: string;
117
};
118

119
export type JwtAlgorithm =
120
  | 'HS256'
121
  | 'HS384'
122
  | 'HS512'
123
  | 'RS256'
124
  | 'RS384'
125
  | 'RS512'
126
  | 'ES256'
127
  | 'ES384'
128
  | 'ES512'
129
  | 'PS256'
130
  | 'PS384'
131
  | 'PS512'
132
  | 'none';
133

134
export type JwtSignOptions = {
135
  algorithm?: JwtAlgorithm;
136
  keyid?: string | undefined;
137
  expiresIn?: string | number | undefined;
138
  notBefore?: string | number | undefined;
139
  audience?: string | string[] | undefined;
140
  subject?: string | undefined;
141
  issuer?: string | undefined;
142
  jwtid?: string | undefined;
143
};
144

145
/**
146
 * Creates a JWT assertion.
147
 *
148
 * @param claims
149
 * @param key
150
 * @param options
151
 * @returns
152
 */
153
export async function createJwtAssertion(
225✔
154
  claims: {
155
    readonly [key: string]: any;
156
  },
157
  key: JwtKey,
158
  options: JwtSignOptions,
159
): Promise<string> {
160
  const privateKey = crypto.createPrivateKey({
399✔
161
    key: key.key,
162
    format: 'pem',
163
    type: 'pkcs8',
164
    passphrase: key.passphrase,
165
  });
166
  const pem = privateKey.export({ type: 'pkcs8', format: 'pem' }).toString();
399✔
167
  const pkcs8 = await importPKCS8(pem, options.algorithm || 'RS256');
399!
168
  let signer = new SignJWT(claims);
399✔
169
  signer = options.audience ? signer.setAudience(options.audience) : signer;
399!
170
  signer = options.expiresIn
399!
171
    ? signer.setExpirationTime(options.expiresIn)
172
    : signer;
173
  signer = options.issuer ? signer.setIssuer(options.issuer) : signer;
399!
174
  signer = options.jwtid ? signer.setJti(options.jwtid) : signer;
399!
175
  signer = options.notBefore ? signer.setNotBefore(options.notBefore) : signer;
399!
176
  signer = options.subject ? signer.setSubject(options.subject) : signer;
399!
177
  signer = options.algorithm
399!
178
    ? signer.setProtectedHeader({ alg: options.algorithm })
179
    : signer;
180
  signer = signer.setIssuedAt();
399✔
181
  return await signer.sign(pkcs8);
399✔
182
}
183

184
/**
185
 * Reads a text file and returns its content.
186
 */
187
export function readTextFromFile(filepath: string): string {
225✔
188
  return fs.readFileSync(filepath, 'utf8');
×
189
}
190

191
/**
192
 * Create web agent from proxy agent options.
193
 */
194
export function createAgent(options?: AgentOptions, proxyConfig?: any): Agent {
225✔
195
  let agentOptions = options;
36,759✔
196

197
  if (proxyConfig && proxyConfig.url) {
36,759!
198
    if (!proxyConfig.url.startsWith('http')) {
×
199
      throw new Error('Invalid proxy URL');
×
200
    }
201

202
    const proxyHost = proxyConfig.url.split('//')[1];
×
203
    const proxyAuth =
204
      proxyConfig.username && proxyConfig.password
×
205
        ? `${proxyConfig.username}:${proxyConfig.password}@`
206
        : '';
207
    const proxyUrl = `http://${proxyAuth}${proxyHost}`;
×
208
    agentOptions = Object.assign(
×
209
      { getProxyForUrl: (url: string) => proxyUrl },
×
210
      options || {},
×
211
    );
212
  }
213

214
  return agentOptions ? new ProxyAgent(agentOptions) : new ProxyAgent();
36,759!
215
}
216

217
/**
218
 * Stringify JSON with escaped multibyte Unicode characters to ensure computed signatures match PHP's default behavior
219
 *
220
 * @param {Object} body - The parsed JSON object
221
 * @returns {string} - Stringified JSON with escaped multibyte Unicode characters
222
 * @private
223
 */
224
export function jsonStringifyWithEscapedUnicode(body: string) {
225✔
225
  return body.replace(
72✔
226
    /[\u007f-\uffff]/g,
227
    (char) => `\\u${`0000${char.charCodeAt(0).toString(16)}`.slice(-4)}`,
195✔
228
  );
229
}
230

231
/**
232
 * Compute the message signature
233
 * @see {@Link https://developer.box.com/en/guides/webhooks/handle/setup-signatures/}
234
 *
235
 * @param {string} body - The request body of the webhook message
236
 * @param {Object} headers - The request headers of the webhook message
237
 * @param {string} signatureKey - The signature to verify the message with
238
 * @returns {?string} - The message signature (or null, if it can't be computed)
239
 * @private
240
 */
241
export async function computeWebhookSignature(
225✔
242
  body: string,
243
  headers: {
244
    [key: string]: string;
245
  },
246
  signatureKey: string,
247
): Promise<string | null> {
248
  const escapedBody = jsonStringifyWithEscapedUnicode(body).replace(
72✔
249
    /\//g,
250
    '\\/',
251
  );
252
  if (headers['box-signature-version'] !== '1') {
72!
253
    return null;
×
254
  }
255
  if (headers['box-signature-algorithm'] !== 'HmacSHA256') {
72!
256
    return null;
×
257
  }
258
  let signature: string | null = null;
72✔
259

260
  let hmac = crypto.createHmac('sha256', signatureKey);
72✔
261
  hmac.update(escapedBody);
72✔
262
  hmac.update(headers['box-delivery-timestamp']);
72✔
263
  signature = hmac.digest('base64');
72✔
264
  return signature;
72✔
265
}
266

267
export function random(min: number, max: number): number {
225✔
268
  return Math.random() * (max - min) + min;
×
269
}
270

271
export async function calculateMD5Hash(data: string | Buffer): Promise<string> {
225✔
272
  return crypto.createHash('sha1').update(data).digest('hex');
180✔
273
}
274

275
export function getEnvVar(name: string): string {
225✔
276
  if (typeof process === 'undefined' || !process.env) {
645!
NEW
277
    throw new Error('This function requires a Node.js environment');
×
278
  }
279
  return process.env[name] || '';
645!
280
}
281

282
export function setEnvVar(name: string, value: string): void {
225✔
NEW
283
  if (typeof process === 'undefined' || !process.env) {
×
NEW
284
    throw new Error('This function requires a Node.js environment');
×
285
  }
NEW
286
  process.env[name] = value;
×
287
}
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

© 2026 Coveralls, Inc