UNPKG

8.42 kBJavaScriptView Raw
1// Copyright 2019 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14import * as path from 'path';
15import * as querystring from 'querystring';
16import { PassThrough } from 'stream';
17import * as url from 'url';
18// eslint-disable-next-line @typescript-eslint/ban-ts-comment
19// @ts-ignore
20import { getPackageJSON } from './package-json-helper.cjs';
21// Done to avoid a problem with mangling of identifiers when using esModuleInterop
22const fileURLToPath = url.fileURLToPath;
23const isEsm = true;
24export function normalize(optionsOrCallback, cb) {
25 const options = (typeof optionsOrCallback === 'object' ? optionsOrCallback : {});
26 const callback = (typeof optionsOrCallback === 'function' ? optionsOrCallback : cb);
27 return { options, callback };
28}
29/**
30 * Flatten an object into an Array of arrays, [[key, value], ..].
31 * Implements Object.entries() for Node.js <8
32 * @internal
33 */
34export function objectEntries(obj) {
35 return Object.keys(obj).map(key => [key, obj[key]]);
36}
37/**
38 * Encode `str` with encodeURIComponent, plus these
39 * reserved characters: `! * ' ( )`.
40 *
41 * See {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent| MDN: fixedEncodeURIComponent}
42 *
43 * @param {string} str The URI component to encode.
44 * @return {string} The encoded string.
45 */
46export function fixedEncodeURIComponent(str) {
47 return encodeURIComponent(str).replace(/[!'()*]/g, c => '%' + c.charCodeAt(0).toString(16).toUpperCase());
48}
49/**
50 * URI encode `uri` for generating signed URLs, using fixedEncodeURIComponent.
51 *
52 * Encode every byte except `A-Z a-Z 0-9 ~ - . _`.
53 *
54 * @param {string} uri The URI to encode.
55 * @param [boolean=false] encodeSlash If `true`, the "/" character is not encoded.
56 * @return {string} The encoded string.
57 */
58export function encodeURI(uri, encodeSlash) {
59 // Split the string by `/`, and conditionally rejoin them with either
60 // %2F if encodeSlash is `true`, or '/' if `false`.
61 return uri
62 .split('/')
63 .map(fixedEncodeURIComponent)
64 .join(encodeSlash ? '%2F' : '/');
65}
66/**
67 * Serialize an object to a URL query string using util.encodeURI(uri, true).
68 * @param {string} url The object to serialize.
69 * @return {string} Serialized string.
70 */
71export function qsStringify(qs) {
72 return querystring.stringify(qs, '&', '=', {
73 encodeURIComponent: (component) => encodeURI(component, true),
74 });
75}
76export function objectKeyToLowercase(object) {
77 const newObj = {};
78 for (let key of Object.keys(object)) {
79 const value = object[key];
80 key = key.toLowerCase();
81 newObj[key] = value;
82 }
83 return newObj;
84}
85/**
86 * JSON encode str, with unicode \u+ representation.
87 * @param {object} obj The object to encode.
88 * @return {string} Serialized string.
89 */
90export function unicodeJSONStringify(obj) {
91 return JSON.stringify(obj).replace(/[\u0080-\uFFFF]/g, (char) => '\\u' + ('0000' + char.charCodeAt(0).toString(16)).slice(-4));
92}
93/**
94 * Converts the given objects keys to snake_case
95 * @param {object} obj object to convert keys to snake case.
96 * @returns {object} object with keys converted to snake case.
97 */
98export function convertObjKeysToSnakeCase(obj) {
99 if (obj instanceof Date || obj instanceof RegExp) {
100 return obj;
101 }
102 if (Array.isArray(obj)) {
103 return obj.map(convertObjKeysToSnakeCase);
104 }
105 if (obj instanceof Object) {
106 return Object.keys(obj).reduce((acc, cur) => {
107 const s = cur[0].toLocaleLowerCase() +
108 cur.slice(1).replace(/([A-Z]+)/g, (match, p1) => {
109 return `_${p1.toLowerCase()}`;
110 });
111 acc[s] = convertObjKeysToSnakeCase(obj[cur]);
112 return acc;
113 }, Object());
114 }
115 return obj;
116}
117/**
118 * Formats the provided date object as a UTC ISO string.
119 * @param {Date} dateTimeToFormat date object to be formatted.
120 * @param {boolean} includeTime flag to include hours, minutes, seconds in output.
121 * @param {string} dateDelimiter delimiter between date components.
122 * @param {string} timeDelimiter delimiter between time components.
123 * @returns {string} UTC ISO format of provided date obect.
124 */
125export function formatAsUTCISO(dateTimeToFormat, includeTime = false, dateDelimiter = '', timeDelimiter = '') {
126 const year = dateTimeToFormat.getUTCFullYear();
127 const month = dateTimeToFormat.getUTCMonth() + 1;
128 const day = dateTimeToFormat.getUTCDate();
129 const hour = dateTimeToFormat.getUTCHours();
130 const minute = dateTimeToFormat.getUTCMinutes();
131 const second = dateTimeToFormat.getUTCSeconds();
132 let resultString = `${year.toString().padStart(4, '0')}${dateDelimiter}${month
133 .toString()
134 .padStart(2, '0')}${dateDelimiter}${day.toString().padStart(2, '0')}`;
135 if (includeTime) {
136 resultString = `${resultString}T${hour
137 .toString()
138 .padStart(2, '0')}${timeDelimiter}${minute
139 .toString()
140 .padStart(2, '0')}${timeDelimiter}${second.toString().padStart(2, '0')}Z`;
141 }
142 return resultString;
143}
144/**
145 * Examines the runtime environment and returns the appropriate tracking string.
146 * @returns {string} metrics tracking string based on the current runtime environment.
147 */
148export function getRuntimeTrackingString() {
149 if (
150 // eslint-disable-next-line @typescript-eslint/ban-ts-comment
151 // @ts-ignore
152 globalThis.Deno &&
153 // eslint-disable-next-line @typescript-eslint/ban-ts-comment
154 // @ts-ignore
155 globalThis.Deno.version &&
156 // eslint-disable-next-line @typescript-eslint/ban-ts-comment
157 // @ts-ignore
158 globalThis.Deno.version.deno) {
159 // eslint-disable-next-line @typescript-eslint/ban-ts-comment
160 // @ts-ignore
161 return `gl-deno/${globalThis.Deno.version.deno}`;
162 }
163 else {
164 return `gl-node/${process.versions.node}`;
165 }
166}
167/**
168 * Looks at package.json and creates the user-agent string to be applied to request headers.
169 * @returns {string} user agent string.
170 */
171export function getUserAgentString() {
172 const pkg = getPackageJSON();
173 const hyphenatedPackageName = pkg.name
174 .replace('@google-cloud', 'gcloud-node') // For legacy purposes.
175 .replace('/', '-'); // For UA spec-compliance purposes.
176 return hyphenatedPackageName + '/' + pkg.version;
177}
178export function getDirName() {
179 let dirToUse = '';
180 try {
181 dirToUse = __dirname;
182 }
183 catch (e) {
184 // eslint-disable-next-line @typescript-eslint/ban-ts-comment
185 // @ts-ignore
186 dirToUse = path.dirname(fileURLToPath(import.meta.url));
187 }
188 return dirToUse;
189}
190export function getModuleFormat() {
191 return isEsm ? 'ESM' : 'CJS';
192}
193export class PassThroughShim extends PassThrough {
194 constructor() {
195 super(...arguments);
196 this.shouldEmitReading = true;
197 this.shouldEmitWriting = true;
198 }
199 _read(size) {
200 if (this.shouldEmitReading) {
201 this.emit('reading');
202 this.shouldEmitReading = false;
203 }
204 super._read(size);
205 }
206 _write(chunk, encoding, callback) {
207 if (this.shouldEmitWriting) {
208 this.emit('writing');
209 this.shouldEmitWriting = false;
210 }
211 // Per the nodejs documention, callback must be invoked on the next tick
212 process.nextTick(() => {
213 super._write(chunk, encoding, callback);
214 });
215 }
216 _final(callback) {
217 // If the stream is empty (i.e. empty file) final will be invoked before _read / _write
218 // and we should still emit the proper events.
219 if (this.shouldEmitReading) {
220 this.emit('reading');
221 this.shouldEmitReading = false;
222 }
223 if (this.shouldEmitWriting) {
224 this.emit('writing');
225 this.shouldEmitWriting = false;
226 }
227 callback(null);
228 }
229}