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

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 (199) hide show
  1. package/dist/lib/browser/{chunk-MQ6PWJ76.mjs → chunk-TZQLGPMV.mjs} +1596 -3099
  2. package/dist/lib/browser/chunk-TZQLGPMV.mjs.map +7 -0
  3. package/dist/lib/browser/{chunk-NQSC7HOE.mjs → chunk-XJRPB3GA.mjs} +1 -1
  4. package/dist/lib/browser/index.mjs +106 -194
  5. package/dist/lib/browser/index.mjs.map +3 -3
  6. package/dist/lib/browser/meta.json +1 -1
  7. package/dist/lib/browser/packlets/diagnostics/browser-diagnostics-broadcast.mjs +1 -6
  8. package/dist/lib/browser/packlets/diagnostics/browser-diagnostics-broadcast.mjs.map +2 -2
  9. package/dist/lib/browser/packlets/diagnostics/diagnostics-broadcast.mjs +1 -1
  10. package/dist/lib/browser/packlets/locks/browser.mjs +9 -49
  11. package/dist/lib/browser/packlets/locks/browser.mjs.map +2 -2
  12. package/dist/lib/browser/packlets/locks/node.mjs +4 -22
  13. package/dist/lib/browser/packlets/locks/node.mjs.map +2 -2
  14. package/dist/lib/browser/testing/index.mjs +9 -22
  15. package/dist/lib/browser/testing/index.mjs.map +3 -3
  16. package/dist/lib/node-esm/{chunk-PKEGMOQ4.mjs → chunk-2DT3MZRL.mjs} +1 -1
  17. package/dist/lib/node-esm/{chunk-GUAL4U7S.mjs → chunk-6DX3BNLB.mjs} +1596 -3099
  18. package/dist/lib/node-esm/chunk-6DX3BNLB.mjs.map +7 -0
  19. package/dist/lib/node-esm/index.mjs +106 -194
  20. package/dist/lib/node-esm/index.mjs.map +3 -3
  21. package/dist/lib/node-esm/meta.json +1 -1
  22. package/dist/lib/node-esm/packlets/diagnostics/browser-diagnostics-broadcast.mjs +1 -6
  23. package/dist/lib/node-esm/packlets/diagnostics/browser-diagnostics-broadcast.mjs.map +2 -2
  24. package/dist/lib/node-esm/packlets/diagnostics/diagnostics-broadcast.mjs +1 -1
  25. package/dist/lib/node-esm/packlets/locks/browser.mjs +9 -49
  26. package/dist/lib/node-esm/packlets/locks/browser.mjs.map +2 -2
  27. package/dist/lib/node-esm/packlets/locks/node.mjs +4 -22
  28. package/dist/lib/node-esm/packlets/locks/node.mjs.map +2 -2
  29. package/dist/lib/node-esm/testing/index.mjs +9 -22
  30. package/dist/lib/node-esm/testing/index.mjs.map +3 -3
  31. package/dist/types/src/index.d.ts +1 -0
  32. package/dist/types/src/index.d.ts.map +1 -1
  33. package/dist/types/src/packlets/agents/edge-agent-manager.d.ts +3 -2
  34. package/dist/types/src/packlets/agents/edge-agent-manager.d.ts.map +1 -1
  35. package/dist/types/src/packlets/agents/edge-agent-service.d.ts +2 -1
  36. package/dist/types/src/packlets/agents/edge-agent-service.d.ts.map +1 -1
  37. package/dist/types/src/packlets/devices/devices-service.d.ts.map +1 -1
  38. package/dist/types/src/packlets/devtools/devtools.d.ts.map +1 -1
  39. package/dist/types/src/packlets/devtools/feeds.d.ts.map +1 -1
  40. package/dist/types/src/packlets/devtools/keys.d.ts.map +1 -1
  41. package/dist/types/src/packlets/devtools/metadata.d.ts.map +1 -1
  42. package/dist/types/src/packlets/devtools/network.d.ts.map +1 -1
  43. package/dist/types/src/packlets/devtools/spaces.d.ts.map +1 -1
  44. package/dist/types/src/packlets/diagnostics/browser-diagnostics-broadcast.d.ts.map +1 -1
  45. package/dist/types/src/packlets/diagnostics/diagnostics-broadcast.d.ts.map +1 -1
  46. package/dist/types/src/packlets/diagnostics/diagnostics-collector.d.ts.map +1 -1
  47. package/dist/types/src/packlets/diagnostics/diagnostics.d.ts +2 -3
  48. package/dist/types/src/packlets/diagnostics/diagnostics.d.ts.map +1 -1
  49. package/dist/types/src/packlets/identity/authenticator.d.ts.map +1 -1
  50. package/dist/types/src/packlets/identity/contacts-service.d.ts.map +1 -1
  51. package/dist/types/src/packlets/identity/identity-manager.d.ts +3 -3
  52. package/dist/types/src/packlets/identity/identity-manager.d.ts.map +1 -1
  53. package/dist/types/src/packlets/identity/identity-recovery-manager.d.ts +6 -5
  54. package/dist/types/src/packlets/identity/identity-recovery-manager.d.ts.map +1 -1
  55. package/dist/types/src/packlets/identity/identity-service.d.ts +6 -10
  56. package/dist/types/src/packlets/identity/identity-service.d.ts.map +1 -1
  57. package/dist/types/src/packlets/identity/identity.d.ts +6 -9
  58. package/dist/types/src/packlets/identity/identity.d.ts.map +1 -1
  59. package/dist/types/src/packlets/invitations/device-invitation-protocol.d.ts +2 -1
  60. package/dist/types/src/packlets/invitations/device-invitation-protocol.d.ts.map +1 -1
  61. package/dist/types/src/packlets/invitations/edge-invitation-handler.d.ts +1 -1
  62. package/dist/types/src/packlets/invitations/edge-invitation-handler.d.ts.map +1 -1
  63. package/dist/types/src/packlets/invitations/invitation-guest-extenstion.d.ts.map +1 -1
  64. package/dist/types/src/packlets/invitations/invitation-host-extension.d.ts.map +1 -1
  65. package/dist/types/src/packlets/invitations/invitation-protocol.d.ts +5 -1
  66. package/dist/types/src/packlets/invitations/invitation-protocol.d.ts.map +1 -1
  67. package/dist/types/src/packlets/invitations/invitation-state.d.ts.map +1 -1
  68. package/dist/types/src/packlets/invitations/invitation-topology.d.ts.map +1 -1
  69. package/dist/types/src/packlets/invitations/invitations-handler.d.ts.map +1 -1
  70. package/dist/types/src/packlets/invitations/invitations-manager.d.ts +3 -3
  71. package/dist/types/src/packlets/invitations/invitations-manager.d.ts.map +1 -1
  72. package/dist/types/src/packlets/invitations/invitations-service.d.ts +3 -3
  73. package/dist/types/src/packlets/invitations/invitations-service.d.ts.map +1 -1
  74. package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts +2 -1
  75. package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts.map +1 -1
  76. package/dist/types/src/packlets/invitations/utils.d.ts.map +1 -1
  77. package/dist/types/src/packlets/locks/browser.d.ts.map +1 -1
  78. package/dist/types/src/packlets/locks/node.d.ts.map +1 -1
  79. package/dist/types/src/packlets/logging/logging-service.d.ts.map +1 -1
  80. package/dist/types/src/packlets/network/network-service.d.ts +5 -4
  81. package/dist/types/src/packlets/network/network-service.d.ts.map +1 -1
  82. package/dist/types/src/packlets/services/client-rpc-server.d.ts +3 -3
  83. package/dist/types/src/packlets/services/client-rpc-server.d.ts.map +1 -1
  84. package/dist/types/src/packlets/services/feed-syncer.d.ts +2 -2
  85. package/dist/types/src/packlets/services/feed-syncer.d.ts.map +1 -1
  86. package/dist/types/src/packlets/services/service-context.d.ts +2 -3
  87. package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
  88. package/dist/types/src/packlets/services/service-host.d.ts +2 -3
  89. package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
  90. package/dist/types/src/packlets/services/service-registry.d.ts.map +1 -1
  91. package/dist/types/src/packlets/services/util.d.ts.map +1 -1
  92. package/dist/types/src/packlets/space-export/archive-format.d.ts +9 -0
  93. package/dist/types/src/packlets/space-export/archive-format.d.ts.map +1 -0
  94. package/dist/types/src/packlets/space-export/index.d.ts +4 -1
  95. package/dist/types/src/packlets/space-export/index.d.ts.map +1 -1
  96. package/dist/types/src/packlets/space-export/serialized-space-reader.d.ts +23 -0
  97. package/dist/types/src/packlets/space-export/serialized-space-reader.d.ts.map +1 -0
  98. package/dist/types/src/packlets/space-export/serialized-space-writer.d.ts +36 -0
  99. package/dist/types/src/packlets/space-export/serialized-space-writer.d.ts.map +1 -0
  100. package/dist/types/src/packlets/space-export/space-archive-reader.d.ts +9 -1
  101. package/dist/types/src/packlets/space-export/space-archive-reader.d.ts.map +1 -1
  102. package/dist/types/src/packlets/space-export/space-archive-writer.d.ts +7 -1
  103. package/dist/types/src/packlets/space-export/space-archive-writer.d.ts.map +1 -1
  104. package/dist/types/src/packlets/space-export/space-archive.test.d.ts +2 -0
  105. package/dist/types/src/packlets/space-export/space-archive.test.d.ts.map +1 -0
  106. package/dist/types/src/packlets/spaces/automerge-space-state.d.ts.map +1 -1
  107. package/dist/types/src/packlets/spaces/data-space-manager.d.ts +18 -12
  108. package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
  109. package/dist/types/src/packlets/spaces/data-space.d.ts +24 -7
  110. package/dist/types/src/packlets/spaces/data-space.d.ts.map +1 -1
  111. package/dist/types/src/packlets/spaces/edge-feed-replicator.d.ts.map +1 -1
  112. package/dist/types/src/packlets/spaces/epoch-migrations.d.ts.map +1 -1
  113. package/dist/types/src/packlets/spaces/genesis.d.ts +2 -1
  114. package/dist/types/src/packlets/spaces/genesis.d.ts.map +1 -1
  115. package/dist/types/src/packlets/spaces/notarization-plugin.d.ts +1 -4
  116. package/dist/types/src/packlets/spaces/notarization-plugin.d.ts.map +1 -1
  117. package/dist/types/src/packlets/spaces/spaces-service.d.ts +10 -7
  118. package/dist/types/src/packlets/spaces/spaces-service.d.ts.map +1 -1
  119. package/dist/types/src/packlets/storage/level.d.ts.map +1 -1
  120. package/dist/types/src/packlets/storage/profile-archive.d.ts.map +1 -1
  121. package/dist/types/src/packlets/storage/storage.d.ts.map +1 -1
  122. package/dist/types/src/packlets/storage/util.d.ts.map +1 -1
  123. package/dist/types/src/packlets/system/system-service.d.ts +1 -1
  124. package/dist/types/src/packlets/system/system-service.d.ts.map +1 -1
  125. package/dist/types/src/packlets/testing/credential-utils.d.ts.map +1 -1
  126. package/dist/types/src/packlets/testing/invitation-utils.d.ts.map +1 -1
  127. package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
  128. package/dist/types/src/packlets/worker/worker-runtime.d.ts +11 -1
  129. package/dist/types/src/packlets/worker/worker-runtime.d.ts.map +1 -1
  130. package/dist/types/src/packlets/worker/worker-session.d.ts +0 -2
  131. package/dist/types/src/packlets/worker/worker-session.d.ts.map +1 -1
  132. package/dist/types/src/testing/setup.d.ts.map +1 -1
  133. package/dist/types/src/version.d.ts +1 -1
  134. package/dist/types/tsconfig.tsbuildinfo +1 -1
  135. package/package.json +42 -49
  136. package/src/index.ts +1 -0
  137. package/src/packlets/agents/edge-agent-manager.ts +8 -5
  138. package/src/packlets/agents/edge-agent-service.ts +4 -2
  139. package/src/packlets/devices/devices-service.test.ts +0 -1
  140. package/src/packlets/devtools/devtools.ts +0 -1
  141. package/src/packlets/diagnostics/diagnostics.ts +1 -2
  142. package/src/packlets/identity/contacts-service.ts +0 -1
  143. package/src/packlets/identity/identity-manager.test.ts +5 -5
  144. package/src/packlets/identity/identity-manager.ts +18 -17
  145. package/src/packlets/identity/identity-recovery-manager.ts +20 -16
  146. package/src/packlets/identity/identity-service.test.ts +6 -27
  147. package/src/packlets/identity/identity-service.ts +13 -81
  148. package/src/packlets/identity/identity.test.ts +2 -2
  149. package/src/packlets/identity/identity.ts +9 -32
  150. package/src/packlets/invitations/device-invitation-protocol.ts +3 -2
  151. package/src/packlets/invitations/edge-invitation-handler.ts +9 -5
  152. package/src/packlets/invitations/invitation-host-extension.ts +7 -10
  153. package/src/packlets/invitations/invitation-protocol.ts +5 -1
  154. package/src/packlets/invitations/invitation-state.ts +1 -15
  155. package/src/packlets/invitations/invitations-handler.test.ts +4 -5
  156. package/src/packlets/invitations/invitations-handler.ts +67 -15
  157. package/src/packlets/invitations/invitations-manager.ts +40 -15
  158. package/src/packlets/invitations/invitations-service.ts +9 -9
  159. package/src/packlets/invitations/space-invitation-protocol.test.ts +17 -16
  160. package/src/packlets/invitations/space-invitation-protocol.ts +4 -3
  161. package/src/packlets/logging/logging-service.ts +15 -15
  162. package/src/packlets/network/network-service.test.ts +0 -1
  163. package/src/packlets/network/network-service.ts +10 -8
  164. package/src/packlets/services/client-rpc-server.ts +15 -12
  165. package/src/packlets/services/feed-syncer.test.ts +3 -3
  166. package/src/packlets/services/feed-syncer.ts +24 -17
  167. package/src/packlets/services/service-context.test.ts +3 -2
  168. package/src/packlets/services/service-context.ts +91 -40
  169. package/src/packlets/services/service-host.test.ts +8 -8
  170. package/src/packlets/services/service-host.ts +17 -24
  171. package/src/packlets/services/service-registry.test.ts +0 -1
  172. package/src/packlets/space-export/archive-format.ts +42 -0
  173. package/src/packlets/space-export/index.ts +4 -1
  174. package/src/packlets/space-export/serialized-space-reader.ts +111 -0
  175. package/src/packlets/space-export/serialized-space-writer.ts +252 -0
  176. package/src/packlets/space-export/space-archive-reader.ts +64 -3
  177. package/src/packlets/space-export/space-archive-writer.ts +38 -2
  178. package/src/packlets/space-export/space-archive.test.ts +461 -0
  179. package/src/packlets/spaces/data-space-manager.test.ts +79 -13
  180. package/src/packlets/spaces/data-space-manager.ts +75 -111
  181. package/src/packlets/spaces/data-space.ts +49 -29
  182. package/src/packlets/spaces/edge-feed-replicator.test.ts +1 -1
  183. package/src/packlets/spaces/edge-feed-replicator.ts +9 -7
  184. package/src/packlets/spaces/epoch-migrations.ts +3 -3
  185. package/src/packlets/spaces/genesis.ts +6 -1
  186. package/src/packlets/spaces/notarization-plugin.ts +2 -1
  187. package/src/packlets/spaces/spaces-service.test.ts +18 -11
  188. package/src/packlets/spaces/spaces-service.ts +113 -17
  189. package/src/packlets/testing/invitation-utils.ts +3 -2
  190. package/src/packlets/worker/worker-runtime.ts +39 -6
  191. package/src/packlets/worker/worker-session.ts +4 -10
  192. package/src/version.ts +1 -1
  193. package/dist/lib/browser/chunk-MQ6PWJ76.mjs.map +0 -7
  194. package/dist/lib/node-esm/chunk-GUAL4U7S.mjs.map +0 -7
  195. package/dist/types/src/packlets/identity/default-space-state-machine.d.ts +0 -19
  196. package/dist/types/src/packlets/identity/default-space-state-machine.d.ts.map +0 -1
  197. package/src/packlets/identity/default-space-state-machine.ts +0 -44
  198. /package/dist/lib/browser/{chunk-NQSC7HOE.mjs.map → chunk-XJRPB3GA.mjs.map} +0 -0
  199. /package/dist/lib/node-esm/{chunk-PKEGMOQ4.mjs.map → chunk-2DT3MZRL.mjs.map} +0 -0
@@ -132,7 +132,7 @@ export class EdgeFeedReplicator extends Resource {
132
132
 
133
133
  private async _replicateFeed(ctx: Context, feed: FeedWrapper<any>): Promise<void> {
134
134
  log('replicateFeed', { key: feed.key });
135
- await this._sendMessage({
135
+ await this._sendMessage(ctx, {
136
136
  type: 'get-metadata',
137
137
  feedKey: feed.key.toHex(),
138
138
  });
@@ -142,7 +142,7 @@ export class EdgeFeedReplicator extends Resource {
142
142
  });
143
143
  }
144
144
 
145
- private async _sendMessage(message: ProtocolMessage): Promise<void> {
145
+ private async _sendMessage(ctx: Context, message: ProtocolMessage): Promise<void> {
146
146
  if (!this._connectionCtx) {
147
147
  log('message dropped because connection was disposed');
148
148
  return;
@@ -160,6 +160,7 @@ export class EdgeFeedReplicator extends Resource {
160
160
 
161
161
  log('send', { type: message.type });
162
162
  await this._messenger.send(
163
+ ctx,
163
164
  buf.create(RouterMessageSchema, {
164
165
  source: {
165
166
  identityKey: this._messenger.identityKey,
@@ -194,7 +195,7 @@ export class EdgeFeedReplicator extends Resource {
194
195
  if (message.length > feed.length) {
195
196
  log('requesting missing blocks', logMeta);
196
197
 
197
- await this._sendMessage({
198
+ await this._sendMessage(this._connectionCtx!, {
198
199
  type: 'request',
199
200
  feedKey: feedKey.toHex(),
200
201
  range: { from: feed.length, to: message.length },
@@ -202,7 +203,7 @@ export class EdgeFeedReplicator extends Resource {
202
203
  } else if (message.length < feed.length) {
203
204
  log('pushing blocks to remote', logMeta);
204
205
 
205
- await this._pushBlocks(feed, message.length, feed.length);
206
+ await this._pushBlocks(this._connectionCtx!, feed, message.length, feed.length);
206
207
  }
207
208
 
208
209
  break;
@@ -229,7 +230,7 @@ export class EdgeFeedReplicator extends Resource {
229
230
  });
230
231
  }
231
232
 
232
- private async _pushBlocks(feed: FeedWrapper<any>, from: number, to: number): Promise<void> {
233
+ private async _pushBlocks(ctx: Context, feed: FeedWrapper<any>, from: number, to: number): Promise<void> {
233
234
  log('pushing blocks', { feed: feed.key.toHex(), from, to });
234
235
 
235
236
  const blocks: FeedBlock[] = await Promise.all(
@@ -247,7 +248,7 @@ export class EdgeFeedReplicator extends Resource {
247
248
  }),
248
249
  );
249
250
 
250
- await this._sendMessage({
251
+ await this._sendMessage(ctx, {
251
252
  type: 'data',
252
253
  feedKey: feed.key.toHex(),
253
254
  blocks,
@@ -283,12 +284,13 @@ export class EdgeFeedReplicator extends Resource {
283
284
 
284
285
  const remoteLength = this._remoteLength.get(feed.key)!;
285
286
  if (remoteLength < feed.length) {
286
- await this._pushBlocks(feed, remoteLength, feed.length);
287
+ await this._pushBlocks(this._connectionCtx!, feed, remoteLength, feed.length);
287
288
  }
288
289
  }
289
290
 
290
291
  private _createConnectionContext(): Context {
291
292
  const connectionCtx = new Context({
293
+ parent: this._ctx,
292
294
  onError: async (err: any) => {
293
295
  if (connectionCtx !== this._connectionCtx) {
294
296
  return;
@@ -37,7 +37,7 @@ export const runEpochMigration = async (ctx: Context, context: MigrationContext)
37
37
  switch (context.migration) {
38
38
  case CreateEpochRequest.Migration.INIT_AUTOMERGE: {
39
39
  const document = await context.echoHost.createDoc();
40
- await context.echoHost.flush();
40
+ await context.echoHost.flush(ctx);
41
41
  return { newRoot: document.url };
42
42
  }
43
43
  case CreateEpochRequest.Migration.PRUNE_AUTOMERGE_ROOT_HISTORY: {
@@ -49,7 +49,7 @@ export const runEpochMigration = async (ctx: Context, context: MigrationContext)
49
49
  });
50
50
 
51
51
  const newRoot = await context.echoHost.createDoc(rootHandle.doc());
52
- await context.echoHost.flush();
52
+ await context.echoHost.flush(ctx);
53
53
  return { newRoot: newRoot.url };
54
54
  }
55
55
  case CreateEpochRequest.Migration.FRAGMENT_AUTOMERGE_ROOT: {
@@ -63,7 +63,7 @@ export const runEpochMigration = async (ctx: Context, context: MigrationContext)
63
63
  invariant(context.newAutomergeRoot);
64
64
 
65
65
  // Defensive programming - it should be the responsibility of the caller to flush the new root.
66
- await context.echoHost.flush();
66
+ await context.echoHost.flush(ctx);
67
67
  return {
68
68
  newRoot: context.newAutomergeRoot,
69
69
  };
@@ -6,7 +6,7 @@ import { createCredential } from '@dxos/credentials';
6
6
  import { failUndefined } from '@dxos/debug';
7
7
  import { type Space } from '@dxos/echo-pipeline';
8
8
  import { type Keyring } from '@dxos/keyring';
9
- import { AdmittedFeed, SpaceMember } from '@dxos/protocols/proto/dxos/halo/credentials';
9
+ import { AdmittedFeed, MembershipPolicy, SpaceMember } from '@dxos/protocols/proto/dxos/halo/credentials';
10
10
  import { Timeframe } from '@dxos/timeframe';
11
11
 
12
12
  import { type SigningContext } from './data-space-manager';
@@ -16,6 +16,8 @@ export const spaceGenesis = async (
16
16
  signingContext: SigningContext,
17
17
  space: Space,
18
18
  automergeRoot?: string,
19
+ tags?: string[],
20
+ membershipPolicy?: MembershipPolicy,
19
21
  ) => {
20
22
  // TODO(dmaretskyi): Find a way to reconcile with credential generator.
21
23
  const credentials = [
@@ -26,6 +28,8 @@ export const spaceGenesis = async (
26
28
  assertion: {
27
29
  '@type': 'dxos.halo.credentials.SpaceGenesis',
28
30
  spaceKey: space.key,
31
+ tags: tags ?? [],
32
+ membershipPolicy: membershipPolicy ?? MembershipPolicy.INVITE,
29
33
  },
30
34
  }),
31
35
 
@@ -39,6 +43,7 @@ export const spaceGenesis = async (
39
43
  role: SpaceMember.Role.OWNER,
40
44
  profile: signingContext.getProfile(),
41
45
  genesisFeedKey: space.controlFeedKey ?? failUndefined(),
46
+ tags: tags ?? [],
42
47
  },
43
48
  }),
44
49
 
@@ -246,6 +246,7 @@ export class NotarizationPlugin extends Resource implements CredentialProcessor
246
246
  scheduleTask(ctx, async () => {
247
247
  try {
248
248
  await client.notarizeCredentials(
249
+ ctx,
249
250
  this._spaceId,
250
251
  { credentials: encodedCredentials },
251
252
  { retry: { count: MAX_EDGE_RETRIES, timeout: timeouts.retryTimeout, jitter: timeouts.jitter } },
@@ -302,7 +303,7 @@ export class NotarizationPlugin extends Resource implements CredentialProcessor
302
303
  private _notarizePendingEdgeCredentials(client: EdgeHttpClient, writer: FeedWriter<Credential>): void {
303
304
  scheduleMicroTask(this._ctx, async () => {
304
305
  try {
305
- const response = await client.getCredentialsForNotarization(this._spaceId, {
306
+ const response = await client.getCredentialsForNotarization(this._ctx, this._spaceId, {
306
307
  retry: { count: MAX_EDGE_RETRIES },
307
308
  });
308
309
 
@@ -8,10 +8,10 @@ import { Trigger } from '@dxos/async';
8
8
  import { Context } from '@dxos/context';
9
9
  import { PublicKey } from '@dxos/keys';
10
10
  import { type Space, type SpacesService } from '@dxos/protocols/proto/dxos/client/services';
11
+ import { MembershipPolicy } from '@dxos/protocols/proto/dxos/halo/credentials';
11
12
 
12
13
  import { type ServiceContext } from '../services';
13
14
  import { createServiceContext } from '../testing';
14
-
15
15
  import { SpacesServiceImpl } from './spaces-service';
16
16
 
17
17
  describe('SpacesService', () => {
@@ -21,10 +21,15 @@ describe('SpacesService', () => {
21
21
  beforeEach(async () => {
22
22
  serviceContext = await createServiceContext();
23
23
  await serviceContext.open(new Context());
24
- spacesService = new SpacesServiceImpl(serviceContext.identityManager, serviceContext.spaceManager, async () => {
25
- await serviceContext.initialized.wait();
26
- return serviceContext.dataSpaceManager!;
27
- });
24
+ spacesService = new SpacesServiceImpl(
25
+ serviceContext.identityManager,
26
+ serviceContext.spaceManager,
27
+ serviceContext.echoHost,
28
+ async () => {
29
+ await serviceContext.initialized.wait();
30
+ return serviceContext.dataSpaceManager!;
31
+ },
32
+ );
28
33
  });
29
34
 
30
35
  afterEach(async () => {
@@ -33,12 +38,14 @@ describe('SpacesService', () => {
33
38
 
34
39
  describe('createSpace', () => {
35
40
  test('fails if no identity is available', async () => {
36
- await expect(spacesService.createSpace()).rejects.toBeInstanceOf(Error);
41
+ await expect(spacesService.createSpace({ membershipPolicy: MembershipPolicy.INVITE })).rejects.toBeInstanceOf(
42
+ Error,
43
+ );
37
44
  });
38
45
 
39
46
  test('creates a new space', async () => {
40
47
  await serviceContext.createIdentity();
41
- const space = await spacesService.createSpace();
48
+ const space = await spacesService.createSpace({ membershipPolicy: MembershipPolicy.INVITE });
42
49
  expect(space).to.exist;
43
50
  expect(space.spaceKey).to.be.instanceof(PublicKey);
44
51
  });
@@ -60,9 +67,9 @@ describe('SpacesService', () => {
60
67
  test('returns list of existing spaces', async () => {
61
68
  await serviceContext.createIdentity();
62
69
  const existingSpaces = [
63
- await spacesService.createSpace(),
64
- await spacesService.createSpace(),
65
- await spacesService.createSpace(),
70
+ await spacesService.createSpace({ membershipPolicy: MembershipPolicy.INVITE }),
71
+ await spacesService.createSpace({ membershipPolicy: MembershipPolicy.INVITE }),
72
+ await spacesService.createSpace({ membershipPolicy: MembershipPolicy.INVITE }),
66
73
  ];
67
74
 
68
75
  const query = spacesService.querySpaces();
@@ -88,7 +95,7 @@ describe('SpacesService', () => {
88
95
  expect(await result.wait()).to.be.length(0);
89
96
 
90
97
  result.reset();
91
- const space = await spacesService.createSpace();
98
+ const space = await spacesService.createSpace({ membershipPolicy: MembershipPolicy.INVITE });
92
99
  const spaces = await result.wait();
93
100
  expect(spaces).to.be.length(1);
94
101
  expect(spaces?.[0].spaceKey.equals(space.spaceKey)).to.be.true;
@@ -5,7 +5,9 @@
5
5
  import type { AutomergeUrl } from '@automerge/automerge-repo';
6
6
 
7
7
  import { SubscriptionList, UpdateScheduler, scheduleTask } from '@dxos/async';
8
+ import { type RequestOptions } from '@dxos/codec-protobuf';
8
9
  import { Stream } from '@dxos/codec-protobuf/stream';
10
+ import { Context } from '@dxos/context';
9
11
  import {
10
12
  type CredentialProcessor,
11
13
  createAdmissionCredentials,
@@ -13,7 +15,8 @@ import {
13
15
  getCredentialAssertion,
14
16
  } from '@dxos/credentials';
15
17
  import { raise } from '@dxos/debug';
16
- import { type SpaceManager } from '@dxos/echo-pipeline';
18
+ import { type EchoHost, type SpaceManager } from '@dxos/echo-pipeline';
19
+ import { type DatabaseDirectory } from '@dxos/echo-protocol';
17
20
  import { writeMessages } from '@dxos/feed-store';
18
21
  import { assertArgument, assertState, invariant } from '@dxos/invariant';
19
22
  import { SpaceId } from '@dxos/keys';
@@ -21,6 +24,7 @@ import { log } from '@dxos/log';
21
24
  import {
22
25
  ApiError,
23
26
  AuthorizationError,
27
+ FeedProtocol,
24
28
  IdentityNotInitializedError,
25
29
  SpaceNotFoundError,
26
30
  encodeError,
@@ -40,11 +44,13 @@ import {
40
44
  type QueryCredentialsRequest,
41
45
  type QuerySpacesResponse,
42
46
  type Space,
47
+ SpaceArchive,
43
48
  SpaceMember,
44
49
  SpaceState,
45
50
  type SpacesService,
46
51
  type SubscribeMessagesRequest,
47
52
  type UpdateMemberRoleRequest,
53
+ type CreateSpaceRequest,
48
54
  type UpdateSpaceRequest,
49
55
  type WriteCredentialsRequest,
50
56
  } from '@dxos/protocols/proto/dxos/client/services';
@@ -54,8 +60,14 @@ import { trace } from '@dxos/tracing';
54
60
  import { type Provider } from '@dxos/util';
55
61
 
56
62
  import { type IdentityManager } from '../identity';
57
- import { SpaceArchiveWriter, extractSpaceArchive } from '../space-export';
58
-
63
+ import {
64
+ SpaceArchiveWriter,
65
+ detectSpaceArchiveFormat,
66
+ extractSpaceArchive,
67
+ readSerializedSpaceArchive,
68
+ writeSerializedSpaceArchive,
69
+ objJsonToObjectStructure,
70
+ } from '../space-export';
59
71
  import { type DataSpace } from './data-space';
60
72
  import { type DataSpaceManager } from './data-space-manager';
61
73
 
@@ -63,29 +75,35 @@ export class SpacesServiceImpl implements SpacesService {
63
75
  constructor(
64
76
  private readonly _identityManager: IdentityManager,
65
77
  private readonly _spaceManager: SpaceManager,
78
+ private readonly _echoHost: EchoHost,
66
79
  private readonly _getDataSpaceManager: Provider<Promise<DataSpaceManager>>,
67
80
  ) {}
68
81
 
69
- async createSpace(): Promise<Space> {
82
+ async createSpace(request: CreateSpaceRequest, options?: RequestOptions): Promise<Space> {
70
83
  this._requireIdentity();
84
+ const ctx = options?.ctx ?? new Context();
71
85
  const dataSpaceManager = await this._getDataSpaceManager();
72
- const space = await dataSpaceManager.createSpace();
86
+ const space = await dataSpaceManager.createSpace(ctx, {
87
+ tags: request?.tags,
88
+ membershipPolicy: request?.membershipPolicy,
89
+ });
73
90
  await this._updateMetrics();
74
91
  return this._serializeSpace(space);
75
92
  }
76
93
 
77
- async updateSpace({ spaceKey, state, edgeReplication }: UpdateSpaceRequest): Promise<void> {
94
+ async updateSpace({ spaceKey, state, edgeReplication }: UpdateSpaceRequest, options?: RequestOptions): Promise<void> {
95
+ const ctx = options?.ctx ?? Context.default();
78
96
  const dataSpaceManager = await this._getDataSpaceManager();
79
97
  const space = dataSpaceManager.spaces.get(spaceKey) ?? raise(new SpaceNotFoundError(spaceKey));
80
98
 
81
99
  if (state) {
82
100
  switch (state) {
83
101
  case SpaceState.SPACE_ACTIVE:
84
- await space.activate();
102
+ await space.activate(ctx);
85
103
  break;
86
104
 
87
105
  case SpaceState.SPACE_INACTIVE:
88
- await space.deactivate();
106
+ await space.deactivate(ctx);
89
107
  break;
90
108
  default:
91
109
  throw new ApiError({ message: 'Invalid space state' });
@@ -93,7 +111,7 @@ export class SpacesServiceImpl implements SpacesService {
93
111
  }
94
112
 
95
113
  if (edgeReplication !== undefined) {
96
- await dataSpaceManager.setSpaceEdgeReplicationSetting(spaceKey, edgeReplication);
114
+ await dataSpaceManager.setSpaceEdgeReplicationSetting(ctx, spaceKey, edgeReplication);
97
115
  }
98
116
  }
99
117
 
@@ -262,18 +280,26 @@ export class SpacesServiceImpl implements SpacesService {
262
280
  });
263
281
  }
264
282
 
265
- async joinBySpaceKey({ spaceKey }: JoinBySpaceKeyRequest): Promise<JoinSpaceResponse> {
283
+ async joinBySpaceKey({ spaceKey }: JoinBySpaceKeyRequest, options?: RequestOptions): Promise<JoinSpaceResponse> {
284
+ const ctx = options?.ctx ?? Context.default();
266
285
  const dataSpaceManager = await this._getDataSpaceManager();
267
- const credential = await dataSpaceManager.requestSpaceAdmissionCredential(spaceKey);
268
- return this._joinByAdmission({ credential });
286
+ const credential = await dataSpaceManager.requestSpaceAdmissionCredential(ctx, spaceKey);
287
+ return this._joinByAdmission(ctx, { credential });
269
288
  }
270
289
 
271
290
  async exportSpace(request: ExportSpaceRequest): Promise<ExportSpaceResponse> {
272
- await using writer = await new SpaceArchiveWriter().open();
273
291
  assertArgument(SpaceId.isValid(request.spaceId), 'spaceId', 'Invalid space ID');
274
292
 
275
293
  const dataSpaceManager = await this._getDataSpaceManager();
276
294
  const space = dataSpaceManager.getSpaceById(request.spaceId) ?? raise(new Error('Space not found'));
295
+
296
+ const format = request.format ?? SpaceArchive.Format.BINARY;
297
+ if (format === SpaceArchive.Format.JSON) {
298
+ const archive = await writeSerializedSpaceArchive({ space, echoHost: this._echoHost });
299
+ return { archive };
300
+ }
301
+
302
+ await using writer = await new SpaceArchiveWriter().open();
277
303
  await writer.begin({ spaceId: space.id });
278
304
  const rootUrl = space.automergeSpaceState.lastEpoch?.subject.assertion.automergeRoot;
279
305
  assertState(rootUrl, 'Space does not have a root URL');
@@ -283,15 +309,40 @@ export class SpacesServiceImpl implements SpacesService {
283
309
  await writer.writeDocument(documentId, data);
284
310
  }
285
311
 
312
+ const feeds = await space.getAllFeeds();
313
+ for (const feed of feeds) {
314
+ const archiveBlocks = feed.blocks.map((block) => ({
315
+ actorId: block.actorId,
316
+ sequence: block.sequence,
317
+ prevActorId: block.prevActorId,
318
+ prevSequence: block.prevSequence,
319
+ position: block.position,
320
+ timestamp: block.timestamp,
321
+ data: Buffer.from(block.data).toString('base64'),
322
+ }));
323
+ await writer.writeFeed(feed.feedId, feed.feedNamespace, archiveBlocks);
324
+ }
325
+
286
326
  const archive = await writer.finish();
287
327
  return { archive };
288
328
  }
289
329
 
290
- async importSpace(request: ImportSpaceRequest): Promise<ImportSpaceResponse> {
330
+ async importSpace(request: ImportSpaceRequest, options?: RequestOptions): Promise<ImportSpaceResponse> {
331
+ const ctx = options?.ctx ?? Context.default();
291
332
  const dataSpaceManager = await this._getDataSpaceManager();
333
+
334
+ const format = request.archive.format ?? detectSpaceArchiveFormat(request.archive);
335
+ if (format === SpaceArchive.Format.JSON) {
336
+ const serialized = readSerializedSpaceArchive(request.archive);
337
+ const space = await dataSpaceManager.createSpace(ctx);
338
+ await this._hydrateSpaceFromSerialized(space, serialized);
339
+ await this._updateMetrics();
340
+ return { newSpaceId: space.id };
341
+ }
342
+
292
343
  const extracted = await extractSpaceArchive(request.archive);
293
344
  invariant(extracted.metadata.echo?.currentRootUrl, 'Space archive does not contain a root URL');
294
- const space = await dataSpaceManager.createSpace({
345
+ const space = await dataSpaceManager.createSpace(ctx, {
295
346
  documents: extracted.documents,
296
347
  rootUrl: extracted.metadata.echo?.currentRootUrl as AutomergeUrl,
297
348
  });
@@ -299,7 +350,49 @@ export class SpacesServiceImpl implements SpacesService {
299
350
  return { newSpaceId: space.id };
300
351
  }
301
352
 
302
- private async _joinByAdmission({ credential }: ContactAdmission): Promise<JoinSpaceResponse> {
353
+ /**
354
+ * Populate a freshly-created space with the objects and feed messages described in a {@link SerializedSpace}.
355
+ *
356
+ * Objects are written directly into the space's automerge root document as inline
357
+ * {@link ObjectStructure} entries; feed messages are appended to the appropriate queue
358
+ * via {@link EchoHost.queuesService}.
359
+ */
360
+ private async _hydrateSpaceFromSerialized(
361
+ space: DataSpace,
362
+ serialized: ReturnType<typeof readSerializedSpaceArchive>,
363
+ ): Promise<void> {
364
+ const databaseRoot = space.databaseRoot;
365
+ assertState(databaseRoot, 'Space database root is not ready');
366
+
367
+ databaseRoot.handle.change((doc: DatabaseDirectory) => {
368
+ if (!doc.objects) {
369
+ doc.objects = {};
370
+ }
371
+ for (const obj of serialized.objects) {
372
+ doc.objects[obj.id] = objJsonToObjectStructure(obj);
373
+ }
374
+ });
375
+
376
+ for (const feed of serialized.feeds ?? []) {
377
+ if (feed.messages.length === 0) {
378
+ continue;
379
+ }
380
+ const namespace =
381
+ feed.namespace === 'trace' ? FeedProtocol.WellKnownNamespaces.trace : FeedProtocol.WellKnownNamespaces.data;
382
+ try {
383
+ await this._echoHost.queuesService.insertIntoQueue({
384
+ spaceId: space.id,
385
+ queueId: feed.feedObjectId,
386
+ subspaceTag: namespace,
387
+ objects: feed.messages.map((message) => JSON.stringify(message)),
388
+ });
389
+ } catch (err) {
390
+ log.warn('failed to import feed data', { feedObjectId: feed.feedObjectId, error: err });
391
+ }
392
+ }
393
+ }
394
+
395
+ private async _joinByAdmission(ctx: Context, { credential }: ContactAdmission): Promise<JoinSpaceResponse> {
303
396
  const assertion = getCredentialAssertion(credential);
304
397
  invariant(assertion['@type'] === 'dxos.halo.credentials.SpaceMember', 'Invalid credential');
305
398
  const myIdentity = this._identityManager.identity;
@@ -308,9 +401,10 @@ export class SpacesServiceImpl implements SpacesService {
308
401
  const dataSpaceManager = await this._getDataSpaceManager();
309
402
  let dataSpace = dataSpaceManager.spaces.get(assertion.spaceKey);
310
403
  if (!dataSpace) {
311
- dataSpace = await dataSpaceManager.acceptSpace({
404
+ dataSpace = await dataSpaceManager.acceptSpace(ctx, {
312
405
  spaceKey: assertion.spaceKey,
313
406
  genesisFeedKey: assertion.genesisFeedKey,
407
+ tags: assertion.tags,
314
408
  });
315
409
  await myIdentity.controlPipeline.writer.write({ credential: { credential } });
316
410
  }
@@ -363,6 +457,8 @@ export class SpacesServiceImpl implements SpacesService {
363
457
  }),
364
458
  ),
365
459
  creator: space.inner.spaceState.creator?.key,
460
+ tags: space.tags,
461
+ membershipPolicy: space.membershipPolicy,
366
462
  cache: space.cache,
367
463
  metrics: space.metrics,
368
464
  edgeReplication: space.getEdgeReplicationSetting(),
@@ -4,6 +4,7 @@
4
4
 
5
5
  import { Trigger } from '@dxos/async';
6
6
  import { type AuthenticatingInvitation, type CancellableInvitation, InvitationEncoder } from '@dxos/client-protocol';
7
+ import { Context } from '@dxos/context';
7
8
  import { invariant } from '@dxos/invariant';
8
9
  import { Invitation } from '@dxos/protocols/proto/dxos/client/services';
9
10
  import { type DeviceProfileDocument } from '@dxos/protocols/proto/dxos/halo/credentials';
@@ -229,7 +230,7 @@ export const createInvitation = async (
229
230
  };
230
231
 
231
232
  if (host instanceof ServiceContext) {
232
- return host.invitationsManager.createInvitation({
233
+ return host.invitationsManager.createInvitation(new Context(), {
233
234
  kind: Invitation.Kind.SPACE,
234
235
  ...options,
235
236
  });
@@ -246,7 +247,7 @@ export const acceptInvitation = (
246
247
  invitation = sanitizeInvitation(invitation);
247
248
 
248
249
  if (guest instanceof ServiceContext) {
249
- return guest.invitationsManager.acceptInvitation({
250
+ return guest.invitationsManager.acceptInvitation(new Context(), {
250
251
  invitation,
251
252
  deviceProfile: guestDeviceProfile,
252
253
  });
@@ -29,7 +29,6 @@ import * as SqlTransaction from '@dxos/sql-sqlite/SqlTransaction';
29
29
  import { type MaybePromise } from '@dxos/util';
30
30
 
31
31
  import { ClientServicesHost } from '../services';
32
-
33
32
  import { WorkerSession } from './worker-session';
34
33
 
35
34
  // NOTE: Keep as RpcPorts to avoid dependency on @dxos/rpc-tunnel so we don't depend on browser-specific apis.
@@ -131,20 +130,32 @@ export class WorkerRuntime {
131
130
  async start(): Promise<void> {
132
131
  log('starting...');
133
132
  try {
133
+ log('worker-runtime: acquiring liveness lock (background)');
134
134
  void this._livenessLock.acquire();
135
135
 
136
136
  // Steal the lock from the other worker.
137
+ log('worker-runtime: broadcasting stop to displace previous worker');
137
138
  this._broadcastChannel = new BroadcastChannel(this._channel);
138
139
  this._broadcastChannel.postMessage({ action: 'stop' });
139
140
  this._broadcastChannel.onmessage = async (event) => {
140
141
  if (event.data?.action === 'stop') {
142
+ log('worker-runtime: received stop broadcast');
141
143
  await this.stop();
142
144
  }
143
145
  };
144
146
 
147
+ log('worker-runtime: acquiring storage lock');
145
148
  await this._acquireLock();
149
+ log('worker-runtime: storage lock acquired, resolving config');
146
150
  this._config = await this._configProvider();
151
+ log('worker-runtime: config resolved');
152
+ this._signalTelemetryEnabled = this._config.get('runtime.client.signalTelemetryEnabled') ?? false;
153
+ const observabilityGroup = this._config.get('runtime.client.observabilityGroup');
154
+ if (observabilityGroup) {
155
+ this._signalMetadataTags.group = observabilityGroup;
156
+ }
147
157
  const signals = this._config.get('runtime.services.signaling');
158
+ log('worker-runtime: initializing client services host');
148
159
  this._clientServices.initialize({
149
160
  config: this._config,
150
161
  signalManager: this._config.get('runtime.client.edgeFeatures')?.signaling
@@ -154,8 +165,10 @@ export class WorkerRuntime {
154
165
  : new MemorySignalManager(new MemorySignalManagerContext()), // TODO(dmaretskyi): Inject this context.
155
166
  transportFactory: this._transportFactory,
156
167
  });
168
+ log('worker-runtime: client services host initialized, opening');
157
169
 
158
170
  await this._clientServices.open(new Context());
171
+ log('worker-runtime: client services host opened, signalling ready');
159
172
  this._ready.wake(undefined);
160
173
  log('started');
161
174
  setIdentityTags({
@@ -176,12 +189,36 @@ export class WorkerRuntime {
176
189
  this._releaseLock();
177
190
  this._broadcastChannel?.close();
178
191
  this._broadcastChannel = undefined;
179
- await this._clientServices.close();
192
+ await this._clientServices.close(Context.default());
180
193
  await this._runtime.dispose();
181
194
  await this._onStop?.();
182
195
  await this._livenessLock.release();
183
196
  }
184
197
 
198
+ /**
199
+ * Update signaling telemetry tags from a client-supplied config overlay.
200
+ *
201
+ * The worker services outlive individual client connections, so the first client seeds the
202
+ * worker's core config (storage, signaling, edge features). For fields that can legitimately
203
+ * differ per tab — `observabilityGroup` and `signalTelemetryEnabled` — this method lets later
204
+ * connections refresh the signal metadata the worker attaches to its signaling requests
205
+ * (last-writer-wins, matching the pre-DX-930 per-session RPC behaviour).
206
+ */
207
+ updateSignalMetadata(config: Config): void {
208
+ const observabilityGroup = config.get('runtime.client.observabilityGroup');
209
+ if (observabilityGroup) {
210
+ this._signalMetadataTags.group = observabilityGroup;
211
+ } else {
212
+ // Clear stale group so a later config that removes observabilityGroup stops attributing
213
+ // telemetry to the previous client's group (last-writer-wins).
214
+ delete this._signalMetadataTags.group;
215
+ }
216
+ const signalTelemetryEnabled = config.get('runtime.client.signalTelemetryEnabled');
217
+ if (signalTelemetryEnabled !== undefined) {
218
+ this._signalTelemetryEnabled = signalTelemetryEnabled;
219
+ }
220
+ }
221
+
185
222
  /**
186
223
  * Create a new session.
187
224
  */
@@ -214,10 +251,6 @@ export class WorkerRuntime {
214
251
  !this._signalMetadataTags.origin || this._signalMetadataTags.origin === session.origin,
215
252
  `worker origin changed from ${this._signalMetadataTags.origin} to ${session.origin}?`,
216
253
  );
217
- if (session.observabilityGroup) {
218
- this._signalMetadataTags.group = session.observabilityGroup;
219
- }
220
- this._signalTelemetryEnabled = session.signalTelemetryEnabled ?? false;
221
254
  this._signalMetadataTags.origin = session.origin;
222
255
  this._sessions.add(session);
223
256
 
@@ -41,10 +41,6 @@ export class WorkerSession {
41
41
  @logInfo
42
42
  public origin?: string;
43
43
 
44
- // TODO(nf): factor out?
45
- public observabilityGroup?: string;
46
- public signalTelemetryEnabled?: boolean;
47
-
48
44
  @logInfo
49
45
  public lockKey?: string;
50
46
 
@@ -55,21 +51,21 @@ export class WorkerSession {
55
51
  this._serviceHost = serviceHost;
56
52
 
57
53
  const middleware: Pick<ClientRpcServerProps, 'handleCall' | 'handleStream'> = {
58
- handleCall: async (method, params, handler) => {
54
+ handleCall: async (method, params, handler, options) => {
59
55
  const error = await readySignal.wait({ timeout: PROXY_CONNECTION_TIMEOUT });
60
56
  if (error) {
61
57
  throw error;
62
58
  }
63
59
 
64
- return handler(method, params);
60
+ return handler(method, params, options);
65
61
  },
66
- handleStream: async (method, params, handler) => {
62
+ handleStream: async (method, params, handler, options) => {
67
63
  const error = await readySignal.wait({ timeout: PROXY_CONNECTION_TIMEOUT });
68
64
  if (error) {
69
65
  throw error;
70
66
  }
71
67
 
72
- return handler(method, params);
68
+ return handler(method, params, options);
73
69
  },
74
70
  };
75
71
 
@@ -95,8 +91,6 @@ export class WorkerSession {
95
91
  start: async (request) => {
96
92
  this.origin = request.origin;
97
93
  this.lockKey = request.lockKey;
98
- this.observabilityGroup = request.observabilityGroup;
99
- this.signalTelemetryEnabled = request.signalTelemetryEnabled;
100
94
  this._startTrigger.wake();
101
95
  },
102
96
 
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const DXOS_VERSION = "0.8.4-main.c85a9c8dae";
1
+ export const DXOS_VERSION = "0.8.4-main.cb12b3f963";