@dxos/client-services 0.10.0 → 0.11.1

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 (304) hide show
  1. package/dist/lib/chunk-client-services-layer.mjs +11425 -0
  2. package/dist/lib/chunk-client-services-layer.mjs.map +1 -0
  3. package/dist/lib/chunk-decorate.mjs +9 -0
  4. package/dist/lib/index.mjs +731 -0
  5. package/dist/lib/index.mjs.map +1 -0
  6. package/dist/lib/packlets/diagnostics/browser-diagnostics-broadcast.mjs +72 -0
  7. package/dist/lib/packlets/diagnostics/browser-diagnostics-broadcast.mjs.map +1 -0
  8. package/dist/lib/packlets/diagnostics/diagnostics-broadcast.mjs +14 -0
  9. package/dist/lib/packlets/diagnostics/diagnostics-broadcast.mjs.map +1 -0
  10. package/dist/lib/packlets/locks/browser.mjs +105 -0
  11. package/dist/lib/packlets/locks/browser.mjs.map +1 -0
  12. package/dist/lib/packlets/locks/node.mjs +53 -0
  13. package/dist/lib/packlets/locks/node.mjs.map +1 -0
  14. package/dist/lib/testing.mjs +570 -0
  15. package/dist/lib/testing.mjs.map +1 -0
  16. package/dist/types/src/index.d.ts +2 -0
  17. package/dist/types/src/index.d.ts.map +1 -1
  18. package/dist/types/src/packlets/agents/edge-agent-manager.d.ts +21 -4
  19. package/dist/types/src/packlets/agents/edge-agent-manager.d.ts.map +1 -1
  20. package/dist/types/src/packlets/agents/edge-agent-service.d.ts +8 -7
  21. package/dist/types/src/packlets/agents/edge-agent-service.d.ts.map +1 -1
  22. package/dist/types/src/packlets/devices/devices-service.d.ts +7 -5
  23. package/dist/types/src/packlets/devices/devices-service.d.ts.map +1 -1
  24. package/dist/types/src/packlets/devtools/devtools.d.ts +30 -29
  25. package/dist/types/src/packlets/devtools/devtools.d.ts.map +1 -1
  26. package/dist/types/src/packlets/devtools/feeds.d.ts +1 -1
  27. package/dist/types/src/packlets/devtools/feeds.d.ts.map +1 -1
  28. package/dist/types/src/packlets/devtools/network.d.ts +2 -1
  29. package/dist/types/src/packlets/devtools/network.d.ts.map +1 -1
  30. package/dist/types/src/packlets/devtools/spaces.d.ts.map +1 -1
  31. package/dist/types/src/packlets/diagnostics/diagnostics-broadcast.d.ts +2 -2
  32. package/dist/types/src/packlets/diagnostics/diagnostics-broadcast.d.ts.map +1 -1
  33. package/dist/types/src/packlets/diagnostics/diagnostics-collector.d.ts.map +1 -1
  34. package/dist/types/src/packlets/diagnostics/diagnostics.d.ts +1 -8
  35. package/dist/types/src/packlets/diagnostics/diagnostics.d.ts.map +1 -1
  36. package/dist/types/src/packlets/identity/authenticator.d.ts +1 -1
  37. package/dist/types/src/packlets/identity/authenticator.d.ts.map +1 -1
  38. package/dist/types/src/packlets/identity/contacts-service.d.ts +9 -6
  39. package/dist/types/src/packlets/identity/contacts-service.d.ts.map +1 -1
  40. package/dist/types/src/packlets/identity/identity-manager.d.ts +32 -3
  41. package/dist/types/src/packlets/identity/identity-manager.d.ts.map +1 -1
  42. package/dist/types/src/packlets/identity/identity-recovery-manager.d.ts +23 -4
  43. package/dist/types/src/packlets/identity/identity-recovery-manager.d.ts.map +1 -1
  44. package/dist/types/src/packlets/identity/identity-service.d.ts +15 -20
  45. package/dist/types/src/packlets/identity/identity-service.d.ts.map +1 -1
  46. package/dist/types/src/packlets/identity/identity.d.ts +2 -2
  47. package/dist/types/src/packlets/identity/identity.d.ts.map +1 -1
  48. package/dist/types/src/packlets/invitations/edge-invitation-handler.d.ts.map +1 -1
  49. package/dist/types/src/packlets/invitations/invitations-handler.d.ts +17 -0
  50. package/dist/types/src/packlets/invitations/invitations-handler.d.ts.map +1 -1
  51. package/dist/types/src/packlets/invitations/invitations-manager.d.ts +25 -4
  52. package/dist/types/src/packlets/invitations/invitations-manager.d.ts.map +1 -1
  53. package/dist/types/src/packlets/invitations/invitations-service.d.ts +11 -11
  54. package/dist/types/src/packlets/invitations/invitations-service.d.ts.map +1 -1
  55. package/dist/types/src/packlets/logging/logging-service.d.ts +14 -12
  56. package/dist/types/src/packlets/logging/logging-service.d.ts.map +1 -1
  57. package/dist/types/src/packlets/metadata/index.d.ts +3 -0
  58. package/dist/types/src/packlets/metadata/index.d.ts.map +1 -0
  59. package/dist/types/src/packlets/metadata/metadata-store.d.ts +107 -0
  60. package/dist/types/src/packlets/metadata/metadata-store.d.ts.map +1 -0
  61. package/dist/types/src/packlets/metadata/metadata-store.test.d.ts +2 -0
  62. package/dist/types/src/packlets/metadata/metadata-store.test.d.ts.map +1 -0
  63. package/dist/types/src/packlets/metadata/sqlite-metadata-store.d.ts +65 -0
  64. package/dist/types/src/packlets/metadata/sqlite-metadata-store.d.ts.map +1 -0
  65. package/dist/types/src/packlets/network/network-service.d.ts +15 -14
  66. package/dist/types/src/packlets/network/network-service.d.ts.map +1 -1
  67. package/dist/types/src/packlets/pipeline/codec.d.ts +11 -0
  68. package/dist/types/src/packlets/pipeline/codec.d.ts.map +1 -0
  69. package/dist/types/src/packlets/pipeline/feeds.d.ts +7 -0
  70. package/dist/types/src/packlets/pipeline/feeds.d.ts.map +1 -0
  71. package/dist/types/src/packlets/pipeline/index.d.ts +5 -0
  72. package/dist/types/src/packlets/pipeline/index.d.ts.map +1 -0
  73. package/dist/types/src/packlets/pipeline/message-selector.d.ts +11 -0
  74. package/dist/types/src/packlets/pipeline/message-selector.d.ts.map +1 -0
  75. package/dist/types/src/packlets/pipeline/pipeline-stress.test.d.ts +2 -0
  76. package/dist/types/src/packlets/pipeline/pipeline-stress.test.d.ts.map +1 -0
  77. package/dist/types/src/packlets/pipeline/pipeline.d.ts +131 -0
  78. package/dist/types/src/packlets/pipeline/pipeline.d.ts.map +1 -0
  79. package/dist/types/src/packlets/pipeline/pipeline.test.d.ts +2 -0
  80. package/dist/types/src/packlets/pipeline/pipeline.test.d.ts.map +1 -0
  81. package/dist/types/src/packlets/pipeline/testing/index.d.ts +2 -0
  82. package/dist/types/src/packlets/pipeline/testing/index.d.ts.map +1 -0
  83. package/dist/types/src/packlets/pipeline/testing/test-feed-builder.d.ts +9 -0
  84. package/dist/types/src/packlets/pipeline/testing/test-feed-builder.d.ts.map +1 -0
  85. package/dist/types/src/packlets/pipeline/timeframe-clock.d.ts +31 -0
  86. package/dist/types/src/packlets/pipeline/timeframe-clock.d.ts.map +1 -0
  87. package/dist/types/src/packlets/services/client-services-layer.d.ts +21 -0
  88. package/dist/types/src/packlets/services/client-services-layer.d.ts.map +1 -0
  89. package/dist/types/src/packlets/services/cross-device-space-synchronizer.d.ts +28 -0
  90. package/dist/types/src/packlets/services/cross-device-space-synchronizer.d.ts.map +1 -0
  91. package/dist/types/src/packlets/services/effect-rpc.test.d.ts +2 -0
  92. package/dist/types/src/packlets/services/effect-rpc.test.d.ts.map +1 -0
  93. package/dist/types/src/packlets/services/feed-syncer.d.ts +22 -2
  94. package/dist/types/src/packlets/services/feed-syncer.d.ts.map +1 -1
  95. package/dist/types/src/packlets/services/index.d.ts +2 -2
  96. package/dist/types/src/packlets/services/index.d.ts.map +1 -1
  97. package/dist/types/src/packlets/services/platform.d.ts.map +1 -1
  98. package/dist/types/src/packlets/services/service-context.d.ts +38 -57
  99. package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
  100. package/dist/types/src/packlets/services/service-host.d.ts +82 -31
  101. package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
  102. package/dist/types/src/packlets/services/sqlite-storage.d.ts +29 -0
  103. package/dist/types/src/packlets/services/sqlite-storage.d.ts.map +1 -1
  104. package/dist/types/src/packlets/services/sqlite-storage.test.d.ts +2 -0
  105. package/dist/types/src/packlets/services/sqlite-storage.test.d.ts.map +1 -0
  106. package/dist/types/src/packlets/space/admission-discovery-extension.d.ts +30 -0
  107. package/dist/types/src/packlets/space/admission-discovery-extension.d.ts.map +1 -0
  108. package/dist/types/src/packlets/space/auth.d.ts +24 -0
  109. package/dist/types/src/packlets/space/auth.d.ts.map +1 -0
  110. package/dist/types/src/packlets/space/control-pipeline.d.ts +46 -0
  111. package/dist/types/src/packlets/space/control-pipeline.d.ts.map +1 -0
  112. package/dist/types/src/packlets/space/control-pipeline.test.d.ts +2 -0
  113. package/dist/types/src/packlets/space/control-pipeline.test.d.ts.map +1 -0
  114. package/dist/types/src/packlets/space/index.d.ts +6 -0
  115. package/dist/types/src/packlets/space/index.d.ts.map +1 -0
  116. package/dist/types/src/packlets/space/replication.browser.test.d.ts +2 -0
  117. package/dist/types/src/packlets/space/replication.browser.test.d.ts.map +1 -0
  118. package/dist/types/src/packlets/space/space-manager.browser.test.d.ts +2 -0
  119. package/dist/types/src/packlets/space/space-manager.browser.test.d.ts.map +1 -0
  120. package/dist/types/src/packlets/space/space-manager.d.ts +72 -0
  121. package/dist/types/src/packlets/space/space-manager.d.ts.map +1 -0
  122. package/dist/types/src/packlets/space/space-protocol.browser.test.d.ts +2 -0
  123. package/dist/types/src/packlets/space/space-protocol.browser.test.d.ts.map +1 -0
  124. package/dist/types/src/packlets/space/space-protocol.d.ts +99 -0
  125. package/dist/types/src/packlets/space/space-protocol.d.ts.map +1 -0
  126. package/dist/types/src/packlets/space/space-protocol.test.d.ts +2 -0
  127. package/dist/types/src/packlets/space/space-protocol.test.d.ts.map +1 -0
  128. package/dist/types/src/packlets/space/space.d.ts +65 -0
  129. package/dist/types/src/packlets/space/space.d.ts.map +1 -0
  130. package/dist/types/src/packlets/space/space.test.d.ts +2 -0
  131. package/dist/types/src/packlets/space/space.test.d.ts.map +1 -0
  132. package/dist/types/src/packlets/space/testing/change-metadata.d.ts +8 -0
  133. package/dist/types/src/packlets/space/testing/change-metadata.d.ts.map +1 -0
  134. package/dist/types/src/packlets/space/testing/index.d.ts +4 -0
  135. package/dist/types/src/packlets/space/testing/index.d.ts.map +1 -0
  136. package/dist/types/src/packlets/space/testing/test-agent-builder.d.ts +62 -0
  137. package/dist/types/src/packlets/space/testing/test-agent-builder.d.ts.map +1 -0
  138. package/dist/types/src/packlets/space-export/serialized-space-reader.d.ts +1 -1
  139. package/dist/types/src/packlets/space-export/serialized-space-reader.d.ts.map +1 -1
  140. package/dist/types/src/packlets/space-export/serialized-space-writer.d.ts.map +1 -1
  141. package/dist/types/src/packlets/spaces/data-space-manager.d.ts +40 -7
  142. package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
  143. package/dist/types/src/packlets/spaces/data-space.d.ts +3 -1
  144. package/dist/types/src/packlets/spaces/data-space.d.ts.map +1 -1
  145. package/dist/types/src/packlets/spaces/genesis.d.ts +1 -1
  146. package/dist/types/src/packlets/spaces/genesis.d.ts.map +1 -1
  147. package/dist/types/src/packlets/spaces/spaces-service.d.ts +25 -23
  148. package/dist/types/src/packlets/spaces/spaces-service.d.ts.map +1 -1
  149. package/dist/types/src/packlets/storage/level.d.ts.map +1 -1
  150. package/dist/types/src/packlets/system/system-service.d.ts +18 -18
  151. package/dist/types/src/packlets/system/system-service.d.ts.map +1 -1
  152. package/dist/types/src/packlets/testing/invitation-utils.d.ts +1 -1
  153. package/dist/types/src/packlets/testing/invitation-utils.d.ts.map +1 -1
  154. package/dist/types/src/packlets/testing/test-builder.d.ts +7 -6
  155. package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
  156. package/dist/types/src/packlets/worker/worker-runtime.d.ts +41 -58
  157. package/dist/types/src/packlets/worker/worker-runtime.d.ts.map +1 -1
  158. package/dist/types/src/packlets/worker/worker-session.d.ts +21 -8
  159. package/dist/types/src/packlets/worker/worker-session.d.ts.map +1 -1
  160. package/dist/types/src/testing/index.d.ts +1 -0
  161. package/dist/types/src/testing/index.d.ts.map +1 -1
  162. package/dist/types/src/version.d.ts +1 -1
  163. package/dist/types/tsconfig.tsbuildinfo +1 -1
  164. package/package.json +52 -48
  165. package/src/index.ts +2 -0
  166. package/src/packlets/agents/edge-agent-manager.ts +56 -15
  167. package/src/packlets/agents/edge-agent-service.ts +25 -15
  168. package/src/packlets/devices/devices-service.test.ts +20 -19
  169. package/src/packlets/devices/devices-service.ts +17 -17
  170. package/src/packlets/devtools/devtools.ts +133 -75
  171. package/src/packlets/devtools/feeds.ts +2 -1
  172. package/src/packlets/devtools/network.ts +42 -12
  173. package/src/packlets/devtools/spaces.ts +1 -1
  174. package/src/packlets/diagnostics/diagnostics-broadcast.ts +4 -2
  175. package/src/packlets/diagnostics/diagnostics-collector.ts +5 -18
  176. package/src/packlets/diagnostics/diagnostics.ts +0 -11
  177. package/src/packlets/identity/authenticator.ts +2 -1
  178. package/src/packlets/identity/contacts-service.ts +46 -37
  179. package/src/packlets/identity/identity-manager.test.ts +3 -1
  180. package/src/packlets/identity/identity-manager.ts +73 -5
  181. package/src/packlets/identity/identity-recovery-manager.ts +56 -7
  182. package/src/packlets/identity/identity-service.test.ts +27 -18
  183. package/src/packlets/identity/identity-service.ts +114 -71
  184. package/src/packlets/identity/identity.test.ts +4 -9
  185. package/src/packlets/identity/identity.ts +1 -2
  186. package/src/packlets/invitations/edge-invitation-handler.ts +0 -1
  187. package/src/packlets/invitations/invitations-handler.ts +39 -3
  188. package/src/packlets/invitations/invitations-manager.ts +52 -4
  189. package/src/packlets/invitations/invitations-service.ts +54 -28
  190. package/src/packlets/logging/logging-service.ts +41 -29
  191. package/src/packlets/logging/logging.test.ts +28 -15
  192. package/src/packlets/metadata/index.ts +6 -0
  193. package/src/packlets/metadata/metadata-store.test.ts +49 -0
  194. package/src/packlets/metadata/metadata-store.ts +446 -0
  195. package/src/packlets/metadata/sqlite-metadata-store.ts +409 -0
  196. package/src/packlets/network/network-service.test.ts +19 -12
  197. package/src/packlets/network/network-service.ts +96 -29
  198. package/src/packlets/pipeline/codec.ts +18 -0
  199. package/src/packlets/pipeline/feeds.ts +22 -0
  200. package/src/packlets/pipeline/index.ts +8 -0
  201. package/src/packlets/pipeline/message-selector.ts +35 -0
  202. package/src/packlets/pipeline/pipeline-stress.test.ts +230 -0
  203. package/src/packlets/pipeline/pipeline.test.ts +140 -0
  204. package/src/packlets/pipeline/pipeline.ts +439 -0
  205. package/src/packlets/pipeline/testing/index.ts +5 -0
  206. package/src/packlets/pipeline/testing/test-feed-builder.ts +19 -0
  207. package/src/packlets/pipeline/timeframe-clock.ts +81 -0
  208. package/src/packlets/services/client-services-layer.ts +186 -0
  209. package/src/packlets/services/cross-device-space-synchronizer.ts +132 -0
  210. package/src/packlets/services/effect-rpc.test.ts +314 -0
  211. package/src/packlets/services/feed-syncer.ts +57 -3
  212. package/src/packlets/services/index.ts +2 -2
  213. package/src/packlets/services/platform.ts +0 -6
  214. package/src/packlets/services/service-context.ts +219 -522
  215. package/src/packlets/services/service-host.test.ts +34 -11
  216. package/src/packlets/services/service-host.ts +693 -213
  217. package/src/packlets/services/spec.md +106 -0
  218. package/src/packlets/services/sqlite-storage.test.ts +48 -0
  219. package/src/packlets/services/sqlite-storage.ts +117 -20
  220. package/src/packlets/space/admission-discovery-extension.ts +91 -0
  221. package/src/packlets/space/auth.ts +92 -0
  222. package/src/packlets/space/control-pipeline.test.ts +165 -0
  223. package/src/packlets/space/control-pipeline.ts +229 -0
  224. package/src/packlets/space/index.ts +9 -0
  225. package/src/packlets/space/replication.browser.test.ts +58 -0
  226. package/src/packlets/space/space-manager.browser.test.ts +40 -0
  227. package/src/packlets/space/space-manager.ts +224 -0
  228. package/src/packlets/space/space-protocol.browser.test.ts +129 -0
  229. package/src/packlets/space/space-protocol.test.ts +158 -0
  230. package/src/packlets/space/space-protocol.ts +308 -0
  231. package/src/packlets/space/space.test.ts +181 -0
  232. package/src/packlets/space/space.ts +196 -0
  233. package/src/packlets/space/testing/change-metadata.ts +27 -0
  234. package/src/packlets/space/testing/index.ts +7 -0
  235. package/src/packlets/space/testing/test-agent-builder.ts +252 -0
  236. package/src/packlets/space-export/serialized-space-reader.ts +3 -1
  237. package/src/packlets/space-export/serialized-space-writer.ts +53 -30
  238. package/src/packlets/space-export/space-archive.test.ts +30 -0
  239. package/src/packlets/spaces/data-space-manager.test.ts +1 -1
  240. package/src/packlets/spaces/data-space-manager.ts +148 -33
  241. package/src/packlets/spaces/data-space.ts +29 -18
  242. package/src/packlets/spaces/edge-feed-replicator.test.ts +1 -1
  243. package/src/packlets/spaces/genesis.ts +1 -1
  244. package/src/packlets/spaces/spaces-service.test.ts +35 -22
  245. package/src/packlets/spaces/spaces-service.ts +259 -180
  246. package/src/packlets/storage/level.ts +3 -1
  247. package/src/packlets/system/system-service.test.ts +31 -13
  248. package/src/packlets/system/system-service.ts +65 -37
  249. package/src/packlets/testing/invitation-utils.ts +3 -3
  250. package/src/packlets/testing/test-builder.ts +35 -20
  251. package/src/packlets/worker/worker-runtime.ts +222 -260
  252. package/src/packlets/worker/worker-session.ts +108 -91
  253. package/src/testing/index.ts +1 -0
  254. package/src/version.ts +1 -1
  255. package/dist/lib/browser/chunk-QCWEHHJW.mjs +0 -24
  256. package/dist/lib/browser/chunk-QCWEHHJW.mjs.map +0 -7
  257. package/dist/lib/browser/chunk-XJRPB3GA.mjs +0 -22
  258. package/dist/lib/browser/chunk-XJRPB3GA.mjs.map +0 -7
  259. package/dist/lib/browser/chunk-XU5R2BMF.mjs +0 -8837
  260. package/dist/lib/browser/chunk-XU5R2BMF.mjs.map +0 -7
  261. package/dist/lib/browser/index.mjs +0 -876
  262. package/dist/lib/browser/index.mjs.map +0 -7
  263. package/dist/lib/browser/meta.json +0 -1
  264. package/dist/lib/browser/packlets/diagnostics/browser-diagnostics-broadcast.mjs +0 -88
  265. package/dist/lib/browser/packlets/diagnostics/browser-diagnostics-broadcast.mjs.map +0 -7
  266. package/dist/lib/browser/packlets/diagnostics/diagnostics-broadcast.mjs +0 -11
  267. package/dist/lib/browser/packlets/diagnostics/diagnostics-broadcast.mjs.map +0 -7
  268. package/dist/lib/browser/packlets/locks/browser.mjs +0 -86
  269. package/dist/lib/browser/packlets/locks/browser.mjs.map +0 -7
  270. package/dist/lib/browser/packlets/locks/node.mjs +0 -48
  271. package/dist/lib/browser/packlets/locks/node.mjs.map +0 -7
  272. package/dist/lib/browser/testing/index.mjs +0 -436
  273. package/dist/lib/browser/testing/index.mjs.map +0 -7
  274. package/dist/lib/node-esm/chunk-2DT3MZRL.mjs +0 -22
  275. package/dist/lib/node-esm/chunk-2DT3MZRL.mjs.map +0 -7
  276. package/dist/lib/node-esm/chunk-2SZHAWBN.mjs +0 -24
  277. package/dist/lib/node-esm/chunk-2SZHAWBN.mjs.map +0 -7
  278. package/dist/lib/node-esm/chunk-H5YTUD35.mjs +0 -8837
  279. package/dist/lib/node-esm/chunk-H5YTUD35.mjs.map +0 -7
  280. package/dist/lib/node-esm/index.mjs +0 -876
  281. package/dist/lib/node-esm/index.mjs.map +0 -7
  282. package/dist/lib/node-esm/meta.json +0 -1
  283. package/dist/lib/node-esm/packlets/diagnostics/browser-diagnostics-broadcast.mjs +0 -88
  284. package/dist/lib/node-esm/packlets/diagnostics/browser-diagnostics-broadcast.mjs.map +0 -7
  285. package/dist/lib/node-esm/packlets/diagnostics/diagnostics-broadcast.mjs +0 -11
  286. package/dist/lib/node-esm/packlets/diagnostics/diagnostics-broadcast.mjs.map +0 -7
  287. package/dist/lib/node-esm/packlets/locks/browser.mjs +0 -86
  288. package/dist/lib/node-esm/packlets/locks/browser.mjs.map +0 -7
  289. package/dist/lib/node-esm/packlets/locks/node.mjs +0 -48
  290. package/dist/lib/node-esm/packlets/locks/node.mjs.map +0 -7
  291. package/dist/lib/node-esm/testing/index.mjs +0 -436
  292. package/dist/lib/node-esm/testing/index.mjs.map +0 -7
  293. package/dist/types/src/packlets/services/client-rpc-server.d.ts +0 -24
  294. package/dist/types/src/packlets/services/client-rpc-server.d.ts.map +0 -1
  295. package/dist/types/src/packlets/services/service-registry.d.ts +0 -15
  296. package/dist/types/src/packlets/services/service-registry.d.ts.map +0 -1
  297. package/dist/types/src/packlets/services/service-registry.test.d.ts +0 -2
  298. package/dist/types/src/packlets/services/service-registry.test.d.ts.map +0 -1
  299. package/dist/types/src/testing/setup.d.ts +0 -3
  300. package/dist/types/src/testing/setup.d.ts.map +0 -1
  301. package/src/packlets/services/client-rpc-server.ts +0 -111
  302. package/src/packlets/services/service-registry.test.ts +0 -78
  303. package/src/packlets/services/service-registry.ts +0 -36
  304. package/src/testing/setup.ts +0 -11
@@ -0,0 +1,308 @@
1
+ //
2
+ // Copyright 2022 DXOS.org
3
+ //
4
+
5
+ import { type Event } from '@dxos/async';
6
+ import { Context } from '@dxos/context';
7
+ import { createIdFromSpaceKey } from '@dxos/echo-protocol';
8
+ import { type FeedWrapper } from '@dxos/feed-store';
9
+ import { PublicKey, SpaceId } from '@dxos/keys';
10
+ import { log, logInfo } from '@dxos/log';
11
+ import {
12
+ MMSTTopology,
13
+ type SwarmConnection,
14
+ type SwarmNetworkManager,
15
+ type WireProtocol,
16
+ type WireProtocolProps,
17
+ type WireProtocolProvider,
18
+ } from '@dxos/network-manager';
19
+ import type { FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
20
+ import { type MuxerStats, Teleport } from '@dxos/teleport';
21
+ import { type BlobStoreApi, BlobSync } from '@dxos/teleport-extension-object-sync';
22
+ import { ReplicatorExtension } from '@dxos/teleport-extension-replicator';
23
+ import { type AsyncCallback, CallbackCollection, ComplexMap } from '@dxos/util';
24
+
25
+ import { AuthExtension, type AuthProvider, type AuthVerifier } from './auth';
26
+
27
+ export const MOCK_AUTH_PROVIDER: AuthProvider = async (nonce: Uint8Array) => Buffer.from('mock');
28
+ export const MOCK_AUTH_VERIFIER: AuthVerifier = async (nonce: Uint8Array, credential: Uint8Array) => true;
29
+
30
+ // TODO(burdon): Reconcile with SigningContext (define types together).
31
+ export interface SwarmIdentity {
32
+ peerKey: PublicKey;
33
+ identityKey: PublicKey;
34
+ credentialProvider: AuthProvider;
35
+ credentialAuthenticator: AuthVerifier;
36
+ }
37
+
38
+ export type SpaceProtocolOptions = {
39
+ topic: PublicKey; // TODO(burdon): Rename?
40
+ swarmIdentity: SwarmIdentity;
41
+ networkManager: SwarmNetworkManager;
42
+
43
+ blobStore: BlobStoreApi;
44
+
45
+ onFeed?: (feed: FeedWrapper<FeedMessage>) => Promise<void>;
46
+
47
+ /**
48
+ * Called when new session is authenticated.
49
+ * Additional extensions can be added here.
50
+ */
51
+ onSessionAuth?: (session: Teleport) => void;
52
+ onAuthFailure?: (session: Teleport) => void;
53
+
54
+ disableP2pReplication?: boolean;
55
+ };
56
+
57
+ /**
58
+ * Manages Teleport protocol stream creation and joining swarms with replication and presence extensions.
59
+ */
60
+ export class SpaceProtocol {
61
+ private readonly _networkManager: SwarmNetworkManager;
62
+ private readonly _swarmIdentity: SwarmIdentity;
63
+ private readonly _onSessionAuth?: (session: Teleport) => void;
64
+ private readonly _onAuthFailure?: (session: Teleport) => void;
65
+
66
+ public readonly blobSync: BlobSync;
67
+
68
+ private readonly _disableP2pReplication: boolean;
69
+
70
+ @logInfo
71
+ private readonly _topic: Promise<PublicKey>;
72
+
73
+ private readonly _spaceKey: PublicKey;
74
+
75
+ private readonly _feeds = new Set<FeedWrapper<FeedMessage>>();
76
+ private readonly _sessions = new ComplexMap<PublicKey, SpaceProtocolSession>(PublicKey.hash);
77
+ // TODO(burdon): Move to config (with sensible defaults).
78
+ private readonly _topology = new MMSTTopology({
79
+ originateConnections: 4,
80
+ maxPeers: 10,
81
+ sampleSize: 20,
82
+ });
83
+
84
+ private _connection?: SwarmConnection;
85
+
86
+ public readonly feedAdded = new CallbackCollection<AsyncCallback<FeedWrapper<FeedMessage>>>();
87
+
88
+ get sessions(): ReadonlyMap<PublicKey, SpaceProtocolSession> {
89
+ return this._sessions;
90
+ }
91
+
92
+ get feeds(): ReadonlySet<FeedWrapper<FeedMessage>> {
93
+ return this._feeds;
94
+ }
95
+
96
+ @logInfo
97
+ private get _ownPeerKey() {
98
+ return this._swarmIdentity.peerKey;
99
+ }
100
+
101
+ constructor({
102
+ topic,
103
+ swarmIdentity,
104
+ networkManager,
105
+ onSessionAuth,
106
+ onAuthFailure,
107
+ blobStore,
108
+ disableP2pReplication,
109
+ }: SpaceProtocolOptions) {
110
+ this._spaceKey = topic;
111
+ this._networkManager = networkManager;
112
+ this._swarmIdentity = swarmIdentity;
113
+ this._onSessionAuth = onSessionAuth;
114
+ this._onAuthFailure = onAuthFailure;
115
+ this.blobSync = new BlobSync({ blobStore });
116
+
117
+ // Space swarm topic contract (DX-1125): the SpaceId bytes (first 20 bytes of SHA-256(spaceKey)).
118
+ // Both peers and the edge must derive the identical swarm key; SpaceId is the only derivation
119
+ // computable on workerd (pure subtle-crypto — the previous hypercore `discoveryKey` needs keyed
120
+ // BLAKE2b, unavailable there). Edge counterpart: `TraceFeedWriter.getSpaceSwarmKey`.
121
+ this._topic = createIdFromSpaceKey(topic).then((spaceId) => PublicKey.from(SpaceId.decode(spaceId)));
122
+
123
+ this._disableP2pReplication = disableP2pReplication ?? false;
124
+ }
125
+
126
+ // TODO(burdon): Create abstraction for Space (e.g., add keys and have provider).
127
+ async addFeed(feed: FeedWrapper<FeedMessage>): Promise<void> {
128
+ log('addFeed', { key: feed.key });
129
+
130
+ this._feeds.add(feed);
131
+ for (const session of this._sessions.values()) {
132
+ session.replicator.addFeed(feed);
133
+ }
134
+
135
+ await this.feedAdded.callSerial(feed);
136
+ }
137
+
138
+ // TODO(burdon): Rename open? Common open/close interfaces for all services?
139
+ async start(ctx: Context): Promise<void> {
140
+ if (this._connection) {
141
+ return;
142
+ }
143
+
144
+ // TODO(burdon): Document why empty buffer.
145
+ const credentials = await this._swarmIdentity.credentialProvider(Buffer.from(''));
146
+
147
+ await this.blobSync.open();
148
+
149
+ log('starting...');
150
+ const topic = await this._topic;
151
+ this._connection = await this._networkManager.joinSwarm(ctx, {
152
+ protocolProvider: this._createProtocolProvider(credentials),
153
+ topic,
154
+ topology: this._topology,
155
+ label: `swarm ${topic.truncate()} for space ${this._spaceKey.truncate()}`,
156
+ });
157
+
158
+ log('started');
159
+ }
160
+
161
+ public updateTopology(): void {
162
+ this._topology.forceUpdate();
163
+ }
164
+
165
+ async stop(ctx: Context): Promise<void> {
166
+ await this.blobSync.close();
167
+
168
+ if (this._connection) {
169
+ log('stopping...');
170
+ await this._connection.close(ctx);
171
+ log('stopped');
172
+ }
173
+ }
174
+
175
+ private _createProtocolProvider(credentials: Uint8Array | undefined): WireProtocolProvider {
176
+ return (wireProps) => {
177
+ const session = new SpaceProtocolSession({
178
+ wireProps,
179
+ swarmIdentity: this._swarmIdentity,
180
+ onSessionAuth: this._onSessionAuth,
181
+ onAuthFailure: this._onAuthFailure,
182
+ blobSync: this.blobSync,
183
+ disableP2pReplication: this._disableP2pReplication,
184
+ });
185
+ this._sessions.set(wireProps.remotePeerId, session);
186
+
187
+ for (const feed of this._feeds) {
188
+ session.replicator.addFeed(feed);
189
+ }
190
+
191
+ return session;
192
+ };
193
+ }
194
+ }
195
+
196
+ export type SpaceProtocolSessionProps = {
197
+ wireProps: WireProtocolProps;
198
+ swarmIdentity: SwarmIdentity;
199
+
200
+ blobSync: BlobSync;
201
+
202
+ /**
203
+ * Called when new session is authenticated.
204
+ * Additional extensions can be added here.
205
+ */
206
+ onSessionAuth?: (session: Teleport) => void;
207
+
208
+ onAuthFailure?: (session: Teleport) => void;
209
+
210
+ disableP2pReplication?: boolean;
211
+ };
212
+
213
+ export enum AuthStatus {
214
+ INITIAL = 'INITIAL',
215
+ SUCCESS = 'SUCCESS',
216
+ FAILURE = 'FAILURE',
217
+ }
218
+
219
+ // TODO(dmaretskyi): Move to a separate file.
220
+ /**
221
+ * Represents a single connection to a remote peer
222
+ */
223
+ export class SpaceProtocolSession implements WireProtocol {
224
+ @logInfo
225
+ private readonly _wireProps: WireProtocolProps;
226
+
227
+ private readonly _disableP2pReplication: boolean;
228
+
229
+ private readonly _onSessionAuth?: (session: Teleport) => void;
230
+ private readonly _onAuthFailure?: (session: Teleport) => void;
231
+ private readonly _swarmIdentity: SwarmIdentity;
232
+ private readonly _blobSync: BlobSync;
233
+
234
+ private readonly _teleport: Teleport;
235
+
236
+ // TODO(dmaretskyi): Start with upload=false when switching it on the fly works.
237
+ public readonly replicator = new ReplicatorExtension().setOptions({ upload: true });
238
+
239
+ private _authStatus = AuthStatus.INITIAL;
240
+
241
+ @logInfo
242
+ get authStatus() {
243
+ return this._authStatus;
244
+ }
245
+
246
+ get stats(): Event<MuxerStats> {
247
+ return this._teleport.stats;
248
+ }
249
+
250
+ // TODO(dmaretskyi): Allow to pass in extra extensions.
251
+ constructor({
252
+ wireProps,
253
+ swarmIdentity,
254
+ onSessionAuth,
255
+ onAuthFailure,
256
+ blobSync,
257
+ disableP2pReplication,
258
+ }: SpaceProtocolSessionProps) {
259
+ this._wireProps = wireProps;
260
+ this._swarmIdentity = swarmIdentity;
261
+ this._onSessionAuth = onSessionAuth;
262
+ this._onAuthFailure = onAuthFailure;
263
+ this._blobSync = blobSync;
264
+
265
+ this._teleport = new Teleport(wireProps);
266
+
267
+ this._disableP2pReplication = disableP2pReplication ?? false;
268
+ }
269
+
270
+ get stream() {
271
+ return this._teleport.stream;
272
+ }
273
+
274
+ async open(sessionId?: PublicKey): Promise<void> {
275
+ await this._teleport.open(sessionId);
276
+ this._teleport.addExtension(
277
+ 'dxos.mesh.teleport.auth',
278
+ new AuthExtension({
279
+ provider: this._swarmIdentity.credentialProvider,
280
+ verifier: this._swarmIdentity.credentialAuthenticator,
281
+ onAuthSuccess: () => {
282
+ log('Peer authenticated');
283
+ this._authStatus = AuthStatus.SUCCESS;
284
+ this._onSessionAuth?.(this._teleport);
285
+ // TODO(dmaretskyi): Configure replicator to upload.
286
+ },
287
+ onAuthFailure: () => {
288
+ this._authStatus = AuthStatus.FAILURE;
289
+ this._onAuthFailure?.(this._teleport);
290
+ },
291
+ }),
292
+ );
293
+
294
+ if (!this._disableP2pReplication) {
295
+ this._teleport.addExtension('dxos.mesh.teleport.replicator', this.replicator);
296
+ }
297
+ this._teleport.addExtension('dxos.mesh.teleport.blobsync', this._blobSync.createExtension());
298
+ }
299
+
300
+ async close(): Promise<void> {
301
+ log('close');
302
+ await this._teleport.close();
303
+ }
304
+
305
+ async abort(): Promise<void> {
306
+ await this._teleport.abort();
307
+ }
308
+ }
@@ -0,0 +1,181 @@
1
+ //
2
+ // Copyright 2022 DXOS.org
3
+ //
4
+
5
+ import { describe, expect, onTestFinished, test } from 'vitest';
6
+
7
+ import { Context } from '@dxos/context';
8
+ import { CredentialGenerator } from '@dxos/credentials';
9
+
10
+ import { TestAgentBuilder } from './testing';
11
+
12
+ // TODO(burdon): Factor out?
13
+ const run = <T>(cb: () => Promise<T>): Promise<T> => cb();
14
+
15
+ describe('space/space', () => {
16
+ test('creates a database with object model', async () => {
17
+ const builder = new TestAgentBuilder();
18
+ onTestFinished(async () => {
19
+ await builder.close();
20
+ });
21
+ const agent = await builder.createPeer();
22
+ const space = await agent.createSpace();
23
+
24
+ await space.open(Context.default());
25
+ expect(space.isOpen).toBeTruthy();
26
+ onTestFinished(async () => {
27
+ await space.close();
28
+ });
29
+
30
+ await agent.spaceGenesis(space);
31
+
32
+ await space.controlPipeline.state!.waitUntilTimeframe(space.controlPipeline.state!.endTimeframe);
33
+
34
+ await builder.close();
35
+ expect(space.isOpen).toBeFalsy();
36
+ });
37
+
38
+ test('two spaces replicating', async () => {
39
+ const builder = new TestAgentBuilder();
40
+ onTestFinished(async () => {
41
+ await builder.close();
42
+ });
43
+
44
+ //
45
+ // Agent 1
46
+ //
47
+ const [agent1, space1] = await run(async () => {
48
+ const agent = await builder.createPeer();
49
+ const space = await agent.createSpace(agent.identityKey);
50
+
51
+ await space.open(Context.default());
52
+ await space.startProtocol(Context.default());
53
+ expect(space.isOpen).toBeTruthy();
54
+ onTestFinished(async () => {
55
+ await space.close();
56
+ });
57
+
58
+ await agent.spaceGenesis(space);
59
+
60
+ await space.controlPipeline.state!.waitUntilTimeframe(space.controlPipeline.state!.endTimeframe);
61
+
62
+ return [agent, space];
63
+ });
64
+
65
+ //
66
+ // Agent 2
67
+ //
68
+ const [agent2, space2] = await run(async () => {
69
+ // NOTE: The genesisKey would be passed as part of the invitation.
70
+ const agent = await builder.createPeer();
71
+ const space = await agent.createSpace(agent.identityKey, space1.key, space1.genesisFeedKey, undefined, true);
72
+
73
+ await space.open(Context.default());
74
+ await space.startProtocol(Context.default());
75
+ expect(space.isOpen).toBeTruthy();
76
+ onTestFinished(async () => {
77
+ await space.close();
78
+ });
79
+
80
+ return [agent, space];
81
+ });
82
+
83
+ expect(agent1).toBeDefined();
84
+ expect(agent2).toBeDefined();
85
+
86
+ {
87
+ // Write invitation from agent 1.
88
+ const generator = new CredentialGenerator(agent1.keyring, agent1.identityKey, agent1.deviceKey);
89
+ const credentials = await generator.createMemberInvitation(
90
+ space1.key,
91
+ agent2.identityKey,
92
+ agent2.deviceKey,
93
+ space2.controlFeedKey!,
94
+ space2.dataFeedKey!,
95
+ space1.genesisFeedKey,
96
+ );
97
+
98
+ for (const credential of credentials) {
99
+ await space1.controlPipeline.writer.write({
100
+ credential: { credential },
101
+ });
102
+ }
103
+ }
104
+
105
+ {
106
+ // Initial data exchange.
107
+
108
+ // Agent 1 reads all feed messages.
109
+ await space1.controlPipeline.state!.waitUntilTimeframe(space1.controlPipeline.state!.endTimeframe);
110
+
111
+ // Agent 2 reads all feed messages.
112
+ await space2.controlPipeline.state!.waitUntilTimeframe(space1.controlPipeline.state!.endTimeframe);
113
+ }
114
+
115
+ // TODO(burdon): Write multiple items (extract for all tests).
116
+
117
+ await builder.close();
118
+ expect(space1.isOpen).toBeFalsy();
119
+ expect(space2.isOpen).toBeFalsy();
120
+ });
121
+
122
+ test('open & close', async () => {
123
+ const builder = new TestAgentBuilder();
124
+ onTestFinished(async () => {
125
+ await builder.close();
126
+ });
127
+ const agent = await builder.createPeer();
128
+ const space1 = await agent.createSpace();
129
+
130
+ await space1.open(Context.default());
131
+ expect(space1.isOpen).toBeTruthy();
132
+ onTestFinished(async () => {
133
+ await space1.close();
134
+ });
135
+
136
+ await agent.spaceGenesis(space1);
137
+
138
+ await space1.controlPipeline.state!.waitUntilTimeframe(space1.controlPipeline.state!.endTimeframe);
139
+
140
+ await space1.close();
141
+ expect(space1.isOpen).toBeFalsy();
142
+
143
+ // Re-open.
144
+ const space2 = await agent.createSpace(agent.identityKey, space1.key, space1.genesisFeedKey, space1.dataFeedKey);
145
+
146
+ await space2.open(Context.default());
147
+ await space2.controlPipeline.state!.waitUntilTimeframe(space2.controlPipeline.state!.endTimeframe);
148
+ });
149
+
150
+ test('re-open', async () => {
151
+ const builder = new TestAgentBuilder();
152
+ onTestFinished(async () => {
153
+ await builder.close();
154
+ });
155
+ const agent = await builder.createPeer();
156
+ const space = await agent.createSpace();
157
+
158
+ {
159
+ await space.open(Context.default());
160
+ onTestFinished(async () => {
161
+ await space.close();
162
+ });
163
+ expect(space.isOpen).toBeTruthy();
164
+
165
+ await agent.spaceGenesis(space);
166
+
167
+ await space.controlPipeline.state!.waitUntilTimeframe(space.controlPipeline.state!.endTimeframe);
168
+
169
+ await space.close();
170
+ expect(space.isOpen).toBeFalsy();
171
+ }
172
+
173
+ // Re-open.
174
+ {
175
+ await space.open(Context.default());
176
+ expect(space.isOpen).toBeTruthy();
177
+
178
+ await space.controlPipeline.state!.waitUntilTimeframe(space.controlPipeline.state!.endTimeframe);
179
+ }
180
+ });
181
+ });
@@ -0,0 +1,196 @@
1
+ //
2
+ // Copyright 2022 DXOS.org
3
+ //
4
+
5
+ import { Event, scheduleMicroTask, synchronized, trackLeaks } from '@dxos/async';
6
+ import { type Context, Resource } from '@dxos/context';
7
+ import { type DelegateInvitationCredential, type FeedInfo, type MemberInfo } from '@dxos/credentials';
8
+ import { type FeedOptions, type FeedWrapper } from '@dxos/feed-store';
9
+ import { invariant } from '@dxos/invariant';
10
+ import { type PublicKey, type SpaceId } from '@dxos/keys';
11
+ import { log, logInfo } from '@dxos/log';
12
+ import type { FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
13
+ import { AdmittedFeed, type Credential } from '@dxos/protocols/proto/dxos/halo/credentials';
14
+ import { type Timeframe } from '@dxos/timeframe';
15
+ import { trace } from '@dxos/tracing';
16
+ import { type AsyncCallback, Callback } from '@dxos/util';
17
+
18
+ import { type IMetadataStore } from '../metadata';
19
+ import { type PipelineAccessor } from '../pipeline';
20
+ import { ControlPipeline } from './control-pipeline';
21
+ import { type SpaceProtocol } from './space-protocol';
22
+
23
+ // TODO(burdon): Factor out?
24
+ type FeedProvider = (feedKey: PublicKey, opts?: FeedOptions) => Promise<FeedWrapper<FeedMessage>>;
25
+
26
+ export type SpaceProps = {
27
+ id: SpaceId;
28
+ spaceKey: PublicKey;
29
+ protocol: SpaceProtocol;
30
+ genesisFeed: FeedWrapper<FeedMessage>;
31
+ feedProvider: FeedProvider;
32
+ metadataStore: IMetadataStore;
33
+ memberKey: PublicKey;
34
+
35
+ // TODO(dmaretskyi): Superseded by epochs.
36
+ snapshotId?: string | undefined;
37
+
38
+ onDelegatedInvitationStatusChange: (invitation: DelegateInvitationCredential, isActive: boolean) => Promise<void>;
39
+ onMemberRolesChanged: (member: MemberInfo[]) => Promise<void>;
40
+ };
41
+
42
+ export type CreatePipelineProps = {
43
+ start: Timeframe;
44
+ // designation: AdmittedFeed.Designation;
45
+ };
46
+
47
+ /**
48
+ * Spaces are globally addressable databases with access control.
49
+ */
50
+ // TODO(dmaretskyi): Extract database stuff.
51
+ // TODO(dmaretskyi): Rename HaloGraph move to HALO.
52
+ @trackLeaks('open', 'close')
53
+ export class Space extends Resource {
54
+ public readonly onCredentialProcessed = new Callback<AsyncCallback<Credential>>();
55
+ public readonly stateUpdate = new Event();
56
+ public readonly protocol: SpaceProtocol;
57
+
58
+ private readonly _id: SpaceId;
59
+ private readonly _key: PublicKey;
60
+ private readonly _genesisFeedKey: PublicKey;
61
+ private readonly _feedProvider: FeedProvider;
62
+ private readonly _controlPipeline: ControlPipeline;
63
+
64
+ private _controlFeed?: FeedWrapper<FeedMessage>;
65
+ private _dataFeed?: FeedWrapper<FeedMessage>;
66
+
67
+ constructor(params: SpaceProps) {
68
+ super();
69
+ invariant(params.spaceKey && params.feedProvider);
70
+ this._id = params.id;
71
+ this._key = params.spaceKey;
72
+ this._genesisFeedKey = params.genesisFeed.key;
73
+ this._feedProvider = params.feedProvider;
74
+
75
+ this._controlPipeline = new ControlPipeline({
76
+ spaceKey: params.spaceKey,
77
+ genesisFeed: params.genesisFeed,
78
+ feedProvider: params.feedProvider,
79
+ metadataStore: params.metadataStore,
80
+ });
81
+
82
+ // TODO(dmaretskyi): Feed set abstraction.
83
+ this._controlPipeline.onFeedAdmitted.set(async (info) => {
84
+ // Enable sparse replication to not download mutations covered by prior epochs.
85
+ const sparse = info.assertion.designation === AdmittedFeed.Designation.DATA;
86
+
87
+ if (!info.key.equals(params.genesisFeed.key)) {
88
+ scheduleMicroTask(this._ctx, async () => {
89
+ await this.protocol.addFeed(await params.feedProvider(info.key, { sparse }));
90
+ });
91
+ }
92
+ });
93
+
94
+ this._controlPipeline.onCredentialProcessed.set(async (credential) => {
95
+ await this.onCredentialProcessed.callIfSet(credential);
96
+ log('onCredentialProcessed', { credential });
97
+ this.stateUpdate.emit();
98
+ });
99
+ this._controlPipeline.onDelegatedInvitation.set(async (invitation) => {
100
+ log('onDelegatedInvitation', { invitation });
101
+ await params.onDelegatedInvitationStatusChange(invitation, true);
102
+ });
103
+ this._controlPipeline.onDelegatedInvitationRemoved.set(async (invitation) => {
104
+ log('onDelegatedInvitationRemoved', { invitation });
105
+ await params.onDelegatedInvitationStatusChange(invitation, false);
106
+ });
107
+ this._controlPipeline.onMemberRoleChanged.set(async (changedMembers) => {
108
+ log('onMemberRoleChanged', () => ({ changedMembers: changedMembers.map((m) => [m.key, m.role]) }));
109
+ await params.onMemberRolesChanged(changedMembers);
110
+ });
111
+
112
+ // Start replicating the genesis feed.
113
+ this.protocol = params.protocol;
114
+ }
115
+
116
+ @logInfo
117
+ get id() {
118
+ return this._id;
119
+ }
120
+
121
+ @logInfo
122
+ get key() {
123
+ return this._key;
124
+ }
125
+
126
+ get genesisFeedKey(): PublicKey {
127
+ return this._genesisFeedKey;
128
+ }
129
+
130
+ get controlFeedKey() {
131
+ return this._controlFeed?.key;
132
+ }
133
+
134
+ get dataFeedKey() {
135
+ return this._dataFeed?.key;
136
+ }
137
+
138
+ get spaceState() {
139
+ return this._controlPipeline.spaceState;
140
+ }
141
+
142
+ /**
143
+ * @test-only
144
+ */
145
+ get controlPipeline(): PipelineAccessor {
146
+ return this._controlPipeline.pipeline;
147
+ }
148
+
149
+ async setControlFeed(feed: FeedWrapper<FeedMessage>): Promise<this> {
150
+ invariant(!this._controlFeed, 'Control feed already set.');
151
+ this._controlFeed = feed;
152
+ await this._controlPipeline.setWriteFeed(feed);
153
+ return this;
154
+ }
155
+
156
+ async setDataFeed(feed: FeedWrapper<FeedMessage>): Promise<this> {
157
+ invariant(!this._dataFeed, 'Data feed already set.');
158
+ this._dataFeed = feed;
159
+ return this;
160
+ }
161
+
162
+ /**
163
+ * Use for diagnostics.
164
+ */
165
+ getControlFeeds(): FeedInfo[] {
166
+ return Array.from(this._controlPipeline.spaceState.feeds.values());
167
+ }
168
+
169
+ @trace.span({ op: 'lifecycle' })
170
+ protected override async _open(ctx: Context): Promise<void> {
171
+ log('opening...');
172
+
173
+ // Order is important.
174
+ await this._controlPipeline.start(ctx);
175
+
176
+ log('opened');
177
+ }
178
+
179
+ @synchronized
180
+ public async startProtocol(ctx: Context): Promise<void> {
181
+ invariant(this.isOpen);
182
+ await this.protocol.start(ctx);
183
+ await this.protocol.addFeed(await this._feedProvider(this._genesisFeedKey));
184
+ }
185
+
186
+ @synchronized
187
+ protected override async _close(ctx: Context): Promise<void> {
188
+ log('closing...', { key: this._key });
189
+
190
+ // Closes in reverse order to open.
191
+ await this.protocol.stop(ctx);
192
+ await this._controlPipeline.stop();
193
+
194
+ log('closed');
195
+ }
196
+ }
@@ -0,0 +1,27 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { log } from '@dxos/log';
6
+ import { schema } from '@dxos/protocols/proto';
7
+ import type { Storage } from '@dxos/random-access-storage';
8
+
9
+ import { MetadataStore } from '../../metadata';
10
+
11
+ const EchoMetadata = schema.getCodecForType('dxos.echo.metadata.EchoMetadata');
12
+
13
+ /**
14
+ * This function will change the storage version in the metadata.
15
+ * This will break your storage and make it unusable.
16
+ * Use this only for testing purposes.
17
+ */
18
+ export const changeStorageVersionInMetadata = async (storage: Storage, version: number) => {
19
+ log('Changing storage version in metadata. USE ONLY FOR TESTING.');
20
+ const metadata = new MetadataStore(storage.createDirectory('metadata'));
21
+ await metadata.load();
22
+ const echoMetadata = metadata.metadata;
23
+ echoMetadata.version = version;
24
+ const file = metadata._directory.getOrCreateFile('EchoMetadata');
25
+ await metadata._writeFile(file, EchoMetadata, echoMetadata);
26
+ await metadata._directory.flush();
27
+ };