@dxos/client-services 0.8.4-main.f5c0578 → 0.8.4-main.fcfe5033a5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/browser/{chunk-WBMHLEAB.mjs → chunk-HYGNOM23.mjs} +2843 -2114
- package/dist/lib/browser/chunk-HYGNOM23.mjs.map +7 -0
- package/dist/lib/browser/chunk-NQSC7HOE.mjs +22 -0
- package/dist/lib/browser/chunk-NQSC7HOE.mjs.map +7 -0
- package/dist/lib/browser/chunk-QCWEHHJW.mjs +24 -0
- package/dist/lib/browser/chunk-QCWEHHJW.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +512 -90
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/packlets/diagnostics/browser-diagnostics-broadcast.mjs +93 -0
- package/dist/lib/browser/packlets/diagnostics/browser-diagnostics-broadcast.mjs.map +7 -0
- package/dist/lib/browser/packlets/diagnostics/diagnostics-broadcast.mjs +11 -0
- package/dist/lib/browser/packlets/diagnostics/diagnostics-broadcast.mjs.map +7 -0
- package/dist/lib/browser/packlets/locks/browser.mjs +126 -0
- package/dist/lib/browser/packlets/locks/browser.mjs.map +7 -0
- package/dist/lib/browser/packlets/locks/node.mjs +66 -0
- package/dist/lib/browser/packlets/locks/node.mjs.map +7 -0
- package/dist/lib/browser/testing/index.mjs +37 -19
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/node-esm/chunk-2SZHAWBN.mjs +24 -0
- package/dist/lib/node-esm/chunk-2SZHAWBN.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-IPVFX6KI.mjs → chunk-GFT7MAQE.mjs} +2571 -1711
- package/dist/lib/node-esm/chunk-GFT7MAQE.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-PKEGMOQ4.mjs +22 -0
- package/dist/lib/node-esm/chunk-PKEGMOQ4.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +512 -90
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/packlets/diagnostics/browser-diagnostics-broadcast.mjs +93 -0
- package/dist/lib/node-esm/packlets/diagnostics/browser-diagnostics-broadcast.mjs.map +7 -0
- package/dist/lib/node-esm/packlets/diagnostics/diagnostics-broadcast.mjs +11 -0
- package/dist/lib/node-esm/packlets/diagnostics/diagnostics-broadcast.mjs.map +7 -0
- package/dist/lib/node-esm/packlets/locks/browser.mjs +126 -0
- package/dist/lib/node-esm/packlets/locks/browser.mjs.map +7 -0
- package/dist/lib/node-esm/packlets/locks/node.mjs +66 -0
- package/dist/lib/node-esm/packlets/locks/node.mjs.map +7 -0
- package/dist/lib/node-esm/testing/index.mjs +37 -19
- 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/devtools/devtools.d.ts +2 -2
- package/dist/types/src/packlets/devtools/devtools.d.ts.map +1 -1
- package/dist/types/src/packlets/diagnostics/diagnostics.d.ts +1 -2
- package/dist/types/src/packlets/diagnostics/diagnostics.d.ts.map +1 -1
- package/dist/types/src/packlets/diagnostics/index.d.ts +1 -1
- package/dist/types/src/packlets/diagnostics/index.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/authenticator.d.ts +2 -2
- 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 +6 -6
- package/dist/types/src/packlets/identity/identity-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity-recovery-manager.d.ts +7 -6
- 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 +8 -11
- package/dist/types/src/packlets/identity/identity.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/device-invitation-protocol.d.ts +6 -5
- 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 +7 -4
- 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/invitations-handler.d.ts +4 -4
- 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 +4 -3
- package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts.map +1 -1
- package/dist/types/src/packlets/locks/index.d.ts +1 -1
- package/dist/types/src/packlets/locks/index.d.ts.map +1 -1
- package/dist/types/src/packlets/logging/logging-service.d.ts +4 -0
- 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 +5 -5
- package/dist/types/src/packlets/services/client-rpc-server.d.ts.map +1 -1
- package/dist/types/src/packlets/services/feed-syncer.d.ts +59 -0
- package/dist/types/src/packlets/services/feed-syncer.d.ts.map +1 -0
- 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/platform.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-context.d.ts +13 -8
- package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-host.d.ts +20 -7
- package/dist/types/src/packlets/services/service-host.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/data-space-manager.d.ts +28 -16
- package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/data-space.d.ts +26 -9
- package/dist/types/src/packlets/spaces/data-space.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/edge-feed-replicator.d.ts +2 -2
- package/dist/types/src/packlets/spaces/edge-feed-replicator.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 +6 -6
- package/dist/types/src/packlets/spaces/notarization-plugin.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/spaces-service.d.ts +17 -7
- package/dist/types/src/packlets/spaces/spaces-service.d.ts.map +1 -1
- package/dist/types/src/packlets/testing/invitation-utils.d.ts +6 -3
- package/dist/types/src/packlets/testing/invitation-utils.d.ts.map +1 -1
- package/dist/types/src/packlets/testing/test-builder.d.ts +6 -5
- package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
- package/dist/types/src/packlets/worker/worker-runtime.d.ts +41 -4
- package/dist/types/src/packlets/worker/worker-runtime.d.ts.map +1 -1
- package/dist/types/src/packlets/worker/worker-session.d.ts +2 -4
- package/dist/types/src/packlets/worker/worker-session.d.ts.map +1 -1
- package/dist/types/src/version.d.ts +1 -1
- package/dist/types/src/version.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +70 -48
- package/src/index.ts +1 -0
- package/src/packlets/agents/edge-agent-manager.ts +10 -7
- package/src/packlets/agents/edge-agent-service.ts +15 -3
- package/src/packlets/devices/devices-service.test.ts +0 -1
- package/src/packlets/devices/devices-service.ts +1 -1
- package/src/packlets/devtools/devtools.ts +2 -3
- package/src/packlets/diagnostics/diagnostics.ts +1 -2
- package/src/packlets/diagnostics/index.ts +1 -1
- package/src/packlets/identity/authenticator.ts +2 -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 +21 -18
- package/src/packlets/identity/identity-recovery-manager.ts +22 -18
- 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 +6 -6
- package/src/packlets/identity/identity.ts +11 -34
- package/src/packlets/invitations/device-invitation-protocol.ts +8 -7
- package/src/packlets/invitations/edge-invitation-handler.ts +9 -5
- package/src/packlets/invitations/invitation-guest-extenstion.ts +6 -4
- package/src/packlets/invitations/invitation-host-extension.ts +6 -4
- package/src/packlets/invitations/invitation-protocol.ts +7 -4
- package/src/packlets/invitations/invitation-state.ts +0 -10
- package/src/packlets/invitations/invitations-handler.test.ts +4 -5
- package/src/packlets/invitations/invitations-handler.ts +69 -13
- 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 +11 -16
- package/src/packlets/locks/index.ts +1 -1
- package/src/packlets/logging/logging-service.ts +5 -1
- 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 +19 -16
- package/src/packlets/services/feed-syncer.test.ts +340 -0
- package/src/packlets/services/feed-syncer.ts +337 -0
- package/src/packlets/services/platform.ts +7 -1
- package/src/packlets/services/service-context.test.ts +3 -2
- package/src/packlets/services/service-context.ts +141 -55
- package/src/packlets/services/service-host.test.ts +8 -8
- package/src/packlets/services/service-host.ts +77 -32
- 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 +246 -0
- package/src/packlets/space-export/space-archive-reader.ts +65 -4
- package/src/packlets/space-export/space-archive-writer.ts +43 -5
- 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 +122 -110
- package/src/packlets/spaces/data-space.ts +58 -33
- package/src/packlets/spaces/edge-feed-replicator.test.ts +2 -2
- package/src/packlets/spaces/edge-feed-replicator.ts +12 -10
- package/src/packlets/spaces/epoch-migrations.ts +5 -5
- package/src/packlets/spaces/genesis.ts +6 -1
- package/src/packlets/spaces/notarization-plugin.test.ts +2 -2
- package/src/packlets/spaces/notarization-plugin.ts +10 -9
- package/src/packlets/spaces/spaces-service.test.ts +18 -11
- package/src/packlets/spaces/spaces-service.ts +124 -25
- package/src/packlets/storage/storage.ts +4 -4
- package/src/packlets/testing/invitation-utils.ts +10 -6
- package/src/packlets/testing/test-builder.ts +36 -10
- package/src/packlets/worker/worker-runtime.ts +179 -17
- package/src/packlets/worker/worker-session.ts +12 -18
- package/src/version.ts +1 -1
- package/dist/lib/browser/chunk-WBMHLEAB.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-IPVFX6KI.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
|
@@ -2,11 +2,15 @@
|
|
|
2
2
|
// Copyright 2021 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import * as SqlClient from '@effect/sql/SqlClient';
|
|
6
|
+
import * as Effect from 'effect/Effect';
|
|
7
|
+
|
|
5
8
|
import { Event, synchronized } from '@dxos/async';
|
|
6
9
|
import { type ClientServices, clientServiceBundle } from '@dxos/client-protocol';
|
|
7
|
-
import { type Config } from '@dxos/config';
|
|
10
|
+
import { type Config, resolveTelemetryTag } from '@dxos/config';
|
|
8
11
|
import { Context } from '@dxos/context';
|
|
9
12
|
import { EdgeClient, type EdgeConnection, EdgeHttpClient, createStubEdgeIdentity } from '@dxos/edge-client';
|
|
13
|
+
import { RuntimeProvider } from '@dxos/effect';
|
|
10
14
|
import { invariant } from '@dxos/invariant';
|
|
11
15
|
import { PublicKey } from '@dxos/keys';
|
|
12
16
|
import { type LevelDB } from '@dxos/kv-store';
|
|
@@ -21,7 +25,9 @@ import {
|
|
|
21
25
|
import { trace } from '@dxos/protocols';
|
|
22
26
|
import { SystemStatus } from '@dxos/protocols/proto/dxos/client/services';
|
|
23
27
|
import { type Storage } from '@dxos/random-access-storage';
|
|
24
|
-
import
|
|
28
|
+
import * as SqlExport from '@dxos/sql-sqlite/SqlExport';
|
|
29
|
+
import type * as SqlTransaction from '@dxos/sql-sqlite/SqlTransaction';
|
|
30
|
+
import { trace as Trace } from '@dxos/tracing';
|
|
25
31
|
import { WebsocketRpcClient } from '@dxos/websocket-rpc';
|
|
26
32
|
|
|
27
33
|
import { EdgeAgentServiceImpl } from '../agents';
|
|
@@ -41,11 +47,10 @@ import { NetworkServiceImpl } from '../network';
|
|
|
41
47
|
import { SpacesServiceImpl } from '../spaces';
|
|
42
48
|
import { createLevel, createStorageObjects } from '../storage';
|
|
43
49
|
import { SystemServiceImpl } from '../system';
|
|
44
|
-
|
|
45
|
-
import { ServiceContext, type ServiceContextRuntimeParams } from './service-context';
|
|
50
|
+
import { ServiceContext, type ServiceContextRuntimeProps } from './service-context';
|
|
46
51
|
import { ServiceRegistry } from './service-registry';
|
|
47
52
|
|
|
48
|
-
export type
|
|
53
|
+
export type ClientServicesHostProps = {
|
|
49
54
|
/**
|
|
50
55
|
* Can be omitted if `initialize` is later called.
|
|
51
56
|
*/
|
|
@@ -57,7 +62,8 @@ export type ClientServicesHostParams = {
|
|
|
57
62
|
level?: LevelDB;
|
|
58
63
|
lockKey?: string;
|
|
59
64
|
callbacks?: ClientServicesHostCallbacks;
|
|
60
|
-
|
|
65
|
+
runtime: RuntimeProvider.RuntimeProvider<SqlClient.SqlClient | SqlExport.SqlExport | SqlTransaction.SqlTransaction>;
|
|
66
|
+
runtimeProps?: ServiceContextRuntimeProps;
|
|
61
67
|
};
|
|
62
68
|
|
|
63
69
|
export type ClientServicesHostCallbacks = {
|
|
@@ -80,8 +86,6 @@ export class ClientServicesHost {
|
|
|
80
86
|
private readonly _serviceRegistry: ServiceRegistry<ClientServices>;
|
|
81
87
|
private readonly _systemService: SystemServiceImpl;
|
|
82
88
|
private readonly _loggingService: LoggingServiceImpl;
|
|
83
|
-
private readonly _tracingService = TRACE_PROCESSOR.createTraceSender();
|
|
84
|
-
|
|
85
89
|
private readonly _statusUpdate = new Event<void>();
|
|
86
90
|
|
|
87
91
|
private _config?: Config;
|
|
@@ -95,7 +99,10 @@ export class ClientServicesHost {
|
|
|
95
99
|
private _edgeHttpClient?: EdgeHttpClient = undefined;
|
|
96
100
|
|
|
97
101
|
private _serviceContext!: ServiceContext;
|
|
98
|
-
private readonly
|
|
102
|
+
private readonly _runtime: RuntimeProvider.RuntimeProvider<
|
|
103
|
+
SqlClient.SqlClient | SqlExport.SqlExport | SqlTransaction.SqlTransaction
|
|
104
|
+
>;
|
|
105
|
+
private readonly _runtimeProps: ServiceContextRuntimeProps;
|
|
99
106
|
private diagnosticsBroadcastHandler: CollectDiagnosticsBroadcastHandler;
|
|
100
107
|
|
|
101
108
|
@Trace.info()
|
|
@@ -116,20 +123,14 @@ export class ClientServicesHost {
|
|
|
116
123
|
// TODO(wittjosiah): Turn this on by default.
|
|
117
124
|
lockKey,
|
|
118
125
|
callbacks,
|
|
119
|
-
|
|
120
|
-
|
|
126
|
+
runtime,
|
|
127
|
+
runtimeProps,
|
|
128
|
+
}: ClientServicesHostProps) {
|
|
121
129
|
this._storage = storage;
|
|
122
130
|
this._level = level;
|
|
123
131
|
this._callbacks = callbacks;
|
|
124
|
-
this.
|
|
125
|
-
|
|
126
|
-
if (this._runtimeParams.disableP2pReplication === undefined) {
|
|
127
|
-
this._runtimeParams.disableP2pReplication = config?.get('runtime.client.disableP2pReplication', false);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
if (this._runtimeParams.enableVectorIndexing === undefined) {
|
|
131
|
-
this._runtimeParams.enableVectorIndexing = config?.get('runtime.client.enableVectorIndexing', false);
|
|
132
|
-
}
|
|
132
|
+
this._runtime = runtime;
|
|
133
|
+
this._runtimeProps = runtimeProps ?? {};
|
|
133
134
|
|
|
134
135
|
if (config) {
|
|
135
136
|
this.initialize({ config, transportFactory, signalManager });
|
|
@@ -143,7 +144,7 @@ export class ClientServicesHost {
|
|
|
143
144
|
void this.open(new Context());
|
|
144
145
|
}
|
|
145
146
|
},
|
|
146
|
-
onRelease: () => this.close(),
|
|
147
|
+
onRelease: () => this.close(Context.default()),
|
|
147
148
|
});
|
|
148
149
|
}
|
|
149
150
|
|
|
@@ -172,7 +173,6 @@ export class ClientServicesHost {
|
|
|
172
173
|
|
|
173
174
|
this._serviceRegistry = new ServiceRegistry<ClientServices>(clientServiceBundle, {
|
|
174
175
|
SystemService: this._systemService,
|
|
175
|
-
TracingService: this._tracingService,
|
|
176
176
|
});
|
|
177
177
|
}
|
|
178
178
|
|
|
@@ -200,6 +200,30 @@ export class ClientServicesHost {
|
|
|
200
200
|
return this._serviceRegistry.services;
|
|
201
201
|
}
|
|
202
202
|
|
|
203
|
+
/**
|
|
204
|
+
* Debugging util.
|
|
205
|
+
*/
|
|
206
|
+
async exportSqliteDatabase(): Promise<Uint8Array> {
|
|
207
|
+
return await RuntimeProvider.runPromise(this._runtime)(
|
|
208
|
+
Effect.gen(function* () {
|
|
209
|
+
const sql = yield* SqlExport.SqlExport;
|
|
210
|
+
return yield* sql.export;
|
|
211
|
+
}),
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Debugging util.
|
|
217
|
+
*/
|
|
218
|
+
async runSqliteQuery(query: string, params?: any[]): Promise<readonly Record<string, unknown>[]> {
|
|
219
|
+
return await RuntimeProvider.runPromise(this._runtime)(
|
|
220
|
+
Effect.gen(function* () {
|
|
221
|
+
const sql = yield* SqlClient.SqlClient;
|
|
222
|
+
return yield* sql`${sql.unsafe(query, params)}`;
|
|
223
|
+
}),
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
|
|
203
227
|
/**
|
|
204
228
|
* Initialize the service host with the config.
|
|
205
229
|
* Config can also be provided in the constructor.
|
|
@@ -210,6 +234,13 @@ export class ClientServicesHost {
|
|
|
210
234
|
log('initializing...');
|
|
211
235
|
|
|
212
236
|
if (config) {
|
|
237
|
+
if (this._runtimeProps.disableP2pReplication === undefined) {
|
|
238
|
+
this._runtimeProps.disableP2pReplication = config?.get('runtime.client.disableP2pReplication', false);
|
|
239
|
+
}
|
|
240
|
+
if (this._runtimeProps.enableVectorIndexing === undefined) {
|
|
241
|
+
this._runtimeProps.enableVectorIndexing = config?.get('runtime.client.enableVectorIndexing', false);
|
|
242
|
+
}
|
|
243
|
+
|
|
213
244
|
invariant(!this._config, 'config already set');
|
|
214
245
|
this._config = config;
|
|
215
246
|
if (!this._storage) {
|
|
@@ -217,14 +248,16 @@ export class ClientServicesHost {
|
|
|
217
248
|
}
|
|
218
249
|
}
|
|
219
250
|
|
|
251
|
+
// TODO(wittjosiah): This is quite noisy during tests. Make configurable? Remove?
|
|
220
252
|
if (!options.signalManager) {
|
|
221
|
-
log.warn('running signaling without telemetry metadata.');
|
|
253
|
+
// log.warn('running signaling without telemetry metadata.');
|
|
222
254
|
}
|
|
223
255
|
|
|
224
256
|
const endpoint = config?.get('runtime.services.edge.url');
|
|
225
257
|
if (endpoint) {
|
|
226
|
-
|
|
227
|
-
this.
|
|
258
|
+
const clientTag = resolveTelemetryTag(config);
|
|
259
|
+
this._edgeConnection = new EdgeClient(createStubEdgeIdentity(), { socketEndpoint: endpoint, clientTag });
|
|
260
|
+
this._edgeHttpClient = new EdgeHttpClient(endpoint, { clientTag });
|
|
228
261
|
}
|
|
229
262
|
|
|
230
263
|
const {
|
|
@@ -290,7 +323,8 @@ export class ClientServicesHost {
|
|
|
290
323
|
this._signalManager,
|
|
291
324
|
this._edgeConnection,
|
|
292
325
|
this._edgeHttpClient,
|
|
293
|
-
this.
|
|
326
|
+
this._runtime,
|
|
327
|
+
this._runtimeProps,
|
|
294
328
|
this._config.get('runtime.client.edgeFeatures'),
|
|
295
329
|
);
|
|
296
330
|
|
|
@@ -308,8 +342,7 @@ export class ClientServicesHost {
|
|
|
308
342
|
this._serviceContext.identityManager,
|
|
309
343
|
this._serviceContext.recoveryManager,
|
|
310
344
|
this._serviceContext.keyring,
|
|
311
|
-
() => this.
|
|
312
|
-
(params) => this._createIdentity(params),
|
|
345
|
+
(params, ctx) => this._createIdentity(params, ctx),
|
|
313
346
|
(profile) => this._serviceContext.broadcastProfileUpdate(profile),
|
|
314
347
|
);
|
|
315
348
|
|
|
@@ -329,11 +362,13 @@ export class ClientServicesHost {
|
|
|
329
362
|
SpacesService: new SpacesServiceImpl(
|
|
330
363
|
this._serviceContext.identityManager,
|
|
331
364
|
this._serviceContext.spaceManager,
|
|
365
|
+
this._serviceContext.echoHost,
|
|
332
366
|
dataSpaceManagerProvider,
|
|
333
367
|
),
|
|
334
368
|
|
|
335
369
|
DataService: this._serviceContext.echoHost.dataService,
|
|
336
370
|
QueryService: this._serviceContext.echoHost.queryService,
|
|
371
|
+
QueueService: this._serviceContext.echoHost.queuesService,
|
|
337
372
|
|
|
338
373
|
NetworkService: new NetworkServiceImpl(
|
|
339
374
|
this._serviceContext.networkManager,
|
|
@@ -342,7 +377,6 @@ export class ClientServicesHost {
|
|
|
342
377
|
),
|
|
343
378
|
|
|
344
379
|
LoggingService: this._loggingService,
|
|
345
|
-
TracingService: this._tracingService,
|
|
346
380
|
|
|
347
381
|
// TODO(burdon): Move to new protobuf definitions.
|
|
348
382
|
DevtoolsHost: new DevtoolsServiceImpl({
|
|
@@ -354,8 +388,13 @@ export class ClientServicesHost {
|
|
|
354
388
|
EdgeAgentService: new EdgeAgentServiceImpl(agentManagerProvider, this._edgeConnection),
|
|
355
389
|
});
|
|
356
390
|
|
|
391
|
+
log('service-host: opening service context...');
|
|
357
392
|
await this._serviceContext.open(ctx);
|
|
393
|
+
log('service-host: service context opened');
|
|
394
|
+
|
|
395
|
+
log('service-host: opening identity service...');
|
|
358
396
|
await identityService.open();
|
|
397
|
+
log('service-host: identity service opened');
|
|
359
398
|
|
|
360
399
|
const devtoolsProxy = this._config?.get('runtime.client.devtoolsProxy');
|
|
361
400
|
if (devtoolsProxy) {
|
|
@@ -379,7 +418,7 @@ export class ClientServicesHost {
|
|
|
379
418
|
|
|
380
419
|
@synchronized
|
|
381
420
|
@Trace.span()
|
|
382
|
-
async close(): Promise<void> {
|
|
421
|
+
async close(ctx: Context): Promise<void> {
|
|
383
422
|
if (!this._open) {
|
|
384
423
|
return;
|
|
385
424
|
}
|
|
@@ -407,14 +446,20 @@ export class ClientServicesHost {
|
|
|
407
446
|
this._resetting = true;
|
|
408
447
|
this._statusUpdate.emit();
|
|
409
448
|
await this._serviceContext?.close();
|
|
449
|
+
// Clear LevelDB contents to remove all persisted Echo/Automerge/index data.
|
|
450
|
+
try {
|
|
451
|
+
await this._level!.clear();
|
|
452
|
+
} catch (err) {
|
|
453
|
+
log.warn('failed to clear leveldb during reset', { err });
|
|
454
|
+
}
|
|
410
455
|
await this._storage!.reset();
|
|
411
456
|
log.info('reset');
|
|
412
457
|
log.trace('dxos.sdk.client-services-host.reset', trace.end({ id: traceId }));
|
|
413
458
|
await this._callbacks?.onReset?.();
|
|
414
459
|
}
|
|
415
460
|
|
|
416
|
-
private async _createIdentity(params: CreateIdentityOptions) {
|
|
417
|
-
const identity = await this._serviceContext.createIdentity(params);
|
|
461
|
+
private async _createIdentity(params: CreateIdentityOptions, ctx?: Context) {
|
|
462
|
+
const identity = await this._serviceContext.createIdentity(params, ctx);
|
|
418
463
|
await this._serviceContext.initialized.wait();
|
|
419
464
|
return identity;
|
|
420
465
|
}
|
|
@@ -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,246 @@
|
|
|
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
|
+
await handle.whenReady();
|
|
109
|
+
const doc = handle.doc();
|
|
110
|
+
if (!doc) {
|
|
111
|
+
log.warn('linked document did not load; skipping', { url: linkedUrl });
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
collectObjectsFromDoc(doc, objects);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Export queue/feed messages for every Feed object in the space.
|
|
118
|
+
const feeds = await exportFeedData(space, echoHost, objects);
|
|
119
|
+
|
|
120
|
+
const serialized: SerializedSpace = {
|
|
121
|
+
version: SERIALIZED_SPACE_VERSION,
|
|
122
|
+
timestamp: new Date().toISOString(),
|
|
123
|
+
originalSpaceId: space.id,
|
|
124
|
+
exportedBy,
|
|
125
|
+
createdAt: Date.now(),
|
|
126
|
+
objects,
|
|
127
|
+
...(feeds.length > 0 ? { feeds } : {}),
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const encoded = new TextEncoder().encode(JSON.stringify(serialized));
|
|
131
|
+
return {
|
|
132
|
+
filename: createFilename({ parts: [space.id], ext: 'dx.json' }),
|
|
133
|
+
contents: encoded,
|
|
134
|
+
format: SpaceArchive.Format.JSON,
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const collectObjectsFromDoc = (doc: DatabaseDirectory, out: Obj.JSON[]): void => {
|
|
139
|
+
const docObjects = doc.objects ?? {};
|
|
140
|
+
for (const [objectId, structure] of Object.entries(docObjects)) {
|
|
141
|
+
out.push(objectStructureToObjJson(objectId, structure));
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Convert an internal {@link ObjectStructure} into an {@link Obj.JSON}.
|
|
147
|
+
*
|
|
148
|
+
* Unlike the equivalent helper used for indexing, this preserves the object's
|
|
149
|
+
* `@meta` section so archives produced by this writer can be round-tripped
|
|
150
|
+
* through {@link Obj.fromJSON}.
|
|
151
|
+
*/
|
|
152
|
+
export const objectStructureToObjJson = (objectId: string, structure: ObjectStructure): Obj.JSON => {
|
|
153
|
+
const result: Record<string, unknown> = {
|
|
154
|
+
[ATTR_ID]: objectId,
|
|
155
|
+
[ATTR_TYPE]: (structure.system?.type?.['/'] ?? '') as any,
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
if (structure.meta) {
|
|
159
|
+
result[ATTR_META] = {
|
|
160
|
+
keys: structure.meta.keys ?? [],
|
|
161
|
+
...(structure.meta.tags ? { tags: structure.meta.tags } : {}),
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
if (structure.system?.deleted) {
|
|
165
|
+
result[ATTR_DELETED] = true;
|
|
166
|
+
}
|
|
167
|
+
if (structure.system?.parent) {
|
|
168
|
+
result[ATTR_PARENT] = structure.system.parent['/'];
|
|
169
|
+
}
|
|
170
|
+
if (structure.system?.source) {
|
|
171
|
+
result[ATTR_RELATION_SOURCE] = structure.system.source['/'];
|
|
172
|
+
}
|
|
173
|
+
if (structure.system?.target) {
|
|
174
|
+
result[ATTR_RELATION_TARGET] = structure.system.target['/'];
|
|
175
|
+
}
|
|
176
|
+
Object.assign(result, structure.data);
|
|
177
|
+
|
|
178
|
+
return result as Obj.JSON;
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const exportFeedData = async (space: DataSpace, echoHost: EchoHost, objects: Obj.JSON[]): Promise<SerializedFeed[]> => {
|
|
182
|
+
const feeds: SerializedFeed[] = [];
|
|
183
|
+
const spaceId: SpaceId = space.id;
|
|
184
|
+
|
|
185
|
+
for (const obj of objects) {
|
|
186
|
+
if (obj[ATTR_TYPE] == null) {
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const typeDxn = DXN.tryParse(obj[ATTR_TYPE] as string);
|
|
191
|
+
if (typeDxn?.asTypeDXN()?.type !== FEED_TYPENAME) {
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const namespace = (obj as any).namespace === 'trace' ? 'trace' : 'data';
|
|
196
|
+
const queueDxn = new DXN(DXN.kind.QUEUE, [namespace, spaceId, obj.id]);
|
|
197
|
+
|
|
198
|
+
try {
|
|
199
|
+
const messages = await collectQueueMessages(echoHost, queueDxn);
|
|
200
|
+
if (messages.length > 0) {
|
|
201
|
+
feeds.push({
|
|
202
|
+
feedObjectId: obj.id,
|
|
203
|
+
namespace,
|
|
204
|
+
messages,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
} catch (err) {
|
|
208
|
+
log.warn('failed to export feed data', { feedObjectId: obj.id, error: err });
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return feeds;
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
const collectQueueMessages = async (echoHost: EchoHost, queueDxn: DXN): Promise<Obj.JSON[]> => {
|
|
216
|
+
const parts = queueDxn.asQueueDXN();
|
|
217
|
+
invariant(parts, 'Expected a queue DXN');
|
|
218
|
+
|
|
219
|
+
const namespace =
|
|
220
|
+
parts.subspaceTag === 'trace' ? FeedProtocol.WellKnownNamespaces.trace : FeedProtocol.WellKnownNamespaces.data;
|
|
221
|
+
|
|
222
|
+
const messages: Obj.JSON[] = [];
|
|
223
|
+
let cursor: string | undefined;
|
|
224
|
+
while (true) {
|
|
225
|
+
const result = await echoHost.queuesService.queryQueue({
|
|
226
|
+
query: {
|
|
227
|
+
spaceId: parts.spaceId,
|
|
228
|
+
queueIds: [parts.queueId],
|
|
229
|
+
queuesNamespace: namespace,
|
|
230
|
+
after: cursor,
|
|
231
|
+
},
|
|
232
|
+
});
|
|
233
|
+
const batch = (result.objects ?? []) as Obj.JSON[];
|
|
234
|
+
if (batch.length === 0) {
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
for (const message of batch) {
|
|
238
|
+
messages.push(orderObjJsonFields(message));
|
|
239
|
+
}
|
|
240
|
+
if (!result.nextCursor || result.nextCursor === cursor) {
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
cursor = result.nextCursor;
|
|
244
|
+
}
|
|
245
|
+
return messages;
|
|
246
|
+
};
|