├── .npmrc ├── src ├── set │ ├── index.ts │ └── proto │ │ ├── difference.spec.ts │ │ ├── intersection.spec.ts │ │ ├── difference.ts │ │ └── intersection.ts ├── boolean │ ├── index.ts │ └── static │ │ ├── is-falsy.ts │ │ ├── is-truthy.ts │ │ ├── is-falsy.spec.ts │ │ └── is-truthy.spec.ts ├── map │ ├── index.ts │ ├── merge.spec.ts │ └── merge.ts ├── math │ ├── index.ts │ ├── static │ │ ├── min-max.ts │ │ └── min-max.spec.ts │ ├── min-max.ts │ └── min-max.spec.ts ├── string │ ├── index.ts │ ├── proto │ │ ├── limit.spec.ts │ │ └── limit.ts │ ├── limit.spec.ts │ └── limit.ts ├── object │ ├── index.ts │ ├── deepest.spec.ts │ ├── static │ │ ├── deepest.spec.ts │ │ ├── clone.spec.ts │ │ ├── compact.spec.ts │ │ ├── compact.ts │ │ ├── deepest.ts │ │ └── clone.ts │ ├── compact.spec.ts │ ├── deepest.ts │ ├── compact.ts │ ├── clone.spec.ts │ └── clone.ts ├── function │ ├── index.ts │ ├── noop.spec.ts │ ├── static │ │ ├── noop.spec.ts │ │ ├── of.spec.ts │ │ ├── noop.ts │ │ ├── defer.ts │ │ ├── of.ts │ │ └── defer.spec.ts │ ├── noop.ts │ ├── of.spec.ts │ ├── proto │ │ ├── debounce.spec.ts │ │ └── debounce.ts │ ├── defer.ts │ ├── of.ts │ ├── defer.spec.ts │ ├── debounce.spec.ts │ └── debounce.ts ├── promise │ ├── index.ts │ ├── try-catch.spec.ts │ ├── proto │ │ ├── try-catch.spec.ts │ │ ├── revert.spec.ts │ │ ├── revert.ts │ │ └── try-catch.ts │ ├── static │ │ ├── timeout.ts │ │ ├── try-one-by-one.ts │ │ ├── never.spec.ts │ │ ├── never.ts │ │ └── timeout.spec.ts │ ├── revert.spec.ts │ ├── never.ts │ ├── never.spec.ts │ ├── try-one-by-one.spec.ts │ ├── revert.ts │ ├── try-catch.ts │ ├── try-one-by-one.ts │ ├── timeout.spec.ts │ └── timeout.ts ├── array │ ├── index.ts │ ├── uniquify.spec.ts │ ├── proto │ │ ├── uniquify.spec.ts │ │ ├── remove.spec.ts │ │ ├── limit.spec.ts │ │ ├── find-by-key.spec.ts │ │ ├── compact.ts │ │ ├── difference.spec.ts │ │ ├── compact.spec.ts │ │ ├── intersection.spec.ts │ │ ├── async-map.spec.ts │ │ ├── uniquify.ts │ │ ├── limit.ts │ │ ├── uniquify-by-key.spec.ts │ │ ├── remove.ts │ │ ├── replace.spec.ts │ │ ├── find-by-key.ts │ │ ├── uniquify-by-key.ts │ │ ├── async-map.ts │ │ ├── intersection.ts │ │ ├── difference.ts │ │ └── replace.ts │ ├── async-map.spec.ts │ ├── find-by-key.spec.ts │ ├── remove.spec.ts │ ├── compact.spec.ts │ ├── uniquify-by-key.spec.ts │ ├── uniquify.ts │ ├── limit.spec.ts │ ├── limit.ts │ ├── find-by-key.ts │ ├── compact.ts │ ├── async-map.ts │ ├── uniquify-by-key.ts │ ├── remove.ts │ ├── replace.spec.ts │ ├── difference.spec.ts │ ├── intersection.spec.ts │ ├── intersection.ts │ ├── difference.ts │ └── replace.ts ├── arrayize.ts ├── arrayize.spec.ts ├── index.ts ├── is-falsy.spec.ts ├── is-truthy.spec.ts ├── unique-id.spec.ts ├── is-falsy.ts ├── is-truthy.ts ├── compact.spec.ts ├── limit.spec.ts ├── limit.ts ├── compact.ts ├── unique-id.ts ├── smart-map.spec.ts ├── open-promise.spec.ts ├── open-promise.ts └── smart-map.ts ├── .gitignore ├── .editorconfig ├── config ├── tsconfig.spec.json ├── tsconfig.lib.json └── tsconfig.doc.json ├── .npmignore ├── jest.config.js ├── tsconfig.json ├── .github ├── workflows │ ├── pullrequest.yml │ ├── pushmaster.yml │ └── publish.yml ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md └── FUNDING.yml ├── LICENSE ├── tslint.json ├── package.json ├── CODE_OF_CONDUCT.md └── README.md /.npmrc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/set/index.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/boolean/index.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/map/index.ts: -------------------------------------------------------------------------------- 1 | export * from './merge'; 2 | -------------------------------------------------------------------------------- /src/math/index.ts: -------------------------------------------------------------------------------- 1 | export * from './min-max'; 2 | -------------------------------------------------------------------------------- /src/string/index.ts: -------------------------------------------------------------------------------- 1 | export * from './limit'; 2 | -------------------------------------------------------------------------------- /src/object/index.ts: -------------------------------------------------------------------------------- 1 | export * from './clone'; 2 | export * from './compact'; 3 | export * from './deepest'; 4 | -------------------------------------------------------------------------------- /src/function/index.ts: -------------------------------------------------------------------------------- 1 | export * from './of'; 2 | export * from './noop'; 3 | export * from './debounce'; 4 | export * from './defer'; 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .vscode/ 3 | dist/ 4 | package-lock.json 5 | *.tgz 6 | docs/ 7 | 8 | # Testing 9 | .nyc_output/ 10 | coverage/ -------------------------------------------------------------------------------- /src/promise/index.ts: -------------------------------------------------------------------------------- 1 | export * from './never'; 2 | export * from './revert'; 3 | export * from './try-catch'; 4 | export * from './try-one-by-one'; 5 | export * from './timeout'; 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = false 9 | trim_trailing_whitespace = true -------------------------------------------------------------------------------- /src/map/merge.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import { merge } from './merge'; 4 | 5 | describe('Map Merge', () => { 6 | it('should be defined', () => { 7 | expect(merge).toBeDefined(); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /config/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "types": [ 5 | "node", 6 | "jest" 7 | ] 8 | }, 9 | "include": [ 10 | "src/**/*.spec.ts" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | docs/ 3 | .vscode/ 4 | .github/ 5 | config/ 6 | src/ 7 | *.tgz 8 | package-lock.json 9 | 10 | .editorconfig 11 | tslint.json 12 | .travis.yml 13 | 14 | # Testing 15 | .nyc_output/ 16 | coverage/ 17 | jest.config.json 18 | -------------------------------------------------------------------------------- /config/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../", 5 | }, 6 | "include": [ 7 | "../src" 8 | ], 9 | "exclude": [ 10 | "../**/*.spec.ts", 11 | "node_modules" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /src/function/noop.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import { noop } from './noop'; 4 | 5 | describe('Noop Function', () => { 6 | it('should return void', () => { 7 | expect(noop()).toBe(undefined); 8 | expect(noop.call(this, 'a', 1, true, {})).toBe(undefined); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /src/function/static/noop.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import './noop'; 4 | 5 | describe('Noop Static Function', () => { 6 | it('should return void', () => { 7 | expect(Function.noop()).toBe(undefined); 8 | expect(Function.noop.call(this, 'a', 1, true, {})).toBe(undefined); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /src/object/deepest.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import { deepest } from './deepest'; 4 | 5 | describe('Deepest Function', () => { 6 | it('should get deepest object', () => { 7 | const a = {x: null}; 8 | const b = {x: a}; 9 | const c = {x: b}; 10 | 11 | expect(deepest(c, 'x')).toBe(a); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /src/function/static/of.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import './of'; 4 | import { of } from '../of'; 5 | 6 | describe('Function Of Static Function', () => { 7 | it('should work as same', () => { 8 | const foo = of('foo'); 9 | const fooStatic = Function.of('foo'); 10 | 11 | expect(foo).toEqual(fooStatic); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /src/array/index.ts: -------------------------------------------------------------------------------- 1 | export * from './async-map'; 2 | export * from './compact'; 3 | export * from './difference'; 4 | export * from './find-by-key'; 5 | export * from './intersection'; 6 | export * from './limit'; 7 | export * from './remove'; 8 | export * from './replace'; 9 | export * from './uniquify'; 10 | export * from './uniquify-by-key'; 11 | -------------------------------------------------------------------------------- /src/object/static/deepest.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import './deepest'; 4 | import { deepest } from '../deepest'; 5 | 6 | describe('Deepest Static Function', () => { 7 | it('should act as same', () => { 8 | const a = {x: null}; 9 | const b = {x: a}; 10 | const c = {x: b}; 11 | 12 | expect(deepest(c, 'x')).toBe(Object.deepest(c, 'x')); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /src/array/uniquify.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import { uniquify } from './uniquify'; 4 | 5 | describe('Uniquify Function', () => { 6 | it('should remove repeated items from array', () => { 7 | const a1 = [1, 2, 3]; 8 | const a2 = [1, 1, 2, 2, 3, 3]; 9 | 10 | expect(uniquify(a1)).toEqual([1, 2, 3]); 11 | expect(uniquify(a2)).toEqual([1, 2, 3]); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /src/object/static/clone.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import './clone'; 4 | import { clone } from '../clone'; 5 | 6 | describe('Clone Static Function', () => { 7 | it('should act as same', () => { 8 | const foo = {x: 1, y: 2, z: [{a: true, b: () => false}]}; 9 | const bar = clone(foo); 10 | const baz = Object.clone(foo); 11 | 12 | expect(bar).toEqual(baz); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /src/object/static/compact.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import './compact'; 4 | import { compact } from '../compact'; 5 | 6 | describe('Compact Static Function', () => { 7 | it('should act as same', () => { 8 | const foo = {x: undefined, y: null, z: 20}; 9 | const bar = compact(foo); 10 | const baz = Object.compact(foo); 11 | 12 | expect(bar).toEqual(baz); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /src/array/proto/uniquify.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import './uniquify'; 4 | import { uniquify } from '../uniquify'; 5 | 6 | describe('Array Uniquify Proto Function', () => { 7 | it('should work as same', () => { 8 | const foo = [1, 2, 3, 1, 2, 3]; 9 | const bar = [1, 2]; 10 | 11 | expect(foo.uniquify()).toEqual(uniquify(foo)); 12 | expect(bar.uniquify()).toEqual(uniquify(bar)); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: [ 3 | "/src" 4 | ], 5 | coverageDirectory: "coverage", 6 | coverageReporters: ["html", "json"], 7 | collectCoverageFrom: [ 8 | "src/**/*.ts", 9 | "!**/*/index.ts" 10 | ], 11 | testMatch: [ 12 | "**/__tests__/**/*.+(ts|tsx|js)", 13 | "**/?(*.)+(spec|test).+(ts|tsx|js)" 14 | ], 15 | transform: { 16 | "^.+\\.(ts|tsx)$": "ts-jest" 17 | }, 18 | } -------------------------------------------------------------------------------- /src/array/proto/remove.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import './remove'; 4 | import { remove } from '../remove'; 5 | 6 | describe('Array Remove Proto Function', () => { 7 | it('should work as same', () => { 8 | const foo = [1, 2, 3, 1, 2, 3]; 9 | 10 | expect(foo.remove(2)).toEqual(remove(foo, 2)); 11 | expect(foo.remove(4)).toEqual(remove(foo, 4)); 12 | expect(foo.remove(3, true)).toEqual(remove(foo, 3, true)); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /src/function/noop.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * #### Noop function 3 | * * * * 4 | * Example usage: 5 | * ```typescript 6 | * import { noop } from "@thalesrc/js-utils/function"; 7 | * 8 | * noop(); 9 | * document.onload = noop; 10 | * ``` 11 | * Static usage example: 12 | * ```typescript 13 | * import "@thalesrc/js-utils/function/static/noop"; 14 | * 15 | * Function.noop(); 16 | * document.onload = Function.noop; 17 | * ``` 18 | * * * * 19 | */ 20 | export function noop(): void {} 21 | -------------------------------------------------------------------------------- /src/string/proto/limit.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import './limit'; 4 | import { limit } from '../limit'; 5 | 6 | describe('String Limit Proto Function', () => { 7 | it('should work as same', () => { 8 | const foo = 'foobarbaz'; 9 | 10 | expect(foo.limit(3)).toEqual(limit(foo, 3)); 11 | expect(foo.limit(0)).toEqual(limit(foo, 0)); 12 | expect(foo.limit(-1)).toEqual(limit(foo, -1)); 13 | expect(foo.limit(10)).toEqual(limit(foo, 10)); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/array/async-map.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import { asyncMap } from './async-map'; 4 | import { timeout } from '../promise/timeout'; 5 | 6 | describe('AsyncMap Function', () => { 7 | it('should map asynchronous', done => { 8 | const foo = [1, 2, 3]; 9 | 10 | asyncMap(foo, async value => timeout(Math.random() * 100).then(() => value + 1)) 11 | .then(result => { 12 | expect(result).toEqual([2, 3, 4]); 13 | done(); 14 | }); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /src/array/proto/limit.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import './limit'; 4 | import { limit } from '../limit'; 5 | 6 | describe('Array Limit Proto Function', () => { 7 | it('should work as same', () => { 8 | const foo = [1, 2, 3, 4, 5, 6]; 9 | 10 | expect(foo.limit(3)).toEqual(limit(foo, 3)); 11 | expect(foo.limit(0)).toEqual(limit(foo, 0)); 12 | expect(foo.limit(-1)).toEqual(limit(foo, -1)); 13 | expect(foo.limit(10)).toEqual(limit(foo, 10)); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/function/static/noop.ts: -------------------------------------------------------------------------------- 1 | import { noop } from '../noop'; 2 | 3 | declare global { 4 | export interface FunctionConstructor { 5 | /** 6 | * #### Noop function 7 | * * * * 8 | * Static usage example: 9 | * ```typescript 10 | * import "@thalesrc/js-utils/function/static/noop"; 11 | * 12 | * Function.noop(); 13 | * document.onload = Function.noop; 14 | * ``` 15 | * * * * 16 | */ 17 | noop: typeof noop; 18 | } 19 | } 20 | 21 | Function.noop = noop; 22 | -------------------------------------------------------------------------------- /src/array/proto/find-by-key.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import './find-by-key'; 4 | import { findByKey } from '../find-by-key'; 5 | 6 | describe('Array FindByKey Proto Function', () => { 7 | it('should work as same', () => { 8 | const foo = [{a: 1}, {a: 2}, {a: 3}]; 9 | 10 | expect(foo.findByKey('a', 1)).toEqual(findByKey(foo, 'a', 1)); 11 | expect(foo.findByKey('a', 2)).toEqual(findByKey(foo, 'a', 2)); 12 | 13 | expect(foo.findByKey('a', 4)).toBe(findByKey(foo, 'a', 4)); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /config/tsconfig.doc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "allowSyntheticDefaultImports": true 5 | }, 6 | "typedocOptions": { 7 | "mode": "modules", 8 | "out": "docs", 9 | "excludeNotExported": true, 10 | "excludePrivate": true, 11 | "excludeExternals": true, 12 | "disableOutputCheck": true, 13 | "gitRevision": "master", 14 | "exclude": [ 15 | "**/index.ts", 16 | "**/+(proto|static)/*", 17 | "**/*.spec.ts" 18 | ] 19 | } 20 | } -------------------------------------------------------------------------------- /src/array/find-by-key.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | import { findByKey } from './find-by-key'; 3 | 4 | describe('FindByKey Function', () => { 5 | let foo: {a: number; }[]; 6 | 7 | beforeEach(() => { 8 | foo = [{a: 1}, {a: 2}, {a: 3}]; 9 | }); 10 | 11 | it('should find item successfully', () => { 12 | expect(findByKey(foo, 'a', 1)).toEqual({a: 1}); 13 | }); 14 | 15 | it('should return `undefined` when item not found', () => { 16 | expect(findByKey(foo, 'a', 4)).toBe(undefined); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /src/array/proto/compact.ts: -------------------------------------------------------------------------------- 1 | import { compact } from '../compact'; 2 | 3 | declare global { 4 | export interface Array { 5 | /** 6 | * Filters falsy values of the given array 7 | * 8 | * __Does not modify the original array__ 9 | * 10 | * Values to be filtered: `[undefined, null, "", 0, false, NaN]` 11 | * 12 | * @returns T[] filtered array 13 | */ 14 | compact(): T[]; 15 | } 16 | } 17 | 18 | Array.prototype.compact = function() { 19 | return compact(this); 20 | }; 21 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "CommonJS", 4 | "target": "esnext", 5 | "outDir": "./dist", 6 | "moduleResolution": "node", 7 | "declaration": true, 8 | "experimentalDecorators": true, 9 | "emitDecoratorMetadata": true, 10 | "allowSyntheticDefaultImports": true, 11 | "lib": [ 12 | "esnext", 13 | "esnext.asynciterable" 14 | ], 15 | "types": [ 16 | "node" 17 | ] 18 | }, 19 | "include": [ 20 | "src/" 21 | ], 22 | "exclude": [ 23 | "node_modules/" 24 | ] 25 | } -------------------------------------------------------------------------------- /.github/workflows/pullrequest.yml: -------------------------------------------------------------------------------- 1 | name: Pull Request 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | test-and-lint: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v1 13 | - uses: actions/setup-node@v1 14 | with: 15 | node-version: 12 16 | - run: npm install 17 | - run: npm run test:coverage 18 | - name: Upload coverage to Codecov 19 | uses: codecov/codecov-action@v1 20 | with: 21 | token: ${{ secrets.CODECOV_TOKEN }} 22 | file: ./coverage/coverage-final.json -------------------------------------------------------------------------------- /src/arrayize.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Encapsulates a non array value with an array that contains it unless the value is already an array 3 | * 4 | * * * * 5 | * Example usage: 6 | * ```typescript 7 | * import { arrayize } from "@thalesrc/js-utils"; 8 | * 9 | * const foo = 'foo'; 10 | * const bar = ['bar']; 11 | * const fooArr = arrayize(foo); // ['foo']; 12 | * const barArr = arrayize(bar); // ['bar']; 13 | * ``` 14 | * * * * 15 | * @param value Array or single value to capsulate 16 | */ 17 | export function arrayize(value: T | T[]): T[] { 18 | return value instanceof Array ? value : [value]; 19 | } 20 | -------------------------------------------------------------------------------- /src/array/remove.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import { remove } from './remove'; 4 | 5 | describe('Remove Function', () => { 6 | let foo: number[]; 7 | 8 | beforeEach(() => { 9 | foo = [1, 2, 3, 1, 2, 3]; 10 | }); 11 | 12 | it('should remove one item successfully', () => { 13 | expect(remove(foo, 2)).toEqual([1, 3, 1, 2, 3]); 14 | }); 15 | 16 | it('shouldn\'t remove nonfound item', () => { 17 | expect(remove(foo, 4)).toEqual([1, 2, 3, 1, 2, 3]); 18 | }); 19 | 20 | it('should remove multiply', () => { 21 | expect(remove(foo, 3, true)).toEqual([1, 2, 1, 2]); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /src/arrayize.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import { arrayize } from './arrayize'; 4 | 5 | describe('Arrayize Function', () => { 6 | it('should encapsulate a non array value', () => { 7 | const foo = 'foo'; 8 | const bar = {bar: 'bar'}; 9 | const baz = null; 10 | 11 | expect(arrayize(foo)).toEqual(['foo']); 12 | expect(arrayize(bar)).toEqual([{bar: 'bar'}]); 13 | expect(arrayize(baz)).toEqual([null]); 14 | }); 15 | 16 | it('should not encapsulate when the value is an array', () => { 17 | const foo = ['foo']; 18 | 19 | expect(arrayize(foo)).toEqual(['foo']); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/math/static/min-max.ts: -------------------------------------------------------------------------------- 1 | import { minMax } from '../min-max'; 2 | 3 | declare global { 4 | export interface Math { 5 | /** 6 | * #### Limits the value by given parameters 7 | * 8 | * * * * 9 | * ```typescript 10 | * import "@thalesrc/js-utils/math/static/min-max"; 11 | * 12 | * const limitedValue = Math.minMax(200, 300, Math.random() * 1000); 13 | * ``` 14 | * * * * 15 | * @param min minimum limit 16 | * @param max maximum limit 17 | * @param value value to limit 18 | */ 19 | minMax: typeof minMax; 20 | } 21 | } 22 | 23 | Math.minMax = minMax; 24 | -------------------------------------------------------------------------------- /src/object/compact.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import { compact } from './compact'; 4 | 5 | describe('Compact Function', () => { 6 | it('should delete null values', () => { 7 | const a = {x: null, y: null, z: 20}; 8 | 9 | expect(compact(a)).toEqual({z: 20}); 10 | }); 11 | 12 | it('should delete undefined values', () => { 13 | const a = {x: undefined, y: undefined, z: 20}; 14 | 15 | expect(compact(a)).toEqual({z: 20}); 16 | }); 17 | 18 | it('should delete undefined/null values', () => { 19 | const a = {x: undefined, y: null, z: 20}; 20 | 21 | expect(compact(a)).toEqual({z: 20}); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /src/array/proto/difference.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import './difference'; 4 | import { difference } from '../difference'; 5 | 6 | describe('Array Difference Proto Function', () => { 7 | it('should work as same', () => { 8 | const foo = [1, 2, 3, 1, 2, 3]; 9 | const bar = [1, 2]; 10 | const baz = new Set([2, 3]); 11 | 12 | expect(foo.difference(bar)).toEqual(difference(foo, bar)); 13 | expect(foo.difference(baz)).toEqual(difference(foo, baz)); 14 | 15 | expect(foo.difference(bar, true)).toEqual(difference(foo, bar, true)); 16 | expect(foo.difference(baz, true)).toEqual(difference(foo, baz, true)); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { ReplaceByMapOptions, ReplaceItemsOptions, TInclusion, TSubstraction, asyncMap, difference, findByKey, intersection, remove, replace, uniquify, uniquifyByKey } from './array'; 2 | export * from './function'; 3 | export * from './map'; 4 | export * from './math'; 5 | export { clone, deepest } from './object'; 6 | export * from './promise'; 7 | export { } from './string'; 8 | 9 | export * from './arrayize'; 10 | export * from './compact'; 11 | export * from './is-falsy'; 12 | export * from './is-truthy'; 13 | export * from './open-promise'; 14 | export * from './smart-map'; 15 | export * from './unique-id'; 16 | export * from './limit'; 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /src/array/compact.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import { compact } from './compact'; 4 | 5 | describe('Compact Function', () => { 6 | it('should remove all falsy values', () => { 7 | const array = [0, '', undefined, NaN, null, false]; 8 | const result = compact([...array, ...array]); 9 | expect(result.length).toBe(0); 10 | }); 11 | 12 | it('should keep only truthy values', () => { 13 | const falsyValues = [0, '', undefined, NaN, null, false]; 14 | const truthyValues = ['asd', 1, true, [], {}, Symbol()]; 15 | const result = compact([...falsyValues, ...truthyValues]); 16 | expect(result).toEqual(truthyValues); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /src/array/proto/compact.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import './compact'; 4 | 5 | describe('Compact Proto Function', () => { 6 | it('should remove all falsy values', () => { 7 | const array = [0, '', undefined, NaN, null, false]; 8 | const result = [...array, ...array].compact(); 9 | expect(result.length).toBe(0); 10 | }); 11 | 12 | it('should keep only truthy values', () => { 13 | const falsyValues = [0, '', undefined, NaN, null, false]; 14 | const truthyValues = ['asd', 1, true, [], {}, Symbol()]; 15 | const result = [...falsyValues, ...truthyValues].compact(); 16 | expect(result).toEqual(truthyValues); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /src/set/proto/difference.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import './difference'; 4 | import { difference } from '../../array/difference'; 5 | 6 | describe('Set Difference Proto Function', () => { 7 | it('should work as same', () => { 8 | const foo = new Set([1, 2, 3, 4, 5]); 9 | const bar = [1, 2]; 10 | const baz = new Set([2, 3]); 11 | 12 | expect(foo.difference(bar)).toEqual(difference(foo, bar)); 13 | expect(foo.difference(baz)).toEqual(difference(foo, baz)); 14 | 15 | expect(foo.difference(bar, true)).toEqual(difference(foo, bar, true)); 16 | expect(foo.difference(baz, true)).toEqual(difference(foo, baz, true)); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /src/array/proto/intersection.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import './intersection'; 4 | import { intersection } from '../intersection'; 5 | 6 | describe('Array Intersecion Proto Function', () => { 7 | it('should work as same', () => { 8 | const foo = [1, 2, 3, 1, 2, 3]; 9 | const bar = [1, 2]; 10 | const baz = new Set([2, 3]); 11 | 12 | expect(foo.intersection(bar)).toEqual(intersection(foo, bar)); 13 | expect(foo.intersection(baz)).toEqual(intersection(foo, baz)); 14 | 15 | expect(foo.intersection(bar, false)).toEqual(intersection(foo, bar, false)); 16 | expect(foo.intersection(baz, false)).toEqual(intersection(foo, baz, false)); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /src/function/of.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import { of } from './of'; 4 | 5 | describe('FunctionOf Function', () => { 6 | it('should create a function which returns given function', () => { 7 | const returnFoo = of('foo'); 8 | const returnBar = of('bar'); 9 | const fooResult = returnFoo(); 10 | const barResult = returnBar(); 11 | 12 | expect(fooResult).toBe('foo'); 13 | expect(barResult).toBe('bar'); 14 | }); 15 | 16 | it('should cache created functions and should use from there', () => { 17 | const previouslyCreated = of('foo'); 18 | 19 | expect(previouslyCreated).toBe(of('foo')); 20 | expect(of('foo')).toBe(of('foo')); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /src/promise/try-catch.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import { tryCatch } from './try-catch'; 4 | 5 | describe('AsyncTryCatch Function', () => { 6 | it('should resolve if promise resolved', done => { 7 | const foo = Promise.resolve('foo'); 8 | 9 | tryCatch(foo).then(([err, res]) => { 10 | expect(err).toBe(null); 11 | expect(res).toBe('foo'); 12 | done(); 13 | }); 14 | }); 15 | 16 | it('should reject if promise rejected', done => { 17 | const foo = Promise.reject('error'); 18 | 19 | tryCatch(foo).then(([err, res]) => { 20 | expect(err).toBe('error'); 21 | expect(res).toBe(null); 22 | done(); 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/array/proto/async-map.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import './async-map'; 4 | import '../../promise/static/timeout'; 5 | import { asyncMap } from '../async-map'; 6 | 7 | describe('Array Async Map Proto Function', () => { 8 | it('should work as same', done => { 9 | const foo = [1, 2, 3]; 10 | 11 | async function addOneAfterSomeTime(value) { 12 | return await Promise.timeout(Math.random() * 100).then(() => value + 1); 13 | } 14 | 15 | Promise.all([ 16 | asyncMap(foo, addOneAfterSomeTime), 17 | foo.asyncMap(addOneAfterSomeTime) 18 | ]).then(([first, second]) => { 19 | expect(first).toEqual(second); 20 | done(); 21 | }); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /src/array/uniquify-by-key.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import { uniquifyByKey } from './uniquify-by-key'; 4 | 5 | describe('Uniquify By Key Function', () => { 6 | it('should remove repeated items from array', () => { 7 | const a1 = [{a: 1}, {a: 1}, {a: 2}, {a: 3}, {a: 3}, {a: 4}]; 8 | const a2 = [{a: 1}, {a: 2}, {a: 3}, {a: 4}, {a: 1}]; 9 | 10 | const firstObj = {a: 1}; 11 | const a3 = [firstObj, {a: 2}, {a: 3}, {a: 1}, {a: 1}]; 12 | 13 | expect(uniquifyByKey(a1, 'a')).toEqual([{a: 1}, {a: 2}, {a: 3}, {a: 4}]); 14 | expect(uniquifyByKey(a2, 'a')).toEqual([{a: 1}, {a: 2}, {a: 3}, {a: 4}]); 15 | expect(uniquifyByKey(a3, 'a')[0]).toBe(firstObj); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/promise/proto/try-catch.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import './try-catch'; 4 | 5 | describe('Promise Try Catch Proto Function', () => { 6 | it('should resolve when its resolved', done => { 7 | Promise.resolve('foo') 8 | .tryCatch() 9 | .then(([error, result]) => { 10 | expect(error).toBe(null); 11 | expect(result).toBe('foo'); 12 | done(); 13 | }); 14 | }); 15 | 16 | it('should reject when its rejected', done => { 17 | Promise.reject('foo') 18 | .tryCatch() 19 | .then(([error, result]) => { 20 | expect(error).toBe('foo'); 21 | expect(result).toBe(null); 22 | done(); 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/set/proto/intersection.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import './intersection'; 4 | import { intersection } from '../../array/intersection'; 5 | 6 | describe('Set Intersection Proto Function', () => { 7 | it('should work as same', () => { 8 | const foo = new Set([1, 2, 3, 4, 5]); 9 | const bar = [1, 2]; 10 | const baz = new Set([2, 3]); 11 | 12 | expect(foo.intersection(bar)).toEqual(intersection(foo, bar)); 13 | expect(foo.intersection(baz)).toEqual(intersection(foo, baz)); 14 | 15 | expect(foo.intersection(bar, false)).toEqual(intersection(foo, bar, false)); 16 | expect(foo.intersection(baz, false)).toEqual(intersection(foo, baz, false)); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /src/promise/static/timeout.ts: -------------------------------------------------------------------------------- 1 | import { timeout, PromiseTimeoutFunction } from '../timeout'; 2 | 3 | declare global { 4 | export interface PromiseConstructor { 5 | /** 6 | * #### Promise Timeout 7 | * Returns a promise which resolves after given time 8 | * 9 | * * * * 10 | * Example: 11 | * ```typescript 12 | * import "@thalesrc/js-utils/promise/static/timeout"; 13 | * 14 | * Promise.timeout(1000); 15 | * .then(() => console.log("will be logged after a second")); 16 | * ``` 17 | * * * * 18 | * @see PromiseTimeoutFunction#cancel for cancelling 19 | */ 20 | timeout: PromiseTimeoutFunction; 21 | } 22 | } 23 | 24 | Promise.timeout = timeout; 25 | -------------------------------------------------------------------------------- /src/promise/revert.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import { revert } from './revert'; 4 | 5 | class CustomError { 6 | constructor(public message: string) {} 7 | } 8 | 9 | describe('Promise Revert Function', () => { 10 | it('should throw error when its resolved', done => { 11 | revert(Promise.resolve('foo')) 12 | .then(() => { 13 | throw new CustomError('bar'); 14 | }) 15 | .catch(err => { 16 | expect(err).toBe('foo'); 17 | done(); 18 | }); 19 | }); 20 | 21 | it('should resolve when its rejected', done => { 22 | revert(Promise.reject('foo')) 23 | .then(res => { 24 | expect(res).toBe('foo'); 25 | done(); 26 | }); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /src/string/limit.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import { limit } from './limit'; 4 | 5 | describe('Limit Function', () => { 6 | let foo: string; 7 | 8 | beforeEach(() => { 9 | foo = 'foobarbaz'; 10 | }); 11 | 12 | it('should slice the string up', () => { 13 | expect(limit(foo, 3)).toBe('foo'); 14 | }); 15 | 16 | it('should return empty string when count is 0', () => { 17 | expect(limit(foo, 0)).toBe(''); 18 | }); 19 | 20 | it('shouldn\'t slice when count is negative', () => { 21 | expect(limit(foo, -1)).toBe('foobarbaz'); 22 | }); 23 | 24 | it('should return whole items when count is bigger than length', () => { 25 | expect(limit(foo, 10)).toBe('foobarbaz'); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /src/array/proto/uniquify.ts: -------------------------------------------------------------------------------- 1 | import { uniquify } from '../uniquify'; 2 | 3 | declare global { 4 | export interface Array { 5 | /** 6 | * #### Uniquify 7 | * 8 | * Removes repeated items from the array 9 | * 10 | * * * * 11 | * Example: 12 | * ```typescript 13 | * import "@thalesrc/js-utils/array/proto/uniquify"; 14 | * 15 | * const array = ["a", "b", "c", "a", "b", "c"]; 16 | * 17 | * array.uniquify(); // ["a", "b", "c"] 18 | * ``` 19 | * * * * 20 | * @return The new uniquified array 21 | */ 22 | uniquify(): T[]; 23 | } 24 | } 25 | 26 | Array.prototype.uniquify = function(this: T[]): T[] { 27 | return uniquify(this); 28 | }; 29 | -------------------------------------------------------------------------------- /src/array/uniquify.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * #### Uniquify 3 | * 4 | * Removes repeated items from the array 5 | * 6 | * * * * 7 | * Example: 8 | * ```typescript 9 | * import { uniquify } "@thalesrc/js-utils/array"; 10 | * 11 | * const array = ["a", "b", "c", "a", "b", "c"]; 12 | * 13 | * uniquify(array); // ["a", "b", "c"] 14 | * ``` 15 | * 16 | * Prototype Example: 17 | * ```typescript 18 | * import "@thalesrc/js-utils/array/proto/uniquify"; 19 | * 20 | * const array = ["a", "b", "c", "a", "b", "c"]; 21 | * 22 | * array.uniquify(); // ["a", "b", "c"] 23 | * ``` 24 | * * * * 25 | * @return The new uniquified array 26 | */ 27 | export function uniquify(array: T[]): T[] { 28 | return [...new Set(array)]; 29 | } 30 | -------------------------------------------------------------------------------- /src/array/proto/limit.ts: -------------------------------------------------------------------------------- 1 | import { limit } from '../limit'; 2 | 3 | declare global { 4 | export interface Array { 5 | /** 6 | * #### Limit 7 | * 8 | * Returns first `n` children of an array 9 | * 10 | * * * * 11 | * Example: 12 | * ```typescript 13 | * import "@thalesrc/js-utils/array/proto/limit"; 14 | * 15 | * const array = ["a", "b", "c", "d", "e", "f"]; 16 | * 17 | * array.limit(3); // ["a", "b", "c"] 18 | * ``` 19 | * * * * 20 | * @param count Limiter 21 | * @return New array 22 | */ 23 | limit(count: number): T[]; 24 | } 25 | } 26 | 27 | Array.prototype.limit = function(count: number) { 28 | return limit(this, count); 29 | }; 30 | -------------------------------------------------------------------------------- /src/array/proto/uniquify-by-key.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import './uniquify-by-key'; 4 | import { uniquifyByKey } from '../uniquify-by-key'; 5 | 6 | describe('Array Uniquify By Key Proto Function', () => { 7 | it('should work as same', () => { 8 | const foo = [{a: 1}, {a: 1}, {a: 2}, {a: 3}, {a: 3}, {a: 4}]; 9 | const bar = [{a: 1}, {a: 2}, {a: 3}, {a: 4}, {a: 1}]; 10 | 11 | const firstObj = {a: 1}; 12 | const baz = [firstObj, {a: 2}, {a: 3}, {a: 1}, {a: 1}]; 13 | 14 | expect(foo.uniquifyByKey('a')).toEqual(uniquifyByKey(foo, 'a')); 15 | expect(bar.uniquifyByKey('a')).toEqual(uniquifyByKey(bar, 'a')); 16 | expect(baz.uniquifyByKey('a')[0]).toBe(uniquifyByKey(baz, 'a')[0]); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /src/string/proto/limit.ts: -------------------------------------------------------------------------------- 1 | import { limit } from '../limit'; 2 | 3 | declare global { 4 | export interface String { 5 | /** 6 | * #### Limit 7 | * 8 | * Limits the string to `n` character 9 | * 10 | * * * * 11 | * Example: 12 | * ```typescript 13 | * import "@thalesrc/js-utils/string/proto/limit"; 14 | * 15 | * const str = 'foobarbaz'; 16 | * 17 | * str.limit(3); // 'foo' 18 | * ``` 19 | * * * * 20 | * @param count Count to limit string character size 21 | * @return Limited string 22 | */ 23 | limit(count: number): string; 24 | } 25 | } 26 | 27 | String.prototype.limit = function(count: number) { 28 | return limit(this, count); 29 | }; 30 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: alisahin 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /src/promise/proto/revert.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import './revert'; 4 | 5 | class CustomError { 6 | constructor(public message: string) {} 7 | } 8 | 9 | describe('Promise Revert Proto Function', () => { 10 | it('should throw error when its resolved', done => { 11 | Promise.resolve('foo') 12 | .revert() 13 | .then(() => { 14 | throw new CustomError('bar'); 15 | }) 16 | .catch(err => { 17 | expect(err).toBe('foo'); 18 | done(); 19 | }); 20 | }); 21 | 22 | it('should resolve when its rejected', done => { 23 | Promise.reject('foo') 24 | .revert() 25 | .then(res => { 26 | expect(res).toBe('foo'); 27 | done(); 28 | }); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /src/array/limit.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import { limit } from './limit'; 4 | 5 | describe('Limit Function', () => { 6 | let foo: number[]; 7 | 8 | beforeEach(() => { 9 | foo = [1, 2, 3, 4, 5, 6]; 10 | }); 11 | 12 | it('should slice the array up', () => { 13 | expect(limit(foo, 3)).toEqual([1, 2, 3]); 14 | }); 15 | 16 | it('should return empty array when count is 0', () => { 17 | expect(limit(foo, 0)).toEqual([]); 18 | }); 19 | 20 | it('shouldn\'t slice when count is negative', () => { 21 | expect(limit(foo, -1)).toEqual([1, 2, 3, 4, 5, 6]); 22 | }); 23 | 24 | it('should return whole items when count is bigger than length', () => { 25 | expect(limit(foo, 10)).toEqual([1, 2, 3, 4, 5, 6]); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /src/is-falsy.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import { isFalsy } from './is-falsy'; 4 | 5 | describe('IsFalsy Function', () => { 6 | it('should return "false" when truthy values passed', () => { 7 | expect(isFalsy(true)).toBe(false); 8 | expect(isFalsy('a')).toBe(false); 9 | expect(isFalsy(1)).toBe(false); 10 | expect(isFalsy(-1)).toBe(false); 11 | expect(isFalsy({})).toBe(false); 12 | }); 13 | 14 | it('should return "true" when falsy values passed', () => { 15 | expect(isFalsy(false)).toBe(true); 16 | expect(isFalsy('')).toBe(true); 17 | expect(isFalsy(0)).toBe(true); 18 | expect(isFalsy(null)).toBe(true); 19 | expect(isFalsy(undefined)).toBe(true); 20 | expect(isFalsy(NaN)).toBe(true); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /src/object/static/compact.ts: -------------------------------------------------------------------------------- 1 | import { compact } from '../compact'; 2 | 3 | declare global { 4 | export interface ObjectConstructor { 5 | /** 6 | * #### Removes `null` and `undefined` values and their keys from an object 7 | * 8 | * * * * * 9 | * Example usage: 10 | * ```typescript 11 | * import "@thalesrc/js-utils/object/static/compact"; 12 | * 13 | * const a = { 14 | * x: null, 15 | * y: undefined, 16 | * z: 20 17 | * }; 18 | * 19 | * Object.compact(a); // {z: 20} 20 | * ``` 21 | * * * * 22 | * @param object Object delete empty keys 23 | * @returns Compacted object 24 | */ 25 | compact: typeof compact; 26 | } 27 | } 28 | 29 | Object.compact = compact; 30 | -------------------------------------------------------------------------------- /src/is-truthy.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import { isTruthy } from './is-truthy'; 4 | 5 | describe('IsTruthy Function', () => { 6 | it('should return "true" when truthy values passed', () => { 7 | expect(isTruthy(true)).toBe(true); 8 | expect(isTruthy('a')).toBe(true); 9 | expect(isTruthy(1)).toBe(true); 10 | expect(isTruthy(-1)).toBe(true); 11 | expect(isTruthy({})).toBe(true); 12 | }); 13 | 14 | it('should return "false" when falsy values passed', () => { 15 | expect(isTruthy(false)).toBe(false); 16 | expect(isTruthy('')).toBe(false); 17 | expect(isTruthy(0)).toBe(false); 18 | expect(isTruthy(null)).toBe(false); 19 | expect(isTruthy(undefined)).toBe(false); 20 | expect(isTruthy(NaN)).toBe(false); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /src/unique-id.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import { uniqueId } from './unique-id'; 4 | 5 | describe('Unique Id Function', () => { 6 | it('should return a unique number on every call', () => { 7 | const idSet = []; 8 | for (let i = 0; i < 100; i++) { 9 | const id = uniqueId(); 10 | expect(idSet).not.toContain(id); 11 | idSet.push(id); 12 | } 13 | }); 14 | 15 | it('should start a new counter for each unique prefix', () => { 16 | expect(uniqueId('foo')).toBe('foo-0'); 17 | expect(uniqueId('bar')).toBe('bar-0'); 18 | expect(uniqueId('baz')).toBe('baz-0'); 19 | 20 | expect(uniqueId('foo')).toBe('foo-1'); 21 | expect(uniqueId('bar')).toBe('bar-1'); 22 | expect(uniqueId('baz')).toBe('baz-1'); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/boolean/static/is-falsy.ts: -------------------------------------------------------------------------------- 1 | import { isFalsy } from '../../is-falsy'; 2 | 3 | declare global { 4 | export interface BooleanConstructor { 5 | /** 6 | * #### Returns whether the entered value is falsy 7 | * 8 | * * * * 9 | * Example usage: 10 | * ```typescript 11 | * import "@thalesrc/js-utils/boolean/static/is-falsy"; 12 | * 13 | * Boolean.isFalsy(undefined); // true 14 | * Boolean.isFalsy(true); // false 15 | * Boolean.isFalsy([]) // false 16 | * 17 | * const falsyValues = ["a", undefined, "b", "", "c"].filter(Boolean.isFalsy); // [undefined, ""] 18 | * ``` 19 | * * * * 20 | * @param value value to be checked 21 | */ 22 | isFalsy: typeof isFalsy; 23 | } 24 | } 25 | 26 | Boolean.isFalsy = isFalsy; 27 | -------------------------------------------------------------------------------- /src/boolean/static/is-truthy.ts: -------------------------------------------------------------------------------- 1 | import { isTruthy } from '../../is-truthy'; 2 | 3 | declare global { 4 | export interface BooleanConstructor { 5 | /** 6 | * #### Returns whether the entered value is truthy 7 | * 8 | * * * * 9 | * Example usage: 10 | * ```typescript 11 | * import "@thalesrc/js-utils/boolean/static/is-truthy"; 12 | * 13 | * Boolean.isTruthy(undefined); // false 14 | * Boolean.isTruthy(true); // true 15 | * Boolean.isTruthy([]) // true 16 | * 17 | * const truthyValues = ["a", undefined, "b", "", "c"].filter(Boolean.isTruthy); // ["a", "b", "c"] 18 | * ``` 19 | * * * * 20 | * @param value value to be checked 21 | */ 22 | isTruthy: typeof isTruthy; 23 | } 24 | } 25 | 26 | Boolean.isTruthy = isTruthy; 27 | -------------------------------------------------------------------------------- /src/math/min-max.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * #### Limits the value by specified range 3 | * 4 | * * * * 5 | * Example usage: 6 | * ```typescript 7 | * import { minMax } from "@thalesrc/js-utils/math"; 8 | * 9 | * const limitedValue = minMax(200, 300, Math.random() * 1000); 10 | * ``` 11 | * 12 | * Example as Math static: 13 | * ```typescript 14 | * import "@thalesrc/js-utils/math/static/min-max"; 15 | * 16 | * const limitedValue = Math.minMax(200, 300, Math.random() * 1000); 17 | * ``` 18 | * * * * 19 | * @param min minimum limit 20 | * @param max maximum limit 21 | * @param value value to limit 22 | * @return the limited value *(returns NaN if an argument is NaN)* 23 | */ 24 | export function minMax(min: number, max: number, value: number): number { 25 | return Math.max(Math.min(max, value), min); 26 | } 27 | -------------------------------------------------------------------------------- /src/map/merge.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * #### Map Merge 3 | * 4 | * Merges two maps 5 | * 6 | * * * * 7 | * Example usage: 8 | * ```typescript 9 | * import { merge } from "@thalesrc/js-utils/map"; 10 | * 11 | * const first = new Map(); 12 | * first.set("a", 1); 13 | * 14 | * const second = new Map(); 15 | * second.set("b", 2); 16 | * 17 | * merge(first, second); // [{key: "a", value: 1}, {key: "b", value: 2}] 18 | * ``` 19 | * * * * 20 | * @param first First map 21 | * @param second Second map 22 | * @returns A new merged map 23 | */ 24 | export function merge(first: Map, second: Map): Map { 25 | const newMap = new Map(first); 26 | 27 | for (const key of second.keys()) { 28 | newMap.set(key, second.get(key)); 29 | } 30 | 31 | return newMap; 32 | } 33 | -------------------------------------------------------------------------------- /src/string/limit.ts: -------------------------------------------------------------------------------- 1 | import { limit as arrLimit } from '../array/limit'; 2 | 3 | /** 4 | * #### Limit 5 | * 6 | * Limits the string to `n` character 7 | * 8 | * * * * 9 | * Example: 10 | * ```typescript 11 | * import { limit } from "@thalesrc/js-utils/string"; 12 | * 13 | * const str = 'foobarbaz'; 14 | * 15 | * limit(str, 3); // 'foo' 16 | * ``` 17 | * 18 | * Prototype Example: 19 | * ```typescript 20 | * import "@thalesrc/js-utils/string/proto/limit"; 21 | * 22 | * const str = 'foobarbaz'; 23 | * 24 | * str.limit(3); // 'foo' 25 | * ``` 26 | * * * * 27 | * @param str: String to limit 28 | * @param count Count to limit string character size 29 | * @return Limited string 30 | */ 31 | export function limit(str: string, count: number): string { 32 | return arrLimit(str.split(''), count).join(''); 33 | } 34 | -------------------------------------------------------------------------------- /src/boolean/static/is-falsy.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import './is-falsy'; 4 | 5 | describe('IsFalsy Static Function', () => { 6 | it('should return "false" when truthy values passed', () => { 7 | expect(Boolean.isFalsy(true)).toBe(false); 8 | expect(Boolean.isFalsy('a')).toBe(false); 9 | expect(Boolean.isFalsy(1)).toBe(false); 10 | expect(Boolean.isFalsy(-1)).toBe(false); 11 | expect(Boolean.isFalsy({})).toBe(false); 12 | }); 13 | 14 | it('should return "true" when falsy values passed', () => { 15 | expect(Boolean.isFalsy(false)).toBe(true); 16 | expect(Boolean.isFalsy('')).toBe(true); 17 | expect(Boolean.isFalsy(0)).toBe(true); 18 | expect(Boolean.isFalsy(null)).toBe(true); 19 | expect(Boolean.isFalsy(undefined)).toBe(true); 20 | expect(Boolean.isFalsy(NaN)).toBe(true); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /src/object/static/deepest.ts: -------------------------------------------------------------------------------- 1 | import { deepest } from '../deepest'; 2 | 3 | declare global { 4 | export interface ObjectConstructor { 5 | /** 6 | * #### Get deepest value in an object chain 7 | * 8 | * * * * * 9 | * Example usage: 10 | * ```typescript 11 | * import "@thalesrc/js-utils/object/static/deepest"; 12 | * 13 | * const a = { 14 | * x: null 15 | * }; 16 | * 17 | * const b = { 18 | * x: a 19 | * }; 20 | * 21 | * const c = { 22 | * x: b 23 | * }; 24 | * 25 | * Object.deepest(c, 'x'); // a 26 | * ``` 27 | * * * * 28 | * @param object Object to deep dive 29 | * @param key key of the object which contains same type instance 30 | */ 31 | deepest: typeof deepest; 32 | } 33 | } 34 | 35 | Object.deepest = deepest; 36 | -------------------------------------------------------------------------------- /src/boolean/static/is-truthy.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import './is-truthy'; 4 | 5 | describe('IsTruthy Static Function', () => { 6 | it('should return "true" when truthy values passed', () => { 7 | expect(Boolean.isTruthy(true)).toBe(true); 8 | expect(Boolean.isTruthy('a')).toBe(true); 9 | expect(Boolean.isTruthy(1)).toBe(true); 10 | expect(Boolean.isTruthy(-1)).toBe(true); 11 | expect(Boolean.isTruthy({})).toBe(true); 12 | }); 13 | 14 | it('should return "false" when falsy values passed', () => { 15 | expect(Boolean.isTruthy(false)).toBe(false); 16 | expect(Boolean.isTruthy('')).toBe(false); 17 | expect(Boolean.isTruthy(0)).toBe(false); 18 | expect(Boolean.isTruthy(null)).toBe(false); 19 | expect(Boolean.isTruthy(undefined)).toBe(false); 20 | expect(Boolean.isTruthy(NaN)).toBe(false); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /src/function/static/defer.ts: -------------------------------------------------------------------------------- 1 | import { defer } from '../defer'; 2 | 3 | declare global { 4 | export interface FunctionConstructor { 5 | /** 6 | * #### Delays the execution of the passed function to increase the render performance 7 | * 8 | * * * * 9 | * Example as promise static method 10 | * ```typescript 11 | * import "@thalesrc/js-utils/function/static/defer"; 12 | * 13 | * Function.defer(() => aFunctionToDefer()) 14 | * .then(res => ...) 15 | * .catch(err => ...); 16 | * ``` 17 | * * * * 18 | * @param callback Callback function to be executed 19 | * @typeparam T type of the return value of the callback 20 | * @returns A promise which resolves with the value of the callback right after the execution 21 | */ 22 | defer: typeof defer; 23 | } 24 | } 25 | 26 | Function.defer = defer; 27 | -------------------------------------------------------------------------------- /src/array/limit.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * #### Limit 3 | * 4 | * Returns first `n` children of an array 5 | * 6 | * * * * 7 | * Example: 8 | * ```typescript 9 | * import { limit } from "@thalesrc/js-utils/array"; 10 | * 11 | * const array = ["a", "b", "c", "d", "e", "f"]; 12 | * 13 | * limit(array, 3); // ["a", "b", "c"] 14 | * ``` 15 | * 16 | * Prototype Example: 17 | * ```typescript 18 | * import "@thalesrc/js-utils/array/proto/limit"; 19 | * 20 | * const array = ["a", "b", "c", "d", "e", "f"]; 21 | * 22 | * array.limit(3); // ["a", "b", "c"] 23 | * ``` 24 | * * * * 25 | * @param array: Array to return its children 26 | * @param count Limiter 27 | * @return New array 28 | */ 29 | export function limit(array: T[], count: number): T[] { 30 | if (count < 0) { 31 | count = Number.MAX_SAFE_INTEGER; 32 | } 33 | 34 | return array.slice(0, count); 35 | } 36 | -------------------------------------------------------------------------------- /src/promise/never.ts: -------------------------------------------------------------------------------- 1 | import { noop } from '../function/noop'; 2 | 3 | /** 4 | * A promise which never resolves 5 | * 6 | * Example: 7 | * ```typescript 8 | * import { NEVER } from '@thalesrc/js-utils/promise'; 9 | * 10 | * function foo(promise = NEVER) { 11 | * promise.then(val => { 12 | * ... 13 | * }); 14 | * } 15 | * ``` 16 | */ 17 | export const NEVER = new Promise(noop); 18 | 19 | /** 20 | * Creates a promise which never resolves 21 | * 22 | * Example: 23 | * ```typescript 24 | * import { never } from '@thalesrc/js-utils/promise'; 25 | * 26 | * function foo(promise) { 27 | * promise = promise || never(); 28 | * 29 | * promise.then(val => { 30 | * ... 31 | * }); 32 | * } 33 | * ``` 34 | * 35 | * @returns the promise which never resolves 36 | */ 37 | export function never(): Promise { 38 | return NEVER; 39 | } 40 | -------------------------------------------------------------------------------- /.github/workflows/pushmaster.yml: -------------------------------------------------------------------------------- 1 | name: Master Update 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | send-codecov: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v1 13 | - uses: actions/setup-node@v1 14 | with: 15 | node-version: 12 16 | - run: npm install 17 | - run: npm run test:coverage 18 | - name: Upload coverage to Codecov 19 | uses: codecov/codecov-action@v1 20 | with: 21 | token: ${{ secrets.CODECOV_TOKEN }} 22 | file: ./coverage/coverage-final.json 23 | - run: npm run prepare:github-pages 24 | - name: Publish Github Pages 25 | uses: peaceiris/actions-gh-pages@v2 26 | env: 27 | ACTIONS_DEPLOY_KEY: ${{ secrets.ACTIONS_DEPLOY_KEY }} 28 | PUBLISH_BRANCH: gh-pages 29 | PUBLISH_DIR: ./docs -------------------------------------------------------------------------------- /src/function/static/of.ts: -------------------------------------------------------------------------------- 1 | import { of } from '../of'; 2 | 3 | declare global { 4 | export interface FunctionConstructor { 5 | /** 6 | * ### Function Of 7 | * 8 | * Creates a function which returns the value given 9 | * 10 | * * * * 11 | * Example: 12 | * ```typescript 13 | * import "@thalesrc/js-utils/function/static/of"; 14 | * 15 | * const base = [1, 2, 5, {}, "x", "y"]; 16 | * const mapTo = Function.of("thales rocks"); 17 | * 18 | * const mapped = base.map(mapTo); 19 | * // ["thales rocks", "thales rocks", "thales rocks", "thales rocks", "thales rocks", "thales rocks"] 20 | * ``` 21 | * * * * 22 | * @param returnValue The value which created function returns 23 | * @returns A function which returns the `returnValue` 24 | */ 25 | of: typeof of; 26 | } 27 | } 28 | 29 | Function.of = of; 30 | -------------------------------------------------------------------------------- /src/is-falsy.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * #### Returns whether the entered value is falsy 3 | * 4 | * * * * 5 | * Example usage: 6 | * ```typescript 7 | * import { isFalsy } from "@thalesrc/js-utils"; 8 | * 9 | * isFalsy(undefined); // true 10 | * isFalsy(true); // false 11 | * isFalsy([]) // false 12 | * 13 | * const falsyValues = ["a", undefined, "b", "", "c"].filter(isFalsy); // [undefined, ""] 14 | * ``` 15 | * Static usage example: 16 | * ```typescript 17 | * import "@thalesrc/js-utils/dist/as-static/is-falsy"; 18 | * 19 | * Boolean.isFalsy(undefined); // true 20 | * Boolean.isFalsy(true); // false 21 | * Boolean.isFalsy([]) // false 22 | * 23 | * const falsyValues = ["a", undefined, "b", "", "c"].filter(Boolean.isFalsy); // [undefined, ""] 24 | * ``` 25 | * * * * 26 | * @param value value to be checked 27 | */ 28 | export function isFalsy(value: any): boolean { 29 | return !value; 30 | } 31 | -------------------------------------------------------------------------------- /src/array/proto/remove.ts: -------------------------------------------------------------------------------- 1 | import { remove } from '../remove'; 2 | 3 | declare global { 4 | export interface Array { 5 | /** 6 | * #### Remove 7 | * 8 | * Removes an item from the array 9 | * 10 | * Removes all item references if multi is set to `true` 11 | * 12 | * * * * 13 | * Example: 14 | * ```typescript 15 | * import "@thalesrc/js-utils/array/proto/remove"; 16 | * 17 | * const array = ["a", "b", "c", "a", "b", "c"]; 18 | * 19 | * array.remove("b", true); // ["a", "c", "a", "c"] 20 | * ``` 21 | * * * * 22 | * @param itemToRemove Item to remove 23 | * @return New array 24 | */ 25 | remove(itemToRemove: T, multi?: boolean): T[]; 26 | } 27 | } 28 | 29 | Array.prototype.remove = function(this: T[], itemToRemove: T, multi = false): T[] { 30 | return remove.call(null, this, ...arguments); 31 | }; 32 | -------------------------------------------------------------------------------- /src/is-truthy.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * #### Returns whether the entered value is truthy 3 | * 4 | * * * * 5 | * Example usage: 6 | * ```typescript 7 | * import { isTruthy } from "@thalesrc/js-utils"; 8 | * 9 | * isTruthy(undefined); // false 10 | * isTruthy(true); // true 11 | * isTruthy([]) // true 12 | * 13 | * const truthyValues = ["a", undefined, "b", "", "c"].filter(isTruthy); // ["a", "b", "c"] 14 | * ``` 15 | * Static usage example: 16 | * ```typescript 17 | * import "@thalesrc/js-utils/dist/as-static/is-truthy"; 18 | * 19 | * Boolean.isTruthy(undefined); // false 20 | * Boolean.isTruthy(true); // true 21 | * Boolean.isTruthy([]) // true 22 | * 23 | * const truthyValues = ["a", undefined, "b", "", "c"].filter(Boolean.isTruthy); // ["a", "b", "c"] 24 | * ``` 25 | * * * * 26 | * @param value value to be checked 27 | */ 28 | export function isTruthy(value: any): boolean { 29 | return !!value; 30 | } 31 | -------------------------------------------------------------------------------- /src/array/proto/replace.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import './replace'; 4 | import { replace } from '../replace'; 5 | 6 | describe('Array Replace Proto Function', () => { 7 | it('should work as same', () => { 8 | const foo = [1, 2, 3, 1, 2, 3]; 9 | 10 | expect(foo.replace(2, 100)).toEqual(replace(foo, 2, 100)); 11 | expect(foo.replace(4, 100)).toEqual(replace(foo, 4, 100)); 12 | 13 | const itemOptions = {startingIndex: 2, deleteCount: 2, itemsToReplace: [100, 101]}; 14 | expect(foo.replace(itemOptions)).toEqual(replace(foo, itemOptions)); 15 | 16 | const mapOptions = {itemsToReplace: new Map([[2, 100], [3, 101]])}; 17 | expect(foo.replace(mapOptions)).toEqual(replace(foo, mapOptions)); 18 | 19 | const mapOptionsMulti = {itemsToReplace: new Map([[2, 100], [3, 101]]), multi: true}; 20 | expect(foo.replace(mapOptionsMulti)).toEqual(replace(foo, mapOptionsMulti)); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /src/promise/never.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import { never, NEVER } from './never'; 4 | import { timeout } from './timeout'; 5 | 6 | describe('Never Function', () => { 7 | it('should not resolve', done => { 8 | const foo = never(); 9 | 10 | foo.then(() => { 11 | throw new Error(); 12 | }, () => { 13 | throw new Error(); 14 | }); 15 | 16 | timeout(50) 17 | .then(() => { 18 | done(); 19 | }); 20 | }); 21 | 22 | it('should return the same `NEVER` instance', () => { 23 | const foo = never(); 24 | 25 | expect(foo).toBe(NEVER); 26 | }); 27 | }); 28 | 29 | describe('Never Constant', () => { 30 | it('should not resolve', done => { 31 | NEVER.then(() => { 32 | throw new Error(); 33 | }, () => { 34 | throw new Error(); 35 | }); 36 | 37 | timeout(50) 38 | .then(() => { 39 | done(); 40 | }); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /src/promise/try-one-by-one.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import { tryOneByOne } from './try-one-by-one'; 4 | 5 | class CustomError { 6 | constructor(public message: string) {} 7 | } 8 | 9 | describe('TryOneByOne Function', () => { 10 | it('should resolve if a promise resolved', done => { 11 | tryOneByOne([Promise.resolve('foo')]) 12 | .then(res => { 13 | expect(res).toBe('foo'); 14 | done(); 15 | }) 16 | .catch(err => { 17 | throw new CustomError("didn't resolve"); 18 | }); 19 | }); 20 | 21 | it('should try the next promise when the first threw error', done => { 22 | tryOneByOne([ 23 | Promise.reject('foo'), 24 | Promise.resolve('bar'), 25 | ]) 26 | .then(res => { 27 | expect(res).toBe('bar'); 28 | done(); 29 | }) 30 | .catch(err => { 31 | throw new CustomError("didn't resolve"); 32 | }); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /src/array/proto/find-by-key.ts: -------------------------------------------------------------------------------- 1 | import { findByKey } from '../find-by-key'; 2 | 3 | declare global { 4 | export interface Array { 5 | /** 6 | * #### Find By Key 7 | * 8 | * Finds an object in an array by matching the value set on the key 9 | * 10 | * * * * 11 | * Example: 12 | * ```typescript 13 | * import "@thalesrc/js-utils/array/proto/find-by-key"; 14 | * 15 | * const array = [{a: 1}, {a: 2}, {a: 3}]; 16 | * 17 | * array.findByKey("a", 2); // {a: 2} 18 | * ``` 19 | * * * * 20 | * @param key Key to search the value on 21 | * @param value Value to match for the key 22 | * @return Found object or undefined 23 | */ 24 | findByKey(key: K, value: T[K]): T; 25 | } 26 | } 27 | 28 | Array.prototype.findByKey = function(this: T[], key: K, value: T[K]): T { 29 | return findByKey(this, key, value); 30 | }; 31 | -------------------------------------------------------------------------------- /src/promise/static/try-one-by-one.ts: -------------------------------------------------------------------------------- 1 | import { tryOneByOne } from '../try-one-by-one'; 2 | 3 | declare global { 4 | export interface PromiseConstructor { 5 | /** 6 | * #### Try One By One 7 | * Tries a set of promises one by one with given order. Breaks the call when a promise resolved. Otherwise keeps trying incoming promises until the list is finished. 8 | * 9 | * * * * 10 | * Example: 11 | * ```typescript 12 | * import "@thalesrc/js-utils/promise/static/try-one-by-one"; 13 | * 14 | * async function fooFunction() { 15 | * const foo = await Promise.tryOneByOne([ 16 | * () => someCall(), 17 | * (err) => anotherCall(), 18 | * (err) => fooPromise() 19 | * ]); 20 | * 21 | * // do stuff 22 | * } 23 | * ``` 24 | * * * * 25 | */ 26 | tryOneByOne: typeof tryOneByOne; 27 | } 28 | } 29 | 30 | Promise.tryOneByOne = tryOneByOne; 31 | -------------------------------------------------------------------------------- /src/array/find-by-key.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * #### Find By Key 3 | * 4 | * Finds an object in an array by matching the value set on the key 5 | * 6 | * * * * 7 | * Example: 8 | * ```typescript 9 | * import { findByKey } from "@thalesrc/js-utils/array"; 10 | * 11 | * const array = [{a: 1}, {a: 2}, {a: 3}]; 12 | * 13 | * findByKey(array, "a", 2); // {a: 2} 14 | * ``` 15 | * 16 | * Prototype Example: 17 | * ```typescript 18 | * import "@thalesrc/js-utils/array/proto/find-by-key"; 19 | * 20 | * const array = [{a: 1}, {a: 2}, {a: 3}]; 21 | * 22 | * array.findByKey("a", 2); // {a: 2} 23 | * ``` 24 | * * * * 25 | * @param array Collection of the objects 26 | * @param key Key to search the value on 27 | * @param value Value to match for the key 28 | * @return Found object or undefined 29 | */ 30 | export function findByKey(array: T[], key: K, value: T[K]): T { 31 | return array.find(item => item[key] === value); 32 | } 33 | -------------------------------------------------------------------------------- /src/array/proto/uniquify-by-key.ts: -------------------------------------------------------------------------------- 1 | import { uniquifyByKey } from '../uniquify-by-key'; 2 | 3 | declare global { 4 | export interface Array { 5 | /** 6 | * #### Uniquify By Key 7 | * 8 | * Removes objects from the array which the value of its specifed key included before by another 9 | * 10 | * * * * 11 | * Example: 12 | * ```typescript 13 | * import "@thalesrc/js-utils/array/proto/uniquify-by-key"; 14 | * 15 | * const array = [{a: 1}, {a: 1}, {a: 2}, {a: 3}, {a: 3}, {a: 4}]; 16 | * 17 | * array.uniquifyByKey('a'); // [{a: 1}, {a: 2}, {a: 3}, {a: 4}] 18 | * ``` 19 | * * * * 20 | * @param key Key to search the value on 21 | * @return The new uniquified array 22 | */ 23 | uniquifyByKey(key: keyof T): T[]; 24 | } 25 | } 26 | 27 | Array.prototype.uniquifyByKey = function(this: T[], key: keyof T): T[] { 28 | return uniquifyByKey(this, key); 29 | }; 30 | -------------------------------------------------------------------------------- /src/promise/static/never.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import './never'; 4 | 5 | import { timeout } from '../timeout'; 6 | 7 | describe('Never Function', () => { 8 | it('should not resolve', done => { 9 | const foo = Promise.never(); 10 | 11 | foo.then(() => { 12 | throw new Error(); 13 | }, () => { 14 | throw new Error(); 15 | }); 16 | 17 | timeout(50) 18 | .then(() => { 19 | done(); 20 | }); 21 | }); 22 | 23 | it('should return the same `NEVER` instance', () => { 24 | const foo = Promise.never(); 25 | 26 | expect(foo).toBe(Promise.NEVER); 27 | }); 28 | }); 29 | 30 | describe('Never Constant', () => { 31 | it('should not resolve', done => { 32 | Promise.NEVER.then(() => { 33 | throw new Error(); 34 | }, () => { 35 | throw new Error(); 36 | }); 37 | 38 | timeout(50) 39 | .then(() => { 40 | done(); 41 | }); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior: 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 4. See error 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | 20 | **Screenshots** 21 | If applicable, add screenshots to help explain your problem. 22 | 23 | **Desktop (please complete the following information):** 24 | - OS: [e.g. iOS] 25 | - Browser [e.g. chrome, safari] 26 | - Version [e.g. 22] 27 | 28 | **Smartphone (please complete the following information):** 29 | - Device: [e.g. iPhone6] 30 | - OS: [e.g. iOS8.1] 31 | - Browser [e.g. stock browser, safari] 32 | - Version [e.g. 22] 33 | 34 | **Additional context** 35 | Add any other context about the problem here. 36 | -------------------------------------------------------------------------------- /src/compact.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import { compact } from './compact'; 4 | import { compact as arrCompact } from './array/compact'; 5 | import { compact as objCompact } from './object/compact'; 6 | 7 | describe('Compact Function', () => { 8 | it('should act as array compact', () => { 9 | const arr = [0, undefined, null, '', 'a']; 10 | 11 | expect(compact(arr)).toEqual(arrCompact(arr)); 12 | }); 13 | 14 | it('should act as object compact', () => { 15 | const object = {x: undefined, y: null, z: 20}; 16 | 17 | expect(compact(object)).toEqual(objCompact(object)); 18 | }); 19 | 20 | it('should throw error when the value is not array nor object', () => { 21 | expect(() => compact(null)).toThrow(TypeError); 22 | expect(() => compact(0)).toThrow(TypeError); 23 | expect(() => compact('')).toThrow(TypeError); 24 | expect(() => compact(false)).toThrow(TypeError); 25 | expect(() => compact(Symbol())).toThrow(TypeError); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /src/limit.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import { limit } from './limit'; 4 | 5 | describe('Limit Universal Function', () => { 6 | let foo: number[]; 7 | let bar: string; 8 | 9 | beforeEach(() => { 10 | foo = [1, 2, 3, 4, 5, 6]; 11 | bar = 'foobarbaz'; 12 | }); 13 | 14 | it('should slice the array up', () => { 15 | expect(limit(foo, 3)).toEqual([1, 2, 3]); 16 | expect(limit(bar, 3)).toBe('foo'); 17 | }); 18 | 19 | it('should return empty array when count is 0', () => { 20 | expect(limit(foo, 0)).toEqual([]); 21 | expect(limit(bar, 0)).toBe(''); 22 | }); 23 | 24 | it('shouldn\'t slice when count is negative', () => { 25 | expect(limit(foo, -1)).toEqual([1, 2, 3, 4, 5, 6]); 26 | expect(limit(bar, -1)).toBe('foobarbaz'); 27 | }); 28 | 29 | it('should return whole items when count is bigger than length', () => { 30 | expect(limit(foo, 10)).toEqual([1, 2, 3, 4, 5, 6]); 31 | expect(limit(bar, 10)).toBe('foobarbaz'); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /src/promise/proto/revert.ts: -------------------------------------------------------------------------------- 1 | import { revert } from '../revert'; 2 | 3 | declare global { 4 | export interface Promise { 5 | /** 6 | * #### Revert Promise 7 | * 8 | * Exchanges resolve state with rejection of a promise 9 | * 10 | * * * * 11 | * Example usage: 12 | * ```typescript 13 | * import "@thalesrc/js-utils/dist/as-proto/promise-revert"; 14 | * 15 | * Promise.reject(new Error('foo')) 16 | * .revert() 17 | * .then(err => { 18 | * console.log("this will be logged", err); 19 | * }) 20 | * .catch(res => { 21 | * console.log("this won't be logged", res); 22 | * }); 23 | * ``` 24 | * * * * 25 | * @param promise The promise to revert its statements 26 | * @return the reverted promise 27 | */ 28 | revert(): Promise; 29 | } 30 | } 31 | 32 | Promise.prototype.revert = function(this: Promise): Promise { 33 | return revert(this); 34 | }; 35 | -------------------------------------------------------------------------------- /src/array/compact.ts: -------------------------------------------------------------------------------- 1 | import { isTruthy } from '../is-truthy'; 2 | 3 | /** 4 | * #### Filters falsy values of the given array 5 | * * does not modify the original array 6 | * * Values to be filtered: `[undefined, null, "", 0, false, NaN]` 7 | * 8 | * * * * 9 | * Example usage: 10 | * ```typescript 11 | * import { compact } from "@thalesrc/js-utils/array"; 12 | * 13 | * const arr = [undefined, "", false, 0, 1, "1"]; 14 | * const compacted = compact(arr); // [1, "1"]; 15 | * 16 | * ``` 17 | * 18 | * Example as Array Prototype: 19 | * ```typescript 20 | * import "@thalesrc/js-utils/array/proto/compact"; 21 | * 22 | * const arr = [undefined, "", false, 0, 1, "1"]; 23 | * const compacted = arr.compact(); // [1, "1"]; 24 | * ``` 25 | * * * * 26 | * @param arrayToCompact array to compact 27 | * @typeparam T type of array 28 | * @returns __a new__ compacted array 29 | */ 30 | export function compact(arrayToCompact: T[]): T[] { 31 | return arrayToCompact.filter(isTruthy); 32 | } 33 | -------------------------------------------------------------------------------- /src/array/proto/async-map.ts: -------------------------------------------------------------------------------- 1 | import { asyncMap } from '../async-map'; 2 | 3 | declare global { 4 | export interface Array { 5 | /** 6 | * #### Async Map 7 | * 8 | * Maps an array asynchronously 9 | * 10 | * * * * 11 | * Example: 12 | * ```typescript 13 | * import "@thalesrc/js-utils/array/proto/async-map"; 14 | * 15 | * const array = [1, 2, 3]; 16 | * 17 | * const mapped = await array.asyncMap(async value => await addOneAfterASecond(value)); // [2, 3, 4] 18 | * 19 | * ``` 20 | * * * * 21 | * @param mapper Callback async function to map the array 22 | * @return A promise contains asynchronusly mapped array result 23 | */ 24 | asyncMap(callbackFn: (value: T, index: number, array: T[]) => Promise): Promise; 25 | } 26 | } 27 | 28 | Array.prototype.asyncMap = async function(this: T[], callbackFn: (value: T, index: number, array: T[]) => Promise): Promise { 29 | return await asyncMap(this, callbackFn); 30 | }; 31 | -------------------------------------------------------------------------------- /src/math/min-max.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import { minMax } from './min-max'; 4 | 5 | describe('MinMax Function', () => { 6 | it('should return min limit when the value below limit', () => { 7 | const result = minMax(10, 20, 5); 8 | expect(result).toBe(10); 9 | }); 10 | 11 | it('should return max limit when the value above limit', () => { 12 | const result = minMax(10, 20, 25); 13 | expect(result).toBe(20); 14 | }); 15 | 16 | it('should return passed value when the value is between limits', () => { 17 | const result = minMax(10, 20, 15); 18 | expect(result).toBe(15); 19 | }); 20 | 21 | it('should return NaN if an argument is NaN', () => { 22 | expect(minMax(NaN, 20, 15)).toEqual(NaN); 23 | expect(minMax(10, NaN, 15)).toEqual(NaN); 24 | expect(minMax(10, 20, NaN)).toEqual(NaN); 25 | expect(minMax(NaN, NaN, 15)).toEqual(NaN); 26 | expect(minMax(10, NaN, NaN)).toEqual(NaN); 27 | expect(minMax(NaN, 15, NaN)).toEqual(NaN); 28 | expect(minMax(NaN, NaN, NaN)).toEqual(NaN); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /src/promise/proto/try-catch.ts: -------------------------------------------------------------------------------- 1 | import { tryCatch } from '../try-catch'; 2 | 3 | declare global { 4 | export interface Promise { 5 | /** 6 | * #### Promise Try Catch 7 | * 8 | * Merges result and error in the same callback 9 | * 10 | * * * * 11 | * Example: 12 | * ```typescript 13 | * import "@thalesrc/js-utils/as-proto/promise-try-catch"; 14 | * 15 | * async function fooFunction() { 16 | * const [error, result] = await anAsyncCall().tryCatch(); 17 | * 18 | * if (error) { 19 | * // handle error 20 | * } 21 | * 22 | * // do stuff 23 | * } 24 | * 25 | * ``` 26 | * * * * 27 | * @param defaultResult Setting this will put the value into the result field when the promise throws error 28 | */ 29 | tryCatch(defaultResult?: T): Promise<[E, T]>; 30 | } 31 | } 32 | 33 | Promise.prototype.tryCatch = function(this: Promise, defaultResult: T = null): Promise<[E, T]> { 34 | return tryCatch(this, defaultResult); 35 | }; 36 | -------------------------------------------------------------------------------- /src/function/proto/debounce.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import './debounce'; 4 | 5 | describe('Debounce Proto Function', () => { 6 | it('should debounce', done => { 7 | let bar = 0; 8 | function foo() { 9 | bar++; 10 | } 11 | 12 | foo.debounce(); 13 | foo.debounce(); 14 | foo.debounce(); 15 | 16 | foo.debounce().then(() => { 17 | expect(bar).toBe(1); 18 | done(); 19 | }); 20 | }); 21 | 22 | it('should work as same', done => { 23 | let bar = {x: 0}; 24 | 25 | function foo(a, b) { 26 | this.x = a + b; 27 | } 28 | 29 | const startedAt = new Date().getTime(); 30 | 31 | foo.debounce(50, bar, 0, 0) 32 | .then(() => { 33 | const sequence = new Date().getTime() - startedAt; 34 | expect(sequence).toBeGreaterThan(48); 35 | expect(sequence).toBeLessThan(60); 36 | 37 | expect(bar.x).toBe(8); 38 | 39 | done(); 40 | }); 41 | 42 | foo.debounce(50, bar, 0, 0); 43 | foo.debounce(50, bar, 0, 0); 44 | foo.debounce(50, bar, 5, 3); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /src/limit.ts: -------------------------------------------------------------------------------- 1 | import { limit as strLimit } from './string'; 2 | import { limit as arrLimit } from './array'; 3 | 4 | export function limit(array: T[], count: number): T[]; 5 | export function limit(str: string, count: number): string; 6 | /** 7 | * #### Limit 8 | * 9 | * Limits the string or array to `n` character 10 | * 11 | * * * * 12 | * Example usage: 13 | * ```typescript 14 | * import { limit } from "@thalesrc/js-utils"; 15 | * 16 | * const str = 'foobarbaz'; 17 | * const array = ["a", "b", "c", "d", "e", "f"]; 18 | * 19 | * limit(str, 3); // 'foo' 20 | * limit(array, 3); // ["a", "b", "c"] 21 | * ``` 22 | * * * * 23 | * @param arrayOrString value to limit 24 | * @param count Max count 25 | */ 26 | export function limit(arrayOrString: T[] | string, count: number): T[] | string { 27 | if (typeof arrayOrString === 'string') { 28 | return strLimit(arrayOrString, count); 29 | } 30 | 31 | if (arrayOrString instanceof Array) { 32 | return arrLimit(arrayOrString, count); 33 | } 34 | 35 | throw new Error('Value is not string nor array'); 36 | } 37 | -------------------------------------------------------------------------------- /src/promise/static/never.ts: -------------------------------------------------------------------------------- 1 | import { never, NEVER } from '../never'; 2 | 3 | declare global { 4 | export interface PromiseConstructor { 5 | /** 6 | * Creates a promise which never resolves 7 | * 8 | * Example: 9 | * ```typescript 10 | * import '@thalesrc/js-utils/promise/static/never'; 11 | * 12 | * function foo(promise) { 13 | * promise = promise || Promise.never(); 14 | * 15 | * promise.then(val => { 16 | * ... 17 | * }); 18 | * } 19 | * ``` 20 | * 21 | * @returns the promise which never resolves 22 | */ 23 | never: typeof never; 24 | 25 | /** 26 | * A promise which never resolves 27 | * 28 | * Example: 29 | * ```typescript 30 | * import '@thalesrc/js-utils/promise/static/never'; 31 | * 32 | * function foo(promise = Promise.NEVER) { 33 | * promise.then(val => { 34 | * ... 35 | * }); 36 | * } 37 | * ``` 38 | */ 39 | NEVER: typeof NEVER; 40 | } 41 | } 42 | 43 | Promise.never = never; 44 | Promise.NEVER = NEVER; 45 | -------------------------------------------------------------------------------- /src/math/static/min-max.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import './min-max'; 4 | 5 | describe('MinMax Static Function', () => { 6 | it('should return min limit when the value below limit', () => { 7 | const result = Math.minMax(10, 20, 5); 8 | expect(result).toBe(10); 9 | }); 10 | 11 | it('should return max limit when the value above limit', () => { 12 | const result = Math.minMax(10, 20, 25); 13 | expect(result).toBe(20); 14 | }); 15 | 16 | it('should return passed value when the value is between limits', () => { 17 | const result = Math.minMax(10, 20, 15); 18 | expect(result).toBe(15); 19 | }); 20 | 21 | it('should return NaN if an argument is NaN', () => { 22 | expect(Math.minMax(NaN, 20, 15)).toEqual(NaN); 23 | expect(Math.minMax(10, NaN, 15)).toEqual(NaN); 24 | expect(Math.minMax(10, 20, NaN)).toEqual(NaN); 25 | expect(Math.minMax(NaN, NaN, 15)).toEqual(NaN); 26 | expect(Math.minMax(10, NaN, NaN)).toEqual(NaN); 27 | expect(Math.minMax(NaN, 15, NaN)).toEqual(NaN); 28 | expect(Math.minMax(NaN, NaN, NaN)).toEqual(NaN); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /src/object/deepest.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * #### Get deepest value in an object chain 3 | * 4 | * * * * * 5 | * Example usage: 6 | * ```typescript 7 | * import { deepest } from "@thalesrc/js-utils/object"; 8 | * 9 | * const a = { 10 | * x: null 11 | * }; 12 | * 13 | * const b = { 14 | * x: a 15 | * }; 16 | * 17 | * const c = { 18 | * x: b 19 | * }; 20 | * 21 | * deepest(c, 'x'); // {x: null} (a) 22 | * 23 | * ``` 24 | * Static usage example: 25 | * ```typescript 26 | * import "@thalesrc/js-utils/object/static/deepest"; 27 | * 28 | * const a = { 29 | * x: null 30 | * }; 31 | * 32 | * const b = { 33 | * x: a 34 | * }; 35 | * 36 | * const c = { 37 | * x: b 38 | * }; 39 | * 40 | * Object.deepest(c, 'x'); // a 41 | * ``` 42 | * * * * 43 | * @param object Object to deep dive 44 | * @param key key of the object which contains same type instance 45 | */ 46 | export function deepest(object: T, key: K): T { 47 | let obj: any = object; 48 | 49 | while (obj[key]) { 50 | obj = obj[key]; 51 | } 52 | 53 | return obj; 54 | } 55 | -------------------------------------------------------------------------------- /src/array/async-map.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * #### Async Map 3 | * 4 | * Maps an array asynchronously 5 | * 6 | * * * * 7 | * Example: 8 | * ```typescript 9 | * import { asyncMap } "@thalesrc/js-utils/array"; 10 | * 11 | * const array = [1, 2, 3]; 12 | * 13 | * asyncMap(array, async value => { 14 | * return await addOneAfterASecond(value); 15 | * }).then(result => { 16 | * console.log(result); // [2, 3, 4] 17 | * }); 18 | * ``` 19 | * 20 | * Example as Array Prototype: 21 | * ```typescript 22 | * import "@thalesrc/js-utils/array/proto/async-map"; 23 | * 24 | * const array = [1, 2, 3]; 25 | * 26 | * const result = await array.asyncMap(async value => await addOneAfterASecond(value)); 27 | * // [2, 3, 4] 28 | * ``` 29 | * * * * 30 | * @param array Array to map 31 | * @param mapper Callback async function to map the array 32 | * @return A promise contains asynchronusly mapped array result 33 | */ 34 | export async function asyncMap(array: T[], mapper: (value: T, index: number, array: T[]) => Promise): Promise { 35 | const promises = array.map(mapper); 36 | 37 | return await Promise.all(promises); 38 | } 39 | -------------------------------------------------------------------------------- /src/set/proto/difference.ts: -------------------------------------------------------------------------------- 1 | import { difference, TSubstraction } from '../../array/difference'; 2 | 3 | declare global { 4 | export interface Set { 5 | /** 6 | * #### Difference 7 | * 8 | * Gets the difference of the sets 9 | * 10 | * * * * 11 | * Example: 12 | * ```typescript 13 | * import "@thalesrc/js-utils/set/proto/difference"; 14 | * 15 | * const base = new Set(["a", "b", "c", "d"]); 16 | * 17 | * base.difference(["a", "b"]); // Set(["c", "d"]) 18 | * ``` 19 | * * * * 20 | * @param base Base Set 21 | * @param substraction Set or Array to remove its values from the base 22 | * @param allDiff By default all the same items encountered in substraction will be removed, set this argument as true to get real difference 23 | * @returns Difference of base from substraction 24 | */ 25 | difference(substraction: TSubstraction, allDiff?: boolean): Set; 26 | } 27 | } 28 | 29 | Set.prototype.difference = function(this: Set, substraction: TSubstraction, allDiff = false) { 30 | return difference(this, substraction, allDiff); 31 | }; 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Thales.Rocks 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/promise/revert.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * #### Revert Promise 3 | * Exchanges resolve state with rejection of a promise 4 | * 5 | * * * * 6 | * Example usage: 7 | * ```typescript 8 | * import { revert } from "@thalesrc/js-utils/promise"; 9 | * 10 | * const errorPromise = Promise.reject(new Error('foo')); 11 | * 12 | * revert(errorPromise) 13 | * .then(err => { 14 | * console.log("this will be logged", err); 15 | * }) 16 | * .catch(res => { 17 | * console.log("this won't be logged", res); 18 | * }); 19 | * ``` 20 | * 21 | * Proto usage example: 22 | * ```typescript 23 | * import "@thalesrc/js-utils/promise/proto/revert"; 24 | * 25 | * Promise.reject(new Error('foo')) 26 | * .revert() 27 | * .then(err => { 28 | * console.log("this will be logged", err); 29 | * }) 30 | * .catch(res => { 31 | * console.log("this won't be logged", res); 32 | * }); 33 | * ``` 34 | * * * * 35 | * @param promise The promise to revert its statements 36 | * @return the reverted promise 37 | */ 38 | export function revert(promise: Promise): Promise { 39 | return promise.then(res => {throw res; }, err => err); 40 | } 41 | -------------------------------------------------------------------------------- /src/set/proto/intersection.ts: -------------------------------------------------------------------------------- 1 | import { intersection, TInclusion } from '../../array/intersection'; 2 | 3 | declare global { 4 | export interface Set { 5 | /** 6 | * #### Intersection 7 | * 8 | * Gets the intersection of the two arrays or sets 9 | * 10 | * * * * 11 | * Example: 12 | * ```typescript 13 | * import "@thalesrc/js-utils/set/proto/intersection"; 14 | * 15 | * const base = new Set(["a", "b", "c", "d"]); 16 | * 17 | * base.intersection(["a", "b"]); // Set(["a", "b"]) 18 | * ``` 19 | * * * * 20 | * @param base Base Set 21 | * @param inclusion Set or Array to include its values 22 | * @param allEquals By default all the same items encountered in the inclusion will be included, set this argument as false to get real intersection 23 | * @returns Intersection of base and inclusion 24 | */ 25 | intersection(inclusion: TInclusion, allEquals?: boolean): Set; 26 | } 27 | } 28 | 29 | Set.prototype.intersection = function(this: Set, inclusion: TInclusion, allEquals = true) { 30 | return intersection(this, inclusion, allEquals); 31 | }; 32 | -------------------------------------------------------------------------------- /src/array/proto/intersection.ts: -------------------------------------------------------------------------------- 1 | import { intersection, TInclusion } from '../intersection'; 2 | 3 | declare global { 4 | export interface Array { 5 | /** 6 | * #### Intersection 7 | * 8 | * Gets the intersection of the two arrays or sets 9 | * 10 | * * * * 11 | * Example: 12 | * ```typescript 13 | * import "@thalesrc/js-utils/array/proto/intersection"; 14 | * 15 | * const base = ["a", "b", "c", "d", "a", "b", "c", "d"]; 16 | * 17 | * base.intersection(["a", "b"]); // ["a", "b", "a", "b"] 18 | * base.intersection(["a", "b"], false); // ["a", "b"] 19 | * ``` 20 | * * * * 21 | * @param base Base Set or Array 22 | * @param inclusion Set or Array to include its values 23 | * @param allEquals By default all the same items encountered in the inclusion will be included, set this argument as false to get real intersection 24 | * @returns Intersection of base and inclusion 25 | */ 26 | intersection(inclusion: TInclusion, allEquals?: boolean): T[]; 27 | } 28 | } 29 | 30 | Array.prototype.intersection = function(inclusion: TInclusion, allEquals = true) { 31 | return intersection(this, inclusion, allEquals); 32 | }; 33 | -------------------------------------------------------------------------------- /src/array/uniquify-by-key.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * #### Uniquify By Key 3 | * 4 | * Removes objects from the array which the value of its specifed key included before by another 5 | * 6 | * * * * 7 | * Example: 8 | * ```typescript 9 | * import { uniquifyByKey } "@thalesrc/js-utils/array"; 10 | * 11 | * const array = [{a: 1}, {a: 1}, {a: 2}, {a: 3}, {a: 3}, {a: 4}]; 12 | * 13 | * uniquifyByKey(array, 'a'); // [{a: 1}, {a: 2}, {a: 3}, {a: 4}] 14 | * ``` 15 | * 16 | * Prototype Example: 17 | * ```typescript 18 | * import "@thalesrc/js-utils/array/proto/uniquify-by-key"; 19 | * 20 | * const array = [{a: 1}, {a: 1}, {a: 2}, {a: 3}, {a: 3}, {a: 4}]; 21 | * 22 | * array.uniquifyByKey('a'); // [{a: 1}, {a: 2}, {a: 3}, {a: 4}] 23 | * ``` 24 | * * * * 25 | * @param array Collection of the objects 26 | * @param key Key to search the value on 27 | * @return The new uniquified array 28 | */ 29 | export function uniquifyByKey(array: T[], key: P): T[] { 30 | const cache = new Set(); 31 | 32 | return array.filter(item => { 33 | if (cache.has(item[key])) { 34 | return false; 35 | } 36 | 37 | cache.add(item[key]); 38 | 39 | return true; 40 | }); 41 | } 42 | -------------------------------------------------------------------------------- /src/array/proto/difference.ts: -------------------------------------------------------------------------------- 1 | import { difference, TSubstraction } from '../difference'; 2 | 3 | declare global { 4 | export interface Array { 5 | /** 6 | * #### Difference 7 | * 8 | * Gets the difference of the arrays 9 | * 10 | * * * * 11 | * Example: 12 | * ```typescript 13 | * import "@thalesrc/js-utils/array/proto/array-difference"; 14 | * 15 | * const base = ["a", "b", "c", "d", "a", "b", "c", "d"]; 16 | * 17 | * base.difference(["a", "b"]); // ["c", "d", "c", "d"] 18 | * base.difference(["a", "b"], true); // ["c", "d", "a", "b", "c", "d"] 19 | * ``` 20 | * * * * 21 | * @param base Base Array 22 | * @param substraction Set or Array to remove its values from the base 23 | * @param allDiff By default all the same items encountered in substraction will be removed, set this argument as true to get real difference 24 | * @returns Difference of base from substraction 25 | */ 26 | difference(substraction: TSubstraction, allDiff?: boolean): T[]; 27 | } 28 | } 29 | 30 | Array.prototype.difference = function(substraction: TSubstraction, allDiff = false) { 31 | return difference(this, substraction, allDiff); 32 | }; 33 | -------------------------------------------------------------------------------- /src/function/defer.ts: -------------------------------------------------------------------------------- 1 | import { noop } from './noop'; 2 | 3 | /** 4 | * #### Delays the execution of the passed function to increase the render performance 5 | * 6 | * * * * 7 | * Example usage: 8 | * ```typescript 9 | * import { defer } from "@thalesrc/js-utils/function"; 10 | * 11 | * defer(() => aFunctionToDefer()) 12 | * .then(res => ...) 13 | * .catch(err => ...); 14 | * ``` 15 | * Example as promise static method 16 | * ```typescript 17 | * import "@thalesrc/js-utils/function/static/defer"; 18 | * 19 | * Function.defer(() => aFunctionToDefer()) 20 | * .then(res => ...) 21 | * .catch(err => ...); 22 | * ``` 23 | * * * * 24 | * @param callback Callback function to be executed 25 | * @typeparam T type of the return value of the callback 26 | * @returns A promise which resolves with the value of the callback right after the execution 27 | */ 28 | export function defer(callback: () => T = noop as () => T): Promise { 29 | return new Promise((resolve, reject) => { 30 | setTimeout(() => { 31 | try { 32 | const returnValue = callback(); 33 | resolve(returnValue); 34 | } catch (error) { 35 | reject(error); 36 | } 37 | }, 0); 38 | }); 39 | } 40 | -------------------------------------------------------------------------------- /src/compact.ts: -------------------------------------------------------------------------------- 1 | import { compact as objCompact } from './object'; 2 | import { compact as arrCompact } from './array'; 3 | 4 | export function compact(array: T[]): T[]; 5 | export function compact(object: T): Partial; 6 | /** 7 | * #### Compact 8 | * 9 | * Filters falsy values of the given array 10 | * Removes `null` and `undefined` values and their keys from an object 11 | * 12 | * * * * 13 | * Example usage: 14 | * ```typescript 15 | * import { compact } from "@thalesrc/js-utils"; 16 | * 17 | * const arr = [undefined, "", false, 0, 1, "1"]; 18 | * const compacted = compact(arr); // [1, "1"]; 19 | * 20 | * const object = { 21 | * x: null, 22 | * y: undefined, 23 | * z: 20 24 | * }; 25 | * 26 | * const compacted = compact(object); // {z: 20} 27 | * ``` 28 | * * * * 29 | * @param arrayOrObject Array or Object to compact 30 | */ 31 | export function compact(arrayOrObject: T): T extends U[] ? U[] : Object { 32 | if (arrayOrObject instanceof Array) { 33 | return arrCompact(arrayOrObject) as any; 34 | } 35 | 36 | if (arrayOrObject instanceof Object) { 37 | return objCompact(arrayOrObject) as any; 38 | } 39 | 40 | throw new TypeError('Value is not object nor array'); 41 | } 42 | 43 | -------------------------------------------------------------------------------- /src/array/remove.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * #### Remove 3 | * 4 | * Removes an item from the array 5 | * 6 | * Removes all item references if multi is set to `true` 7 | * 8 | * * * * 9 | * Example: 10 | * ```typescript 11 | * import { remove } from "@thalesrc/js-utils/array"; 12 | * 13 | * const array = ["a", "b", "c", "a", "b", "c"]; 14 | * 15 | * remove(array, "b"); // ["a", "c", "a", "b", "c"] 16 | * remove(array, "b", true); // ["a", "c", "a", "c"] 17 | * ``` 18 | * 19 | * Prototype Example: 20 | * ```typescript 21 | * import "@thalesrc/js-utils/array/proto/remove"; 22 | * 23 | * const array = ["a", "b", "c", "a", "b", "c"]; 24 | * 25 | * array.remove("b"); // ["a", "c", "a", "b", "c"] 26 | * array.remove("b", true); // ["a", "c", "a", "c"] 27 | * ``` 28 | * * * * 29 | * @param array: Array to remove the item 30 | * @param itemToRemove Item to remove 31 | * @return New array 32 | */ 33 | export function remove(array: T[], item: T, multi = false): T[] { 34 | let index: number = array.indexOf(item); 35 | 36 | if (index < 0) { 37 | return [...array]; 38 | } 39 | 40 | do { 41 | array = [...array.slice(0, index), ...array.slice(index + 1)]; 42 | index = array.indexOf(item); 43 | } while (multi && index > -1); 44 | 45 | return array; 46 | } 47 | -------------------------------------------------------------------------------- /src/function/proto/debounce.ts: -------------------------------------------------------------------------------- 1 | import { debounce, DEFAULT_DEBOUNCE_TIME } from '../debounce'; 2 | 3 | declare global { 4 | export interface Function { 5 | /** 6 | * #### Debounces a function that delays invoking until after given time have elapsed since the last time the debounced function was invoked 7 | * 8 | * * * * 9 | * Example usage: 10 | * ```typescript 11 | * import "@thalesrc/js-utils/function/proto/debounce"; 12 | * 13 | * function foo() { 14 | * console.log("hello"); 15 | * } 16 | * 17 | * for (let i = 0; i < 5; i++) { 18 | * foo.debounce(); 19 | * } 20 | * 21 | * // logs "hello" only once 22 | * ``` 23 | * * * * 24 | * @param [time = 180] Time for debouncing 25 | * @param [thisObject = null] This object to execute the callback function with 26 | * @param args Function arguments 27 | * @return A promise which resolves right after the debouncing sequence has been finished 28 | */ 29 | debounce(time?: number, thisObject?: any, ...args: any[]): Promise; 30 | } 31 | } 32 | 33 | Function.prototype.debounce = function(time = DEFAULT_DEBOUNCE_TIME, thisObject: any = null, ...args: any[]): Promise { 34 | return debounce(this, time, thisObject, ...args); 35 | }; 36 | -------------------------------------------------------------------------------- /src/promise/try-catch.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * #### Try Catch 3 | * 4 | * Merges result and error in the same callback 5 | * 6 | * * * * 7 | * Example: 8 | * ```typescript 9 | * import { tryCatch } from "@thalesrc/js-utils/promise"; 10 | * 11 | * async function fooFunction() { 12 | * const promise = anAsyncCall(); 13 | * const [error, result] = await tryCatch(promise); 14 | * 15 | * if (error) { 16 | * // handle error 17 | * } 18 | * 19 | * // do stuff 20 | * } 21 | * 22 | * ``` 23 | * 24 | * Prototype Example: 25 | * ```typescript 26 | * import "@thalesrc/js-utils/promise/proto/try-catch"; 27 | * 28 | * async function fooFunction() { 29 | * const [error, result] = await anAsyncCall().tryCatch(); 30 | * 31 | * if (error) { 32 | * // handle error 33 | * } 34 | * 35 | * // do stuff 36 | * } 37 | * 38 | * ``` 39 | * * * * 40 | * @param promise Promise to try 41 | * @param defaultResult Setting this will put the value into the result field when the promise throws error 42 | * @returns Error and result array 43 | */ 44 | export async function tryCatch(promise: Promise, defaultResult: T = null): Promise<[E, T]> { 45 | try { 46 | const result = await promise; 47 | return [null, result]; 48 | } catch (error) { 49 | return [error, defaultResult]; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/unique-id.ts: -------------------------------------------------------------------------------- 1 | const CACHE: {[key: string]: Iterator} = {}; 2 | 3 | /** 4 | * Unique Id Generator 5 | * @param {string} [prefix] 6 | */ 7 | function* uniqueIdGenerator(prefix?: string) { 8 | let counter = 0; 9 | 10 | while (true) { 11 | yield prefix ? prefix + '-' + counter++ : counter++; 12 | } 13 | } 14 | 15 | /** 16 | * #### Generates a unique id 17 | * 18 | * Starts a new counter for every unique prefix and if a prefix is given, returns the id by prefixing it, otherwise returns the id as number 19 | * * * * 20 | * Example usage: 21 | * ```typescript 22 | * import { uniqueId } from "@thalesrc/js-utils"; 23 | * 24 | * uniqueId(); // 0 25 | * uniqueId(); // 1 26 | * uniqueId("some-str"); // "some-str-0"; 27 | * uniqueId("some-str"); // "some-str-1"; 28 | * uniqueId(); // 3 29 | * ``` 30 | * * * * 31 | * @param prefix prefix to prepend into the unique id 32 | * @returns A unique id 33 | */ 34 | export function uniqueId(): number; 35 | export function uniqueId(prefix: string): string; 36 | export function uniqueId(prefix?: string): string | number { 37 | if (!prefix || typeof prefix !== 'string') { 38 | prefix = undefined; 39 | } 40 | 41 | if (!(prefix in CACHE)) { 42 | CACHE[prefix] = uniqueIdGenerator(prefix); 43 | } 44 | 45 | return CACHE[prefix].next().value; 46 | } 47 | -------------------------------------------------------------------------------- /src/object/compact.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * #### Removes `null` and `undefined` values and their keys from an object 3 | * 4 | * Additional values can be removed by passing to an array as the second argument 5 | * 6 | * * * * * 7 | * Example usage: 8 | * ```typescript 9 | * import { compact } from "@thalesrc/js-utils/object"; 10 | * 11 | * const a = { 12 | * x: null, 13 | * y: undefined, 14 | * z: 20 15 | * }; 16 | * 17 | * compact(a); // {z: 20} 18 | * 19 | * ``` 20 | * Static usage example: 21 | * ```typescript 22 | * import "@thalesrc/js-utils/object/static/compact"; 23 | * 24 | * const a = { 25 | * x: null, 26 | * y: undefined, 27 | * z: 20 28 | * }; 29 | * 30 | * Object.compact(a); // {z: 20} 31 | * ``` 32 | * * * * 33 | * @param object Object delete empty keys 34 | * @param additionalValuesToRemove Other values to delete 35 | * @returns Compacted object 36 | */ 37 | export function compact>( 38 | object: T, 39 | additionalValuesToRemove: any[] = [] 40 | ): Partial { 41 | const newObject = {...object}; 42 | const valuesToRemove = [undefined, null, ...additionalValuesToRemove]; 43 | 44 | for (const [key, value] of Object.entries(newObject)) { 45 | if (valuesToRemove.includes(value)) { 46 | delete newObject[key]; 47 | } 48 | } 49 | 50 | return newObject; 51 | } 52 | -------------------------------------------------------------------------------- /src/function/of.ts: -------------------------------------------------------------------------------- 1 | import { SmartMap } from '../smart-map'; 2 | 3 | /** 4 | * Cache object to store previously created functions 5 | */ 6 | const CACHE = new SmartMap any>(); 7 | 8 | /** 9 | * ### Function Of 10 | * 11 | * Creates a function which returns the value given 12 | * 13 | * * * * 14 | * Example: 15 | * ```typescript 16 | * import { of } from "@thalesrc/js-utils/function"; 17 | * 18 | * const base = [1, 2, 5, {}, "x", "y"]; 19 | * const mapTo = of("thales rocks"); 20 | * 21 | * const mapped = base.map(mapTo); 22 | * // ["thales rocks", "thales rocks", "thales rocks", "thales rocks", "thales rocks", "thales rocks"] 23 | * ``` 24 | * Function Static Example: 25 | * ```typescript 26 | * import "@thalesrc/js-utils/function/static/of"; 27 | * 28 | * const base = [1, 2, 5, {}, "x", "y"]; 29 | * const mapTo = Function.of("thales rocks"); 30 | * 31 | * const mapped = base.map(mapTo); 32 | * // ["thales rocks", "thales rocks", "thales rocks", "thales rocks", "thales rocks", "thales rocks"] 33 | * ``` 34 | * * * * 35 | * @param returnValue The value which created function returns 36 | * @returns A function which returns the `returnValue` 37 | */ 38 | export function of(returnValue: T): (...args: any[]) => T { 39 | if (!CACHE.has(returnValue)) { 40 | CACHE.set(returnValue, function() {return returnValue; }); 41 | } 42 | 43 | return CACHE.get(returnValue); 44 | } 45 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "max-line-length": [true, 200], 4 | "no-inferrable-types": true, 5 | "class-name": true, 6 | "comment-format": [ 7 | true, 8 | "check-space" 9 | ], 10 | "indent": [ 11 | true, 12 | "spaces", 13 | 2 14 | ], 15 | "eofline": true, 16 | "no-duplicate-variable": true, 17 | "no-eval": true, 18 | "no-arg": true, 19 | "no-internal-module": true, 20 | "no-trailing-whitespace": true, 21 | "no-bitwise": false, 22 | "no-unused-expression": true, 23 | "no-var-keyword": true, 24 | "no-unused-vars": ["error", { "vars": "all", "args": "after-used", "ignoreRestSiblings": false }], 25 | "one-line": [ 26 | true, 27 | "check-catch", 28 | "check-else", 29 | "check-open-brace", 30 | "check-whitespace" 31 | ], 32 | "quotemark": [ 33 | true, 34 | "single", 35 | "avoid-escape" 36 | ], 37 | "semicolon": [true, "always"], 38 | "typedef-whitespace": [ 39 | true, 40 | { 41 | "call-signature": "nospace", 42 | "index-signature": "nospace", 43 | "parameter": "nospace", 44 | "property-declaration": "nospace", 45 | "variable-declaration": "nospace" 46 | } 47 | ], 48 | "curly": true, 49 | "variable-name": [ 50 | true, 51 | "ban-keywords", 52 | "check-format", 53 | "allow-leading-underscore", 54 | "allow-pascal-case" 55 | ], 56 | "whitespace": [ 57 | true, 58 | "check-branch", 59 | "check-decl", 60 | "check-operator", 61 | "check-separator", 62 | "check-type" 63 | ] 64 | } 65 | } -------------------------------------------------------------------------------- /src/object/static/clone.ts: -------------------------------------------------------------------------------- 1 | import { clone } from '../clone'; 2 | 3 | declare global { 4 | export interface ObjectConstructor { 5 | /** 6 | * #### Deep Clone 7 | * 8 | * A function to recursively clone objects, arrays etc. with [cloning options]{@link ICloneOptions} 9 | * 10 | * *References Functions & Symbols by default* 11 | * 12 | * * * * 13 | * Example: 14 | * ```typescript 15 | * import { clone } from "@thalesrc/js-utils"; 16 | * 17 | * const object = {a: 1, b: {c: true, d: ["x", "y"]}}; 18 | * 19 | * // Clone all 20 | * const clonedObject = clone(object); 21 | * // {a: 1, b: {c: true, d: ["x", "y"]}} 22 | * // object.b.d === clonedObject.b.d // false 23 | * 24 | * // Clone all but reference "d" 25 | * const clonedObject = clone(object, {propsToRefer: ["d"]}); 26 | * // {a: 1, b: {c: true, d: ["x", "y"]}} 27 | * // object.b.d === clonedObject.b.d // true 28 | * ``` 29 | * 30 | * Static usage example: 31 | * ```typescript 32 | * import "@thalesrc/js-utils/dist/as-static/clone"; 33 | * 34 | * const object = {a: 1, b: 2}; 35 | * const clonedObject = Object.clone(object); // {a: 1, b: 2} 36 | * ``` 37 | * * * * 38 | * @param objectToClone Object to clone 39 | * @param options {ICloneOptions} Cloning Options 40 | * @see {@link ICloneOptions} for more information. 41 | */ 42 | clone: typeof clone; 43 | } 44 | } 45 | 46 | Object.clone = clone; 47 | -------------------------------------------------------------------------------- /src/array/replace.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import { replace } from './replace'; 4 | 5 | describe('Replace Function', () => { 6 | let foo: number[]; 7 | 8 | beforeEach(() => { 9 | foo = [1, 2, 3, 1, 2, 3]; 10 | }); 11 | 12 | it('should replace one item successfully', () => { 13 | expect(replace(foo, 2, 100)).toEqual([1, 100, 3, 1, 2, 3]); 14 | }); 15 | 16 | it('shouldn\'t replace nonfound item', () => { 17 | expect(replace(foo, 4, 100)).toEqual([1, 2, 3, 1, 2, 3]); 18 | }); 19 | 20 | it('should replace items from index 0 when startingIndex is not defined', () => { 21 | expect(replace(foo, {itemsToReplace: [100]})).toEqual([100, 2, 3, 1, 2, 3]); 22 | }); 23 | 24 | it('should add items when itemsToReplace array contains multiple elements', () => { 25 | expect(replace(foo, {itemsToReplace: [100, 101, 102]})).toEqual([100, 101, 102, 2, 3, 1, 2, 3]); 26 | }); 27 | 28 | it('should delete items as defined', () => { 29 | expect(replace(foo, {itemsToReplace: [100], deleteCount: 2})).toEqual([100, 3, 1, 2, 3]); 30 | }); 31 | 32 | it('should start replacing from startingIndex', () => { 33 | expect(replace(foo, { startingIndex: 2, itemsToReplace: [100]})).toEqual([1, 2, 100, 1, 2, 3]); 34 | }); 35 | 36 | it('should replace by using mode: `replace by map`', () => { 37 | expect(replace(foo, {itemsToReplace: new Map([[2, 100]])})).toEqual([1, 100, 3, 1, 2, 3]); 38 | }); 39 | 40 | it('should replace multiple when multi set as true', () => { 41 | expect(replace(foo, {itemsToReplace: new Map([[2, 100]]), multi: true})).toEqual([1, 100, 3, 1, 100, 3]); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /src/function/defer.spec.ts: -------------------------------------------------------------------------------- 1 | 2 | import { fail } from 'assert'; 3 | import 'jest'; 4 | 5 | import { defer } from './defer'; 6 | 7 | class CustomError { 8 | constructor(public message: string) {} 9 | } 10 | 11 | describe('Defer Function', () => { 12 | it('should defer execution', done => { 13 | let counter = 0; 14 | 15 | defer(() => { 16 | expect(counter).toBe(1000000); 17 | done(); 18 | }); 19 | 20 | expect(counter).toBe(0); 21 | 22 | for (let i = 0; i < 1000000; i++) { 23 | counter++; 24 | } 25 | }); 26 | 27 | it('should return a deferred void promise if callback is not defined', done => { 28 | let counter = 0; 29 | 30 | defer() 31 | .then(() => { 32 | expect(counter).toBe(1); 33 | done(); 34 | }); 35 | counter++; 36 | }); 37 | 38 | it('should return a promise which resolves after callback execution', done => { 39 | let counter = 0; 40 | defer(() => counter++) 41 | .then(() => { 42 | expect(counter).toBe(1); 43 | done(); 44 | }); 45 | expect(counter).toBe(0); 46 | }); 47 | 48 | it('should return a promise which resolves the callback return value', done => { 49 | defer(() => 'test') 50 | .then(value => { 51 | expect(value).toBe('test'); 52 | done(); 53 | }); 54 | }); 55 | 56 | it('should catch callback errors', done => { 57 | defer(() => { 58 | throw new CustomError('foo'); 59 | }) 60 | .then(value => { 61 | throw new CustomError("couldn't catch error"); 62 | }) 63 | .catch((err: CustomError) => { 64 | expect(err.message).toBe('foo'); 65 | done(); 66 | }); 67 | }); 68 | }); 69 | -------------------------------------------------------------------------------- /src/function/static/defer.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import './defer'; 4 | 5 | class CustomError { 6 | constructor(public message: string) {} 7 | } 8 | 9 | describe('Defer Static Function', () => { 10 | it('should defer execution', done => { 11 | let counter = 0; 12 | 13 | Function.defer(() => { 14 | expect(counter).toBe(1000000); 15 | done(); 16 | }); 17 | 18 | expect(counter).toBe(0); 19 | 20 | for (let i = 0; i < 1000000; i++) { 21 | counter++; 22 | } 23 | }); 24 | 25 | it('should return a deferred void promise if callback is not defined', done => { 26 | let counter = 0; 27 | 28 | Function.defer() 29 | .then(() => { 30 | expect(counter).toBe(1); 31 | done(); 32 | }); 33 | counter++; 34 | }); 35 | 36 | it('should return a promise which resolves after callback execution', done => { 37 | let counter = 0; 38 | Function.defer(() => counter++) 39 | .then(() => { 40 | expect(counter).toBe(1); 41 | done(); 42 | }); 43 | expect(counter).toBe(0); 44 | }); 45 | 46 | it('should return a promise which resolves the callback return value', done => { 47 | Function.defer(() => 'test') 48 | .then(value => { 49 | expect(value).toBe('test'); 50 | done(); 51 | }); 52 | }); 53 | 54 | it('should catch callback errors', done => { 55 | Function.defer(() => { 56 | throw new CustomError('foo'); 57 | }) 58 | .then(value => { 59 | throw new CustomError("couldn't catch error"); 60 | }) 61 | .catch((err: CustomError) => { 62 | expect(err.message).toBe('foo'); 63 | done(); 64 | }); 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /src/promise/try-one-by-one.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * #### Try One By One 3 | * 4 | * Tries a set of promises one by one with given order. Breaks the call when a promise resolved. Otherwise keeps trying incoming promises until the list is finished. 5 | * 6 | * * * * 7 | * Example: 8 | * ```typescript 9 | * import { tryOneByOne } from "@thalesrc/js-utils/promise"; 10 | * 11 | * async function fooFunction() { 12 | * const foo = await tryOneByOne([ 13 | * () => someCall(), 14 | * (err) => anotherCall(), 15 | * (err) => fooPromise() 16 | * ]); 17 | * 18 | * // do stuff 19 | * } 20 | * 21 | * ``` 22 | * 23 | * Static Example: 24 | * ```typescript 25 | * import "@thalesrc/js-utils/promise/static/try-one-by-one"; 26 | * 27 | * async function fooFunction() { 28 | * const foo = await Promise.tryOneByOne([ 29 | * () => someCall(), 30 | * (err) => anotherCall(), 31 | * (err) => fooPromise() 32 | * ]); 33 | * 34 | * // do stuff 35 | * } 36 | * 37 | * ``` 38 | * * * * 39 | * @param promises List of promises to try one by one with the order 40 | * @returns 41 | */ 42 | export async function tryOneByOne(promises: Array | ((lastError: unknown) => Promise)>): Promise { 43 | return await tryPromises([...promises], null); 44 | } 45 | 46 | async function tryPromises(promises: Array | ((lastError: unknown) => Promise)>, lastError: unknown): Promise { 47 | const promiseFn = promises.shift(); 48 | 49 | if (!promiseFn) throw lastError; 50 | 51 | try { 52 | const promise = typeof promiseFn === "function" ? promiseFn(lastError) : promiseFn; 53 | 54 | return await promise; 55 | } catch (err) { 56 | return tryPromises(promises, err); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/array/difference.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import { difference } from './difference'; 4 | 5 | describe('Difference Function', () => { 6 | it('should return difference of array base and array substractor', () => { 7 | const foo = [1, 2, 3, 1, 2, 3]; 8 | const bar = [1, 2]; 9 | 10 | expect(difference(foo, bar)).toEqual([3, 3]); 11 | }); 12 | 13 | it('should return difference of set base and array substractor', () => { 14 | const foo = new Set([1, 2, 3, 4, 5]); 15 | const bar = [1, 2]; 16 | 17 | expect(difference(foo, bar)).toEqual(new Set([3, 4, 5])); 18 | }); 19 | 20 | it('should return difference of array base and set substractor', () => { 21 | const foo = [1, 2, 3, 1, 2, 3]; 22 | const bar = new Set([1, 2]); 23 | 24 | expect(difference(foo, bar)).toEqual([3, 3]); 25 | }); 26 | 27 | it('should return difference of set base and set substractor', () => { 28 | const foo = new Set([1, 2, 3, 4, 5]); 29 | const bar = new Set([1, 2]); 30 | 31 | expect(difference(foo, bar)).toEqual(new Set([3, 4, 5])); 32 | }); 33 | 34 | it('should return all difference without removing same values more than once', () => { 35 | const foo = [1, 2, 3, 1, 2, 3]; 36 | const bar = [1, 2]; 37 | const baz = [2, 3, 1]; 38 | 39 | expect(difference(foo, bar, true)).toEqual([3, 1, 2, 3]); 40 | expect(difference(foo, baz, true)).toEqual([1, 2, 3]); 41 | }); 42 | 43 | it('should work properly when substraction array has values which base hasn\'t', () => { 44 | const foo = [1, 2, 3, 1, 2, 3]; 45 | const bar = [1, 2, 'x', 'y']; 46 | 47 | expect(difference(foo, bar)).toEqual([3, 3]); 48 | expect(difference(foo, bar, true)).toEqual([3, 1, 2, 3]); 49 | }); 50 | }); 51 | -------------------------------------------------------------------------------- /src/array/intersection.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import { intersection } from './intersection'; 4 | 5 | describe('Intersection Function', () => { 6 | it('should return intersection of array base and array inclusion', () => { 7 | const foo = [1, 2, 3, 1, 2, 3]; 8 | const bar = [1, 2]; 9 | 10 | expect(intersection(foo, bar)).toEqual([1, 2, 1, 2]); 11 | }); 12 | 13 | it('should return intersection of set base and array inclusion', () => { 14 | const foo = new Set([1, 2, 3, 4, 5]); 15 | const bar = [1, 2]; 16 | 17 | expect(intersection(foo, bar)).toEqual(new Set([1, 2])); 18 | }); 19 | 20 | it('should return intersection of array base and set inclusion', () => { 21 | const foo = [1, 2, 3, 1, 2, 3]; 22 | const bar = new Set([1, 2]); 23 | 24 | expect(intersection(foo, bar)).toEqual([1, 2, 1, 2]); 25 | }); 26 | 27 | it('should return intersection of set base and set inclusion', () => { 28 | const foo = new Set([1, 2, 3, 4, 5]); 29 | const bar = new Set([1, 2]); 30 | 31 | expect(intersection(foo, bar)).toEqual(new Set([1, 2])); 32 | }); 33 | 34 | it('should return real intesection without adding same values more than once', () => { 35 | const foo = [1, 2, 3, 1, 2, 3]; 36 | const bar = [1, 2]; 37 | const baz = [2, 3, 2]; 38 | 39 | expect(intersection(foo, bar, false)).toEqual([1, 2]); 40 | expect(intersection(foo, baz, false)).toEqual([2, 3, 2]); 41 | }); 42 | 43 | it('should work properly when inclusion array has values which base hasn\'t', () => { 44 | const foo = [1, 2, 3, 1, 2, 3]; 45 | const bar = [1, 2, 'x', 'y']; 46 | 47 | expect(intersection(foo, bar)).toEqual([1, 2, 1, 2]); 48 | expect(intersection(foo, bar, false)).toEqual([1, 2]); 49 | }); 50 | }); 51 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@thalesrc/js-utils", 3 | "version": "0.0.0", 4 | "description": "Javascript utility functions for web development", 5 | "main": "index.js", 6 | "typings": "index.d.ts", 7 | "homepage": "https://thalesrc.github.io/js-utils", 8 | "keywords": [ 9 | "object", 10 | "deep clone", 11 | "array", 12 | "async", 13 | "map", 14 | "find by key", 15 | "try catch", 16 | "compact", 17 | "difference", 18 | "intersecion", 19 | "promise", 20 | "defer", 21 | "open", 22 | "is truthy", 23 | "is falsy", 24 | "math", 25 | "min max", 26 | "noop", 27 | "unique id", 28 | "replace", 29 | "remove", 30 | "deepest", 31 | "never", 32 | "arrayize" 33 | ], 34 | "repository": { 35 | "type": "git", 36 | "url": "https://github.com/thalesrc/js-utils.git" 37 | }, 38 | "scripts": { 39 | "clean:docs": "rimraf docs", 40 | "clean:dist": "rimraf dist", 41 | "document": "typedoc --tsconfig config/tsconfig.doc.json", 42 | "test": "jest", 43 | "test:coverage": "jest --collectCoverage", 44 | "prepare:github-pages": "npm run clean:docs && npm run document && echo \"\" > ./docs/.nojekyll", 45 | "prepare:package": "tsc -p ./config/tsconfig.lib.json", 46 | "version-update": "npm version $PACKAGE_VERSION --no-git-tag-version", 47 | "change-ts-version": "rjp ./package.json typescript ~3.4.5" 48 | }, 49 | "author": "alisahinozcelik@gmail.com", 50 | "license": "MIT", 51 | "publishConfig": { 52 | "access": "public" 53 | }, 54 | "devDependencies": { 55 | "@types/jest": "^24.0.24", 56 | "@types/node": "^12.12.21", 57 | "jest": "^24.9.0", 58 | "replace-json-property": "^1.4.3", 59 | "rimraf": "^3.0.0", 60 | "ts-jest": "^24.2.0", 61 | "typedoc": "^0.15.5", 62 | "typescript": "^3.6.2" 63 | }, 64 | "dependencies": {} 65 | } 66 | -------------------------------------------------------------------------------- /src/array/intersection.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Intersection Inclusion Type 3 | */ 4 | export type TInclusion = any[] | Set; 5 | 6 | /** 7 | * Intersection 8 | * 9 | * Gets the intersection of the two arrays or sets 10 | * 11 | * * * * 12 | * _Example:_ 13 | * ```typescript 14 | * import { intersection } from "@thalesrc/js-utils/array"; 15 | * 16 | * const base = ["a", "b", "c", "d", "a", "b", "c", "d"]; 17 | * 18 | * intersection(base, ["a", "b", "x"]); // ["a", "b", "a", "b"] 19 | * intersection(base, ["a", "b", "x"], false); // ["a", "b"] 20 | * ``` 21 | * 22 | * _Array Prototype Example:_ 23 | * ```typescript 24 | * import "@thalesrc/js-utils/array/proto/intersection"; 25 | * 26 | * const base = ["a", "b", "c", "d", "a", "b", "c", "d"]; 27 | * 28 | * base.intersection(["a", "b"]); // ["a", "b", "a", "b"] 29 | * ``` 30 | * 31 | * _Set Prototype Example:_ 32 | * ```typescript 33 | * import "@thalesrc/js-utils/set/proto/intersection"; 34 | * 35 | * const base = new Set(["a", "b", "c", "d"]); 36 | * 37 | * base.intersection(["a", "b"]); // Set(["a", "b"]) 38 | * ``` 39 | * * * * 40 | * @param base Base Set or Array 41 | * @param inclusion Set or Array to include its values 42 | * @param allEquals By default all the same items encountered in the inclusion will be included, set this argument as false to get real intersection 43 | * @returns Intersection of base and inclusion 44 | */ 45 | export function intersection(base: T[], inclusion: TInclusion, allEquals?: boolean): T[]; 46 | export function intersection(base: Set, inclusion: TInclusion, allEquals?: boolean): Set; 47 | export function intersection(base: T[] | Set, inclusion: TInclusion, allEquals = true): T[] | Set { 48 | if (base instanceof Set) { 49 | return new Set(intersection(Array.from(base), inclusion)); 50 | } 51 | 52 | const incs: T[] = Array.from(inclusion); 53 | 54 | return base.filter(value => { 55 | const index = incs.indexOf(value); 56 | 57 | if (index > -1 && !allEquals) { 58 | incs.splice(index, 1); 59 | } 60 | 61 | return index > -1; 62 | }); 63 | } 64 | -------------------------------------------------------------------------------- /src/array/difference.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Difference Substraction Type 3 | */ 4 | export type TSubstraction = any[] | Set; 5 | 6 | /** 7 | * #### Difference 8 | * 9 | * Gets the difference of the two arrays or sets 10 | * 11 | * * * * 12 | * Example: 13 | * ```typescript 14 | * import { difference } from "@thalesrc/js-utils/array"; 15 | * 16 | * const base = ["a", "b", "c", "d", "a", "b", "c", "d"]; 17 | * 18 | * difference(base, ["a", "b"]); // ["c", "d", "c", "d"] 19 | * difference(base, ["a", "b"], true); // ["c", "d", "a", "b", "c", "d"] 20 | * ``` 21 | * 22 | * Array Prototype Example: 23 | * ```typescript 24 | * import "@thalesrc/js-utils/array/proto/difference"; 25 | * 26 | * const base = ["a", "b", "c", "d", "a", "b", "c", "d"]; 27 | * 28 | * base.difference(["a", "b"]); // ["c", "d", "c", "d"] 29 | * ``` 30 | * 31 | * Set Prototype Example: 32 | * ```typescript 33 | * import "@thalesrc/js-utils/set/proto/difference"; 34 | * 35 | * const base = new Set(["a", "b", "c", "d"]); 36 | * 37 | * base.difference(["a", "b"]); // Set(["c", "d"]) 38 | * ``` 39 | * * * * 40 | * @param base Base Set or Array 41 | * @param substraction Set or Array to remove its values from the base 42 | * @param allDiff By default all the same items encountered in substraction will be removed, set this argument as true to get real difference 43 | * @returns Difference of base from substraction 44 | */ 45 | export function difference(base: T[], substraction: TSubstraction, allDiff?: boolean): T[]; 46 | export function difference(base: Set, substraction: TSubstraction, allDiff?: boolean): Set; 47 | export function difference(base: T[] | Set, substraction: TSubstraction, allDiff = false): T[] | Set { 48 | if (base instanceof Set) { 49 | return new Set(difference(Array.from(base), substraction)); 50 | } 51 | 52 | const subs: T[] = Array.from(substraction); 53 | 54 | return base.filter(value => { 55 | const index = subs.indexOf(value); 56 | 57 | if (index > -1 && allDiff) { 58 | subs.splice(index, 1); 59 | } 60 | 61 | return index === -1; 62 | }); 63 | } 64 | -------------------------------------------------------------------------------- /src/function/debounce.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import { debounce, debounceWithKey } from './debounce'; 4 | 5 | class CustomError { 6 | constructor(public message: string) {} 7 | } 8 | 9 | describe('Debounce Function', () => { 10 | let foo = 0; 11 | function bar() { 12 | foo++; 13 | } 14 | 15 | beforeEach(() => { 16 | foo = 0; 17 | }); 18 | 19 | it('should debounce functions', done => { 20 | debounce(bar); 21 | debounce(bar); 22 | 23 | debounce(bar) 24 | .then(() => { 25 | expect(foo).toBe(1); 26 | done(); 27 | }); 28 | }); 29 | 30 | it('should wait the debouncing time to execute function', done => { 31 | const started = new Date().getTime(); 32 | 33 | debounce(bar).then(() => { 34 | expect(foo).toBe(1); 35 | expect(new Date().getTime() - started).toBeGreaterThan(179); 36 | done(); 37 | }); 38 | }); 39 | 40 | it('should debounce by given time', done => { 41 | const started = new Date().getTime(); 42 | 43 | debounce(bar, 50) 44 | .then(() => { 45 | const executedIn = new Date().getTime() - started; 46 | 47 | expect(foo).toBe(1); 48 | expect(executedIn).toBeGreaterThan(48); 49 | expect(executedIn).toBeLessThan(70); 50 | 51 | done(); 52 | }); 53 | }); 54 | 55 | it('should use passed this object', done => { 56 | const john = {x: 0}; 57 | 58 | function baz() { 59 | this.x++; 60 | } 61 | 62 | debounce(baz, undefined, john) 63 | .then(() => { 64 | expect(john.x).toBe(1); 65 | done(); 66 | }); 67 | }); 68 | 69 | it('should use passed arguments', done => { 70 | let john; 71 | 72 | function baz(a, b) { 73 | john = a + b; 74 | } 75 | 76 | debounce(baz, undefined, undefined, 5, 3) 77 | .then(() => { 78 | expect(john).toBe(8); 79 | done(); 80 | }); 81 | }); 82 | 83 | it('should throw the callback error if thrown', done => { 84 | function baz() { 85 | throw new CustomError('baz error'); 86 | } 87 | 88 | debounce(baz) 89 | .then(() => { 90 | throw new CustomError("couldn't catch error"); 91 | }) 92 | .catch(err => { 93 | expect(err.message).toBe('baz error'); 94 | done(); 95 | }); 96 | }); 97 | }); 98 | -------------------------------------------------------------------------------- /src/smart-map.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import { SmartMap } from './smart-map'; 4 | 5 | describe('SmartMap Class', () => { 6 | let map: SmartMap; 7 | 8 | beforeEach(() => { 9 | map = new SmartMap(); 10 | }); 11 | 12 | it('should construct properly', () => { 13 | const mapWithValues = new SmartMap([['key', 'value'], ['key2', 'value2']]); 14 | 15 | expect(map).toBeDefined(); 16 | expect(mapWithValues).toBeDefined(); 17 | expect([...mapWithValues.entries()]).toEqual([...new Map([['key', 'value'], ['key2', 'value2']]).entries()]); 18 | }); 19 | 20 | it('should set a value by primitive type keys', () => { 21 | map.set(1, 'number'); 22 | map.set('string', 'string'); 23 | map.set(true, 'boolean'); 24 | map.set(Symbol(), 'symbol'); 25 | map.set(undefined, 'undefined'); 26 | map.set(null, 'null'); 27 | 28 | expect(map.size).toBe(6); 29 | }); 30 | 31 | it('should set a value by object type keys', () => { 32 | const anObj = {}; 33 | 34 | map.set(anObj, 'foo'); 35 | expect(map.has(anObj)).toBe(true); 36 | }); 37 | 38 | it('should store object keys in weakmap storage', () => { 39 | const anObj = {}; 40 | 41 | map.set(anObj, 'foo'); 42 | 43 | expect(map.size).toBe(0); 44 | expect(map.has(anObj)).toBe(true); 45 | }); 46 | 47 | it('should check correctly when has method called via both primitive and object keys', () => { 48 | const anObj = {}; 49 | 50 | map.set(1, '1'); 51 | map.set(anObj, 'an object'); 52 | 53 | expect(map.has(1)).toBe(true); 54 | expect(map.has(anObj)).toBe(true); 55 | }); 56 | 57 | it('should get value when get method called via both primitive and object keys', () => { 58 | const anObj = {}; 59 | 60 | map.set(1, '1'); 61 | map.set(anObj, 'an object'); 62 | map.set(null, 'null'); 63 | 64 | expect(map.get(1)).toBe('1'); 65 | expect(map.get(anObj)).toBe('an object'); 66 | expect(map.get(null)).toBe('null'); 67 | }); 68 | 69 | it('should delete value when delete method called via both primitive and object key', () => { 70 | const anObj = {}; 71 | 72 | map.set(1, '1'); 73 | map.set(anObj, 'an object'); 74 | 75 | const primitiveResult = map.delete(1); 76 | const objectResult = map.delete(anObj); 77 | 78 | expect(primitiveResult).toBe(true); 79 | expect(objectResult).toBe(true); 80 | 81 | expect(map.size).toBe(0); 82 | expect(map.has(anObj)).toBe(false); 83 | }); 84 | }); 85 | -------------------------------------------------------------------------------- /src/array/proto/replace.ts: -------------------------------------------------------------------------------- 1 | import { replace, ReplaceItemsOptions, ReplaceByMapOptions } from '../replace'; 2 | 3 | declare global { 4 | export interface Array { 5 | /** 6 | * #### Replace 7 | * 8 | * Replaces an item with passed one of an array 9 | * 10 | * * * * 11 | * Example: 12 | * ```typescript 13 | * import "@thalesrc/js-utils/array/proto/replace"; 14 | * 15 | * const array = ["a", "b", "c", "a", "b", "c"]; 16 | * 17 | * array.replace("b", "x"); // ["a", "x", "c", "a", "b", "c"] 18 | * ``` 19 | * * * * 20 | * @param array Array to replace its item 21 | * @param itemToRemove Item to remove 22 | * @param itemToReplace Item to replace with 23 | * @return New replaced array 24 | */ 25 | replace(itemToRemove: T, itemToReplace: T): T[]; 26 | 27 | /** 28 | * #### Replace 29 | * 30 | * Deletes items and replaces new ones from passed starting index of an array 31 | * 32 | * * * * 33 | * Example: 34 | * ```typescript 35 | * import "@thalesrc/js-utils/array/proto/replace"; 36 | * 37 | * const array = ["a", "b", "c", "a", "b", "c"]; 38 | * 39 | * array.replace({startingIndex: 3, deleteCount: 1, itemsToReplace: ['x', 'y']}); // ["a", "b", "c", "x", "y", "b", "c"]; 40 | * ``` 41 | * * * * 42 | * @param array Array to replace its item 43 | * @param replaceOptions Replace options 44 | * @template T Typeof array items 45 | * @return New replaced array 46 | */ 47 | replace(replaceOptions: ReplaceItemsOptions): T[]; 48 | 49 | /** 50 | * #### Replace 51 | * 52 | * Deletes items and replaces new ones by passed matcher map 53 | * 54 | * * * * 55 | * Example: 56 | * ```typescript 57 | * import "@thalesrc/js-utils/array/proto/replace"; 58 | * 59 | * const array = ["a", "b", "c", "a", "b", "c"]; 60 | * const map = new Map(); 61 | * map.set("a", "x") 62 | * map.set("b", "y"); 63 | * 64 | * array.replace({itemsToReplace: map}); // ["x", "y", "c", "a", "b", "c"]; 65 | * array.replace({itemsToReplace: map, multi: true}); // ["x", "y", "c", "x", "y", "c"]; 66 | * ``` 67 | * * * * 68 | * @param array Array to replace its item 69 | * @param replaceOptions Replace options 70 | * @template T Typeof array items 71 | * @return New replaced array 72 | */ 73 | replace(replaceOptions: ReplaceByMapOptions): T[]; 74 | } 75 | } 76 | 77 | Array.prototype.replace = function(options: T | ReplaceItemsOptions | ReplaceByMapOptions, itemToReplace: T): T[] { 78 | return replace.call(null, this, ...arguments); 79 | }; 80 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Npm Package 2 | 3 | on: 4 | release: 5 | types: [created] 6 | 7 | jobs: 8 | # publish-npm: 9 | # runs-on: ubuntu-latest 10 | # steps: 11 | # - uses: actions/checkout@v1 12 | # - name: Set env 13 | # run: echo ::set-env name=PACKAGE_VERSION::$(echo ${GITHUB_REF:10}) 14 | # - uses: actions/setup-node@v1 15 | # with: 16 | # node-version: 12 17 | # registry-url: https://registry.npmjs.org/ 18 | # - run: npm install 19 | # - run: npm test 20 | # - run: npm run version-update 21 | # - run: npm run prepare:package 22 | # - run: npm run change-ts-version 23 | # - run: rm -rf node_modules 24 | # - run: rm ./package-lock.json 25 | # - run: npm install 26 | # - run: npm run prepare:legacy 27 | # - run: npm publish 28 | # env: 29 | # NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} 30 | 31 | prepare-lts: 32 | runs-on: ubuntu-latest 33 | steps: 34 | - uses: actions/checkout@v1 35 | - name: Set env 36 | run: echo "PACKAGE_VERSION=${GITHUB_REF:10}" >> $GITHUB_ENV 37 | - uses: actions/setup-node@v1 38 | with: 39 | node-version: 12 40 | registry-url: https://registry.npmjs.org/ 41 | - run: npm i 42 | - run: npm test 43 | - run: npm version $PACKAGE_VERSION --no-git-tag-version 44 | - run: npm run prepare:package 45 | - run: rm -rf node_modules 46 | - name: Upload Package Artifact 47 | uses: actions/upload-artifact@v2 48 | with: 49 | name: lts 50 | path: . 51 | 52 | prepare-legacy: 53 | runs-on: ubuntu-latest 54 | steps: 55 | - uses: actions/checkout@v1 56 | - name: Set env 57 | run: echo "PACKAGE_VERSION=${GITHUB_REF:10}" >> $GITHUB_ENV 58 | - uses: actions/setup-node@v1 59 | with: 60 | node-version: 12 61 | registry-url: https://registry.npmjs.org/ 62 | - run: npm version $PACKAGE_VERSION-legacy --no-git-tag-version 63 | - run: npx replace-json-property ./package.json typescript ~3.4.5 64 | - run: npx replace-json-property ./package.json @types/node 12.12.21 65 | - run: npm i 66 | - run: npm test 67 | - run: npm run prepare:package 68 | - run: rm -rf node_modules 69 | - name: Upload Package Artifact 70 | uses: actions/upload-artifact@v2 71 | with: 72 | name: legacy 73 | path: . 74 | publish: 75 | runs-on: ubuntu-latest 76 | needs: [prepare-lts, prepare-legacy] 77 | steps: 78 | - uses: actions/download-artifact@v2 79 | - uses: actions/setup-node@v1 80 | with: 81 | node-version: 12 82 | registry-url: https://registry.npmjs.org/ 83 | - name: Publish legacy 84 | run: cd ./legacy && npm publish --tag legacy 85 | env: 86 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} 87 | - name: Publish Latest 88 | run: cd ./lts && npm publish 89 | env: 90 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} -------------------------------------------------------------------------------- /src/function/debounce.ts: -------------------------------------------------------------------------------- 1 | import { timeout } from '../promise/timeout'; 2 | import { OpenPromise } from '../open-promise'; 3 | 4 | export type TDebounceFunction = (...args: any[]) => (T | Promise); 5 | 6 | interface ICacheObject { 7 | timeout: Promise; 8 | promise: OpenPromise; 9 | } 10 | 11 | export const DEFAULT_DEBOUNCE_TIME = 180; 12 | const KEY_REFERENCES = new Map(); 13 | const CACHE = new Map(); 14 | 15 | export function debounceWithKey( 16 | key: symbol, 17 | callback: TDebounceFunction, 18 | time = DEFAULT_DEBOUNCE_TIME, 19 | thisObject: any = null, 20 | ...args: any[] 21 | ): Promise { 22 | let timeoutPromise: Promise; 23 | let promise: OpenPromise; 24 | 25 | if (CACHE.has(key)) { 26 | const stocked = CACHE.get(key); 27 | timeoutPromise = stocked.timeout; 28 | promise = stocked.promise; 29 | timeout.cancel(timeoutPromise); 30 | } else { 31 | promise = new OpenPromise(); 32 | } 33 | 34 | timeoutPromise = timeout(time); 35 | CACHE.set(key, {promise, timeout: timeoutPromise}); 36 | 37 | timeoutPromise 38 | .then(() => { 39 | CACHE.delete(key); 40 | const result = callback.call(thisObject, ...args); 41 | promise.bindTo(result); 42 | return result; 43 | }) 44 | .catch(err => { 45 | if (err !== timeout.TIMEOUT_CANCELLED) { 46 | promise.reject(err); 47 | } 48 | }); 49 | 50 | return promise; 51 | } 52 | 53 | /** 54 | * #### Debounces a function that delays invoking until after given time have elapsed since the last time the debounced function was invoked 55 | * 56 | * * * * 57 | * Example usage: 58 | * ```typescript 59 | * import { debounce } from "@thalesrc/js-utils/promise"; 60 | * 61 | * function foo() { 62 | * console.log("hello"); 63 | * } 64 | * 65 | * for (let i = 0; i < 5; i++) { 66 | * debounce(foo); 67 | * } 68 | * 69 | * // logs "hello" only once 70 | * ``` 71 | * 72 | * Static usage example: 73 | * ```typescript 74 | * import "@thalesrc/js-utils/dist/as-proto/debounce"; 75 | * 76 | * function foo() { 77 | * console.log("hello"); 78 | * } 79 | * 80 | * for (let i = 0; i < 5; i++) { 81 | * foo.debounce(); 82 | * } 83 | * 84 | * // logs "hello" only once 85 | * ``` 86 | * * * * 87 | * @param callback The function to execute only last of multiple execute requests by given time 88 | * @param [time = 180] Time for debouncing 89 | * @param [thisObject = null] This object to execute the callback function with 90 | * @param args Function arguments 91 | * @return A promise which resolves right after the debouncing sequence has been finished 92 | */ 93 | export function debounce( 94 | callback: TDebounceFunction, 95 | time = DEFAULT_DEBOUNCE_TIME, 96 | thisObject: any = null, 97 | ...args: any[] 98 | ): Promise { 99 | if (!KEY_REFERENCES.has(callback)) { 100 | KEY_REFERENCES.set(callback, Symbol()); 101 | } 102 | 103 | return debounceWithKey(KEY_REFERENCES.get(callback), callback, time, thisObject, ...args); 104 | } 105 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at alisahinozcelik@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /src/open-promise.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import { OpenPromise } from './open-promise'; 4 | import { noop } from './function/noop'; 5 | 6 | class CustomError { 7 | constructor(public message: string) {} 8 | } 9 | 10 | describe('Open Promise Class', () => { 11 | it('should resolve properly', done => { 12 | const op = new OpenPromise(); 13 | const startedAt = new Date().getTime(); 14 | 15 | op.then(value => { 16 | expect(new Date().getTime() - startedAt).toBeGreaterThan(48); 17 | expect(value).toBe(1); 18 | done(); 19 | }); 20 | 21 | setTimeout(() => { 22 | op.resolve(1); 23 | }, 50); 24 | }); 25 | 26 | it('should reject properly', done => { 27 | const op = new OpenPromise(); 28 | 29 | op 30 | .then(value => { 31 | throw new CustomError('bar'); 32 | }) 33 | .catch((err: CustomError) => { 34 | expect(err.message).toBe('foo'); 35 | done(); 36 | }); 37 | 38 | setTimeout(() => { 39 | op.reject(new CustomError('foo')); 40 | }, 50); 41 | }); 42 | 43 | it('should return resolved state properly', done => { 44 | const op = new OpenPromise(); 45 | 46 | expect(op.resolved).toBe(false); 47 | op.resolve(1); 48 | 49 | setTimeout(() => { 50 | expect(op.resolved).toBe(true); 51 | expect(op.rejected).toBe(false); 52 | done(); 53 | }, 50); 54 | }); 55 | 56 | it('should return rejected state properly', done => { 57 | const op = new OpenPromise(); 58 | op.catch(noop); 59 | 60 | expect(op.rejected).toBe(false); 61 | op.reject(new CustomError('foo')); 62 | 63 | setTimeout(() => { 64 | expect(op.resolved).toBe(false); 65 | expect(op.rejected).toBe(true); 66 | done(); 67 | }, 50); 68 | }); 69 | 70 | it('should return finished state properly', done => { 71 | const op = new OpenPromise(); 72 | const op2 = new OpenPromise(); 73 | op2.catch(noop); 74 | 75 | expect(op.finished).toBe(false); 76 | expect(op2.finished).toBe(false); 77 | op.resolve(1); 78 | op2.reject(new CustomError('foo')); 79 | 80 | setTimeout(() => { 81 | expect(op.finished).toBe(true); 82 | expect(op2.finished).toBe(true); 83 | done(); 84 | }, 50); 85 | }); 86 | 87 | it('should resolve when bound promise resolved', done => { 88 | const op = new OpenPromise(); 89 | const startedAt = new Date().getTime(); 90 | 91 | op.then(val => { 92 | expect(new Date().getTime() - startedAt).toBeGreaterThan(48); 93 | expect(val).toBe(1); 94 | done(); 95 | }); 96 | 97 | op.bindTo(new Promise(resolve => setTimeout(resolve.bind(null, 1), 50))); 98 | }); 99 | 100 | it('should reject when bound promise rejected', done => { 101 | const op = new OpenPromise(); 102 | const startedAt = new Date().getTime(); 103 | 104 | op 105 | .then(() => { 106 | throw new CustomError('bar'); 107 | }) 108 | .catch((err: CustomError) => { 109 | expect(new Date().getTime() - startedAt).toBeGreaterThan(48); 110 | expect(err.message).toBe('foo'); 111 | done(); 112 | }); 113 | 114 | op.bindTo(new Promise((resolve, reject) => setTimeout(reject.bind(null, new CustomError('foo')), 50))); 115 | }); 116 | }); 117 | -------------------------------------------------------------------------------- /src/open-promise.ts: -------------------------------------------------------------------------------- 1 | import { noop } from './function/noop'; 2 | 3 | type Resolver = (value?: T) => any; 4 | type Rejector = (reason?: any) => any; 5 | type PromiseExecutor = (resolve: Resolver, reject: Rejector) => any; 6 | 7 | const RESOLVED = Symbol('Open Promise Resolved'); 8 | const REJECTED = Symbol('Open Promise Rejected'); 9 | 10 | let resolver: Resolver; 11 | let rejector: Rejector; 12 | 13 | /** 14 | * #### Open Promise 15 | * A promise constructor to resolve or reject from outside 16 | * 17 | * * * * 18 | * Example: 19 | * ```typescript 20 | * import { OpenPromise } from "@thalesrc/js-utils"; 21 | * 22 | * const aPromiseWillBeResolvedLater = new OpenPromise(); 23 | * 24 | * aPromiseWillBeResolvedLater.then(val => console.log(val)); 25 | * // aPromiseWillBeResolvedLater.finished // false 26 | * ... 27 | * ... 28 | * 29 | * aPromiseWillBeResolvedLater.resolve({x: 1}); 30 | * // aPromiseWillBeResolvedLater.finished // true 31 | * ``` 32 | * * * * 33 | * @template T typeof the value which is going to be resolved 34 | */ 35 | export class OpenPromise extends Promise { 36 | private [RESOLVED] = false; 37 | private [REJECTED] = false; 38 | 39 | /** 40 | * Resolves promise 41 | * @param value Value to resolve the promise 42 | */ 43 | public resolve: Resolver; 44 | 45 | /** 46 | * Rejects promise 47 | * @param reason Error to reject promise 48 | */ 49 | public reject: Rejector; 50 | 51 | /** 52 | * Returns whether is the promise resolved 53 | */ 54 | public resolved: boolean; 55 | 56 | /** 57 | * Returns whether is the promise rejected 58 | */ 59 | public rejected: boolean; 60 | 61 | /** 62 | * Returns whether is the promise finished 63 | */ 64 | public finished: boolean; 65 | 66 | /** 67 | * Open Promise Constructor 68 | */ 69 | constructor(executor: PromiseExecutor = noop) { 70 | super((resolve, reject) => { 71 | 72 | resolver = new Proxy(noop, { 73 | apply(t, c, [value]) { 74 | this.context[RESOLVED] = true; 75 | resolve(value); 76 | }, 77 | set(t, prop, value) { 78 | if (prop !== 'context') { 79 | return false; 80 | } 81 | 82 | this.context = value; 83 | 84 | return true; 85 | } 86 | }); 87 | 88 | rejector = new Proxy(noop, { 89 | apply(t, c, [reason]) { 90 | this.context[REJECTED] = true; 91 | reject(reason); 92 | }, 93 | set(t, prop, value) { 94 | if (prop !== 'context') { 95 | return false; 96 | } 97 | 98 | this.context = value; 99 | 100 | return true; 101 | } 102 | }); 103 | 104 | (executor || noop).call(null, resolver, rejector); 105 | }); 106 | 107 | resolver['context'] = this; 108 | rejector['context'] = this; 109 | 110 | this.resolve = resolver; 111 | this.reject = rejector; 112 | } 113 | 114 | /** 115 | * Binds a promise to the inner promise to resolve or reject with it 116 | * @param promise A promise to bind inner promise 117 | */ 118 | public bindTo(promise: Promise): void { 119 | if (promise instanceof Promise) { 120 | promise.then(e => this.resolve(e)).catch(e => this.reject(e)); 121 | } else { 122 | this.resolve(promise); 123 | } 124 | } 125 | } 126 | 127 | Object.defineProperty(OpenPromise.prototype, 'resolved', {get() { 128 | return this[RESOLVED]; 129 | }}); 130 | Object.defineProperty(OpenPromise.prototype, 'rejected', {get() { 131 | return this[REJECTED]; 132 | }}); 133 | Object.defineProperty(OpenPromise.prototype, 'finished', {get() { 134 | return this[RESOLVED] || this[REJECTED]; 135 | }}); 136 | -------------------------------------------------------------------------------- /src/smart-map.ts: -------------------------------------------------------------------------------- 1 | // Symbols to protect overriding private methods & properties of SmartMap Class 2 | const KEY__WEAKMAP: unique symbol = Symbol('SmartMap.weakMap'); 3 | 4 | /** 5 | * ### SmartMap 6 | * 7 | * Like WeakMap but can also store values using primitive keys 8 | * 9 | * * * * 10 | * Example usage: 11 | * ```typescript 12 | * import { SmartMap } from "@thalesrc/js-utils"; 13 | * 14 | * const aMap = new SmartMap(); 15 | * 16 | * aMap.set("foo", "foo"); 17 | * aMap.set(1, "thales rocks"); 18 | * 19 | * console.log(aMap.size) // 2 20 | * 21 | * aMap.set({}, "thales rocks again"); 22 | * console.log(aMap.size) // 2 23 | * 24 | * const anObject = {}; 25 | * aMap.set(anObject, "thales rocks again and again"); 26 | * console.log(aMap.size) // 3 27 | * console.log(aMap.get(anObject)) // "thales rocks again and again" 28 | * ``` 29 | * * * * 30 | * @template K Typeof Key 31 | * @template V Typeof Value 32 | */ 33 | export class SmartMap extends Map { 34 | /** 35 | * Symbol refference of weakmap property 36 | */ 37 | protected static readonly KEY__WEAKMAP = KEY__WEAKMAP; 38 | 39 | /** 40 | * Allowed key types to store them in a weakmap instance 41 | */ 42 | public static readonly TYPES_TO_STORE_IN_WEAKMAP = Object.freeze(['object', 'function']); 43 | 44 | /** 45 | * The WeakMap object to store values with associated non-primitive (object) keys 46 | */ 47 | protected [KEY__WEAKMAP] = new WeakMap(); 48 | 49 | /** 50 | * Returns a boolean asserting whether a value has been associated to the key in the SmartMap object or not. 51 | * 52 | * @param key The key of the element to test for presence in the SmartMap object. 53 | * @returns A boolean asserting whether a value has been associated to the key in the SmartMap object or not. 54 | */ 55 | public has(key: K): boolean { 56 | if (this.isStorableInWeakMap(key)) { 57 | return this[KEY__WEAKMAP].has(key); 58 | } 59 | 60 | return super.has(key); 61 | } 62 | 63 | /** 64 | * Returns the value associated to the key, or undefined if there is none. 65 | * 66 | * @param key The key of the element to return from the SmartMap object. 67 | * @returns The value associated to the key 68 | */ 69 | public get(key: K): V { 70 | if (this.isStorableInWeakMap(key)) { 71 | return this[KEY__WEAKMAP].get(key); 72 | } 73 | 74 | return super.get(key); 75 | } 76 | 77 | /** 78 | * Sets the value for the key in the SmartMap object. 79 | * 80 | * @param key The key of the element to add to the SmartMap object. 81 | * @param value The value of the element to add to the SmartMap object. 82 | * @returns Returns the SmartMap object. 83 | */ 84 | public set(key: K, value: V): this { 85 | if (this.isStorableInWeakMap(key)) { 86 | this[KEY__WEAKMAP].set(key, value); 87 | } else { 88 | super.set(key, value); 89 | } 90 | 91 | return this; 92 | } 93 | 94 | /** 95 | * Removes the associated value from the SmartMap object, 96 | * 97 | * @param key The key of the element to remove from the SmartMap object. 98 | * @returns `true` if an element in the SmartMap object existed and has been removed, or `false` if the element does not exist. `SmartMap.prototype.has(key)` will return false afterwards. 99 | */ 100 | public delete(key: K): boolean { 101 | return this[KEY__WEAKMAP].delete(key) || super.delete(key); 102 | } 103 | 104 | /** 105 | * Returns a boolean whether if the key is storable in weakmap or not 106 | * 107 | * @param key Key to check if it can be stored in weakmap 108 | * @returns A boolean whether if the key is storable in weakmap or not 109 | */ 110 | protected isStorableInWeakMap(key: K): boolean { 111 | return key !== null && SmartMap.TYPES_TO_STORE_IN_WEAKMAP.some(_key => typeof key === _key); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/promise/timeout.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import { timeout } from './timeout'; 4 | 5 | class CustomError { 6 | constructor(public message: string) {} 7 | } 8 | 9 | describe('Timeout Promise Function', () => { 10 | it('should resolve after 50ms', done => { 11 | const startedAt = new Date().getTime(); 12 | 13 | timeout(50) 14 | .then(() => { 15 | expect(new Date().getTime() - startedAt).toBeGreaterThan(48); 16 | done(); 17 | }); 18 | }); 19 | 20 | it('should resolve "foo" after 50ms', done => { 21 | const startedAt = new Date().getTime(); 22 | 23 | timeout(50, 'foo') 24 | .then(val => { 25 | expect(new Date().getTime() - startedAt).toBeGreaterThan(48); 26 | expect(val).toBe('foo'); 27 | done(); 28 | }); 29 | }); 30 | 31 | it('should be able to cancel with own instance', done => { 32 | const timeoutPromise = timeout(100); 33 | 34 | timeoutPromise 35 | .then(() => { 36 | throw new CustomError('bar'); 37 | }) 38 | .catch(err => { 39 | expect(err).not.toBeInstanceOf(CustomError); 40 | done(); 41 | }); 42 | 43 | timeout.cancel(timeoutPromise); 44 | }); 45 | 46 | it('should be able to cancel with a key', done => { 47 | const key = Symbol(); 48 | 49 | timeout(100, undefined, key) 50 | .then(() => { 51 | throw new CustomError('bar'); 52 | }) 53 | .catch(err => { 54 | expect(err).not.toBeInstanceOf(CustomError); 55 | done(); 56 | }); 57 | 58 | timeout.cancel(key); 59 | }); 60 | 61 | it('should throw default error when cancelled', done => { 62 | const key = Symbol(); 63 | 64 | timeout(100, undefined, key) 65 | .then(() => { 66 | throw new CustomError('bar'); 67 | }) 68 | .catch(err => { 69 | expect(err).toEqual(timeout.TIMEOUT_CANCELLED); 70 | done(); 71 | }); 72 | 73 | timeout.cancel(key); 74 | }); 75 | 76 | it('should throw related error when identifier not found', done => { 77 | const byKey = timeout.cancel(Symbol()) 78 | .then(() => { 79 | throw new CustomError('foo'); 80 | }) 81 | .catch(err => { 82 | expect(err).toEqual(timeout.IDENTIFIER_NOT_FOUND); 83 | }); 84 | 85 | const byPromise = timeout.cancel(Promise.resolve()) 86 | .then(() => { 87 | throw new CustomError('foo'); 88 | }) 89 | .catch(err => { 90 | expect(err).toEqual(timeout.IDENTIFIER_NOT_FOUND); 91 | }); 92 | 93 | Promise.all([byKey, byPromise]).then(() => { 94 | done(); 95 | }); 96 | }); 97 | 98 | 99 | it('should throw related error when timeout finished already', done => { 100 | const resolved = timeout(10); 101 | const rejected = timeout(10); 102 | 103 | rejected.catch(() => {}); 104 | 105 | timeout.cancel(rejected); 106 | 107 | setTimeout(() => { 108 | const _resolved = timeout.cancel(resolved) 109 | .then(() => { 110 | throw new CustomError('foo'); 111 | }) 112 | .catch(err => { 113 | expect(err).toEqual(timeout.FINISHED_ALREADY); 114 | }); 115 | 116 | const _rejected = timeout.cancel(rejected) 117 | .then(() => { 118 | throw new CustomError('foo'); 119 | }) 120 | .catch(err => { 121 | expect(err).toEqual(timeout.FINISHED_ALREADY); 122 | }); 123 | 124 | Promise.all([_resolved, _rejected]).then(() => { 125 | done(); 126 | }); 127 | }, 50); 128 | }); 129 | 130 | it('should throw custom error when cancelling', done => { 131 | const timeoutPromise = timeout(100); 132 | 133 | timeoutPromise 134 | .then(() => { 135 | throw new CustomError('bar'); 136 | }) 137 | .catch(err => { 138 | expect(err).toBe('foo'); 139 | done(); 140 | }); 141 | 142 | timeout.cancel(timeoutPromise, 'foo'); 143 | }); 144 | }); 145 | -------------------------------------------------------------------------------- /src/promise/static/timeout.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import './timeout'; 4 | 5 | class CustomError { 6 | constructor(public message: string) {} 7 | } 8 | 9 | describe('Promise Timeout Static Function', () => { 10 | it('should resolve after 50ms', done => { 11 | const startedAt = new Date().getTime(); 12 | 13 | Promise.timeout(50) 14 | .then(() => { 15 | expect(new Date().getTime() - startedAt).toBeGreaterThan(48); 16 | done(); 17 | }); 18 | }); 19 | 20 | it('should resolve "foo" after 50ms', done => { 21 | const startedAt = new Date().getTime(); 22 | 23 | Promise.timeout(50, 'foo') 24 | .then(val => { 25 | expect(new Date().getTime() - startedAt).toBeGreaterThan(48); 26 | expect(val).toBe('foo'); 27 | done(); 28 | }); 29 | }); 30 | 31 | it('should be able to cancel with own instance', done => { 32 | const timeout = Promise.timeout(100); 33 | 34 | timeout 35 | .then(() => { 36 | throw new CustomError('bar'); 37 | }) 38 | .catch(err => { 39 | expect(err).not.toBeInstanceOf(CustomError); 40 | done(); 41 | }); 42 | 43 | Promise.timeout.cancel(timeout); 44 | }); 45 | 46 | it('should be able to cancel with a key', done => { 47 | const key = Symbol(); 48 | 49 | Promise.timeout(100, undefined, key) 50 | .then(() => { 51 | throw new CustomError('bar'); 52 | }) 53 | .catch(err => { 54 | expect(err).not.toBeInstanceOf(CustomError); 55 | done(); 56 | }); 57 | 58 | Promise.timeout.cancel(key); 59 | }); 60 | 61 | it('should throw default error when cancelled', done => { 62 | const key = Symbol(); 63 | 64 | Promise.timeout(100, undefined, key) 65 | .then(() => { 66 | throw new CustomError('bar'); 67 | }) 68 | .catch(err => { 69 | expect(err).toEqual(Promise.timeout.TIMEOUT_CANCELLED); 70 | done(); 71 | }); 72 | 73 | Promise.timeout.cancel(key); 74 | }); 75 | 76 | it('should throw related error when identifier not found', done => { 77 | const byKey = Promise.timeout.cancel(Symbol()) 78 | .then(() => { 79 | throw new CustomError('foo'); 80 | }) 81 | .catch(err => { 82 | expect(err).toEqual(Promise.timeout.IDENTIFIER_NOT_FOUND); 83 | }); 84 | 85 | const byPromise = Promise.timeout.cancel(Promise.resolve()) 86 | .then(() => { 87 | throw new CustomError('foo'); 88 | }) 89 | .catch(err => { 90 | expect(err).toEqual(Promise.timeout.IDENTIFIER_NOT_FOUND); 91 | }); 92 | 93 | Promise.all([byKey, byPromise]).then(() => { 94 | done(); 95 | }); 96 | }); 97 | 98 | 99 | it('should throw related error when timeout finished already', done => { 100 | const resolved = Promise.timeout(10); 101 | const rejected = Promise.timeout(10); 102 | 103 | rejected.catch(() => {}); 104 | 105 | Promise.timeout.cancel(rejected); 106 | 107 | setTimeout(() => { 108 | const _resolved = Promise.timeout.cancel(resolved) 109 | .then(() => { 110 | throw new CustomError('foo'); 111 | }) 112 | .catch(err => { 113 | expect(err).toEqual(Promise.timeout.FINISHED_ALREADY); 114 | }); 115 | 116 | const _rejected = Promise.timeout.cancel(rejected) 117 | .then(() => { 118 | throw new CustomError('foo'); 119 | }) 120 | .catch(err => { 121 | expect(err).toEqual(Promise.timeout.FINISHED_ALREADY); 122 | }); 123 | 124 | Promise.all([_resolved, _rejected]).then(() => { 125 | done(); 126 | }); 127 | }, 50); 128 | }); 129 | 130 | it('should throw custom error when cancelling', done => { 131 | const timeout = Promise.timeout(100); 132 | 133 | timeout 134 | .then(() => { 135 | throw new CustomError('bar'); 136 | }) 137 | .catch(err => { 138 | expect(err).toBe('foo'); 139 | done(); 140 | }); 141 | 142 | Promise.timeout.cancel(timeout, 'foo'); 143 | }); 144 | }); 145 | -------------------------------------------------------------------------------- /src/object/clone.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | 3 | import { clone } from './clone'; 4 | 5 | describe('Clone Function', () => { 6 | it('should clone primitives properly', () => { 7 | expect(clone(0)).toBe(0); 8 | expect(clone(NaN)).toEqual(NaN); 9 | expect(clone('foo')).toBe('foo'); 10 | expect(clone(false)).toBe(false); 11 | expect(clone(undefined)).toBe(undefined); 12 | expect(clone(null)).toBe(null); 13 | }); 14 | 15 | it('should reference functions and symbols by default', () => { 16 | const sym = Symbol('foo'); 17 | expect(clone(sym)).toBe(sym); 18 | 19 | function foo() {} 20 | const bar = foo; 21 | 22 | expect(clone(foo)).toBe(foo); 23 | expect(clone(bar)).toBe(foo); 24 | }); 25 | 26 | it('should clone object', () => { 27 | const foo = {x: 1, y: 2}; 28 | const bar = clone(foo); 29 | 30 | expect(bar).not.toBe(foo); 31 | expect(bar).toEqual(foo); 32 | }); 33 | 34 | it('should clone array', () => { 35 | const foo = [1, 2]; 36 | const bar = clone(foo); 37 | 38 | expect(bar).not.toBe(foo); 39 | expect(bar).toEqual(foo); 40 | }); 41 | 42 | it('should clone map', () => { 43 | const foo = new Map([['x', 1], ['y', 2]]); 44 | const bar = clone(foo); 45 | 46 | expect(bar).not.toBe(foo); 47 | expect(bar).toEqual(foo); 48 | }); 49 | 50 | it('should clone set', () => { 51 | const foo = new Set(['a', 'b']); 52 | const bar = clone(foo); 53 | 54 | expect(bar).not.toBe(foo); 55 | expect(bar).toEqual(foo); 56 | }); 57 | 58 | it('should clone object deeply', () => { 59 | const map = new Map(); 60 | map.set(1, 'a'); 61 | 62 | const foo = {x: 1, y: 2, z: {a: 'a', b: 'b'}, t: [1, 2], m: map}; 63 | const bar = clone(foo); 64 | 65 | expect(bar).toEqual(foo); 66 | 67 | expect(bar.z).not.toBe(foo.z); 68 | expect(bar.z).toEqual(foo.z); 69 | 70 | expect(bar.t).not.toBe(foo.t); 71 | expect(bar.t).toEqual(foo.t); 72 | 73 | expect(bar.m).not.toBe(foo.m); 74 | expect(bar.m).toEqual(map); 75 | }); 76 | 77 | 78 | it('should clone array deeply', () => { 79 | const foo: any = [[1, 2], {a: 1, b: ['x', 'y']}]; 80 | const bar = clone(foo); 81 | 82 | expect(bar).toEqual(foo); 83 | 84 | expect(bar[0]).not.toBe(foo[0]); 85 | expect(bar[0]).toEqual(foo[0]); 86 | 87 | expect(bar[1]).not.toBe(foo[1]); 88 | expect(bar[1]).toEqual(foo[1]); 89 | 90 | expect(bar[1].b).not.toBe(foo[1].b); 91 | expect(bar[1].b).toEqual(foo[1].b); 92 | }); 93 | 94 | it('should reference unwanted instances', () => { 95 | class Foo {} 96 | class Foo2 {} 97 | 98 | const bar = {x: new Foo(), y: new Foo(), z: new Foo2()}; 99 | const baz = clone(bar, {instancesToRefer: [Foo, Foo2]}); 100 | 101 | expect(baz).toEqual(bar); 102 | 103 | expect(baz.x).toBe(bar.x); 104 | expect(baz.y).toBe(bar.y); 105 | expect(baz.z).toBe(bar.z); 106 | }); 107 | 108 | it('should reference filtered values', () => { 109 | const foo = {a: {x: true}, b: {x: false}}; 110 | const bar = clone(foo, {valueFiltererToRefer: value => value.x}); 111 | 112 | expect(bar.a).toBe(foo.a); 113 | expect(bar.b).not.toBe(foo.b); 114 | expect(bar.b).toEqual(foo.b); 115 | }); 116 | 117 | it('should reference unwanted properties', () => { 118 | const sym = Symbol(); 119 | const anObjKey = {}; 120 | const map = new Map(); 121 | map.set(anObjKey, {x: 5}); 122 | 123 | const foo = {a: {x: 1}, b: {x: 2}, [sym]: {x: 3}, c: [{x: 3}, {x: 4}], d: map}; 124 | const bar = clone(foo, {propsToRefer: ['a', sym, 1, anObjKey]}); 125 | 126 | expect(bar.a).toBe(foo.a); 127 | 128 | expect(bar.b).not.toBe(foo.b); 129 | expect(bar.b).toEqual(foo.b); 130 | 131 | expect(bar[sym]).toBe(foo[sym]); 132 | 133 | expect(bar.c[0]).not.toBe(foo.c[0]); 134 | expect(bar.c[1]).toBe(foo.c[1]); 135 | 136 | expect(bar.d.get(anObjKey)).toBe(foo.d.get(anObjKey)); 137 | }); 138 | 139 | it('should clone via custom cloner', () => { 140 | class A { 141 | public customCloned = false; 142 | } 143 | 144 | function aCloner(object: T, options, internal): T { 145 | const obj = new A(); 146 | obj.customCloned = true; 147 | return obj as any; 148 | } 149 | 150 | const foo = [new A()]; 151 | const bar = clone(foo, {customCloners: new Map([[A, aCloner]])}); 152 | 153 | expect(bar[0].customCloned).toBe(true); 154 | }); 155 | 156 | it('should overcome circular reference', () => { 157 | const foo: {[key: string]: any} = {}; 158 | const bar: {[key: string]: any} = {}; 159 | const x: {[key: string]: any} = {}; 160 | foo.bar = bar; 161 | foo.x = x; 162 | x.bar = bar; 163 | foo.foo = foo; 164 | 165 | const baz = clone(foo); 166 | 167 | expect(baz.foo).toBe(baz); 168 | expect(baz.foo.bar).toBe(baz.bar); 169 | expect(baz.foo.x.bar).toBe(baz.bar); 170 | }); 171 | }); 172 | -------------------------------------------------------------------------------- /src/promise/timeout.ts: -------------------------------------------------------------------------------- 1 | import { OpenPromise } from '../open-promise'; 2 | 3 | export interface PromiseTimeoutFunction { 4 | /** 5 | * @see timeout 6 | * @param time Time to resolve promise in miliseconds 7 | * @param value Value to resolve promise with 8 | * @param key Key to cancel the timeout if needed 9 | */ 10 | (time: number, value?: T, key?: symbol): Promise; 11 | 12 | /** 13 | * #### Promise Timeout Canceller 14 | * 15 | * * * * 16 | * Usage via promise instance: 17 | * ```typescript 18 | * import { timeout } from "@thalesrc/js-utils/promise"; 19 | * 20 | * const timeout = timeout(1000); 21 | * 22 | * timeout 23 | * .then(() => console.log("this won't be logged")) 24 | * .catch(() => console.log("this will be logged, because timer has been cancelled")); 25 | * 26 | * timeout.cancel(timeout); 27 | * ``` 28 | * 29 | * Usage via key 30 | * ```typescript 31 | * import { timeout } from "@thalesrc/js-utils/promise"; 32 | * 33 | * const key = Symbol(); 34 | * 35 | * timeout(1000, null, key) 36 | * .then(() => console.log("this won't be logged")) 37 | * .catch(() => console.log("this will be logged, because timer has been cancelled")); 38 | * 39 | * timeout.cancel(key); 40 | * ``` 41 | * Static usage example: 42 | * ```typescript 43 | * import "@thalesrc/js-utils/promise/static/timeout"; 44 | * 45 | * const timeout = Promise.timeout(1000); 46 | * 47 | * timeout 48 | * .then(() => console.log("this won't be logged")) 49 | * .catch(() => console.log("this will be logged, because timer has been cancelled")); 50 | * 51 | * Promise.timeout.cancel(timeout); 52 | * ``` 53 | * * * * 54 | * @param identifier The identifier of the promise to cancel 55 | * @param error The error which will be throwed by the cancelled promise 56 | * @returns A promise which resolves if cancelling process is successfull, rejects otherwise 57 | */ 58 | cancel(identifier: Promise | symbol, error?: any): Promise; 59 | 60 | /** 61 | * Will be throwed in cancelling promise when the timer has already finished or cancelled 62 | */ 63 | readonly FINISHED_ALREADY: symbol; 64 | 65 | /** 66 | * Will be throwed in cancelling promise when the timer has not found via identifier 67 | */ 68 | readonly IDENTIFIER_NOT_FOUND: symbol; 69 | 70 | /** 71 | * The default timeout cancelling rejection error 72 | * Will be throwed when the timer has cancelled 73 | */ 74 | readonly TIMEOUT_CANCELLED: symbol; 75 | } 76 | 77 | interface ITimeoutCache { 78 | id: number; 79 | openPromise: OpenPromise; 80 | } 81 | 82 | // Defined here for documentation 83 | const promiseTimeoutInitializer = (() => { 84 | const REFERANCE_CACHE = new WeakMap, ITimeoutCache>(); 85 | const KEY_CACHE = new Map(); 86 | 87 | const timeout = (time: number, value: T = undefined, key: symbol = undefined): Promise => { 88 | const openPromise = new OpenPromise(); 89 | const id: any = setTimeout(() => { 90 | openPromise.resolve(value); 91 | }, time); 92 | 93 | const cacheObject: ITimeoutCache = {openPromise, id}; 94 | 95 | REFERANCE_CACHE.set(openPromise, cacheObject); 96 | 97 | if (key) { 98 | KEY_CACHE.set(key, cacheObject); 99 | } 100 | 101 | return openPromise; 102 | }; 103 | 104 | Object.defineProperties(timeout, { 105 | FINISHED_ALREADY: { 106 | writable: false, 107 | value: Symbol('Timeout finished already') 108 | }, 109 | IDENTIFIER_NOT_FOUND: { 110 | writable: false, 111 | value: Symbol('Timeout not found via identifier') 112 | }, 113 | TIMEOUT_CANCELLED: { 114 | writable: false, 115 | value: Symbol('Timeout cancelled') 116 | } 117 | }); 118 | 119 | (timeout).cancel = async (identifier: Promise | symbol, error?: any) => { 120 | const { FINISHED_ALREADY, IDENTIFIER_NOT_FOUND, TIMEOUT_CANCELLED } = timeout; 121 | 122 | if (typeof error === 'undefined') { 123 | error = TIMEOUT_CANCELLED; 124 | } 125 | 126 | let cache: ITimeoutCache; 127 | 128 | if (typeof identifier === 'symbol') { 129 | cache = KEY_CACHE.get(identifier); 130 | } else { 131 | cache = REFERANCE_CACHE.get(identifier); 132 | } 133 | 134 | if (!cache) { 135 | throw IDENTIFIER_NOT_FOUND; 136 | } 137 | 138 | if (cache.openPromise.finished) { 139 | throw FINISHED_ALREADY; 140 | } 141 | 142 | clearTimeout(cache.id as any); 143 | cache.openPromise.reject(error); 144 | 145 | if (typeof identifier === 'symbol') { 146 | KEY_CACHE.delete(identifier); 147 | } 148 | }; 149 | 150 | return timeout; 151 | })(); 152 | 153 | /** 154 | * #### Promise Timeout 155 | * Returns a promise which resolves after given time 156 | * 157 | * * * * 158 | * Example: 159 | * ```typescript 160 | * import { timeout } from "@thalesrc/js-utils/promise"; 161 | * 162 | * timeout(1000); 163 | * .then(() => console.log("will be logged after a second")); 164 | * ``` 165 | * Example with a resolve value: 166 | * ```typescript 167 | * import { timeout } from "@thalesrc/js-utils/promise"; 168 | * 169 | * timeout(1000, "foo"); 170 | * .then(val => console.log("will log 'foo' after a second", val)); 171 | * ``` 172 | * Can be used in promise chaining: 173 | * ```typescript 174 | * import { timeout } from "@thalesrc/js-utils/promise"; 175 | * 176 | * fetch("http://localhost:8080/anEndpoint") // Fetch something 177 | * .then(val => timeout(1000, val)) // Wait a second after response 178 | * .then(val => { 179 | * ...do something else 180 | * }); 181 | * ``` 182 | * Static usage example: 183 | * ```typescript 184 | * import "@thalesrc/js-utils/promise/static/timeout"; 185 | * 186 | * Promise.timeout(1000); 187 | * .then(() => console.log("will be logged after a second")); 188 | * ``` 189 | * * * * 190 | * @see PromiseTimeoutFunction#cancel for cancelling 191 | */ 192 | export const timeout: PromiseTimeoutFunction = promiseTimeoutInitializer; 193 | -------------------------------------------------------------------------------- /src/array/replace.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Replace Items Options Interface 3 | * 4 | * @template T typeof the items of the array 5 | */ 6 | export interface ReplaceItemsOptions { 7 | /** 8 | * Starting index of the array for replacement operation 9 | * 10 | * @default 0 11 | */ 12 | startingIndex?: number; 13 | 14 | /** 15 | * Determines how many items will be deleted from the array 16 | * 17 | * @default 1 18 | */ 19 | deleteCount?: number; 20 | 21 | /** 22 | * Items to replace 23 | * 24 | * @default [] 25 | */ 26 | itemsToReplace?: T[]; 27 | } 28 | 29 | /** 30 | * Replace by Item Matcher Map Options Interface 31 | * 32 | * @template T typeof the items of the array 33 | */ 34 | export interface ReplaceByMapOptions { 35 | /** 36 | * Items' Matcher Map to replace 37 | */ 38 | itemsToReplace: Map; 39 | 40 | /** 41 | * Set as `true` to replace all items in the array if matches with the key of the matcher map while iterating 42 | * 43 | * @default false 44 | */ 45 | multi?: boolean; 46 | } 47 | 48 | /** 49 | * Default Options for Replace Items Mode 50 | */ 51 | const REPLACE_ITEMS_DEFAULT_OPTIONS: Required> = { 52 | deleteCount: 1, 53 | itemsToReplace: [], 54 | startingIndex: 0 55 | }; 56 | 57 | /** 58 | * Default Options for Replace By Map Mode 59 | */ 60 | const REPLACE_BY_MAP_DEFAULT_OPTIONS: Required> = { 61 | itemsToReplace: new Map(), 62 | multi: false 63 | }; 64 | 65 | /** 66 | * #### Replace 67 | * 68 | * Replaces an item with passed one of an array 69 | * 70 | * * * * 71 | * Example: 72 | * ```typescript 73 | * import { replace } from "@thalesrc/js-utils/array"; 74 | * 75 | * const array = ["a", "b", "c", "a", "b", "c"]; 76 | * 77 | * replace(array, "b", "x"); // ["a", "x", "c", "a", "b", "c"] 78 | * ``` 79 | * 80 | * Array Prototype Example: 81 | * ```typescript 82 | * import "@thalesrc/js-utils/array/proto/replace"; 83 | * 84 | * const array = ["a", "b", "c", "a", "b", "c"]; 85 | * 86 | * array.replace("b", "x"); // ["a", "x", "c", "a", "b", "c"] 87 | * ``` 88 | * * * * 89 | * @param array Array to replace its item 90 | * @param itemToRemove Item to remove 91 | * @param itemToReplace Item to replace with 92 | * @template T Typeof array items 93 | * @return New replaced array 94 | */ 95 | export function replace(array: T[], itemToRemove: T, itemToReplace: T): T[]; 96 | /** 97 | * #### Replace 98 | * 99 | * Deletes items and replaces new ones from passed starting index of an array 100 | * 101 | * * * * 102 | * Example: 103 | * ```typescript 104 | * import { replace } from "@thalesrc/js-utils"; 105 | * 106 | * const array = ["a", "b", "c", "a", "b", "c"]; 107 | * 108 | * replace(array, {startingIndex: 3, deleteCount: 1, itemsToReplace: ['x', 'y']}); // ["a", "b", "c", "x", "y", "b", "c"]; 109 | * ``` 110 | * Array Prototype Example: 111 | * ```typescript 112 | * import "@thalesrc/js-utils/dist/as-proto/array-replace"; 113 | * 114 | * const array = ["a", "b", "c", "a", "b", "c"]; 115 | * 116 | * array.replace({startingIndex: 3, deleteCount: 1, itemsToReplace: ['x', 'y']}); // ["a", "b", "c", "x", "y", "b", "c"]; 117 | * ``` 118 | * * * * 119 | * @param array Array to replace its item 120 | * @param replaceOptions Replace options 121 | * @template T Typeof array items 122 | * @return New replaced array 123 | */ 124 | export function replace(array: T[], replaceOptions: ReplaceItemsOptions): T[]; 125 | /** 126 | * #### Replace 127 | * 128 | * Deletes items and replaces new ones by passed matcher map 129 | * 130 | * * * * 131 | * Example: 132 | * ```typescript 133 | * import { replace } from "@thalesrc/js-utils"; 134 | * 135 | * const array = ["a", "b", "c", "a", "b", "c"]; 136 | * const map = new Map(); 137 | * map.set("a", "x") 138 | * map.set("b", "y"); 139 | * 140 | * replace(array, {itemsToReplace: map}); // ["x", "y", "c", "a", "b", "c"]; 141 | * replace(array, {itemsToReplace: map, multi: true}); // ["x", "y", "c", "x", "y", "c"]; 142 | * ``` 143 | * Array Prototype Example: 144 | * ```typescript 145 | * import "@thalesrc/js-utils/dist/as-proto/array-replace"; 146 | * 147 | * const array = ["a", "b", "c", "a", "b", "c"]; 148 | * const map = new Map(); 149 | * map.set("a", "x") 150 | * map.set("b", "y"); 151 | * 152 | * array.replace({itemsToReplace: map}); // ["x", "y", "c", "a", "b", "c"]; 153 | * array.replace({itemsToReplace: map, multi: true}); // ["x", "y", "c", "x", "y", "c"]; 154 | * ``` 155 | * * * * 156 | * @param array Array to replace its item 157 | * @param replaceOptions Replace options 158 | * @template T Typeof array items 159 | * @return New replaced array 160 | */ 161 | export function replace(array: T[], replaceOptions: ReplaceByMapOptions): T[]; 162 | export function replace( 163 | array: T[], 164 | options: T | ReplaceItemsOptions | ReplaceByMapOptions, 165 | itemToReplace?: T 166 | ): T[] { 167 | if (arguments.length > 2) { 168 | options = >{ 169 | startingIndex: array.indexOf(options), 170 | itemsToReplace: [itemToReplace] 171 | }; 172 | } 173 | 174 | if (( | ReplaceItemsOptions>options).itemsToReplace instanceof Array) { 175 | return _replaceItems(array, >options); 176 | } else { 177 | return _replaceByMap(array, >options); 178 | } 179 | } 180 | 181 | /** 182 | * Replace items by 'replace items' mode 183 | * 184 | * @param array Array to replace items 185 | * @param options Replace options 186 | */ 187 | function _replaceItems(array: T[], options: ReplaceItemsOptions): T[] { 188 | const { startingIndex, deleteCount, itemsToReplace}: Required> = Object.assign({}, REPLACE_ITEMS_DEFAULT_OPTIONS, options); 189 | 190 | const newArray = [...array]; 191 | 192 | // If item not found or index number is negative; do nothing 193 | if (startingIndex < 0) { 194 | return newArray; 195 | } 196 | 197 | newArray.splice(startingIndex, deleteCount, ...itemsToReplace); 198 | 199 | return newArray; 200 | } 201 | 202 | /** 203 | * Replace items by 'replace by map' mode 204 | * 205 | * @param array Array to replace items 206 | * @param options Replace options 207 | */ 208 | function _replaceByMap(array: T[], options: ReplaceByMapOptions): T[] { 209 | const { itemsToReplace, multi }: Required> = Object.assign({}, REPLACE_BY_MAP_DEFAULT_OPTIONS, options); 210 | const iterationMethod = multi ? Array.prototype.forEach : Array.prototype.some; 211 | 212 | const newArray = [...array]; 213 | 214 | itemsToReplace.forEach((value, key) => { 215 | iterationMethod.call(newArray, (item, index) => { 216 | const matches = item === key; 217 | 218 | if (matches) { 219 | newArray.splice(index, 1, value); 220 | } 221 | 222 | return matches; 223 | }); 224 | }); 225 | 226 | return newArray; 227 | } 228 | -------------------------------------------------------------------------------- /src/object/clone.ts: -------------------------------------------------------------------------------- 1 | import { difference } from '../array/difference'; 2 | import { intersection } from '../array/intersection'; 3 | import { merge } from '../map/merge'; 4 | 5 | /** 6 | * #### Cloning Options 7 | * Cloning options of the [clone]{@link clone} function 8 | */ 9 | export interface ICloneOptions { 10 | /** 11 | * #### Instance constructors which cloning unwanted 12 | * Give the instance constructors to prevent them being cloned 13 | * 14 | * * * * 15 | * Example: 16 | * ```typescript 17 | * import { clone } from "@thalesrc/js-utils/object"; 18 | * 19 | * const object = { 20 | * width: 250, 21 | * element: document.querySelector("div") 22 | * }; 23 | * 24 | * const clonedObject = clone(object, {instancesToRefer: [ HTMLElement ]}); 25 | * // clonedObject.element === object.element 26 | * ``` 27 | * * * * 28 | * @default [] 29 | */ 30 | instancesToRefer?: any[]; 31 | 32 | /** 33 | * #### Value filterer to pass same references 34 | * Being called for each value while cloning. Return true to prevent cloning for the incoming value and pass the same reference instead. 35 | * 36 | * * * * 37 | * Example: 38 | * ```typescript 39 | * import { clone } from "@thalesrc/js-utils/object"; 40 | * 41 | * const john = { age: 20, willBeReferenced: true }; 42 | * const jane = { age: 25, willBeReferenced: false }; 43 | * 44 | * const array = [john, jane]; 45 | * 46 | * const clonedArray = clone(array, {valueFiltererToRefer: person => person.willBeReferenced }); 47 | * 48 | * // clonedArray[0] === array[0] 49 | * // clonedArray[1] !== array[1] 50 | * ``` 51 | * * * * 52 | * @param value incoming value to be cloned 53 | * @return Return true to pass same reference, false to clone 54 | * @default () => false 55 | */ 56 | valueFiltererToRefer?(value: any): boolean; 57 | 58 | /** 59 | * #### Properties which cloning unwanted 60 | * Give the unwanted properties to prevent them being cloned 61 | * 62 | * * * * 63 | * Example: 64 | * ```typescript 65 | * import { clone } from "@thalesrc/js-utils/object"; 66 | * 67 | * const object = { 68 | * width: 250, 69 | * element: document.querySelector("div") 70 | * }; 71 | * 72 | * const clonedObject = clone(object, {propsToRefer: [ "element" ]}); 73 | * // clonedObject.element === object.element 74 | * ``` 75 | * * * * 76 | * @default [] 77 | */ 78 | propsToRefer?: Array; 79 | 80 | /** 81 | * #### Custom Cloner Map 82 | * 83 | * * * * 84 | * Example: 85 | * ```typescript 86 | * import { clone } from "@thalesrc/js-utils/object"; 87 | * 88 | * const object = { 89 | * width: 250, 90 | * element: document.querySelector("div") 91 | * }; 92 | * 93 | * function divCloner(objectToClone, options, internalData) { 94 | * const newElement = document.createElement("div"); 95 | * newElement.classList.add("cloned"); 96 | * return newElement; 97 | * } 98 | * 99 | * const customCloners = new Map(); 100 | * customCloners.set(HTMLDivElement, divCloner); 101 | * 102 | * const clonedObject = clone(object); 103 | * ``` 104 | * * * * 105 | */ 106 | customCloners?: Map; 107 | } 108 | 109 | /** 110 | * #### Internal Data For Cloning Process 111 | */ 112 | export interface IClonerInternalData { 113 | clonedObjects: Map; 114 | parent?: Object; 115 | key?: number | string | symbol; 116 | finalizers: Array<() => void>; 117 | } 118 | 119 | // TODO: uncomment the code below and delete polyfill when typedoc supports ts@2.8.x 120 | // type TNonOptionalCloneOptions = Required; 121 | export type PolyfillRequired = { [P in TNames]: (T & { [name: string]: never })[P] }; 122 | 123 | /** 124 | * Cloning options with all properties required 125 | * @see ICloneOptions 126 | */ 127 | export type TNonOptionalCloneOptions = PolyfillRequired; 128 | 129 | /** 130 | * Custom Cloner Function 131 | */ 132 | export type TInstanceCloner = (objectToClone: T, options: TNonOptionalCloneOptions, internalData: IClonerInternalData) => T; 133 | 134 | /** 135 | * A placeholder to symbolize the cloning progress of `objectToClone` hasn't finished yet 136 | */ 137 | const CLONING_IN_PROGRESS = Symbol('Cloning In Progress'); 138 | 139 | /** 140 | * Function Map to clone specific objects types 141 | */ 142 | const DEFAULT_INSTANCE_CLONERS = new Map(); 143 | 144 | // Set cloning functions 145 | DEFAULT_INSTANCE_CLONERS.set(Array, arrayCloner); 146 | DEFAULT_INSTANCE_CLONERS.set(Map, mapCloner as any); 147 | DEFAULT_INSTANCE_CLONERS.set(Set, setCloner as any); 148 | DEFAULT_INSTANCE_CLONERS.set(Object, objectCloner as any); 149 | 150 | /** 151 | * Default Array Cloner 152 | */ 153 | function arrayCloner(objectToClone, options: TNonOptionalCloneOptions, internalData) { 154 | return (objectToClone as any).map((val, i, arr) => { 155 | if (options.propsToRefer.indexOf(i) > -1) { 156 | return val; 157 | } 158 | 159 | return cloner(val, options, {...internalData, parent: arr, key: i}); 160 | }) as any; 161 | } 162 | 163 | /** 164 | * Default Map Cloner 165 | */ 166 | function mapCloner(objectToClone: Map, options: TNonOptionalCloneOptions, internalData) { 167 | const newMap = new Map(); 168 | 169 | intersection(options.propsToRefer, Array.from(objectToClone.keys())).forEach(key => { 170 | newMap.set(key, objectToClone.get(key)); 171 | }); 172 | 173 | difference(Array.from(objectToClone.keys()), options.propsToRefer).forEach(key => { 174 | newMap.set(key, cloner(objectToClone.get(key), options, {...internalData, key, parent: newMap})); 175 | }); 176 | 177 | return newMap; 178 | } 179 | 180 | /** 181 | * Default Set Cloner 182 | */ 183 | function setCloner(objectToClone: Set, options: TNonOptionalCloneOptions, internalData) { 184 | return new Set(objectToClone); 185 | } 186 | 187 | /** 188 | * Default Object Cloner 189 | */ 190 | function objectCloner(objectToClone, options: TNonOptionalCloneOptions, internalData) { 191 | const { propsToRefer } = options; 192 | 193 | /** 194 | * New Object Clone 195 | */ 196 | const _clone = {}; 197 | 198 | /** 199 | * Object cloner 200 | * @param key incoming property of object to clone 201 | */ 202 | function cloneObject(key) { 203 | _clone[key] = cloner(objectToClone[key], options, {...internalData, parent: _clone, key}); 204 | } 205 | 206 | const allProperties = [...Object.keys(objectToClone), ...Object.getOwnPropertySymbols(objectToClone)]; 207 | 208 | // Pass unwanted props 209 | intersection(propsToRefer, allProperties).forEach(prop => { 210 | _clone[prop] = objectToClone[prop]; 211 | }); 212 | 213 | // Clone remaining properties 214 | difference(allProperties, propsToRefer).forEach(cloneObject); 215 | 216 | return _clone; 217 | } 218 | 219 | /** 220 | * #### Deep Clone 221 | * 222 | * A function to recursively clone objects, arrays etc. with [cloning options]{@link ICloneOptions} 223 | * 224 | * *References Functions & Symbols by default* 225 | * 226 | * * * * 227 | * Example: 228 | * ```typescript 229 | * import { clone } from "@thalesrc/js-utils/object"; 230 | * 231 | * const object = {a: 1, b: {c: true, d: ["x", "y"]}}; 232 | * 233 | * // Clone all 234 | * const clonedObject = clone(object); 235 | * // {a: 1, b: {c: true, d: ["x", "y"]}} 236 | * // object.b.d === clonedObject.b.d // false 237 | * 238 | * // Clone all but reference "d" 239 | * const clonedObject = clone(object, {propsToRefer: ["d"]}); 240 | * // {a: 1, b: {c: true, d: ["x", "y"]}} 241 | * // object.b.d === clonedObject.b.d // true 242 | * ``` 243 | * 244 | * Static usage example: 245 | * ```typescript 246 | * import "@thalesrc/js-utils/object/static/clone"; 247 | * 248 | * const object = {a: 1, b: 2}; 249 | * const clonedObject = Object.clone(object); // {a: 1, b: 2} 250 | * ``` 251 | * * * * 252 | * @param objectToClone Object to clone 253 | * @param options {ICloneOptions} Cloning Options 254 | * @see {@link ICloneOptions} for more information. 255 | */ 256 | export function clone( 257 | objectToClone: T, 258 | { 259 | instancesToRefer = [], 260 | propsToRefer = [], 261 | valueFiltererToRefer = () => false, 262 | customCloners = new Map() 263 | }: ICloneOptions = {} 264 | ): T { 265 | /** 266 | * Whole options with defaults 267 | */ 268 | const mergedCloners = merge(customCloners, DEFAULT_INSTANCE_CLONERS); 269 | const allOptions: TNonOptionalCloneOptions = {instancesToRefer, propsToRefer, valueFiltererToRefer, customCloners: mergedCloners}; 270 | 271 | const finalizers = []; 272 | const cloned = cloner(objectToClone, allOptions, {clonedObjects: new Map(), finalizers}); 273 | 274 | finalizers.forEach(callback => {callback(); }); 275 | 276 | return cloned; 277 | } 278 | 279 | function cloner( 280 | objectToClone: T, 281 | allOptions: TNonOptionalCloneOptions, 282 | internalData: IClonerInternalData 283 | ): T { 284 | const { instancesToRefer, propsToRefer, valueFiltererToRefer, customCloners } = allOptions; 285 | const { clonedObjects, finalizers, parent, key } = internalData; 286 | 287 | // Pass primitives & functions 288 | if (objectToClone === null || !(objectToClone instanceof Object) || objectToClone instanceof Function) { 289 | return objectToClone; 290 | } 291 | 292 | // Pass unwanted references 293 | if (instancesToRefer.some(instance => objectToClone instanceof instance)) { 294 | return objectToClone; 295 | } 296 | 297 | // Pass filterer results 298 | if (valueFiltererToRefer(objectToClone)) { 299 | return objectToClone; 300 | } 301 | 302 | // If cloned before, skip circular cloning 303 | if (clonedObjects.has(objectToClone)) { 304 | if (clonedObjects.get(objectToClone) === CLONING_IN_PROGRESS) { 305 | finalizers.push(() => { 306 | parent[key] = clonedObjects.get(objectToClone); 307 | }); 308 | return null; 309 | } 310 | return clonedObjects.get(objectToClone) as T; 311 | } 312 | 313 | // Mark object as cloning 314 | clonedObjects.set(objectToClone, CLONING_IN_PROGRESS); 315 | 316 | // Clone 317 | const customCloner = Array.from(customCloners.keys()).find(val => objectToClone instanceof val); 318 | const result = customCloners.get(customCloner)(objectToClone, allOptions, {...internalData}); 319 | clonedObjects.set(objectToClone, result); 320 | return result; 321 | } 322 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @thalesrc/js-utils 2 | Javascript utility functions for web development 3 | 4 | [![travis](https://travis-ci.org/thalesrc/js-utils.svg)](https://travis-ci.org/thalesrc/js-utils) 5 | [![codecov](https://codecov.io/gh/thalesrc/js-utils/branch/master/graph/badge.svg)](https://codecov.io/gh/thalesrc/js-utils) 6 | [![npm](https://img.shields.io/npm/v/@thalesrc/js-utils.svg)](https://www.npmjs.com/package/@thalesrc/js-utils) 7 | [![npm](https://img.shields.io/npm/dw/@thalesrc/js-utils.svg)](https://www.npmjs.com/package/@thalesrc/js-utils) 8 | [![TypeScript](https://badges.frapsoft.com/typescript/version/typescript-next.svg?v=101)](https://github.com/ellerbrock/typescript-badges/) 9 | [![npm](https://img.shields.io/npm/l/@thalesrc/js-utils.svg)](https://github.com/thalesrc/js-utils/blob/master/LICENSE) 10 | [![Join the chat at https://gitter.im/thalesrc_js-utils/Lobby](https://badges.gitter.im/thalesrc_js-utils/Lobby.svg)](https://gitter.im/thalesrc_js-utils/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 11 | 12 | ## Motivation 13 | 14 | Collecting commonly used utility functions in a package. 15 | 16 | ## Goals 17 | 18 | * Typescript support 19 | * Tree-shaking 20 | * No dependencies 21 | * High performance 22 | * Documentation 23 | * Well tested 24 | * Static/Prototype method support 25 | 26 | ## Installation 27 | * npm: `npm install @thalesrc/js-utils --save` 28 | * yarn: `yarn add @thalesrc/js-utils` 29 | 30 | ## Documentation 31 | 32 | See: [thalesrc.github.io/js-utils](https://thalesrc.github.io/js-utils) 33 | 34 | ## Functions 35 | 36 | ### Array 37 | 38 | #### [Async Map](https://thalesrc.github.io/js-utils/modules/_array_async_map_.html) 39 | Maps an array asynchronously 40 | 41 | ```typescript 42 | import { asyncMap } "@thalesrc/js-utils/array"; 43 | 44 | const array = [1, 2, 3]; 45 | 46 | const result = await asyncMap(array, async value => { 47 | return await addOneAfterASecond(value); 48 | }); 49 | 50 | console.log(result); // [2, 3, 4] 51 | ``` 52 | 53 | #### [Compact](https://thalesrc.github.io/js-utils/modules/_array_compact_.html) 54 | Filters falsy values of an array 55 | 56 | ```typescript 57 | import { compact } from "@thalesrc/js-utils/array"; 58 | 59 | const arr = [undefined, "", false, 0, 1, "1", null]; 60 | const compacted = compact(arr); // [1, "1"]; 61 | ``` 62 | 63 | #### [Difference](https://thalesrc.github.io/js-utils/modules/_array_difference_.html) 64 | Gets the difference of the two arrays or sets 65 | 66 | ```typescript 67 | import { difference } from "@thalesrc/js-utils/array"; 68 | 69 | const base = ["a", "b", "c", "d", "a", "b", "c", "d"]; 70 | 71 | difference(base, ["a", "b"]); // ["c", "d", "c", "d"] 72 | difference(base, ["a", "b"], true); // ["c", "d", "a", "b", "c", "d"] 73 | ``` 74 | 75 | #### [Find By Key](https://thalesrc.github.io/js-utils/modules/_array_find_by_key_.html) 76 | Finds an object in an array by matching the value set on the key 77 | 78 | ```typescript 79 | import { findByKey } from "@thalesrc/js-utils/array"; 80 | 81 | const array = [{a: 1}, {a: 2}, {a: 3}]; 82 | 83 | findByKey(array, "a", 2); // {a: 2} 84 | ``` 85 | 86 | #### [Intersection](https://thalesrc.github.io/js-utils/modules/_array_intersection_.html) 87 | Gets the intersection of the two arrays or sets 88 | 89 | ```typescript 90 | import { intersection } from "@thalesrc/js-utils/array"; 91 | 92 | const base = ["a", "b", "c", "d", "a", "b", "c", "d"]; 93 | 94 | intersection(base, ["a", "b", "x"]); // ["a", "b", "a", "b"] 95 | intersection(base, ["a", "b", "x"], false); // ["a", "b"] 96 | ``` 97 | 98 | #### [Limit](https://thalesrc.github.io/js-utils/modules/_array_limit_.html) 99 | Returns first `n` children of an array 100 | 101 | ```typescript 102 | import { limit } from "@thalesrc/js-utils/array"; 103 | 104 | const array = ["a", "b", "c", "d", "e", "f"]; 105 | 106 | limit(array, 3); // ["a", "b", "c"] 107 | ``` 108 | 109 | #### [Remove](https://thalesrc.github.io/js-utils/modules/_array_remove_.html) 110 | Removes an item from an array 111 | 112 | ```typescript 113 | import { remove } from "@thalesrc/js-utils/array"; 114 | 115 | const array = ["a", "b", "c", "a", "b", "c"]; 116 | 117 | remove(array, "b"); // ["a", "c", "a", "b", "c"] 118 | remove(array, "b", true); // ["a", "c", "a", "c"] 119 | ``` 120 | 121 | #### [Replace](https://thalesrc.github.io/js-utils/modules/_array_replace_.html) 122 | Replaces an item with passed one of an array 123 | 124 | ```typescript 125 | import { replace } from "@thalesrc/js-utils/array"; 126 | 127 | const array = ["a", "b", "c", "a", "b", "c"]; 128 | 129 | replace(array, "b", "x"); // ["a", "x", "c", "a", "b", "c"] 130 | replace(array, {startingIndex: 3, deleteCount: 1, itemsToReplace: ['x', 'y']}); // ["a", "b", "c", "x", "y", "b", "c"]; 131 | 132 | const config = new Map(); 133 | config.set("a", "x") 134 | config.set("b", "y"); 135 | 136 | replace(array, {itemsToReplace: config}); // ["x", "y", "c", "a", "b", "c"]; 137 | replace(array, {itemsToReplace: config, multi: true}); // ["x", "y", "c", "x", "y", "c"]; 138 | ``` 139 | 140 | #### [Uniquify](https://thalesrc.github.io/js-utils/modules/_array_uniquify_.html) 141 | Removes repeated items from the array 142 | 143 | ```typescript 144 | import { uniquify } "@thalesrc/js-utils/array"; 145 | 146 | const array = ["a", "b", "c", "a", "b", "c"]; 147 | 148 | uniquify(array); // ["a", "b", "c"] 149 | ``` 150 | 151 | #### [Uniquify By Key](https://thalesrc.github.io/js-utils/modules/_array_uniquify_by_key_.html) 152 | Removes objects from the array which the value of its specifed key included before by another 153 | 154 | ```typescript 155 | import { uniquifyByKey } "@thalesrc/js-utils/array"; 156 | 157 | const array = [{a: 1}, {a: 1}, {a: 2}, {a: 3}, {a: 3}, {a: 4}]; 158 | 159 | uniquifyByKey(array, 'a'); // [{a: 1}, {a: 2}, {a: 3}, {a: 4}] 160 | ``` 161 | 162 | ### Function 163 | 164 | #### [Debounce](https://thalesrc.github.io/js-utils/modules/_function_debounce_.html) 165 | Debounces a function that delays invoking until after configured time have elapsed since the last time the debounced function was invoked 166 | 167 | ```typescript 168 | import { debounce } from "@thalesrc/js-utils/promise"; 169 | 170 | function foo() { 171 | console.log("hello"); 172 | } 173 | 174 | for (let i = 0; i < 10; i++) { 175 | debounce(foo); 176 | } 177 | 178 | // logs "hello" only once 179 | ``` 180 | 181 | #### [Defer](https://thalesrc.github.io/js-utils/modules/_function_defer_.html) 182 | Delays the execution of the passed function 183 | 184 | ```typescript 185 | import { defer } from "@thalesrc/js-utils/function"; 186 | 187 | const result = await defer(() => aFunctionToDeferThatReturnsHello()); 188 | 189 | console.log(result); // 'hello' 190 | ``` 191 | 192 | #### [Noop](https://thalesrc.github.io/js-utils/modules/_function_noop_.html) 193 | Noop function 194 | 195 | ```typescript 196 | import { noop } from "@thalesrc/js-utils/function"; 197 | 198 | noop(); 199 | ``` 200 | 201 | #### [Of](https://thalesrc.github.io/js-utils/modules/_function_of_.html) 202 | Creates a function which returns the specified value 203 | 204 | ```typescript 205 | import { of } from "@thalesrc/js-utils/function"; 206 | 207 | const base = [1, 2, 5, {}, "x", "y"]; 208 | 209 | base.map(of('hi')); // ["hi", "hi", "hi", "hi", "hi", "hi"] 210 | ``` 211 | 212 | ### Map 213 | 214 | #### [Merge](https://thalesrc.github.io/js-utils/modules/_map_merge_.html) 215 | Merges two maps 216 | 217 | ```typescript 218 | import { merge } from "@thalesrc/js-utils/map"; 219 | 220 | const first = new Map(); 221 | first.set("a", 1); 222 | 223 | const second = new Map(); 224 | second.set("b", 2); 225 | merge(first, second); // [{key: "a", value: 1}, {key: "b", value: 2}] 226 | ``` 227 | 228 | ### Math 229 | 230 | #### [Min-Max](https://thalesrc.github.io/js-utils/modules/_math_min_max_.html) 231 | Limits the value by specified range 232 | 233 | ```typescript 234 | import { minMax } from "@thalesrc/js-utils/math"; 235 | 236 | const limitedValue = minMax(200, 300, Math.random() * 1000); // Outputs between 200-300 237 | ``` 238 | 239 | ### Object 240 | 241 | #### [Clone](https://thalesrc.github.io/js-utils/modules/_object_clone_.html) 242 | A function to deep clone anything (recursively) 243 | 244 | ```typescript 245 | import { clone } from "@thalesrc/js-utils/object"; 246 | 247 | const object = {a: 1, b: {c: true, d: ["x", "y"]}}; 248 | 249 | const clonedObject = clone(object); 250 | // {a: 1, b: {c: true, d: ["x", "y"]}} 251 | // object.b.d === clonedObject.b.d // false 252 | ``` 253 | 254 | #### [Compact](https://thalesrc.github.io/js-utils/modules/_object_compact_.html) 255 | Removes `null` and `undefined` values and their keys from an object 256 | 257 | ```typescript 258 | import { compact } from "@thalesrc/js-utils/object"; 259 | 260 | const a = { 261 | x: null, 262 | y: undefined, 263 | z: 20 264 | }; 265 | 266 | compact(a); // {z: 20} 267 | ``` 268 | 269 | #### [Deepest](https://thalesrc.github.io/js-utils/modules/_object_deepest_.html) 270 | Get deepest value in an object chain 271 | 272 | ```typescript 273 | import { deepest } from "@thalesrc/js-utils/object"; 274 | 275 | const a = {x: null}; 276 | const b = {x: a}; 277 | const c = {x: b}; 278 | 279 | deepest(c, 'x'); // {x: null} (a) 280 | ``` 281 | 282 | ### Promise 283 | 284 | #### [Never](https://thalesrc.github.io/js-utils/modules/_promise_never_.html) 285 | A promise which never resolves 286 | 287 | ```typescript 288 | import { never, NEVER } from '@thalesrc/js-utils/promise'; 289 | 290 | function foo(promise = never()) { 291 | promise.then(val => { 292 | ... 293 | }); 294 | } 295 | 296 | // or 297 | 298 | function foo(promise = NEVER) { 299 | promise.then(val => { 300 | ... 301 | }); 302 | } 303 | ``` 304 | 305 | #### [Revert](https://thalesrc.github.io/js-utils/modules/_promise_revert_.html) 306 | Exchanges resolve state with rejection of a promise 307 | 308 | ```typescript 309 | import { revert } from "@thalesrc/js-utils/promise"; 310 | 311 | const errorPromise = Promise.reject(new Error('foo')); 312 | 313 | revert(errorPromise) 314 | .then(err => { 315 | console.log("this will be logged", err); 316 | }) 317 | .catch(res => { 318 | console.log("this won't be logged", res); 319 | }); 320 | ``` 321 | 322 | #### [Timeout](https://thalesrc.github.io/js-utils/modules/_promise_timeout_.html) 323 | Returns a promise which resolves after specified time 324 | 325 | ```typescript 326 | import { timeout } from "@thalesrc/js-utils/promise"; 327 | 328 | timeout(1000) 329 | .then(() => console.log("will be logged after a second")); 330 | ``` 331 | 332 | #### [Try Catch](https://thalesrc.github.io/js-utils/modules/_promise_try_catch_.html) 333 | Merges result and error in the same callback 334 | 335 | ```typescript 336 | import { tryCatch } from "@thalesrc/js-utils/promise"; 337 | 338 | const promise = anAsyncCall(); 339 | 340 | const [error, result] = await tryCatch(promise); 341 | ``` 342 | 343 | #### [Try One By One](https://thalesrc.github.io/js-utils/modules/_promise_try_one_by_one_.html) 344 | Tries a set of promises one by one with given order. Breaks the call when a promise resolved. Otherwise keeps trying incoming promises until the list is finished. 345 | 346 | ```typescript 347 | import { tryOneByOne } from "@thalesrc/js-utils/promise"; 348 | 349 | async function fooFunction() { 350 | const foo = await tryOneByOne([ 351 | () => someCall(), 352 | (err) => anotherCall(), 353 | (err) => fooPromise() 354 | ]); 355 | 356 | // do stuff 357 | } 358 | ``` 359 | 360 | ### String 361 | 362 | #### [Limit](https://thalesrc.github.io/js-utils/modules/_string_limit_.html) 363 | Limits the string to `n` character 364 | 365 | ```typescript 366 | import { limit } from "@thalesrc/js-utils/string"; 367 | 368 | const str = 'foobarbaz'; 369 | 370 | limit(str, 3); // 'foo' 371 | ``` 372 | 373 | ### Etc. 374 | 375 | #### [Arrayize](https://thalesrc.github.io/js-utils/modules/_arrayize_.html) 376 | Encapsulates a non array value with an array that contains it unless the value is already an array 377 | 378 | ```typescript 379 | import { arrayize } from "@thalesrc/js-utils"; 380 | 381 | const foo = 'foo'; 382 | const bar = ['bar']; 383 | const fooArr = arrayize(foo); // ['foo']; 384 | const barArr = arrayize(bar); // ['bar']; 385 | ``` 386 | 387 | #### [Compact](https://thalesrc.github.io/js-utils/modules/_compact_.html) 388 | Filters falsy values of the given array 389 | Removes `null` and `undefined` values and their keys from an object 390 | 391 | ```typescript 392 | import { compact } from "@thalesrc/js-utils"; 393 | 394 | const arr = [undefined, "", false, 0, 1, "1"]; 395 | const compacted = compact(arr); // [1, "1"]; 396 | 397 | const object = { 398 | x: null, 399 | y: undefined, 400 | z: 20 401 | }; 402 | 403 | const compacted = compact(object); // {z: 20} 404 | ``` 405 | 406 | #### [Is Falsy](https://thalesrc.github.io/js-utils/modules/_is_falsy_.html) 407 | Returns whether the entered value is falsy 408 | 409 | ```typescript 410 | import { isFalsy } from "@thalesrc/js-utils"; 411 | 412 | isFalsy(undefined); // true 413 | isFalsy(true); // false 414 | ``` 415 | 416 | #### [Is Truthy](https://thalesrc.github.io/js-utils/modules/_is_truthy_.html) 417 | Returns whether the entered value is truthy 418 | 419 | ```typescript 420 | import { isTruthy } from "@thalesrc/js-utils"; 421 | 422 | isTruthy(undefined); // false 423 | isTruthy(true); // true 424 | ``` 425 | 426 | #### [Limit](https://thalesrc.github.io/js-utils/modules/_limit_.html) 427 | Limits the string or array to `n` character 428 | 429 | ```typescript 430 | import { limit } from "@thalesrc/js-utils"; 431 | 432 | const str = 'foobarbaz'; 433 | const array = ["a", "b", "c", "d", "e", "f"]; 434 | 435 | limit(str, 3); // 'foo' 436 | limit(array, 3); // ["a", "b", "c"] 437 | ``` 438 | 439 | #### [Open Promise](https://thalesrc.github.io/js-utils/modules/_open-promise_.html) 440 | A promise constructor to resolve or reject from outside 441 | 442 | ```typescript 443 | import { OpenPromise } from "@thalesrc/js-utils"; 444 | 445 | const aPromiseWillBeResolvedLater = new OpenPromise(); 446 | 447 | aPromiseWillBeResolvedLater.then(val => console.log(val)); 448 | 449 | aPromiseWillBeResolvedLater.resolve({x: 1}); 450 | // logs `{x: 1}` 451 | ``` 452 | 453 | #### [Smart Map](https://thalesrc.github.io/js-utils/modules/_smart_map_.html) 454 | Like WeakMap but can also store values using primitive keys 455 | 456 | See: [WeakMap](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap) 457 | 458 | ```typescript 459 | import { SmartMap } from "@thalesrc/js-utils"; 460 | 461 | const aMap = new SmartMap(); 462 | 463 | aMap.set("foo", "foo"); 464 | aMap.set(1, "thales rocks"); 465 | console.log(aMap.size) // 2 466 | 467 | aMap.set({}, "thales rocks again"); 468 | console.log(aMap.size) // 2 469 | 470 | const anObject = {}; 471 | aMap.set(anObject, "thales rocks again and again"); 472 | console.log(aMap.size) // 3 473 | console.log(aMap.get(anObject)) // "thales rocks again and again" 474 | ``` 475 | 476 | #### [Unique Id](https://thalesrc.github.io/js-utils/modules/_unique-id_.html) 477 | Starts a new counter for every unique prefix and if a prefix is given, returns the id by prefixing it, otherwise returns the id as number 478 | 479 | ```typescript 480 | import { uniqueId } from "@thalesrc/js-utils"; 481 | 482 | uniqueId(); // 0 483 | uniqueId(); // 1 484 | uniqueId("some-str"); // "some-str-0"; 485 | uniqueId("some-str"); // "some-str-1"; 486 | uniqueId(); // 3 487 | ``` 488 | 489 | ## Static/Prototype Methods 490 | 491 | You may use any of these methods by adding them to the constructors or prototypes to native objects in main file. 492 | 493 | Prototype Example: 494 | 495 | ```typescript 496 | // main.ts 497 | import "@thalesrc/js-utils/array/proto/compact"; 498 | 499 | // somewhere else 500 | const arr = [undefined, "", false, 0, 1, "1", null]; 501 | const compacted = arr.compact(); // [1, "1"]; 502 | 503 | ``` 504 | 505 | Static Example: 506 | 507 | ```typescript 508 | // main.ts 509 | import "@thalesrc/js-utils/promise/static/timeout"; 510 | 511 | // somewhere else 512 | Promise.timeout(1000) 513 | .then(() => console.log("will be logged after a second")); 514 | ``` 515 | 516 | ## Legacy Typescript Support 517 | 518 | Install legacy build 519 | 520 | `npm install @thalesrc/js-utils@legacy --save` 521 | ## License 522 | 523 | [MIT](./LICENSE) --------------------------------------------------------------------------------