diff --git a/hoek/hoek-tests.ts b/hoek/hoek-tests.ts new file mode 100644 index 0000000000..3568ba024b --- /dev/null +++ b/hoek/hoek-tests.ts @@ -0,0 +1,290 @@ +import * as Hoek from "hoek"; + +/** + * Import the entire module as above or use named imports: + * import { clone, merge, assert} from "hoek"; + * "Tests" taken straight from hoek API reference and adapted to TypeScript. + */ + +// clone(obj) + +let nestedObj = { + w: /^something$/ig, + x: { + a: [1, 2, 3], + b: 123456, + c: new Date() + }, + y: 'y', + z: new Date() +}; + +let copy = Hoek.clone(nestedObj); + +copy.x.b = 100; + +console.log(copy.y); // results in 'y' +console.log(nestedObj.x.b); // results in 123456 +console.log(copy.x.b); // results in 100 + +// cloneWithShallow(obj, keys) + +nestedObj = { + w: /^something$/ig, + x: { + a: [1, 2, 3], + b: 123456, + c: new Date() + }, + y: 'y', + z: new Date() +}; + +copy = Hoek.cloneWithShallow(nestedObj, ['x']); + +copy.x.b = 100; + +console.log(copy.y); // results in 'y' +console.log(nestedObj.x.b); // results in 100 +console.log(copy.x.b); // results in 100 + +// merge(target, source, isNullOverride, isMergeArrays) + +let target = { a: 1, b: 2 }; +let source = { a: 0, c: 5 }; +let source2 = { a: null, c: 5 }; + +Hoek.merge(target, source); // results in {a: 0, b: 2, c: 5} +Hoek.merge(target, source2); // results in {a: null, b: 2, c: 5} +Hoek.merge(target, source2, false); // results in {a: 1, b: 2, c: 5} + +let targetArray = [1, 2, 3]; +let sourceArray = [4, 5]; + +Hoek.merge(targetArray, sourceArray); // results in [1, 2, 3, 4, 5] +Hoek.merge(targetArray, sourceArray, true, false); // results in [4, 5] + +// applyToDefaults(defaults, options, isNullOverride) + +let defaults = { host: "localhost", port: 8000 }; +let options = { port: 8080 }; + +let config = Hoek.applyToDefaults(defaults, options); // results in { host: "localhost", port: 8080 } + +defaults = { host: "localhost", port: 8000 }; +let options1 = { host: null, port: 8080 }; + +config = Hoek.applyToDefaults(defaults, options1, true); // results in { host: null, port: 8080 } + +// applyToDefaultsWithShallow(defaults, options, keys) + +let defaults1 = { + server: { + host: "localhost", + port: 8000 + }, + name: 'example' +}; + +let options2 = { server: { port: 8080 } }; + +let config1 = Hoek.applyToDefaultsWithShallow(defaults1, options2, ['server']); // results in { server: { port: 8080 }, name: 'example' } + +// deepEqual(b, a, [options]) + +Hoek.deepEqual({ a: [1, 2], b: 'string', c: { d: true } }, { a: [1, 2], b: 'string', c: { d: true } }); //results in true +Hoek.deepEqual(Object.create(null), {}, { prototype: false }); //results in true +Hoek.deepEqual(Object.create(null), {}); //results in false + +// unique(array, key) + +let array = [1, 2, 2, 3, 3, 4, 5, 6]; + +let newArray = Hoek.unique(array); // results in [1,2,3,4,5,6] + +let array1 = [{ id: 1 }, { id: 1 }, { id: 2 }]; + +let newArray1 = Hoek.unique(array1, "id"); // results in [{id: 1}, {id: 2}] + +// mapToObject(array, key) + +array = [1, 2, 3]; +let newObject = Hoek.mapToObject(array); // results in {"1": true, "2": true, "3": true} + +array1 = [{ id: 1 }, { id: 2 }]; +newObject = Hoek.mapToObject(array1, "id"); // results in {"1": true, "2": true} + +// intersect(array1, array2) + +array = [1, 2, 3]; +let array2 = [1, 4, 5]; + +let newArray2 = Hoek.intersect(array, array2); // results in [1] + +// contain(ref, values, [options]) + +Hoek.contain('aaa', 'a', { only: true }); // true +Hoek.contain([{ a: 1 }], [{ a: 1 }], { deep: true }); // true +Hoek.contain([1, 2, 2], [1, 2], { once: true }); // false +Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 1, d: 4 }, { part: true }); // true + +// flatten(array, [target]) + +let array3 = [1, [2, 3]]; + +let flattenedArray = Hoek.flatten(array); // results in [1, 2, 3] + +array3 = [1, [2, 3]]; +let target1 = [4, [5]]; + +flattenedArray = Hoek.flatten(array3, target1); // results in [4, [5], 1, 2, 3] + +// reach(obj, chain, [options]) + +let chain = 'a.b.c'; +let obj = { a: { b: { c: 1 } } }; + +Hoek.reach(obj, chain); // returns 1 + +chain = 'a.b.-1'; +let obj1 = { a: { b: [2, 3, 6] } }; + +Hoek.reach(obj1, chain); // returns 6 + +// reachTemplate(obj, template, [options]) + +chain = 'a.b.c'; +obj = { a: { b: { c: 1 } } }; + +Hoek.reachTemplate(obj, '1+{a.b.c}=2'); // returns '1+1=2' + +// transform(obj, transform, [options]) + +let source1 = { + address: { + one: '123 main street', + two: 'PO Box 1234' + }, + title: 'Warehouse', + state: 'CA' +}; + +let result = Hoek.transform(source1, { + 'person.address.lineOne': 'address.one', + 'person.address.lineTwo': 'address.two', + 'title': 'title', + 'person.address.region': 'state' +}); +// Results in +// { +// person: { +// address: { +// lineOne: '123 main street', +// lineTwo: 'PO Box 1234', +// region: 'CA' +// } +// }, +// title: 'Warehouse' +// } + +// shallow(obj) + +let shallow = Hoek.shallow({ a: { b: 1 } }); + +// stringify(obj) + +let a: any = {}; +a.b = a; +Hoek.stringify(a); // Returns '[Cannot display object: Converting circular structure to JSON]' + +// Timer + +let timerObj = new Hoek.Timer(); +console.log("Time is now: " + timerObj.ts); +console.log("Elapsed time from initialization: " + timerObj.elapsed() + 'milliseconds'); + +// Bench + +let benchObj = new Hoek.Bench(); +console.log("Elapsed time from initialization: " + benchObj.elapsed() + 'milliseconds'); + +// base64urlEncode(value) + +Hoek.base64urlEncode("hoek"); + +// base64urlDecode(value) + +Hoek.base64urlDecode("aG9law=="); + +// escapeHtml(string) + +let string = ' hey '; +let escapedString = Hoek.escapeHtml(string); // returns <html> hey </html> + +// escapeHeaderAttribute(attribute) + +a = Hoek.escapeHeaderAttribute('I said "go w\\o me"'); //returns I said \"go w\\o me\" + +// escapeRegex(string) + +a = Hoek.escapeRegex('4^f$s.4*5+-_?%=#!:@|~\\/`"(>)[<]d{}s,'); // returns 4\^f\$s\.4\*5\+\-_\?%\=#\!\:@\|~\\\/`"\(>\)\[<\]d\{\}s\, + +// assert(condition, message) + +let x = 1, y = 2; + +Hoek.assert(x === y, 'x should equal y'); // Throws 'x should equal y' + +Hoek.assert(x === y, new Error('x should equal y')); // Throws the given error object + +// abort(message) + +Hoek.abort("Error message"); + +// displayStack(slice) + +let stack = Hoek.displayStack(); +console.log(stack); + +// callStack(slice) + +let stack2 = Hoek.callStack(); +console.log(stack2); + +// nextTick(fn) + +let myFn = function () { + console.log('Do this later'); +}; + +let nextFn = Hoek.nextTick(myFn); + +nextFn(); +console.log('Do this first'); + +// Results in: +// +// Do this first +// Do this later + +// once(fn) + +myFn = function () { + console.log('Ran myFn'); +}; + +let onceFn = Hoek.once(myFn); +onceFn(); // results in "Ran myFn" +onceFn(); // results in undefined + +// ignore + +Hoek.ignore(); + +// uniqueFilename(path, extension) + +let result1 = Hoek.uniqueFilename('./test/modules', 'txt'); // results in "full/path/test/modules/{random}.txt" + +// isInteger(value) + +result = Hoek.isInteger('23'); diff --git a/hoek/index.d.ts b/hoek/index.d.ts new file mode 100644 index 0000000000..9c749d1660 --- /dev/null +++ b/hoek/index.d.ts @@ -0,0 +1,211 @@ +// Type definitions for Hoek 4.1.0 +// Project: https://github.com/hapijs/hoek +// Definitions by: Prashant Tiwari +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +interface ContainOptions { + /** Perform a deep comparison of the values? */ + deep?: boolean; + /** Allow only one occurrence of each value? */ + once?: boolean; + /** Don't allow values not explicitly listed? */ + only?: boolean; + /** Allow partial match of the values? */ + part?: boolean; +} + +interface ReachOptions { + /** String to split chain path on. Defaults to ".". */ + separator?: string; + /** Value to return if the path or value is not present. Default is undefined. */ + default?: any; + /** Throw an error on missing member? Default is false. */ + strict?: boolean; + /** Allow traversing functions for properties? */ + functions?: boolean; +} + +// Object + +/** + * Clone an object or an array. + */ +export function clone(obj: T): T; + +/** + * Clone an object or array. + * */ +export function cloneWithShallow(obj: any, keys: string[]): any; + +/** + * Merge all the properties of source into target. */ +export function merge(target: T1, source: T2, isNullOverride?: boolean, isMergeArrays?: boolean): T1 & T2; + +/** + * Apply options to a copy of the defaults. + * */ +export function applyToDefaults(defaults: T1, options: T2, isNullOverride?: boolean): T1 & T2; + +/** + * Apply options to a copy of the defaults. + */ +export function applyToDefaultsWithShallow(defaults: T1, options: T2, keys?: string[]): T1 & T2; + +/** + * Perform a deep comparison of the two values. + */ +export function deepEqual(b: T, a: T, options?: any): T; + +/** + * Remove duplicate items from Array. + */ +export function unique(array: T[], key?: string): T[]; + +/** + * Convert an Array into an Object. + */ +export function mapToObject(array: any[], key?: string): any; + +/** + * Find the common unique items in two arrays. + */ +export function intersect(array1: any[], array2: any[]): any; + +/** + * Test if the reference value contains the provided values. + */ +export function contain(ref: any, values: any, options?: ContainOptions): boolean; + +/** + * Flatten an array. + */ +export function flatten(array: any[], target?: any[]): any[]; + +/** + * Convert an object key chain string to reference. + */ +export function reach(obj: any, chain: any, options?: ReachOptions): any; + +/** + * Replace string parameters ({name}) with their corresponding object key values. + */ +export function reachTemplate(obj: any, template: string, options?: ReachOptions): any; + +/** + * Transform an existing object into a new one based on the supplied obj and transform map. + */ +export function transform(obj: any, transform: any, options?: ReachOptions): any; + +/** + * Perform a shallow copy by copying the references of all the top level children. + */ +export function shallow(obj: any): any; + +/** + * Convert an object to string. Any errors are caught and reported back in the form of the returned string. + */ +export function stringify(obj: any): string; + +// Timer + +/** + * A Timer object. + */ +export class Timer { + /** The number of milliseconds elapsed since the epoch. */ + ts: number; + /** The time (ms) elapsed since the timer was created. */ + elapsed(): number; +} + +// Bench + +/** + * Same as Timer, except ts stores the internal node clock. + */ +export class Bench { + /** The number of milliseconds on the node clock elapsed since the epoch. */ + ts: number; + /** The time (ms) elapsed since the timer was created. */ + elapsed(): number; +} + +// Binary Encoding/Decoding + +/** + * Encode value of string or buffer type in Base64 or URL encoding. + */ +export function base64urlEncode(value: string): string; + +/** + * Decode string into Base64 or URL encoding. + */ +export function base64urlDecode(value: string): string; + +// Escaping Characters + +/** + * Escape html characters. + */ +export function escapeHtml(htmlString: string): string; + +/** + * Escape attribute value for use in HTTP header. + */ +export function escapeHeaderAttribute(attribute: string): string; + +/** + * Escape string for Regex construction. + */ +export function escapeRegex(regexString: string): string; + +// Errors + +/** + * Print message or throw error if condition fails. + */ +export function assert(condition: boolean, message: string | Error): void | Error; + +/** + * Throw if process.env.NODE_ENV === 'test'. Else display most recent stack and exit process. + */ +export function abort(message: string | Error): void; + +/** + * Display the trace stack. + */ +export function displayStack(slice?: any): string[]; + +/** + * Return a trace stack array. + */ +export function callStack(slice?: any): Array[]; + +// Function + +/** + * Wrap fn in process.nextTick. + */ +export function nextTick(fn: Function): Function; + +/** + * Make sure fn is only run once. + */ +export function once(fn: Function): Function; + +/** + * A simple no-op function. + */ +export function ignore(): void; + +// Miscellaneous + +/** + * path to prepend to a randomly generated file name. + */ +export function uniqueFilename(path: string, extension?: string): string; + +/** + * Check value to see if it is an integer. + */ +export function isInteger(value: any): boolean; diff --git a/hoek/tsconfig.json b/hoek/tsconfig.json new file mode 100644 index 0000000000..ea03427572 --- /dev/null +++ b/hoek/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es6", + "noImplicitAny": true, + "strictNullChecks": true, + "baseUrl": "../", + "typeRoots": [ + "../" + ], + "types": [], + "noEmit": true, + "forceConsistentCasingInFileNames": true + }, + "files": [ + "index.d.ts", + "hoek-tests.ts" + ] +} \ No newline at end of file