@automerge/automerge-repo 1.0.13 → 1.0.15
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/Repo.js +1 -1
- package/dist/helpers/cbor.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/network/NetworkAdapter.d.ts +3 -3
- package/dist/network/NetworkAdapter.d.ts.map +1 -1
- package/dist/network/messages.d.ts +7 -18
- package/dist/network/messages.d.ts.map +1 -1
- package/dist/storage/StorageAdapter.d.ts +19 -22
- package/dist/storage/StorageAdapter.d.ts.map +1 -1
- package/dist/storage/StorageAdapter.js +2 -2
- package/dist/storage/StorageSubsystem.d.ts +39 -3
- package/dist/storage/StorageSubsystem.d.ts.map +1 -1
- package/dist/storage/StorageSubsystem.js +128 -75
- package/dist/storage/chunkTypeFromKey.d.ts +13 -0
- package/dist/storage/chunkTypeFromKey.d.ts.map +1 -0
- package/dist/storage/chunkTypeFromKey.js +18 -0
- package/dist/storage/keyHash.d.ts +4 -0
- package/dist/storage/keyHash.d.ts.map +1 -0
- package/dist/storage/keyHash.js +15 -0
- package/dist/storage/types.d.ts +37 -0
- package/dist/storage/types.d.ts.map +1 -0
- package/dist/storage/types.js +1 -0
- package/package.json +2 -2
- package/src/Repo.ts +1 -1
- package/src/helpers/cbor.ts +1 -1
- package/src/index.ts +11 -3
- package/src/network/NetworkAdapter.ts +3 -3
- package/src/network/messages.ts +8 -21
- package/src/storage/StorageAdapter.ts +23 -30
- package/src/storage/StorageSubsystem.ts +159 -93
- package/src/storage/chunkTypeFromKey.ts +22 -0
- package/src/storage/keyHash.ts +17 -0
- package/src/storage/types.ts +39 -0
- package/test/StorageSubsystem.test.ts +143 -35
- package/test/helpers/DummyStorageAdapter.ts +2 -4
package/dist/Repo.js
CHANGED
|
@@ -81,7 +81,7 @@ export class Repo extends EventEmitter {
|
|
|
81
81
|
// TODO Pass the delete on to the network
|
|
82
82
|
// synchronizer.removeDocument(documentId)
|
|
83
83
|
if (storageSubsystem) {
|
|
84
|
-
storageSubsystem.
|
|
84
|
+
storageSubsystem.removeDoc(documentId).catch(err => {
|
|
85
85
|
this.#log("error deleting document", { documentId, err });
|
|
86
86
|
});
|
|
87
87
|
}
|
package/dist/helpers/cbor.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Encoder, decode as cborXdecode } from "cbor-x";
|
|
2
2
|
export function encode(obj) {
|
|
3
|
-
const encoder = new Encoder({ tagUint8Array: false });
|
|
3
|
+
const encoder = new Encoder({ tagUint8Array: false, useRecords: false });
|
|
4
4
|
return encoder.encode(obj);
|
|
5
5
|
}
|
|
6
6
|
export function decode(buf) {
|
package/dist/index.d.ts
CHANGED
|
@@ -36,7 +36,7 @@ export * as cbor from "./helpers/cbor.js";
|
|
|
36
36
|
export type { DocHandleChangePayload, DocHandleDeletePayload, DocHandleEncodedChangePayload, DocHandleEphemeralMessagePayload, DocHandleEvents, DocHandleOptions, DocHandleOutboundEphemeralMessagePayload, HandleState, } from "./DocHandle.js";
|
|
37
37
|
export type { DeleteDocumentPayload, DocumentPayload, RepoConfig, RepoEvents, SharePolicy, } from "./Repo.js";
|
|
38
38
|
export type { NetworkAdapterEvents, OpenPayload, PeerCandidatePayload, PeerDisconnectedPayload, } from "./network/NetworkAdapter.js";
|
|
39
|
-
export type {
|
|
40
|
-
export type { StorageKey } from "./storage/
|
|
39
|
+
export type { DocumentUnavailableMessage, EphemeralMessage, Message, RepoMessage, RequestMessage, SyncMessage, } from "./network/messages.js";
|
|
40
|
+
export type { Chunk, ChunkInfo, ChunkType, StorageKey, } from "./storage/types.js";
|
|
41
41
|
export * from "./types.js";
|
|
42
42
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAE5D,eAAe;AACf,OAAO,KAAK,IAAI,MAAM,mBAAmB,CAAA;AAIzC,YAAY,EACV,sBAAsB,EACtB,sBAAsB,EACtB,6BAA6B,EAC7B,gCAAgC,EAChC,eAAe,EACf,gBAAgB,EAChB,wCAAwC,EACxC,WAAW,GACZ,MAAM,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAE5D,eAAe;AACf,OAAO,KAAK,IAAI,MAAM,mBAAmB,CAAA;AAIzC,YAAY,EACV,sBAAsB,EACtB,sBAAsB,EACtB,6BAA6B,EAC7B,gCAAgC,EAChC,eAAe,EACf,gBAAgB,EAChB,wCAAwC,EACxC,WAAW,GACZ,MAAM,gBAAgB,CAAA;AAEvB,YAAY,EACV,qBAAqB,EACrB,eAAe,EACf,UAAU,EACV,UAAU,EACV,WAAW,GACZ,MAAM,WAAW,CAAA;AAElB,YAAY,EACV,oBAAoB,EACpB,WAAW,EACX,oBAAoB,EACpB,uBAAuB,GACxB,MAAM,6BAA6B,CAAA;AAEpC,YAAY,EACV,0BAA0B,EAC1B,gBAAgB,EAChB,OAAO,EACP,WAAW,EACX,cAAc,EACd,WAAW,GACZ,MAAM,uBAAuB,CAAA;AAE9B,YAAY,EACV,KAAK,EACL,SAAS,EACT,SAAS,EACT,UAAU,GACX,MAAM,oBAAoB,CAAA;AAE3B,cAAc,YAAY,CAAA"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EventEmitter } from "eventemitter3";
|
|
2
2
|
import { PeerId } from "../types.js";
|
|
3
|
-
import {
|
|
3
|
+
import { Message } from "./messages.js";
|
|
4
4
|
/** An interface representing some way to connect to other peers
|
|
5
5
|
*
|
|
6
6
|
* @remarks
|
|
@@ -19,7 +19,7 @@ export declare abstract class NetworkAdapter extends EventEmitter<NetworkAdapter
|
|
|
19
19
|
*
|
|
20
20
|
* @argument message - the message to send
|
|
21
21
|
*/
|
|
22
|
-
abstract send(message:
|
|
22
|
+
abstract send(message: Message): void;
|
|
23
23
|
/** Called by the {@link Repo} to disconnect from the network */
|
|
24
24
|
abstract disconnect(): void;
|
|
25
25
|
}
|
|
@@ -33,7 +33,7 @@ export interface NetworkAdapterEvents {
|
|
|
33
33
|
/** Emitted when the network adapter learns that a peer has disconnected */
|
|
34
34
|
"peer-disconnected": (payload: PeerDisconnectedPayload) => void;
|
|
35
35
|
/** Emitted when the network adapter receives a message from a peer */
|
|
36
|
-
message: (payload:
|
|
36
|
+
message: (payload: Message) => void;
|
|
37
37
|
}
|
|
38
38
|
export interface OpenPayload {
|
|
39
39
|
network: NetworkAdapter;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NetworkAdapter.d.ts","sourceRoot":"","sources":["../../src/network/NetworkAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"NetworkAdapter.d.ts","sourceRoot":"","sources":["../../src/network/NetworkAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAEvC;;;;;;GAMG;AACH,8BAAsB,cAAe,SAAQ,YAAY,CAAC,oBAAoB,CAAC;IAC7E,MAAM,CAAC,EAAE,MAAM,CAAA;IAEf;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAEtC;;;OAGG;IACH,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAErC,gEAAgE;IAChE,QAAQ,CAAC,UAAU,IAAI,IAAI;CAC5B;AAID,MAAM,WAAW,oBAAoB;IACnC,mDAAmD;IACnD,KAAK,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAA;IAErC,yCAAyC;IACzC,KAAK,EAAE,MAAM,IAAI,CAAA;IAEjB,+DAA+D;IAC/D,gBAAgB,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,IAAI,CAAA;IAEzD,2EAA2E;IAC3E,mBAAmB,EAAE,CAAC,OAAO,EAAE,uBAAuB,KAAK,IAAI,CAAA;IAE/D,sEAAsE;IACtE,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;CACpC;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,cAAc,CAAA;CACxB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,MAAM,CAAA;CACf"}
|
|
@@ -65,31 +65,20 @@ export type RequestMessage = {
|
|
|
65
65
|
/** The document ID this message requests */
|
|
66
66
|
documentId: DocumentId;
|
|
67
67
|
};
|
|
68
|
-
/**
|
|
69
|
-
export type
|
|
70
|
-
type: "
|
|
68
|
+
/** (anticipating work in progress) */
|
|
69
|
+
export type AuthMessage<TPayload = any> = {
|
|
70
|
+
type: "auth";
|
|
71
71
|
/** The peer ID of the sender of this message */
|
|
72
72
|
senderId: PeerId;
|
|
73
|
-
/** Arrive messages don't have a targetId */
|
|
74
|
-
targetId: never;
|
|
75
|
-
};
|
|
76
|
-
/** Respond to an arriving peer with our peer ID */
|
|
77
|
-
export type WelcomeMessage = {
|
|
78
|
-
type: "welcome";
|
|
79
|
-
/** The peer ID of the recipient sender this message */
|
|
80
|
-
senderId: PeerId;
|
|
81
73
|
/** The peer ID of the recipient of this message */
|
|
82
74
|
targetId: PeerId;
|
|
75
|
+
/** The payload of the auth message (up to the specific auth provider) */
|
|
76
|
+
payload: TPayload;
|
|
83
77
|
};
|
|
84
78
|
/** These are message types that a {@link NetworkAdapter} surfaces to a {@link Repo}. */
|
|
85
79
|
export type RepoMessage = SyncMessage | EphemeralMessage | RequestMessage | DocumentUnavailableMessage;
|
|
86
|
-
/** These are all the message types that a {@link NetworkAdapter} might see.
|
|
87
|
-
|
|
88
|
-
* @remarks
|
|
89
|
-
* It is not _required_ that a {@link NetworkAdapter} use these types: They are free to use
|
|
90
|
-
* whatever message type makes sense for their transport. However, this type is a useful default.
|
|
91
|
-
* */
|
|
92
|
-
export type Message = RepoMessage | ArriveMessage | WelcomeMessage;
|
|
80
|
+
/** These are all the message types that a {@link NetworkAdapter} might see. */
|
|
81
|
+
export type Message = RepoMessage | AuthMessage;
|
|
93
82
|
/**
|
|
94
83
|
* The contents of a message, without the sender ID or other properties added by the {@link NetworkSubsystem})
|
|
95
84
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/network/messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAE3D;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAA;IAEZ,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAA;IAEhB,mDAAmD;IACnD,QAAQ,EAAE,MAAM,CAAA;IAEhB,iCAAiC;IACjC,IAAI,EAAE,UAAU,CAAA;IAEhB,0DAA0D;IAC1D,UAAU,EAAE,UAAU,CAAA;CACvB,CAAA;AAED;;;;;;;;;KASK;AACL,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,WAAW,CAAA;IAEjB,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAA;IAEhB,mDAAmD;IACnD,QAAQ,EAAE,MAAM,CAAA;IAEhB,qFAAqF;IACrF,KAAK,EAAE,MAAM,CAAA;IAEb,8GAA8G;IAC9G,SAAS,EAAE,SAAS,CAAA;IAEpB,+CAA+C;IAC/C,UAAU,EAAE,UAAU,CAAA;IAEtB,qCAAqC;IACrC,IAAI,EAAE,UAAU,CAAA;CACjB,CAAA;AAED,uHAAuH;AACvH,MAAM,MAAM,0BAA0B,GAAG;IACvC,IAAI,EAAE,iBAAiB,CAAA;IAEvB,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAA;IAEhB,mDAAmD;IACnD,QAAQ,EAAE,MAAM,CAAA;IAEhB,yDAAyD;IACzD,UAAU,EAAE,UAAU,CAAA;CACvB,CAAA;AAED;;;;;KAKK;AACL,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,SAAS,CAAA;IAEf,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAA;IAEhB,mDAAmD;IACnD,QAAQ,EAAE,MAAM,CAAA;IAEhB,yCAAyC;IACzC,IAAI,EAAE,UAAU,CAAA;IAEhB,4CAA4C;IAC5C,UAAU,EAAE,UAAU,CAAA;CACvB,CAAA;AAED,
|
|
1
|
+
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/network/messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAE3D;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAA;IAEZ,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAA;IAEhB,mDAAmD;IACnD,QAAQ,EAAE,MAAM,CAAA;IAEhB,iCAAiC;IACjC,IAAI,EAAE,UAAU,CAAA;IAEhB,0DAA0D;IAC1D,UAAU,EAAE,UAAU,CAAA;CACvB,CAAA;AAED;;;;;;;;;KASK;AACL,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,WAAW,CAAA;IAEjB,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAA;IAEhB,mDAAmD;IACnD,QAAQ,EAAE,MAAM,CAAA;IAEhB,qFAAqF;IACrF,KAAK,EAAE,MAAM,CAAA;IAEb,8GAA8G;IAC9G,SAAS,EAAE,SAAS,CAAA;IAEpB,+CAA+C;IAC/C,UAAU,EAAE,UAAU,CAAA;IAEtB,qCAAqC;IACrC,IAAI,EAAE,UAAU,CAAA;CACjB,CAAA;AAED,uHAAuH;AACvH,MAAM,MAAM,0BAA0B,GAAG;IACvC,IAAI,EAAE,iBAAiB,CAAA;IAEvB,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAA;IAEhB,mDAAmD;IACnD,QAAQ,EAAE,MAAM,CAAA;IAEhB,yDAAyD;IACzD,UAAU,EAAE,UAAU,CAAA;CACvB,CAAA;AAED;;;;;KAKK;AACL,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,SAAS,CAAA;IAEf,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAA;IAEhB,mDAAmD;IACnD,QAAQ,EAAE,MAAM,CAAA;IAEhB,yCAAyC;IACzC,IAAI,EAAE,UAAU,CAAA;IAEhB,4CAA4C;IAC5C,UAAU,EAAE,UAAU,CAAA;CACvB,CAAA;AAED,sCAAsC;AACtC,MAAM,MAAM,WAAW,CAAC,QAAQ,GAAG,GAAG,IAAI;IACxC,IAAI,EAAE,MAAM,CAAA;IAEZ,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAA;IAEhB,mDAAmD;IACnD,QAAQ,EAAE,MAAM,CAAA;IAEhB,yEAAyE;IACzE,OAAO,EAAE,QAAQ,CAAA;CAClB,CAAA;AAED,wFAAwF;AACxF,MAAM,MAAM,WAAW,GACnB,WAAW,GACX,gBAAgB,GAChB,cAAc,GACd,0BAA0B,CAAA;AAE9B,+EAA+E;AAC/E,MAAM,MAAM,OAAO,GAAG,WAAW,GAAG,WAAW,CAAA;AAE/C;;GAEG;AACH,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO,IACrD,CAAC,SAAS,gBAAgB,GACtB,IAAI,CAAC,CAAC,EAAE,UAAU,GAAG,OAAO,GAAG,WAAW,CAAC,GAC3C,IAAI,CAAC,CAAC,EAAE,UAAU,CAAC,CAAA;AAIzB,eAAO,MAAM,kBAAkB,YAAa,OAAO,2BAOT,CAAA;AAG1C,eAAO,MAAM,4BAA4B,QAAS,OAAO,sCACzB,CAAA;AAEhC,eAAO,MAAM,gBAAgB,QAAS,OAAO,0BACrB,CAAA;AAExB,eAAO,MAAM,aAAa,QAAS,OAAO,uBACrB,CAAA;AAErB,eAAO,MAAM,kBAAkB,QAAS,OAAO,4BACrB,CAAA"}
|
|
@@ -1,33 +1,30 @@
|
|
|
1
|
+
import { StorageKey, Chunk } from "./types.js";
|
|
1
2
|
/** A storage adapter represents some way of storing binary data for a {@link Repo}
|
|
2
3
|
*
|
|
3
4
|
* @remarks
|
|
4
|
-
* `StorageAdapter`s
|
|
5
|
-
*
|
|
5
|
+
* `StorageAdapter`s provide a key/value storage interface. The keys are arrays of strings
|
|
6
|
+
* ({@link StorageKey}) and the values are binary blobs.
|
|
6
7
|
*/
|
|
7
8
|
export declare abstract class StorageAdapter {
|
|
8
|
-
/** Load the single
|
|
9
|
+
/** Load the single value corresponding to `key` */
|
|
9
10
|
abstract load(key: StorageKey): Promise<Uint8Array | undefined>;
|
|
10
|
-
/**
|
|
11
|
+
/** Save the value `data` to the key `key` */
|
|
11
12
|
abstract save(key: StorageKey, data: Uint8Array): Promise<void>;
|
|
12
|
-
/**
|
|
13
|
+
/** Remove the value corresponding to `key` */
|
|
13
14
|
abstract remove(key: StorageKey): Promise<void>;
|
|
14
|
-
/**
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Load all values with keys that start with `keyPrefix`.
|
|
17
|
+
*
|
|
18
|
+
* @remarks
|
|
19
|
+
* The `keyprefix` will match any key that starts with the given array. For example:
|
|
20
|
+
* - `[documentId, "incremental"]` will match all incremental saves
|
|
21
|
+
* - `[documentId]` will match all data for a given document.
|
|
22
|
+
*
|
|
23
|
+
* Be careful! `[documentId]` would also match something like `[documentId, "syncState"]`! We
|
|
24
|
+
* aren't using this yet but keep it in mind.)
|
|
25
|
+
*/
|
|
26
|
+
abstract loadRange(keyPrefix: StorageKey): Promise<Chunk[]>;
|
|
27
|
+
/** Remove all values with keys that start with `keyPrefix` */
|
|
20
28
|
abstract removeRange(keyPrefix: StorageKey): Promise<void>;
|
|
21
29
|
}
|
|
22
|
-
/** The type of keys for a {@link StorageAdapter}
|
|
23
|
-
*
|
|
24
|
-
* @remarks
|
|
25
|
-
* Storage keys are arrays because they are hierarchical and the storage
|
|
26
|
-
* subsystem will need to be able to do range queries for all keys that
|
|
27
|
-
* have a particular prefix. For example, incremental changes for a given
|
|
28
|
-
* document might be stored under `[<documentId>, "incremental", <SHA256>]`.
|
|
29
|
-
* `StorageAdapter` implementations should not assume any particular structure
|
|
30
|
-
* though.
|
|
31
|
-
**/
|
|
32
|
-
export type StorageKey = string[];
|
|
33
30
|
//# sourceMappingURL=StorageAdapter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StorageAdapter.d.ts","sourceRoot":"","sources":["../../src/storage/StorageAdapter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,8BAAsB,cAAc;
|
|
1
|
+
{"version":3,"file":"StorageAdapter.d.ts","sourceRoot":"","sources":["../../src/storage/StorageAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAE9C;;;;;GAKG;AACH,8BAAsB,cAAc;IAClC,mDAAmD;IACnD,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAE/D,6CAA6C;IAC7C,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAE/D,8CAA8C;IAC9C,QAAQ,CAAC,MAAM,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAE/C;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,SAAS,CAAC,SAAS,EAAE,UAAU,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;IAE3D,8DAA8D;IAC9D,QAAQ,CAAC,WAAW,CAAC,SAAS,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;CAC3D"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/** A storage adapter represents some way of storing binary data for a {@link Repo}
|
|
2
2
|
*
|
|
3
3
|
* @remarks
|
|
4
|
-
* `StorageAdapter`s
|
|
5
|
-
*
|
|
4
|
+
* `StorageAdapter`s provide a key/value storage interface. The keys are arrays of strings
|
|
5
|
+
* ({@link StorageKey}) and the values are binary blobs.
|
|
6
6
|
*/
|
|
7
7
|
export class StorageAdapter {
|
|
8
8
|
}
|
|
@@ -1,12 +1,48 @@
|
|
|
1
1
|
import * as A from "@automerge/automerge/next";
|
|
2
2
|
import { type DocumentId } from "../types.js";
|
|
3
3
|
import { StorageAdapter } from "./StorageAdapter.js";
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* The storage subsystem is responsible for saving and loading Automerge documents to and from
|
|
6
|
+
* storage adapter. It also provides a generic key/value storage interface for other uses.
|
|
7
|
+
*/
|
|
5
8
|
export declare class StorageSubsystem {
|
|
6
9
|
#private;
|
|
7
10
|
constructor(storageAdapter: StorageAdapter);
|
|
8
|
-
|
|
11
|
+
/** Loads a value from storage. */
|
|
12
|
+
load(
|
|
13
|
+
/** Namespace to prevent collisions with other users of the storage subsystem. */
|
|
14
|
+
namespace: string,
|
|
15
|
+
/** Key to load. Typically a UUID or other unique identifier, but could be any string. */
|
|
16
|
+
key: string): Promise<Uint8Array | undefined>;
|
|
17
|
+
/** Saves a value in storage. */
|
|
18
|
+
save(
|
|
19
|
+
/** Namespace to prevent collisions with other users of the storage subsystem. */
|
|
20
|
+
namespace: string,
|
|
21
|
+
/** Key to load. Typically a UUID or other unique identifier, but could be any string. */
|
|
22
|
+
key: string,
|
|
23
|
+
/** Data to save, as a binary blob. */
|
|
24
|
+
data: Uint8Array): Promise<void>;
|
|
25
|
+
/** Removes a value from storage. */
|
|
26
|
+
remove(
|
|
27
|
+
/** Namespace to prevent collisions with other users of the storage subsystem. */
|
|
28
|
+
namespace: string,
|
|
29
|
+
/** Key to remove. Typically a UUID or other unique identifier, but could be any string. */
|
|
30
|
+
key: string): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Loads the Automerge document with the given ID from storage.
|
|
33
|
+
*/
|
|
34
|
+
loadDoc<T>(documentId: DocumentId): Promise<A.Doc<T> | null>;
|
|
35
|
+
/**
|
|
36
|
+
* Saves the provided Automerge document to storage.
|
|
37
|
+
*
|
|
38
|
+
* @remarks
|
|
39
|
+
* Under the hood this makes incremental saves until the incremental size is greater than the
|
|
40
|
+
* snapshot size, at which point the document is compacted into a single snapshot.
|
|
41
|
+
*/
|
|
9
42
|
saveDoc(documentId: DocumentId, doc: A.Doc<unknown>): Promise<void>;
|
|
10
|
-
|
|
43
|
+
/**
|
|
44
|
+
* Removes the Automerge document with the given ID from storage
|
|
45
|
+
*/
|
|
46
|
+
removeDoc(documentId: DocumentId): Promise<void>;
|
|
11
47
|
}
|
|
12
48
|
//# sourceMappingURL=StorageSubsystem.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StorageSubsystem.d.ts","sourceRoot":"","sources":["../../src/storage/StorageSubsystem.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,2BAA2B,CAAA;
|
|
1
|
+
{"version":3,"file":"StorageSubsystem.d.ts","sourceRoot":"","sources":["../../src/storage/StorageSubsystem.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,2BAA2B,CAAA;AAI9C,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAA;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAKpD;;;GAGG;AACH,qBAAa,gBAAgB;;gBAef,cAAc,EAAE,cAAc;IAc1C,kCAAkC;IAC5B,IAAI;IACR,iFAAiF;IACjF,SAAS,EAAE,MAAM;IAEjB,yFAAyF;IACzF,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAKlC,gCAAgC;IAC1B,IAAI;IACR,iFAAiF;IACjF,SAAS,EAAE,MAAM;IAEjB,yFAAyF;IACzF,GAAG,EAAE,MAAM;IAEX,sCAAsC;IACtC,IAAI,EAAE,UAAU,GACf,OAAO,CAAC,IAAI,CAAC;IAKhB,oCAAoC;IAC9B,MAAM;IACV,iFAAiF;IACjF,SAAS,EAAE,MAAM;IAEjB,2FAA2F;IAC3F,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,IAAI,CAAC;IAOhB;;OAEG;IACG,OAAO,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAmClE;;;;;;OAMG;IACG,OAAO,CAAC,UAAU,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAazE;;OAEG;IACG,SAAS,CAAC,UAAU,EAAE,UAAU;CAsGvC"}
|
|
@@ -1,28 +1,127 @@
|
|
|
1
1
|
import * as A from "@automerge/automerge/next";
|
|
2
2
|
import debug from "debug";
|
|
3
|
-
import * as sha256 from "fast-sha256";
|
|
4
3
|
import { headsAreSame } from "../helpers/headsAreSame.js";
|
|
5
4
|
import { mergeArrays } from "../helpers/mergeArrays.js";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
function headsHash(heads) {
|
|
13
|
-
const encoder = new TextEncoder();
|
|
14
|
-
const headsbinary = mergeArrays(heads.map((h) => encoder.encode(h)));
|
|
15
|
-
return keyHash(headsbinary);
|
|
16
|
-
}
|
|
5
|
+
import { keyHash, headsHash } from "./keyHash.js";
|
|
6
|
+
import { chunkTypeFromKey } from "./chunkTypeFromKey.js";
|
|
7
|
+
/**
|
|
8
|
+
* The storage subsystem is responsible for saving and loading Automerge documents to and from
|
|
9
|
+
* storage adapter. It also provides a generic key/value storage interface for other uses.
|
|
10
|
+
*/
|
|
17
11
|
export class StorageSubsystem {
|
|
12
|
+
/** The storage adapter to use for saving and loading documents */
|
|
18
13
|
#storageAdapter;
|
|
19
|
-
|
|
14
|
+
/** Record of the latest heads we've loaded or saved for each document */
|
|
20
15
|
#storedHeads = new Map();
|
|
16
|
+
/** Metadata on the chunks we've already loaded for each document */
|
|
17
|
+
#chunkInfos = new Map();
|
|
18
|
+
/** Flag to avoid compacting when a compaction is already underway */
|
|
19
|
+
#compacting = false;
|
|
21
20
|
#log = debug(`automerge-repo:storage-subsystem`);
|
|
22
|
-
#snapshotting = false;
|
|
23
21
|
constructor(storageAdapter) {
|
|
24
22
|
this.#storageAdapter = storageAdapter;
|
|
25
23
|
}
|
|
24
|
+
// ARBITRARY KEY/VALUE STORAGE
|
|
25
|
+
// The `load`, `save`, and `remove` methods are for generic key/value storage, as opposed to
|
|
26
|
+
// Automerge documents. For example, they're used by the LocalFirstAuthProvider to persist the
|
|
27
|
+
// encrypted team graph that encodes group membership and permissions.
|
|
28
|
+
//
|
|
29
|
+
// The namespace parameter is to prevent collisions with other users of the storage subsystem.
|
|
30
|
+
// Typically this will be the name of the plug-in, adapter, or other system that is using it. For
|
|
31
|
+
// example, the LocalFirstAuthProvider uses the namespace `LocalFirstAuthProvider`.
|
|
32
|
+
/** Loads a value from storage. */
|
|
33
|
+
async load(
|
|
34
|
+
/** Namespace to prevent collisions with other users of the storage subsystem. */
|
|
35
|
+
namespace,
|
|
36
|
+
/** Key to load. Typically a UUID or other unique identifier, but could be any string. */
|
|
37
|
+
key) {
|
|
38
|
+
const storageKey = [namespace, key];
|
|
39
|
+
return await this.#storageAdapter.load(storageKey);
|
|
40
|
+
}
|
|
41
|
+
/** Saves a value in storage. */
|
|
42
|
+
async save(
|
|
43
|
+
/** Namespace to prevent collisions with other users of the storage subsystem. */
|
|
44
|
+
namespace,
|
|
45
|
+
/** Key to load. Typically a UUID or other unique identifier, but could be any string. */
|
|
46
|
+
key,
|
|
47
|
+
/** Data to save, as a binary blob. */
|
|
48
|
+
data) {
|
|
49
|
+
const storageKey = [namespace, key];
|
|
50
|
+
await this.#storageAdapter.save(storageKey, data);
|
|
51
|
+
}
|
|
52
|
+
/** Removes a value from storage. */
|
|
53
|
+
async remove(
|
|
54
|
+
/** Namespace to prevent collisions with other users of the storage subsystem. */
|
|
55
|
+
namespace,
|
|
56
|
+
/** Key to remove. Typically a UUID or other unique identifier, but could be any string. */
|
|
57
|
+
key) {
|
|
58
|
+
const storageKey = [namespace, key];
|
|
59
|
+
await this.#storageAdapter.remove(storageKey);
|
|
60
|
+
}
|
|
61
|
+
// AUTOMERGE DOCUMENT STORAGE
|
|
62
|
+
/**
|
|
63
|
+
* Loads the Automerge document with the given ID from storage.
|
|
64
|
+
*/
|
|
65
|
+
async loadDoc(documentId) {
|
|
66
|
+
// Load all the chunks for this document
|
|
67
|
+
const chunks = await this.#storageAdapter.loadRange([documentId]);
|
|
68
|
+
const binaries = [];
|
|
69
|
+
const chunkInfos = [];
|
|
70
|
+
for (const chunk of chunks) {
|
|
71
|
+
// chunks might have been deleted in the interim
|
|
72
|
+
if (chunk.data === undefined)
|
|
73
|
+
continue;
|
|
74
|
+
const chunkType = chunkTypeFromKey(chunk.key);
|
|
75
|
+
if (chunkType == null)
|
|
76
|
+
continue;
|
|
77
|
+
chunkInfos.push({
|
|
78
|
+
key: chunk.key,
|
|
79
|
+
type: chunkType,
|
|
80
|
+
size: chunk.data.length,
|
|
81
|
+
});
|
|
82
|
+
binaries.push(chunk.data);
|
|
83
|
+
}
|
|
84
|
+
this.#chunkInfos.set(documentId, chunkInfos);
|
|
85
|
+
// Merge the chunks into a single binary
|
|
86
|
+
const binary = mergeArrays(binaries);
|
|
87
|
+
if (binary.length === 0)
|
|
88
|
+
return null;
|
|
89
|
+
// Load into an Automerge document
|
|
90
|
+
const newDoc = A.loadIncremental(A.init(), binary);
|
|
91
|
+
// Record the latest heads for the document
|
|
92
|
+
this.#storedHeads.set(documentId, A.getHeads(newDoc));
|
|
93
|
+
return newDoc;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Saves the provided Automerge document to storage.
|
|
97
|
+
*
|
|
98
|
+
* @remarks
|
|
99
|
+
* Under the hood this makes incremental saves until the incremental size is greater than the
|
|
100
|
+
* snapshot size, at which point the document is compacted into a single snapshot.
|
|
101
|
+
*/
|
|
102
|
+
async saveDoc(documentId, doc) {
|
|
103
|
+
// Don't bother saving if the document hasn't changed
|
|
104
|
+
if (!this.#shouldSave(documentId, doc))
|
|
105
|
+
return;
|
|
106
|
+
const sourceChunks = this.#chunkInfos.get(documentId) ?? [];
|
|
107
|
+
if (this.#shouldCompact(sourceChunks)) {
|
|
108
|
+
await this.#saveTotal(documentId, doc, sourceChunks);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
await this.#saveIncremental(documentId, doc);
|
|
112
|
+
}
|
|
113
|
+
this.#storedHeads.set(documentId, A.getHeads(doc));
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Removes the Automerge document with the given ID from storage
|
|
117
|
+
*/
|
|
118
|
+
async removeDoc(documentId) {
|
|
119
|
+
await this.#storageAdapter.removeRange([documentId, "snapshot"]);
|
|
120
|
+
await this.#storageAdapter.removeRange([documentId, "incremental"]);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Saves just the incremental changes since the last save.
|
|
124
|
+
*/
|
|
26
125
|
async #saveIncremental(documentId, doc) {
|
|
27
126
|
const binary = A.saveSince(doc, this.#storedHeads.get(documentId) ?? []);
|
|
28
127
|
if (binary && binary.length > 0) {
|
|
@@ -43,8 +142,11 @@ export class StorageSubsystem {
|
|
|
43
142
|
return Promise.resolve();
|
|
44
143
|
}
|
|
45
144
|
}
|
|
145
|
+
/**
|
|
146
|
+
* Compacts the document storage into a single shapshot.
|
|
147
|
+
*/
|
|
46
148
|
async #saveTotal(documentId, doc, sourceChunks) {
|
|
47
|
-
this.#
|
|
149
|
+
this.#compacting = true;
|
|
48
150
|
const binary = A.save(doc);
|
|
49
151
|
const snapshotHash = headsHash(A.getHeads(doc));
|
|
50
152
|
const key = [documentId, "snapshot", snapshotHash];
|
|
@@ -58,66 +160,30 @@ export class StorageSubsystem {
|
|
|
58
160
|
const newChunkInfos = this.#chunkInfos.get(documentId)?.filter(c => !oldKeys.has(c.key)) ?? [];
|
|
59
161
|
newChunkInfos.push({ key, type: "snapshot", size: binary.length });
|
|
60
162
|
this.#chunkInfos.set(documentId, newChunkInfos);
|
|
61
|
-
this.#
|
|
62
|
-
}
|
|
63
|
-
async loadDoc(documentId) {
|
|
64
|
-
const loaded = await this.#storageAdapter.loadRange([documentId]);
|
|
65
|
-
const binaries = [];
|
|
66
|
-
const chunkInfos = [];
|
|
67
|
-
for (const chunk of loaded) {
|
|
68
|
-
const chunkType = chunkTypeFromKey(chunk.key);
|
|
69
|
-
if (chunkType == null) {
|
|
70
|
-
continue;
|
|
71
|
-
}
|
|
72
|
-
chunkInfos.push({
|
|
73
|
-
key: chunk.key,
|
|
74
|
-
type: chunkType,
|
|
75
|
-
size: chunk.data.length,
|
|
76
|
-
});
|
|
77
|
-
binaries.push(chunk.data);
|
|
78
|
-
}
|
|
79
|
-
this.#chunkInfos.set(documentId, chunkInfos);
|
|
80
|
-
const binary = mergeArrays(binaries);
|
|
81
|
-
if (binary.length === 0) {
|
|
82
|
-
return null;
|
|
83
|
-
}
|
|
84
|
-
const newDoc = A.loadIncremental(A.init(), binary);
|
|
85
|
-
this.#storedHeads.set(documentId, A.getHeads(newDoc));
|
|
86
|
-
return newDoc;
|
|
87
|
-
}
|
|
88
|
-
async saveDoc(documentId, doc) {
|
|
89
|
-
if (!this.#shouldSave(documentId, doc)) {
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
const sourceChunks = this.#chunkInfos.get(documentId) ?? [];
|
|
93
|
-
if (this.#shouldCompact(sourceChunks)) {
|
|
94
|
-
void this.#saveTotal(documentId, doc, sourceChunks);
|
|
95
|
-
}
|
|
96
|
-
else {
|
|
97
|
-
void this.#saveIncremental(documentId, doc);
|
|
98
|
-
}
|
|
99
|
-
this.#storedHeads.set(documentId, A.getHeads(doc));
|
|
100
|
-
}
|
|
101
|
-
async remove(documentId) {
|
|
102
|
-
void this.#storageAdapter.removeRange([documentId, "snapshot"]);
|
|
103
|
-
void this.#storageAdapter.removeRange([documentId, "incremental"]);
|
|
163
|
+
this.#compacting = false;
|
|
104
164
|
}
|
|
165
|
+
/**
|
|
166
|
+
* Returns true if the document has changed since the last time it was saved.
|
|
167
|
+
*/
|
|
105
168
|
#shouldSave(documentId, doc) {
|
|
106
169
|
const oldHeads = this.#storedHeads.get(documentId);
|
|
107
170
|
if (!oldHeads) {
|
|
171
|
+
// we haven't saved this document before
|
|
108
172
|
return true;
|
|
109
173
|
}
|
|
110
174
|
const newHeads = A.getHeads(doc);
|
|
111
175
|
if (headsAreSame(newHeads, oldHeads)) {
|
|
176
|
+
// the document hasn't changed
|
|
112
177
|
return false;
|
|
113
178
|
}
|
|
114
|
-
return true;
|
|
179
|
+
return true; // the document has changed
|
|
115
180
|
}
|
|
181
|
+
/**
|
|
182
|
+
* We only compact if the incremental size is greater than the snapshot size.
|
|
183
|
+
*/
|
|
116
184
|
#shouldCompact(sourceChunks) {
|
|
117
|
-
if (this.#
|
|
185
|
+
if (this.#compacting)
|
|
118
186
|
return false;
|
|
119
|
-
}
|
|
120
|
-
// compact if the incremental size is greater than the snapshot size
|
|
121
187
|
let snapshotSize = 0;
|
|
122
188
|
let incrementalSize = 0;
|
|
123
189
|
for (const chunk of sourceChunks) {
|
|
@@ -131,16 +197,3 @@ export class StorageSubsystem {
|
|
|
131
197
|
return incrementalSize >= snapshotSize;
|
|
132
198
|
}
|
|
133
199
|
}
|
|
134
|
-
function chunkTypeFromKey(key) {
|
|
135
|
-
if (key.length < 2) {
|
|
136
|
-
return null;
|
|
137
|
-
}
|
|
138
|
-
const chunkTypeStr = key[key.length - 2];
|
|
139
|
-
if (chunkTypeStr === "snapshot" || chunkTypeStr === "incremental") {
|
|
140
|
-
const chunkType = chunkTypeStr;
|
|
141
|
-
return chunkType;
|
|
142
|
-
}
|
|
143
|
-
else {
|
|
144
|
-
return null;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { StorageKey } from "./types.js";
|
|
2
|
+
import { ChunkType } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Keys for storing Automerge documents are of the form:
|
|
5
|
+
* ```ts
|
|
6
|
+
* [documentId, "snapshot", hash] // OR
|
|
7
|
+
* [documentId, "incremental", hash]
|
|
8
|
+
* ```
|
|
9
|
+
* This function returns the chunk type ("snapshot" or "incremental") if the key is in one of these
|
|
10
|
+
* forms.
|
|
11
|
+
*/
|
|
12
|
+
export declare function chunkTypeFromKey(key: StorageKey): ChunkType | null;
|
|
13
|
+
//# sourceMappingURL=chunkTypeFromKey.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunkTypeFromKey.d.ts","sourceRoot":"","sources":["../../src/storage/chunkTypeFromKey.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAEtC;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,UAAU,GAAG,SAAS,GAAG,IAAI,CASlE"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keys for storing Automerge documents are of the form:
|
|
3
|
+
* ```ts
|
|
4
|
+
* [documentId, "snapshot", hash] // OR
|
|
5
|
+
* [documentId, "incremental", hash]
|
|
6
|
+
* ```
|
|
7
|
+
* This function returns the chunk type ("snapshot" or "incremental") if the key is in one of these
|
|
8
|
+
* forms.
|
|
9
|
+
*/
|
|
10
|
+
export function chunkTypeFromKey(key) {
|
|
11
|
+
if (key.length < 2)
|
|
12
|
+
return null;
|
|
13
|
+
const chunkTypeStr = key[key.length - 2]; // next-to-last element in key
|
|
14
|
+
if (chunkTypeStr === "snapshot" || chunkTypeStr === "incremental") {
|
|
15
|
+
return chunkTypeStr;
|
|
16
|
+
}
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyHash.d.ts","sourceRoot":"","sources":["../../src/storage/keyHash.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,2BAA2B,CAAA;AAI9C,wBAAgB,OAAO,CAAC,MAAM,EAAE,UAAU,UAIzC;AACD,wBAAgB,SAAS,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,GAAG,MAAM,CAIhD"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as sha256 from "fast-sha256";
|
|
2
|
+
import { mergeArrays } from "../helpers/mergeArrays.js";
|
|
3
|
+
export function keyHash(binary) {
|
|
4
|
+
// calculate hash
|
|
5
|
+
const hash = sha256.hash(binary);
|
|
6
|
+
return bufferToHexString(hash);
|
|
7
|
+
}
|
|
8
|
+
export function headsHash(heads) {
|
|
9
|
+
const encoder = new TextEncoder();
|
|
10
|
+
const headsbinary = mergeArrays(heads.map((h) => encoder.encode(h)));
|
|
11
|
+
return keyHash(headsbinary);
|
|
12
|
+
}
|
|
13
|
+
function bufferToHexString(data) {
|
|
14
|
+
return Array.from(data, byte => byte.toString(16).padStart(2, "0")).join("");
|
|
15
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A chunk is a snapshot or incremental change that is stored in a {@link StorageAdapter}.
|
|
3
|
+
*/
|
|
4
|
+
export type Chunk = {
|
|
5
|
+
key: StorageKey;
|
|
6
|
+
data: Uint8Array | undefined;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Metadata about a chunk of data loaded from storage. This is stored on the StorageSubsystem so
|
|
10
|
+
* when we are compacting we know what chunks we can safely delete.
|
|
11
|
+
*/
|
|
12
|
+
export type ChunkInfo = {
|
|
13
|
+
key: StorageKey;
|
|
14
|
+
type: ChunkType;
|
|
15
|
+
size: number;
|
|
16
|
+
};
|
|
17
|
+
export type ChunkType = "snapshot" | "incremental";
|
|
18
|
+
/**
|
|
19
|
+
* A storage key is an array of strings that represents a path to a value in a
|
|
20
|
+
* {@link StorageAdapter}.
|
|
21
|
+
*
|
|
22
|
+
* @remarks
|
|
23
|
+
* Storage keys are arrays because they are hierarchical and they allow the storage subsystem to do
|
|
24
|
+
* range queries for all keys that have a particular prefix. For example, incremental changes for a
|
|
25
|
+
* given document might be stored under `[<documentId>, "incremental", <SHA256>]`.
|
|
26
|
+
*
|
|
27
|
+
* automerge-repo mostly uses keys in the following form:
|
|
28
|
+
* ```ts
|
|
29
|
+
* [documentId, "snapshot", hash] // OR
|
|
30
|
+
* [documentId, "incremental", hash]
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* However, the storage adapter implementation should be agnostic to the meaning of the key and
|
|
34
|
+
* should not assume any particular structure.
|
|
35
|
+
**/
|
|
36
|
+
export type StorageKey = string[];
|
|
37
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/storage/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,KAAK,GAAG;IAClB,GAAG,EAAE,UAAU,CAAA;IACf,IAAI,EAAE,UAAU,GAAG,SAAS,CAAA;CAC7B,CAAA;AAED;;;GAGG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB,GAAG,EAAE,UAAU,CAAA;IACf,IAAI,EAAE,SAAS,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,UAAU,GAAG,aAAa,CAAA;AAElD;;;;;;;;;;;;;;;;;IAiBI;AACJ,MAAM,MAAM,UAAU,GAAG,MAAM,EAAE,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automerge/automerge-repo",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.15",
|
|
4
4
|
"description": "A repository object to manage a collection of automerge documents",
|
|
5
5
|
"repository": "https://github.com/automerge/automerge-repo/tree/master/packages/automerge-repo",
|
|
6
6
|
"author": "Peter van Hardenberg <pvh@pvh.ca>",
|
|
@@ -57,5 +57,5 @@
|
|
|
57
57
|
"publishConfig": {
|
|
58
58
|
"access": "public"
|
|
59
59
|
},
|
|
60
|
-
"gitHead": "
|
|
60
|
+
"gitHead": "f60bd2a4594cc23b0ee6debed62aea61fb48ea56"
|
|
61
61
|
}
|
package/src/Repo.ts
CHANGED
|
@@ -106,7 +106,7 @@ export class Repo extends EventEmitter<RepoEvents> {
|
|
|
106
106
|
// synchronizer.removeDocument(documentId)
|
|
107
107
|
|
|
108
108
|
if (storageSubsystem) {
|
|
109
|
-
storageSubsystem.
|
|
109
|
+
storageSubsystem.removeDoc(documentId).catch(err => {
|
|
110
110
|
this.#log("error deleting document", { documentId, err })
|
|
111
111
|
})
|
|
112
112
|
}
|