@sphereon/ssi-types
Version:
SSI Common Types
211 lines (182 loc) • 5.97 kB
text/typescript
import { EventEmitter } from 'events'
import { Loggers, LogLevel, LogMethod } from '../logging'
export enum System {
GENERAL = 'general',
KMS = 'kms',
IDENTITY = 'identity',
OID4VCI = 'oid4vci',
OID4VP = 'oid4vp',
SIOPv2 = 'siopv2',
PE = 'PE',
CREDENTIALS = 'credentials',
WEB3 = 'web3',
PROFILE = 'profile',
CONTACT = 'contact',
}
export enum SubSystem {
KEY = 'key',
DID_PROVIDER = 'did_provider',
DID_RESOLVER = 'did_resolver',
OID4VP_OP = 'oid4vp_op',
OID4VCI_CLIENT = 'oid4vci_client',
SIOPv2_OP = 'siopv2_op',
CONTACT_MANAGER = 'contact_manager',
VC_ISSUER = 'vc_issuer',
VC_VERIFIER = 'vc_verifier',
VC_PERSISTENCE = 'vc_persistence',
TRANSPORT = 'transport',
PROFILE = 'profile',
API = 'api',
}
export enum ActionType {
CREATE = 'create',
READ = 'read',
UPDATE = 'update',
DELETE = 'delete',
EXECUTE = 'execute',
}
export enum DefaultActionSubType {
KEY_GENERATION = 'Key generation',
KEY_IMPORT = 'Key import',
KEY_PERSISTENCE = 'Key persistence',
KEY_REMOVAL = 'Key removal',
DID_CREATION = 'DID creation',
DID_RESOLUTION = 'DID resolution',
DID_SERVICE_UPDATE = 'DID service update',
VC_ISSUE = 'VC issue',
VC_VERIFY = 'VC verify',
VC_SHARE = 'VC share',
VC_DELETE = 'VC delete',
VC_ISSUE_DECLINE = 'VC issue decline',
VC_SHARE_DECLINE = 'VC share decline',
}
export type ActionSubType = DefaultActionSubType | string
export enum InitiatorType {
USER = 'user',
SYSTEM = 'system',
EXTERNAL = 'external',
}
export enum SystemCorrelationIdType {
DID = 'did',
URL = 'url',
EMAIL = 'email',
HOSTNAME = 'hostname',
PHONE = 'phone',
USER = 'user',
}
export type EventData = {
system: string
subSystemType: string
}
export interface BasicEvent<SourceType, PayloadType extends EventData> {
id: string
correlationId?: string
eventName: string
initiator?: string
initiatorType: InitiatorType
system: System
subsystem: SubSystem
data: PayloadType
}
type EmitterInstance = {
enabled: boolean
emitter: EventEmitter
}
export class EventManager {
private static readonly INSTANCE = new EventManager()
private _emitters = new Map<string, EmitterInstance>()
private constructor() {}
public static instance(): EventManager {
return EventManager.INSTANCE
}
register(name: string, emitter: EventEmitter, opts?: { disabled: boolean }): EventEmitter {
this._emitters.set(name, { emitter, enabled: opts?.disabled !== true })
return emitter
}
get(name: string, opts?: { onlyEnabled?: boolean }): EventEmitter {
const { emitter, enabled } = this._emitters.get(name) ?? {}
if (!emitter) {
throw Error(`No emitter registered with name ${name}`)
} else if (opts?.onlyEnabled && !enabled) {
throw Error(`Emitter with name ${name} is not enabled`)
}
return emitter
}
getOrCreate(name: string, opts?: { onlyEnabled?: boolean }): EventEmitter {
if (this.has(name)) {
return this.get(name, opts)
}
return this.register(name, new BasicEventEmitter())
}
has(name: string): boolean {
return this._emitters.has(name)
}
emitters(filter?: { eventName?: string | symbol; onlyEnabled?: boolean }): Array<EventEmitter> {
const all = Array.from(new Set(this._emitters.values()))
return this.emittersImpl(all, filter).map((e) => e.emitter)
}
hasEventName(eventName: string | symbol) {
return this.eventNames().includes(eventName)
}
eventNames(): Array<string | symbol> {
return Array.from(new Set(this.emitters().flatMap((emitter) => emitter.eventNames())))
}
emitBasic(event: BasicEvent<any, any>, ...args: any[]) {
return this.emit(event.eventName, event, args)
}
emit(eventName: string | symbol, event: Omit<BasicEvent<any, any>, 'eventName'> | any, ...args: any[]): void {
if ('id' in event && 'system' in event && !event.eventName) {
event.eventName = eventName
}
Loggers.DEFAULT.options('sphereon:events', {
methods: [LogMethod.CONSOLE],
defaultLogLevel: LogLevel.INFO,
})
.get('sphereon:events')
.log(`Emitting '${eventName.toString()}' event`, event)
const emitters = this.emitters({ eventName })
emitters.flatMap((emitter) => emitter.emit(eventName, event, args))
}
listenerCount(eventName: string | symbol) {
const emitters = this.emitters({ eventName })
return emitters.map((emitter) => emitter.listenerCount(eventName)).reduce((previous, current) => current + previous)
}
listeners(filter: { emitterName?: string; eventName: string; onlyEnabled?: boolean }): Array<Function> {
const emitters = filter?.emitterName ? [this.get(filter.emitterName, filter)] : this.emitters(filter)
return Array.from(
new Set(
this.emittersImpl(
emitters.map((emitter) => {
return { emitter, enabled: true }
}),
filter,
).flatMap((emitter) => emitter.emitter.listeners(filter.eventName)),
),
)
}
private emittersImpl(
all: { emitter: EventEmitter; enabled: boolean }[],
filter?: {
eventName?: string | symbol
onlyEnabled?: boolean
},
): Array<EmitterInstance> {
const { eventName } = filter ?? {}
if (!eventName) {
return all
}
const filtered = all.filter((emitter) => emitter.emitter.eventNames().includes(eventName) && (emitter.enabled || filter?.onlyEnabled !== true))
return Array.from(new Set(filtered))
}
}
export class BasicEventEmitter extends EventEmitter {
addListener(eventName: string | symbol, listener: (event: BasicEvent<any, any>, ...args: any[]) => void): this {
return super.addListener(eventName, listener)
}
once(eventName: string | symbol, listener: (event: BasicEvent<any, any>, ...args: any[]) => void): this {
return super.once(eventName, listener)
}
emit(eventName: string, event: BasicEvent<any, any>, ...args: any[]): boolean {
return super.emit(eventName, ...args)
}
}