@quantform/core 0.3.242 → 0.3.248

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 (42) hide show
  1. package/dist/adapter/adapter-aggregate.d.ts +11 -5
  2. package/dist/adapter/adapter-aggregate.js +38 -8
  3. package/dist/adapter/adapter-aggregate.js.map +1 -1
  4. package/dist/adapter/backtester/backtester-adapter.d.ts +1 -2
  5. package/dist/adapter/backtester/backtester-adapter.js.map +1 -1
  6. package/dist/adapter/backtester/backtester-streamer.d.ts +2 -1
  7. package/dist/adapter/backtester/backtester-streamer.js +8 -7
  8. package/dist/adapter/backtester/backtester-streamer.js.map +1 -1
  9. package/dist/adapter/backtester/backtester-streamer.spec.js +9 -10
  10. package/dist/adapter/backtester/backtester-streamer.spec.js.map +1 -1
  11. package/dist/bin.d.ts +3 -4
  12. package/dist/bin.js +6 -6
  13. package/dist/bin.js.map +1 -1
  14. package/dist/ipc.d.ts +6 -12
  15. package/dist/ipc.js +64 -54
  16. package/dist/ipc.js.map +1 -1
  17. package/dist/ipc.spec.js +9 -9
  18. package/dist/ipc.spec.js.map +1 -1
  19. package/dist/session/session-descriptor.d.ts +6 -3
  20. package/dist/session/session.d.ts +2 -2
  21. package/dist/session/session.js +9 -25
  22. package/dist/session/session.js.map +1 -1
  23. package/dist/session/session.spec.js +1 -5
  24. package/dist/session/session.spec.js.map +1 -1
  25. package/dist/store/event/store-order.event.js +0 -8
  26. package/dist/store/event/store-order.event.js.map +1 -1
  27. package/dist/tests/backtester-adapter.spec.js +7 -8
  28. package/dist/tests/backtester-adapter.spec.js.map +1 -1
  29. package/dist/tsconfig.tsbuildinfo +1 -1
  30. package/package.json +2 -1
  31. package/src/adapter/adapter-aggregate.ts +110 -13
  32. package/src/adapter/backtester/backtester-adapter.ts +2 -3
  33. package/src/adapter/backtester/backtester-streamer.spec.ts +10 -6
  34. package/src/adapter/backtester/backtester-streamer.ts +8 -7
  35. package/src/bin.ts +21 -11
  36. package/src/ipc.spec.ts +9 -8
  37. package/src/ipc.ts +90 -89
  38. package/src/session/session-descriptor.ts +7 -4
  39. package/src/session/session.spec.ts +1 -5
  40. package/src/session/session.ts +10 -49
  41. package/src/store/event/store-order.event.ts +0 -12
  42. package/src/tests/backtester-adapter.spec.ts +11 -7
@@ -4,8 +4,15 @@ import { Logger } from '../shared';
4
4
  import {
5
5
  AdapterAccountCommand,
6
6
  AdapterAwakeCommand,
7
- AdapterDisposeCommand
7
+ AdapterDisposeCommand,
8
+ AdapterFeedCommand,
9
+ AdapterHistoryQuery,
10
+ AdapterOrderCancelCommand,
11
+ AdapterOrderOpenCommand,
12
+ AdapterSubscribeCommand
8
13
  } from './adapter.event';
14
+ import { InstrumentSelector, Order, Candle } from '../domain';
15
+ import { Feed } from './../storage';
9
16
 
10
17
  /**
11
18
  * Manages instances of all adapters provided in session descriptor.
@@ -14,21 +21,26 @@ import {
14
21
  export class AdapterAggregate {
15
22
  private readonly adapter: Record<string, Adapter> = {};
16
23
 
17
- constructor(private readonly store: Store, adapters: Adapter[]) {
24
+ constructor(adapters: Adapter[], private readonly store: Store) {
18
25
  adapters.forEach(it => (this.adapter[it.name] = it));
19
26
  }
20
27
 
28
+ /**
29
+ * Returns adapter by name.
30
+ * @param adapterName adapter name.
31
+ * @returns
32
+ */
33
+ get(adapterName: string): Adapter {
34
+ return this.adapter[adapterName];
35
+ }
36
+
21
37
  /**
22
38
  * Sets up all adapters.
23
- * @param usePrivateScope use private api (api keys needed).
24
39
  */
25
- async awake(usePrivateScope = true): Promise<void> {
40
+ async awake(): Promise<void> {
26
41
  for (const adapter in this.adapter) {
27
42
  await this.dispatch(adapter, new AdapterAwakeCommand());
28
-
29
- if (usePrivateScope) {
30
- await this.dispatch(adapter, new AdapterAccountCommand());
31
- }
43
+ await this.dispatch(adapter, new AdapterAccountCommand());
32
44
  }
33
45
  }
34
46
 
@@ -41,17 +53,102 @@ export class AdapterAggregate {
41
53
  }
42
54
  }
43
55
 
56
+ /**
57
+ * Subscribe to collection of instruments.
58
+ * @param selectors
59
+ */
60
+ async subscribe(selectors: InstrumentSelector[]): Promise<void> {
61
+ const grouped = selectors
62
+ .filter(it => it != null)
63
+ .reduce((aggregate, it) => {
64
+ const adapter = it.base.adapter;
65
+
66
+ if (aggregate[adapter]) {
67
+ aggregate[adapter].push(it);
68
+ } else {
69
+ aggregate[adapter] = [it];
70
+ }
71
+
72
+ return aggregate;
73
+ }, {});
74
+
75
+ for (const adapterName in grouped) {
76
+ await this.dispatch(adapterName, new AdapterSubscribeCommand(grouped[adapterName]));
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Opens new order.
82
+ * @param order an order to open.
83
+ */
84
+ open(order: Order): Promise<void> {
85
+ return this.dispatch<AdapterOrderOpenCommand, void>(
86
+ order.instrument.base.adapter,
87
+ new AdapterOrderOpenCommand(order)
88
+ );
89
+ }
90
+
91
+ /**
92
+ * Cancels specific order.
93
+ */
94
+ cancel(order: Order): Promise<void> {
95
+ return this.dispatch(
96
+ order.instrument.base.adapter,
97
+ new AdapterOrderCancelCommand(order)
98
+ );
99
+ }
100
+
101
+ /**
102
+ *
103
+ * @param selector Returns collection of candles for specific history.
104
+ * @param timeframe
105
+ * @param length
106
+ * @returns
107
+ */
108
+ history(
109
+ selector: InstrumentSelector,
110
+ timeframe: number,
111
+ length: number
112
+ ): Promise<Candle[]> {
113
+ return this.dispatch<AdapterHistoryQuery, Candle[]>(
114
+ selector.base.adapter,
115
+ new AdapterHistoryQuery(selector, timeframe, length)
116
+ );
117
+ }
118
+
119
+ /**
120
+ * Feeds a storage with historical instrument data.
121
+ * @param selector
122
+ * @param from
123
+ * @param to
124
+ * @param destination
125
+ * @param callback
126
+ * @returns
127
+ */
128
+ feed(
129
+ selector: InstrumentSelector,
130
+ from: number,
131
+ to: number,
132
+ destination: Feed,
133
+ callback: (timestamp: number) => void
134
+ ): Promise<void> {
135
+ return this.dispatch(
136
+ selector.base.adapter,
137
+ new AdapterFeedCommand(selector, from, to, destination, callback)
138
+ );
139
+ }
140
+
44
141
  /**
45
142
  * Routes and executes command to a specific adapter.
46
143
  * @param adapterName name of adapter
47
- * @param event
144
+ * @param command
48
145
  * @returns
49
146
  */
50
- dispatch<TEvent extends { type: string }, TResponse>(
147
+ private dispatch<TCommand extends { type: string }, TResponse>(
51
148
  adapterName: string,
52
- event: TEvent
149
+ command: TCommand
53
150
  ): Promise<TResponse> {
54
- const adapter = this.adapter[adapterName];
151
+ const adapter = this.get(adapterName);
55
152
 
56
153
  if (!adapter) {
57
154
  throw new Error(
@@ -60,7 +157,7 @@ export class AdapterAggregate {
60
157
  }
61
158
 
62
159
  try {
63
- return adapter.dispatch(event, new AdapterContext(adapter, this.store));
160
+ return adapter.dispatch(command, new AdapterContext(adapter, this.store));
64
161
  } catch (e) {
65
162
  Logger.error(e);
66
163
  }
@@ -1,15 +1,14 @@
1
1
  import { Adapter, AdapterContext } from '..';
2
- import { BacktesterListener, BacktesterStreamer } from './backtester-streamer';
2
+ import { BacktesterStreamer } from './backtester-streamer';
3
3
  import { PaperAdapter, PaperOptions } from '../paper';
4
4
  import { handler } from '../../shared/topic';
5
- import { Logger, timestamp } from '../../shared';
5
+ import { timestamp } from '../../shared';
6
6
  import { AdapterSubscribeCommand, AdapterHistoryQuery } from '../adapter.event';
7
7
  import { InstrumentSubscriptionEvent } from '../../store/event';
8
8
 
9
9
  export class BacktesterOptions extends PaperOptions {
10
10
  from: timestamp;
11
11
  to: timestamp;
12
- listener?: BacktesterListener;
13
12
  }
14
13
 
15
14
  export class BacktesterAdapter extends Adapter {
@@ -18,11 +18,15 @@ describe('backtester streamer tests', () => {
18
18
  store.snapshot.universe.instrument[instrument.toString()] = instrument;
19
19
  store.snapshot.subscription.instrument[instrument.toString()] = instrument;
20
20
 
21
- const streamer = new BacktesterStreamer(store, feed, {
22
- balance: {},
23
- from: 0,
24
- to: 10,
25
- listener: {
21
+ const streamer = new BacktesterStreamer(
22
+ store,
23
+ feed,
24
+ {
25
+ balance: {},
26
+ from: 0,
27
+ to: 10
28
+ },
29
+ {
26
30
  onBacktestCompleted: () => {
27
31
  const trade = store.snapshot.trade[instrument.toString()];
28
32
 
@@ -34,7 +38,7 @@ describe('backtester streamer tests', () => {
34
38
  done();
35
39
  }
36
40
  }
37
- });
41
+ );
38
42
 
39
43
  feed
40
44
  .save(instrument, [
@@ -36,7 +36,8 @@ export class BacktesterStreamer {
36
36
  constructor(
37
37
  private readonly store: Store,
38
38
  private readonly feed: Feed,
39
- private readonly options: BacktesterOptions
39
+ private readonly options: BacktesterOptions,
40
+ private readonly listener?: BacktesterListener
40
41
  ) {
41
42
  this.timestamp = this.options.from;
42
43
  }
@@ -73,15 +74,15 @@ export class BacktesterStreamer {
73
74
  }
74
75
 
75
76
  if (this.sequence == 0) {
76
- if (this.options.listener.onBacktestStarted) {
77
- this.options.listener.onBacktestStarted(this);
77
+ if (this.listener.onBacktestStarted) {
78
+ this.listener.onBacktestStarted(this);
78
79
  }
79
80
  }
80
81
 
81
82
  while (await this.processNext()) {
82
83
  if (this.sequence % this.sequenceUpdateBatch == 0) {
83
- if (this.options.listener.onBacktestUpdated) {
84
- this.options.listener.onBacktestUpdated(this);
84
+ if (this.listener.onBacktestUpdated) {
85
+ this.listener.onBacktestUpdated(this);
85
86
  }
86
87
  }
87
88
 
@@ -90,8 +91,8 @@ export class BacktesterStreamer {
90
91
  }
91
92
  }
92
93
 
93
- if (this.options.listener.onBacktestCompleted) {
94
- this.options.listener.onBacktestCompleted(this);
94
+ if (this.listener.onBacktestCompleted) {
95
+ this.listener.onBacktestCompleted(this);
95
96
  }
96
97
  }
97
98
 
package/src/bin.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  BacktesterAdapter,
3
- BacktesterOptions,
3
+ BacktesterListener,
4
4
  BacktesterStreamer
5
5
  } from './adapter/backtester';
6
6
  import { AdapterAggregate } from './adapter';
7
- import { PaperAdapter, PaperOptions } from './adapter/paper';
7
+ import { PaperAdapter } from './adapter/paper';
8
8
  import { Session, SessionDescriptor } from './session';
9
9
  import { Store } from './store';
10
10
 
@@ -16,16 +16,26 @@ import { Store } from './store';
16
16
  */
17
17
  export function backtest(
18
18
  descriptor: SessionDescriptor,
19
- options: BacktesterOptions
19
+ listener?: BacktesterListener
20
20
  ): [Session, BacktesterStreamer] {
21
21
  const store = new Store();
22
22
 
23
- const streamer = new BacktesterStreamer(store, descriptor.feed, options);
24
- const aggregate = new AdapterAggregate(
23
+ const streamer = new BacktesterStreamer(
25
24
  store,
25
+ descriptor.feed,
26
+ descriptor.options.backtester,
27
+ listener
28
+ );
29
+ const aggregate = new AdapterAggregate(
26
30
  descriptor.adapter.map(
27
- it => new PaperAdapter(new BacktesterAdapter(it, streamer), store, options)
28
- )
31
+ it =>
32
+ new PaperAdapter(
33
+ new BacktesterAdapter(it, streamer),
34
+ store,
35
+ descriptor.options.backtester
36
+ )
37
+ ),
38
+ store
29
39
  );
30
40
 
31
41
  return [new Session(store, aggregate, descriptor), streamer];
@@ -37,12 +47,12 @@ export function backtest(
37
47
  * @param options backtest options.
38
48
  * @returns new session object.
39
49
  */
40
- export function paper(descriptor: SessionDescriptor, options: PaperOptions): Session {
50
+ export function paper(descriptor: SessionDescriptor): Session {
41
51
  const store = new Store();
42
52
 
43
53
  const aggregate = new AdapterAggregate(
44
- store,
45
- descriptor.adapter.map(it => new PaperAdapter(it, store, options))
54
+ descriptor.adapter.map(it => new PaperAdapter(it, store, descriptor.options.paper)),
55
+ store
46
56
  );
47
57
 
48
58
  return new Session(store, aggregate, descriptor);
@@ -55,7 +65,7 @@ export function paper(descriptor: SessionDescriptor, options: PaperOptions): Ses
55
65
  */
56
66
  export function live(descriptor: SessionDescriptor): Session {
57
67
  const store = new Store();
58
- const aggregate = new AdapterAggregate(store, descriptor.adapter);
68
+ const aggregate = new AdapterAggregate(descriptor.adapter, store);
59
69
 
60
70
  return new Session(store, aggregate, descriptor);
61
71
  }
package/src/ipc.spec.ts CHANGED
@@ -55,8 +55,7 @@ describe('ipc feed tests', () => {
55
55
  const session = await run(
56
56
  {
57
57
  adapter: [new DefaultAdapter()],
58
- feed: new Feed(new InMemoryStorage()),
59
- describe: (session: Session) => session.trade(instrumentOf('default:btc-usdt'))
58
+ feed: new Feed(new InMemoryStorage())
60
59
  },
61
60
  command
62
61
  );
@@ -66,8 +65,7 @@ describe('ipc feed tests', () => {
66
65
 
67
66
  test('should dispatch session started event', done => {
68
67
  const command = {
69
- type: 'paper',
70
- balance: { 'default:usd': 100 }
68
+ type: 'paper'
71
69
  };
72
70
 
73
71
  const ipcSub = new EventEmitter();
@@ -80,8 +78,12 @@ describe('ipc feed tests', () => {
80
78
  run(
81
79
  {
82
80
  adapter: [new DefaultAdapter()],
83
- describe: (session: Session) => session.trade(instrumentOf('default:btc-usdt')),
84
- ipcSub
81
+ ipcSub,
82
+ options: {
83
+ paper: {
84
+ balance: { 'default:usd': 100 }
85
+ }
86
+ }
85
87
  },
86
88
  command
87
89
  );
@@ -102,8 +104,7 @@ describe('ipc feed tests', () => {
102
104
 
103
105
  run(
104
106
  {
105
- adapter: [new DefaultAdapter()],
106
- describe: (session: Session) => session.trade(instrumentOf('default:btc-usdt'))
107
+ adapter: [new DefaultAdapter()]
107
108
  },
108
109
  command
109
110
  );
package/src/ipc.ts CHANGED
@@ -1,12 +1,17 @@
1
- import { AdapterFeedCommand } from './adapter';
2
1
  import { Session, SessionDescriptor } from './session';
3
2
  import { instrumentOf } from './domain';
4
3
  import { Topic, event, handler } from './shared/topic';
5
4
  import { runTask, Logger } from './shared';
6
5
  import { backtest, live, paper } from './bin';
7
6
  import { BacktesterStreamer } from './adapter/backtester';
7
+ import { Observable } from 'rxjs';
8
8
  import { EventEmitter } from 'events';
9
+ import { join } from 'path';
9
10
  import minimist = require('minimist');
11
+ import dotenv = require('dotenv');
12
+
13
+ // force to load environment variables from .env file if this file imported.
14
+ dotenv.config();
10
15
 
11
16
  /**
12
17
  * Base command/query interface for IPC communication.
@@ -42,12 +47,6 @@ export class IpcPaperCommand implements IpcCommand {
42
47
  * The optional session identifier.
43
48
  */
44
49
  id?: number;
45
-
46
- /**
47
- * Specifies trading balance, for example:
48
- * { "binance:usdt": 1000 }
49
- */
50
- balance: { [key: string]: number };
51
50
  }
52
51
 
53
52
  /**
@@ -58,20 +57,14 @@ export class IpcBacktestCommand implements IpcCommand {
58
57
  type = 'backtest';
59
58
 
60
59
  /**
61
- * Start date of the backtest in unix timestamp.
62
- */
63
- from: number;
64
-
65
- /**
66
- * Due date of the backtest in unix timestamp.
60
+ * Start date of the feed in unix timestamp.
67
61
  */
68
- to: number;
62
+ from?: number;
69
63
 
70
64
  /**
71
- * Specifies trading balance, for example:
72
- * { "binance:usdt": 1000 }
65
+ * Due date of the feed in unix timestamp.
73
66
  */
74
- balance: { [key: string]: number };
67
+ to?: number;
75
68
  }
76
69
 
77
70
  /**
@@ -89,12 +82,12 @@ export class IpcFeedCommand implements IpcCommand {
89
82
  /**
90
83
  * Start date of the feed in unix timestamp.
91
84
  */
92
- from: number;
85
+ from?: number;
93
86
 
94
87
  /**
95
88
  * Due date of the feed in unix timestamp.
96
89
  */
97
- to: number;
90
+ to?: number;
98
91
  }
99
92
 
100
93
  /**
@@ -117,13 +110,13 @@ class IpcSessionAccessor {
117
110
  session: Session;
118
111
  }
119
112
 
120
- export declare type SessionRunDescriptor = SessionDescriptor & { ipcSub?: EventEmitter };
113
+ export declare type IpcSessionDescriptor = SessionDescriptor & { ipcSub?: EventEmitter };
121
114
 
122
115
  /**
123
116
  * Inter process communication handler.
124
117
  */
125
118
  class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
126
- constructor(private readonly descriptor: SessionRunDescriptor) {
119
+ constructor(private readonly descriptor: IpcSessionDescriptor) {
127
120
  super();
128
121
  }
129
122
 
@@ -138,12 +131,12 @@ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
138
131
 
139
132
  accessor.session = live(this.descriptor);
140
133
 
141
- this.notify({
134
+ this.emit({
142
135
  type: 'live:started',
143
136
  session: accessor.session.descriptor?.id
144
137
  });
145
138
 
146
- await accessor.session.awake();
139
+ await accessor.session.awake(this.describe());
147
140
  }
148
141
 
149
142
  /**
@@ -155,16 +148,14 @@ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
155
148
  this.descriptor.id = command.id;
156
149
  }
157
150
 
158
- accessor.session = paper(this.descriptor, {
159
- balance: command.balance
160
- });
151
+ accessor.session = paper(this.descriptor);
161
152
 
162
- this.notify({
153
+ this.emit({
163
154
  type: 'paper:started',
164
155
  session: accessor.session.descriptor?.id
165
156
  });
166
157
 
167
- await accessor.session.awake();
158
+ await accessor.session.awake(this.describe());
168
159
  }
169
160
 
170
161
  /**
@@ -172,49 +163,51 @@ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
172
163
  */
173
164
  @handler(IpcBacktestCommand)
174
165
  onBacktestMode(command: IpcBacktestCommand, accessor: IpcSessionAccessor) {
166
+ if (command.from) {
167
+ this.descriptor.options.backtester.from = command.from;
168
+ }
169
+ if (command.to) {
170
+ this.descriptor.options.backtester.to = command.to;
171
+ }
172
+
175
173
  return new Promise<void>(async resolve => {
176
174
  const [session, streamer] = backtest(this.descriptor, {
177
- from: command.from,
178
- to: command.to,
179
- balance: command.balance,
180
- listener: {
181
- onBacktestStarted: (streamer: BacktesterStreamer) => {
182
- this.notify({
183
- type: 'backtest:started',
184
- session: session.descriptor?.id,
185
- timestamp: streamer.timestamp,
186
- from: command.from,
187
- to: command.to
188
- });
189
- },
190
- onBacktestUpdated: (streamer: BacktesterStreamer) => {
191
- this.notify({
192
- type: 'backtest:updated',
193
- session: session.descriptor?.id,
194
- timestamp: streamer.timestamp,
195
- from: command.from,
196
- to: command.to
197
- });
198
- },
199
- onBacktestCompleted: async (streamer: BacktesterStreamer) => {
200
- await accessor.session.dispose();
201
-
202
- this.notify({
203
- type: 'backtest:completed',
204
- session: session.descriptor?.id,
205
- timestamp: streamer.timestamp,
206
- from: command.from,
207
- to: command.to
208
- });
209
-
210
- resolve();
211
- }
175
+ onBacktestStarted: (streamer: BacktesterStreamer) => {
176
+ this.emit({
177
+ type: 'backtest:started',
178
+ session: session.descriptor?.id,
179
+ timestamp: streamer.timestamp,
180
+ from: this.descriptor.options.backtester.from,
181
+ to: this.descriptor.options.backtester.to
182
+ });
183
+ },
184
+ onBacktestUpdated: (streamer: BacktesterStreamer) => {
185
+ this.emit({
186
+ type: 'backtest:updated',
187
+ session: session.descriptor?.id,
188
+ timestamp: streamer.timestamp,
189
+ from: this.descriptor.options.backtester.from,
190
+ to: this.descriptor.options.backtester.to
191
+ });
192
+ },
193
+ onBacktestCompleted: async (streamer: BacktesterStreamer) => {
194
+ await accessor.session.dispose();
195
+
196
+ this.emit({
197
+ type: 'backtest:completed',
198
+ session: session.descriptor?.id,
199
+ timestamp: streamer.timestamp,
200
+ from: this.descriptor.options.backtester.from,
201
+ to: this.descriptor.options.backtester.to
202
+ });
203
+
204
+ resolve();
212
205
  }
213
206
  });
214
207
 
215
208
  accessor.session = session;
216
209
 
217
- await accessor.session.awake();
210
+ await accessor.session.awake(this.describe());
218
211
  await streamer.tryContinue().catch(it => Logger.error(it));
219
212
  });
220
213
  }
@@ -227,28 +220,25 @@ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
227
220
  accessor.session = accessor.session ?? live(this.descriptor);
228
221
  const instrument = instrumentOf(command.instrument);
229
222
 
230
- await accessor.session.awake(true);
231
-
232
- this.notify({ type: 'feed:started' });
233
-
234
- await accessor.session.aggregate.dispatch(
235
- instrument.base.adapter,
236
- new AdapterFeedCommand(
237
- instrument,
238
- command.from,
239
- command.to,
240
- this.descriptor.feed,
241
- timestamp =>
242
- this.notify({
243
- type: 'feed:updated',
244
- timestamp,
245
- from: command.from,
246
- to: command.to
247
- })
248
- )
223
+ await accessor.session.awake(undefined);
224
+
225
+ this.emit({ type: 'feed:started' });
226
+
227
+ await accessor.session.aggregate.feed(
228
+ instrument,
229
+ command.from,
230
+ command.to,
231
+ this.descriptor.feed,
232
+ timestamp =>
233
+ this.emit({
234
+ type: 'feed:updated',
235
+ timestamp,
236
+ from: command.from,
237
+ to: command.to
238
+ })
249
239
  );
250
240
 
251
- this.notify({ type: 'feed:completed' });
241
+ this.emit({ type: 'feed:completed' });
252
242
 
253
243
  await accessor.session.dispose();
254
244
  }
@@ -260,9 +250,9 @@ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
260
250
  async onTask(query: IpcTaskCommand, accessor: IpcSessionAccessor) {
261
251
  accessor.session = accessor.session ?? live(this.descriptor);
262
252
 
263
- await accessor.session.awake(true);
253
+ await accessor.session.awake(undefined);
264
254
 
265
- this.notify({ type: 'task:started', taskName: query.taskName });
255
+ this.emit({ type: 'task:started', taskName: query.taskName });
266
256
 
267
257
  let result = undefined;
268
258
 
@@ -272,15 +262,26 @@ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
272
262
  result = e;
273
263
  }
274
264
 
275
- this.notify({ type: 'task:completed', taskName: query.taskName, result });
265
+ this.emit({ type: 'task:completed', taskName: query.taskName, result });
276
266
 
277
267
  await accessor.session.dispose();
278
268
  }
279
269
 
270
+ describe(): (session: Session) => Observable<any> {
271
+ const pkg = require(join(process.cwd(), 'package.json'));
272
+ const describe = require(join(process.cwd(), pkg.main))?.default;
273
+
274
+ if (describe instanceof Function) {
275
+ return describe;
276
+ }
277
+
278
+ return undefined;
279
+ }
280
+
280
281
  /**
281
282
  * Sends a message to parent process.
282
283
  */
283
- private notify(message: any) {
284
+ private emit(message: any) {
284
285
  if (process.send) {
285
286
  process.send(message);
286
287
  }
@@ -296,7 +297,7 @@ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
296
297
  * @returns new session.
297
298
  */
298
299
  export async function run(
299
- descriptor: SessionRunDescriptor,
300
+ descriptor: IpcSessionDescriptor,
300
301
  ...commands: IpcCommand[]
301
302
  ): Promise<Session> {
302
303
  const handler = new IpcHandler(descriptor);