utils - js

This commit is contained in:
Salakar 2017-02-14 15:56:28 +00:00
parent 1cef58939d
commit 081adee1dc
2 changed files with 290 additions and 0 deletions

View File

@ -12,6 +12,7 @@ const FirebaseAuthEvt = new NativeEventEmitter(FirebaseAuth);
type AuthResultType = { authenticated: boolean, user: Object|null };
type CredentialType = { provider: string, token: string, secret: string };
// TODO move user methods to user class
export default class Auth extends Base {
_user: User|null;
_authResult: AuthResultType | null;

289
lib/utils/index.js Normal file
View File

@ -0,0 +1,289 @@
// modeled after base64 web-safe chars, but ordered by ASCII
const PUSH_CHARS = '-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz';
const hasOwnProperty = Object.hasOwnProperty;
const DEFAULT_CHUNK_SIZE = 50;
// internal promise handler
const _handler = (resolve, reject, errorPrefix, err, resp) => {
// resolve / reject after events etc
setImmediate(() => {
if (err) {
return reject(errorPrefix ? {
code: toWebSDKErrorCode(err.code || err.errCode || err.errorCode || '', errorPrefix),
message: err.message,
} : err);
}
return resolve(resp);
});
};
export function toWebSDKErrorCode(code, prefix) {
if (!code || typeof code !== 'string') return '';
return code.toLowerCase().replace('error_', prefix).replace(/_/g, '-');
}
/**
* Deep get a value from an object.
* @website https://github.com/Salakar/deeps
* @param object
* @param path
* @param joiner
* @returns {*}
*/
export function deepGet(object, path, joiner = '/') {
const keys = path.split(joiner);
let i = 0;
let tmp = object;
const len = keys.length;
while (i < len) {
const key = keys[i += 1];
if (!tmp || !hasOwnProperty.call(tmp, key)) return null;
tmp = tmp[key];
}
return tmp;
}
/**
* Deep check if a key exists.
* @website https://github.com/Salakar/deeps
* @param object
* @param path
* @param joiner
* @returns {*}
*/
export function deepExists(object, path, joiner = '/') {
const keys = path.split(joiner);
let i = 0;
let tmp = object;
const len = keys.length;
while (i < len) {
const key = keys[i += 1];
if (!tmp || !hasOwnProperty.call(tmp, key)) return false;
tmp = tmp[key];
}
return tmp !== undefined;
}
/**
* Simple is object check.
* @param item
* @returns {boolean}
*/
export function isObject(item) {
return (item && typeof item === 'object' && !Array.isArray(item) && item !== null);
}
/**
* Simple is function check
* @param item
* @returns {*|boolean}
*/
export function isFunction(item) {
return (item && typeof item === 'function');
}
/**
*
* @param string
* @returns {*}
*/
export function tryJSONParse(string) {
try {
return JSON.parse(string);
} catch (jsonError) {
return string;
}
}
/**
*
* @param data
* @returns {*}
*/
export function tryJSONStringify(data) {
try {
return JSON.stringify(data);
} catch (jsonError) {
return null;
}
}
// noinspection Eslint
export const windowOrGlobal = (typeof self === 'object' && self.self === self && self) || (typeof global === 'object' && global.global === global && global) || this;
/**
* Makes an objects keys it's values
* @param object
* @returns {{}}
*/
export function reverseKeyValues(object: Object): Object {
const output = {};
for (const key in object) {
output[object[key]] = key;
}
return output;
}
/**
* No operation func
*/
export function noop(): void {
}
/**
* Wraps a native module method to support promises.
* @param fn
* @param NativeModule
* @param errorPrefix
*/
export function promisify(fn: Function, NativeModule: Object, errorPrefix): Function<Promise> {
return (...args) => {
return new Promise((resolve, reject) => {
const _fn = typeof fn === 'function' ? fn : NativeModule[fn];
if (!_fn || typeof _fn !== 'function') return reject(new Error('Missing function for promisify.'));
return _fn.apply(NativeModule, [...args, _handler.bind(_handler, resolve, reject, errorPrefix)]);
});
};
}
/**
* Delays chunks based on sizes per event loop.
* @param collection
* @param chunkSize
* @param operation
* @param callback
* @private
*/
function _delayChunk(collection, chunkSize, operation, callback): void {
const length = collection.length;
const iterations = Math.ceil(length / chunkSize);
// noinspection ES6ConvertVarToLetConst
let thisIteration = 0;
setImmediate(function next() {
const start = thisIteration * chunkSize;
const _end = start + chunkSize;
const end = _end >= length ? length : _end;
const result = operation(collection.slice(start, end), start, end);
if (thisIteration++ > iterations) {
callback(null, result);
} else {
setImmediate(next);
}
});
}
/**
* Async each with optional chunk size limit
* @param array
* @param chunkSize
* @param iterator
* @param cb
*/
export function each(array: Array, chunkSize?: number, iterator: Function, cb: Function): void {
if (typeof chunkSize === 'function') {
cb = iterator;
iterator = chunkSize;
chunkSize = DEFAULT_CHUNK_SIZE;
}
_delayChunk(array, chunkSize, (slice, start) => {
for (let ii = 0, jj = slice.length; ii < jj; ii += 1) {
iterator(slice[ii], start + ii);
}
}, cb);
}
export function typeOf(value) {
if (Array.isArray(value)) return 'array';
return value === null ? 'null' : typeof value;
}
/**
* Async map with optional chunk size limit
* @param array
* @param chunkSize
* @param iterator
* @param cb
* @returns {*}
*/
export function map(array: Array, chunkSize?: number, iterator: Function, cb: Function): void {
if (typeof chunkSize === 'function') {
cb = iterator;
iterator = chunkSize;
chunkSize = DEFAULT_CHUNK_SIZE;
}
const result = [];
_delayChunk(array, chunkSize, (slice, start) => {
for (let ii = 0, jj = slice.length; ii < jj; ii += 1) {
result.push(iterator(slice[ii], start + ii, array));
}
return result;
}, () => cb(result));
}
// timestamp of last push, used to prevent local collisions if you push twice in one ms.
let lastPushTime = 0;
// we generate 72-bits of randomness which get turned into 12 characters and appended to the
// timestamp to prevent collisions with other clients. We store the last characters we
// generated because in the event of a collision, we'll use those same characters except
// "incremented" by one.
const lastRandChars = [];
/**
* Generate a firebase id - for use with ref().push(val, cb) - e.g. -KXMr7k2tXUFQqiaZRY4'
* @param serverTimeOffset - pass in server time offset from native side
* @returns {string}
*/
export function generatePushID(serverTimeOffset?: number = 0): string {
const timeStampChars = new Array(8);
let now = new Date().getTime() + serverTimeOffset;
const duplicateTime = (now === lastPushTime);
lastPushTime = now;
for (let i = 7; i >= 0; i -= 1) {
timeStampChars[i] = PUSH_CHARS.charAt(now % 64);
now = Math.floor(now / 64);
}
if (now !== 0) throw new Error('We should have converted the entire timestamp.');
let id = timeStampChars.join('');
if (!duplicateTime) {
for (let i = 0; i < 12; i += 1) {
lastRandChars[i] = Math.floor(Math.random() * 64);
}
} else {
// if the timestamp hasn't changed since last push,
// use the same random number, but increment it by 1.
let i;
for (i = 11; i >= 0 && lastRandChars[i] === 63; i -= 1) {
lastRandChars[i] = 0;
}
lastRandChars[i] += 1;
}
for (let i = 0; i < 12; i++) {
id += PUSH_CHARS.charAt(lastRandChars[i]);
}
if (id.length !== 20) throw new Error('Length should be 20.');
return id;
}