@logosdx/hooks 1.0.0-beta.2 → 1.0.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,75 @@
1
1
  # @logosdx/hooks
2
2
 
3
+ ## 1.0.0-beta.3
4
+
5
+ ### Major Changes
6
+
7
+ - 2f9c85c: ## Breaking Changes
8
+
9
+ ### API verbs renamed
10
+
11
+ Methods renamed to distinguish from Observer (`on`/`emit`):
12
+
13
+ | Before | After |
14
+ | ---------------------------- | -------------------------------------- |
15
+ | `engine.on(name, cb)` | `engine.add(name, cb, options?)` |
16
+ | `engine.once(name, cb)` | `engine.add(name, cb, { once: true })` |
17
+ | `engine.emit(name, ...args)` | `engine.run(name, ...args)` |
18
+
19
+ ### Callback signature changed
20
+
21
+ Callbacks now receive spread args with ctx as the last parameter instead of a context object:
22
+
23
+ **Before:**
24
+
25
+ ```ts
26
+ hooks.on("beforeRequest", async (ctx) => {
27
+ const [url, opts] = ctx.args;
28
+ ctx.setArgs([url, { ...opts, cache: "no-store" }]);
29
+ });
30
+ ```
31
+
32
+ **After:**
33
+
34
+ ```ts
35
+ hooks.add("beforeRequest", (url, opts, ctx) => {
36
+ ctx.args(url, { ...opts, cache: "no-store" });
37
+ });
38
+ ```
39
+
40
+ ### Context methods replaced
41
+
42
+ | Before | After |
43
+ | ---------------------- | ----------------------------------------------------- |
44
+ | `ctx.args` (property) | `ctx.args(...)` (method — replaces args) |
45
+ | `ctx.setArgs([...])` | `ctx.args(...)` (spread, no array wrapper) |
46
+ | `ctx.setResult(value)` | `return ctx.returns(value)` |
47
+ | `ctx.returnEarly()` | `return ctx.args(...)` or `return ctx.returns(value)` |
48
+
49
+ ### Return type renamed
50
+
51
+ `EmitResult` → `RunResult`, `earlyReturn` → `returned`:
52
+
53
+ ```ts
54
+ // Before
55
+ const { args, result, earlyReturn } = await hooks.emit("hook", data);
56
+ // After
57
+ const { args, result, returned } = await hooks.run("hook", data);
58
+ ```
59
+
60
+ ## Added
61
+
62
+ - `feat(hooks):` Sync execution via `runSync()` and `wrapSync()` for non-async hook chains
63
+ - `feat(hooks):` Priority ordering — `add(name, cb, { priority: -10 })`, lower runs first
64
+ - `feat(hooks):` `times` option — run a callback N times then auto-remove
65
+ - `feat(hooks):` Per-request ephemeral hooks via `RunOptions.append`
66
+ - `feat(hooks):` `HookContext` is now a class (exported for `instanceof` checks)
67
+
68
+ ### Patch Changes
69
+
70
+ - Updated dependencies [879cea2]
71
+ - @logosdx/utils@6.1.0-beta.1
72
+
3
73
  ## 1.0.0-beta.1
4
74
 
5
75
  ### Patch Changes
@@ -1,2 +1,2 @@
1
- var L=Object.defineProperty;var $=r=>{throw TypeError(r)};var q=(r,s,o)=>s in r?L(r,s,{enumerable:!0,configurable:!0,writable:!0,value:o}):r[s]=o;var v=(r,s,o)=>q(r,typeof s!="symbol"?s+"":s,o),F=(r,s,o)=>s.has(r)||$("Cannot "+o);var l=(r,s,o)=>(F(r,s,"read from private field"),o?o.call(r):s.get(r)),y=(r,s,o)=>s.has(r)?$("Cannot add the same private member more than once"):s instanceof WeakSet?s.add(r):s.set(r,o),b=(r,s,o,g)=>(F(r,s,"write to private field"),g?g.call(r,o):s.set(r,o),o),E=(r,s,o)=>(F(r,s,"access private method"),o);this.LogosDx=this.LogosDx||{};this.LogosDx.Hooks=function(r){"use strict";var p,d,m,f,w,H;class s extends Error{}const o=(i,e,t)=>{if((i instanceof Function?!!i():!!i)===!1)throw new s(e||"assertion failed")},g=i=>i instanceof Function,A=i=>i instanceof Object,j=async i=>{o(g(i),"fn must be a function");try{return[await i(),null]}catch(e){return[null,e]}},M=i=>{o(g(i),"fn must be a function");try{return[i(),null]}catch(e){return[null,e]}};class k extends Error{constructor(t){super(t);v(this,"hookName");v(this,"originalError")}}const C=i=>i?.constructor?.name===k.name;class D{constructor(e={}){y(this,w);y(this,p,new Map);y(this,d,new WeakMap);y(this,m);y(this,f,null);b(this,m,e.handleFail??(t=>{throw new k(t)}))}register(...e){o(e.length>0,"register() requires at least one hook name"),l(this,f)===null&&b(this,f,new Set);for(const t of e)o(typeof t=="string",`Hook name must be a string, got ${typeof t}`),l(this,f).add(t);return this}on(e,t){const c=typeof t=="function"?t:t?.callback,u=typeof t=="function"?{}:t;o(typeof e=="string",'"name" must be a string'),o(g(c)||A(t),'"cbOrOpts" must be a callback or options'),o(g(c),"callback must be a function"),E(this,w,H).call(this,e,"on");const a=l(this,p).get(e)??new Set;return a.add(c),l(this,p).set(e,a),l(this,d).set(c,u),()=>{a.delete(c)}}once(e,t){return this.on(e,{callback:t,once:!0})}async emit(e,...t){E(this,w,H).call(this,e,"emit");let c=!1;const u=l(this,p).get(e),a={args:t,removeHook(){},returnEarly(){c=!0},setArgs:n=>{o(Array.isArray(n),`setArgs: args for '${String(e)}' must be an array`),a.args=n},setResult:n=>{a.result=n},fail:(...n)=>{const h=l(this,m),S=typeof h=="function"&&h.prototype?.constructor===h,[,R]=M(()=>{if(S)throw new h(...n);h(...n)});throw R?(R instanceof k&&(R.hookName=String(e)),R):new k("ctx.fail() handler did not throw")}};if(!u||u.size===0)return{args:a.args,result:a.result,earlyReturn:!1};for(const n of u){a.removeHook=()=>u.delete(n);const h=l(this,d).get(n)??{},[,S]=await j(()=>n({...a}));if(h.once&&a.removeHook(),S&&h.ignoreOnFail!==!0)throw S;if(c)break}return{args:a.args,result:a.result,earlyReturn:c}}clear(){l(this,p).clear(),b(this,d,new WeakMap),b(this,f,null)}wrap(e,t){return o(t.pre||t.post,'wrap() requires at least one of "pre" or "post" hooks'),t.pre&&E(this,w,H).call(this,t.pre,"wrap"),t.post&&E(this,w,H).call(this,t.post,"wrap"),async(...c)=>{let u=c,a;if(t.pre){const n=await this.emit(t.pre,...u);if(u=n.args,n.earlyReturn&&n.result!==void 0)return n.result}if(a=await e(...u),t.post){const n=await this.emit(t.post,a,...u);if(n.result!==void 0)return n.result}return a}}}return p=new WeakMap,d=new WeakMap,m=new WeakMap,f=new WeakMap,w=new WeakSet,H=function(e,t){if(l(this,f)!==null&&!l(this,f).has(e)){const c=[...l(this,f)].map(String).join(", ");throw new Error(`Hook "${String(e)}" is not registered. Call register("${String(e)}") before using ${t}(). Registered hooks: ${c||"(none)"}`)}},r.HookEngine=D,r.HookError=k,r.isHookError=C,Object.defineProperty(r,Symbol.toStringTag,{value:"Module"}),r}({});
1
+ var pt=Object.defineProperty;var at=c=>{throw TypeError(c)};var ft=(c,h,l)=>h in c?pt(c,h,{enumerable:!0,configurable:!0,writable:!0,value:l}):c[h]=l;var V=(c,h,l)=>ft(c,typeof h!="symbol"?h+"":h,l),ot=(c,h,l)=>h.has(c)||at("Cannot "+l);var n=(c,h,l)=>(ot(c,h,"read from private field"),l?l.call(c):h.get(c)),w=(c,h,l)=>h.has(c)?at("Cannot add the same private member more than once"):h instanceof WeakSet?h.add(c):h.set(c,l),S=(c,h,l,$)=>(ot(c,h,"write to private field"),$?$.call(c,l):h.set(c,l),l),u=(c,h,l)=>(ot(c,h,"access private method"),l);this.LogosDx=this.LogosDx||{};this.LogosDx.Hooks=function(c){"use strict";var I,P,T,W,U,Y,z,B,G,J,K,Q,F,M,C,D,s,H,X,Z,N,tt;class h extends Error{}const l=(f,t,e)=>{if((f instanceof Function?!!f():!!f)===!1)throw new h(t||"assertion failed")},$=f=>f instanceof Function,ut=f=>f instanceof Object,ct=async f=>{l($(f),"fn must be a function");try{return[await f(),null]}catch(t){return[null,t]}},et=f=>{l($(f),"fn must be a function");try{return[f(),null]}catch(t){return[null,t]}};class j extends Error{constructor(e){super(e);V(this,"hookName");V(this,"originalError")}}const lt=f=>f?.constructor?.name===j.name;class q{constructor(){w(this,I,new Map)}get(t){return n(this,I).get(t)}set(t,e){n(this,I).set(t,e)}has(t){return n(this,I).has(t)}delete(t){return n(this,I).delete(t)}}I=new WeakMap;const rt=Symbol("early-return");class st{constructor(t,e,i,r){w(this,P);w(this,T,!1);w(this,W);w(this,U,!1);w(this,Y);w(this,z);w(this,B);V(this,"scope");S(this,Y,t),S(this,z,e),S(this,B,i),this.scope=r}args(...t){return S(this,P,t),S(this,T,!0),rt}returns(t){return S(this,W,t),S(this,U,!0),rt}fail(...t){const e=n(this,Y),i=typeof e=="function"&&e.prototype?.constructor===e,[,r]=et(()=>{if(i)throw new e(...t);e(...t)});throw r?(r instanceof j&&(r.hookName=n(this,z)),r):new j("ctx.fail() handler did not throw")}removeHook(){n(this,B).call(this)}get _argsChanged(){return n(this,T)}get _newArgs(){return n(this,P)}get _result(){return n(this,W)}get _earlyReturn(){return n(this,U)}}P=new WeakMap,T=new WeakMap,W=new WeakMap,U=new WeakMap,Y=new WeakMap,z=new WeakMap,B=new WeakMap;class nt{constructor(t,e,i,r,o){w(this,G);w(this,J);w(this,K);w(this,Q);V(this,"scope");S(this,G,t),S(this,J,e),S(this,K,i),this.scope=r,S(this,Q,o)}args(...t){n(this,Q).call(this,t)}fail(...t){const e=n(this,G),i=typeof e=="function"&&e.prototype?.constructor===e,[,r]=et(()=>{if(i)throw new e(...t);e(...t)});throw r?(r instanceof j&&(r.hookName=n(this,J)),r):new j("ctx.fail() handler did not throw")}removeHook(){n(this,K).call(this)}}G=new WeakMap,J=new WeakMap,K=new WeakMap,Q=new WeakMap;class ht{constructor(t={}){w(this,s);w(this,F,new Map);w(this,M);w(this,C,null);w(this,D,new WeakMap);S(this,M,t.handleFail??(e=>{throw new j(e)}))}register(...t){l(t.length>0,"register() requires at least one hook name"),n(this,C)===null&&S(this,C,new Set);for(const e of t)l(typeof e=="string",`Hook name must be a string, got ${typeof e}`),n(this,C).add(e);return this}add(t,e,i={}){l(typeof t=="string",'"name" must be a string'),l($(e),'"callback" must be a function'),u(this,s,H).call(this,t,"add");const r=i.priority??0,o={callback:e,options:i,priority:r},a=n(this,F).get(t)??[];let A=!1;for(let p=0;p<a.length;p++)if(a[p].priority>r){a.splice(p,0,o),A=!0;break}return A||a.push(o),n(this,F).set(t,a),()=>{const p=n(this,F).get(t);if(p){const y=p.indexOf(o);y!==-1&&p.splice(y,1)}}}async run(t,...e){u(this,s,H).call(this,t,"run");const{realArgs:i,runOptions:r}=u(this,s,tt).call(this,e);let o=i;const a=r?.scope??new q,A=n(this,F).get(t),p=A?[...A]:[];r?.append&&p.push({callback:r.append,options:{},priority:1/0});let y,g=!1;for(const k of p){const{callback:E,options:m}=k;if(u(this,s,Z).call(this,E,m)){u(this,s,N).call(this,t,k);continue}const v=()=>u(this,s,N).call(this,t,k),b=new st(n(this,M),String(t),v,a);if(m.ignoreOnFail){const[,d]=await ct(async()=>{const _=await E(...o,b);u(this,s,X).call(this,b,_,x=>{o=x},x=>{y=x},()=>{g=!0})});if(!d&&m.once&&v(),g)break;continue}const R=await E(...o,b);if(u(this,s,X).call(this,b,R,d=>{o=d},d=>{y=d},()=>{g=!0}),m.once&&v(),g)break}return{args:o,result:y,returned:g,scope:a}}runSync(t,...e){u(this,s,H).call(this,t,"runSync");const{realArgs:i,runOptions:r}=u(this,s,tt).call(this,e);let o=i;const a=r?.scope??new q,A=n(this,F).get(t),p=A?[...A]:[];r?.append&&p.push({callback:r.append,options:{},priority:1/0});let y,g=!1;for(const k of p){const{callback:E,options:m}=k;if(u(this,s,Z).call(this,E,m)){u(this,s,N).call(this,t,k);continue}const v=()=>u(this,s,N).call(this,t,k),b=new st(n(this,M),String(t),v,a);if(m.ignoreOnFail){const[,d]=et(()=>{const _=E(...o,b);u(this,s,X).call(this,b,_,x=>{o=x},x=>{y=x},()=>{g=!0})});if(!d&&m.once&&v(),g)break;continue}const R=E(...o,b);if(u(this,s,X).call(this,b,R,d=>{o=d},d=>{y=d},()=>{g=!0}),m.once&&v(),g)break}return{args:o,result:y,returned:g,scope:a}}wrap(t,e){return l(e.pre||e.post,'wrap() requires at least one of "pre" or "post" hooks'),e.pre&&u(this,s,H).call(this,e.pre,"wrap"),e.post&&u(this,s,H).call(this,e.post,"wrap"),async(...i)=>{let r=i;if(e.pre){const a=await this.run(e.pre,...r);if(r=a.args,a.returned&&a.result!==void 0)return a.result}const o=await t(...r);if(e.post){const a=await this.run(e.post,o,...r);if(a.returned)return a.result}return o}}wrapSync(t,e){return l(e.pre||e.post,'wrapSync() requires at least one of "pre" or "post" hooks'),e.pre&&u(this,s,H).call(this,e.pre,"wrapSync"),e.post&&u(this,s,H).call(this,e.post,"wrapSync"),(...i)=>{let r=i;if(e.pre){const a=this.runSync(e.pre,...r);if(r=a.args,a.returned&&a.result!==void 0)return a.result}const o=t(...r);if(e.post){const a=this.runSync(e.post,o,...r);if(a.returned)return a.result}return o}}async pipe(t,e,...i){u(this,s,H).call(this,t,"pipe");const{realArgs:r,runOptions:o}=u(this,s,tt).call(this,i),a=o?.scope??new q,A=n(this,F).get(t),p=A?[...A]:[];o?.append&&p.push({callback:o.append,options:{},priority:1/0});let y=r;const g=k=>k>=p.length?e:async()=>{const E=p[k],{callback:m,options:O}=E;if(u(this,s,Z).call(this,m,O))return u(this,s,N).call(this,t,E),g(k+1)();const b=()=>u(this,s,N).call(this,t,E),R=new nt(n(this,M),String(t),b,a,L=>{y=L}),d=g(k+1),_=m;if(O.ignoreOnFail){const[L,it]=await ct(async()=>_(d,...y,R));return O.once&&b(),it?d():L}const x=await _(d,...y,R);return O.once&&b(),x};return g(0)()}pipeSync(t,e,...i){u(this,s,H).call(this,t,"pipeSync");const{realArgs:r,runOptions:o}=u(this,s,tt).call(this,i),a=o?.scope??new q,A=n(this,F).get(t),p=A?[...A]:[];o?.append&&p.push({callback:o.append,options:{},priority:1/0});let y=r;const g=k=>k>=p.length?e:()=>{const E=p[k],{callback:m,options:O}=E;if(u(this,s,Z).call(this,m,O))return u(this,s,N).call(this,t,E),g(k+1)();const b=()=>u(this,s,N).call(this,t,E),R=new nt(n(this,M),String(t),b,a,L=>{y=L}),d=g(k+1),_=m;if(O.ignoreOnFail){const[L,it]=et(()=>_(d,...y,R));return O.once&&b(),it?d():L}const x=_(d,...y,R);return O.once&&b(),x};return g(0)()}clear(){n(this,F).clear(),S(this,C,null),S(this,D,new WeakMap)}}return F=new WeakMap,M=new WeakMap,C=new WeakMap,D=new WeakMap,s=new WeakSet,H=function(t,e){if(n(this,C)!==null&&!n(this,C).has(t)){const i=[...n(this,C)].map(String).join(", ");throw new Error(`Hook "${String(t)}" is not registered. Call register("${String(t)}") before using ${e}(). Registered hooks: ${i||"(none)"}`)}},X=function(t,e,i,r,o){if(t._earlyReturn){r(t._result),o();return}t._argsChanged&&(i(t._newArgs),e===rt&&o())},Z=function(t,e){if(e.times===void 0)return!1;const i=n(this,D).get(t)??0;return i>=e.times?!0:(n(this,D).set(t,i+1),!1)},N=function(t,e){const i=n(this,F).get(t);if(i){const r=i.indexOf(e);r!==-1&&i.splice(r,1)}},tt=function(t){const e=t[t.length-1];return ut(e)&&("append"in e&&$(e.append)||"scope"in e&&e.scope instanceof q)?{realArgs:t.slice(0,-1),runOptions:e}:{realArgs:t,runOptions:void 0}},c.HookContext=st,c.HookEngine=ht,c.HookError=j,c.HookScope=q,c.PipeContext=nt,c.isHookError=lt,Object.defineProperty(c,Symbol.toStringTag,{value:"Module"}),c}({});
2
2
  //# sourceMappingURL=bundle.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"bundle.js","sources":["../../../utils/dist/esm/validation/assert.mjs","../../../utils/dist/esm/validation/type-guards.mjs","../../../utils/dist/esm/async/attempt.mjs","../../src/index.ts"],"sourcesContent":["import { reach } from '../object-utils/index.mjs';\n/**\n * Error class for assertions that fail\n */ export class AssertError extends Error {\n}\n/**\n * Checks if an error is an AssertError.\n *\n * @param err error to check\n * @returns true if error is an AssertError\n */ export const isAssertError = (err)=>err?.constructor?.name === AssertError.name;\n/**\n * Optional value check with custom validation.\n *\n * Returns true if value is undefined/null OR if the custom check passes.\n * Useful for validating optional parameters with specific criteria.\n *\n * @param val value to check\n * @param check function or boolean to validate the value\n * @returns true if value is optional or passes the check\n *\n * @example\n * // With function check\n * function processData(data: any, timeout?: number) {\n * assert(isOptional(timeout, (t) => t > 0), 'Timeout must be positive');\n * // Process data...\n * }\n *\n * @example\n * // With boolean check\n * const isValid = validateInput(input);\n * if (isOptional(config.strict, isValid)) {\n * // Either strict mode is off or input is valid\n * processInput(input);\n * }\n *\n * @example\n * // Check optional email format\n * isOptional(user.email, (email) => email.includes('@')) // true if email is undefined or contains @\n */ export const isOptional = (val, check)=>val === undefined || val === null || (check instanceof Function ? !!check(val) : !!check);\n/**\n * Asserts that a value is true. Even though NodeJS has\n * an `assert` builtin library, this aims to bring a\n * single API for asserting across all environments.\n *\n * @param test value that is coerced to true\n * @param message error message to display when test is false\n * @param ErrorClass error class to throw\n *\n * @example\n *\n * ```ts\n * assert(true, 'this is true');\n * assert(false, 'this is false');\n * assert(() => true, 'this is true');\n * assert(() => false, 'this is false');\n * ```\n *\n * ```ts\n *\n * const SomeErrorClass = class extends Error {\n * constructor(message: string) {\n * super(message);\n * }\n * }\n *\n *\n * const someFunc = () => {\n *\n * assert(true, 'this is true', SomeErrorClass);\n * assert(false, 'this is false', SomeErrorClass);\n * assert(() => true, 'this is true', SomeErrorClass);\n * assert(() => false, 'this is false', SomeErrorClass);\n *\n * // some logic\n * }\n *\n * someFunc();\n * ```\n */ export const assert = (test, message, ErrorClass)=>{\n const check = test instanceof Function ? !!test() : !!test;\n if (check === false) {\n throw new (ErrorClass || AssertError)(message || 'assertion failed');\n }\n};\n/**\n * Asserts the values in an object based on the provided assertions.\n * The assertions are a map of paths to functions that return a tuple\n * of a boolean and a message. This is intended to be used for testing\n * and validation when there is no schema validator available.\n *\n *\n * @param obj\n * @param assertions\n *\n * @example\n *\n * const obj = {\n * a: 1,\n * b: 'hello',\n * c: { d: 2 }\n * }\n *\n * assertObject(obj, {\n * a: (val) => [val === 1, 'a should be 1'],\n * b: (val) => [val === 'hello', 'b should be hello'],\n * c: [\n * (val) => [!!val, 'c should not be empty'],\n * (val) => [isObject(val), 'c should be an object']\n * ],\n * 'c.d': (val) => [isOptional(val, v === 2), 'c.d should be 2']\n * });\n */ export const assertObject = (obj, assertions)=>{\n const tests = [];\n for(const path in assertions){\n const val = reach(obj, path);\n const test = assertions[path];\n if (test === undefined) {\n throw new Error(`assertion for path ${path} is undefined`);\n }\n if (test instanceof Array) {\n for (const t of test){\n tests.push([\n val,\n t\n ]);\n }\n continue;\n }\n tests.push([\n val,\n test\n ]);\n }\n for (const [val, test] of tests){\n const res = test(val);\n assert(res instanceof Array, `assertion did not return a tuple [boolean, string]`);\n const [check, message] = res;\n assert(check, message);\n }\n};\n/**\n * Asserts only if value is not undefined.\n *\n * Provides conditional assertion that only executes when the value is defined.\n * Useful for validating optional parameters or properties.\n *\n * @param val value to test\n * @param test assertion test\n * @param message error message\n * @param ErrorClass error class to throw\n *\n * @example\n * function processUser(user: User, options?: ProcessOptions) {\n * // Only assert options if they are provided\n * assertOptional(options, isObject(options), 'Options must be an object');\n *\n * // Process user...\n * }\n *\n * @example\n * const config = getConfig();\n * assertOptional(config.timeout, config.timeout > 0, 'Timeout must be positive');\n */ export const assertOptional = (val, ...rest)=>{\n if (val !== undefined) {\n assert(...rest);\n }\n};\n","/**\n * Checks if value is non-iterable by testing if it can be iterated over.\n *\n * Uses Symbol.iterator to determine if a value is iterable. Returns true\n * for values that cannot be iterated (null, undefined, primitives).\n *\n * @param val value to check for iterability\n * @returns true if value is not iterable, false if it can be iterated\n *\n * @example\n * isNonIterable(null) // true\n * isNonIterable('string') // true\n * isNonIterable([1,2,3]) // false\n * isNonIterable(new Set()) // false\n */ export const isNonIterable = (val)=>{\n // null and undefined are not iterable\n if (val === null || val === undefined) {\n return true;\n }\n // Check if value has Symbol.iterator property\n return !val[Symbol.iterator];\n};\n/**\n * Checks if value is a primitive type.\n *\n * @param val value to check\n * @returns true if value is a primitive type\n *\n * @example\n * isPrimitive(null) // true\n * isPrimitive(undefined) // true\n * isPrimitive('string') // true\n * isPrimitive(1) // true\n * isPrimitive(true) // true\n * isPrimitive(Symbol('symbol')) // true\n * isPrimitive(new Date()) // false\n * isPrimitive(new RegExp('')) // false\n * isPrimitive(new Error('')) // false\n * isPrimitive(new Set()) // false\n * isPrimitive(new Map()) // false\n * isPrimitive(new Array()) // true\n * isPrimitive(new Object()) // true\n */ export const isPrimitive = (val)=>val === null || val === undefined || typeof val === 'string' || typeof val === 'number' || typeof val === 'boolean' || typeof val === 'symbol' || typeof val === 'bigint';\n/**\n * Checks if value is a type that does not have a constructor.\n *\n * Tests for null and undefined values which lack constructors.\n * Useful for deep comparison and cloning operations.\n *\n * @param val value to check\n * @returns true if value has no constructor (null or undefined)\n *\n * @example\n * hasNoConstructor(null) // true\n * hasNoConstructor(undefined) // true\n * hasNoConstructor({}) // false\n * hasNoConstructor('string') // false\n * hasNoConstructor(42) // false\n */ export const hasNoConstructor = (val)=>val === null || val === undefined;\n/**\n * Checks if both values have the same constructor.\n *\n * Compares the constructor property of two values to determine if they\n * are instances of the same class or type.\n *\n * @param value first value to compare\n * @param compare second value to compare\n * @returns true if both values have the same constructor\n *\n * @example\n * hasSameConstructor([], [1, 2, 3]) // true (both Arrays)\n * hasSameConstructor({}, { a: 1 }) // true (both Objects)\n * hasSameConstructor([], {}) // false (Array vs Object)\n * hasSameConstructor(new Date(), new Date()) // true (both Dates)\n * hasSameConstructor('string', 42) // false (String vs Number)\n */ export const hasSameConstructor = (value, compare)=>hasNoConstructor(value) === false && hasNoConstructor(compare) === false && value.constructor === compare.constructor;\n/**\n * Checks if both values have the same length or size.\n *\n * Compares the length property for arrays or size property for Sets.\n * Both iterables must be the same data type (both arrays or both Sets).\n * Useful for validating collections before performing operations.\n *\n * @param a first collection (array or Set)\n * @param b second collection (array or Set) - must be same type as `a`\n * @returns true if both collections have the same length/size\n *\n * @example\n * isSameLength([1, 2, 3], ['a', 'b', 'c']) // true\n * isSameLength([1, 2], [1, 2, 3]) // false\n * isSameLength(new Set([1, 2]), new Set(['a', 'b'])) // true\n * isSameLength(new Set([1, 2]), new Set([1, 2, 3])) // false\n */ export const isSameLength = (a, b)=>a.length === b.length && a.size === b.size;\n/**\n * Checks if value is a function.\n *\n * Uses instanceof to test if the value is a Function.\n * More reliable than typeof for all function types.\n *\n * @param a value to check\n * @returns true if value is a function\n *\n * @example\n * isFunction(() => {}) // true\n * isFunction(function() {}) // true\n * isFunction(async () => {}) // true\n * isFunction(class MyClass {}) // true\n * isFunction('string') // false\n * isFunction({}) // false\n */ export const isFunction = (a)=>a instanceof Function;\n/**\n * Checks if value is an object.\n *\n * Uses instanceof to test if the value is an Object.\n * Returns true for objects, arrays, functions, dates, etc.\n *\n * @param a value to check\n * @returns true if value is an object\n *\n * @example\n * isObject({}) // true\n * isObject([]) // true\n * isObject(new Date()) // true\n * isObject(() => {}) // true\n * isObject('string') // false\n * isObject(null) // false\n */ export const isObject = (a)=>a instanceof Object;\nconst commonObjects = new Set([\n Date,\n RegExp,\n Function,\n Error,\n EvalError,\n RangeError,\n ReferenceError,\n SyntaxError,\n TypeError,\n URIError,\n AggregateError,\n DOMException,\n Array,\n Set,\n Map,\n WeakMap,\n WeakSet,\n Promise,\n Proxy,\n Symbol,\n BigInt,\n WeakRef,\n FinalizationRegistry,\n DataView,\n ArrayBuffer,\n Int8Array,\n Uint8Array,\n Uint8ClampedArray,\n Int16Array,\n Uint16Array,\n Int32Array,\n Uint32Array,\n Float32Array,\n Float64Array,\n BigInt64Array,\n BigUint64Array,\n FormData,\n URLSearchParams\n]);\n/**\n * Checks if value is an uncommon object. Used to determine if a value\n * is a user defined object.\n *\n * @param a value to check\n * @returns true if value is an uncommon object\n *\n * @example\n *\n * // Returns false for common objects\n * isPlainObject(new Date()) // false\n * isPlainObject(new RegExp('')) // false\n * isPlainObject(new Function()) // false\n * isPlainObject(new Error()) // false\n * isPlainObject(new Array()) // false\n * isPlainObject(new Set()) // false\n *\n * // Returns true for uncommon objects\n * isPlainObject({}) // true\n * isPlainObject(new MyClass()) // true\n *\n */ export const isPlainObject = (a)=>!isPrimitive(a) && isObject(a) && !commonObjects.has(a.constructor);\n/**\n * Checks if value is specifically undefined.\n *\n * Strict equality check for undefined values.\n * More explicit than checking truthiness.\n *\n * @param val value to check\n * @returns true if value is exactly undefined\n *\n * @example\n * isUndefined(undefined) // true\n * isUndefined(null) // false\n * isUndefined('') // false\n * isUndefined(0) // false\n *\n * let x;\n * isUndefined(x) // true\n */ export const isUndefined = (val)=>val === undefined;\n/**\n * Checks if value is specifically not undefined.\n *\n * Inverse of isUndefined. Returns true for all values except undefined,\n * including null, false, 0, and empty strings.\n *\n * @param val value to check\n * @returns true if value is not undefined\n *\n * @example\n * isDefined(null) // true\n * isDefined(0) // true\n * isDefined('') // true\n * isDefined(false) // true\n * isDefined(undefined) // false\n *\n * const config = getConfig();\n * if (isDefined(config.apiKey)) {\n * // Safe to use config.apiKey\n * }\n */ export const isDefined = (val)=>val !== undefined;\n/**\n * Checks if value is specifically null.\n *\n * Strict equality check for null values.\n * More explicit than checking truthiness.\n *\n * @param val value to check\n * @returns true if value is exactly null\n *\n * @example\n * isNull(null) // true\n * isNull(undefined) // false\n * isNull('') // false\n * isNull(0) // false\n *\n * const result = findUser(id);\n * if (isNull(result)) {\n * // User was explicitly not found\n * }\n */ export const isNull = (val)=>val === null;\n","import { assert, isFunction } from '../validation/index.mjs';\n/**\n * Error tuple, go-style.\n *\n * @param fn async function to run\n *\n * @example\n *\n * const [result, error] = await attempt(async () => {\n * return 'hello';\n * });\n *\n * if (error) {\n * console.error(error);\n * }\n *\n * console.log(result);\n */ export const attempt = async (fn)=>{\n assert(isFunction(fn), 'fn must be a function');\n try {\n return [\n await fn(),\n null\n ];\n } catch (e) {\n return [\n null,\n e\n ];\n }\n};\n/**\n * Synchronous error tuple, go-style.\n *\n * @example\n *\n * const [result, error] = attemptSync(() => {\n * return 'hello';\n * });\n *\n * if (error) {\n * console.error(error);\n * }\n *\n * console.log(result);\n */ export const attemptSync = (fn)=>{\n assert(isFunction(fn), 'fn must be a function');\n try {\n return [\n fn(),\n null\n ];\n } catch (e) {\n return [\n null,\n e\n ];\n }\n};\n","import {\n assert,\n AsyncFunc,\n attempt,\n attemptSync,\n FunctionProps,\n isFunction,\n isObject\n} from '@logosdx/utils';\n\n/**\n * Error thrown when a hook calls `ctx.fail()`.\n *\n * This error is only created when using the default `handleFail` behavior.\n * If a custom `handleFail` is provided, that error type is thrown instead.\n *\n * @example\n * hooks.on('validate', async (ctx) => {\n * if (!ctx.args[0].isValid) {\n * ctx.fail('Validation failed');\n * }\n * });\n *\n * const [, err] = await attempt(() => engine.emit('validate', data));\n * if (isHookError(err)) {\n * console.log(err.hookName); // 'validate'\n * }\n */\nexport class HookError extends Error {\n\n /** Name of the hook where the error occurred */\n hookName?: string;\n\n /** Original error if `fail()` was called with an Error instance */\n originalError?: Error;\n\n constructor(message: string) {\n\n super(message)\n }\n}\n\n/**\n * Type guard to check if an error is a HookError.\n *\n * @example\n * const { error } = await engine.emit('validate', data);\n * if (isHookError(error)) {\n * console.log(`Hook \"${error.hookName}\" failed`);\n * }\n */\nexport const isHookError = (error: unknown): error is HookError => {\n\n return (error as HookError)?.constructor?.name === HookError.name\n}\n\n/**\n * Result returned from `emit()` after running all hook callbacks.\n */\nexport interface EmitResult<F extends AsyncFunc> {\n\n /** Current arguments (possibly modified by callbacks) */\n args: Parameters<F>;\n\n /** Result value (if set by a callback) */\n result?: Awaited<ReturnType<F>> | undefined;\n\n /** Whether a callback called `returnEarly()` */\n earlyReturn: boolean;\n}\n\n/**\n * Context object passed to hook callbacks.\n * Provides access to arguments, results, and control methods.\n *\n * @example\n * hooks.on('cacheCheck', async (ctx) => {\n * const [url] = ctx.args;\n * const cached = cache.get(url);\n *\n * if (cached) {\n * ctx.setResult(cached);\n * ctx.returnEarly();\n * }\n * });\n */\nexport interface HookContext<F extends AsyncFunc, FailArgs extends unknown[] = [string]> {\n\n /** Current arguments passed to emit() */\n args: Parameters<F>;\n\n /** Result value (can be set by callbacks) */\n result?: Awaited<ReturnType<F>>;\n\n /** Abort hook execution with an error. */\n fail: (...args: FailArgs) => never;\n\n /** Replace the arguments for subsequent callbacks */\n setArgs: (next: Parameters<F>) => void;\n\n /** Set the result value */\n setResult: (next: Awaited<ReturnType<F>>) => void;\n\n /** Stop processing remaining callbacks and return early */\n returnEarly: () => void;\n\n /** Remove this callback from the hook */\n removeHook: () => void;\n}\n\nexport type HookFn<F extends AsyncFunc, FailArgs extends unknown[] = [string]> =\n (ctx: HookContext<F, FailArgs>) => Promise<void>;\n\ntype HookOptions<F extends AsyncFunc, FailArgs extends unknown[] = [string]> = {\n callback: HookFn<F, FailArgs>;\n once?: true;\n ignoreOnFail?: true;\n}\n\ntype HookOrOptions<F extends AsyncFunc, FailArgs extends unknown[] = [string]> =\n HookFn<F, FailArgs> | HookOptions<F, FailArgs>;\n\ntype FuncOrNever<T> = T extends AsyncFunc ? T : never;\n\n/**\n * Custom error handler for `ctx.fail()`.\n * Can be an Error constructor or a function that throws.\n */\nexport type HandleFail<Args extends unknown[] = [string]> =\n | (new (...args: Args) => Error)\n | ((...args: Args) => never);\n\n/**\n * Options for HookEngine constructor.\n */\nexport interface HookEngineOptions<FailArgs extends unknown[] = [string]> {\n\n /**\n * Custom handler for `ctx.fail()`.\n * Can be an Error constructor or a function that throws.\n *\n * @example\n * // Use Firebase HttpsError\n * new HookEngine({ handleFail: HttpsError });\n *\n * // Use custom function\n * new HookEngine({\n * handleFail: (msg, data) => { throw Boom.badRequest(msg, data); }\n * });\n */\n handleFail?: HandleFail<FailArgs>;\n}\n\n/**\n * A lightweight, type-safe lifecycle hook system.\n *\n * HookEngine allows you to define lifecycle events and subscribe to them.\n * Callbacks can modify arguments, set results, or abort execution.\n *\n * @example\n * interface FetchLifecycle {\n * preRequest(url: string, options: RequestInit): Promise<Response>;\n * rateLimit(error: Error, attempt: number): Promise<void>;\n * cacheHit(url: string, data: unknown): Promise<unknown>;\n * }\n *\n * const hooks = new HookEngine<FetchLifecycle>();\n *\n * hooks.on('rateLimit', async (ctx) => {\n * const [error, attempt] = ctx.args;\n * if (attempt > 3) ctx.fail('Max retries exceeded');\n * await sleep(error.retryAfter * 1000);\n * });\n *\n * hooks.on('cacheHit', async (ctx) => {\n * console.log('Cache hit for:', ctx.args[0]);\n * });\n *\n * // In your implementation\n * const result = await hooks.emit('cacheHit', url, cachedData);\n *\n * @typeParam Lifecycle - Interface defining the lifecycle hooks\n * @typeParam FailArgs - Arguments type for ctx.fail() (default: [string])\n */\n/**\n * Default permissive lifecycle type when no type parameter is provided.\n */\ntype DefaultLifecycle = Record<string, AsyncFunc>;\n\n/**\n * Extract only function property keys from a type.\n * This ensures only methods are available as hook names, not data properties.\n *\n * @example\n * interface Doc {\n * id: string;\n * save(): Promise<void>;\n * delete(): Promise<void>;\n * }\n *\n * type DocHooks = HookName<Doc>; // 'save' | 'delete' (excludes 'id')\n */\nexport type HookName<T> = FunctionProps<T>;\n\nexport class HookEngine<Lifecycle = DefaultLifecycle, FailArgs extends unknown[] = [string]> {\n\n #hooks: Map<HookName<Lifecycle>, Set<HookFn<FuncOrNever<Lifecycle[HookName<Lifecycle>]>, FailArgs>>> = new Map();\n #hookOpts = new WeakMap<HookFn<any, any>, HookOptions<any, any>>();\n #handleFail: HandleFail<FailArgs>;\n #registered: Set<HookName<Lifecycle>> | null = null;\n\n constructor(options: HookEngineOptions<FailArgs> = {}) {\n\n this.#handleFail = options.handleFail ?? ((message: string): never => {\n\n throw new HookError(message);\n }) as unknown as HandleFail<FailArgs>;\n }\n\n /**\n * Validate that a hook is registered (if registration is enabled).\n */\n #assertRegistered(name: HookName<Lifecycle>, method: string) {\n\n if (this.#registered !== null && !this.#registered.has(name)) {\n\n const registered = [...this.#registered].map(String).join(', ');\n throw new Error(\n `Hook \"${String(name)}\" is not registered. ` +\n `Call register(\"${String(name)}\") before using ${method}(). ` +\n `Registered hooks: ${registered || '(none)'}`\n );\n }\n }\n\n /**\n * Register hook names for runtime validation.\n * Once any hooks are registered, all hooks must be registered before use.\n *\n * @param names - Hook names to register\n * @returns this (for chaining)\n *\n * @example\n * const hooks = new HookEngine<FetchLifecycle>()\n * .register('preRequest', 'postRequest', 'rateLimit');\n *\n * hooks.on('preRequest', cb); // OK\n * hooks.on('preRequset', cb); // Error: not registered (typo caught!)\n */\n register(...names: HookName<Lifecycle>[]) {\n\n assert(names.length > 0, 'register() requires at least one hook name');\n\n if (this.#registered === null) {\n\n this.#registered = new Set();\n }\n\n for (const name of names) {\n\n assert(typeof name === 'string', `Hook name must be a string, got ${typeof name}`);\n this.#registered.add(name);\n }\n\n return this;\n }\n\n /**\n * Subscribe to a lifecycle hook.\n *\n * @param name - Name of the lifecycle hook\n * @param cbOrOpts - Callback function or options object\n * @returns Cleanup function to remove the subscription\n *\n * @example\n * // Simple callback\n * const cleanup = hooks.on('preRequest', async (ctx) => {\n * console.log('Request:', ctx.args[0]);\n * });\n *\n * // With options\n * hooks.on('analytics', {\n * callback: async (ctx) => { track(ctx.args); },\n * once: true, // Remove after first run\n * ignoreOnFail: true // Don't throw if callback fails\n * });\n *\n * // Remove subscription\n * cleanup();\n */\n on<K extends HookName<Lifecycle>>(\n name: K,\n cbOrOpts: HookOrOptions<FuncOrNever<Lifecycle[K]>, FailArgs>\n ) {\n\n const callback = typeof cbOrOpts === 'function' ? cbOrOpts : cbOrOpts?.callback;\n const opts = typeof cbOrOpts === 'function'\n ? {} as HookOptions<FuncOrNever<Lifecycle[K]>, FailArgs>\n : cbOrOpts;\n\n assert(typeof name === 'string', '\"name\" must be a string');\n assert(isFunction(callback) || isObject(cbOrOpts), '\"cbOrOpts\" must be a callback or options');\n assert(isFunction(callback), 'callback must be a function');\n\n this.#assertRegistered(name, 'on');\n\n const hooks = this.#hooks.get(name) ?? new Set();\n\n hooks.add(callback as HookFn<FuncOrNever<Lifecycle[keyof Lifecycle]>, FailArgs>);\n\n this.#hooks.set(name, hooks);\n this.#hookOpts.set(callback, opts);\n\n return () => {\n\n hooks.delete(callback as HookFn<FuncOrNever<Lifecycle[keyof Lifecycle]>, FailArgs>);\n }\n }\n\n /**\n * Subscribe to a lifecycle hook that fires only once.\n * Sugar for `on(name, { callback, once: true })`.\n *\n * @param name - Name of the lifecycle hook\n * @param callback - Callback function\n * @returns Cleanup function to remove the subscription\n *\n * @example\n * // Log only the first request\n * hooks.once('preRequest', async (ctx) => {\n * console.log('First request:', ctx.args[0]);\n * });\n */\n once<K extends HookName<Lifecycle>>(\n name: K,\n callback: HookFn<FuncOrNever<Lifecycle[K]>, FailArgs>\n ) {\n\n return this.on(name, { callback, once: true });\n }\n\n /**\n * Emit a lifecycle hook, running all subscribed callbacks.\n *\n * @param name - Name of the lifecycle hook to emit\n * @param args - Arguments to pass to callbacks\n * @returns EmitResult with final args, result, and earlyReturn flag\n *\n * @example\n * const result = await hooks.emit('cacheCheck', url);\n *\n * if (result.earlyReturn && result.result) {\n * return result.result; // Use cached value\n * }\n *\n * // Continue with modified args\n * const [modifiedUrl] = result.args;\n */\n async emit<K extends HookName<Lifecycle>>(\n name: K,\n ...args: Parameters<FuncOrNever<Lifecycle[K]>>\n ): Promise<EmitResult<FuncOrNever<Lifecycle[K]>>> {\n\n this.#assertRegistered(name, 'emit');\n\n let earlyReturn = false;\n\n const hooks = this.#hooks.get(name);\n\n const context: HookContext<FuncOrNever<Lifecycle[K]>, FailArgs> = {\n args,\n removeHook() {},\n returnEarly() {\n\n earlyReturn = true;\n },\n setArgs: (next) => {\n\n assert(\n Array.isArray(next),\n `setArgs: args for '${String(name)}' must be an array`\n );\n\n context.args = next;\n },\n setResult: (next) => {\n\n context.result = next;\n },\n fail: ((...failArgs: FailArgs) => {\n\n const handler = this.#handleFail;\n\n // Check if handler is a constructor (class or function with prototype)\n const isConstructor = typeof handler === 'function' &&\n handler.prototype?.constructor === handler;\n\n const [, error] = attemptSync(() => {\n\n if (isConstructor) {\n\n throw new (handler as new (...args: FailArgs) => Error)(...failArgs);\n }\n\n (handler as (...args: FailArgs) => never)(...failArgs);\n });\n\n if (error) {\n\n if (error instanceof HookError) {\n\n error.hookName = String(name);\n }\n\n throw error;\n }\n\n // If handler didn't throw, we need to throw something\n throw new HookError('ctx.fail() handler did not throw');\n }) as (...args: FailArgs) => never\n };\n\n if (!hooks || hooks.size === 0) {\n\n return {\n args: context.args,\n result: context.result,\n earlyReturn: false\n };\n }\n\n for (const fn of hooks) {\n\n context.removeHook = () => hooks.delete(fn as any);\n\n const opts: HookOptions<any, any> = this.#hookOpts.get(fn) ?? { callback: fn };\n const [, err] = await attempt(() => fn({ ...context } as any));\n\n if (opts.once) context.removeHook();\n\n if (err && opts.ignoreOnFail !== true) {\n\n throw err;\n }\n\n if (earlyReturn) break;\n }\n\n return {\n args: context.args,\n result: context.result,\n earlyReturn\n };\n }\n\n /**\n * Clear all registered hooks.\n *\n * @example\n * hooks.on('preRequest', validator);\n * hooks.on('postRequest', logger);\n *\n * // Reset for testing\n * hooks.clear();\n */\n clear() {\n\n this.#hooks.clear();\n this.#hookOpts = new WeakMap();\n this.#registered = null;\n }\n\n /**\n * Wrap a function with pre/post lifecycle hooks.\n *\n * - Pre hook: emitted with function args, can modify args or returnEarly with result\n * - Post hook: emitted with [result, ...args], can modify result\n *\n * @param fn - The async function to wrap\n * @param hooks - Object with optional pre and post hook names\n * @returns Wrapped function with same signature\n *\n * @example\n * interface Lifecycle {\n * preRequest(url: string, opts: RequestInit): Promise<Response>;\n * postRequest(result: Response, url: string, opts: RequestInit): Promise<Response>;\n * }\n *\n * const hooks = new HookEngine<Lifecycle>();\n *\n * // Add cache check in pre hook\n * hooks.on('preRequest', async (ctx) => {\n * const cached = cache.get(ctx.args[0]);\n * if (cached) {\n * ctx.setResult(cached);\n * ctx.returnEarly();\n * }\n * });\n *\n * // Log result in post hook\n * hooks.on('postRequest', async (ctx) => {\n * const [result, url] = ctx.args;\n * console.log(`Fetched ${url}:`, result.status);\n * });\n *\n * // Wrap the fetch function\n * const wrappedFetch = hooks.wrap(\n * async (url: string, opts: RequestInit) => fetch(url, opts),\n * { pre: 'preRequest', post: 'postRequest' }\n * );\n */\n wrap<F extends AsyncFunc>(\n fn: F,\n hooks:\n | { pre: HookName<Lifecycle>; post?: HookName<Lifecycle> }\n | { pre?: HookName<Lifecycle>; post: HookName<Lifecycle> }\n ): (...args: Parameters<F>) => Promise<Awaited<ReturnType<F>>> {\n\n assert(\n hooks.pre || hooks.post,\n 'wrap() requires at least one of \"pre\" or \"post\" hooks'\n );\n\n if (hooks.pre) this.#assertRegistered(hooks.pre, 'wrap');\n if (hooks.post) this.#assertRegistered(hooks.post, 'wrap');\n\n return async (...args: Parameters<F>): Promise<Awaited<ReturnType<F>>> => {\n\n let currentArgs = args;\n let result: Awaited<ReturnType<F>> | undefined;\n\n // Pre hook\n if (hooks.pre) {\n\n const preResult = await this.emit(hooks.pre, ...currentArgs as any);\n\n currentArgs = preResult.args as Parameters<F>;\n\n if (preResult.earlyReturn && preResult.result !== undefined) {\n\n return preResult.result as Awaited<ReturnType<F>>;\n }\n }\n\n // Execute function\n result = await fn(...currentArgs);\n\n // Post hook\n if (hooks.post) {\n\n const postResult = await this.emit(\n hooks.post,\n ...[result, ...currentArgs] as any\n );\n\n if (postResult.result !== undefined) {\n\n return postResult.result as Awaited<ReturnType<F>>;\n }\n }\n\n return result as Awaited<ReturnType<F>>;\n };\n }\n}\n"],"names":["AssertError","assert","test","message","ErrorClass","isFunction","a","isObject","attempt","fn","attemptSync","HookError","__publicField","isHookError","error","HookEngine","options","__privateAdd","_HookEngine_instances","_hooks","_hookOpts","_handleFail","_registered","__privateSet","names","__privateGet","name","cbOrOpts","callback","opts","__privateMethod","assertRegistered_fn","hooks","args","earlyReturn","context","next","failArgs","handler","isConstructor","err","currentArgs","result","preResult","postResult","method","registered"],"mappings":"mnBAGW,MAAMA,UAAoB,KAAM,CAC3C,CA2EW,MAAMC,EAAS,CAACC,EAAMC,EAASC,IAAa,CAEnD,IADcF,aAAgB,SAAW,CAAC,CAACA,EAAI,EAAK,CAAC,CAACA,KACxC,GACV,MAAM,IAAmBF,EAAaG,GAAW,kBAAkB,CAE3E,ECyBiBE,EAAcC,GAAIA,aAAa,SAiB/BC,EAAYD,GAAIA,aAAa,OC7G7BE,EAAU,MAAOC,GAAK,CACnCR,EAAOI,EAAWI,CAAE,EAAG,uBAAuB,EAC9C,GAAI,CACA,MAAO,CACH,MAAMA,EAAI,EACV,IACH,CACJ,OAAQ,EAAG,CACR,MAAO,CACH,KACA,CACH,CACT,CACA,EAeiBC,EAAeD,GAAK,CACjCR,EAAOI,EAAWI,CAAE,EAAG,uBAAuB,EAC9C,GAAI,CACA,MAAO,CACHA,EAAI,EACJ,IACH,CACJ,OAAQ,EAAG,CACR,MAAO,CACH,KACA,CACH,CACT,CACA,EC9BO,MAAME,UAAkB,KAAM,CAQjC,YAAYR,EAAiB,CAEzB,MAAMA,CAAO,EAPjBS,EAAA,iBAGAA,EAAA,qBAIiB,CAErB,CAWa,MAAAC,EAAeC,GAEhBA,GAAqB,aAAa,OAASH,EAAU,KAuJ1D,MAAMI,CAAgF,CAOzF,YAAYC,EAAuC,GAAI,CAPpDC,EAAA,KAAAC,GAEHD,EAAA,KAAAE,MAA2G,KAC3GF,EAAA,KAAAG,MAAgB,SAChBH,EAAA,KAAAI,GACAJ,EAAA,KAAAK,EAA+C,MAI3CC,EAAA,KAAKF,EAAcL,EAAQ,aAAgBb,GAA2B,CAE5D,MAAA,IAAIQ,EAAUR,CAAO,CAAA,GAC/B,CAiCJ,YAAYqB,EAA8B,CAE/BvB,EAAAuB,EAAM,OAAS,EAAG,4CAA4C,EAEjEC,EAAA,KAAKH,KAAgB,MAEhBC,EAAA,KAAAD,MAAkB,KAG3B,UAAWI,KAAQF,EAEfvB,EAAO,OAAOyB,GAAS,SAAU,mCAAmC,OAAOA,CAAI,EAAE,EAC5ED,EAAA,KAAAH,GAAY,IAAII,CAAI,EAGtB,OAAA,IAAA,CA0BX,GACIA,EACAC,EACF,CAEE,MAAMC,EAAW,OAAOD,GAAa,WAAaA,EAAWA,GAAU,SACjEE,EAAO,OAAOF,GAAa,WAC3B,CACA,EAAAA,EAEC1B,EAAA,OAAOyB,GAAS,SAAU,yBAAyB,EAC1DzB,EAAOI,EAAWuB,CAAQ,GAAKrB,EAASoB,CAAQ,EAAG,0CAA0C,EACtF1B,EAAAI,EAAWuB,CAAQ,EAAG,6BAA6B,EAErDE,EAAA,KAAAZ,EAAAa,GAAA,UAAkBL,EAAM,MAE7B,MAAMM,EAAQP,EAAA,KAAKN,GAAO,IAAIO,CAAI,OAAS,IAE3C,OAAAM,EAAM,IAAIJ,CAAqE,EAE1EH,EAAA,KAAAN,GAAO,IAAIO,EAAMM,CAAK,EACtBP,EAAA,KAAAL,GAAU,IAAIQ,EAAUC,CAAI,EAE1B,IAAM,CAETG,EAAM,OAAOJ,CAAqE,CACtF,CAAA,CAiBJ,KACIF,EACAE,EACF,CAEE,OAAO,KAAK,GAAGF,EAAM,CAAE,SAAAE,EAAU,KAAM,GAAM,CAAA,CAoBjD,MAAM,KACFF,KACGO,EAC2C,CAEzCH,EAAA,KAAAZ,EAAAa,GAAA,UAAkBL,EAAM,QAE7B,IAAIQ,EAAc,GAElB,MAAMF,EAAQP,EAAA,KAAKN,GAAO,IAAIO,CAAI,EAE5BS,EAA4D,CAC9D,KAAAF,EACA,YAAa,CAAC,EACd,aAAc,CAEIC,EAAA,EAClB,EACA,QAAUE,GAAS,CAEfnC,EACI,MAAM,QAAQmC,CAAI,EAClB,sBAAsB,OAAOV,CAAI,CAAC,oBACtC,EAEAS,EAAQ,KAAOC,CACnB,EACA,UAAYA,GAAS,CAEjBD,EAAQ,OAASC,CACrB,EACA,KAAO,IAAIC,IAAuB,CAE9B,MAAMC,EAAUb,EAAA,KAAKJ,GAGfkB,EAAgB,OAAOD,GAAY,YACrCA,EAAQ,WAAW,cAAgBA,EAEjC,CAAG,CAAAxB,CAAK,EAAIJ,EAAY,IAAM,CAEhC,GAAI6B,EAEM,MAAA,IAAKD,EAA6C,GAAGD,CAAQ,EAGtEC,EAAyC,GAAGD,CAAQ,CAAA,CACxD,EAED,MAAIvB,GAEIA,aAAiBH,IAEXG,EAAA,SAAW,OAAOY,CAAI,GAG1BZ,GAIJ,IAAIH,EAAU,kCAAkC,CAAA,CAE9D,EAEA,GAAI,CAACqB,GAASA,EAAM,OAAS,EAElB,MAAA,CACH,KAAMG,EAAQ,KACd,OAAQA,EAAQ,OAChB,YAAa,EACjB,EAGJ,UAAW1B,KAAMuB,EAAO,CAEpBG,EAAQ,WAAa,IAAMH,EAAM,OAAOvB,CAAS,EAE3C,MAAAoB,EAA8BJ,EAAA,KAAKL,GAAU,IAAIX,CAAE,GAAK,CAAe,EACvE,CAAG,CAAA+B,CAAG,EAAI,MAAMhC,EAAQ,IAAMC,EAAG,CAAE,GAAG0B,CAAQ,CAAQ,CAAC,EAIzD,GAFAN,EAAK,MAAMM,EAAQ,WAAW,EAE9BK,GAAOX,EAAK,eAAiB,GAEvB,MAAAW,EAGV,GAAIN,EAAa,KAAA,CAGd,MAAA,CACH,KAAMC,EAAQ,KACd,OAAQA,EAAQ,OAChB,YAAAD,CACJ,CAAA,CAaJ,OAAQ,CAEJT,EAAA,KAAKN,GAAO,MAAM,EACbI,EAAA,KAAAH,MAAgB,SACrBG,EAAA,KAAKD,EAAc,KAAA,CA0CvB,KACIb,EACAuB,EAG2D,CAE3D,OAAA/B,EACI+B,EAAM,KAAOA,EAAM,KACnB,uDACJ,EAEIA,EAAM,KAAKF,EAAA,KAAKZ,EAAAa,GAAL,UAAuBC,EAAM,IAAK,QAC7CA,EAAM,MAAMF,EAAA,KAAKZ,EAAAa,GAAL,UAAuBC,EAAM,KAAM,QAE5C,SAAUC,IAAyD,CAEtE,IAAIQ,EAAcR,EACdS,EAGJ,GAAIV,EAAM,IAAK,CAEX,MAAMW,EAAY,MAAM,KAAK,KAAKX,EAAM,IAAK,GAAGS,CAAkB,EAIlE,GAFAA,EAAcE,EAAU,KAEpBA,EAAU,aAAeA,EAAU,SAAW,OAE9C,OAAOA,EAAU,MACrB,CAOJ,GAHSD,EAAA,MAAMjC,EAAG,GAAGgC,CAAW,EAG5BT,EAAM,KAAM,CAEN,MAAAY,EAAa,MAAM,KAAK,KAC1BZ,EAAM,KACFU,EAAQ,GAAGD,CACnB,EAEI,GAAAG,EAAW,SAAW,OAEtB,OAAOA,EAAW,MACtB,CAGG,OAAAF,CACX,CAAA,CAER,CAtWI,OAAAvB,EAAA,YACAC,EAAA,YACAC,EAAA,YACAC,EAAA,YALGJ,EAAA,YAkBHa,EAAA,SAAkBL,EAA2BmB,EAAgB,CAErD,GAAApB,EAAA,KAAKH,KAAgB,MAAQ,CAACG,EAAA,KAAKH,GAAY,IAAII,CAAI,EAAG,CAEpD,MAAAoB,EAAa,CAAC,GAAGrB,EAAA,KAAKH,EAAW,EAAE,IAAI,MAAM,EAAE,KAAK,IAAI,EAC9D,MAAM,IAAI,MACN,SAAS,OAAOI,CAAI,CAAC,uCACH,OAAOA,CAAI,CAAC,mBAAmBmB,CAAM,yBAClCC,GAAc,QAAQ,EAC/C,CAAA,CACJ"}
1
+ {"version":3,"file":"bundle.js","sources":["../../../utils/dist/esm/validation/assert.mjs","../../../utils/dist/esm/validation/type-guards.mjs","../../../utils/dist/esm/async/attempt.mjs","../../src/index.ts"],"sourcesContent":["import { reach } from '../object-utils/index.mjs';\n/**\n * Error class for assertions that fail\n */ export class AssertError extends Error {\n}\n/**\n * Checks if an error is an AssertError.\n *\n * @param err error to check\n * @returns true if error is an AssertError\n */ export const isAssertError = (err)=>err?.constructor?.name === AssertError.name;\n/**\n * Optional value check with custom validation.\n *\n * Returns true if value is undefined/null OR if the custom check passes.\n * Useful for validating optional parameters with specific criteria.\n *\n * @param val value to check\n * @param check function or boolean to validate the value\n * @returns true if value is optional or passes the check\n *\n * @example\n * // With function check\n * function processData(data: any, timeout?: number) {\n * assert(isOptional(timeout, (t) => t > 0), 'Timeout must be positive');\n * // Process data...\n * }\n *\n * @example\n * // With boolean check\n * const isValid = validateInput(input);\n * if (isOptional(config.strict, isValid)) {\n * // Either strict mode is off or input is valid\n * processInput(input);\n * }\n *\n * @example\n * // Check optional email format\n * isOptional(user.email, (email) => email.includes('@')) // true if email is undefined or contains @\n */ export const isOptional = (val, check)=>val === undefined || val === null || (check instanceof Function ? !!check(val) : !!check);\n/**\n * Asserts that a value is true. Even though NodeJS has\n * an `assert` builtin library, this aims to bring a\n * single API for asserting across all environments.\n *\n * @param test value that is coerced to true\n * @param message error message to display when test is false\n * @param ErrorClass error class to throw\n *\n * @example\n *\n * ```ts\n * assert(true, 'this is true');\n * assert(false, 'this is false');\n * assert(() => true, 'this is true');\n * assert(() => false, 'this is false');\n * ```\n *\n * ```ts\n *\n * const SomeErrorClass = class extends Error {\n * constructor(message: string) {\n * super(message);\n * }\n * }\n *\n *\n * const someFunc = () => {\n *\n * assert(true, 'this is true', SomeErrorClass);\n * assert(false, 'this is false', SomeErrorClass);\n * assert(() => true, 'this is true', SomeErrorClass);\n * assert(() => false, 'this is false', SomeErrorClass);\n *\n * // some logic\n * }\n *\n * someFunc();\n * ```\n */ export const assert = (test, message, ErrorClass)=>{\n const check = test instanceof Function ? !!test() : !!test;\n if (check === false) {\n throw new (ErrorClass || AssertError)(message || 'assertion failed');\n }\n};\n/**\n * Asserts the values in an object based on the provided assertions.\n * The assertions are a map of paths to functions that return a tuple\n * of a boolean and a message. This is intended to be used for testing\n * and validation when there is no schema validator available.\n *\n *\n * @param obj\n * @param assertions\n *\n * @example\n *\n * const obj = {\n * a: 1,\n * b: 'hello',\n * c: { d: 2 }\n * }\n *\n * assertObject(obj, {\n * a: (val) => [val === 1, 'a should be 1'],\n * b: (val) => [val === 'hello', 'b should be hello'],\n * c: [\n * (val) => [!!val, 'c should not be empty'],\n * (val) => [isObject(val), 'c should be an object']\n * ],\n * 'c.d': (val) => [isOptional(val, v === 2), 'c.d should be 2']\n * });\n */ export const assertObject = (obj, assertions)=>{\n const tests = [];\n for(const path in assertions){\n const val = reach(obj, path);\n const test = assertions[path];\n if (test === undefined) {\n throw new Error(`assertion for path ${path} is undefined`);\n }\n if (test instanceof Array) {\n for (const t of test){\n tests.push([\n val,\n t\n ]);\n }\n continue;\n }\n tests.push([\n val,\n test\n ]);\n }\n for (const [val, test] of tests){\n const res = test(val);\n assert(res instanceof Array, `assertion did not return a tuple [boolean, string]`);\n const [check, message] = res;\n assert(check, message);\n }\n};\n/**\n * Asserts only if value is not undefined.\n *\n * Provides conditional assertion that only executes when the value is defined.\n * Useful for validating optional parameters or properties.\n *\n * @param val value to test\n * @param test assertion test\n * @param message error message\n * @param ErrorClass error class to throw\n *\n * @example\n * function processUser(user: User, options?: ProcessOptions) {\n * // Only assert options if they are provided\n * assertOptional(options, isObject(options), 'Options must be an object');\n *\n * // Process user...\n * }\n *\n * @example\n * const config = getConfig();\n * assertOptional(config.timeout, config.timeout > 0, 'Timeout must be positive');\n */ export const assertOptional = (val, ...rest)=>{\n if (val !== undefined) {\n assert(...rest);\n }\n};\n","/**\n * Checks if value is non-iterable by testing if it can be iterated over.\n *\n * Uses Symbol.iterator to determine if a value is iterable. Returns true\n * for values that cannot be iterated (null, undefined, primitives).\n *\n * @param val value to check for iterability\n * @returns true if value is not iterable, false if it can be iterated\n *\n * @example\n * isNonIterable(null) // true\n * isNonIterable('string') // true\n * isNonIterable([1,2,3]) // false\n * isNonIterable(new Set()) // false\n */ export const isNonIterable = (val)=>{\n // null and undefined are not iterable\n if (val === null || val === undefined) {\n return true;\n }\n // Check if value has Symbol.iterator property\n return !val[Symbol.iterator];\n};\n/**\n * Checks if value is a primitive type.\n *\n * @param val value to check\n * @returns true if value is a primitive type\n *\n * @example\n * isPrimitive(null) // true\n * isPrimitive(undefined) // true\n * isPrimitive('string') // true\n * isPrimitive(1) // true\n * isPrimitive(true) // true\n * isPrimitive(Symbol('symbol')) // true\n * isPrimitive(new Date()) // false\n * isPrimitive(new RegExp('')) // false\n * isPrimitive(new Error('')) // false\n * isPrimitive(new Set()) // false\n * isPrimitive(new Map()) // false\n * isPrimitive(new Array()) // true\n * isPrimitive(new Object()) // true\n */ export const isPrimitive = (val)=>val === null || val === undefined || typeof val === 'string' || typeof val === 'number' || typeof val === 'boolean' || typeof val === 'symbol' || typeof val === 'bigint';\n/**\n * Checks if value is a type that does not have a constructor.\n *\n * Tests for null and undefined values which lack constructors.\n * Useful for deep comparison and cloning operations.\n *\n * @param val value to check\n * @returns true if value has no constructor (null or undefined)\n *\n * @example\n * hasNoConstructor(null) // true\n * hasNoConstructor(undefined) // true\n * hasNoConstructor({}) // false\n * hasNoConstructor('string') // false\n * hasNoConstructor(42) // false\n */ export const hasNoConstructor = (val)=>val === null || val === undefined;\n/**\n * Checks if both values have the same constructor.\n *\n * Compares the constructor property of two values to determine if they\n * are instances of the same class or type.\n *\n * @param value first value to compare\n * @param compare second value to compare\n * @returns true if both values have the same constructor\n *\n * @example\n * hasSameConstructor([], [1, 2, 3]) // true (both Arrays)\n * hasSameConstructor({}, { a: 1 }) // true (both Objects)\n * hasSameConstructor([], {}) // false (Array vs Object)\n * hasSameConstructor(new Date(), new Date()) // true (both Dates)\n * hasSameConstructor('string', 42) // false (String vs Number)\n */ export const hasSameConstructor = (value, compare)=>hasNoConstructor(value) === false && hasNoConstructor(compare) === false && value.constructor === compare.constructor;\n/**\n * Checks if both values have the same length or size.\n *\n * Compares the length property for arrays or size property for Sets.\n * Both iterables must be the same data type (both arrays or both Sets).\n * Useful for validating collections before performing operations.\n *\n * @param a first collection (array or Set)\n * @param b second collection (array or Set) - must be same type as `a`\n * @returns true if both collections have the same length/size\n *\n * @example\n * isSameLength([1, 2, 3], ['a', 'b', 'c']) // true\n * isSameLength([1, 2], [1, 2, 3]) // false\n * isSameLength(new Set([1, 2]), new Set(['a', 'b'])) // true\n * isSameLength(new Set([1, 2]), new Set([1, 2, 3])) // false\n */ export const isSameLength = (a, b)=>a.length === b.length && a.size === b.size;\n/**\n * Checks if value is a function.\n *\n * Uses instanceof to test if the value is a Function.\n * More reliable than typeof for all function types.\n *\n * @param a value to check\n * @returns true if value is a function\n *\n * @example\n * isFunction(() => {}) // true\n * isFunction(function() {}) // true\n * isFunction(async () => {}) // true\n * isFunction(class MyClass {}) // true\n * isFunction('string') // false\n * isFunction({}) // false\n */ export const isFunction = (a)=>a instanceof Function;\n/**\n * Checks if value is an object.\n *\n * Uses instanceof to test if the value is an Object.\n * Returns true for objects, arrays, functions, dates, etc.\n *\n * @param a value to check\n * @returns true if value is an object\n *\n * @example\n * isObject({}) // true\n * isObject([]) // true\n * isObject(new Date()) // true\n * isObject(() => {}) // true\n * isObject('string') // false\n * isObject(null) // false\n */ export const isObject = (a)=>a instanceof Object;\nconst commonObjects = new Set([\n Date,\n RegExp,\n Function,\n Error,\n EvalError,\n RangeError,\n ReferenceError,\n SyntaxError,\n TypeError,\n URIError,\n AggregateError,\n DOMException,\n Array,\n Set,\n Map,\n WeakMap,\n WeakSet,\n Promise,\n Proxy,\n Symbol,\n BigInt,\n WeakRef,\n FinalizationRegistry,\n DataView,\n ArrayBuffer,\n Int8Array,\n Uint8Array,\n Uint8ClampedArray,\n Int16Array,\n Uint16Array,\n Int32Array,\n Uint32Array,\n Float32Array,\n Float64Array,\n BigInt64Array,\n BigUint64Array,\n FormData,\n URLSearchParams\n]);\n/**\n * Checks if value is an uncommon object. Used to determine if a value\n * is a user defined object.\n *\n * @param a value to check\n * @returns true if value is an uncommon object\n *\n * @example\n *\n * // Returns false for common objects\n * isPlainObject(new Date()) // false\n * isPlainObject(new RegExp('')) // false\n * isPlainObject(new Function()) // false\n * isPlainObject(new Error()) // false\n * isPlainObject(new Array()) // false\n * isPlainObject(new Set()) // false\n *\n * // Returns true for uncommon objects\n * isPlainObject({}) // true\n * isPlainObject(new MyClass()) // true\n *\n */ export const isPlainObject = (a)=>!isPrimitive(a) && isObject(a) && !commonObjects.has(a.constructor);\n/**\n * Checks if value is specifically undefined.\n *\n * Strict equality check for undefined values.\n * More explicit than checking truthiness.\n *\n * @param val value to check\n * @returns true if value is exactly undefined\n *\n * @example\n * isUndefined(undefined) // true\n * isUndefined(null) // false\n * isUndefined('') // false\n * isUndefined(0) // false\n *\n * let x;\n * isUndefined(x) // true\n */ export const isUndefined = (val)=>val === undefined;\n/**\n * Checks if value is specifically not undefined.\n *\n * Inverse of isUndefined. Returns true for all values except undefined,\n * including null, false, 0, and empty strings.\n *\n * @param val value to check\n * @returns true if value is not undefined\n *\n * @example\n * isDefined(null) // true\n * isDefined(0) // true\n * isDefined('') // true\n * isDefined(false) // true\n * isDefined(undefined) // false\n *\n * const config = getConfig();\n * if (isDefined(config.apiKey)) {\n * // Safe to use config.apiKey\n * }\n */ export const isDefined = (val)=>val !== undefined;\n/**\n * Checks if value is specifically null.\n *\n * Strict equality check for null values.\n * More explicit than checking truthiness.\n *\n * @param val value to check\n * @returns true if value is exactly null\n *\n * @example\n * isNull(null) // true\n * isNull(undefined) // false\n * isNull('') // false\n * isNull(0) // false\n *\n * const result = findUser(id);\n * if (isNull(result)) {\n * // User was explicitly not found\n * }\n */ export const isNull = (val)=>val === null;\n","import { assert, isFunction } from '../validation/index.mjs';\n/**\n * Error tuple, go-style.\n *\n * @param fn async function to run\n *\n * @example\n *\n * const [result, error] = await attempt(async () => {\n * return 'hello';\n * });\n *\n * if (error) {\n * console.error(error);\n * }\n *\n * console.log(result);\n */ export const attempt = async (fn)=>{\n assert(isFunction(fn), 'fn must be a function');\n try {\n return [\n await fn(),\n null\n ];\n } catch (e) {\n return [\n null,\n e\n ];\n }\n};\n/**\n * Synchronous error tuple, go-style.\n *\n * @example\n *\n * const [result, error] = attemptSync(() => {\n * return 'hello';\n * });\n *\n * if (error) {\n * console.error(error);\n * }\n *\n * console.log(result);\n */ export const attemptSync = (fn)=>{\n assert(isFunction(fn), 'fn must be a function');\n try {\n return [\n fn(),\n null\n ];\n } catch (e) {\n return [\n null,\n e\n ];\n }\n};\n","import {\n assert,\n attempt,\n attemptSync,\n FunctionProps,\n isFunction,\n isObject\n} from '@logosdx/utils';\n\n/**\n * Error thrown when a hook calls `ctx.fail()`.\n *\n * Only created when using the default `handleFail` behavior.\n * If a custom `handleFail` is provided, that error type is thrown instead.\n *\n * @example\n * hooks.add('validate', (data, ctx) => {\n * if (!data.isValid) ctx.fail('Validation failed');\n * });\n *\n * const [, err] = await attempt(() => engine.run('validate', data));\n * if (isHookError(err)) {\n * console.log(err.hookName); // 'validate'\n * }\n */\nexport class HookError extends Error {\n\n /** Name of the hook where the error occurred */\n hookName?: string;\n\n /** Original error if `fail()` was called with an Error instance */\n originalError?: Error;\n\n constructor(message: string) {\n\n super(message);\n }\n}\n\n/**\n * Type guard to check if an error is a HookError.\n *\n * @example\n * const [, err] = await attempt(() => engine.run('validate', data));\n * if (isHookError(err)) {\n * console.log(`Hook \"${err.hookName}\" failed`);\n * }\n */\nexport const isHookError = (error: unknown): error is HookError => {\n\n return (error as HookError)?.constructor?.name === HookError.name;\n};\n\n/**\n * Custom error handler for `ctx.fail()`.\n * Can be an Error constructor or a function that throws.\n */\nexport type HandleFail<Args extends unknown[] = [string]> =\n | (new (...args: Args) => Error)\n | ((...args: Args) => never);\n\n/**\n * Request-scoped state bag that flows across hook runs and engine instances.\n *\n * Use symbols for private plugin state and strings for shared cross-plugin contracts.\n *\n * @example\n * // Private plugin state (symbol key)\n * const CACHE_STATE = Symbol('cache');\n * ctx.scope.set(CACHE_STATE, { key, rule });\n *\n * // Shared cross-plugin contract (string key)\n * ctx.scope.set('serializedKey', key);\n */\nexport class HookScope {\n\n #data = new Map<symbol | string, unknown>();\n\n /**\n * Get a value from the scope.\n *\n * @example\n * const state = ctx.scope.get<CacheState>(CACHE_STATE);\n */\n get<T = unknown>(key: symbol | string): T | undefined {\n\n return this.#data.get(key) as T | undefined;\n }\n\n /**\n * Set a value in the scope.\n *\n * @example\n * ctx.scope.set(CACHE_STATE, { key: 'abc', rule });\n */\n set<T = unknown>(key: symbol | string, value: T): void {\n\n this.#data.set(key, value);\n }\n\n /**\n * Check if a key exists in the scope.\n */\n has(key: symbol | string): boolean {\n\n return this.#data.has(key);\n }\n\n /**\n * Delete a key from the scope.\n */\n delete(key: symbol | string): boolean {\n\n return this.#data.delete(key);\n }\n}\n\nconst EARLY_RETURN: unique symbol = Symbol('early-return');\ntype EarlyReturnSignal = typeof EARLY_RETURN;\n\ntype FuncOrNever<T> = T extends (...args: any[]) => any ? T : never;\n\n/**\n * Extract only function property keys from a type.\n *\n * @example\n * interface Doc {\n * id: string;\n * save(): Promise<void>;\n * delete(): Promise<void>;\n * }\n *\n * type DocHooks = HookName<Doc>; // 'save' | 'delete' (excludes 'id')\n */\nexport type HookName<T> = FunctionProps<T>;\n\n/**\n * Context object passed as the last argument to hook callbacks.\n * Provides methods to modify args, short-circuit, fail, or self-remove.\n *\n * @example\n * // Replace args for downstream callbacks\n * hooks.add('beforeRequest', (url, opts, ctx) => {\n * ctx.args(url, { ...opts, cache: 'no-store' });\n * });\n *\n * // Short-circuit: replace args AND stop the chain\n * hooks.add('beforeRequest', (url, opts, ctx) => {\n * return ctx.args(normalizedUrl, opts);\n * });\n *\n * // Short-circuit with a result value\n * hooks.add('beforeRequest', (url, opts, ctx) => {\n * const cached = cache.get(url);\n * if (cached) return ctx.returns(cached);\n * });\n */\nexport class HookContext<\n Args extends unknown[] = unknown[],\n Result = unknown,\n FailArgs extends unknown[] = [string]\n> {\n\n #args: Args | undefined;\n #argsChanged = false;\n #result: Result | undefined;\n #earlyReturn = false;\n #handleFail: HandleFail<FailArgs>;\n #hookName: string;\n #removeFn: () => void;\n\n /**\n * Request-scoped state bag shared across hook runs and engine instances.\n *\n * @example\n * // In beforeRequest hook\n * ctx.scope.set(CACHE_KEY, serializedKey);\n *\n * // In afterRequest hook (same scope)\n * const key = ctx.scope.get<string>(CACHE_KEY);\n */\n readonly scope: HookScope;\n\n /** @internal */\n constructor(\n handleFail: HandleFail<FailArgs>,\n hookName: string,\n removeFn: () => void,\n scope: HookScope\n ) {\n\n this.#handleFail = handleFail;\n this.#hookName = hookName;\n this.#removeFn = removeFn;\n this.scope = scope;\n }\n\n /**\n * Replace args for downstream callbacks.\n * When used with `return`, also stops the chain.\n *\n * @example\n * // Just replace args, continue chain\n * ctx.args(newUrl, newOpts);\n *\n * // Replace args AND stop the chain\n * return ctx.args(newUrl, newOpts);\n */\n args(...args: Args): EarlyReturnSignal {\n\n this.#args = args;\n this.#argsChanged = true;\n return EARLY_RETURN;\n }\n\n /**\n * Set a result value and stop the chain.\n * Always used with `return`.\n *\n * @example\n * return ctx.returns(cachedResponse);\n */\n returns(value: Result): EarlyReturnSignal {\n\n this.#result = value;\n this.#earlyReturn = true;\n return EARLY_RETURN;\n }\n\n /**\n * Abort hook execution with an error.\n * Uses the engine's `handleFail` to create the error.\n *\n * @example\n * ctx.fail('Validation failed');\n */\n fail(...args: FailArgs): never {\n\n const handler = this.#handleFail;\n\n const isConstructor = typeof handler === 'function' &&\n handler.prototype?.constructor === handler;\n\n const [, error] = attemptSync(() => {\n\n if (isConstructor) {\n\n throw new (handler as new (...a: FailArgs) => Error)(...args);\n }\n\n (handler as (...a: FailArgs) => never)(...args);\n });\n\n if (error) {\n\n if (error instanceof HookError) {\n\n error.hookName = this.#hookName;\n }\n\n throw error;\n }\n\n throw new HookError('ctx.fail() handler did not throw');\n }\n\n /**\n * Remove this callback from future runs.\n *\n * @example\n * hooks.add('init', (config, ctx) => {\n * bootstrap(config);\n * ctx.removeHook();\n * });\n */\n removeHook(): void {\n\n this.#removeFn();\n }\n\n /** @internal */\n get _argsChanged(): boolean { return this.#argsChanged; }\n\n /** @internal */\n get _newArgs(): Args | undefined { return this.#args; }\n\n /** @internal */\n get _result(): Result | undefined { return this.#result; }\n\n /** @internal */\n get _earlyReturn(): boolean { return this.#earlyReturn; }\n}\n\n/**\n * Context object passed as the last argument to pipe middleware callbacks.\n * Simpler than HookContext — no `returns()` needed since you control\n * flow by calling or not calling `next()`.\n *\n * @example\n * hooks.add('execute', async (next, opts, ctx) => {\n * // Modify opts for inner layers\n * ctx.args({ ...opts, headers: { ...opts.headers, Auth: token } });\n *\n * // Call next to continue the chain, or don't to short-circuit\n * return next();\n * });\n */\nexport class PipeContext<\n FailArgs extends unknown[] = [string]\n> {\n\n #handleFail: HandleFail<FailArgs>;\n #hookName: string;\n #removeFn: () => void;\n #setArgs: (args: unknown[]) => void;\n\n /**\n * Request-scoped state bag shared across hook runs and engine instances.\n */\n readonly scope: HookScope;\n\n /** @internal */\n constructor(\n handleFail: HandleFail<FailArgs>,\n hookName: string,\n removeFn: () => void,\n scope: HookScope,\n setArgs: (args: unknown[]) => void\n ) {\n\n this.#handleFail = handleFail;\n this.#hookName = hookName;\n this.#removeFn = removeFn;\n this.scope = scope;\n this.#setArgs = setArgs;\n }\n\n /**\n * Replace args for `next()` and downstream middleware.\n *\n * @example\n * ctx.args({ ...opts, timeout: 5000 });\n * return next(); // next receives modified opts\n */\n args(...args: unknown[]): void {\n\n this.#setArgs(args);\n }\n\n /**\n * Abort execution with an error.\n *\n * @example\n * ctx.fail('Rate limit exceeded');\n */\n fail(...args: FailArgs): never {\n\n const handler = this.#handleFail;\n\n const isConstructor = typeof handler === 'function' &&\n handler.prototype?.constructor === handler;\n\n const [, error] = attemptSync(() => {\n\n if (isConstructor) {\n\n throw new (handler as new (...a: FailArgs) => Error)(...args);\n }\n\n (handler as (...a: FailArgs) => never)(...args);\n });\n\n if (error) {\n\n if (error instanceof HookError) {\n\n error.hookName = this.#hookName;\n }\n\n throw error;\n }\n\n throw new HookError('ctx.fail() handler did not throw');\n }\n\n /**\n * Remove this middleware from future runs.\n */\n removeHook(): void {\n\n this.#removeFn();\n }\n}\n\n/**\n * Callback type for hooks. Receives spread args + ctx as last param.\n *\n * @example\n * type BeforeRequest = HookCallback<(url: string, opts: RequestInit) => Promise<Response>>;\n * // (url: string, opts: RequestInit, ctx: HookContext) => void | EarlyReturnSignal | Promise<...>\n */\nexport type HookCallback<\n F extends (...args: any[]) => any = (...args: any[]) => any,\n FailArgs extends unknown[] = [string]\n> = F extends (...args: infer A) => infer R\n ? (...args: [...A, HookContext<A, Awaited<R>, FailArgs>]) => void | EarlyReturnSignal | Promise<void | EarlyReturnSignal>\n : never;\n\n/**\n * Callback type for pipe middleware. Receives `(next, ...args, ctx)`.\n *\n * @example\n * type ExecuteMiddleware = PipeCallback<[opts: RequestOpts]>;\n * // (next: () => Promise<R>, opts: RequestOpts, ctx: PipeContext) => R | Promise<R>\n */\nexport type PipeCallback<\n Args extends unknown[] = unknown[],\n R = unknown,\n FailArgs extends unknown[] = [string]\n> = (next: () => R | Promise<R>, ...args: [...Args, PipeContext<FailArgs>]) => R | Promise<R>;\n\n/**\n * Result returned from `run()`/`runSync()` after executing all hook callbacks.\n */\nexport interface RunResult<F extends (...args: any[]) => any = (...args: any[]) => any> {\n\n /** Current arguments (possibly modified by callbacks) */\n args: Parameters<F>;\n\n /** Result value (if set via `ctx.returns()`) */\n result: Awaited<ReturnType<F>> | undefined;\n\n /** Whether a callback short-circuited via `return ctx.returns()` or `return ctx.args()` */\n returned: boolean;\n\n /** The scope used during this run (pass to subsequent runs to share state) */\n scope: HookScope;\n}\n\ntype DefaultLifecycle = Record<string, (...args: any[]) => any>;\n\ntype HookEntry<F extends (...args: any[]) => any, FailArgs extends unknown[]> = {\n callback: HookCallback<F, FailArgs>;\n options: HookEngine.AddOptions;\n priority: number;\n};\n\n/**\n * A lightweight, type-safe lifecycle hook system.\n *\n * HookEngine allows you to define lifecycle events and subscribe to them.\n * Callbacks receive spread arguments with a context object as the last param.\n *\n * @example\n * interface FetchLifecycle {\n * beforeRequest(url: string, options: RequestInit): Promise<Response>;\n * afterRequest(response: Response, url: string): Promise<Response>;\n * }\n *\n * const hooks = new HookEngine<FetchLifecycle>();\n *\n * hooks.add('beforeRequest', (url, opts, ctx) => {\n * ctx.args(url, { ...opts, headers: { ...opts.headers, 'X-Token': token } });\n * });\n *\n * hooks.add('beforeRequest', (url, opts, ctx) => {\n * const cached = cache.get(url);\n * if (cached) return ctx.returns(cached);\n * });\n *\n * const pre = await hooks.run('beforeRequest', url, options);\n * if (pre.returned) return pre.result;\n * const response = await fetch(...pre.args);\n *\n * @typeParam Lifecycle - Interface defining the lifecycle hooks\n * @typeParam FailArgs - Arguments type for ctx.fail() (default: [string])\n */\nexport class HookEngine<Lifecycle = DefaultLifecycle, FailArgs extends unknown[] = [string]> {\n\n #hooks: Map<string, Array<HookEntry<any, FailArgs>>> = new Map();\n #handleFail: HandleFail<FailArgs>;\n #registered: Set<HookName<Lifecycle>> | null = null;\n #callCounts: WeakMap<Function, number> = new WeakMap();\n\n constructor(options: HookEngine.Options<FailArgs> = {}) {\n\n this.#handleFail = options.handleFail ?? ((message: string): never => {\n\n throw new HookError(message);\n }) as unknown as HandleFail<FailArgs>;\n }\n\n /**\n * Validate that a hook name is registered (when strict mode is active).\n */\n #assertRegistered(name: HookName<Lifecycle>, method: string) {\n\n if (this.#registered !== null && !this.#registered.has(name)) {\n\n const registered = [...this.#registered].map(String).join(', ');\n throw new Error(\n `Hook \"${String(name)}\" is not registered. ` +\n `Call register(\"${String(name)}\") before using ${method}(). ` +\n `Registered hooks: ${registered || '(none)'}`\n );\n }\n }\n\n /**\n * Register hook names for runtime validation.\n * Once any hooks are registered, all hooks must be registered before use.\n *\n * @param names - Hook names to register\n * @returns this (for chaining)\n *\n * @example\n * const hooks = new HookEngine<FetchLifecycle>()\n * .register('beforeRequest', 'afterRequest');\n *\n * hooks.add('beforeRequest', cb); // OK\n * hooks.add('beforeRequset', cb); // Error: not registered (typo caught!)\n */\n register(...names: HookName<Lifecycle>[]) {\n\n assert(names.length > 0, 'register() requires at least one hook name');\n\n if (this.#registered === null) {\n\n this.#registered = new Set();\n }\n\n for (const name of names) {\n\n assert(typeof name === 'string', `Hook name must be a string, got ${typeof name}`);\n this.#registered.add(name);\n }\n\n return this;\n }\n\n /**\n * Subscribe to a lifecycle hook.\n *\n * @param name - Name of the lifecycle hook\n * @param callback - Callback function receiving spread args + ctx\n * @param options - Options for this subscription\n * @returns Cleanup function to remove the subscription\n *\n * @example\n * // Simple callback\n * const cleanup = hooks.add('beforeRequest', (url, opts, ctx) => {\n * console.log('Request:', url);\n * });\n *\n * // With options\n * hooks.add('analytics', (event, ctx) => {\n * track(event);\n * }, { once: true, ignoreOnFail: true });\n *\n * // With priority (lower runs first)\n * hooks.add('beforeRequest', cb, { priority: -10 });\n *\n * // Remove subscription\n * cleanup();\n */\n add<K extends HookName<Lifecycle>>(\n name: K,\n callback: HookCallback<FuncOrNever<Lifecycle[K]>, FailArgs>,\n options: HookEngine.AddOptions = {}\n ): () => void {\n\n assert(typeof name === 'string', '\"name\" must be a string');\n assert(isFunction(callback), '\"callback\" must be a function');\n\n this.#assertRegistered(name, 'add');\n\n const priority = options.priority ?? 0;\n const entry: HookEntry<any, FailArgs> = { callback, options, priority };\n\n const hooks = this.#hooks.get(name as string) ?? [];\n\n let inserted = false;\n\n for (let i = 0; i < hooks.length; i++) {\n\n if (hooks[i]!.priority > priority) {\n\n hooks.splice(i, 0, entry);\n inserted = true;\n break;\n }\n }\n\n if (!inserted) {\n\n hooks.push(entry);\n }\n\n this.#hooks.set(name as string, hooks);\n\n return () => {\n\n const arr = this.#hooks.get(name as string);\n\n if (arr) {\n\n const idx = arr.indexOf(entry);\n\n if (idx !== -1) {\n\n arr.splice(idx, 1);\n }\n }\n };\n }\n\n /**\n * Run all callbacks for a hook asynchronously.\n *\n * @param name - Name of the lifecycle hook to run\n * @param args - Arguments to pass to callbacks (spread + ctx)\n * @returns RunResult with final args, result, and returned flag\n *\n * @example\n * const pre = await hooks.run('beforeRequest', url, options);\n * if (pre.returned) return pre.result;\n * const response = await fetch(...pre.args);\n */\n async run<K extends HookName<Lifecycle>>(\n name: K,\n ...args: Parameters<FuncOrNever<Lifecycle[K]>> | [...Parameters<FuncOrNever<Lifecycle[K]>>, HookEngine.RunOptions<FuncOrNever<Lifecycle[K]>>]\n ): Promise<RunResult<FuncOrNever<Lifecycle[K]>>> {\n\n this.#assertRegistered(name, 'run');\n\n const { realArgs, runOptions } = this.#extractRunOptions(args);\n let currentArgs = realArgs as Parameters<FuncOrNever<Lifecycle[K]>>;\n const scope = runOptions?.scope ?? new HookScope();\n\n const hooks = this.#hooks.get(name as string);\n const entries = hooks ? [...hooks] : [];\n\n if (runOptions?.append) {\n\n entries.push({\n callback: runOptions.append as unknown as HookCallback<any, FailArgs>,\n options: {},\n priority: Infinity\n });\n }\n\n let result: Awaited<ReturnType<FuncOrNever<Lifecycle[K]>>> | undefined;\n let returned = false;\n\n for (const entry of entries) {\n\n const { callback, options: opts } = entry;\n\n const timesExceeded = this.#checkTimes(callback, opts);\n\n if (timesExceeded) {\n\n this.#removeEntry(name as string, entry);\n continue;\n }\n\n const removeFn = () => this.#removeEntry(name as string, entry);\n const ctx = new HookContext<any, any, FailArgs>(this.#handleFail, String(name), removeFn, scope);\n\n if (opts.ignoreOnFail) {\n\n const [, err] = await attempt(async () => {\n\n const signal = await callback(...currentArgs, ctx);\n this.#processCtx(ctx, signal, (a) => { currentArgs = a; }, (r) => { result = r; }, () => { returned = true; });\n });\n\n if (!err && opts.once) removeFn();\n\n if (returned) break;\n continue;\n }\n\n const signal = await callback(...currentArgs, ctx);\n this.#processCtx(ctx, signal, (a) => { currentArgs = a; }, (r) => { result = r; }, () => { returned = true; });\n\n if (opts.once) removeFn();\n\n if (returned) break;\n }\n\n return { args: currentArgs, result, returned, scope };\n }\n\n /**\n * Run all callbacks for a hook synchronously.\n *\n * @param name - Name of the lifecycle hook to run\n * @param args - Arguments to pass to callbacks (spread + ctx)\n * @returns RunResult with final args, result, and returned flag\n *\n * @example\n * const pre = hooks.runSync('beforeValidation', data);\n * if (pre.returned) return pre.result;\n */\n runSync<K extends HookName<Lifecycle>>(\n name: K,\n ...args: Parameters<FuncOrNever<Lifecycle[K]>> | [...Parameters<FuncOrNever<Lifecycle[K]>>, HookEngine.RunOptions<FuncOrNever<Lifecycle[K]>>]\n ): RunResult<FuncOrNever<Lifecycle[K]>> {\n\n this.#assertRegistered(name, 'runSync');\n\n const { realArgs, runOptions } = this.#extractRunOptions(args as unknown[]);\n let currentArgs = realArgs as Parameters<FuncOrNever<Lifecycle[K]>>;\n const scope = runOptions?.scope ?? new HookScope();\n\n const hooks = this.#hooks.get(name as string);\n const entries = hooks ? [...hooks] : [];\n\n if (runOptions?.append) {\n\n entries.push({\n callback: runOptions.append as unknown as HookCallback<any, FailArgs>,\n options: {},\n priority: Infinity\n });\n }\n\n let result: Awaited<ReturnType<FuncOrNever<Lifecycle[K]>>> | undefined;\n let returned = false;\n\n for (const entry of entries) {\n\n const { callback, options: opts } = entry;\n\n const timesExceeded = this.#checkTimes(callback, opts);\n\n if (timesExceeded) {\n\n this.#removeEntry(name as string, entry);\n continue;\n }\n\n const removeFn = () => this.#removeEntry(name as string, entry);\n const ctx = new HookContext<any, any, FailArgs>(this.#handleFail, String(name), removeFn, scope);\n\n if (opts.ignoreOnFail) {\n\n const [, err] = attemptSync(() => {\n\n const signal = callback(...currentArgs, ctx);\n this.#processCtx(ctx, signal, (a) => { currentArgs = a; }, (r) => { result = r; }, () => { returned = true; });\n });\n\n if (!err && opts.once) removeFn();\n\n if (returned) break;\n continue;\n }\n\n const signal = callback(...currentArgs, ctx);\n this.#processCtx(ctx, signal, (a) => { currentArgs = a; }, (r) => { result = r; }, () => { returned = true; });\n\n if (opts.once) removeFn();\n\n if (returned) break;\n }\n\n return { args: currentArgs, result, returned, scope };\n }\n\n /**\n * Wrap an async function with pre/post lifecycle hooks.\n *\n * - Pre hook: called with function args, can modify args or return early\n * - Post hook: called with `(result, ...originalArgs)`, can transform result\n *\n * @param fn - The async function to wrap\n * @param hooks - Object with optional pre and post hook names\n * @returns Wrapped function with same signature\n *\n * @example\n * const wrappedFetch = hooks.wrap(\n * async (url: string, opts: RequestInit) => fetch(url, opts),\n * { pre: 'beforeRequest', post: 'afterRequest' }\n * );\n */\n wrap<F extends (...args: any[]) => Promise<any>>(\n fn: F,\n hooks:\n | { pre: HookName<Lifecycle>; post?: HookName<Lifecycle> }\n | { pre?: HookName<Lifecycle>; post: HookName<Lifecycle> }\n ): (...args: Parameters<F>) => Promise<Awaited<ReturnType<F>>> {\n\n assert(\n hooks.pre || hooks.post,\n 'wrap() requires at least one of \"pre\" or \"post\" hooks'\n );\n\n if (hooks.pre) this.#assertRegistered(hooks.pre, 'wrap');\n if (hooks.post) this.#assertRegistered(hooks.post, 'wrap');\n\n return async (...args: Parameters<F>): Promise<Awaited<ReturnType<F>>> => {\n\n let currentArgs = args;\n\n if (hooks.pre) {\n\n const preResult = await this.run(hooks.pre, ...currentArgs as any);\n currentArgs = preResult.args as Parameters<F>;\n\n if (preResult.returned && preResult.result !== undefined) {\n\n return preResult.result as Awaited<ReturnType<F>>;\n }\n }\n\n const result = await fn(...currentArgs);\n\n if (hooks.post) {\n\n const postResult = await this.run(\n hooks.post,\n ...[result, ...currentArgs] as any\n );\n\n if (postResult.returned) {\n\n return postResult.result as Awaited<ReturnType<F>>;\n }\n }\n\n return result as Awaited<ReturnType<F>>;\n };\n }\n\n /**\n * Wrap a synchronous function with pre/post lifecycle hooks.\n *\n * @param fn - The sync function to wrap\n * @param hooks - Object with optional pre and post hook names\n * @returns Wrapped function with same signature\n *\n * @example\n * const wrappedValidate = hooks.wrapSync(\n * (data: UserData) => validate(data),\n * { pre: 'beforeValidate' }\n * );\n */\n wrapSync<F extends (...args: any[]) => any>(\n fn: F,\n hooks:\n | { pre: HookName<Lifecycle>; post?: HookName<Lifecycle> }\n | { pre?: HookName<Lifecycle>; post: HookName<Lifecycle> }\n ): (...args: Parameters<F>) => ReturnType<F> {\n\n assert(\n hooks.pre || hooks.post,\n 'wrapSync() requires at least one of \"pre\" or \"post\" hooks'\n );\n\n if (hooks.pre) this.#assertRegistered(hooks.pre, 'wrapSync');\n if (hooks.post) this.#assertRegistered(hooks.post, 'wrapSync');\n\n return (...args: Parameters<F>): ReturnType<F> => {\n\n let currentArgs = args;\n\n if (hooks.pre) {\n\n const preResult = this.runSync(hooks.pre, ...currentArgs as any);\n currentArgs = preResult.args as Parameters<F>;\n\n if (preResult.returned && preResult.result !== undefined) {\n\n return preResult.result as ReturnType<F>;\n }\n }\n\n const result = fn(...currentArgs);\n\n if (hooks.post) {\n\n const postResult = this.runSync(\n hooks.post,\n ...[result, ...currentArgs] as any\n );\n\n if (postResult.returned) {\n\n return postResult.result as ReturnType<F>;\n }\n }\n\n return result as ReturnType<F>;\n };\n }\n\n /**\n * Execute middleware hooks as an onion (nested) composition.\n *\n * Unlike `run()` which executes hooks linearly, `pipe()` composes hooks\n * as nested middleware. Each hook receives a `next` function that calls\n * the next layer. The innermost layer is `coreFn`. Control flow is\n * managed by calling or not calling `next()` — no `ctx.returns()` needed.\n *\n * Hooks execute in priority order (lower first = outermost layer).\n *\n * @param name - Name of the lifecycle hook\n * @param coreFn - The innermost function to wrap\n * @param args - Arguments passed to each middleware\n * @returns The result from the middleware chain\n *\n * @example\n * // Retry plugin wraps the fetch call\n * hooks.add('execute', async (next, opts, ctx) => {\n * for (let i = 0; i < 3; i++) {\n * const [result, err] = await attempt(next);\n * if (!err) return result;\n * await wait(1000 * i);\n * }\n * throw lastError;\n * }, { priority: -20 });\n *\n * // Dedupe plugin wraps retry\n * hooks.add('execute', async (next, opts, ctx) => {\n * const inflight = getInflight(key);\n * if (inflight) return inflight;\n * const result = await next();\n * share(result);\n * return result;\n * }, { priority: -30 });\n *\n * // Execute: dedupe( retry( makeCall() ) )\n * const response = await hooks.pipe(\n * 'execute',\n * () => makeCall(opts),\n * opts\n * );\n */\n async pipe<K extends HookName<Lifecycle>, R = unknown>(\n name: K,\n coreFn: () => Promise<R>,\n ...args: unknown[]\n ): Promise<R> {\n\n this.#assertRegistered(name, 'pipe');\n\n const { realArgs, runOptions } = this.#extractRunOptions(args);\n const scope = runOptions?.scope ?? new HookScope();\n\n const hooks = this.#hooks.get(name as string);\n const entries = hooks ? [...hooks] : [];\n\n if (runOptions?.append) {\n\n entries.push({\n callback: runOptions.append as unknown as HookCallback<any, FailArgs>,\n options: {},\n priority: Infinity\n });\n }\n\n let currentArgs = realArgs;\n\n const buildChain = (index: number): (() => Promise<R>) => {\n\n if (index >= entries.length) return coreFn;\n\n return async () => {\n\n const entry = entries[index]!;\n const { callback, options: opts } = entry;\n\n const timesExceeded = this.#checkTimes(callback, opts);\n\n if (timesExceeded) {\n\n this.#removeEntry(name as string, entry);\n return buildChain(index + 1)();\n }\n\n const removeFn = () => this.#removeEntry(name as string, entry);\n const ctx = new PipeContext<FailArgs>(\n this.#handleFail,\n String(name),\n removeFn,\n scope,\n (newArgs) => { currentArgs = newArgs; }\n );\n\n const next = buildChain(index + 1);\n const cb = callback as any;\n\n if (opts.ignoreOnFail) {\n\n const [result, err] = await attempt(\n async () => cb(next, ...currentArgs, ctx)\n );\n\n if (opts.once) removeFn();\n\n if (err) return next();\n\n return result as R;\n }\n\n const result = await cb(next, ...currentArgs, ctx);\n\n if (opts.once) removeFn();\n\n return result as R;\n };\n };\n\n return buildChain(0)();\n }\n\n /**\n * Synchronous version of `pipe()`.\n *\n * @param name - Name of the lifecycle hook\n * @param coreFn - The innermost function to wrap\n * @param args - Arguments passed to each middleware\n * @returns The result from the middleware chain\n */\n pipeSync<K extends HookName<Lifecycle>, R = unknown>(\n name: K,\n coreFn: () => R,\n ...args: unknown[]\n ): R {\n\n this.#assertRegistered(name, 'pipeSync');\n\n const { realArgs, runOptions } = this.#extractRunOptions(args);\n const scope = runOptions?.scope ?? new HookScope();\n\n const hooks = this.#hooks.get(name as string);\n const entries = hooks ? [...hooks] : [];\n\n if (runOptions?.append) {\n\n entries.push({\n callback: runOptions.append as unknown as HookCallback<any, FailArgs>,\n options: {},\n priority: Infinity\n });\n }\n\n let currentArgs = realArgs;\n\n const buildChain = (index: number): (() => R) => {\n\n if (index >= entries.length) return coreFn;\n\n return () => {\n\n const entry = entries[index]!;\n const { callback, options: opts } = entry;\n\n const timesExceeded = this.#checkTimes(callback, opts);\n\n if (timesExceeded) {\n\n this.#removeEntry(name as string, entry);\n return buildChain(index + 1)();\n }\n\n const removeFn = () => this.#removeEntry(name as string, entry);\n const ctx = new PipeContext<FailArgs>(\n this.#handleFail,\n String(name),\n removeFn,\n scope,\n (newArgs) => { currentArgs = newArgs; }\n );\n\n const next = buildChain(index + 1);\n const cb = callback as any;\n\n if (opts.ignoreOnFail) {\n\n const [result, err] = attemptSync(\n () => cb(next, ...currentArgs, ctx)\n );\n\n if (opts.once) removeFn();\n\n if (err) return next();\n\n return result as R;\n }\n\n const result = cb(next, ...currentArgs, ctx);\n\n if (opts.once) removeFn();\n\n return result as R;\n };\n };\n\n return buildChain(0)();\n }\n\n /**\n * Clear all hooks and reset registration state.\n *\n * @example\n * hooks.add('beforeRequest', validator);\n * hooks.clear();\n * // All hooks removed, back to permissive mode\n */\n clear() {\n\n this.#hooks.clear();\n this.#registered = null;\n this.#callCounts = new WeakMap();\n }\n\n /**\n * Process a HookContext after a callback has run.\n */\n #processCtx(\n ctx: HookContext<any, any, FailArgs>,\n signal: unknown,\n setArgs: (a: any) => void,\n setResult: (r: any) => void,\n setReturned: () => void\n ) {\n\n if (ctx._earlyReturn) {\n\n setResult(ctx._result);\n setReturned();\n return;\n }\n\n if (ctx._argsChanged) {\n\n setArgs(ctx._newArgs);\n\n if (signal === EARLY_RETURN) {\n\n setReturned();\n }\n }\n }\n\n /**\n * Check times limit and increment counter. Returns true if exceeded.\n */\n #checkTimes(callback: Function, opts: HookEngine.AddOptions): boolean {\n\n if (opts.times === undefined) return false;\n\n const count = this.#callCounts.get(callback) ?? 0;\n\n if (count >= opts.times) return true;\n\n this.#callCounts.set(callback, count + 1);\n return false;\n }\n\n /**\n * Remove an entry from the hooks array.\n */\n #removeEntry(name: string, entry: HookEntry<any, FailArgs>) {\n\n const arr = this.#hooks.get(name);\n\n if (arr) {\n\n const idx = arr.indexOf(entry);\n\n if (idx !== -1) {\n\n arr.splice(idx, 1);\n }\n }\n }\n\n /**\n * Extract RunOptions from the args array if present.\n */\n #extractRunOptions(\n args: unknown[]\n ): { realArgs: unknown[]; runOptions: HookEngine.RunOptions<any> | undefined } {\n\n const last = args[args.length - 1];\n\n if (isObject(last) && (\n ('append' in (last as object) && isFunction((last as any).append)) ||\n ('scope' in (last as object) && (last as any).scope instanceof HookScope)\n )) {\n\n return {\n realArgs: args.slice(0, -1),\n runOptions: last as HookEngine.RunOptions<any>\n };\n }\n\n return { realArgs: args, runOptions: undefined };\n }\n}\n\nexport namespace HookEngine {\n\n export interface Options<FailArgs extends unknown[] = [string]> {\n\n /**\n * Custom handler for `ctx.fail()`.\n * Can be an Error constructor or a function that throws.\n *\n * @example\n * new HookEngine({ handleFail: HttpsError });\n */\n handleFail?: HandleFail<FailArgs>;\n }\n\n export interface AddOptions {\n\n /** Remove after first run (sugar for `times: 1`) */\n once?: true;\n\n /** Run N times then auto-remove */\n times?: number;\n\n /** Swallow errors from this callback, continue chain */\n ignoreOnFail?: true;\n\n /** Execution order, lower runs first. Default 0. */\n priority?: number;\n }\n\n export interface RunOptions<F extends (...args: any[]) => any = (...args: any[]) => any> {\n\n /** Ephemeral callback that runs last (for per-request hooks) */\n append?: HookCallback<F>;\n\n /** Shared scope that flows across hook runs and engine instances */\n scope?: HookScope;\n }\n\n export interface PipeOptions {\n\n /** Ephemeral middleware that runs last (innermost before coreFn) */\n append?: (...args: any[]) => any;\n\n /** Shared scope that flows across hook runs and engine instances */\n scope?: HookScope;\n }\n}\n"],"names":["AssertError","assert","test","message","ErrorClass","isFunction","a","isObject","attempt","fn","e","attemptSync","HookError","__publicField","isHookError","error","HookScope","__privateAdd","_data","key","__privateGet","value","EARLY_RETURN","HookContext","handleFail","hookName","removeFn","scope","_args","_argsChanged","_result","_earlyReturn","_handleFail","_hookName","_removeFn","__privateSet","args","handler","isConstructor","PipeContext","setArgs","_setArgs","HookEngine","options","_HookEngine_instances","_hooks","_registered","_callCounts","names","name","callback","__privateMethod","assertRegistered_fn","priority","entry","hooks","inserted","i","arr","idx","realArgs","runOptions","extractRunOptions_fn","currentArgs","entries","result","returned","opts","checkTimes_fn","removeEntry_fn","ctx","err","signal","processCtx_fn","r","preResult","postResult","coreFn","buildChain","index","newArgs","next","cb","method","registered","setResult","setReturned","count","last"],"mappings":"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"}