class-resolver 2.1.1 → 4.0.0
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 +329 -6
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +190 -3
- package/dist/index.mjs +163 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +41 -30
- package/dist/index.js +0 -26
- package/dist/index.js.map +0 -1
- package/dist/interface.d.ts +0 -23
- package/dist/interface.js +0 -3
- package/dist/interface.js.map +0 -1
- package/dist/resolver.d.ts +0 -48
- package/dist/resolver.js +0 -75
- package/dist/resolver.js.map +0 -1
- package/index.js +0 -5
- package/libs/index.ts +0 -6
- package/libs/interface.ts +0 -26
- package/libs/resolver.ts +0 -84
- package/readme-ja.md +0 -301
package/README.md
CHANGED
|
@@ -10,6 +10,11 @@ A lightweight TypeScript/JavaScript library for implementing the Chain of Respon
|
|
|
10
10
|
- Support for multiple resolvers with different handling logic
|
|
11
11
|
- Clear error handling for unsupported types
|
|
12
12
|
- Generic type support for better type safety
|
|
13
|
+
- **Fallback handler support for graceful handling of unsupported types**
|
|
14
|
+
- **Method chaining support for fluent API usage**
|
|
15
|
+
- **🎯 NEW: Multiple handler execution (resolveAll, handleAll)**
|
|
16
|
+
- **🎯 NEW: Priority-based handler resolution**
|
|
17
|
+
- **🎯 NEW: Async handler support (handleAllAsync, handleAllSequential)**
|
|
13
18
|
|
|
14
19
|
## Installation
|
|
15
20
|
|
|
@@ -55,6 +60,27 @@ try {
|
|
|
55
60
|
}
|
|
56
61
|
```
|
|
57
62
|
|
|
63
|
+
## Fallback Handler Usage
|
|
64
|
+
|
|
65
|
+
The fallback handler allows you to gracefully handle unsupported types without throwing errors:
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
const resolver = new Resolver(new ExampleClass(), new ExampleClass2())
|
|
69
|
+
|
|
70
|
+
// Set a fallback handler for unsupported types
|
|
71
|
+
resolver.setFallbackHandler((type) => {
|
|
72
|
+
return `Fallback: ${type}`
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
// Now unsupported types will use the fallback handler instead of throwing errors
|
|
76
|
+
const result = resolver.resolve('xxx')
|
|
77
|
+
console.log(result.handle('xxx')) // Output: Fallback: xxx
|
|
78
|
+
|
|
79
|
+
// Supported types still work normally
|
|
80
|
+
const c = resolver.resolve('hoge')
|
|
81
|
+
console.log(c.handle()) // Output: hoge
|
|
82
|
+
```
|
|
83
|
+
|
|
58
84
|
## Advanced Usage
|
|
59
85
|
|
|
60
86
|
### With TypeScript and Parameters
|
|
@@ -113,6 +139,33 @@ resolver.addUpdater(new MessageFormatter())
|
|
|
113
139
|
resolver.addUpdater(new ErrorFormatter())
|
|
114
140
|
```
|
|
115
141
|
|
|
142
|
+
### Fallback Handler with TypeScript
|
|
143
|
+
|
|
144
|
+
The fallback handler maintains full type safety and automatically infers types from your resolver configuration:
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
// Create a resolver with specific types
|
|
148
|
+
const resolver = new Resolver<ResolveTarget<[string, number], string>>(
|
|
149
|
+
new MessageFormatter()
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
// Set a fallback handler with the same type signature
|
|
153
|
+
resolver.setFallbackHandler((name: string, count: number): string => {
|
|
154
|
+
return `Default greeting for ${name} (message #${count})`
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
// The fallback handler will be used for unsupported types
|
|
158
|
+
const result = resolver.resolve('unknown').handle('John', 5)
|
|
159
|
+
console.log(result) // Output: Default greeting for John (message #5)
|
|
160
|
+
|
|
161
|
+
// Method chaining is also supported
|
|
162
|
+
resolver
|
|
163
|
+
.setFallbackHandler((name: string, count: number): string => {
|
|
164
|
+
return `Custom fallback: ${name} - ${count}`
|
|
165
|
+
})
|
|
166
|
+
.addUpdater(new ErrorFormatter())
|
|
167
|
+
```
|
|
168
|
+
|
|
116
169
|
## Generic Type Support
|
|
117
170
|
|
|
118
171
|
From version 2.0.0, class-resolver supports generic types for better type safety:
|
|
@@ -202,20 +255,290 @@ This advanced type support allows you to:
|
|
|
202
255
|
- Handle domain-specific objects like Stripe events, database records, or custom business objects
|
|
203
256
|
- Create more expressive and type-safe event handling systems
|
|
204
257
|
|
|
258
|
+
## Breaking Changes in v3.0.0
|
|
259
|
+
|
|
260
|
+
### `resolve()` Method Behavior Change
|
|
261
|
+
|
|
262
|
+
**Important**: The `resolve()` method now returns the **highest priority handler** instead of the first matching handler based on registration order.
|
|
263
|
+
|
|
264
|
+
#### Before (v2.x)
|
|
265
|
+
```typescript
|
|
266
|
+
const resolver = new Resolver(handler1, handler2, handler3);
|
|
267
|
+
const result = resolver.resolve('type'); // Returns handler1 (first registered)
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
#### After (v3.0.0)
|
|
271
|
+
```typescript
|
|
272
|
+
// Without priority - behavior unchanged (returns first matching handler)
|
|
273
|
+
const resolver = new Resolver(handler1, handler2, handler3);
|
|
274
|
+
const result = resolver.resolve('type'); // Still returns handler1
|
|
275
|
+
|
|
276
|
+
// With priority - returns highest priority handler
|
|
277
|
+
class HighPriority implements PrioritizedResolveTarget {
|
|
278
|
+
priority = 100;
|
|
279
|
+
// ...
|
|
280
|
+
}
|
|
281
|
+
class LowPriority implements PrioritizedResolveTarget {
|
|
282
|
+
priority = 10;
|
|
283
|
+
// ...
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const resolver = new Resolver(lowPriority, highPriority);
|
|
287
|
+
const result = resolver.resolve('type'); // Returns highPriority (priority: 100)
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
**Migration Guide**: If you rely on registration order and don't want priority-based resolution:
|
|
291
|
+
- Continue using handlers without the `priority` property - they will maintain registration order (all have default priority of 0)
|
|
292
|
+
- Or explicitly set the same priority on all handlers to maintain registration order
|
|
293
|
+
|
|
294
|
+
## New Features in v3.0.0
|
|
295
|
+
|
|
296
|
+
### Multiple Handler Execution
|
|
297
|
+
|
|
298
|
+
Execute all matching handlers for a single event type. Perfect for webhook fanout patterns where one event needs multiple processors.
|
|
299
|
+
|
|
300
|
+
```typescript
|
|
301
|
+
import Resolver from 'class-resolver';
|
|
302
|
+
import { ResolveTarget } from 'class-resolver';
|
|
303
|
+
|
|
304
|
+
interface StripeEvent {
|
|
305
|
+
type: string;
|
|
306
|
+
data: { amount: number };
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
class AccountingHandler implements ResolveTarget<[StripeEvent], string, StripeEvent> {
|
|
310
|
+
supports(event: StripeEvent): boolean {
|
|
311
|
+
return event.type === 'payment.succeeded';
|
|
312
|
+
}
|
|
313
|
+
handle(event: StripeEvent): string {
|
|
314
|
+
return `Accounting: Recorded ${event.data.amount}`;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
class EmailHandler implements ResolveTarget<[StripeEvent], string, StripeEvent> {
|
|
319
|
+
supports(event: StripeEvent): boolean {
|
|
320
|
+
return event.type === 'payment.succeeded';
|
|
321
|
+
}
|
|
322
|
+
handle(event: StripeEvent): string {
|
|
323
|
+
return `Email: Sent confirmation for ${event.data.amount}`;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
class AnalyticsHandler implements ResolveTarget<[StripeEvent], string, StripeEvent> {
|
|
328
|
+
supports(event: StripeEvent): boolean {
|
|
329
|
+
return event.type === 'payment.succeeded';
|
|
330
|
+
}
|
|
331
|
+
handle(event: StripeEvent): string {
|
|
332
|
+
return `Analytics: Logged ${event.data.amount}`;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const resolver = new Resolver<ResolveTarget<[StripeEvent], string, StripeEvent>, StripeEvent>(
|
|
337
|
+
new AccountingHandler(),
|
|
338
|
+
new EmailHandler(),
|
|
339
|
+
new AnalyticsHandler()
|
|
340
|
+
);
|
|
341
|
+
|
|
342
|
+
const event: StripeEvent = {
|
|
343
|
+
type: 'payment.succeeded',
|
|
344
|
+
data: { amount: 1000 }
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
// Execute ALL matching handlers
|
|
348
|
+
const results = resolver.handleAll(event, event);
|
|
349
|
+
// Results: [
|
|
350
|
+
// 'Accounting: Recorded 1000',
|
|
351
|
+
// 'Email: Sent confirmation for 1000',
|
|
352
|
+
// 'Analytics: Logged 1000'
|
|
353
|
+
// ]
|
|
354
|
+
|
|
355
|
+
// Or get all matching handlers
|
|
356
|
+
const handlers = resolver.resolveAll(event);
|
|
357
|
+
// handlers.length === 3
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### Priority-Based Handler Resolution
|
|
361
|
+
|
|
362
|
+
Control execution order with priority levels. Higher priority handlers execute first.
|
|
363
|
+
|
|
364
|
+
```typescript
|
|
365
|
+
import { PrioritizedResolveTarget } from 'class-resolver';
|
|
366
|
+
|
|
367
|
+
class ValidationHandler implements PrioritizedResolveTarget<[any], boolean, string> {
|
|
368
|
+
priority = 100; // Highest priority
|
|
369
|
+
|
|
370
|
+
supports(type: string): boolean {
|
|
371
|
+
return type === 'webhook';
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
handle(data: any): boolean {
|
|
375
|
+
return data !== null && data !== undefined;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
class BusinessLogicHandler implements PrioritizedResolveTarget<[any], string, string> {
|
|
380
|
+
priority = 50; // Medium priority
|
|
381
|
+
|
|
382
|
+
supports(type: string): boolean {
|
|
383
|
+
return type === 'webhook';
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
handle(data: any): string {
|
|
387
|
+
return `Processed: ${JSON.stringify(data)}`;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
class LoggingHandler implements PrioritizedResolveTarget<[any], void, string> {
|
|
392
|
+
priority = 10; // Lowest priority
|
|
393
|
+
|
|
394
|
+
supports(type: string): boolean {
|
|
395
|
+
return type === 'webhook';
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
handle(data: any): void {
|
|
399
|
+
console.log(`Logged: ${JSON.stringify(data)}`);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const resolver = new Resolver<PrioritizedResolveTarget<[any], any, string>, string>(
|
|
404
|
+
new LoggingHandler(), // Registered third
|
|
405
|
+
new ValidationHandler(), // Registered first
|
|
406
|
+
new BusinessLogicHandler() // Registered second
|
|
407
|
+
);
|
|
408
|
+
|
|
409
|
+
// Handlers execute in PRIORITY order (not registration order):
|
|
410
|
+
// 1. ValidationHandler (priority: 100)
|
|
411
|
+
// 2. BusinessLogicHandler (priority: 50)
|
|
412
|
+
// 3. LoggingHandler (priority: 10)
|
|
413
|
+
const results = resolver.handleAll('webhook', { test: true });
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
### Async Handler Support
|
|
417
|
+
|
|
418
|
+
Execute async handlers in parallel or sequentially.
|
|
419
|
+
|
|
420
|
+
```typescript
|
|
421
|
+
import { AsyncResolveTarget } from 'class-resolver';
|
|
422
|
+
|
|
423
|
+
class SaveToDBHandler implements AsyncResolveTarget<[any], string, string> {
|
|
424
|
+
supports(type: string): boolean {
|
|
425
|
+
return type === 'payment';
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
async handle(data: any): Promise<string> {
|
|
429
|
+
// Simulate DB save
|
|
430
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
431
|
+
return 'Saved to DB';
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
class SendWebhookHandler implements AsyncResolveTarget<[any], string, string> {
|
|
436
|
+
supports(type: string): boolean {
|
|
437
|
+
return type === 'payment';
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
async handle(data: any): Promise<string> {
|
|
441
|
+
// Simulate external API call
|
|
442
|
+
await new Promise(resolve => setTimeout(resolve, 200));
|
|
443
|
+
return 'Webhook sent';
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const resolver = new Resolver<AsyncResolveTarget<[any], string, string>, string>(
|
|
448
|
+
new SaveToDBHandler(),
|
|
449
|
+
new SendWebhookHandler()
|
|
450
|
+
);
|
|
451
|
+
|
|
452
|
+
// Execute handlers in PARALLEL (fastest)
|
|
453
|
+
const results = await resolver.handleAllAsync('payment', { amount: 1000 });
|
|
454
|
+
// Results: ['Saved to DB', 'Webhook sent']
|
|
455
|
+
// Total time: ~200ms (not 300ms)
|
|
456
|
+
|
|
457
|
+
// Or execute SEQUENTIALLY (ordered, stops on error)
|
|
458
|
+
const results2 = await resolver.handleAllSequential('payment', { amount: 1000 });
|
|
459
|
+
// Results: ['Saved to DB', 'Webhook sent']
|
|
460
|
+
// Total time: ~300ms
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
### Priority + Async Combined
|
|
464
|
+
|
|
465
|
+
```typescript
|
|
466
|
+
import { PrioritizedAsyncResolveTarget } from 'class-resolver';
|
|
467
|
+
|
|
468
|
+
class ValidationHandler implements PrioritizedAsyncResolveTarget<[any], boolean, string> {
|
|
469
|
+
priority = 100;
|
|
470
|
+
|
|
471
|
+
supports(type: string): boolean {
|
|
472
|
+
return type === 'order';
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
async handle(data: any): Promise<boolean> {
|
|
476
|
+
// Async validation
|
|
477
|
+
return data.amount > 0;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
class ProcessHandler implements PrioritizedAsyncResolveTarget<[any], string, string> {
|
|
482
|
+
priority = 50;
|
|
483
|
+
|
|
484
|
+
supports(type: string): boolean {
|
|
485
|
+
return type === 'order';
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
async handle(data: any): Promise<string> {
|
|
489
|
+
return `Processed order ${data.id}`;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
const resolver = new Resolver<PrioritizedAsyncResolveTarget<[any], any, string>, string>(
|
|
494
|
+
new ProcessHandler(), // priority: 50
|
|
495
|
+
new ValidationHandler() // priority: 100
|
|
496
|
+
);
|
|
497
|
+
|
|
498
|
+
// Executes in priority order: Validation → Process
|
|
499
|
+
const results = await resolver.handleAllAsync('order', { id: 123, amount: 1000 });
|
|
500
|
+
// Results: [true, 'Processed order 123']
|
|
501
|
+
```
|
|
502
|
+
|
|
205
503
|
## Use Cases
|
|
206
504
|
|
|
207
|
-
1. **
|
|
208
|
-
2. **
|
|
209
|
-
3. **
|
|
210
|
-
4. **
|
|
211
|
-
5. **
|
|
505
|
+
1. **Webhook Fanout**: Process a single webhook event with multiple handlers (accounting, notifications, analytics)
|
|
506
|
+
2. **Event-Driven Architecture**: Route events to multiple subscribers based on event type
|
|
507
|
+
3. **Command Pattern Implementation**: Handle different types of commands with specific handlers
|
|
508
|
+
4. **Validation Pipeline**: Execute validation, business logic, and logging in priority order
|
|
509
|
+
5. **Async Workflows**: Coordinate multiple async operations (DB saves, API calls, file operations)
|
|
510
|
+
6. **Plugin System**: Implement a plugin system where different plugins handle specific types of operations
|
|
511
|
+
7. **Message Formatting**: Format different types of messages with specific formatters
|
|
512
|
+
8. **Graceful Degradation**: Use fallback handlers to provide default behavior for unknown types
|
|
513
|
+
9. **API Versioning**: Handle different API versions with fallback to backward-compatible behavior
|
|
514
|
+
10. **LINE Bot / Discord Bot**: Route different message types to appropriate handlers
|
|
212
515
|
|
|
213
516
|
## Error Handling
|
|
214
517
|
|
|
215
518
|
The resolver will throw errors in the following cases:
|
|
216
|
-
- When no resolvers are registered: `"
|
|
519
|
+
- When no resolvers are registered: `"Unassigned resolve target."`
|
|
217
520
|
- When trying to resolve an unsupported type: `"Unsupported type: xxx"`
|
|
218
521
|
|
|
522
|
+
### Fallback Handler for Error Prevention
|
|
523
|
+
|
|
524
|
+
With the fallback handler, you can prevent errors for unsupported types:
|
|
525
|
+
|
|
526
|
+
```typescript
|
|
527
|
+
const resolver = new Resolver(new ExampleClass())
|
|
528
|
+
|
|
529
|
+
// Without fallback handler - throws error
|
|
530
|
+
try {
|
|
531
|
+
resolver.resolve('unknown')
|
|
532
|
+
} catch (e) {
|
|
533
|
+
console.log(e) // Error: Unsupported type: unknown
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// With fallback handler - no error thrown
|
|
537
|
+
resolver.setFallbackHandler((type) => `Default: ${type}`)
|
|
538
|
+
const result = resolver.resolve('unknown') // No error, uses fallback
|
|
539
|
+
console.log(result.handle('unknown')) // Output: Default: unknown
|
|
540
|
+
```
|
|
541
|
+
|
|
219
542
|
## Upgrade Guide
|
|
220
543
|
|
|
221
544
|
### Upgrading from 1.x to 2.0.0
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";class i{updaters=[];fallbackHandler;constructor(...t){t.length>0&&this.set(t)}getArgs(t){return[...t]}set(t){this.updaters=t}setUpdaters(...t){this.set(this.getArgs(t))}addUpdater(t){return this.updaters.push(t),this}setFallbackHandler(t){return this.fallbackHandler=t,this}getPriority(t){const r=t;return typeof r.priority=="number"?r.priority:0}sortByPriority(t){return[...t].sort((r,e)=>{const s=this.getPriority(r);return this.getPriority(e)-s})}resolve(t){if(this.updaters.length<1)throw new Error("Unassigned resolve target.");const r=this.updaters.filter(n=>n.supports(t)),s=this.sortByPriority(r)[0];if(!s){if(this.fallbackHandler)return{supports:()=>!0,handle:this.fallbackHandler};const n=typeof t=="object"&&t!==null?JSON.stringify(t):String(t);throw new Error(`Unsupported type: ${n}`)}return s}resolveAll(t){if(this.updaters.length<1)throw new Error("Unassigned resolve target.");const r=this.updaters.filter(e=>e.supports(t));return r.length===0?this.fallbackHandler?[{supports:()=>!0,handle:this.fallbackHandler}]:[]:this.sortByPriority(r)}handleAll(t,...r){return this.resolveAll(t).map(s=>s.handle(...r))}async handleAllAsync(t,...r){const s=this.resolveAll(t).map(n=>n.handle(...r));return Promise.all(s)}async handleAllSequential(t,...r){const e=this.resolveAll(t),s=[];for(const n of e){const l=await n.handle(...r);s.push(l)}return s}}module.exports=i;module.exports=i;
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../libs/resolver.ts","../libs/index.ts"],"sourcesContent":["import type { ResolveTarget } from './interface';\n\n/**\n * Resolver class implementing the Chain of Responsibility pattern\n * Resolves handlers for specific types\n */\nclass Resolver<\n TBase extends ResolveTarget<any[], any, any> = ResolveTarget<any[], any, any>,\n TType = string,\n> {\n /**\n * Array of registered resolver targets\n * @private\n */\n private updaters: TBase[] = [];\n\n /**\n * Fallback handler function\n * @private\n */\n private fallbackHandler?: (...args: Parameters<TBase['handle']>) => ReturnType<TBase['handle']>;\n\n /**\n * Initializes the resolver\n * @param args Initial resolver targets\n */\n constructor(...args: TBase[]) {\n if (args.length > 0) {\n this.set(args);\n }\n }\n\n /**\n * Processes an array of arguments\n * @param args Array of resolver targets\n * @returns Processed array\n * @private\n */\n private getArgs(args: TBase[]): TBase[] {\n return [...args];\n }\n\n /**\n * Sets resolver targets\n * @param updaters Array of resolver targets\n */\n public set(updaters: TBase[]): void {\n this.updaters = updaters;\n }\n\n /**\n * Sets resolver targets (variadic version)\n * @param args Resolver targets\n */\n public setUpdaters(...args: TBase[]): void {\n this.set(this.getArgs(args));\n }\n\n /**\n * Adds a resolver target\n * @param updater Resolver target to add\n */\n public addUpdater(updater: TBase): this {\n this.updaters.push(updater);\n return this;\n }\n\n /**\n * Sets a fallback handler for unsupported types\n * @param handler Fallback handler function\n * @returns This resolver instance for method chaining\n */\n public setFallbackHandler(\n handler: (...args: Parameters<TBase['handle']>) => ReturnType<TBase['handle']>\n ): this {\n this.fallbackHandler = handler;\n return this;\n }\n\n /**\n * Gets the priority of a handler\n * @param handler The handler to get priority for\n * @returns Priority value (0 if not defined)\n * @private\n */\n private getPriority(handler: TBase): number {\n const handlerWithPriority = handler as Partial<{ priority: number }>;\n return typeof handlerWithPriority.priority === 'number' ? handlerWithPriority.priority : 0;\n }\n\n /**\n * Sorts handlers by priority (highest first), maintaining registration order for equal priorities\n * @param handlers Array of handlers to sort\n * @returns Sorted array of handlers\n * @private\n */\n private sortByPriority(handlers: TBase[]): TBase[] {\n return [...handlers].sort((a, b) => {\n const priorityA = this.getPriority(a);\n const priorityB = this.getPriority(b);\n return priorityB - priorityA; // Higher priority first\n });\n }\n\n /**\n * Resolves a resolver target for the specified type\n * @param type Type to resolve\n * @returns Resolved resolver target\n * @throws {Error} When no resolver targets are registered\n * @throws {Error} When no resolver target supporting the specified type is found and no fallback is set\n */\n public resolve(type: TType): TBase {\n if (this.updaters.length < 1) {\n throw new Error('Unassigned resolve target.');\n }\n\n // Get all matching handlers and sort by priority\n const matchingHandlers = this.updaters.filter((updater) => updater.supports(type));\n const sortedHandlers = this.sortByPriority(matchingHandlers);\n const target = sortedHandlers[0];\n\n if (!target) {\n // If fallback handler is set, create a temporary target that uses it\n if (this.fallbackHandler) {\n return {\n supports: () => true,\n handle: this.fallbackHandler,\n } as unknown as TBase;\n }\n\n // Determine the string representation of the unsupported type\n // If it's a non-null object, use JSON.stringify for detailed output\n // Otherwise, use String() for basic conversion\n const typeString =\n typeof type === 'object' && type !== null ? JSON.stringify(type) : String(type);\n throw new Error(`Unsupported type: ${typeString}`);\n }\n\n return target;\n }\n\n /**\n * Resolves all resolver targets for the specified type\n * @param type Type to resolve\n * @returns Array of all matching resolver targets sorted by priority (highest first).\n * Returns an empty array if no handlers match and no fallback is set.\n * Note: Unlike resolve(), this method does not throw when no handlers match.\n * @throws {Error} When no resolver targets are registered\n */\n public resolveAll(type: TType): TBase[] {\n if (this.updaters.length < 1) {\n throw new Error('Unassigned resolve target.');\n }\n\n const targets = this.updaters.filter((updater) => updater.supports(type));\n\n if (targets.length === 0) {\n // If fallback handler is set, return it as a single-element array\n if (this.fallbackHandler) {\n return [\n {\n supports: () => true,\n handle: this.fallbackHandler,\n } as unknown as TBase,\n ];\n }\n\n // Return empty array if no handlers match\n return [];\n }\n\n // Sort by priority (highest first)\n return this.sortByPriority(targets);\n }\n\n /**\n * Executes all matching handlers for the specified type\n * @param type Type to resolve\n * @param args Arguments to pass to the handlers\n * @returns Array of results from all matching handlers\n * @throws {Error} When no resolver targets are registered\n */\n public handleAll(\n type: TType,\n ...args: Parameters<TBase['handle']>\n ): ReturnType<TBase['handle']>[] {\n const targets = this.resolveAll(type);\n return targets.map((target) => target.handle(...args));\n }\n\n /**\n * Executes all matching async handlers in parallel for the specified type\n * @param type Type to resolve\n * @param args Arguments to pass to the handlers\n * @returns Promise that resolves to array of results from all matching handlers\n * @throws {Error} When no resolver targets are registered\n */\n public async handleAllAsync(\n type: TType,\n ...args: Parameters<TBase['handle']>\n ): Promise<Awaited<ReturnType<TBase['handle']>>[]> {\n const targets = this.resolveAll(type);\n const promises = targets.map((target) => target.handle(...args));\n return Promise.all(promises);\n }\n\n /**\n * Executes all matching async handlers sequentially for the specified type\n * Stops on first error\n * @param type Type to resolve\n * @param args Arguments to pass to the handlers\n * @returns Promise that resolves to array of results from all matching handlers\n * @throws {Error} When no resolver targets are registered\n * @throws {Error} When any handler throws an error\n */\n public async handleAllSequential(\n type: TType,\n ...args: Parameters<TBase['handle']>\n ): Promise<Awaited<ReturnType<TBase['handle']>>[]> {\n const targets = this.resolveAll(type);\n const results: Awaited<ReturnType<TBase['handle']>>[] = [];\n\n for (const target of targets) {\n const result = await target.handle(...args);\n results.push(result);\n }\n\n return results;\n }\n}\n\nexport default Resolver;\n","export * from './interface';\nimport Resolver from './resolver';\nexport default Resolver;\n// Export for CommonJS compatibility\n// @ts-ignore\nmodule.exports = Resolver;\n"],"names":["Resolver","args","updaters","updater","handler","handlerWithPriority","handlers","a","b","priorityA","type","matchingHandlers","target","typeString","targets","promises","results","result"],"mappings":"aAMA,MAAMA,CAGJ,CAKQ,SAAoB,CAAA,EAMpB,gBAMR,eAAeC,EAAe,CACxBA,EAAK,OAAS,GAChB,KAAK,IAAIA,CAAI,CAEjB,CAQQ,QAAQA,EAAwB,CACtC,MAAO,CAAC,GAAGA,CAAI,CACjB,CAMO,IAAIC,EAAyB,CAClC,KAAK,SAAWA,CAClB,CAMO,eAAeD,EAAqB,CACzC,KAAK,IAAI,KAAK,QAAQA,CAAI,CAAC,CAC7B,CAMO,WAAWE,EAAsB,CACtC,YAAK,SAAS,KAAKA,CAAO,EACnB,IACT,CAOO,mBACLC,EACM,CACN,YAAK,gBAAkBA,EAChB,IACT,CAQQ,YAAYA,EAAwB,CAC1C,MAAMC,EAAsBD,EAC5B,OAAO,OAAOC,EAAoB,UAAa,SAAWA,EAAoB,SAAW,CAC3F,CAQQ,eAAeC,EAA4B,CACjD,MAAO,CAAC,GAAGA,CAAQ,EAAE,KAAK,CAACC,EAAGC,IAAM,CAClC,MAAMC,EAAY,KAAK,YAAYF,CAAC,EAEpC,OADkB,KAAK,YAAYC,CAAC,EACjBC,CACrB,CAAC,CACH,CASO,QAAQC,EAAoB,CACjC,GAAI,KAAK,SAAS,OAAS,EACzB,MAAM,IAAI,MAAM,4BAA4B,EAI9C,MAAMC,EAAmB,KAAK,SAAS,OAAQR,GAAYA,EAAQ,SAASO,CAAI,CAAC,EAE3EE,EADiB,KAAK,eAAeD,CAAgB,EAC7B,CAAC,EAE/B,GAAI,CAACC,EAAQ,CAEX,GAAI,KAAK,gBACP,MAAO,CACL,SAAU,IAAM,GAChB,OAAQ,KAAK,eAAA,EAOjB,MAAMC,EACJ,OAAOH,GAAS,UAAYA,IAAS,KAAO,KAAK,UAAUA,CAAI,EAAI,OAAOA,CAAI,EAChF,MAAM,IAAI,MAAM,qBAAqBG,CAAU,EAAE,CACnD,CAEA,OAAOD,CACT,CAUO,WAAWF,EAAsB,CACtC,GAAI,KAAK,SAAS,OAAS,EACzB,MAAM,IAAI,MAAM,4BAA4B,EAG9C,MAAMI,EAAU,KAAK,SAAS,OAAQX,GAAYA,EAAQ,SAASO,CAAI,CAAC,EAExE,OAAII,EAAQ,SAAW,EAEjB,KAAK,gBACA,CACL,CACE,SAAU,IAAM,GAChB,OAAQ,KAAK,eAAA,CACf,EAKG,CAAA,EAIF,KAAK,eAAeA,CAAO,CACpC,CASO,UACLJ,KACGT,EAC4B,CAE/B,OADgB,KAAK,WAAWS,CAAI,EACrB,IAAKE,GAAWA,EAAO,OAAO,GAAGX,CAAI,CAAC,CACvD,CASA,MAAa,eACXS,KACGT,EAC8C,CAEjD,MAAMc,EADU,KAAK,WAAWL,CAAI,EACX,IAAKE,GAAWA,EAAO,OAAO,GAAGX,CAAI,CAAC,EAC/D,OAAO,QAAQ,IAAIc,CAAQ,CAC7B,CAWA,MAAa,oBACXL,KACGT,EAC8C,CACjD,MAAMa,EAAU,KAAK,WAAWJ,CAAI,EAC9BM,EAAkD,CAAA,EAExD,UAAWJ,KAAUE,EAAS,CAC5B,MAAMG,EAAS,MAAML,EAAO,OAAO,GAAGX,CAAI,EAC1Ce,EAAQ,KAAKC,CAAM,CACrB,CAEA,OAAOD,CACT,CACF,CChOA,OAAO,QAAUhB"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,190 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Interface for async resolver targets
|
|
3
|
+
* Handle method returns a Promise
|
|
4
|
+
*/
|
|
5
|
+
export declare interface AsyncResolveTarget<TArgs extends any[] = any[], TReturn = any, TType = string> extends SupportsType<TType> {
|
|
6
|
+
/**
|
|
7
|
+
* Handles the request asynchronously
|
|
8
|
+
* @param args Arguments needed for processing
|
|
9
|
+
* @returns Promise with processing result
|
|
10
|
+
*/
|
|
11
|
+
handle(...args: TArgs): Promise<TReturn>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Interface for prioritized handlers
|
|
16
|
+
* Higher priority values are executed first
|
|
17
|
+
*/
|
|
18
|
+
export declare interface HasPriority {
|
|
19
|
+
/**
|
|
20
|
+
* Priority for this handler (higher values execute first)
|
|
21
|
+
* Default is 0 if not specified
|
|
22
|
+
*/
|
|
23
|
+
priority?: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export declare namespace interfaces {
|
|
27
|
+
export interface ResolveTarget<TArgs extends any[] = any[], TReturn = any, TType = string> {
|
|
28
|
+
supports(type: TType): boolean;
|
|
29
|
+
handle(...args: TArgs): TReturn;
|
|
30
|
+
}
|
|
31
|
+
export interface PrioritizedResolveTarget<TArgs extends any[] = any[], TReturn = any, TType = string> extends ResolveTarget<TArgs, TReturn, TType> {
|
|
32
|
+
priority?: number;
|
|
33
|
+
}
|
|
34
|
+
export interface AsyncResolveTarget<TArgs extends any[] = any[], TReturn = any, TType = string> {
|
|
35
|
+
supports(type: TType): boolean;
|
|
36
|
+
handle(...args: TArgs): Promise<TReturn>;
|
|
37
|
+
}
|
|
38
|
+
export interface PrioritizedAsyncResolveTarget<TArgs extends any[] = any[], TReturn = any, TType = string> extends AsyncResolveTarget<TArgs, TReturn, TType> {
|
|
39
|
+
priority?: number;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Interface for async resolver targets with priority support
|
|
45
|
+
* Higher priority values are executed first
|
|
46
|
+
*/
|
|
47
|
+
export declare interface PrioritizedAsyncResolveTarget<TArgs extends any[] = any[], TReturn = any, TType = string> extends AsyncResolveTarget<TArgs, TReturn, TType>, HasPriority {
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Interface for resolver targets with priority support
|
|
52
|
+
* Higher priority values are executed first
|
|
53
|
+
*/
|
|
54
|
+
export declare interface PrioritizedResolveTarget<TArgs extends any[] = any[], TReturn = any, TType = string> extends ResolveTarget<TArgs, TReturn, TType>, HasPriority {
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Resolver class implementing the Chain of Responsibility pattern
|
|
59
|
+
* Resolves handlers for specific types
|
|
60
|
+
*/
|
|
61
|
+
declare class Resolver<TBase extends ResolveTarget<any[], any, any> = ResolveTarget<any[], any, any>, TType = string> {
|
|
62
|
+
/**
|
|
63
|
+
* Array of registered resolver targets
|
|
64
|
+
* @private
|
|
65
|
+
*/
|
|
66
|
+
private updaters;
|
|
67
|
+
/**
|
|
68
|
+
* Fallback handler function
|
|
69
|
+
* @private
|
|
70
|
+
*/
|
|
71
|
+
private fallbackHandler?;
|
|
72
|
+
/**
|
|
73
|
+
* Initializes the resolver
|
|
74
|
+
* @param args Initial resolver targets
|
|
75
|
+
*/
|
|
76
|
+
constructor(...args: TBase[]);
|
|
77
|
+
/**
|
|
78
|
+
* Processes an array of arguments
|
|
79
|
+
* @param args Array of resolver targets
|
|
80
|
+
* @returns Processed array
|
|
81
|
+
* @private
|
|
82
|
+
*/
|
|
83
|
+
private getArgs;
|
|
84
|
+
/**
|
|
85
|
+
* Sets resolver targets
|
|
86
|
+
* @param updaters Array of resolver targets
|
|
87
|
+
*/
|
|
88
|
+
set(updaters: TBase[]): void;
|
|
89
|
+
/**
|
|
90
|
+
* Sets resolver targets (variadic version)
|
|
91
|
+
* @param args Resolver targets
|
|
92
|
+
*/
|
|
93
|
+
setUpdaters(...args: TBase[]): void;
|
|
94
|
+
/**
|
|
95
|
+
* Adds a resolver target
|
|
96
|
+
* @param updater Resolver target to add
|
|
97
|
+
*/
|
|
98
|
+
addUpdater(updater: TBase): this;
|
|
99
|
+
/**
|
|
100
|
+
* Sets a fallback handler for unsupported types
|
|
101
|
+
* @param handler Fallback handler function
|
|
102
|
+
* @returns This resolver instance for method chaining
|
|
103
|
+
*/
|
|
104
|
+
setFallbackHandler(handler: (...args: Parameters<TBase['handle']>) => ReturnType<TBase['handle']>): this;
|
|
105
|
+
/**
|
|
106
|
+
* Gets the priority of a handler
|
|
107
|
+
* @param handler The handler to get priority for
|
|
108
|
+
* @returns Priority value (0 if not defined)
|
|
109
|
+
* @private
|
|
110
|
+
*/
|
|
111
|
+
private getPriority;
|
|
112
|
+
/**
|
|
113
|
+
* Sorts handlers by priority (highest first), maintaining registration order for equal priorities
|
|
114
|
+
* @param handlers Array of handlers to sort
|
|
115
|
+
* @returns Sorted array of handlers
|
|
116
|
+
* @private
|
|
117
|
+
*/
|
|
118
|
+
private sortByPriority;
|
|
119
|
+
/**
|
|
120
|
+
* Resolves a resolver target for the specified type
|
|
121
|
+
* @param type Type to resolve
|
|
122
|
+
* @returns Resolved resolver target
|
|
123
|
+
* @throws {Error} When no resolver targets are registered
|
|
124
|
+
* @throws {Error} When no resolver target supporting the specified type is found and no fallback is set
|
|
125
|
+
*/
|
|
126
|
+
resolve(type: TType): TBase;
|
|
127
|
+
/**
|
|
128
|
+
* Resolves all resolver targets for the specified type
|
|
129
|
+
* @param type Type to resolve
|
|
130
|
+
* @returns Array of all matching resolver targets sorted by priority (highest first).
|
|
131
|
+
* Returns an empty array if no handlers match and no fallback is set.
|
|
132
|
+
* Note: Unlike resolve(), this method does not throw when no handlers match.
|
|
133
|
+
* @throws {Error} When no resolver targets are registered
|
|
134
|
+
*/
|
|
135
|
+
resolveAll(type: TType): TBase[];
|
|
136
|
+
/**
|
|
137
|
+
* Executes all matching handlers for the specified type
|
|
138
|
+
* @param type Type to resolve
|
|
139
|
+
* @param args Arguments to pass to the handlers
|
|
140
|
+
* @returns Array of results from all matching handlers
|
|
141
|
+
* @throws {Error} When no resolver targets are registered
|
|
142
|
+
*/
|
|
143
|
+
handleAll(type: TType, ...args: Parameters<TBase['handle']>): ReturnType<TBase['handle']>[];
|
|
144
|
+
/**
|
|
145
|
+
* Executes all matching async handlers in parallel for the specified type
|
|
146
|
+
* @param type Type to resolve
|
|
147
|
+
* @param args Arguments to pass to the handlers
|
|
148
|
+
* @returns Promise that resolves to array of results from all matching handlers
|
|
149
|
+
* @throws {Error} When no resolver targets are registered
|
|
150
|
+
*/
|
|
151
|
+
handleAllAsync(type: TType, ...args: Parameters<TBase['handle']>): Promise<Awaited<ReturnType<TBase['handle']>>[]>;
|
|
152
|
+
/**
|
|
153
|
+
* Executes all matching async handlers sequentially for the specified type
|
|
154
|
+
* Stops on first error
|
|
155
|
+
* @param type Type to resolve
|
|
156
|
+
* @param args Arguments to pass to the handlers
|
|
157
|
+
* @returns Promise that resolves to array of results from all matching handlers
|
|
158
|
+
* @throws {Error} When no resolver targets are registered
|
|
159
|
+
* @throws {Error} When any handler throws an error
|
|
160
|
+
*/
|
|
161
|
+
handleAllSequential(type: TType, ...args: Parameters<TBase['handle']>): Promise<Awaited<ReturnType<TBase['handle']>>[]>;
|
|
162
|
+
}
|
|
163
|
+
export default Resolver;
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Interface that classes which are targets for the resolver should implement
|
|
167
|
+
*/
|
|
168
|
+
export declare interface ResolveTarget<TArgs extends any[] = any[], TReturn = any, TType = string> extends SupportsType<TType> {
|
|
169
|
+
/**
|
|
170
|
+
* Handles the request
|
|
171
|
+
* @param args Arguments needed for processing
|
|
172
|
+
* @returns Processing result
|
|
173
|
+
*/
|
|
174
|
+
handle(...args: TArgs): TReturn;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Base interface for type matching
|
|
179
|
+
* Extracted to reduce code duplication across sync and async targets
|
|
180
|
+
*/
|
|
181
|
+
export declare interface SupportsType<TType = string> {
|
|
182
|
+
/**
|
|
183
|
+
* Determines whether the specified type is supported
|
|
184
|
+
* @param type The type to check for support
|
|
185
|
+
* @returns true if supported, false otherwise
|
|
186
|
+
*/
|
|
187
|
+
supports(type: TType): boolean;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export { }
|