@dxos/echo-pipeline 0.5.9-main.ea1d25b → 0.5.9-main.eacfffa
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/lib/browser/{chunk-I2J5TTHJ.mjs → chunk-ZCEMVITI.mjs} +28 -7
- package/dist/lib/browser/{chunk-I2J5TTHJ.mjs.map → chunk-ZCEMVITI.mjs.map} +1 -1
- package/dist/lib/browser/index.mjs +138 -230
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +11 -7
- package/dist/lib/browser/testing/index.mjs.map +1 -1
- package/dist/lib/node/{chunk-QPCNQ4ZK.cjs → chunk-V2O4ACHA.cjs} +31 -10
- package/dist/lib/node/{chunk-QPCNQ4ZK.cjs.map → chunk-V2O4ACHA.cjs.map} +1 -1
- package/dist/lib/node/index.cjs +172 -259
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/testing/index.cjs +21 -17
- package/dist/lib/node/testing/index.cjs.map +1 -1
- package/dist/types/src/automerge/automerge-doc-loader.d.ts.map +1 -1
- package/dist/types/src/automerge/automerge-host.d.ts +1 -9
- package/dist/types/src/automerge/automerge-host.d.ts.map +1 -1
- package/dist/types/src/automerge/echo-network-adapter.d.ts +6 -0
- package/dist/types/src/automerge/echo-network-adapter.d.ts.map +1 -1
- package/dist/types/src/automerge/echo-replicator.d.ts +1 -0
- package/dist/types/src/automerge/echo-replicator.d.ts.map +1 -1
- package/dist/types/src/automerge/index.d.ts +0 -1
- package/dist/types/src/automerge/index.d.ts.map +1 -1
- package/dist/types/src/automerge/mesh-echo-replicator.d.ts.map +1 -1
- package/package.json +33 -33
- package/src/automerge/automerge-doc-loader.test.ts +2 -3
- package/src/automerge/automerge-doc-loader.ts +4 -2
- package/src/automerge/automerge-host.ts +8 -35
- package/src/automerge/echo-network-adapter.ts +24 -8
- package/src/automerge/echo-replicator.ts +2 -0
- package/src/automerge/index.ts +0 -1
- package/src/automerge/mesh-echo-replicator.ts +8 -1
- package/src/automerge/storage-adapter.test.ts +103 -139
- package/src/space/space-protocol.test.ts +2 -0
- package/dist/types/src/automerge/automerge-storage-adapter.d.ts +0 -16
- package/dist/types/src/automerge/automerge-storage-adapter.d.ts.map +0 -1
- package/dist/types/src/automerge/automerge-storage/342/200/223wrapper.d.ts +0 -25
- package/dist/types/src/automerge/automerge-storage/342/200/223wrapper.d.ts.map +0 -1
- package/dist/types/src/automerge/migrations.d.ts +0 -7
- package/dist/types/src/automerge/migrations.d.ts.map +0 -1
- package/src/automerge/automerge-storage-adapter.ts +0 -103
- package/src/automerge/automerge-storage/342/200/223wrapper.ts +0 -59
- package/src/automerge/migrations.ts +0 -42
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/echo-pipeline",
|
|
3
|
-
"version": "0.5.9-main.
|
|
3
|
+
"version": "0.5.9-main.eacfffa",
|
|
4
4
|
"description": "ECHO database.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -39,38 +39,38 @@
|
|
|
39
39
|
"crc-32": "^1.2.2",
|
|
40
40
|
"level": "^8.0.1",
|
|
41
41
|
"level-transcoder": "^1.0.1",
|
|
42
|
-
"@dxos/
|
|
43
|
-
"@dxos/
|
|
44
|
-
"@dxos/automerge": "0.5.9-main.
|
|
45
|
-
"@dxos/context": "0.5.9-main.
|
|
46
|
-
"@dxos/
|
|
47
|
-
"@dxos/crypto": "0.5.9-main.
|
|
48
|
-
"@dxos/
|
|
49
|
-
"@dxos/echo-protocol": "0.5.9-main.
|
|
50
|
-
"@dxos/
|
|
51
|
-
"@dxos/
|
|
52
|
-
"@dxos/
|
|
53
|
-
"@dxos/
|
|
54
|
-
"@dxos/
|
|
55
|
-
"@dxos/
|
|
56
|
-
"@dxos/
|
|
57
|
-
"@dxos/
|
|
58
|
-
"@dxos/log": "0.5.9-main.
|
|
59
|
-
"@dxos/messaging": "0.5.9-main.
|
|
60
|
-
"@dxos/network-manager": "0.5.9-main.
|
|
61
|
-
"@dxos/
|
|
62
|
-
"@dxos/
|
|
63
|
-
"@dxos/
|
|
64
|
-
"@dxos/rpc": "0.5.9-main.
|
|
65
|
-
"@dxos/teleport": "0.5.9-main.
|
|
66
|
-
"@dxos/teleport-extension-automerge-replicator": "0.5.9-main.
|
|
67
|
-
"@dxos/teleport-extension-gossip": "0.5.9-main.
|
|
68
|
-
"@dxos/teleport-extension-
|
|
69
|
-
"@dxos/
|
|
70
|
-
"@dxos/
|
|
71
|
-
"@dxos/
|
|
72
|
-
"@dxos/
|
|
73
|
-
"@dxos/
|
|
42
|
+
"@dxos/codec-protobuf": "0.5.9-main.eacfffa",
|
|
43
|
+
"@dxos/async": "0.5.9-main.eacfffa",
|
|
44
|
+
"@dxos/automerge": "0.5.9-main.eacfffa",
|
|
45
|
+
"@dxos/context": "0.5.9-main.eacfffa",
|
|
46
|
+
"@dxos/debug": "0.5.9-main.eacfffa",
|
|
47
|
+
"@dxos/crypto": "0.5.9-main.eacfffa",
|
|
48
|
+
"@dxos/credentials": "0.5.9-main.eacfffa",
|
|
49
|
+
"@dxos/echo-protocol": "0.5.9-main.eacfffa",
|
|
50
|
+
"@dxos/echo-schema": "0.5.9-main.eacfffa",
|
|
51
|
+
"@dxos/hypercore": "0.5.9-main.eacfffa",
|
|
52
|
+
"@dxos/feed-store": "0.5.9-main.eacfffa",
|
|
53
|
+
"@dxos/invariant": "0.5.9-main.eacfffa",
|
|
54
|
+
"@dxos/keys": "0.5.9-main.eacfffa",
|
|
55
|
+
"@dxos/indexing": "0.5.9-main.eacfffa",
|
|
56
|
+
"@dxos/keyring": "0.5.9-main.eacfffa",
|
|
57
|
+
"@dxos/kv-store": "0.5.9-main.eacfffa",
|
|
58
|
+
"@dxos/log": "0.5.9-main.eacfffa",
|
|
59
|
+
"@dxos/messaging": "0.5.9-main.eacfffa",
|
|
60
|
+
"@dxos/network-manager": "0.5.9-main.eacfffa",
|
|
61
|
+
"@dxos/protocols": "0.5.9-main.eacfffa",
|
|
62
|
+
"@dxos/node-std": "0.5.9-main.eacfffa",
|
|
63
|
+
"@dxos/random-access-storage": "0.5.9-main.eacfffa",
|
|
64
|
+
"@dxos/rpc": "0.5.9-main.eacfffa",
|
|
65
|
+
"@dxos/teleport": "0.5.9-main.eacfffa",
|
|
66
|
+
"@dxos/teleport-extension-automerge-replicator": "0.5.9-main.eacfffa",
|
|
67
|
+
"@dxos/teleport-extension-gossip": "0.5.9-main.eacfffa",
|
|
68
|
+
"@dxos/teleport-extension-object-sync": "0.5.9-main.eacfffa",
|
|
69
|
+
"@dxos/timeframe": "0.5.9-main.eacfffa",
|
|
70
|
+
"@dxos/teleport-extension-replicator": "0.5.9-main.eacfffa",
|
|
71
|
+
"@dxos/tracing": "0.5.9-main.eacfffa",
|
|
72
|
+
"@dxos/util": "0.5.9-main.eacfffa",
|
|
73
|
+
"@dxos/typings": "0.5.9-main.eacfffa"
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
76
76
|
"fast-check": "^3.19.0",
|
|
@@ -22,6 +22,7 @@ import { createIdFromSpaceKey } from '../space';
|
|
|
22
22
|
const ctx = new Context();
|
|
23
23
|
const SPACE_KEY = PublicKey.random();
|
|
24
24
|
const randomId = () => generateEchoId();
|
|
25
|
+
|
|
25
26
|
describe('AutomergeDocumentLoader', () => {
|
|
26
27
|
test('space access is set on root doc handle and it is accessible', async () => {
|
|
27
28
|
const { loader, spaceRootDocHandle } = await setupTest();
|
|
@@ -77,9 +78,7 @@ describe('AutomergeDocumentLoader', () => {
|
|
|
77
78
|
const repo = new Repo({ network: [] });
|
|
78
79
|
const loader = new AutomergeDocumentLoaderImpl(spaceId, repo, SPACE_KEY);
|
|
79
80
|
const spaceRootDocHandle = repo.create<SpaceDoc>();
|
|
80
|
-
await loader.loadSpaceRootDocHandle(ctx, {
|
|
81
|
-
rootUrl: spaceRootDocHandle.url,
|
|
82
|
-
});
|
|
81
|
+
await loader.loadSpaceRootDocHandle(ctx, { rootUrl: spaceRootDocHandle.url });
|
|
83
82
|
return { loader, spaceRootDocHandle, repo };
|
|
84
83
|
};
|
|
85
84
|
|
|
@@ -6,7 +6,7 @@ import { Event } from '@dxos/async';
|
|
|
6
6
|
import { type DocHandle, type AutomergeUrl, type DocumentId, type Repo } from '@dxos/automerge/automerge-repo';
|
|
7
7
|
import { cancelWithContext, type Context } from '@dxos/context';
|
|
8
8
|
import { warnAfterTimeout } from '@dxos/debug';
|
|
9
|
-
import { type SpaceState, type SpaceDoc } from '@dxos/echo-protocol';
|
|
9
|
+
import { type SpaceState, type SpaceDoc, SpaceDocVersion } from '@dxos/echo-protocol';
|
|
10
10
|
import { invariant } from '@dxos/invariant';
|
|
11
11
|
import { type PublicKey, type SpaceId } from '@dxos/keys';
|
|
12
12
|
import { log } from '@dxos/log';
|
|
@@ -125,7 +125,9 @@ export class AutomergeDocumentLoaderImpl implements AutomergeDocumentLoader {
|
|
|
125
125
|
|
|
126
126
|
public createDocumentForObject(objectId: string): DocHandle<SpaceDoc> {
|
|
127
127
|
invariant(this._spaceRootDocHandle);
|
|
128
|
-
const spaceDocHandle = this._repo.create<SpaceDoc>(
|
|
128
|
+
const spaceDocHandle = this._repo.create<SpaceDoc>({
|
|
129
|
+
version: SpaceDocVersion.CURRENT,
|
|
130
|
+
});
|
|
129
131
|
this._initDocAccess(spaceDocHandle);
|
|
130
132
|
this.onObjectBoundToDocument(spaceDocHandle, objectId);
|
|
131
133
|
this._spaceRootDocHandle.change((newDoc: SpaceDoc) => {
|
|
@@ -19,7 +19,6 @@ import { type IndexMetadataStore } from '@dxos/indexing';
|
|
|
19
19
|
import { invariant } from '@dxos/invariant';
|
|
20
20
|
import { PublicKey } from '@dxos/keys';
|
|
21
21
|
import { type SublevelDB } from '@dxos/kv-store';
|
|
22
|
-
import { log } from '@dxos/log';
|
|
23
22
|
import { objectPointerCodec } from '@dxos/protocols';
|
|
24
23
|
import {
|
|
25
24
|
type FlushRequest,
|
|
@@ -27,7 +26,6 @@ import {
|
|
|
27
26
|
type SyncRepoRequest,
|
|
28
27
|
type SyncRepoResponse,
|
|
29
28
|
} from '@dxos/protocols/proto/dxos/echo/service';
|
|
30
|
-
import { type Directory } from '@dxos/random-access-storage';
|
|
31
29
|
import { trace } from '@dxos/tracing';
|
|
32
30
|
import { mapValues } from '@dxos/util';
|
|
33
31
|
|
|
@@ -35,17 +33,12 @@ import { EchoNetworkAdapter } from './echo-network-adapter';
|
|
|
35
33
|
import { type EchoReplicator } from './echo-replicator';
|
|
36
34
|
import { LevelDBStorageAdapter, type BeforeSaveParams } from './leveldb-storage-adapter';
|
|
37
35
|
import { LocalHostNetworkAdapter } from './local-host-network-adapter';
|
|
38
|
-
import { levelMigration } from './migrations';
|
|
39
36
|
|
|
40
37
|
// TODO: Remove
|
|
41
38
|
export type { DocumentId };
|
|
42
39
|
|
|
43
40
|
export type AutomergeHostParams = {
|
|
44
41
|
db: SublevelDB;
|
|
45
|
-
/**
|
|
46
|
-
* For migration purposes.
|
|
47
|
-
*/
|
|
48
|
-
directory?: Directory;
|
|
49
42
|
|
|
50
43
|
indexMetadataStore: IndexMetadataStore;
|
|
51
44
|
};
|
|
@@ -54,8 +47,6 @@ export type AutomergeHostParams = {
|
|
|
54
47
|
export class AutomergeHost {
|
|
55
48
|
private readonly _indexMetadataStore: IndexMetadataStore;
|
|
56
49
|
private readonly _ctx = new Context();
|
|
57
|
-
private readonly _directory?: Directory;
|
|
58
|
-
private readonly _db: SublevelDB;
|
|
59
50
|
private readonly _echoNetworkAdapter = new EchoNetworkAdapter({
|
|
60
51
|
getContainingSpaceForDocument: this._getContainingSpaceForDocument.bind(this),
|
|
61
52
|
});
|
|
@@ -67,11 +58,14 @@ export class AutomergeHost {
|
|
|
67
58
|
@trace.info()
|
|
68
59
|
private _peerId!: string;
|
|
69
60
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
61
|
+
constructor({ db, indexMetadataStore }: AutomergeHostParams) {
|
|
62
|
+
this._storage = new LevelDBStorageAdapter({
|
|
63
|
+
db,
|
|
64
|
+
callbacks: {
|
|
65
|
+
beforeSave: async (params) => this._beforeSave(params),
|
|
66
|
+
afterSave: async () => this._afterSave(),
|
|
67
|
+
},
|
|
68
|
+
});
|
|
75
69
|
this._indexMetadataStore = indexMetadataStore;
|
|
76
70
|
}
|
|
77
71
|
|
|
@@ -79,17 +73,7 @@ export class AutomergeHost {
|
|
|
79
73
|
// TODO(burdon): Should this be stable?
|
|
80
74
|
this._peerId = `host-${PublicKey.random().toHex()}` as PeerId;
|
|
81
75
|
|
|
82
|
-
// TODO(mykola): remove this before 0.6 release.
|
|
83
|
-
this._directory && (await levelMigration({ db: this._db, directory: this._directory }));
|
|
84
|
-
this._storage = new LevelDBStorageAdapter({
|
|
85
|
-
db: this._db,
|
|
86
|
-
callbacks: {
|
|
87
|
-
beforeSave: async (params) => this._beforeSave(params),
|
|
88
|
-
afterSave: async () => this._afterSave(),
|
|
89
|
-
},
|
|
90
|
-
});
|
|
91
76
|
await this._storage.open?.();
|
|
92
|
-
|
|
93
77
|
this._clientNetwork = new LocalHostNetworkAdapter();
|
|
94
78
|
|
|
95
79
|
// Construct the automerge repo.
|
|
@@ -146,17 +130,6 @@ export class AutomergeHost {
|
|
|
146
130
|
return false;
|
|
147
131
|
}
|
|
148
132
|
|
|
149
|
-
// Workaround for https://github.com/automerge/automerge-repo/pull/292
|
|
150
|
-
// NOTE: This must override the per-connection policy.
|
|
151
|
-
const doc = this._repo.handles[documentId]?.docSync();
|
|
152
|
-
if (!doc) {
|
|
153
|
-
// TODO(dmaretskyi): Verify that this works as intended.
|
|
154
|
-
// TODO(dmaretskyi): Move to MESH replicator?
|
|
155
|
-
const isRequested = this._requestedDocs.has(`automerge:${documentId}`);
|
|
156
|
-
log('doc share policy check', { peerId, documentId, isRequested });
|
|
157
|
-
return isRequested;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
133
|
const peerMetadata = this.repo.peerMetadataByPeerId[peerId];
|
|
161
134
|
if ((peerMetadata as any)?.dxos_peerSource === 'EchoNetworkAdapter') {
|
|
162
135
|
return this._echoNetworkAdapter.shouldAdvertize(peerId, { documentId });
|
|
@@ -93,6 +93,7 @@ export class EchoNetworkAdapter extends NetworkAdapter {
|
|
|
93
93
|
peerId: this.peerId,
|
|
94
94
|
onConnectionOpen: this._onConnectionOpen.bind(this),
|
|
95
95
|
onConnectionClosed: this._onConnectionClosed.bind(this),
|
|
96
|
+
onConnectionAuthScopeChanged: this._onConnectionAuthScopeChanged.bind(this),
|
|
96
97
|
getContainingSpaceForDocument: this._params.getContainingSpaceForDocument,
|
|
97
98
|
});
|
|
98
99
|
}
|
|
@@ -101,7 +102,6 @@ export class EchoNetworkAdapter extends NetworkAdapter {
|
|
|
101
102
|
async removeReplicator(replicator: EchoReplicator) {
|
|
102
103
|
invariant(this._lifecycleState === LifecycleState.OPEN);
|
|
103
104
|
invariant(this._replicators.has(replicator));
|
|
104
|
-
('');
|
|
105
105
|
await replicator.disconnect();
|
|
106
106
|
this._replicators.delete(replicator);
|
|
107
107
|
}
|
|
@@ -142,13 +142,19 @@ export class EchoNetworkAdapter extends NetworkAdapter {
|
|
|
142
142
|
});
|
|
143
143
|
|
|
144
144
|
log('emit peer-candidate', { peerId: connection.peerId });
|
|
145
|
-
this.
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
145
|
+
this._emitPeerCandidate(connection);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Trigger doc-synchronizer shared documents set recalculation. Happens on peer-candidate.
|
|
150
|
+
* TODO(y): replace with a proper API call when sharePolicy update becomes supported by automerge-repo
|
|
151
|
+
*/
|
|
152
|
+
private _onConnectionAuthScopeChanged(connection: ReplicatorConnection) {
|
|
153
|
+
log('Connection auth scope changed', { peerId: connection.peerId });
|
|
154
|
+
const entry = this._connections.get(connection.peerId as PeerId);
|
|
155
|
+
invariant(entry);
|
|
156
|
+
this.emit('peer-disconnected', { peerId: connection.peerId as PeerId });
|
|
157
|
+
this._emitPeerCandidate(connection);
|
|
152
158
|
}
|
|
153
159
|
|
|
154
160
|
private _onConnectionClosed(connection: ReplicatorConnection) {
|
|
@@ -164,6 +170,16 @@ export class EchoNetworkAdapter extends NetworkAdapter {
|
|
|
164
170
|
|
|
165
171
|
this._connections.delete(connection.peerId as PeerId);
|
|
166
172
|
}
|
|
173
|
+
|
|
174
|
+
private _emitPeerCandidate(connection: ReplicatorConnection) {
|
|
175
|
+
this.emit('peer-candidate', {
|
|
176
|
+
peerId: connection.peerId as PeerId,
|
|
177
|
+
peerMetadata: {
|
|
178
|
+
// TODO(dmaretskyi): Refactor this.
|
|
179
|
+
dxos_peerSource: 'EchoNetworkAdapter',
|
|
180
|
+
} as any,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
167
183
|
}
|
|
168
184
|
|
|
169
185
|
type ConnectionEntry = {
|
|
@@ -27,6 +27,8 @@ export interface EchoReplicatorContext {
|
|
|
27
27
|
|
|
28
28
|
onConnectionClosed(connection: ReplicatorConnection): void;
|
|
29
29
|
|
|
30
|
+
onConnectionAuthScopeChanged(connection: ReplicatorConnection): void;
|
|
31
|
+
|
|
30
32
|
getContainingSpaceForDocument(documentId: string): Promise<PublicKey | null>;
|
|
31
33
|
}
|
|
32
34
|
|
package/src/automerge/index.ts
CHANGED
|
@@ -59,7 +59,9 @@ export class MeshEchoReplicator implements EchoReplicator {
|
|
|
59
59
|
log('onRemoteConnected', { peerId: connection.peerId });
|
|
60
60
|
invariant(this._context);
|
|
61
61
|
|
|
62
|
-
if (
|
|
62
|
+
if (this._connectionsPerPeer.has(connection.peerId)) {
|
|
63
|
+
this._context.onConnectionAuthScopeChanged(connection);
|
|
64
|
+
} else {
|
|
63
65
|
this._connectionsPerPeer.set(connection.peerId, connection);
|
|
64
66
|
await connection.enable();
|
|
65
67
|
this._context.onConnectionOpen(connection);
|
|
@@ -119,6 +121,11 @@ export class MeshEchoReplicator implements EchoReplicator {
|
|
|
119
121
|
authorizeDevice(spaceKey: PublicKey, deviceKey: PublicKey) {
|
|
120
122
|
log('authorizeDevice', { spaceKey, deviceKey });
|
|
121
123
|
defaultMap(this._authorizedDevices, spaceKey, () => new ComplexSet(PublicKey.hash)).add(deviceKey);
|
|
124
|
+
for (const connection of this._connections) {
|
|
125
|
+
if (connection.remoteDeviceKey && connection.remoteDeviceKey.equals(deviceKey)) {
|
|
126
|
+
this._context?.onConnectionAuthScopeChanged(connection);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
122
129
|
}
|
|
123
130
|
}
|
|
124
131
|
|
|
@@ -4,153 +4,117 @@
|
|
|
4
4
|
|
|
5
5
|
import { expect } from 'chai';
|
|
6
6
|
|
|
7
|
-
import { type StorageAdapterInterface } from '@dxos/automerge/automerge-repo';
|
|
8
7
|
import { randomBytes } from '@dxos/crypto';
|
|
9
8
|
import { PublicKey } from '@dxos/keys';
|
|
10
9
|
import { createTestLevel } from '@dxos/kv-store/testing';
|
|
11
|
-
import { StorageType, createStorage } from '@dxos/random-access-storage';
|
|
12
10
|
import { afterTest, describe, test } from '@dxos/test';
|
|
13
|
-
import { arrayToBuffer, bufferToArray
|
|
11
|
+
import { arrayToBuffer, bufferToArray } from '@dxos/util';
|
|
14
12
|
|
|
15
|
-
import { AutomergeStorageAdapter } from './automerge-storage-adapter';
|
|
16
13
|
import { LevelDBStorageAdapter } from './leveldb-storage-adapter';
|
|
17
14
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
adapter:
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
test('should store and retrieve data', async () => {
|
|
36
|
-
const { adapter } = await createAdapter();
|
|
37
|
-
|
|
38
|
-
await adapter.save(chunks[0].key, chunks[0].data);
|
|
39
|
-
expect(await adapter.load(chunks[0].key)).to.deep.equal(chunks[0].data);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
test('loadRange return inputs with correct prefixes', async () => {
|
|
43
|
-
const { adapter } = await createAdapter();
|
|
44
|
-
|
|
45
|
-
for (const chunk of chunks) {
|
|
46
|
-
await adapter.save(chunk.key, chunk.data);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
expect((await adapter.loadRange(['a', 'b'])).length).to.equal(4);
|
|
50
|
-
expect((await adapter.loadRange(['a', 'b', 'c']))[0]).to.deep.equal(chunks[0]);
|
|
51
|
-
expect((await adapter.loadRange(['a', 'b', 'c'])).length).to.equal(2);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
test('deletion works', async () => {
|
|
55
|
-
const { adapter } = await createAdapter();
|
|
56
|
-
|
|
57
|
-
for (const chunk of chunks) {
|
|
58
|
-
await adapter.save(chunk.key, chunk.data);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
await adapter.remove(['a', 'b', 'c', '1']);
|
|
62
|
-
|
|
63
|
-
expect((await adapter.loadRange(['a', 'b'])).length).to.equal(3);
|
|
64
|
-
expect((await adapter.loadRange(['a', 'b', 'c'])).length).to.equal(1);
|
|
65
|
-
|
|
66
|
-
await adapter.removeRange(['a', 'b', 'd']);
|
|
67
|
-
|
|
68
|
-
expect((await adapter.loadRange(['a', 'b'])).length).to.equal(1);
|
|
69
|
-
expect((await adapter.loadRange(['a', 'b']))[0]).to.deep.equal(chunks[1]);
|
|
70
|
-
expect(await adapter.load(['a', 'b', 'c', '2'])).to.deep.equal(chunks[1].data);
|
|
71
|
-
expect(await adapter.load(['a', 'b', 'd', '3'])).to.be.undefined;
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
test('loadRange', async () => {
|
|
75
|
-
const root = `/tmp/${randomBytes(16).toString('hex')}`;
|
|
76
|
-
{
|
|
77
|
-
const { adapter, close } = await createAdapter(root);
|
|
78
|
-
await adapter.save(['test', '1'], bufferToArray(Buffer.from('one')));
|
|
79
|
-
await adapter.save(['test', '2'], bufferToArray(Buffer.from('two')));
|
|
80
|
-
await adapter.save(['bar', '1'], bufferToArray(Buffer.from('bar')));
|
|
81
|
-
await close();
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
{
|
|
85
|
-
const { adapter } = await createAdapter(root);
|
|
86
|
-
const range = await adapter.loadRange(['test']);
|
|
87
|
-
expect(range.map((chunk) => arrayToBuffer(chunk.data!).toString())).to.deep.eq(['one', 'two']);
|
|
88
|
-
expect(range.map((chunk) => chunk.key)).to.deep.eq([
|
|
89
|
-
['test', '1'],
|
|
90
|
-
['test', '2'],
|
|
91
|
-
]);
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
test('removeRange', async () => {
|
|
96
|
-
const root = `/tmp/${randomBytes(16).toString('hex')}`;
|
|
97
|
-
{
|
|
98
|
-
const { adapter, close } = await createAdapter(root);
|
|
99
|
-
await adapter.save(['test', '1'], bufferToArray(Buffer.from('one')));
|
|
100
|
-
await adapter.save(['test', '2'], bufferToArray(Buffer.from('two')));
|
|
101
|
-
await adapter.save(['bar', '1'], bufferToArray(Buffer.from('bar')));
|
|
102
|
-
await close();
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
{
|
|
106
|
-
const { adapter } = await createAdapter(root);
|
|
107
|
-
await adapter.removeRange(['test']);
|
|
108
|
-
const range = await adapter.loadRange(['test']);
|
|
109
|
-
expect(range.map((chunk) => arrayToBuffer(chunk.data!).toString())).to.deep.eq([]);
|
|
110
|
-
const range2 = await adapter.loadRange(['bar']);
|
|
111
|
-
expect(range2.map((chunk) => arrayToBuffer(chunk.data!).toString())).to.deep.eq(['bar']);
|
|
112
|
-
expect(range2.map((chunk) => chunk.key)).to.deep.eq([['bar', '1']]);
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
});
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Run tests for AutomergeStorageAdapter.
|
|
120
|
-
*/
|
|
121
|
-
runTests('AutomergeStorageAdapter', (root?: string) => {
|
|
122
|
-
const storage = createStorage({ type: root ? StorageType.NODE : StorageType.RAM, root });
|
|
123
|
-
const dir = storage.createDirectory('automerge');
|
|
124
|
-
const adapter = new AutomergeStorageAdapter(dir);
|
|
125
|
-
|
|
126
|
-
const close = async () => {
|
|
127
|
-
await adapter.close();
|
|
128
|
-
await storage.close();
|
|
15
|
+
describe('LevelDBStorageAdapter', () => {
|
|
16
|
+
const createAdapter = async (root?: string) => {
|
|
17
|
+
const level = createTestLevel(root);
|
|
18
|
+
await level.open();
|
|
19
|
+
const adapter = new LevelDBStorageAdapter({ db: level.sublevel('automerge') });
|
|
20
|
+
await adapter.open?.();
|
|
21
|
+
|
|
22
|
+
const close = async () => {
|
|
23
|
+
await adapter.close();
|
|
24
|
+
await level.close();
|
|
25
|
+
};
|
|
26
|
+
afterTest(close);
|
|
27
|
+
return {
|
|
28
|
+
adapter,
|
|
29
|
+
close,
|
|
30
|
+
};
|
|
129
31
|
};
|
|
130
|
-
afterTest(close);
|
|
131
32
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
33
|
+
const chunks = [
|
|
34
|
+
{ key: ['a', 'b', 'c', '1'], data: PublicKey.random().asUint8Array() },
|
|
35
|
+
{ key: ['a', 'b', 'c', '2'], data: PublicKey.random().asUint8Array() },
|
|
36
|
+
{ key: ['a', 'b', 'd', '3'], data: PublicKey.random().asUint8Array() },
|
|
37
|
+
{ key: ['a', 'b', 'd', '4'], data: PublicKey.random().asUint8Array() },
|
|
38
|
+
];
|
|
137
39
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
adapter,
|
|
154
|
-
|
|
155
|
-
};
|
|
40
|
+
test('should store and retrieve data', async () => {
|
|
41
|
+
const { adapter } = await createAdapter();
|
|
42
|
+
|
|
43
|
+
await adapter.save(chunks[0].key, chunks[0].data);
|
|
44
|
+
expect(await adapter.load(chunks[0].key)).to.deep.equal(chunks[0].data);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('loadRange return inputs with correct prefixes', async () => {
|
|
48
|
+
const { adapter } = await createAdapter();
|
|
49
|
+
|
|
50
|
+
for (const chunk of chunks) {
|
|
51
|
+
await adapter.save(chunk.key, chunk.data);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
expect((await adapter.loadRange(['a', 'b'])).length).to.equal(4);
|
|
55
|
+
expect((await adapter.loadRange(['a', 'b', 'c']))[0]).to.deep.equal(chunks[0]);
|
|
56
|
+
expect((await adapter.loadRange(['a', 'b', 'c'])).length).to.equal(2);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('deletion works', async () => {
|
|
60
|
+
const { adapter } = await createAdapter();
|
|
61
|
+
|
|
62
|
+
for (const chunk of chunks) {
|
|
63
|
+
await adapter.save(chunk.key, chunk.data);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
await adapter.remove(['a', 'b', 'c', '1']);
|
|
67
|
+
|
|
68
|
+
expect((await adapter.loadRange(['a', 'b'])).length).to.equal(3);
|
|
69
|
+
expect((await adapter.loadRange(['a', 'b', 'c'])).length).to.equal(1);
|
|
70
|
+
|
|
71
|
+
await adapter.removeRange(['a', 'b', 'd']);
|
|
72
|
+
|
|
73
|
+
expect((await adapter.loadRange(['a', 'b'])).length).to.equal(1);
|
|
74
|
+
expect((await adapter.loadRange(['a', 'b']))[0]).to.deep.equal(chunks[1]);
|
|
75
|
+
expect(await adapter.load(['a', 'b', 'c', '2'])).to.deep.equal(chunks[1].data);
|
|
76
|
+
expect(await adapter.load(['a', 'b', 'd', '3'])).to.be.undefined;
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test('loadRange', async () => {
|
|
80
|
+
const root = `/tmp/${randomBytes(16).toString('hex')}`;
|
|
81
|
+
{
|
|
82
|
+
const { adapter, close } = await createAdapter(root);
|
|
83
|
+
await adapter.save(['test', '1'], bufferToArray(Buffer.from('one')));
|
|
84
|
+
await adapter.save(['test', '2'], bufferToArray(Buffer.from('two')));
|
|
85
|
+
await adapter.save(['bar', '1'], bufferToArray(Buffer.from('bar')));
|
|
86
|
+
await close();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
{
|
|
90
|
+
const { adapter } = await createAdapter(root);
|
|
91
|
+
const range = await adapter.loadRange(['test']);
|
|
92
|
+
expect(range.map((chunk) => arrayToBuffer(chunk.data!).toString())).to.deep.eq(['one', 'two']);
|
|
93
|
+
expect(range.map((chunk) => chunk.key)).to.deep.eq([
|
|
94
|
+
['test', '1'],
|
|
95
|
+
['test', '2'],
|
|
96
|
+
]);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test('removeRange', async () => {
|
|
101
|
+
const root = `/tmp/${randomBytes(16).toString('hex')}`;
|
|
102
|
+
{
|
|
103
|
+
const { adapter, close } = await createAdapter(root);
|
|
104
|
+
await adapter.save(['test', '1'], bufferToArray(Buffer.from('one')));
|
|
105
|
+
await adapter.save(['test', '2'], bufferToArray(Buffer.from('two')));
|
|
106
|
+
await adapter.save(['bar', '1'], bufferToArray(Buffer.from('bar')));
|
|
107
|
+
await close();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
{
|
|
111
|
+
const { adapter } = await createAdapter(root);
|
|
112
|
+
await adapter.removeRange(['test']);
|
|
113
|
+
const range = await adapter.loadRange(['test']);
|
|
114
|
+
expect(range.map((chunk) => arrayToBuffer(chunk.data!).toString())).to.deep.eq([]);
|
|
115
|
+
const range2 = await adapter.loadRange(['bar']);
|
|
116
|
+
expect(range2.map((chunk) => arrayToBuffer(chunk.data!).toString())).to.deep.eq(['bar']);
|
|
117
|
+
expect(range2.map((chunk) => chunk.key)).to.deep.eq([['bar', '1']]);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
156
120
|
});
|
|
@@ -25,11 +25,13 @@ describe('space/space-protocol', () => {
|
|
|
25
25
|
const peer1 = await builder.createPeer();
|
|
26
26
|
const gossip1 = peer1.createGossip();
|
|
27
27
|
const presence1 = peer1.createPresence(gossip1);
|
|
28
|
+
await presence1.open();
|
|
28
29
|
const protocol1 = peer1.createSpaceProtocol(topic, gossip1);
|
|
29
30
|
|
|
30
31
|
const peer2 = await builder.createPeer();
|
|
31
32
|
const gossip2 = peer2.createGossip();
|
|
32
33
|
const presence2 = peer2.createPresence(gossip2);
|
|
34
|
+
await presence2.open();
|
|
33
35
|
const protocol2 = peer2.createSpaceProtocol(topic, gossip2);
|
|
34
36
|
|
|
35
37
|
await protocol1.start();
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { type Chunk, type StorageKey, type StorageAdapterInterface } from '@dxos/automerge/automerge-repo';
|
|
2
|
-
import { type Directory } from '@dxos/random-access-storage';
|
|
3
|
-
export declare class AutomergeStorageAdapter implements StorageAdapterInterface {
|
|
4
|
-
private readonly _directory;
|
|
5
|
-
private _state;
|
|
6
|
-
constructor(_directory: Directory);
|
|
7
|
-
load(key: StorageKey): Promise<Uint8Array | undefined>;
|
|
8
|
-
save(key: StorageKey, data: Uint8Array): Promise<void>;
|
|
9
|
-
remove(key: StorageKey): Promise<void>;
|
|
10
|
-
loadRange(keyPrefix: StorageKey): Promise<Chunk[]>;
|
|
11
|
-
removeRange(keyPrefix: StorageKey): Promise<void>;
|
|
12
|
-
close(): Promise<void>;
|
|
13
|
-
private _getFilename;
|
|
14
|
-
private _getKeyFromFilename;
|
|
15
|
-
}
|
|
16
|
-
//# sourceMappingURL=automerge-storage-adapter.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"automerge-storage-adapter.d.ts","sourceRoot":"","sources":["../../../../src/automerge/automerge-storage-adapter.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,KAAK,KAAK,EAAE,KAAK,UAAU,EAAE,KAAK,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAC3G,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAG7D,qBAAa,uBAAwB,YAAW,uBAAuB;IAKzD,OAAO,CAAC,QAAQ,CAAC,UAAU;IAFvC,OAAO,CAAC,MAAM,CAAiC;gBAElB,UAAU,EAAE,SAAS;IAE5C,IAAI,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IActD,IAAI,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAYtD,MAAM,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAUtC,SAAS,CAAC,SAAS,EAAE,UAAU,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;IAqBlD,WAAW,CAAC,SAAS,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBjD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,mBAAmB;CAG5B"}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { type StorageKey, type Chunk, type StorageAdapterInterface } from '@dxos/automerge/automerge-repo';
|
|
2
|
-
import { type MaybePromise } from '@dxos/util';
|
|
3
|
-
export type StorageCallbacks = {
|
|
4
|
-
beforeSave?: (path: string[]) => MaybePromise<void>;
|
|
5
|
-
afterSave?: (path: string[]) => MaybePromise<void>;
|
|
6
|
-
};
|
|
7
|
-
export type AutomergeStorageWrapperParams = {
|
|
8
|
-
storage: StorageAdapterInterface;
|
|
9
|
-
callbacks: StorageCallbacks;
|
|
10
|
-
};
|
|
11
|
-
/**
|
|
12
|
-
* Wrapper for automerge storage that adds callback on save.
|
|
13
|
-
*/
|
|
14
|
-
export declare class AutomergeStorageWrapper implements StorageAdapterInterface {
|
|
15
|
-
private readonly _storage;
|
|
16
|
-
private readonly _callbacks;
|
|
17
|
-
constructor({ storage, callbacks }: AutomergeStorageWrapperParams);
|
|
18
|
-
load(key: StorageKey): Promise<Uint8Array | undefined>;
|
|
19
|
-
save(key: StorageKey, value: Uint8Array): Promise<void>;
|
|
20
|
-
remove(key: StorageKey): Promise<void>;
|
|
21
|
-
loadRange(keyPrefix: StorageKey): Promise<Chunk[]>;
|
|
22
|
-
removeRange(keyPrefix: StorageKey): Promise<void>;
|
|
23
|
-
close(): Promise<void>;
|
|
24
|
-
}
|
|
25
|
-
//# sourceMappingURL=automerge-storage%E2%80%93wrapper.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"automerge-storage–wrapper.d.ts","sourceRoot":"","sources":["../../../../src/automerge/automerge-storage–wrapper.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,KAAK,EAAE,KAAK,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAC3G,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAI/C,MAAM,MAAM,gBAAgB,GAAG;IAC7B,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,YAAY,CAAC,IAAI,CAAC,CAAC;IACpD,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,YAAY,CAAC,IAAI,CAAC,CAAC;CACpD,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG;IAC1C,OAAO,EAAE,uBAAuB,CAAC;IACjC,SAAS,EAAE,gBAAgB,CAAC;CAC7B,CAAC;AAEF;;GAEG;AACH,qBAAa,uBAAwB,YAAW,uBAAuB;IACrE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA0B;IACnD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAmB;gBAElC,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,6BAA6B;IAK3D,IAAI,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAItD,IAAI,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAMvD,MAAM,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAItC,SAAS,CAAC,SAAS,EAAE,UAAU,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;IAIlD,WAAW,CAAC,SAAS,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAIjD,KAAK;CAKZ"}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { type SublevelDB } from '@dxos/kv-store';
|
|
2
|
-
import { type Directory } from '@dxos/random-access-storage';
|
|
3
|
-
export declare const levelMigration: ({ db, directory }: {
|
|
4
|
-
db: SublevelDB;
|
|
5
|
-
directory: Directory;
|
|
6
|
-
}) => Promise<void>;
|
|
7
|
-
//# sourceMappingURL=migrations.d.ts.map
|