@codefresh-io/eventbus 2.3.0 → 3.0.0-alpha.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/lib/Store.js CHANGED
@@ -1,19 +1,29 @@
1
1
  "use strict";
2
2
 
3
- const EventEmitter = require('events');
4
- const Promise = require('bluebird');
5
- const debug = require('debug')('eventbus:store');
6
- const pg = require('pg');
7
- const _ = require('lodash');
8
- const Cursor = require('pg-cursor');
9
- const CFError = require('cf-errors');
10
- const tables = require('./tables');
11
-
12
- const INIT_RETRY_INTERVAL = 5; // in seconds
13
- const SCHEMA = 'eventstore.events';
3
+ const { promisify } = require('node:util');
4
+ const EventEmitter = require('node:events');
5
+ const CFError = require('cf-errors');
6
+ const pg = require('pg');
7
+ const Cursor = require('pg-cursor');
8
+ const { logger } = require('./logger');
9
+ const tables = require('./tables');
10
+
11
+ const INIT_RETRY_INTERVAL_SEC = 5;
12
+ const SCHEMA = 'eventstore.events';
14
13
 
15
14
  class Store extends EventEmitter {
15
+ _logger = logger.child(this.constructor.name);
16
16
 
17
+ /**
18
+ * @param {string} microServiceName
19
+ * @param {Object} options
20
+ * @param {string} [options.host]
21
+ * @param {string} [options.database]
22
+ * @param {string} [options.user]
23
+ * @param {string} [options.password]
24
+ * @param {number} [options.port]
25
+ * @param {boolean} [options.ssl]
26
+ */
17
27
  constructor(microServiceName, options) {
18
28
  super();
19
29
  this.microServiceName = microServiceName;
@@ -30,88 +40,93 @@ class Store extends EventEmitter {
30
40
  this.ssl = 'POSTGRES_SSL_ENABLE' in process.env
31
41
  ? process.env.POSTGRES_SSL_ENABLE === 'true'
32
42
  : options.ssl;
33
- this.readyPromise = new Promise((resolve, reject) => {
34
- this.readyDeferred = {
35
- resolve: () => {
36
- resolve();
37
- },
38
- reject: () => {
39
- reject();
40
- }
41
- };
42
- });
43
+ this.disablePostgresForEventbus = process.env.DISABLE_POSTGRES_FOR_EVENTBUS === 'true';
44
+ /** @type {PromiseWithResolvers<void>} */
45
+ this.readyDeferred = Promise.withResolvers();
46
+ /** @type {Promise<void>} */
47
+ this.readyPromise = this.readyDeferred.promise;
43
48
  }
44
49
 
45
50
  async quit() {
46
- debug(`Pool client state before closing. total: ${this.pool.totalCount}. idle: ${this.pool.idleCount}. active: ${this.pool.activeCount}`);
51
+ this._logger.info(`Closing store for microservice "${this.microServiceName}"`);
52
+ if (this.disablePostgresForEventbus || !this.pool) return;
53
+
54
+ this._logger.debug(`Pool client state before closing: total: ${this.pool.totalCount}, idle: ${this.pool.idleCount}, active: ${this.pool.activeCount}`);
47
55
  await this.pool.end();
48
- debug(`Pool client state after closing. total: ${this.pool.totalCount}. idle: ${this.pool.idleCount}. active: ${this.pool.activeCount}`);
49
- debug('store ');
56
+ this._logger.debug(`Pool client state after closing: total: ${this.pool.totalCount}, idle: ${this.pool.idleCount}, active: ${this.pool.activeCount}`);
57
+ this._logger.info(`Store closed for microservice "${this.microServiceName}"`);
50
58
  }
51
59
 
52
- init() {
53
- const config = {
54
- user: this.user,
55
- database: this.database,
56
- password: this.password,
57
- host: this.host,
58
- port: this.port,
59
- max: 10,
60
- idleTimeoutMillis: 30000, // how long a client is allowed to remain idle before being closed
61
- ssl: this.ssl,
62
- };
63
- this.pool = new pg.Pool(config);
60
+ async init() {
61
+ if (this.disablePostgresForEventbus) {
62
+ this._logger.info(`Store is disabled. Skipping initialization`);
63
+ return;
64
+ }
64
65
 
65
- this.pool.on('error', (err) => {
66
- this.emit('error', new CFError({
67
- cause: err,
68
- message: 'postgres pool error'
69
- }));
70
- });
66
+ try {
67
+ this._logger.info(`Initializing store for microservice "${this.microServiceName}"`);
68
+ this.pool = new pg.Pool({
69
+ user: this.user,
70
+ database: this.database,
71
+ password: this.password,
72
+ host: this.host,
73
+ port: this.port,
74
+ max: 10,
75
+ idleTimeoutMillis: 30000, // how long a client is allowed to remain idle before being closed
76
+ ssl: this.ssl,
77
+ });
71
78
 
72
- this._assertSystemTables()
73
- .then(() => {
74
- debug(`ready`);
75
- this.emit('ready');
76
- })
77
- .catch((err) => {
79
+ this.pool.on('error', (err) => {
78
80
  this.emit('error', new CFError({
79
81
  cause: err,
80
- message: `Initialization failed`
82
+ message: 'postgres pool error'
81
83
  }));
82
- debug(`initialization failed trying again in ${INIT_RETRY_INTERVAL} seconds`);
83
- setTimeout(() => {
84
- this.init();
85
- }, INIT_RETRY_INTERVAL * 1000);
86
84
  });
85
+
86
+ await this._assertSystemTables();
87
+ this._logger.info(`Store initialized for microservice "${this.microServiceName}"`);
88
+ this.emit('ready');
89
+ } catch (exception) {
90
+ this.emit('error', new CFError({
91
+ cause: exception,
92
+ message: `Initialization failed`
93
+ }));
94
+ this._logger.error(`Store initialization failed for microservice "${this.microServiceName}"`, exception);
95
+ this._logger.info(`Retrying store initialization in ${INIT_RETRY_INTERVAL_SEC} seconds...`);
96
+ setTimeout(() => {
97
+ this.init();
98
+ }, INIT_RETRY_INTERVAL_SEC * 1000);
99
+ }
87
100
  }
88
101
 
89
102
  async _assertSystemTables() {
90
- if (process.env.EVENTBUS_IGNORE_CREATE_SCHEMA){
91
- return Promise.resolve();
92
- }
93
- await this.pool.query(tables);
94
- debug('system tables asserted');
103
+ if (process.env.EVENTBUS_IGNORE_CREATE_SCHEMA) return;
104
+
105
+ this._logger.info('Asserting system tables');
106
+ await this.pool?.query(tables);
107
+ this._logger.info('System tables asserted');
95
108
  }
96
109
 
97
110
  /**
98
111
  * extends the pg cursor with an easy api for the client
99
- * @param cursor
100
- * @param queryCondition
112
+ * @param {Cursor} cursor
113
+ * @param {pg.PoolClient} client
114
+ * @param {string} queryCondition
115
+ * @return {void}
101
116
  * @private
102
117
  */
103
118
  _extendPgCursor(cursor, client, queryCondition) {
104
- cursor.read = Promise.promisify(cursor.read.bind(cursor));
119
+ cursor.read = promisify(cursor.read.bind(cursor));
105
120
 
106
121
  cursor.count = async () => {
107
122
  const countResults = await this.pool.query(`SELECT COUNT(*) FROM ${SCHEMA} ${queryCondition}`);
108
- const totalRows = Number(countResults.rows[0].count);
123
+ const totalRows = Number(countResults.rows[0].count);
109
124
  return totalRows;
110
125
  };
111
126
 
112
127
  cursor.next = async () => {
113
128
  const rowResult = await cursor.read(1);
114
- const row = rowResult[0];
129
+ const row = rowResult[0];
115
130
  return row;
116
131
  };
117
132
 
@@ -120,26 +135,36 @@ class Store extends EventEmitter {
120
135
  }
121
136
  }
122
137
 
138
+ _emptyPgCursor() {
139
+ return {
140
+ count: () => 0,
141
+ next: () => false,
142
+ close: () => {},
143
+ };
144
+ }
145
+
123
146
  async save(eventName, event) {
124
- let data = [
147
+ if (this.disablePostgresForEventbus) return;
148
+
149
+ const data = [
125
150
  { field: 'event', value: eventName },
126
- { field: 'props', value: _.get(event, 'props', {}) },
127
- { field: 'aggregate_id', value: _.get(event, 'aggregateId', '').toString() },
128
- { field: 'timestamp', value: _.get(event, 'timestamp', new Date()) },
129
- { field: 'publisher', value: _.get(event, 'publisher', this.microServiceName) }
151
+ { field: 'props', value: event?.props ?? {} },
152
+ { field: 'aggregate_id', value: event?.aggregateId?.toString?.() ?? '' },
153
+ { field: 'timestamp', value: event?.timestamp ?? new Date() },
154
+ { field: 'publisher', value: event?.publisher ?? this.microServiceName },
130
155
  ];
131
156
 
132
157
  if (event.accountId) {
133
- data.push({ field: 'account_id', value: _.get(event, 'accountId', '').toString() });
158
+ data.push({ field: 'account_id', value: event.accountId.toString?.() });
134
159
  }
135
160
 
136
161
  if (event.userId) {
137
- data.push({ field: 'user_id', value: _.get(event, 'userId', '').toString() });
162
+ data.push({ field: 'user_id', value: event.userId.toString?.() });
138
163
  }
139
164
 
140
165
  let fieldString = '';
141
166
  let valuesString = '';
142
- let dataArr = [];
167
+ const dataArr = [];
143
168
  data.forEach((d, index) => {
144
169
  if (!index) {
145
170
  fieldString += `${d.field}`;
@@ -156,18 +181,18 @@ class Store extends EventEmitter {
156
181
  }
157
182
 
158
183
  async getAllAggregateEvents(aggregateId, fromIndex) {
184
+ if (this.disablePostgresForEventbus) {
185
+ this._logger.debug('Store is disabled for Eventbus. Returning empty cursor');
186
+ return this._emptyPgCursor();
187
+ }
188
+
159
189
  try {
160
190
  const queryCondition = `WHERE id >= ${fromIndex} AND aggregate_id = '${aggregateId}'`;
161
-
162
191
  const client = await this.pool.connect();
163
-
164
192
  const cursor = await client.query(new Cursor(`SELECT * FROM ${SCHEMA} ${queryCondition} ORDER BY timestamp`));
165
-
166
193
  this._extendPgCursor(cursor, client, queryCondition);
167
-
168
194
  return cursor;
169
- }
170
- catch (err) {
195
+ } catch (err) {
171
196
  throw new CFError({
172
197
  cause: err,
173
198
  message: `Failed to perform getAllAggregateEvents with aggregateId: ${aggregateId}`
@@ -176,17 +201,17 @@ class Store extends EventEmitter {
176
201
  }
177
202
 
178
203
  async getAllEventsByName(name, fromIndex) {
204
+ if (this.disablePostgresForEventbus) {
205
+ this._logger.debug('Store is disabled for Eventbus. Returning empty cursor');
206
+ return this._emptyPgCursor();
207
+ }
208
+
179
209
  try {
180
210
  const queryCondition = `WHERE id >= ${fromIndex} AND event LIKE '${name}%'`;
181
-
182
211
  const client = await this.pool.connect();
183
-
184
212
  const cursor = await client.query(new Cursor(`SELECT * FROM ${SCHEMA} ${queryCondition} ORDER BY timestamp`));
185
-
186
213
  this._extendPgCursor(cursor, client, queryCondition);
187
-
188
214
  await cursor.count();
189
-
190
215
  return cursor;
191
216
  }
192
217
  catch (err) {
@@ -198,7 +223,4 @@ class Store extends EventEmitter {
198
223
  }
199
224
  }
200
225
 
201
-
202
-
203
-
204
226
  module.exports = Store;
package/lib/index.js CHANGED
@@ -1,12 +1,10 @@
1
1
  'use strict';
2
2
 
3
- const EventEmitter = require('events');
4
- const Promise = require('bluebird');
5
- const debug = require('debug')('eventbus:main');
6
- const uuid = require('uuid');
7
- const CFError = require('cf-errors');
8
- const Bus = require('./Bus');
9
- const Store = require('./Store');
3
+ const EventEmitter = require('node:events');
4
+ const CFError = require('cf-errors');
5
+ const Bus = require('./Bus');
6
+ const Store = require('./Store');
7
+ const { logger } = require('./logger');
10
8
 
11
9
  //TODO
12
10
  // 1. queue published messages in case of failure in the connection
@@ -15,60 +13,89 @@ const Store = require('./Store');
15
13
  // 4. add ability to get all possible event types in the system
16
14
 
17
15
  class EventBus extends EventEmitter {
16
+ _logger = logger.child(this.constructor.name);
18
17
 
19
18
  constructor() {
20
19
  super();
21
20
  this.busReady = false;
22
21
  this.storeReady = false;
23
- this.readyPromise = new Promise((resolve, reject) => {
24
- this.readyDeferred = {
25
- resolve: () => {
26
- resolve();
27
- },
28
- reject: () => {
29
- reject();
30
- }
31
- };
32
- });
22
+ /** @type {PromiseWithResolvers<void>} */
23
+ this.readyDeferred = Promise.withResolvers();
24
+ this.readyPromise = this.readyDeferred.promise;
33
25
  this.on('error', () => {});
34
26
  }
35
27
 
36
- quit() {
37
- return Promise.resolve()
38
- .then(async () => {
39
- try {
40
- await Promise.all([this.bus.quit(), this.store.quit()]);
41
- this.emit('quit');
42
- } catch (err) {
43
- throw new CFError({
44
- cause: err,
45
- message: 'Failed during quit execution'
46
- });
28
+ /**
29
+ * Unsubscribes all consumers and forbids new consumers from being subscribed.
30
+ * This should be used when you want to gracefully terminate the connection and ensure that no new messages are consumed while terminating.
31
+ * @returns {Promise<void>}
32
+ */
33
+ async cancelAllConsumers() {
34
+ await this.bus?.cancelAllConsumers();
35
+ }
36
+
37
+ /**
38
+ * Waits for all consumers in progress to finish.
39
+ * This should be used before terminating the connection
40
+ * to ensure that all messages are processed before the connection is closed.
41
+ * @returns {Promise<void>}
42
+ */
43
+ async waitForConsumersToFinish() {
44
+ await this.bus?.waitForConsumersToFinish();
45
+ }
46
+
47
+ /**
48
+ * Terminates the bus connection gracefully by cancelling all consumers, waiting for all consumers in progress to finish, and then closing the connection.
49
+ * @param {object} [options]
50
+ * @param {boolean} [options.force] If `true`, closes connection immediately.
51
+ */
52
+ async quit(options) {
53
+ try {
54
+ const results = await Promise.allSettled([
55
+ this.bus?.quit(options),
56
+ this.store?.quit(),
57
+ ]);
58
+ const failures = results.reduce((acc, result) => {
59
+ if (result.status === 'rejected') {
60
+ acc.push(result.reason);
47
61
  }
62
+ return acc;
63
+ }, /** @type {any[]} */ ([]));
64
+ if (failures.length > 0) {
65
+ const error = new AggregateError(failures, 'One or more errors occurred during EventBus termination');
66
+ this._logger.error(error);
67
+ throw error;
68
+ }
69
+ this.emit('quit');
70
+ } catch (err) {
71
+ throw new CFError({
72
+ cause: err,
73
+ message: 'Failed during quit execution'
48
74
  });
75
+ }
49
76
  }
50
77
 
51
78
  init(options = {}) {
52
- this.microServiceName = options.microServiceName || options.serviceName || `eventBus_client_${uuid()}`;
79
+ this.microServiceName = options.microServiceName || options.serviceName || `eventBus_client_${globalThis.crypto.randomUUID()}`;
53
80
 
54
81
  this.bus = new Bus(this.microServiceName, options.bus);
55
82
  this.store = new Store(this.microServiceName, options.store);
56
83
 
57
84
  // wait for both the bus and the store to send their ready event
58
85
  this.bus.on('ready', () => {
59
- debug(`bus is ready. registered as: ${this.microServiceName}`);
86
+ this._logger.info(`Eventbus bus is ready. registered as: ${this.microServiceName}`);
60
87
  this.busReady = true;
61
88
  this._emitReady();
62
89
  });
63
90
 
64
91
  this.store.on('ready', () => {
65
- debug(`store is ready. registered as: ${this.microServiceName}`);
92
+ this._logger.info(`Eventbus store is ready. registered as: ${this.microServiceName}`);
66
93
  this.storeReady = true;
67
94
  this._emitReady();
68
95
  });
69
96
 
70
97
  this.bus.on('error', (err) => {
71
- debug(`bus error: ${err.toString()}`);
98
+ this._logger.error(`Eventbus bus error`, err);
72
99
  this.emit('error', new CFError({
73
100
  cause: err,
74
101
  message: 'eventbus bus error'
@@ -76,7 +103,7 @@ class EventBus extends EventEmitter {
76
103
  });
77
104
 
78
105
  this.store.on('error', (err) => {
79
- debug(`store error: ${err.toString()}`);
106
+ this._logger.error(`Eventbus store error`, err);
80
107
  this.emit('error', new CFError({
81
108
  cause: err,
82
109
  message: 'eventbus store error'
@@ -90,27 +117,20 @@ class EventBus extends EventEmitter {
90
117
  _emitReady() {
91
118
  if (this.busReady) {
92
119
  this.readyDeferred.resolve();
93
- debug('ready');
120
+ this._logger.info('Eventbus is ready');
94
121
  this.emit('ready');
95
122
  }
96
123
  }
97
124
 
98
- publish(eventName, msg) {
99
- return Promise.resolve()
100
- .then(async () => {
101
- await this.readyPromise;
102
- await this.bus.publish(eventName, msg);
103
- }).then(() => {
104
- this.store.save(eventName, msg);
105
- })
125
+ async publish(eventName, msg) {
126
+ await this.readyPromise;
127
+ await this.bus.publish(eventName, msg);
128
+ this.store.save(eventName, msg);
106
129
  }
107
130
 
108
- subscribe(eventName, handler, options) {
109
- return Promise.resolve()
110
- .then(async () => {
111
- await this.readyPromise;
112
- return await this.bus.subscribe(eventName, handler, options);
113
- });
131
+ async subscribe(eventName, handler, options) {
132
+ await this.readyPromise;
133
+ return this.bus.subscribe(eventName, handler, options);
114
134
  }
115
135
 
116
136
  /**
@@ -118,12 +138,9 @@ class EventBus extends EventEmitter {
118
138
  * @param aggregateId
119
139
  * @returns pg extended Cursor
120
140
  */
121
- getAllAggregateEvents(aggregateId, fromIndex) {
122
- return Promise.resolve()
123
- .then(async () => {
124
- await this.readyPromise;
125
- return await this.store.getAllAggregateEvents(aggregateId, fromIndex);
126
- });
141
+ async getAllAggregateEvents(aggregateId, fromIndex) {
142
+ await this.readyPromise;
143
+ return await this.store.getAllAggregateEvents(aggregateId, fromIndex);
127
144
  }
128
145
 
129
146
  /**
@@ -131,14 +148,10 @@ class EventBus extends EventEmitter {
131
148
  * @param name
132
149
  * @returns pg extended Cursor
133
150
  */
134
- getAllEventsByName(name, fromIndex) {
135
- return Promise.resolve()
136
- .then(async () => {
137
- await this.readyPromise;
138
- return await this.store.getAllEventsByName(name, fromIndex);
139
- });
151
+ async getAllEventsByName(name, fromIndex) {
152
+ await this.readyPromise;
153
+ return await this.store.getAllEventsByName(name, fromIndex);
140
154
  }
141
-
142
155
  }
143
156
 
144
157
  module.exports = new EventBus();
package/lib/logger.js ADDED
@@ -0,0 +1,5 @@
1
+ const { Logger } = require('@codefresh-io/cf-telemetry/logs');
2
+
3
+ const logger = new Logger('@codefresh-io/eventbus');
4
+
5
+ module.exports = { logger };
package/package.json CHANGED
@@ -1,10 +1,16 @@
1
1
  {
2
2
  "name": "@codefresh-io/eventbus",
3
- "version": "2.3.0",
3
+ "version": "3.0.0-alpha.0",
4
4
  "description": "",
5
5
  "main": "index.js",
6
+ "files": [
7
+ "lib/**/*",
8
+ "index.js"
9
+ ],
6
10
  "scripts": {
7
- "test": "gulp unit_test"
11
+ "test": "echo \"Not implemented\" && exit 0",
12
+ "lint": "echo \"Not implemented\" && exit 0",
13
+ "build": "echo \"Not required\" && exit 0"
8
14
  },
9
15
  "repository": {
10
16
  "type": "git",
@@ -16,37 +22,20 @@
16
22
  "url": "https://github.com/codefresh-io/cf-eventbus/issues"
17
23
  },
18
24
  "homepage": "https://github.com/codefresh-io/cf-eventbus#readme",
25
+ "engines": {
26
+ "node": ">=22 <=24"
27
+ },
19
28
  "dependencies": {
20
- "amqplib": "^0.10.7",
21
- "bluebird": "^3.5.0",
22
- "cf-errors": "^0.1.16",
23
- "debug": "2.6.9",
24
- "lodash": "^4.17.21",
25
- "pg": "^8.11.1",
26
- "pg-cursor": "^1.2.0",
27
- "uuid": "^3.0.1"
29
+ "amqplib": "^0.10.9",
30
+ "cf-errors": "^0.1.17",
31
+ "pg": "^8.19.0",
32
+ "pg-cursor": "^1.2.0"
28
33
  },
29
34
  "devDependencies": {
30
- "chai": "^3.0.0",
31
- "git-rev": "^0.2.1",
32
- "gulp": "^3.9.1",
33
- "gulp-coveralls": "^0.1.4",
34
- "gulp-env": "^0.2.0",
35
- "gulp-git": "^1.2.1",
36
- "gulp-ignore": "^1.2.1",
37
- "gulp-istanbul": "^0.10.4",
38
- "gulp-jshint": "^1.11.0",
39
- "gulp-logger": "0.0.2",
40
- "gulp-mocha": "^2.1.1",
41
- "gulp-nodemon": "^2.0.4",
42
- "gulp-rimraf": "^0.1.1",
43
- "gulp-run": "^1.6.7",
44
- "gulp-util": "^3.0.6",
45
- "isparta": "^4.0.0",
46
- "proxyquire": "^1.7.10",
47
- "run-sequence": "^1.1.0",
48
- "sinon": "^1.15.4",
49
- "sinon-chai": "^2.8.0"
35
+ "@codefresh-io/cf-telemetry": "^3.11.1"
36
+ },
37
+ "peerDependencies": {
38
+ "@codefresh-io/cf-telemetry": "^3.11.1"
50
39
  },
51
40
  "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
52
41
  }
package/.dockerignore DELETED
@@ -1,2 +0,0 @@
1
- node_modules
2
- logs/*.log
package/.eslintrc DELETED
@@ -1,71 +0,0 @@
1
- // Codefresh Code Style - eslint ruleset
2
- // Based on AirBnB.
3
- //
4
- // More details: https://codefresh-io.atlassian.net/wiki/display/COD/Code+Style+Guide
5
- {
6
- "env": {
7
- "node": true,
8
- "mocha": true
9
- },
10
-
11
- "plugins": [
12
- "chai-friendly"
13
- ],
14
-
15
- "extends": "airbnb",
16
- "parserOptions": {
17
- "ecmaVersion": 6,
18
- "sourceType": "script",
19
- "ecmaFeatures": {
20
- "jsx": true
21
- }
22
- },
23
-
24
- "rules": {
25
- "max-len": [1, 130],
26
- "indent": [
27
- "error",
28
- 4,
29
- {
30
- "SwitchCase": 1,
31
- "VariableDeclarator": 1
32
- }
33
- ],
34
- "func-names": [
35
- "error",
36
- "never"
37
- ],
38
- "quotes": [
39
- "error",
40
- "single",
41
- {
42
- "allowTemplateLiterals": true
43
- }
44
- ],
45
- "no-underscore-dangle": "off",
46
- "no-param-reassign": "off",
47
- "no-else-return": "off",
48
- "arrow-body-style": "off",
49
- "strict": [
50
- "error",
51
- "global"
52
- ],
53
- "no-multi-spaces": "off",
54
- "padded-blocks": "off",
55
- "import/no-extraneous-dependencies": [
56
- 2,
57
- {
58
- "devDependencies": true
59
- }
60
- ],
61
- "guard-for-in": "error",
62
- "no-console": "off",
63
- "comma-dangle": ["error", "only-multiline"],
64
- "quote-props": ["error", "consistent"],
65
- "class-methods-use-this": "off",
66
- "no-use-before-define": ["error", { "functions": false, "classes": true }],
67
- "no-restricted-syntax": ["error", "WithStatement", "ForInStatement", "LabeledStatement"],
68
- "no-unused-expressions": 0,
69
- "chai-friendly/no-unused-expressions": 2
70
- }
71
- }
package/Dockerfile DELETED
@@ -1,15 +0,0 @@
1
- FROM node:6.9.2
2
-
3
- RUN npm install --global yarn
4
-
5
- COPY ./package.json /app/
6
-
7
- RUN cd /app && yarn install
8
-
9
- COPY . /app/
10
-
11
- WORKDIR /app
12
-
13
- CMD [ "yarn", "start" ]
14
-
15
- EXPOSE 3000