@enbox/dwn-sdk-js 0.3.9 → 0.4.0
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/browser.mjs +11 -11
- package/dist/browser.mjs.map +4 -4
- package/dist/esm/generated/precompiled-validators.js +175 -512
- package/dist/esm/generated/precompiled-validators.js.map +1 -1
- package/dist/esm/src/core/dwn-error.js +1 -3
- package/dist/esm/src/core/dwn-error.js.map +1 -1
- package/dist/esm/src/core/messages-grant-authorization.js +1 -17
- package/dist/esm/src/core/messages-grant-authorization.js.map +1 -1
- package/dist/esm/src/core/protocol-authorization-validation.js +1 -1
- package/dist/esm/src/core/protocol-authorization-validation.js.map +1 -1
- package/dist/esm/src/core/replication-apply.js +200 -0
- package/dist/esm/src/core/replication-apply.js.map +1 -0
- package/dist/esm/src/dwn.js +212 -0
- package/dist/esm/src/dwn.js.map +1 -1
- package/dist/esm/src/handlers/messages-sync.js +66 -369
- package/dist/esm/src/handlers/messages-sync.js.map +1 -1
- package/dist/esm/src/index.js +1 -1
- package/dist/esm/src/index.js.map +1 -1
- package/dist/esm/src/interfaces/messages-sync.js +0 -11
- package/dist/esm/src/interfaces/messages-sync.js.map +1 -1
- package/dist/esm/tests/core/replication-apply.spec.js +220 -0
- package/dist/esm/tests/core/replication-apply.spec.js.map +1 -0
- package/dist/esm/tests/dwn.spec.js +139 -2
- package/dist/esm/tests/dwn.spec.js.map +1 -1
- package/dist/esm/tests/handlers/messages-sync.spec.js +1 -684
- package/dist/esm/tests/handlers/messages-sync.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-write.spec.js +2 -2
- package/dist/esm/tests/handlers/records-write.spec.js.map +1 -1
- package/dist/esm/tests/test-suite.js +0 -2
- package/dist/esm/tests/test-suite.js.map +1 -1
- package/dist/types/generated/precompiled-validators.d.ts.map +1 -1
- package/dist/types/src/core/dwn-error.d.ts +1 -3
- package/dist/types/src/core/dwn-error.d.ts.map +1 -1
- package/dist/types/src/core/messages-grant-authorization.d.ts +0 -1
- package/dist/types/src/core/messages-grant-authorization.d.ts.map +1 -1
- package/dist/types/src/core/replication-apply.d.ts +93 -0
- package/dist/types/src/core/replication-apply.d.ts.map +1 -0
- package/dist/types/src/dwn.d.ts +22 -1
- package/dist/types/src/dwn.d.ts.map +1 -1
- package/dist/types/src/handlers/messages-sync.d.ts +10 -54
- package/dist/types/src/handlers/messages-sync.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +3 -3
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/interfaces/messages-sync.d.ts +0 -3
- package/dist/types/src/interfaces/messages-sync.d.ts.map +1 -1
- package/dist/types/src/types/messages-types.d.ts +0 -18
- package/dist/types/src/types/messages-types.d.ts.map +1 -1
- package/dist/types/tests/core/replication-apply.spec.d.ts +2 -0
- package/dist/types/tests/core/replication-apply.spec.d.ts.map +1 -0
- package/dist/types/tests/dwn.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/messages-sync.spec.d.ts.map +1 -1
- package/dist/types/tests/test-suite.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/core/dwn-error.ts +1 -3
- package/src/core/messages-grant-authorization.ts +1 -31
- package/src/core/protocol-authorization-validation.ts +2 -2
- package/src/core/replication-apply.ts +272 -0
- package/src/dwn.ts +296 -2
- package/src/handlers/messages-sync.ts +92 -585
- package/src/index.ts +3 -4
- package/src/interfaces/messages-sync.ts +8 -25
- package/src/types/messages-types.ts +0 -20
- package/dist/esm/src/sync/records-projection.js +0 -228
- package/dist/esm/src/sync/records-projection.js.map +0 -1
- package/dist/esm/tests/sync/records-projection.spec.js +0 -245
- package/dist/esm/tests/sync/records-projection.spec.js.map +0 -1
- package/dist/types/src/sync/records-projection.d.ts +0 -98
- package/dist/types/src/sync/records-projection.d.ts.map +0 -1
- package/dist/types/tests/sync/records-projection.spec.d.ts +0 -2
- package/dist/types/tests/sync/records-projection.spec.d.ts.map +0 -1
- package/src/sync/records-projection.ts +0 -328
|
@@ -2,8 +2,7 @@ import type { GenericMessage } from '../types/message-types.js';
|
|
|
2
2
|
import type { MessageStore } from '../types/message-store.js';
|
|
3
3
|
import type { StateIndex } from '../types/state-index.js';
|
|
4
4
|
import type { HandlerDependencies, MethodHandler } from '../types/method-handler.js';
|
|
5
|
-
import type {
|
|
6
|
-
import type { RecordsProjectionScope, RecordsProjectionSnapshot } from '../sync/records-projection.js';
|
|
5
|
+
import type { MessagesSyncDiffEntry, MessagesSyncMessage, MessagesSyncReply } from '../types/messages-types.js';
|
|
7
6
|
|
|
8
7
|
import { authenticate } from '../core/auth.js';
|
|
9
8
|
import { DwnConstant } from '../core/dwn-constant.js';
|
|
@@ -14,39 +13,20 @@ import { messageReplyFromError } from '../core/message-reply.js';
|
|
|
14
13
|
import { MessagesGrantAuthorization } from '../core/messages-grant-authorization.js';
|
|
15
14
|
import { MessagesSync } from '../interfaces/messages-sync.js';
|
|
16
15
|
import { Records } from '../utils/records.js';
|
|
17
|
-
import { RecordsProjection } from '../sync/records-projection.js';
|
|
18
|
-
import { RecordsWrite } from '../interfaces/records-write.js';
|
|
19
|
-
import { SortDirection } from '../types/query-types.js';
|
|
20
16
|
import { DwnError, DwnErrorCode } from '../core/dwn-error.js';
|
|
21
|
-
import { DwnInterfaceName, DwnMethodName } from '../enums/dwn-interface-method.js';
|
|
22
17
|
|
|
23
18
|
/**
|
|
24
|
-
* Maximum inline data size for diff responses
|
|
25
|
-
* {@link DwnConstant.maxDataSizeAllowedToBeEncoded} threshold
|
|
26
|
-
* RecordsWrite data payloads smaller than this are base64url-encoded and
|
|
27
|
-
* included directly in the diff reply. Larger payloads must be fetched
|
|
28
|
-
* separately via MessagesRead.
|
|
19
|
+
* Maximum inline data size for diff responses, aligned with the
|
|
20
|
+
* {@link DwnConstant.maxDataSizeAllowedToBeEncoded} threshold.
|
|
29
21
|
*/
|
|
30
22
|
const DEFAULT_MAX_INLINE_DATA_SIZE = DwnConstant.maxDataSizeAllowedToBeEncoded;
|
|
31
23
|
|
|
32
24
|
type StoredMessageWithEncodedData = GenericMessage & { encodedData?: string };
|
|
33
|
-
type StoredMessageWithInternalFields = GenericMessage & {
|
|
34
|
-
encodedData?: unknown;
|
|
35
|
-
initialWrite?: unknown;
|
|
36
|
-
};
|
|
37
|
-
type RecordsWriteProtocolDescriptor = GenericMessage['descriptor'] & { protocol?: unknown };
|
|
38
|
-
type RecordsWriteProtocolMetadata = { protocol: string; messageTimestamp: string };
|
|
39
|
-
type ProtocolsConfigureDefinitionDescriptor = GenericMessage['descriptor'] & {
|
|
40
|
-
definition?: {
|
|
41
|
-
protocol?: unknown;
|
|
42
|
-
uses?: Record<string, unknown>;
|
|
43
|
-
};
|
|
44
|
-
};
|
|
45
|
-
type ProjectionScopes = readonly [RecordsProjectionScope, ...RecordsProjectionScope[]];
|
|
46
|
-
|
|
47
25
|
|
|
48
26
|
export class MessagesSyncHandler implements MethodHandler {
|
|
49
27
|
|
|
28
|
+
private _defaultHashHexCache?: Map<number, string>;
|
|
29
|
+
|
|
50
30
|
constructor(private readonly deps: HandlerDependencies) { }
|
|
51
31
|
|
|
52
32
|
public async handle({
|
|
@@ -68,33 +48,25 @@ export class MessagesSyncHandler implements MethodHandler {
|
|
|
68
48
|
return messageReplyFromError(e, 401);
|
|
69
49
|
}
|
|
70
50
|
|
|
71
|
-
const { action } = message.descriptor;
|
|
72
|
-
const projectionScopes = MessagesSyncHandler.getProjectionScopes(message);
|
|
73
|
-
|
|
74
51
|
try {
|
|
75
|
-
switch (action) {
|
|
76
|
-
case 'root':
|
|
77
|
-
return await this.handleRoot(tenant, message
|
|
78
|
-
}
|
|
52
|
+
switch (message.descriptor.action) {
|
|
53
|
+
case 'root':
|
|
54
|
+
return await this.handleRoot(tenant, message);
|
|
79
55
|
|
|
80
|
-
case 'subtree':
|
|
81
|
-
return await this.handleSubtree(tenant, message
|
|
82
|
-
}
|
|
56
|
+
case 'subtree':
|
|
57
|
+
return await this.handleSubtree(tenant, message);
|
|
83
58
|
|
|
84
|
-
case 'leaves':
|
|
85
|
-
return await this.handleLeaves(tenant, message
|
|
86
|
-
}
|
|
59
|
+
case 'leaves':
|
|
60
|
+
return await this.handleLeaves(tenant, message);
|
|
87
61
|
|
|
88
|
-
case 'diff':
|
|
62
|
+
case 'diff':
|
|
89
63
|
return await this.handleDiff(tenant, message);
|
|
90
|
-
}
|
|
91
64
|
|
|
92
|
-
default:
|
|
65
|
+
default:
|
|
93
66
|
return {
|
|
94
|
-
status: { code: 400, detail: `Unknown action: ${action as string}` },
|
|
67
|
+
status: { code: 400, detail: `Unknown action: ${message.descriptor.action as string}` },
|
|
95
68
|
};
|
|
96
69
|
}
|
|
97
|
-
}
|
|
98
70
|
} catch (e) {
|
|
99
71
|
return messageReplyFromError(e, 500);
|
|
100
72
|
}
|
|
@@ -103,46 +75,25 @@ export class MessagesSyncHandler implements MethodHandler {
|
|
|
103
75
|
private async handleRoot(
|
|
104
76
|
tenant: string,
|
|
105
77
|
message: MessagesSyncMessage,
|
|
106
|
-
projectionScopes: ProjectionScopes | undefined
|
|
107
78
|
): Promise<MessagesSyncReply> {
|
|
108
|
-
const root = await this.
|
|
79
|
+
const root = hashToHex(await this.getIndexedRootHash(tenant, message.descriptor.protocol));
|
|
109
80
|
return {
|
|
110
|
-
status
|
|
111
|
-
root
|
|
81
|
+
status: { code: 200, detail: 'OK' },
|
|
82
|
+
root,
|
|
112
83
|
};
|
|
113
84
|
}
|
|
114
85
|
|
|
115
|
-
private async getRootHex(
|
|
116
|
-
tenant: string,
|
|
117
|
-
protocol: string | undefined,
|
|
118
|
-
projectionScopes: ProjectionScopes | undefined
|
|
119
|
-
): Promise<string> {
|
|
120
|
-
if (projectionScopes === undefined) {
|
|
121
|
-
const rootHash = await this.getIndexedRootHash(tenant, protocol);
|
|
122
|
-
return hashToHex(rootHash);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
return this.withProjectionSnapshot(tenant, projectionScopes, snapshot => snapshot.getRootHex());
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
private async getIndexedRootHash(
|
|
129
|
-
tenant: string,
|
|
130
|
-
protocol: string | undefined
|
|
131
|
-
): Promise<Uint8Array> {
|
|
132
|
-
if (protocol === undefined) {
|
|
133
|
-
return this.deps.stateIndex!.getRoot(tenant);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return this.deps.stateIndex!.getProtocolRoot(tenant, protocol);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
86
|
private async handleSubtree(
|
|
140
87
|
tenant: string,
|
|
141
88
|
message: MessagesSyncMessage,
|
|
142
|
-
projectionScopes: ProjectionScopes | undefined
|
|
143
89
|
): Promise<MessagesSyncReply> {
|
|
144
90
|
const bitPath = MessagesSyncHandler.parseBitPrefix(message.descriptor.prefix!);
|
|
145
|
-
const hash = await
|
|
91
|
+
const hash = await MessagesSyncHandler.getIndexedSubtreeHash(
|
|
92
|
+
this.deps.stateIndex!,
|
|
93
|
+
tenant,
|
|
94
|
+
message.descriptor.protocol,
|
|
95
|
+
bitPath,
|
|
96
|
+
);
|
|
146
97
|
|
|
147
98
|
return {
|
|
148
99
|
status : { code: 200, detail: 'OK' },
|
|
@@ -153,10 +104,14 @@ export class MessagesSyncHandler implements MethodHandler {
|
|
|
153
104
|
private async handleLeaves(
|
|
154
105
|
tenant: string,
|
|
155
106
|
message: MessagesSyncMessage,
|
|
156
|
-
projectionScopes: ProjectionScopes | undefined
|
|
157
107
|
): Promise<MessagesSyncReply> {
|
|
158
108
|
const bitPath = MessagesSyncHandler.parseBitPrefix(message.descriptor.prefix!);
|
|
159
|
-
const leaves = await
|
|
109
|
+
const leaves = await MessagesSyncHandler.getIndexedLeaves(
|
|
110
|
+
this.deps.stateIndex!,
|
|
111
|
+
tenant,
|
|
112
|
+
message.descriptor.protocol,
|
|
113
|
+
bitPath,
|
|
114
|
+
);
|
|
160
115
|
|
|
161
116
|
return {
|
|
162
117
|
status : { code: 200, detail: 'OK' },
|
|
@@ -164,65 +119,15 @@ export class MessagesSyncHandler implements MethodHandler {
|
|
|
164
119
|
};
|
|
165
120
|
}
|
|
166
121
|
|
|
167
|
-
private async getSubtreeHash(
|
|
168
|
-
tenant: string,
|
|
169
|
-
protocol: string | undefined,
|
|
170
|
-
projectionScopes: ProjectionScopes | undefined,
|
|
171
|
-
bitPath: boolean[]
|
|
172
|
-
): Promise<Uint8Array> {
|
|
173
|
-
if (projectionScopes === undefined) {
|
|
174
|
-
return this.getIndexedSubtreeHash(tenant, protocol, bitPath);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
return this.withProjectionSnapshot(tenant, projectionScopes, snapshot => snapshot.getSubtreeHash(bitPath));
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
private async getLeaves(
|
|
181
|
-
tenant: string,
|
|
182
|
-
protocol: string | undefined,
|
|
183
|
-
projectionScopes: ProjectionScopes | undefined,
|
|
184
|
-
bitPath: boolean[]
|
|
185
|
-
): Promise<string[]> {
|
|
186
|
-
if (projectionScopes === undefined) {
|
|
187
|
-
return this.getIndexedLeaves(tenant, protocol, bitPath);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
return this.withProjectionSnapshot(tenant, projectionScopes, snapshot => snapshot.getLeaves(bitPath));
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
private async getIndexedSubtreeHash(
|
|
194
|
-
tenant: string,
|
|
195
|
-
protocol: string | undefined,
|
|
196
|
-
bitPath: boolean[]
|
|
197
|
-
): Promise<Uint8Array> {
|
|
198
|
-
return MessagesSyncHandler.getIndexedSubtreeHashFromStateIndex(this.deps.stateIndex!, tenant, protocol, bitPath);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
private async getIndexedLeaves(
|
|
202
|
-
tenant: string,
|
|
203
|
-
protocol: string | undefined,
|
|
204
|
-
bitPath: boolean[]
|
|
205
|
-
): Promise<string[]> {
|
|
206
|
-
return MessagesSyncHandler.getIndexedLeavesFromStateIndex(this.deps.stateIndex!, tenant, protocol, bitPath);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
122
|
/**
|
|
210
|
-
*
|
|
211
|
-
*
|
|
212
|
-
* set difference in a single round-trip.
|
|
213
|
-
*
|
|
214
|
-
* Response includes:
|
|
215
|
-
* - `onlyRemote`: messages the server has that the client doesn't, with
|
|
216
|
-
* inline data for small payloads.
|
|
217
|
-
* - `onlyLocal`: bit prefixes where the client has entries the server
|
|
218
|
-
* doesn't (client can enumerate its own leaves for these prefixes).
|
|
123
|
+
* Computes a single-round diff between the client's sparse Merkle tree view
|
|
124
|
+
* and this DWN's full/protocol StateIndex tree.
|
|
219
125
|
*/
|
|
220
126
|
private async handleDiff(
|
|
221
127
|
tenant: string,
|
|
222
128
|
message: MessagesSyncMessage,
|
|
223
129
|
): Promise<MessagesSyncReply> {
|
|
224
130
|
const { protocol, hashes: clientHashes, depth } = message.descriptor;
|
|
225
|
-
const projectionScopes = MessagesSyncHandler.getProjectionScopes(message);
|
|
226
131
|
|
|
227
132
|
if (!clientHashes || depth === undefined) {
|
|
228
133
|
return {
|
|
@@ -230,121 +135,98 @@ export class MessagesSyncHandler implements MethodHandler {
|
|
|
230
135
|
};
|
|
231
136
|
}
|
|
232
137
|
|
|
233
|
-
const stateIndex = this.deps.stateIndex!;
|
|
234
|
-
const projectionSnapshot = await this.createProjectionSnapshot(tenant, projectionScopes);
|
|
235
138
|
const onlyRemoteCids: string[] = [];
|
|
236
139
|
const onlyLocalPrefixes: string[] = [];
|
|
140
|
+
const defaultHashHex = await this.getDefaultHashHex(depth);
|
|
141
|
+
const allPrefixes = new Set<string>();
|
|
237
142
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
// filter out client entries that represent empty subtrees.
|
|
241
|
-
const defaultHashHex = await this.getDefaultHashHex(depth);
|
|
242
|
-
|
|
243
|
-
// Build the set of all prefixes at the given depth that either side has.
|
|
244
|
-
// Filter out client prefixes whose hash equals the default (empty subtree)
|
|
245
|
-
// hash — these represent empty subtrees and should be treated the same as
|
|
246
|
-
// omitted prefixes.
|
|
247
|
-
const allPrefixes = new Set<string>();
|
|
248
|
-
for (const [pfx, hash] of Object.entries(clientHashes)) {
|
|
249
|
-
if (hash !== defaultHashHex) {
|
|
250
|
-
allPrefixes.add(pfx);
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
// Enumerate server-side non-empty prefixes by walking the server's
|
|
255
|
-
// tree to the given depth and collecting leaf prefixes.
|
|
256
|
-
const serverHashes = await this.collectSubtreeHashes(tenant, protocol, projectionSnapshot, depth);
|
|
257
|
-
for (const prefix of Object.keys(serverHashes)) {
|
|
143
|
+
for (const [prefix, hash] of Object.entries(clientHashes)) {
|
|
144
|
+
if (hash !== defaultHashHex) {
|
|
258
145
|
allPrefixes.add(prefix);
|
|
259
146
|
}
|
|
147
|
+
}
|
|
260
148
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
149
|
+
const serverHashes = await this.collectSubtreeHashes(tenant, protocol, depth);
|
|
150
|
+
for (const prefix of Object.keys(serverHashes)) {
|
|
151
|
+
allPrefixes.add(prefix);
|
|
152
|
+
}
|
|
265
153
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
}
|
|
154
|
+
for (const prefix of allPrefixes) {
|
|
155
|
+
const clientHash = clientHashes[prefix];
|
|
156
|
+
const serverHash = serverHashes[prefix];
|
|
270
157
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
continue;
|
|
275
|
-
}
|
|
158
|
+
if (clientHash === serverHash) {
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
276
161
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
tenant,
|
|
281
|
-
protocol,
|
|
282
|
-
projectionSnapshot,
|
|
283
|
-
bitPath
|
|
284
|
-
);
|
|
285
|
-
|
|
286
|
-
onlyRemoteCids.push(...serverLeaves);
|
|
287
|
-
if (clientHash !== undefined) {
|
|
288
|
-
// Both sides have entries but they differ. The client will enumerate
|
|
289
|
-
// its own leaves for this prefix and de-duplicate server leaves.
|
|
290
|
-
onlyLocalPrefixes.push(pfx);
|
|
291
|
-
}
|
|
162
|
+
if (serverHash === undefined) {
|
|
163
|
+
onlyLocalPrefixes.push(prefix);
|
|
164
|
+
continue;
|
|
292
165
|
}
|
|
293
|
-
} finally {
|
|
294
|
-
await projectionSnapshot?.close();
|
|
295
|
-
}
|
|
296
166
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
167
|
+
const bitPath = MessagesSyncHandler.parseBitPrefix(prefix);
|
|
168
|
+
const serverLeaves = await MessagesSyncHandler.getIndexedLeaves(
|
|
169
|
+
this.deps.stateIndex!,
|
|
170
|
+
tenant,
|
|
171
|
+
protocol,
|
|
172
|
+
bitPath,
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
onlyRemoteCids.push(...serverLeaves);
|
|
176
|
+
if (clientHash !== undefined) {
|
|
177
|
+
onlyLocalPrefixes.push(prefix);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
302
180
|
|
|
303
181
|
return {
|
|
304
|
-
status
|
|
305
|
-
onlyRemote,
|
|
306
|
-
onlyLocal
|
|
307
|
-
...(dependencies.length > 0 ? { dependencies } : {}),
|
|
182
|
+
status : { code: 200, detail: 'OK' },
|
|
183
|
+
onlyRemote : await this.buildDiffEntries(tenant, onlyRemoteCids),
|
|
184
|
+
onlyLocal : onlyLocalPrefixes,
|
|
308
185
|
};
|
|
309
186
|
}
|
|
310
187
|
|
|
188
|
+
private async getIndexedRootHash(
|
|
189
|
+
tenant: string,
|
|
190
|
+
protocol: string | undefined
|
|
191
|
+
): Promise<Uint8Array> {
|
|
192
|
+
if (protocol === undefined) {
|
|
193
|
+
return this.deps.stateIndex!.getRoot(tenant);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return this.deps.stateIndex!.getProtocolRoot(tenant, protocol);
|
|
197
|
+
}
|
|
198
|
+
|
|
311
199
|
/**
|
|
312
|
-
*
|
|
313
|
-
* subtree hashes
|
|
200
|
+
* Walks this DWN's StateIndex tree to the requested depth and returns only
|
|
201
|
+
* non-empty subtree hashes keyed by bit prefix.
|
|
314
202
|
*/
|
|
315
203
|
private async collectSubtreeHashes(
|
|
316
204
|
tenant: string,
|
|
317
205
|
protocol: string | undefined,
|
|
318
|
-
projectionSnapshot: RecordsProjectionSnapshot | undefined,
|
|
319
206
|
depth: number,
|
|
320
207
|
): Promise<Record<string, string>> {
|
|
321
|
-
const stateIndex = this.deps.stateIndex!;
|
|
322
208
|
const result: Record<string, string> = {};
|
|
323
209
|
|
|
324
210
|
const walk = async (prefix: string, currentDepth: number): Promise<void> => {
|
|
325
211
|
const bitPath = MessagesSyncHandler.parseBitPrefix(prefix);
|
|
326
|
-
const hash = await MessagesSyncHandler.
|
|
327
|
-
stateIndex
|
|
212
|
+
const hash = await MessagesSyncHandler.getIndexedSubtreeHash(
|
|
213
|
+
this.deps.stateIndex!,
|
|
328
214
|
tenant,
|
|
329
215
|
protocol,
|
|
330
|
-
|
|
331
|
-
bitPath
|
|
216
|
+
bitPath,
|
|
332
217
|
);
|
|
333
218
|
const hexHash = hashToHex(hash);
|
|
334
219
|
const defaultHashHex = await this.getDefaultHashHex(currentDepth);
|
|
335
220
|
|
|
336
221
|
if (hexHash === defaultHashHex) {
|
|
337
|
-
// Empty subtree — don't include in the result.
|
|
338
222
|
return;
|
|
339
223
|
}
|
|
340
224
|
|
|
341
225
|
if (currentDepth >= depth) {
|
|
342
|
-
// Reached target depth with a non-empty subtree.
|
|
343
226
|
result[prefix] = hexHash;
|
|
344
227
|
return;
|
|
345
228
|
}
|
|
346
229
|
|
|
347
|
-
// Recurse into children.
|
|
348
230
|
await Promise.all([
|
|
349
231
|
walk(prefix + '0', currentDepth + 1),
|
|
350
232
|
walk(prefix + '1', currentDepth + 1),
|
|
@@ -355,50 +237,7 @@ export class MessagesSyncHandler implements MethodHandler {
|
|
|
355
237
|
return result;
|
|
356
238
|
}
|
|
357
239
|
|
|
358
|
-
private async
|
|
359
|
-
tenant: string,
|
|
360
|
-
projectionScopes: ProjectionScopes | undefined
|
|
361
|
-
): Promise<RecordsProjectionSnapshot | undefined> {
|
|
362
|
-
if (projectionScopes === undefined) {
|
|
363
|
-
return undefined;
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
return RecordsProjection.createSnapshot({
|
|
367
|
-
tenant,
|
|
368
|
-
messageStore : this.deps.messageStore,
|
|
369
|
-
scopes : projectionScopes,
|
|
370
|
-
});
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
private static async getServerLeaves(
|
|
374
|
-
stateIndex: StateIndex,
|
|
375
|
-
tenant: string,
|
|
376
|
-
protocol: string | undefined,
|
|
377
|
-
projectionSnapshot: RecordsProjectionSnapshot | undefined,
|
|
378
|
-
bitPath: boolean[]
|
|
379
|
-
): Promise<string[]> {
|
|
380
|
-
if (projectionSnapshot === undefined) {
|
|
381
|
-
return MessagesSyncHandler.getIndexedLeavesFromStateIndex(stateIndex, tenant, protocol, bitPath);
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
return projectionSnapshot.getLeaves(bitPath);
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
private static async getServerSubtreeHash(
|
|
388
|
-
stateIndex: StateIndex,
|
|
389
|
-
tenant: string,
|
|
390
|
-
protocol: string | undefined,
|
|
391
|
-
projectionSnapshot: RecordsProjectionSnapshot | undefined,
|
|
392
|
-
bitPath: boolean[]
|
|
393
|
-
): Promise<Uint8Array> {
|
|
394
|
-
if (projectionSnapshot === undefined) {
|
|
395
|
-
return MessagesSyncHandler.getIndexedSubtreeHashFromStateIndex(stateIndex, tenant, protocol, bitPath);
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
return projectionSnapshot.getSubtreeHash(bitPath);
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
private static async getIndexedLeavesFromStateIndex(
|
|
240
|
+
private static async getIndexedLeaves(
|
|
402
241
|
stateIndex: StateIndex,
|
|
403
242
|
tenant: string,
|
|
404
243
|
protocol: string | undefined,
|
|
@@ -411,7 +250,7 @@ export class MessagesSyncHandler implements MethodHandler {
|
|
|
411
250
|
return stateIndex.getProtocolLeaves(tenant, protocol, bitPath);
|
|
412
251
|
}
|
|
413
252
|
|
|
414
|
-
private static async
|
|
253
|
+
private static async getIndexedSubtreeHash(
|
|
415
254
|
stateIndex: StateIndex,
|
|
416
255
|
tenant: string,
|
|
417
256
|
protocol: string | undefined,
|
|
@@ -424,10 +263,6 @@ export class MessagesSyncHandler implements MethodHandler {
|
|
|
424
263
|
return stateIndex.getProtocolSubtreeHash(tenant, protocol, bitPath);
|
|
425
264
|
}
|
|
426
265
|
|
|
427
|
-
/**
|
|
428
|
-
* Get the hex-encoded default hash for a given depth. Lazily cached.
|
|
429
|
-
*/
|
|
430
|
-
private _defaultHashHexCache?: Map<number, string>;
|
|
431
266
|
private async getDefaultHashHex(depth: number): Promise<string> {
|
|
432
267
|
if (this._defaultHashHexCache === undefined) {
|
|
433
268
|
const { initDefaultHashes } = await import('../smt/smt-utils.js');
|
|
@@ -441,9 +276,8 @@ export class MessagesSyncHandler implements MethodHandler {
|
|
|
441
276
|
}
|
|
442
277
|
|
|
443
278
|
/**
|
|
444
|
-
*
|
|
445
|
-
*
|
|
446
|
-
* payloads, inlines the data as base64url.
|
|
279
|
+
* Builds diff entries and inlines data when it is small enough for the
|
|
280
|
+
* MessagesSync response. Large record data remains fetch-by-CID.
|
|
447
281
|
*/
|
|
448
282
|
private async buildDiffEntries(
|
|
449
283
|
tenant: string,
|
|
@@ -454,22 +288,18 @@ export class MessagesSyncHandler implements MethodHandler {
|
|
|
454
288
|
for (const messageCid of messageCids) {
|
|
455
289
|
const { message, encodedData: inlineData, data } = await this.readMessageByCid(tenant, messageCid);
|
|
456
290
|
if (!message) {
|
|
457
|
-
// Message was deleted between diff computation and read — skip.
|
|
458
291
|
continue;
|
|
459
292
|
}
|
|
460
293
|
|
|
461
294
|
const entry: MessagesSyncDiffEntry = { messageCid, message };
|
|
462
295
|
|
|
463
|
-
// Use inline data from the MessageStore if available (small payloads).
|
|
464
296
|
if (inlineData) {
|
|
465
297
|
entry.encodedData = inlineData;
|
|
466
298
|
} else if (data) {
|
|
467
|
-
// Data is in the DataStore — inline it if small enough.
|
|
468
299
|
const bytes = await MessagesSyncHandler.streamToBytes(data);
|
|
469
300
|
if (bytes.byteLength <= DEFAULT_MAX_INLINE_DATA_SIZE) {
|
|
470
301
|
entry.encodedData = Encoder.bytesToBase64Url(bytes);
|
|
471
302
|
}
|
|
472
|
-
// Large payloads are NOT inlined — client fetches via MessagesRead.
|
|
473
303
|
}
|
|
474
304
|
|
|
475
305
|
entries.push(entry);
|
|
@@ -478,281 +308,6 @@ export class MessagesSyncHandler implements MethodHandler {
|
|
|
478
308
|
return entries;
|
|
479
309
|
}
|
|
480
310
|
|
|
481
|
-
private async buildProjectedDependencyEntries(
|
|
482
|
-
tenant: string,
|
|
483
|
-
primaryEntries: MessagesSyncDiffEntry[],
|
|
484
|
-
): Promise<MessagesSyncDependencyEntry[]> {
|
|
485
|
-
const dependenciesByCid = new Map<string, MessagesSyncDependencyEntry>();
|
|
486
|
-
const configsByProtocol = new Map<string, GenericMessage[]>();
|
|
487
|
-
|
|
488
|
-
for (const primaryEntry of primaryEntries) {
|
|
489
|
-
const protocolMetadata = MessagesSyncHandler.recordsWriteProtocolMetadata(primaryEntry.message);
|
|
490
|
-
if (protocolMetadata !== undefined) {
|
|
491
|
-
await this.addProtocolConfigClosureDependencies(
|
|
492
|
-
tenant,
|
|
493
|
-
protocolMetadata.protocol,
|
|
494
|
-
protocolMetadata.messageTimestamp,
|
|
495
|
-
primaryEntry.messageCid,
|
|
496
|
-
configsByProtocol,
|
|
497
|
-
dependenciesByCid,
|
|
498
|
-
);
|
|
499
|
-
continue;
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
const initialWrite = await this.readRecordsDeleteInitialWrite(tenant, primaryEntry.message);
|
|
503
|
-
if (initialWrite === undefined) {
|
|
504
|
-
continue;
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
await MessagesSyncHandler.addRecordsInitialWriteDependency(
|
|
508
|
-
primaryEntry.messageCid,
|
|
509
|
-
initialWrite,
|
|
510
|
-
dependenciesByCid,
|
|
511
|
-
);
|
|
512
|
-
|
|
513
|
-
const initialWriteMetadata = MessagesSyncHandler.recordsWriteProtocolMetadata(initialWrite);
|
|
514
|
-
if (initialWriteMetadata === undefined) {
|
|
515
|
-
continue;
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
await this.addProtocolConfigClosureDependencies(
|
|
519
|
-
tenant,
|
|
520
|
-
initialWriteMetadata.protocol,
|
|
521
|
-
initialWriteMetadata.messageTimestamp,
|
|
522
|
-
primaryEntry.messageCid,
|
|
523
|
-
configsByProtocol,
|
|
524
|
-
dependenciesByCid,
|
|
525
|
-
);
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
return [...dependenciesByCid.values()];
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
private async readRecordsDeleteInitialWrite(
|
|
532
|
-
tenant: string,
|
|
533
|
-
message: GenericMessage | undefined,
|
|
534
|
-
): Promise<GenericMessage | undefined> {
|
|
535
|
-
const recordId = MessagesSyncHandler.recordsDeleteRecordId(message);
|
|
536
|
-
if (recordId === undefined) {
|
|
537
|
-
return undefined;
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
const { messages } = await this.deps.messageStore.query(tenant, [{
|
|
541
|
-
interface : DwnInterfaceName.Records,
|
|
542
|
-
method : DwnMethodName.Write,
|
|
543
|
-
recordId,
|
|
544
|
-
}]);
|
|
545
|
-
const initialWrite = await RecordsWrite.getInitialWrite(messages);
|
|
546
|
-
return initialWrite === undefined ? undefined : MessagesSyncHandler.toWireMessage(initialWrite);
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
private static async addRecordsInitialWriteDependency(
|
|
550
|
-
rootMessageCid: string,
|
|
551
|
-
dependency: GenericMessage,
|
|
552
|
-
dependenciesByCid: Map<string, MessagesSyncDependencyEntry>,
|
|
553
|
-
): Promise<void> {
|
|
554
|
-
const dependencyCid = await Message.getCid(dependency);
|
|
555
|
-
if (dependenciesByCid.has(dependencyCid)) {
|
|
556
|
-
return;
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
dependenciesByCid.set(dependencyCid, {
|
|
560
|
-
dependencyClass : 'recordsInitialWrite',
|
|
561
|
-
messageCid : dependencyCid,
|
|
562
|
-
message : dependency,
|
|
563
|
-
rootMessageCid,
|
|
564
|
-
});
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
private async addProtocolConfigClosureDependencies(
|
|
568
|
-
tenant: string,
|
|
569
|
-
rootProtocol: string,
|
|
570
|
-
rootMessageTimestamp: string,
|
|
571
|
-
rootMessageCid: string,
|
|
572
|
-
configsByProtocol: Map<string, GenericMessage[]>,
|
|
573
|
-
dependenciesByCid: Map<string, MessagesSyncDependencyEntry>,
|
|
574
|
-
): Promise<void> {
|
|
575
|
-
// Dependency hints are not part of the projected root; they are advisory
|
|
576
|
-
// bootstrap data for the receiver. Bound each closure to the primary
|
|
577
|
-
// RecordsWrite timestamp because protocol authorization uses the definition
|
|
578
|
-
// active when that record was created, not whatever config is newest today.
|
|
579
|
-
const visitedProtocols = new Set<string>();
|
|
580
|
-
const pendingProtocols = [rootProtocol];
|
|
581
|
-
|
|
582
|
-
for (
|
|
583
|
-
let protocol = MessagesSyncHandler.takeNextUnvisitedProtocol(pendingProtocols, visitedProtocols);
|
|
584
|
-
protocol !== undefined;
|
|
585
|
-
protocol = MessagesSyncHandler.takeNextUnvisitedProtocol(pendingProtocols, visitedProtocols)
|
|
586
|
-
) {
|
|
587
|
-
const configs = await this.getCachedGoverningProtocolsConfigure(
|
|
588
|
-
tenant,
|
|
589
|
-
protocol,
|
|
590
|
-
rootMessageTimestamp,
|
|
591
|
-
configsByProtocol,
|
|
592
|
-
);
|
|
593
|
-
await MessagesSyncHandler.addProtocolConfigDependencies({
|
|
594
|
-
configs,
|
|
595
|
-
rootMessageCid,
|
|
596
|
-
visitedProtocols,
|
|
597
|
-
pendingProtocols,
|
|
598
|
-
dependenciesByCid,
|
|
599
|
-
});
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
private async getCachedGoverningProtocolsConfigure(
|
|
604
|
-
tenant: string,
|
|
605
|
-
protocol: string,
|
|
606
|
-
messageTimestamp: string,
|
|
607
|
-
configsByProtocol: Map<string, GenericMessage[]>,
|
|
608
|
-
): Promise<GenericMessage[]> {
|
|
609
|
-
const configCacheKey = JSON.stringify([protocol, messageTimestamp]);
|
|
610
|
-
const cachedConfigs = configsByProtocol.get(configCacheKey);
|
|
611
|
-
if (cachedConfigs !== undefined) {
|
|
612
|
-
return cachedConfigs;
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
const configs = await this.readGoverningProtocolsConfigure(tenant, protocol, messageTimestamp);
|
|
616
|
-
configsByProtocol.set(configCacheKey, configs);
|
|
617
|
-
return configs;
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
private static async addProtocolConfigDependencies({
|
|
621
|
-
configs,
|
|
622
|
-
rootMessageCid,
|
|
623
|
-
visitedProtocols,
|
|
624
|
-
pendingProtocols,
|
|
625
|
-
dependenciesByCid,
|
|
626
|
-
}: {
|
|
627
|
-
configs: GenericMessage[];
|
|
628
|
-
rootMessageCid: string;
|
|
629
|
-
visitedProtocols: Set<string>;
|
|
630
|
-
pendingProtocols: string[];
|
|
631
|
-
dependenciesByCid: Map<string, MessagesSyncDependencyEntry>;
|
|
632
|
-
}): Promise<void> {
|
|
633
|
-
for (const dependency of configs) {
|
|
634
|
-
await MessagesSyncHandler.addProtocolConfigDependency(rootMessageCid, dependency, dependenciesByCid);
|
|
635
|
-
MessagesSyncHandler.queueUnvisitedProtocols(
|
|
636
|
-
MessagesSyncHandler.protocolsConfigureUses(dependency),
|
|
637
|
-
visitedProtocols,
|
|
638
|
-
pendingProtocols,
|
|
639
|
-
);
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
private static async addProtocolConfigDependency(
|
|
644
|
-
rootMessageCid: string,
|
|
645
|
-
dependency: GenericMessage,
|
|
646
|
-
dependenciesByCid: Map<string, MessagesSyncDependencyEntry>,
|
|
647
|
-
): Promise<void> {
|
|
648
|
-
const dependencyCid = await Message.getCid(dependency);
|
|
649
|
-
if (dependenciesByCid.has(dependencyCid)) {
|
|
650
|
-
return;
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
dependenciesByCid.set(dependencyCid, {
|
|
654
|
-
dependencyClass : 'protocolsConfigure',
|
|
655
|
-
messageCid : dependencyCid,
|
|
656
|
-
message : dependency,
|
|
657
|
-
rootMessageCid,
|
|
658
|
-
});
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
private static takeNextUnvisitedProtocol(
|
|
662
|
-
pendingProtocols: string[],
|
|
663
|
-
visitedProtocols: Set<string>,
|
|
664
|
-
): string | undefined {
|
|
665
|
-
while (pendingProtocols.length > 0) {
|
|
666
|
-
const protocol = pendingProtocols.shift()!;
|
|
667
|
-
if (visitedProtocols.has(protocol)) {
|
|
668
|
-
continue;
|
|
669
|
-
}
|
|
670
|
-
visitedProtocols.add(protocol);
|
|
671
|
-
return protocol;
|
|
672
|
-
}
|
|
673
|
-
return undefined;
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
private static queueUnvisitedProtocols(
|
|
677
|
-
protocols: string[],
|
|
678
|
-
visitedProtocols: Set<string>,
|
|
679
|
-
pendingProtocols: string[],
|
|
680
|
-
): void {
|
|
681
|
-
for (const protocol of protocols) {
|
|
682
|
-
if (!visitedProtocols.has(protocol)) {
|
|
683
|
-
pendingProtocols.push(protocol);
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
private static protocolsConfigureUses(message: GenericMessage): string[] {
|
|
689
|
-
if (
|
|
690
|
-
message.descriptor.interface !== DwnInterfaceName.Protocols ||
|
|
691
|
-
message.descriptor.method !== DwnMethodName.Configure
|
|
692
|
-
) {
|
|
693
|
-
return [];
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
const uses = (message.descriptor as ProtocolsConfigureDefinitionDescriptor).definition?.uses;
|
|
697
|
-
return uses === undefined
|
|
698
|
-
? []
|
|
699
|
-
: Object.values(uses).filter((protocol): protocol is string => typeof protocol === 'string');
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
private async readGoverningProtocolsConfigure(
|
|
703
|
-
tenant: string,
|
|
704
|
-
protocol: string,
|
|
705
|
-
messageTimestamp: string,
|
|
706
|
-
): Promise<GenericMessage[]> {
|
|
707
|
-
const { messages } = await this.deps.messageStore.query(
|
|
708
|
-
tenant,
|
|
709
|
-
[{
|
|
710
|
-
interface : DwnInterfaceName.Protocols,
|
|
711
|
-
method : DwnMethodName.Configure,
|
|
712
|
-
protocol,
|
|
713
|
-
messageTimestamp : { lte: messageTimestamp },
|
|
714
|
-
}],
|
|
715
|
-
{ messageTimestamp: SortDirection.Descending },
|
|
716
|
-
);
|
|
717
|
-
|
|
718
|
-
const governingMessage = await Message.getNewestMessage(messages);
|
|
719
|
-
return governingMessage === undefined ? [] : [governingMessage];
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
private static recordsWriteProtocolMetadata(message: GenericMessage | undefined): RecordsWriteProtocolMetadata | undefined {
|
|
723
|
-
if (
|
|
724
|
-
message?.descriptor.interface !== DwnInterfaceName.Records ||
|
|
725
|
-
message.descriptor.method !== DwnMethodName.Write
|
|
726
|
-
) {
|
|
727
|
-
return undefined;
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
const protocol = (message.descriptor as RecordsWriteProtocolDescriptor).protocol;
|
|
731
|
-
return typeof protocol === 'string'
|
|
732
|
-
? { protocol, messageTimestamp: message.descriptor.messageTimestamp }
|
|
733
|
-
: undefined;
|
|
734
|
-
}
|
|
735
|
-
|
|
736
|
-
private static recordsDeleteRecordId(message: GenericMessage | undefined): string | undefined {
|
|
737
|
-
if (
|
|
738
|
-
message?.descriptor.interface !== DwnInterfaceName.Records ||
|
|
739
|
-
message.descriptor.method !== DwnMethodName.Delete
|
|
740
|
-
) {
|
|
741
|
-
return undefined;
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
const recordId = (message.descriptor as Record<string, unknown>).recordId;
|
|
745
|
-
return typeof recordId === 'string' ? recordId : undefined;
|
|
746
|
-
}
|
|
747
|
-
|
|
748
|
-
private static toWireMessage(message: GenericMessage): GenericMessage {
|
|
749
|
-
const { encodedData: _encodedData, initialWrite: _initialWrite, ...wireMessage } = message as StoredMessageWithInternalFields;
|
|
750
|
-
return wireMessage;
|
|
751
|
-
}
|
|
752
|
-
|
|
753
|
-
/**
|
|
754
|
-
* Read a message and its data from the MessageStore + DataStore by CID.
|
|
755
|
-
*/
|
|
756
311
|
private async readMessageByCid(
|
|
757
312
|
tenant: string,
|
|
758
313
|
messageCid: string,
|
|
@@ -762,11 +317,6 @@ export class MessagesSyncHandler implements MethodHandler {
|
|
|
762
317
|
return {};
|
|
763
318
|
}
|
|
764
319
|
|
|
765
|
-
// Extract and strip `encodedData` from the stored message.
|
|
766
|
-
// `encodedData` is an internal storage optimization for small payloads
|
|
767
|
-
// that are stored inline in the MessageStore rather than in the DataStore.
|
|
768
|
-
// It must not be included in the wire-format message (the recipient's
|
|
769
|
-
// DWN would reject it as an unexpected top-level property).
|
|
770
320
|
let inlineEncodedData: string | undefined;
|
|
771
321
|
if (MessagesSyncHandler.hasEncodedData(storedMessage)) {
|
|
772
322
|
inlineEncodedData = storedMessage.encodedData;
|
|
@@ -774,20 +324,11 @@ export class MessagesSyncHandler implements MethodHandler {
|
|
|
774
324
|
}
|
|
775
325
|
|
|
776
326
|
let data: ReadableStream<Uint8Array> | undefined;
|
|
777
|
-
|
|
778
|
-
// Check if this is a RecordsWrite with data that is small enough to inline.
|
|
779
327
|
if (inlineEncodedData === undefined && Records.isRecordsWrite(storedMessage)) {
|
|
780
328
|
const { dataCid, dataSize } = storedMessage.descriptor;
|
|
781
|
-
if (dataSize <= DEFAULT_MAX_INLINE_DATA_SIZE) {
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
// DataStore uses recordId, not messageCid. For RecordsWrite,
|
|
785
|
-
// recordId is a top-level message property.
|
|
786
|
-
const dataResult = await this.deps.dataStore.get(tenant, storedMessage.recordId, dataCid);
|
|
787
|
-
if (dataResult?.dataStream) {
|
|
788
|
-
data = dataResult.dataStream;
|
|
789
|
-
}
|
|
790
|
-
}
|
|
329
|
+
if (dataSize <= DEFAULT_MAX_INLINE_DATA_SIZE && this.deps.dataStore) {
|
|
330
|
+
const dataResult = await this.deps.dataStore.get(tenant, storedMessage.recordId, dataCid);
|
|
331
|
+
data = dataResult?.dataStream;
|
|
791
332
|
}
|
|
792
333
|
}
|
|
793
334
|
|
|
@@ -798,38 +339,6 @@ export class MessagesSyncHandler implements MethodHandler {
|
|
|
798
339
|
return 'encodedData' in message && typeof message.encodedData === 'string';
|
|
799
340
|
}
|
|
800
341
|
|
|
801
|
-
private static getProjectionScopes(
|
|
802
|
-
message: MessagesSyncMessage,
|
|
803
|
-
): ProjectionScopes | undefined {
|
|
804
|
-
const { projectionScopes } = message.descriptor;
|
|
805
|
-
if (projectionScopes === undefined) {
|
|
806
|
-
return undefined;
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
return projectionScopes as [RecordsProjectionScope, ...RecordsProjectionScope[]];
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
private async withProjectionSnapshot<T>(
|
|
813
|
-
tenant: string,
|
|
814
|
-
scopes: readonly [RecordsProjectionScope, ...RecordsProjectionScope[]],
|
|
815
|
-
fn: (snapshot: RecordsProjectionSnapshot) => Promise<T>,
|
|
816
|
-
): Promise<T> {
|
|
817
|
-
const snapshot = await RecordsProjection.createSnapshot({
|
|
818
|
-
tenant : tenant,
|
|
819
|
-
messageStore : this.deps.messageStore,
|
|
820
|
-
scopes : scopes,
|
|
821
|
-
});
|
|
822
|
-
|
|
823
|
-
try {
|
|
824
|
-
return await fn(snapshot);
|
|
825
|
-
} finally {
|
|
826
|
-
await snapshot.close();
|
|
827
|
-
}
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
/**
|
|
831
|
-
* Read a ReadableStream to completion and return the bytes.
|
|
832
|
-
*/
|
|
833
342
|
private static async streamToBytes(stream: ReadableStream<Uint8Array>): Promise<Uint8Array> {
|
|
834
343
|
const reader = stream.getReader();
|
|
835
344
|
const chunks: Uint8Array[] = [];
|
|
@@ -851,9 +360,6 @@ export class MessagesSyncHandler implements MethodHandler {
|
|
|
851
360
|
return result;
|
|
852
361
|
}
|
|
853
362
|
|
|
854
|
-
/**
|
|
855
|
-
* Parse a bit prefix string (e.g. "0110101") into a boolean array.
|
|
856
|
-
*/
|
|
857
363
|
private static parseBitPrefix(prefix: string): boolean[] {
|
|
858
364
|
if (!/^[01]*$/.test(prefix)) {
|
|
859
365
|
throw new DwnError(
|
|
@@ -889,8 +395,9 @@ export class MessagesSyncHandler implements MethodHandler {
|
|
|
889
395
|
permissionGrants,
|
|
890
396
|
messageStore
|
|
891
397
|
});
|
|
892
|
-
|
|
893
|
-
throw new DwnError(DwnErrorCode.MessagesSyncAuthorizationFailed, 'message failed authorization');
|
|
398
|
+
return;
|
|
894
399
|
}
|
|
400
|
+
|
|
401
|
+
throw new DwnError(DwnErrorCode.MessagesSyncAuthorizationFailed, 'message failed authorization');
|
|
895
402
|
}
|
|
896
403
|
}
|