@dxos/echo-pipeline 0.8.4-main.ae835ea → 0.8.4-main.bc2380dfbc

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 (221) hide show
  1. package/LICENSE +102 -5
  2. package/README.md +1 -1
  3. package/dist/lib/{browser/chunk-WJJXJTNS.mjs → neutral/chunk-46DCRDA7.mjs} +44 -13
  4. package/dist/lib/neutral/chunk-46DCRDA7.mjs.map +7 -0
  5. package/dist/lib/{browser/chunk-FR6GJ4S4.mjs → neutral/chunk-LR7UFVQX.mjs} +145 -618
  6. package/dist/lib/neutral/chunk-LR7UFVQX.mjs.map +7 -0
  7. package/dist/lib/{browser → neutral}/filter/index.mjs +1 -2
  8. package/dist/lib/neutral/index.mjs +7332 -0
  9. package/dist/lib/neutral/index.mjs.map +7 -0
  10. package/dist/lib/neutral/meta.json +1 -0
  11. package/dist/lib/{browser → neutral}/testing/index.mjs +49 -292
  12. package/dist/lib/neutral/testing/index.mjs.map +7 -0
  13. package/dist/types/src/automerge/automerge-host-subduction.test.d.ts +2 -0
  14. package/dist/types/src/automerge/automerge-host-subduction.test.d.ts.map +1 -0
  15. package/dist/types/src/automerge/automerge-host.d.ts +105 -27
  16. package/dist/types/src/automerge/automerge-host.d.ts.map +1 -1
  17. package/dist/types/src/automerge/automerge-repo-subduction.test.d.ts +2 -0
  18. package/dist/types/src/automerge/automerge-repo-subduction.test.d.ts.map +1 -0
  19. package/dist/types/src/automerge/automerge-repo.test.d.ts +1 -1
  20. package/dist/types/src/automerge/automerge-repo.test.d.ts.map +1 -1
  21. package/dist/types/src/automerge/automerge-subduction.test.d.ts +2 -0
  22. package/dist/types/src/automerge/automerge-subduction.test.d.ts.map +1 -0
  23. package/dist/types/src/automerge/collection-synchronizer.d.ts +22 -2
  24. package/dist/types/src/automerge/collection-synchronizer.d.ts.map +1 -1
  25. package/dist/types/src/automerge/echo-data-monitor.d.ts.map +1 -1
  26. package/dist/types/src/automerge/echo-network-adapter.d.ts +17 -11
  27. package/dist/types/src/automerge/echo-network-adapter.d.ts.map +1 -1
  28. package/dist/types/src/automerge/echo-replicator.d.ts +28 -16
  29. package/dist/types/src/automerge/echo-replicator.d.ts.map +1 -1
  30. package/dist/types/src/automerge/errors.d.ts +35 -0
  31. package/dist/types/src/automerge/errors.d.ts.map +1 -0
  32. package/dist/types/src/automerge/handle-state.d.ts +52 -0
  33. package/dist/types/src/automerge/handle-state.d.ts.map +1 -0
  34. package/dist/types/src/automerge/heads-store.d.ts +9 -2
  35. package/dist/types/src/automerge/heads-store.d.ts.map +1 -1
  36. package/dist/types/src/automerge/index.d.ts +2 -0
  37. package/dist/types/src/automerge/index.d.ts.map +1 -1
  38. package/dist/types/src/automerge/leveldb-storage-adapter.d.ts +10 -4
  39. package/dist/types/src/automerge/leveldb-storage-adapter.d.ts.map +1 -1
  40. package/dist/types/src/automerge/mesh-echo-replicator-connection.d.ts +8 -8
  41. package/dist/types/src/automerge/mesh-echo-replicator-connection.d.ts.map +1 -1
  42. package/dist/types/src/automerge/mesh-echo-replicator.d.ts +6 -5
  43. package/dist/types/src/automerge/mesh-echo-replicator.d.ts.map +1 -1
  44. package/dist/types/src/automerge/network-protocol.d.ts.map +1 -1
  45. package/dist/types/src/automerge/space-collection.d.ts.map +1 -1
  46. package/dist/types/src/common/feeds.d.ts.map +1 -1
  47. package/dist/types/src/common/space-id.d.ts.map +1 -1
  48. package/dist/types/src/db-host/automerge-data-source.d.ts +35 -0
  49. package/dist/types/src/db-host/automerge-data-source.d.ts.map +1 -0
  50. package/dist/types/src/db-host/automerge-data-source.test.d.ts +2 -0
  51. package/dist/types/src/db-host/automerge-data-source.test.d.ts.map +1 -0
  52. package/dist/types/src/db-host/automerge-metrics.d.ts.map +1 -1
  53. package/dist/types/src/db-host/data-service.d.ts +12 -6
  54. package/dist/types/src/db-host/data-service.d.ts.map +1 -1
  55. package/dist/types/src/db-host/database-root.d.ts +9 -3
  56. package/dist/types/src/db-host/database-root.d.ts.map +1 -1
  57. package/dist/types/src/db-host/documents-synchronizer.d.ts +30 -8
  58. package/dist/types/src/db-host/documents-synchronizer.d.ts.map +1 -1
  59. package/dist/types/src/db-host/echo-host.d.ts +74 -28
  60. package/dist/types/src/db-host/echo-host.d.ts.map +1 -1
  61. package/dist/types/src/db-host/index.d.ts +2 -0
  62. package/dist/types/src/db-host/index.d.ts.map +1 -1
  63. package/dist/types/src/db-host/invalidation-hint.d.ts +19 -0
  64. package/dist/types/src/db-host/invalidation-hint.d.ts.map +1 -0
  65. package/dist/types/src/db-host/local-queue-service.d.ts +19 -0
  66. package/dist/types/src/db-host/local-queue-service.d.ts.map +1 -0
  67. package/dist/types/src/db-host/query-invalidation.test.d.ts +2 -0
  68. package/dist/types/src/db-host/query-invalidation.test.d.ts.map +1 -0
  69. package/dist/types/src/db-host/query-service.d.ts +18 -10
  70. package/dist/types/src/db-host/query-service.d.ts.map +1 -1
  71. package/dist/types/src/db-host/queue-data-source.d.ts +38 -0
  72. package/dist/types/src/db-host/queue-data-source.d.ts.map +1 -0
  73. package/dist/types/src/db-host/queue-service.test.d.ts +2 -0
  74. package/dist/types/src/db-host/queue-service.test.d.ts.map +1 -0
  75. package/dist/types/src/db-host/space-state-manager.d.ts +3 -2
  76. package/dist/types/src/db-host/space-state-manager.d.ts.map +1 -1
  77. package/dist/types/src/db-host/stub.d.ts +11 -0
  78. package/dist/types/src/db-host/stub.d.ts.map +1 -0
  79. package/dist/types/src/edge/echo-edge-replicator.d.ts +7 -6
  80. package/dist/types/src/edge/echo-edge-replicator.d.ts.map +1 -1
  81. package/dist/types/src/edge/echo-edge-subduction-replicator.d.ts +39 -0
  82. package/dist/types/src/edge/echo-edge-subduction-replicator.d.ts.map +1 -0
  83. package/dist/types/src/edge/echo-edge-subduction-replicator.test.d.ts +2 -0
  84. package/dist/types/src/edge/echo-edge-subduction-replicator.test.d.ts.map +1 -0
  85. package/dist/types/src/edge/index.d.ts +1 -0
  86. package/dist/types/src/edge/index.d.ts.map +1 -1
  87. package/dist/types/src/edge/inflight-request-limiter.d.ts.map +1 -1
  88. package/dist/types/src/filter/filter-match.d.ts +1 -1
  89. package/dist/types/src/filter/filter-match.d.ts.map +1 -1
  90. package/dist/types/src/metadata/metadata-store.d.ts.map +1 -1
  91. package/dist/types/src/pipeline/message-selector.d.ts.map +1 -1
  92. package/dist/types/src/pipeline/pipeline.d.ts +2 -2
  93. package/dist/types/src/pipeline/pipeline.d.ts.map +1 -1
  94. package/dist/types/src/pipeline/timeframe-clock.d.ts.map +1 -1
  95. package/dist/types/src/query/errors.d.ts +16 -20
  96. package/dist/types/src/query/errors.d.ts.map +1 -1
  97. package/dist/types/src/query/plan.d.ts +59 -8
  98. package/dist/types/src/query/plan.d.ts.map +1 -1
  99. package/dist/types/src/query/query-executor.d.ts +55 -12
  100. package/dist/types/src/query/query-executor.d.ts.map +1 -1
  101. package/dist/types/src/query/query-planner.d.ts +10 -1
  102. package/dist/types/src/query/query-planner.d.ts.map +1 -1
  103. package/dist/types/src/space/admission-discovery-extension.d.ts.map +1 -1
  104. package/dist/types/src/space/auth.d.ts +3 -3
  105. package/dist/types/src/space/auth.d.ts.map +1 -1
  106. package/dist/types/src/space/control-pipeline.d.ts +4 -3
  107. package/dist/types/src/space/control-pipeline.d.ts.map +1 -1
  108. package/dist/types/src/space/space-manager.d.ts +7 -7
  109. package/dist/types/src/space/space-manager.d.ts.map +1 -1
  110. package/dist/types/src/space/space-protocol.d.ts +9 -8
  111. package/dist/types/src/space/space-protocol.d.ts.map +1 -1
  112. package/dist/types/src/space/space.d.ts +5 -5
  113. package/dist/types/src/space/space.d.ts.map +1 -1
  114. package/dist/types/src/testing/change-metadata.d.ts.map +1 -1
  115. package/dist/types/src/testing/index.d.ts +0 -2
  116. package/dist/types/src/testing/index.d.ts.map +1 -1
  117. package/dist/types/src/testing/test-agent-builder.d.ts.map +1 -1
  118. package/dist/types/src/testing/test-feed-builder.d.ts.map +1 -1
  119. package/dist/types/src/testing/test-network-adapter.d.ts +1 -1
  120. package/dist/types/src/testing/test-network-adapter.d.ts.map +1 -1
  121. package/dist/types/src/testing/test-replicator.d.ts +13 -13
  122. package/dist/types/src/testing/test-replicator.d.ts.map +1 -1
  123. package/dist/types/src/util.d.ts.map +1 -1
  124. package/dist/types/tsconfig.tsbuildinfo +1 -1
  125. package/package.json +56 -54
  126. package/src/automerge/automerge-host-subduction.test.ts +263 -0
  127. package/src/automerge/automerge-host.test.ts +145 -32
  128. package/src/automerge/automerge-host.ts +442 -163
  129. package/src/automerge/automerge-repo-subduction.test.ts +1054 -0
  130. package/src/automerge/automerge-repo.test.ts +284 -77
  131. package/src/automerge/automerge-subduction.test.ts +330 -0
  132. package/src/automerge/collection-synchronizer.test.ts +96 -16
  133. package/src/automerge/collection-synchronizer.ts +125 -26
  134. package/src/automerge/echo-network-adapter.test.ts +2 -1
  135. package/src/automerge/echo-network-adapter.ts +44 -32
  136. package/src/automerge/echo-replicator.ts +30 -16
  137. package/src/automerge/errors.ts +10 -0
  138. package/src/automerge/handle-state.ts +62 -0
  139. package/src/automerge/heads-store.ts +42 -3
  140. package/src/automerge/index.ts +2 -0
  141. package/src/automerge/leveldb-storage-adapter.ts +70 -4
  142. package/src/automerge/mesh-echo-replicator-connection.ts +8 -8
  143. package/src/automerge/mesh-echo-replicator.ts +14 -11
  144. package/src/db-host/automerge-data-source.test.ts +299 -0
  145. package/src/db-host/automerge-data-source.ts +187 -0
  146. package/src/db-host/data-service.ts +27 -10
  147. package/src/db-host/database-root.ts +19 -7
  148. package/src/db-host/documents-synchronizer.test.ts +145 -14
  149. package/src/db-host/documents-synchronizer.ts +158 -59
  150. package/src/db-host/echo-host.ts +376 -90
  151. package/src/db-host/index.ts +2 -0
  152. package/src/db-host/invalidation-hint.ts +50 -0
  153. package/src/db-host/local-queue-service.ts +127 -0
  154. package/src/db-host/query-invalidation.test.ts +270 -0
  155. package/src/db-host/query-service.ts +108 -111
  156. package/src/db-host/queue-data-source.ts +160 -0
  157. package/src/db-host/queue-service.test.ts +145 -0
  158. package/src/db-host/space-state-manager.ts +11 -9
  159. package/src/db-host/stub.ts +33 -0
  160. package/src/edge/echo-edge-replicator.test.ts +9 -9
  161. package/src/edge/echo-edge-replicator.ts +50 -42
  162. package/src/edge/echo-edge-subduction-replicator.test.ts +158 -0
  163. package/src/edge/echo-edge-subduction-replicator.ts +377 -0
  164. package/src/edge/index.ts +1 -0
  165. package/src/filter/filter-match.test.ts +41 -25
  166. package/src/filter/filter-match.ts +75 -20
  167. package/src/metadata/metadata-store.ts +8 -5
  168. package/src/pipeline/pipeline-stress.test.ts +1 -3
  169. package/src/pipeline/pipeline.test.ts +0 -1
  170. package/src/pipeline/pipeline.ts +2 -3
  171. package/src/query/errors.ts +2 -2
  172. package/src/query/plan.ts +73 -9
  173. package/src/query/query-executor.ts +1064 -172
  174. package/src/query/query-planner.test.ts +854 -86
  175. package/src/query/query-planner.ts +444 -38
  176. package/src/space/admission-discovery-extension.ts +1 -1
  177. package/src/space/auth.ts +6 -6
  178. package/src/space/control-pipeline.test.ts +2 -2
  179. package/src/space/control-pipeline.ts +7 -7
  180. package/src/space/space-manager.ts +12 -17
  181. package/src/space/space-protocol.browser.test.ts +13 -12
  182. package/src/space/space-protocol.test.ts +13 -13
  183. package/src/space/space-protocol.ts +16 -15
  184. package/src/space/space.test.ts +2 -2
  185. package/src/space/space.ts +9 -10
  186. package/src/testing/index.ts +0 -2
  187. package/src/testing/test-agent-builder.ts +0 -1
  188. package/src/testing/test-network-adapter.ts +24 -3
  189. package/src/testing/test-replicator.ts +15 -20
  190. package/src/util.ts +7 -3
  191. package/dist/lib/browser/chunk-CGS2ULMK.mjs +0 -11
  192. package/dist/lib/browser/chunk-CGS2ULMK.mjs.map +0 -7
  193. package/dist/lib/browser/chunk-FR6GJ4S4.mjs.map +0 -7
  194. package/dist/lib/browser/chunk-WJJXJTNS.mjs.map +0 -7
  195. package/dist/lib/browser/index.mjs +0 -5492
  196. package/dist/lib/browser/index.mjs.map +0 -7
  197. package/dist/lib/browser/meta.json +0 -1
  198. package/dist/lib/browser/testing/index.mjs.map +0 -7
  199. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +0 -11
  200. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs.map +0 -7
  201. package/dist/lib/node-esm/chunk-JQCCVDLT.mjs +0 -2144
  202. package/dist/lib/node-esm/chunk-JQCCVDLT.mjs.map +0 -7
  203. package/dist/lib/node-esm/chunk-LEQ77KAT.mjs +0 -240
  204. package/dist/lib/node-esm/chunk-LEQ77KAT.mjs.map +0 -7
  205. package/dist/lib/node-esm/filter/index.mjs +0 -13
  206. package/dist/lib/node-esm/filter/index.mjs.map +0 -7
  207. package/dist/lib/node-esm/index.mjs +0 -5492
  208. package/dist/lib/node-esm/index.mjs.map +0 -7
  209. package/dist/lib/node-esm/meta.json +0 -1
  210. package/dist/lib/node-esm/testing/index.mjs +0 -764
  211. package/dist/lib/node-esm/testing/index.mjs.map +0 -7
  212. package/dist/types/src/db-host/documents-iterator.d.ts +0 -7
  213. package/dist/types/src/db-host/documents-iterator.d.ts.map +0 -1
  214. package/dist/types/src/testing/test-data.d.ts +0 -18
  215. package/dist/types/src/testing/test-data.d.ts.map +0 -1
  216. package/dist/types/src/testing/test-schema.d.ts +0 -37
  217. package/dist/types/src/testing/test-schema.d.ts.map +0 -1
  218. package/src/db-host/documents-iterator.ts +0 -74
  219. package/src/testing/test-data.ts +0 -127
  220. package/src/testing/test-schema.ts +0 -53
  221. /package/dist/lib/{browser → neutral}/filter/index.mjs.map +0 -0
package/package.json CHANGED
@@ -1,41 +1,35 @@
1
1
  {
2
2
  "name": "@dxos/echo-pipeline",
3
- "version": "0.8.4-main.ae835ea",
3
+ "version": "0.8.4-main.bc2380dfbc",
4
4
  "description": "ECHO database.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
7
- "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/dxos/dxos"
10
+ },
11
+ "license": "FSL-1.1-Apache-2.0",
8
12
  "author": "info@dxos.org",
9
- "sideEffects": true,
13
+ "sideEffects": false,
10
14
  "type": "module",
11
15
  "exports": {
12
16
  ".": {
13
17
  "source": "./src/index.ts",
14
18
  "types": "./dist/types/src/index.d.ts",
15
- "browser": "./dist/lib/browser/index.mjs",
16
- "node": "./dist/lib/node-esm/index.mjs"
19
+ "default": "./dist/lib/neutral/index.mjs"
17
20
  },
18
21
  "./testing": {
19
22
  "source": "./src/testing/index.ts",
20
23
  "types": "./dist/types/src/testing/index.d.ts",
21
- "browser": "./dist/lib/browser/testing/index.mjs",
22
- "node": "./dist/lib/node-esm/testing/index.mjs"
24
+ "default": "./dist/lib/neutral/testing/index.mjs"
23
25
  },
24
26
  "./filter": {
25
27
  "source": "./src/filter/index.ts",
26
28
  "types": "./dist/types/src/filter/index.d.ts",
27
- "browser": "./dist/lib/browser/filter/index.mjs",
28
- "node": "./dist/lib/node-esm/filter/index.mjs"
29
+ "default": "./dist/lib/neutral/filter/index.mjs"
29
30
  }
30
31
  },
31
32
  "types": "dist/types/src/index.d.ts",
32
- "typesVersions": {
33
- "*": {
34
- "testing": [
35
- "dist/types/src/testing/index.d.ts"
36
- ]
37
- }
38
- },
39
33
  "files": [
40
34
  "testing.d.ts",
41
35
  "testing.js",
@@ -43,50 +37,58 @@
43
37
  "src"
44
38
  ],
45
39
  "dependencies": {
46
- "@automerge/automerge": "3.1.2",
47
- "@automerge/automerge-repo": "2.4.0",
40
+ "@automerge/automerge": "3.2.6",
41
+ "@automerge/automerge-repo": "2.6.0-subduction.17",
42
+ "@automerge/automerge-subduction": "0.12.1",
43
+ "@effect/sql": "0.49.0",
44
+ "@effect/sql-sqlite-node": "0.50.1",
45
+ "bs58check": "^4.0.0",
48
46
  "crc-32": "^1.2.2",
49
- "effect": "3.18.3",
47
+ "effect": "3.20.0",
48
+ "fast-deep-equal": "^3.1.3",
50
49
  "level-transcoder": "^1.0.1",
51
- "lodash.isequal": "^4.5.0",
52
- "@dxos/codec-protobuf": "0.8.4-main.ae835ea",
53
- "@dxos/async": "0.8.4-main.ae835ea",
54
- "@dxos/context": "0.8.4-main.ae835ea",
55
- "@dxos/credentials": "0.8.4-main.ae835ea",
56
- "@dxos/crypto": "0.8.4-main.ae835ea",
57
- "@dxos/debug": "0.8.4-main.ae835ea",
58
- "@dxos/echo-protocol": "0.8.4-main.ae835ea",
59
- "@dxos/echo": "0.8.4-main.ae835ea",
60
- "@dxos/edge-client": "0.8.4-main.ae835ea",
61
- "@dxos/errors": "0.8.4-main.ae835ea",
62
- "@dxos/feed-store": "0.8.4-main.ae835ea",
63
- "@dxos/hypercore": "0.8.4-main.ae835ea",
64
- "@dxos/invariant": "0.8.4-main.ae835ea",
65
- "@dxos/keyring": "0.8.4-main.ae835ea",
66
- "@dxos/keys": "0.8.4-main.ae835ea",
67
- "@dxos/kv-store": "0.8.4-main.ae835ea",
68
- "@dxos/indexing": "0.8.4-main.ae835ea",
69
- "@dxos/log": "0.8.4-main.ae835ea",
70
- "@dxos/messaging": "0.8.4-main.ae835ea",
71
- "@dxos/network-manager": "0.8.4-main.ae835ea",
72
- "@dxos/protocols": "0.8.4-main.ae835ea",
73
- "@dxos/node-std": "0.8.4-main.ae835ea",
74
- "@dxos/random-access-storage": "0.8.4-main.ae835ea",
75
- "@dxos/teleport-extension-automerge-replicator": "0.8.4-main.ae835ea",
76
- "@dxos/teleport-extension-gossip": "0.8.4-main.ae835ea",
77
- "@dxos/teleport-extension-object-sync": "0.8.4-main.ae835ea",
78
- "@dxos/timeframe": "0.8.4-main.ae835ea",
79
- "@dxos/teleport": "0.8.4-main.ae835ea",
80
- "@dxos/tracing": "0.8.4-main.ae835ea",
81
- "@dxos/teleport-extension-replicator": "0.8.4-main.ae835ea",
82
- "@dxos/typings": "0.8.4-main.ae835ea",
83
- "@dxos/util": "0.8.4-main.ae835ea"
50
+ "semver": "^7.7.3",
51
+ "@dxos/async": "0.8.4-main.bc2380dfbc",
52
+ "@dxos/codec-protobuf": "0.8.4-main.bc2380dfbc",
53
+ "@dxos/context": "0.8.4-main.bc2380dfbc",
54
+ "@dxos/credentials": "0.8.4-main.bc2380dfbc",
55
+ "@dxos/debug": "0.8.4-main.bc2380dfbc",
56
+ "@dxos/crypto": "0.8.4-main.bc2380dfbc",
57
+ "@dxos/echo-protocol": "0.8.4-main.bc2380dfbc",
58
+ "@dxos/echo": "0.8.4-main.bc2380dfbc",
59
+ "@dxos/effect": "0.8.4-main.bc2380dfbc",
60
+ "@dxos/edge-client": "0.8.4-main.bc2380dfbc",
61
+ "@dxos/errors": "0.8.4-main.bc2380dfbc",
62
+ "@dxos/feed-store": "0.8.4-main.bc2380dfbc",
63
+ "@dxos/feed": "0.8.4-main.bc2380dfbc",
64
+ "@dxos/hypercore": "0.8.4-main.bc2380dfbc",
65
+ "@dxos/index-core": "0.8.4-main.bc2380dfbc",
66
+ "@dxos/invariant": "0.8.4-main.bc2380dfbc",
67
+ "@dxos/keyring": "0.8.4-main.bc2380dfbc",
68
+ "@dxos/kv-store": "0.8.4-main.bc2380dfbc",
69
+ "@dxos/log": "0.8.4-main.bc2380dfbc",
70
+ "@dxos/messaging": "0.8.4-main.bc2380dfbc",
71
+ "@dxos/keys": "0.8.4-main.bc2380dfbc",
72
+ "@dxos/network-manager": "0.8.4-main.bc2380dfbc",
73
+ "@dxos/protocols": "0.8.4-main.bc2380dfbc",
74
+ "@dxos/node-std": "0.8.4-main.bc2380dfbc",
75
+ "@dxos/teleport": "0.8.4-main.bc2380dfbc",
76
+ "@dxos/sql-sqlite": "0.8.4-main.bc2380dfbc",
77
+ "@dxos/teleport-extension-automerge-replicator": "0.8.4-main.bc2380dfbc",
78
+ "@dxos/teleport-extension-gossip": "0.8.4-main.bc2380dfbc",
79
+ "@dxos/teleport-extension-object-sync": "0.8.4-main.bc2380dfbc",
80
+ "@dxos/random-access-storage": "0.8.4-main.bc2380dfbc",
81
+ "@dxos/teleport-extension-replicator": "0.8.4-main.bc2380dfbc",
82
+ "@dxos/typings": "0.8.4-main.bc2380dfbc",
83
+ "@dxos/timeframe": "0.8.4-main.bc2380dfbc",
84
+ "@dxos/util": "0.8.4-main.bc2380dfbc",
85
+ "@dxos/tracing": "0.8.4-main.bc2380dfbc"
84
86
  },
85
87
  "devDependencies": {
86
- "@types/lodash.isequal": "^4.5.0",
88
+ "@types/semver": "^7.7.1",
87
89
  "fast-check": "^3.19.0",
88
90
  "get-port-please": "^3.1.1",
89
- "@dxos/test-utils": "0.8.4-main.ae835ea"
91
+ "@dxos/test-utils": "0.8.4-main.bc2380dfbc"
90
92
  },
91
93
  "publishConfig": {
92
94
  "access": "public"
@@ -0,0 +1,263 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { getHeads } from '@automerge/automerge';
6
+ import * as Automerge from '@automerge/automerge';
7
+ import { type DocumentId, type Heads, generateAutomergeUrl, parseAutomergeUrl } from '@automerge/automerge-repo';
8
+ import { describe, onTestFinished, test } from 'vitest';
9
+
10
+ import { sleep } from '@dxos/async';
11
+ import { Context } from '@dxos/context';
12
+ import type { CollectionId } from '@dxos/echo-protocol';
13
+ import { invariant } from '@dxos/invariant';
14
+ import { PublicKey } from '@dxos/keys';
15
+ import type { LevelDB } from '@dxos/kv-store';
16
+ import { createTestLevel } from '@dxos/kv-store/testing';
17
+ import { openAndClose } from '@dxos/test-utils';
18
+ import { range } from '@dxos/util';
19
+
20
+ import { TestReplicationNetwork } from '../testing';
21
+ import { AutomergeHost } from './automerge-host';
22
+
23
+ describe('AutomergeHost with Subduction', () => {
24
+ test('can create documents', async ({ expect }) => {
25
+ const level = await createLevel();
26
+ const host = await setupAutomergeHost({ level });
27
+ const handle = await host.createDoc<any>();
28
+ handle.change((doc: any) => {
29
+ doc.text = 'Hello world';
30
+ });
31
+ await host.flush(Context.default());
32
+ expect(handle.doc()!.text).toEqual('Hello world');
33
+ });
34
+
35
+ test('changes are preserved in storage', async ({ expect }) => {
36
+ const level = await createLevel();
37
+ const host = await setupAutomergeHost({ level });
38
+ const handle = await host.createDoc<any>();
39
+ handle.change((doc: any) => {
40
+ doc.text = 'Hello world';
41
+ });
42
+ const url = handle.url;
43
+
44
+ await host.flush(Context.default());
45
+ await host.close();
46
+
47
+ const host2 = await setupAutomergeHost({ level });
48
+ const handle2 = await host2.loadDoc<any>(Context.default(), url);
49
+ invariant(handle2);
50
+ await handle2.whenReady();
51
+ expect(handle2.doc()!.text).toEqual('Hello world');
52
+ await host2.flush(Context.default());
53
+ });
54
+
55
+ test('load resolves when document is created from binary', async ({ expect }) => {
56
+ const level = await createLevel();
57
+ const host = await setupAutomergeHost({ level });
58
+
59
+ const document = Automerge.from({ text: 'Hello world' });
60
+ const binary = Automerge.save(document);
61
+ const { documentId } = parseAutomergeUrl(generateAutomergeUrl());
62
+
63
+ const loadPromise = host.loadDoc(Context.default(), documentId);
64
+
65
+ const createdHandle = await host.createDoc(binary, { preserveHistory: true, documentId });
66
+
67
+ const loadedHandle = await loadPromise;
68
+ invariant(loadedHandle);
69
+ expect(loadedHandle.doc()).toEqual(createdHandle.doc());
70
+ });
71
+
72
+ test('query single document heads', async ({ expect }) => {
73
+ const tmpPath = `/tmp/dxos-${PublicKey.random().toHex()}`;
74
+
75
+ const level = await createLevel(tmpPath);
76
+ const host = await setupAutomergeHost({ level });
77
+ const handle = await host.createDoc({ text: 'Hello world' });
78
+ const expectedHeads = getHeads(handle.doc()!);
79
+ await host.flush(Context.default());
80
+
81
+ expect(await host.getHeads([handle.documentId])).toEqual([expectedHeads]);
82
+ await host.close();
83
+ await level.close();
84
+
85
+ {
86
+ const host = await setupAutomergeHost({ level: await createLevel(tmpPath) });
87
+ expect(await host.getHeads([handle.documentId])).toEqual([expectedHeads]);
88
+ }
89
+ });
90
+
91
+ test('query multiple document heads', async ({ expect }) => {
92
+ const tmpPath = `/tmp/dxos-${PublicKey.random().toHex()}`;
93
+
94
+ const level = await createLevel(tmpPath);
95
+ const host = await setupAutomergeHost({ level });
96
+ const handles = await Promise.all(range(2, () => host.createDoc({ text: 'Hello world' })));
97
+ const expectedHeads: (Heads | undefined)[] = handles.map((handle) => getHeads(handle.doc()!));
98
+ await host.flush(Context.default());
99
+
100
+ const ids = handles.map((handle) => handle.documentId);
101
+ ids.splice(1, 0, 'non-existent-id' as DocumentId);
102
+ expectedHeads.splice(1, 0, undefined);
103
+
104
+ expect(await host.getHeads(ids)).toEqual(expectedHeads);
105
+ await host.close();
106
+ await level.close();
107
+
108
+ {
109
+ const level = await createLevel(tmpPath);
110
+ const host = await setupAutomergeHost({ level });
111
+ expect(await host.getHeads(ids)).toEqual(expectedHeads);
112
+ await host.close();
113
+ await level.close();
114
+ }
115
+ });
116
+
117
+ test('loads remote document over replication network', { timeout: 5_000 }, async ({ expect }) => {
118
+ const level1 = await createLevel();
119
+ const host1 = await setupAutomergeHost({ level: level1 });
120
+
121
+ const level2 = await createLevel();
122
+ const host2 = await setupAutomergeHost({ level: level2 });
123
+ const handle = await host2.createDoc({ text: 'Hello from Subduction' });
124
+ await host2.flush(Context.default());
125
+ await waitForSubductionSave();
126
+
127
+ const network = await new TestReplicationNetwork().open();
128
+ try {
129
+ await host1.addReplicator(Context.default(), await network.createReplicator());
130
+ await host2.addReplicator(Context.default(), await network.createReplicator());
131
+
132
+ const loaded = await host1.loadDoc<{ text: string }>(Context.default(), handle.documentId, { timeout: 1_000 });
133
+ invariant(loaded);
134
+ expect(loaded.doc()!.text).toEqual('Hello from Subduction');
135
+ } finally {
136
+ await host1.close();
137
+ await host2.close();
138
+ await network.close();
139
+ }
140
+ });
141
+
142
+ test('collection synchronization', { timeout: 5_000 }, async ({ expect }) => {
143
+ const NUM_DOCUMENTS = 10;
144
+
145
+ const level1 = await createLevel();
146
+ const host1 = await setupAutomergeHost({ level: level1 });
147
+
148
+ const level2 = await createLevel();
149
+ const host2 = await setupAutomergeHost({ level: level2 });
150
+ const documentIds: DocumentId[] = [];
151
+ for (const i of range(NUM_DOCUMENTS)) {
152
+ const handle = await host2.createDoc({ docIndex: i });
153
+ documentIds.push(handle.documentId);
154
+ }
155
+ await host2.flush(Context.default());
156
+ await waitForSubductionSave();
157
+
158
+ const network = await new TestReplicationNetwork().open();
159
+ try {
160
+ await host1.addReplicator(Context.default(), await network.createReplicator());
161
+ await host2.addReplicator(Context.default(), await network.createReplicator());
162
+
163
+ const collectionId = 'test-collection';
164
+ const collectionUpdates: CollectionId[] = [];
165
+ const unsubscribe = host1.collectionStateUpdated.on(({ collectionId }) => {
166
+ collectionUpdates.push(collectionId);
167
+ });
168
+ await host1.updateLocalCollectionState(collectionId, documentIds);
169
+ await host2.updateLocalCollectionState(collectionId, documentIds);
170
+
171
+ for (const documentId of documentIds) {
172
+ await expect
173
+ .poll(() => host1.getHeads([documentId]), { timeout: 1_000 })
174
+ .toEqual(await host2.getHeads([documentId]));
175
+ }
176
+
177
+ // collectionStateUpdated fires whenever host1 observes a `collection-state` from host2
178
+ // (see {@link CollectionSynchronizer.remoteStateUpdated}). At least one event must
179
+ // arrive for the heads to have converged via the collection-sync path rather than
180
+ // raw Subduction byte transport.
181
+ expect(collectionUpdates).toContain(collectionId as CollectionId);
182
+ unsubscribe();
183
+ } finally {
184
+ await host1.close();
185
+ await host2.close();
186
+ await network.close();
187
+ }
188
+ });
189
+
190
+ test('collection synchronization is bidirectional', { timeout: 10_000 }, async ({ expect }) => {
191
+ const host1DocumentIds: DocumentId[] = [];
192
+ const host2DocumentIds: DocumentId[] = [];
193
+
194
+ const level1 = await createLevel();
195
+ const host1 = await setupAutomergeHost({ level: level1 });
196
+ for (const i of range(3)) {
197
+ const handle = await host1.createDoc({ host: 1, docIndex: i });
198
+ host1DocumentIds.push(handle.documentId);
199
+ }
200
+ await host1.flush(Context.default());
201
+
202
+ const level2 = await createLevel();
203
+ const host2 = await setupAutomergeHost({ level: level2 });
204
+ for (const i of range(3)) {
205
+ const handle = await host2.createDoc({ host: 2, docIndex: i });
206
+ host2DocumentIds.push(handle.documentId);
207
+ }
208
+ await host2.flush(Context.default());
209
+ await waitForSubductionSave();
210
+
211
+ const network = await new TestReplicationNetwork().open();
212
+ try {
213
+ await host1.addReplicator(Context.default(), await network.createReplicator());
214
+ await host2.addReplicator(Context.default(), await network.createReplicator());
215
+
216
+ const host1CollectionId = 'test-host1-collection';
217
+ await host1.updateLocalCollectionState(host1CollectionId, host1DocumentIds);
218
+ await host2.updateLocalCollectionState(host1CollectionId, host1DocumentIds);
219
+
220
+ for (const documentId of host1DocumentIds) {
221
+ await expect
222
+ .poll(() => host2.getHeads([documentId]), { timeout: 1_000 })
223
+ .toEqual(await host1.getHeads([documentId]));
224
+ }
225
+
226
+ const host2CollectionId = 'test-host2-collection';
227
+ await host1.updateLocalCollectionState(host2CollectionId, host2DocumentIds);
228
+ await host2.updateLocalCollectionState(host2CollectionId, host2DocumentIds);
229
+
230
+ for (const documentId of host2DocumentIds) {
231
+ await expect
232
+ .poll(() => host1.getHeads([documentId]), { timeout: 1_000 })
233
+ .toEqual(await host2.getHeads([documentId]));
234
+ }
235
+ } finally {
236
+ await host1.close();
237
+ await host2.close();
238
+ await network.close();
239
+ }
240
+ });
241
+ });
242
+
243
+ const createLevel = async (tmpPath?: string) => {
244
+ const level = createTestLevel(tmpPath);
245
+ await openAndClose(level);
246
+ return level;
247
+ };
248
+
249
+ const waitForSubductionSave = async () => {
250
+ await sleep(150);
251
+ };
252
+
253
+ const setupAutomergeHost = async ({ level }: { level: LevelDB }) => {
254
+ const host = new AutomergeHost({
255
+ db: level,
256
+ useSubduction: true,
257
+ });
258
+ await host.open();
259
+ onTestFinished(async () => {
260
+ await host.close();
261
+ });
262
+ return host;
263
+ };
@@ -2,11 +2,13 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { getHeads } from '@automerge/automerge';
6
- import type { DocumentId, Heads } from '@automerge/automerge-repo';
5
+ import * as A from '@automerge/automerge';
6
+ import { type DocumentId, type Heads, generateAutomergeUrl, parseAutomergeUrl } from '@automerge/automerge-repo';
7
7
  import { describe, expect, onTestFinished, test } from 'vitest';
8
8
 
9
- import { IndexMetadataStore } from '@dxos/indexing';
9
+ import { sleep } from '@dxos/async';
10
+ import { Context } from '@dxos/context';
11
+ import { invariant } from '@dxos/invariant';
10
12
  import { PublicKey } from '@dxos/keys';
11
13
  import type { LevelDB } from '@dxos/kv-store';
12
14
  import { createTestLevel } from '@dxos/kv-store/testing';
@@ -14,38 +16,60 @@ import { openAndClose } from '@dxos/test-utils';
14
16
  import { range } from '@dxos/util';
15
17
 
16
18
  import { TestReplicationNetwork } from '../testing';
17
-
18
19
  import { AutomergeHost } from './automerge-host';
20
+ import { type EchoNetworkAdapter } from './echo-network-adapter';
19
21
 
20
22
  describe('AutomergeHost', () => {
21
23
  test('can create documents', async () => {
22
24
  const level = await createLevel();
23
25
  const host = await setupAutomergeHost({ level });
24
- const handle = host.repo.create<any>();
26
+ const handle = await host.createDoc<any>();
25
27
  handle.change((doc: any) => {
26
28
  doc.text = 'Hello world';
27
29
  });
28
- await host.repo.flush();
30
+ await host.flush(Context.default());
29
31
  expect(handle.doc()!.text).toEqual('Hello world');
30
32
  });
31
33
 
32
34
  test('changes are preserved in storage', async () => {
33
35
  const level = await createLevel();
34
36
  const host = await setupAutomergeHost({ level });
35
- const handle = host.repo.create();
37
+ const handle = await host.createDoc<any>();
36
38
  handle.change((doc: any) => {
37
39
  doc.text = 'Hello world';
38
40
  });
39
41
  const url = handle.url;
40
42
 
41
- await host.repo.flush();
43
+ await host.flush(Context.default());
42
44
  await host.close();
43
45
 
44
46
  const host2 = await setupAutomergeHost({ level });
45
- const handle2 = await host2.repo.find<any>(url);
47
+ const handle2 = await host2.loadDoc<any>(Context.default(), url);
48
+ invariant(handle2);
46
49
  await handle2.whenReady();
47
50
  expect(handle2.doc()!.text).toEqual('Hello world');
48
- await host2.repo.flush();
51
+ await host2.flush(Context.default());
52
+ });
53
+
54
+ test('load resolves when document is created from binary', async () => {
55
+ const level = await createLevel();
56
+ const host = await setupAutomergeHost({ level });
57
+
58
+ // Create a document to get its binary representation
59
+ const document = A.from({ text: 'Hello world' });
60
+ const binary = A.save(document);
61
+ const { documentId } = parseAutomergeUrl(generateAutomergeUrl());
62
+
63
+ // Start loading a non-existent document (should hang until created)
64
+ const loadPromise = host.loadDoc(Context.default(), documentId);
65
+
66
+ // Create the document from binary - this should resolve the load
67
+ const createdHandle = await host.createDoc(binary, { preserveHistory: true, documentId });
68
+
69
+ // The load should now resolve
70
+ const loadedHandle = await loadPromise;
71
+ invariant(loadedHandle);
72
+ expect(loadedHandle.doc()).toEqual(createdHandle.doc());
49
73
  });
50
74
 
51
75
  test('query single document heads', async () => {
@@ -53,9 +77,9 @@ describe('AutomergeHost', () => {
53
77
 
54
78
  const level = await createLevel(tmpPath);
55
79
  const host = await setupAutomergeHost({ level });
56
- const handle = host.createDoc({ text: 'Hello world' });
57
- const expectedHeads = getHeads(handle.doc()!);
58
- await host.flush();
80
+ const handle = await host.createDoc({ text: 'Hello world' });
81
+ const expectedHeads = A.getHeads(handle.doc()!);
82
+ await host.flush(Context.default());
59
83
 
60
84
  expect(await host.getHeads([handle.documentId])).toEqual([expectedHeads]);
61
85
  await host.close();
@@ -73,9 +97,9 @@ describe('AutomergeHost', () => {
73
97
 
74
98
  const level = await createLevel(tmpPath);
75
99
  const host = await setupAutomergeHost({ level });
76
- const handles = range(2, () => host.createDoc({ text: 'Hello world' }));
77
- const expectedHeads: (Heads | undefined)[] = handles.map((handle) => getHeads(handle.doc()!));
78
- await host.flush();
100
+ const handles = await Promise.all(range(2, () => host.createDoc({ text: 'Hello world' })));
101
+ const expectedHeads: (Heads | undefined)[] = handles.map((handle) => A.getHeads(handle.doc()!));
102
+ await host.flush(Context.default());
79
103
 
80
104
  const ids = handles.map((handle) => handle.documentId);
81
105
  ids.splice(1, 0, 'non-existent-id' as DocumentId);
@@ -95,40 +119,130 @@ describe('AutomergeHost', () => {
95
119
  }
96
120
  });
97
121
 
98
- test('collection synchronization', async () => {
122
+ test('loadDoc respects fetchFromNetwork', { timeout: 10_000 }, async () => {
123
+ const level1 = await createLevel();
124
+ const host1 = await setupAutomergeHost({ level: level1 });
125
+
126
+ const level2 = await createLevel();
127
+ const host2 = await setupAutomergeHost({ level: level2 });
128
+ const handle = await host2.createDoc({ text: 'Hello world' });
129
+ await host2.flush(Context.default());
130
+
131
+ const network = await new TestReplicationNetwork().open();
132
+ await host1.addReplicator(Context.default(), await network.createReplicator());
133
+ await host2.addReplicator(Context.default(), await network.createReplicator());
134
+
135
+ // (1) fetchFromNetwork=false on a doc not yet on disk: returns null immediately
136
+ // without waiting on the network, even though host2 has it.
137
+ expect(
138
+ await host1.loadDoc(Context.default(), handle.documentId, { fetchFromNetwork: false, timeout: 1_000 }),
139
+ ).toBeNull();
140
+
141
+ // (2) fetchFromNetwork=true: host1 announces, host2 sends bytes, doc syncs.
142
+ const loaded = await host1.loadDoc<{ text: string }>(Context.default(), handle.documentId, {
143
+ fetchFromNetwork: true,
144
+ timeout: 5_000,
145
+ });
146
+ invariant(loaded);
147
+ expect(loaded.doc()!.text).toEqual('Hello world');
148
+
149
+ // (3) Now that host1 has the doc on disk, fetchFromNetwork=false succeeds.
150
+ await host1.flush(Context.default());
151
+ const localOnly = await host1.loadDoc<{ text: string }>(Context.default(), handle.documentId, {
152
+ fetchFromNetwork: false,
153
+ });
154
+ invariant(localOnly);
155
+ expect(localOnly.doc()!.text).toEqual('Hello world');
156
+
157
+ await host1.close();
158
+ await host2.close();
159
+ await network.close();
160
+ });
161
+
162
+ test('loadDoc with fetchFromNetwork=false does not announce when doc is in storage', { timeout: 5_000 }, async () => {
163
+ // Pre-populate host1's storage with the doc, then close so that the
164
+ // host1 we test with has the doc on disk but did not author it (i.e.
165
+ // it's not in `_createdDocuments` and won't auto-announce).
166
+ const tmpPath = `/tmp/dxos-${PublicKey.random().toHex()}`;
167
+ let documentId: DocumentId;
168
+ {
169
+ const level = await createLevel(tmpPath);
170
+ const provider = await setupAutomergeHost({ level });
171
+ const handle = await provider.createDoc({ text: 'Hello world' });
172
+ documentId = handle.documentId;
173
+ await provider.flush(Context.default());
174
+ await provider.close();
175
+ await level.close();
176
+ }
177
+
178
+ const level1 = await createLevel(tmpPath);
179
+ const host1 = await setupAutomergeHost({ level: level1 });
180
+
181
+ const level2 = await createLevel();
182
+ const host2 = await setupAutomergeHost({ level: level2 });
183
+
184
+ const network = await new TestReplicationNetwork().open();
185
+ await host1.addReplicator(Context.default(), await network.createReplicator());
186
+ await host2.addReplicator(Context.default(), await network.createReplicator());
187
+
188
+ // Spy on host2's incoming-request event — fires when any peer sends
189
+ // an automerge `request` message for a doc to host2. If host1 ever
190
+ // announced wanting `documentId`, this would trigger.
191
+ const requests: DocumentId[] = [];
192
+ const adapter = (host2 as unknown as { _echoNetworkAdapter: EchoNetworkAdapter })._echoNetworkAdapter;
193
+ adapter.documentRequested.on(({ documentId: requestedId }) => {
194
+ requests.push(requestedId);
195
+ });
196
+
197
+ // Load the doc that's already on disk; should resolve from storage.
198
+ const loaded = await host1.loadDoc<{ text: string }>(Context.default(), documentId, {
199
+ fetchFromNetwork: false,
200
+ });
201
+ invariant(loaded);
202
+ expect(loaded.doc()!.text).toEqual('Hello world');
203
+
204
+ // Give any in-flight share-policy debounce a chance to fire.
205
+ await sleep(100);
206
+
207
+ expect(requests).not.toContain(documentId);
208
+
209
+ await host1.close();
210
+ await host2.close();
211
+ await network.close();
212
+ });
213
+
214
+ test('collection synchronization', { timeout: 30_000 }, async () => {
99
215
  const NUM_DOCUMENTS = 10;
100
216
 
101
217
  const level1 = await createLevel();
102
218
  const host1 = await setupAutomergeHost({ level: level1 });
103
219
 
104
220
  const level2 = await createLevel();
221
+ const host2 = await setupAutomergeHost({ level: level2 });
105
222
  const documentIds: DocumentId[] = [];
106
- {
107
- const host2 = await setupAutomergeHost({ level: level2 });
108
- for (const i of range(NUM_DOCUMENTS)) {
109
- const handle = host2.createDoc({ docIndex: i });
110
- documentIds.push(handle.documentId);
111
- }
112
- await host2.flush();
113
- await host2.close();
223
+ for (const i of range(NUM_DOCUMENTS)) {
224
+ const handle = await host2.createDoc({ docIndex: i });
225
+ documentIds.push(handle.documentId);
114
226
  }
227
+ await host2.flush(Context.default());
115
228
 
116
- const host2 = await setupAutomergeHost({ level: level2 });
229
+ const network = await new TestReplicationNetwork().open();
230
+ await host1.addReplicator(Context.default(), await network.createReplicator());
231
+ await host2.addReplicator(Context.default(), await network.createReplicator());
117
232
 
118
233
  const collectionId = 'test-collection';
119
234
  await host1.updateLocalCollectionState(collectionId, documentIds);
120
235
  await host2.updateLocalCollectionState(collectionId, documentIds);
121
236
 
122
- await using network = await new TestReplicationNetwork().open();
123
- await host1.addReplicator(await network.createReplicator());
124
- await host2.addReplicator(await network.createReplicator());
125
-
126
237
  for (const documentId of documentIds) {
127
- await expect.poll(() => host1.getHeads([documentId])).toEqual(await host2.getHeads([documentId]));
238
+ await expect
239
+ .poll(() => host1.getHeads([documentId]), { timeout: 20_000 })
240
+ .toEqual(await host2.getHeads([documentId]));
128
241
  }
129
242
 
130
243
  await host1.close();
131
244
  await host2.close();
245
+ await network.close();
132
246
  });
133
247
 
134
248
  const createLevel = async (tmpPath?: string) => {
@@ -141,7 +255,6 @@ describe('AutomergeHost', () => {
141
255
  const setupAutomergeHost = async ({ level }: { level: LevelDB }) => {
142
256
  const host = new AutomergeHost({
143
257
  db: level,
144
- indexMetadataStore: new IndexMetadataStore({ db: level.sublevel('index-metadata') }),
145
258
  });
146
259
  await host.open();
147
260
  onTestFinished(async () => {