From e227fa6691fe0381c9aab86c5d1fcdd121011d9b Mon Sep 17 00:00:00 2001 From: ssube Date: Sun, 29 Mar 2020 08:43:52 -0500 Subject: [PATCH] feat: add utils from isolex --- .gitlab-ci.yml | 2 +- config/eslint.json | 1 - config/rollup-named.json | 6 + docs/api/index.md | 2 +- .../api/js-utils.arraymapper._constructor_.md | 20 ++ docs/api/js-utils.arraymapper.map.md | 22 +++ docs/api/js-utils.arraymapper.md | 32 +++ docs/api/js-utils.arraymapper.rest.md | 11 ++ docs/api/js-utils.arraymapper.skip.md | 11 ++ docs/api/js-utils.arraymapper.take.md | 11 ++ .../js-utils.asynctracker._constructor_.md | 13 ++ docs/api/js-utils.asynctracker.clear.md | 15 ++ docs/api/js-utils.asynctracker.disable.md | 15 ++ docs/api/js-utils.asynctracker.dump.md | 15 ++ docs/api/js-utils.asynctracker.enable.md | 15 ++ docs/api/js-utils.asynctracker.getstack.md | 15 ++ docs/api/js-utils.asynctracker.md | 38 ++++ docs/api/js-utils.asynctracker.size.md | 11 ++ ...s-utils.childprocesserror._constructor_.md | 21 ++ docs/api/js-utils.childprocesserror.md | 18 ++ docs/api/js-utils.concat.md | 22 +++ docs/api/js-utils.countof.md | 26 +++ docs/api/js-utils.defaultwhen.md | 23 +++ docs/api/js-utils.defer.md | 25 +++ docs/api/js-utils.dict.md | 11 ++ docs/api/js-utils.doesexist.md | 24 +++ docs/api/js-utils.encode.md | 23 +++ docs/api/js-utils.entriesof.md | 22 +++ docs/api/js-utils.externalmodule.data.md | 11 ++ docs/api/js-utils.externalmodule.export.md | 11 ++ docs/api/js-utils.externalmodule.md | 20 ++ docs/api/js-utils.externalmodule.require.md | 11 ++ docs/api/js-utils.filternil.md | 24 +++ docs/api/js-utils.gettestlogger.md | 22 +++ ...tils.invalidargumenterror._constructor_.md | 21 ++ docs/api/js-utils.invalidargumenterror.md | 18 ++ docs/api/js-utils.isdebug.md | 15 ++ docs/api/js-utils.isnil.md | 22 +++ docs/api/js-utils.makedict.md | 24 +++ docs/api/js-utils.makemap.md | 24 +++ docs/api/js-utils.maplike.md | 13 ++ docs/api/js-utils.md | 75 +++++++ docs/api/js-utils.mergelist.md | 24 +++ docs/api/js-utils.mergemap.md | 23 +++ .../js-utils.missingkeyerror._constructor_.md | 21 ++ docs/api/js-utils.missingkeyerror.md | 18 ++ docs/api/js-utils.modulector.md | 11 ++ docs/api/js-utils.mustcoalesce.md | 24 +++ docs/api/js-utils.mustexist.md | 30 +++ docs/api/js-utils.mustfind.md | 25 +++ docs/api/js-utils.mustget.md | 25 +++ docs/api/js-utils.nil.md | 13 ++ .../js-utils.notfounderror._constructor_.md | 21 ++ docs/api/js-utils.notfounderror.md | 18 ++ ...utils.notimplementederror._constructor_.md | 21 ++ docs/api/js-utils.notimplementederror.md | 18 ++ docs/api/js-utils.optional.md | 13 ++ docs/api/js-utils.pushmergemap.md | 22 +++ docs/api/js-utils.removepid.md | 22 +++ docs/api/js-utils.setorpush.md | 26 +++ docs/api/js-utils.signal.md | 22 +++ docs/api/js-utils.signal_reload.md | 11 ++ docs/api/js-utils.signal_reset.md | 11 ++ docs/api/js-utils.signal_stop.md | 11 ++ docs/api/js-utils.spylogger.md | 22 +++ docs/api/js-utils.timeout.md | 25 +++ .../js-utils.timeouterror._constructor_.md | 21 ++ docs/api/js-utils.timeouterror.md | 18 ++ docs/api/js-utils.waitforchild.md | 22 +++ docs/api/js-utils.writepid.md | 22 +++ docs/api/rollup-template.md | 6 - package.json | 3 +- src/app.ts | 4 +- src/entity/README.md | 3 - src/error/ChildProcessError.ts | 7 + src/error/InvalidArgumentError.ts | 7 + src/error/MissingKeyError.ts | 7 + src/error/NotFoundError.ts | 7 + src/error/NotImplementedError.ts | 7 + src/error/TimeoutError.ts | 7 + src/index.ts | 71 ++++++- src/lib.ts | 5 - src/migration/README.md | 3 - src/module/README.md | 3 - src/resource/json.json | 3 - src/resource/yaml.yml | 1 - src/test/AsyncTracker.ts | 79 ++++++++ src/test/Logger.ts | 19 ++ src/utils/ArrayMapper.ts | 41 ++++ src/utils/Async.ts | 38 ++++ src/utils/Buffer.ts | 12 ++ src/utils/Child.ts | 84 ++++++++ src/utils/Env.ts | 3 + src/utils/ExternalModule.ts | 14 ++ src/utils/Map.ts | 185 ++++++++++++++++++ src/utils/PidFile.ts | 36 ++++ src/utils/Reflect.ts | 36 ++++ src/utils/Signal.ts | 18 ++ src/utils/String.ts | 23 +++ src/utils/index.ts | 119 +++++++++++ test/TestHelpers.ts | 6 + test/error/TestError.ts | 41 ++++ test/helpers/async.ts | 98 +--------- test/utils/TestArrayMapper.ts | 42 ++++ test/utils/TestMap.ts | 143 ++++++++++++++ test/utils/TestString.ts | 41 ++++ test/utils/TestUtils.ts | 45 +++++ yarn.lock | 15 ++ 108 files changed, 2491 insertions(+), 132 deletions(-) create mode 100644 docs/api/js-utils.arraymapper._constructor_.md create mode 100644 docs/api/js-utils.arraymapper.map.md create mode 100644 docs/api/js-utils.arraymapper.md create mode 100644 docs/api/js-utils.arraymapper.rest.md create mode 100644 docs/api/js-utils.arraymapper.skip.md create mode 100644 docs/api/js-utils.arraymapper.take.md create mode 100644 docs/api/js-utils.asynctracker._constructor_.md create mode 100644 docs/api/js-utils.asynctracker.clear.md create mode 100644 docs/api/js-utils.asynctracker.disable.md create mode 100644 docs/api/js-utils.asynctracker.dump.md create mode 100644 docs/api/js-utils.asynctracker.enable.md create mode 100644 docs/api/js-utils.asynctracker.getstack.md create mode 100644 docs/api/js-utils.asynctracker.md create mode 100644 docs/api/js-utils.asynctracker.size.md create mode 100644 docs/api/js-utils.childprocesserror._constructor_.md create mode 100644 docs/api/js-utils.childprocesserror.md create mode 100644 docs/api/js-utils.concat.md create mode 100644 docs/api/js-utils.countof.md create mode 100644 docs/api/js-utils.defaultwhen.md create mode 100644 docs/api/js-utils.defer.md create mode 100644 docs/api/js-utils.dict.md create mode 100644 docs/api/js-utils.doesexist.md create mode 100644 docs/api/js-utils.encode.md create mode 100644 docs/api/js-utils.entriesof.md create mode 100644 docs/api/js-utils.externalmodule.data.md create mode 100644 docs/api/js-utils.externalmodule.export.md create mode 100644 docs/api/js-utils.externalmodule.md create mode 100644 docs/api/js-utils.externalmodule.require.md create mode 100644 docs/api/js-utils.filternil.md create mode 100644 docs/api/js-utils.gettestlogger.md create mode 100644 docs/api/js-utils.invalidargumenterror._constructor_.md create mode 100644 docs/api/js-utils.invalidargumenterror.md create mode 100644 docs/api/js-utils.isdebug.md create mode 100644 docs/api/js-utils.isnil.md create mode 100644 docs/api/js-utils.makedict.md create mode 100644 docs/api/js-utils.makemap.md create mode 100644 docs/api/js-utils.maplike.md create mode 100644 docs/api/js-utils.md create mode 100644 docs/api/js-utils.mergelist.md create mode 100644 docs/api/js-utils.mergemap.md create mode 100644 docs/api/js-utils.missingkeyerror._constructor_.md create mode 100644 docs/api/js-utils.missingkeyerror.md create mode 100644 docs/api/js-utils.modulector.md create mode 100644 docs/api/js-utils.mustcoalesce.md create mode 100644 docs/api/js-utils.mustexist.md create mode 100644 docs/api/js-utils.mustfind.md create mode 100644 docs/api/js-utils.mustget.md create mode 100644 docs/api/js-utils.nil.md create mode 100644 docs/api/js-utils.notfounderror._constructor_.md create mode 100644 docs/api/js-utils.notfounderror.md create mode 100644 docs/api/js-utils.notimplementederror._constructor_.md create mode 100644 docs/api/js-utils.notimplementederror.md create mode 100644 docs/api/js-utils.optional.md create mode 100644 docs/api/js-utils.pushmergemap.md create mode 100644 docs/api/js-utils.removepid.md create mode 100644 docs/api/js-utils.setorpush.md create mode 100644 docs/api/js-utils.signal.md create mode 100644 docs/api/js-utils.signal_reload.md create mode 100644 docs/api/js-utils.signal_reset.md create mode 100644 docs/api/js-utils.signal_stop.md create mode 100644 docs/api/js-utils.spylogger.md create mode 100644 docs/api/js-utils.timeout.md create mode 100644 docs/api/js-utils.timeouterror._constructor_.md create mode 100644 docs/api/js-utils.timeouterror.md create mode 100644 docs/api/js-utils.waitforchild.md create mode 100644 docs/api/js-utils.writepid.md delete mode 100644 docs/api/rollup-template.md delete mode 100644 src/entity/README.md create mode 100644 src/error/ChildProcessError.ts create mode 100644 src/error/InvalidArgumentError.ts create mode 100644 src/error/MissingKeyError.ts create mode 100644 src/error/NotFoundError.ts create mode 100644 src/error/NotImplementedError.ts create mode 100644 src/error/TimeoutError.ts delete mode 100644 src/lib.ts delete mode 100644 src/migration/README.md delete mode 100644 src/module/README.md delete mode 100644 src/resource/json.json delete mode 100644 src/resource/yaml.yml create mode 100644 src/test/AsyncTracker.ts create mode 100644 src/test/Logger.ts create mode 100644 src/utils/ArrayMapper.ts create mode 100644 src/utils/Async.ts create mode 100644 src/utils/Buffer.ts create mode 100644 src/utils/Child.ts create mode 100644 src/utils/Env.ts create mode 100644 src/utils/ExternalModule.ts create mode 100644 src/utils/Map.ts create mode 100644 src/utils/PidFile.ts create mode 100644 src/utils/Reflect.ts create mode 100644 src/utils/Signal.ts create mode 100644 src/utils/String.ts create mode 100644 src/utils/index.ts create mode 100644 test/TestHelpers.ts create mode 100644 test/error/TestError.ts create mode 100644 test/utils/TestArrayMapper.ts create mode 100644 test/utils/TestMap.ts create mode 100644 test/utils/TestString.ts create mode 100644 test/utils/TestUtils.ts diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c8b671a..7f723bb 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -150,7 +150,7 @@ sonar-success: script: - make node_modules - sonar-scanner - -Dsonar.projectKey=ssube_rollup-template + -Dsonar.projectKey=ssube_js-utils -Dsonar.projectVersion=${CI_COMMIT_REF_SLUG} -Dsonar.organization=ssube-github -Dsonar.sources=src/,test/ diff --git a/config/eslint.json b/config/eslint.json index a388976..dc1c719 100644 --- a/config/eslint.json +++ b/config/eslint.json @@ -154,7 +154,6 @@ "error", "any", "Number", - "String", "Boolean", "Undefined" ], diff --git a/config/rollup-named.json b/config/rollup-named.json index b31da20..be1d2ca 100644 --- a/config/rollup-named.json +++ b/config/rollup-named.json @@ -2,5 +2,11 @@ "node_modules/chai/index.js": [ "expect", "use" + ], + "node_modules/lodash/lodash.js": [ + "isFunction", + "isMap", + "isObject", + "isString" ] } \ No newline at end of file diff --git a/docs/api/index.md b/docs/api/index.md index 5c5b582..e4b0089 100644 --- a/docs/api/index.md +++ b/docs/api/index.md @@ -8,5 +8,5 @@ | Package | Description | | --- | --- | -| [@apextoaster/rollup-template](./rollup-template.md) | | +| [@apextoaster/js-utils](./js-utils.md) | | diff --git a/docs/api/js-utils.arraymapper._constructor_.md b/docs/api/js-utils.arraymapper._constructor_.md new file mode 100644 index 0000000..92a0fc9 --- /dev/null +++ b/docs/api/js-utils.arraymapper._constructor_.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [ArrayMapper](./js-utils.arraymapper.md) > [(constructor)](./js-utils.arraymapper._constructor_.md) + +## ArrayMapper.(constructor) + +Constructs a new instance of the `ArrayMapper` class + +Signature: + +```typescript +constructor(options: ArrayMapperOptions); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| options | ArrayMapperOptions | | + diff --git a/docs/api/js-utils.arraymapper.map.md b/docs/api/js-utils.arraymapper.map.md new file mode 100644 index 0000000..a7b672c --- /dev/null +++ b/docs/api/js-utils.arraymapper.map.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [ArrayMapper](./js-utils.arraymapper.md) > [map](./js-utils.arraymapper.map.md) + +## ArrayMapper.map() method + +Signature: + +```typescript +map(input: Array): Map>; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| input | Array<string> | | + +Returns: + +`Map>` + diff --git a/docs/api/js-utils.arraymapper.md b/docs/api/js-utils.arraymapper.md new file mode 100644 index 0000000..552a004 --- /dev/null +++ b/docs/api/js-utils.arraymapper.md @@ -0,0 +1,32 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [ArrayMapper](./js-utils.arraymapper.md) + +## ArrayMapper class + +Signature: + +```typescript +export declare class ArrayMapper +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(options)](./js-utils.arraymapper._constructor_.md) | | Constructs a new instance of the ArrayMapper class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [rest](./js-utils.arraymapper.rest.md) | | string | | +| [skip](./js-utils.arraymapper.skip.md) | | number | | +| [take](./js-utils.arraymapper.take.md) | | Array<string> | | + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [map(input)](./js-utils.arraymapper.map.md) | | | + diff --git a/docs/api/js-utils.arraymapper.rest.md b/docs/api/js-utils.arraymapper.rest.md new file mode 100644 index 0000000..08464b6 --- /dev/null +++ b/docs/api/js-utils.arraymapper.rest.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [ArrayMapper](./js-utils.arraymapper.md) > [rest](./js-utils.arraymapper.rest.md) + +## ArrayMapper.rest property + +Signature: + +```typescript +readonly rest: string; +``` diff --git a/docs/api/js-utils.arraymapper.skip.md b/docs/api/js-utils.arraymapper.skip.md new file mode 100644 index 0000000..ad04011 --- /dev/null +++ b/docs/api/js-utils.arraymapper.skip.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [ArrayMapper](./js-utils.arraymapper.md) > [skip](./js-utils.arraymapper.skip.md) + +## ArrayMapper.skip property + +Signature: + +```typescript +readonly skip: number; +``` diff --git a/docs/api/js-utils.arraymapper.take.md b/docs/api/js-utils.arraymapper.take.md new file mode 100644 index 0000000..9b59ac5 --- /dev/null +++ b/docs/api/js-utils.arraymapper.take.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [ArrayMapper](./js-utils.arraymapper.md) > [take](./js-utils.arraymapper.take.md) + +## ArrayMapper.take property + +Signature: + +```typescript +readonly take: Array; +``` diff --git a/docs/api/js-utils.asynctracker._constructor_.md b/docs/api/js-utils.asynctracker._constructor_.md new file mode 100644 index 0000000..7e179fe --- /dev/null +++ b/docs/api/js-utils.asynctracker._constructor_.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [AsyncTracker](./js-utils.asynctracker.md) > [(constructor)](./js-utils.asynctracker._constructor_.md) + +## AsyncTracker.(constructor) + +Constructs a new instance of the `AsyncTracker` class + +Signature: + +```typescript +constructor(); +``` diff --git a/docs/api/js-utils.asynctracker.clear.md b/docs/api/js-utils.asynctracker.clear.md new file mode 100644 index 0000000..f223a02 --- /dev/null +++ b/docs/api/js-utils.asynctracker.clear.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [AsyncTracker](./js-utils.asynctracker.md) > [clear](./js-utils.asynctracker.clear.md) + +## AsyncTracker.clear() method + +Signature: + +```typescript +clear(): void; +``` +Returns: + +`void` + diff --git a/docs/api/js-utils.asynctracker.disable.md b/docs/api/js-utils.asynctracker.disable.md new file mode 100644 index 0000000..92a1591 --- /dev/null +++ b/docs/api/js-utils.asynctracker.disable.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [AsyncTracker](./js-utils.asynctracker.md) > [disable](./js-utils.asynctracker.disable.md) + +## AsyncTracker.disable() method + +Signature: + +```typescript +disable(): void; +``` +Returns: + +`void` + diff --git a/docs/api/js-utils.asynctracker.dump.md b/docs/api/js-utils.asynctracker.dump.md new file mode 100644 index 0000000..16e432a --- /dev/null +++ b/docs/api/js-utils.asynctracker.dump.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [AsyncTracker](./js-utils.asynctracker.md) > [dump](./js-utils.asynctracker.dump.md) + +## AsyncTracker.dump() method + +Signature: + +```typescript +dump(): void; +``` +Returns: + +`void` + diff --git a/docs/api/js-utils.asynctracker.enable.md b/docs/api/js-utils.asynctracker.enable.md new file mode 100644 index 0000000..1c0afb6 --- /dev/null +++ b/docs/api/js-utils.asynctracker.enable.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [AsyncTracker](./js-utils.asynctracker.md) > [enable](./js-utils.asynctracker.enable.md) + +## AsyncTracker.enable() method + +Signature: + +```typescript +enable(): void; +``` +Returns: + +`void` + diff --git a/docs/api/js-utils.asynctracker.getstack.md b/docs/api/js-utils.asynctracker.getstack.md new file mode 100644 index 0000000..844364b --- /dev/null +++ b/docs/api/js-utils.asynctracker.getstack.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [AsyncTracker](./js-utils.asynctracker.md) > [getStack](./js-utils.asynctracker.getstack.md) + +## AsyncTracker.getStack() method + +Signature: + +```typescript +static getStack(): string; +``` +Returns: + +`string` + diff --git a/docs/api/js-utils.asynctracker.md b/docs/api/js-utils.asynctracker.md new file mode 100644 index 0000000..fa23d30 --- /dev/null +++ b/docs/api/js-utils.asynctracker.md @@ -0,0 +1,38 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [AsyncTracker](./js-utils.asynctracker.md) + +## AsyncTracker class + +Async resource tracker using node's internal hooks. + +This probably won't work in a browser. It does not hold references to the resource, to avoid leaks. Adapted from https://gist.github.com/boneskull/7fe75b63d613fa940db7ec990a5f5843\#file-async-dump-js + +Signature: + +```typescript +export declare class AsyncTracker +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)()](./js-utils.asynctracker._constructor_.md) | | Constructs a new instance of the AsyncTracker class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [size](./js-utils.asynctracker.size.md) | | number | | + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [clear()](./js-utils.asynctracker.clear.md) | | | +| [disable()](./js-utils.asynctracker.disable.md) | | | +| [dump()](./js-utils.asynctracker.dump.md) | | | +| [enable()](./js-utils.asynctracker.enable.md) | | | +| [getStack()](./js-utils.asynctracker.getstack.md) | static | | + diff --git a/docs/api/js-utils.asynctracker.size.md b/docs/api/js-utils.asynctracker.size.md new file mode 100644 index 0000000..f5c8b8a --- /dev/null +++ b/docs/api/js-utils.asynctracker.size.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [AsyncTracker](./js-utils.asynctracker.md) > [size](./js-utils.asynctracker.size.md) + +## AsyncTracker.size property + +Signature: + +```typescript +get size(): number; +``` diff --git a/docs/api/js-utils.childprocesserror._constructor_.md b/docs/api/js-utils.childprocesserror._constructor_.md new file mode 100644 index 0000000..30a0db3 --- /dev/null +++ b/docs/api/js-utils.childprocesserror._constructor_.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [ChildProcessError](./js-utils.childprocesserror.md) > [(constructor)](./js-utils.childprocesserror._constructor_.md) + +## ChildProcessError.(constructor) + +Constructs a new instance of the `ChildProcessError` class + +Signature: + +```typescript +constructor(msg?: string, ...nested: Array); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| msg | string | | +| nested | Array<Error> | | + diff --git a/docs/api/js-utils.childprocesserror.md b/docs/api/js-utils.childprocesserror.md new file mode 100644 index 0000000..73e40eb --- /dev/null +++ b/docs/api/js-utils.childprocesserror.md @@ -0,0 +1,18 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [ChildProcessError](./js-utils.childprocesserror.md) + +## ChildProcessError class + +Signature: + +```typescript +export declare class ChildProcessError extends BaseError +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(msg, nested)](./js-utils.childprocesserror._constructor_.md) | | Constructs a new instance of the ChildProcessError class | + diff --git a/docs/api/js-utils.concat.md b/docs/api/js-utils.concat.md new file mode 100644 index 0000000..b3dc778 --- /dev/null +++ b/docs/api/js-utils.concat.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [concat](./js-utils.concat.md) + +## concat() function + +Signature: + +```typescript +export declare function concat(chunks: Array): Buffer; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| chunks | Array<Buffer> | | + +Returns: + +`Buffer` + diff --git a/docs/api/js-utils.countof.md b/docs/api/js-utils.countof.md new file mode 100644 index 0000000..8e29555 --- /dev/null +++ b/docs/api/js-utils.countof.md @@ -0,0 +1,26 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [countOf](./js-utils.countof.md) + +## countOf() function + +Calculate the "length" of an array or value. + +Arrays return their length, single values return 1, and nil values return 0. This counts the number of elements that setOrPush would add. + +Signature: + +```typescript +export declare function countOf(val: unknown): number; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| val | unknown | | + +Returns: + +`number` + diff --git a/docs/api/js-utils.defaultwhen.md b/docs/api/js-utils.defaultwhen.md new file mode 100644 index 0000000..789c455 --- /dev/null +++ b/docs/api/js-utils.defaultwhen.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [defaultWhen](./js-utils.defaultwhen.md) + +## defaultWhen() function + +Signature: + +```typescript +export declare function defaultWhen(condition: boolean, ...items: Array): TVal; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| condition | boolean | | +| items | Array<TVal> | | + +Returns: + +`TVal` + diff --git a/docs/api/js-utils.defer.md b/docs/api/js-utils.defer.md new file mode 100644 index 0000000..469f26f --- /dev/null +++ b/docs/api/js-utils.defer.md @@ -0,0 +1,25 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [defer](./js-utils.defer.md) + +## defer() function + +Resolve after a set amount of time. + +Signature: + +```typescript +export declare function defer(ms: number, val?: T): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| ms | number | | +| val | T | | + +Returns: + +`Promise` + diff --git a/docs/api/js-utils.dict.md b/docs/api/js-utils.dict.md new file mode 100644 index 0000000..985d78b --- /dev/null +++ b/docs/api/js-utils.dict.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [Dict](./js-utils.dict.md) + +## Dict interface + +Signature: + +```typescript +export interface Dict +``` diff --git a/docs/api/js-utils.doesexist.md b/docs/api/js-utils.doesexist.md new file mode 100644 index 0000000..115b812 --- /dev/null +++ b/docs/api/js-utils.doesexist.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [doesExist](./js-utils.doesexist.md) + +## doesExist() function + +Check if a variable is not nil. + +Signature: + +```typescript +export declare function doesExist(val: Optional): val is T; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| val | Optional<T> | | + +Returns: + +`val is T` + diff --git a/docs/api/js-utils.encode.md b/docs/api/js-utils.encode.md new file mode 100644 index 0000000..a69a62c --- /dev/null +++ b/docs/api/js-utils.encode.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [encode](./js-utils.encode.md) + +## encode() function + +Signature: + +```typescript +export declare function encode(chunks: Array, encoding: string): string; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| chunks | Array<Buffer> | | +| encoding | string | | + +Returns: + +`string` + diff --git a/docs/api/js-utils.entriesof.md b/docs/api/js-utils.entriesof.md new file mode 100644 index 0000000..75c899e --- /dev/null +++ b/docs/api/js-utils.entriesof.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [entriesOf](./js-utils.entriesof.md) + +## entriesOf() function + +Signature: + +```typescript +export declare function entriesOf(map: Optional>): Array<[string, TVal]>; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| map | Optional<MapLike<TVal>> | | + +Returns: + +`Array<[string, TVal]>` + diff --git a/docs/api/js-utils.externalmodule.data.md b/docs/api/js-utils.externalmodule.data.md new file mode 100644 index 0000000..8cc84dc --- /dev/null +++ b/docs/api/js-utils.externalmodule.data.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [ExternalModule](./js-utils.externalmodule.md) > [data](./js-utils.externalmodule.data.md) + +## ExternalModule.data property + +Signature: + +```typescript +data?: unknown; +``` diff --git a/docs/api/js-utils.externalmodule.export.md b/docs/api/js-utils.externalmodule.export.md new file mode 100644 index 0000000..7719542 --- /dev/null +++ b/docs/api/js-utils.externalmodule.export.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [ExternalModule](./js-utils.externalmodule.md) > [export](./js-utils.externalmodule.export.md) + +## ExternalModule.export property + +Signature: + +```typescript +export: string; +``` diff --git a/docs/api/js-utils.externalmodule.md b/docs/api/js-utils.externalmodule.md new file mode 100644 index 0000000..5910888 --- /dev/null +++ b/docs/api/js-utils.externalmodule.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [ExternalModule](./js-utils.externalmodule.md) + +## ExternalModule interface + +Signature: + +```typescript +export interface ExternalModule +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [data](./js-utils.externalmodule.data.md) | unknown | | +| [export](./js-utils.externalmodule.export.md) | string | | +| [require](./js-utils.externalmodule.require.md) | string | | + diff --git a/docs/api/js-utils.externalmodule.require.md b/docs/api/js-utils.externalmodule.require.md new file mode 100644 index 0000000..a499da4 --- /dev/null +++ b/docs/api/js-utils.externalmodule.require.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [ExternalModule](./js-utils.externalmodule.md) > [require](./js-utils.externalmodule.require.md) + +## ExternalModule.require property + +Signature: + +```typescript +require: string; +``` diff --git a/docs/api/js-utils.filternil.md b/docs/api/js-utils.filternil.md new file mode 100644 index 0000000..fdc6b27 --- /dev/null +++ b/docs/api/js-utils.filternil.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [filterNil](./js-utils.filternil.md) + +## filterNil() function + +Remove any null or undefined items from the list. + +Signature: + +```typescript +export declare function filterNil(list: ArrayLike>): Array; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| list | ArrayLike<Optional<TItem>> | | + +Returns: + +`Array` + diff --git a/docs/api/js-utils.gettestlogger.md b/docs/api/js-utils.gettestlogger.md new file mode 100644 index 0000000..729bfbf --- /dev/null +++ b/docs/api/js-utils.gettestlogger.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [getTestLogger](./js-utils.gettestlogger.md) + +## getTestLogger() function + +Signature: + +```typescript +export declare function getTestLogger(verbose?: boolean): Logger; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| verbose | boolean | | + +Returns: + +`Logger` + diff --git a/docs/api/js-utils.invalidargumenterror._constructor_.md b/docs/api/js-utils.invalidargumenterror._constructor_.md new file mode 100644 index 0000000..8d3d12b --- /dev/null +++ b/docs/api/js-utils.invalidargumenterror._constructor_.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [InvalidArgumentError](./js-utils.invalidargumenterror.md) > [(constructor)](./js-utils.invalidargumenterror._constructor_.md) + +## InvalidArgumentError.(constructor) + +Constructs a new instance of the `InvalidArgumentError` class + +Signature: + +```typescript +constructor(msg?: string, ...nested: Array); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| msg | string | | +| nested | Array<Error> | | + diff --git a/docs/api/js-utils.invalidargumenterror.md b/docs/api/js-utils.invalidargumenterror.md new file mode 100644 index 0000000..a4f9056 --- /dev/null +++ b/docs/api/js-utils.invalidargumenterror.md @@ -0,0 +1,18 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [InvalidArgumentError](./js-utils.invalidargumenterror.md) + +## InvalidArgumentError class + +Signature: + +```typescript +export declare class InvalidArgumentError extends BaseError +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(msg, nested)](./js-utils.invalidargumenterror._constructor_.md) | | Constructs a new instance of the InvalidArgumentError class | + diff --git a/docs/api/js-utils.isdebug.md b/docs/api/js-utils.isdebug.md new file mode 100644 index 0000000..d9a0305 --- /dev/null +++ b/docs/api/js-utils.isdebug.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [isDebug](./js-utils.isdebug.md) + +## isDebug() function + +Signature: + +```typescript +export declare function isDebug(): boolean; +``` +Returns: + +`boolean` + diff --git a/docs/api/js-utils.isnil.md b/docs/api/js-utils.isnil.md new file mode 100644 index 0000000..e8e4a14 --- /dev/null +++ b/docs/api/js-utils.isnil.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [isNil](./js-utils.isnil.md) + +## isNil() function + +Signature: + +```typescript +export declare function isNil(val: Optional): val is Nil; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| val | Optional<T> | | + +Returns: + +`val is Nil` + diff --git a/docs/api/js-utils.makedict.md b/docs/api/js-utils.makedict.md new file mode 100644 index 0000000..1336a84 --- /dev/null +++ b/docs/api/js-utils.makedict.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [makeDict](./js-utils.makedict.md) + +## makeDict() function + +Turns a map or dict into a dict + +Signature: + +```typescript +export declare function makeDict(map: Optional>): Dict; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| map | Optional<MapLike<TVal>> | | + +Returns: + +`Dict` + diff --git a/docs/api/js-utils.makemap.md b/docs/api/js-utils.makemap.md new file mode 100644 index 0000000..0749acd --- /dev/null +++ b/docs/api/js-utils.makemap.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [makeMap](./js-utils.makemap.md) + +## makeMap() function + +Clone a map or map-like object into a new map. + +Signature: + +```typescript +export declare function makeMap(val: Optional>): Map; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| val | Optional<MapLike<TVal>> | | + +Returns: + +`Map` + diff --git a/docs/api/js-utils.maplike.md b/docs/api/js-utils.maplike.md new file mode 100644 index 0000000..ec100a5 --- /dev/null +++ b/docs/api/js-utils.maplike.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [MapLike](./js-utils.maplike.md) + +## MapLike type + +A `Map` or dictionary object with string keys and `TVal` values. + +Signature: + +```typescript +export declare type MapLike = Map | Dict; +``` diff --git a/docs/api/js-utils.md b/docs/api/js-utils.md new file mode 100644 index 0000000..6369b93 --- /dev/null +++ b/docs/api/js-utils.md @@ -0,0 +1,75 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) + +## js-utils package + +## Classes + +| Class | Description | +| --- | --- | +| [ArrayMapper](./js-utils.arraymapper.md) | | +| [AsyncTracker](./js-utils.asynctracker.md) | Async resource tracker using node's internal hooks.This probably won't work in a browser. It does not hold references to the resource, to avoid leaks. Adapted from https://gist.github.com/boneskull/7fe75b63d613fa940db7ec990a5f5843\#file-async-dump-js | +| [ChildProcessError](./js-utils.childprocesserror.md) | | +| [InvalidArgumentError](./js-utils.invalidargumenterror.md) | | +| [MissingKeyError](./js-utils.missingkeyerror.md) | | +| [NotFoundError](./js-utils.notfounderror.md) | | +| [NotImplementedError](./js-utils.notimplementederror.md) | | +| [TimeoutError](./js-utils.timeouterror.md) | | + +## Functions + +| Function | Description | +| --- | --- | +| [concat(chunks)](./js-utils.concat.md) | | +| [countOf(val)](./js-utils.countof.md) | Calculate the "length" of an array or value.Arrays return their length, single values return 1, and nil values return 0. This counts the number of elements that setOrPush would add. | +| [defaultWhen(condition, items)](./js-utils.defaultwhen.md) | | +| [defer(ms, val)](./js-utils.defer.md) | Resolve after a set amount of time. | +| [doesExist(val)](./js-utils.doesexist.md) | Check if a variable is not nil. | +| [encode(chunks, encoding)](./js-utils.encode.md) | | +| [entriesOf(map)](./js-utils.entriesof.md) | | +| [filterNil(list)](./js-utils.filternil.md) | Remove any null or undefined items from the list. | +| [getTestLogger(verbose)](./js-utils.gettestlogger.md) | | +| [isDebug()](./js-utils.isdebug.md) | | +| [isNil(val)](./js-utils.isnil.md) | | +| [makeDict(map)](./js-utils.makedict.md) | Turns a map or dict into a dict | +| [makeMap(val)](./js-utils.makemap.md) | Clone a map or map-like object into a new map. | +| [mergeList(parts)](./js-utils.mergelist.md) | Merge arguments, which may or may not be arrays, into one return that is definitely an array. | +| [mergeMap(target, source)](./js-utils.mergemap.md) | | +| [mustCoalesce(values)](./js-utils.mustcoalesce.md) | Return the first value that is not nil. | +| [mustExist(val)](./js-utils.mustexist.md) | Assert that a variable is not nil and return the value. | +| [mustFind(list, predicate)](./js-utils.mustfind.md) | Find a value matching the given predicate or throw. | +| [mustGet(map, key)](./js-utils.mustget.md) | Get an element from a Map and guard against nil values. | +| [pushMergeMap(args)](./js-utils.pushmergemap.md) | | +| [removePid(path)](./js-utils.removepid.md) | | +| [setOrPush(map, key, val)](./js-utils.setorpush.md) | Set a map key to a new array or push to the existing value. | +| [signal(signals)](./js-utils.signal.md) | | +| [spyLogger(spies)](./js-utils.spylogger.md) | | +| [timeout(ms, oper)](./js-utils.timeout.md) | Reject after a set amount of time if the original promise has not yet resolved. | +| [waitForChild(child)](./js-utils.waitforchild.md) | | +| [writePid(path)](./js-utils.writepid.md) | | + +## Interfaces + +| Interface | Description | +| --- | --- | +| [Dict](./js-utils.dict.md) | | +| [ExternalModule](./js-utils.externalmodule.md) | | + +## Variables + +| Variable | Description | +| --- | --- | +| [SIGNAL\_RELOAD](./js-utils.signal_reload.md) | | +| [SIGNAL\_RESET](./js-utils.signal_reset.md) | | +| [SIGNAL\_STOP](./js-utils.signal_stop.md) | | + +## Type Aliases + +| Type Alias | Description | +| --- | --- | +| [MapLike](./js-utils.maplike.md) | A Map or dictionary object with string keys and TVal values. | +| [ModuleCtor](./js-utils.modulector.md) | | +| [Nil](./js-utils.nil.md) | Unset value. | +| [Optional](./js-utils.optional.md) | Value that may be nil. | + diff --git a/docs/api/js-utils.mergelist.md b/docs/api/js-utils.mergelist.md new file mode 100644 index 0000000..0f41eb7 --- /dev/null +++ b/docs/api/js-utils.mergelist.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [mergeList](./js-utils.mergelist.md) + +## mergeList() function + +Merge arguments, which may or may not be arrays, into one return that is definitely an array. + +Signature: + +```typescript +export declare function mergeList(...parts: Array>): Array; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| parts | Array<TItem | Array<TItem>> | | + +Returns: + +`Array` + diff --git a/docs/api/js-utils.mergemap.md b/docs/api/js-utils.mergemap.md new file mode 100644 index 0000000..2be0058 --- /dev/null +++ b/docs/api/js-utils.mergemap.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [mergeMap](./js-utils.mergemap.md) + +## mergeMap() function + +Signature: + +```typescript +export declare function mergeMap(target: Map, source: Map | Array<[TKey, TVal]>): Map; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| target | Map<TKey, TVal> | | +| source | Map<TKey, TVal> | Array<[TKey, TVal]> | | + +Returns: + +`Map` + diff --git a/docs/api/js-utils.missingkeyerror._constructor_.md b/docs/api/js-utils.missingkeyerror._constructor_.md new file mode 100644 index 0000000..636d600 --- /dev/null +++ b/docs/api/js-utils.missingkeyerror._constructor_.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [MissingKeyError](./js-utils.missingkeyerror.md) > [(constructor)](./js-utils.missingkeyerror._constructor_.md) + +## MissingKeyError.(constructor) + +Constructs a new instance of the `MissingKeyError` class + +Signature: + +```typescript +constructor(msg?: string, ...nested: Array); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| msg | string | | +| nested | Array<Error> | | + diff --git a/docs/api/js-utils.missingkeyerror.md b/docs/api/js-utils.missingkeyerror.md new file mode 100644 index 0000000..ab29b2d --- /dev/null +++ b/docs/api/js-utils.missingkeyerror.md @@ -0,0 +1,18 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [MissingKeyError](./js-utils.missingkeyerror.md) + +## MissingKeyError class + +Signature: + +```typescript +export declare class MissingKeyError extends BaseError +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(msg, nested)](./js-utils.missingkeyerror._constructor_.md) | | Constructs a new instance of the MissingKeyError class | + diff --git a/docs/api/js-utils.modulector.md b/docs/api/js-utils.modulector.md new file mode 100644 index 0000000..a6d09d4 --- /dev/null +++ b/docs/api/js-utils.modulector.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [ModuleCtor](./js-utils.modulector.md) + +## ModuleCtor type + +Signature: + +```typescript +export declare type ModuleCtor = new (data: unknown) => Module; +``` diff --git a/docs/api/js-utils.mustcoalesce.md b/docs/api/js-utils.mustcoalesce.md new file mode 100644 index 0000000..ecc0a8a --- /dev/null +++ b/docs/api/js-utils.mustcoalesce.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [mustCoalesce](./js-utils.mustcoalesce.md) + +## mustCoalesce() function + +Return the first value that is not nil. + +Signature: + +```typescript +export declare function mustCoalesce(...values: Array>): T; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| values | Array<Optional<T>> | | + +Returns: + +`T` + diff --git a/docs/api/js-utils.mustexist.md b/docs/api/js-utils.mustexist.md new file mode 100644 index 0000000..3300a80 --- /dev/null +++ b/docs/api/js-utils.mustexist.md @@ -0,0 +1,30 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [mustExist](./js-utils.mustexist.md) + +## mustExist() function + +Assert that a variable is not nil and return the value. + +Signature: + +```typescript +export declare function mustExist(val: Optional): T; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| val | Optional<T> | | + +Returns: + +`T` + +val + +## Exceptions + +NotFoundError + diff --git a/docs/api/js-utils.mustfind.md b/docs/api/js-utils.mustfind.md new file mode 100644 index 0000000..f78dd00 --- /dev/null +++ b/docs/api/js-utils.mustfind.md @@ -0,0 +1,25 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [mustFind](./js-utils.mustfind.md) + +## mustFind() function + +Find a value matching the given predicate or throw. + +Signature: + +```typescript +export declare function mustFind(list: Array>, predicate: PredicateC1): TVal; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| list | Array<Optional<TVal>> | | +| predicate | PredicateC1<TVal> | | + +Returns: + +`TVal` + diff --git a/docs/api/js-utils.mustget.md b/docs/api/js-utils.mustget.md new file mode 100644 index 0000000..d085a26 --- /dev/null +++ b/docs/api/js-utils.mustget.md @@ -0,0 +1,25 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [mustGet](./js-utils.mustget.md) + +## mustGet() function + +Get an element from a Map and guard against nil values. + +Signature: + +```typescript +export declare function mustGet(map: Map, key: TKey): TVal; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| map | Map<TKey, TVal> | | +| key | TKey | | + +Returns: + +`TVal` + diff --git a/docs/api/js-utils.nil.md b/docs/api/js-utils.nil.md new file mode 100644 index 0000000..929829f --- /dev/null +++ b/docs/api/js-utils.nil.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [Nil](./js-utils.nil.md) + +## Nil type + +Unset value. + +Signature: + +```typescript +export declare type Nil = null | undefined; +``` diff --git a/docs/api/js-utils.notfounderror._constructor_.md b/docs/api/js-utils.notfounderror._constructor_.md new file mode 100644 index 0000000..65b7c57 --- /dev/null +++ b/docs/api/js-utils.notfounderror._constructor_.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [NotFoundError](./js-utils.notfounderror.md) > [(constructor)](./js-utils.notfounderror._constructor_.md) + +## NotFoundError.(constructor) + +Constructs a new instance of the `NotFoundError` class + +Signature: + +```typescript +constructor(msg?: string, ...nested: Array); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| msg | string | | +| nested | Array<Error> | | + diff --git a/docs/api/js-utils.notfounderror.md b/docs/api/js-utils.notfounderror.md new file mode 100644 index 0000000..a6d7e29 --- /dev/null +++ b/docs/api/js-utils.notfounderror.md @@ -0,0 +1,18 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [NotFoundError](./js-utils.notfounderror.md) + +## NotFoundError class + +Signature: + +```typescript +export declare class NotFoundError extends BaseError +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(msg, nested)](./js-utils.notfounderror._constructor_.md) | | Constructs a new instance of the NotFoundError class | + diff --git a/docs/api/js-utils.notimplementederror._constructor_.md b/docs/api/js-utils.notimplementederror._constructor_.md new file mode 100644 index 0000000..3fe412a --- /dev/null +++ b/docs/api/js-utils.notimplementederror._constructor_.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [NotImplementedError](./js-utils.notimplementederror.md) > [(constructor)](./js-utils.notimplementederror._constructor_.md) + +## NotImplementedError.(constructor) + +Constructs a new instance of the `NotImplementedError` class + +Signature: + +```typescript +constructor(msg?: string, ...nested: Array); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| msg | string | | +| nested | Array<Error> | | + diff --git a/docs/api/js-utils.notimplementederror.md b/docs/api/js-utils.notimplementederror.md new file mode 100644 index 0000000..f370cd6 --- /dev/null +++ b/docs/api/js-utils.notimplementederror.md @@ -0,0 +1,18 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [NotImplementedError](./js-utils.notimplementederror.md) + +## NotImplementedError class + +Signature: + +```typescript +export declare class NotImplementedError extends BaseError +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(msg, nested)](./js-utils.notimplementederror._constructor_.md) | | Constructs a new instance of the NotImplementedError class | + diff --git a/docs/api/js-utils.optional.md b/docs/api/js-utils.optional.md new file mode 100644 index 0000000..d345543 --- /dev/null +++ b/docs/api/js-utils.optional.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [Optional](./js-utils.optional.md) + +## Optional type + +Value that may be nil. + +Signature: + +```typescript +export declare type Optional = T | Nil; +``` diff --git a/docs/api/js-utils.pushmergemap.md b/docs/api/js-utils.pushmergemap.md new file mode 100644 index 0000000..a96e0ad --- /dev/null +++ b/docs/api/js-utils.pushmergemap.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [pushMergeMap](./js-utils.pushmergemap.md) + +## pushMergeMap() function + +Signature: + +```typescript +export declare function pushMergeMap(...args: Array>>): Map>; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| args | Array<Map<TKey, TVal | Array<TVal>>> | | + +Returns: + +`Map>` + diff --git a/docs/api/js-utils.removepid.md b/docs/api/js-utils.removepid.md new file mode 100644 index 0000000..f725802 --- /dev/null +++ b/docs/api/js-utils.removepid.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [removePid](./js-utils.removepid.md) + +## removePid() function + +Signature: + +```typescript +export declare function removePid(path: string): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| path | string | | + +Returns: + +`Promise` + diff --git a/docs/api/js-utils.setorpush.md b/docs/api/js-utils.setorpush.md new file mode 100644 index 0000000..1ff5f6b --- /dev/null +++ b/docs/api/js-utils.setorpush.md @@ -0,0 +1,26 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [setOrPush](./js-utils.setorpush.md) + +## setOrPush() function + +Set a map key to a new array or push to the existing value. + +Signature: + +```typescript +export declare function setOrPush(map: Map>, key: TKey, val: TVal | Array): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| map | Map<TKey, Array<TVal>> | | +| key | TKey | | +| val | TVal | Array<TVal> | | + +Returns: + +`void` + diff --git a/docs/api/js-utils.signal.md b/docs/api/js-utils.signal.md new file mode 100644 index 0000000..9f969a5 --- /dev/null +++ b/docs/api/js-utils.signal.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [signal](./js-utils.signal.md) + +## signal() function + +Signature: + +```typescript +export declare function signal(...signals: Array): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| signals | Array<NodeJS.Signals> | | + +Returns: + +`Promise` + diff --git a/docs/api/js-utils.signal_reload.md b/docs/api/js-utils.signal_reload.md new file mode 100644 index 0000000..ed4b2d7 --- /dev/null +++ b/docs/api/js-utils.signal_reload.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [SIGNAL\_RELOAD](./js-utils.signal_reload.md) + +## SIGNAL\_RELOAD variable + +Signature: + +```typescript +SIGNAL_RELOAD: NodeJS.Signals +``` diff --git a/docs/api/js-utils.signal_reset.md b/docs/api/js-utils.signal_reset.md new file mode 100644 index 0000000..810bc3a --- /dev/null +++ b/docs/api/js-utils.signal_reset.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [SIGNAL\_RESET](./js-utils.signal_reset.md) + +## SIGNAL\_RESET variable + +Signature: + +```typescript +SIGNAL_RESET: NodeJS.Signals +``` diff --git a/docs/api/js-utils.signal_stop.md b/docs/api/js-utils.signal_stop.md new file mode 100644 index 0000000..5624c2d --- /dev/null +++ b/docs/api/js-utils.signal_stop.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [SIGNAL\_STOP](./js-utils.signal_stop.md) + +## SIGNAL\_STOP variable + +Signature: + +```typescript +SIGNAL_STOP: NodeJS.Signals +``` diff --git a/docs/api/js-utils.spylogger.md b/docs/api/js-utils.spylogger.md new file mode 100644 index 0000000..f17033c --- /dev/null +++ b/docs/api/js-utils.spylogger.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [spyLogger](./js-utils.spylogger.md) + +## spyLogger() function + +Signature: + +```typescript +export declare function spyLogger(spies: Partial): Logger; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| spies | Partial<Logger> | | + +Returns: + +`Logger` + diff --git a/docs/api/js-utils.timeout.md b/docs/api/js-utils.timeout.md new file mode 100644 index 0000000..0ec4f18 --- /dev/null +++ b/docs/api/js-utils.timeout.md @@ -0,0 +1,25 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [timeout](./js-utils.timeout.md) + +## timeout() function + +Reject after a set amount of time if the original promise has not yet resolved. + +Signature: + +```typescript +export declare function timeout(ms: number, oper: Promise): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| ms | number | | +| oper | Promise<T> | | + +Returns: + +`Promise` + diff --git a/docs/api/js-utils.timeouterror._constructor_.md b/docs/api/js-utils.timeouterror._constructor_.md new file mode 100644 index 0000000..ea5823b --- /dev/null +++ b/docs/api/js-utils.timeouterror._constructor_.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [TimeoutError](./js-utils.timeouterror.md) > [(constructor)](./js-utils.timeouterror._constructor_.md) + +## TimeoutError.(constructor) + +Constructs a new instance of the `TimeoutError` class + +Signature: + +```typescript +constructor(msg?: string, ...nested: Array); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| msg | string | | +| nested | Array<Error> | | + diff --git a/docs/api/js-utils.timeouterror.md b/docs/api/js-utils.timeouterror.md new file mode 100644 index 0000000..5ba9aeb --- /dev/null +++ b/docs/api/js-utils.timeouterror.md @@ -0,0 +1,18 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [TimeoutError](./js-utils.timeouterror.md) + +## TimeoutError class + +Signature: + +```typescript +export declare class TimeoutError extends BaseError +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(msg, nested)](./js-utils.timeouterror._constructor_.md) | | Constructs a new instance of the TimeoutError class | + diff --git a/docs/api/js-utils.waitforchild.md b/docs/api/js-utils.waitforchild.md new file mode 100644 index 0000000..7740ff6 --- /dev/null +++ b/docs/api/js-utils.waitforchild.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [waitForChild](./js-utils.waitforchild.md) + +## waitForChild() function + +Signature: + +```typescript +export declare function waitForChild(child: ChildProcessWithoutNullStreams): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| child | ChildProcessWithoutNullStreams | | + +Returns: + +`Promise` + diff --git a/docs/api/js-utils.writepid.md b/docs/api/js-utils.writepid.md new file mode 100644 index 0000000..ec3a7eb --- /dev/null +++ b/docs/api/js-utils.writepid.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [@apextoaster/js-utils](./js-utils.md) > [writePid](./js-utils.writepid.md) + +## writePid() function + +Signature: + +```typescript +export declare function writePid(path: string): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| path | string | | + +Returns: + +`Promise` + diff --git a/docs/api/rollup-template.md b/docs/api/rollup-template.md deleted file mode 100644 index bfabd42..0000000 --- a/docs/api/rollup-template.md +++ /dev/null @@ -1,6 +0,0 @@ - - -[Home](./index.md) > [@apextoaster/rollup-template](./rollup-template.md) - -## rollup-template package - diff --git a/package.json b/package.json index 42322bd..d501f61 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "@apextoaster/js-utils", "version": "0.1.1", "description": "utility functions", - "main": "out/index.js", + "main": "out/main.js", "types": "out/index.d.ts", "typings": "out/index.d.ts", "directories": { @@ -28,6 +28,7 @@ "@types/chai-as-promised": "7.1.2", "@types/lodash": "^4.14.149", "@types/mocha": "7.0.2", + "@types/node": "^13.9.5", "@types/sinon-chai": "3.2.3", "@types/source-map-support": "0.5.1", "@typescript-eslint/eslint-plugin": "2.25.0", diff --git a/src/app.ts b/src/app.ts index ecc0edd..cc31bd3 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,9 +1,7 @@ -import JSON_DATA from './resource/json.json'; -import YAML_DATA from './resource/yaml.yml'; import { VERSION_INFO } from './version'; export async function main(argv: Array): Promise { /* eslint-disable-next-line no-console */ - console.log('Hello World!', VERSION_INFO, JSON_DATA, YAML_DATA, argv); + console.log('Hello World!', VERSION_INFO, argv); return 1; } diff --git a/src/entity/README.md b/src/entity/README.md deleted file mode 100644 index 0d06fd0..0000000 --- a/src/entity/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Entity - -Database entities. diff --git a/src/error/ChildProcessError.ts b/src/error/ChildProcessError.ts new file mode 100644 index 0000000..bb8160b --- /dev/null +++ b/src/error/ChildProcessError.ts @@ -0,0 +1,7 @@ +import { BaseError } from 'noicejs'; + +export class ChildProcessError extends BaseError { + constructor(msg = 'child process exited with error status', ...nested: Array) { + super(msg, ...nested); + } +} diff --git a/src/error/InvalidArgumentError.ts b/src/error/InvalidArgumentError.ts new file mode 100644 index 0000000..b81c474 --- /dev/null +++ b/src/error/InvalidArgumentError.ts @@ -0,0 +1,7 @@ +import { BaseError } from 'noicejs'; + +export class InvalidArgumentError extends BaseError { + constructor(msg = 'invalid argument passed', ...nested: Array) { + super(msg, ...nested); + } +} diff --git a/src/error/MissingKeyError.ts b/src/error/MissingKeyError.ts new file mode 100644 index 0000000..9fb32f1 --- /dev/null +++ b/src/error/MissingKeyError.ts @@ -0,0 +1,7 @@ +import { BaseError } from 'noicejs'; + +export class MissingKeyError extends BaseError { + constructor(msg = 'missing key', ...nested: Array) { + super(msg, ...nested); + } +} diff --git a/src/error/NotFoundError.ts b/src/error/NotFoundError.ts new file mode 100644 index 0000000..d5b179c --- /dev/null +++ b/src/error/NotFoundError.ts @@ -0,0 +1,7 @@ +import { BaseError } from 'noicejs'; + +export class NotFoundError extends BaseError { + constructor(msg = 'value not found', ...nested: Array) { + super(msg, ...nested); + } +} diff --git a/src/error/NotImplementedError.ts b/src/error/NotImplementedError.ts new file mode 100644 index 0000000..cf211d3 --- /dev/null +++ b/src/error/NotImplementedError.ts @@ -0,0 +1,7 @@ +import { BaseError } from 'noicejs'; + +export class NotImplementedError extends BaseError { + constructor(msg = 'method not implemented', ...nested: Array) { + super(msg, ...nested); + } +} diff --git a/src/error/TimeoutError.ts b/src/error/TimeoutError.ts new file mode 100644 index 0000000..5481eaf --- /dev/null +++ b/src/error/TimeoutError.ts @@ -0,0 +1,7 @@ +import { BaseError } from 'noicejs'; + +export class TimeoutError extends BaseError { + constructor(msg = 'operation timed out', ...nested: Array) { + super(msg, ...nested); + } +} diff --git a/src/index.ts b/src/index.ts index 154ca87..ec24593 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,12 +1,63 @@ -import { main } from './app'; +export { ChildProcessError } from './error/ChildProcessError'; +export { InvalidArgumentError } from './error/InvalidArgumentError'; +export { MissingKeyError } from './error/MissingKeyError'; +export { NotFoundError } from './error/NotFoundError'; +export { NotImplementedError } from './error/NotImplementedError'; +export { TimeoutError } from './error/TimeoutError'; -const STATUS_ERROR = 1; +export { + AsyncTracker, +} from './test/AsyncTracker'; +export { + getTestLogger, + spyLogger, +} from './test/Logger'; -/** - * This is the main entry-point to the program and the only file not included in the main bundle. - */ -main(process.argv).then((status) => process.exit(status)).catch((err: Error) => { - // eslint-disable-next-line no-console - console.error('uncaught error during main:', err); - process.exit(STATUS_ERROR); -}); +export { + Nil, + Optional, + countOf, + defaultWhen, + doesExist, + filterNil, + isNil, + mergeList, + mustCoalesce, + mustExist, + mustFind, +} from './utils'; +export { + ArrayMapper, +} from './utils/ArrayMapper'; +export { + defer, + timeout, +} from './utils/Async'; +export { + concat, + encode, +} from './utils/Buffer'; +export { waitForChild } from './utils/Child'; +export { ExternalModule, ModuleCtor } from './utils/ExternalModule'; +export { isDebug } from './utils/Env'; +export { + Dict, + MapLike, + entriesOf, + makeDict, + makeMap, + mergeMap, + mustGet, + pushMergeMap, + setOrPush, +} from './utils/Map'; +export { + removePid, + writePid, +} from './utils/PidFile'; +export { + SIGNAL_RELOAD, + SIGNAL_RESET, + SIGNAL_STOP, + signal, +} from './utils/Signal'; diff --git a/src/lib.ts b/src/lib.ts deleted file mode 100644 index c59d8d0..0000000 --- a/src/lib.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { VERSION_INFO } from './version'; - -export default { - VERSION_INFO, -}; diff --git a/src/migration/README.md b/src/migration/README.md deleted file mode 100644 index 572f262..0000000 --- a/src/migration/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Migration - -Database schema migrations for the entities. diff --git a/src/module/README.md b/src/module/README.md deleted file mode 100644 index bec666d..0000000 --- a/src/module/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Module - -Dependency injection modules. diff --git a/src/resource/json.json b/src/resource/json.json deleted file mode 100644 index 1f97972..0000000 --- a/src/resource/json.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "foo": {} -} \ No newline at end of file diff --git a/src/resource/yaml.yml b/src/resource/yaml.yml deleted file mode 100644 index 9b1b394..0000000 --- a/src/resource/yaml.yml +++ /dev/null @@ -1 +0,0 @@ -foo: {} \ No newline at end of file diff --git a/src/test/AsyncTracker.ts b/src/test/AsyncTracker.ts new file mode 100644 index 0000000..d2a5005 --- /dev/null +++ b/src/test/AsyncTracker.ts @@ -0,0 +1,79 @@ +import { AsyncHook, createHook } from 'async_hooks'; + +import { isNil } from '../utils'; +import { isDebug } from '../utils/Env'; + +export interface TrackedResource { + source: string; + triggerAsyncId: number; + type: string; +} + +/** + * Async resource tracker using node's internal hooks. + * + * This probably won't work in a browser. It does not hold references to the resource, to avoid leaks. + * Adapted from https://gist.github.com/boneskull/7fe75b63d613fa940db7ec990a5f5843#file-async-dump-js + */ +export class AsyncTracker { + public static getStack(): string { + const err = new Error(); + if (isNil(err.stack)) { + return 'no stack trace available'; + } else { + return err.stack; // TODO: filterStack(err.stack); + } + } + + private readonly hook: AsyncHook; + private readonly resources: Map; + + constructor() { + this.resources = new Map(); + this.hook = createHook({ + destroy: (id: number) => { + this.resources.delete(id); + }, + init: (id: number, type: string, triggerAsyncId: number) => { + const source = AsyncTracker.getStack(); + // @TODO: exclude async hooks, including this one + this.resources.set(id, { + source, + triggerAsyncId, + type, + }); + }, + promiseResolve: (id: number) => { + this.resources.delete(id); + }, + }); + } + + public clear() { + this.resources.clear(); + } + + public disable() { + this.hook.disable(); + } + + /* eslint-disable no-console, no-invalid-this */ + public dump() { + console.error(`tracking ${this.resources.size} async resources`); + this.resources.forEach((res, id) => { + console.error(`${id}: ${res.type}`); + if (isDebug()) { + console.error(res.source); + console.error('\n'); + } + }); + } + + public enable() { + this.hook.enable(); + } + + public get size(): number { + return this.resources.size; + } +} diff --git a/src/test/Logger.ts b/src/test/Logger.ts new file mode 100644 index 0000000..49bdd7c --- /dev/null +++ b/src/test/Logger.ts @@ -0,0 +1,19 @@ +import { ConsoleLogger, Logger, NullLogger } from 'noicejs'; + +const ENV_DEBUG = 'DEBUG'; + +export function getTestLogger(verbose = false): Logger { + if (verbose || process.env[ENV_DEBUG] === 'TRUE') { + return new ConsoleLogger(); + } else { + return new NullLogger(); + } +} + +export function spyLogger(spies: Partial): Logger { + const logger = { + ...spies, + child: () => logger, + } as Logger; + return logger; +} diff --git a/src/utils/ArrayMapper.ts b/src/utils/ArrayMapper.ts new file mode 100644 index 0000000..2944346 --- /dev/null +++ b/src/utils/ArrayMapper.ts @@ -0,0 +1,41 @@ +import { setOrPush } from './Map'; + +export interface ArrayMapperOptions { + rest: string; + skip: number; + take: Array; +} + +export class ArrayMapper { + public readonly rest: string; + public readonly skip: number; + public readonly take: Array; + + constructor(options: ArrayMapperOptions) { + this.rest = options.rest; + this.skip = options.skip; + this.take = Array.from(options.take); + } + + public map(input: Array): Map> { + const result = new Map(); + input.forEach((it, idx) => { + if (idx < this.skip) { + return; + } + + const skipdx = idx - this.skip; + if (skipdx < this.take.length) { + setOrPush(result, this.take[skipdx], it); + } else { + setOrPush(result, this.rest, it); + } + }); + + if (!result.has(this.rest)) { + result.set(this.rest, []); + } + + return result; + } +} diff --git a/src/utils/Async.ts b/src/utils/Async.ts new file mode 100644 index 0000000..8d5aedf --- /dev/null +++ b/src/utils/Async.ts @@ -0,0 +1,38 @@ +import { TimeoutError } from '../error/TimeoutError'; + +/** + * Resolve after a set amount of time. + */ +export function defer(ms: number, val?: T): Promise { + return new Promise((res, rej) => { + setTimeout(() => { + res(val); + }, ms); + }); +} + +/** + * Reject after a set amount of time if the original promise has not yet resolved. + */ +export function timeout(ms: number, oper: Promise): Promise { + const limit = new Promise((res, rej) => { + setTimeout(() => { + rej(new TimeoutError()); + }, ms); + }); + + return Promise.race([limit, oper]); +} + +export async function waitFor(cb: () => boolean, step: number, count: number): Promise { + let accum = 0; + while (accum < count) { + await defer(step); + if (cb()) { + return; + } + accum += 1; + } + + throw new TimeoutError(); +} diff --git a/src/utils/Buffer.ts b/src/utils/Buffer.ts new file mode 100644 index 0000000..3b2b11d --- /dev/null +++ b/src/utils/Buffer.ts @@ -0,0 +1,12 @@ +export function concat(chunks: Array): Buffer { + const sum = chunks.map((it) => it.length).reduce((p, c) => p + c, 0); + return Buffer.concat(chunks, sum); +} + +export function encode(chunks: Array, encoding: string): string { + if (chunks.length === 0) { + return ''; + } + + return concat(chunks).toString(encoding); +} diff --git a/src/utils/Child.ts b/src/utils/Child.ts new file mode 100644 index 0000000..2b8ea0e --- /dev/null +++ b/src/utils/Child.ts @@ -0,0 +1,84 @@ +import { ChildProcessWithoutNullStreams, spawn } from 'child_process'; +import { BaseError } from 'noicejs'; +import { Writable } from 'stream'; + +import { doesExist, Optional } from '.'; +import { ChildProcessError } from '../error/ChildProcessError'; +import { encode } from './Buffer'; +import { NameValuePair } from './Map'; + +export interface ChildOptions { + args: Array; + command: string; + cwd: string; + env: Array>; + timeout: number; +} + +export interface ChildResult { + status: number; + stderr: string; + stdout: string; +} + +export type ChildSpawner = typeof spawn; + +const CHILD_ENCODING = 'utf-8'; +const CHILD_EVENT = 'child process emitted error event'; +const CHILD_STATUS = 'child process exited with error status'; +const CHILD_OUTPUT = 'child process emitted error output'; + +export function waitForChild(child: ChildProcessWithoutNullStreams): Promise { + return new Promise((res, rej) => { + const stderr: Array = []; + const stdout: Array = []; + + child.stderr.on('data', (chunk) => { + stderr.push(chunk); + }); + + child.stdout.on('data', (chunk) => { + stdout.push(chunk); + }); + + child.on('error', (err: Error | number) => { + if (err instanceof Error) { + rej(new ChildProcessError(CHILD_EVENT, err)); + } + }); + + child.on('close', (status: number) => { + const errors = encode(stderr, CHILD_ENCODING); + if (status > 0) { + const msg = `${CHILD_STATUS}: ${status}`; + rej(new ChildProcessError(msg, new BaseError(errors))); + return; + } + + if (errors.length > 0) { + rej(new ChildProcessError(CHILD_OUTPUT, new BaseError(errors))); + return; + } + + res({ + status, + stderr: errors, + stdout: encode(stdout, CHILD_ENCODING), + }); + }); + }); +} + +export function writeValue(stream: Writable, value: string): Promise { + return new Promise((res, rej) => { + stream.write(value, (err: Optional) => { + if (doesExist(err)) { + rej(err); + } else { + stream.end(() => { + res(true); + }); + } + }); + }); +} diff --git a/src/utils/Env.ts b/src/utils/Env.ts new file mode 100644 index 0000000..96bc09f --- /dev/null +++ b/src/utils/Env.ts @@ -0,0 +1,3 @@ +export function isDebug() { + return Reflect.has(process.env, 'DEBUG'); +} diff --git a/src/utils/ExternalModule.ts b/src/utils/ExternalModule.ts new file mode 100644 index 0000000..0f2671c --- /dev/null +++ b/src/utils/ExternalModule.ts @@ -0,0 +1,14 @@ +import { Module } from 'noicejs'; + +export interface ExternalModule { + data?: unknown; + export: string; + require: string; +} + +export type ModuleCtor = new (data: unknown) => Module; + +export function isModule(it: object): it is ModuleCtor { + const p = Reflect.getPrototypeOf(it); + return p === Module || p instanceof Module; +} diff --git a/src/utils/Map.ts b/src/utils/Map.ts new file mode 100644 index 0000000..34e531c --- /dev/null +++ b/src/utils/Map.ts @@ -0,0 +1,185 @@ +import { isMap, isObject, isString } from 'lodash'; + +import { doesExist, isNil, mergeList, mustExist, Optional } from '.'; +import { NotFoundError } from '../error/NotFoundError'; + +export interface Dict { + [key: string]: TVal; +} + +/** + * A `Map` or dictionary object with string keys and `TVal` values. + */ +export type MapLike = Map | Dict; + +/** + * Get an element from a Map and guard against nil values. + */ +export function mustGet(map: Map, key: TKey): TVal { + const val = map.get(key); + return mustExist(val); +} + +export function getOrDefault(map: Map, key: TKey, defaultValue: TVal): TVal { + if (map.has(key)) { + const data = map.get(key); + if (doesExist(data)) { + return data; + } + } + + return defaultValue; +} + +export function getHead(map: Map>, key: TKey): TVal { + const value = map.get(key); + if (isNil(value) || value.length === 0) { + throw new NotFoundError(); + } + return value[0]; +} + +export function getHeadOrDefault(map: Map>>, key: TKey, defaultValue: TVal): TVal { + if (!map.has(key)) { + return defaultValue; + } + + const data = map.get(key); + if (isNil(data)) { + return defaultValue; + } + + const [head] = data; + if (isNil(head)) { + return defaultValue; + } + + return head; +} + +/** + * Set a map key to a new array or push to the existing value. + * @param map The destination map and source of existing values. + * @param key The key to get and set. + * @param val The value to add. + */ +export function setOrPush(map: Map>, key: TKey, val: TVal | Array) { + const prev = map.get(key); + if (doesExist(prev)) { + map.set(key, mergeList(prev, val)); + } else { + if (Array.isArray(val)) { + map.set(key, val); + } else { + map.set(key, [val]); + } + } +} + +export function mergeMap(target: Map, source: Map | Array<[TKey, TVal]>) { + for (const [k, v] of source) { + target.set(k, v); + } + + return target; +} + +export function pushMergeMap(...args: Array>>): Map> { + const out = new Map(); + for (const arg of args) { + for (const [key, val] of arg) { + setOrPush(out, key, val); + } + } + return out; +} + +/** + * Clone a map or map-like object into a new map. + */ +export function makeMap(val: Optional>): Map { + // nil: empty map + if (isNil(val)) { + return new Map(); + } + + // already a map: make a copy + if (isMap(val)) { + return new Map(val.entries()); + } + + // otherwise: dict + return new Map(Object.entries(val)); +} + +/** + * Turns a map or dict into a dict + */ +export function makeDict(map: Optional>): Dict { + if (isNil(map)) { + return {}; + } + + if (isMap(map)) { + const result: Dict = {}; + for (const [key, val] of map) { + result[key] = val; + } + return result; + } + + return map; +} + +export interface NameValuePair { + name: string; + value: TVal; +} + +export function pairsToMap(pairs: Array>): Map { + const map = new Map(); + for (const p of pairs) { + map.set(p.name, p.value); + } + return map; +} + +export function dictValuesToArrays(map: MapLike): Dict> { + const data: Dict> = {}; + for (const [key, value] of makeMap(map)) { + if (Array.isArray(value)) { + data[key] = value; + } else { + data[key] = [value]; + } + } + + return data; +} + +export function normalizeMap(map: MapLike): Dict> { + const data: Dict> = {}; + for (const [key, value] of makeMap(map)) { + if (Array.isArray(value)) { + data[key] = value; + } else if (isString(value)) { + data[key] = [value]; + } else if (isObject(value)) { + data[key] = [value.toString()]; + } + } + + return data; +} + +export function entriesOf(map: Optional>): Array<[string, TVal]> { + if (map instanceof Map) { + return Array.from(map.entries()); + } + + if (map instanceof Object) { + return Object.entries(map); + } + + return []; +} diff --git a/src/utils/PidFile.ts b/src/utils/PidFile.ts new file mode 100644 index 0000000..0fd5e39 --- /dev/null +++ b/src/utils/PidFile.ts @@ -0,0 +1,36 @@ +import { open, unlink, write } from 'fs'; +import { pid } from 'process'; + +import { doesExist, Optional } from '.'; + +type OptionalErrno = Optional; + +export async function writePid(path: string): Promise { + return new Promise((res, rej) => { + open(path, 'wx', (openErr: OptionalErrno, fd: number) => { + if (doesExist(openErr)) { + rej(openErr); + } else { + write(fd, pid.toString(), 0, 'utf8', (writeErr: OptionalErrno) => { + if (doesExist(writeErr)) { + rej(writeErr); + } else { + res(); + } + }); + } + }); + }); +} + +export async function removePid(path: string): Promise { + return new Promise((res, rej) => { + unlink(path, (err: OptionalErrno) => { + if (doesExist(err)) { + rej(err); + } else { + res(); + } + }); + }); +} diff --git a/src/utils/Reflect.ts b/src/utils/Reflect.ts new file mode 100644 index 0000000..b693004 --- /dev/null +++ b/src/utils/Reflect.ts @@ -0,0 +1,36 @@ +import { isFunction } from 'lodash'; + +import { doesExist, isNil } from '.'; + +export function getConstructor(val: object) { + return val.constructor; +} + +export function getMethods(value: TValue): Set { + const methods = new Set(); + + for (const name of Object.getOwnPropertyNames(value)) { + const desc = Object.getOwnPropertyDescriptor(value, name); + if (isNil(desc)) { + continue; + } + + const method = desc.value; + if (isFunction(method)) { + methods.add(method); + } + } + + const proto = Reflect.getPrototypeOf(value); + if (proto !== value && doesExist(proto)) { + for (const m of getMethods(proto)) { + methods.add(m); + } + } + + return methods; +} + +export function constructorName(val: object) { + return getConstructor(Reflect.getPrototypeOf(val)).name; +} diff --git a/src/utils/Signal.ts b/src/utils/Signal.ts new file mode 100644 index 0000000..35e0266 --- /dev/null +++ b/src/utils/Signal.ts @@ -0,0 +1,18 @@ +export const SIGNAL_RELOAD: NodeJS.Signals = 'SIGHUP'; +export const SIGNAL_RESET: NodeJS.Signals = 'SIGINT'; +export const SIGNAL_STOP: NodeJS.Signals = 'SIGTERM'; + +export function signal(...signals: Array): Promise { + return new Promise((res, _) => { + function handler(fired: NodeJS.Signals) { + for (const s of signals) { + process.removeListener(s, handler); + } + res(fired); + } + + for (const sig of signals) { + process.on(sig, handler); + } + }); +} diff --git a/src/utils/String.ts b/src/utils/String.ts new file mode 100644 index 0000000..eb48a48 --- /dev/null +++ b/src/utils/String.ts @@ -0,0 +1,23 @@ + +export function leftPad(val: string, min = 8, fill = '0'): string { + if (val.length < min) { + const len = min - val.length; + const pre = Array(len).fill(fill).join('').slice(0, len); + return `${pre}${val}`; + } else { + return val; + } +} + +export function trim(val: string, max: number, tail = '...'): string { + if (val.length <= max) { + return val; + } + + if (max < tail.length) { + return val.substr(0, max); + } + + const start = val.substr(0, max - tail.length); + return `${start}${tail}`; +} diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 0000000..e3586e1 --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1,119 @@ +import { NotFoundError } from '../error/NotFoundError'; + +/** + * Unset value. + */ +/* eslint-disable-next-line @typescript-eslint/ban-types */ +export type Nil = null | undefined; + +/** + * Value that may be nil. + */ +export type Optional = T | Nil; + +/** + * Comparison (filter) predicate for a single value. + */ +export type PredicateC1 = (val: TVal, idx: number, list: Array) => boolean; + +/** + * Comparison (sort) predicate for two values. + */ +export type PredicateC2 = (pval: TVal, nval: TVal, idx: number, list: Array) => number; + +/** + * Reduction predicate for two values. + */ +export type PredicateR2 = (pval: TVal, nval: TVal, idx: number, list: Array) => TVal; + +/* eslint-disable-next-line @typescript-eslint/ban-types */ +export function isNil(val: Optional): val is Nil { + /* eslint-disable-next-line no-null/no-null */ + return val === null || val === undefined; +} + +/** + * Calculate the "length" of an array or value. + * + * Arrays return their length, single values return 1, and nil values return 0. + * This counts the number of elements that setOrPush would add. + */ +export function countOf(val: unknown): number { + if (Array.isArray(val)) { + return val.length; + } + + if (doesExist(val)) { + return 1; + } + + return 0; +} + +export function defaultWhen(condition: boolean, ...items: Array): TVal { + if (condition) { + return items[0]; + } else { + return items[1]; + } +} + +/** + * Remove any null or undefined items from the list. + */ +export function filterNil(list: ArrayLike>): Array { + return Array.from(list).filter(doesExist); +} + +/** + * Merge arguments, which may or may not be arrays, into one return that is definitely an array. + */ +export function mergeList(...parts: Array>): Array { + const out = []; + + for (const part of parts) { + if (Array.isArray(part)) { + out.push(...part); + } else { + out.push(part); + } + } + + return out; +} + +/** + * Find a value matching the given predicate or throw. + */ +export function mustFind(list: Array>, predicate: PredicateC1): TVal { + const val = filterNil(list).find(predicate); + return mustExist(val); +} + +/** + * Check if a variable is not nil. + */ +export function doesExist(val: Optional): val is T { + return !isNil(val); +} + +/** + * Assert that a variable is not nil and return the value. + * + * @throws NotFoundError + * @returns val + */ +export function mustExist(val: Optional): T { + if (isNil(val)) { + throw new NotFoundError(); + } + + return val; +} + +/** + * Return the first value that is not nil. + */ +export function mustCoalesce(...values: Array>): T { + return mustFind(values, doesExist); +} diff --git a/test/TestHelpers.ts b/test/TestHelpers.ts new file mode 100644 index 0000000..2488ed7 --- /dev/null +++ b/test/TestHelpers.ts @@ -0,0 +1,6 @@ +import { describeLeaks, itLeaks } from './helpers/async'; + +describeLeaks('test helpers', async () => { + itLeaks('should wrap suites'); + itLeaks('should wrap tests'); +}); diff --git a/test/error/TestError.ts b/test/error/TestError.ts new file mode 100644 index 0000000..b9aed46 --- /dev/null +++ b/test/error/TestError.ts @@ -0,0 +1,41 @@ +import { expect } from 'chai'; + +import { ChildProcessError } from '../../../js-utils/src/error/ChildProcessError'; +import { InvalidArgumentError } from '../../../js-utils/src/error/InvalidArgumentError'; +import { MissingKeyError } from '../../../js-utils/src/error/MissingKeyError'; +import { NotFoundError } from '../../src/error/NotFoundError'; +import { NotImplementedError } from '../../../js-utils/src/error/NotImplementedError'; +import { TimeoutError } from '../../../js-utils/src/error/TimeoutError'; + +const errors = [ + ChildProcessError, + InvalidArgumentError, + MissingKeyError, + NotFoundError, + NotImplementedError, + TimeoutError, +]; + +describe('errors', () => { + for (const errorType of errors) { + describe(errorType.name, () => { + it('should have a message', () => { + const err = new errorType(); + expect(err.message).to.not.equal(''); + }); + + it('should include nested errors in the stack trace', () => { + const inner = new Error('inner error'); + const err = new errorType('outer error', inner); + expect(err.stack).to.include('inner', 'inner error message').and.include('outer', 'outer error message'); + }); + + it('should have the nested error', () => { + const inner = new Error('inner error'); + const err = new errorType('outer error', inner); + expect(err.cause()).to.equal(inner); + expect(err.length).to.equal(1); + }); + }); + } +}); diff --git a/test/helpers/async.ts b/test/helpers/async.ts index 6cf68c4..651ef78 100644 --- a/test/helpers/async.ts +++ b/test/helpers/async.ts @@ -1,7 +1,6 @@ -import { AsyncHook, createHook } from 'async_hooks'; - -/* eslint-disable-next-line @typescript-eslint/ban-types */ -export type Maybe = TValue | null | undefined; +import { AsyncTracker } from '../../src/test/AsyncTracker'; +import { isNil } from '../../src/utils'; +import { isDebug } from '../../src/utils/Env'; // this will pull Mocha internals out of the stacks /* eslint-disable-next-line @typescript-eslint/no-var-requires */ @@ -11,97 +10,12 @@ const filterStack = stackTraceFilter(); type AsyncMochaTest = (this: Mocha.Context | void) => Promise; type AsyncMochaSuite = (this: Mocha.Suite) => Promise; -/* eslint-disable-next-line @typescript-eslint/ban-types */ -function isNil(val: Maybe): val is null | undefined { - /* eslint-disable-next-line no-null/no-null */ - return val === null || val === undefined; -} - -export interface TrackedResource { - source: string; - triggerAsyncId: number; - type: string; -} - -function debugMode() { - return Reflect.has(process.env, 'DEBUG'); -} - -/** - * Async resource tracker using node's internal hooks. - * - * This probably won't work in a browser. It does not hold references to the resource, to avoid leaks. - * Adapted from https://gist.github.com/boneskull/7fe75b63d613fa940db7ec990a5f5843#file-async-dump-js - */ -export class Tracker { - public static getStack(): string { - const err = new Error(); - if (isNil(err.stack)) { - return 'no stack trace available'; - } else { - return filterStack(err.stack); - } - } - - private readonly hook: AsyncHook; - private readonly resources: Map; - - constructor() { - this.resources = new Map(); - this.hook = createHook({ - destroy: (id: number) => { - this.resources.delete(id); - }, - init: (id: number, type: string, triggerAsyncId: number) => { - const source = Tracker.getStack(); - // @TODO: exclude async hooks, including this one - this.resources.set(id, { - source, - triggerAsyncId, - type, - }); - }, - promiseResolve: (id: number) => { - this.resources.delete(id); - }, - }); - } - - public clear() { - this.resources.clear(); - } - - public disable() { - this.hook.disable(); - } - - /* eslint-disable no-console, no-invalid-this */ - public dump() { - console.error(`tracking ${this.resources.size} async resources`); - this.resources.forEach((res, id) => { - console.error(`${id}: ${res.type}`); - if (debugMode()) { - console.error(res.source); - console.error('\n'); - } - }); - } - - public enable() { - this.hook.enable(); - } - - public get size(): number { - return this.resources.size; - } -} - /** * Describe a suite of async tests. This wraps mocha's describe to track async resources and report leaks. */ export function describeLeaks(description: string, cb: AsyncMochaSuite): Mocha.Suite { return describe(description, function trackSuite(this: Mocha.Suite) { - const tracker = new Tracker(); + const tracker = new AsyncTracker(); beforeEach(() => { tracker.enable(); @@ -115,7 +29,7 @@ export function describeLeaks(description: string, cb: AsyncMochaSuite): Mocha.S if (leaked > 1) { tracker.dump(); const msg = `test leaked ${leaked - 1} async resources`; - if (debugMode()) { + if (isDebug()) { throw new Error(msg); } else { /* eslint-disable-next-line no-console */ @@ -126,6 +40,7 @@ export function describeLeaks(description: string, cb: AsyncMochaSuite): Mocha.S tracker.clear(); }); + /* eslint-disable-next-line no-invalid-this */ const suite: PromiseLike | undefined = cb.call(this); if (isNil(suite) || !Reflect.has(suite, 'then')) { /* eslint-disable-next-line no-console */ @@ -148,6 +63,7 @@ export function itLeaks(expectation: string, cb?: AsyncMochaTest): Mocha.Test { return it(expectation, function trackTest(this: Mocha.Context) { return new Promise((res, rej) => { + /* eslint-disable-next-line no-invalid-this */ cb.call(this).then((value: unknown) => { res(value); }, (err: Error) => { diff --git a/test/utils/TestArrayMapper.ts b/test/utils/TestArrayMapper.ts new file mode 100644 index 0000000..3b0a9a8 --- /dev/null +++ b/test/utils/TestArrayMapper.ts @@ -0,0 +1,42 @@ +import { expect } from 'chai'; + +import { ArrayMapper } from '../../src/utils/ArrayMapper'; +import { describeLeaks, itLeaks } from '../helpers/async'; + +describeLeaks('utils', async () => { + describeLeaks('array mapper', async () => { + itLeaks('should take initial args', async () => { + const mapper = new ArrayMapper({ + rest: 'others', + skip: 0, + take: ['first', 'second'], + }); + const results = mapper.map(['1', '2', '3', '4']); + + expect(results.get('first'), 'args should be collected').to.deep.equal(['1']); + expect(results.get('second'), 'second should be collected').to.deep.equal(['2']); + expect(results.get('others'), 'rest should be collected').to.deep.equal(['3', '4']); + }); + + itLeaks('should always include rest arg', async () => { + const mapper = new ArrayMapper({ + rest: 'empty', + skip: 0, + take: ['first', 'second'], + }); + const results = mapper.map(['1', '2']); + expect(results, 'all keys should be present').to.have.all.keys('first', 'second', 'empty'); + expect(results.get('empty'), 'rest key should be empty').to.have.lengthOf(0); + }); + + itLeaks('should skit initial args', async () => { + const mapper = new ArrayMapper({ + rest: 'empty', + skip: 3, + take: ['first'], + }); + const results = mapper.map(['1', '2', '3', '4']); + expect(results.get('first'), 'first key should take fourth element').to.deep.equal(['4']); + }); + }); +}); diff --git a/test/utils/TestMap.ts b/test/utils/TestMap.ts new file mode 100644 index 0000000..0876230 --- /dev/null +++ b/test/utils/TestMap.ts @@ -0,0 +1,143 @@ +import { expect } from 'chai'; + +import { NotFoundError } from '../../src/error/NotFoundError'; +import { + entriesOf, + getHead, + getHeadOrDefault, + getOrDefault, + makeDict, + makeMap, + mustGet, + pairsToMap, +} from '../../src/utils/Map'; +import { describeLeaks, itLeaks } from '../helpers/async'; + +const DEFAULT_VALUE = 'default'; +const mapKey = 'key'; +const mapValue = 'value'; +const singleItem = new Map([[mapKey, mapValue]]); +const multiItem = new Map([ + [mapKey, [mapValue]], + /* eslint-disable */ + ['nilKey', null as any], + ['nilValue', [null]], + /* eslint-enable */ +]); + +describeLeaks('map utils', async () => { + describeLeaks('make dict', async () => { + itLeaks('should return an empty dict for nil values', async () => { + /* eslint-disable-next-line no-null/no-null */ + expect(makeDict(null)).to.deep.equal({}); + expect(makeDict(undefined)).to.deep.equal({}); + }); + + itLeaks('should return an existing dict', async () => { + const input = {}; + expect(makeDict(input)).to.equal(input); + }); + }); + + describeLeaks('make map', async () => { + itLeaks('should convert objects to maps', async () => { + const data = { + bar: '2', + foo: '1', + }; + const map = makeMap(data); + + expect(Array.from(map.entries())).to.deep.equal(Object.entries(data)); + }); + }); + + describeLeaks('must get helper', async () => { + itLeaks('should get existing keys', async () => { + expect(mustGet(singleItem, mapKey)).to.equal(mapValue); + }); + + itLeaks('should throw on missing keys', async () => { + expect(() => { + mustGet(singleItem, 'nope'); + }).to.throw(NotFoundError); + }); + }); + + describeLeaks('get head helper', async () => { + itLeaks('should get the first item from existing keys', async () => { + expect(getHead(multiItem, mapKey)).to.equal(mapValue); + }); + + itLeaks('should throw on missing keys', async () => { + expect(() => { + getHead(multiItem, 'nope'); + }).to.throw(NotFoundError); + }); + }); + + describeLeaks('get head or default helper', async () => { + itLeaks('should get the first item from existing keys', async () => { + expect(getHeadOrDefault(multiItem, mapKey, 'nope')).to.equal(mapValue); + }); + + itLeaks('should get the default for missing keys', async () => { + expect(getHeadOrDefault(multiItem, 'nope', mapValue)).to.equal(mapValue); + }); + + itLeaks('should return the default value for nil values', async () => { + expect(getHeadOrDefault(multiItem, 'nilValue', mapValue)).to.equal(mapValue); + }); + + itLeaks('should return the default value for nil keys', async () => { + expect(getHeadOrDefault(multiItem, 'nilKey', mapValue)).to.equal(mapValue); + }); + }); + + describe('get or default helper', () => { + it('should get the item for existing keys', () => { + expect(getOrDefault(singleItem, mapKey, DEFAULT_VALUE)).to.equal(mapValue); + }); + + it('should get the item for missing keys', () => { + expect(getOrDefault(singleItem, 'missing', DEFAULT_VALUE)).to.equal(DEFAULT_VALUE); + }); + + xit('should return the default for nil values'); + xit('should return falsy values for existing keys'); + }); + + describe('pairs to map helper', () => { + it('should convert pairs', () => { + const result = pairsToMap([{ + name: 'foo', + value: 3, + }]); + expect(result.get('foo')).to.equal(3); + }); + }); + + describe('entries of helper', () => { + it('should return entries for maps', () => { + const input: Array<[string, number]> = [['foo', 1], ['bar', 3]]; + const value = new Map(input); + const result = entriesOf(value); + expect(result.length).to.equal(2); + expect(result).to.deep.equal(input); + }); + + it('should return entries for objects', () => { + const result = entriesOf({ + bar: 3, + foo: 1, + }); + expect(result.length).to.equal(2); + expect(result[0][0]).to.equal('bar'); + }); + + it('should return empty entries for nil values', () => { + /* eslint-disable-next-line no-null/no-null */ + expect(entriesOf(null)).to.deep.equal([]); + expect(entriesOf(undefined)).to.deep.equal([]); + }); + }); +}); diff --git a/test/utils/TestString.ts b/test/utils/TestString.ts new file mode 100644 index 0000000..8793c17 --- /dev/null +++ b/test/utils/TestString.ts @@ -0,0 +1,41 @@ +import { expect } from 'chai'; + +import { leftPad, trim } from '../../src/utils/String'; +import { describeLeaks, itLeaks } from '../helpers/async'; + +const TEST_SHORT = 'hello'; +const TEST_LONG = 'hello world'; + +describeLeaks('left pad helper', async () => { + itLeaks('should prepend padding', async () => { + expect(leftPad('test')).to.equal('0000test'); + }); + + itLeaks('should return long strings as-is', async () => { + const long = 'testing-words'; + expect(leftPad(long, 8)).to.equal(long); + }); + + itLeaks('should use padding string', async () => { + expect(leftPad('test', 8, 'too')).to.equal('toottest', 'must repeat and truncate the padding string'); + }); +}); + +describeLeaks('trim helper', async () => { + itLeaks('should return strings shorter than max', async () => { + expect(trim('yes', 5)).to.equal('yes', 'shorter than max'); + expect(trim(TEST_SHORT, 5)).to.equal(TEST_SHORT, 'equal to max'); + }); + + itLeaks('should trim strings longer than max', async () => { + expect(trim(TEST_LONG, 3, '...')).to.equal('...'); + expect(trim(TEST_LONG, 5)).to.equal('he...'); + expect(trim(TEST_LONG, 8)).to.equal('hello...'); + }); + + itLeaks('should not add tail when max is small', async () => { + expect(trim(TEST_SHORT, 2, '...')).to.equal('he'); + expect(trim(TEST_LONG, 5, 'very long tail')).to.equal(TEST_SHORT); + expect(trim(TEST_SHORT, 8, 'very long tail')).to.equal(TEST_SHORT); + }); +}); diff --git a/test/utils/TestUtils.ts b/test/utils/TestUtils.ts new file mode 100644 index 0000000..5e13a03 --- /dev/null +++ b/test/utils/TestUtils.ts @@ -0,0 +1,45 @@ +import { expect } from 'chai'; + +import { NotFoundError } from '../../src/error/NotFoundError'; +import { countOf, filterNil, mustFind } from '../../src/utils'; +import { describeLeaks, itLeaks } from '../helpers/async'; + +describeLeaks('utils', async () => { + describeLeaks('count list', async () => { + itLeaks('should count a single item', async () => { + expect(countOf(1)).to.equal(1, 'numbers'); + expect(countOf('')).to.equal(1, 'empty strings'); + expect(countOf('123')).to.equal(1, 'other strings'); + }); + + itLeaks('should count an array of items', async () => { + expect(countOf([1])).to.equal(1, 'single item list'); + expect(countOf([1, 2, 3])).to.equal(3, 'multi item list'); + }); + + itLeaks('should count an unknown argument as 0', async () => { + expect(countOf(undefined)).to.equal(0, 'undefined'); + // eslint-disable-next-line no-null/no-null + expect(countOf(null)).to.equal(0, 'null'); + }); + }); + + describeLeaks('filter nil', async () => { + itLeaks('should remove nil items', async () => { + // eslint-disable-next-line no-null/no-null + expect(filterNil([1, undefined, 2, null, 3])).to.deep.equal([1, 2, 3]); + }); + }); + + describeLeaks('must find helper', async () => { + itLeaks('should return matching item', async () => { + expect(mustFind([1, 2, 3], (val) => (val % 2) === 0)).to.equal(2); + }); + + itLeaks('should throw if no item matches', async () => { + expect(() => { + mustFind([1, 2, 3], (val) => val === 4); + }).to.throw(NotFoundError); + }); + }); +}); diff --git a/yarn.lock b/yarn.lock index 4b1e260..3ef0094 100644 --- a/yarn.lock +++ b/yarn.lock @@ -373,6 +373,11 @@ resolved "https://artifacts.apextoaster.com/repository/group-npm/@types/json-schema/-/json-schema-7.0.3.tgz#bdfd69d61e464dcc81b25159c270d75a73c1a636" integrity sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A== +"@types/lodash@^4.14.149": + version "4.14.149" + resolved "https://artifacts.apextoaster.com/repository/group-npm/@types/lodash/-/lodash-4.14.149.tgz#1342d63d948c6062838fbf961012f74d4e638440" + integrity sha512-ijGqzZt/b7BfzcK9vTrS6MFljQRPn5BFWOx8oE0GYxribu6uV+aA9zZuXI1zc/etK9E8nrgdoF2+LgUw7+9tJQ== + "@types/mocha@7.0.2": version "7.0.2" resolved "https://artifacts.apextoaster.com/repository/group-npm/@types/mocha/-/mocha-7.0.2.tgz#b17f16cf933597e10d6d78eae3251e692ce8b0ce" @@ -388,6 +393,11 @@ resolved "https://artifacts.apextoaster.com/repository/group-npm/@types/node/-/node-10.17.13.tgz#ccebcdb990bd6139cd16e84c39dc2fb1023ca90c" integrity sha512-pMCcqU2zT4TjqYFrWtYHKal7Sl30Ims6ulZ4UFXxI4xbtQqK/qqKwkDoBFCfooRqqmRu9vY3xaJRwxSh673aYg== +"@types/node@^13.9.5": + version "13.9.5" + resolved "https://artifacts.apextoaster.com/repository/group-npm/@types/node/-/node-13.9.5.tgz#59738bf30b31aea1faa2df7f4a5f55613750cf00" + integrity sha512-hkzMMD3xu6BrJpGVLeQ3htQQNAcOrJjX7WFmtK8zWQpz2UJf13LCFF2ALA7c9OVdvc2vQJeDdjfR35M0sBCxvw== + "@types/resolve@0.0.8": version "0.0.8" resolved "https://artifacts.apextoaster.com/repository/group-npm/@types/resolve/-/resolve-0.0.8.tgz#f26074d238e02659e323ce1a13d041eee280e194" @@ -2624,6 +2634,11 @@ node-preload@^0.2.0: dependencies: process-on-spawn "^1.0.0" +noicejs@^3.0.1: + version "3.0.1" + resolved "https://artifacts.apextoaster.com/repository/group-npm/noicejs/-/noicejs-3.0.1.tgz#7e48547f0db5025f291074867016f760b502bb82" + integrity sha512-7qABkA6Xi0EXauRDirZSRP7WRBY7AVwm2ajMybNxRPWQP6ZBL7jk4s7RqN+VzF6w+kdaX1mdTuCdWeHGzEpXoQ== + normalize-package-data@^2.3.0, normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package-data@^2.3.5: version "2.5.0" resolved "https://artifacts.apextoaster.com/repository/group-npm/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"