@bett3r-dev/pv3-types 1.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/build/authentication.d.ts +261 -0
- package/build/authentication.d.ts.map +1 -0
- package/build/authentication.js +108 -0
- package/build/authentication.js.map +1 -0
- package/build/cache.d.ts +101 -0
- package/build/cache.d.ts.map +1 -0
- package/build/cache.js +3 -0
- package/build/cache.js.map +1 -0
- package/build/clusterAdministration.d.ts +642 -0
- package/build/clusterAdministration.d.ts.map +1 -0
- package/build/clusterAdministration.js +177 -0
- package/build/clusterAdministration.js.map +1 -0
- package/build/configuration.d.ts +66 -0
- package/build/configuration.d.ts.map +1 -0
- package/build/configuration.js +3 -0
- package/build/configuration.js.map +1 -0
- package/build/database.d.ts +120 -0
- package/build/database.d.ts.map +1 -0
- package/build/database.js +41 -0
- package/build/database.js.map +1 -0
- package/build/endpoints.d.ts +183 -0
- package/build/endpoints.d.ts.map +1 -0
- package/build/endpoints.js +78 -0
- package/build/endpoints.js.map +1 -0
- package/build/evenstore.d.ts +26 -0
- package/build/evenstore.d.ts.map +1 -0
- package/build/evenstore.js +12 -0
- package/build/evenstore.js.map +1 -0
- package/build/eventsourcing/aggregateBuilder.d.ts +10 -0
- package/build/eventsourcing/aggregateBuilder.d.ts.map +1 -0
- package/build/eventsourcing/aggregateBuilder.js +3 -0
- package/build/eventsourcing/aggregateBuilder.js.map +1 -0
- package/build/eventsourcing/commandBuilder.d.ts +58 -0
- package/build/eventsourcing/commandBuilder.d.ts.map +1 -0
- package/build/eventsourcing/commandBuilder.js +6 -0
- package/build/eventsourcing/commandBuilder.js.map +1 -0
- package/build/eventsourcing/commandHandlers.d.ts +104 -0
- package/build/eventsourcing/commandHandlers.d.ts.map +1 -0
- package/build/eventsourcing/commandHandlers.js +28 -0
- package/build/eventsourcing/commandHandlers.js.map +1 -0
- package/build/eventsourcing/eventHandlers.d.ts +102 -0
- package/build/eventsourcing/eventHandlers.d.ts.map +1 -0
- package/build/eventsourcing/eventHandlers.js +33 -0
- package/build/eventsourcing/eventHandlers.js.map +1 -0
- package/build/eventsourcing/eventHandlersBuilder.d.ts +36 -0
- package/build/eventsourcing/eventHandlersBuilder.d.ts.map +1 -0
- package/build/eventsourcing/eventHandlersBuilder.js +13 -0
- package/build/eventsourcing/eventHandlersBuilder.js.map +1 -0
- package/build/eventsourcing/externalSystemBuilder.d.ts +14 -0
- package/build/eventsourcing/externalSystemBuilder.d.ts.map +1 -0
- package/build/eventsourcing/externalSystemBuilder.js +3 -0
- package/build/eventsourcing/externalSystemBuilder.js.map +1 -0
- package/build/eventsourcing/index.d.ts +386 -0
- package/build/eventsourcing/index.d.ts.map +1 -0
- package/build/eventsourcing/index.js +47 -0
- package/build/eventsourcing/index.js.map +1 -0
- package/build/eventsourcing/invariants.d.ts +19 -0
- package/build/eventsourcing/invariants.d.ts.map +1 -0
- package/build/eventsourcing/invariants.js +3 -0
- package/build/eventsourcing/invariants.js.map +1 -0
- package/build/eventsourcing/utils.d.ts +4 -0
- package/build/eventsourcing/utils.d.ts.map +1 -0
- package/build/eventsourcing/utils.js +3 -0
- package/build/eventsourcing/utils.js.map +1 -0
- package/build/index.d.ts +16 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +32 -0
- package/build/index.js.map +1 -0
- package/build/infrastructure.d.ts +40 -0
- package/build/infrastructure.d.ts.map +1 -0
- package/build/infrastructure.js +11 -0
- package/build/infrastructure.js.map +1 -0
- package/build/logger.d.ts +32 -0
- package/build/logger.d.ts.map +1 -0
- package/build/logger.js +29 -0
- package/build/logger.js.map +1 -0
- package/build/outboxManager.d.ts +38 -0
- package/build/outboxManager.d.ts.map +1 -0
- package/build/outboxManager.js +21 -0
- package/build/outboxManager.js.map +1 -0
- package/build/ports.d.ts +39 -0
- package/build/ports.d.ts.map +1 -0
- package/build/ports.js +3 -0
- package/build/ports.js.map +1 -0
- package/build/routesAuth.d.ts +58 -0
- package/build/routesAuth.d.ts.map +1 -0
- package/build/routesAuth.js +37 -0
- package/build/routesAuth.js.map +1 -0
- package/build/typedEventEmitter.d.ts +23 -0
- package/build/typedEventEmitter.d.ts.map +1 -0
- package/build/typedEventEmitter.js +55 -0
- package/build/typedEventEmitter.js.map +1 -0
- package/build/validation.d.ts +64 -0
- package/build/validation.d.ts.map +1 -0
- package/build/validation.js +57 -0
- package/build/validation.js.map +1 -0
- package/package.json +15 -0
- package/src/authentication.ts +273 -0
- package/src/cache.ts +109 -0
- package/src/clusterAdministration.ts +216 -0
- package/src/configuration.ts +71 -0
- package/src/database.ts +157 -0
- package/src/endpoints.ts +198 -0
- package/src/evenstore.ts +27 -0
- package/src/eventsourcing/aggregateBuilder.ts +11 -0
- package/src/eventsourcing/commandBuilder.ts +64 -0
- package/src/eventsourcing/commandHandlers.ts +137 -0
- package/src/eventsourcing/eventHandlers.ts +118 -0
- package/src/eventsourcing/eventHandlersBuilder.ts +42 -0
- package/src/eventsourcing/externalSystemBuilder.ts +20 -0
- package/src/eventsourcing/index.ts +111 -0
- package/src/eventsourcing/invariants.ts +27 -0
- package/src/eventsourcing/utils.ts +4 -0
- package/src/index.ts +15 -0
- package/src/infrastructure.ts +41 -0
- package/src/logger.ts +45 -0
- package/src/outboxManager.ts +36 -0
- package/src/ports.ts +42 -0
- package/src/routesAuth.ts +58 -0
- package/src/typedEventEmitter.ts +73 -0
- package/src/validation.ts +89 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import S, { ObjectSchema } from '@bett3r-dev/jsonschema-definer';
|
|
2
|
+
import { AppErrorInstance } from '../validation';
|
|
3
|
+
import { Collection, CollectionQueryParams, DatabaseChange, DatabasePort, Document } from '../database';
|
|
4
|
+
import { EndpointActionParams, RequestObject, ResponseObject } from '../endpoints';
|
|
5
|
+
import { CommittedEvent, EventFactory, GetMessageType } from '../eventsourcing';
|
|
6
|
+
import { LoggerType } from '../logger';
|
|
7
|
+
import { System } from './commandHandlers';
|
|
8
|
+
import { TypedEventEmitter } from '../typedEventEmitter';
|
|
9
|
+
|
|
10
|
+
const SortStringSchema = S.string().pattern( /^(\w+),?(asc|desc)?$/ );
|
|
11
|
+
const FilterStringSchema = S
|
|
12
|
+
.string()
|
|
13
|
+
.pattern( /^([\w\d._]+),(eq|neq|lt|gt|lte|gte|in|nin|contains|notContains|some|every|exists|notExists),(.+?)$/ );
|
|
14
|
+
|
|
15
|
+
export const ReadModelDefaultParamsSchema = S.shape({
|
|
16
|
+
id: S.string().optional()
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export const ReadModelDefaultQuerySchema = S.shape({
|
|
20
|
+
access_token: S.string().optional(),
|
|
21
|
+
apikey: S.string().optional(),
|
|
22
|
+
limit: S.string().optional(),
|
|
23
|
+
after: S.string().optional(),
|
|
24
|
+
sort: S.oneOf( SortStringSchema, S.array().items( SortStringSchema )).optional(),
|
|
25
|
+
select: S.array().items( S.string()).optional(),
|
|
26
|
+
//TODO: scape , for when the value has commas.
|
|
27
|
+
filter: S
|
|
28
|
+
.oneOf( FilterStringSchema, S.array().items( FilterStringSchema ))
|
|
29
|
+
.optional()
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export type ReadModelDefaultQuery = typeof ReadModelDefaultQuerySchema.type
|
|
33
|
+
|
|
34
|
+
export type ReadModelDefaultParams = typeof ReadModelDefaultParamsSchema.type;
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
export const defaultParamsSchemas = {
|
|
38
|
+
params: ReadModelDefaultParamsSchema
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const defaultQuerySchemas = {
|
|
42
|
+
query: ReadModelDefaultQuerySchema
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export type EventHandler<EVENTS extends Record<string, EventFactory>=any> = {
|
|
46
|
+
name: string;
|
|
47
|
+
events: EVENTS;
|
|
48
|
+
concurrency?: number;
|
|
49
|
+
errorHandlers?: {
|
|
50
|
+
[Name in keyof EVENTS]?: HandlerExecutionErrorType<CommittedEvent<GetMessageType<EVENTS[Name]>>>
|
|
51
|
+
},
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export type PolicyResponse = {name: string, id?: string, data?: any};
|
|
55
|
+
export type HandlerExecutionErrorType<EVENT extends CommittedEvent = CommittedEvent> = ( error: AppErrorInstance, event: EVENT ) => Promise<unknown>;
|
|
56
|
+
|
|
57
|
+
//*******************************************
|
|
58
|
+
// Policies
|
|
59
|
+
//*******************************************
|
|
60
|
+
|
|
61
|
+
export type Policy<CommandHandler extends System=any, Events extends Record<string, EventFactory>=any> = EventHandler<Events> & {
|
|
62
|
+
commandHandler?: CommandHandler,
|
|
63
|
+
eventHandlers: {
|
|
64
|
+
[Name in keyof Events]: ( event: CommittedEvent<GetMessageType<Events[Name]>> ) => Promise<PolicyResponse | void>
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
//*******************************************
|
|
69
|
+
// Readmodels
|
|
70
|
+
//*******************************************
|
|
71
|
+
|
|
72
|
+
export type QueryParamsTranslator<Model extends Document, Params, Query> = ( schema: ObjectSchema<Model>, params: Pick<EndpointActionParams<any, Query, Params>, 'params' | 'query'> ) => CollectionQueryParams<Model>;
|
|
73
|
+
|
|
74
|
+
export type QueryHandler<MODEL extends Document=any,PARAMS=any,QUERY=any> = ( repository: Collection<MODEL>, params: CollectionQueryParams<MODEL> & {params: EndpointActionParams<any, QUERY, PARAMS>}) => Promise<MODEL | MODEL[]>;
|
|
75
|
+
|
|
76
|
+
export type QueryTransformer<MODEL extends Document=any,PARAMS=any,QUERY=any> = ( projectionParams: CollectionQueryParams<MODEL>, params: EndpointActionParams<any, QUERY, PARAMS> ) => Promise<CollectionQueryParams<MODEL>> | CollectionQueryParams<MODEL>;
|
|
77
|
+
|
|
78
|
+
export type ReadModelSubscriptionCloseEmitterEvents = {close: string};
|
|
79
|
+
|
|
80
|
+
export type ReadmodelSubscriptionManagerType<Model extends Document = any> = {
|
|
81
|
+
type: string,
|
|
82
|
+
startSubscription: ( projectionParams: CollectionQueryParams<Model>, req: RequestObject, res: ResponseObject ) => string | void;
|
|
83
|
+
notifySubscriptions: ( logger: LoggerType, collection: string ) => <T extends Document>( change: DatabaseChange<T>, subscriptionId?: string ) => void;
|
|
84
|
+
readModelSubscriptionCloseEmitter: TypedEventEmitter<ReadModelSubscriptionCloseEmitterEvents>
|
|
85
|
+
send: ( logger:LoggerType, id: string, change:DatabaseChange,collection: string, res: ResponseObject, data: string ) => void;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export type Query<MODEL extends Document=any, PARAMS extends Record<string, any>=any, QUERY extends Record<string, any>=any> = {
|
|
89
|
+
route?: string;
|
|
90
|
+
protocol?: 'http',
|
|
91
|
+
description?: string;
|
|
92
|
+
schemas?: {
|
|
93
|
+
query?: ObjectSchema<QUERY>
|
|
94
|
+
params?: ObjectSchema<PARAMS>
|
|
95
|
+
};
|
|
96
|
+
translator?: QueryParamsTranslator<MODEL, PARAMS, QUERY>;
|
|
97
|
+
subscriptions?: ReadmodelSubscriptionManagerType[];
|
|
98
|
+
transformers?: QueryTransformer<MODEL,PARAMS, QUERY>[],
|
|
99
|
+
queryHandler?: QueryHandler<MODEL, PARAMS, QUERY>;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
export type EventProjectors<EVENTS extends Record<string, EventFactory>=any> = {
|
|
103
|
+
[Name in keyof EVENTS]: ( event: CommittedEvent<GetMessageType<EVENTS[Name]>> ) => Promise<any>
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
export type ReadModel<Model extends Document =any, EVENTS extends Record<string, EventFactory>=any> = EventHandler<EVENTS> & {
|
|
107
|
+
isReadModel: true;
|
|
108
|
+
schema: ObjectSchema<Model>;
|
|
109
|
+
queries: Query<Model>[];
|
|
110
|
+
eventProjectors?:EventProjectors<EVENTS>;
|
|
111
|
+
database?: DatabasePort;
|
|
112
|
+
collection?:string
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export type ReadmodelChange<Model extends Document> = {
|
|
116
|
+
type: 'insert' | 'update' | 'delete',
|
|
117
|
+
data: Model
|
|
118
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { ObjectSchema } from '@bett3r-dev/jsonschema-definer';
|
|
2
|
+
import { A, O } from 'ts-toolbelt';
|
|
3
|
+
import { Builder, CommittedEvent, EventFactory, EventHandler, EventMetadata, EventProjectors, GetBuilderType, GetMessageType, Message, Policy, PolicyResponse, Query, ReadModel, System } from '.';
|
|
4
|
+
import { AppErrorInstance, DatabasePort, Document } from '..';
|
|
5
|
+
|
|
6
|
+
export type GetCommandType<SYSTEM extends System | EventHandler, Name extends A.Key> = SYSTEM extends System
|
|
7
|
+
? O.Path<SYSTEM, ['commands', Name]> extends ( Builder<infer T extends Object> ) ? O.Path<T, ['schema', 'type']> : O.Path<SYSTEM, ['commands', Name, 'schema', 'type']>
|
|
8
|
+
: O.Path<SYSTEM, ['commandHandler','commands', Name]> extends ( Builder<infer T extends Object> ) ? O.Path<T, ['schema', 'type']> : O.Path<SYSTEM, ['commandHandler','commands', Name, 'schema', 'type']>
|
|
9
|
+
|
|
10
|
+
export type CommandInstance<Payload=any> = Message<Payload> & {
|
|
11
|
+
name: string;
|
|
12
|
+
id?: string;
|
|
13
|
+
metadata?: EventMetadata
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const createCommand = <Handler extends System | EventHandler> ( handler: Handler )=> <COMMAND extends keyof ( Handler extends System ? Handler['commands'] : O.Path<GetBuilderType<Handler>,['commandHandler','commands']> )>( command: COMMAND, id?: string, data?: GetCommandType<Handler, COMMAND>, metadata?: EventMetadata ): CommandInstance => {
|
|
17
|
+
return {
|
|
18
|
+
name: command as string,
|
|
19
|
+
...( id ? { id } : {}),
|
|
20
|
+
...( data ? { data } : {}),
|
|
21
|
+
...( metadata ? { metadata } : {})
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type EventHandlerBuilderType<EVENTS extends Record<string, EventFactory>, T extends Policy | ReadModel> = Builder<EventHandler<EVENTS>> & {
|
|
26
|
+
eventHandler: EventHandler<EVENTS>,
|
|
27
|
+
withErrorHandlers: ( errorHandlers: {
|
|
28
|
+
[Name in keyof EVENTS]?: ( error: AppErrorInstance, event: CommittedEvent<GetMessageType<EVENTS[Name]>> ) => Promise<unknown>
|
|
29
|
+
}) => { build: () => T extends Policy ? Policy<T['commandHandler'], EVENTS> : T extends ReadModel ? ReadModel<T['schema'] extends ObjectSchema<infer MODEL extends Document> ? MODEL: Document, EVENTS> : any},
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export type PolicyBuilderType<CommandHandler extends System, EVENTS extends Record<string, EventFactory>> = Builder<Policy<CommandHandler, EVENTS>> & EventHandlerBuilderType<EVENTS, Policy<CommandHandler, EVENTS>> & {
|
|
33
|
+
withEventHandlers: ( handlers: ( params: {createCommand: ReturnType<typeof createCommand<CommandHandler>>}) => {
|
|
34
|
+
[Name in keyof EVENTS]: ( event: CommittedEvent<GetMessageType<EVENTS[Name]>> ) => Promise<PolicyResponse|void>
|
|
35
|
+
}) => Pick<PolicyBuilderType<CommandHandler, EVENTS>, 'build' | 'withErrorHandlers'>,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type ReadmodelBuilderType<Model extends Document, EVENTS extends Record<string, EventFactory>> = Builder<ReadModel<Model, EVENTS>> & EventHandlerBuilderType<EVENTS, ReadModel<Model, EVENTS>> & {
|
|
39
|
+
withDatabase: ( database: DatabasePort, collection?:string ) => ReadmodelBuilderType<Model, EVENTS>
|
|
40
|
+
withProjector: ( eventProjectors?: ( params: {database: DatabasePort, collection:string}) => EventProjectors<EVENTS> ) => ReadmodelBuilderType<Model, EVENTS>
|
|
41
|
+
withQuery: <PARAMS extends Record<string, any>, QUERY extends Record<string, any>>( query?: Query<Model, PARAMS, QUERY> ) => ReadmodelBuilderType<Model, EVENTS>
|
|
42
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Builder, Command, CommandBuilderType, EventFactory, System } from '.';
|
|
2
|
+
import { CreateEventType } from './utils';
|
|
3
|
+
|
|
4
|
+
export type ExternalSystemBuilderType<Model, EVENTS extends Record<string, EventFactory>> = {
|
|
5
|
+
|
|
6
|
+
withCommandTemplate: ( command: Command ) => ExternalSystemBuilderType<Model, EVENTS>
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* This is the last function that builds the aggregate, the last function you'll call
|
|
11
|
+
*/
|
|
12
|
+
withCommands: <
|
|
13
|
+
EVENT extends keyof EVENTS,
|
|
14
|
+
Commands extends Record<string, Command<EVENTS, EVENT, Model> | Builder<Command<EVENTS, EVENT, Model>>>
|
|
15
|
+
> ( commands: ( params: {
|
|
16
|
+
commandBuilder: CommandBuilderType<Model, EVENTS>,
|
|
17
|
+
createEvent: CreateEventType<System< any, EVENTS >>,
|
|
18
|
+
}) => Commands ) => System<Commands, EVENTS>
|
|
19
|
+
system: System<any, EVENTS>
|
|
20
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import S, { BaseSchema, ObjectSchema } from '@bett3r-dev/jsonschema-definer';
|
|
2
|
+
import { CollectionFilter, Document } from '../database';
|
|
3
|
+
import { ResponseObject } from '../endpoints';
|
|
4
|
+
import { PortEmitter } from '../ports';
|
|
5
|
+
import { EventSourcingCommandHandlers, ExecuteCommandFunction, System } from './commandHandlers';
|
|
6
|
+
import { EventHandler } from './eventHandlers';
|
|
7
|
+
|
|
8
|
+
export * from './commandHandlers';
|
|
9
|
+
export * from './eventHandlers';
|
|
10
|
+
export * from './invariants';
|
|
11
|
+
export * from './aggregateBuilder';
|
|
12
|
+
export * from './commandBuilder';
|
|
13
|
+
export * from './eventHandlersBuilder';
|
|
14
|
+
export * from './externalSystemBuilder';
|
|
15
|
+
|
|
16
|
+
export type Message<Payload, Name = string> = {
|
|
17
|
+
name?: Name,
|
|
18
|
+
data?: Payload,
|
|
19
|
+
description?: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type MessageSchema<Payload> = {
|
|
23
|
+
schema?: BaseSchema<Payload> | BaseSchema<Payload, false> | ObjectSchema | BaseSchema | null
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type EventInstance<Payload=any, Name=any> = Message<Payload, Name> & MessageSchema<Payload>
|
|
27
|
+
|
|
28
|
+
export const Event = <Payload>( eventInstance: EventInstance<Payload> ): EventInstance<Payload> => eventInstance;
|
|
29
|
+
|
|
30
|
+
export type EventFactory<Name=any, EventPayload = any> = () => EventInstance<EventPayload, Name>
|
|
31
|
+
|
|
32
|
+
export const EventMetadataSchema = S.shape({
|
|
33
|
+
causationId: S.string().optional(),
|
|
34
|
+
correlationId: S.string().optional(),
|
|
35
|
+
user: S.any().optional()
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
export type EventMetadata = typeof EventMetadataSchema.type;
|
|
39
|
+
|
|
40
|
+
export type EventToCommit<Payload = any> = {
|
|
41
|
+
name: string,
|
|
42
|
+
data?: Payload,
|
|
43
|
+
stream: string
|
|
44
|
+
metadata: EventMetadata
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export type CommittedEvent<Payload = any> = EventToCommit<Payload> & {
|
|
48
|
+
id: number
|
|
49
|
+
timestamp: string
|
|
50
|
+
version: number
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const ensureArray = ( maybeArray: any ) => Array.isArray( maybeArray ) ? maybeArray : [maybeArray];
|
|
54
|
+
|
|
55
|
+
export const CommittedEventSchema = <T>( payloadSchema: BaseSchema<T>, names?: string | string[]) => S.shape({
|
|
56
|
+
id: S.number(),
|
|
57
|
+
name: ( names ? S.string().enum( ...ensureArray( names )) : S.string()),
|
|
58
|
+
timestamp: S.string().format( 'date-time' ),
|
|
59
|
+
data: payloadSchema,
|
|
60
|
+
version: S.number(),
|
|
61
|
+
stream: S.string(),
|
|
62
|
+
metadata: EventMetadataSchema,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
export type GetMessageType<Payload> = Payload extends (()=> EventInstance<infer T> ) ? T : unknown
|
|
66
|
+
|
|
67
|
+
export type Builder<T = any> = {
|
|
68
|
+
build:() => T
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export type GetBuilderType<T> = T extends ( Builder<infer InnerT> ) ? InnerT : T
|
|
72
|
+
|
|
73
|
+
//*******************************************
|
|
74
|
+
// Readmodels
|
|
75
|
+
//*******************************************
|
|
76
|
+
// export type GetEventType<SYSTEM extends System, Name extends A.Key> = GetMessageType<O.Path<SYSTEM, ['events', Name]>>
|
|
77
|
+
|
|
78
|
+
// export type GetErrorDataType<Payload> = Payload extends (()=> AppErrorType<infer T>) ? T : unknown
|
|
79
|
+
// export type CreateErrorType<ERRORS extends Record<string, AppErrorFactory>> = (errors: ERRORS)=> <ERROR extends keyof ERRORS>(error: ERROR, params: Pick<AppErrorInstance<GetErrorDataType<ERRORS[ERROR]>>, 'details' | 'description'> ) => AppErrorInstance<GetErrorDataType<ERRORS[ERROR]>>;
|
|
80
|
+
|
|
81
|
+
export type ReadModelSubscription<Model extends Document = any> = {
|
|
82
|
+
id: string;
|
|
83
|
+
res: ResponseObject;
|
|
84
|
+
filter: CollectionFilter<Model>[];
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
export type ReadStreamOptions = {
|
|
89
|
+
stream?: string,
|
|
90
|
+
direction?: 'forwards' | 'backwards',
|
|
91
|
+
from?: number,
|
|
92
|
+
fromVersion?: number,
|
|
93
|
+
limit?: number,
|
|
94
|
+
events?: string[],
|
|
95
|
+
before?: string,
|
|
96
|
+
after?: string,
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export type ReadMultipleStreamOptions = Omit<ReadStreamOptions, 'stream'> & {
|
|
100
|
+
streams: string[],
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export type EventSourcingType = Pick<EventSourcingCommandHandlers, 'executeCommand' | 'loadAggregateState' | 'loadStream'> & {
|
|
104
|
+
routeEventHandler: ( eventHandler: EventHandler | Builder<EventHandler> ) => void,
|
|
105
|
+
routeCommandHandler: ( commandHandler: System ) => void,
|
|
106
|
+
|
|
107
|
+
handleEvent: ( eventHandler: EventHandler, executeCommand: ExecuteCommandFunction ) => ( event: CommittedEvent ) => Promise<any>,
|
|
108
|
+
logEventsourcingRoutes: () => void
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export type EventSourcingPort = PortEmitter & EventSourcingType;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { DatabaseOps, ErrorTypes, LoggerType } from '..';
|
|
2
|
+
|
|
3
|
+
export type InvariantParams = {
|
|
4
|
+
errorName?: string;
|
|
5
|
+
errorType?: ErrorTypes
|
|
6
|
+
logger?: LoggerType;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export type ReadmodelInvariantComparisonOR = {
|
|
10
|
+
OR: ReadmodelInvariantComparison[]
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type ReadmodelInvariantComparisonAND = {
|
|
14
|
+
AND: ReadmodelInvariantComparison[]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type ReadmodelInvariantComparisonInstance = {
|
|
18
|
+
path: string;
|
|
19
|
+
readmodelPath: string;
|
|
20
|
+
op: DatabaseOps;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type ReadmodelInvariantComparison = Partial<
|
|
24
|
+
& ReadmodelInvariantComparisonOR
|
|
25
|
+
& ReadmodelInvariantComparisonAND
|
|
26
|
+
& ReadmodelInvariantComparisonInstance
|
|
27
|
+
>
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { CommittedEvent, EventFactory, EventHandler, GetMessageType, System } from '.';
|
|
2
|
+
import { O } from 'ts-toolbelt';
|
|
3
|
+
|
|
4
|
+
export type CreateEventType<Handler extends System | EventHandler> = <EVENT extends keyof O.Path<Handler, ['events']>>( event: EVENT, data?: GetMessageType<Handler['events'][EVENT]>, committedEvent?: Partial<Omit<CommittedEvent, 'data' | 'name'>> ) => EventFactory<EVENT, GetMessageType<Handler['events'][EVENT]>>;
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export * from './authentication';
|
|
2
|
+
export * from './cache';
|
|
3
|
+
export * from './clusterAdministration';
|
|
4
|
+
export * from './configuration';
|
|
5
|
+
export * from './database';
|
|
6
|
+
export * from './endpoints';
|
|
7
|
+
export * from './evenstore';
|
|
8
|
+
export * from './eventsourcing';
|
|
9
|
+
export * from './infrastructure';
|
|
10
|
+
export * from './logger';
|
|
11
|
+
export * from './outboxManager';
|
|
12
|
+
export * from './ports';
|
|
13
|
+
export * from './routesAuth';
|
|
14
|
+
export * from './typedEventEmitter';
|
|
15
|
+
export * from './validation';
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import S from '@bett3r-dev/jsonschema-definer';
|
|
2
|
+
import { OpenAPIV3_1 } from 'openapi-types';
|
|
3
|
+
import { StructureServiceType } from './clusterAdministration';
|
|
4
|
+
import { AdapterBase } from './ports';
|
|
5
|
+
|
|
6
|
+
export const InfrastructureConfigSchema = S.shape({
|
|
7
|
+
infrastructureLeaderRandomBackoff: S.number().default( 1000 ).optional()
|
|
8
|
+
});
|
|
9
|
+
export type InfrastructureConfigType = typeof InfrastructureConfigSchema.type;
|
|
10
|
+
|
|
11
|
+
export type EnvironmentServiceType = {
|
|
12
|
+
openApi: OpenAPIV3_1.Document,
|
|
13
|
+
service: string,
|
|
14
|
+
openApiEndpoint?: string,
|
|
15
|
+
instances: {host: string, timeRunning: number}[],
|
|
16
|
+
port: number,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type InfrastructureServiceType = Omit<StructureServiceType, 'hosts'> & {
|
|
20
|
+
instances: {host: string, timeRunning: number}[],
|
|
21
|
+
service: string,
|
|
22
|
+
}
|
|
23
|
+
export type InfrastructureThisServiceType = InfrastructureServiceType & {
|
|
24
|
+
leadershipRank: number;
|
|
25
|
+
host: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type InfrastructureAdapterType = AdapterBase & {
|
|
29
|
+
getServices: () => Promise<EnvironmentServiceType[]>;
|
|
30
|
+
// addService: (baseService: string, modules: string[]) => Promise<void>;
|
|
31
|
+
// removeService: (service: string) => Promise<void>;
|
|
32
|
+
scaleService: ( service: string, replicasCount: number ) => Promise<void>;
|
|
33
|
+
listenForInfrastructureChanges: ( callback: ( event: 'destroy' | 'create', service: EnvironmentServiceType ) => void ) => void;
|
|
34
|
+
reloadLoadBalancer: () => Promise<void>;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export type InfrastructurePortType = InfrastructureAdapterType & {
|
|
38
|
+
getServicesOpenApiDocumentation: () => Record<string, OpenAPIV3_1.Document>;
|
|
39
|
+
getThisServiceInfo: () => InfrastructureThisServiceType;
|
|
40
|
+
start: InfrastructureAdapterType['start'];
|
|
41
|
+
}
|
package/src/logger.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import S from '@bett3r-dev/jsonschema-definer';
|
|
2
|
+
import { AdapterBase } from './ports';
|
|
3
|
+
|
|
4
|
+
export const LoggerConfigSchema = S.shape({
|
|
5
|
+
logLevel: S.string().enum( 'silly', 'debug', 'log', 'info', 'warn', 'error' ).optional(),
|
|
6
|
+
modulesWhitelist: S.array().items( S.string()).optional(),
|
|
7
|
+
modulesBlacklist: S.array().items( S.string()).optional()
|
|
8
|
+
}).optional();
|
|
9
|
+
export type LoggerConfig = typeof LoggerConfigSchema.type;
|
|
10
|
+
|
|
11
|
+
export type LogLevels = 'silly'| 'debug'| 'log'| 'info'| 'warn'| 'error';
|
|
12
|
+
|
|
13
|
+
export const LogLevelMap = {
|
|
14
|
+
error: 0,
|
|
15
|
+
warn: 1,
|
|
16
|
+
info: 2,
|
|
17
|
+
log: 3,
|
|
18
|
+
debug: 4,
|
|
19
|
+
silly: 5,
|
|
20
|
+
};
|
|
21
|
+
export const LevelLogMap: Record<number, LogLevels> = {
|
|
22
|
+
0: 'error',
|
|
23
|
+
1: 'warn',
|
|
24
|
+
2: 'info',
|
|
25
|
+
3: 'log',
|
|
26
|
+
4: 'debug',
|
|
27
|
+
5: 'silly',
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export type LoggerAdapter = AdapterBase & {
|
|
31
|
+
log: ( originalArgs: any[], level: LogLevels , module: string ) => void
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export type LoggerPort = {
|
|
35
|
+
createLoggerInstance: ( module: string ) => LoggerType
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type LoggerType = {
|
|
39
|
+
silly: ( ...args: any[])=>void
|
|
40
|
+
debug: ( ...args: any[])=>void
|
|
41
|
+
log: ( ...args: any[])=>void
|
|
42
|
+
info: ( ...args: any[])=>void
|
|
43
|
+
warn: ( ...args: any[])=>void
|
|
44
|
+
error: ( ...args: any[])=>void
|
|
45
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import S from '@bett3r-dev/jsonschema-definer';
|
|
2
|
+
import { InfrastructureConfigSchema } from '@bett3r-dev/pv3-types';
|
|
3
|
+
import { CommittedEvent } from '@bett3r-dev/pv3-types';
|
|
4
|
+
import { ClusterStructureConfigSchema } from '@bett3r-dev/pv3-types';
|
|
5
|
+
|
|
6
|
+
export const UdpConfigSchema = S.shape({
|
|
7
|
+
port: S.number().default( 16706 ).optional(),
|
|
8
|
+
multicastAddress: S.string().default( '224.0.1.50' ).optional(),
|
|
9
|
+
ttl: S.number().optional()
|
|
10
|
+
});
|
|
11
|
+
export type UdpConfigType = typeof UdpConfigSchema.type;
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
export const AdminServicesConfigSchema = S.shape({
|
|
15
|
+
udp: UdpConfigSchema.optional().default({}),
|
|
16
|
+
structure: ClusterStructureConfigSchema.optional().default({}),
|
|
17
|
+
infrastructure: InfrastructureConfigSchema.optional().default({}),
|
|
18
|
+
metricsInterval: S.number().optional().default( 1000 )
|
|
19
|
+
});
|
|
20
|
+
export type AdminServicesConfigType = typeof AdminServicesConfigSchema.type;
|
|
21
|
+
|
|
22
|
+
export type OutboxManagerType = {
|
|
23
|
+
start: ( activateDiscoveredSubscriptions?: boolean ) => Promise<void>,
|
|
24
|
+
stop: () => Promise<void>
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type TcpEventMessageRequest = {
|
|
28
|
+
endpoint: string,
|
|
29
|
+
event: CommittedEvent
|
|
30
|
+
}
|
|
31
|
+
export type TcpEventMessageResponse = {
|
|
32
|
+
id: number,
|
|
33
|
+
status: number,
|
|
34
|
+
error?: string
|
|
35
|
+
}
|
|
36
|
+
|
package/src/ports.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { AuthenticationPort } from './authentication';
|
|
2
|
+
import { CachePort } from './cache';
|
|
3
|
+
import { ConfigurationPort } from './configuration';
|
|
4
|
+
import { DatabasePort } from './database';
|
|
5
|
+
import { EndpointsPort } from './endpoints';
|
|
6
|
+
import { EventStorePort } from './evenstore';
|
|
7
|
+
import { EventSourcingPort } from './eventsourcing';
|
|
8
|
+
import { InfrastructurePortType } from './infrastructure';
|
|
9
|
+
import { LoggerPort } from './logger';
|
|
10
|
+
import { TypedEventEmitter } from './typedEventEmitter';
|
|
11
|
+
|
|
12
|
+
export type PortEmitterEvents = {
|
|
13
|
+
started: void,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Base type for Porst that exposes an EventEmitter interface for handling the 'started' event.
|
|
18
|
+
*/
|
|
19
|
+
export type PortEmitter = {
|
|
20
|
+
isStarted: boolean,
|
|
21
|
+
emitter: TypedEventEmitter<PortEmitterEvents>,
|
|
22
|
+
onStarted: ( fn: () => void ) => void
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type AdapterBase<T = any> = {
|
|
26
|
+
name: string,
|
|
27
|
+
_adapter: () => T,
|
|
28
|
+
start: () => Promise<void>,
|
|
29
|
+
stop: () => Promise<void>,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type Ports = {
|
|
33
|
+
configuration: ConfigurationPort,
|
|
34
|
+
logger: LoggerPort,
|
|
35
|
+
cache: CachePort,
|
|
36
|
+
endpoints: EndpointsPort,
|
|
37
|
+
database: DatabasePort,
|
|
38
|
+
eventstore: EventStorePort,
|
|
39
|
+
eventsourcing: EventSourcingPort,
|
|
40
|
+
authentication: AuthenticationPort,
|
|
41
|
+
infrastructure: InfrastructurePortType
|
|
42
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import S from '@bett3r-dev/jsonschema-definer';
|
|
2
|
+
import { METHODS } from 'http';
|
|
3
|
+
import { Event } from './eventsourcing';
|
|
4
|
+
import { Ports } from './ports';
|
|
5
|
+
import { ClusterRoutesConfigType } from './clusterAdministration';
|
|
6
|
+
|
|
7
|
+
export const ClusterRoutesAggregateSchema = S.shape({
|
|
8
|
+
method: S.string().enum( ...METHODS.map( m=>m.toLocaleLowerCase())),
|
|
9
|
+
id: S.string(),
|
|
10
|
+
isPublic: S.boolean().optional(),
|
|
11
|
+
roles: S.array().items( S.string()).optional(),
|
|
12
|
+
});
|
|
13
|
+
export type ClusterRoutesAggregateType = typeof ClusterRoutesAggregateSchema.type;
|
|
14
|
+
|
|
15
|
+
export type RouteManagerEndpointAuth = ClusterRoutesAggregateType;
|
|
16
|
+
|
|
17
|
+
export const UpsertRouteCommandSchema = S.shape({}).additionalProperties( ClusterRoutesAggregateSchema );
|
|
18
|
+
export const DeleteRouteCommandSchema = S.array().items( S.string());
|
|
19
|
+
|
|
20
|
+
export const ClusterManagerEvents = {
|
|
21
|
+
RoutesUpserted: () => Event({
|
|
22
|
+
schema: UpsertRouteCommandSchema
|
|
23
|
+
}),
|
|
24
|
+
RouteDeleted: () => Event({
|
|
25
|
+
schema: DeleteRouteCommandSchema
|
|
26
|
+
})
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Represents the place where the routes are stored and consumed by an authorization server.
|
|
32
|
+
* Usually this store will live in a Cache and holds all the endpoints of the application
|
|
33
|
+
* and their access requirements. That means if the resource is public, or what roles can have access.
|
|
34
|
+
*/
|
|
35
|
+
export type RoutesAuthManagerType = {
|
|
36
|
+
getAllRoutes: () => Promise<RouteManagerEndpointAuth[]>;
|
|
37
|
+
getRoute: ( method:string, route: string ) => Promise<Pick<RouteManagerEndpointAuth, 'isPublic'|'roles'> | false>
|
|
38
|
+
upsertRoute: ( endpoint: RouteManagerEndpointAuth ) => Promise<RouteManagerEndpointAuth>;
|
|
39
|
+
subscribeToRoutesAuthChanges: ( onStructureChanges: ( change: ClusterRoutesAggregateType ) => void ) => Promise<{ unsubscribe: () => void }>;
|
|
40
|
+
stop: () => Promise<void>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type RouteAuthManagerFactory = ( ports: Pick<Ports, 'database'|'eventstore'>, config: ClusterRoutesConfigType, eventstore?: Ports['eventstore']) => RoutesAuthManagerType
|
|
44
|
+
|
|
45
|
+
// Endpoints Schemas
|
|
46
|
+
export const EndpointMarkedAsPublicSchema = S.shape({
|
|
47
|
+
method: S.string().enum( ...METHODS.map( m=>m.toLocaleLowerCase())),
|
|
48
|
+
id: S.string(),
|
|
49
|
+
isPublic: S.boolean().optional()
|
|
50
|
+
});
|
|
51
|
+
export type EndpointMarkedAsPublic = typeof EndpointMarkedAsPublicSchema.type
|
|
52
|
+
|
|
53
|
+
export const EndpointRolesUpsertedSchema = S.shape({
|
|
54
|
+
method: S.string().enum( ...METHODS.map( m=>m.toLocaleLowerCase())),
|
|
55
|
+
id: S.string(),
|
|
56
|
+
roles: S.array().items( S.string()),
|
|
57
|
+
});
|
|
58
|
+
export type EndpointRolesUpserted = typeof EndpointRolesUpsertedSchema.type
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { EventEmitter as NativeEventEmitter } from 'events';
|
|
2
|
+
|
|
3
|
+
declare module 'events' {
|
|
4
|
+
interface EventEmitter {
|
|
5
|
+
on<K extends keyof any>( event: K, listener: ( ...args: any[]) => void ): this;
|
|
6
|
+
emit<K extends keyof any>( event: K, ...args: any[]): boolean;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class TypedEventEmitter<T> {
|
|
11
|
+
|
|
12
|
+
private emitter = new NativeEventEmitter();
|
|
13
|
+
|
|
14
|
+
on<K extends keyof T>( event: K, listener: ( arg: T[K]) => void ): this {
|
|
15
|
+
this.emitter.on( event as string, listener );
|
|
16
|
+
return this;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
emit<K extends keyof T>( event: K, arg?: T[K]): boolean {
|
|
20
|
+
return this.emitter.emit( event as string, arg );
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
off<K extends keyof T>( event: K, listener: ( arg: T[K]) => void ): this {
|
|
24
|
+
this.emitter.off( event as string, listener );
|
|
25
|
+
return this;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
once<K extends keyof T>( event: K, listener: ( arg: T[K]) => void ): this {
|
|
29
|
+
this.emitter.once( event as string, listener );
|
|
30
|
+
return this;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
removeAllListeners<K extends keyof T>( event?: K ): this {
|
|
34
|
+
this.emitter.removeAllListeners( event as string );
|
|
35
|
+
return this;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
listeners<K extends keyof T>( event: K ): Function[] {
|
|
39
|
+
return this.emitter.listeners( event as string );
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
rawListeners<K extends keyof T>( event: K ): Function[] {
|
|
43
|
+
return this.emitter.rawListeners( event as string );
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
listenerCount<K extends keyof T>( event: K ): number {
|
|
47
|
+
return this.emitter.listenerCount( event as string );
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
prependListener<K extends keyof T>( event: K, listener: ( arg: T[K]) => void ): this {
|
|
51
|
+
this.emitter.prependListener( event as string, listener );
|
|
52
|
+
return this;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
prependOnceListener<K extends keyof T>( event: K, listener: ( arg: T[K]) => void ): this {
|
|
56
|
+
this.emitter.prependOnceListener( event as string, listener );
|
|
57
|
+
return this;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
eventNames(): ( keyof T )[] {
|
|
61
|
+
return this.emitter.eventNames() as ( keyof T )[];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
getMaxListeners(): number {
|
|
65
|
+
return this.emitter.getMaxListeners();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
setMaxListeners( n: number ): this {
|
|
69
|
+
this.emitter.setMaxListeners( n );
|
|
70
|
+
return this;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
}
|