@dxos/client-services 0.8.4-main.f9ba587 → 0.8.4-main.fcfe5033a5

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 (239) hide show
  1. package/dist/lib/browser/{chunk-ERQJUBAW.mjs → chunk-HYGNOM23.mjs} +4279 -3342
  2. package/dist/lib/browser/chunk-HYGNOM23.mjs.map +7 -0
  3. package/dist/lib/browser/chunk-NQSC7HOE.mjs +22 -0
  4. package/dist/lib/browser/chunk-NQSC7HOE.mjs.map +7 -0
  5. package/dist/lib/browser/chunk-QCWEHHJW.mjs +24 -0
  6. package/dist/lib/browser/chunk-QCWEHHJW.mjs.map +7 -0
  7. package/dist/lib/browser/index.mjs +539 -98
  8. package/dist/lib/browser/index.mjs.map +4 -4
  9. package/dist/lib/browser/meta.json +1 -1
  10. package/dist/lib/browser/packlets/diagnostics/browser-diagnostics-broadcast.mjs +93 -0
  11. package/dist/lib/browser/packlets/diagnostics/browser-diagnostics-broadcast.mjs.map +7 -0
  12. package/dist/lib/browser/packlets/diagnostics/diagnostics-broadcast.mjs +11 -0
  13. package/dist/lib/browser/packlets/diagnostics/diagnostics-broadcast.mjs.map +7 -0
  14. package/dist/lib/browser/packlets/locks/browser.mjs +126 -0
  15. package/dist/lib/browser/packlets/locks/browser.mjs.map +7 -0
  16. package/dist/lib/browser/packlets/locks/node.mjs +66 -0
  17. package/dist/lib/browser/packlets/locks/node.mjs.map +7 -0
  18. package/dist/lib/browser/testing/index.mjs +45 -26
  19. package/dist/lib/browser/testing/index.mjs.map +3 -3
  20. package/dist/lib/node-esm/chunk-2SZHAWBN.mjs +24 -0
  21. package/dist/lib/node-esm/chunk-2SZHAWBN.mjs.map +7 -0
  22. package/dist/lib/node-esm/{chunk-TMEG7JOG.mjs → chunk-GFT7MAQE.mjs} +3764 -2695
  23. package/dist/lib/node-esm/chunk-GFT7MAQE.mjs.map +7 -0
  24. package/dist/lib/node-esm/chunk-PKEGMOQ4.mjs +22 -0
  25. package/dist/lib/node-esm/chunk-PKEGMOQ4.mjs.map +7 -0
  26. package/dist/lib/node-esm/index.mjs +539 -98
  27. package/dist/lib/node-esm/index.mjs.map +4 -4
  28. package/dist/lib/node-esm/meta.json +1 -1
  29. package/dist/lib/node-esm/packlets/diagnostics/browser-diagnostics-broadcast.mjs +93 -0
  30. package/dist/lib/node-esm/packlets/diagnostics/browser-diagnostics-broadcast.mjs.map +7 -0
  31. package/dist/lib/node-esm/packlets/diagnostics/diagnostics-broadcast.mjs +11 -0
  32. package/dist/lib/node-esm/packlets/diagnostics/diagnostics-broadcast.mjs.map +7 -0
  33. package/dist/lib/node-esm/packlets/locks/browser.mjs +126 -0
  34. package/dist/lib/node-esm/packlets/locks/browser.mjs.map +7 -0
  35. package/dist/lib/node-esm/packlets/locks/node.mjs +66 -0
  36. package/dist/lib/node-esm/packlets/locks/node.mjs.map +7 -0
  37. package/dist/lib/node-esm/testing/index.mjs +45 -26
  38. package/dist/lib/node-esm/testing/index.mjs.map +3 -3
  39. package/dist/types/src/index.d.ts +1 -0
  40. package/dist/types/src/index.d.ts.map +1 -1
  41. package/dist/types/src/packlets/agents/edge-agent-manager.d.ts +3 -2
  42. package/dist/types/src/packlets/agents/edge-agent-manager.d.ts.map +1 -1
  43. package/dist/types/src/packlets/agents/edge-agent-service.d.ts +3 -2
  44. package/dist/types/src/packlets/agents/edge-agent-service.d.ts.map +1 -1
  45. package/dist/types/src/packlets/devices/devices-service.d.ts.map +1 -1
  46. package/dist/types/src/packlets/devtools/devtools.d.ts +20 -20
  47. package/dist/types/src/packlets/devtools/devtools.d.ts.map +1 -1
  48. package/dist/types/src/packlets/devtools/feeds.d.ts +1 -1
  49. package/dist/types/src/packlets/devtools/feeds.d.ts.map +1 -1
  50. package/dist/types/src/packlets/devtools/network.d.ts +1 -1
  51. package/dist/types/src/packlets/devtools/network.d.ts.map +1 -1
  52. package/dist/types/src/packlets/diagnostics/browser-diagnostics-broadcast.d.ts +1 -1
  53. package/dist/types/src/packlets/diagnostics/browser-diagnostics-broadcast.d.ts.map +1 -1
  54. package/dist/types/src/packlets/diagnostics/diagnostics-broadcast.d.ts +1 -1
  55. package/dist/types/src/packlets/diagnostics/diagnostics-broadcast.d.ts.map +1 -1
  56. package/dist/types/src/packlets/diagnostics/diagnostics.d.ts +2 -3
  57. package/dist/types/src/packlets/diagnostics/diagnostics.d.ts.map +1 -1
  58. package/dist/types/src/packlets/diagnostics/index.d.ts +1 -1
  59. package/dist/types/src/packlets/diagnostics/index.d.ts.map +1 -1
  60. package/dist/types/src/packlets/identity/authenticator.d.ts +2 -2
  61. package/dist/types/src/packlets/identity/authenticator.d.ts.map +1 -1
  62. package/dist/types/src/packlets/identity/contacts-service.d.ts +1 -1
  63. package/dist/types/src/packlets/identity/contacts-service.d.ts.map +1 -1
  64. package/dist/types/src/packlets/identity/identity-manager.d.ts +7 -7
  65. package/dist/types/src/packlets/identity/identity-manager.d.ts.map +1 -1
  66. package/dist/types/src/packlets/identity/identity-recovery-manager.d.ts +7 -6
  67. package/dist/types/src/packlets/identity/identity-recovery-manager.d.ts.map +1 -1
  68. package/dist/types/src/packlets/identity/identity-service.d.ts +6 -10
  69. package/dist/types/src/packlets/identity/identity-service.d.ts.map +1 -1
  70. package/dist/types/src/packlets/identity/identity.d.ts +9 -12
  71. package/dist/types/src/packlets/identity/identity.d.ts.map +1 -1
  72. package/dist/types/src/packlets/invitations/device-invitation-protocol.d.ts +6 -5
  73. package/dist/types/src/packlets/invitations/device-invitation-protocol.d.ts.map +1 -1
  74. package/dist/types/src/packlets/invitations/edge-invitation-handler.d.ts +2 -2
  75. package/dist/types/src/packlets/invitations/edge-invitation-handler.d.ts.map +1 -1
  76. package/dist/types/src/packlets/invitations/index.d.ts +1 -1
  77. package/dist/types/src/packlets/invitations/index.d.ts.map +1 -1
  78. package/dist/types/src/packlets/invitations/invitation-guest-extenstion.d.ts.map +1 -1
  79. package/dist/types/src/packlets/invitations/invitation-host-extension.d.ts.map +1 -1
  80. package/dist/types/src/packlets/invitations/invitation-protocol.d.ts +8 -5
  81. package/dist/types/src/packlets/invitations/invitation-protocol.d.ts.map +1 -1
  82. package/dist/types/src/packlets/invitations/invitation-state.d.ts.map +1 -1
  83. package/dist/types/src/packlets/invitations/invitations-handler.d.ts +4 -4
  84. package/dist/types/src/packlets/invitations/invitations-handler.d.ts.map +1 -1
  85. package/dist/types/src/packlets/invitations/invitations-manager.d.ts +3 -3
  86. package/dist/types/src/packlets/invitations/invitations-manager.d.ts.map +1 -1
  87. package/dist/types/src/packlets/invitations/invitations-service.d.ts +4 -4
  88. package/dist/types/src/packlets/invitations/invitations-service.d.ts.map +1 -1
  89. package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts +5 -4
  90. package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts.map +1 -1
  91. package/dist/types/src/packlets/invitations/utils.d.ts.map +1 -1
  92. package/dist/types/src/packlets/locks/index.d.ts +2 -2
  93. package/dist/types/src/packlets/locks/index.d.ts.map +1 -1
  94. package/dist/types/src/packlets/logging/logging-service.d.ts +5 -1
  95. package/dist/types/src/packlets/logging/logging-service.d.ts.map +1 -1
  96. package/dist/types/src/packlets/network/network-service.d.ts +7 -6
  97. package/dist/types/src/packlets/network/network-service.d.ts.map +1 -1
  98. package/dist/types/src/packlets/services/client-rpc-server.d.ts +5 -5
  99. package/dist/types/src/packlets/services/client-rpc-server.d.ts.map +1 -1
  100. package/dist/types/src/packlets/services/feed-syncer.d.ts +59 -0
  101. package/dist/types/src/packlets/services/feed-syncer.d.ts.map +1 -0
  102. package/dist/types/src/packlets/services/feed-syncer.test.d.ts +2 -0
  103. package/dist/types/src/packlets/services/feed-syncer.test.d.ts.map +1 -0
  104. package/dist/types/src/packlets/services/platform.d.ts.map +1 -1
  105. package/dist/types/src/packlets/services/service-context.d.ts +14 -9
  106. package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
  107. package/dist/types/src/packlets/services/service-host.d.ts +21 -8
  108. package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
  109. package/dist/types/src/packlets/space-export/archive-format.d.ts +9 -0
  110. package/dist/types/src/packlets/space-export/archive-format.d.ts.map +1 -0
  111. package/dist/types/src/packlets/space-export/index.d.ts +4 -1
  112. package/dist/types/src/packlets/space-export/index.d.ts.map +1 -1
  113. package/dist/types/src/packlets/space-export/serialized-space-reader.d.ts +23 -0
  114. package/dist/types/src/packlets/space-export/serialized-space-reader.d.ts.map +1 -0
  115. package/dist/types/src/packlets/space-export/serialized-space-writer.d.ts +36 -0
  116. package/dist/types/src/packlets/space-export/serialized-space-writer.d.ts.map +1 -0
  117. package/dist/types/src/packlets/space-export/space-archive-reader.d.ts +9 -1
  118. package/dist/types/src/packlets/space-export/space-archive-reader.d.ts.map +1 -1
  119. package/dist/types/src/packlets/space-export/space-archive-writer.d.ts +8 -2
  120. package/dist/types/src/packlets/space-export/space-archive-writer.d.ts.map +1 -1
  121. package/dist/types/src/packlets/space-export/space-archive.test.d.ts +2 -0
  122. package/dist/types/src/packlets/space-export/space-archive.test.d.ts.map +1 -0
  123. package/dist/types/src/packlets/spaces/automerge-space-state.d.ts +1 -1
  124. package/dist/types/src/packlets/spaces/automerge-space-state.d.ts.map +1 -1
  125. package/dist/types/src/packlets/spaces/data-space-manager.d.ts +29 -17
  126. package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
  127. package/dist/types/src/packlets/spaces/data-space.d.ts +30 -13
  128. package/dist/types/src/packlets/spaces/data-space.d.ts.map +1 -1
  129. package/dist/types/src/packlets/spaces/edge-feed-replicator.d.ts +2 -2
  130. package/dist/types/src/packlets/spaces/edge-feed-replicator.d.ts.map +1 -1
  131. package/dist/types/src/packlets/spaces/genesis.d.ts +2 -1
  132. package/dist/types/src/packlets/spaces/genesis.d.ts.map +1 -1
  133. package/dist/types/src/packlets/spaces/notarization-plugin.d.ts +6 -6
  134. package/dist/types/src/packlets/spaces/notarization-plugin.d.ts.map +1 -1
  135. package/dist/types/src/packlets/spaces/spaces-service.d.ts +18 -8
  136. package/dist/types/src/packlets/spaces/spaces-service.d.ts.map +1 -1
  137. package/dist/types/src/packlets/storage/profile-archive.d.ts.map +1 -1
  138. package/dist/types/src/packlets/storage/storage.d.ts.map +1 -1
  139. package/dist/types/src/packlets/system/system-service.d.ts +1 -1
  140. package/dist/types/src/packlets/system/system-service.d.ts.map +1 -1
  141. package/dist/types/src/packlets/testing/invitation-utils.d.ts +6 -3
  142. package/dist/types/src/packlets/testing/invitation-utils.d.ts.map +1 -1
  143. package/dist/types/src/packlets/testing/test-builder.d.ts +8 -7
  144. package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
  145. package/dist/types/src/packlets/worker/worker-runtime.d.ts +41 -4
  146. package/dist/types/src/packlets/worker/worker-runtime.d.ts.map +1 -1
  147. package/dist/types/src/packlets/worker/worker-session.d.ts +2 -4
  148. package/dist/types/src/packlets/worker/worker-session.d.ts.map +1 -1
  149. package/dist/types/src/version.d.ts +1 -1
  150. package/dist/types/src/version.d.ts.map +1 -1
  151. package/dist/types/tsconfig.tsbuildinfo +1 -1
  152. package/package.json +72 -48
  153. package/src/index.ts +1 -0
  154. package/src/packlets/agents/edge-agent-manager.ts +10 -7
  155. package/src/packlets/agents/edge-agent-service.ts +17 -5
  156. package/src/packlets/devices/devices-service.test.ts +3 -3
  157. package/src/packlets/devices/devices-service.ts +2 -2
  158. package/src/packlets/devtools/devtools.ts +29 -29
  159. package/src/packlets/devtools/feeds.ts +2 -2
  160. package/src/packlets/devtools/network.ts +1 -1
  161. package/src/packlets/diagnostics/browser-diagnostics-broadcast.ts +1 -1
  162. package/src/packlets/diagnostics/diagnostics-broadcast.ts +1 -1
  163. package/src/packlets/diagnostics/diagnostics-collector.ts +1 -1
  164. package/src/packlets/diagnostics/diagnostics.ts +2 -3
  165. package/src/packlets/diagnostics/index.ts +1 -1
  166. package/src/packlets/identity/authenticator.node.test.ts +1 -1
  167. package/src/packlets/identity/authenticator.ts +3 -3
  168. package/src/packlets/identity/contacts-service.ts +2 -2
  169. package/src/packlets/identity/identity-manager.test.ts +8 -8
  170. package/src/packlets/identity/identity-manager.ts +23 -20
  171. package/src/packlets/identity/identity-recovery-manager.ts +22 -18
  172. package/src/packlets/identity/identity-service.test.ts +8 -28
  173. package/src/packlets/identity/identity-service.ts +13 -80
  174. package/src/packlets/identity/identity.test.ts +11 -11
  175. package/src/packlets/identity/identity.ts +17 -39
  176. package/src/packlets/invitations/device-invitation-protocol.test.ts +4 -4
  177. package/src/packlets/invitations/device-invitation-protocol.ts +8 -6
  178. package/src/packlets/invitations/edge-invitation-handler.ts +10 -6
  179. package/src/packlets/invitations/index.ts +1 -1
  180. package/src/packlets/invitations/invitation-guest-extenstion.ts +7 -5
  181. package/src/packlets/invitations/invitation-host-extension.ts +8 -6
  182. package/src/packlets/invitations/invitation-protocol.ts +8 -5
  183. package/src/packlets/invitations/invitation-state.ts +0 -10
  184. package/src/packlets/invitations/invitations-handler.test.ts +301 -292
  185. package/src/packlets/invitations/invitations-handler.ts +72 -16
  186. package/src/packlets/invitations/invitations-manager.ts +43 -18
  187. package/src/packlets/invitations/invitations-service.ts +10 -10
  188. package/src/packlets/invitations/space-invitation-protocol.test.ts +26 -25
  189. package/src/packlets/invitations/space-invitation-protocol.ts +13 -17
  190. package/src/packlets/invitations/utils.ts +1 -1
  191. package/src/packlets/locks/browser.ts +1 -1
  192. package/src/packlets/locks/index.ts +2 -2
  193. package/src/packlets/logging/logging-service.ts +8 -3
  194. package/src/packlets/logging/logging.test.ts +1 -1
  195. package/src/packlets/network/network-service.test.ts +3 -3
  196. package/src/packlets/network/network-service.ts +12 -10
  197. package/src/packlets/services/client-rpc-server.ts +20 -17
  198. package/src/packlets/services/feed-syncer.test.ts +340 -0
  199. package/src/packlets/services/feed-syncer.ts +337 -0
  200. package/src/packlets/services/platform.ts +7 -1
  201. package/src/packlets/services/service-context.test.ts +4 -3
  202. package/src/packlets/services/service-context.ts +145 -59
  203. package/src/packlets/services/service-host.test.ts +10 -9
  204. package/src/packlets/services/service-host.ts +85 -38
  205. package/src/packlets/services/service-registry.test.ts +1 -1
  206. package/src/packlets/space-export/archive-format.ts +42 -0
  207. package/src/packlets/space-export/index.ts +4 -1
  208. package/src/packlets/space-export/serialized-space-reader.ts +111 -0
  209. package/src/packlets/space-export/serialized-space-writer.ts +246 -0
  210. package/src/packlets/space-export/space-archive-reader.ts +65 -4
  211. package/src/packlets/space-export/space-archive-writer.ts +44 -6
  212. package/src/packlets/space-export/space-archive.test.ts +461 -0
  213. package/src/packlets/space-export/tar.test.ts +1 -1
  214. package/src/packlets/spaces/automerge-space-state.ts +1 -1
  215. package/src/packlets/spaces/data-space-manager.test.ts +79 -13
  216. package/src/packlets/spaces/data-space-manager.ts +131 -118
  217. package/src/packlets/spaces/data-space.ts +65 -39
  218. package/src/packlets/spaces/edge-feed-replicator.test.ts +4 -4
  219. package/src/packlets/spaces/edge-feed-replicator.ts +13 -11
  220. package/src/packlets/spaces/epoch-migrations.ts +5 -5
  221. package/src/packlets/spaces/genesis.ts +6 -1
  222. package/src/packlets/spaces/notarization-plugin.test.ts +3 -3
  223. package/src/packlets/spaces/notarization-plugin.ts +13 -12
  224. package/src/packlets/spaces/spaces-service.test.ts +20 -12
  225. package/src/packlets/spaces/spaces-service.ts +138 -38
  226. package/src/packlets/storage/profile-archive.ts +1 -1
  227. package/src/packlets/storage/storage.ts +7 -8
  228. package/src/packlets/system/system-service.test.ts +1 -1
  229. package/src/packlets/system/system-service.ts +4 -4
  230. package/src/packlets/testing/invitation-utils.ts +11 -7
  231. package/src/packlets/testing/test-builder.ts +39 -13
  232. package/src/packlets/worker/worker-runtime.ts +180 -17
  233. package/src/packlets/worker/worker-session.ts +15 -21
  234. package/src/version.ts +1 -1
  235. package/dist/lib/browser/chunk-ERQJUBAW.mjs.map +0 -7
  236. package/dist/lib/node-esm/chunk-TMEG7JOG.mjs.map +0 -7
  237. package/dist/types/src/packlets/identity/default-space-state-machine.d.ts +0 -19
  238. package/dist/types/src/packlets/identity/default-space-state-machine.d.ts.map +0 -1
  239. package/src/packlets/identity/default-space-state-machine.ts +0 -44
@@ -0,0 +1,337 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import type * as SqlClient from '@effect/sql/SqlClient';
6
+ import { Encoder, decode as cborXdecode } from 'cbor-x';
7
+ import * as Effect from 'effect/Effect';
8
+ import * as Schema from 'effect/Schema';
9
+
10
+ import { AsyncTask, scheduleTask } from '@dxos/async';
11
+ import { Context, Resource } from '@dxos/context';
12
+ import { type EdgeConnection, MessageSchema } from '@dxos/edge-client';
13
+ import { RuntimeProvider } from '@dxos/effect';
14
+ import { type FeedStore, SyncClient } from '@dxos/feed';
15
+ import { invariant } from '@dxos/invariant';
16
+ import { SpaceId } from '@dxos/keys';
17
+ import { FeedProtocol } from '@dxos/protocols';
18
+ import { EdgeService } from '@dxos/protocols';
19
+ import { createBuf } from '@dxos/protocols/buf';
20
+ import { type Message as RouterMessage } from '@dxos/protocols/buf/dxos/edge/messenger_pb';
21
+ import type { SqlTransaction } from '@dxos/sql-sqlite';
22
+ import { bufferToArray } from '@dxos/util';
23
+
24
+ const encoder = new Encoder({ tagUint8Array: false, useRecords: false });
25
+
26
+ const DEFAULT_MESSAGE_BLOCKS_LIMIT = 50;
27
+ const DEFAULT_SYNC_CONCURRENCY = 5;
28
+ const DEFAULT_POLLING_INTERVAL = 5_000;
29
+ const DEFAULT_POLL_REQUEST_THROTTLE_MS = 250;
30
+ const MAX_BLOCKING_SYNC_ITERATIONS = 100;
31
+
32
+ interface FeedSyncerOptions {
33
+ runtime: RuntimeProvider.RuntimeProvider<SqlClient.SqlClient | SqlTransaction.SqlTransaction>;
34
+ feedStore: FeedStore;
35
+ edgeClient: EdgeConnection;
36
+ peerId: string;
37
+ getSpaceIds: () => SpaceId[];
38
+
39
+ /**
40
+ * Namespaces to sync.
41
+ */
42
+ syncNamespaces: string[];
43
+
44
+ /**
45
+ * Maximum number of blocks to sync in a single message.
46
+ * @default 50
47
+ */
48
+ messageBlocksLimit?: number;
49
+
50
+ /**
51
+ * Maximum number of spaces to sync concurrently.
52
+ * @default 5
53
+ */
54
+ syncConcurrency?: number;
55
+
56
+ /**
57
+ * Interval between full polls.
58
+ * @default 10 seconds
59
+ */
60
+ pollingInterval?: number;
61
+
62
+ /**
63
+ * Minimum delay between externally requested best-effort polls.
64
+ * @default 250 ms
65
+ */
66
+ pollRequestThrottleMs?: number;
67
+ }
68
+
69
+ export class FeedSyncer extends Resource {
70
+ readonly #syncNamespaces: string[];
71
+ readonly #messageBlocksLimit: number;
72
+ readonly #syncConcurrency: number;
73
+ readonly #pollingInterval: number;
74
+ readonly #pollRequestThrottleMs: number;
75
+
76
+ readonly #runtime: RuntimeProvider.RuntimeProvider<SqlClient.SqlClient | SqlTransaction.SqlTransaction>;
77
+ readonly #feedStore: FeedStore;
78
+ readonly #edgeClient: EdgeConnection;
79
+ readonly #syncClient: SyncClient;
80
+ readonly #getSpaceIds: () => SpaceId[];
81
+
82
+ #spacesToPoll = new Set<SpaceId>();
83
+ /** Last time full poll was completed. */
84
+ #lastFullPoll: number | null = null;
85
+ #throttledPollScheduled = false;
86
+ #lastRequestedPollAt: number | null = null;
87
+
88
+ constructor(options: FeedSyncerOptions) {
89
+ super();
90
+ this.#runtime = options.runtime;
91
+ this.#feedStore = options.feedStore;
92
+ this.#edgeClient = options.edgeClient;
93
+ this.#syncClient = new SyncClient({
94
+ peerId: options.peerId,
95
+ feedStore: options.feedStore,
96
+ sendMessage: this.#sendMessage.bind(this),
97
+ });
98
+ this.#getSpaceIds = options.getSpaceIds;
99
+ this.#syncNamespaces = options.syncNamespaces;
100
+ this.#messageBlocksLimit = options.messageBlocksLimit ?? DEFAULT_MESSAGE_BLOCKS_LIMIT;
101
+ this.#syncConcurrency = options.syncConcurrency ?? DEFAULT_SYNC_CONCURRENCY;
102
+ this.#pollingInterval = options.pollingInterval ?? DEFAULT_POLLING_INTERVAL;
103
+ this.#pollRequestThrottleMs = options.pollRequestThrottleMs ?? DEFAULT_POLL_REQUEST_THROTTLE_MS;
104
+ }
105
+
106
+ protected override async _open(): Promise<void> {
107
+ this._ctx.onDispose(
108
+ this.#edgeClient.onMessage((msg: RouterMessage) => {
109
+ if (!msg.serviceId) {
110
+ return;
111
+ }
112
+ const service = msg.serviceId.split(':')[0];
113
+ if (service !== EdgeService.QUEUE_REPLICATOR) {
114
+ return;
115
+ }
116
+ const handleMessageEffect = Effect.gen(this, function* () {
117
+ const decoded = yield* Effect.try({
118
+ try: () => cborXdecode(msg.payload!.value),
119
+ catch: (error) => new Error(`Failed to decode feed sync message: ${error}`),
120
+ });
121
+ const payload = yield* Schema.validate(FeedProtocol.ProtocolMessage)(decoded);
122
+ yield* this.#syncClient.handleMessage(payload);
123
+ });
124
+
125
+ void RuntimeProvider.runPromise(this.#runtime)(handleMessageEffect);
126
+ }),
127
+ );
128
+
129
+ this._ctx.onDispose(
130
+ // NOTE: This will fire immediately if the connection is already open.
131
+ this.#edgeClient.onReconnected(async () => {}),
132
+ );
133
+
134
+ this.#feedStore.onNewBlocks.on(this._ctx, () => {
135
+ this.#pushTask.schedule();
136
+ });
137
+
138
+ await this.#pollTask.open();
139
+ await this.#pushTask.open();
140
+
141
+ this.#resetSpacesToPoll();
142
+ this.#pollTask.schedule();
143
+ }
144
+
145
+ protected override async _close(): Promise<void> {
146
+ await this.#pollTask.close();
147
+ await this.#pushTask.close();
148
+ }
149
+
150
+ /**
151
+ * Schedules a best-effort pull without blocking the caller.
152
+ */
153
+ schedulePoll(): void {
154
+ this.#resetSpacesToPoll();
155
+ if (this.#throttledPollScheduled) {
156
+ return;
157
+ }
158
+
159
+ const now = Date.now();
160
+ const delay =
161
+ this.#lastRequestedPollAt == null
162
+ ? 0
163
+ : Math.max(this.#pollRequestThrottleMs - (now - this.#lastRequestedPollAt), 0);
164
+ this.#throttledPollScheduled = true;
165
+ scheduleTask(
166
+ this._ctx,
167
+ () => {
168
+ this.#throttledPollScheduled = false;
169
+ this.#lastRequestedPollAt = Date.now();
170
+ this.#pollTask.schedule();
171
+ },
172
+ delay,
173
+ );
174
+ }
175
+
176
+ /**
177
+ * Performs queue sync and blocks until there are no pending sync batches.
178
+ */
179
+ async syncBlocking(
180
+ ctx: Context,
181
+ {
182
+ spaceId,
183
+ subspaceTag,
184
+ shouldPush = true,
185
+ shouldPull = true,
186
+ }: {
187
+ spaceId: SpaceId;
188
+ subspaceTag: string;
189
+ shouldPush?: boolean;
190
+ shouldPull?: boolean;
191
+ },
192
+ ): Promise<void> {
193
+ invariant(SpaceId.isValid(spaceId));
194
+ invariant(FeedProtocol.isWellKnownNamespace(subspaceTag));
195
+ if (!shouldPush && !shouldPull) {
196
+ return;
197
+ }
198
+
199
+ await RuntimeProvider.runPromise(this.#runtime)(
200
+ Effect.gen(this, function* () {
201
+ let done = false;
202
+ let iterations = 0;
203
+ while (!done) {
204
+ done = true;
205
+ if (shouldPull) {
206
+ const pullResult = yield* this.#syncClient.pull(ctx, {
207
+ spaceId,
208
+ feedNamespace: subspaceTag,
209
+ limit: this.#messageBlocksLimit,
210
+ });
211
+ done &&= pullResult.done;
212
+ }
213
+
214
+ if (shouldPush) {
215
+ const pushResult = yield* this.#syncClient.push(ctx, {
216
+ spaceId,
217
+ feedNamespace: subspaceTag,
218
+ limit: this.#messageBlocksLimit,
219
+ });
220
+ done &&= pushResult.done;
221
+ }
222
+ iterations++;
223
+ if (iterations > MAX_BLOCKING_SYNC_ITERATIONS) {
224
+ throw new Error('Blocking sync exceeded max iterations.');
225
+ }
226
+ }
227
+ }),
228
+ );
229
+ }
230
+
231
+ #resetSpacesToPoll(): void {
232
+ this.#spacesToPoll.clear();
233
+ this.#getSpaceIds().forEach((spaceId) => {
234
+ this.#spacesToPoll.add(spaceId);
235
+ });
236
+ this.#lastFullPoll = Date.now();
237
+ }
238
+
239
+ #sendMessage(
240
+ ctx: Context,
241
+ message: FeedProtocol.QueryRequest | FeedProtocol.AppendRequest,
242
+ ): Effect.Effect<void, unknown, never> {
243
+ return Effect.gen(this, function* () {
244
+ const encoded = encoder.encode(message);
245
+ yield* Effect.tryPromise(async () =>
246
+ this.#edgeClient.send(
247
+ ctx,
248
+ createBuf(MessageSchema, {
249
+ source: {
250
+ identityKey: this.#edgeClient.identityKey,
251
+ peerKey: this.#edgeClient.peerKey,
252
+ },
253
+ serviceId: this.#getTargetServiceId(message),
254
+ payload: { value: bufferToArray(encoded) },
255
+ }),
256
+ ),
257
+ );
258
+ });
259
+ }
260
+
261
+ #getTargetServiceId(message: FeedProtocol.QueryRequest | FeedProtocol.AppendRequest): string {
262
+ // TODO(dmaretskyi): Perhaps in the future we will want to include the queue namespace here as well.
263
+ // This would require putting it at the top level of the message.
264
+ // For now, we let the edge router handle it.
265
+ return FeedProtocol.encodeServiceId(message.feedNamespace, message.spaceId as SpaceId);
266
+ }
267
+
268
+ readonly #pollTask = new AsyncTask(async () =>
269
+ Effect.gen(this, function* () {
270
+ yield* Effect.forEach(
271
+ this.#spacesToPoll,
272
+ (spaceId) =>
273
+ Effect.gen(this, function* () {
274
+ let doneForAllNamespaces = true;
275
+ for (const feedNamespace of this.#syncNamespaces) {
276
+ const { done } = yield* this.#syncClient.pull(this._ctx, {
277
+ spaceId,
278
+ feedNamespace,
279
+ limit: this.#messageBlocksLimit,
280
+ });
281
+ if (!done) {
282
+ doneForAllNamespaces = false;
283
+ }
284
+ }
285
+ if (doneForAllNamespaces) {
286
+ this.#spacesToPoll.delete(spaceId);
287
+ }
288
+ }),
289
+ { concurrency: this.#syncConcurrency },
290
+ );
291
+
292
+ // If its time to do a full poll, reset the spaces to poll and schedule the next poll immediately.
293
+ if (this.#lastFullPoll == null || Date.now() - this.#lastFullPoll > this.#pollingInterval) {
294
+ this.#resetSpacesToPoll();
295
+ this.#pollTask.schedule();
296
+ } else if (this.#spacesToPoll.size > 0) {
297
+ // If there are some spaces still syncing, poll them immediately.
298
+ this.#pollTask.schedule();
299
+ } else {
300
+ // All spaces sync, and there's time before the next full poll, schedule it later.
301
+ this.#resetSpacesToPoll();
302
+ scheduleTask(
303
+ this._ctx,
304
+ () => this.#pollTask.schedule(),
305
+ Math.max(this.#pollingInterval - (Date.now() - (this.#lastFullPoll ?? 0)), 0),
306
+ );
307
+ }
308
+ }).pipe(RuntimeProvider.runPromise(this.#runtime)),
309
+ );
310
+
311
+ readonly #pushTask = new AsyncTask(async () =>
312
+ Effect.gen(this, function* () {
313
+ yield* Effect.forEach(
314
+ this.#getSpaceIds(),
315
+ (spaceId) =>
316
+ Effect.gen(this, function* () {
317
+ let doneForAllNamespaces = true;
318
+ for (const feedNamespace of this.#syncNamespaces) {
319
+ const { done } = yield* this.#syncClient.push(this._ctx, {
320
+ spaceId,
321
+ feedNamespace,
322
+ limit: this.#messageBlocksLimit,
323
+ });
324
+ if (!done) {
325
+ doneForAllNamespaces = false;
326
+ }
327
+ }
328
+ if (!doneForAllNamespaces) {
329
+ // Keep pushing until all blocks are pushed.
330
+ this.#pushTask.schedule();
331
+ }
332
+ }),
333
+ { concurrency: this.#syncConcurrency },
334
+ );
335
+ }).pipe(RuntimeProvider.runPromise(this.#runtime)),
336
+ );
337
+ }
@@ -14,12 +14,18 @@ export const getPlatform = (): Platform => {
14
14
  userAgent,
15
15
  uptime: Math.floor((Date.now() - window.performance.timeOrigin) / 1_000),
16
16
  };
17
- } else {
17
+ } else if (typeof SharedWorkerGlobalScope !== 'undefined') {
18
18
  // Shared worker.
19
19
  return {
20
20
  type: Platform.PLATFORM_TYPE.SHARED_WORKER,
21
21
  uptime: Math.floor((Date.now() - performance.timeOrigin) / 1_000),
22
22
  };
23
+ } else {
24
+ // Dedicated worker.
25
+ return {
26
+ type: Platform.PLATFORM_TYPE.DEDICATED_WORKER,
27
+ uptime: Math.floor((Date.now() - performance.timeOrigin) / 1_000),
28
+ };
23
29
  }
24
30
  } else {
25
31
  // Node.
@@ -4,7 +4,8 @@
4
4
 
5
5
  import { describe, test } from 'vitest';
6
6
 
7
- import { MemorySignalManagerContext, MemorySignalManager } from '@dxos/messaging';
7
+ import { Context } from '@dxos/context';
8
+ import { MemorySignalManager, MemorySignalManagerContext } from '@dxos/messaging';
8
9
  import { Invitation } from '@dxos/protocols/proto/dxos/client/services';
9
10
  import { openAndClose } from '@dxos/test-utils';
10
11
 
@@ -19,7 +20,7 @@ describe('services/ServiceContext', () => {
19
20
  const device2 = await createOpenServiceContext(networkContext);
20
21
  await Promise.all(performInvitation({ host: device1, guest: device2, options: { kind: Invitation.Kind.DEVICE } }));
21
22
 
22
- const space1 = await device1.dataSpaceManager!.createSpace();
23
+ const space1 = await device1.dataSpaceManager!.createSpace(new Context());
23
24
  await device2.dataSpaceManager!.waitUntilSpaceReady(space1!.key);
24
25
  const space2 = await device2.dataSpaceManager!.spaces.get(space1.key);
25
26
  await space2!.inner.controlPipeline.state.waitUntilTimeframe(space1.inner.controlPipeline.state.timeframe);
@@ -35,7 +36,7 @@ describe('services/ServiceContext', () => {
35
36
 
36
37
  const identity2 = await createOpenServiceContext(networkContext);
37
38
  await identity2.createIdentity();
38
- const space1 = await identity2.dataSpaceManager!.createSpace();
39
+ const space1 = await identity2.dataSpaceManager!.createSpace(new Context());
39
40
  await Promise.all(
40
41
  performInvitation({
41
42
  host: identity2,