defered-branch 2.0.0 โ†’ 2.1.1

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/README.md CHANGED
@@ -12,7 +12,7 @@ A TypeScript/JavaScript library that provides elegant deferred conditional branc
12
12
  - ๐Ÿ”’ **Type Safe**: Full TypeScript support with proper type inference
13
13
  - ๐Ÿชถ **Lightweight**: Zero dependencies, minimal footprint
14
14
  - ๐Ÿงฉ **Flexible**: Works with any function signature and return types
15
- - ๐Ÿ“ฆ **ESM Ready**: Modern module system support
15
+ - ๐Ÿ“ฆ **Type Annotation**: Full type annotation supported for the exported functions. You can customize handler's type of branch/condition/nomatch.
16
16
 
17
17
  ## ๐Ÿ“ฆ Installation
18
18
 
@@ -59,6 +59,8 @@ branch
59
59
  // 3. Handle no-match case (optional)
60
60
  branch.nomatch(() => 'Access denied');
61
61
 
62
+ // some other logic... like validate other arguments
63
+
62
64
  // 4. Execute and get result
63
65
  const result = branch.run();
64
66
  console.log(result); // Output based on user.role
@@ -165,7 +167,7 @@ Creates a new deferred branch instance for immediate execution scenarios.
165
167
  ##### `.add(condition: boolean, branch: Function): DeferBranch`
166
168
 
167
169
  - Adds a conditional branch that executes if condition is `true`
168
- - **Overrides** previous matched branches
170
+ - First-match wins: the first truthy branch will be selected; later entries are ignored
169
171
  - Returns the instance for chaining
170
172
 
171
173
  ##### `.nomatch(handler: Function): DeferBranch`
@@ -190,13 +192,70 @@ Creates a new dynamic deferred branch instance for reusable logic patterns.
190
192
 
191
193
  - Adds a predicate-based conditional branch
192
194
  - `condition` is a function that returns boolean
193
- - **Overrides** previous matched branches
195
+ - First-match wins: the first truthy branch will be selected; later entries are ignored
194
196
 
195
197
  ##### `.nomatch(handler: Function): DeferBranchDynamic`
196
198
 
197
199
  - Sets a fallback handler for when no predicates match
198
200
  - Executes during `.predicate()` evaluation
199
201
 
202
+ ## ๐Ÿงพ Type Annotation
203
+
204
+ The factory functions `deferedBranch` and `deferedBranchDynamic` are generic helpers. You can provide explicit type parameters to describe the branch function signature and the optional `nomatch` handler.
205
+
206
+ Below is an example using `deferedBranch`:
207
+
208
+ ```typescript
209
+ import { deferedBranch } from 'defered-branch';
210
+
211
+ // Here we declare that branches accept a message (string) and return void.
212
+ // The factory signature is: deferedBranch<BranchFn, NoMatchFn>()
213
+ const handler = deferedBranch<(msg: string) => void, () => void>();
214
+
215
+ handler
216
+ .add(
217
+ process.env.STATUS === 'ok',
218
+ (msg) => console.log('OK:', msg) // this is restricted by `(msg: string) => void`
219
+ )
220
+ .add(
221
+ process.env.STATUS === 'warn',
222
+ (msg) => console.warn('WARN:', msg) // this is restricted by `(msg: string) => void`
223
+ )
224
+ .nomatch(
225
+ () => console.debug('unknown') // this is restricted by `() => void`
226
+ );
227
+
228
+ handler.run('hello world'); // input args for `run` are restricted by `(msg: string) => void`
229
+ ```
230
+
231
+ Notes:
232
+
233
+ - If you omit generic parameters TypeScript will fall back to `AnyFn` and inference is weaker.
234
+ - `BranchFn` defines the parameter types that `.run(...)` accepts and what each branch receives.
235
+ - `NoMatchFn` defines the signature of the `nomatch`/`deferedNomatch` handler.
236
+
237
+ Example: calculator with typed branches
238
+
239
+ ```typescript
240
+ const calc = deferedBranchDynamic<(a: number, b: number) => number, () => never>();
241
+
242
+ calc
243
+ .add(
244
+ () => operation === 'add',
245
+ (a, b) => a + b
246
+ )
247
+ .add(
248
+ () => operation === 'mul',
249
+ (a, b) => a * b
250
+ )
251
+ .nomatch(() => {
252
+ throw new Error('unknown operation');
253
+ });
254
+
255
+ calc.predicate();
256
+ const result = calc.run(2, 3); // typed as number | undefined
257
+ ```
258
+
200
259
  ##### `.predicate(...args): void`
201
260
 
202
261
  - Evaluates all conditions to find the first match
package/dist/index.d.ts CHANGED
@@ -1,21 +1,21 @@
1
- declare class DeferBranch<BranchFn extends AnyFn, NoMatchFn extends AnyFn = AnyFn> {
1
+ declare class DeferBranch<BranchFn extends AnyFn, NoMatchFn extends AnyFn> {
2
2
  private _branch;
3
3
  private _nomatch;
4
4
  /**
5
5
  * Add a new entry
6
- * - will **override** the previous matched branch
6
+ * - **First truthy first served**.
7
7
  * @param condition the condition to match
8
8
  * @param branch the branch to run when matched
9
9
  * @returns this
10
10
  */
11
- add(condition: boolean, branch: BranchFn): DeferBranch<BranchFn>;
11
+ add(condition: boolean, branch: BranchFn): this;
12
12
  /**
13
13
  * Register a handler to be called when no branch matched
14
14
  * - This will be called **instantly and ignores later matched branches**
15
15
  * @param handler handle the exhausted case
16
16
  * @returns this
17
17
  */
18
- nomatch(handler: AnyFn): DeferBranch<BranchFn>;
18
+ nomatch(handler: AnyFn): this;
19
19
  /**
20
20
  * If some branch matched, return its returnValue
21
21
  * - if no branch matched, return the nomatch handler's returnValue
@@ -27,7 +27,7 @@ declare class DeferBranch<BranchFn extends AnyFn, NoMatchFn extends AnyFn = AnyF
27
27
  run(...args: Parameters<BranchFn>): ReturnType<BranchFn> | ReturnType<NoMatchFn> | undefined;
28
28
  }
29
29
 
30
- declare class DeferBranchDynamic<BranchFn extends AnyFn, NoMatchFn extends AnyFn = AnyFn> {
30
+ declare class DeferBranchDynamic<BranchFn extends AnyFn, NoMatchFn extends AnyFn, ConditionFn extends AnyFn = Predicate<BranchFn>> {
31
31
  /**
32
32
  * & Compress conditions and branches into a single array since they are paired
33
33
  */
@@ -36,18 +36,18 @@ declare class DeferBranchDynamic<BranchFn extends AnyFn, NoMatchFn extends AnyFn
36
36
  private _nomatch;
37
37
  /**
38
38
  * Add a new entry
39
- * - will **override** the previous matched branch
39
+ * - **First truthy first served**.
40
40
  * @param condition the condition to match
41
41
  * @param branch the branch to run when matched
42
42
  * @returns this
43
43
  */
44
- add(condition: Predicate<BranchFn>, branch: BranchFn): DeferBranchDynamic<BranchFn, NoMatchFn>;
44
+ add(condition: ConditionFn, branch: BranchFn): this;
45
45
  /**
46
46
  * When calling `this.predicate` and no branch matched, run this handler
47
47
  * @param handler handle the exhausted case
48
48
  * @returns this
49
49
  */
50
- nomatch(handler: AnyFn): DeferBranchDynamic<BranchFn, NoMatchFn>;
50
+ nomatch(handler: AnyFn): this;
51
51
  /**
52
52
  * Run the condition to find the first matched branch
53
53
  */
@@ -63,20 +63,36 @@ declare class DeferBranchDynamic<BranchFn extends AnyFn, NoMatchFn extends AnyFn
63
63
  * ## Steps to use
64
64
  * 1. const a = deferedBranch()
65
65
  * 2. add branches by a.add(condition, branch)
66
- * 3. add defered nomatch handler, or add and run at the same time by a.nomatch(handler)
67
- * 4. run this branch by a.run();
66
+ * 3. (optional) add defered nomatch handler, or add and run at the same time by a.nomatch(handler)
67
+ * 4. ...run some other logic
68
+ * 5. run this branch by a.run();
69
+ *
70
+ * ## Type Annotation (optional)
71
+ * ```ts
72
+ * type BranchFn = (a: number) => void;
73
+ * type NoMatchFn = () => void;
74
+ * const a = deferedBranch<BranchFn, NoMatchFn>();
75
+ *
76
+ * // then we have restrictions:
77
+ * a.add(true, someFn satisfies BranchFn);
78
+ * a.nomatch(otherFn satisfies NoMatchFn);
79
+ *
80
+ * // some other logic
81
+ *
82
+ * a.run(...args); // input args are restricted to Parameters<BranchFn>
83
+ * ```
68
84
  *
69
85
  * ## About
70
86
  * @package DeferedBranch
71
87
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
72
- * @version 2.0.0 (Last Update: 2025.09.20 21:23:15.798)
88
+ * @version 2.1.1 (Last Update: 2025.09.21 09:04:58.051)
73
89
  * @license MIT
74
90
  * @link https://github.com/baendlorel/defered-branch
75
91
  * @link https://baendlorel.github.io/ Welcome to my site!
76
92
  * @description Sometimes we need to judge something with multiple cases, but the main job should be done later. So we have Defered Branch!
77
93
  * @copyright Copyright (c) 2025 Kasukabe Tsumugi. All rights reserved.
78
94
  */
79
- declare const deferedBranch: () => DeferBranch<AnyFn, AnyFn>;
95
+ declare const deferedBranch: <BranchFn extends AnyFn = AnyFn, NoMatchFn extends AnyFn = AnyFn>() => DeferBranch<BranchFn, NoMatchFn>;
80
96
  /**
81
97
  * ## Steps to use
82
98
  * 1. const a = deferedBranchDynamic()
@@ -86,17 +102,38 @@ declare const deferedBranch: () => DeferBranch<AnyFn, AnyFn>;
86
102
  * 5. ...run some other logic
87
103
  * 6. run this branch by a.run();
88
104
  *
105
+ * ## Type Annotation (optional)
106
+ * ```ts
107
+ * type BranchFn = (a: number) => void;
108
+ * type NoMatchFn = () => void;
109
+ * // optional, you can customize the condition function type
110
+ * type ConditionFn = Predicate<BranchFn>;
111
+ * const a = deferedBranch<BranchFn, NoMatchFn, ConditionFn>();
112
+ *
113
+ * // then we have restrictions:
114
+ * a.add(someFn satisfies ConditionFn, secondFn satisfies BranchFn);
115
+ * a.nomatch(thirdFn satisfies NoMatchFn);
116
+ *
117
+ * // some other logic
118
+ *
119
+ * a.predicate(...args);
120
+ *
121
+ * // some other logic
122
+ *
123
+ * a.run(...args); // input args are restricted to Parameters<BranchFn>
124
+ * ```
125
+ *
89
126
  * ## About
90
127
  * @package DeferedBranch
91
128
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
92
- * @version 2.0.0 (Last Update: 2025.09.20 21:23:15.798)
129
+ * @version 2.1.1 (Last Update: 2025.09.21 09:04:58.051)
93
130
  * @license MIT
94
131
  * @link https://github.com/baendlorel/defered-branch
95
132
  * @link https://baendlorel.github.io/ Welcome to my site!
96
133
  * @description Sometimes we need to judge something with multiple cases, but the main job should be done later. So we have Defered Branch!
97
134
  * @copyright Copyright (c) 2025 Kasukabe Tsumugi. All rights reserved.
98
135
  */
99
- declare const deferedBranchDynamic: () => DeferBranchDynamic<AnyFn, AnyFn>;
136
+ declare const deferedBranchDynamic: <BranchFn extends AnyFn = AnyFn, NoMatchFn extends AnyFn = AnyFn, ConditionFn extends AnyFn = Predicate<BranchFn>>() => DeferBranchDynamic<BranchFn, NoMatchFn, ConditionFn>;
100
137
 
101
138
  export { deferedBranch, deferedBranchDynamic };
102
139
 
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- class n{t=null;h=null;add(n,t){if("function"!=typeof t)throw new TypeError("DeferBranch: branch must be a function");return n&&(this.t=t),this}nomatch(n){if("function"!=typeof n)throw new TypeError("DeferBranch: branch must be a function");return this.t||n(),this}run(...n){return this.t?this.t.apply(null,n):this.h?this.h.apply(null,n):void 0}}class t{i=[];t=null;h=null;add(n,t){if("function"!=typeof n)throw new TypeError("DeferBranch: condition must be a function");if("function"!=typeof t)throw new TypeError("DeferBranch: branch must be a function");return this.i.push(n,t),this}nomatch(n){if("function"!=typeof n)throw new TypeError("DeferBranch: handler must be a function");return this.h=n,this}predicate(...n){const t=this.i.length;for(let n=0;n<t;n+=2)if(this.i[n]())return void(this.t=this.i[n+1]);this.h?.apply(this,n)}run(...n){return this.t?.apply(this,n)}}const r=()=>new n,e=()=>new t;export{r as deferedBranch,e as deferedBranchDynamic};
1
+ class n{t=null;h=null;add(n,t){if("function"!=typeof t)throw new TypeError("DeferBranch: branch must be a function");return n&&!this.t&&(this.t=t),this}nomatch(n){if("function"!=typeof n)throw new TypeError("DeferBranch: branch must be a function");return this.t||n(),this}run(...n){return this.t?this.t.apply(null,n):this.h?this.h.apply(null,n):void 0}}class t{i=[];t=null;h=null;add(n,t){if("function"!=typeof n)throw new TypeError("DeferBranch: condition must be a function");if("function"!=typeof t)throw new TypeError("DeferBranch: branch must be a function");return this.i.push(n,t),this}nomatch(n){if("function"!=typeof n)throw new TypeError("DeferBranch: handler must be a function");return this.h=n,this}predicate(...n){const t=this.i.length;for(let n=0;n<t;n+=2)if(this.i[n]())return void(this.t=this.i[n+1]);this.h?.apply(this,n)}run(...n){return this.t?.apply(this,n)}}const r=()=>new n,e=()=>new t;export{r as deferedBranch,e as deferedBranchDynamic};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "defered-branch",
3
- "version": "2.0.0",
3
+ "version": "2.1.1",
4
4
  "author": {
5
5
  "name": "Kasukabe Tsumugi",
6
6
  "email": "futami16237@gmail.com"