Add StateEvent implementation
This commit is contained in:
parent
d00e6a02e2
commit
fc85c9d2c8
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@extollo/lib",
|
"name": "@extollo/lib",
|
||||||
"version": "0.11.0",
|
"version": "0.12.0",
|
||||||
"description": "The framework library that lifts up your code.",
|
"description": "The framework library that lifts up your code.",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"types": "lib/index.d.ts",
|
"types": "lib/index.d.ts",
|
||||||
|
122
src/support/bus/StateEvent.ts
Normal file
122
src/support/bus/StateEvent.ts
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
import {Container, Inject, Injectable, Instantiable} from '../../di'
|
||||||
|
import {Awaitable, ErrorWithContext, JSONState, uuid4} from '../../util'
|
||||||
|
import {BaseSerializer} from './serial/BaseSerializer'
|
||||||
|
import {Event} from './types'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An event base class that provides its own validator and serializer implementation.
|
||||||
|
* This is meant to streamline event creation for app-space by forcing the payload of
|
||||||
|
* the event to be a JSONState.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export abstract class StateEvent<T extends JSONState> implements Event {
|
||||||
|
@Inject()
|
||||||
|
protected readonly injector!: Container
|
||||||
|
|
||||||
|
/** The payload of the event. */
|
||||||
|
private state?: T
|
||||||
|
|
||||||
|
eventUuid = uuid4()
|
||||||
|
|
||||||
|
public get eventName(): string {
|
||||||
|
return this.getName()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Validate the incoming data. */
|
||||||
|
public abstract validate(state: JSONState): state is T
|
||||||
|
|
||||||
|
/** Get the event's name for the bus. */
|
||||||
|
public getName(): string {
|
||||||
|
const ctor = this.constructor as typeof StateEvent
|
||||||
|
return `state:${ctor.name}`
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get the Serializer implementation for this event. */
|
||||||
|
public getSerializer(): StateEventSerializer {
|
||||||
|
const ctor = this.constructor as typeof StateEvent
|
||||||
|
return this.injector.makeNew<StateEventSerializer>(StateEventSerializer, ctor)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Validate & set the internal state of this event instance. */
|
||||||
|
public setState(state: JSONState): this {
|
||||||
|
if ( !this.validate(state) ) {
|
||||||
|
throw new ErrorWithContext('Invalid state', {
|
||||||
|
state,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.state = state
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get the internal state of this event, validated. */
|
||||||
|
public getState(): T {
|
||||||
|
if ( typeof this.state === 'undefined' ) {
|
||||||
|
throw new ErrorWithContext('State has not been set on event')
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic serializer implementation for StateEvents.
|
||||||
|
* Note that this is NOT registered with @ObjectSerializer() because we need to
|
||||||
|
* create an instance of this class for EACH implementation of StateEvent.
|
||||||
|
* Still working that one out. For now, you'll need to manually register the
|
||||||
|
* serializer. You can do this with the `getSerializer` method.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* type MyState = {
|
||||||
|
* id: number
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* class MyStateEvent extends StateEvent<MyState> {
|
||||||
|
* public validate(state: JSONState): state is MyState {
|
||||||
|
* return is<MyState>(state)
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class StateEventSerializer extends BaseSerializer<StateEvent<JSONState>, JSONState> {
|
||||||
|
@Inject()
|
||||||
|
protected readonly injector!: Container
|
||||||
|
|
||||||
|
/** The StateEvent implementation. */
|
||||||
|
public readonly eventClass: Instantiable<StateEvent<JSONState>>;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
eventClass: Instantiable<StateEvent<JSONState>>,
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
this.eventClass = eventClass
|
||||||
|
}
|
||||||
|
|
||||||
|
matchActual(some: StateEvent<JSONState>): boolean {
|
||||||
|
return some instanceof this.eventClass
|
||||||
|
}
|
||||||
|
|
||||||
|
protected encodeActual(actual: StateEvent<JSONState>): Awaitable<JSONState> {
|
||||||
|
return actual.getState()
|
||||||
|
}
|
||||||
|
|
||||||
|
protected decodeSerial(serial: JSONState): Awaitable<StateEvent<JSONState>> {
|
||||||
|
const inst = this.injector.makeNew<StateEvent<JSONState>>(this.eventClass)
|
||||||
|
if ( !inst.validate(serial) ) {
|
||||||
|
throw new ErrorWithContext('Invalid serial state', {
|
||||||
|
serial,
|
||||||
|
eventClass: inst.getName(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
inst.setState(serial)
|
||||||
|
return inst
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getName(): string {
|
||||||
|
const inst = this.injector.makeNew<StateEvent<JSONState>>(this.eventClass)
|
||||||
|
return `${inst.getName()}Serializer`
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import {AwareOfContainerLifecycle, Container, Inject, Injectable, StaticInstantiable} from '../../di'
|
import {AwareOfContainerLifecycle, Inject, Injectable, StaticInstantiable} from '../../di'
|
||||||
import {
|
import {
|
||||||
EventBus,
|
EventBus,
|
||||||
Event,
|
Event,
|
||||||
|
@ -64,10 +64,21 @@ export class Serialization {
|
|||||||
protected serializers: Collection<RegisteredSerializer<Serializer<unknown, JSONState>>> = new Collection()
|
protected serializers: Collection<RegisteredSerializer<Serializer<unknown, JSONState>>> = new Collection()
|
||||||
|
|
||||||
/** Register a new serializer with the service. */
|
/** Register a new serializer with the service. */
|
||||||
public register(serializer: Instantiable<Serializer<unknown, JSONState>>): this {
|
public register(key: Instantiable<Serializer<unknown, JSONState>>): this {
|
||||||
// Prepend instead of push so that later-registered serializers are prioritized when matching
|
// Prepend instead of push so that later-registered serializers are prioritized when matching
|
||||||
this.serializers.prepend({
|
this.serializers.prepend({
|
||||||
key: serializer,
|
key,
|
||||||
|
})
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Register an already-realized serializer instance with this service. */
|
||||||
|
public registerInstance(key: Instantiable<Serializer<unknown, JSONState>>, instance: Serializer<unknown, JSONState>): this {
|
||||||
|
// Prepend instead of push so that later-registered serializers are prioritized when matching
|
||||||
|
this.serializers.prepend({
|
||||||
|
key,
|
||||||
|
instance,
|
||||||
})
|
})
|
||||||
|
|
||||||
return this
|
return this
|
||||||
|
Loading…
Reference in New Issue
Block a user