@dxos/client-services 0.8.4-main.3eb6e50203 → 0.8.4-main.3fbcb4aa9b
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.
- package/dist/lib/browser/{chunk-675YEKTQ.mjs → chunk-OBWND4JJ.mjs} +1701 -3078
- package/dist/lib/browser/chunk-OBWND4JJ.mjs.map +7 -0
- package/dist/lib/browser/{chunk-NQSC7HOE.mjs → chunk-XJRPB3GA.mjs} +1 -1
- package/dist/lib/browser/index.mjs +106 -194
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/packlets/diagnostics/browser-diagnostics-broadcast.mjs +1 -6
- package/dist/lib/browser/packlets/diagnostics/browser-diagnostics-broadcast.mjs.map +2 -2
- package/dist/lib/browser/packlets/diagnostics/diagnostics-broadcast.mjs +1 -1
- package/dist/lib/browser/packlets/locks/browser.mjs +9 -49
- package/dist/lib/browser/packlets/locks/browser.mjs.map +2 -2
- package/dist/lib/browser/packlets/locks/node.mjs +4 -22
- package/dist/lib/browser/packlets/locks/node.mjs.map +2 -2
- package/dist/lib/browser/testing/index.mjs +9 -22
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/node-esm/{chunk-PKEGMOQ4.mjs → chunk-2DT3MZRL.mjs} +1 -1
- package/dist/lib/node-esm/{chunk-RUUAV2BD.mjs → chunk-PB2SRGWV.mjs} +1701 -3078
- package/dist/lib/node-esm/chunk-PB2SRGWV.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +106 -194
- package/dist/lib/node-esm/index.mjs.map +3 -3
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/packlets/diagnostics/browser-diagnostics-broadcast.mjs +1 -6
- package/dist/lib/node-esm/packlets/diagnostics/browser-diagnostics-broadcast.mjs.map +2 -2
- package/dist/lib/node-esm/packlets/diagnostics/diagnostics-broadcast.mjs +1 -1
- package/dist/lib/node-esm/packlets/locks/browser.mjs +9 -49
- package/dist/lib/node-esm/packlets/locks/browser.mjs.map +2 -2
- package/dist/lib/node-esm/packlets/locks/node.mjs +4 -22
- package/dist/lib/node-esm/packlets/locks/node.mjs.map +2 -2
- package/dist/lib/node-esm/testing/index.mjs +9 -22
- package/dist/lib/node-esm/testing/index.mjs.map +3 -3
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/packlets/agents/edge-agent-manager.d.ts +3 -2
- package/dist/types/src/packlets/agents/edge-agent-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/agents/edge-agent-service.d.ts +2 -1
- package/dist/types/src/packlets/agents/edge-agent-service.d.ts.map +1 -1
- package/dist/types/src/packlets/devices/devices-service.d.ts.map +1 -1
- package/dist/types/src/packlets/devtools/devtools.d.ts.map +1 -1
- package/dist/types/src/packlets/devtools/feeds.d.ts.map +1 -1
- package/dist/types/src/packlets/devtools/keys.d.ts.map +1 -1
- package/dist/types/src/packlets/devtools/metadata.d.ts.map +1 -1
- package/dist/types/src/packlets/devtools/network.d.ts.map +1 -1
- package/dist/types/src/packlets/devtools/spaces.d.ts.map +1 -1
- package/dist/types/src/packlets/diagnostics/browser-diagnostics-broadcast.d.ts.map +1 -1
- package/dist/types/src/packlets/diagnostics/diagnostics-broadcast.d.ts.map +1 -1
- package/dist/types/src/packlets/diagnostics/diagnostics-collector.d.ts.map +1 -1
- package/dist/types/src/packlets/diagnostics/diagnostics.d.ts +2 -3
- package/dist/types/src/packlets/diagnostics/diagnostics.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/authenticator.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/contacts-service.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity-manager.d.ts +3 -3
- package/dist/types/src/packlets/identity/identity-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity-recovery-manager.d.ts +6 -5
- package/dist/types/src/packlets/identity/identity-recovery-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity-service.d.ts +6 -10
- package/dist/types/src/packlets/identity/identity-service.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity.d.ts +6 -9
- package/dist/types/src/packlets/identity/identity.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/device-invitation-protocol.d.ts +2 -1
- package/dist/types/src/packlets/invitations/device-invitation-protocol.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/edge-invitation-handler.d.ts +1 -1
- package/dist/types/src/packlets/invitations/edge-invitation-handler.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitation-guest-extenstion.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitation-host-extension.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitation-protocol.d.ts +5 -1
- package/dist/types/src/packlets/invitations/invitation-protocol.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitation-state.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitation-topology.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitations-handler.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitations-manager.d.ts +3 -3
- package/dist/types/src/packlets/invitations/invitations-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitations-service.d.ts +3 -3
- package/dist/types/src/packlets/invitations/invitations-service.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts +2 -1
- package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/utils.d.ts.map +1 -1
- package/dist/types/src/packlets/locks/browser.d.ts.map +1 -1
- package/dist/types/src/packlets/locks/node.d.ts.map +1 -1
- package/dist/types/src/packlets/logging/logging-service.d.ts.map +1 -1
- package/dist/types/src/packlets/network/network-service.d.ts +5 -4
- package/dist/types/src/packlets/network/network-service.d.ts.map +1 -1
- package/dist/types/src/packlets/services/client-rpc-server.d.ts +3 -3
- package/dist/types/src/packlets/services/client-rpc-server.d.ts.map +1 -1
- package/dist/types/src/packlets/services/feed-syncer.d.ts +22 -4
- package/dist/types/src/packlets/services/feed-syncer.d.ts.map +1 -1
- package/dist/types/src/packlets/services/feed-syncer.test.d.ts +2 -0
- package/dist/types/src/packlets/services/feed-syncer.test.d.ts.map +1 -0
- package/dist/types/src/packlets/services/service-context.d.ts +2 -4
- package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-host.d.ts +2 -3
- package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-registry.d.ts.map +1 -1
- package/dist/types/src/packlets/services/util.d.ts.map +1 -1
- package/dist/types/src/packlets/space-export/archive-format.d.ts +9 -0
- package/dist/types/src/packlets/space-export/archive-format.d.ts.map +1 -0
- package/dist/types/src/packlets/space-export/index.d.ts +4 -1
- package/dist/types/src/packlets/space-export/index.d.ts.map +1 -1
- package/dist/types/src/packlets/space-export/serialized-space-reader.d.ts +23 -0
- package/dist/types/src/packlets/space-export/serialized-space-reader.d.ts.map +1 -0
- package/dist/types/src/packlets/space-export/serialized-space-writer.d.ts +36 -0
- package/dist/types/src/packlets/space-export/serialized-space-writer.d.ts.map +1 -0
- package/dist/types/src/packlets/space-export/space-archive-reader.d.ts +9 -1
- package/dist/types/src/packlets/space-export/space-archive-reader.d.ts.map +1 -1
- package/dist/types/src/packlets/space-export/space-archive-writer.d.ts +7 -1
- package/dist/types/src/packlets/space-export/space-archive-writer.d.ts.map +1 -1
- package/dist/types/src/packlets/space-export/space-archive.test.d.ts +2 -0
- package/dist/types/src/packlets/space-export/space-archive.test.d.ts.map +1 -0
- package/dist/types/src/packlets/spaces/automerge-space-state.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/data-space-manager.d.ts +18 -12
- package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/data-space.d.ts +24 -7
- package/dist/types/src/packlets/spaces/data-space.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/edge-feed-replicator.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/epoch-migrations.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/genesis.d.ts +2 -1
- package/dist/types/src/packlets/spaces/genesis.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/notarization-plugin.d.ts +1 -4
- package/dist/types/src/packlets/spaces/notarization-plugin.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/spaces-service.d.ts +10 -7
- package/dist/types/src/packlets/spaces/spaces-service.d.ts.map +1 -1
- package/dist/types/src/packlets/storage/level.d.ts.map +1 -1
- package/dist/types/src/packlets/storage/profile-archive.d.ts.map +1 -1
- package/dist/types/src/packlets/storage/storage.d.ts.map +1 -1
- package/dist/types/src/packlets/storage/util.d.ts.map +1 -1
- package/dist/types/src/packlets/system/system-service.d.ts +1 -1
- package/dist/types/src/packlets/system/system-service.d.ts.map +1 -1
- package/dist/types/src/packlets/testing/credential-utils.d.ts.map +1 -1
- package/dist/types/src/packlets/testing/invitation-utils.d.ts.map +1 -1
- package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
- package/dist/types/src/packlets/worker/worker-runtime.d.ts +11 -1
- package/dist/types/src/packlets/worker/worker-runtime.d.ts.map +1 -1
- package/dist/types/src/packlets/worker/worker-session.d.ts +0 -2
- package/dist/types/src/packlets/worker/worker-session.d.ts.map +1 -1
- package/dist/types/src/testing/setup.d.ts.map +1 -1
- package/dist/types/src/version.d.ts +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +42 -49
- package/src/index.ts +1 -0
- package/src/packlets/agents/edge-agent-manager.ts +8 -5
- package/src/packlets/agents/edge-agent-service.ts +4 -2
- package/src/packlets/devices/devices-service.test.ts +0 -1
- package/src/packlets/devtools/devtools.ts +0 -1
- package/src/packlets/diagnostics/diagnostics.ts +1 -2
- package/src/packlets/identity/contacts-service.ts +0 -1
- package/src/packlets/identity/identity-manager.test.ts +5 -5
- package/src/packlets/identity/identity-manager.ts +18 -17
- package/src/packlets/identity/identity-recovery-manager.ts +20 -16
- package/src/packlets/identity/identity-service.test.ts +6 -27
- package/src/packlets/identity/identity-service.ts +13 -81
- package/src/packlets/identity/identity.test.ts +2 -2
- package/src/packlets/identity/identity.ts +9 -32
- package/src/packlets/invitations/device-invitation-protocol.ts +3 -2
- package/src/packlets/invitations/edge-invitation-handler.ts +9 -5
- package/src/packlets/invitations/invitation-host-extension.ts +7 -10
- package/src/packlets/invitations/invitation-protocol.ts +5 -1
- package/src/packlets/invitations/invitation-state.ts +1 -15
- package/src/packlets/invitations/invitations-handler.test.ts +4 -5
- package/src/packlets/invitations/invitations-handler.ts +67 -15
- package/src/packlets/invitations/invitations-manager.ts +40 -15
- package/src/packlets/invitations/invitations-service.ts +9 -9
- package/src/packlets/invitations/space-invitation-protocol.test.ts +17 -16
- package/src/packlets/invitations/space-invitation-protocol.ts +4 -3
- package/src/packlets/logging/logging-service.ts +15 -15
- package/src/packlets/network/network-service.test.ts +0 -1
- package/src/packlets/network/network-service.ts +10 -8
- package/src/packlets/services/client-rpc-server.ts +15 -12
- package/src/packlets/services/feed-syncer.test.ts +340 -0
- package/src/packlets/services/feed-syncer.ts +175 -25
- package/src/packlets/services/service-context.test.ts +3 -2
- package/src/packlets/services/service-context.ts +98 -41
- package/src/packlets/services/service-host.test.ts +8 -8
- package/src/packlets/services/service-host.ts +17 -27
- package/src/packlets/services/service-registry.test.ts +0 -1
- package/src/packlets/space-export/archive-format.ts +42 -0
- package/src/packlets/space-export/index.ts +4 -1
- package/src/packlets/space-export/serialized-space-reader.ts +111 -0
- package/src/packlets/space-export/serialized-space-writer.ts +252 -0
- package/src/packlets/space-export/space-archive-reader.ts +64 -3
- package/src/packlets/space-export/space-archive-writer.ts +38 -2
- package/src/packlets/space-export/space-archive.test.ts +461 -0
- package/src/packlets/spaces/data-space-manager.test.ts +79 -13
- package/src/packlets/spaces/data-space-manager.ts +75 -111
- package/src/packlets/spaces/data-space.ts +49 -29
- package/src/packlets/spaces/edge-feed-replicator.test.ts +1 -1
- package/src/packlets/spaces/edge-feed-replicator.ts +9 -7
- package/src/packlets/spaces/epoch-migrations.ts +3 -3
- package/src/packlets/spaces/genesis.ts +6 -1
- package/src/packlets/spaces/notarization-plugin.ts +2 -1
- package/src/packlets/spaces/spaces-service.test.ts +18 -11
- package/src/packlets/spaces/spaces-service.ts +113 -17
- package/src/packlets/testing/invitation-utils.ts +3 -2
- package/src/packlets/worker/worker-runtime.ts +39 -6
- package/src/packlets/worker/worker-session.ts +8 -14
- package/src/version.ts +1 -1
- package/dist/lib/browser/chunk-675YEKTQ.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-RUUAV2BD.mjs.map +0 -7
- package/dist/types/src/packlets/identity/default-space-state-machine.d.ts +0 -19
- package/dist/types/src/packlets/identity/default-space-state-machine.d.ts.map +0 -1
- package/src/packlets/identity/default-space-state-machine.ts +0 -44
- /package/dist/lib/browser/{chunk-NQSC7HOE.mjs.map → chunk-XJRPB3GA.mjs.map} +0 -0
- /package/dist/lib/node-esm/{chunk-PKEGMOQ4.mjs.map → chunk-2DT3MZRL.mjs.map} +0 -0
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { rmSync } from 'node:fs';
|
|
6
|
-
|
|
7
6
|
import { afterEach, describe, expect, onTestFinished, test } from 'vitest';
|
|
8
7
|
|
|
9
8
|
import { Trigger, asyncTimeout, latch } from '@dxos/async';
|
|
@@ -13,6 +12,7 @@ import { verifyPresentation } from '@dxos/credentials';
|
|
|
13
12
|
import { type PublicKey } from '@dxos/keys';
|
|
14
13
|
import { MemorySignalManagerContext } from '@dxos/messaging';
|
|
15
14
|
import { type Identity } from '@dxos/protocols/proto/dxos/client/services';
|
|
15
|
+
import { MembershipPolicy } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
16
16
|
import { type Credential } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
17
17
|
import { isNode } from '@dxos/util';
|
|
18
18
|
|
|
@@ -29,16 +29,16 @@ describe('ClientServicesHost', () => {
|
|
|
29
29
|
test('open and close', async () => {
|
|
30
30
|
const host = createServiceHost(new Config(), new MemorySignalManagerContext());
|
|
31
31
|
await host.open(new Context());
|
|
32
|
-
await host.close();
|
|
32
|
+
await host.close(Context.default());
|
|
33
33
|
});
|
|
34
34
|
|
|
35
35
|
test('queryCredentials', async () => {
|
|
36
36
|
const host = createServiceHost(new Config(), new MemorySignalManagerContext());
|
|
37
37
|
await host.open(new Context());
|
|
38
|
-
onTestFinished(() => host.close());
|
|
38
|
+
onTestFinished(() => host.close(Context.default()));
|
|
39
39
|
|
|
40
40
|
await host.services.IdentityService!.createIdentity({});
|
|
41
|
-
const { spaceKey } = await host.services.SpacesService!.createSpace();
|
|
41
|
+
const { spaceKey } = await host.services.SpacesService!.createSpace({ membershipPolicy: MembershipPolicy.INVITE });
|
|
42
42
|
|
|
43
43
|
const stream = host.services.SpacesService!.queryCredentials({ spaceKey });
|
|
44
44
|
const [done, tick] = latch({ count: 3 });
|
|
@@ -54,7 +54,7 @@ describe('ClientServicesHost', () => {
|
|
|
54
54
|
test('write and query credentials', async () => {
|
|
55
55
|
const host = createServiceHost(new Config(), new MemorySignalManagerContext());
|
|
56
56
|
await host.open(new Context());
|
|
57
|
-
onTestFinished(() => host.close());
|
|
57
|
+
onTestFinished(() => host.close(Context.default()));
|
|
58
58
|
|
|
59
59
|
await host.services.IdentityService!.createIdentity({});
|
|
60
60
|
|
|
@@ -91,7 +91,7 @@ describe('ClientServicesHost', () => {
|
|
|
91
91
|
test('sign presentation', async () => {
|
|
92
92
|
const host = createServiceHost(new Config(), new MemorySignalManagerContext());
|
|
93
93
|
await host.open(new Context());
|
|
94
|
-
onTestFinished(() => host.close());
|
|
94
|
+
onTestFinished(() => host.close(Context.default()));
|
|
95
95
|
|
|
96
96
|
await host.services.IdentityService!.createIdentity({});
|
|
97
97
|
|
|
@@ -128,7 +128,7 @@ describe('ClientServicesHost', () => {
|
|
|
128
128
|
expect(host.context.storage.size).to.exist;
|
|
129
129
|
|
|
130
130
|
await asyncTimeout(host.reset(), 1000);
|
|
131
|
-
await host.close();
|
|
131
|
+
await host.close(Context.default());
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
{
|
|
@@ -146,7 +146,7 @@ describe('ClientServicesHost', () => {
|
|
|
146
146
|
});
|
|
147
147
|
await expect(asyncTimeout(trigger.wait(), 200)).rejects.toBeInstanceOf(Error);
|
|
148
148
|
await stream?.close();
|
|
149
|
-
await host.close();
|
|
149
|
+
await host.close(Context.default());
|
|
150
150
|
}
|
|
151
151
|
});
|
|
152
152
|
});
|
|
@@ -7,12 +7,11 @@ import * as Effect from 'effect/Effect';
|
|
|
7
7
|
|
|
8
8
|
import { Event, synchronized } from '@dxos/async';
|
|
9
9
|
import { type ClientServices, clientServiceBundle } from '@dxos/client-protocol';
|
|
10
|
-
import { type Config } from '@dxos/config';
|
|
10
|
+
import { type Config, resolveTelemetryTag } from '@dxos/config';
|
|
11
11
|
import { Context } from '@dxos/context';
|
|
12
12
|
import { EdgeClient, type EdgeConnection, EdgeHttpClient, createStubEdgeIdentity } from '@dxos/edge-client';
|
|
13
13
|
import { RuntimeProvider } from '@dxos/effect';
|
|
14
14
|
import { invariant } from '@dxos/invariant';
|
|
15
|
-
import { PublicKey } from '@dxos/keys';
|
|
16
15
|
import { type LevelDB } from '@dxos/kv-store';
|
|
17
16
|
import { log } from '@dxos/log';
|
|
18
17
|
import { EdgeSignalManager, type SignalManager, WebsocketSignalManager } from '@dxos/messaging';
|
|
@@ -22,12 +21,11 @@ import {
|
|
|
22
21
|
createIceProvider,
|
|
23
22
|
createRtcTransportFactory,
|
|
24
23
|
} from '@dxos/network-manager';
|
|
25
|
-
import { trace } from '@dxos/protocols';
|
|
26
24
|
import { SystemStatus } from '@dxos/protocols/proto/dxos/client/services';
|
|
27
25
|
import { type Storage } from '@dxos/random-access-storage';
|
|
28
26
|
import * as SqlExport from '@dxos/sql-sqlite/SqlExport';
|
|
29
27
|
import type * as SqlTransaction from '@dxos/sql-sqlite/SqlTransaction';
|
|
30
|
-
import {
|
|
28
|
+
import { trace as Trace } from '@dxos/tracing';
|
|
31
29
|
import { WebsocketRpcClient } from '@dxos/websocket-rpc';
|
|
32
30
|
|
|
33
31
|
import { EdgeAgentServiceImpl } from '../agents';
|
|
@@ -47,7 +45,6 @@ import { NetworkServiceImpl } from '../network';
|
|
|
47
45
|
import { SpacesServiceImpl } from '../spaces';
|
|
48
46
|
import { createLevel, createStorageObjects } from '../storage';
|
|
49
47
|
import { SystemServiceImpl } from '../system';
|
|
50
|
-
|
|
51
48
|
import { ServiceContext, type ServiceContextRuntimeProps } from './service-context';
|
|
52
49
|
import { ServiceRegistry } from './service-registry';
|
|
53
50
|
|
|
@@ -87,8 +84,6 @@ export class ClientServicesHost {
|
|
|
87
84
|
private readonly _serviceRegistry: ServiceRegistry<ClientServices>;
|
|
88
85
|
private readonly _systemService: SystemServiceImpl;
|
|
89
86
|
private readonly _loggingService: LoggingServiceImpl;
|
|
90
|
-
private readonly _tracingService = TRACE_PROCESSOR.createTraceSender();
|
|
91
|
-
|
|
92
87
|
private readonly _statusUpdate = new Event<void>();
|
|
93
88
|
|
|
94
89
|
private _config?: Config;
|
|
@@ -147,7 +142,7 @@ export class ClientServicesHost {
|
|
|
147
142
|
void this.open(new Context());
|
|
148
143
|
}
|
|
149
144
|
},
|
|
150
|
-
onRelease: () => this.close(),
|
|
145
|
+
onRelease: () => this.close(Context.default()),
|
|
151
146
|
});
|
|
152
147
|
}
|
|
153
148
|
|
|
@@ -176,7 +171,6 @@ export class ClientServicesHost {
|
|
|
176
171
|
|
|
177
172
|
this._serviceRegistry = new ServiceRegistry<ClientServices>(clientServiceBundle, {
|
|
178
173
|
SystemService: this._systemService,
|
|
179
|
-
TracingService: this._tracingService,
|
|
180
174
|
});
|
|
181
175
|
}
|
|
182
176
|
|
|
@@ -244,9 +238,6 @@ export class ClientServicesHost {
|
|
|
244
238
|
if (this._runtimeProps.enableVectorIndexing === undefined) {
|
|
245
239
|
this._runtimeProps.enableVectorIndexing = config?.get('runtime.client.enableVectorIndexing', false);
|
|
246
240
|
}
|
|
247
|
-
if (this._runtimeProps.enableLocalQueues === undefined) {
|
|
248
|
-
this._runtimeProps.enableLocalQueues = config?.get('runtime.client.enableLocalQueues', false);
|
|
249
|
-
}
|
|
250
241
|
|
|
251
242
|
invariant(!this._config, 'config already set');
|
|
252
243
|
this._config = config;
|
|
@@ -262,8 +253,9 @@ export class ClientServicesHost {
|
|
|
262
253
|
|
|
263
254
|
const endpoint = config?.get('runtime.services.edge.url');
|
|
264
255
|
if (endpoint) {
|
|
265
|
-
|
|
266
|
-
this.
|
|
256
|
+
const clientTag = resolveTelemetryTag(config);
|
|
257
|
+
this._edgeConnection = new EdgeClient(createStubEdgeIdentity(), { socketEndpoint: endpoint, clientTag });
|
|
258
|
+
this._edgeHttpClient = new EdgeHttpClient(endpoint, { clientTag });
|
|
267
259
|
}
|
|
268
260
|
|
|
269
261
|
const {
|
|
@@ -302,8 +294,7 @@ export class ClientServicesHost {
|
|
|
302
294
|
return;
|
|
303
295
|
}
|
|
304
296
|
|
|
305
|
-
|
|
306
|
-
log.trace('dxos.client-services.host.open', trace.begin({ id: traceId }));
|
|
297
|
+
log('opening service host');
|
|
307
298
|
|
|
308
299
|
invariant(this._config, 'config not set');
|
|
309
300
|
invariant(this._storage, 'storage not set');
|
|
@@ -348,8 +339,7 @@ export class ClientServicesHost {
|
|
|
348
339
|
this._serviceContext.identityManager,
|
|
349
340
|
this._serviceContext.recoveryManager,
|
|
350
341
|
this._serviceContext.keyring,
|
|
351
|
-
() => this.
|
|
352
|
-
(params) => this._createIdentity(params),
|
|
342
|
+
(params, ctx) => this._createIdentity(params, ctx),
|
|
353
343
|
(profile) => this._serviceContext.broadcastProfileUpdate(profile),
|
|
354
344
|
);
|
|
355
345
|
|
|
@@ -369,6 +359,7 @@ export class ClientServicesHost {
|
|
|
369
359
|
SpacesService: new SpacesServiceImpl(
|
|
370
360
|
this._serviceContext.identityManager,
|
|
371
361
|
this._serviceContext.spaceManager,
|
|
362
|
+
this._serviceContext.echoHost,
|
|
372
363
|
dataSpaceManagerProvider,
|
|
373
364
|
),
|
|
374
365
|
|
|
@@ -383,7 +374,6 @@ export class ClientServicesHost {
|
|
|
383
374
|
),
|
|
384
375
|
|
|
385
376
|
LoggingService: this._loggingService,
|
|
386
|
-
TracingService: this._tracingService,
|
|
387
377
|
|
|
388
378
|
// TODO(burdon): Move to new protobuf definitions.
|
|
389
379
|
DevtoolsHost: new DevtoolsServiceImpl({
|
|
@@ -395,8 +385,13 @@ export class ClientServicesHost {
|
|
|
395
385
|
EdgeAgentService: new EdgeAgentServiceImpl(agentManagerProvider, this._edgeConnection),
|
|
396
386
|
});
|
|
397
387
|
|
|
388
|
+
log('service-host: opening service context...');
|
|
398
389
|
await this._serviceContext.open(ctx);
|
|
390
|
+
log('service-host: service context opened');
|
|
391
|
+
|
|
392
|
+
log('service-host: opening identity service...');
|
|
399
393
|
await identityService.open();
|
|
394
|
+
log('service-host: identity service opened');
|
|
400
395
|
|
|
401
396
|
const devtoolsProxy = this._config?.get('runtime.client.devtoolsProxy');
|
|
402
397
|
if (devtoolsProxy) {
|
|
@@ -415,12 +410,11 @@ export class ClientServicesHost {
|
|
|
415
410
|
this._statusUpdate.emit();
|
|
416
411
|
const deviceKey = this._serviceContext.identityManager.identity?.deviceKey;
|
|
417
412
|
log('opened', { deviceKey });
|
|
418
|
-
log.trace('dxos.client-services.host.open', trace.end({ id: traceId }));
|
|
419
413
|
}
|
|
420
414
|
|
|
421
415
|
@synchronized
|
|
422
416
|
@Trace.span()
|
|
423
|
-
async close(): Promise<void> {
|
|
417
|
+
async close(ctx: Context): Promise<void> {
|
|
424
418
|
if (!this._open) {
|
|
425
419
|
return;
|
|
426
420
|
}
|
|
@@ -439,9 +433,6 @@ export class ClientServicesHost {
|
|
|
439
433
|
}
|
|
440
434
|
|
|
441
435
|
async reset(): Promise<void> {
|
|
442
|
-
const traceId = PublicKey.random().toHex();
|
|
443
|
-
log.trace('dxos.sdk.client-services-host.reset', trace.begin({ id: traceId }));
|
|
444
|
-
|
|
445
436
|
log.info('resetting...');
|
|
446
437
|
// Emit this status update immediately so app returns to fallback.
|
|
447
438
|
// This state is never cleared because the app reloads.
|
|
@@ -456,12 +447,11 @@ export class ClientServicesHost {
|
|
|
456
447
|
}
|
|
457
448
|
await this._storage!.reset();
|
|
458
449
|
log.info('reset');
|
|
459
|
-
log.trace('dxos.sdk.client-services-host.reset', trace.end({ id: traceId }));
|
|
460
450
|
await this._callbacks?.onReset?.();
|
|
461
451
|
}
|
|
462
452
|
|
|
463
|
-
private async _createIdentity(params: CreateIdentityOptions) {
|
|
464
|
-
const identity = await this._serviceContext.createIdentity(params);
|
|
453
|
+
private async _createIdentity(params: CreateIdentityOptions, ctx?: Context) {
|
|
454
|
+
const identity = await this._serviceContext.createIdentity(params, ctx);
|
|
465
455
|
await this._serviceContext.initialized.wait();
|
|
466
456
|
return identity;
|
|
467
457
|
}
|
|
@@ -15,7 +15,6 @@ import { createLinkedPorts, createProtoRpcPeer, createServiceBundle } from '@dxo
|
|
|
15
15
|
|
|
16
16
|
import { SystemServiceImpl } from '../system';
|
|
17
17
|
import { createServiceContext } from '../testing';
|
|
18
|
-
|
|
19
18
|
import { ServiceRegistry } from './service-registry';
|
|
20
19
|
|
|
21
20
|
// TODO(burdon): Create TestService (that doesn't require peers).
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { SpaceArchive } from '@dxos/protocols/proto/dxos/client/services';
|
|
6
|
+
|
|
7
|
+
const JSON_EXTENSIONS = ['.dx.json', '.dx.json.gz', '.json'];
|
|
8
|
+
const BINARY_EXTENSIONS = ['.tar', '.tar.gz'];
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Detect the format of a space archive.
|
|
12
|
+
*
|
|
13
|
+
* Uses filename extension as the primary signal, and falls back to sniffing
|
|
14
|
+
* the first bytes of the archive contents to distinguish JSON from tar.
|
|
15
|
+
*/
|
|
16
|
+
export const detectSpaceArchiveFormat = (archive: Pick<SpaceArchive, 'filename' | 'contents'>): SpaceArchive.Format => {
|
|
17
|
+
const filename = archive.filename?.toLowerCase() ?? '';
|
|
18
|
+
if (JSON_EXTENSIONS.some((ext) => filename.endsWith(ext))) {
|
|
19
|
+
return SpaceArchive.Format.JSON;
|
|
20
|
+
}
|
|
21
|
+
if (BINARY_EXTENSIONS.some((ext) => filename.endsWith(ext))) {
|
|
22
|
+
return SpaceArchive.Format.BINARY;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Fall back to byte sniffing: JSON archives start with '{' (0x7B) or whitespace.
|
|
26
|
+
const bytes = archive.contents;
|
|
27
|
+
if (bytes && bytes.length > 0) {
|
|
28
|
+
for (let i = 0; i < Math.min(bytes.length, 16); i++) {
|
|
29
|
+
const byte = bytes[i];
|
|
30
|
+
// Skip whitespace.
|
|
31
|
+
if (byte === 0x20 || byte === 0x09 || byte === 0x0a || byte === 0x0d) {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (byte === 0x7b /* '{' */) {
|
|
35
|
+
return SpaceArchive.Format.JSON;
|
|
36
|
+
}
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return SpaceArchive.Format.BINARY;
|
|
42
|
+
};
|
|
@@ -2,5 +2,8 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
export * from './
|
|
5
|
+
export * from './archive-format';
|
|
6
|
+
export * from './serialized-space-reader';
|
|
7
|
+
export * from './serialized-space-writer';
|
|
6
8
|
export * from './space-archive-reader';
|
|
9
|
+
export * from './space-archive-writer';
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type Obj } from '@dxos/echo';
|
|
6
|
+
import { type SerializedSpace } from '@dxos/echo-db';
|
|
7
|
+
import { type DatabaseDirectory, type ObjectStructure } from '@dxos/echo-protocol';
|
|
8
|
+
import { assertArgument } from '@dxos/invariant';
|
|
9
|
+
import { type SpaceArchive } from '@dxos/protocols/proto/dxos/client/services';
|
|
10
|
+
|
|
11
|
+
const ATTR_TYPE = '@type';
|
|
12
|
+
const ATTR_META = '@meta';
|
|
13
|
+
const ATTR_DELETED = '@deleted';
|
|
14
|
+
const ATTR_PARENT = '@parent';
|
|
15
|
+
const ATTR_RELATION_SOURCE = '@relationSource';
|
|
16
|
+
const ATTR_RELATION_TARGET = '@relationTarget';
|
|
17
|
+
|
|
18
|
+
const INTERNAL_KEYS: ReadonlySet<string> = new Set([
|
|
19
|
+
'id',
|
|
20
|
+
ATTR_TYPE,
|
|
21
|
+
ATTR_META,
|
|
22
|
+
ATTR_DELETED,
|
|
23
|
+
ATTR_PARENT,
|
|
24
|
+
ATTR_RELATION_SOURCE,
|
|
25
|
+
ATTR_RELATION_TARGET,
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Parse a JSON space archive.
|
|
30
|
+
*
|
|
31
|
+
* The archive contents are expected to be the UTF-8 encoding of
|
|
32
|
+
* `JSON.stringify(serializedSpace)`.
|
|
33
|
+
*/
|
|
34
|
+
export const readSerializedSpaceArchive = (archive: SpaceArchive): SerializedSpace => {
|
|
35
|
+
const text = new TextDecoder().decode(archive.contents);
|
|
36
|
+
const parsed = JSON.parse(text) as SerializedSpace;
|
|
37
|
+
assertArgument(typeof parsed === 'object' && parsed !== null, 'archive', 'Invalid JSON archive payload');
|
|
38
|
+
assertArgument(typeof parsed.version === 'number', 'archive', 'Missing SerializedSpace.version');
|
|
39
|
+
assertArgument(Array.isArray(parsed.objects), 'archive', 'Missing SerializedSpace.objects');
|
|
40
|
+
return parsed;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Convert an {@link Obj.JSON} back into an internal {@link ObjectStructure} suitable
|
|
45
|
+
* for embedding into a {@link DatabaseDirectory}.
|
|
46
|
+
*/
|
|
47
|
+
export const objJsonToObjectStructure = (obj: Obj.JSON): ObjectStructure => {
|
|
48
|
+
const data: Record<string, unknown> = {};
|
|
49
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
50
|
+
if (INTERNAL_KEYS.has(key)) {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
data[key] = value;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const system: NonNullable<ObjectStructure['system']> = {};
|
|
57
|
+
|
|
58
|
+
const type = obj[ATTR_TYPE];
|
|
59
|
+
if (type) {
|
|
60
|
+
system.type = { '/': type as string };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const parent = (obj as any)[ATTR_PARENT];
|
|
64
|
+
if (typeof parent === 'string') {
|
|
65
|
+
system.parent = { '/': parent };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const relationSource = (obj as any)[ATTR_RELATION_SOURCE];
|
|
69
|
+
const relationTarget = (obj as any)[ATTR_RELATION_TARGET];
|
|
70
|
+
if (typeof relationSource === 'string' || typeof relationTarget === 'string') {
|
|
71
|
+
system.kind = 'relation';
|
|
72
|
+
if (typeof relationSource === 'string') {
|
|
73
|
+
system.source = { '/': relationSource };
|
|
74
|
+
}
|
|
75
|
+
if (typeof relationTarget === 'string') {
|
|
76
|
+
system.target = { '/': relationTarget };
|
|
77
|
+
}
|
|
78
|
+
} else {
|
|
79
|
+
system.kind = 'object';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if ((obj as any)[ATTR_DELETED]) {
|
|
83
|
+
system.deleted = true;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const meta = (obj as any)[ATTR_META];
|
|
87
|
+
return {
|
|
88
|
+
system,
|
|
89
|
+
meta: {
|
|
90
|
+
keys: meta?.keys ?? [],
|
|
91
|
+
...(meta?.tags ? { tags: meta.tags } : {}),
|
|
92
|
+
},
|
|
93
|
+
data,
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Build a new {@link DatabaseDirectory} containing every object from the archive,
|
|
99
|
+
* keyed by object id. The caller is responsible for stamping the `access.spaceKey`
|
|
100
|
+
* and version fields after the document is created.
|
|
101
|
+
*/
|
|
102
|
+
export const buildDatabaseDirectoryFromObjects = (objects: readonly Obj.JSON[]): DatabaseDirectory => {
|
|
103
|
+
const map: Record<string, ObjectStructure> = {};
|
|
104
|
+
for (const obj of objects) {
|
|
105
|
+
map[obj.id] = objJsonToObjectStructure(obj);
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
objects: map,
|
|
109
|
+
links: {},
|
|
110
|
+
};
|
|
111
|
+
};
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type AutomergeUrl } from '@automerge/automerge-repo';
|
|
6
|
+
|
|
7
|
+
import { Context } from '@dxos/context';
|
|
8
|
+
import { type Obj } from '@dxos/echo';
|
|
9
|
+
import { type SerializedFeed, type SerializedSpace } from '@dxos/echo-db';
|
|
10
|
+
import { type EchoHost } from '@dxos/echo-pipeline';
|
|
11
|
+
import { type DatabaseDirectory, type ObjectStructure } from '@dxos/echo-protocol';
|
|
12
|
+
import { assertState, invariant } from '@dxos/invariant';
|
|
13
|
+
import { DXN, type IdentityDid, type SpaceId } from '@dxos/keys';
|
|
14
|
+
import { log } from '@dxos/log';
|
|
15
|
+
import { FeedProtocol } from '@dxos/protocols';
|
|
16
|
+
import { SpaceArchive } from '@dxos/protocols/proto/dxos/client/services';
|
|
17
|
+
import { createFilename } from '@dxos/util';
|
|
18
|
+
|
|
19
|
+
import { type DataSpace } from '../spaces/data-space';
|
|
20
|
+
|
|
21
|
+
const SERIALIZED_SPACE_VERSION = 1;
|
|
22
|
+
|
|
23
|
+
const FEED_TYPENAME = 'org.dxos.type.feed';
|
|
24
|
+
|
|
25
|
+
const ATTR_ID = 'id';
|
|
26
|
+
const ATTR_TYPE = '@type';
|
|
27
|
+
const ATTR_META = '@meta';
|
|
28
|
+
const ATTR_DELETED = '@deleted';
|
|
29
|
+
const ATTR_PARENT = '@parent';
|
|
30
|
+
const ATTR_RELATION_SOURCE = '@relationSource';
|
|
31
|
+
const ATTR_RELATION_TARGET = '@relationTarget';
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Canonical order of well-known system fields in a serialized object.
|
|
35
|
+
* All remaining `@*` fields follow in the order they appear on the source
|
|
36
|
+
* object, and finally the data fields are appended in their existing order.
|
|
37
|
+
*/
|
|
38
|
+
const SYSTEM_FIELD_ORDER: readonly string[] = [
|
|
39
|
+
ATTR_ID,
|
|
40
|
+
ATTR_TYPE,
|
|
41
|
+
ATTR_META,
|
|
42
|
+
ATTR_DELETED,
|
|
43
|
+
ATTR_PARENT,
|
|
44
|
+
ATTR_RELATION_SOURCE,
|
|
45
|
+
ATTR_RELATION_TARGET,
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Reorder the keys of an {@link Obj.JSON} so that system fields appear first
|
|
50
|
+
* (`id`, `@type`, `@meta`, then any other `@*` attributes), followed by data
|
|
51
|
+
* fields. The returned object has identical values to the input — only the key
|
|
52
|
+
* iteration order changes.
|
|
53
|
+
*/
|
|
54
|
+
export const orderObjJsonFields = (obj: Obj.JSON): Obj.JSON => {
|
|
55
|
+
const source = obj as Record<string, unknown>;
|
|
56
|
+
const result: Record<string, unknown> = {};
|
|
57
|
+
for (const key of SYSTEM_FIELD_ORDER) {
|
|
58
|
+
if (key in source) {
|
|
59
|
+
result[key] = source[key];
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
for (const key of Object.keys(source)) {
|
|
63
|
+
if (key.startsWith('@') && !(key in result)) {
|
|
64
|
+
result[key] = source[key];
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
for (const key of Object.keys(source)) {
|
|
68
|
+
if (!(key in result)) {
|
|
69
|
+
result[key] = source[key];
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return result as Obj.JSON;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export type WriteSerializedSpaceArchiveOptions = {
|
|
76
|
+
space: DataSpace;
|
|
77
|
+
echoHost: EchoHost;
|
|
78
|
+
exportedBy?: IdentityDid;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Write a JSON space archive from a live {@link DataSpace}.
|
|
83
|
+
*
|
|
84
|
+
* This runs entirely inside the worker and walks automerge documents directly —
|
|
85
|
+
* it does not require a client-side {@link EchoDatabase}. The output conforms to
|
|
86
|
+
* {@link SerializedSpace} and is compatible with the JSON format consumed by the
|
|
87
|
+
* importer in {@link readSerializedSpaceArchive}.
|
|
88
|
+
*/
|
|
89
|
+
export const writeSerializedSpaceArchive = async (
|
|
90
|
+
options: WriteSerializedSpaceArchiveOptions,
|
|
91
|
+
): Promise<SpaceArchive> => {
|
|
92
|
+
const { space, echoHost, exportedBy } = options;
|
|
93
|
+
|
|
94
|
+
const rootUrl = space.automergeSpaceState.lastEpoch?.subject.assertion.automergeRoot;
|
|
95
|
+
assertState(rootUrl, 'Space does not have a root URL');
|
|
96
|
+
const databaseRoot = space.databaseRoot;
|
|
97
|
+
assertState(databaseRoot, 'Space database root is not ready');
|
|
98
|
+
|
|
99
|
+
const rootDoc = databaseRoot.doc();
|
|
100
|
+
invariant(rootDoc, 'Space database root document is not loaded');
|
|
101
|
+
|
|
102
|
+
// Collect all object structures across the root doc and any linked docs.
|
|
103
|
+
const objects: Obj.JSON[] = [];
|
|
104
|
+
collectObjectsFromDoc(rootDoc, objects);
|
|
105
|
+
|
|
106
|
+
for (const linkedUrl of databaseRoot.getAllLinkedDocuments()) {
|
|
107
|
+
const handle = await echoHost.loadDoc<DatabaseDirectory>(Context.default(), linkedUrl as AutomergeUrl);
|
|
108
|
+
const doc = handle.doc();
|
|
109
|
+
if (!doc) {
|
|
110
|
+
log.warn('linked document did not load; skipping', { url: linkedUrl });
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
collectObjectsFromDoc(doc, objects);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Export queue/feed messages for every Feed object in the space.
|
|
117
|
+
const feeds = await exportFeedData(space, echoHost, objects);
|
|
118
|
+
|
|
119
|
+
const serialized: SerializedSpace = {
|
|
120
|
+
version: SERIALIZED_SPACE_VERSION,
|
|
121
|
+
timestamp: new Date().toISOString(),
|
|
122
|
+
originalSpaceId: space.id,
|
|
123
|
+
exportedBy,
|
|
124
|
+
createdAt: Date.now(),
|
|
125
|
+
objects,
|
|
126
|
+
...(feeds.length > 0 ? { feeds } : {}),
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const encoded = new TextEncoder().encode(JSON.stringify(serialized));
|
|
130
|
+
return {
|
|
131
|
+
filename: createFilename({ parts: [space.id], ext: 'dx.json' }),
|
|
132
|
+
contents: encoded,
|
|
133
|
+
format: SpaceArchive.Format.JSON,
|
|
134
|
+
};
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const collectObjectsFromDoc = (doc: DatabaseDirectory, out: Obj.JSON[]): void => {
|
|
138
|
+
const docObjects = doc.objects ?? {};
|
|
139
|
+
for (const [objectId, structure] of Object.entries(docObjects)) {
|
|
140
|
+
out.push(objectStructureToObjJson(objectId, structure));
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Convert an internal {@link ObjectStructure} into an {@link Obj.JSON}.
|
|
146
|
+
*
|
|
147
|
+
* Unlike the equivalent helper used for indexing, this preserves the object's
|
|
148
|
+
* `@meta` section so archives produced by this writer can be round-tripped
|
|
149
|
+
* through {@link Obj.fromJSON}.
|
|
150
|
+
*/
|
|
151
|
+
export const objectStructureToObjJson = (objectId: string, structure: ObjectStructure): Obj.JSON => {
|
|
152
|
+
const result: Record<string, unknown> = {
|
|
153
|
+
[ATTR_ID]: objectId,
|
|
154
|
+
[ATTR_TYPE]: (structure.system?.type?.['/'] ?? '') as any,
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
if (structure.meta) {
|
|
158
|
+
result[ATTR_META] = {
|
|
159
|
+
keys: structure.meta.keys ?? [],
|
|
160
|
+
...(structure.meta.tags ? { tags: structure.meta.tags } : {}),
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
if (structure.system?.deleted) {
|
|
164
|
+
result[ATTR_DELETED] = true;
|
|
165
|
+
}
|
|
166
|
+
if (structure.system?.parent) {
|
|
167
|
+
result[ATTR_PARENT] = structure.system.parent['/'];
|
|
168
|
+
}
|
|
169
|
+
if (structure.system?.source) {
|
|
170
|
+
result[ATTR_RELATION_SOURCE] = structure.system.source['/'];
|
|
171
|
+
}
|
|
172
|
+
if (structure.system?.target) {
|
|
173
|
+
result[ATTR_RELATION_TARGET] = structure.system.target['/'];
|
|
174
|
+
}
|
|
175
|
+
Object.assign(result, structure.data);
|
|
176
|
+
|
|
177
|
+
return result as Obj.JSON;
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
const exportFeedData = async (space: DataSpace, echoHost: EchoHost, objects: Obj.JSON[]): Promise<SerializedFeed[]> => {
|
|
181
|
+
const feeds: SerializedFeed[] = [];
|
|
182
|
+
const spaceId: SpaceId = space.id;
|
|
183
|
+
|
|
184
|
+
for (const obj of objects) {
|
|
185
|
+
if (obj[ATTR_TYPE] == null) {
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const typeDxn = DXN.tryParse(obj[ATTR_TYPE] as string);
|
|
190
|
+
if (typeDxn?.asTypeDXN()?.type !== FEED_TYPENAME) {
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const namespace = (obj as any).namespace === 'trace' ? 'trace' : 'data';
|
|
195
|
+
const queueDxn = new DXN(DXN.kind.QUEUE, [namespace, spaceId, obj.id]);
|
|
196
|
+
|
|
197
|
+
try {
|
|
198
|
+
const messages = await collectQueueMessages(echoHost, queueDxn);
|
|
199
|
+
if (messages.length > 0) {
|
|
200
|
+
feeds.push({
|
|
201
|
+
feedObjectId: obj.id,
|
|
202
|
+
namespace,
|
|
203
|
+
messages,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
} catch (err) {
|
|
207
|
+
log.warn('failed to export feed data', { feedObjectId: obj.id, error: err });
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return feeds;
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
const collectQueueMessages = async (echoHost: EchoHost, queueDxn: DXN): Promise<Obj.JSON[]> => {
|
|
215
|
+
const parts = queueDxn.asQueueDXN();
|
|
216
|
+
invariant(parts, 'Expected a queue DXN');
|
|
217
|
+
|
|
218
|
+
const namespace =
|
|
219
|
+
parts.subspaceTag === 'trace' ? FeedProtocol.WellKnownNamespaces.trace : FeedProtocol.WellKnownNamespaces.data;
|
|
220
|
+
|
|
221
|
+
const messages: Obj.JSON[] = [];
|
|
222
|
+
let cursor: string | undefined;
|
|
223
|
+
while (true) {
|
|
224
|
+
const result = await echoHost.queuesService.queryQueue({
|
|
225
|
+
query: {
|
|
226
|
+
spaceId: parts.spaceId,
|
|
227
|
+
queueIds: [parts.queueId],
|
|
228
|
+
queuesNamespace: namespace,
|
|
229
|
+
after: cursor,
|
|
230
|
+
},
|
|
231
|
+
});
|
|
232
|
+
const batch = (result.objects ?? []).flatMap((encoded): Obj.JSON[] => {
|
|
233
|
+
try {
|
|
234
|
+
return [JSON.parse(encoded) as Obj.JSON];
|
|
235
|
+
} catch (err) {
|
|
236
|
+
log.verbose('queue object JSON parse failed; object ignored', { encoded, error: err });
|
|
237
|
+
return [];
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
if (batch.length === 0) {
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
for (const message of batch) {
|
|
244
|
+
messages.push(orderObjJsonFields(message));
|
|
245
|
+
}
|
|
246
|
+
if (!result.nextCursor || result.nextCursor === cursor) {
|
|
247
|
+
break;
|
|
248
|
+
}
|
|
249
|
+
cursor = result.nextCursor;
|
|
250
|
+
}
|
|
251
|
+
return messages;
|
|
252
|
+
};
|