@dxos/echo-pipeline 0.3.9-main.e4d8e16 → 0.3.9-main.eb5df3b

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 (37) hide show
  1. package/dist/lib/browser/{chunk-HSZ2EJ74.mjs → chunk-T627FAUD.mjs} +204 -31
  2. package/dist/lib/browser/chunk-T627FAUD.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +3 -1
  4. package/dist/lib/browser/meta.json +1 -1
  5. package/dist/lib/browser/testing/index.mjs +7 -2
  6. package/dist/lib/browser/testing/index.mjs.map +3 -3
  7. package/dist/lib/node/{chunk-RMIDX4JE.cjs → chunk-XWUUSV4Y.cjs} +198 -33
  8. package/dist/lib/node/chunk-XWUUSV4Y.cjs.map +7 -0
  9. package/dist/lib/node/index.cjs +27 -25
  10. package/dist/lib/node/index.cjs.map +2 -2
  11. package/dist/lib/node/meta.json +1 -1
  12. package/dist/lib/node/testing/index.cjs +18 -14
  13. package/dist/lib/node/testing/index.cjs.map +3 -3
  14. package/dist/types/src/automerge/automerge-host.d.ts +15 -0
  15. package/dist/types/src/automerge/automerge-host.d.ts.map +1 -0
  16. package/dist/types/src/automerge/automerge-host.test.d.ts +2 -0
  17. package/dist/types/src/automerge/automerge-host.test.d.ts.map +1 -0
  18. package/dist/types/src/automerge/index.d.ts +2 -0
  19. package/dist/types/src/automerge/index.d.ts.map +1 -0
  20. package/dist/types/src/db-host/data-service.d.ts +3 -1
  21. package/dist/types/src/db-host/data-service.d.ts.map +1 -1
  22. package/dist/types/src/index.d.ts +1 -0
  23. package/dist/types/src/index.d.ts.map +1 -1
  24. package/dist/types/src/space/data-pipeline.d.ts +4 -0
  25. package/dist/types/src/space/data-pipeline.d.ts.map +1 -1
  26. package/dist/types/src/testing/util.d.ts.map +1 -1
  27. package/package.json +32 -31
  28. package/src/automerge/automerge-host.test.ts +42 -0
  29. package/src/automerge/automerge-host.ts +212 -0
  30. package/src/automerge/index.ts +5 -0
  31. package/src/db-host/data-service.ts +5 -1
  32. package/src/index.ts +1 -0
  33. package/src/space/data-pipeline.ts +10 -0
  34. package/src/testing/util.ts +4 -1
  35. package/src/tests/database.test.ts +4 -1
  36. package/dist/lib/browser/chunk-HSZ2EJ74.mjs.map +0 -7
  37. package/dist/lib/node/chunk-RMIDX4JE.cjs.map +0 -7
@@ -3,4 +3,5 @@ export * from './db-host';
3
3
  export * from './metadata';
4
4
  export * from './pipeline';
5
5
  export * from './space';
6
+ export * from './automerge';
6
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAIA,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAIA,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,SAAS,CAAC;AACxB,cAAc,aAAa,CAAC"}
@@ -3,6 +3,7 @@ import { type CredentialProcessor, type FeedInfo, type SpecificCredential } from
3
3
  import { ItemManager } from '@dxos/echo-db';
4
4
  import { type PublicKey } from '@dxos/keys';
5
5
  import { type ModelFactory } from '@dxos/model-factory';
6
+ import { type CreateEpochRequest } from '@dxos/protocols/proto/dxos/client/services';
6
7
  import { type Credential, type Epoch } from '@dxos/protocols/proto/dxos/halo/credentials';
7
8
  import { Timeframe } from '@dxos/timeframe';
8
9
  import { DatabaseHost, type SnapshotManager } from '../db-host';
@@ -24,6 +25,9 @@ export type DataPipelineParams = {
24
25
  */
25
26
  onPipelineCreated: (pipeline: Pipeline) => Promise<void>;
26
27
  };
28
+ export type CreateEpochOptions = {
29
+ migration?: CreateEpochRequest.Migration;
30
+ };
27
31
  /**
28
32
  * Controls data pipeline in the space.
29
33
  * Consumes the pipeline and updates the database.
@@ -1 +1 @@
1
- {"version":3,"file":"data-pipeline.d.ts","sourceRoot":"","sources":["../../../../src/space/data-pipeline.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,EAAiD,MAAM,aAAa,CAAC;AAEnF,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,QAAQ,EACb,KAAK,kBAAkB,EAExB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAA2B,WAAW,EAAmB,MAAM,eAAe,CAAC;AAGtF,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAMxD,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,KAAK,EAAE,MAAM,6CAA6C,CAAC;AAC1F,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAI5C,OAAO,EAAE,YAAY,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAChE,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC,MAAM,WAAW,eAAe;IAC9B,YAAY,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;CACvD;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,YAAY,EAAE,YAAY,CAAC;IAC3B,eAAe,EAAE,eAAe,CAAC;IACjC,aAAa,EAAE,aAAa,CAAC;IAC7B,SAAS,EAAE,SAAS,CAAC;IACrB,QAAQ,EAAE,SAAS,CAAC;IACpB,gBAAgB,EAAE,CAAC,OAAO,EAAE,SAAS,KAAK,QAAQ,GAAG,SAAS,CAAC;IAC/D,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAE/B;;OAEG;IACH,iBAAiB,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1D,CAAC;AAiBF;;;;GAIG;AACH,qBAEa,YAAa,YAAW,mBAAmB;IAmC1C,OAAO,CAAC,QAAQ,CAAC,OAAO;IAlCpC,OAAO,CAAC,IAAI,CAAiB;IAC7B,OAAO,CAAC,SAAS,CAAC,CAAuB;IACzC,OAAO,CAAC,gBAAgB,CAAC,CAAwB;IAEjD,OAAO,CAAC,+BAA+B,CAAmB;IAC1D,OAAO,CAAC,OAAO,CAAS;IAExB,OAAO,CAAC,sBAAsB,CAAK;IACnC,OAAO,CAAC,qBAAqB,CAAK;IAClC,OAAO,CAAC,mBAAmB,CAAM;IACjC,OAAO,CAAC,SAAS,CAAC,CAAU;IAG5B,OAAO,CAAC,MAAM,CAA0B;IAGxC,OAAO,CAAC,UAAU,CAA2B;IAEtC,YAAY,CAAC,EAAE,YAAY,CAAC;IAE5B,WAAW,EAAG,WAAW,CAAC;IAEjC;;OAEG;IACI,YAAY,CAAC,EAAE,kBAAkB,CAAC,KAAK,CAAC,CAAa;IAE5D;;OAEG;IACI,YAAY,CAAC,EAAE,kBAAkB,CAAC,KAAK,CAAC,CAAa;IAE5D,SAAgB,UAAU,oBAA2B;gBAExB,OAAO,EAAE,kBAAkB;IAExD,IAAI,MAAM,YAET;IAED,IAAI,QAAQ,yBAEX;IAED,IAAI,aAAa,6DAEhB;IAED,kBAAkB,CAAC,SAAS,EAAE,SAAS;IAKjC,iBAAiB,CAAC,UAAU,EAAE,UAAU;IAaxC,IAAI;IAuCJ,KAAK;YAiCG,gBAAgB;IA8D9B,OAAO,CAAC,eAAe;YAST,oBAAoB;YAMpB,UAAU;YAsBV,wBAAwB;YAqBxB,2BAA2B;YA4B3B,aAAa;IAiBrB,kBAAkB,CAAC,SAAS,EAAE,SAAS;IAMvC,WAAW,IAAI,OAAO,CAAC,KAAK,CAAC;IAqB7B,sBAAsB;YAId,MAAM;CAYrB"}
1
+ {"version":3,"file":"data-pipeline.d.ts","sourceRoot":"","sources":["../../../../src/space/data-pipeline.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../../../src/testing/util.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAG3D,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAInD,OAAO,EAAE,YAAY,EAAE,KAAK,eAAe,EAA6C,MAAM,YAAY,CAAC;AAC3G,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,UAAU,CAAC;AAE7C,eAAO,MAAM,oBAAoB,iBAAwB,YAAY;;;EAuBpE,CAAC;AAEF,eAAO,MAAM,uCAAuC,iBACpC,YAAY,mBACT,eAAe;;;EAejC,CAAC;AAEF,eAAO,MAAM,iBAAiB,WAAkB,YAAY,UAAS,YAAY,kBAmBhF,CAAC"}
1
+ {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../../../src/testing/util.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAG3D,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAMnD,OAAO,EAAE,YAAY,EAAE,KAAK,eAAe,EAA6C,MAAM,YAAY,CAAC;AAC3G,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,UAAU,CAAC;AAE7C,eAAO,MAAM,oBAAoB,iBAAwB,YAAY;;;EAuBpE,CAAC;AAEF,eAAO,MAAM,uCAAuC,iBACpC,YAAY,mBACT,eAAe;;;EAgBjC,CAAC;AAEF,eAAO,MAAM,iBAAiB,WAAkB,YAAY,UAAS,YAAY,kBAmBhF,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/echo-pipeline",
3
- "version": "0.3.9-main.e4d8e16",
3
+ "version": "0.3.9-main.eb5df3b",
4
4
  "description": "ECHO database.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -36,36 +36,37 @@
36
36
  ],
37
37
  "dependencies": {
38
38
  "crc-32": "^1.2.2",
39
- "@dxos/async": "0.3.9-main.e4d8e16",
40
- "@dxos/context": "0.3.9-main.e4d8e16",
41
- "@dxos/codec-protobuf": "0.3.9-main.e4d8e16",
42
- "@dxos/credentials": "0.3.9-main.e4d8e16",
43
- "@dxos/crypto": "0.3.9-main.e4d8e16",
44
- "@dxos/document-model": "0.3.9-main.e4d8e16",
45
- "@dxos/echo-db": "0.3.9-main.e4d8e16",
46
- "@dxos/debug": "0.3.9-main.e4d8e16",
47
- "@dxos/feed-store": "0.3.9-main.e4d8e16",
48
- "@dxos/invariant": "0.3.9-main.e4d8e16",
49
- "@dxos/keys": "0.3.9-main.e4d8e16",
50
- "@dxos/keyring": "0.3.9-main.e4d8e16",
51
- "@dxos/hypercore": "0.3.9-main.e4d8e16",
52
- "@dxos/messaging": "0.3.9-main.e4d8e16",
53
- "@dxos/log": "0.3.9-main.e4d8e16",
54
- "@dxos/model-factory": "0.3.9-main.e4d8e16",
55
- "@dxos/network-manager": "0.3.9-main.e4d8e16",
56
- "@dxos/node-std": "0.3.9-main.e4d8e16",
57
- "@dxos/protocols": "0.3.9-main.e4d8e16",
58
- "@dxos/random-access-storage": "0.3.9-main.e4d8e16",
59
- "@dxos/rpc": "0.3.9-main.e4d8e16",
60
- "@dxos/teleport": "0.3.9-main.e4d8e16",
61
- "@dxos/teleport-extension-gossip": "0.3.9-main.e4d8e16",
62
- "@dxos/teleport-extension-object-sync": "0.3.9-main.e4d8e16",
63
- "@dxos/teleport-extension-replicator": "0.3.9-main.e4d8e16",
64
- "@dxos/text-model": "0.3.9-main.e4d8e16",
65
- "@dxos/timeframe": "0.3.9-main.e4d8e16",
66
- "@dxos/tracing": "0.3.9-main.e4d8e16",
67
- "@dxos/util": "0.3.9-main.e4d8e16",
68
- "@dxos/typings": "0.3.9-main.e4d8e16"
39
+ "@dxos/automerge": "0.3.9-main.eb5df3b",
40
+ "@dxos/codec-protobuf": "0.3.9-main.eb5df3b",
41
+ "@dxos/async": "0.3.9-main.eb5df3b",
42
+ "@dxos/context": "0.3.9-main.eb5df3b",
43
+ "@dxos/crypto": "0.3.9-main.eb5df3b",
44
+ "@dxos/credentials": "0.3.9-main.eb5df3b",
45
+ "@dxos/debug": "0.3.9-main.eb5df3b",
46
+ "@dxos/document-model": "0.3.9-main.eb5df3b",
47
+ "@dxos/echo-db": "0.3.9-main.eb5df3b",
48
+ "@dxos/feed-store": "0.3.9-main.eb5df3b",
49
+ "@dxos/hypercore": "0.3.9-main.eb5df3b",
50
+ "@dxos/keyring": "0.3.9-main.eb5df3b",
51
+ "@dxos/invariant": "0.3.9-main.eb5df3b",
52
+ "@dxos/keys": "0.3.9-main.eb5df3b",
53
+ "@dxos/log": "0.3.9-main.eb5df3b",
54
+ "@dxos/messaging": "0.3.9-main.eb5df3b",
55
+ "@dxos/model-factory": "0.3.9-main.eb5df3b",
56
+ "@dxos/network-manager": "0.3.9-main.eb5df3b",
57
+ "@dxos/node-std": "0.3.9-main.eb5df3b",
58
+ "@dxos/protocols": "0.3.9-main.eb5df3b",
59
+ "@dxos/rpc": "0.3.9-main.eb5df3b",
60
+ "@dxos/random-access-storage": "0.3.9-main.eb5df3b",
61
+ "@dxos/teleport": "0.3.9-main.eb5df3b",
62
+ "@dxos/teleport-extension-gossip": "0.3.9-main.eb5df3b",
63
+ "@dxos/teleport-extension-object-sync": "0.3.9-main.eb5df3b",
64
+ "@dxos/text-model": "0.3.9-main.eb5df3b",
65
+ "@dxos/teleport-extension-replicator": "0.3.9-main.eb5df3b",
66
+ "@dxos/timeframe": "0.3.9-main.eb5df3b",
67
+ "@dxos/tracing": "0.3.9-main.eb5df3b",
68
+ "@dxos/typings": "0.3.9-main.eb5df3b",
69
+ "@dxos/util": "0.3.9-main.eb5df3b"
69
70
  },
70
71
  "devDependencies": {
71
72
  "fast-check": "~3.3.0",
@@ -0,0 +1,42 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import expect from 'expect';
6
+
7
+ import { sleep } from '@dxos/async';
8
+ import { StorageType, createStorage } from '@dxos/random-access-storage';
9
+ import { describe, test } from '@dxos/test';
10
+
11
+ import { AutomergeHost } from './automerge-host';
12
+
13
+ describe('AutomergeHost', () => {
14
+ test('can create documents', () => {
15
+ const host = new AutomergeHost(createStorage({ type: StorageType.RAM }).createDirectory());
16
+
17
+ const handle = host.repo.create();
18
+ handle.change((doc: any) => {
19
+ doc.text = 'Hello world';
20
+ });
21
+ expect(handle.docSync().text).toEqual('Hello world');
22
+ });
23
+
24
+ test('changes are preserved in storage', async () => {
25
+ const storageDirectory = createStorage({ type: StorageType.RAM }).createDirectory();
26
+
27
+ const host = new AutomergeHost(storageDirectory);
28
+ const handle = host.repo.create();
29
+ handle.change((doc: any) => {
30
+ doc.text = 'Hello world';
31
+ });
32
+ const url = handle.url;
33
+
34
+ // TODO(dmaretskyi): Is there a way to know when automerge has finished saving?
35
+ await sleep(100);
36
+
37
+ const host2 = new AutomergeHost(storageDirectory);
38
+ const handle2 = host2.repo.find(url);
39
+ await handle2.whenReady();
40
+ expect(handle2.docSync().text).toEqual('Hello world');
41
+ });
42
+ });
@@ -0,0 +1,212 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import {
6
+ Repo,
7
+ NetworkAdapter,
8
+ StorageAdapter,
9
+ type Message,
10
+ type PeerId,
11
+ type Chunk,
12
+ type StorageKey,
13
+ cbor,
14
+ } from '@dxos/automerge/automerge-repo';
15
+ import { Stream } from '@dxos/codec-protobuf';
16
+ import { invariant } from '@dxos/invariant';
17
+ import { type SyncRepoRequest, type SyncRepoResponse } from '@dxos/protocols/proto/dxos/echo/service';
18
+ import { type Directory } from '@dxos/random-access-storage';
19
+ import { arrayToBuffer, bufferToArray } from '@dxos/util';
20
+
21
+ export class AutomergeHost {
22
+ private readonly _repo: Repo;
23
+ private readonly _meshNetwork: MeshNetworkAdapter;
24
+ private readonly _clientNetwork: LocalHostNetworkAdapter;
25
+ private readonly _storage: AutomergeStorageAdapter;
26
+
27
+ constructor(storageDirectory: Directory) {
28
+ this._meshNetwork = new MeshNetworkAdapter();
29
+ this._clientNetwork = new LocalHostNetworkAdapter();
30
+ this._storage = new AutomergeStorageAdapter(storageDirectory);
31
+ this._repo = new Repo({
32
+ network: [
33
+ // this._meshNetwork,
34
+ this._clientNetwork,
35
+ ],
36
+
37
+ storage: this._storage,
38
+
39
+ // TODO(dmaretskyi): Share based on HALO permissions and space affinity.
40
+ sharePolicy: async (peerId, documentId) => true, // Share everything.
41
+ });
42
+ }
43
+
44
+ get repo(): Repo {
45
+ return this._repo;
46
+ }
47
+
48
+ syncRepo(request: SyncRepoRequest): Stream<SyncRepoResponse> {
49
+ return this._clientNetwork.syncRepo(request);
50
+ }
51
+
52
+ sendSyncMessage(request: SyncRepoRequest): Promise<void> {
53
+ return this._clientNetwork.sendSyncMessage(request);
54
+ }
55
+ }
56
+
57
+ type ClientSyncState = {
58
+ connected: boolean;
59
+ send: (message: Message) => void;
60
+ disconnect: () => void;
61
+ };
62
+
63
+ /**
64
+ * Used to replicate with apps running on the same device.
65
+ */
66
+ class LocalHostNetworkAdapter extends NetworkAdapter {
67
+ private readonly _peers: Map<PeerId, ClientSyncState> = new Map();
68
+
69
+ constructor() {
70
+ super();
71
+
72
+ this.emit('ready', {
73
+ network: this,
74
+ });
75
+ }
76
+
77
+ override connect(peerId: PeerId): void {
78
+ // No-op. Client always connects first
79
+ }
80
+
81
+ override send(message: Message): void {
82
+ const peer = this._peers.get(message.targetId);
83
+ invariant(peer, 'Peer not found.');
84
+ peer.send(message);
85
+ }
86
+
87
+ override disconnect(): void {
88
+ throw new Error('Method not implemented.');
89
+ }
90
+
91
+ syncRepo({ id, syncMessage }: SyncRepoRequest): Stream<SyncRepoResponse> {
92
+ const peerId = this._getPeerId(id);
93
+
94
+ return new Stream(({ next, close }) => {
95
+ invariant(!this._peers.has(peerId), 'Peer already connected.');
96
+ this._peers.set(peerId, {
97
+ connected: true,
98
+ send: (message) => {
99
+ next({
100
+ syncMessage: cbor.encode(message),
101
+ });
102
+ },
103
+ disconnect: () => {
104
+ this._peers.delete(peerId);
105
+ close();
106
+ this.emit('peer-disconnected', {
107
+ peerId,
108
+ });
109
+ },
110
+ });
111
+
112
+ this.emit('peer-candidate', {
113
+ peerId,
114
+ });
115
+ });
116
+ }
117
+
118
+ async sendSyncMessage({ id, syncMessage }: SyncRepoRequest): Promise<void> {
119
+ const message = cbor.decode(syncMessage!) as Message;
120
+ this.emit('message', message);
121
+ }
122
+
123
+ private _getPeerId(id: string): PeerId {
124
+ return id as PeerId;
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Used to replicate with other peers over the network.
130
+ */
131
+ class MeshNetworkAdapter extends NetworkAdapter {
132
+ override connect(peerId: PeerId): void {
133
+ throw new Error('Method not implemented.');
134
+ }
135
+
136
+ override send(message: Message): void {
137
+ throw new Error('Method not implemented.');
138
+ }
139
+
140
+ override disconnect(): void {
141
+ throw new Error('Method not implemented.');
142
+ }
143
+ }
144
+
145
+ class AutomergeStorageAdapter extends StorageAdapter {
146
+ constructor(private readonly _directory: Directory) {
147
+ super();
148
+ }
149
+
150
+ override async load(key: StorageKey): Promise<Uint8Array | undefined> {
151
+ const filename = this._getFilename(key);
152
+ const file = this._directory.getOrCreateFile(filename);
153
+ const { size } = await file.stat();
154
+ const buffer = await file.read(0, size);
155
+ return bufferToArray(buffer);
156
+ }
157
+
158
+ override async save(key: StorageKey, data: Uint8Array): Promise<void> {
159
+ const filename = this._getFilename(key);
160
+ const file = this._directory.getOrCreateFile(filename);
161
+ await file.write(0, arrayToBuffer(data));
162
+ await file.truncate?.(data.length);
163
+
164
+ await file.flush?.();
165
+ }
166
+
167
+ override async remove(key: StorageKey): Promise<void> {
168
+ // TODO(dmaretskyi): Better deletion.
169
+ const filename = this._getFilename(key);
170
+ const file = this._directory.getOrCreateFile(filename);
171
+ await file.truncate?.(0);
172
+ }
173
+
174
+ override async loadRange(keyPrefix: StorageKey): Promise<Chunk[]> {
175
+ const filename = this._getFilename(keyPrefix);
176
+ const entries = await this._directory.list();
177
+ return Promise.all(
178
+ entries
179
+ .filter((entry) => entry.startsWith(filename))
180
+ .map(async (entry): Promise<Chunk> => {
181
+ const file = this._directory.getOrCreateFile(entry);
182
+ const { size } = await file.stat();
183
+ const buffer = await file.read(0, size);
184
+ return {
185
+ key: this._getKeyFromFilename(entry),
186
+ data: bufferToArray(buffer),
187
+ };
188
+ }),
189
+ );
190
+ }
191
+
192
+ override async removeRange(keyPrefix: StorageKey): Promise<void> {
193
+ const filename = this._getFilename(keyPrefix);
194
+ const entries = await this._directory.list();
195
+ await Promise.all(
196
+ entries
197
+ .filter((entry) => entry.startsWith(filename))
198
+ .map(async (entry): Promise<void> => {
199
+ const file = this._directory.getOrCreateFile(filename);
200
+ await file.truncate?.(0);
201
+ }),
202
+ );
203
+ }
204
+
205
+ private _getFilename(key: StorageKey): string {
206
+ return key.map((k) => k.replaceAll('%', '%25').replaceAll('-', '%2D')).join('-');
207
+ }
208
+
209
+ private _getKeyFromFilename(filename: string): StorageKey {
210
+ return filename.split('-').map((k) => k.replaceAll('%2D', '-').replaceAll('%25', '%'));
211
+ }
212
+ }
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ export * from './automerge-host';
@@ -20,6 +20,7 @@ import {
20
20
  import { ComplexMap } from '@dxos/util';
21
21
 
22
22
  import { type DataServiceHost } from './data-service-host';
23
+ import { type AutomergeHost } from '../automerge';
23
24
 
24
25
  // TODO(burdon): Clear on close.
25
26
  export class DataServiceSubscriptions {
@@ -53,7 +54,10 @@ export class DataServiceSubscriptions {
53
54
  */
54
55
  // TODO(burdon): Move to client-services.
55
56
  export class DataServiceImpl implements DataService {
56
- constructor(private readonly _subscriptions: DataServiceSubscriptions) {}
57
+ constructor(
58
+ private readonly _subscriptions: DataServiceSubscriptions,
59
+ private readonly _automergeHost: AutomergeHost,
60
+ ) {}
57
61
 
58
62
  subscribe(request: SubscribeRequest): Stream<EchoEvent> {
59
63
  invariant(request.spaceKey);
package/src/index.ts CHANGED
@@ -7,3 +7,4 @@ export * from './db-host';
7
7
  export * from './metadata';
8
8
  export * from './pipeline';
9
9
  export * from './space';
10
+ export * from './automerge';
@@ -17,6 +17,7 @@ import { type PublicKey } from '@dxos/keys';
17
17
  import { log, omit } from '@dxos/log';
18
18
  import { type ModelFactory } from '@dxos/model-factory';
19
19
  import { CancelledError, type DataPipelineProcessed } from '@dxos/protocols';
20
+ import { type CreateEpochRequest } from '@dxos/protocols/proto/dxos/client/services';
20
21
  import { type DataMessage } from '@dxos/protocols/proto/dxos/echo/feed';
21
22
  import { type SpaceCache } from '@dxos/protocols/proto/dxos/echo/metadata';
22
23
  import { type ObjectSnapshot } from '@dxos/protocols/proto/dxos/echo/model/document';
@@ -64,6 +65,10 @@ const AUTOMATIC_SNAPSHOT_DEBOUNCE_INTERVAL = 5_000;
64
65
  */
65
66
  const TIMEFRAME_SAVE_DEBOUNCE_INTERVAL = 5_000;
66
67
 
68
+ export type CreateEpochOptions = {
69
+ migration?: CreateEpochRequest.Migration;
70
+ };
71
+
67
72
  /**
68
73
  * Controls data pipeline in the space.
69
74
  * Consumes the pipeline and updates the database.
@@ -352,6 +357,11 @@ export class DataPipeline implements CredentialProcessor {
352
357
  }
353
358
  await this._processEpoch(ctx, epoch.subject.assertion);
354
359
 
360
+ // Carry over the snapshot CID from the previous epoch.
361
+ if (epoch.subject.assertion.snapshotCid === undefined) {
362
+ epoch.subject.assertion.snapshotCid = this.appliedEpoch?.subject.assertion.snapshotCid;
363
+ }
364
+
355
365
  this.appliedEpoch = epoch;
356
366
  this.onNewEpoch.emit(epoch);
357
367
  });
@@ -9,8 +9,10 @@ import { MockFeedWriter } from '@dxos/feed-store/testing';
9
9
  import { PublicKey } from '@dxos/keys';
10
10
  import { ModelFactory } from '@dxos/model-factory';
11
11
  import { type DataMessage } from '@dxos/protocols/proto/dxos/echo/feed';
12
+ import { StorageType, createStorage } from '@dxos/random-access-storage';
12
13
  import { Timeframe } from '@dxos/timeframe';
13
14
 
15
+ import { AutomergeHost } from '../automerge';
14
16
  import { DatabaseHost, type DataServiceHost, DataServiceImpl, DataServiceSubscriptions } from '../db-host';
15
17
  import { type DataPipeline } from '../space';
16
18
 
@@ -44,7 +46,8 @@ export const createRemoteDatabaseFromDataServiceHost = async (
44
46
  dataServiceHost: DataServiceHost,
45
47
  ) => {
46
48
  const dataServiceSubscriptions = new DataServiceSubscriptions();
47
- const dataService = new DataServiceImpl(dataServiceSubscriptions);
49
+ const automergeHost = new AutomergeHost(createStorage({ type: StorageType.RAM }).createDirectory());
50
+ const dataService = new DataServiceImpl(dataServiceSubscriptions, automergeHost);
48
51
 
49
52
  const spaceKey = PublicKey.random();
50
53
  await dataServiceSubscriptions.registerSpace(spaceKey, dataServiceHost);
@@ -10,8 +10,10 @@ import { TestBuilder as FeedTestBuilder } from '@dxos/feed-store/testing';
10
10
  import { PublicKey } from '@dxos/keys';
11
11
  import { ModelFactory } from '@dxos/model-factory';
12
12
  import { type DataMessage } from '@dxos/protocols/proto/dxos/echo/feed';
13
+ import { createStorage, StorageType } from '@dxos/random-access-storage';
13
14
  import { test } from '@dxos/test';
14
15
 
16
+ import { AutomergeHost } from '../automerge';
15
17
  import { createMappedFeedWriter } from '../common';
16
18
  import { DatabaseHost, DataServiceImpl, DataServiceSubscriptions } from '../db-host';
17
19
  import { createMemoryDatabase, createRemoteDatabaseFromDataServiceHost } from '../testing';
@@ -41,7 +43,8 @@ const createDatabaseWithFeeds = async () => {
41
43
  await host.open(new ItemManager(modelFactory), new ModelFactory().registerModel(DocumentModel));
42
44
 
43
45
  const dataServiceSubscriptions = new DataServiceSubscriptions();
44
- const dataService = new DataServiceImpl(dataServiceSubscriptions);
46
+ const automergeHost = new AutomergeHost(createStorage({ type: StorageType.RAM }).createDirectory());
47
+ const dataService = new DataServiceImpl(dataServiceSubscriptions, automergeHost);
45
48
 
46
49
  const spaceKey = PublicKey.random();
47
50
  await dataServiceSubscriptions.registerSpace(spaceKey, host.createDataServiceHost());