@automerge/automerge-repo 1.0.13 → 1.0.14

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 (36) hide show
  1. package/dist/Repo.js +1 -1
  2. package/dist/helpers/cbor.js +1 -1
  3. package/dist/index.d.ts +2 -2
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/network/NetworkAdapter.d.ts +3 -3
  6. package/dist/network/NetworkAdapter.d.ts.map +1 -1
  7. package/dist/network/messages.d.ts +7 -18
  8. package/dist/network/messages.d.ts.map +1 -1
  9. package/dist/storage/StorageAdapter.d.ts +19 -22
  10. package/dist/storage/StorageAdapter.d.ts.map +1 -1
  11. package/dist/storage/StorageAdapter.js +2 -2
  12. package/dist/storage/StorageSubsystem.d.ts +39 -3
  13. package/dist/storage/StorageSubsystem.d.ts.map +1 -1
  14. package/dist/storage/StorageSubsystem.js +128 -75
  15. package/dist/storage/chunkTypeFromKey.d.ts +13 -0
  16. package/dist/storage/chunkTypeFromKey.d.ts.map +1 -0
  17. package/dist/storage/chunkTypeFromKey.js +18 -0
  18. package/dist/storage/keyHash.d.ts +4 -0
  19. package/dist/storage/keyHash.d.ts.map +1 -0
  20. package/dist/storage/keyHash.js +15 -0
  21. package/dist/storage/types.d.ts +37 -0
  22. package/dist/storage/types.d.ts.map +1 -0
  23. package/dist/storage/types.js +1 -0
  24. package/package.json +2 -2
  25. package/src/Repo.ts +1 -1
  26. package/src/helpers/cbor.ts +1 -1
  27. package/src/index.ts +11 -3
  28. package/src/network/NetworkAdapter.ts +3 -3
  29. package/src/network/messages.ts +8 -21
  30. package/src/storage/StorageAdapter.ts +23 -30
  31. package/src/storage/StorageSubsystem.ts +159 -93
  32. package/src/storage/chunkTypeFromKey.ts +22 -0
  33. package/src/storage/keyHash.ts +17 -0
  34. package/src/storage/types.ts +39 -0
  35. package/test/StorageSubsystem.test.ts +143 -35
  36. 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.remove(documentId).catch(err => {
84
+ storageSubsystem.removeDoc(documentId).catch(err => {
85
85
  this.#log("error deleting document", { documentId, err });
86
86
  });
87
87
  }
@@ -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 { ArriveMessage, DocumentUnavailableMessage, EphemeralMessage, Message, RepoMessage, RequestMessage, SyncMessage, WelcomeMessage, } from "./network/messages.js";
40
- export type { StorageKey } from "./storage/StorageAdapter.js";
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
@@ -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;AACvB,YAAY,EACV,qBAAqB,EACrB,eAAe,EACf,UAAU,EACV,UAAU,EACV,WAAW,GACZ,MAAM,WAAW,CAAA;AAClB,YAAY,EACV,oBAAoB,EACpB,WAAW,EACX,oBAAoB,EACpB,uBAAuB,GACxB,MAAM,6BAA6B,CAAA;AACpC,YAAY,EACV,aAAa,EACb,0BAA0B,EAC1B,gBAAgB,EAChB,OAAO,EACP,WAAW,EACX,cAAc,EACd,WAAW,EACX,cAAc,GACf,MAAM,uBAAuB,CAAA;AAC9B,YAAY,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAA;AAC7D,cAAc,YAAY,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 { RepoMessage } from "./messages.js";
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: RepoMessage): void;
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: RepoMessage) => void;
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,WAAW,EAAE,MAAM,eAAe,CAAA;AAE3C;;;;;;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,WAAW,GAAG,IAAI;IAEzC,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,WAAW,KAAK,IAAI,CAAA;CACxC;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"}
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
- /** Notify the network that we have arrived so everyone knows our peer ID */
69
- export type ArriveMessage = {
70
- type: "arrive";
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,4EAA4E;AAC5E,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,QAAQ,CAAA;IAEd,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAA;IAEhB,4CAA4C;IAC5C,QAAQ,EAAE,KAAK,CAAA;CAChB,CAAA;AAED,mDAAmD;AACnD,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,SAAS,CAAA;IAEf,uDAAuD;IACvD,QAAQ,EAAE,MAAM,CAAA;IAEhB,mDAAmD;IACnD,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,wFAAwF;AACxF,MAAM,MAAM,WAAW,GACnB,WAAW,GACX,gBAAgB,GAChB,cAAc,GACd,0BAA0B,CAAA;AAE9B;;;;;KAKK;AACL,MAAM,MAAM,OAAO,GAAG,WAAW,GAAG,aAAa,GAAG,cAAc,CAAA;AAElE;;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
+ {"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 are a little like a key/value store. The keys are arrays
5
- * of strings ({@link StorageKey}) and the values are binary blobs.
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 blob correspongind to `key` */
9
+ /** Load the single value corresponding to `key` */
9
10
  abstract load(key: StorageKey): Promise<Uint8Array | undefined>;
10
- /** save the blod `data` to the key `key` */
11
+ /** Save the value `data` to the key `key` */
11
12
  abstract save(key: StorageKey, data: Uint8Array): Promise<void>;
12
- /** remove the blob corresponding to `key` */
13
+ /** Remove the value corresponding to `key` */
13
14
  abstract remove(key: StorageKey): Promise<void>;
14
- /** Load all blobs with keys that start with `keyPrefix` */
15
- abstract loadRange(keyPrefix: StorageKey): Promise<{
16
- key: StorageKey;
17
- data: Uint8Array;
18
- }[]>;
19
- /** Remove all blobs with keys that start with `keyPrefix` */
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;IAMlC,kDAAkD;IAClD,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAC/D,4CAA4C;IAC5C,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAC/D,6CAA6C;IAC7C,QAAQ,CAAC,MAAM,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAO/C,2DAA2D;IAC3D,QAAQ,CAAC,SAAS,CAAC,SAAS,EAAE,UAAU,GAAG,OAAO,CAAC;QAAC,GAAG,EAAE,UAAU,CAAC;QAAC,IAAI,EAAE,UAAU,CAAA;KAAC,EAAE,CAAC;IACzF,6DAA6D;IAC7D,QAAQ,CAAC,WAAW,CAAC,SAAS,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;CAC3D;AAED;;;;;;;;;IASI;AACJ,MAAM,MAAO,UAAU,GAAG,MAAM,EAAE,CAAA"}
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 are a little like a key/value store. The keys are arrays
5
- * of strings ({@link StorageKey}) and the values are binary blobs.
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
- export type ChunkType = "snapshot" | "incremental";
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
- loadDoc(documentId: DocumentId): Promise<A.Doc<unknown> | null>;
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
- remove(documentId: DocumentId): Promise<void>;
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;AAK9C,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAA;AAC7C,OAAO,EAAE,cAAc,EAAc,MAAM,qBAAqB,CAAA;AAUhE,MAAM,MAAM,SAAS,GAAG,UAAU,GAAG,aAAa,CAAA;AAelD,qBAAa,gBAAgB;;gBAQf,cAAc,EAAE,cAAc;IAuDpC,OAAO,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;IA0B/D,OAAO,CAAC,UAAU,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAanE,MAAM,CAAC,UAAU,EAAE,UAAU;CAmCpC"}
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
- function keyHash(binary) {
7
- const hash = sha256.hash(binary);
8
- const hashArray = Array.from(new Uint8Array(hash)); // convert buffer to byte array
9
- const hashHex = hashArray.map(b => ("00" + b.toString(16)).slice(-2)).join(""); // convert bytes to hex string
10
- return hashHex;
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
- #chunkInfos = new Map();
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.#snapshotting = true;
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.#snapshotting = false;
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.#snapshotting) {
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,4 @@
1
+ import * as A from "@automerge/automerge/next";
2
+ export declare function keyHash(binary: Uint8Array): string;
3
+ export declare function headsHash(heads: A.Heads): string;
4
+ //# sourceMappingURL=keyHash.d.ts.map
@@ -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.13",
3
+ "version": "1.0.14",
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": "48ca7b968758d7fd95f13fa03dc69d452cdd15f5"
60
+ "gitHead": "c4a155e56613bbd396d3b764f3a2ec5807ac02db"
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.remove(documentId).catch(err => {
109
+ storageSubsystem.removeDoc(documentId).catch(err => {
110
110
  this.#log("error deleting document", { documentId, err })
111
111
  })
112
112
  }