diff --git a/docs/api/js-yaml-schema.config_schema.md b/docs/api/js-yaml-schema.config_schema.md
new file mode 100644
index 0000000..272b833
--- /dev/null
+++ b/docs/api/js-yaml-schema.config_schema.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [@apextoaster/js-yaml-schema](./js-yaml-schema.md) > [CONFIG\_SCHEMA](./js-yaml-schema.config_schema.md)
+
+## CONFIG\_SCHEMA variable
+
+Signature:
+
+```typescript
+CONFIG_SCHEMA: Schema
+```
diff --git a/docs/api/js-yaml-schema.envtype.md b/docs/api/js-yaml-schema.envtype.md
new file mode 100644
index 0000000..86dbb85
--- /dev/null
+++ b/docs/api/js-yaml-schema.envtype.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [@apextoaster/js-yaml-schema](./js-yaml-schema.md) > [envType](./js-yaml-schema.envtype.md)
+
+## envType variable
+
+Signature:
+
+```typescript
+envType: YamlType
+```
diff --git a/docs/api/js-yaml-schema.includeschema.md b/docs/api/js-yaml-schema.includeschema.md
new file mode 100644
index 0000000..72f32bd
--- /dev/null
+++ b/docs/api/js-yaml-schema.includeschema.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@apextoaster/js-yaml-schema](./js-yaml-schema.md) > [includeSchema](./js-yaml-schema.includeschema.md)
+
+## includeSchema variable
+
+Signature:
+
+```typescript
+includeSchema: {
+ schema: import("js-yaml").Schema;
+}
+```
diff --git a/docs/api/js-yaml-schema.includetype.md b/docs/api/js-yaml-schema.includetype.md
new file mode 100644
index 0000000..8302a07
--- /dev/null
+++ b/docs/api/js-yaml-schema.includetype.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [@apextoaster/js-yaml-schema](./js-yaml-schema.md) > [includeType](./js-yaml-schema.includetype.md)
+
+## includeType variable
+
+Signature:
+
+```typescript
+includeType: YamlType
+```
diff --git a/docs/api/js-yaml-schema.md b/docs/api/js-yaml-schema.md
index c59e68c..02a7e14 100644
--- a/docs/api/js-yaml-schema.md
+++ b/docs/api/js-yaml-schema.md
@@ -4,3 +4,14 @@
## js-yaml-schema package
+## Variables
+
+| Variable | Description |
+| --- | --- |
+| [CONFIG\_SCHEMA](./js-yaml-schema.config_schema.md) | |
+| [envType](./js-yaml-schema.envtype.md) | |
+| [includeSchema](./js-yaml-schema.includeschema.md) | |
+| [includeType](./js-yaml-schema.includetype.md) | |
+| [regexpType](./js-yaml-schema.regexptype.md) | |
+| [streamType](./js-yaml-schema.streamtype.md) | |
+
diff --git a/docs/api/js-yaml-schema.regexptype.md b/docs/api/js-yaml-schema.regexptype.md
new file mode 100644
index 0000000..548324f
--- /dev/null
+++ b/docs/api/js-yaml-schema.regexptype.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [@apextoaster/js-yaml-schema](./js-yaml-schema.md) > [regexpType](./js-yaml-schema.regexptype.md)
+
+## regexpType variable
+
+Signature:
+
+```typescript
+regexpType: YamlType
+```
diff --git a/docs/api/js-yaml-schema.streamtype.md b/docs/api/js-yaml-schema.streamtype.md
new file mode 100644
index 0000000..f8c17cb
--- /dev/null
+++ b/docs/api/js-yaml-schema.streamtype.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [@apextoaster/js-yaml-schema](./js-yaml-schema.md) > [streamType](./js-yaml-schema.streamtype.md)
+
+## streamType variable
+
+Signature:
+
+```typescript
+streamType: YamlType
+```
diff --git a/package.json b/package.json
index a79a2bb..091a8bc 100644
--- a/package.json
+++ b/package.json
@@ -11,6 +11,7 @@
"author": "ssube",
"license": "MIT",
"devDependencies": {
+ "@apextoaster/js-utils": "^0.1.1",
"@istanbuljs/nyc-config-typescript": "1.0.1",
"@microsoft/api-documenter": "7.7.15",
"@microsoft/api-extractor": "7.7.10",
diff --git a/src/error/InvalidArgumentError.ts b/src/error/InvalidArgumentError.ts
deleted file mode 100644
index 463fe29..0000000
--- a/src/error/InvalidArgumentError.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import { BaseError } from 'noicejs';
-
-export class InvalidArgumentError extends BaseError {
- constructor(msg = 'invalid argument passed', ...nested: Array) {
- super(msg, ...nested);
- }
-}
diff --git a/src/error/NotFoundError.ts b/src/error/NotFoundError.ts
deleted file mode 100644
index d5b179c..0000000
--- a/src/error/NotFoundError.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import { BaseError } from 'noicejs';
-
-export class NotFoundError extends BaseError {
- constructor(msg = 'value not found', ...nested: Array) {
- super(msg, ...nested);
- }
-}
diff --git a/src/index.ts b/src/index.ts
index 154ca87..cc9573a 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,5 +1,11 @@
import { main } from './app';
+export { CONFIG_SCHEMA } from './schema';
+export { envType } from './type/Env';
+export { includeSchema, includeType } from './type/Include';
+export { regexpType } from './type/Regexp';
+export { streamType } from './type/Stream';
+
const STATUS_ERROR = 1;
/**
diff --git a/src/schema.ts b/src/schema.ts
index 283cb19..4e6314a 100644
--- a/src/schema.ts
+++ b/src/schema.ts
@@ -1,7 +1,7 @@
import { DEFAULT_SAFE_SCHEMA, Schema } from 'js-yaml';
import { envType } from './type/Env';
-import { includeType } from './type/Include';
+import { includeSchema, includeType } from './type/Include';
import { regexpType } from './type/Regexp';
import { streamType } from './type/Stream';
@@ -11,3 +11,5 @@ export const CONFIG_SCHEMA = Schema.create([DEFAULT_SAFE_SCHEMA], [
regexpType,
streamType,
]);
+
+includeSchema.schema = CONFIG_SCHEMA;
diff --git a/src/type/Env.ts b/src/type/Env.ts
index d984a12..35ddde6 100644
--- a/src/type/Env.ts
+++ b/src/type/Env.ts
@@ -1,7 +1,6 @@
+import { NotFoundError } from '@apextoaster/js-utils';
import { Type as YamlType } from 'js-yaml';
-import { NotFoundError } from '../error/NotFoundError';
-
export const envType = new YamlType('!env', {
kind: 'scalar',
resolve(name: string) {
diff --git a/src/type/Include.ts b/src/type/Include.ts
index a989a8d..3d3e12c 100644
--- a/src/type/Include.ts
+++ b/src/type/Include.ts
@@ -1,10 +1,8 @@
+import { InvalidArgumentError, NotFoundError } from '@apextoaster/js-utils';
import { existsSync, readFileSync, realpathSync } from 'fs';
import { SAFE_SCHEMA, safeLoad, Type as YamlType } from 'js-yaml';
import { join } from 'path';
-import { InvalidArgumentError } from '../error/InvalidArgumentError';
-import { NotFoundError } from '../error/NotFoundError';
-
// work around the circular dependency by setting the schema later
export const includeSchema = {
schema: SAFE_SCHEMA,
diff --git a/src/type/Regexp.ts b/src/type/Regexp.ts
index c3c71f1..2c32869 100644
--- a/src/type/Regexp.ts
+++ b/src/type/Regexp.ts
@@ -1,8 +1,7 @@
+import { InvalidArgumentError } from '@apextoaster/js-utils';
import { Type as YamlType } from 'js-yaml';
import { isNil } from 'lodash';
-import { InvalidArgumentError } from '../error/InvalidArgumentError';
-
export const REGEXP_REGEXP = /^\/(.+)\/([gimsuy]*)$/;
export const regexpType = new YamlType('!regexp', {
diff --git a/src/type/Stream.ts b/src/type/Stream.ts
index bb079d0..6070372 100644
--- a/src/type/Stream.ts
+++ b/src/type/Stream.ts
@@ -1,7 +1,6 @@
+import { NotFoundError } from '@apextoaster/js-utils';
import { Type as YamlType } from 'js-yaml';
-import { NotFoundError } from '../error/NotFoundError';
-
const ALLOWED_STREAMS = new Set([
'stdout',
'stderr',
diff --git a/test/helpers/async.ts b/test/helpers/async.ts
deleted file mode 100644
index b9769be..0000000
--- a/test/helpers/async.ts
+++ /dev/null
@@ -1,155 +0,0 @@
-import { AsyncHook, createHook } from 'async_hooks';
-
-// this will pull Mocha internals out of the stacks
-/* eslint-disable-next-line @typescript-eslint/no-var-requires */
-const { stackTraceFilter } = require('mocha/lib/utils');
-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: T | null | undefined): 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();
-
- beforeEach(() => {
- tracker.enable();
- });
-
- afterEach(() => {
- tracker.disable();
- const leaked = tracker.size;
-
- // @TODO: this should only exclude the single Immediate set by the Tracker
- if (leaked > 1) {
- tracker.dump();
- const msg = `test leaked ${leaked - 1} async resources`;
- if (debugMode()) {
- throw new Error(msg);
- } else {
- /* eslint-disable-next-line no-console */
- console.warn(msg);
- }
- }
-
- tracker.clear();
- });
-
- const suite: PromiseLike | undefined = cb.call(this);
- if (isNil(suite) || !Reflect.has(suite, 'then')) {
- /* eslint-disable-next-line no-console */
- console.error(`test suite '${description}' did not return a promise`);
- }
-
- return suite;
- });
-}
-
-/**
- * Run an asynchronous test with unhandled rejection guards.
- *
- * This function may not have any direct test coverage. It is too simple to reasonably mock.
- */
-export function itLeaks(expectation: string, cb?: AsyncMochaTest): Mocha.Test {
- if (isNil(cb)) {
- return it(expectation);
- }
-
- return it(expectation, function trackTest(this: Mocha.Context) {
- return new Promise((res, rej) => {
- cb.call(this).then((value: unknown) => {
- res(value);
- }, (err: Error) => {
- rej(err);
- });
- });
- });
-}
diff --git a/test/type/TestEnv.ts b/test/type/TestEnv.ts
index 2e978e8..38a6c78 100644
--- a/test/type/TestEnv.ts
+++ b/test/type/TestEnv.ts
@@ -1,22 +1,21 @@
import { expect } from 'chai';
-import { NotFoundError } from '../../src/error/NotFoundError';
+import { NotFoundError } from '@apextoaster/js-utils';
import { envType } from '../../src/type/Env';
import { VERSION_INFO } from '../../src/version';
-import { describeLeaks, itLeaks } from '../helpers/async';
-describeLeaks('env config type', async () => {
- itLeaks('should throw on missing variables', async () => {
+describe('env config type', async () => {
+ it('should throw on missing variables', async () => {
expect(() => {
envType.resolve('DOES_NOT_EXIST_');
}).to.throw(NotFoundError);
});
- itLeaks('should resolve existing variables', async () => {
+ it('should resolve existing variables', async () => {
expect(envType.resolve('CI_COMMIT_SHA')).to.equal(true);
});
- itLeaks('should construct a value from variables', async () => {
+ it('should construct a value from variables', async () => {
expect(envType.construct('CI_COMMIT_SHA')).to.equal(VERSION_INFO.git.commit);
});
});
diff --git a/test/type/TestInclude.ts b/test/type/TestInclude.ts
index bebc357..5cf37cb 100644
--- a/test/type/TestInclude.ts
+++ b/test/type/TestInclude.ts
@@ -1,29 +1,27 @@
import { expect } from 'chai';
import { join } from 'path';
-import { InvalidArgumentError } from '../../src/error/InvalidArgumentError';
-import { NotFoundError } from '../../src/error/NotFoundError';
+import { InvalidArgumentError, NotFoundError } from '@apextoaster/js-utils';
import { includeType } from '../../src/type/Include';
-import { describeLeaks, itLeaks } from '../helpers/async';
const TEST_ROOT = '../test/type';
-describeLeaks('include config type', async () => {
- itLeaks('should resolve existing files', async () => {
+describe('include config type', async () => {
+ it('should resolve existing files', async () => {
expect(includeType.resolve(join(TEST_ROOT, 'include.yml'))).to.equal(true);
});
- itLeaks('should throw when resolving missing files', async () => {
+ it('should throw when resolving missing files', async () => {
expect(() => {
includeType.resolve(join(TEST_ROOT, 'missing.yml'));
}).to.throw(NotFoundError);
});
- itLeaks('should construct data from file', async () => {
+ it('should construct data from file', async () => {
expect(includeType.construct(join(TEST_ROOT, 'include.yml'))).to.equal('test');
});
- itLeaks('should throw when constructing missing files', async () => {
+ it('should throw when constructing missing files', async () => {
expect(() => {
includeType.construct(join(TEST_ROOT, 'missing.yml'));
}).to.throw(InvalidArgumentError);
diff --git a/test/type/TestRegexp.ts b/test/type/TestRegexp.ts
index 5a1c1b0..1727717 100644
--- a/test/type/TestRegexp.ts
+++ b/test/type/TestRegexp.ts
@@ -1,27 +1,26 @@
import { expect } from 'chai';
import { regexpType } from '../../src/type/Regexp';
-import { describeLeaks, itLeaks } from '../helpers/async';
-describeLeaks('regexp config type', async () => {
- itLeaks('match slashed strings', async () => {
+describe('regexp config type', async () => {
+ it('match slashed strings', async () => {
expect(regexpType.resolve('/foo/')).to.equal(true);
});
- itLeaks('should match flags', async () => {
+ it('should match flags', async () => {
const regexp: RegExp = regexpType.construct('/foo/g');
expect(regexp.flags).to.equal('g');
});
- itLeaks('should not match bare strings', async () => {
+ it('should not match bare strings', async () => {
expect(regexpType.resolve('foo')).to.equal(false);
});
- itLeaks('should not match invalid flags', async () => {
+ it('should not match invalid flags', async () => {
expect(regexpType.resolve('/foo/notrealflags')).to.equal(false);
});
- itLeaks('should not match regex embedded in a longer string', async () => {
+ it('should not match regex embedded in a longer string', async () => {
expect(regexpType.resolve('some/regex/with-padding')).to.equal(false);
});
});
diff --git a/yarn.lock b/yarn.lock
index debb13a..5032631 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2,6 +2,11 @@
# yarn lockfile v1
+"@apextoaster/js-utils@^0.1.1":
+ version "0.1.1"
+ resolved "https://artifacts.apextoaster.com/repository/group-npm/@apextoaster/js-utils/-/js-utils-0.1.1.tgz#035548ce76f12c1de4b9c20f9b9aeebd0c840546"
+ integrity sha512-hJZY3smbpTPdKidZIaa+VDROkCIjaEahhPqqF7YCqvFP5E/CNbwbiNqASxZjz97Dde2Dw6II8YGDV5Ef8yfO3g==
+
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.5.5":
version "7.5.5"
resolved "https://artifacts.apextoaster.com/repository/group-npm/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d"