@gasket/core
Advanced tools
Comparing version
@@ -169,3 +169,3 @@ "use strict"; | ||
* plugins to finish. | ||
* @param gasket | ||
* @param {import("@gasket/core").Gasket} gasket - The gasket instance | ||
* @param {string} event The event to execute | ||
@@ -207,3 +207,3 @@ * @param {...*} args Args for hooks | ||
* possible. | ||
* @param gasket | ||
* @param {import("@gasket/core").Gasket} gasket - The gasket instance | ||
* @param {string} event The event to execute | ||
@@ -239,3 +239,3 @@ * @param {...*} args Args for hooks | ||
* present in the map. | ||
* @param gasket | ||
* @param {import("@gasket/core").Gasket} gasket - The gasket instance | ||
* @param {string} event The event to execute | ||
@@ -283,3 +283,3 @@ * @param {...*} args Args for hooks | ||
* Like `execMap`, only all hooks must execute synchronously | ||
* @param gasket | ||
* @param {import("@gasket/core").Gasket} gasket - The gasket instance | ||
* @param {string} event The event to execute | ||
@@ -316,3 +316,3 @@ * @param {...*} args Args for hooks | ||
* to the next hook. It's like an asynchronous version of `Array.prototype.reduce`. | ||
* @param gasket | ||
* @param {import("@gasket/core").Gasket} gasket - The gasket instance | ||
* @param {string} event The event to execute | ||
@@ -351,3 +351,3 @@ * @param {any} value Value to pass to initial hook | ||
* methods whenever possible. | ||
* @param gasket | ||
* @param {import("@gasket/core").Gasket} gasket - The gasket instance | ||
* @param {string} event The event to execute | ||
@@ -382,3 +382,3 @@ * @param {any} value Value to pass to initial hook | ||
* the plugin itself. | ||
* @param gasket | ||
* @param {import("@gasket/core").Gasket} gasket - The gasket instance | ||
* @param {string} event The event to execute | ||
@@ -418,3 +418,3 @@ * @param {Function} applyFn Function to apply | ||
* Like `execApply`, only all hooks must execute synchronously. | ||
* @param gasket | ||
* @param {import("@gasket/core").Gasket} gasket - The gasket instance | ||
* @param {string} event The event to execute | ||
@@ -450,7 +450,6 @@ * @param {Function} applyFn Function to apply | ||
* @param {object} options options | ||
* @param [options.gasket] | ||
* @param options.event | ||
* @param options.type | ||
* @param options.prepare | ||
* @param options.exec | ||
* @param {string} options.event - The event to execute | ||
* @param {string} options.type - The type of execution | ||
* @param {Function} options.prepare - Prepare function | ||
* @param {Function} options.exec - Execution function | ||
* @returns {*} result | ||
@@ -457,0 +456,0 @@ */ _execWithCachedPlan({ event, type, prepare, exec }) { |
@@ -21,3 +21,3 @@ /* eslint-disable no-console, no-process-env */ "use strict"; | ||
const _utils = require("@gasket/utils"); | ||
const _branch = require("./branch.js"); | ||
const _trace = require("./trace.js"); | ||
/** | ||
@@ -36,12 +36,18 @@ * Get the environment to use for the gasket instance. | ||
class Gasket { | ||
branch() { | ||
return (0, _branch.makeBranch)(this); | ||
traceBranch() { | ||
return (0, _trace.makeTraceBranch)(this); | ||
} | ||
traceRoot() { | ||
return this; | ||
} | ||
get actions() { | ||
return this.branch().actions; | ||
// @ts-ignore -- actions from proxy | ||
return this.traceBranch().actions; | ||
} | ||
constructor(gasketConfig){ | ||
/** | ||
* @param {import('@gasket/core').GasketConfigDefinition} configDef - Gasket configuration | ||
*/ constructor(configDef){ | ||
var _config; | ||
const env = getEnvironment(); | ||
const config = (0, _utils.applyConfigOverrides)(gasketConfig, { | ||
const config = (0, _utils.applyConfigOverrides)(configDef, { | ||
env | ||
@@ -59,3 +65,3 @@ }); | ||
this[method] = (...args)=>{ | ||
return this.branch()[method](...args); | ||
return this.traceBranch()[method](...args); | ||
}; | ||
@@ -76,4 +82,4 @@ }); | ||
} | ||
function makeGasket(gasketConfigDefinition) { | ||
return new Gasket(gasketConfigDefinition); | ||
function makeGasket(configDef) { | ||
return new Gasket(configDef); | ||
} |
@@ -152,3 +152,3 @@ let dynamicNamingId = 0; | ||
* plugins to finish. | ||
* @param gasket | ||
* @param {import("@gasket/core").Gasket} gasket - The gasket instance | ||
* @param {string} event The event to execute | ||
@@ -194,3 +194,3 @@ * @param {...*} args Args for hooks | ||
* possible. | ||
* @param gasket | ||
* @param {import("@gasket/core").Gasket} gasket - The gasket instance | ||
* @param {string} event The event to execute | ||
@@ -228,3 +228,3 @@ * @param {...*} args Args for hooks | ||
* present in the map. | ||
* @param gasket | ||
* @param {import("@gasket/core").Gasket} gasket - The gasket instance | ||
* @param {string} event The event to execute | ||
@@ -273,3 +273,3 @@ * @param {...*} args Args for hooks | ||
* Like `execMap`, only all hooks must execute synchronously | ||
* @param gasket | ||
* @param {import("@gasket/core").Gasket} gasket - The gasket instance | ||
* @param {string} event The event to execute | ||
@@ -308,3 +308,3 @@ * @param {...*} args Args for hooks | ||
* to the next hook. It's like an asynchronous version of `Array.prototype.reduce`. | ||
* @param gasket | ||
* @param {import("@gasket/core").Gasket} gasket - The gasket instance | ||
* @param {string} event The event to execute | ||
@@ -347,3 +347,3 @@ * @param {any} value Value to pass to initial hook | ||
* methods whenever possible. | ||
* @param gasket | ||
* @param {import("@gasket/core").Gasket} gasket - The gasket instance | ||
* @param {string} event The event to execute | ||
@@ -382,3 +382,3 @@ * @param {any} value Value to pass to initial hook | ||
* the plugin itself. | ||
* @param gasket | ||
* @param {import("@gasket/core").Gasket} gasket - The gasket instance | ||
* @param {string} event The event to execute | ||
@@ -422,3 +422,3 @@ * @param {Function} applyFn Function to apply | ||
* Like `execApply`, only all hooks must execute synchronously. | ||
* @param gasket | ||
* @param {import("@gasket/core").Gasket} gasket - The gasket instance | ||
* @param {string} event The event to execute | ||
@@ -455,7 +455,6 @@ * @param {Function} applyFn Function to apply | ||
* @param {object} options options | ||
* @param [options.gasket] | ||
* @param options.event | ||
* @param options.type | ||
* @param options.prepare | ||
* @param options.exec | ||
* @param {string} options.event - The event to execute | ||
* @param {string} options.type - The type of execution | ||
* @param {Function} options.prepare - Prepare function | ||
* @param {Function} options.exec - Execution function | ||
* @returns {*} result | ||
@@ -462,0 +461,0 @@ */ |
@@ -5,3 +5,3 @@ /* eslint-disable no-console, no-process-env */ | ||
import { applyConfigOverrides } from '@gasket/utils'; | ||
import { makeBranch } from './branch.js'; | ||
import { makeTraceBranch } from './trace.js'; | ||
@@ -25,8 +25,13 @@ /** | ||
// TODO: Add JSDoc types | ||
/** | ||
* The Gasket class is the main entry point for the Gasket API. | ||
*/ | ||
export class Gasket { | ||
constructor(gasketConfig) { | ||
/** | ||
* @param {import('@gasket/core').GasketConfigDefinition} configDef - Gasket configuration | ||
*/ | ||
constructor(configDef) { | ||
const env = getEnvironment(); | ||
const config = applyConfigOverrides(gasketConfig, { env }); | ||
const config = applyConfigOverrides(configDef, { env }); | ||
config.env = env; | ||
@@ -46,3 +51,3 @@ config.root ??= process.cwd(); | ||
this[method] = (...args) => { | ||
return this.branch()[method](...args); | ||
return this.traceBranch()[method](...args); | ||
}; | ||
@@ -66,18 +71,23 @@ }); | ||
branch() { | ||
return makeBranch(this); | ||
traceBranch() { | ||
return makeTraceBranch(this); | ||
} | ||
traceRoot() { | ||
return this; | ||
} | ||
get actions() { | ||
return this.branch().actions; | ||
// @ts-ignore -- actions from proxy | ||
return this.traceBranch().actions; | ||
} | ||
} | ||
// TODO: Add JSDoc types | ||
/** | ||
* | ||
* @param gasketConfigDefinition | ||
* Make a new Gasket instance. | ||
* @param {import('@gasket/core').GasketConfigDefinition} configDef - Gasket configuration | ||
* @returns {Gasket} gasket instance | ||
*/ | ||
export function makeGasket(gasketConfigDefinition) { | ||
return new Gasket(gasketConfigDefinition); | ||
export function makeGasket(configDef) { | ||
return new Gasket(configDef); | ||
} |
@@ -118,7 +118,7 @@ declare module '@gasket/core' { | ||
new (config: GasketConfigDefinition): Gasket | ||
branch(): GasketBranch | ||
root(): Gasket | ||
traceBranch(): GasketTrace | ||
traceRoot(): Gasket | ||
} | ||
export interface GasketBranch extends Gasket {} | ||
export type GasketTrace = Proxy<Gasket>; | ||
@@ -147,2 +147,1 @@ type PartialRecursive<T> = T extends Object | ||
} | ||
{ | ||
"name": "@gasket/core", | ||
"version": "7.0.0-next.59", | ||
"version": "7.0.0-next.60", | ||
"description": "Entry point to setting up Gasket instances", | ||
@@ -89,5 +89,15 @@ "type": "module", | ||
"no-sync": 0 | ||
} | ||
}, | ||
"overrides": [ | ||
{ | ||
"files": [ | ||
"test/**/*.js" | ||
], | ||
"rules": { | ||
"jsdoc/require-jsdoc": "off" | ||
} | ||
} | ||
] | ||
}, | ||
"gitHead": "3bace117422475f9e5239728e1870658ac97de52" | ||
"gitHead": "ea9feae6cd291d4361c06e09659e50dbec19bcea" | ||
} |
197
README.md
@@ -46,4 +46,4 @@ # @gasket/core | ||
1. [init] | ||
2. [actions] | ||
3. [configure] | ||
2. [configure] | ||
2. [ready] | ||
@@ -73,2 +73,76 @@ ### init | ||
recommended. | ||
Instead, a plugin can register [actions] that can be executed to retrieve | ||
values the plugin wishes to make available. | ||
### configure | ||
The `configure` lifecycle is the first lifecycle executed when a Gasket is | ||
instantiated. | ||
This allows any [registered plugins] to adjust the configuration before further | ||
lifecycles are executed. | ||
```js | ||
// gasket-plugin-example.js | ||
const name = 'gasket-plugin-example'; | ||
const hooks = { | ||
configure(gasket, gasketConfig) { | ||
// Modify the configuration | ||
return { | ||
...gasketConfig, | ||
example: true | ||
}; | ||
} | ||
}; | ||
export default { name, hooks }; | ||
``` | ||
In this example, we register an action `getDoodads` that will only execute if the | ||
`example` configuration is set to `true`. | ||
It will then execute the `doodads` lifecycle, allowing any registered plugin to | ||
provide doodads. | ||
### ready | ||
The `ready` lifecycle is the last lifecycle executed and is used to signal that | ||
the Gasket instance is fully initialized and ready to be used. | ||
```js | ||
// gasket-plugin-example.js | ||
const name = 'gasket-plugin-example'; | ||
const hooks = { | ||
async ready(gasket) { | ||
console.log('Gasket is ready!'); | ||
} | ||
}; | ||
export default { name, hooks }; | ||
``` | ||
## Actions | ||
Plugins can register actions that can be fired by the application code | ||
where the Gasket is imported, or in other plugins. | ||
```js | ||
// gasket-plugin-example.js | ||
const name = 'gasket-plugin-example'; | ||
const actions = { | ||
async getDoodads(gasket) { | ||
if (gasket.config.example) { | ||
const dodaads = await gasket.exec('dodaads'); | ||
return dodaads.flat() | ||
} | ||
} | ||
}; | ||
export default { name, actions }; | ||
``` | ||
If a plugin needs to make properties available to other plugins, it should | ||
@@ -82,4 +156,10 @@ register an action that can be executed to retrieve the value. | ||
let _initializedTime; | ||
+ let _initializedTime; | ||
+ const actions = { | ||
+ getInitializedTime() { | ||
+ return _initializedTime; | ||
+ } | ||
+ }; | ||
const hooks = { | ||
@@ -90,79 +170,96 @@ init(gasket) { | ||
}, | ||
+ actions() { | ||
+ return { | ||
+ getInitializedTime() { | ||
+ return _initializedTime; | ||
+ } | ||
+ } | ||
+ }, | ||
configure(gasket) { | ||
- const time = gasket.initializedTime; | ||
+ const time = gasket.actions.getInitializedTime(); | ||
// do something with time... | ||
} | ||
}; | ||
export default { name, hooks }; | ||
- export default { name, hooks }; | ||
+ export default { name, actions, hooks }; | ||
``` | ||
### actions | ||
## Debugging | ||
The `actions` lifecycle is the second lifecycle executed when a Gasket is created. | ||
This will let plugins register actions that can be fired by the application code | ||
where the Gasket is imported, or in other plugins. | ||
Gasket makes use of the [debug] module to provide various debug outputs. Gasket | ||
packages and plugins use the `gasket` namespace. | ||
```js | ||
// gasket-plugin-example.js | ||
```shell | ||
DEBUG=gasket:* npm run start | ||
``` | ||
const name = 'gasket-plugin-example'; | ||
### Tracing | ||
const hooks = { | ||
actions(gasket) { | ||
return { | ||
async getDoodads() { | ||
if(gasket.config.example) { | ||
const dodaads = await gasket.exec('dodaads'); | ||
return dodaads.flat() | ||
} | ||
} | ||
} | ||
} | ||
}; | ||
You can narrow down to see the action and lifecycle execution order in the | ||
console output under the `gasket:trace` namespace. | ||
export default { name, hooks }; | ||
```shell | ||
DEBUG=gasket:trace* npm run start | ||
``` | ||
### configure | ||
 | ||
The `configure` lifecycle is the first lifecycle executed when a Gasket is | ||
instantiated. | ||
This allows any [registered plugins] to adjust the configuration before further | ||
lifecycles are executed. | ||
The following symbols indicate the step and type of execution: | ||
- `⋌` New Trace Branch | ||
- `★` Action Start | ||
- `◆` Synchronous Lifecycle Start | ||
- `◇` Asynchronous Lifecycle Start | ||
- `↪` Plugin lifecycle Hook | ||
When ever the app or a plugin executes a lifecycle or an action, it will be | ||
passed a traceable proxy object, which can be used to follow the execution | ||
path of the application. | ||
Any action or lifecycle that is executed from the root `gasket` object will | ||
start a new trace "branch". | ||
New branches can be created by calling `gasket.traceBranch()` to help debug | ||
certain lifecycle flows. | ||
Additionally, it is possible to start fresh traces by calling | ||
`gasket.traceRoot()`. | ||
This method should will exit the current branch's trace history | ||
and start a fresh. | ||
Use this sparingly only for situations such as tracing handling for new requests. | ||
## Recursion Protection | ||
Gasket uses the trace history to catch and prevent infinite recursion. | ||
If a lifecycle is executed more than once in the same trace history, | ||
it will throw an error and halt the execution. | ||
While it is ok to execute the same action at various steps in an event chain, | ||
you must avoid calling the same **lifecycle** from within itself. | ||
Memoization can help avoid this issue, and using `req` as a key can help for | ||
request-specific memoization, which is also a good performance optimization. | ||
```js | ||
// gasket-plugin-example.js | ||
const reqMap = new WeakMap(); | ||
const name = 'gasket-plugin-example'; | ||
const hooks = { | ||
configure(gasket, gasketConfig) { | ||
// Modify the configuration | ||
return { | ||
...gasketConfig, | ||
example: true | ||
}; | ||
const actions = { | ||
async getDoodads(gasket, req) { | ||
if(!reqMap.has(req)) { | ||
const doodads = await gasket.exec('doodads', req); | ||
reqMap.set(req, doodads); | ||
} | ||
return reqMap.get(req); | ||
} | ||
}; | ||
const hooks = { | ||
// ... | ||
}; | ||
export default { name, hooks }; | ||
export default { name, actions, hooks }; | ||
``` | ||
In this example, we register an action `getDoodads` that will only execute if the | ||
`example` configuration is set to `true`. | ||
It will then execute the `doodads` lifecycle, allowing any registered plugin to | ||
provide doodads. | ||
[init]: #init | ||
[configure]: #configure | ||
[ready]: #ready | ||
[actions]: #actions | ||
[configure]: #configure | ||
[registered plugins]: #registered-plugins | ||
[Plugins Guide]:/packages/gasket-cli/docs/plugins.md | ||
[debug]:https://github.com/debug-js/debug |
74001
12.69%13
8.33%1729
7.59%262
58.79%