UNPKG

29.4 kBJavaScriptView Raw
1"use strict";
2/*!
3 * Copyright 2022 Google LLC. All Rights Reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
18 if (k2 === undefined) k2 = k;
19 var desc = Object.getOwnPropertyDescriptor(m, k);
20 if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
21 desc = { enumerable: true, get: function() { return m[k]; } };
22 }
23 Object.defineProperty(o, k2, desc);
24}) : (function(o, m, k, k2) {
25 if (k2 === undefined) k2 = k;
26 o[k2] = m[k];
27}));
28var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
29 Object.defineProperty(o, "default", { enumerable: true, value: v });
30}) : function(o, v) {
31 o["default"] = v;
32});
33var __importStar = (this && this.__importStar) || function (mod) {
34 if (mod && mod.__esModule) return mod;
35 var result = {};
36 if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
37 __setModuleDefault(result, mod);
38 return result;
39};
40var __importDefault = (this && this.__importDefault) || function (mod) {
41 return (mod && mod.__esModule) ? mod : { "default": mod };
42};
43Object.defineProperty(exports, "__esModule", { value: true });
44exports.util = exports.Util = exports.PartialFailureError = exports.ApiError = exports.GCCL_GCS_CMD_KEY = void 0;
45/*!
46 * @module common/util
47 */
48const projectify_1 = require("@google-cloud/projectify");
49const ent = __importStar(require("ent"));
50const google_auth_library_1 = require("google-auth-library");
51const retry_request_1 = __importDefault(require("retry-request"));
52const stream_1 = require("stream");
53const teeny_request_1 = require("teeny-request");
54const uuid = __importStar(require("uuid"));
55const service_js_1 = require("./service.js");
56const util_js_1 = require("../util.js");
57const duplexify_1 = __importDefault(require("duplexify"));
58// eslint-disable-next-line @typescript-eslint/ban-ts-comment
59// @ts-ignore
60const package_json_helper_cjs_1 = require("../package-json-helper.cjs");
61const packageJson = (0, package_json_helper_cjs_1.getPackageJSON)();
62/**
63 * A unique symbol for providing a `gccl-gcs-cmd` value
64 * for the `X-Goog-API-Client` header.
65 *
66 * E.g. the `V` in `X-Goog-API-Client: gccl-gcs-cmd/V`
67 **/
68exports.GCCL_GCS_CMD_KEY = Symbol.for('GCCL_GCS_CMD');
69const requestDefaults = {
70 timeout: 60000,
71 gzip: true,
72 forever: true,
73 pool: {
74 maxSockets: Infinity,
75 },
76};
77/**
78 * Default behavior: Automatically retry retriable server errors.
79 *
80 * @const {boolean}
81 * @private
82 */
83const AUTO_RETRY_DEFAULT = true;
84/**
85 * Default behavior: Only attempt to retry retriable errors 3 times.
86 *
87 * @const {number}
88 * @private
89 */
90const MAX_RETRY_DEFAULT = 3;
91/**
92 * Custom error type for API errors.
93 *
94 * @param {object} errorBody - Error object.
95 */
96class ApiError extends Error {
97 constructor(errorBodyOrMessage) {
98 super();
99 if (typeof errorBodyOrMessage !== 'object') {
100 this.message = errorBodyOrMessage || '';
101 return;
102 }
103 const errorBody = errorBodyOrMessage;
104 this.code = errorBody.code;
105 this.errors = errorBody.errors;
106 this.response = errorBody.response;
107 try {
108 this.errors = JSON.parse(this.response.body).error.errors;
109 }
110 catch (e) {
111 this.errors = errorBody.errors;
112 }
113 this.message = ApiError.createMultiErrorMessage(errorBody, this.errors);
114 Error.captureStackTrace(this);
115 }
116 /**
117 * Pieces together an error message by combining all unique error messages
118 * returned from a single GoogleError
119 *
120 * @private
121 *
122 * @param {GoogleErrorBody} err The original error.
123 * @param {GoogleInnerError[]} [errors] Inner errors, if any.
124 * @returns {string}
125 */
126 static createMultiErrorMessage(err, errors) {
127 const messages = new Set();
128 if (err.message) {
129 messages.add(err.message);
130 }
131 if (errors && errors.length) {
132 errors.forEach(({ message }) => messages.add(message));
133 }
134 else if (err.response && err.response.body) {
135 messages.add(ent.decode(err.response.body.toString()));
136 }
137 else if (!err.message) {
138 messages.add('A failure occurred during this request.');
139 }
140 let messageArr = Array.from(messages);
141 if (messageArr.length > 1) {
142 messageArr = messageArr.map((message, i) => ` ${i + 1}. ${message}`);
143 messageArr.unshift('Multiple errors occurred during the request. Please see the `errors` array for complete details.\n');
144 messageArr.push('\n');
145 }
146 return messageArr.join('\n');
147 }
148}
149exports.ApiError = ApiError;
150/**
151 * Custom error type for partial errors returned from the API.
152 *
153 * @param {object} b - Error object.
154 */
155class PartialFailureError extends Error {
156 constructor(b) {
157 super();
158 const errorObject = b;
159 this.errors = errorObject.errors;
160 this.name = 'PartialFailureError';
161 this.response = errorObject.response;
162 this.message = ApiError.createMultiErrorMessage(errorObject, this.errors);
163 }
164}
165exports.PartialFailureError = PartialFailureError;
166class Util {
167 constructor() {
168 this.ApiError = ApiError;
169 this.PartialFailureError = PartialFailureError;
170 }
171 /**
172 * No op.
173 *
174 * @example
175 * function doSomething(callback) {
176 * callback = callback || noop;
177 * }
178 */
179 noop() { }
180 /**
181 * Uniformly process an API response.
182 *
183 * @param {*} err - Error value.
184 * @param {*} resp - Response value.
185 * @param {*} body - Body value.
186 * @param {function} callback - The callback function.
187 */
188 handleResp(err, resp, body, callback) {
189 callback = callback || util.noop;
190 const parsedResp = {
191 err: err || null,
192 ...(resp && util.parseHttpRespMessage(resp)),
193 ...(body && util.parseHttpRespBody(body)),
194 };
195 // Assign the parsed body to resp.body, even if { json: false } was passed
196 // as a request option.
197 // We assume that nobody uses the previously unparsed value of resp.body.
198 if (!parsedResp.err && resp && typeof parsedResp.body === 'object') {
199 parsedResp.resp.body = parsedResp.body;
200 }
201 if (parsedResp.err && resp) {
202 parsedResp.err.response = resp;
203 }
204 callback(parsedResp.err, parsedResp.body, parsedResp.resp);
205 }
206 /**
207 * Sniff an incoming HTTP response message for errors.
208 *
209 * @param {object} httpRespMessage - An incoming HTTP response message from `request`.
210 * @return {object} parsedHttpRespMessage - The parsed response.
211 * @param {?error} parsedHttpRespMessage.err - An error detected.
212 * @param {object} parsedHttpRespMessage.resp - The original response object.
213 */
214 parseHttpRespMessage(httpRespMessage) {
215 const parsedHttpRespMessage = {
216 resp: httpRespMessage,
217 };
218 if (httpRespMessage.statusCode < 200 || httpRespMessage.statusCode > 299) {
219 // Unknown error. Format according to ApiError standard.
220 parsedHttpRespMessage.err = new ApiError({
221 errors: new Array(),
222 code: httpRespMessage.statusCode,
223 message: httpRespMessage.statusMessage,
224 response: httpRespMessage,
225 });
226 }
227 return parsedHttpRespMessage;
228 }
229 /**
230 * Parse the response body from an HTTP request.
231 *
232 * @param {object} body - The response body.
233 * @return {object} parsedHttpRespMessage - The parsed response.
234 * @param {?error} parsedHttpRespMessage.err - An error detected.
235 * @param {object} parsedHttpRespMessage.body - The original body value provided
236 * will try to be JSON.parse'd. If it's successful, the parsed value will
237 * be returned here, otherwise the original value and an error will be returned.
238 */
239 parseHttpRespBody(body) {
240 const parsedHttpRespBody = {
241 body,
242 };
243 if (typeof body === 'string') {
244 try {
245 parsedHttpRespBody.body = JSON.parse(body);
246 }
247 catch (err) {
248 parsedHttpRespBody.body = body;
249 }
250 }
251 if (parsedHttpRespBody.body && parsedHttpRespBody.body.error) {
252 // Error from JSON API.
253 parsedHttpRespBody.err = new ApiError(parsedHttpRespBody.body.error);
254 }
255 return parsedHttpRespBody;
256 }
257 /**
258 * Take a Duplexify stream, fetch an authenticated connection header, and
259 * create an outgoing writable stream.
260 *
261 * @param {Duplexify} dup - Duplexify stream.
262 * @param {object} options - Configuration object.
263 * @param {module:common/connection} options.connection - A connection instance used to get a token with and send the request through.
264 * @param {object} options.metadata - Metadata to send at the head of the request.
265 * @param {object} options.request - Request object, in the format of a standard Node.js http.request() object.
266 * @param {string=} options.request.method - Default: "POST".
267 * @param {string=} options.request.qs.uploadType - Default: "multipart".
268 * @param {string=} options.streamContentType - Default: "application/octet-stream".
269 * @param {function} onComplete - Callback, executed after the writable Request stream has completed.
270 */
271 makeWritableStream(dup, options, onComplete) {
272 var _a;
273 onComplete = onComplete || util.noop;
274 const writeStream = new ProgressStream();
275 writeStream.on('progress', evt => dup.emit('progress', evt));
276 dup.setWritable(writeStream);
277 const defaultReqOpts = {
278 method: 'POST',
279 qs: {
280 uploadType: 'multipart',
281 },
282 timeout: 0,
283 maxRetries: 0,
284 };
285 const metadata = options.metadata || {};
286 const reqOpts = {
287 ...defaultReqOpts,
288 ...options.request,
289 qs: {
290 ...defaultReqOpts.qs,
291 ...(_a = options.request) === null || _a === void 0 ? void 0 : _a.qs,
292 },
293 multipart: [
294 {
295 'Content-Type': 'application/json',
296 body: JSON.stringify(metadata),
297 },
298 {
299 'Content-Type': metadata.contentType || 'application/octet-stream',
300 body: writeStream,
301 },
302 ],
303 };
304 options.makeAuthenticatedRequest(reqOpts, {
305 onAuthenticated(err, authenticatedReqOpts) {
306 if (err) {
307 dup.destroy(err);
308 return;
309 }
310 requestDefaults.headers = util._getDefaultHeaders(reqOpts[exports.GCCL_GCS_CMD_KEY]);
311 const request = teeny_request_1.teenyRequest.defaults(requestDefaults);
312 request(authenticatedReqOpts, (err, resp, body) => {
313 util.handleResp(err, resp, body, (err, data) => {
314 if (err) {
315 dup.destroy(err);
316 return;
317 }
318 dup.emit('response', resp);
319 onComplete(data);
320 });
321 });
322 },
323 });
324 }
325 /**
326 * Returns true if the API request should be retried, given the error that was
327 * given the first time the request was attempted. This is used for rate limit
328 * related errors as well as intermittent server errors.
329 *
330 * @param {error} err - The API error to check if it is appropriate to retry.
331 * @return {boolean} True if the API request should be retried, false otherwise.
332 */
333 shouldRetryRequest(err) {
334 if (err) {
335 if ([408, 429, 500, 502, 503, 504].indexOf(err.code) !== -1) {
336 return true;
337 }
338 if (err.errors) {
339 for (const e of err.errors) {
340 const reason = e.reason;
341 if (reason === 'rateLimitExceeded') {
342 return true;
343 }
344 if (reason === 'userRateLimitExceeded') {
345 return true;
346 }
347 if (reason && reason.includes('EAI_AGAIN')) {
348 return true;
349 }
350 }
351 }
352 }
353 return false;
354 }
355 /**
356 * Get a function for making authenticated requests.
357 *
358 * @param {object} config - Configuration object.
359 * @param {boolean=} config.autoRetry - Automatically retry requests if the
360 * response is related to rate limits or certain intermittent server
361 * errors. We will exponentially backoff subsequent requests by default.
362 * (default: true)
363 * @param {object=} config.credentials - Credentials object.
364 * @param {boolean=} config.customEndpoint - If true, just return the provided request options. Default: false.
365 * @param {boolean=} config.useAuthWithCustomEndpoint - If true, will authenticate when using a custom endpoint. Default: false.
366 * @param {string=} config.email - Account email address, required for PEM/P12 usage.
367 * @param {number=} config.maxRetries - Maximum number of automatic retries attempted before returning the error. (default: 3)
368 * @param {string=} config.keyFile - Path to a .json, .pem, or .p12 keyfile.
369 * @param {array} config.scopes - Array of scopes required for the API.
370 */
371 makeAuthenticatedRequestFactory(config) {
372 const googleAutoAuthConfig = { ...config };
373 if (googleAutoAuthConfig.projectId === service_js_1.DEFAULT_PROJECT_ID_TOKEN) {
374 delete googleAutoAuthConfig.projectId;
375 }
376 let authClient;
377 if (googleAutoAuthConfig.authClient instanceof google_auth_library_1.GoogleAuth) {
378 // Use an existing `GoogleAuth`
379 authClient = googleAutoAuthConfig.authClient;
380 }
381 else {
382 // Pass an `AuthClient` & `clientOptions` to `GoogleAuth`, if available
383 authClient = new google_auth_library_1.GoogleAuth({
384 ...googleAutoAuthConfig,
385 authClient: googleAutoAuthConfig.authClient,
386 clientOptions: googleAutoAuthConfig.clientOptions,
387 });
388 }
389 function makeAuthenticatedRequest(reqOpts, optionsOrCallback) {
390 let stream;
391 let projectId;
392 const reqConfig = { ...config };
393 let activeRequest_;
394 if (!optionsOrCallback) {
395 stream = (0, duplexify_1.default)();
396 reqConfig.stream = stream;
397 }
398 const options = typeof optionsOrCallback === 'object' ? optionsOrCallback : undefined;
399 const callback = typeof optionsOrCallback === 'function' ? optionsOrCallback : undefined;
400 async function setProjectId() {
401 projectId = await authClient.getProjectId();
402 }
403 const onAuthenticated = async (err, authenticatedReqOpts) => {
404 const authLibraryError = err;
405 const autoAuthFailed = err &&
406 typeof err.message === 'string' &&
407 err.message.indexOf('Could not load the default credentials') > -1;
408 if (autoAuthFailed) {
409 // Even though authentication failed, the API might not actually
410 // care.
411 authenticatedReqOpts = reqOpts;
412 }
413 if (!err || autoAuthFailed) {
414 try {
415 // Try with existing `projectId` value
416 authenticatedReqOpts = util.decorateRequest(authenticatedReqOpts, projectId);
417 err = null;
418 }
419 catch (e) {
420 if (e instanceof projectify_1.MissingProjectIdError) {
421 // A `projectId` was required, but we don't have one.
422 try {
423 // Attempt to get the `projectId`
424 await setProjectId();
425 authenticatedReqOpts = util.decorateRequest(authenticatedReqOpts, projectId);
426 err = null;
427 }
428 catch (e) {
429 // Re-use the "Could not load the default credentials error" if
430 // auto auth failed.
431 err = err || e;
432 }
433 }
434 else {
435 // Some other error unrelated to missing `projectId`
436 err = err || e;
437 }
438 }
439 }
440 if (err) {
441 if (stream) {
442 stream.destroy(err);
443 }
444 else {
445 const fn = options && options.onAuthenticated
446 ? options.onAuthenticated
447 : callback;
448 fn(err);
449 }
450 return;
451 }
452 if (options && options.onAuthenticated) {
453 options.onAuthenticated(null, authenticatedReqOpts);
454 }
455 else {
456 activeRequest_ = util.makeRequest(authenticatedReqOpts, reqConfig, (apiResponseError, ...params) => {
457 if (apiResponseError &&
458 apiResponseError.code === 401 &&
459 authLibraryError) {
460 // Re-use the "Could not load the default credentials error" if
461 // the API request failed due to missing credentials.
462 apiResponseError = authLibraryError;
463 }
464 callback(apiResponseError, ...params);
465 });
466 }
467 };
468 const prepareRequest = async () => {
469 try {
470 const getProjectId = async () => {
471 if (config.projectId &&
472 config.projectId !== service_js_1.DEFAULT_PROJECT_ID_TOKEN) {
473 // The user provided a project ID. We don't need to check with the
474 // auth client, it could be incorrect.
475 return config.projectId;
476 }
477 if (config.projectIdRequired === false) {
478 // A projectId is not required. Return the default.
479 return service_js_1.DEFAULT_PROJECT_ID_TOKEN;
480 }
481 return setProjectId();
482 };
483 const authorizeRequest = async () => {
484 if (reqConfig.customEndpoint &&
485 !reqConfig.useAuthWithCustomEndpoint) {
486 // Using a custom API override. Do not use `google-auth-library` for
487 // authentication. (ex: connecting to a local Datastore server)
488 return reqOpts;
489 }
490 else {
491 return authClient.authorizeRequest(reqOpts);
492 }
493 };
494 const [_projectId, authorizedReqOpts] = await Promise.all([
495 getProjectId(),
496 authorizeRequest(),
497 ]);
498 if (_projectId) {
499 projectId = _projectId;
500 }
501 return onAuthenticated(null, authorizedReqOpts);
502 }
503 catch (e) {
504 return onAuthenticated(e);
505 }
506 };
507 prepareRequest();
508 if (stream) {
509 return stream;
510 }
511 return {
512 abort() {
513 setImmediate(() => {
514 if (activeRequest_) {
515 activeRequest_.abort();
516 activeRequest_ = null;
517 }
518 });
519 },
520 };
521 }
522 const mar = makeAuthenticatedRequest;
523 mar.getCredentials = authClient.getCredentials.bind(authClient);
524 mar.authClient = authClient;
525 return mar;
526 }
527 /**
528 * Make a request through the `retryRequest` module with built-in error
529 * handling and exponential back off.
530 *
531 * @param {object} reqOpts - Request options in the format `request` expects.
532 * @param {object=} config - Configuration object.
533 * @param {boolean=} config.autoRetry - Automatically retry requests if the
534 * response is related to rate limits or certain intermittent server
535 * errors. We will exponentially backoff subsequent requests by default.
536 * (default: true)
537 * @param {number=} config.maxRetries - Maximum number of automatic retries
538 * attempted before returning the error. (default: 3)
539 * @param {object=} config.request - HTTP module for request calls.
540 * @param {function} callback - The callback function.
541 */
542 makeRequest(reqOpts, config, callback) {
543 var _a, _b, _c, _d, _e;
544 let autoRetryValue = AUTO_RETRY_DEFAULT;
545 if (config.autoRetry !== undefined) {
546 autoRetryValue = config.autoRetry;
547 }
548 else if (((_a = config.retryOptions) === null || _a === void 0 ? void 0 : _a.autoRetry) !== undefined) {
549 autoRetryValue = config.retryOptions.autoRetry;
550 }
551 let maxRetryValue = MAX_RETRY_DEFAULT;
552 if (config.maxRetries !== undefined) {
553 maxRetryValue = config.maxRetries;
554 }
555 else if (((_b = config.retryOptions) === null || _b === void 0 ? void 0 : _b.maxRetries) !== undefined) {
556 maxRetryValue = config.retryOptions.maxRetries;
557 }
558 requestDefaults.headers = this._getDefaultHeaders(reqOpts[exports.GCCL_GCS_CMD_KEY]);
559 const options = {
560 request: teeny_request_1.teenyRequest.defaults(requestDefaults),
561 retries: autoRetryValue !== false ? maxRetryValue : 0,
562 noResponseRetries: autoRetryValue !== false ? maxRetryValue : 0,
563 shouldRetryFn(httpRespMessage) {
564 var _a, _b;
565 const err = util.parseHttpRespMessage(httpRespMessage).err;
566 if ((_a = config.retryOptions) === null || _a === void 0 ? void 0 : _a.retryableErrorFn) {
567 return err && ((_b = config.retryOptions) === null || _b === void 0 ? void 0 : _b.retryableErrorFn(err));
568 }
569 return err && util.shouldRetryRequest(err);
570 },
571 maxRetryDelay: (_c = config.retryOptions) === null || _c === void 0 ? void 0 : _c.maxRetryDelay,
572 retryDelayMultiplier: (_d = config.retryOptions) === null || _d === void 0 ? void 0 : _d.retryDelayMultiplier,
573 totalTimeout: (_e = config.retryOptions) === null || _e === void 0 ? void 0 : _e.totalTimeout,
574 };
575 if (typeof reqOpts.maxRetries === 'number') {
576 options.retries = reqOpts.maxRetries;
577 options.noResponseRetries = reqOpts.maxRetries;
578 }
579 if (!config.stream) {
580 return (0, retry_request_1.default)(reqOpts, options,
581 // eslint-disable-next-line @typescript-eslint/no-explicit-any
582 (err, response, body) => {
583 util.handleResp(err, response, body, callback);
584 });
585 }
586 const dup = config.stream;
587 // eslint-disable-next-line @typescript-eslint/no-explicit-any
588 let requestStream;
589 const isGetRequest = (reqOpts.method || 'GET').toUpperCase() === 'GET';
590 if (isGetRequest) {
591 requestStream = (0, retry_request_1.default)(reqOpts, options);
592 dup.setReadable(requestStream);
593 }
594 else {
595 // Streaming writable HTTP requests cannot be retried.
596 requestStream = options.request(reqOpts);
597 dup.setWritable(requestStream);
598 }
599 // Replay the Request events back to the stream.
600 requestStream
601 .on('error', dup.destroy.bind(dup))
602 .on('response', dup.emit.bind(dup, 'response'))
603 .on('complete', dup.emit.bind(dup, 'complete'));
604 dup.abort = requestStream.abort;
605 return dup;
606 }
607 /**
608 * Decorate the options about to be made in a request.
609 *
610 * @param {object} reqOpts - The options to be passed to `request`.
611 * @param {string} projectId - The project ID.
612 * @return {object} reqOpts - The decorated reqOpts.
613 */
614 decorateRequest(reqOpts, projectId) {
615 delete reqOpts.autoPaginate;
616 delete reqOpts.autoPaginateVal;
617 delete reqOpts.objectMode;
618 if (reqOpts.qs !== null && typeof reqOpts.qs === 'object') {
619 delete reqOpts.qs.autoPaginate;
620 delete reqOpts.qs.autoPaginateVal;
621 reqOpts.qs = (0, projectify_1.replaceProjectIdToken)(reqOpts.qs, projectId);
622 }
623 if (Array.isArray(reqOpts.multipart)) {
624 reqOpts.multipart = reqOpts.multipart.map(part => {
625 return (0, projectify_1.replaceProjectIdToken)(part, projectId);
626 });
627 }
628 if (reqOpts.json !== null && typeof reqOpts.json === 'object') {
629 delete reqOpts.json.autoPaginate;
630 delete reqOpts.json.autoPaginateVal;
631 reqOpts.json = (0, projectify_1.replaceProjectIdToken)(reqOpts.json, projectId);
632 }
633 reqOpts.uri = (0, projectify_1.replaceProjectIdToken)(reqOpts.uri, projectId);
634 return reqOpts;
635 }
636 // eslint-disable-next-line @typescript-eslint/no-explicit-any
637 isCustomType(unknown, module) {
638 function getConstructorName(obj) {
639 return obj.constructor && obj.constructor.name.toLowerCase();
640 }
641 const moduleNameParts = module.split('/');
642 const parentModuleName = moduleNameParts[0] && moduleNameParts[0].toLowerCase();
643 const subModuleName = moduleNameParts[1] && moduleNameParts[1].toLowerCase();
644 if (subModuleName && getConstructorName(unknown) !== subModuleName) {
645 return false;
646 }
647 let walkingModule = unknown;
648 // eslint-disable-next-line no-constant-condition
649 while (true) {
650 if (getConstructorName(walkingModule) === parentModuleName) {
651 return true;
652 }
653 walkingModule = walkingModule.parent;
654 if (!walkingModule) {
655 return false;
656 }
657 }
658 }
659 /**
660 * Given two parameters, figure out if this is either:
661 * - Just a callback function
662 * - An options object, and then a callback function
663 * @param optionsOrCallback An options object or callback.
664 * @param cb A potentially undefined callback.
665 */
666 maybeOptionsOrCallback(optionsOrCallback, cb) {
667 return typeof optionsOrCallback === 'function'
668 ? [{}, optionsOrCallback]
669 : [optionsOrCallback, cb];
670 }
671 _getDefaultHeaders(gcclGcsCmd) {
672 const headers = {
673 'User-Agent': (0, util_js_1.getUserAgentString)(),
674 'x-goog-api-client': `${(0, util_js_1.getRuntimeTrackingString)()} gccl/${packageJson.version}-${(0, util_js_1.getModuleFormat)()} gccl-invocation-id/${uuid.v4()}`,
675 };
676 if (gcclGcsCmd) {
677 headers['x-goog-api-client'] += ` gccl-gcs-cmd/${gcclGcsCmd}`;
678 }
679 return headers;
680 }
681}
682exports.Util = Util;
683/**
684 * Basic Passthrough Stream that records the number of bytes read
685 * every time the cursor is moved.
686 */
687class ProgressStream extends stream_1.Transform {
688 constructor() {
689 super(...arguments);
690 this.bytesRead = 0;
691 }
692 // eslint-disable-next-line @typescript-eslint/no-explicit-any
693 _transform(chunk, encoding, callback) {
694 this.bytesRead += chunk.length;
695 this.emit('progress', { bytesWritten: this.bytesRead, contentLength: '*' });
696 this.push(chunk);
697 callback();
698 }
699}
700const util = new Util();
701exports.util = util;