@automerge/automerge-repo 2.0.0-collectionsync-alpha.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (203) hide show
  1. package/README.md +8 -8
  2. package/dist/AutomergeUrl.d.ts +17 -5
  3. package/dist/AutomergeUrl.d.ts.map +1 -1
  4. package/dist/AutomergeUrl.js +71 -24
  5. package/dist/DocHandle.d.ts +33 -41
  6. package/dist/DocHandle.d.ts.map +1 -1
  7. package/dist/DocHandle.js +105 -66
  8. package/dist/FindProgress.d.ts +30 -0
  9. package/dist/FindProgress.d.ts.map +1 -0
  10. package/dist/FindProgress.js +1 -0
  11. package/dist/RemoteHeadsSubscriptions.d.ts +4 -5
  12. package/dist/RemoteHeadsSubscriptions.d.ts.map +1 -1
  13. package/dist/RemoteHeadsSubscriptions.js +4 -1
  14. package/dist/Repo.d.ts +24 -5
  15. package/dist/Repo.d.ts.map +1 -1
  16. package/dist/Repo.js +355 -169
  17. package/dist/helpers/abortable.d.ts +36 -0
  18. package/dist/helpers/abortable.d.ts.map +1 -0
  19. package/dist/helpers/abortable.js +47 -0
  20. package/dist/helpers/arraysAreEqual.d.ts.map +1 -1
  21. package/dist/helpers/bufferFromHex.d.ts +3 -0
  22. package/dist/helpers/bufferFromHex.d.ts.map +1 -0
  23. package/dist/helpers/bufferFromHex.js +13 -0
  24. package/dist/helpers/debounce.d.ts.map +1 -1
  25. package/dist/helpers/eventPromise.d.ts.map +1 -1
  26. package/dist/helpers/headsAreSame.d.ts +2 -2
  27. package/dist/helpers/headsAreSame.d.ts.map +1 -1
  28. package/dist/helpers/mergeArrays.d.ts +1 -1
  29. package/dist/helpers/mergeArrays.d.ts.map +1 -1
  30. package/dist/helpers/pause.d.ts.map +1 -1
  31. package/dist/helpers/tests/network-adapter-tests.d.ts.map +1 -1
  32. package/dist/helpers/tests/network-adapter-tests.js +13 -13
  33. package/dist/helpers/tests/storage-adapter-tests.d.ts.map +1 -1
  34. package/dist/helpers/tests/storage-adapter-tests.js +6 -9
  35. package/dist/helpers/throttle.d.ts.map +1 -1
  36. package/dist/helpers/withTimeout.d.ts.map +1 -1
  37. package/dist/index.d.ts +35 -7
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +37 -6
  40. package/dist/network/NetworkSubsystem.d.ts +0 -1
  41. package/dist/network/NetworkSubsystem.d.ts.map +1 -1
  42. package/dist/network/NetworkSubsystem.js +0 -3
  43. package/dist/network/messages.d.ts +1 -7
  44. package/dist/network/messages.d.ts.map +1 -1
  45. package/dist/network/messages.js +1 -2
  46. package/dist/storage/StorageAdapter.d.ts +0 -9
  47. package/dist/storage/StorageAdapter.d.ts.map +1 -1
  48. package/dist/storage/StorageAdapter.js +0 -33
  49. package/dist/storage/StorageSubsystem.d.ts +6 -2
  50. package/dist/storage/StorageSubsystem.d.ts.map +1 -1
  51. package/dist/storage/StorageSubsystem.js +131 -37
  52. package/dist/storage/keyHash.d.ts +1 -1
  53. package/dist/storage/keyHash.d.ts.map +1 -1
  54. package/dist/synchronizer/CollectionSynchronizer.d.ts +3 -4
  55. package/dist/synchronizer/CollectionSynchronizer.d.ts.map +1 -1
  56. package/dist/synchronizer/CollectionSynchronizer.js +32 -26
  57. package/dist/synchronizer/DocSynchronizer.d.ts +8 -8
  58. package/dist/synchronizer/DocSynchronizer.d.ts.map +1 -1
  59. package/dist/synchronizer/DocSynchronizer.js +205 -79
  60. package/dist/types.d.ts +4 -1
  61. package/dist/types.d.ts.map +1 -1
  62. package/fuzz/fuzz.ts +3 -3
  63. package/package.json +4 -5
  64. package/src/AutomergeUrl.ts +101 -26
  65. package/src/DocHandle.ts +158 -77
  66. package/src/FindProgress.ts +48 -0
  67. package/src/RemoteHeadsSubscriptions.ts +11 -9
  68. package/src/Repo.ts +465 -180
  69. package/src/helpers/abortable.ts +62 -0
  70. package/src/helpers/bufferFromHex.ts +14 -0
  71. package/src/helpers/headsAreSame.ts +2 -2
  72. package/src/helpers/tests/network-adapter-tests.ts +14 -13
  73. package/src/helpers/tests/storage-adapter-tests.ts +13 -24
  74. package/src/index.ts +57 -38
  75. package/src/network/NetworkSubsystem.ts +0 -4
  76. package/src/network/messages.ts +2 -11
  77. package/src/storage/StorageAdapter.ts +0 -42
  78. package/src/storage/StorageSubsystem.ts +155 -45
  79. package/src/storage/keyHash.ts +1 -1
  80. package/src/synchronizer/CollectionSynchronizer.ts +42 -29
  81. package/src/synchronizer/DocSynchronizer.ts +263 -89
  82. package/src/types.ts +4 -1
  83. package/test/AutomergeUrl.test.ts +130 -0
  84. package/test/CollectionSynchronizer.test.ts +6 -8
  85. package/test/DocHandle.test.ts +161 -77
  86. package/test/DocSynchronizer.test.ts +11 -9
  87. package/test/RemoteHeadsSubscriptions.test.ts +1 -1
  88. package/test/Repo.test.ts +406 -341
  89. package/test/StorageSubsystem.test.ts +95 -20
  90. package/test/remoteHeads.test.ts +28 -13
  91. package/dist/CollectionHandle.d.ts +0 -14
  92. package/dist/CollectionHandle.d.ts.map +0 -1
  93. package/dist/CollectionHandle.js +0 -37
  94. package/dist/DocUrl.d.ts +0 -47
  95. package/dist/DocUrl.d.ts.map +0 -1
  96. package/dist/DocUrl.js +0 -72
  97. package/dist/EphemeralData.d.ts +0 -20
  98. package/dist/EphemeralData.d.ts.map +0 -1
  99. package/dist/EphemeralData.js +0 -1
  100. package/dist/ferigan.d.ts +0 -51
  101. package/dist/ferigan.d.ts.map +0 -1
  102. package/dist/ferigan.js +0 -98
  103. package/dist/src/DocHandle.d.ts +0 -182
  104. package/dist/src/DocHandle.d.ts.map +0 -1
  105. package/dist/src/DocHandle.js +0 -405
  106. package/dist/src/DocUrl.d.ts +0 -49
  107. package/dist/src/DocUrl.d.ts.map +0 -1
  108. package/dist/src/DocUrl.js +0 -72
  109. package/dist/src/EphemeralData.d.ts +0 -19
  110. package/dist/src/EphemeralData.d.ts.map +0 -1
  111. package/dist/src/EphemeralData.js +0 -1
  112. package/dist/src/Repo.d.ts +0 -74
  113. package/dist/src/Repo.d.ts.map +0 -1
  114. package/dist/src/Repo.js +0 -208
  115. package/dist/src/helpers/arraysAreEqual.d.ts +0 -2
  116. package/dist/src/helpers/arraysAreEqual.d.ts.map +0 -1
  117. package/dist/src/helpers/arraysAreEqual.js +0 -2
  118. package/dist/src/helpers/cbor.d.ts +0 -4
  119. package/dist/src/helpers/cbor.d.ts.map +0 -1
  120. package/dist/src/helpers/cbor.js +0 -8
  121. package/dist/src/helpers/eventPromise.d.ts +0 -11
  122. package/dist/src/helpers/eventPromise.d.ts.map +0 -1
  123. package/dist/src/helpers/eventPromise.js +0 -7
  124. package/dist/src/helpers/headsAreSame.d.ts +0 -2
  125. package/dist/src/helpers/headsAreSame.d.ts.map +0 -1
  126. package/dist/src/helpers/headsAreSame.js +0 -4
  127. package/dist/src/helpers/mergeArrays.d.ts +0 -2
  128. package/dist/src/helpers/mergeArrays.d.ts.map +0 -1
  129. package/dist/src/helpers/mergeArrays.js +0 -15
  130. package/dist/src/helpers/pause.d.ts +0 -6
  131. package/dist/src/helpers/pause.d.ts.map +0 -1
  132. package/dist/src/helpers/pause.js +0 -10
  133. package/dist/src/helpers/tests/network-adapter-tests.d.ts +0 -21
  134. package/dist/src/helpers/tests/network-adapter-tests.d.ts.map +0 -1
  135. package/dist/src/helpers/tests/network-adapter-tests.js +0 -122
  136. package/dist/src/helpers/withTimeout.d.ts +0 -12
  137. package/dist/src/helpers/withTimeout.d.ts.map +0 -1
  138. package/dist/src/helpers/withTimeout.js +0 -24
  139. package/dist/src/index.d.ts +0 -53
  140. package/dist/src/index.d.ts.map +0 -1
  141. package/dist/src/index.js +0 -40
  142. package/dist/src/network/NetworkAdapter.d.ts +0 -26
  143. package/dist/src/network/NetworkAdapter.d.ts.map +0 -1
  144. package/dist/src/network/NetworkAdapter.js +0 -4
  145. package/dist/src/network/NetworkSubsystem.d.ts +0 -23
  146. package/dist/src/network/NetworkSubsystem.d.ts.map +0 -1
  147. package/dist/src/network/NetworkSubsystem.js +0 -120
  148. package/dist/src/network/messages.d.ts +0 -85
  149. package/dist/src/network/messages.d.ts.map +0 -1
  150. package/dist/src/network/messages.js +0 -23
  151. package/dist/src/storage/StorageAdapter.d.ts +0 -14
  152. package/dist/src/storage/StorageAdapter.d.ts.map +0 -1
  153. package/dist/src/storage/StorageAdapter.js +0 -1
  154. package/dist/src/storage/StorageSubsystem.d.ts +0 -12
  155. package/dist/src/storage/StorageSubsystem.d.ts.map +0 -1
  156. package/dist/src/storage/StorageSubsystem.js +0 -145
  157. package/dist/src/synchronizer/CollectionSynchronizer.d.ts +0 -25
  158. package/dist/src/synchronizer/CollectionSynchronizer.d.ts.map +0 -1
  159. package/dist/src/synchronizer/CollectionSynchronizer.js +0 -106
  160. package/dist/src/synchronizer/DocSynchronizer.d.ts +0 -29
  161. package/dist/src/synchronizer/DocSynchronizer.d.ts.map +0 -1
  162. package/dist/src/synchronizer/DocSynchronizer.js +0 -263
  163. package/dist/src/synchronizer/Synchronizer.d.ts +0 -9
  164. package/dist/src/synchronizer/Synchronizer.d.ts.map +0 -1
  165. package/dist/src/synchronizer/Synchronizer.js +0 -2
  166. package/dist/src/types.d.ts +0 -16
  167. package/dist/src/types.d.ts.map +0 -1
  168. package/dist/src/types.js +0 -1
  169. package/dist/test/CollectionSynchronizer.test.d.ts +0 -2
  170. package/dist/test/CollectionSynchronizer.test.d.ts.map +0 -1
  171. package/dist/test/CollectionSynchronizer.test.js +0 -57
  172. package/dist/test/DocHandle.test.d.ts +0 -2
  173. package/dist/test/DocHandle.test.d.ts.map +0 -1
  174. package/dist/test/DocHandle.test.js +0 -238
  175. package/dist/test/DocSynchronizer.test.d.ts +0 -2
  176. package/dist/test/DocSynchronizer.test.d.ts.map +0 -1
  177. package/dist/test/DocSynchronizer.test.js +0 -111
  178. package/dist/test/Network.test.d.ts +0 -2
  179. package/dist/test/Network.test.d.ts.map +0 -1
  180. package/dist/test/Network.test.js +0 -11
  181. package/dist/test/Repo.test.d.ts +0 -2
  182. package/dist/test/Repo.test.d.ts.map +0 -1
  183. package/dist/test/Repo.test.js +0 -568
  184. package/dist/test/StorageSubsystem.test.d.ts +0 -2
  185. package/dist/test/StorageSubsystem.test.d.ts.map +0 -1
  186. package/dist/test/StorageSubsystem.test.js +0 -56
  187. package/dist/test/helpers/DummyNetworkAdapter.d.ts +0 -9
  188. package/dist/test/helpers/DummyNetworkAdapter.d.ts.map +0 -1
  189. package/dist/test/helpers/DummyNetworkAdapter.js +0 -15
  190. package/dist/test/helpers/DummyStorageAdapter.d.ts +0 -16
  191. package/dist/test/helpers/DummyStorageAdapter.d.ts.map +0 -1
  192. package/dist/test/helpers/DummyStorageAdapter.js +0 -33
  193. package/dist/test/helpers/generate-large-object.d.ts +0 -5
  194. package/dist/test/helpers/generate-large-object.d.ts.map +0 -1
  195. package/dist/test/helpers/generate-large-object.js +0 -9
  196. package/dist/test/helpers/getRandomItem.d.ts +0 -2
  197. package/dist/test/helpers/getRandomItem.d.ts.map +0 -1
  198. package/dist/test/helpers/getRandomItem.js +0 -4
  199. package/dist/test/types.d.ts +0 -4
  200. package/dist/test/types.d.ts.map +0 -1
  201. package/dist/test/types.js +0 -1
  202. package/src/CollectionHandle.ts +0 -54
  203. package/src/ferigan.ts +0 -184
@@ -1,122 +0,0 @@
1
- import { Repo } from "../../index.js"
2
- import { eventPromise, eventPromises } from "../eventPromise.js"
3
- import { assert } from "chai"
4
- import { describe, it } from "mocha"
5
- import { pause } from "../pause.js"
6
- /**
7
- * Runs a series of tests against a set of three peers, each represented by one or more instantiated
8
- * network adapters.
9
- *
10
- * The adapter `setup` function should return an object with the following properties:
11
- *
12
- * - `adapters`: A tuple representing three peers' network configuration. Each element can be either
13
- * a single adapter or an array of adapters. Each will be used to instantiate a Repo for that
14
- * peer.
15
- * - `teardown`: An optional function that will be called after the tests have run. This can be used
16
- * to clean up any resources that were created during the test.
17
- */
18
- export function runAdapterTests(_setup, title) {
19
- // Wrap the provided setup function
20
- const setup = async () => {
21
- const { adapters, teardown = NO_OP } = await _setup()
22
- // these might be individual adapters or arrays of adapters; normalize them to arrays
23
- const [a, b, c] = adapters.map(toArray)
24
- return { adapters: [a, b, c], teardown }
25
- }
26
- describe(`Adapter acceptance tests ${title ? `(${title})` : ""}`, () => {
27
- it("can sync 2 repos", async () => {
28
- const doTest = async (a, b) => {
29
- const aliceRepo = new Repo({ network: a, peerId: alice })
30
- const bobRepo = new Repo({ network: b, peerId: bob })
31
- // Alice creates a document
32
- const aliceHandle = aliceRepo.create()
33
- // Bob receives the document
34
- await eventPromise(bobRepo, "document")
35
- const bobHandle = bobRepo.find(aliceHandle.url)
36
- // Alice changes the document
37
- aliceHandle.change(d => {
38
- d.foo = "bar"
39
- })
40
- // Bob receives the change
41
- await eventPromise(bobHandle, "change")
42
- assert.equal((await bobHandle.doc())?.foo, "bar")
43
- // Bob changes the document
44
- bobHandle.change(d => {
45
- d.foo = "baz"
46
- })
47
- // Alice receives the change
48
- await eventPromise(aliceHandle, "change")
49
- assert.equal((await aliceHandle.doc())?.foo, "baz")
50
- }
51
- // Run the test in both directions, in case they're different types of adapters
52
- {
53
- const { adapters, teardown } = await setup()
54
- const [x, y] = adapters
55
- await doTest(x, y) // x is Alice
56
- teardown()
57
- }
58
- {
59
- const { adapters, teardown } = await setup()
60
- const [x, y] = adapters
61
- await doTest(y, x) // y is Alice
62
- teardown()
63
- }
64
- })
65
- it("can sync 3 repos", async () => {
66
- const { adapters, teardown } = await setup()
67
- const [a, b, c] = adapters
68
- const aliceRepo = new Repo({ network: a, peerId: alice })
69
- const bobRepo = new Repo({ network: b, peerId: bob })
70
- const charlieRepo = new Repo({ network: c, peerId: charlie })
71
- // Alice creates a document
72
- const aliceHandle = aliceRepo.create()
73
- const docUrl = aliceHandle.url
74
- // Bob and Charlie receive the document
75
- await eventPromises([bobRepo, charlieRepo], "document")
76
- const bobHandle = bobRepo.find(docUrl)
77
- const charlieHandle = charlieRepo.find(docUrl)
78
- // Alice changes the document
79
- aliceHandle.change(d => {
80
- d.foo = "bar"
81
- })
82
- // Bob and Charlie receive the change
83
- await eventPromises([bobHandle, charlieHandle], "change")
84
- assert.equal((await bobHandle.doc())?.foo, "bar")
85
- assert.equal((await charlieHandle.doc())?.foo, "bar")
86
- // Charlie changes the document
87
- charlieHandle.change(d => {
88
- d.foo = "baz"
89
- })
90
- // Alice and Bob receive the change
91
- await eventPromises([aliceHandle, bobHandle], "change")
92
- assert.equal((await bobHandle.doc())?.foo, "baz")
93
- assert.equal((await charlieHandle.doc())?.foo, "baz")
94
- teardown()
95
- })
96
- it("can broadcast a message", async () => {
97
- const { adapters, teardown } = await setup()
98
- const [a, b, c] = adapters
99
- const aliceRepo = new Repo({ network: a, peerId: alice })
100
- const bobRepo = new Repo({ network: b, peerId: bob })
101
- const charlieRepo = new Repo({ network: c, peerId: charlie })
102
- await eventPromises(
103
- [aliceRepo, bobRepo, charlieRepo].map(r => r.networkSubsystem),
104
- "peer"
105
- )
106
- const aliceHandle = aliceRepo.create()
107
- const charlieHandle = charlieRepo.find(aliceHandle.url)
108
- // pause to give charlie a chance to let alice know it wants the doc
109
- await pause(100)
110
- const alicePresenceData = { presence: "alice" }
111
- aliceHandle.broadcast(alicePresenceData)
112
- const { message } = await eventPromise(charlieHandle, "ephemeral-message")
113
- assert.deepStrictEqual(message, alicePresenceData)
114
- teardown()
115
- })
116
- })
117
- }
118
- const NO_OP = () => {}
119
- const toArray = x => (Array.isArray(x) ? x : [x])
120
- const alice = "alice"
121
- const bob = "bob"
122
- const charlie = "charlie"
@@ -1,12 +0,0 @@
1
- /**
2
- * If `promise` is resolved before `t` ms elapse, the timeout is cleared and the result of the
3
- * promise is returned. If the timeout ends first, a `TimeoutError` is thrown.
4
- */
5
- export declare const withTimeout: <T>(
6
- promise: Promise<T>,
7
- t: number
8
- ) => Promise<T>
9
- export declare class TimeoutError extends Error {
10
- constructor(message: string)
11
- }
12
- //# sourceMappingURL=withTimeout.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"withTimeout.d.ts","sourceRoot":"","sources":["../../../src/helpers/withTimeout.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,WAAW,8BAEnB,MAAM,eAcV,CAAA;AAED,qBAAa,YAAa,SAAQ,KAAK;gBACzB,OAAO,EAAE,MAAM;CAI5B"}
@@ -1,24 +0,0 @@
1
- /**
2
- * If `promise` is resolved before `t` ms elapse, the timeout is cleared and the result of the
3
- * promise is returned. If the timeout ends first, a `TimeoutError` is thrown.
4
- */
5
- export const withTimeout = async (promise, t) => {
6
- let timeoutId
7
- const timeoutPromise = new Promise((_, reject) => {
8
- timeoutId = setTimeout(
9
- () => reject(new TimeoutError(`withTimeout: timed out after ${t}ms`)),
10
- t
11
- )
12
- })
13
- try {
14
- return await Promise.race([promise, timeoutPromise])
15
- } finally {
16
- clearTimeout(timeoutId)
17
- }
18
- }
19
- export class TimeoutError extends Error {
20
- constructor(message) {
21
- super(message)
22
- this.name = "TimeoutError"
23
- }
24
- }
@@ -1,53 +0,0 @@
1
- /**
2
- * @packageDocumentation
3
- *
4
- * The [`automerge`](https://www.npmjs.com/package/@automerge/automerge) CRDT
5
- * provides a core CRDT data structure and an implementation of a storage
6
- * format and sync protocol but doesn't provide the plumbing to use these tools
7
- * in a JS application. `automerge-repo` provides the plumbing.
8
- *
9
- * The main entry point is the {@link Repo} class, which you instantiate with
10
- * a {@link StorageAdapter} and zero or more {@link NetworkAdapter}s. Once you
11
- * have a repo you can use it to create {@link DocHandle}s. {@link DocHandle}s
12
- * are a reference to a document, identified by a {@link AutomergeUrl}, a place to
13
- * listen for changes to the document, and to make new changes.
14
- *
15
- * A typical example of how to use this library then might look like this:
16
- *
17
- * ```typescript
18
- * import { Repo } from "@automerge/automerge-repo";
19
- *
20
- * const repo = new Repo({
21
- * storage: <storage adapter>,
22
- * network: [<network adapter>, <network adapter>]
23
- * })
24
- *
25
- * const handle = repo.create
26
- * ```
27
- */
28
- export { DocHandle, HandleState, type DocHandleOptions } from "./DocHandle.js"
29
- export type { DocHandleChangePayload } from "./DocHandle.js"
30
- export { NetworkAdapter } from "./network/NetworkAdapter.js"
31
- export type {
32
- OpenPayload,
33
- PeerCandidatePayload,
34
- PeerDisconnectedPayload,
35
- } from "./network/NetworkAdapter.js"
36
- export type {
37
- Message,
38
- NetworkAdapterMessage,
39
- EphemeralMessage,
40
- SyncMessage,
41
- } from "./network/messages.js"
42
- export { isValidMessage } from "./network/messages.js"
43
- export { Repo, type SharePolicy, type RepoConfig } from "./Repo.js"
44
- export { StorageAdapter, type StorageKey } from "./storage/StorageAdapter.js"
45
- export {
46
- parseAutomergeUrl,
47
- isValidAutomergeUrl,
48
- stringifyAutomergeUrl as generateAutomergeUrl,
49
- } from "./DocUrl.js"
50
- export * from "./types.js"
51
- /** @hidden **/
52
- export * as cbor from "./helpers/cbor.js"
53
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAC9E,YAAY,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAA;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAC5D,YAAY,EACV,WAAW,EACX,oBAAoB,EACpB,uBAAuB,GACxB,MAAM,6BAA6B,CAAA;AAMpC,YAAY,EACV,OAAO,EACP,qBAAqB,EACrB,gBAAgB,EAChB,WAAW,GACZ,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAEtD,OAAO,EAAE,IAAI,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,WAAW,CAAA;AACnE,OAAO,EAAE,cAAc,EAAE,KAAK,UAAU,EAAE,MAAM,6BAA6B,CAAA;AAC7E,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,qBAAqB,IAAI,oBAAoB,GAC9C,MAAM,aAAa,CAAA;AACpB,cAAc,YAAY,CAAA;AAE1B,eAAe;AACf,OAAO,KAAK,IAAI,MAAM,mBAAmB,CAAA"}
package/dist/src/index.js DELETED
@@ -1,40 +0,0 @@
1
- /**
2
- * @packageDocumentation
3
- *
4
- * The [`automerge`](https://www.npmjs.com/package/@automerge/automerge) CRDT
5
- * provides a core CRDT data structure and an implementation of a storage
6
- * format and sync protocol but doesn't provide the plumbing to use these tools
7
- * in a JS application. `automerge-repo` provides the plumbing.
8
- *
9
- * The main entry point is the {@link Repo} class, which you instantiate with
10
- * a {@link StorageAdapter} and zero or more {@link NetworkAdapter}s. Once you
11
- * have a repo you can use it to create {@link DocHandle}s. {@link DocHandle}s
12
- * are a reference to a document, identified by a {@link AutomergeUrl}, a place to
13
- * listen for changes to the document, and to make new changes.
14
- *
15
- * A typical example of how to use this library then might look like this:
16
- *
17
- * ```typescript
18
- * import { Repo } from "@automerge/automerge-repo";
19
- *
20
- * const repo = new Repo({
21
- * storage: <storage adapter>,
22
- * network: [<network adapter>, <network adapter>]
23
- * })
24
- *
25
- * const handle = repo.create
26
- * ```
27
- */
28
- export { DocHandle, HandleState } from "./DocHandle.js"
29
- export { NetworkAdapter } from "./network/NetworkAdapter.js"
30
- export { isValidMessage } from "./network/messages.js"
31
- export { Repo } from "./Repo.js"
32
- export { StorageAdapter } from "./storage/StorageAdapter.js"
33
- export {
34
- parseAutomergeUrl,
35
- isValidAutomergeUrl,
36
- stringifyAutomergeUrl as generateAutomergeUrl,
37
- } from "./DocUrl.js"
38
- export * from "./types.js"
39
- /** @hidden **/
40
- export * as cbor from "./helpers/cbor.js"
@@ -1,26 +0,0 @@
1
- import { EventEmitter } from "eventemitter3"
2
- import { PeerId } from "../types.js"
3
- import { Message } from "./messages.js"
4
- export declare abstract class NetworkAdapter extends EventEmitter<NetworkAdapterEvents> {
5
- peerId?: PeerId
6
- abstract connect(peerId: PeerId): void
7
- abstract send(message: Message): void
8
- abstract disconnect(): void
9
- }
10
- export interface NetworkAdapterEvents {
11
- ready: (payload: OpenPayload) => void
12
- close: () => void
13
- "peer-candidate": (payload: PeerCandidatePayload) => void
14
- "peer-disconnected": (payload: PeerDisconnectedPayload) => void
15
- message: (payload: Message) => void
16
- }
17
- export interface OpenPayload {
18
- network: NetworkAdapter
19
- }
20
- export interface PeerCandidatePayload {
21
- peerId: PeerId
22
- }
23
- export interface PeerDisconnectedPayload {
24
- peerId: PeerId
25
- }
26
- //# sourceMappingURL=NetworkAdapter.d.ts.map
@@ -1 +0,0 @@
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,8BAAsB,cAAe,SAAQ,YAAY,CAAC,oBAAoB,CAAC;IAC7E,MAAM,CAAC,EAAE,MAAM,CAAA;IAEf,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAEtC,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAErC,QAAQ,CAAC,UAAU,IAAI,IAAI;CAC5B;AAID,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAA;IACrC,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,gBAAgB,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,IAAI,CAAA;IACzD,mBAAmB,EAAE,CAAC,OAAO,EAAE,uBAAuB,KAAK,IAAI,CAAA;IAC/D,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"}
@@ -1,4 +0,0 @@
1
- import { EventEmitter } from "eventemitter3"
2
- export class NetworkAdapter extends EventEmitter {
3
- peerId // hmmm, maybe not
4
- }
@@ -1,23 +0,0 @@
1
- import { EventEmitter } from "eventemitter3"
2
- import { PeerId } from "../types.js"
3
- import { NetworkAdapter, PeerDisconnectedPayload } from "./NetworkAdapter.js"
4
- import { Message, MessageContents } from "./messages.js"
5
- export declare class NetworkSubsystem extends EventEmitter<NetworkSubsystemEvents> {
6
- #private
7
- peerId: PeerId
8
- constructor(adapters: NetworkAdapter[], peerId?: PeerId)
9
- addNetworkAdapter(networkAdapter: NetworkAdapter): void
10
- send(message: MessageContents): void
11
- isReady: () => boolean
12
- whenReady: () => Promise<void>
13
- }
14
- export interface NetworkSubsystemEvents {
15
- peer: (payload: PeerPayload) => void
16
- "peer-disconnected": (payload: PeerDisconnectedPayload) => void
17
- message: (payload: Message) => void
18
- ready: () => void
19
- }
20
- export interface PeerPayload {
21
- peerId: PeerId
22
- }
23
- //# sourceMappingURL=NetworkSubsystem.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"NetworkSubsystem.d.ts","sourceRoot":"","sources":["../../../src/network/NetworkSubsystem.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,EAAE,cAAc,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAA;AAE7E,OAAO,EAIL,OAAO,EACP,eAAe,EAChB,MAAM,eAAe,CAAA;AAUtB,qBAAa,gBAAiB,SAAQ,YAAY,CAAC,sBAAsB,CAAC;;IAUzB,MAAM;gBAAzC,QAAQ,EAAE,cAAc,EAAE,EAAS,MAAM,SAAiB;IAMtE,iBAAiB,CAAC,cAAc,EAAE,cAAc;IAsEhD,IAAI,CAAC,OAAO,EAAE,eAAe;IA2B7B,OAAO,gBAEN;IAED,SAAS,sBAUR;CACF;AAQD,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAA;IACpC,mBAAmB,EAAE,CAAC,OAAO,EAAE,uBAAuB,KAAK,IAAI,CAAA;IAC/D,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IACnC,KAAK,EAAE,MAAM,IAAI,CAAA;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAA;CACf"}
@@ -1,120 +0,0 @@
1
- import { EventEmitter } from "eventemitter3"
2
- import { isEphemeralMessage, isValidMessage } from "./messages.js"
3
- import debug from "debug"
4
- const getEphemeralMessageSource = message =>
5
- `${message.senderId}:${message.sessionId}`
6
- export class NetworkSubsystem extends EventEmitter {
7
- peerId
8
- #log
9
- #adaptersByPeer = {}
10
- #count = 0
11
- #sessionId = Math.random().toString(36).slice(2)
12
- #ephemeralSessionCounts = {}
13
- #readyAdapterCount = 0
14
- #adapters = []
15
- constructor(adapters, peerId = randomPeerId()) {
16
- super()
17
- this.peerId = peerId
18
- this.#log = debug(`automerge-repo:network:${this.peerId}`)
19
- adapters.forEach(a => this.addNetworkAdapter(a))
20
- }
21
- addNetworkAdapter(networkAdapter) {
22
- this.#adapters.push(networkAdapter)
23
- networkAdapter.once("ready", () => {
24
- this.#readyAdapterCount++
25
- this.#log(
26
- "Adapters ready: ",
27
- this.#readyAdapterCount,
28
- "/",
29
- this.#adapters.length
30
- )
31
- if (this.#readyAdapterCount === this.#adapters.length) {
32
- this.emit("ready")
33
- }
34
- })
35
- networkAdapter.on("peer-candidate", ({ peerId }) => {
36
- this.#log(`peer candidate: ${peerId} `)
37
- // TODO: This is where authentication would happen
38
- if (!this.#adaptersByPeer[peerId]) {
39
- // TODO: handle losing a server here
40
- this.#adaptersByPeer[peerId] = networkAdapter
41
- }
42
- this.emit("peer", { peerId })
43
- })
44
- networkAdapter.on("peer-disconnected", ({ peerId }) => {
45
- this.#log(`peer disconnected: ${peerId} `)
46
- delete this.#adaptersByPeer[peerId]
47
- this.emit("peer-disconnected", { peerId })
48
- })
49
- networkAdapter.on("message", msg => {
50
- if (!isValidMessage(msg)) {
51
- this.#log(`invalid message: ${JSON.stringify(msg)}`)
52
- return
53
- }
54
- this.#log(`message from ${msg.senderId}`)
55
- if (isEphemeralMessage(msg)) {
56
- const source = getEphemeralMessageSource(msg)
57
- if (
58
- this.#ephemeralSessionCounts[source] === undefined ||
59
- msg.count > this.#ephemeralSessionCounts[source]
60
- ) {
61
- this.#ephemeralSessionCounts[source] = msg.count
62
- this.emit("message", msg)
63
- }
64
- return
65
- }
66
- this.emit("message", msg)
67
- })
68
- networkAdapter.on("close", () => {
69
- this.#log("adapter closed")
70
- Object.entries(this.#adaptersByPeer).forEach(([peerId, other]) => {
71
- if (other === networkAdapter) {
72
- delete this.#adaptersByPeer[peerId]
73
- }
74
- })
75
- })
76
- networkAdapter.connect(this.peerId)
77
- }
78
- send(message) {
79
- const peer = this.#adaptersByPeer[message.targetId]
80
- if (!peer) {
81
- this.#log(`Tried to send message but peer not found: ${message.targetId}`)
82
- return
83
- }
84
- this.#log(`Sending message to ${message.targetId}`)
85
- if (isEphemeralMessage(message)) {
86
- const outbound =
87
- "count" in message
88
- ? message
89
- : {
90
- ...message,
91
- count: ++this.#count,
92
- sessionId: this.#sessionId,
93
- senderId: this.peerId,
94
- }
95
- this.#log("Ephemeral message", outbound)
96
- peer.send(outbound)
97
- } else {
98
- const outbound = { ...message, senderId: this.peerId }
99
- this.#log("Sync message", outbound)
100
- peer.send(outbound)
101
- }
102
- }
103
- isReady = () => {
104
- return this.#readyAdapterCount === this.#adapters.length
105
- }
106
- whenReady = async () => {
107
- if (this.isReady()) {
108
- return
109
- } else {
110
- return new Promise(resolve => {
111
- this.once("ready", () => {
112
- resolve()
113
- })
114
- })
115
- }
116
- }
117
- }
118
- function randomPeerId() {
119
- return `user-${Math.round(Math.random() * 100000)}`
120
- }
@@ -1,85 +0,0 @@
1
- import { SessionId } from "../EphemeralData.js"
2
- import { DocumentId, PeerId } from "../types.js"
3
- export declare function isValidMessage(
4
- message: NetworkAdapterMessage
5
- ): message is
6
- | SyncMessage
7
- | EphemeralMessage
8
- | RequestMessage
9
- | DocumentUnavailableMessage
10
- export declare function isDocumentUnavailableMessage(
11
- message: NetworkAdapterMessage
12
- ): message is DocumentUnavailableMessage
13
- export declare function isRequestMessage(
14
- message: NetworkAdapterMessage
15
- ): message is RequestMessage
16
- export declare function isSyncMessage(
17
- message: NetworkAdapterMessage
18
- ): message is SyncMessage
19
- export declare function isEphemeralMessage(
20
- message: NetworkAdapterMessage | MessageContents
21
- ): message is EphemeralMessage | EphemeralMessageContents
22
- export interface SyncMessageEnvelope {
23
- senderId: PeerId
24
- }
25
- export interface SyncMessageContents {
26
- type: "sync"
27
- data: Uint8Array
28
- targetId: PeerId
29
- documentId: DocumentId
30
- }
31
- export type SyncMessage = SyncMessageEnvelope & SyncMessageContents
32
- export interface EphemeralMessageEnvelope {
33
- senderId: PeerId
34
- count: number
35
- sessionId: SessionId
36
- }
37
- export interface EphemeralMessageContents {
38
- type: "ephemeral"
39
- targetId: PeerId
40
- documentId: DocumentId
41
- data: Uint8Array
42
- }
43
- export type EphemeralMessage = EphemeralMessageEnvelope &
44
- EphemeralMessageContents
45
- export interface DocumentUnavailableMessageContents {
46
- type: "doc-unavailable"
47
- documentId: DocumentId
48
- targetId: PeerId
49
- }
50
- export type DocumentUnavailableMessage = SyncMessageEnvelope &
51
- DocumentUnavailableMessageContents
52
- export interface RequestMessageContents {
53
- type: "request"
54
- data: Uint8Array
55
- targetId: PeerId
56
- documentId: DocumentId
57
- }
58
- export type RequestMessage = SyncMessageEnvelope & RequestMessageContents
59
- export type MessageContents =
60
- | SyncMessageContents
61
- | EphemeralMessageContents
62
- | RequestMessageContents
63
- | DocumentUnavailableMessageContents
64
- export type Message =
65
- | SyncMessage
66
- | EphemeralMessage
67
- | RequestMessage
68
- | DocumentUnavailableMessage
69
- export type SynchronizerMessage =
70
- | SyncMessage
71
- | RequestMessage
72
- | DocumentUnavailableMessage
73
- | EphemeralMessage
74
- type ArriveMessage = {
75
- senderId: PeerId
76
- type: "arrive"
77
- }
78
- type WelcomeMessage = {
79
- senderId: PeerId
80
- targetId: PeerId
81
- type: "welcome"
82
- }
83
- export type NetworkAdapterMessage = ArriveMessage | WelcomeMessage | Message
84
- export {}
85
- //# sourceMappingURL=messages.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../../src/network/messages.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEhD,wBAAgB,cAAc,CAC5B,OAAO,EAAE,qBAAqB,GAC7B,OAAO,IACN,WAAW,GACX,gBAAgB,GAChB,cAAc,GACd,0BAA0B,CAU7B;AAED,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,qBAAqB,GAC7B,OAAO,IAAI,0BAA0B,CAEvC;AAED,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,qBAAqB,GAC7B,OAAO,IAAI,cAAc,CAE3B;AAED,wBAAgB,aAAa,CAC3B,OAAO,EAAE,qBAAqB,GAC7B,OAAO,IAAI,WAAW,CAExB;AAED,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,qBAAqB,GAAG,eAAe,GAC/C,OAAO,IAAI,gBAAgB,GAAG,wBAAwB,CAExD;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,UAAU,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,UAAU,CAAA;CACvB;AAED,MAAM,MAAM,WAAW,GAAG,mBAAmB,GAAG,mBAAmB,CAAA;AAEnE,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,SAAS,CAAA;CACrB;AAED,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,WAAW,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,UAAU,CAAA;IACtB,IAAI,EAAE,UAAU,CAAA;CACjB;AAED,MAAM,MAAM,gBAAgB,GAAG,wBAAwB,GACrD,wBAAwB,CAAA;AAE1B,MAAM,WAAW,kCAAkC;IACjD,IAAI,EAAE,iBAAiB,CAAA;IACvB,UAAU,EAAE,UAAU,CAAA;IACtB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,MAAM,0BAA0B,GAAG,mBAAmB,GAC1D,kCAAkC,CAAA;AAEpC,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,SAAS,CAAA;IACf,IAAI,EAAE,UAAU,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,UAAU,CAAA;CACvB;AAED,MAAM,MAAM,cAAc,GAAG,mBAAmB,GAAG,sBAAsB,CAAA;AAEzE,MAAM,MAAM,eAAe,GACvB,mBAAmB,GACnB,wBAAwB,GACxB,sBAAsB,GACtB,kCAAkC,CAAA;AAEtC,MAAM,MAAM,OAAO,GACf,WAAW,GACX,gBAAgB,GAChB,cAAc,GACd,0BAA0B,CAAA;AAE9B,MAAM,MAAM,mBAAmB,GAC3B,WAAW,GACX,cAAc,GACd,0BAA0B,GAC1B,gBAAgB,CAAA;AAEpB,KAAK,aAAa,GAAG;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,QAAQ,CAAA;CACf,CAAA;AAED,KAAK,cAAc,GAAG;IACpB,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,SAAS,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG,aAAa,GAAG,cAAc,GAAG,OAAO,CAAA"}
@@ -1,23 +0,0 @@
1
- export function isValidMessage(message) {
2
- return (
3
- typeof message === "object" &&
4
- typeof message.type === "string" &&
5
- typeof message.senderId === "string" &&
6
- (isSyncMessage(message) ||
7
- isEphemeralMessage(message) ||
8
- isRequestMessage(message) ||
9
- isDocumentUnavailableMessage(message))
10
- )
11
- }
12
- export function isDocumentUnavailableMessage(message) {
13
- return message.type === "doc-unavailable"
14
- }
15
- export function isRequestMessage(message) {
16
- return message.type === "request"
17
- }
18
- export function isSyncMessage(message) {
19
- return message.type === "sync"
20
- }
21
- export function isEphemeralMessage(message) {
22
- return message.type === "ephemeral"
23
- }
@@ -1,14 +0,0 @@
1
- export declare abstract class StorageAdapter {
2
- abstract load(key: StorageKey): Promise<Uint8Array | undefined>
3
- abstract save(key: StorageKey, data: Uint8Array): Promise<void>
4
- abstract remove(key: StorageKey): Promise<void>
5
- abstract loadRange(keyPrefix: StorageKey): Promise<
6
- {
7
- key: StorageKey
8
- data: Uint8Array
9
- }[]
10
- >
11
- abstract removeRange(keyPrefix: StorageKey): Promise<void>
12
- }
13
- export type StorageKey = string[]
14
- //# sourceMappingURL=StorageAdapter.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"StorageAdapter.d.ts","sourceRoot":"","sources":["../../../src/storage/StorageAdapter.ts"],"names":[],"mappings":"AAAA,8BAAsB,cAAc;IAMlC,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAC/D,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAC/D,QAAQ,CAAC,MAAM,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAO/C,QAAQ,CAAC,SAAS,CAAC,SAAS,EAAE,UAAU,GAAG,OAAO,CAAC;QAAC,GAAG,EAAE,UAAU,CAAC;QAAC,IAAI,EAAE,UAAU,CAAA;KAAC,EAAE,CAAC;IACzF,QAAQ,CAAC,WAAW,CAAC,SAAS,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;CAC3D;AAED,MAAM,MAAO,UAAU,GAAG,MAAM,EAAE,CAAA"}
@@ -1 +0,0 @@
1
- export class StorageAdapter {}
@@ -1,12 +0,0 @@
1
- import * as A from "@automerge/automerge/next"
2
- import { StorageAdapter } from "./StorageAdapter.js"
3
- import { type DocumentId } from "../types.js"
4
- export type ChunkType = "snapshot" | "incremental"
5
- export declare class StorageSubsystem {
6
- #private
7
- constructor(storageAdapter: StorageAdapter)
8
- loadDoc(documentId: DocumentId): Promise<A.Doc<unknown> | null>
9
- saveDoc(documentId: DocumentId, doc: A.Doc<unknown>): Promise<void>
10
- remove(documentId: DocumentId): Promise<void>
11
- }
12
- //# sourceMappingURL=StorageSubsystem.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"StorageSubsystem.d.ts","sourceRoot":"","sources":["../../../src/storage/StorageSubsystem.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,2BAA2B,CAAA;AAC9C,OAAO,EAAE,cAAc,EAAc,MAAM,qBAAqB,CAAA;AAEhE,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAA;AAa7C,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"}