@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
@@ -2,15 +2,25 @@
2
2
  // Copyright 2022 DXOS.org
3
3
  //
4
4
 
5
+ import * as EffectContext from 'effect/Context';
6
+ import * as Effect from 'effect/Effect';
7
+ import * as Layer from 'effect/Layer';
8
+ import * as Option from 'effect/Option';
9
+
5
10
  import { type PushStream, TimeoutError, type Trigger, scheduleTask } from '@dxos/async';
6
11
  import { INVITATION_TIMEOUT, getExpirationTime } from '@dxos/client-protocol';
7
12
  import { type Context, ContextDisposedError } from '@dxos/context';
8
13
  import { createKeyPair, sign } from '@dxos/crypto';
9
- import { type EdgeHttpClient } from '@dxos/edge-client';
14
+ import { type EdgeHttpClient, EdgeHttpClientService } from '@dxos/edge-client';
10
15
  import { invariant } from '@dxos/invariant';
11
16
  import { PublicKey } from '@dxos/keys';
12
17
  import { log } from '@dxos/log';
13
- import { type SwarmConnection, type SwarmNetworkManager, createTeleportProtocolFactory } from '@dxos/network-manager';
18
+ import {
19
+ type SwarmConnection,
20
+ type SwarmNetworkManager,
21
+ SwarmNetworkManagerService,
22
+ createTeleportProtocolFactory,
23
+ } from '@dxos/network-manager';
14
24
  import { InvalidInvitationError, InvalidInvitationExtensionRoleError } from '@dxos/protocols';
15
25
  import { type AdmissionKeypair, Invitation } from '@dxos/protocols/proto/dxos/client/services';
16
26
  import { type DeviceProfileDocument } from '@dxos/protocols/proto/dxos/halo/credentials';
@@ -64,7 +74,14 @@ export type InvitationConnectionProps = {
64
74
  * TODO: the flow logic should either be contained in invitations-handler or in extensions, not be split across
65
75
  * TODO: potentially re-evaluate host-side API to allow multiple concurrent connection, so that mutex can be removed
66
76
  */
67
- @_trace.resource()
77
+ /**
78
+ * Effect service tag for {@link InvitationsHandler}.
79
+ */
80
+ export class InvitationsHandlerService extends EffectContext.Tag('@dxos/client-services/InvitationsHandler')<
81
+ InvitationsHandlerService,
82
+ InvitationsHandler
83
+ >() {}
84
+
68
85
  export class InvitationsHandler {
69
86
  /**
70
87
  * @internal
@@ -564,3 +581,22 @@ export const createAdmissionKeypair = (): AdmissionKeypair => {
564
581
  const keypair = createKeyPair();
565
582
  return { publicKey: PublicKey.from(keypair.publicKey), privateKey: keypair.secretKey };
566
583
  };
584
+
585
+ export type InvitationsHandlerLayerOptions = {
586
+ connectionProps?: InvitationConnectionProps;
587
+ };
588
+
589
+ /**
590
+ * Effect Layer constructing an {@link InvitationsHandler}.
591
+ */
592
+ export const InvitationsHandlerLayer = (
593
+ options: InvitationsHandlerLayerOptions = {},
594
+ ): Layer.Layer<InvitationsHandlerService, never, SwarmNetworkManagerService> =>
595
+ Layer.effect(
596
+ InvitationsHandlerService,
597
+ Effect.gen(function* () {
598
+ const networkManager = yield* SwarmNetworkManagerService;
599
+ const edgeClient = yield* Effect.serviceOption(EdgeHttpClientService);
600
+ return new InvitationsHandler(networkManager, Option.getOrUndefined(edgeClient), options.connectionProps);
601
+ }),
602
+ );
@@ -2,6 +2,10 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
+ import * as EffectContext from 'effect/Context';
6
+ import * as Effect from 'effect/Effect';
7
+ import * as Layer from 'effect/Layer';
8
+
5
9
  import { Event, PushStream, TimeoutError, Trigger } from '@dxos/async';
6
10
  import {
7
11
  AuthenticatingInvitation,
@@ -11,7 +15,6 @@ import {
11
15
  } from '@dxos/client-protocol';
12
16
  import { Context } from '@dxos/context';
13
17
  import { generatePasscode } from '@dxos/credentials';
14
- import { type IMetadataStore, hasInvitationExpired } from '@dxos/echo-host';
15
18
  import { invariant } from '@dxos/invariant';
16
19
  import { PublicKey } from '@dxos/keys';
17
20
  import { log } from '@dxos/log';
@@ -23,14 +26,22 @@ import {
23
26
  import { SpaceMember } from '@dxos/protocols/proto/dxos/halo/credentials';
24
27
  import { trace } from '@dxos/tracing';
25
28
 
29
+ import { type IMetadataStore, IMetadataStoreService, hasInvitationExpired } from '../metadata';
26
30
  import type { InvitationProtocol } from './invitation-protocol';
27
- import { type InvitationsHandler, createAdmissionKeypair } from './invitations-handler';
31
+ import { type InvitationsHandler, InvitationsHandlerService, createAdmissionKeypair } from './invitations-handler';
32
+
33
+ /**
34
+ * Effect service tag for {@link InvitationsManager}.
35
+ */
36
+ export class InvitationsManagerService extends EffectContext.Tag('@dxos/client-services/InvitationsManager')<
37
+ InvitationsManagerService,
38
+ InvitationsManager
39
+ >() {}
28
40
 
29
41
  /**
30
42
  * Entry point for creating and accepting invitations, keeps track of existing invitation set and
31
43
  * emits events when the set changes.
32
44
  */
33
- @trace.resource()
34
45
  export class InvitationsManager {
35
46
  private readonly _createInvitations = new Map<string, CancellableInvitation>();
36
47
  private readonly _acceptInvitations = new Map<string, AuthenticatingInvitation>();
@@ -44,12 +55,29 @@ export class InvitationsManager {
44
55
  private readonly _persistentInvitationsLoadedEvent = new Event();
45
56
  private _persistentInvitationsLoaded = false;
46
57
 
58
+ private _invitationHandlerFactory?: (
59
+ invitation: Partial<Invitation> & Pick<Invitation, 'kind'>,
60
+ ) => InvitationProtocol;
61
+
47
62
  constructor(
48
63
  private readonly _invitationsHandler: InvitationsHandler,
49
- private readonly _getHandler: (invitation: Partial<Invitation> & Pick<Invitation, 'kind'>) => InvitationProtocol,
50
64
  private readonly _metadataStore: IMetadataStore,
51
65
  ) {}
52
66
 
67
+ /**
68
+ * Wires the invitation handler factory after the composing stack is fully constructed.
69
+ */
70
+ setInvitationHandlerFactory(
71
+ getHandler: (invitation: Partial<Invitation> & Pick<Invitation, 'kind'>) => InvitationProtocol,
72
+ ): void {
73
+ this._invitationHandlerFactory = getHandler;
74
+ }
75
+
76
+ private _getHandler(invitation: Partial<Invitation> & Pick<Invitation, 'kind'>): InvitationProtocol {
77
+ invariant(this._invitationHandlerFactory, 'Invitation handler factory not set.');
78
+ return this._invitationHandlerFactory(invitation);
79
+ }
80
+
53
81
  @trace.span({ showInBrowserTimeline: true, op: 'lifecycle' })
54
82
  async createInvitation(
55
83
  ctx: Context,
@@ -354,3 +382,23 @@ export class InvitationsManager {
354
382
  );
355
383
  }
356
384
  }
385
+
386
+ /**
387
+ * Effect Layer constructing an {@link InvitationsManager}.
388
+ *
389
+ * The invitation handler factory points "up the stack" and is wired via
390
+ * {@link InvitationsManager.setInvitationHandlerFactory} after composition.
391
+ */
392
+ export const InvitationsManagerLayer = (): Layer.Layer<
393
+ InvitationsManagerService,
394
+ never,
395
+ InvitationsHandlerService | IMetadataStoreService
396
+ > =>
397
+ Layer.effect(
398
+ InvitationsManagerService,
399
+ Effect.gen(function* () {
400
+ const invitationsHandler = yield* InvitationsHandlerService;
401
+ const metadataStore = yield* IMetadataStoreService;
402
+ return new InvitationsManager(invitationsHandler, metadataStore);
403
+ }),
404
+ );
@@ -2,14 +2,18 @@
2
2
  // Copyright 2022 DXOS.org
3
3
  //
4
4
 
5
- import { type RequestOptions, Stream } from '@dxos/codec-protobuf';
5
+ import * as Effect from 'effect/Effect';
6
+ import * as EffectStream from 'effect/Stream';
7
+
8
+ import { Context } from '@dxos/context';
6
9
  import {
7
10
  type AcceptInvitationRequest,
8
11
  type AuthenticationRequest,
12
+ type CancelInvitationRequest,
9
13
  type Invitation,
10
- type InvitationsService,
11
14
  QueryInvitationsResponse,
12
15
  } from '@dxos/protocols/proto/dxos/client/services';
16
+ import { type InvitationsService } from '@dxos/protocols/rpc';
13
17
  import { trace } from '@dxos/tracing';
14
18
 
15
19
  import { type InvitationsManager } from './invitations-manager';
@@ -17,48 +21,68 @@ import { type InvitationsManager } from './invitations-manager';
17
21
  /**
18
22
  * Adapts invitation service observable to client/service stream.
19
23
  */
20
- export class InvitationsServiceImpl implements InvitationsService {
21
- constructor(private readonly _invitationsManager: InvitationsManager) {}
24
+ export class InvitationsServiceImpl implements InvitationsService.Handlers {
25
+ 'constructor'(private readonly _invitationsManager: InvitationsManager) {}
22
26
 
23
27
  // TODO(burdon): Guest/host label.
24
- getLoggingContext() {
28
+ 'getLoggingContext'() {
25
29
  return {
26
30
  // deviceKey: this._identityManager.identity?.deviceKey
27
31
  };
28
32
  }
29
33
 
30
- createInvitation(request: Invitation, options?: RequestOptions): Stream<Invitation> {
31
- return new Stream<Invitation>(({ ctx, next, close }) => {
34
+ ['InvitationsService.createInvitation'](request: Invitation): EffectStream.Stream<Invitation, Error> {
35
+ return EffectStream.async<Invitation, Error>((emit) => {
36
+ const ctx = Context.default();
32
37
  void this._invitationsManager
33
38
  .createInvitation(ctx, request)
34
39
  .then((invitation) => {
35
40
  trace.metrics.increment('dxos.invitation.created');
36
- invitation.subscribe(next, close, close);
41
+ invitation.subscribe(
42
+ (value) => void emit.single(value),
43
+ (err) => void emit.fail(err),
44
+ () => void emit.end(),
45
+ );
37
46
  })
38
- .catch(close);
39
- }, options?.ctx);
47
+ .catch((err) => void emit.fail(err));
48
+ return Effect.promise(() => ctx.dispose());
49
+ });
40
50
  }
41
51
 
42
- acceptInvitation(request: AcceptInvitationRequest, options?: RequestOptions): Stream<Invitation> {
43
- return new Stream<Invitation>(({ ctx, next, close }) => {
52
+ ['InvitationsService.acceptInvitation'](request: AcceptInvitationRequest): EffectStream.Stream<Invitation, Error> {
53
+ return EffectStream.async<Invitation, Error>((emit) => {
54
+ const ctx = Context.default();
44
55
  const invitation = this._invitationsManager.acceptInvitation(ctx, request);
45
- invitation.subscribe(next, close, close);
46
- }, options?.ctx);
56
+ invitation.subscribe(
57
+ (value) => void emit.single(value),
58
+ (err) => void emit.fail(err),
59
+ () => void emit.end(),
60
+ );
61
+ return Effect.promise(() => ctx.dispose());
62
+ });
47
63
  }
48
64
 
49
- async authenticate(request: AuthenticationRequest): Promise<void> {
50
- return this._invitationsManager.authenticate(request);
65
+ ['InvitationsService.authenticate'](request: AuthenticationRequest): Effect.Effect<void, Error> {
66
+ return Effect.tryPromise({
67
+ try: () => this._invitationsManager.authenticate(request),
68
+ catch: (error) => error as Error,
69
+ });
51
70
  }
52
71
 
53
- async cancelInvitation(request: { invitationId: string }): Promise<void> {
54
- return this._invitationsManager.cancelInvitation(request);
72
+ ['InvitationsService.cancelInvitation'](request: CancelInvitationRequest): Effect.Effect<void, Error> {
73
+ return Effect.tryPromise({
74
+ try: () => this._invitationsManager.cancelInvitation(request),
75
+ catch: (error) => error as Error,
76
+ });
55
77
  }
56
78
 
57
- queryInvitations(): Stream<QueryInvitationsResponse> {
58
- return new Stream<QueryInvitationsResponse>(({ next, ctx }) => {
79
+ ['InvitationsService.queryInvitations'](): EffectStream.Stream<QueryInvitationsResponse, Error> {
80
+ return EffectStream.async<QueryInvitationsResponse, Error>((emit) => {
81
+ const ctx = Context.default();
82
+
59
83
  // Push added invitations to the stream.
60
84
  this._invitationsManager.invitationCreated.on(ctx, (invitation) => {
61
- next({
85
+ void emit.single({
62
86
  action: QueryInvitationsResponse.Action.ADDED,
63
87
  type: QueryInvitationsResponse.Type.CREATED,
64
88
  invitations: [invitation],
@@ -66,7 +90,7 @@ export class InvitationsServiceImpl implements InvitationsService {
66
90
  });
67
91
 
68
92
  this._invitationsManager.invitationAccepted.on(ctx, (invitation) => {
69
- next({
93
+ void emit.single({
70
94
  action: QueryInvitationsResponse.Action.ADDED,
71
95
  type: QueryInvitationsResponse.Type.ACCEPTED,
72
96
  invitations: [invitation],
@@ -75,7 +99,7 @@ export class InvitationsServiceImpl implements InvitationsService {
75
99
 
76
100
  // Push removed invitations to the stream.
77
101
  this._invitationsManager.removedCreated.on(ctx, (invitation) => {
78
- next({
102
+ void emit.single({
79
103
  action: QueryInvitationsResponse.Action.REMOVED,
80
104
  type: QueryInvitationsResponse.Type.CREATED,
81
105
  invitations: [invitation],
@@ -83,7 +107,7 @@ export class InvitationsServiceImpl implements InvitationsService {
83
107
  });
84
108
 
85
109
  this._invitationsManager.removedAccepted.on(ctx, (invitation) => {
86
- next({
110
+ void emit.single({
87
111
  action: QueryInvitationsResponse.Action.REMOVED,
88
112
  type: QueryInvitationsResponse.Type.ACCEPTED,
89
113
  invitations: [invitation],
@@ -92,7 +116,7 @@ export class InvitationsServiceImpl implements InvitationsService {
92
116
 
93
117
  // used only for testing
94
118
  this._invitationsManager.saved.on(ctx, (invitation) => {
95
- next({
119
+ void emit.single({
96
120
  action: QueryInvitationsResponse.Action.SAVED,
97
121
  type: QueryInvitationsResponse.Type.CREATED,
98
122
  invitations: [invitation],
@@ -100,14 +124,14 @@ export class InvitationsServiceImpl implements InvitationsService {
100
124
  });
101
125
 
102
126
  // Push existing invitations to the stream.
103
- next({
127
+ void emit.single({
104
128
  action: QueryInvitationsResponse.Action.ADDED,
105
129
  type: QueryInvitationsResponse.Type.CREATED,
106
130
  invitations: this._invitationsManager.getCreatedInvitations(),
107
131
  existing: true,
108
132
  });
109
133
 
110
- next({
134
+ void emit.single({
111
135
  action: QueryInvitationsResponse.Action.ADDED,
112
136
  type: QueryInvitationsResponse.Type.ACCEPTED,
113
137
  invitations: this._invitationsManager.getAcceptedInvitations(),
@@ -115,13 +139,15 @@ export class InvitationsServiceImpl implements InvitationsService {
115
139
  });
116
140
 
117
141
  this._invitationsManager.onPersistentInvitationsLoaded(ctx, () => {
118
- next({
142
+ void emit.single({
119
143
  action: QueryInvitationsResponse.Action.LOAD_COMPLETE,
120
144
  type: QueryInvitationsResponse.Type.CREATED,
121
145
  // TODO(nf): populate with invitations
122
146
  });
123
147
  });
124
148
  // TODO(nf): expired invitations?
149
+
150
+ return Effect.promise(() => ctx.dispose());
125
151
  });
126
152
  }
127
153
  }
@@ -2,20 +2,23 @@
2
2
  // Copyright 2022 DXOS.org
3
3
  //
4
4
 
5
+ import * as Effect from 'effect/Effect';
6
+ import * as EffectStream from 'effect/Stream';
7
+
5
8
  import { Event } from '@dxos/async';
6
- import { Stream } from '@dxos/codec-protobuf/stream';
9
+ import { Context } from '@dxos/context';
7
10
  import { PublicKey } from '@dxos/keys';
8
11
  import { type LogLevel, type LogProcessor, type LogEntry as NaturalLogEntry, log } from '@dxos/log';
9
12
  import {
10
13
  type ControlMetricsRequest,
11
14
  type ControlMetricsResponse,
12
15
  type LogEntry,
13
- type LoggingService,
14
16
  type Metrics,
15
17
  QueryLogsRequest,
16
18
  type QueryMetricsRequest,
17
19
  type QueryMetricsResponse,
18
20
  } from '@dxos/protocols/proto/dxos/client/services';
21
+ import { type LoggingService } from '@dxos/protocols/rpc';
19
22
  import { numericalValues, tracer } from '@dxos/util';
20
23
 
21
24
  /**
@@ -25,45 +28,52 @@ import { numericalValues, tracer } from '@dxos/util';
25
28
  * seen in the main window console without opening the shared worker DevTools. Shared worker client
26
29
  * services is deprecated; dedicated worker logs already show in the main window console.
27
30
  */
28
- export class LoggingServiceImpl implements LoggingService {
29
- private readonly _logs = new Event<NaturalLogEntry>();
30
- private readonly _started = Date.now();
31
- private readonly _sessionId = PublicKey.random().toHex();
31
+ export class LoggingServiceImpl implements LoggingService.Handlers {
32
+ private readonly '_logs' = new Event<NaturalLogEntry>();
33
+ private readonly '_started' = Date.now();
34
+ private readonly '_sessionId' = PublicKey.random().toHex();
32
35
 
33
- async open(): Promise<void> {
36
+ async 'open'(): Promise<void> {
34
37
  log.runtimeConfig.processors.push(this._logProcessor);
35
38
  }
36
39
 
37
- async close(): Promise<void> {
40
+ async 'close'(): Promise<void> {
38
41
  const index = log.runtimeConfig.processors.findIndex((processor: LogProcessor) => processor === this._logProcessor);
39
42
  log.runtimeConfig.processors.splice(index, 1);
40
43
  }
41
44
 
42
- async controlMetrics({ reset, record }: ControlMetricsRequest): Promise<ControlMetricsResponse> {
43
- if (reset) {
44
- tracer.clear();
45
- }
46
-
47
- if (record === true) {
48
- tracer.start();
49
- } else if (record === false) {
50
- tracer.stop();
51
- }
52
-
53
- return { recording: tracer.recording };
45
+ ['LoggingService.controlMetrics']({
46
+ reset,
47
+ record,
48
+ }: ControlMetricsRequest): Effect.Effect<ControlMetricsResponse, Error> {
49
+ return Effect.sync(() => {
50
+ if (reset) {
51
+ tracer.clear();
52
+ }
53
+
54
+ if (record === true) {
55
+ tracer.start();
56
+ } else if (record === false) {
57
+ tracer.stop();
58
+ }
59
+
60
+ return { recording: tracer.recording };
61
+ });
54
62
  }
55
63
 
56
64
  /**
57
65
  * @deprecated (Move to diagnostics).
58
66
  */
59
- queryMetrics({ interval = 5_000 }: QueryMetricsRequest): Stream<QueryMetricsResponse> {
67
+ ['LoggingService.queryMetrics']({
68
+ interval = 5_000,
69
+ }: QueryMetricsRequest): EffectStream.Stream<QueryMetricsResponse, Error> {
60
70
  // TODO(burdon): Map all traces; how to bind to reducer/metrics shape (e.g., numericalValues)?
61
71
  const getNumericalValues = (key: string) => {
62
72
  const events = tracer.get(key) ?? [];
63
73
  return { key, stats: numericalValues(events, 'duration') };
64
74
  };
65
75
 
66
- return new Stream(({ next }) => {
76
+ return EffectStream.async<QueryMetricsResponse, Error>((emit) => {
67
77
  const update = () => {
68
78
  const metrics: Metrics = {
69
79
  timestamp: new Date(),
@@ -73,7 +83,7 @@ export class LoggingServiceImpl implements LoggingService {
73
83
  ].filter(Boolean) as Metrics.KeyPair[],
74
84
  };
75
85
 
76
- next({
86
+ void emit.single({
77
87
  timestamp: new Date(),
78
88
  metrics,
79
89
  });
@@ -81,14 +91,15 @@ export class LoggingServiceImpl implements LoggingService {
81
91
 
82
92
  update();
83
93
  const i = setInterval(update, Math.max(interval, 1_000));
84
- return () => {
94
+ return Effect.sync(() => {
85
95
  clearInterval(i);
86
- };
96
+ });
87
97
  });
88
98
  }
89
99
 
90
- queryLogs(request: QueryLogsRequest): Stream<LogEntry> {
91
- return new Stream<LogEntry>(({ ctx, next }) => {
100
+ ['LoggingService.queryLogs'](request: QueryLogsRequest): EffectStream.Stream<LogEntry, Error> {
101
+ return EffectStream.async<LogEntry, Error>((emit) => {
102
+ const ctx = Context.default();
92
103
  const handler = (entry: NaturalLogEntry) => {
93
104
  // This call was caused by the logging service itself.
94
105
  if (LOG_PROCESSING > 0) {
@@ -133,17 +144,18 @@ export class LoggingServiceImpl implements LoggingService {
133
144
 
134
145
  try {
135
146
  LOG_PROCESSING++;
136
- next(record);
147
+ void emit.single(record);
137
148
  } finally {
138
149
  LOG_PROCESSING--;
139
150
  }
140
151
  };
141
152
 
142
153
  this._logs.on(ctx, handler);
154
+ return Effect.promise(() => ctx.dispose());
143
155
  });
144
156
  }
145
157
 
146
- private _logProcessor: LogProcessor = (_config, entry) => {
158
+ private '_logProcessor': LogProcessor = (_config, entry) => {
147
159
  this._logs.emit(entry);
148
160
  };
149
161
  }
@@ -2,11 +2,14 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
+ import * as Effect from 'effect/Effect';
6
+ import * as Fiber from 'effect/Fiber';
7
+ import * as Option from 'effect/Option';
8
+ import * as Stream from 'effect/Stream';
5
9
  import { afterEach, beforeEach, describe, expect, test } from 'vitest';
6
10
 
7
- import { Trigger } from '@dxos/async';
11
+ import { EffectEx } from '@dxos/effect';
8
12
  import { LogLevel, log } from '@dxos/log';
9
- import { type LogEntry } from '@dxos/protocols/proto/dxos/client/services';
10
13
 
11
14
  import { LoggingServiceImpl } from './logging-service';
12
15
 
@@ -23,26 +26,36 @@ describe('LoggingService', () => {
23
26
  });
24
27
 
25
28
  test('queryLogs streams logs', async () => {
26
- const trigger = new Trigger<LogEntry>();
27
- loggingService.queryLogs({}).subscribe((entry) => {
28
- trigger.wake(entry);
29
- });
30
29
  const message = 'Hello World!';
31
- log(message);
32
- const entry = await trigger.wait();
30
+ const entry = await EffectEx.runPromise(
31
+ Effect.gen(function* () {
32
+ const fiber = yield* Effect.fork(Stream.runHead(loggingService['LoggingService.queryLogs']({})));
33
+ // Yield so the forked subscription registers its log handler before emitting.
34
+ yield* Effect.yieldNow();
35
+ yield* Effect.sync(() => log(message));
36
+ return Option.getOrThrow(yield* Fiber.join(fiber));
37
+ }),
38
+ );
33
39
  expect(entry.message).to.eq(message);
34
40
  expect(entry.level).to.eq(LogLevel.DEBUG);
35
41
  });
36
42
 
37
43
  test('queryLogs filters logs', async () => {
38
- const trigger = new Trigger<LogEntry>();
39
- loggingService.queryLogs({ filters: [{ level: LogLevel.ERROR }] }).subscribe((entry) => {
40
- trigger.wake(entry);
41
- });
42
- log('debugging something');
43
44
  const message = 'This is a failure';
44
- log.error(message);
45
- const entry = await trigger.wait();
45
+ const entry = await EffectEx.runPromise(
46
+ Effect.gen(function* () {
47
+ const fiber = yield* Effect.fork(
48
+ Stream.runHead(loggingService['LoggingService.queryLogs']({ filters: [{ level: LogLevel.ERROR }] })),
49
+ );
50
+ // Yield so the forked subscription registers its log handler before emitting.
51
+ yield* Effect.yieldNow();
52
+ yield* Effect.sync(() => {
53
+ log('debugging something');
54
+ log.error(message);
55
+ });
56
+ return Option.getOrThrow(yield* Fiber.join(fiber));
57
+ }),
58
+ );
46
59
  expect(entry.message).to.eq(message);
47
60
  expect(entry.level).to.eq(LogLevel.ERROR);
48
61
  });
@@ -0,0 +1,6 @@
1
+ //
2
+ // Copyright 2022 DXOS.org
3
+ //
4
+
5
+ export * from './metadata-store';
6
+ export * from './sqlite-metadata-store';
@@ -0,0 +1,49 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { describe, test } from 'vitest';
6
+
7
+ import { PublicKey } from '@dxos/keys';
8
+ import { StorageType, createStorage } from '@dxos/random-access-storage';
9
+
10
+ import { MetadataStore } from './metadata-store';
11
+
12
+ describe('MetadataStore', () => {
13
+ describe('deleted spaces', () => {
14
+ test('addDeletedSpace records a tombstone', async ({ expect }) => {
15
+ const store = new MetadataStore(createStorage({ type: StorageType.RAM }).createDirectory());
16
+ const spaceKey = PublicKey.random();
17
+
18
+ expect(store.deletedSpaces).to.have.length(0);
19
+
20
+ await store.addDeletedSpace(spaceKey);
21
+ expect(store.deletedSpaces).to.have.length(1);
22
+ expect(store.deletedSpaces[0].equals(spaceKey)).to.be.true;
23
+ });
24
+
25
+ test('addDeletedSpace is idempotent', async ({ expect }) => {
26
+ const store = new MetadataStore(createStorage({ type: StorageType.RAM }).createDirectory());
27
+ const spaceKey = PublicKey.random();
28
+
29
+ await store.addDeletedSpace(spaceKey);
30
+ await store.addDeletedSpace(spaceKey);
31
+ expect(store.deletedSpaces).to.have.length(1);
32
+ });
33
+
34
+ test('tombstones survive reload', async ({ expect }) => {
35
+ const directory = createStorage({ type: StorageType.RAM }).createDirectory();
36
+ const spaceKey = PublicKey.random();
37
+
38
+ const store = new MetadataStore(directory);
39
+ await store.load();
40
+ await store.addDeletedSpace(spaceKey);
41
+ await store.close();
42
+
43
+ const reloaded = new MetadataStore(directory);
44
+ await reloaded.load();
45
+ expect(reloaded.deletedSpaces).to.have.length(1);
46
+ expect(reloaded.deletedSpaces[0].equals(spaceKey)).to.be.true;
47
+ });
48
+ });
49
+ });