feat(dispatcher): dependency injection (#55)
This commit is contained in:
commit
cb0dbb712a
3 changed files with 86 additions and 5 deletions
|
@ -90,6 +90,10 @@ export interface DispatcherParams {
|
||||||
key?: StateKeyDelegate
|
key?: StateKeyDelegate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface DispatcherDependencies {
|
||||||
|
// intentionally empty, to be extended by consumers
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates dispatcher
|
* Updates dispatcher
|
||||||
*/
|
*/
|
||||||
|
@ -112,6 +116,8 @@ export class Dispatcher<State extends object = never> {
|
||||||
private _customStateKeyDelegate?: StateKeyDelegate
|
private _customStateKeyDelegate?: StateKeyDelegate
|
||||||
private _customStorage?: StateService
|
private _customStorage?: StateService
|
||||||
|
|
||||||
|
private _deps: DispatcherDependencies = {}
|
||||||
|
|
||||||
private _errorHandler?: <T = {}>(
|
private _errorHandler?: <T = {}>(
|
||||||
err: Error,
|
err: Error,
|
||||||
update: ParsedUpdate & T,
|
update: ParsedUpdate & T,
|
||||||
|
@ -193,6 +199,42 @@ export class Dispatcher<State extends object = never> {
|
||||||
return this._scene
|
return this._scene
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inject a dependency to be available in this dispatcher and all its children.
|
||||||
|
*
|
||||||
|
* **Note**: This is only available for the root dispatcher.
|
||||||
|
*/
|
||||||
|
inject<Name extends keyof DispatcherDependencies>(name: Name, value: DispatcherDependencies[Name]): void
|
||||||
|
/**
|
||||||
|
* Inject dependencies to be available in this dispatcher and all its children.
|
||||||
|
*
|
||||||
|
* **Note**: This is only available for the root dispatcher.
|
||||||
|
*/
|
||||||
|
inject(deps: Partial<DispatcherDependencies>): void
|
||||||
|
inject<Name extends keyof DispatcherDependencies>(
|
||||||
|
name: Name | Partial<DispatcherDependencies>,
|
||||||
|
value?: DispatcherDependencies[Name],
|
||||||
|
): void {
|
||||||
|
if (this._parent) {
|
||||||
|
throw new MtArgumentError('Cannot inject dependencies to child dispatchers')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof name === 'object') {
|
||||||
|
for (const [k, v] of Object.entries(name)) {
|
||||||
|
(this._deps as any)[k] = v
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this._deps[name] = value!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the dependencies injected into this dispatcher.
|
||||||
|
*/
|
||||||
|
get deps(): DispatcherDependencies {
|
||||||
|
return this._deps
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bind the dispatcher to the client.
|
* Bind the dispatcher to the client.
|
||||||
* Called by the constructor automatically if
|
* Called by the constructor automatically if
|
||||||
|
@ -678,6 +720,7 @@ export class Dispatcher<State extends object = never> {
|
||||||
child._parent = this as any
|
child._parent = this as any
|
||||||
child._client = this._client
|
child._client = this._client
|
||||||
child._storage = this._storage
|
child._storage = this._storage
|
||||||
|
child._deps = this._deps
|
||||||
child._stateKeyDelegate = this._stateKeyDelegate
|
child._stateKeyDelegate = this._stateKeyDelegate
|
||||||
child._customStorage ??= this._customStorage
|
child._customStorage ??= this._customStorage
|
||||||
child._customStateKeyDelegate ??= this._customStateKeyDelegate
|
child._customStateKeyDelegate ??= this._customStateKeyDelegate
|
||||||
|
@ -773,8 +816,9 @@ export class Dispatcher<State extends object = never> {
|
||||||
|
|
||||||
private _unparent(): void {
|
private _unparent(): void {
|
||||||
this._parent = this._client = undefined
|
this._parent = this._client = undefined
|
||||||
;(this as any)._stateKeyDelegate = undefined
|
this._deps = {} // to avoid dangling references
|
||||||
;(this as any)._storage = undefined
|
this._stateKeyDelegate = undefined
|
||||||
|
this._storage = undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
|
|
||||||
import { PeersIndex } from '@mtcute/core'
|
import { Message, PeersIndex } from '@mtcute/core'
|
||||||
import { TelegramClient } from '@mtcute/core/client.js'
|
import { TelegramClient } from '@mtcute/core/client.js'
|
||||||
import { StubTelegramClient } from '@mtcute/test'
|
import { createStub, StubTelegramClient } from '@mtcute/test'
|
||||||
|
|
||||||
import { Dispatcher, PropagationAction } from '../src/index.js'
|
import { Dispatcher, PropagationAction } from '../src/index.js'
|
||||||
|
|
||||||
|
@ -237,4 +237,41 @@ describe('Dispatcher', () => {
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('Dependency injection', () => {
|
||||||
|
it('should inject dependencies into update contexts', async () => {
|
||||||
|
const dp = Dispatcher.for(client)
|
||||||
|
|
||||||
|
dp.inject('foo' as never, 'foo' as never)
|
||||||
|
|
||||||
|
const log: string[] = []
|
||||||
|
|
||||||
|
dp.onNewMessage(() => {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
log.push(`received ${(dp.deps as any).foo}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
const dp2 = Dispatcher.child()
|
||||||
|
|
||||||
|
dp2.onNewMessage(() => {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
log.push(`received ${(dp.deps as any).foo} (child)`)
|
||||||
|
})
|
||||||
|
|
||||||
|
dp.addChild(dp2)
|
||||||
|
|
||||||
|
await dp.dispatchUpdateNow({
|
||||||
|
name: 'new_message',
|
||||||
|
data: new Message(
|
||||||
|
createStub('message'),
|
||||||
|
emptyPeers,
|
||||||
|
),
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(log).eql([
|
||||||
|
'received foo',
|
||||||
|
'received foo (child)',
|
||||||
|
])
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
"noImplicitAny": true,
|
"noImplicitAny": true,
|
||||||
"noImplicitThis": true,
|
"noImplicitThis": true,
|
||||||
"incremental": true,
|
"incremental": true,
|
||||||
"stripInternal": true,
|
"stripInternal": false,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"composite": true,
|
"composite": true,
|
||||||
"types": [
|
"types": [
|
||||||
|
|
Loading…
Reference in a new issue