@dxos/feed-store 2.12.20 → 2.13.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/dist/src/benchmark.js +2 -3
- package/dist/src/benchmark.js.map +1 -1
- package/dist/src/create-batch-stream.test.js +1 -2
- package/dist/src/create-batch-stream.test.js.map +1 -1
- package/dist/src/feed-descriptor.d.ts +9 -27
- package/dist/src/feed-descriptor.d.ts.map +1 -1
- package/dist/src/feed-descriptor.js +9 -39
- package/dist/src/feed-descriptor.js.map +1 -1
- package/dist/src/feed-descriptor.test.js +19 -33
- package/dist/src/feed-descriptor.test.js.map +1 -1
- package/dist/src/feed-store.d.ts +4 -53
- package/dist/src/feed-store.d.ts.map +1 -1
- package/dist/src/feed-store.js +16 -140
- package/dist/src/feed-store.js.map +1 -1
- package/dist/src/feed-store.test.js +34 -96
- package/dist/src/feed-store.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +10 -9
- package/src/benchmark.ts +2 -3
- package/src/create-batch-stream.test.ts +1 -2
- package/src/feed-descriptor.test.ts +20 -41
- package/src/feed-descriptor.ts +20 -59
- package/src/feed-store.test.ts +39 -114
- package/src/feed-store.ts +18 -174
package/src/feed-store.test.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
/* eslint-disable jest/no-done-callback */
|
|
6
6
|
|
|
7
|
+
import assert from 'assert';
|
|
7
8
|
import hypercore from 'hypercore';
|
|
8
9
|
import hypertrie from 'hypertrie';
|
|
9
10
|
import pify from 'pify';
|
|
@@ -13,6 +14,7 @@ import { sleep } from '@dxos/async';
|
|
|
13
14
|
import { PublicKey, createKeyPair } from '@dxos/crypto';
|
|
14
15
|
import { IStorage, STORAGE_NODE, STORAGE_RAM, createStorage } from '@dxos/random-access-multi-storage';
|
|
15
16
|
|
|
17
|
+
import { FeedDescriptor } from './feed-descriptor';
|
|
16
18
|
import { FeedStore } from './feed-store';
|
|
17
19
|
import { HypercoreFeed } from './hypercore-types';
|
|
18
20
|
|
|
@@ -23,9 +25,8 @@ interface KeyPair {
|
|
|
23
25
|
|
|
24
26
|
const feedNames = ['booksFeed', 'usersFeed', 'groupsFeed'];
|
|
25
27
|
|
|
26
|
-
const createFeedStore =
|
|
28
|
+
const createFeedStore = (storage: IStorage, options = {}) => {
|
|
27
29
|
const feedStore = new FeedStore(storage, options);
|
|
28
|
-
await feedStore.open();
|
|
29
30
|
return feedStore;
|
|
30
31
|
};
|
|
31
32
|
|
|
@@ -34,23 +35,21 @@ async function createDefault () {
|
|
|
34
35
|
|
|
35
36
|
return {
|
|
36
37
|
directory,
|
|
37
|
-
feedStore:
|
|
38
|
+
feedStore: createFeedStore(createStorage(directory, STORAGE_NODE), { valueEncoding: 'utf-8' })
|
|
38
39
|
};
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
async function defaultFeeds (feedStore: FeedStore, keys: Record<string, KeyPair>) : Promise<Record<string,
|
|
42
|
+
async function defaultFeeds (feedStore: FeedStore, keys: Record<string, KeyPair>) : Promise<Record<string, FeedDescriptor>> {
|
|
42
43
|
return Object.fromEntries(await Promise.all(Object.entries<KeyPair>(keys).map(async ([feed, keyPair]) =>
|
|
43
|
-
feed
|
|
44
|
-
? [feed, await feedStore.createReadWriteFeed({ key: keyPair.key, secretKey: keyPair.secretKey, metadata: { topic: 'books' } })]
|
|
45
|
-
: [feed, await feedStore.createReadWriteFeed({ key: keyPair.key, secretKey: keyPair.secretKey })]
|
|
44
|
+
[feed, await feedStore.openReadWriteFeed(keyPair.key, keyPair.secretKey)]
|
|
46
45
|
)));
|
|
47
46
|
}
|
|
48
47
|
|
|
49
|
-
function append (feed:
|
|
48
|
+
function append (feed: HypercoreFeed, message: any) {
|
|
50
49
|
return pify(feed.append.bind(feed))(message);
|
|
51
50
|
}
|
|
52
51
|
|
|
53
|
-
function head (feed:
|
|
52
|
+
function head (feed: HypercoreFeed) {
|
|
54
53
|
return pify(feed.head.bind(feed))();
|
|
55
54
|
}
|
|
56
55
|
|
|
@@ -67,13 +66,9 @@ describe('FeedStore', () => {
|
|
|
67
66
|
test('Config default', async () => {
|
|
68
67
|
const feedStore = await createFeedStore(createStorage('', STORAGE_RAM));
|
|
69
68
|
expect(feedStore).toBeInstanceOf(FeedStore);
|
|
70
|
-
expect(feedStore.opened).toBeTruthy();
|
|
71
69
|
|
|
72
70
|
const feedStore2 = new FeedStore(createStorage('', STORAGE_RAM));
|
|
73
71
|
expect(feedStore2).toBeInstanceOf(FeedStore);
|
|
74
|
-
expect(feedStore2.opened).toBeFalsy();
|
|
75
|
-
await feedStore2.open();
|
|
76
|
-
expect(feedStore2.opened).toBeTruthy();
|
|
77
72
|
});
|
|
78
73
|
|
|
79
74
|
test('Config default + custom database + custom hypercore', async () => {
|
|
@@ -85,140 +80,99 @@ describe('FeedStore', () => {
|
|
|
85
80
|
const database = hypertrie(storage.createOrOpen.bind(storage), { valueEncoding: 'json' });
|
|
86
81
|
database.list = jest.fn((_, cb) => cb(null, []));
|
|
87
82
|
|
|
88
|
-
const feedStore =
|
|
83
|
+
const feedStore = createFeedStore(createStorage('', STORAGE_RAM), {
|
|
89
84
|
hypercore: customHypercore
|
|
90
85
|
});
|
|
91
86
|
|
|
92
87
|
expect(feedStore).toBeInstanceOf(FeedStore);
|
|
93
88
|
|
|
94
|
-
await feedStore.
|
|
89
|
+
await feedStore.openReadOnlyFeed(PublicKey.random());
|
|
95
90
|
|
|
96
91
|
expect(customHypercore.mock.calls.length).toBe(1);
|
|
97
92
|
});
|
|
98
93
|
|
|
99
94
|
test('Create feed', async () => {
|
|
100
95
|
const { feedStore } = await createDefault();
|
|
101
|
-
const { booksFeed } = await defaultFeeds(feedStore, keys);
|
|
96
|
+
const { booksFeed: { feed: booksFeed } } = await defaultFeeds(feedStore, keys);
|
|
102
97
|
|
|
103
98
|
expect(booksFeed).toBeInstanceOf(hypercore);
|
|
104
99
|
|
|
105
|
-
const booksFeedDescriptor = feedStore.
|
|
100
|
+
const booksFeedDescriptor = await feedStore.openReadWriteFeed(PublicKey.from(booksFeed.key), booksFeed.secretKey);
|
|
106
101
|
expect(booksFeedDescriptor).toHaveProperty('key', PublicKey.from(booksFeed.key));
|
|
107
|
-
expect(booksFeedDescriptor?.metadata).toHaveProperty('topic', 'books');
|
|
108
102
|
|
|
109
103
|
await append(booksFeed, 'Foundation and Empire');
|
|
110
104
|
await expect(head(booksFeed)).resolves.toBe('Foundation and Empire');
|
|
111
105
|
|
|
112
106
|
// It should return the same opened instance.
|
|
113
|
-
await expect(feedStore.
|
|
107
|
+
await expect(feedStore.openReadOnlyFeed(PublicKey.from(booksFeed.key))).resolves.toBe(booksFeedDescriptor);
|
|
114
108
|
});
|
|
115
109
|
|
|
116
110
|
test('Create duplicate feed', async () => {
|
|
117
111
|
const { feedStore } = await createDefault();
|
|
118
112
|
|
|
119
|
-
const fds = await feedStore.
|
|
113
|
+
const { feed: fds } = await feedStore.openReadWriteFeed(keys.usersFeed.key, keys.usersFeed.secretKey);
|
|
114
|
+
assert(fds.secretKey);
|
|
120
115
|
|
|
121
116
|
const [usersFeed, feed2] = await Promise.all([
|
|
122
|
-
feedStore.
|
|
123
|
-
feedStore.
|
|
117
|
+
feedStore.openReadWriteFeed(PublicKey.from(fds.key), fds.secretKey),
|
|
118
|
+
feedStore.openReadWriteFeed(PublicKey.from(fds.key), fds.secretKey)
|
|
124
119
|
]);
|
|
125
|
-
expect(usersFeed).toBe(feed2);
|
|
120
|
+
expect(usersFeed.feed).toBe(feed2.feed);
|
|
126
121
|
|
|
127
|
-
await append(usersFeed, 'alice');
|
|
128
|
-
await expect(head(usersFeed)).resolves.toBe('alice');
|
|
122
|
+
await append(usersFeed.feed, 'alice');
|
|
123
|
+
await expect(head(usersFeed.feed)).resolves.toBe('alice');
|
|
129
124
|
});
|
|
130
125
|
|
|
131
126
|
test('Create and close a feed', async () => {
|
|
132
127
|
const { feedStore } = await createDefault();
|
|
133
128
|
const publicKey = PublicKey.random();
|
|
134
129
|
|
|
135
|
-
await
|
|
136
|
-
|
|
137
|
-
const foo = await feedStore.createReadOnlyFeed({ key: publicKey });
|
|
130
|
+
const foo = await feedStore.openReadOnlyFeed(publicKey);
|
|
138
131
|
expect(foo.opened).toBeTruthy();
|
|
139
|
-
expect(foo.closed).toBeFalsy();
|
|
140
132
|
|
|
141
|
-
await feedStore.
|
|
142
|
-
expect(foo.
|
|
133
|
+
await feedStore.close();
|
|
134
|
+
expect(foo.opened).toBeFalsy();
|
|
143
135
|
});
|
|
144
136
|
|
|
145
137
|
test('Descriptors', async () => {
|
|
146
138
|
const { feedStore } = await createDefault();
|
|
147
|
-
|
|
139
|
+
await defaultFeeds(feedStore, keys);
|
|
148
140
|
|
|
149
|
-
expect(
|
|
150
|
-
|
|
141
|
+
expect(Array.from((feedStore as any)._descriptors.values()).map((fd: any) => fd.key))
|
|
142
|
+
.toEqual(Object.entries(keys).map(([, keyPair]) => keyPair.key)
|
|
143
|
+
);
|
|
151
144
|
});
|
|
152
145
|
|
|
153
146
|
test('Feeds', async () => {
|
|
154
147
|
const { feedStore } = await createDefault();
|
|
155
148
|
const { booksFeed, usersFeed, groupsFeed } = await defaultFeeds(feedStore, keys);
|
|
156
149
|
|
|
157
|
-
expect(
|
|
158
|
-
|
|
159
|
-
expect(feedStore.getOpenFeed(() => false)).toBeUndefined();
|
|
160
|
-
expect(feedStore.getOpenFeeds(fd => fd.key.equals(keys.booksFeed.key))).toEqual([booksFeed]);
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
test('Close/Load feed', async () => {
|
|
164
|
-
const { feedStore } = await createDefault();
|
|
165
|
-
const { booksFeed } = await defaultFeeds(feedStore, keys);
|
|
166
|
-
|
|
167
|
-
await feedStore.closeFeed(keys.booksFeed.key);
|
|
168
|
-
expect(feedStore.getDescriptors().find(fd => fd.key.equals(keys.booksFeed.key))).toHaveProperty('opened', false);
|
|
169
|
-
|
|
170
|
-
const [feed] = await feedStore.openFeeds(fd => fd.key.equals(keys.booksFeed.key));
|
|
171
|
-
expect(feed).toBeDefined();
|
|
172
|
-
expect(feed.key).toEqual(booksFeed.key);
|
|
173
|
-
expect(feedStore.getDescriptors().find(fd => fd.key.equals(keys.booksFeed.key))).toHaveProperty('opened', true);
|
|
150
|
+
expect(Array.from((feedStore as any)._descriptors.values()).map((fd: any) => fd.key))
|
|
151
|
+
.toEqual([booksFeed.key, usersFeed.key, groupsFeed.key]);
|
|
174
152
|
});
|
|
175
153
|
|
|
176
154
|
test('Close feedStore and their feeds', async () => {
|
|
177
155
|
const { feedStore } = await createDefault();
|
|
178
156
|
await defaultFeeds(feedStore, keys);
|
|
179
157
|
|
|
180
|
-
expect(feedStore.
|
|
181
|
-
expect(feedStore.closed).toBe(false);
|
|
182
|
-
expect(feedStore.getDescriptors().filter(fd => fd.opened).length).toBe(3);
|
|
183
|
-
|
|
158
|
+
expect(Array.from((feedStore as any)._descriptors.values()).map((fd: any) => fd.key).length).toBe(3);
|
|
184
159
|
await feedStore.close();
|
|
185
|
-
expect(feedStore.
|
|
186
|
-
expect(feedStore.opened).toBe(false);
|
|
187
|
-
expect(feedStore.closed).toBe(true);
|
|
160
|
+
expect(Array.from((feedStore as any)._descriptors.values()).map((fd: any) => fd.key).length).toBe(0);
|
|
188
161
|
});
|
|
189
162
|
|
|
190
163
|
test('Default codec: binary', async () => {
|
|
191
|
-
const feedStore =
|
|
164
|
+
const feedStore = createFeedStore(createStorage('', STORAGE_RAM));
|
|
192
165
|
expect(feedStore).toBeInstanceOf(FeedStore);
|
|
193
166
|
|
|
194
167
|
const { publicKey, secretKey } = createKeyPair();
|
|
195
|
-
const feed = await feedStore.
|
|
168
|
+
const { feed } = await feedStore.openReadWriteFeed(PublicKey.from(publicKey), secretKey);
|
|
196
169
|
expect(feed).toBeInstanceOf(hypercore);
|
|
197
170
|
await append(feed, 'test');
|
|
198
171
|
await expect(head(feed)).resolves.toBeInstanceOf(Buffer);
|
|
199
172
|
});
|
|
200
173
|
|
|
201
|
-
test('on open error should unlock the descriptor', async () => {
|
|
202
|
-
const feedStore = await createFeedStore(createStorage('', STORAGE_RAM), {
|
|
203
|
-
hypercore: () => {
|
|
204
|
-
throw new Error('open error');
|
|
205
|
-
}
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
const publicKey = PublicKey.random();
|
|
209
|
-
await expect(feedStore.createReadOnlyFeed({ key: publicKey })).rejects.toThrow(/open error/);
|
|
210
|
-
|
|
211
|
-
const fd = feedStore.getDescriptors().find(fd => fd.key.equals(publicKey));
|
|
212
|
-
|
|
213
|
-
if (!fd) {
|
|
214
|
-
throw new Error('Descriptor not found');
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
await expect(fd.lock.executeSynchronized(async () => 'Unlocked')).resolves.toBe('Unlocked');
|
|
218
|
-
});
|
|
219
|
-
|
|
220
174
|
test('on close error should unlock the descriptor', async () => {
|
|
221
|
-
const feedStore =
|
|
175
|
+
const feedStore = createFeedStore(createStorage('', STORAGE_RAM), {
|
|
222
176
|
hypercore: () => ({
|
|
223
177
|
opened: true,
|
|
224
178
|
ready (cb: () => void) {
|
|
@@ -232,38 +186,9 @@ describe('FeedStore', () => {
|
|
|
232
186
|
});
|
|
233
187
|
|
|
234
188
|
const publicKey = PublicKey.random();
|
|
235
|
-
await feedStore.
|
|
236
|
-
const fd = feedStore.getDescriptors().find(fd => fd.key.equals(publicKey));
|
|
237
|
-
|
|
238
|
-
if (!fd) {
|
|
239
|
-
throw new Error('Descriptor not found');
|
|
240
|
-
}
|
|
189
|
+
await feedStore.openReadOnlyFeed(publicKey);
|
|
241
190
|
|
|
242
|
-
await expect(feedStore.closeFeed(publicKey)).rejects.toThrow(/close error/);
|
|
243
191
|
await expect(feedStore.close()).rejects.toThrow(/close error/);
|
|
244
|
-
|
|
245
|
-
await expect(fd.lock.executeSynchronized(async () => 'Unlocked')).resolves.toBe('Unlocked');
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
test('append event', async (done) => {
|
|
249
|
-
const feedStore = await createFeedStore(createStorage('', STORAGE_RAM));
|
|
250
|
-
const { publicKey, secretKey } = createKeyPair();
|
|
251
|
-
const feed = await feedStore.createReadWriteFeed({ key: PublicKey.from(publicKey), secretKey });
|
|
252
|
-
|
|
253
|
-
feedStore.appendEvent.on((descriptor) => {
|
|
254
|
-
expect(descriptor.feed).toBe(feed);
|
|
255
|
-
done();
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
feed.append('test');
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
test('openFeed should wait until FeedStore is ready', async () => {
|
|
262
|
-
const feedStore = new FeedStore(createStorage('', STORAGE_RAM));
|
|
263
|
-
await feedStore.open();
|
|
264
|
-
const publicKey = PublicKey.random();
|
|
265
|
-
const feed = await feedStore.createReadOnlyFeed({ key: publicKey });
|
|
266
|
-
expect(feed).toBeDefined();
|
|
267
192
|
});
|
|
268
193
|
|
|
269
194
|
test('feed event does not get called twice', async () => {
|
|
@@ -275,11 +200,11 @@ describe('FeedStore', () => {
|
|
|
275
200
|
});
|
|
276
201
|
|
|
277
202
|
const key = PublicKey.random();
|
|
278
|
-
await feedStore.
|
|
203
|
+
await feedStore.openReadOnlyFeed(key);
|
|
279
204
|
|
|
280
|
-
await feedStore.
|
|
281
|
-
await feedStore.
|
|
282
|
-
await feedStore.
|
|
205
|
+
await feedStore.openReadOnlyFeed(key);
|
|
206
|
+
await feedStore.openReadOnlyFeed(key);
|
|
207
|
+
await feedStore.openReadOnlyFeed(key);
|
|
283
208
|
|
|
284
209
|
await sleep(20); // To flush events
|
|
285
210
|
|
package/src/feed-store.ts
CHANGED
|
@@ -6,30 +6,25 @@ import assert from 'assert';
|
|
|
6
6
|
import defaultHypercore from 'hypercore';
|
|
7
7
|
|
|
8
8
|
import { synchronized, Event } from '@dxos/async';
|
|
9
|
-
import { PublicKey
|
|
9
|
+
import { PublicKey } from '@dxos/crypto';
|
|
10
10
|
import { IStorage } from '@dxos/random-access-multi-storage';
|
|
11
11
|
|
|
12
12
|
import FeedDescriptor from './feed-descriptor';
|
|
13
|
-
import type {
|
|
13
|
+
import type { Hypercore } from './hypercore-types';
|
|
14
14
|
import type { ValueEncoding } from './types';
|
|
15
15
|
|
|
16
|
-
type DescriptorCallback = (descriptor: FeedDescriptor) => boolean;
|
|
17
|
-
|
|
18
16
|
export interface CreateDescriptorOptions {
|
|
19
17
|
key: PublicKey,
|
|
20
|
-
secretKey?: Buffer
|
|
21
|
-
metadata?: any
|
|
18
|
+
secretKey?: Buffer
|
|
22
19
|
}
|
|
23
20
|
|
|
24
21
|
export interface CreateReadWriteFeedOptions {
|
|
25
22
|
key: PublicKey,
|
|
26
23
|
secretKey: Buffer
|
|
27
|
-
metadata?: any
|
|
28
24
|
}
|
|
29
25
|
|
|
30
26
|
export interface CreateReadOnlyFeedOptions {
|
|
31
27
|
key: PublicKey
|
|
32
|
-
metadata?: any
|
|
33
28
|
}
|
|
34
29
|
|
|
35
30
|
export interface FeedStoreOptions {
|
|
@@ -54,12 +49,6 @@ export class FeedStore {
|
|
|
54
49
|
private _valueEncoding: ValueEncoding | undefined;
|
|
55
50
|
private _hypercore: Hypercore;
|
|
56
51
|
private _descriptors: Map<string, FeedDescriptor>;
|
|
57
|
-
private _open: boolean;
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Is emitted when data gets appended to one of the FeedStore's feeds represented by FeedDescriptors.
|
|
61
|
-
*/
|
|
62
|
-
readonly appendEvent = new Event<FeedDescriptor>();
|
|
63
52
|
|
|
64
53
|
/**
|
|
65
54
|
* Is emitted when a new feed represented by FeedDescriptor is opened.
|
|
@@ -84,16 +73,6 @@ export class FeedStore {
|
|
|
84
73
|
this._hypercore = hypercore;
|
|
85
74
|
|
|
86
75
|
this._descriptors = new Map();
|
|
87
|
-
|
|
88
|
-
this._open = false;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
get opened () {
|
|
92
|
-
return this._open;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
get closed () {
|
|
96
|
-
return !this._open;
|
|
97
76
|
}
|
|
98
77
|
|
|
99
78
|
/**
|
|
@@ -103,187 +82,52 @@ export class FeedStore {
|
|
|
103
82
|
return this._storage;
|
|
104
83
|
}
|
|
105
84
|
|
|
106
|
-
@synchronized
|
|
107
|
-
async open () {
|
|
108
|
-
if (this._open) {
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
this._open = true;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
85
|
@synchronized
|
|
115
86
|
async close () {
|
|
116
|
-
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
this._open = false;
|
|
120
|
-
|
|
121
|
-
await Promise.all(this
|
|
122
|
-
.getDescriptors()
|
|
123
|
-
.map(descriptor => descriptor.close())
|
|
124
|
-
);
|
|
125
|
-
|
|
87
|
+
await Promise.all(Array.from(this._descriptors.values()).map(descriptor => descriptor.close()));
|
|
126
88
|
this._descriptors.clear();
|
|
127
89
|
}
|
|
128
90
|
|
|
129
|
-
/**
|
|
130
|
-
* Get the list of descriptors.
|
|
131
|
-
*/
|
|
132
|
-
getDescriptors () {
|
|
133
|
-
return Array.from(this._descriptors.values());
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Get desciptor by its public key
|
|
138
|
-
*/
|
|
139
|
-
getDescriptor (key: PublicKeyLike) {
|
|
140
|
-
return this.getDescriptors().find(descriptor => descriptor.key.equals(key));
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Fast access to a descriptor
|
|
145
|
-
*/
|
|
146
|
-
getDescriptorByDiscoveryKey (discoverKey: Buffer) {
|
|
147
|
-
return this._descriptors.get(discoverKey.toString('hex'));
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Get the list of opened feeds, with optional filter.
|
|
152
|
-
*/
|
|
153
|
-
getOpenFeeds (callback?: DescriptorCallback): HypercoreFeed[] {
|
|
154
|
-
const notNull = <T>(value: T | null): value is T => Boolean(value);
|
|
155
|
-
return this.getDescriptors()
|
|
156
|
-
.filter(descriptor => descriptor.opened && (!callback || callback(descriptor)))
|
|
157
|
-
.map(descriptor => descriptor.feed)
|
|
158
|
-
.filter(notNull);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Find an opened feed using a filter callback.
|
|
163
|
-
*/
|
|
164
|
-
getOpenFeed (callback: DescriptorCallback): HypercoreFeed | undefined {
|
|
165
|
-
const descriptor = this.getDescriptors()
|
|
166
|
-
.find(descriptor => descriptor.opened && callback(descriptor));
|
|
167
|
-
|
|
168
|
-
if (descriptor && descriptor.feed) {
|
|
169
|
-
return descriptor.feed;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
return undefined;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Checks if feedstore has a feed with specified key.
|
|
177
|
-
*/
|
|
178
|
-
hasFeed (key: PublicKeyLike): boolean {
|
|
179
|
-
return this.getDescriptors().some(fd => fd.key.equals(key));
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Open multiple feeds using a filter callback.
|
|
184
|
-
*/
|
|
185
|
-
@synchronized
|
|
186
|
-
async openFeeds (callback: DescriptorCallback): Promise<HypercoreFeed[]> {
|
|
187
|
-
assert(this._open, 'FeedStore closed');
|
|
188
|
-
|
|
189
|
-
const descriptors = this.getDescriptors()
|
|
190
|
-
.filter(descriptor => callback(descriptor));
|
|
191
|
-
|
|
192
|
-
return Promise.all(descriptors.map(descriptor => descriptor.open()));
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Open a feed to FeedStore.
|
|
197
|
-
*/
|
|
198
|
-
@synchronized
|
|
199
|
-
async openFeed (key: PublicKey): Promise<HypercoreFeed> {
|
|
200
|
-
assert(this._open, 'FeedStore closed');
|
|
201
|
-
|
|
202
|
-
const descriptor = this.getDescriptors().find(fd => fd.key.equals(key));
|
|
203
|
-
|
|
204
|
-
assert(descriptor, 'Descriptor not found');
|
|
205
|
-
|
|
206
|
-
const feed = await descriptor.open();
|
|
207
|
-
|
|
208
|
-
return feed;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
91
|
/**
|
|
212
92
|
* Create a feed to Feedstore
|
|
213
93
|
*/
|
|
214
|
-
async
|
|
215
|
-
this.
|
|
216
|
-
|
|
94
|
+
async openReadWriteFeed (key: PublicKey, secretKey: Buffer): Promise<FeedDescriptor> {
|
|
95
|
+
const descriptor = this._descriptors.get(key.toString());
|
|
96
|
+
if (descriptor && descriptor.secretKey) {
|
|
97
|
+
return descriptor;
|
|
98
|
+
}
|
|
99
|
+
return this._createDescriptor({ key, secretKey });
|
|
217
100
|
}
|
|
218
101
|
|
|
219
102
|
/**
|
|
220
103
|
* Create a readonly feed to Feedstore
|
|
221
104
|
*/
|
|
222
|
-
async
|
|
223
|
-
this._createDescriptor(
|
|
224
|
-
return
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Close a feed by the key.
|
|
229
|
-
*/
|
|
230
|
-
@synchronized
|
|
231
|
-
async closeFeed (key: PublicKey) {
|
|
232
|
-
assert(this._open, 'FeedStore closed');
|
|
233
|
-
|
|
234
|
-
const descriptor = this.getDescriptors().find(fd => fd.key.equals(key));
|
|
235
|
-
|
|
236
|
-
if (!descriptor) {
|
|
237
|
-
throw new Error(`Feed not found: ${key.toString()}`);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
await descriptor.close();
|
|
105
|
+
async openReadOnlyFeed (key: PublicKey): Promise<FeedDescriptor> {
|
|
106
|
+
const descriptor = this._descriptors.get(key.toString()) ?? await this._createDescriptor({ key });
|
|
107
|
+
return descriptor;
|
|
241
108
|
}
|
|
242
109
|
|
|
243
110
|
/**
|
|
244
111
|
* Factory to create a new FeedDescriptor.
|
|
245
112
|
*/
|
|
246
|
-
private _createDescriptor (options: CreateDescriptorOptions) {
|
|
247
|
-
const { key, secretKey
|
|
248
|
-
|
|
249
|
-
const existing = this.getDescriptors().find(fd => fd.key.equals(key));
|
|
250
|
-
if (existing) {
|
|
251
|
-
return existing;
|
|
252
|
-
}
|
|
113
|
+
private async _createDescriptor (options: CreateDescriptorOptions) {
|
|
114
|
+
const { key, secretKey } = options;
|
|
253
115
|
|
|
254
116
|
const descriptor = new FeedDescriptor({
|
|
255
117
|
storage: this._storage,
|
|
256
118
|
key,
|
|
257
119
|
secretKey,
|
|
258
120
|
valueEncoding: this._valueEncoding,
|
|
259
|
-
metadata,
|
|
260
121
|
hypercore: this._hypercore
|
|
261
122
|
});
|
|
262
123
|
|
|
263
124
|
this._descriptors.set(
|
|
264
|
-
descriptor.
|
|
125
|
+
descriptor.key.toString(),
|
|
265
126
|
descriptor
|
|
266
127
|
);
|
|
267
128
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
descriptor.watch(async (event) => {
|
|
271
|
-
if (event === 'updated') {
|
|
272
|
-
return;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
const { feed } = descriptor;
|
|
276
|
-
|
|
277
|
-
if (event === 'opened' && feed) {
|
|
278
|
-
feed.on('append', append);
|
|
279
|
-
this.feedOpenedEvent.emit(descriptor);
|
|
280
|
-
return;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
if (event === 'closed' && feed) {
|
|
284
|
-
feed.removeListener('append', append);
|
|
285
|
-
}
|
|
286
|
-
});
|
|
129
|
+
await descriptor.open();
|
|
130
|
+
this.feedOpenedEvent.emit(descriptor);
|
|
287
131
|
|
|
288
132
|
return descriptor;
|
|
289
133
|
}
|