@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.
Files changed (122) hide show
  1. package/build/authentication.d.ts +261 -0
  2. package/build/authentication.d.ts.map +1 -0
  3. package/build/authentication.js +108 -0
  4. package/build/authentication.js.map +1 -0
  5. package/build/cache.d.ts +101 -0
  6. package/build/cache.d.ts.map +1 -0
  7. package/build/cache.js +3 -0
  8. package/build/cache.js.map +1 -0
  9. package/build/clusterAdministration.d.ts +642 -0
  10. package/build/clusterAdministration.d.ts.map +1 -0
  11. package/build/clusterAdministration.js +177 -0
  12. package/build/clusterAdministration.js.map +1 -0
  13. package/build/configuration.d.ts +66 -0
  14. package/build/configuration.d.ts.map +1 -0
  15. package/build/configuration.js +3 -0
  16. package/build/configuration.js.map +1 -0
  17. package/build/database.d.ts +120 -0
  18. package/build/database.d.ts.map +1 -0
  19. package/build/database.js +41 -0
  20. package/build/database.js.map +1 -0
  21. package/build/endpoints.d.ts +183 -0
  22. package/build/endpoints.d.ts.map +1 -0
  23. package/build/endpoints.js +78 -0
  24. package/build/endpoints.js.map +1 -0
  25. package/build/evenstore.d.ts +26 -0
  26. package/build/evenstore.d.ts.map +1 -0
  27. package/build/evenstore.js +12 -0
  28. package/build/evenstore.js.map +1 -0
  29. package/build/eventsourcing/aggregateBuilder.d.ts +10 -0
  30. package/build/eventsourcing/aggregateBuilder.d.ts.map +1 -0
  31. package/build/eventsourcing/aggregateBuilder.js +3 -0
  32. package/build/eventsourcing/aggregateBuilder.js.map +1 -0
  33. package/build/eventsourcing/commandBuilder.d.ts +58 -0
  34. package/build/eventsourcing/commandBuilder.d.ts.map +1 -0
  35. package/build/eventsourcing/commandBuilder.js +6 -0
  36. package/build/eventsourcing/commandBuilder.js.map +1 -0
  37. package/build/eventsourcing/commandHandlers.d.ts +104 -0
  38. package/build/eventsourcing/commandHandlers.d.ts.map +1 -0
  39. package/build/eventsourcing/commandHandlers.js +28 -0
  40. package/build/eventsourcing/commandHandlers.js.map +1 -0
  41. package/build/eventsourcing/eventHandlers.d.ts +102 -0
  42. package/build/eventsourcing/eventHandlers.d.ts.map +1 -0
  43. package/build/eventsourcing/eventHandlers.js +33 -0
  44. package/build/eventsourcing/eventHandlers.js.map +1 -0
  45. package/build/eventsourcing/eventHandlersBuilder.d.ts +36 -0
  46. package/build/eventsourcing/eventHandlersBuilder.d.ts.map +1 -0
  47. package/build/eventsourcing/eventHandlersBuilder.js +13 -0
  48. package/build/eventsourcing/eventHandlersBuilder.js.map +1 -0
  49. package/build/eventsourcing/externalSystemBuilder.d.ts +14 -0
  50. package/build/eventsourcing/externalSystemBuilder.d.ts.map +1 -0
  51. package/build/eventsourcing/externalSystemBuilder.js +3 -0
  52. package/build/eventsourcing/externalSystemBuilder.js.map +1 -0
  53. package/build/eventsourcing/index.d.ts +386 -0
  54. package/build/eventsourcing/index.d.ts.map +1 -0
  55. package/build/eventsourcing/index.js +47 -0
  56. package/build/eventsourcing/index.js.map +1 -0
  57. package/build/eventsourcing/invariants.d.ts +19 -0
  58. package/build/eventsourcing/invariants.d.ts.map +1 -0
  59. package/build/eventsourcing/invariants.js +3 -0
  60. package/build/eventsourcing/invariants.js.map +1 -0
  61. package/build/eventsourcing/utils.d.ts +4 -0
  62. package/build/eventsourcing/utils.d.ts.map +1 -0
  63. package/build/eventsourcing/utils.js +3 -0
  64. package/build/eventsourcing/utils.js.map +1 -0
  65. package/build/index.d.ts +16 -0
  66. package/build/index.d.ts.map +1 -0
  67. package/build/index.js +32 -0
  68. package/build/index.js.map +1 -0
  69. package/build/infrastructure.d.ts +40 -0
  70. package/build/infrastructure.d.ts.map +1 -0
  71. package/build/infrastructure.js +11 -0
  72. package/build/infrastructure.js.map +1 -0
  73. package/build/logger.d.ts +32 -0
  74. package/build/logger.d.ts.map +1 -0
  75. package/build/logger.js +29 -0
  76. package/build/logger.js.map +1 -0
  77. package/build/outboxManager.d.ts +38 -0
  78. package/build/outboxManager.d.ts.map +1 -0
  79. package/build/outboxManager.js +21 -0
  80. package/build/outboxManager.js.map +1 -0
  81. package/build/ports.d.ts +39 -0
  82. package/build/ports.d.ts.map +1 -0
  83. package/build/ports.js +3 -0
  84. package/build/ports.js.map +1 -0
  85. package/build/routesAuth.d.ts +58 -0
  86. package/build/routesAuth.d.ts.map +1 -0
  87. package/build/routesAuth.js +37 -0
  88. package/build/routesAuth.js.map +1 -0
  89. package/build/typedEventEmitter.d.ts +23 -0
  90. package/build/typedEventEmitter.d.ts.map +1 -0
  91. package/build/typedEventEmitter.js +55 -0
  92. package/build/typedEventEmitter.js.map +1 -0
  93. package/build/validation.d.ts +64 -0
  94. package/build/validation.d.ts.map +1 -0
  95. package/build/validation.js +57 -0
  96. package/build/validation.js.map +1 -0
  97. package/package.json +15 -0
  98. package/src/authentication.ts +273 -0
  99. package/src/cache.ts +109 -0
  100. package/src/clusterAdministration.ts +216 -0
  101. package/src/configuration.ts +71 -0
  102. package/src/database.ts +157 -0
  103. package/src/endpoints.ts +198 -0
  104. package/src/evenstore.ts +27 -0
  105. package/src/eventsourcing/aggregateBuilder.ts +11 -0
  106. package/src/eventsourcing/commandBuilder.ts +64 -0
  107. package/src/eventsourcing/commandHandlers.ts +137 -0
  108. package/src/eventsourcing/eventHandlers.ts +118 -0
  109. package/src/eventsourcing/eventHandlersBuilder.ts +42 -0
  110. package/src/eventsourcing/externalSystemBuilder.ts +20 -0
  111. package/src/eventsourcing/index.ts +111 -0
  112. package/src/eventsourcing/invariants.ts +27 -0
  113. package/src/eventsourcing/utils.ts +4 -0
  114. package/src/index.ts +15 -0
  115. package/src/infrastructure.ts +41 -0
  116. package/src/logger.ts +45 -0
  117. package/src/outboxManager.ts +36 -0
  118. package/src/ports.ts +42 -0
  119. package/src/routesAuth.ts +58 -0
  120. package/src/typedEventEmitter.ts +73 -0
  121. package/src/validation.ts +89 -0
  122. package/tsconfig.json +15 -0
@@ -0,0 +1,157 @@
1
+ import S from '@bett3r-dev/jsonschema-definer';
2
+ import { AdapterBase, PortEmitter } from './ports';
3
+
4
+ export type Document = {
5
+ id: string|number,
6
+ [key: string]: any
7
+ }
8
+
9
+ export type IndexOptions = {
10
+ isUnique: boolean
11
+ }
12
+
13
+ export type DatabaseOps =
14
+ | 'eq'
15
+ | 'neq'
16
+ | 'lt'
17
+ | 'gt'
18
+ | 'lte'
19
+ | 'gte'
20
+ | 'in'
21
+ | 'nin'
22
+ | 'contains'
23
+ | 'notContains'
24
+ | 'some'
25
+ | 'none'
26
+ | 'every'
27
+ | 'exists'
28
+ | 'notExists';
29
+
30
+ export const filterOpMap = {
31
+ eq: <T>( a: T, b: T ) => a === b,
32
+ neq: <T>( a: T, b: T ) => a !== b,
33
+ lt: <T>( a: T, b: T ) => a < b,
34
+ gt: <T>( a: T, b: T ) => a > b,
35
+ lte: <T>( a: T, b: T ) => a <= b,
36
+ gte: <T>( a: T, b: T ) => a >= b,
37
+ in: <T>( a: T, b: T[]) => b.includes( a ),
38
+ nin: <T>( a: T, b: T[]) => !b.includes( a ),
39
+ contains: <T>( a: T[], b: T ) => a.includes( b ),
40
+ notContains: <T>( a: T[], b: T ) => !a.includes( b ),
41
+ some: <T>( a: T[], b: T[]) => a.some( x=> b.includes( x )),
42
+ none: <T>( a: T[], b: T[]) => !a.some( x=> b.includes( x )),
43
+ every: <T>( a: T[], b: T[]) => a.every( x=> b.includes( x )),
44
+ exists: <T>( a:T ) => !!a,
45
+ notExists: <T>( a:T ) => !a
46
+ };
47
+
48
+ export const CollectionFilterSchema = S.shape({
49
+ field: S.string(),
50
+ op: S.enum([ 'eq', 'neq', 'lt', 'gt', 'lte', 'gte', 'in', 'nin', 'contains', 'notContains', 'some', 'every', 'exists', 'notExists' ]),
51
+ value: S.any()
52
+ });
53
+
54
+ export type CollectionFilterOR<T extends Document = any> = {
55
+ OR: CollectionFilter<T>[]
56
+ }
57
+
58
+ export type CollectionFilterAND<T extends Document = any> = {
59
+ AND: CollectionFilter<T>[]
60
+ }
61
+
62
+ export type CollectionFilterInstance<Model extends Document> = {
63
+ field: keyof Model;
64
+ op: DatabaseOps;
65
+ value: any;
66
+ };
67
+
68
+ export type CollectionFilter<T extends Document = any> = Partial<
69
+ & CollectionFilterOR<T>
70
+ & CollectionFilterAND<T>
71
+ & CollectionFilterInstance<T>
72
+ >
73
+
74
+ export const CollectionSortSchema = S.shape({
75
+ field: S.string(),
76
+ direction: S.enum([ 'asc', 'desc' ])
77
+ });
78
+
79
+ export type CollectionSort<Model extends Document> = {
80
+ field: keyof Model;
81
+ direction?: 'asc' | 'desc';
82
+ };
83
+
84
+ export const CollectionQueryParamsSchema = S.shape({
85
+ sort: S.array().items( CollectionSortSchema ).optional(),
86
+ filter: S.array().items( CollectionFilterSchema ).optional(),
87
+ limit: S.number().optional(),
88
+ after: S.string().optional(),
89
+ select: S.array().items( S.string()).optional()
90
+ });
91
+
92
+ export type CollectionQueryParams<Model extends Document> = {
93
+ id?: string | number;
94
+ limit?: number;
95
+ after?: string | number;
96
+ sort?: CollectionSort<Model>[];
97
+ filter?: CollectionFilter<Model>[];
98
+ select?: keyof Model | ( keyof Model )[]
99
+ };
100
+
101
+ export type CollectionOperations<Model extends Document> = {
102
+ readById: ( id: string|number ) => Promise<Model|null>
103
+ query: ( params: CollectionQueryParams<Model> ) => Promise<Model[]>,
104
+ forEach: ( params: CollectionQueryParams<Model>, callback: ( record: Model, stop: () => void ) => void | Promise<void> ) => Promise<void>,
105
+ upsert: ( id: string | number, data: Partial<Model> ) => Promise<Model>,
106
+ destroyById: ( id:string | number ) => Promise<void>,
107
+ createMany: ( documents: Model[]) => Promise<Model[]>,
108
+ updateMany: ( filter: CollectionFilter<Model>[], data: Partial<Model> ) => Promise<Model[]>,
109
+ destroyMany: ( filter: CollectionFilter<Model>[]) => Promise<number>,
110
+ atomicIncrement: ( name: string, filter?: CollectionFilter<Model>, amount?: number ) => Promise<number>,
111
+ }
112
+
113
+ export type DatabaseChange<Model extends Document = any> = {
114
+ changeType: 'insert'|'update'|'delete',
115
+ data?: Model,
116
+ previous?: Model,
117
+ }
118
+
119
+ export type Collection<Model extends Document> = CollectionOperations<Model> & {
120
+ ensureIndex: ( properties: string[], options?: Partial<IndexOptions> ) => Promise<void>,
121
+ destroyIndex: ( properties: string[]) => Promise<void>,
122
+ watch: ( changeTypes: ( 'insert'|'update'|'delete' )[], onData: ( data: DatabaseChange<Model> ) => void, onError?: ( e: Error ) => void ) => Promise<{
123
+ close: () => void
124
+ }>;
125
+ }
126
+
127
+ // export type DatabaseTransaction = {
128
+ // commitTransaction: <Model extends Document>( docs?: Model[]) => Promise<Model[] | void>,
129
+ // rollbackTransaction: ( error: Error ) => Promise<void>,
130
+ // getCollection: <Model extends Document>( collection:string ) => CollectionOperations<Model>
131
+ // _transaction: () => any
132
+ // }
133
+
134
+
135
+ /**
136
+ * @category Main Types
137
+ */
138
+ export type DatabaseTransaction = {
139
+ getCollection: <Model extends Document>( collection: string ) => CollectionOperations<Model>
140
+ }
141
+ /**
142
+ * @category Main Types
143
+ */
144
+ export type DatabaseType = {
145
+ getCollection: <Model extends Document>( name:string ) => Collection<Model>
146
+ initTransaction: <Result = any>( operations: ( getCollection: <Model extends Document>( collection: string ) => CollectionOperations<Model> ) => Promise<Result | undefined>, retryBackoff?: number, options?: any ) => Promise<Result>;
147
+ }
148
+ /**
149
+ * @category Main Types
150
+ */
151
+ export type DatabasePort = PortEmitter & DatabaseType & {
152
+ _adapter: DatabaseAdapter
153
+ };
154
+ /**
155
+ * @category Main Types
156
+ */
157
+ export type DatabaseAdapter<T = any> = DatabaseType & AdapterBase<T>;
@@ -0,0 +1,198 @@
1
+ import S, { BaseSchema, ObjectSchema, mergeSchemas } from '@bett3r-dev/jsonschema-definer';
2
+ import { METHODS } from 'http';
3
+ import { JSONSchema7 } from 'json-schema';
4
+ import { AddressInfo } from 'net';
5
+ import { OpenAPIV3_1 } from 'openapi-types';
6
+ import { AdapterBase, PortEmitter } from './ports';
7
+
8
+ const CorsOptions = S.shape({
9
+ origin: S.string().optional(),
10
+ methods: S.string().enum( ...METHODS ).optional(),
11
+ allowedHeaders: S.anyOf( S.string(), S.array().items( S.string())).optional(),
12
+ exposedHeaders: S.anyOf( S.string(), S.array().items( S.string())).optional(),
13
+ credentials: S.boolean().optional(),
14
+ maxAge: S.number().optional(),
15
+ preflightContinue: S.boolean().optional(),
16
+ optionsSuccessStatus: S.number().optional(),
17
+ });
18
+
19
+ const CompressionOptions = S.shape({
20
+ chunkSize: S.number().optional(),
21
+ level: S.number().optional(),
22
+ memLevel: S.number().optional(),
23
+ strategy: S.number().optional(),
24
+ threshold: S.number().optional(),
25
+ windowBits: S.number().optional(),
26
+ });
27
+
28
+ export const HttpConfigSchema = S.shape({
29
+ port: S.number().optional(),
30
+ cookie: S.shape({
31
+ enabled: S.boolean()
32
+ }).additionalProperties( true ).optional(),
33
+ listeningEnabled: S.boolean().optional(),
34
+ cors: mergeSchemas( CorsOptions, S.shape({
35
+ enabled: S.boolean()
36
+ })).additionalProperties( true ).optional(),
37
+ compression: mergeSchemas( CompressionOptions, S.shape({
38
+ enabled: S.boolean()
39
+ })).additionalProperties( true ).optional(),
40
+ metrics: S.shape({
41
+ enabled: S.boolean().optional(),
42
+ httpRequestDurations: S.shape({
43
+ enabled: S.boolean().optional(),
44
+ buckets: S.array().items( S.number()).optional()
45
+ }).optional()
46
+ }).optional()
47
+ }).optional();
48
+
49
+ export type HttpConfig = typeof HttpConfigSchema.type
50
+
51
+
52
+ export type EndpointsFetchInterfaceResponse<T = any> = Pick<Response, 'ok'> & {
53
+ json: () => Promise<T>;
54
+ text: () => Promise<T>;
55
+ }
56
+
57
+ export type RequestObject<RAW extends ReadableStream = any> = {
58
+ type: 'string',
59
+ raw?: RAW,
60
+ body: unknown,
61
+ query: unknown,
62
+ params: unknown,
63
+ method: string,
64
+ route: string,
65
+ filePaths: Record<string, string>,
66
+ headers: Record<string,string | string[]>
67
+ }
68
+
69
+ export type ResponseObject<RAW extends WritableStream = any> = {
70
+ raw?: RAW,
71
+ send: ( payload: unknown ) => unknown
72
+ header: ( key: string, value: any ) => ResponseObject
73
+ }
74
+
75
+ export type EndpointActionParamsContext<USER=any> = {
76
+ res:ResponseObject
77
+ req:RequestObject,
78
+ endpoint: Endpoint,
79
+ user: USER,
80
+ token?: string;
81
+ [key:string]: any
82
+ }
83
+
84
+ export type EndpointActionParams<BODY=any, QUERY=any, PARAMS=any, USER=any> = {
85
+ query: QUERY
86
+ params: PARAMS
87
+ body: BODY
88
+ context: Partial<EndpointActionParamsContext<USER>>
89
+ }
90
+
91
+ export type EndpointSchemas<BODY, QUERY, PARAMS> = {
92
+ body?: BaseSchema<BODY>,
93
+ query?: BaseSchema<QUERY>,
94
+ params?: BaseSchema<PARAMS>,
95
+ headers?: ObjectSchema,
96
+ responses?: Record<number, {schema: BaseSchema , name: string}[]>,
97
+ errors?: Record<number, {schema: BaseSchema, name: string}[]>,
98
+ };
99
+
100
+ export type EndpointAction<BODY=any, RETURN=any, QUERY=any, PARAMS=any> = ( params: EndpointActionParams<BODY, QUERY, PARAMS> ) => Promise<RETURN>
101
+
102
+ export type Endpoint<BODY=any, RETURN=any, QUERY=any, PARAMS=any> = {
103
+ route: string;
104
+ action: EndpointAction<BODY, RETURN, QUERY, PARAMS>,
105
+ module: string;
106
+ name?: string;
107
+ description?: string;
108
+ schemas?: EndpointSchemas<BODY, QUERY, PARAMS>;
109
+ shouldValidate?: boolean;
110
+ responseContentType?: string;
111
+ tags?: string[]
112
+ //REVIEW: Protocol Specific
113
+ method?: 'DELETE' | 'GET' | 'HEAD' | 'PATCH' | 'POST' | 'PUT' | 'OPTIONS';
114
+ //REVIEW: Protocol Specific
115
+ multipartProperties?: string[],
116
+ }
117
+
118
+ export const EndpointSchema = S.shape({
119
+ route: S.string().pattern( /^\/.*/ ),
120
+ action: S.any(),
121
+ module: S.string().optional(),
122
+ name: S.string().optional(),
123
+ description: S.string().optional(),
124
+ schemas: S.any().optional(),
125
+ tags: S.array().items( S.string()).optional(),
126
+ multipartProperties: S.array().items( S.string()).optional(),
127
+ method: S.string().enum( 'DELETE', 'GET', 'HEAD', 'PATCH', 'POST', 'PUT', 'OPTIONS' ).optional(),
128
+ });
129
+
130
+ export type InjectHandlerContext<REQ = any, RES = any> = {
131
+ request: REQ,
132
+ response: RES,
133
+ callInactiveEvent: () => void
134
+ callStartEvent: () => void
135
+ }
136
+
137
+ export type CloudFunctionsAdapter = {
138
+ /**
139
+ * Function used when working on a cloud function environment. Internal function.
140
+ * @param context {InjectHandlerContext}
141
+ * @returns {Promise<void>}
142
+ */
143
+ injectHandler: ( context: InjectHandlerContext )=> Promise<void>,
144
+ /**
145
+ * This method is called when the server is starting a new Events Related connection. Things like SSE, WS, TCP, etc.
146
+ * It is used internally in cloud function environments to keep the function alive.
147
+ * @returns {void}
148
+ */
149
+ callEventConnectionsActive: () => void,
150
+ /**
151
+ * This method is called when the server has no more Events Related connection. Things like SSE, WS, TCP, etc.
152
+ * It is used internally in cloud function environments to mark the function as idle so it can be killed.
153
+ * @returns {void}
154
+ */
155
+ callEventConnectionsInactive: () => void,
156
+ }
157
+
158
+ export type MetricBaseType = {
159
+ name: string,
160
+ help: string,
161
+ labelNames: string[],
162
+ }
163
+ export type CounterMetricsType = MetricBaseType & {}
164
+ export type GaugeMetricsType = MetricBaseType & {}
165
+
166
+ export type EndpointsAdapter<T = any> = AdapterBase<T> & Partial<CloudFunctionsAdapter> & {
167
+ adapterType?: 'httpHandler',
168
+ getServerAddress?: () => AddressInfo;
169
+ registerOpenApi?: ( openapi: Record<string, any> ) => void;
170
+ registerOpenApiDocument?: () => void;
171
+ registerEndpoint: ( endpoint: Endpoint ) => Endpoint;
172
+ serveStaticDirectory?: ( path: string, endpoint?: string ) => void;
173
+ onServicesDirectorySet?: ( callback: ( serviceDirectory: Record<string, string> ) => void ) => void;
174
+ metrics?: {
175
+ addCounter: ( metric: CounterMetricsType ) => ( labels: string[]) => void
176
+ addGauge: ( metric: CounterMetricsType ) => ( labels: string[], value: number ) => void
177
+ }
178
+ }
179
+
180
+ export type EndpointsType = CloudFunctionsAdapter & {
181
+ getEndpoints?(): Endpoint[],
182
+ setServiceSupportedModules: ( modules: Record<string, string[]> ) => void;
183
+ getOpenApiSpec: () => OpenAPIV3_1.Document,
184
+ generateOpenApiSpec: ( servicePath?: string ) => Promise<OpenAPIV3_1.Document>,
185
+ registerEndpoint<BODY, RETURN, QUERY, PARAMS>( endpoint: Endpoint<BODY, RETURN, QUERY, PARAMS> ): void;
186
+ registerSchema( name:string, schema: JSONSchema7, tags: string[]): void;
187
+ addOpenApiTag( name: string ): void;
188
+ call<Type, R=Type>( route: string, method?:string, params?: Partial<Pick<EndpointActionParams<Type>, 'body'|'context'>> ): Promise<R>
189
+ fetch( route: string, params: {method: 'get'| 'post', headers: Record<string,any>, body: string}): Promise<EndpointsFetchInterfaceResponse>
190
+ get<Type, R=Type>( route: string, params?: Partial<Pick<EndpointActionParams<Type>, 'body'|'context'>> ): Promise<R>
191
+ post<Type, R=Type>( route: string, params?: Partial<Pick<EndpointActionParams<Type>, 'body'|'context'>> ): Promise<R>,
192
+ start: () => Promise<void>;
193
+ stop: () => Promise<void>;
194
+ }
195
+
196
+ export type EndpointsPort = PortEmitter & EndpointsType & {
197
+ _adapters: () => EndpointsAdapter[]
198
+ }
@@ -0,0 +1,27 @@
1
+ import S from '@bett3r-dev/jsonschema-definer';
2
+ import { DatabaseTransaction } from './database';
3
+ import { CommittedEvent, EventToCommit, ReadMultipleStreamOptions, ReadStreamOptions } from './eventsourcing';
4
+ import { AdapterBase, PortEmitter } from './ports';
5
+
6
+ export const EventStoreConfigSchema = S.shape({
7
+ enabled: S.boolean().optional(),
8
+ collection: S.string().optional()
9
+ }).optional();
10
+ export type EventStoreConfig = typeof EventStoreConfigSchema.type;
11
+
12
+ export type EventStoreType = {
13
+ appendToStream: <Type>( stream:string, events: EventToCommit|EventToCommit[], expectedVersion?: number, transaction?: DatabaseTransaction ) => Promise<CommittedEvent<Type>[]>;
14
+ appendToMultipleStreams?: <Type>( streams: Record<string, { events: EventToCommit|EventToCommit[], expectedVersion?: number, }>, transaction?: DatabaseTransaction ) => Promise<CommittedEvent<Type>[]>;
15
+ readStream: <Model=any>( readOptions: ReadStreamOptions, callback: ( event: CommittedEvent, stop: () => void ) => Partial<Model> | Promise<Partial<Model>> ) => Promise<Model>
16
+ readMultipleStream?: <Model=any>( readOptions: ReadMultipleStreamOptions, callback: ( event: CommittedEvent, stop: () => void ) => Record<string,Partial<Model>> | Promise<Record<string,Partial<Model>>> ) => Promise<Record<string,Model>>
17
+ subscribe: ( onData: ( event: CommittedEvent ) => void, onError?: ( e: Error ) => void ) => Promise<{
18
+ close: () => void
19
+ }>
20
+ flush: () => Promise<void>
21
+ }
22
+
23
+ export type EventStoreAdapter = AdapterBase & EventStoreType;
24
+
25
+ export type EventStorePort = PortEmitter & EventStoreType & {
26
+ _adapter: EventStoreAdapter
27
+ };
@@ -0,0 +1,11 @@
1
+ import { Command, EventFactory, EventReducers } from '.';
2
+ import { DatabasePort } from '..';
3
+ import { ExternalSystemBuilderType } from './externalSystemBuilder';
4
+
5
+
6
+ export type AggregateBuilderType<Model, EVENTS extends Record<string, EventFactory>> = Omit<ExternalSystemBuilderType<Model, EVENTS>, 'withCommandTemplate'> & {
7
+ withCommandTemplate: ( command: Command ) => AggregateBuilderType<Model, EVENTS>
8
+ notEventsourced: ( database: DatabasePort, collection: string ) => AggregateBuilderType<Model, EVENTS>
9
+ initialValue: ( init: () => ( Partial<Model> | null | undefined )) => Pick<AggregateBuilderType<Model, EVENTS>, 'withEventReducers' | 'withCommands'>
10
+ withEventReducers: ( eventReducers: EventReducers<EVENTS, Model> ) => Pick<AggregateBuilderType<Model, EVENTS>, 'withCommands'>
11
+ }
@@ -0,0 +1,64 @@
1
+ //*******************************************
2
+ // Command Builder
3
+ //*******************************************
4
+
5
+ import { BaseSchema } from '@bett3r-dev/jsonschema-definer';
6
+ import { Command, CommandCommitFunction, CommandErrorHandlerFunction, CommandFormatFunction, CommandHandlerFunction, CommandLoadFunction, EventFactory, InvariantType } from '.';
7
+
8
+ export type CommandBuilderFromSchemaType<Model, EVENTS extends Record<string, EventFactory>, EVENT extends keyof EVENTS, CommandPayload> = {
9
+ /**
10
+ * Loads the streams from the aggregate. If the Command Handler is an Aggregate the fuction will be ignored.
11
+ * Default Implementation:
12
+ * ```ts
13
+ * loadAggregateState(state.commandHandler, (state.streams || []), options)
14
+ * .then(u.ensureArray)
15
+ * .then((aggregateState) => ({...state, aggregateState} as CommandHandlerState));
16
+ * ```
17
+ */
18
+ withLoadFunction: ( loadFunction: CommandLoadFunction<Model, CommandPayload> ) => CommandBuilderFromSchemaType<Model, EVENTS, EVENT, CommandPayload>
19
+ /**
20
+ * Commits the new event/events to the store
21
+ * DefaultImplementation:
22
+ * ```ts
23
+ * const defaultCommit = (state: CommandHandlerState) =>
24
+ * commitCommands(state);
25
+ * ```
26
+ */
27
+ withCommitFunction: ( commitFunction: CommandCommitFunction<Model, CommandPayload> ) => CommandBuilderFromSchemaType<Model, EVENTS, EVENT, CommandPayload>
28
+ withResultFormatFunction: ( resultFormatFunction: CommandFormatFunction<Model, CommandPayload> ) => CommandBuilderFromSchemaType<Model, EVENTS, EVENT, CommandPayload>
29
+ withInvariant: ( invariant: InvariantType<Model, CommandPayload> | (( state: Model, data: CommandPayload ) => Promise<void> )) => CommandBuilderFromSchemaType<Model, EVENTS, EVENT, CommandPayload>,
30
+ produces: <EVENT extends keyof EVENTS>( events: EVENT[]) => {
31
+ withHandler: <USER, StateModel = Model>( handler: CommandHandlerFunction<StateModel, CommandPayload, EVENTS, EVENT, USER> ) => {
32
+ build: () => Command<EVENTS, EVENT, Model, CommandPayload>,
33
+ withErrorHandler: ( errorHandler: CommandErrorHandlerFunction<CommandPayload> ) => {
34
+ build: () => Command<EVENTS, EVENT, Model, CommandPayload>
35
+ }
36
+ },
37
+ withErrorHandler: ( errorHandler: CommandErrorHandlerFunction<CommandPayload> ) => {
38
+ build: () => Command<EVENTS, EVENT, Model, CommandPayload>
39
+ },
40
+ build: () => Command<EVENTS, EVENT, Model, CommandPayload>
41
+ }
42
+ }
43
+
44
+ export type CommandBuilderInstance<Model, EVENTS extends Record<string, EventFactory>, EVENT extends keyof EVENTS, CommandPayload> = {
45
+ withDescription: ( description: string ) => CommandBuilderInstance<Model, EVENTS, EVENT, CommandPayload>
46
+ /** Specify where to find the id of the aggregate when the command has no Id in the Params of the request
47
+ *
48
+ * suppose a structure of
49
+ * ```ts
50
+ * {
51
+ * ISC: 'QWERASDFZXCV',
52
+ * marketing: { promoId: '1q2w3e'}
53
+ * products: [{id: 1}, {id:2}]
54
+ * }
55
+ * ```
56
+ * Some examples are: `ISC`, `warehouse.id`.
57
+ * In the case of an array use a Dollar sign `$`.
58
+ * `products.$.id`
59
+ */
60
+ withDataPropertyId: ( dataPath: string ) => CommandBuilderInstance<Model, EVENTS, EVENT, CommandPayload>
61
+ withSchema: <CommandPayload>( schema: BaseSchema<CommandPayload> ) => CommandBuilderFromSchemaType<Model, EVENTS, EVENT, CommandPayload>,
62
+ }
63
+
64
+ export type CommandBuilderType<Model = any, EVENTS extends Record<string, EventFactory>=any, EVENT extends keyof EVENTS=any, CommandPayload=any> = ( init?: Omit<Command<EVENTS, EVENT, CommandPayload, Model>, 'handler' | 'events' | 'schema'> ) => CommandBuilderInstance<Model, EVENTS, EVENT, CommandPayload>
@@ -0,0 +1,137 @@
1
+ import S, { BaseSchema } from '@bett3r-dev/jsonschema-definer';
2
+ import { Builder, CommittedEvent, EventFactory, EventMetadata, GetMessageType, ReadStreamOptions } from '.';
3
+ import { AppErrorInstance, AppErrorType } from '../validation';
4
+ import { DatabasePort } from '../database';
5
+ import { EndpointActionParams, EndpointActionParamsContext } from '../endpoints';
6
+
7
+ export type CommandHandlerState<Events = Record<string, EventFactory>, Payload=any, Model = any, USER=any> = {
8
+ commandHandler: Aggregate | System,
9
+ command: Command,
10
+ stream: string,
11
+ data: Payload,
12
+ query: {id?: string},
13
+ params: Record<string, any>,
14
+ context: Partial<EndpointActionParamsContext<USER>>
15
+ events: Events[keyof Events][],
16
+ state: Model,
17
+ committedEvents?: Array<CommittedEvent>,
18
+ metadata?: EventMetadata
19
+ expectedVersion?: number
20
+ }
21
+
22
+ export type CommandExecutionResponse<Model = unknown> = {
23
+ state?: Model,
24
+ events: CommittedEvent[]
25
+ }
26
+
27
+ export type ExecuteCommandFunction = <SYSTEM extends System, COMMAND extends keyof SYSTEM['commands']>( commandHandler:SYSTEM, commandName: COMMAND ) => ( params: Partial<EndpointActionParams> ) => Promise<unknown>;
28
+ export type CommandLoadFunction<Model=any, Payload=any> = ( state: CommandHandlerState<Model,Payload>, options?: ReadStreamOptions, initialState?: Model ) => Promise<CommandHandlerState<Model, Payload>>
29
+ export type CommandCommitFunction<Model=any, Payload=any> = ( state: CommandHandlerState<Model,Payload> ) => Promise<CommandHandlerState<Model, Payload>>
30
+ export type CommandFormatFunction<Model=any, Payload=any> = ( handlerState: CommandHandlerState<Model, Payload> ) => Promise<CommandExecutionResponse<Model>>
31
+
32
+ export type CommandHandlerFunction<Model=any, CommandPayload=unknown, Events extends Record<string, EventFactory<EVENT>>=any, EVENT extends keyof Events=any, USER = any> = ( state: Model extends undefined ? null : Model, data: CommandPayload, aggregateState: CommandHandlerState<Events, CommandPayload, any, USER> ) => Promise<EventFactory<EVENT, GetMessageType<Events[EVENT]>>[]> | EventFactory<EVENT, GetMessageType<Events[EVENT]>>[]
33
+
34
+ export type CommandErrorHandlerFunction<CommandPayload=unknown> = ( error: AppErrorInstance, data: CommandPayload ) => Promise<any>
35
+
36
+ export type Command<EVENTS extends Record<string, EventFactory<EVENT>>=any, EVENT extends keyof EVENTS=any, SystemModel=any, CommandPayload=any> = {
37
+ schema: BaseSchema<CommandPayload>
38
+ handler: CommandHandlerFunction<SystemModel, CommandPayload, EVENTS, EVENT>
39
+ onErrorHandler?: CommandErrorHandlerFunction<CommandPayload>,
40
+ dataPropertyId?: string,
41
+ name?: string,
42
+ actors?: string[]
43
+ description?: string
44
+ multipartProperties?: string[],
45
+ events?: EVENT[],
46
+ invariants?: InvariantType[]
47
+ load?: CommandLoadFunction,
48
+ commit?: CommandCommitFunction,
49
+ format?: CommandFormatFunction
50
+ }
51
+
52
+ export type InvariantType<Model=any, Payload=any> = {
53
+ events?: string[],
54
+ rejects: AppErrorType[],
55
+ beforeLoad?: ( state: CommandHandlerState<any, Payload, Model> ) => Promise<CommandHandlerState<any, Payload, Model>>
56
+ check: ( commandHandlerState: CommandHandlerState<any, Payload, Model> ) => Promise<CommandHandlerState<any, Payload, Model> | void> | void | CommandHandlerState<any, Payload, Model>
57
+ }
58
+
59
+ //*******************************************
60
+ // Systems
61
+ //*******************************************
62
+
63
+ export const SystemGetQuerySchema = S.shape({
64
+ stream: S.string(),
65
+ names: S.array().items( S.string()).optional(),
66
+ from: S.number().optional(),
67
+ limit: S.number().optional(),
68
+ backward: S.boolean().optional(),
69
+ after: S.string().format( 'date-time' ).optional(),
70
+ before: S.string().format( 'date-time' ).optional(),
71
+ });
72
+ export type SystemGetQueryType = typeof SystemGetQuerySchema.type;
73
+ export const SystemEndpointParamsSchema = S.shape({
74
+ id: S.string().description( 'id for the aggregate' )
75
+ });
76
+ export const CommandEndpointQuerySchema = S.shape({
77
+ expectedVersion: S.string().optional(),
78
+ causationId: S.string().optional(),
79
+ correlationId: S.string().optional(),
80
+ });
81
+
82
+ export type OnValidationErrorFunctionType = Function;
83
+
84
+ export type System<Commands extends Record<string, Command | Builder<Command>>=any, EVENTS extends Record<string, EventFactory>=any> = {
85
+ name: string,
86
+ events: EVENTS,
87
+ isStreamable?: boolean,
88
+ isAggregate?: boolean,
89
+ isEventsourced?: boolean,
90
+ commands: Commands,
91
+ templateCommand: Command
92
+ }
93
+
94
+ //*******************************************
95
+ // Aggregates
96
+ //*******************************************
97
+
98
+ export type AggregateReducedState<Model = any> = {state: Model, lastEvent?: Partial<Pick<CommittedEvent, 'id'|'version'>>}
99
+
100
+ export type EventReducers<EVENTS extends Record<string, EventFactory>=any, Model=any> = {
101
+ [Name in keyof EVENTS]: ( state: Model, data: GetMessageType<EVENTS[Name]>, metadata: EventMetadata ) => Partial<Model>
102
+ }
103
+
104
+ export type Aggregate<Commands extends Record<string, Command | Builder<Command>>=any, EVENTS extends Record<string, EventFactory>=any, Model=any> = System<Commands, EVENTS> & {
105
+ schema: BaseSchema<Model>
106
+ outboxDatabase?: DatabasePort,
107
+ outboxCollection?: string,
108
+ initialValue: () => Partial<Model> | null | undefined,
109
+ eventReducers: EventReducers<EVENTS, Model>
110
+ }
111
+
112
+ //*******************************************
113
+ // Command Handlers
114
+ //*******************************************
115
+
116
+ export type EventSourcingCommandHandlers = {
117
+ loadStream: (
118
+ stream: string,
119
+ options?: ReadStreamOptions
120
+ ) => Promise<CommittedEvent[] | undefined>;
121
+ loadAggregateState: (
122
+ system: System,
123
+ stream?: string,
124
+ options?: ReadStreamOptions
125
+ ) => Promise<Record<string, any> | null>;
126
+ executeCommand: ExecuteCommandFunction;
127
+ registerCommandHandlerGetEndpoints: (
128
+ aggregateOrSystem: Aggregate | System,
129
+ name?: string
130
+ ) => string[];
131
+ registerCommandHandlerPostEndpoints: (
132
+ aggregateOrSystem: ( Aggregate | System ) & {
133
+ events: Record<string, EventFactory>;
134
+ },
135
+ name?: string
136
+ ) => Record<string, string>;
137
+ };