@dxos/client-services 0.6.12-main.5cc132e → 0.6.12-main.78ddbdf

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 (66) hide show
  1. package/dist/lib/browser/{chunk-7FTR2DGP.mjs → chunk-XSFLJVDP.mjs} +5154 -5061
  2. package/dist/lib/browser/chunk-XSFLJVDP.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +3 -3
  4. package/dist/lib/browser/index.mjs.map +3 -3
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/testing/index.mjs +4 -5
  7. package/dist/lib/browser/testing/index.mjs.map +2 -2
  8. package/dist/lib/node/{chunk-KUHUPLSZ.cjs → chunk-F3WGFGEN.cjs} +4980 -4887
  9. package/dist/lib/node/chunk-F3WGFGEN.cjs.map +7 -0
  10. package/dist/lib/node/index.cjs +46 -46
  11. package/dist/lib/node/index.cjs.map +3 -3
  12. package/dist/lib/node/meta.json +1 -1
  13. package/dist/lib/node/testing/index.cjs +11 -12
  14. package/dist/lib/node/testing/index.cjs.map +2 -2
  15. package/dist/lib/node-esm/{chunk-QGQGMTAW.mjs → chunk-3HDLTAT2.mjs} +5142 -5047
  16. package/dist/lib/node-esm/chunk-3HDLTAT2.mjs.map +7 -0
  17. package/dist/lib/node-esm/index.mjs +4 -3
  18. package/dist/lib/node-esm/index.mjs.map +3 -3
  19. package/dist/lib/node-esm/meta.json +1 -1
  20. package/dist/lib/node-esm/testing/index.mjs +5 -5
  21. package/dist/lib/node-esm/testing/index.mjs.map +2 -2
  22. package/dist/types/src/packlets/identity/identity-manager.d.ts +19 -7
  23. package/dist/types/src/packlets/identity/identity-manager.d.ts.map +1 -1
  24. package/dist/types/src/packlets/identity/identity.d.ts +7 -1
  25. package/dist/types/src/packlets/identity/identity.d.ts.map +1 -1
  26. package/dist/types/src/packlets/invitations/invitation-host-extension.d.ts.map +1 -1
  27. package/dist/types/src/packlets/services/service-context.d.ts +3 -4
  28. package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
  29. package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
  30. package/dist/types/src/packlets/spaces/data-space-manager.d.ts +1 -2
  31. package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
  32. package/dist/types/src/packlets/spaces/data-space.d.ts +1 -2
  33. package/dist/types/src/packlets/spaces/data-space.d.ts.map +1 -1
  34. package/dist/types/src/packlets/spaces/edge-feed-replicator.d.ts +1 -0
  35. package/dist/types/src/packlets/spaces/edge-feed-replicator.d.ts.map +1 -1
  36. package/dist/types/src/packlets/spaces/edge-feed-replicator.test.d.ts +2 -0
  37. package/dist/types/src/packlets/spaces/edge-feed-replicator.test.d.ts.map +1 -0
  38. package/dist/types/src/packlets/spaces/epoch-migrations.d.ts +1 -1
  39. package/dist/types/src/packlets/spaces/epoch-migrations.d.ts.map +1 -1
  40. package/dist/types/src/packlets/testing/test-builder.d.ts +1 -2
  41. package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
  42. package/dist/types/src/packlets/worker/worker-runtime.d.ts.map +1 -1
  43. package/dist/types/src/version.d.ts +1 -1
  44. package/package.json +39 -38
  45. package/src/packlets/identity/identity-manager.test.ts +1 -1
  46. package/src/packlets/identity/identity-manager.ts +35 -19
  47. package/src/packlets/identity/identity.test.ts +126 -236
  48. package/src/packlets/identity/identity.ts +38 -8
  49. package/src/packlets/invitations/invitation-host-extension.ts +0 -3
  50. package/src/packlets/invitations/invitations-handler.ts +1 -1
  51. package/src/packlets/services/service-context.ts +24 -12
  52. package/src/packlets/services/service-host.ts +2 -2
  53. package/src/packlets/spaces/data-space-manager.ts +4 -1
  54. package/src/packlets/spaces/data-space.ts +7 -2
  55. package/src/packlets/spaces/edge-feed-replicator.test.ts +244 -0
  56. package/src/packlets/spaces/edge-feed-replicator.ts +59 -21
  57. package/src/packlets/spaces/epoch-migrations.ts +2 -2
  58. package/src/packlets/testing/test-builder.ts +1 -2
  59. package/src/packlets/worker/worker-runtime.ts +2 -2
  60. package/src/version.ts +1 -1
  61. package/dist/lib/browser/chunk-7FTR2DGP.mjs.map +0 -7
  62. package/dist/lib/node/chunk-KUHUPLSZ.cjs.map +0 -7
  63. package/dist/lib/node-esm/chunk-QGQGMTAW.mjs.map +0 -7
  64. package/dist/types/src/packlets/services/automerge-host.test.d.ts +0 -2
  65. package/dist/types/src/packlets/services/automerge-host.test.d.ts.map +0 -1
  66. package/src/packlets/services/automerge-host.test.ts +0 -62
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/client-services",
3
- "version": "0.6.12-main.5cc132e",
3
+ "version": "0.6.12-main.78ddbdf",
4
4
  "description": "DXOS client services implementation",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -47,47 +47,48 @@
47
47
  "dependencies": {
48
48
  "cbor-x": "^1.5.4",
49
49
  "platform": "^1.3.6",
50
- "@dxos/async": "0.6.12-main.5cc132e",
51
- "@dxos/automerge": "0.6.12-main.5cc132e",
52
- "@dxos/client-protocol": "0.6.12-main.5cc132e",
53
- "@dxos/config": "0.6.12-main.5cc132e",
54
- "@dxos/codec-protobuf": "0.6.12-main.5cc132e",
55
- "@dxos/context": "0.6.12-main.5cc132e",
56
- "@dxos/crypto": "0.6.12-main.5cc132e",
57
- "@dxos/credentials": "0.6.12-main.5cc132e",
58
- "@dxos/debug": "0.6.12-main.5cc132e",
59
- "@dxos/echo-db": "0.6.12-main.5cc132e",
60
- "@dxos/echo-schema": "0.6.12-main.5cc132e",
61
- "@dxos/echo-pipeline": "0.6.12-main.5cc132e",
62
- "@dxos/edge-client": "0.6.12-main.5cc132e",
63
- "@dxos/echo-protocol": "0.6.12-main.5cc132e",
64
- "@dxos/feed-store": "0.6.12-main.5cc132e",
65
- "@dxos/indexing": "0.6.12-main.5cc132e",
66
- "@dxos/keyring": "0.6.12-main.5cc132e",
67
- "@dxos/keys": "0.6.12-main.5cc132e",
68
- "@dxos/lock-file": "0.6.12-main.5cc132e",
69
- "@dxos/invariant": "0.6.12-main.5cc132e",
70
- "@dxos/kv-store": "0.6.12-main.5cc132e",
71
- "@dxos/log": "0.6.12-main.5cc132e",
72
- "@dxos/messaging": "0.6.12-main.5cc132e",
73
- "@dxos/node-std": "0.6.12-main.5cc132e",
74
- "@dxos/network-manager": "0.6.12-main.5cc132e",
75
- "@dxos/protocols": "0.6.12-main.5cc132e",
76
- "@dxos/random-access-storage": "0.6.12-main.5cc132e",
77
- "@dxos/teleport": "0.6.12-main.5cc132e",
78
- "@dxos/rpc": "0.6.12-main.5cc132e",
79
- "@dxos/teleport-extension-gossip": "0.6.12-main.5cc132e",
80
- "@dxos/teleport-extension-object-sync": "0.6.12-main.5cc132e",
81
- "@dxos/timeframe": "0.6.12-main.5cc132e",
82
- "@dxos/tracing": "0.6.12-main.5cc132e",
83
- "@dxos/util": "0.6.12-main.5cc132e",
84
- "@dxos/websocket-rpc": "0.6.12-main.5cc132e"
50
+ "@dxos/async": "0.6.12-main.78ddbdf",
51
+ "@dxos/client-protocol": "0.6.12-main.78ddbdf",
52
+ "@dxos/config": "0.6.12-main.78ddbdf",
53
+ "@dxos/codec-protobuf": "0.6.12-main.78ddbdf",
54
+ "@dxos/automerge": "0.6.12-main.78ddbdf",
55
+ "@dxos/context": "0.6.12-main.78ddbdf",
56
+ "@dxos/crypto": "0.6.12-main.78ddbdf",
57
+ "@dxos/credentials": "0.6.12-main.78ddbdf",
58
+ "@dxos/debug": "0.6.12-main.78ddbdf",
59
+ "@dxos/echo-db": "0.6.12-main.78ddbdf",
60
+ "@dxos/echo-pipeline": "0.6.12-main.78ddbdf",
61
+ "@dxos/echo-protocol": "0.6.12-main.78ddbdf",
62
+ "@dxos/echo-schema": "0.6.12-main.78ddbdf",
63
+ "@dxos/feed-store": "0.6.12-main.78ddbdf",
64
+ "@dxos/indexing": "0.6.12-main.78ddbdf",
65
+ "@dxos/edge-client": "0.6.12-main.78ddbdf",
66
+ "@dxos/invariant": "0.6.12-main.78ddbdf",
67
+ "@dxos/keyring": "0.6.12-main.78ddbdf",
68
+ "@dxos/keys": "0.6.12-main.78ddbdf",
69
+ "@dxos/lock-file": "0.6.12-main.78ddbdf",
70
+ "@dxos/log": "0.6.12-main.78ddbdf",
71
+ "@dxos/kv-store": "0.6.12-main.78ddbdf",
72
+ "@dxos/messaging": "0.6.12-main.78ddbdf",
73
+ "@dxos/network-manager": "0.6.12-main.78ddbdf",
74
+ "@dxos/node-std": "0.6.12-main.78ddbdf",
75
+ "@dxos/random-access-storage": "0.6.12-main.78ddbdf",
76
+ "@dxos/protocols": "0.6.12-main.78ddbdf",
77
+ "@dxos/teleport": "0.6.12-main.78ddbdf",
78
+ "@dxos/rpc": "0.6.12-main.78ddbdf",
79
+ "@dxos/teleport-extension-gossip": "0.6.12-main.78ddbdf",
80
+ "@dxos/timeframe": "0.6.12-main.78ddbdf",
81
+ "@dxos/tracing": "0.6.12-main.78ddbdf",
82
+ "@dxos/teleport-extension-object-sync": "0.6.12-main.78ddbdf",
83
+ "@dxos/util": "0.6.12-main.78ddbdf",
84
+ "@dxos/websocket-rpc": "0.6.12-main.78ddbdf"
85
85
  },
86
86
  "devDependencies": {
87
87
  "@types/platform": "^1.3.4",
88
88
  "@types/readable-stream": "^2.3.9",
89
- "@dxos/signal": "0.6.12-main.5cc132e",
90
- "@dxos/test-utils": "0.6.12-main.5cc132e"
89
+ "get-port-please": "^3.1.1",
90
+ "@dxos/signal": "0.6.12-main.78ddbdf",
91
+ "@dxos/test-utils": "0.6.12-main.78ddbdf"
91
92
  },
92
93
  "publishConfig": {
93
94
  "access": "public"
@@ -50,7 +50,7 @@ describe('identity/identity-manager', () => {
50
50
  blobStore,
51
51
  metadataStore,
52
52
  });
53
- const identityManager = new IdentityManager(metadataStore, keyring, feedStore, spaceManager);
53
+ const identityManager = new IdentityManager({ metadataStore, keyring, feedStore, spaceManager });
54
54
 
55
55
  return {
56
56
  metadataStore,
@@ -7,6 +7,7 @@ import { Event } from '@dxos/async';
7
7
  import { Context } from '@dxos/context';
8
8
  import { createCredentialSignerWithKey, CredentialGenerator } from '@dxos/credentials';
9
9
  import { type MetadataStore, type SpaceManager, type SwarmIdentity } from '@dxos/echo-pipeline';
10
+ import { type EdgeConnection } from '@dxos/edge-client';
10
11
  import { type FeedStore } from '@dxos/feed-store';
11
12
  import { invariant } from '@dxos/invariant';
12
13
  import { type Keyring } from '@dxos/keyring';
@@ -14,6 +15,7 @@ import { PublicKey } from '@dxos/keys';
14
15
  import { log } from '@dxos/log';
15
16
  import { trace } from '@dxos/protocols';
16
17
  import { Device, DeviceKind } from '@dxos/protocols/proto/dxos/client/services';
18
+ import { type Runtime } from '@dxos/protocols/proto/dxos/config';
17
19
  import { type FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
18
20
  import { type IdentityRecord, type SpaceMetadata } from '@dxos/protocols/proto/dxos/echo/metadata';
19
21
  import {
@@ -63,9 +65,20 @@ export type CreateIdentityOptions = {
63
65
  deviceProfile?: DeviceProfileDocument;
64
66
  };
65
67
 
66
- export type IdentityManagerRuntimeParams = {
68
+ export type IdentityManagerCallbacks = {
69
+ onIdentityConstruction?: (identity: Identity) => void;
70
+ };
71
+
72
+ export type IdentityManagerParams = {
73
+ metadataStore: MetadataStore;
74
+ keyring: Keyring;
75
+ feedStore: FeedStore<FeedMessage>;
76
+ spaceManager: SpaceManager;
77
+ edgeConnection?: EdgeConnection;
78
+ edgeFeatures?: Runtime.Client.EdgeFeatures;
67
79
  devicePresenceAnnounceInterval?: number;
68
80
  devicePresenceOfflineTimeout?: number;
81
+ callbacks?: IdentityManagerCallbacks;
69
82
  };
70
83
 
71
84
  // TODO(dmaretskyi): Rename: represents the peer's state machine.
@@ -73,28 +86,29 @@ export type IdentityManagerRuntimeParams = {
73
86
  export class IdentityManager {
74
87
  readonly stateUpdate = new Event();
75
88
 
76
- private _identity?: Identity;
89
+ private readonly _metadataStore: MetadataStore;
90
+ private readonly _keyring: Keyring;
91
+ private readonly _feedStore: FeedStore<FeedMessage>;
92
+ private readonly _spaceManager: SpaceManager;
77
93
  private readonly _devicePresenceAnnounceInterval: number;
78
94
  private readonly _devicePresenceOfflineTimeout: number;
95
+ private readonly _edgeConnection: EdgeConnection | undefined;
96
+ private readonly _edgeFeatures: Runtime.Client.EdgeFeatures | undefined;
97
+ private readonly _callbacks: IdentityManagerCallbacks | undefined;
98
+
99
+ private _identity?: Identity;
79
100
 
80
- // TODO(burdon): IdentityManagerParams.
81
101
  // TODO(dmaretskyi): Perhaps this should take/generate the peerKey outside of an initialized identity.
82
- constructor(
83
- private readonly _metadataStore: MetadataStore,
84
- private readonly _keyring: Keyring,
85
- private readonly _feedStore: FeedStore<FeedMessage>,
86
- private readonly _spaceManager: SpaceManager,
87
- params?: IdentityManagerRuntimeParams,
88
- private readonly _callbacks?: {
89
- onIdentityConstruction?: (identity: Identity) => void;
90
- },
91
- ) {
92
- const {
93
- devicePresenceAnnounceInterval = DEVICE_PRESENCE_ANNOUNCE_INTERVAL,
94
- devicePresenceOfflineTimeout = DEVICE_PRESENCE_OFFLINE_TIMEOUT,
95
- } = params ?? {};
96
- this._devicePresenceAnnounceInterval = devicePresenceAnnounceInterval;
97
- this._devicePresenceOfflineTimeout = devicePresenceOfflineTimeout;
102
+ constructor(params: IdentityManagerParams) {
103
+ this._metadataStore = params.metadataStore;
104
+ this._keyring = params.keyring;
105
+ this._feedStore = params.feedStore;
106
+ this._spaceManager = params.spaceManager;
107
+ this._edgeConnection = params.edgeConnection;
108
+ this._edgeFeatures = params.edgeFeatures;
109
+ this._devicePresenceAnnounceInterval = params.devicePresenceAnnounceInterval ?? DEVICE_PRESENCE_ANNOUNCE_INTERVAL;
110
+ this._devicePresenceOfflineTimeout = params.devicePresenceOfflineTimeout ?? DEVICE_PRESENCE_OFFLINE_TIMEOUT;
111
+ this._callbacks = params.callbacks;
98
112
  }
99
113
 
100
114
  get identity() {
@@ -360,6 +374,8 @@ export class IdentityManager {
360
374
  signer: this._keyring,
361
375
  identityKey: identityRecord.identityKey,
362
376
  deviceKey: identityRecord.deviceKey,
377
+ edgeConnection: this._edgeConnection,
378
+ edgeFeatures: this._edgeFeatures,
363
379
  });
364
380
  log('done', { identityKey: identityRecord.identityKey });
365
381
  this._callbacks?.onIdentityConstruction?.(identity);
@@ -4,6 +4,7 @@
4
4
 
5
5
  import { onTestFinished, describe, expect, test } from 'vitest';
6
6
 
7
+ import { Event } from '@dxos/async';
7
8
  import { Context } from '@dxos/context';
8
9
  import { CredentialGenerator, verifyCredential } from '@dxos/credentials';
9
10
  import {
@@ -15,7 +16,9 @@ import {
15
16
  SpaceProtocol,
16
17
  valueEncoding,
17
18
  } from '@dxos/echo-pipeline';
19
+ import { type EdgeConnection, type MessageListener } from '@dxos/edge-client';
18
20
  import { FeedFactory, FeedStore } from '@dxos/feed-store';
21
+ import { type FeedWrapper } from '@dxos/feed-store';
19
22
  import { Keyring } from '@dxos/keyring';
20
23
  import { type PublicKey } from '@dxos/keys';
21
24
  import { MemorySignalManager, MemorySignalManagerContext } from '@dxos/messaging';
@@ -41,16 +44,108 @@ const createStores = () => {
41
44
 
42
45
  describe('identity/identity', () => {
43
46
  test('create', async () => {
47
+ const setup = await setupIdentity();
48
+
49
+ await writeGenesisCredential(setup);
50
+
51
+ // Wait for identity to be ready.
52
+ await setup.identity.ready();
53
+ const identitySigner = setup.identity.getIdentityCredentialSigner();
54
+ const credential = await identitySigner.createCredential({
55
+ subject: setup.identityKey,
56
+ assertion: {
57
+ '@type': 'dxos.halo.credentials.IdentityProfile',
58
+ profile: {
59
+ displayName: 'Alice',
60
+ },
61
+ },
62
+ });
63
+
64
+ expect(credential.issuer).toEqual(setup.identityKey);
65
+ expect(await verifyCredential(credential)).toEqual({ kind: 'pass' });
66
+ });
67
+
68
+ test('two devices', async () => {
69
+ const signalContext = new MemorySignalManagerContext();
70
+
71
+ const owner = await setupIdentity({ signalContext });
72
+ await writeGenesisCredential(owner);
73
+ await owner.identity.ready();
74
+
75
+ const secondDevice = (
76
+ await setupIdentity({
77
+ signalContext,
78
+ spaceKey: owner.spaceKey,
79
+ identityKey: owner.identityKey,
80
+ genesisFeedKey: owner.controlFeed.key,
81
+ })
82
+ ).identity;
83
+
84
+ //
85
+ // Second device admission
86
+ //
87
+ {
88
+ const signer = owner.identity.getIdentityCredentialSigner();
89
+ void owner.identity.controlPipeline.writer.write({
90
+ credential: {
91
+ credential: await signer.createCredential({
92
+ subject: secondDevice.deviceKey,
93
+ assertion: {
94
+ '@type': 'dxos.halo.credentials.AuthorizedDevice',
95
+ identityKey: owner.identityKey,
96
+ deviceKey: secondDevice.deviceKey,
97
+ },
98
+ }),
99
+ },
100
+ });
101
+
102
+ await secondDevice.ready();
103
+ }
104
+
105
+ expect(Array.from(owner.identity.authorizedDeviceKeys.keys())).toEqual([owner.deviceKey, secondDevice.deviceKey]);
106
+ expect(Array.from(secondDevice.authorizedDeviceKeys.keys())).toEqual([owner.deviceKey, secondDevice.deviceKey]);
107
+ });
108
+
109
+ test('edge feed replicator', async () => {
110
+ let replicationStarted = false;
111
+ const connectedEvent = new Event();
112
+ const setup = await setupIdentity({
113
+ edgeConnection: {
114
+ connected: connectedEvent,
115
+ open: async () => {},
116
+ close: async () => {},
117
+ addListener: (_: MessageListener): (() => void) => {
118
+ return () => {};
119
+ },
120
+ send: async (_) => {
121
+ replicationStarted = true;
122
+ },
123
+ } as EdgeConnection,
124
+ });
125
+
126
+ await writeGenesisCredential(setup);
127
+ connectedEvent.emit();
128
+
129
+ await expect.poll(() => replicationStarted).toBeTruthy();
130
+ });
131
+
132
+ const setupIdentity = async (args?: {
133
+ signalContext?: MemorySignalManagerContext;
134
+ spaceKey?: PublicKey;
135
+ identityKey?: PublicKey;
136
+ genesisFeedKey?: PublicKey;
137
+ edgeConnection?: EdgeConnection;
138
+ }): Promise<TestIdentitySetup> => {
44
139
  const { storage, metadataStore, blobStore } = createStores();
45
140
 
46
141
  const keyring = new Keyring();
47
- const identityKey = await keyring.createKey();
48
142
  const deviceKey = await keyring.createKey();
49
- const spaceKey = await keyring.createKey();
143
+ const identityKey = args?.identityKey ?? (await keyring.createKey());
144
+ const spaceKey = args?.spaceKey ?? (await keyring.createKey());
50
145
 
51
146
  const feedStore = new FeedStore<FeedMessage>({
52
147
  factory: new FeedFactory<FeedMessage>({
53
- root: storage.createDirectory('feeds'),
148
+ root: storage.createDirectory(),
54
149
  signer: keyring,
55
150
  hypercore: {
56
151
  valueEncoding,
@@ -76,7 +171,7 @@ describe('identity/identity', () => {
76
171
  },
77
172
  blobStore,
78
173
  networkManager: new SwarmNetworkManager({
79
- signalManager: new MemorySignalManager(new MemorySignalManagerContext()),
174
+ signalManager: new MemorySignalManager(args?.signalContext ?? new MemorySignalManagerContext()),
80
175
  transportFactory: MemoryTransportFactory,
81
176
  }),
82
177
  });
@@ -86,7 +181,7 @@ describe('identity/identity', () => {
86
181
  id: await createIdFromSpaceKey(spaceKey),
87
182
  spaceKey,
88
183
  protocol,
89
- genesisFeed: controlFeed,
184
+ genesisFeed: args?.genesisFeedKey ? await feedStore.openFeed(args.genesisFeedKey) : controlFeed,
90
185
  feedProvider: (feedKey) => feedStore.openFeed(feedKey),
91
186
  memberKey: identityKey,
92
187
  metadataStore,
@@ -102,242 +197,37 @@ describe('identity/identity', () => {
102
197
  identityKey,
103
198
  deviceKey,
104
199
  space,
200
+ edgeFeatures: args?.edgeConnection && { feedReplicator: true },
201
+ edgeConnection: args?.edgeConnection,
105
202
  });
106
203
 
107
204
  await identity.open(new Context());
108
205
  onTestFinished(() => identity.close(new Context()));
206
+ return { identity, identityKey, keyring, deviceKey, controlFeed, spaceKey, dataFeed };
207
+ };
109
208
 
110
- //
111
- // Identity genesis
112
- //
113
- {
114
- const generator = new CredentialGenerator(keyring, identityKey, deviceKey);
115
- const credentials = [
116
- ...(await generator.createSpaceGenesis(spaceKey, controlFeed.key)),
117
- await generator.createDeviceAuthorization(deviceKey),
118
- await generator.createFeedAdmission(spaceKey, dataFeed.key, AdmittedFeed.Designation.DATA),
119
- ];
120
-
121
- for (const credential of credentials) {
122
- await identity.controlPipeline.writer.write({
123
- credential: { credential },
124
- });
125
- }
126
- }
127
-
128
- // Wait for identity to be ready.
129
- await identity.ready();
130
- const identitySigner = identity.getIdentityCredentialSigner();
131
- const credential = await identitySigner.createCredential({
132
- subject: identityKey,
133
- assertion: {
134
- '@type': 'dxos.halo.credentials.IdentityProfile',
135
- profile: {
136
- displayName: 'Alice',
137
- },
138
- },
139
- });
140
-
141
- expect(credential.issuer).toEqual(identityKey);
142
- expect(await verifyCredential(credential)).toEqual({ kind: 'pass' });
143
- });
144
-
145
- test('two devices', async () => {
146
- const signalContext = new MemorySignalManagerContext();
147
-
148
- let spaceKey: PublicKey;
149
- let identityKey: PublicKey;
150
- let genesisFeedKey: PublicKey;
151
- let identity1: Identity;
152
- let identity2: Identity;
153
-
154
- //
155
- // First device
156
- //
157
- {
158
- const { storage, metadataStore, blobStore } = createStores();
159
-
160
- const keyring = new Keyring();
161
- identityKey = await keyring.createKey();
162
- const deviceKey = await keyring.createKey();
163
- spaceKey = await keyring.createKey();
164
-
165
- const feedStore = new FeedStore<FeedMessage>({
166
- factory: new FeedFactory<FeedMessage>({
167
- root: storage.createDirectory(),
168
- signer: keyring,
169
- hypercore: {
170
- valueEncoding,
171
- },
172
- }),
173
- });
174
-
175
- const createFeed = async () => {
176
- const feedKey = await keyring.createKey();
177
- return feedStore.openFeed(feedKey, { writable: true });
178
- };
179
-
180
- const controlFeed = await createFeed();
181
- genesisFeedKey = controlFeed.key;
182
-
183
- const dataFeed = await createFeed();
184
-
185
- const protocol = new SpaceProtocol({
186
- topic: spaceKey,
187
- swarmIdentity: {
188
- peerKey: deviceKey,
189
- identityKey,
190
- credentialProvider: MOCK_AUTH_PROVIDER, // createHaloAuthProvider(createCredentialSignerWithKey(keyring, device_key)),
191
- credentialAuthenticator: MOCK_AUTH_VERIFIER, // createHaloAuthVerifier(() => identity.authorizedDeviceKeys),
192
- },
193
- blobStore,
194
- networkManager: new SwarmNetworkManager({
195
- signalManager: new MemorySignalManager(signalContext),
196
- transportFactory: MemoryTransportFactory,
197
- }),
198
- });
199
-
200
- await metadataStore.setIdentityRecord({ haloSpace: { key: spaceKey }, identityKey, deviceKey });
201
- const space = new Space({
202
- id: await createIdFromSpaceKey(spaceKey),
203
- spaceKey,
204
- protocol,
205
- genesisFeed: controlFeed,
206
- feedProvider: (feedKey) => feedStore.openFeed(feedKey),
207
- memberKey: identityKey,
208
- metadataStore,
209
- onDelegatedInvitationStatusChange: async () => {},
210
- onMemberRolesChanged: async () => {},
211
- });
212
- await space.setControlFeed(controlFeed);
213
- await space.setDataFeed(dataFeed);
214
-
215
- const identity = (identity1 = new Identity({
216
- signer: keyring,
217
- identityKey,
218
- deviceKey,
219
- space,
220
- }));
221
-
222
- await identity.open(new Context());
223
- onTestFinished(() => identity.close(new Context()));
224
-
225
- //
226
- // Identity genesis
227
- //
228
- {
229
- const generator = new CredentialGenerator(keyring, identityKey, deviceKey);
230
- const credentials = [
231
- ...(await generator.createSpaceGenesis(spaceKey, controlFeed.key)),
232
- await generator.createDeviceAuthorization(deviceKey),
233
- await generator.createFeedAdmission(spaceKey, dataFeed.key, AdmittedFeed.Designation.DATA),
234
- ];
235
-
236
- for (const credential of credentials) {
237
- await identity.controlPipeline.writer.write({
238
- credential: { credential },
239
- });
240
- }
241
- }
242
-
243
- // Wait for identity to be ready.
244
- await identity.ready();
245
- }
246
-
247
- //
248
- // Second device
249
- //
250
- {
251
- const { storage, metadataStore, blobStore } = createStores();
252
-
253
- const keyring = new Keyring();
254
- const deviceKey = await keyring.createKey();
255
-
256
- const feedStore = new FeedStore<FeedMessage>({
257
- factory: new FeedFactory<FeedMessage>({
258
- root: storage.createDirectory(),
259
- signer: keyring,
260
- hypercore: {
261
- valueEncoding,
262
- },
263
- }),
264
- });
265
-
266
- const createFeed = async () => {
267
- const feedKey = await keyring.createKey();
268
- return feedStore.openFeed(feedKey, { writable: true });
269
- };
270
-
271
- const controlFeed = await createFeed();
272
- const dataFeed = await createFeed();
273
-
274
- const protocol = new SpaceProtocol({
275
- topic: spaceKey,
276
- swarmIdentity: {
277
- peerKey: deviceKey,
278
- identityKey,
279
- credentialProvider: MOCK_AUTH_PROVIDER, // createHaloAuthProvider(createCredentialSignerWithKey(keyring, device_key)),
280
- credentialAuthenticator: MOCK_AUTH_VERIFIER, // createHaloAuthVerifier(() => identity.authorizedDeviceKeys),
281
- },
282
- blobStore,
283
- networkManager: new SwarmNetworkManager({
284
- signalManager: new MemorySignalManager(signalContext),
285
- transportFactory: MemoryTransportFactory,
286
- }),
287
- });
288
-
289
- await metadataStore.setIdentityRecord({
290
- haloSpace: { key: spaceKey },
291
- identityKey: identity1.identityKey,
292
- deviceKey,
293
- });
294
- const space = new Space({
295
- id: await createIdFromSpaceKey(spaceKey),
296
- spaceKey,
297
- protocol,
298
- genesisFeed: await feedStore.openFeed(genesisFeedKey),
299
- feedProvider: (feedKey) => feedStore.openFeed(feedKey),
300
- memberKey: identityKey,
301
- metadataStore,
302
- onDelegatedInvitationStatusChange: async () => {},
303
- onMemberRolesChanged: async () => {},
209
+ const writeGenesisCredential = async (setup: TestIdentitySetup) => {
210
+ const generator = new CredentialGenerator(setup.keyring, setup.identityKey, setup.deviceKey);
211
+ const credentials = [
212
+ ...(await generator.createSpaceGenesis(setup.spaceKey, setup.controlFeed.key)),
213
+ await generator.createDeviceAuthorization(setup.deviceKey),
214
+ await generator.createFeedAdmission(setup.spaceKey, setup.dataFeed.key, AdmittedFeed.Designation.DATA),
215
+ ];
216
+
217
+ for (const credential of credentials) {
218
+ await setup.identity.controlPipeline.writer.write({
219
+ credential: { credential },
304
220
  });
305
- await space.setControlFeed(controlFeed);
306
- await space.setDataFeed(dataFeed);
307
-
308
- const identity = (identity2 = new Identity({
309
- signer: keyring,
310
- identityKey: identity1.identityKey,
311
- deviceKey,
312
- space,
313
- }));
314
-
315
- await identity.open(new Context());
316
- onTestFinished(() => identity.close(new Context()));
317
221
  }
318
-
319
- //
320
- // Second device admission
321
- //
322
- {
323
- const signer = identity1.getIdentityCredentialSigner();
324
- void identity1.controlPipeline.writer.write({
325
- credential: {
326
- credential: await signer.createCredential({
327
- subject: identity2.deviceKey,
328
- assertion: {
329
- '@type': 'dxos.halo.credentials.AuthorizedDevice',
330
- identityKey: identity1.identityKey,
331
- deviceKey: identity2.deviceKey,
332
- },
333
- }),
334
- },
335
- });
336
-
337
- await identity2.ready();
338
- }
339
-
340
- expect(Array.from(identity1.authorizedDeviceKeys.keys())).toEqual([identity1.deviceKey, identity2.deviceKey]);
341
- expect(Array.from(identity2.authorizedDeviceKeys.keys())).toEqual([identity1.deviceKey, identity2.deviceKey]);
342
- });
222
+ };
343
223
  });
224
+
225
+ type TestIdentitySetup = {
226
+ identity: Identity;
227
+ keyring: Keyring;
228
+ identityKey: PublicKey;
229
+ deviceKey: PublicKey;
230
+ spaceKey: PublicKey;
231
+ controlFeed: FeedWrapper<FeedMessage>;
232
+ dataFeed: FeedWrapper<FeedMessage>;
233
+ };