@dxos/echo-pipeline 0.1.41 → 0.1.45-next.45667d4

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 (74) hide show
  1. package/dist/lib/browser/{chunk-4YAT2XJW.mjs → chunk-2NGLSBL4.mjs} +1128 -1017
  2. package/dist/lib/browser/chunk-2NGLSBL4.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +1 -3
  4. package/dist/lib/browser/index.mjs.map +1 -1
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/testing/index.mjs +72 -48
  7. package/dist/lib/browser/testing/index.mjs.map +3 -3
  8. package/dist/lib/node/index.cjs +1048 -938
  9. package/dist/lib/node/index.cjs.map +4 -4
  10. package/dist/lib/node/meta.json +1 -1
  11. package/dist/lib/node/testing/index.cjs +735 -540
  12. package/dist/lib/node/testing/index.cjs.map +4 -4
  13. package/dist/types/src/dbhost/database-host.d.ts +2 -4
  14. package/dist/types/src/dbhost/database-host.d.ts.map +1 -1
  15. package/dist/types/src/dbhost/snapshot-store.d.ts +2 -0
  16. package/dist/types/src/dbhost/snapshot-store.d.ts.map +1 -1
  17. package/dist/types/src/metadata/metadata-store.d.ts +3 -2
  18. package/dist/types/src/metadata/metadata-store.d.ts.map +1 -1
  19. package/dist/types/src/pipeline/pipeline.d.ts +27 -11
  20. package/dist/types/src/pipeline/pipeline.d.ts.map +1 -1
  21. package/dist/types/src/pipeline/timeframe-clock.d.ts +1 -0
  22. package/dist/types/src/pipeline/timeframe-clock.d.ts.map +1 -1
  23. package/dist/types/src/space/auth.d.ts +4 -4
  24. package/dist/types/src/space/auth.d.ts.map +1 -1
  25. package/dist/types/src/space/control-pipeline.d.ts +2 -4
  26. package/dist/types/src/space/control-pipeline.d.ts.map +1 -1
  27. package/dist/types/src/space/data-pipeline.d.ts +19 -10
  28. package/dist/types/src/space/data-pipeline.d.ts.map +1 -1
  29. package/dist/types/src/space/index.d.ts +0 -1
  30. package/dist/types/src/space/index.d.ts.map +1 -1
  31. package/dist/types/src/space/space-manager.d.ts +14 -14
  32. package/dist/types/src/space/space-manager.d.ts.map +1 -1
  33. package/dist/types/src/space/space-protocol.d.ts +2 -2
  34. package/dist/types/src/space/space-protocol.d.ts.map +1 -1
  35. package/dist/types/src/space/space.d.ts +20 -7
  36. package/dist/types/src/space/space.d.ts.map +1 -1
  37. package/dist/types/src/testing/database-test-rig.d.ts.map +1 -1
  38. package/dist/types/src/testing/test-agent-builder.d.ts +15 -5
  39. package/dist/types/src/testing/test-agent-builder.d.ts.map +1 -1
  40. package/package.json +30 -30
  41. package/src/common/feeds.ts +2 -2
  42. package/src/dbhost/data-service-host.ts +10 -10
  43. package/src/dbhost/data-service.ts +1 -1
  44. package/src/dbhost/database-host.ts +1 -9
  45. package/src/dbhost/snapshot-store.test.ts +2 -2
  46. package/src/dbhost/snapshot-store.ts +16 -1
  47. package/src/metadata/metadata-store.ts +14 -16
  48. package/src/pipeline/message-selector.ts +1 -1
  49. package/src/pipeline/pipeline.test.ts +68 -44
  50. package/src/pipeline/pipeline.ts +118 -35
  51. package/src/pipeline/timeframe-clock.ts +8 -2
  52. package/src/space/auth.ts +6 -6
  53. package/src/space/control-pipeline.test.ts +19 -20
  54. package/src/space/control-pipeline.ts +3 -5
  55. package/src/space/data-pipeline.ts +131 -69
  56. package/src/space/index.ts +0 -1
  57. package/src/space/replication.browser.test.ts +8 -8
  58. package/src/space/space-manager.browser.test.ts +3 -3
  59. package/src/space/space-manager.ts +25 -20
  60. package/src/space/space-protocol.browser.test.ts +1 -1
  61. package/src/space/space-protocol.test.ts +6 -6
  62. package/src/space/space-protocol.ts +7 -7
  63. package/src/space/space.test.ts +137 -48
  64. package/src/space/space.ts +82 -40
  65. package/src/testing/database-test-rig.ts +25 -24
  66. package/src/testing/test-agent-builder.ts +88 -49
  67. package/src/testing/test-feed-builder.ts +1 -1
  68. package/src/testing/util.ts +15 -15
  69. package/src/tests/database-unit.test.ts +4 -4
  70. package/src/tests/database.test.ts +1 -1
  71. package/dist/lib/browser/chunk-4YAT2XJW.mjs.map +0 -7
  72. package/dist/types/src/space/genesis.d.ts +0 -5
  73. package/dist/types/src/space/genesis.d.ts.map +0 -1
  74. package/src/space/genesis.ts +0 -69
@@ -4,14 +4,16 @@
4
4
 
5
5
  import assert from 'node:assert';
6
6
 
7
- import { sleep, synchronized, Trigger } from '@dxos/async';
7
+ import { Event, sleep, synchronized, Trigger } from '@dxos/async';
8
8
  import { Context, rejectOnDispose } from '@dxos/context';
9
+ import { failUndefined } from '@dxos/debug';
9
10
  import { FeedSetIterator, FeedWrapper, FeedWriter } from '@dxos/feed-store';
10
11
  import { PublicKey } from '@dxos/keys';
11
12
  import { log } from '@dxos/log';
12
13
  import { FeedMessageBlock } from '@dxos/protocols';
13
14
  import type { FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
14
15
  import { Timeframe } from '@dxos/timeframe';
16
+ import { ComplexMap } from '@dxos/util';
15
17
 
16
18
  import { createMappedFeedWriter } from '../common';
17
19
  import { createMessageSelector } from './message-selector';
@@ -37,6 +39,8 @@ export class PipelineState {
37
39
  // TODO(dmaretskyi): Remove?.
38
40
  public readonly timeframeUpdate = this._timeframeClock.update;
39
41
 
42
+ public readonly stalled = new Event();
43
+
40
44
  /**
41
45
  * Target timeframe we are waiting to reach.
42
46
  */
@@ -46,7 +50,7 @@ export class PipelineState {
46
50
 
47
51
  // prettier-ignore
48
52
  constructor(
49
- private _iterator: FeedSetIterator<any>,
53
+ private _feeds: ComplexMap<PublicKey, FeedWrapper<FeedMessage>>,
50
54
  private _timeframeClock: TimeframeClock
51
55
  ) { }
52
56
 
@@ -57,12 +61,12 @@ export class PipelineState {
57
61
  // TODO(dmaretskyi): Rename `totalTimeframe`? or `lastTimeframe`.
58
62
  get endTimeframe() {
59
63
  return mapFeedIndexesToTimeframe(
60
- this._iterator.feeds
64
+ Array.from(this._feeds.values())
61
65
  .filter((feed) => feed.length > 0)
62
66
  .map((feed) => ({
63
67
  feedKey: feed.key,
64
- index: feed.length - 1
65
- }))
68
+ index: feed.length - 1,
69
+ })),
66
70
  );
67
71
  }
68
72
 
@@ -79,7 +83,7 @@ export class PipelineState {
79
83
  }
80
84
 
81
85
  get feeds() {
82
- return this._iterator.feeds;
86
+ return Array.from(this._feeds.values());
83
87
  }
84
88
 
85
89
  async waitUntilTimeframe(target: Timeframe) {
@@ -100,19 +104,19 @@ export class PipelineState {
100
104
  async waitUntilReachedTargetTimeframe({
101
105
  ctx = new Context(),
102
106
  timeout,
103
- breakOnStall = true
107
+ breakOnStall = true,
104
108
  }: WaitUntilReachedTargetParams = {}) {
105
109
  log('waitUntilReachedTargetTimeframe', {
106
110
  timeout,
107
111
  current: this.timeframe,
108
- target: this.targetTimeframe
112
+ target: this.targetTimeframe,
109
113
  });
110
114
 
111
115
  this._reachedTargetPromise ??= Promise.race([
112
116
  this._timeframeClock.update.waitForCondition(() => {
113
117
  return Timeframe.dependencies(this.targetTimeframe, this.timeframe).isEmpty();
114
118
  }),
115
- ...(breakOnStall ? [this._iterator.stalled.discardParameter().waitForCount(1)] : [])
119
+ ...(breakOnStall ? [this.stalled.discardParameter().waitForCount(1)] : []),
116
120
  ]);
117
121
 
118
122
  let done = false;
@@ -132,9 +136,9 @@ export class PipelineState {
132
136
  timeout,
133
137
  current: this.timeframe,
134
138
  target: this.targetTimeframe,
135
- dependencies: Timeframe.dependencies(this.targetTimeframe, this.timeframe)
139
+ dependencies: Timeframe.dependencies(this.targetTimeframe, this.timeframe),
136
140
  });
137
- })
141
+ }),
138
142
  ]);
139
143
  } else {
140
144
  return this._reachedTargetPromise;
@@ -180,33 +184,31 @@ export interface PipelineAccessor {
180
184
  * 5. Wait for the writable feed to be added.
181
185
  */
182
186
  export class Pipeline implements PipelineAccessor {
183
- private readonly _timeframeClock = new TimeframeClock(this._initialTimeframe);
187
+ private readonly _timeframeClock = new TimeframeClock(new Timeframe());
188
+
189
+ private readonly _feeds = new ComplexMap<PublicKey, FeedWrapper<FeedMessage>>(PublicKey.hash);
184
190
 
185
191
  // Inbound feed stream.
186
- private readonly _feedSetIterator = new FeedSetIterator<FeedMessage>(createMessageSelector(this._timeframeClock), {
187
- start: startAfter(this._initialTimeframe),
188
- stallTimeout: 1000
189
- });
192
+ private _feedSetIterator?: FeedSetIterator<FeedMessage>;
190
193
 
191
194
  // External state accessor.
192
- private readonly _state: PipelineState = new PipelineState(this._feedSetIterator, this._timeframeClock);
195
+ private readonly _state: PipelineState = new PipelineState(this._feeds, this._timeframeClock);
193
196
 
194
197
  // Waits for the message consumer to process the message and yield control back to the pipeline.
195
198
  private readonly _processingTrigger = new Trigger().wake();
196
199
 
197
- // Outbound feed writer.
198
- private _writer: FeedWriter<FeedMessage.Payload> | undefined;
200
+ private readonly _pauseTrigger = new Trigger().wake();
201
+
202
+ private _isStopping = false;
203
+
204
+ private _isStarted = false;
199
205
 
200
206
  private _isOpen = false;
201
207
 
202
- // prettier-ignore
203
- constructor(
204
- private readonly _initialTimeframe: Timeframe = new Timeframe()
205
- ) {
206
- this._feedSetIterator.stalled.on((iterator) => {
207
- log.warn(`Stalled after ${iterator.options.stallTimeout}ms with ${iterator.size} feeds.`);
208
- });
209
- }
208
+ private _isPaused = false;
209
+
210
+ // Outbound feed writer.
211
+ private _writer: FeedWriter<FeedMessage.Payload> | undefined;
210
212
 
211
213
  get state() {
212
214
  return this._state;
@@ -218,17 +220,20 @@ export class Pipeline implements PipelineAccessor {
218
220
  }
219
221
 
220
222
  getFeeds(): FeedWrapper<FeedMessage>[] {
221
- return this._feedSetIterator.feeds;
223
+ return this._feedSetIterator!.feeds;
222
224
  }
223
225
 
224
226
  // NOTE: This cannot be synchronized with `stop` because stop waits for the mutation processing to complete,
225
227
  // which might be opening feeds during the mutation processing, which w
226
228
  async addFeed(feed: FeedWrapper<FeedMessage>) {
227
- await this._feedSetIterator.addFeed(feed);
229
+ this._feeds.set(feed.key, feed);
230
+ if (this._feedSetIterator) {
231
+ await this._feedSetIterator.addFeed(feed);
232
+ }
228
233
  }
229
234
 
230
235
  hasFeed(feedKey: PublicKey) {
231
- return this._feedSetIterator.hasFeed(feedKey);
236
+ return this._feeds.has(feedKey);
232
237
  }
233
238
 
234
239
  setWriteFeed(feed: FeedWrapper<FeedMessage>) {
@@ -238,25 +243,69 @@ export class Pipeline implements PipelineAccessor {
238
243
  this._writer = createMappedFeedWriter<FeedMessage.Payload, FeedMessage>(
239
244
  (payload: FeedMessage.Payload) => ({
240
245
  timeframe: this._timeframeClock.timeframe,
241
- payload
246
+ payload,
242
247
  }),
243
- feed.createFeedWriter()
248
+ feed.createFeedWriter(),
244
249
  );
245
250
  }
246
251
 
247
252
  @synchronized
248
253
  async start() {
249
254
  log('starting...', {});
250
- await this._feedSetIterator.open();
255
+ await this._initIterator();
256
+ await this._feedSetIterator!.open();
251
257
  log('started');
258
+ this._isStarted = true;
252
259
  }
253
260
 
254
261
  @synchronized
255
262
  async stop() {
256
263
  log('stopping...', {});
257
- await this._feedSetIterator.close();
264
+ this._isStopping = true;
265
+ await this._feedSetIterator?.close();
258
266
  await this._processingTrigger.wait(); // Wait for the in-flight message to be processed.
259
267
  log('stopped');
268
+ this._isStarted = false;
269
+ }
270
+
271
+ /**
272
+ * @param timeframe Timeframe of already processed messages. The pipeline will start processing messages AFTER this timeframe.
273
+ */
274
+ @synchronized
275
+ async setCursor(timeframe: Timeframe) {
276
+ assert(!this._isStarted || this._isPaused, 'Invalid state.');
277
+
278
+ this._timeframeClock.setTimeframe(timeframe);
279
+
280
+ if (this._feedSetIterator) {
281
+ await this._feedSetIterator.close();
282
+ await this._initIterator();
283
+ await this._feedSetIterator.open();
284
+ }
285
+ }
286
+
287
+ /**
288
+ * Calling pause while processing will cause a deadlock.
289
+ */
290
+ @synchronized
291
+ async pause() {
292
+ assert(this._isStarted, 'Pipeline is not open.');
293
+ if (this._isPaused) {
294
+ return;
295
+ }
296
+
297
+ this._pauseTrigger.reset();
298
+ await this._processingTrigger.wait();
299
+ this._isPaused = true;
300
+ }
301
+
302
+ @synchronized
303
+ async unpause() {
304
+ assert(this._isStarted, 'Pipeline is not open.');
305
+ assert(this._isPaused, 'Pipeline is not paused.');
306
+
307
+ this._pauseTrigger.wake();
308
+ this._isPaused = false;
260
309
  }
261
310
 
262
311
  /**
@@ -267,7 +316,26 @@ export class Pipeline implements PipelineAccessor {
267
316
  assert(!this._isOpen, 'Pipeline is already being consumed.');
268
317
  this._isOpen = true;
269
318
 
270
- for await (const block of this._feedSetIterator) {
319
+ assert(this._feedSetIterator, 'Iterator not initialized.');
320
+ let lastFeedSetIterator = this._feedSetIterator;
321
+ let iterable = lastFeedSetIterator[Symbol.asyncIterator]();
322
+
323
+ while (!this._isStopping) {
324
+ await this._pauseTrigger.wait();
325
+
326
+ // Iterator might have been changed while we were waiting for the processing to complete.
327
+ if (lastFeedSetIterator !== this._feedSetIterator) {
328
+ assert(this._feedSetIterator, 'Iterator not initialized.');
329
+ lastFeedSetIterator = this._feedSetIterator;
330
+ iterable = lastFeedSetIterator[Symbol.asyncIterator]();
331
+ }
332
+
333
+ const { done, value } = await iterable.next();
334
+ if (done) {
335
+ continue;
336
+ }
337
+
338
+ const block = value ?? failUndefined();
271
339
  this._processingTrigger.reset();
272
340
  this._timeframeClock.updatePendingTimeframe(PublicKey.from(block.feedKey), block.seq);
273
341
  yield block;
@@ -279,4 +347,19 @@ export class Pipeline implements PipelineAccessor {
279
347
  // TODO(burdon): Test re-entrant?
280
348
  this._isOpen = false;
281
349
  }
350
+
351
+ private async _initIterator() {
352
+ this._feedSetIterator = new FeedSetIterator<FeedMessage>(createMessageSelector(this._timeframeClock), {
353
+ start: startAfter(this._timeframeClock.timeframe),
354
+ stallTimeout: 1000,
355
+ });
356
+ this._feedSetIterator.stalled.on((iterator) => {
357
+ log.warn(`Stalled after ${iterator.options.stallTimeout}ms with ${iterator.size} feeds.`);
358
+ this._state.stalled.emit();
359
+ });
360
+
361
+ for (const feed of this._feeds.values()) {
362
+ await this._feedSetIterator.addFeed(feed);
363
+ }
364
+ }
282
365
  }
@@ -48,6 +48,12 @@ export class TimeframeClock {
48
48
  return this._pendingTimeframe;
49
49
  }
50
50
 
51
+ setTimeframe(timeframe: Timeframe) {
52
+ this._timeframe = timeframe;
53
+ this._pendingTimeframe = timeframe;
54
+ this.update.emit(this._timeframe);
55
+ }
56
+
51
57
  updatePendingTimeframe(key: PublicKey, seq: number) {
52
58
  this._pendingTimeframe = Timeframe.merge(this._pendingTimeframe, new Timeframe([[key, seq]]));
53
59
  }
@@ -64,12 +70,12 @@ export class TimeframeClock {
64
70
 
65
71
  @timed(5_000)
66
72
  async waitUntilReached(target: Timeframe) {
67
- log.info('waitUntilReached', { target, current: this._timeframe });
73
+ log('waitUntilReached', { target, current: this._timeframe });
68
74
  await this.update.waitForCondition(() => {
69
75
  log('check if reached', {
70
76
  target,
71
77
  current: this._timeframe,
72
- deps: Timeframe.dependencies(target, this._timeframe)
78
+ deps: Timeframe.dependencies(target, this._timeframe),
73
79
  });
74
80
 
75
81
  return Timeframe.dependencies(target, this._timeframe).isEmpty();
package/src/space/auth.ts CHANGED
@@ -27,18 +27,18 @@ export class AuthExtension extends RpcExtension<Services, Services> {
27
27
  private readonly _ctx = new Context({
28
28
  onError: (err) => {
29
29
  log.catch(err);
30
- }
30
+ },
31
31
  });
32
32
 
33
33
  constructor(private readonly _authParams: AuthExtensionParams) {
34
34
  super({
35
35
  requested: {
36
- AuthService: schema.getService('dxos.mesh.teleport.auth.AuthService')
36
+ AuthService: schema.getService('dxos.mesh.teleport.auth.AuthService'),
37
37
  },
38
38
  exposed: {
39
- AuthService: schema.getService('dxos.mesh.teleport.auth.AuthService')
39
+ AuthService: schema.getService('dxos.mesh.teleport.auth.AuthService'),
40
40
  },
41
- timeout: 60 * 1000 // Long timeout because auth can wait for sync in certain cases.
41
+ timeout: 60 * 1000, // Long timeout because auth can wait for sync in certain cases.
42
42
  });
43
43
  }
44
44
 
@@ -56,8 +56,8 @@ export class AuthExtension extends RpcExtension<Services, Services> {
56
56
  log.error('failed to generate auth credentials', err);
57
57
  throw new Error('auth rejected');
58
58
  }
59
- }
60
- }
59
+ },
60
+ },
61
61
  };
62
62
  }
63
63
 
@@ -30,9 +30,9 @@ describe('space/control-pipeline', () => {
30
30
  root: createStorage({ type: StorageType.RAM }).createDirectory(),
31
31
  signer: keyring,
32
32
  hypercore: {
33
- valueEncoding
34
- }
35
- })
33
+ valueEncoding,
34
+ },
35
+ }),
36
36
  });
37
37
 
38
38
  const createFeed = async () => {
@@ -45,8 +45,7 @@ describe('space/control-pipeline', () => {
45
45
  const controlPipeline = new ControlPipeline({
46
46
  spaceKey,
47
47
  genesisFeed,
48
- initialTimeframe: new Timeframe(),
49
- feedProvider: (key) => feedStore.openFeed(key)
48
+ feedProvider: (key) => feedStore.openFeed(key),
50
49
  });
51
50
 
52
51
  const admittedFeeds: PublicKey[] = [];
@@ -72,8 +71,8 @@ describe('space/control-pipeline', () => {
72
71
  for (const credential of credentials) {
73
72
  await controlPipeline.pipeline.writer?.write({
74
73
  credential: {
75
- credential
76
- }
74
+ credential,
75
+ },
77
76
  });
78
77
  }
79
78
 
@@ -95,10 +94,10 @@ describe('space/control-pipeline', () => {
95
94
  spaceKey,
96
95
  identityKey,
97
96
  deviceKey,
98
- designation: AdmittedFeed.Designation.CONTROL
99
- }
100
- })
101
- }
97
+ designation: AdmittedFeed.Designation.CONTROL,
98
+ },
99
+ }),
100
+ },
102
101
  });
103
102
 
104
103
  await controlPipeline.pipeline.state.waitUntilTimeframe(controlPipeline.pipeline.state.endTimeframe);
@@ -119,10 +118,10 @@ describe('space/control-pipeline', () => {
119
118
  spaceKey,
120
119
  identityKey,
121
120
  deviceKey,
122
- designation: AdmittedFeed.Designation.DATA
123
- }
124
- })
125
- }
121
+ designation: AdmittedFeed.Designation.DATA,
122
+ },
123
+ }),
124
+ },
126
125
  });
127
126
 
128
127
  const end = controlPipeline.pipeline.state.endTimeframe;
@@ -147,12 +146,12 @@ describe('space/control-pipeline', () => {
147
146
  spaceKey,
148
147
  identityKey,
149
148
  deviceKey,
150
- designation: AdmittedFeed.Designation.DATA
151
- }
152
- })
153
- }
149
+ designation: AdmittedFeed.Designation.DATA,
150
+ },
151
+ }),
152
+ },
154
153
  },
155
- timeframe: new Timeframe()
154
+ timeframe: new Timeframe(),
156
155
  });
157
156
 
158
157
  await controlPipeline.pipeline.state.waitUntilTimeframe(controlPipeline.pipeline.state.endTimeframe);
@@ -8,7 +8,6 @@ import { PublicKey } from '@dxos/keys';
8
8
  import { log } from '@dxos/log';
9
9
  import type { FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
10
10
  import { AdmittedFeed, Credential } from '@dxos/protocols/proto/dxos/halo/credentials';
11
- import { Timeframe } from '@dxos/timeframe';
12
11
  import { AsyncCallback, Callback } from '@dxos/util';
13
12
 
14
13
  import { Pipeline, PipelineAccessor } from '../pipeline';
@@ -17,7 +16,6 @@ export type ControlPipelineParams = {
17
16
  spaceKey: PublicKey;
18
17
  genesisFeed: FeedWrapper<FeedMessage>;
19
18
  feedProvider: (feedKey: PublicKey) => Promise<FeedWrapper<FeedMessage>>;
20
- initialTimeframe?: Timeframe;
21
19
  };
22
20
 
23
21
  /**
@@ -31,8 +29,8 @@ export class ControlPipeline {
31
29
  public readonly onMemberAdmitted: Callback<AsyncCallback<MemberInfo>>;
32
30
  public readonly onCredentialProcessed: Callback<AsyncCallback<Credential>>;
33
31
 
34
- constructor({ spaceKey, genesisFeed, feedProvider, initialTimeframe }: ControlPipelineParams) {
35
- this._pipeline = new Pipeline(initialTimeframe);
32
+ constructor({ spaceKey, genesisFeed, feedProvider }: ControlPipelineParams) {
33
+ this._pipeline = new Pipeline();
36
34
  void this._pipeline.addFeed(genesisFeed); // TODO(burdon): Require async open/close?
37
35
 
38
36
  this._spaceStateMachine = new SpaceStateMachine(spaceKey);
@@ -79,7 +77,7 @@ export class ControlPipeline {
79
77
  if (msg.data.payload.credential) {
80
78
  const result = await this._spaceStateMachine.process(
81
79
  msg.data.payload.credential.credential,
82
- PublicKey.from(msg.feedKey)
80
+ PublicKey.from(msg.feedKey),
83
81
  );
84
82
  if (!result) {
85
83
  log.warn('processing failed', { msg });