util.js 9.4 KB

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