Base
Environment config and common utilities for travetto applications.
Install: @travetto/base
npm install @travetto/base
Base is the foundation of all Travetto applications. It is intended to be a minimal application set, as well as support for commonly shared functionality. It has support for the following key areas:
- Environment Support
- Console Management
- Standard Error Support
- Stream Support
- Object Utilities
- Data Utilities
- Common Utilities
- Process Execution
- Shutdown Management
Environment Support
The functionality we support for testing and retrieving environment information:
isTrue(key: string): boolean;
- Test whether or not an environment flag is set and is trueisFalse(key: string): boolean;
- Test whether or not an environment flag is set and is falseisSet(key:string): boolean;
- Test whether or not an environment value is set (excludes: null
, ''
, and undefined
)get(key: string, def?: string): string;
- Retrieve an environmental value with a potential defaultgetInt(key: string, def?: number): number;
- Retrieve an environmental value as a numbergetList(key: string): string[];
- Retrieve an environmental value as a list
Standard Error Support
While the framework is 100 % compatible with standard Error
instances, there are cases in which additional functionality is desired. Within the framework we use AppError (or its derivatives) to represent framework errors. This class is available for use in your own projects. Some of the additional benefits of using this class is enhanced error reporting, as well as better integration with other modules (e.g. the RESTful API module and HTTP status codes).
The AppError takes in a message, and an optional payload and / or error classification. The currently supported error classifications are:
general
- General purpose errorssystem
- Synonym for general
data
- Data format, content, etc are incorrect. Generally correlated to bad input.permission
- Operation failed due to lack of permissionsauth
- Operation failed due to lack of authenticationmissing
- Resource was not found when requestedtimeout
- Operation did not finish in a timely mannerunavailable
- Resource was unresponsive
Console Management
This module provides logging functionality, built upon console operations.
The supported operations are:
console.error
which logs at the ERROR
levelconsole.warn
which logs at the WARN
levelconsole.info
which logs at the INFO
levelconsole.debug
which logs at the DEBUG
levelconsole.log
which logs at the INFO
level
Note: All other console methods are excluded, specifically trace
, inspect
, dir
, time
/timeEnd
How Logging is Instrumented
All of the logging instrumentation occurs at transpilation time. All console.*
methods are replaced with a call to a globally defined variable that delegates to the ConsoleManager. This module, hooks into the ConsoleManager and receives all logging events from all files compiled by the Travetto.
A sample of the instrumentation would be:
Code: Sample logging at various levels
export function work() {
console.debug('Start Work');
try {
1 / 0;
} catch (err) {
console.error('Divide by zero', { error: err });
}
console.debug('End Work');
}
Code: Sample After Transpilation
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.work = void 0;
const tslib_1 = require("tslib");
const ᚕ_c = tslib_1.__importStar(require("@travetto/base/src/console.js"));
var ᚕf = "@travetto/base/doc/transpile.js";
function work() {
ᚕ_c.log({ level: "debug", source: ᚕf, line: 2, scope: "work", args: ['Start Work'] });
try {
1 / 0;
}
catch (err) {
ᚕ_c.log({ level: "error", source: ᚕf, line: 7, scope: "work", args: ['Divide by zero', { error: err }] });
}
ᚕ_c.log({ level: "debug", source: ᚕf, line: 9, scope: "work", args: ['End Work'] });
}
exports.work = work;
Filtering Debug
The debug
messages can be filtered using the patterns from the debug. You can specify wild cards to only DEBUG
specific modules, folders or files. You can specify multiple, and you can also add negations to exclude specific packages.
Terminal: Sample environment flags
$ DEBUG=-@travetto/model npx trv run app
$ DEBUG=-@travetto/registry npx trv run app
$ DEBUG=@travetto/rest npx trv run app
$ DEBUG=@travetto/*,-@travetto/model npx trv run app
Additionally, the logging framework will merge debug into the output stream, and supports the standard usage
Terminal: Sample environment flags for standard usage
$ DEBUG=express:*,@travetto/rest npx trv run rest
Stream Support
The StreamUtil class provides basic stream utilities for use within the framework:
toBuffer(src: Readable | Buffer | string): Promise<Buffer>
for converting a stream/buffer/filepath to a Buffer.toReadable(src: Readable | Buffer | string):Promise<Readable>
for converting a stream/buffer/filepath to a ReadablewriteToFile(src: Readable, out: string):Promise<void>
will stream a readable into a file path, and wait for completion.waitForCompletion(src: Readable, finish:()=>Promise<any>)
will ensure the stream remains open until the promise finish produces is satisfied.
Object Utilities
Simple functions for providing a minimal facsimile to lodash, but without all the weight. Currently ObjectUtil includes:
isPrimitive(el)
determines if el
is a string
, boolean
, number
or RegExp
isPlainObject(obj)
determines if the obj is a simple objectisFunction(o)
determines if o
is a simple Function
isClass(o)
determines if o
is a class constructorisSimple(a)
determines if a
is a simple value
Data Utilities
Data utilities for binding values, and type conversion. Currently DataUtil includes:
deepAssign(a, b, mode ?)
which allows for deep assignment of b
onto a
, the mode
determines how aggressive the assignment is, and how flexible it is. mode
can have any of the following values:
loose
, which is the default is the most lenient. It will not error out, and overwrites will always happencoerce
, will attempt to force values from b
to fit the types of a
, and if it can't it will error outstrict
, will error out if the types do not match
Common Utilities
Common utilities used throughout the framework. Currently Util includes:
uuid(len: number)
generates a simple uuid for use within the application.allowDenyMatcher(rules[])
builds a matching function that leverages the rules as an allow/deny list, where order of the rules matters. Negative rules are prefixed by '!'.
Process Execution
Just like child_process, the ExecUtil exposes spawn
and fork
. These are generally wrappers around the underlying functionality. In addition to the base functionality, each of those functions is converted to a Promise
structure, that throws an error on an non-zero return status.
A simple example would be:
Code: Running a directory listing via ls
import { ExecUtil } from '@travetto/base';
export async function executeListing() {
const { result } = ExecUtil.spawn('ls');
const final = await result;
console.log('Listing', { lines: final.stdout.split('\n') });
}
As you can see, the call returns not only the child process information, but the Promise
to wait for. Additionally, some common patterns are provided for the default construction of the child process. In addition to the standard options for running child processes, the module also supports:
timeout
as the number of milliseconds the process can run before terminating and throwing an errorquiet
which suppresses all stdout/stderr outputstdin
as a string, buffer or stream to provide input to the program you are running;timeoutKill
allows for registering functionality to execute when a process is force killed by timeout
Shutdown Management
Another key lifecycle is the process of shutting down. The framework provides centralized functionality for running operations on shutdown. Primarily used by the framework for cleanup operations, this provides a clean interface for registering shutdown handlers. The code overrides process.exit
to properly handle SIGKILL
and SIGINT
, with a default threshold of 3 seconds. In the advent of a SIGTERM
signal, the code exits immediately without any cleanup.
As a registered shutdown handler, you can do.
Code: Registering a shutdown handler
import { ShutdownManager } from '@travetto/base';
export function registerShutdownHandler() {
ShutdownManager.onShutdown('handler-name', async () => {
});
}