Package Exports
- hyperproc
- hyperproc/index.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (hyperproc) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
HyperProc
Chainable, async pipeline over an explicit state object.
- Linear ops:
applyTo,transform,augment,noop/log,chain - Per-instance
envinjected into every op - Clear error semantics via
HyperProcError - Subclassable policy (e.g., bubbling vs. swallowing errors)
Default error policy:
onErrorlogs and returns the last good state (swallow). Override or subclass to bubble.
Table of Contents
Why
You often need a precise, deterministic pipeline over a single state object, with explicit control of mutation vs. replacement and predictable error behavior. HyperProc gives you that with a minimal surface.
Quick Start
import HyperProc from "hyperproc"; // adjust name/path if different
const hp = HyperProc.init({ inc: n => n + 1 });
const out = await hp
.augment("a", async () => 1) // state.a = 1
.transform("a", async (v) => v + 1) // state.a = 2
.applyTo(async (s, env) => ({ ...s, b: env.inc(1) })) // replace state (must be a plain object)
.log(s => `a=${s.a}, b=${s.b}`)
.run({}); // => { a: 2, b: 2 }Semantics
State contract: state is a plain object (prototype is
Object.prototypeornull). Replacement steps (applyTo,chain) must also return a plain object.Mutation model:
transform(id, f)updates an existing key (throws if missing).augment(id, f)creates a new key (throws if it already exists).applyTo(f)may replace the state; result is validated.
Env: per-instance object passed into every op.
Chaining:
chain(child)runs the child pipeline using the child’senvandonError.Errors: any thrown error inside ops is normalized to
HyperProcErrorbefore youronErrorruns.
API
Types: STATE := PlainObject, ENV := Object, ID := String, VALUE := any.
T* means T | Promise<T>.
Constructor & Statics
new HyperProc :: ENV -> this
HyperProc.init :: ENV -> this // subclass-friendly factory
HyperProc.isState :: * -> BOOL // plain-object check
APPLY_TO, TRANSFORM, AUGMENT, CHAIN, NOOP :: SymbolInstance methods
applyTo :: (STATE, ENV) -> STATE* -> this
transform :: ID -> (VALUE, STATE, ENV) -> VALUE* -> this
augment :: ID -> (STATE, ENV) -> VALUE* -> this
noop :: (STATE, ENV) -> any* -> this
log :: (STATE, ENV) -> STRING* | STRING -> this
onError :: (HyperProcError, STATE, ENV) -> (STATE | undefined)* -> this
chain :: HyperProc -> this
run :: STATE -> Promise<STATE>Operational notes
applyTovalidates its return; throws if not a plain object.transformrequires the key to exist; throws if missing.augmentrequires the key to not exist; throws if present.chainuses the child’senvandonError; the parent validates the child’s returned state (must be plain object).runthrows if the input is not a plain object.onErrorcontract:- return
STATE→ recover with that state (must be plain object). - return
undefined→ keep the last good state (swallow). - throw → bubble to caller (or to parent pipeline).
- return
Errors (HyperProcError)
Normalized error passed to your onError and used for bubbling.
Core fields:
name: "HyperProcError"message: stringop: Symbol | undefined(serialized to a string; absent appears as"UNDEFINED"intoJSON())id: string | undefinedcause?: Error(nativeError.causeused; stack chain preserved)statemay be attached as non-enumerable (not emitted viatoJSON())
Example handling:
try {
await HyperProc.init()
.applyTo(() => { throw new TypeError("bad"); })
.run({});
} catch (e) {
if (e.name === "HyperProcError") {
console.error(e.message, String(e.op), e.id, e.cause?.name);
}
}Examples
Error recovery (override default swallow)
const out = await HyperProc.init()
.augment("n", () => 5)
.transform("n", (v) => { if (v > 3) throw new Error("too big"); return v; })
.onError((err, s) => ({ ...s, n: 3, recovered: true }))
.run({});
// => { n: 3, recovered: true }Bubbling child via subclass
import HyperProc from "hyperproc";
export class HyperSubProc extends HyperProc {
constructor(env = {}) { super(env); this._onError = (err) => { throw err; }; }
}
const child = HyperSubProc.init()
.augment("x", () => 1)
.applyTo(() => { throw new Error("boom"); }); // bubbles
const parent = HyperProc.init()
.chain(child)
.onError((err, s) => ({ ...s, parentRecovered: true }));
const res = await parent.run({});
// => { x: 1, parentRecovered: true }ETL-style
const etl = HyperProc.init({ parse: JSON.parse })
.applyTo((s) => ({ raw: '{"a":1,"b":2}', ...s }))
.augment("doc", (s, env) => env.parse(s.raw))
.augment("sum", (s) => s.doc.a + s.doc.b);
await etl.run({}); // => { raw:'...', doc:{a:1,b:2}, sum:3 }Notes
- Reusing the same instance accumulates ops; treat an instance as a small program.
- In a chain, the parent continues after the child completes; subsequent parent ops see the child’s mutations/replacement.
- To assert on thrown errors in tests, use a bubbling policy (
.onError(e => { throw e; })or a subclass likeHyperSubProc). - Docs were created by API. Because that's the world we live in now...
TODO
- pause :: NUMBER, (state, env) -> PROMISE(STRING)|STRING|VOID -> this
- startTimer :: (state, env) -> PROMISE(STRING)-> this
- stopTimer :: (timeElapsed, state, env) -> PROMISE(STRING) -> this
- branch :: (state, env) -> PROMISE(BOOL), {TRUE:HYPERPROC|VOID, FALSE:HYPERPROCVOID} -> this
- batch :: [HyperProc], (([STATE], STATE, ENV) -> STATE*) ?, { clone?: 'structured'|'shallow'|(STATE->STATE), settle?: 'fail-fast'|'all' } ? -> this
License
MIT