@dxos/client-services 0.8.4-main.c4373fc → 0.8.4-main.c85a9c8dae

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 (142) hide show
  1. package/dist/lib/browser/{chunk-HWGM5TYF.mjs → chunk-MQ6PWJ76.mjs} +1936 -2336
  2. package/dist/lib/browser/chunk-MQ6PWJ76.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 +522 -172
  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 +59 -76
  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-XUIY55CH.mjs → chunk-GUAL4U7S.mjs} +1468 -1737
  23. package/dist/lib/node-esm/chunk-GUAL4U7S.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 +522 -172
  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 +59 -76
  38. package/dist/lib/node-esm/testing/index.mjs.map +3 -3
  39. package/dist/types/src/packlets/agents/edge-agent-service.d.ts.map +1 -1
  40. package/dist/types/src/packlets/devtools/devtools.d.ts +2 -2
  41. package/dist/types/src/packlets/devtools/devtools.d.ts.map +1 -1
  42. package/dist/types/src/packlets/diagnostics/index.d.ts +1 -1
  43. package/dist/types/src/packlets/diagnostics/index.d.ts.map +1 -1
  44. package/dist/types/src/packlets/identity/authenticator.d.ts +2 -2
  45. package/dist/types/src/packlets/identity/authenticator.d.ts.map +1 -1
  46. package/dist/types/src/packlets/identity/default-space-state-machine.d.ts +2 -2
  47. package/dist/types/src/packlets/identity/default-space-state-machine.d.ts.map +1 -1
  48. package/dist/types/src/packlets/identity/identity-manager.d.ts +4 -4
  49. package/dist/types/src/packlets/identity/identity-manager.d.ts.map +1 -1
  50. package/dist/types/src/packlets/identity/identity-recovery-manager.d.ts +2 -2
  51. package/dist/types/src/packlets/identity/identity-recovery-manager.d.ts.map +1 -1
  52. package/dist/types/src/packlets/identity/identity.d.ts +2 -2
  53. package/dist/types/src/packlets/identity/identity.d.ts.map +1 -1
  54. package/dist/types/src/packlets/invitations/device-invitation-protocol.d.ts +4 -4
  55. package/dist/types/src/packlets/invitations/device-invitation-protocol.d.ts.map +1 -1
  56. package/dist/types/src/packlets/invitations/invitation-guest-extenstion.d.ts.map +1 -1
  57. package/dist/types/src/packlets/invitations/invitation-host-extension.d.ts.map +1 -1
  58. package/dist/types/src/packlets/invitations/invitation-protocol.d.ts +2 -3
  59. package/dist/types/src/packlets/invitations/invitation-protocol.d.ts.map +1 -1
  60. package/dist/types/src/packlets/invitations/invitations-handler.d.ts +4 -4
  61. package/dist/types/src/packlets/invitations/invitations-handler.d.ts.map +1 -1
  62. package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts +2 -2
  63. package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts.map +1 -1
  64. package/dist/types/src/packlets/locks/index.d.ts +1 -1
  65. package/dist/types/src/packlets/locks/index.d.ts.map +1 -1
  66. package/dist/types/src/packlets/logging/logging-service.d.ts +4 -0
  67. package/dist/types/src/packlets/logging/logging-service.d.ts.map +1 -1
  68. package/dist/types/src/packlets/services/client-rpc-server.d.ts +2 -2
  69. package/dist/types/src/packlets/services/client-rpc-server.d.ts.map +1 -1
  70. package/dist/types/src/packlets/services/feed-syncer.d.ts +59 -0
  71. package/dist/types/src/packlets/services/feed-syncer.d.ts.map +1 -0
  72. package/dist/types/src/packlets/services/feed-syncer.test.d.ts +2 -0
  73. package/dist/types/src/packlets/services/feed-syncer.test.d.ts.map +1 -0
  74. package/dist/types/src/packlets/services/platform.d.ts.map +1 -1
  75. package/dist/types/src/packlets/services/service-context.d.ts +12 -7
  76. package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
  77. package/dist/types/src/packlets/services/service-host.d.ts +19 -5
  78. package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
  79. package/dist/types/src/packlets/space-export/space-archive-writer.d.ts.map +1 -1
  80. package/dist/types/src/packlets/spaces/data-space-manager.d.ts +10 -5
  81. package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
  82. package/dist/types/src/packlets/spaces/data-space.d.ts +2 -2
  83. package/dist/types/src/packlets/spaces/data-space.d.ts.map +1 -1
  84. package/dist/types/src/packlets/spaces/edge-feed-replicator.d.ts +2 -2
  85. package/dist/types/src/packlets/spaces/edge-feed-replicator.d.ts.map +1 -1
  86. package/dist/types/src/packlets/spaces/notarization-plugin.d.ts +6 -6
  87. package/dist/types/src/packlets/spaces/notarization-plugin.d.ts.map +1 -1
  88. package/dist/types/src/packlets/spaces/spaces-service.d.ts.map +1 -1
  89. package/dist/types/src/packlets/testing/invitation-utils.d.ts +6 -3
  90. package/dist/types/src/packlets/testing/invitation-utils.d.ts.map +1 -1
  91. package/dist/types/src/packlets/testing/test-builder.d.ts +6 -5
  92. package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
  93. package/dist/types/src/packlets/worker/worker-runtime.d.ts +31 -4
  94. package/dist/types/src/packlets/worker/worker-runtime.d.ts.map +1 -1
  95. package/dist/types/src/packlets/worker/worker-session.d.ts +2 -2
  96. package/dist/types/src/packlets/worker/worker-session.d.ts.map +1 -1
  97. package/dist/types/src/version.d.ts +1 -1
  98. package/dist/types/src/version.d.ts.map +1 -1
  99. package/dist/types/tsconfig.tsbuildinfo +1 -1
  100. package/package.json +70 -48
  101. package/src/packlets/agents/edge-agent-service.ts +11 -1
  102. package/src/packlets/devices/devices-service.ts +1 -1
  103. package/src/packlets/devtools/devtools.ts +2 -2
  104. package/src/packlets/diagnostics/index.ts +1 -1
  105. package/src/packlets/identity/authenticator.ts +2 -2
  106. package/src/packlets/identity/default-space-state-machine.ts +2 -2
  107. package/src/packlets/identity/identity-manager.ts +6 -6
  108. package/src/packlets/identity/identity-recovery-manager.ts +2 -2
  109. package/src/packlets/identity/identity.test.ts +4 -4
  110. package/src/packlets/identity/identity.ts +2 -2
  111. package/src/packlets/invitations/device-invitation-protocol.ts +5 -5
  112. package/src/packlets/invitations/invitation-guest-extenstion.ts +6 -4
  113. package/src/packlets/invitations/invitation-host-extension.ts +6 -4
  114. package/src/packlets/invitations/invitation-protocol.ts +2 -3
  115. package/src/packlets/invitations/invitations-handler.ts +7 -7
  116. package/src/packlets/invitations/space-invitation-protocol.ts +7 -13
  117. package/src/packlets/locks/index.ts +1 -1
  118. package/src/packlets/logging/logging-service.ts +5 -1
  119. package/src/packlets/services/client-rpc-server.ts +4 -4
  120. package/src/packlets/services/feed-syncer.test.ts +340 -0
  121. package/src/packlets/services/feed-syncer.ts +330 -0
  122. package/src/packlets/services/platform.ts +7 -1
  123. package/src/packlets/services/service-context.ts +53 -21
  124. package/src/packlets/services/service-host.ts +53 -16
  125. package/src/packlets/space-export/space-archive-reader.ts +1 -1
  126. package/src/packlets/space-export/space-archive-writer.ts +3 -1
  127. package/src/packlets/spaces/data-space-manager.ts +56 -21
  128. package/src/packlets/spaces/data-space.ts +10 -6
  129. package/src/packlets/spaces/edge-feed-replicator.test.ts +1 -1
  130. package/src/packlets/spaces/edge-feed-replicator.ts +3 -3
  131. package/src/packlets/spaces/epoch-migrations.ts +2 -2
  132. package/src/packlets/spaces/notarization-plugin.test.ts +2 -2
  133. package/src/packlets/spaces/notarization-plugin.ts +8 -8
  134. package/src/packlets/spaces/spaces-service.ts +10 -7
  135. package/src/packlets/storage/storage.ts +4 -4
  136. package/src/packlets/testing/invitation-utils.ts +7 -4
  137. package/src/packlets/testing/test-builder.ts +36 -10
  138. package/src/packlets/worker/worker-runtime.ts +149 -11
  139. package/src/packlets/worker/worker-session.ts +8 -8
  140. package/src/version.ts +1 -1
  141. package/dist/lib/browser/chunk-HWGM5TYF.mjs.map +0 -7
  142. package/dist/lib/node-esm/chunk-XUIY55CH.mjs.map +0 -7
@@ -2,6 +2,8 @@
2
2
  // Copyright 2022 DXOS.org
3
3
  //
4
4
 
5
+ import type * as SqlClient from '@effect/sql/SqlClient';
6
+
5
7
  import { Mutex, Trigger } from '@dxos/async';
6
8
  import { Context, Resource } from '@dxos/context';
7
9
  import { type CredentialProcessor, getCredentialAssertion } from '@dxos/credentials';
@@ -16,20 +18,23 @@ import {
16
18
  } from '@dxos/echo-pipeline';
17
19
  import { createChainEdgeIdentity, createEphemeralEdgeIdentity } from '@dxos/edge-client';
18
20
  import type { EdgeConnection, EdgeHttpClient, EdgeIdentity } from '@dxos/edge-client';
21
+ import { type RuntimeProvider } from '@dxos/effect';
19
22
  import { FeedFactory, FeedStore } from '@dxos/feed-store';
20
23
  import { invariant } from '@dxos/invariant';
21
24
  import { Keyring } from '@dxos/keyring';
22
- import { PublicKey } from '@dxos/keys';
25
+ import { PublicKey, type SpaceId } from '@dxos/keys';
23
26
  import { type LevelDB } from '@dxos/kv-store';
24
27
  import { log } from '@dxos/log';
25
28
  import { type SignalManager } from '@dxos/messaging';
26
29
  import { type SwarmNetworkManager } from '@dxos/network-manager';
27
30
  import { InvalidStorageVersionError, STORAGE_VERSION, trace } from '@dxos/protocols';
31
+ import { FeedProtocol } from '@dxos/protocols';
28
32
  import { Invitation } from '@dxos/protocols/proto/dxos/client/services';
29
33
  import { type Runtime } from '@dxos/protocols/proto/dxos/config';
30
34
  import type { FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
31
35
  import { type Credential, type ProfileDocument } from '@dxos/protocols/proto/dxos/halo/credentials';
32
36
  import { type Storage } from '@dxos/random-access-storage';
37
+ import type * as SqlTransaction from '@dxos/sql-sqlite/SqlTransaction';
33
38
  import { BlobStore } from '@dxos/teleport-extension-object-sync';
34
39
  import { trace as Trace } from '@dxos/tracing';
35
40
  import { safeInstanceof } from '@dxos/util';
@@ -38,26 +43,28 @@ import { EdgeAgentManager } from '../agents';
38
43
  import {
39
44
  type CreateIdentityOptions,
40
45
  IdentityManager,
41
- type IdentityManagerParams,
42
- type JoinIdentityParams,
46
+ type IdentityManagerProps,
47
+ type JoinIdentityProps,
43
48
  } from '../identity';
44
49
  import { EdgeIdentityRecoveryManager } from '../identity/identity-recovery-manager';
45
50
  import {
46
51
  DeviceInvitationProtocol,
47
- type InvitationConnectionParams,
52
+ type InvitationConnectionProps,
48
53
  type InvitationProtocol,
49
54
  InvitationsHandler,
50
55
  InvitationsManager,
51
56
  SpaceInvitationProtocol,
52
57
  } from '../invitations';
53
- import { DataSpaceManager, type DataSpaceManagerRuntimeParams, type SigningContext } from '../spaces';
58
+ import { DataSpaceManager, type DataSpaceManagerRuntimeProps, type SigningContext } from '../spaces';
59
+
60
+ import { FeedSyncer } from './feed-syncer';
54
61
 
55
- export type ServiceContextRuntimeParams = Pick<
56
- IdentityManagerParams,
62
+ export type ServiceContextRuntimeProps = Pick<
63
+ IdentityManagerProps,
57
64
  'devicePresenceOfflineTimeout' | 'devicePresenceAnnounceInterval'
58
65
  > &
59
- DataSpaceManagerRuntimeParams & {
60
- invitationConnectionDefaultParams?: InvitationConnectionParams;
66
+ DataSpaceManagerRuntimeProps & {
67
+ invitationConnectionDefaultProps?: InvitationConnectionProps;
61
68
  disableP2pReplication?: boolean;
62
69
  enableVectorIndexing?: boolean;
63
70
  };
@@ -84,6 +91,7 @@ export class ServiceContext extends Resource {
84
91
  public readonly echoHost: EchoHost;
85
92
  private readonly _meshReplicator?: MeshEchoReplicator = undefined;
86
93
  private readonly _echoEdgeReplicator?: EchoEdgeReplicator = undefined;
94
+ private readonly _feedSyncer?: FeedSyncer = undefined;
87
95
 
88
96
  // Initialized after identity is initialized.
89
97
  public dataSpaceManager?: DataSpaceManager;
@@ -105,11 +113,15 @@ export class ServiceContext extends Resource {
105
113
  public readonly signalManager: SignalManager,
106
114
  private readonly _edgeConnection: EdgeConnection | undefined,
107
115
  private readonly _edgeHttpClient: EdgeHttpClient | undefined,
108
- public readonly _runtimeParams?: ServiceContextRuntimeParams,
116
+ private readonly _runtime: RuntimeProvider.RuntimeProvider<SqlClient.SqlClient | SqlTransaction.SqlTransaction>,
117
+ public readonly _runtimeProps?: ServiceContextRuntimeProps,
109
118
  private readonly _edgeFeatures?: Runtime.Client.EdgeFeatures,
110
119
  ) {
111
120
  super();
112
121
 
122
+ log('runtimeProps', this._runtimeProps);
123
+ log('edgeFeatures', this._edgeFeatures);
124
+
113
125
  // TODO(burdon): Move strings to constants.
114
126
  this.metadataStore = new MetadataStore(storage.createDirectory('metadata'));
115
127
  this.blobStore = new BlobStore(storage.createDirectory('blobs'));
@@ -131,7 +143,7 @@ export class ServiceContext extends Resource {
131
143
  networkManager: this.networkManager,
132
144
  blobStore: this.blobStore,
133
145
  metadataStore: this.metadataStore,
134
- disableP2pReplication: this._runtimeParams?.disableP2pReplication,
146
+ disableP2pReplication: this._runtimeProps?.disableP2pReplication,
135
147
  });
136
148
 
137
149
  this.identityManager = new IdentityManager({
@@ -139,8 +151,8 @@ export class ServiceContext extends Resource {
139
151
  keyring: this.keyring,
140
152
  feedStore: this.feedStore,
141
153
  spaceManager: this.spaceManager,
142
- devicePresenceOfflineTimeout: this._runtimeParams?.devicePresenceOfflineTimeout,
143
- devicePresenceAnnounceInterval: this._runtimeParams?.devicePresenceAnnounceInterval,
154
+ devicePresenceOfflineTimeout: this._runtimeProps?.devicePresenceOfflineTimeout,
155
+ devicePresenceAnnounceInterval: this._runtimeProps?.devicePresenceAnnounceInterval,
144
156
  edgeConnection: this._edgeConnection,
145
157
  edgeFeatures: this._edgeFeatures,
146
158
  });
@@ -156,17 +168,21 @@ export class ServiceContext extends Resource {
156
168
  kv: this.level,
157
169
  peerIdProvider: () => this.identityManager.identity?.deviceKey?.toHex(),
158
170
  getSpaceKeyByRootDocumentId: (documentId) => this.spaceManager.findSpaceByRootDocumentId(documentId)?.key,
159
- indexing: {
160
- vector: this._runtimeParams?.enableVectorIndexing,
171
+ runtime: this._runtime,
172
+ syncQueue: async (request) => {
173
+ return this._feedSyncer?.syncBlocking({
174
+ spaceId: request.spaceId as SpaceId,
175
+ subspaceTag: request.subspaceTag,
176
+ shouldPush: request.shouldPush,
177
+ shouldPull: request.shouldPull,
178
+ });
161
179
  },
162
180
  });
163
181
 
164
- this._meshReplicator = new MeshEchoReplicator();
165
-
166
182
  this.invitations = new InvitationsHandler(
167
183
  this.networkManager, //
168
184
  this._edgeHttpClient,
169
- _runtimeParams?.invitationConnectionDefaultParams,
185
+ _runtimeProps?.invitationConnectionDefaultProps,
170
186
  );
171
187
  this.invitationsManager = new InvitationsManager(
172
188
  this.invitations,
@@ -186,7 +202,7 @@ export class ServiceContext extends Resource {
186
202
  ),
187
203
  );
188
204
 
189
- if (!this._runtimeParams?.disableP2pReplication) {
205
+ if (!this._runtimeProps?.disableP2pReplication) {
190
206
  this._meshReplicator = new MeshEchoReplicator();
191
207
  }
192
208
  if (this._edgeConnection && this._edgeFeatures?.echoReplicator && this._edgeHttpClient) {
@@ -195,6 +211,17 @@ export class ServiceContext extends Resource {
195
211
  edgeHttpClient: this._edgeHttpClient,
196
212
  });
197
213
  }
214
+
215
+ if (this.echoHost.feedStore && this._edgeConnection) {
216
+ this._feedSyncer = new FeedSyncer({
217
+ runtime: this._runtime,
218
+ feedStore: this.echoHost.feedStore,
219
+ edgeClient: this._edgeConnection,
220
+ peerId: this.identityManager.identity?.deviceKey?.toHex() ?? '',
221
+ getSpaceIds: () => this.echoHost!.spaceIds,
222
+ syncNamespaces: [FeedProtocol.WellKnownNamespaces.data, FeedProtocol.WellKnownNamespaces.trace],
223
+ });
224
+ }
198
225
  }
199
226
 
200
227
  @Trace.span()
@@ -229,6 +256,8 @@ export class ServiceContext extends Resource {
229
256
  await this._initialize(ctx);
230
257
  }
231
258
 
259
+ await this._feedSyncer?.open();
260
+
232
261
  const loadedInvitations = await this.invitationsManager.loadPersistentInvitations();
233
262
  log('loaded persistent invitations', { count: loadedInvitations.invitations?.length });
234
263
 
@@ -238,6 +267,9 @@ export class ServiceContext extends Resource {
238
267
 
239
268
  protected override async _close(ctx: Context): Promise<void> {
240
269
  log('closing...');
270
+
271
+ await this._feedSyncer?.close();
272
+
241
273
  if (this._deviceSpaceSync && this.identityManager.identity) {
242
274
  await this.identityManager.identity.space.spaceState.removeCredentialProcessor(this._deviceSpaceSync);
243
275
  }
@@ -283,7 +315,7 @@ export class ServiceContext extends Resource {
283
315
  }
284
316
  }
285
317
 
286
- private async _acceptIdentity(params: JoinIdentityParams) {
318
+ private async _acceptIdentity(params: JoinIdentityProps) {
287
319
  const { identity, identityRecord } = await this.identityManager.prepareIdentity(params);
288
320
  await this._setNetworkIdentity({ deviceCredential: params.authorizedDeviceCredential! });
289
321
  await identity.joinNetwork();
@@ -327,7 +359,7 @@ export class ServiceContext extends Resource {
327
359
  edgeHttpClient: this._edgeHttpClient,
328
360
  echoEdgeReplicator: this._echoEdgeReplicator,
329
361
  meshReplicator: this._meshReplicator,
330
- runtimeParams: this._runtimeParams as DataSpaceManagerRuntimeParams,
362
+ runtimeProps: this._runtimeProps as DataSpaceManagerRuntimeProps,
331
363
  edgeFeatures: this._edgeFeatures,
332
364
  });
333
365
  await this.dataSpaceManager.open();
@@ -2,11 +2,15 @@
2
2
  // Copyright 2021 DXOS.org
3
3
  //
4
4
 
5
+ import * as SqlClient from '@effect/sql/SqlClient';
6
+ import * as Effect from 'effect/Effect';
7
+
5
8
  import { Event, synchronized } from '@dxos/async';
6
9
  import { type ClientServices, clientServiceBundle } from '@dxos/client-protocol';
7
10
  import { type Config } from '@dxos/config';
8
11
  import { Context } from '@dxos/context';
9
12
  import { EdgeClient, type EdgeConnection, EdgeHttpClient, createStubEdgeIdentity } from '@dxos/edge-client';
13
+ import { RuntimeProvider } from '@dxos/effect';
10
14
  import { invariant } from '@dxos/invariant';
11
15
  import { PublicKey } from '@dxos/keys';
12
16
  import { type LevelDB } from '@dxos/kv-store';
@@ -21,6 +25,8 @@ import {
21
25
  import { trace } from '@dxos/protocols';
22
26
  import { SystemStatus } from '@dxos/protocols/proto/dxos/client/services';
23
27
  import { type Storage } from '@dxos/random-access-storage';
28
+ import * as SqlExport from '@dxos/sql-sqlite/SqlExport';
29
+ import type * as SqlTransaction from '@dxos/sql-sqlite/SqlTransaction';
24
30
  import { TRACE_PROCESSOR, trace as Trace } from '@dxos/tracing';
25
31
  import { WebsocketRpcClient } from '@dxos/websocket-rpc';
26
32
 
@@ -42,10 +48,10 @@ import { SpacesServiceImpl } from '../spaces';
42
48
  import { createLevel, createStorageObjects } from '../storage';
43
49
  import { SystemServiceImpl } from '../system';
44
50
 
45
- import { ServiceContext, type ServiceContextRuntimeParams } from './service-context';
51
+ import { ServiceContext, type ServiceContextRuntimeProps } from './service-context';
46
52
  import { ServiceRegistry } from './service-registry';
47
53
 
48
- export type ClientServicesHostParams = {
54
+ export type ClientServicesHostProps = {
49
55
  /**
50
56
  * Can be omitted if `initialize` is later called.
51
57
  */
@@ -57,7 +63,8 @@ export type ClientServicesHostParams = {
57
63
  level?: LevelDB;
58
64
  lockKey?: string;
59
65
  callbacks?: ClientServicesHostCallbacks;
60
- runtimeParams?: ServiceContextRuntimeParams;
66
+ runtime: RuntimeProvider.RuntimeProvider<SqlClient.SqlClient | SqlExport.SqlExport | SqlTransaction.SqlTransaction>;
67
+ runtimeProps?: ServiceContextRuntimeProps;
61
68
  };
62
69
 
63
70
  export type ClientServicesHostCallbacks = {
@@ -95,7 +102,10 @@ export class ClientServicesHost {
95
102
  private _edgeHttpClient?: EdgeHttpClient = undefined;
96
103
 
97
104
  private _serviceContext!: ServiceContext;
98
- private readonly _runtimeParams: ServiceContextRuntimeParams;
105
+ private readonly _runtime: RuntimeProvider.RuntimeProvider<
106
+ SqlClient.SqlClient | SqlExport.SqlExport | SqlTransaction.SqlTransaction
107
+ >;
108
+ private readonly _runtimeProps: ServiceContextRuntimeProps;
99
109
  private diagnosticsBroadcastHandler: CollectDiagnosticsBroadcastHandler;
100
110
 
101
111
  @Trace.info()
@@ -116,20 +126,14 @@ export class ClientServicesHost {
116
126
  // TODO(wittjosiah): Turn this on by default.
117
127
  lockKey,
118
128
  callbacks,
119
- runtimeParams,
120
- }: ClientServicesHostParams = {}) {
129
+ runtime,
130
+ runtimeProps,
131
+ }: ClientServicesHostProps) {
121
132
  this._storage = storage;
122
133
  this._level = level;
123
134
  this._callbacks = callbacks;
124
- this._runtimeParams = runtimeParams ?? {};
125
-
126
- if (this._runtimeParams.disableP2pReplication === undefined) {
127
- this._runtimeParams.disableP2pReplication = config?.get('runtime.client.disableP2pReplication', false);
128
- }
129
-
130
- if (this._runtimeParams.enableVectorIndexing === undefined) {
131
- this._runtimeParams.enableVectorIndexing = config?.get('runtime.client.enableVectorIndexing', false);
132
- }
135
+ this._runtime = runtime;
136
+ this._runtimeProps = runtimeProps ?? {};
133
137
 
134
138
  if (config) {
135
139
  this.initialize({ config, transportFactory, signalManager });
@@ -200,6 +204,30 @@ export class ClientServicesHost {
200
204
  return this._serviceRegistry.services;
201
205
  }
202
206
 
207
+ /**
208
+ * Debugging util.
209
+ */
210
+ async exportSqliteDatabase(): Promise<Uint8Array> {
211
+ return await RuntimeProvider.runPromise(this._runtime)(
212
+ Effect.gen(function* () {
213
+ const sql = yield* SqlExport.SqlExport;
214
+ return yield* sql.export;
215
+ }),
216
+ );
217
+ }
218
+
219
+ /**
220
+ * Debugging util.
221
+ */
222
+ async runSqliteQuery(query: string, params?: any[]): Promise<readonly Record<string, unknown>[]> {
223
+ return await RuntimeProvider.runPromise(this._runtime)(
224
+ Effect.gen(function* () {
225
+ const sql = yield* SqlClient.SqlClient;
226
+ return yield* sql`${sql.unsafe(query, params)}`;
227
+ }),
228
+ );
229
+ }
230
+
203
231
  /**
204
232
  * Initialize the service host with the config.
205
233
  * Config can also be provided in the constructor.
@@ -210,6 +238,13 @@ export class ClientServicesHost {
210
238
  log('initializing...');
211
239
 
212
240
  if (config) {
241
+ if (this._runtimeProps.disableP2pReplication === undefined) {
242
+ this._runtimeProps.disableP2pReplication = config?.get('runtime.client.disableP2pReplication', false);
243
+ }
244
+ if (this._runtimeProps.enableVectorIndexing === undefined) {
245
+ this._runtimeProps.enableVectorIndexing = config?.get('runtime.client.enableVectorIndexing', false);
246
+ }
247
+
213
248
  invariant(!this._config, 'config already set');
214
249
  this._config = config;
215
250
  if (!this._storage) {
@@ -291,7 +326,8 @@ export class ClientServicesHost {
291
326
  this._signalManager,
292
327
  this._edgeConnection,
293
328
  this._edgeHttpClient,
294
- this._runtimeParams,
329
+ this._runtime,
330
+ this._runtimeProps,
295
331
  this._config.get('runtime.client.edgeFeatures'),
296
332
  );
297
333
 
@@ -335,6 +371,7 @@ export class ClientServicesHost {
335
371
 
336
372
  DataService: this._serviceContext.echoHost.dataService,
337
373
  QueryService: this._serviceContext.echoHost.queryService,
374
+ QueueService: this._serviceContext.echoHost.queuesService,
338
375
 
339
376
  NetworkService: new NetworkServiceImpl(
340
377
  this._serviceContext.networkManager,
@@ -29,6 +29,6 @@ export const extractSpaceArchive = async (archive: SpaceArchive): Promise<Extrac
29
29
  documents[documentId] = entry.content ?? failedInvariant();
30
30
  }
31
31
 
32
- log.info('extracted space archive', { metadata, documents });
32
+ log('extracted space archive', { metadata, documents });
33
33
  return { metadata, documents };
34
34
  };
@@ -9,6 +9,7 @@ import { assertArgument, assertState } from '@dxos/invariant';
9
9
  import type { IdentityDid, SpaceId } from '@dxos/keys';
10
10
  import { SpaceArchiveFileStructure, type SpaceArchiveMetadata, SpaceArchiveVersion } from '@dxos/protocols';
11
11
  import type { SpaceArchive } from '@dxos/protocols/proto/dxos/client/services';
12
+ import { createFilename } from '@dxos/util';
12
13
 
13
14
  export type SpaceArchiveBeginProps = {
14
15
  spaceId?: SpaceId;
@@ -59,6 +60,7 @@ export class SpaceArchiveWriter extends Resource {
59
60
  async finish(): Promise<SpaceArchive> {
60
61
  assertState(this._archive, 'Not open');
61
62
  assertState(this._meta, 'Not started');
63
+ assertState(this._meta.spaceId, 'No space ID set');
62
64
  assertState(this._currentRootUrl, 'No root URL set');
63
65
 
64
66
  const metadata: SpaceArchiveMetadata = {
@@ -76,7 +78,7 @@ export class SpaceArchiveWriter extends Resource {
76
78
  const binary = this._archive.toUint8Array();
77
79
 
78
80
  return {
79
- filename: `${this._meta.spaceId}.tar`,
81
+ filename: createFilename({ parts: [this._meta.spaceId], ext: 'tar' }),
80
82
  contents: binary,
81
83
  };
82
84
  }
@@ -6,7 +6,7 @@ import { type Doc } from '@automerge/automerge';
6
6
  import { type AutomergeUrl, type DocHandle, type DocumentId, interpretAsDocumentId } from '@automerge/automerge-repo';
7
7
 
8
8
  import { Event, synchronized, trackLeaks } from '@dxos/async';
9
- import { PropertiesType, TYPE_PROPERTIES } from '@dxos/client-protocol';
9
+ import { SpaceProperties } from '@dxos/client-protocol';
10
10
  import { Context, LifecycleState, Resource, cancelWithContext } from '@dxos/context';
11
11
  import {
12
12
  type CredentialSigner,
@@ -15,14 +15,14 @@ import {
15
15
  createAdmissionCredentials,
16
16
  getCredentialAssertion,
17
17
  } from '@dxos/credentials';
18
- import { ObjectId, getTypeReference } from '@dxos/echo/internal';
18
+ import { Type } from '@dxos/echo';
19
+ import { getSchemaDXN } from '@dxos/echo/internal';
19
20
  import {
20
21
  AuthStatus,
21
22
  CredentialServerExtension,
22
23
  DatabaseRoot,
23
24
  type EchoEdgeReplicator,
24
25
  type EchoHost,
25
- FIND_PARAMS,
26
26
  type MeshEchoReplicator,
27
27
  type MetadataStore,
28
28
  type Space,
@@ -33,16 +33,16 @@ import {
33
33
  } from '@dxos/echo-pipeline';
34
34
  import {
35
35
  type DatabaseDirectory,
36
+ EncodedReference,
36
37
  type ObjectStructure,
37
38
  SpaceDocVersion,
38
39
  createIdFromSpaceKey,
39
- encodeReference,
40
40
  } from '@dxos/echo-protocol';
41
41
  import type { EdgeConnection, EdgeHttpClient } from '@dxos/edge-client';
42
42
  import { type FeedStore, writeMessages } from '@dxos/feed-store';
43
43
  import { assertArgument, assertState, failedInvariant, invariant } from '@dxos/invariant';
44
44
  import { type Keyring } from '@dxos/keyring';
45
- import { PublicKey, type SpaceId } from '@dxos/keys';
45
+ import { ObjectId, PublicKey, type SpaceId } from '@dxos/keys';
46
46
  import { log } from '@dxos/log';
47
47
  import { AlreadyJoinedError, trace as Trace } from '@dxos/protocols';
48
48
  import { Invitation, SpaceState } from '@dxos/protocols/proto/dxos/client/services';
@@ -104,7 +104,7 @@ export type AdmitMemberOptions = {
104
104
  delegationCredentialId?: PublicKey;
105
105
  };
106
106
 
107
- export type DataSpaceManagerParams = {
107
+ export type DataSpaceManagerProps = {
108
108
  spaceManager: SpaceManager;
109
109
  metadataStore: MetadataStore;
110
110
  keyring: Keyring;
@@ -116,15 +116,20 @@ export type DataSpaceManagerParams = {
116
116
  edgeHttpClient?: EdgeHttpClient;
117
117
  meshReplicator?: MeshEchoReplicator;
118
118
  echoEdgeReplicator?: EchoEdgeReplicator;
119
- runtimeParams?: DataSpaceManagerRuntimeParams;
119
+ runtimeProps?: DataSpaceManagerRuntimeProps;
120
120
  edgeFeatures?: Runtime.Client.EdgeFeatures;
121
121
  };
122
122
 
123
- export type DataSpaceManagerRuntimeParams = {
123
+ export type DataSpaceManagerRuntimeProps = {
124
124
  spaceMemberPresenceAnnounceInterval?: number;
125
125
  spaceMemberPresenceOfflineTimeout?: number;
126
126
  activeEdgeNotarizationPollingInterval?: number;
127
127
  disableP2pReplication?: boolean;
128
+ /**
129
+ * If true, spaces that were previously SPACE_ACTIVE will be automatically activated on startup.
130
+ * This is used in dedicated worker mode to restore space state after leader changeover.
131
+ */
132
+ autoActivateSpaces?: boolean;
128
133
  };
129
134
 
130
135
  export type CreateSpaceOptions = {
@@ -152,9 +157,9 @@ export class DataSpaceManager extends Resource {
152
157
  private readonly _edgeFeatures?: Runtime.Client.EdgeFeatures = undefined;
153
158
  private readonly _meshReplicator?: MeshEchoReplicator = undefined;
154
159
  private readonly _echoEdgeReplicator?: EchoEdgeReplicator = undefined;
155
- private readonly _runtimeParams?: DataSpaceManagerRuntimeParams = undefined;
160
+ private readonly _runtimeProps?: DataSpaceManagerRuntimeProps = undefined;
156
161
 
157
- constructor(params: DataSpaceManagerParams) {
162
+ constructor(params: DataSpaceManagerProps) {
158
163
  super();
159
164
 
160
165
  this._spaceManager = params.spaceManager;
@@ -169,7 +174,7 @@ export class DataSpaceManager extends Resource {
169
174
  this._edgeFeatures = params.edgeFeatures;
170
175
  this._echoEdgeReplicator = params.echoEdgeReplicator;
171
176
  this._edgeHttpClient = params.edgeHttpClient;
172
- this._runtimeParams = params.runtimeParams;
177
+ this._runtimeProps = params.runtimeProps;
173
178
 
174
179
  trace.diagnostic({
175
180
  id: 'spaces',
@@ -179,12 +184,12 @@ export class DataSpaceManager extends Resource {
179
184
  Array.from(this._spaces.values()).map(async (space) => {
180
185
  const rootUrl = space.automergeSpaceState.rootUrl;
181
186
  const rootHandle = rootUrl
182
- ? await this._echoHost.automergeRepo.find<Doc<DatabaseDirectory>>(rootUrl as AutomergeUrl, FIND_PARAMS)
187
+ ? await this._echoHost.loadDoc<Doc<DatabaseDirectory>>(Context.default(), rootUrl as AutomergeUrl)
183
188
  : undefined;
184
189
  await rootHandle?.whenReady();
185
190
  const rootDoc = rootHandle?.doc();
186
191
 
187
- const properties = rootDoc && findInlineObjectOfType(rootDoc, TYPE_PROPERTIES);
192
+ const properties = rootDoc && findInlineObjectOfType(rootDoc, Type.getTypename(SpaceProperties));
188
193
 
189
194
  return {
190
195
  key: space.key.toHex(),
@@ -217,15 +222,28 @@ export class DataSpaceManager extends Resource {
217
222
  log.trace('dxos.echo.data-space-manager.open', Trace.begin({ id: this._instanceId }));
218
223
  log('metadata loaded', { spaces: this._metadataStore.spaces.length });
219
224
 
225
+ const spacesToActivate: DataSpace[] = [];
220
226
  await forEachAsync(this._metadataStore.spaces, async (spaceMetadata) => {
221
227
  try {
222
228
  log('load space', { spaceMetadata });
223
- await this._constructSpace(spaceMetadata);
229
+ const space = await this._constructSpace(spaceMetadata);
230
+ // Track spaces that were previously active for auto-activation (used in dedicated worker mode).
231
+ if (this._runtimeProps?.autoActivateSpaces && spaceMetadata.state === SpaceState.SPACE_ACTIVE) {
232
+ spacesToActivate.push(space);
233
+ }
224
234
  } catch (err) {
225
235
  log.error('Error loading space', { spaceMetadata, err });
226
236
  }
227
237
  });
228
238
 
239
+ // Auto-activate spaces that were previously active (used in dedicated worker mode after leader changeover).
240
+ for (const space of spacesToActivate) {
241
+ log('auto-activating space', { spaceKey: space.key });
242
+ space.activate().catch((err) => {
243
+ log.error('Error auto-activating space', { spaceKey: space.key, err });
244
+ });
245
+ }
246
+
229
247
  this.updated.emit();
230
248
 
231
249
  log.trace('dxos.echo.data-space-manager.open', Trace.end({ id: this._instanceId }));
@@ -279,7 +297,18 @@ export class DataSpaceManager extends Resource {
279
297
  await Promise.all(
280
298
  Object.entries(options.documents).map(async ([documentId, data]) => {
281
299
  log('creating document...', { documentId });
282
- const newDoc = await this._echoHost.createDoc(data, { preserveHistory: true });
300
+ // TODO(dmaretskyi): Broken types -- the bytes get interpreted as CRDT data.
301
+ const newDoc = await this._echoHost.createDoc(data as any as DatabaseDirectory, {
302
+ preserveHistory: true,
303
+ });
304
+
305
+ // The archived documents might have the spaceKey from the space they were expored from, we need to update it to the new spaceKey.
306
+ if (newDoc.doc().access !== undefined && newDoc.doc().access!.spaceKey !== spaceKey.toHex()) {
307
+ newDoc.change((doc) => {
308
+ doc.access!.spaceKey = spaceKey.toHex();
309
+ });
310
+ }
311
+
283
312
  documentIdMapping[documentId as DocumentId] = newDoc.documentId;
284
313
  }),
285
314
  );
@@ -297,6 +326,7 @@ export class DataSpaceManager extends Resource {
297
326
  } else {
298
327
  root = await this._echoHost.createSpaceRoot(spaceKey);
299
328
  }
329
+ await this._echoHost.flush();
300
330
 
301
331
  log('constructing space...', { spaceKey });
302
332
 
@@ -330,9 +360,12 @@ export class DataSpaceManager extends Resource {
330
360
  log.warn('waiting for space root to be ready', { spaceId: space.id });
331
361
  await space.databaseRoot.handle.whenReady();
332
362
  }
333
- const [_, properties] = findInlineObjectOfType(space.databaseRoot.doc()!, TYPE_PROPERTIES) ?? [];
363
+
364
+ const [_, properties] =
365
+ findInlineObjectOfType(space.databaseRoot.doc()!, Type.getTypename(SpaceProperties)) ?? [];
334
366
  return properties?.data?.[DEFAULT_SPACE_KEY] === this._signingContext.identityKey.toHex();
335
367
  }
368
+
336
369
  case SpaceDocVersion.LEGACY: {
337
370
  throw new Error('Legacy space version is not supported');
338
371
  }
@@ -350,7 +383,7 @@ export class DataSpaceManager extends Resource {
350
383
  // TODO(dmaretskyi): Better API for low-level data access.
351
384
  const properties: ObjectStructure = {
352
385
  system: {
353
- type: encodeReference(getTypeReference(PropertiesType)!),
386
+ type: EncodedReference.fromDXN(getSchemaDXN(SpaceProperties)!),
354
387
  },
355
388
  data: {
356
389
  [DEFAULT_SPACE_KEY]: this._signingContext.identityKey.toHex(),
@@ -372,7 +405,9 @@ export class DataSpaceManager extends Resource {
372
405
  private async _getSpaceRootDocument(space: DataSpace): Promise<DocHandle<DatabaseDirectory>> {
373
406
  const automergeIndex = space.automergeSpaceState.rootUrl;
374
407
  invariant(automergeIndex);
375
- const document = await this._echoHost.automergeRepo.find<DatabaseDirectory>(automergeIndex as any, FIND_PARAMS);
408
+ const document = await this._echoHost.loadDoc<DatabaseDirectory>(Context.default(), automergeIndex as any, {
409
+ fetchFromNetwork: true,
410
+ });
376
411
  await document.whenReady();
377
412
  return document;
378
413
  }
@@ -484,8 +519,8 @@ export class DataSpaceManager extends Resource {
484
519
  localPeerId: this._signingContext.deviceKey,
485
520
  });
486
521
  const presence = new Presence({
487
- announceInterval: this._runtimeParams?.spaceMemberPresenceAnnounceInterval ?? PRESENCE_ANNOUNCE_INTERVAL,
488
- offlineTimeout: this._runtimeParams?.spaceMemberPresenceOfflineTimeout ?? PRESENCE_OFFLINE_TIMEOUT,
522
+ announceInterval: this._runtimeProps?.spaceMemberPresenceAnnounceInterval ?? PRESENCE_ANNOUNCE_INTERVAL,
523
+ offlineTimeout: this._runtimeProps?.spaceMemberPresenceOfflineTimeout ?? PRESENCE_OFFLINE_TIMEOUT,
489
524
  identityKey: this._signingContext.identityKey,
490
525
  gossip,
491
526
  });
@@ -571,7 +606,7 @@ export class DataSpaceManager extends Resource {
571
606
  edgeConnection: this._edgeConnection,
572
607
  edgeHttpClient: this._edgeHttpClient,
573
608
  edgeFeatures: this._edgeFeatures,
574
- activeEdgeNotarizationPollingInterval: this._runtimeParams?.activeEdgeNotarizationPollingInterval,
609
+ activeEdgeNotarizationPollingInterval: this._runtimeProps?.activeEdgeNotarizationPollingInterval,
575
610
  });
576
611
  dataSpace.postOpen.append(async () => {
577
612
  const setting = dataSpace.getEdgeReplicationSetting();
@@ -3,7 +3,7 @@
3
3
  //
4
4
 
5
5
  import { save } from '@automerge/automerge';
6
- import { type DocHandle } from '@automerge/automerge-repo';
6
+ import { type AutomergeUrl, type DocHandle } from '@automerge/automerge-repo';
7
7
 
8
8
  import { Event, Mutex, scheduleTask, sleep, synchronized, trackLeaks } from '@dxos/async';
9
9
  import { AUTH_TIMEOUT } from '@dxos/client-protocol';
@@ -13,7 +13,6 @@ import { timed, warnAfterTimeout } from '@dxos/debug';
13
13
  import {
14
14
  type DatabaseRoot,
15
15
  type EchoHost,
16
- FIND_PARAMS,
17
16
  type MetadataStore,
18
17
  type Space,
19
18
  createMappedFeedWriter,
@@ -72,7 +71,7 @@ export type DataSpaceCallbacks = {
72
71
  beforeClose?: () => Promise<void>;
73
72
  };
74
73
 
75
- export type DataSpaceParams = {
74
+ export type DataSpaceProps = {
76
75
  initialState: SpaceState;
77
76
  inner: Space;
78
77
  metadataStore: MetadataStore;
@@ -136,7 +135,7 @@ export class DataSpace {
136
135
 
137
136
  public metrics: SpaceProto.Metrics = {};
138
137
 
139
- constructor(params: DataSpaceParams) {
138
+ constructor(params: DataSpaceProps) {
140
139
  this._inner = params.inner;
141
140
  this._inner.stateUpdate.on(this._ctx, () => this.stateUpdate.emit());
142
141
 
@@ -331,7 +330,7 @@ export class DataSpace {
331
330
  @trace.span({ showInBrowserTimeline: true })
332
331
  async initializeDataPipeline(): Promise<void> {
333
332
  if (this._state !== SpaceState.SPACE_CONTROL_ONLY) {
334
- throw new SystemError('Invalid operation');
333
+ throw new SystemError({ message: 'Invalid operation' });
335
334
  }
336
335
 
337
336
  this._state = SpaceState.SPACE_INITIALIZING;
@@ -451,6 +450,9 @@ export class DataSpace {
451
450
 
452
451
  log('credentials notarized');
453
452
  } catch (err) {
453
+ if (err instanceof ContextDisposedError) {
454
+ return;
455
+ }
454
456
  log.error('error notarizing credentials for feed admission', err);
455
457
  throw err;
456
458
  }
@@ -471,7 +473,9 @@ export class DataSpace {
471
473
  await warnAfterTimeout(5_000, 'Automerge root doc load timeout (DataSpace)', async () => {
472
474
  handle = await cancelWithContext(
473
475
  this._ctx,
474
- this._echoHost.automergeRepo.find<DatabaseDirectory>(rootUrl as any, FIND_PARAMS),
476
+ this._echoHost.loadDoc<DatabaseDirectory>(Context.default(), rootUrl as AutomergeUrl, {
477
+ fetchFromNetwork: true,
478
+ }),
475
479
  );
476
480
  await cancelWithContext(this._ctx, handle.whenReady());
477
481
  });
@@ -45,7 +45,7 @@ describe('EdgeFeedReplicator', () => {
45
45
  const { endpoint, admitConnection, messageSink } = await createEdge();
46
46
  const { messenger } = await createClient(endpoint);
47
47
  admitConnection.wake();
48
- await expect.poll(() => messenger.status).toBe(EdgeStatus.CONNECTED);
48
+ await expect.poll(() => messenger.status.state).toBe(EdgeStatus.ConnectionState.CONNECTED);
49
49
 
50
50
  await attachReplicator(messenger);
51
51
  await expect.poll(() => messageSink.length).toEqual(1);