@strapi/database 4.0.0-beta.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 (52) hide show
  1. package/LICENSE +22 -0
  2. package/examples/connections.js +36 -0
  3. package/examples/data.sqlite +0 -0
  4. package/examples/docker-compose.yml +29 -0
  5. package/examples/index.js +73 -0
  6. package/examples/models.js +341 -0
  7. package/examples/typings.ts +17 -0
  8. package/lib/dialects/dialect.js +45 -0
  9. package/lib/dialects/index.js +28 -0
  10. package/lib/dialects/mysql/index.js +51 -0
  11. package/lib/dialects/mysql/schema-inspector.js +203 -0
  12. package/lib/dialects/postgresql/index.js +49 -0
  13. package/lib/dialects/postgresql/schema-inspector.js +229 -0
  14. package/lib/dialects/sqlite/index.js +74 -0
  15. package/lib/dialects/sqlite/schema-inspector.js +151 -0
  16. package/lib/entity-manager.js +886 -0
  17. package/lib/entity-repository.js +110 -0
  18. package/lib/errors.js +14 -0
  19. package/lib/fields.d.ts +9 -0
  20. package/lib/fields.js +232 -0
  21. package/lib/index.d.ts +146 -0
  22. package/lib/index.js +60 -0
  23. package/lib/lifecycles/index.d.ts +50 -0
  24. package/lib/lifecycles/index.js +66 -0
  25. package/lib/lifecycles/subscribers/index.d.ts +9 -0
  26. package/lib/lifecycles/subscribers/models-lifecycles.js +19 -0
  27. package/lib/lifecycles/subscribers/timestamps.js +65 -0
  28. package/lib/metadata/index.js +219 -0
  29. package/lib/metadata/relations.js +488 -0
  30. package/lib/migrations/index.d.ts +9 -0
  31. package/lib/migrations/index.js +69 -0
  32. package/lib/migrations/storage.js +49 -0
  33. package/lib/query/helpers/index.js +10 -0
  34. package/lib/query/helpers/join.js +95 -0
  35. package/lib/query/helpers/order-by.js +70 -0
  36. package/lib/query/helpers/populate.js +652 -0
  37. package/lib/query/helpers/search.js +84 -0
  38. package/lib/query/helpers/transform.js +84 -0
  39. package/lib/query/helpers/where.js +322 -0
  40. package/lib/query/index.js +7 -0
  41. package/lib/query/query-builder.js +348 -0
  42. package/lib/schema/__tests__/schema-diff.test.js +181 -0
  43. package/lib/schema/builder.js +352 -0
  44. package/lib/schema/diff.js +376 -0
  45. package/lib/schema/index.d.ts +49 -0
  46. package/lib/schema/index.js +95 -0
  47. package/lib/schema/schema.js +209 -0
  48. package/lib/schema/storage.js +75 -0
  49. package/lib/types/index.d.ts +6 -0
  50. package/lib/types/index.js +34 -0
  51. package/lib/utils/content-types.js +41 -0
  52. package/package.json +39 -0
@@ -0,0 +1,110 @@
1
+ 'use strict';
2
+
3
+ const withDefaultPagination = params => {
4
+ const { page = 1, pageSize = 10, ...rest } = params;
5
+
6
+ return {
7
+ page: Number(page),
8
+ pageSize: Number(pageSize),
9
+ ...rest,
10
+ };
11
+ };
12
+
13
+ const createRepository = (uid, db) => {
14
+ return {
15
+ findOne(params) {
16
+ return db.entityManager.findOne(uid, params);
17
+ },
18
+
19
+ findMany(params) {
20
+ return db.entityManager.findMany(uid, params);
21
+ },
22
+
23
+ findWithCount(params) {
24
+ return Promise.all([
25
+ db.entityManager.findMany(uid, params),
26
+ db.entityManager.count(uid, params),
27
+ ]);
28
+ },
29
+
30
+ async findPage(params) {
31
+ const { page, pageSize, ...rest } = withDefaultPagination(params);
32
+
33
+ const offset = Math.max(page - 1, 0) * pageSize;
34
+ const limit = pageSize;
35
+
36
+ const query = {
37
+ ...rest,
38
+ limit,
39
+ offset,
40
+ };
41
+
42
+ const [results, total] = await Promise.all([
43
+ db.entityManager.findMany(uid, query),
44
+ db.entityManager.count(uid, query),
45
+ ]);
46
+
47
+ return {
48
+ results,
49
+ pagination: {
50
+ page,
51
+ pageSize,
52
+ pageCount: Math.ceil(total / pageSize),
53
+ total,
54
+ },
55
+ };
56
+ },
57
+
58
+ create(params) {
59
+ return db.entityManager.create(uid, params);
60
+ },
61
+
62
+ createMany(params) {
63
+ return db.entityManager.createMany(uid, params);
64
+ },
65
+
66
+ update(params) {
67
+ return db.entityManager.update(uid, params);
68
+ },
69
+
70
+ updateMany(params) {
71
+ return db.entityManager.updateMany(uid, params);
72
+ },
73
+
74
+ delete(params) {
75
+ return db.entityManager.delete(uid, params);
76
+ },
77
+
78
+ deleteMany(params) {
79
+ return db.entityManager.deleteMany(uid, params);
80
+ },
81
+
82
+ count(params) {
83
+ return db.entityManager.count(uid, params);
84
+ },
85
+
86
+ attachRelations(id, data) {
87
+ return db.entityManager.attachRelations(uid, id, data);
88
+ },
89
+
90
+ updateRelations(id, data) {
91
+ return db.entityManager.updateRelations(uid, id, data);
92
+ },
93
+
94
+ deleteRelations(id) {
95
+ return db.entityManager.deleteRelations(uid, id);
96
+ },
97
+
98
+ populate(entity, populate) {
99
+ return db.entityManager.populate(uid, entity, populate);
100
+ },
101
+
102
+ load(entity, field, params) {
103
+ return db.entityManager.load(uid, entity, field, params);
104
+ },
105
+ };
106
+ };
107
+
108
+ module.exports = {
109
+ createRepository,
110
+ };
package/lib/errors.js ADDED
@@ -0,0 +1,14 @@
1
+ 'use strict';
2
+
3
+ class NotNullConstraint extends Error {
4
+ constructor({ column = '' } = {}) {
5
+ super();
6
+ this.name = 'NotNullConstraint';
7
+ this.message = `Not null constraint violation${column ? `on on column ${column}` : ''}.`;
8
+ this.stack = '';
9
+ }
10
+ }
11
+
12
+ module.exports = {
13
+ NotNullConstraint,
14
+ };
@@ -0,0 +1,9 @@
1
+ import { Attribute } from './schema';
2
+
3
+ interface Field {
4
+ config: {};
5
+ toDB(value: any): any;
6
+ fromDB(value: any): any;
7
+ }
8
+
9
+ export function createField(attribute: Attribute): Field;
package/lib/fields.js ADDED
@@ -0,0 +1,232 @@
1
+ 'use strict';
2
+
3
+ const _ = require('lodash/fp');
4
+ const dateFns = require('date-fns');
5
+
6
+ class Field {
7
+ constructor(config) {
8
+ this.config = config;
9
+ }
10
+
11
+ // TODO: impl
12
+ validate() {
13
+ // // use config validators directly
14
+ // if (this.config.validators) {
15
+ // this.config.validators.forEach(validator => {
16
+ // validator(value)
17
+ // })
18
+ // }
19
+ }
20
+
21
+ toDB(value) {
22
+ return value;
23
+ }
24
+
25
+ fromDB(value) {
26
+ return value;
27
+ }
28
+ }
29
+
30
+ class StringField extends Field {
31
+ toDB(value) {
32
+ return _.toString(value);
33
+ }
34
+
35
+ fromDB(value) {
36
+ return _.toString(value);
37
+ }
38
+ }
39
+
40
+ class JSONField extends Field {
41
+ toDB(value) {
42
+ return JSON.stringify(value);
43
+ }
44
+
45
+ fromDB(value) {
46
+ if (typeof value === 'string') return JSON.parse(value);
47
+ return value;
48
+ }
49
+ }
50
+
51
+ class BooleanField extends Field {
52
+ toDB(value) {
53
+ if (typeof value === 'boolean') return value;
54
+
55
+ if (['true', 't', '1', 1].includes(value)) {
56
+ return true;
57
+ }
58
+
59
+ if (['false', 'f', '0', 0].includes(value)) {
60
+ return false;
61
+ }
62
+
63
+ return Boolean(value);
64
+ }
65
+
66
+ fromDB(value) {
67
+ if (typeof value === 'boolean') {
68
+ return value;
69
+ }
70
+
71
+ const strVal = _.toString(value);
72
+
73
+ if (strVal === '1') {
74
+ return true;
75
+ } else if (strVal === '0') {
76
+ return false;
77
+ } else {
78
+ return null;
79
+ }
80
+ }
81
+ }
82
+
83
+ class NumberField extends Field {
84
+ toDB(value) {
85
+ const numberValue = _.toNumber(value);
86
+
87
+ if (Number.isNaN(numberValue)) {
88
+ throw new Error(`Expected a valid Number, got ${value}`);
89
+ }
90
+
91
+ return numberValue;
92
+ }
93
+
94
+ fromDB(value) {
95
+ return _.toNumber(value);
96
+ }
97
+ }
98
+
99
+ class BigIntegerField extends NumberField {
100
+ toDB(value) {
101
+ return _.toString(value);
102
+ }
103
+
104
+ fromDB(value) {
105
+ return _.toString(value);
106
+ }
107
+ }
108
+
109
+ const timeRegex = new RegExp('^(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(.[0-9]{1,3})?$');
110
+
111
+ const parseTime = value => {
112
+ if (dateFns.isDate(value)) return dateFns.format(value, 'HH:mm:ss.SSS');
113
+
114
+ if (typeof value !== 'string') {
115
+ throw new Error(`Expected a string, got a ${typeof value}`);
116
+ }
117
+ const result = value.match(timeRegex);
118
+
119
+ if (result === null) {
120
+ throw new Error('Invalid time format, expected HH:mm:ss.SSS');
121
+ }
122
+
123
+ const [, hours, minutes, seconds, fraction = '.000'] = result;
124
+ const fractionPart = _.padCharsEnd('0', 3, fraction.slice(1));
125
+
126
+ return `${hours}:${minutes}:${seconds}.${fractionPart}`;
127
+ };
128
+
129
+ const parseDate = value => {
130
+ if (dateFns.isDate(value)) return dateFns.format(value, 'yyyy-MM-dd');
131
+ try {
132
+ let date = dateFns.parseISO(value);
133
+
134
+ if (dateFns.isValid(date)) return dateFns.format(date, 'yyyy-MM-dd');
135
+
136
+ throw new Error(`Invalid format, expected an ISO compatible date`);
137
+ } catch (error) {
138
+ throw new Error(`Invalid format, expected an ISO compatible date`);
139
+ }
140
+ };
141
+
142
+ const parseDateTimeOrTimestamp = value => {
143
+ if (dateFns.isDate(value)) return value;
144
+ try {
145
+ const date = dateFns.parseISO(value);
146
+ if (dateFns.isValid(date)) return date;
147
+
148
+ const milliUnixDate = dateFns.parse(value, 'T', new Date());
149
+ if (dateFns.isValid(milliUnixDate)) return milliUnixDate;
150
+
151
+ throw new Error(`Invalid format, expected a timestamp or an ISO date`);
152
+ } catch (error) {
153
+ throw new Error(`Invalid format, expected a timestamp or an ISO date`);
154
+ }
155
+ };
156
+
157
+ class DateField extends Field {
158
+ toDB(value) {
159
+ return parseDate(value);
160
+ }
161
+
162
+ fromDB(value) {
163
+ const cast = new Date(value);
164
+ return dateFns.isValid(cast) ? dateFns.formatISO(cast, { representation: 'date' }) : null;
165
+ }
166
+ }
167
+ class DatetimeField extends Field {
168
+ toDB(value) {
169
+ return parseDateTimeOrTimestamp(value);
170
+ }
171
+
172
+ fromDB(value) {
173
+ const cast = new Date(value);
174
+ return dateFns.isValid(cast) ? cast.toISOString() : null;
175
+ }
176
+ }
177
+
178
+ class TimeField extends Field {
179
+ toDB(value) {
180
+ return parseTime(value);
181
+ }
182
+
183
+ fromDB(value) {
184
+ // make sure that's a string with valid format ?
185
+ return value;
186
+ }
187
+ }
188
+ class TimestampField extends Field {
189
+ toDB(value) {
190
+ return parseDateTimeOrTimestamp(value);
191
+ }
192
+
193
+ fromDB(value) {
194
+ const cast = new Date(value);
195
+ return dateFns.isValid(cast) ? dateFns.format(cast, 'T') : null;
196
+ }
197
+ }
198
+
199
+ const typeToFieldMap = {
200
+ increments: Field,
201
+ password: StringField,
202
+ email: StringField,
203
+ string: StringField,
204
+ uid: StringField,
205
+ richtext: StringField,
206
+ text: StringField,
207
+ enumeration: StringField,
208
+ json: JSONField,
209
+ biginteger: BigIntegerField,
210
+ integer: NumberField,
211
+ float: NumberField,
212
+ decimal: NumberField,
213
+ date: DateField,
214
+ time: TimeField,
215
+ datetime: DatetimeField,
216
+ timestamp: TimestampField,
217
+ boolean: BooleanField,
218
+ };
219
+
220
+ const createField = attribute => {
221
+ const { type } = attribute;
222
+
223
+ if (_.has(type, typeToFieldMap)) {
224
+ return new typeToFieldMap[type]({});
225
+ }
226
+
227
+ throw new Error(`Undefined field for type ${type}`);
228
+ };
229
+
230
+ module.exports = {
231
+ createField,
232
+ };
package/lib/index.d.ts ADDED
@@ -0,0 +1,146 @@
1
+ import { LifecycleProvider } from './lifecycles';
2
+ import { MigrationProvider } from './migrations';
3
+ import { SchemaProvideer } from './schema';
4
+
5
+ type BooleanWhere<T> = {
6
+ $and?: WhereParams<T>[];
7
+ $or?: WhereParams<T>[];
8
+ $not?: WhereParams<T>;
9
+ };
10
+
11
+ type WhereParams<T> = {
12
+ [K in keyof T]?: T[K];
13
+ } &
14
+ BooleanWhere<T>;
15
+
16
+ type Sortables<T> = {
17
+ // check sortable
18
+ [P in keyof T]: P;
19
+ }[keyof T];
20
+
21
+ type Direction = 'asc' | 'ASC' | 'DESC' | 'desc';
22
+
23
+ interface FindParams<T> {
24
+ select?: (keyof T)[];
25
+ // TODO: add nested operators & relations
26
+ where?: WhereParams<T>;
27
+ limit?: number;
28
+ offset?: number;
29
+ orderBy?: // TODO: add relations
30
+ | Sortables<T>
31
+ | Sortables<T>[]
32
+ | { [K in Sortables<T>]?: Direction }
33
+ | { [K in Sortables<T>]?: Direction }[];
34
+ // TODO: define nested obj
35
+ populate?: (keyof T)[];
36
+ }
37
+
38
+ interface CreateParams<T> {
39
+ select?: (keyof T)[];
40
+ populate?: (keyof T)[];
41
+ data: T[keyof T];
42
+ }
43
+
44
+ interface CreateManyParams<T> {
45
+ select?: (keyof T)[];
46
+ populate?: (keyof T)[];
47
+ data: T[keyof T][];
48
+ }
49
+
50
+ interface Pagination {
51
+ page: number;
52
+ pageSize: number;
53
+ pageCount: number;
54
+ total: number;
55
+ }
56
+
57
+ interface PopulateParams {}
58
+ interface EntityManager {
59
+ findOne<K extends keyof AllTypes>(uid: K, params: FindParams<AllTypes[K]>): Promise<any>;
60
+ findMany<K extends keyof AllTypes>(uid: K, params: FindParams<AllTypes[K]>): Promise<any[]>;
61
+
62
+ create<K extends keyof AllTypes>(uid: K, params: CreateParams<AllTypes[K]>): Promise<any>;
63
+ createMany<K extends keyof AllTypes>(
64
+ uid: K,
65
+ params: CreateManyParams<AllTypes[K]>
66
+ ): Promise<{ count: number }>;
67
+
68
+ update<K extends keyof AllTypes>(uid: K, params: any): Promise<any>;
69
+ updateMany<K extends keyof AllTypes>(uid: K, params: any): Promise<{ count: number }>;
70
+
71
+ delete<K extends keyof AllTypes>(uid: K, params: any): Promise<any>;
72
+ deleteMany<K extends keyof AllTypes>(uid: K, params: any): Promise<{ count: number }>;
73
+
74
+ count<K extends keyof AllTypes>(uid: K, params: any): Promise<number>;
75
+
76
+ attachRelations<K extends keyof AllTypes>(uid: K, id: ID, data: any): Promise<any>;
77
+ updateRelations<K extends keyof AllTypes>(uid: K, id: ID, data: any): Promise<any>;
78
+ deleteRelations<K extends keyof AllTypes>(uid: K, id: ID): Promise<any>;
79
+
80
+ populate<K extends keyof AllTypes, T extends AllTypes[K]>(
81
+ uid: K,
82
+ entity: T,
83
+ populate: PopulateParams
84
+ ): Promise<T>;
85
+
86
+ load<K extends keyof AllTypes, T extends AllTypes[K], SK extends keyof T>(
87
+ uid: K,
88
+ entity: T,
89
+ field: SK,
90
+ populate: PopulateParams
91
+ ): Promise<T[SK]>;
92
+ }
93
+
94
+ interface QueryFromContentType<T extends keyof AllTypes> {
95
+ findOne(params: FindParams<AllTypes[T]>): Promise<any>;
96
+ findMany(params: FindParams<AllTypes[T]>): Promise<any[]>;
97
+ findWithCount(params: FindParams<AllTypes[T]>): Promise<[any[], number]>;
98
+ findPage(params: FindParams<AllTypes[T]>): Promise<{ results: any[]; pagination: Pagination }>;
99
+
100
+ create(params: CreateParams<AllTypes[T]>): Promise<any>;
101
+ createMany(params: CreateManyParams<AllTypes[T]>): Promise<{ count: number }>;
102
+
103
+ update(params: any): Promise<any>;
104
+ updateMany(params: any): Promise<{ count: number }>;
105
+
106
+ delete(params: any): Promise<any>;
107
+ deleteMany(params: any): Promise<{ count: number }>;
108
+
109
+ count(params: any): Promise<number>;
110
+
111
+ attachRelations(id: ID, data: any): Promise<any>;
112
+ updateRelations(id: ID, data: any): Promise<any>;
113
+ deleteRelations(id: ID): Promise<any>;
114
+
115
+ populate<S extends AllTypes[T]>(entity: S, populate: PopulateParams): Promise<S>;
116
+
117
+ load<S extends AllTypes[T], K extends keyof S>(
118
+ entity: S,
119
+ field: K,
120
+ populate: PopulateParams
121
+ ): Promise<S[K]>;
122
+ }
123
+
124
+ interface ModelConfig {
125
+ tableName: string;
126
+ [k: string]: any;
127
+ }
128
+
129
+ interface ConnectionConfig {}
130
+
131
+ interface DatabaseConfig {
132
+ connection: ConnectionConfig;
133
+ models: ModelConfig[];
134
+ }
135
+ export interface Database {
136
+ schema: SchemaProvideer;
137
+ lifecycles: LifecycleProvider;
138
+ migrations: MigrationProvider;
139
+ entityManager: EntityManager;
140
+
141
+ query<T extends keyof AllTypes>(uid: T): QueryFromContentType<T>;
142
+ }
143
+ export class Database implements Database {
144
+ static transformContentTypes(contentTypes: any[]): ModelConfig[];
145
+ static init(config: DatabaseConfig): Promise<Database>;
146
+ }
package/lib/index.js ADDED
@@ -0,0 +1,60 @@
1
+ 'use strict';
2
+
3
+ const knex = require('knex');
4
+
5
+ const { getDialect } = require('./dialects');
6
+ const createSchemaProvider = require('./schema');
7
+ const createMetadata = require('./metadata');
8
+ const { createEntityManager } = require('./entity-manager');
9
+ const { createMigrationsProvider } = require('./migrations');
10
+ const { createLifecyclesProvider } = require('./lifecycles');
11
+
12
+ // TODO: move back into strapi
13
+ const { transformContentTypes } = require('./utils/content-types');
14
+
15
+ class Database {
16
+ constructor(config) {
17
+ this.metadata = createMetadata(config.models);
18
+
19
+ this.config = config;
20
+
21
+ this.dialect = getDialect(this);
22
+ this.dialect.configure();
23
+
24
+ this.connection = knex(this.config.connection);
25
+
26
+ this.dialect.initialize();
27
+
28
+ this.schema = createSchemaProvider(this);
29
+
30
+ this.migrations = createMigrationsProvider(this);
31
+ this.lifecycles = createLifecyclesProvider(this);
32
+
33
+ this.entityManager = createEntityManager(this);
34
+ }
35
+
36
+ query(uid) {
37
+ if (!this.metadata.has(uid)) {
38
+ throw new Error(`Model ${uid} not found`);
39
+ }
40
+
41
+ return this.entityManager.getRepository(uid);
42
+ }
43
+
44
+ queryBuilder(uid) {
45
+ return this.entityManager.createQueryBuilder(uid);
46
+ }
47
+
48
+ async destroy() {
49
+ await this.lifecycles.clear();
50
+ await this.connection.destroy();
51
+ }
52
+ }
53
+
54
+ // TODO: move into strapi
55
+ Database.transformContentTypes = transformContentTypes;
56
+ Database.init = async config => new Database(config);
57
+
58
+ module.exports = {
59
+ Database,
60
+ };
@@ -0,0 +1,50 @@
1
+ import { Database } from '../';
2
+ import { Model } from '../schema';
3
+ import { Subscriber } from './subscribers';
4
+
5
+ export type Action =
6
+ | 'beforeCreate'
7
+ | 'afterCreate'
8
+ | 'beforeFindOne'
9
+ | 'afterFindOne'
10
+ | 'beforeFindMany'
11
+ | 'afterFindMany'
12
+ | 'beforeCount'
13
+ | 'afterCount'
14
+ | 'beforeCreateMany'
15
+ | 'afterCreateMany'
16
+ | 'beforeUpdate'
17
+ | 'afterUpdate'
18
+ | 'beforeUpdateMany'
19
+ | 'afterUpdateMany'
20
+ | 'beforeDelete'
21
+ | 'afterDelete'
22
+ | 'beforeDeleteMany'
23
+ | 'afterDeleteMany';
24
+
25
+ export interface Params {
26
+ select?: any;
27
+ where?: any;
28
+ _q?: any;
29
+ orderBy?: any;
30
+ groupBy?: any;
31
+ offset?: any;
32
+ limit?: any;
33
+ populate?: any;
34
+ data?: any;
35
+ }
36
+
37
+ export interface Event {
38
+ action: Action;
39
+ model: Model;
40
+ params: Params;
41
+ }
42
+
43
+ export interface LifecycleProvider {
44
+ subscribe(subscriber: Subscriber): () => void;
45
+ clear(): void;
46
+ run(action: Action, uid: string, properties: any): Promise<void>;
47
+ createEvent(action: Action, uid: string, properties: any): Event;
48
+ }
49
+
50
+ export function createLifecyclesProvider(db: Database): LifecycleProvider;
@@ -0,0 +1,66 @@
1
+ 'use strict';
2
+
3
+ const assert = require('assert').strict;
4
+
5
+ const timestampsLifecyclesSubscriber = require('./subscribers/timestamps');
6
+ const modelLifecyclesSubscriber = require('./subscribers/models-lifecycles');
7
+
8
+ const isValidSubscriber = subscriber => {
9
+ return (
10
+ typeof subscriber === 'function' || (typeof subscriber === 'object' && subscriber !== null)
11
+ );
12
+ };
13
+
14
+ /**
15
+ * @type {import('.').createLifecyclesProvider}
16
+ */
17
+ const createLifecyclesProvider = db => {
18
+ let subscribers = [timestampsLifecyclesSubscriber, modelLifecyclesSubscriber];
19
+
20
+ return {
21
+ subscribe(subscriber) {
22
+ assert(isValidSubscriber(subscriber), 'Invalid subscriber. Expected function or object');
23
+
24
+ subscribers.push(subscriber);
25
+
26
+ return () => subscribers.splice(subscribers.indexOf(subscriber), 1);
27
+ },
28
+
29
+ clear() {
30
+ subscribers = [];
31
+ },
32
+
33
+ createEvent(action, uid, properties) {
34
+ const model = db.metadata.get(uid);
35
+
36
+ return {
37
+ action,
38
+ model,
39
+ ...properties,
40
+ };
41
+ },
42
+
43
+ async run(action, uid, properties) {
44
+ for (const subscriber of subscribers) {
45
+ if (typeof subscriber === 'function') {
46
+ const event = this.createEvent(action, uid, properties);
47
+ await subscriber(event);
48
+ continue;
49
+ }
50
+
51
+ const hasAction = action in subscriber;
52
+ const hasModel = !subscriber.models || subscriber.models.includes(uid);
53
+
54
+ if (hasAction && hasModel) {
55
+ const event = this.createEvent(action, uid, properties);
56
+
57
+ await subscriber[action](event);
58
+ }
59
+ }
60
+ },
61
+ };
62
+ };
63
+
64
+ module.exports = {
65
+ createLifecyclesProvider,
66
+ };
@@ -0,0 +1,9 @@
1
+ import { Event, Action } from '../';
2
+
3
+ type SubscriberFn = (event: Event) => Promise<void> | void;
4
+
5
+ type SubscriberMap = {
6
+ [k in Action]: SubscriberFn;
7
+ };
8
+
9
+ export type Subscriber = SubscriberFn | SubscriberMap;