You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
186 lines
7.0 KiB
186 lines
7.0 KiB
"use strict";
|
|
// Copyright 2019 Google LLC
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.PassThroughShim = exports.formatAsUTCISO = exports.convertObjKeysToSnakeCase = exports.unicodeJSONStringify = exports.objectKeyToLowercase = exports.qsStringify = exports.encodeURI = exports.fixedEncodeURIComponent = exports.objectEntries = exports.normalize = void 0;
|
|
const querystring = require("querystring");
|
|
const stream_1 = require("stream");
|
|
function normalize(optionsOrCallback, cb) {
|
|
const options = (typeof optionsOrCallback === 'object' ? optionsOrCallback : {});
|
|
const callback = (typeof optionsOrCallback === 'function' ? optionsOrCallback : cb);
|
|
return { options, callback };
|
|
}
|
|
exports.normalize = normalize;
|
|
/**
|
|
* Flatten an object into an Array of arrays, [[key, value], ..].
|
|
* Implements Object.entries() for Node.js <8
|
|
* @internal
|
|
*/
|
|
function objectEntries(obj) {
|
|
return Object.keys(obj).map(key => [key, obj[key]]);
|
|
}
|
|
exports.objectEntries = objectEntries;
|
|
/**
|
|
* Encode `str` with encodeURIComponent, plus these
|
|
* reserved characters: `! * ' ( )`.
|
|
*
|
|
* See {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent| MDN: fixedEncodeURIComponent}
|
|
*
|
|
* @param {string} str The URI component to encode.
|
|
* @return {string} The encoded string.
|
|
*/
|
|
function fixedEncodeURIComponent(str) {
|
|
return encodeURIComponent(str).replace(/[!'()*]/g, c => '%' + c.charCodeAt(0).toString(16).toUpperCase());
|
|
}
|
|
exports.fixedEncodeURIComponent = fixedEncodeURIComponent;
|
|
/**
|
|
* URI encode `uri` for generating signed URLs, using fixedEncodeURIComponent.
|
|
*
|
|
* Encode every byte except `A-Z a-Z 0-9 ~ - . _`.
|
|
*
|
|
* @param {string} uri The URI to encode.
|
|
* @param [boolean=false] encodeSlash If `true`, the "/" character is not encoded.
|
|
* @return {string} The encoded string.
|
|
*/
|
|
function encodeURI(uri, encodeSlash) {
|
|
// Split the string by `/`, and conditionally rejoin them with either
|
|
// %2F if encodeSlash is `true`, or '/' if `false`.
|
|
return uri
|
|
.split('/')
|
|
.map(fixedEncodeURIComponent)
|
|
.join(encodeSlash ? '%2F' : '/');
|
|
}
|
|
exports.encodeURI = encodeURI;
|
|
/**
|
|
* Serialize an object to a URL query string using util.encodeURI(uri, true).
|
|
* @param {string} url The object to serialize.
|
|
* @return {string} Serialized string.
|
|
*/
|
|
function qsStringify(qs) {
|
|
return querystring.stringify(qs, '&', '=', {
|
|
encodeURIComponent: (component) => encodeURI(component, true),
|
|
});
|
|
}
|
|
exports.qsStringify = qsStringify;
|
|
function objectKeyToLowercase(object) {
|
|
const newObj = {};
|
|
for (let key of Object.keys(object)) {
|
|
const value = object[key];
|
|
key = key.toLowerCase();
|
|
newObj[key] = value;
|
|
}
|
|
return newObj;
|
|
}
|
|
exports.objectKeyToLowercase = objectKeyToLowercase;
|
|
/**
|
|
* JSON encode str, with unicode \u+ representation.
|
|
* @param {object} obj The object to encode.
|
|
* @return {string} Serialized string.
|
|
*/
|
|
function unicodeJSONStringify(obj) {
|
|
return JSON.stringify(obj).replace(/[\u0080-\uFFFF]/g, (char) => '\\u' + ('0000' + char.charCodeAt(0).toString(16)).slice(-4));
|
|
}
|
|
exports.unicodeJSONStringify = unicodeJSONStringify;
|
|
/**
|
|
* Converts the given objects keys to snake_case
|
|
* @param {object} obj object to convert keys to snake case.
|
|
* @returns {object} object with keys converted to snake case.
|
|
*/
|
|
function convertObjKeysToSnakeCase(obj) {
|
|
if (obj instanceof Date || obj instanceof RegExp) {
|
|
return obj;
|
|
}
|
|
if (Array.isArray(obj)) {
|
|
return obj.map(convertObjKeysToSnakeCase);
|
|
}
|
|
if (obj instanceof Object) {
|
|
return Object.keys(obj).reduce((acc, cur) => {
|
|
const s = cur[0].toLocaleLowerCase() +
|
|
cur.slice(1).replace(/([A-Z]+)/g, (match, p1) => {
|
|
return `_${p1.toLowerCase()}`;
|
|
});
|
|
acc[s] = convertObjKeysToSnakeCase(obj[cur]);
|
|
return acc;
|
|
}, Object());
|
|
}
|
|
return obj;
|
|
}
|
|
exports.convertObjKeysToSnakeCase = convertObjKeysToSnakeCase;
|
|
/**
|
|
* Formats the provided date object as a UTC ISO string.
|
|
* @param {Date} dateTimeToFormat date object to be formatted.
|
|
* @param {boolean} includeTime flag to include hours, minutes, seconds in output.
|
|
* @param {string} dateDelimiter delimiter between date components.
|
|
* @param {string} timeDelimiter delimiter between time components.
|
|
* @returns {string} UTC ISO format of provided date obect.
|
|
*/
|
|
function formatAsUTCISO(dateTimeToFormat, includeTime = false, dateDelimiter = '', timeDelimiter = '') {
|
|
const year = dateTimeToFormat.getUTCFullYear();
|
|
const month = dateTimeToFormat.getUTCMonth() + 1;
|
|
const day = dateTimeToFormat.getUTCDate();
|
|
const hour = dateTimeToFormat.getUTCHours();
|
|
const minute = dateTimeToFormat.getUTCMinutes();
|
|
const second = dateTimeToFormat.getUTCSeconds();
|
|
let resultString = `${year.toString().padStart(4, '0')}${dateDelimiter}${month
|
|
.toString()
|
|
.padStart(2, '0')}${dateDelimiter}${day.toString().padStart(2, '0')}`;
|
|
if (includeTime) {
|
|
resultString = `${resultString}T${hour
|
|
.toString()
|
|
.padStart(2, '0')}${timeDelimiter}${minute
|
|
.toString()
|
|
.padStart(2, '0')}${timeDelimiter}${second.toString().padStart(2, '0')}Z`;
|
|
}
|
|
return resultString;
|
|
}
|
|
exports.formatAsUTCISO = formatAsUTCISO;
|
|
class PassThroughShim extends stream_1.PassThrough {
|
|
constructor() {
|
|
super(...arguments);
|
|
this.shouldEmitReading = true;
|
|
this.shouldEmitWriting = true;
|
|
}
|
|
_read(size) {
|
|
if (this.shouldEmitReading) {
|
|
this.emit('reading');
|
|
this.shouldEmitReading = false;
|
|
}
|
|
super._read(size);
|
|
}
|
|
_write(chunk, encoding, callback) {
|
|
if (this.shouldEmitWriting) {
|
|
this.emit('writing');
|
|
this.shouldEmitWriting = false;
|
|
}
|
|
// Per the nodejs documention, callback must be invoked on the next tick
|
|
process.nextTick(() => {
|
|
super._write(chunk, encoding, callback);
|
|
});
|
|
}
|
|
_final(callback) {
|
|
// If the stream is empty (i.e. empty file) final will be invoked before _read / _write
|
|
// and we should still emit the proper events.
|
|
if (this.shouldEmitReading) {
|
|
this.emit('reading');
|
|
this.shouldEmitReading = false;
|
|
}
|
|
if (this.shouldEmitWriting) {
|
|
this.emit('writing');
|
|
this.shouldEmitWriting = false;
|
|
}
|
|
callback(null);
|
|
}
|
|
}
|
|
exports.PassThroughShim = PassThroughShim;
|
|
//# sourceMappingURL=util.js.map
|