@codefresh-io/eventbus 2.4.0 → 3.0.0-alpha.1

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;
@@ -31,95 +41,92 @@ class Store extends EventEmitter {
31
41
  ? process.env.POSTGRES_SSL_ENABLE === 'true'
32
42
  : options.ssl;
33
43
  this.disablePostgresForEventbus = process.env.DISABLE_POSTGRES_FOR_EVENTBUS === 'true';
34
- this.readyPromise = new Promise((resolve, reject) => {
35
- this.readyDeferred = {
36
- resolve: () => {
37
- resolve();
38
- },
39
- reject: () => {
40
- reject();
41
- }
42
- };
43
- });
44
+ /** @type {PromiseWithResolvers<void>} */
45
+ this.readyDeferred = Promise.withResolvers();
46
+ /** @type {Promise<void>} */
47
+ this.readyPromise = this.readyDeferred.promise;
44
48
  }
45
49
 
46
50
  async quit() {
47
- if (this.disablePostgresForEventbus) {
48
- return;
49
- }
50
- 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}, waiting: ${this.pool.waitingCount}`);
51
55
  await this.pool.end();
52
- debug(`Pool client state after closing. total: ${this.pool.totalCount}. idle: ${this.pool.idleCount}. active: ${this.pool.activeCount}`);
53
- debug('store ');
56
+ this._logger.debug(`Pool client state after closing: total: ${this.pool.totalCount}, idle: ${this.pool.idleCount}, waiting: ${this.pool.waitingCount}`);
57
+ this._logger.info(`Store closed for microservice "${this.microServiceName}"`);
54
58
  }
55
59
 
56
- init() {
60
+ async init() {
57
61
  if (this.disablePostgresForEventbus) {
58
- debug(`Store is disabled. Skipping initialization of it`);
62
+ this._logger.info(`Store is disabled. Skipping initialization`);
59
63
  return;
60
64
  }
61
- const config = {
62
- user: this.user,
63
- database: this.database,
64
- password: this.password,
65
- host: this.host,
66
- port: this.port,
67
- max: 10,
68
- idleTimeoutMillis: 30000, // how long a client is allowed to remain idle before being closed
69
- ssl: this.ssl,
70
- };
71
- this.pool = new pg.Pool(config);
72
65
 
73
- this.pool.on('error', (err) => {
74
- this.emit('error', new CFError({
75
- cause: err,
76
- message: 'postgres pool error'
77
- }));
78
- });
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
+ });
79
78
 
80
- this._assertSystemTables()
81
- .then(() => {
82
- debug(`ready`);
83
- this.emit('ready');
84
- })
85
- .catch((err) => {
79
+ this.pool.on('error', (err) => {
86
80
  this.emit('error', new CFError({
87
81
  cause: err,
88
- message: `Initialization failed`
82
+ message: 'postgres pool error'
89
83
  }));
90
- debug(`initialization failed trying again in ${INIT_RETRY_INTERVAL} seconds`);
91
- setTimeout(() => {
92
- this.init();
93
- }, INIT_RETRY_INTERVAL * 1000);
94
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
+ }
95
100
  }
96
101
 
97
102
  async _assertSystemTables() {
98
- if (process.env.EVENTBUS_IGNORE_CREATE_SCHEMA){
99
- return Promise.resolve();
100
- }
101
- await this.pool.query(tables);
102
- 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');
103
108
  }
104
109
 
105
110
  /**
106
111
  * extends the pg cursor with an easy api for the client
107
- * @param cursor
108
- * @param queryCondition
112
+ * @param {Cursor} cursor
113
+ * @param {pg.PoolClient} client
114
+ * @param {string} queryCondition
115
+ * @return {void}
109
116
  * @private
110
117
  */
111
118
  _extendPgCursor(cursor, client, queryCondition) {
112
- cursor.read = Promise.promisify(cursor.read.bind(cursor));
119
+ cursor.read = promisify(cursor.read.bind(cursor));
113
120
 
114
121
  cursor.count = async () => {
115
122
  const countResults = await this.pool.query(`SELECT COUNT(*) FROM ${SCHEMA} ${queryCondition}`);
116
- const totalRows = Number(countResults.rows[0].count);
123
+ const totalRows = Number(countResults.rows[0].count);
117
124
  return totalRows;
118
125
  };
119
126
 
120
127
  cursor.next = async () => {
121
128
  const rowResult = await cursor.read(1);
122
- const row = rowResult[0];
129
+ const row = rowResult[0];
123
130
  return row;
124
131
  };
125
132
 
@@ -137,28 +144,27 @@ class Store extends EventEmitter {
137
144
  }
138
145
 
139
146
  async save(eventName, event) {
140
- if (this.disablePostgresForEventbus) {
141
- return;
142
- }
143
- let data = [
147
+ if (this.disablePostgresForEventbus) return;
148
+
149
+ const data = [
144
150
  { field: 'event', value: eventName },
145
- { field: 'props', value: _.get(event, 'props', {}) },
146
- { field: 'aggregate_id', value: _.get(event, 'aggregateId', '').toString() },
147
- { field: 'timestamp', value: _.get(event, 'timestamp', new Date()) },
148
- { 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 },
149
155
  ];
150
156
 
151
157
  if (event.accountId) {
152
- data.push({ field: 'account_id', value: _.get(event, 'accountId', '').toString() });
158
+ data.push({ field: 'account_id', value: event.accountId.toString?.() });
153
159
  }
154
160
 
155
161
  if (event.userId) {
156
- data.push({ field: 'user_id', value: _.get(event, 'userId', '').toString() });
162
+ data.push({ field: 'user_id', value: event.userId.toString?.() });
157
163
  }
158
164
 
159
165
  let fieldString = '';
160
166
  let valuesString = '';
161
- let dataArr = [];
167
+ const dataArr = [];
162
168
  data.forEach((d, index) => {
163
169
  if (!index) {
164
170
  fieldString += `${d.field}`;
@@ -176,21 +182,17 @@ class Store extends EventEmitter {
176
182
 
177
183
  async getAllAggregateEvents(aggregateId, fromIndex) {
178
184
  if (this.disablePostgresForEventbus) {
179
- debug('getAllAggregateEvents: return _emptyPgCursor');
185
+ this._logger.debug('Store is disabled for Eventbus. Returning empty cursor');
180
186
  return this._emptyPgCursor();
181
187
  }
188
+
182
189
  try {
183
190
  const queryCondition = `WHERE id >= ${fromIndex} AND aggregate_id = '${aggregateId}'`;
184
-
185
191
  const client = await this.pool.connect();
186
-
187
192
  const cursor = await client.query(new Cursor(`SELECT * FROM ${SCHEMA} ${queryCondition} ORDER BY timestamp`));
188
-
189
193
  this._extendPgCursor(cursor, client, queryCondition);
190
-
191
194
  return cursor;
192
- }
193
- catch (err) {
195
+ } catch (err) {
194
196
  throw new CFError({
195
197
  cause: err,
196
198
  message: `Failed to perform getAllAggregateEvents with aggregateId: ${aggregateId}`
@@ -200,20 +202,16 @@ class Store extends EventEmitter {
200
202
 
201
203
  async getAllEventsByName(name, fromIndex) {
202
204
  if (this.disablePostgresForEventbus) {
203
- debug('getAllEventsByName: return _emptyPgCursor');
205
+ this._logger.debug('Store is disabled for Eventbus. Returning empty cursor');
204
206
  return this._emptyPgCursor();
205
207
  }
208
+
206
209
  try {
207
210
  const queryCondition = `WHERE id >= ${fromIndex} AND event LIKE '${name}%'`;
208
-
209
211
  const client = await this.pool.connect();
210
-
211
212
  const cursor = await client.query(new Cursor(`SELECT * FROM ${SCHEMA} ${queryCondition} ORDER BY timestamp`));
212
-
213
213
  this._extendPgCursor(cursor, client, queryCondition);
214
-
215
214
  await cursor.count();
216
-
217
215
  return cursor;
218
216
  }
219
217
  catch (err) {
@@ -225,7 +223,4 @@ class Store extends EventEmitter {
225
223
  }
226
224
  }
227
225
 
228
-
229
-
230
-
231
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.4.0",
3
+ "version": "3.0.0-alpha.1",
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