Base is the foundation of all travetto
applications. It is intended to be a minimal application bootstrap, as well as support for commonly shared functionality. The key areas that it offers
This is a programmatic interface to package.json
, which provides key information on:
- Application Name
- Version
- Package
- Development Dependencies
- ...more
The framework does a bit of file system scanning to auto load files, and to have knowledge of what files are available. The tools provide:
- Very simple functionality for recursively finding files, and caching the results
- Utilizes RegEx in lieu of glob for pattern matching on files (this is to minimize overall code complexity)
A simple example of finding all .config
files in your codebase:
function processServiceConfigs(svc: string) {
const svcConfigs = await findAppFiles('.config', file => path.basename(file).startsWith(`${svc}.`));
for (const conf of svcConfigs) {
... do work
}
}
The framework provides basic environment information, e.g. in prod/test/dev. This is useful for runtime decisions. This is primarily used by the framework, but can prove useful to application developers as well. The information that is available is:
prod: boolean
- is the application in prod modedev: boolean
- is the application in development modetest: boolean
- is the application currently in test modewatch: boolean
- is the application currently watching for file changes and reloads (normally only during development)all: string[]
- a list of all the environments that are passed in and configureddocker: boolean
- does the environment support docker, and should it use it if neededdebug: boolean
- is the application currently in debug modetrace: boolean
- is the application currently in trace modecwd: string
- what is the root folder of the application
This is centralized functionality for running operations on shutdown. Primarily used by the framework for cleanup operations, this provides a clean interface for registering shutdown handlers and awaiting shutdown to finish.
As a registered handler, you can do.
Shutdown.onShutdown('handler-name', async () => {
// Do important work
})
If knowing when shutdown finishes is all you want, you can simply use:
async function messageOnShutdown() {
await Shutdown.onShutdownPromise();
console.log('Shutdown is complete!');
}
Integration with trace.js
to handle asynchronous call stacks, and provide higher quality stack traces. The stack filtering will remove duplicate or unnecessary lines, as well as filter out framework specific steps that do not aid in debugging. The final result should be a stack trace that is concise and clear.
From a test scenario:
function test() {
setTimeout(function inner1() {
setTimeout(function inner2() {
setTimeout(function inner3() {
throw new Error('Uh oh');
}, 1);
}, 1);
}, 1);
}
test();
Will produce the following stack trace:
Error: Uh oh
at Timeout.inner3 [as _onTimeout] (./test/stack.js:6:23)
at Timeout.inner2 [as _onTimeout] (./test/stack.js:5:13)
at Timeout.inner1 [as _onTimeout] (./test/stack.js:4:9)
at Object.load [as .ts] (./bin/travetto.js:27:12)
During the lifecycle of an application, there is a need to handle different phases of execution
- Recursively finds all
phase.<phase>.ts
files undernode_modules/@travetto
, and in the root of your project - The format of each initializer is comprised of three main elements:
- The phase of execution, which is defined by the file name
phase.<phase>.ts
- The priority within the phase, a number in which lower is of higher importance
- The actual functionality to execute
- The phase of execution, which is defined by the file name
An example would be something like phase.bootstrap.ts
in the Config
module.
export const init = {
priority: 1, // Lower is of more importance, and runs first
action: () => {
require('../src/service/config').init();
}
}
The needed functionality cannot be loaded until init.action
executes, and so must be required only at that time.
Simple functions for providing a minimal facsimile to lodash
, but without all the weight. Currently util
only includes:
isPrimitive(el: any)
determines ifel
is astring
,boolean
,number
orRegExp
isPlainObject(obj: any)
determines if the obj is a simple objectisFunction(o: any)
determines ifo
is a simpleFunction
isClass(o: any)
determines ifo
is a class constructorisSimple(a: any)
determines ifa
is a simple valuedeepAssign(a: any, b: any, mode?)
which allows for deep assignment ofb
ontoa
, themode
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 fromb
to fit the types ofa
, and if it can't it will error outstrict
, will error out if the types do not match
throttle(fn, threshhold?: number)
produces a function that will executefn
, at most once perthreshold
A very simple file watching library, with a substantially smaller footprint than gaze
or chokidar
.
const watcher = new Watcher({cwd: 'base/path/to/...'});
watcher.add([
'local.config',
{
testFile: x => x.endsWith('.config') || x.endsWith('.config.json')
}
]);
watcher.run();