@enbox/dwn-sdk-js 0.4.15 → 0.4.17
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 +1 -1
- package/dist/browser.mjs.map +4 -4
- package/dist/esm/generated/precompiled-validators.js +734 -747
- package/dist/esm/generated/precompiled-validators.js.map +1 -1
- package/dist/esm/json-schemas/definitions.json +5 -0
- package/dist/esm/src/core/dwn-constant.js +2 -2
- package/dist/esm/src/core/dwn-error.js +0 -1
- package/dist/esm/src/core/dwn-error.js.map +1 -1
- package/dist/esm/src/core/protocol-authorization-validation.js +1 -24
- package/dist/esm/src/core/protocol-authorization-validation.js.map +1 -1
- package/dist/esm/src/core/protocol-authorization.js +1 -5
- package/dist/esm/src/core/protocol-authorization.js.map +1 -1
- package/dist/esm/src/core/validation-state-reader.js +2 -3
- package/dist/esm/src/core/validation-state-reader.js.map +1 -1
- package/dist/esm/src/handlers/messages-query.js +2 -29
- package/dist/esm/src/handlers/messages-query.js.map +1 -1
- package/dist/esm/src/handlers/messages-subscribe.js +52 -1
- package/dist/esm/src/handlers/messages-subscribe.js.map +1 -1
- package/dist/esm/src/handlers/records-count.js +18 -10
- package/dist/esm/src/handlers/records-count.js.map +1 -1
- package/dist/esm/src/handlers/records-query.js +10 -7
- package/dist/esm/src/handlers/records-query.js.map +1 -1
- package/dist/esm/src/handlers/records-subscribe.js +10 -7
- package/dist/esm/src/handlers/records-subscribe.js.map +1 -1
- package/dist/esm/src/handlers/records-write.js +8 -26
- package/dist/esm/src/handlers/records-write.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/protocols-configure.js +4 -9
- package/dist/esm/src/interfaces/protocols-configure.js.map +1 -1
- package/dist/esm/src/store/index-level.js +143 -6
- package/dist/esm/src/store/index-level.js.map +1 -1
- package/dist/esm/src/store/message-store-level.js +162 -23
- package/dist/esm/src/store/message-store-level.js.map +1 -1
- package/dist/esm/src/store/storage-controller.js +1 -3
- package/dist/esm/src/store/storage-controller.js.map +1 -1
- package/dist/esm/src/types/protocols-types.js +0 -10
- package/dist/esm/src/types/protocols-types.js.map +1 -1
- package/dist/esm/src/utils/filter.js +35 -1
- package/dist/esm/src/utils/filter.js.map +1 -1
- package/dist/esm/src/utils/grant-key-coverage.js +4 -6
- package/dist/esm/src/utils/grant-key-coverage.js.map +1 -1
- package/dist/esm/src/utils/messages.js +40 -14
- package/dist/esm/src/utils/messages.js.map +1 -1
- package/dist/esm/src/utils/permission-scope.js +3 -9
- package/dist/esm/src/utils/permission-scope.js.map +1 -1
- package/dist/esm/src/utils/record-limit-occupancy.js +70 -202
- package/dist/esm/src/utils/record-limit-occupancy.js.map +1 -1
- package/dist/esm/src/utils/records.js +9 -9
- package/dist/esm/src/utils/records.js.map +1 -1
- package/dist/esm/src/utils/replication.js +14 -0
- package/dist/esm/src/utils/replication.js.map +1 -1
- package/dist/esm/tests/core/records-grant-authorization.spec.js +13 -0
- package/dist/esm/tests/core/records-grant-authorization.spec.js.map +1 -1
- package/dist/esm/tests/core/replication-replay-property.spec.js +2 -2
- package/dist/esm/tests/core/replication-replay-property.spec.js.map +1 -1
- package/dist/esm/tests/core/validation-read-closure.spec.js +1 -1
- package/dist/esm/tests/core/validation-read-closure.spec.js.map +1 -1
- package/dist/esm/tests/dwn.spec.js +2 -2
- package/dist/esm/tests/dwn.spec.js.map +1 -1
- package/dist/esm/tests/features/records-delivery.spec.js +2 -5
- package/dist/esm/tests/features/records-delivery.spec.js.map +1 -1
- package/dist/esm/tests/features/records-nested-query-scope.spec.js +138 -73
- package/dist/esm/tests/features/records-nested-query-scope.spec.js.map +1 -1
- package/dist/esm/tests/features/records-record-limit.spec.js +264 -95
- package/dist/esm/tests/features/records-record-limit.spec.js.map +1 -1
- package/dist/esm/tests/fuzz/filter.fuzz.spec.js +19 -4
- package/dist/esm/tests/fuzz/filter.fuzz.spec.js.map +1 -1
- package/dist/esm/tests/handlers/messages-query.spec.js +7 -15
- package/dist/esm/tests/handlers/messages-query.spec.js.map +1 -1
- package/dist/esm/tests/handlers/messages-subscribe.spec.js +129 -1
- package/dist/esm/tests/handlers/messages-subscribe.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-write.spec.js +57 -3
- package/dist/esm/tests/handlers/records-write.spec.js.map +1 -1
- package/dist/esm/tests/store/index-level.spec.js +114 -1
- package/dist/esm/tests/store/index-level.spec.js.map +1 -1
- package/dist/esm/tests/store/message-store-cross-context.spec.js +46 -0
- package/dist/esm/tests/store/message-store-cross-context.spec.js.map +1 -0
- package/dist/esm/tests/store/message-store-level.spec.js +27 -0
- package/dist/esm/tests/store/message-store-level.spec.js.map +1 -1
- package/dist/esm/tests/store/message-store.spec.js +81 -0
- package/dist/esm/tests/store/message-store.spec.js.map +1 -1
- package/dist/esm/tests/utils/filters.spec.js +25 -4
- package/dist/esm/tests/utils/filters.spec.js.map +1 -1
- package/dist/esm/tests/utils/permission-scope.spec.js +4 -0
- package/dist/esm/tests/utils/permission-scope.spec.js.map +1 -1
- package/dist/esm/tests/utils/records.spec.js +12 -0
- package/dist/esm/tests/utils/records.spec.js.map +1 -1
- package/dist/esm/tests/validation/json-schemas/records/records-write.spec.js +3 -1
- package/dist/esm/tests/validation/json-schemas/records/records-write.spec.js.map +1 -1
- package/dist/types/generated/precompiled-validators.d.ts.map +1 -1
- package/dist/types/src/core/dwn-constant.d.ts +2 -2
- package/dist/types/src/core/dwn-error.d.ts +0 -1
- package/dist/types/src/core/dwn-error.d.ts.map +1 -1
- package/dist/types/src/core/protocol-authorization-validation.d.ts +0 -10
- package/dist/types/src/core/protocol-authorization-validation.d.ts.map +1 -1
- package/dist/types/src/core/protocol-authorization.d.ts.map +1 -1
- package/dist/types/src/core/validation-state-reader.d.ts.map +1 -1
- package/dist/types/src/handlers/messages-query.d.ts +0 -2
- package/dist/types/src/handlers/messages-query.d.ts.map +1 -1
- package/dist/types/src/handlers/messages-subscribe.d.ts +16 -0
- package/dist/types/src/handlers/messages-subscribe.d.ts.map +1 -1
- package/dist/types/src/handlers/records-count.d.ts +1 -0
- package/dist/types/src/handlers/records-count.d.ts.map +1 -1
- package/dist/types/src/handlers/records-query.d.ts.map +1 -1
- package/dist/types/src/handlers/records-subscribe.d.ts.map +1 -1
- package/dist/types/src/handlers/records-write.d.ts +3 -0
- package/dist/types/src/handlers/records-write.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/protocols-configure.d.ts.map +1 -1
- package/dist/types/src/store/index-level.d.ts +37 -0
- package/dist/types/src/store/index-level.d.ts.map +1 -1
- package/dist/types/src/store/message-store-level.d.ts +23 -6
- package/dist/types/src/store/message-store-level.d.ts.map +1 -1
- package/dist/types/src/store/storage-controller.d.ts.map +1 -1
- package/dist/types/src/types/message-store.d.ts +23 -2
- package/dist/types/src/types/message-store.d.ts.map +1 -1
- package/dist/types/src/types/messages-types.d.ts +14 -0
- package/dist/types/src/types/messages-types.d.ts.map +1 -1
- package/dist/types/src/types/protocols-types.d.ts +7 -24
- package/dist/types/src/types/protocols-types.d.ts.map +1 -1
- package/dist/types/src/types/query-types.d.ts +8 -1
- package/dist/types/src/types/query-types.d.ts.map +1 -1
- package/dist/types/src/types/records-types.d.ts +2 -1
- package/dist/types/src/types/records-types.d.ts.map +1 -1
- package/dist/types/src/utils/filter.d.ts +14 -1
- package/dist/types/src/utils/filter.d.ts.map +1 -1
- package/dist/types/src/utils/grant-key-coverage.d.ts.map +1 -1
- package/dist/types/src/utils/messages.d.ts +8 -0
- package/dist/types/src/utils/messages.d.ts.map +1 -1
- package/dist/types/src/utils/permission-scope.d.ts +0 -2
- package/dist/types/src/utils/permission-scope.d.ts.map +1 -1
- package/dist/types/src/utils/record-limit-occupancy.d.ts +25 -12
- package/dist/types/src/utils/record-limit-occupancy.d.ts.map +1 -1
- package/dist/types/src/utils/records.d.ts +3 -2
- package/dist/types/src/utils/records.d.ts.map +1 -1
- package/dist/types/src/utils/replication.d.ts +8 -1
- package/dist/types/src/utils/replication.d.ts.map +1 -1
- package/dist/types/tests/features/records-delivery.spec.d.ts.map +1 -1
- package/dist/types/tests/features/records-nested-query-scope.spec.d.ts.map +1 -1
- package/dist/types/tests/features/records-record-limit.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/messages-query.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/messages-subscribe.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-write.spec.d.ts.map +1 -1
- package/dist/types/tests/store/message-store-cross-context.spec.d.ts +2 -0
- package/dist/types/tests/store/message-store-cross-context.spec.d.ts.map +1 -0
- package/dist/types/tests/store/message-store.spec.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/core/dwn-constant.ts +2 -2
- package/src/core/dwn-error.ts +0 -2
- package/src/core/protocol-authorization-validation.ts +1 -33
- package/src/core/protocol-authorization.ts +0 -7
- package/src/core/validation-state-reader.ts +2 -3
- package/src/handlers/messages-query.ts +3 -41
- package/src/handlers/messages-subscribe.ts +67 -3
- package/src/handlers/records-count.ts +34 -13
- package/src/handlers/records-query.ts +14 -11
- package/src/handlers/records-subscribe.ts +18 -11
- package/src/handlers/records-write.ts +17 -32
- package/src/index.ts +3 -3
- package/src/interfaces/protocols-configure.ts +4 -13
- package/src/store/index-level.ts +197 -10
- package/src/store/message-store-level.ts +220 -27
- package/src/store/storage-controller.ts +1 -3
- package/src/types/message-store.ts +25 -2
- package/src/types/messages-types.ts +14 -0
- package/src/types/protocols-types.ts +7 -26
- package/src/types/query-types.ts +10 -2
- package/src/types/records-types.ts +2 -1
- package/src/utils/filter.ts +40 -3
- package/src/utils/grant-key-coverage.ts +4 -7
- package/src/utils/messages.ts +49 -14
- package/src/utils/permission-scope.ts +4 -11
- package/src/utils/record-limit-occupancy.ts +105 -293
- package/src/utils/records.ts +9 -9
- package/src/utils/replication.ts +19 -1
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import sinon from 'sinon';
|
|
2
1
|
import { DataStream } from '../../src/utils/data-stream.js';
|
|
2
|
+
import { DateSort } from '../../src/types/records-types.js';
|
|
3
3
|
import { DidKey } from '@enbox/dids';
|
|
4
4
|
import { Dwn } from '../../src/dwn.js';
|
|
5
5
|
import { DwnConstant } from '../../src/core/dwn-constant.js';
|
|
6
6
|
import { DwnErrorCode } from '../../src/core/dwn-error.js';
|
|
7
7
|
import { Jws } from '../../src/utils/jws.js';
|
|
8
|
+
import { Poller } from '../utils/poller.js';
|
|
8
9
|
import { ProtocolsConfigure } from '../../src/interfaces/protocols-configure.js';
|
|
9
10
|
import { RecordsRead } from '../../src/interfaces/records-read.js';
|
|
10
11
|
import { sleep } from '@enbox/common';
|
|
@@ -34,7 +35,6 @@ export function testRecordsRecordLimit() {
|
|
|
34
35
|
dwn = await Dwn.create({ didResolver, messageStore, dataStore, eventLog, resumableTaskStore });
|
|
35
36
|
});
|
|
36
37
|
beforeEach(async () => {
|
|
37
|
-
sinon.restore();
|
|
38
38
|
await messageStore.clear();
|
|
39
39
|
await dataStore.clear();
|
|
40
40
|
await resumableTaskStore.clear();
|
|
@@ -58,6 +58,7 @@ export function testRecordsRecordLimit() {
|
|
|
58
58
|
protocol: input.protocol,
|
|
59
59
|
protocolPath: input.protocolPath,
|
|
60
60
|
parentContextId: input.parentContextId,
|
|
61
|
+
published: input.published,
|
|
61
62
|
dateCreated: input.dateCreated,
|
|
62
63
|
messageTimestamp: input.dateCreated,
|
|
63
64
|
});
|
|
@@ -73,6 +74,7 @@ export function testRecordsRecordLimit() {
|
|
|
73
74
|
protocolPath: input.protocolPath,
|
|
74
75
|
...(input.contextId === undefined ? {} : { contextId: input.contextId }),
|
|
75
76
|
},
|
|
77
|
+
dateSort: input.dateSort,
|
|
76
78
|
pagination: input.pagination,
|
|
77
79
|
});
|
|
78
80
|
const reply = await (input.targetDwn ?? dwn).processMessage(input.author.did, recordsQuery.message);
|
|
@@ -108,6 +110,7 @@ export function testRecordsRecordLimit() {
|
|
|
108
110
|
filter: {
|
|
109
111
|
protocol: input.protocol,
|
|
110
112
|
protocolPath: input.protocolPath,
|
|
113
|
+
...(input.contextId === undefined ? {} : { contextId: input.contextId }),
|
|
111
114
|
},
|
|
112
115
|
});
|
|
113
116
|
const reply = await (input.targetDwn ?? dwn).processMessage(input.author.did, recordsSubscribe.message, { subscriptionHandler: () => { } });
|
|
@@ -124,7 +127,7 @@ export function testRecordsRecordLimit() {
|
|
|
124
127
|
}
|
|
125
128
|
}
|
|
126
129
|
describe('ProtocolsConfigure validation', () => {
|
|
127
|
-
it('should allow $recordLimit with valid max
|
|
130
|
+
it('should allow $recordLimit with a valid max', async () => {
|
|
128
131
|
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
129
132
|
const definition = {
|
|
130
133
|
protocol: 'http://example.com/record-limit',
|
|
@@ -132,7 +135,7 @@ export function testRecordsRecordLimit() {
|
|
|
132
135
|
types: { message: {} },
|
|
133
136
|
structure: {
|
|
134
137
|
message: {
|
|
135
|
-
$recordLimit: { max: 5
|
|
138
|
+
$recordLimit: { max: 5 },
|
|
136
139
|
}
|
|
137
140
|
}
|
|
138
141
|
};
|
|
@@ -141,10 +144,7 @@ export function testRecordsRecordLimit() {
|
|
|
141
144
|
definition,
|
|
142
145
|
});
|
|
143
146
|
expect(protocolsConfigure.message.descriptor.definition).toBeDefined();
|
|
144
|
-
expect(protocolsConfigure.message.descriptor.definition.structure.message.$recordLimit).toEqual({
|
|
145
|
-
max: 5,
|
|
146
|
-
strategy: 'reject',
|
|
147
|
-
});
|
|
147
|
+
expect(protocolsConfigure.message.descriptor.definition.structure.message.$recordLimit).toEqual({ max: 5 });
|
|
148
148
|
});
|
|
149
149
|
it('should allow $recordLimit with max of 1', async () => {
|
|
150
150
|
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
@@ -154,7 +154,7 @@ export function testRecordsRecordLimit() {
|
|
|
154
154
|
types: { profile: {} },
|
|
155
155
|
structure: {
|
|
156
156
|
profile: {
|
|
157
|
-
$recordLimit: { max: 1
|
|
157
|
+
$recordLimit: { max: 1 },
|
|
158
158
|
}
|
|
159
159
|
}
|
|
160
160
|
};
|
|
@@ -162,10 +162,7 @@ export function testRecordsRecordLimit() {
|
|
|
162
162
|
signer: Jws.createSigner(alice),
|
|
163
163
|
definition,
|
|
164
164
|
});
|
|
165
|
-
expect(protocolsConfigure.message.descriptor.definition.structure.profile.$recordLimit).toEqual({
|
|
166
|
-
max: 1,
|
|
167
|
-
strategy: 'reject',
|
|
168
|
-
});
|
|
165
|
+
expect(protocolsConfigure.message.descriptor.definition.structure.profile.$recordLimit).toEqual({ max: 1 });
|
|
169
166
|
});
|
|
170
167
|
it('should reject $recordLimit above the supported max', async () => {
|
|
171
168
|
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
@@ -175,7 +172,7 @@ export function testRecordsRecordLimit() {
|
|
|
175
172
|
types: { message: {} },
|
|
176
173
|
structure: {
|
|
177
174
|
message: {
|
|
178
|
-
$recordLimit: { max: DwnConstant.maxRecordLimit + 1
|
|
175
|
+
$recordLimit: { max: DwnConstant.maxRecordLimit + 1 },
|
|
179
176
|
}
|
|
180
177
|
}
|
|
181
178
|
};
|
|
@@ -185,27 +182,6 @@ export function testRecordsRecordLimit() {
|
|
|
185
182
|
});
|
|
186
183
|
await expect(promise).rejects.toThrow(DwnErrorCode.ProtocolsConfigureInvalidRecordLimit);
|
|
187
184
|
});
|
|
188
|
-
it('should allow $recordLimit with purgeOldest strategy (schema validation only)', async () => {
|
|
189
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
190
|
-
const definition = {
|
|
191
|
-
protocol: 'http://example.com/record-limit',
|
|
192
|
-
published: true,
|
|
193
|
-
types: { status: {} },
|
|
194
|
-
structure: {
|
|
195
|
-
status: {
|
|
196
|
-
$recordLimit: { max: 10, strategy: 'purgeOldest' },
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
};
|
|
200
|
-
const protocolsConfigure = await ProtocolsConfigure.create({
|
|
201
|
-
signer: Jws.createSigner(alice),
|
|
202
|
-
definition,
|
|
203
|
-
});
|
|
204
|
-
expect(protocolsConfigure.message.descriptor.definition.structure.status.$recordLimit).toEqual({
|
|
205
|
-
max: 10,
|
|
206
|
-
strategy: 'purgeOldest',
|
|
207
|
-
});
|
|
208
|
-
});
|
|
209
185
|
it('should reject $recordLimit with max less than 1', async () => {
|
|
210
186
|
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
211
187
|
const definition = {
|
|
@@ -214,7 +190,7 @@ export function testRecordsRecordLimit() {
|
|
|
214
190
|
types: { message: {} },
|
|
215
191
|
structure: {
|
|
216
192
|
message: {
|
|
217
|
-
$recordLimit: { max: 0
|
|
193
|
+
$recordLimit: { max: 0 },
|
|
218
194
|
}
|
|
219
195
|
}
|
|
220
196
|
};
|
|
@@ -232,25 +208,7 @@ export function testRecordsRecordLimit() {
|
|
|
232
208
|
types: { message: {} },
|
|
233
209
|
structure: {
|
|
234
210
|
message: {
|
|
235
|
-
$recordLimit: { max: 2.5
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
};
|
|
239
|
-
const promise = ProtocolsConfigure.create({
|
|
240
|
-
signer: Jws.createSigner(alice),
|
|
241
|
-
definition,
|
|
242
|
-
});
|
|
243
|
-
await expect(promise).rejects.toThrow('SchemaValidator');
|
|
244
|
-
});
|
|
245
|
-
it('should reject $recordLimit with missing strategy', async () => {
|
|
246
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
247
|
-
const definition = {
|
|
248
|
-
protocol: 'http://example.com/record-limit',
|
|
249
|
-
published: true,
|
|
250
|
-
types: { message: {} },
|
|
251
|
-
structure: {
|
|
252
|
-
message: {
|
|
253
|
-
$recordLimit: { max: 5 },
|
|
211
|
+
$recordLimit: { max: 2.5 },
|
|
254
212
|
}
|
|
255
213
|
}
|
|
256
214
|
};
|
|
@@ -260,7 +218,7 @@ export function testRecordsRecordLimit() {
|
|
|
260
218
|
});
|
|
261
219
|
await expect(promise).rejects.toThrow('SchemaValidator');
|
|
262
220
|
});
|
|
263
|
-
it('should reject $recordLimit
|
|
221
|
+
it('should reject the removed $recordLimit strategy property', async () => {
|
|
264
222
|
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
265
223
|
const definition = {
|
|
266
224
|
protocol: 'http://example.com/record-limit',
|
|
@@ -268,7 +226,7 @@ export function testRecordsRecordLimit() {
|
|
|
268
226
|
types: { message: {} },
|
|
269
227
|
structure: {
|
|
270
228
|
message: {
|
|
271
|
-
$recordLimit: { max: 5, strategy: '
|
|
229
|
+
$recordLimit: { max: 5, strategy: 'reject' },
|
|
272
230
|
}
|
|
273
231
|
}
|
|
274
232
|
};
|
|
@@ -290,7 +248,7 @@ export function testRecordsRecordLimit() {
|
|
|
290
248
|
structure: {
|
|
291
249
|
room: {
|
|
292
250
|
message: {
|
|
293
|
-
$recordLimit: { max: 100
|
|
251
|
+
$recordLimit: { max: 100 },
|
|
294
252
|
}
|
|
295
253
|
}
|
|
296
254
|
}
|
|
@@ -300,13 +258,10 @@ export function testRecordsRecordLimit() {
|
|
|
300
258
|
definition,
|
|
301
259
|
});
|
|
302
260
|
const roomRuleSet = protocolsConfigure.message.descriptor.definition.structure.room;
|
|
303
|
-
expect(roomRuleSet.message.$recordLimit).toEqual({
|
|
304
|
-
max: 100,
|
|
305
|
-
strategy: 'reject',
|
|
306
|
-
});
|
|
261
|
+
expect(roomRuleSet.message.$recordLimit).toEqual({ max: 100 });
|
|
307
262
|
});
|
|
308
263
|
});
|
|
309
|
-
describe('read-time occupancy projection
|
|
264
|
+
describe('read-time occupancy projection', () => {
|
|
310
265
|
it('should admit candidates and project Query, Read, and Count to top-ranked occupants', async () => {
|
|
311
266
|
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
312
267
|
const protocolDefinition = {
|
|
@@ -315,7 +270,7 @@ export function testRecordsRecordLimit() {
|
|
|
315
270
|
types: { blob: {} },
|
|
316
271
|
structure: {
|
|
317
272
|
blob: {
|
|
318
|
-
$recordLimit: { max: 2
|
|
273
|
+
$recordLimit: { max: 2 },
|
|
319
274
|
}
|
|
320
275
|
}
|
|
321
276
|
};
|
|
@@ -362,7 +317,7 @@ export function testRecordsRecordLimit() {
|
|
|
362
317
|
types: { profile: {} },
|
|
363
318
|
structure: {
|
|
364
319
|
profile: {
|
|
365
|
-
$recordLimit: { max: 1
|
|
320
|
+
$recordLimit: { max: 1 },
|
|
366
321
|
}
|
|
367
322
|
}
|
|
368
323
|
};
|
|
@@ -406,7 +361,7 @@ export function testRecordsRecordLimit() {
|
|
|
406
361
|
structure: {
|
|
407
362
|
room: {
|
|
408
363
|
message: {
|
|
409
|
-
$recordLimit: { max: 2
|
|
364
|
+
$recordLimit: { max: 2 },
|
|
410
365
|
}
|
|
411
366
|
}
|
|
412
367
|
}
|
|
@@ -489,6 +444,30 @@ export function testRecordsRecordLimit() {
|
|
|
489
444
|
room2Messages[0].message.recordId,
|
|
490
445
|
room2Messages[1].message.recordId,
|
|
491
446
|
]);
|
|
447
|
+
expect((await queryProtocolRecordIds({
|
|
448
|
+
author: alice,
|
|
449
|
+
protocol,
|
|
450
|
+
protocolPath: 'room/message',
|
|
451
|
+
contextId: room1Messages[0].message.contextId,
|
|
452
|
+
})).recordIds).toEqual([room1Messages[0].message.recordId]);
|
|
453
|
+
expect(await countProtocolRecords({
|
|
454
|
+
author: alice,
|
|
455
|
+
protocol,
|
|
456
|
+
protocolPath: 'room/message',
|
|
457
|
+
contextId: room1Messages[0].message.contextId,
|
|
458
|
+
})).toBe(1);
|
|
459
|
+
expect((await queryProtocolRecordIds({
|
|
460
|
+
author: alice,
|
|
461
|
+
protocol,
|
|
462
|
+
protocolPath: 'room/message',
|
|
463
|
+
contextId: room1Messages[2].message.contextId,
|
|
464
|
+
})).recordIds).toEqual([]);
|
|
465
|
+
expect(await countProtocolRecords({
|
|
466
|
+
author: alice,
|
|
467
|
+
protocol,
|
|
468
|
+
protocolPath: 'room/message',
|
|
469
|
+
contextId: room1Messages[2].message.contextId,
|
|
470
|
+
})).toBe(0);
|
|
492
471
|
const broadQuery = await TestDataGenerator.generateRecordsQuery({
|
|
493
472
|
author: alice,
|
|
494
473
|
filter: {
|
|
@@ -500,6 +479,220 @@ export function testRecordsRecordLimit() {
|
|
|
500
479
|
expect(broadReply.status.code).toBe(400);
|
|
501
480
|
expect(broadReply.status.detail).toContain(DwnErrorCode.RecordsQueryNestedProtocolPathContextIdInvalid);
|
|
502
481
|
});
|
|
482
|
+
it('should project ancestor queries independently across parent contexts', async () => {
|
|
483
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
484
|
+
const protocolDefinition = {
|
|
485
|
+
protocol: `http://record-limit-${TestDataGenerator.randomString(12)}.xyz`,
|
|
486
|
+
published: true,
|
|
487
|
+
types: {
|
|
488
|
+
channel: {},
|
|
489
|
+
community: {},
|
|
490
|
+
message: {},
|
|
491
|
+
},
|
|
492
|
+
structure: {
|
|
493
|
+
community: {
|
|
494
|
+
channel: {
|
|
495
|
+
message: {
|
|
496
|
+
$recordLimit: { max: 2 },
|
|
497
|
+
},
|
|
498
|
+
},
|
|
499
|
+
},
|
|
500
|
+
},
|
|
501
|
+
};
|
|
502
|
+
const protocol = protocolDefinition.protocol;
|
|
503
|
+
await installProtocol({ author: alice, definition: protocolDefinition });
|
|
504
|
+
const community = await writeProtocolRecord({
|
|
505
|
+
author: alice,
|
|
506
|
+
protocol,
|
|
507
|
+
protocolPath: 'community',
|
|
508
|
+
dateCreated: '2025-01-01T00:00:00.000000Z',
|
|
509
|
+
});
|
|
510
|
+
const channel1 = await writeProtocolRecord({
|
|
511
|
+
author: alice,
|
|
512
|
+
protocol,
|
|
513
|
+
protocolPath: 'community/channel',
|
|
514
|
+
parentContextId: community.message.contextId,
|
|
515
|
+
dateCreated: '2025-01-02T00:00:00.000000Z',
|
|
516
|
+
});
|
|
517
|
+
const channel2 = await writeProtocolRecord({
|
|
518
|
+
author: alice,
|
|
519
|
+
protocol,
|
|
520
|
+
protocolPath: 'community/channel',
|
|
521
|
+
parentContextId: community.message.contextId,
|
|
522
|
+
dateCreated: '2025-01-03T00:00:00.000000Z',
|
|
523
|
+
});
|
|
524
|
+
const { cursor: cursorBeforeMessages } = await eventLog.read(alice.did);
|
|
525
|
+
// Creation times are interleaved across channels so pagination proves
|
|
526
|
+
// that projection happens per direct parent before global ordering.
|
|
527
|
+
// Channel 2 arrives newest-first to prove occupancy ignores arrival order.
|
|
528
|
+
const channel1Messages = [
|
|
529
|
+
await writeProtocolRecord({
|
|
530
|
+
author: alice,
|
|
531
|
+
protocol,
|
|
532
|
+
protocolPath: 'community/channel/message',
|
|
533
|
+
parentContextId: channel1.message.contextId,
|
|
534
|
+
dateCreated: '2025-02-01T00:00:00.000000Z',
|
|
535
|
+
}),
|
|
536
|
+
await writeProtocolRecord({
|
|
537
|
+
author: alice,
|
|
538
|
+
protocol,
|
|
539
|
+
protocolPath: 'community/channel/message',
|
|
540
|
+
parentContextId: channel1.message.contextId,
|
|
541
|
+
published: true,
|
|
542
|
+
dateCreated: '2025-02-04T00:00:00.000000Z',
|
|
543
|
+
}),
|
|
544
|
+
await writeProtocolRecord({
|
|
545
|
+
author: alice,
|
|
546
|
+
protocol,
|
|
547
|
+
protocolPath: 'community/channel/message',
|
|
548
|
+
parentContextId: channel1.message.contextId,
|
|
549
|
+
published: true,
|
|
550
|
+
dateCreated: '2025-02-06T00:00:00.000000Z',
|
|
551
|
+
}),
|
|
552
|
+
];
|
|
553
|
+
const channel2Tie1 = await writeProtocolRecord({
|
|
554
|
+
author: alice,
|
|
555
|
+
protocol,
|
|
556
|
+
protocolPath: 'community/channel/message',
|
|
557
|
+
parentContextId: channel2.message.contextId,
|
|
558
|
+
published: true,
|
|
559
|
+
dateCreated: '2025-02-03T00:00:00.000000Z',
|
|
560
|
+
});
|
|
561
|
+
const channel2Tie2 = await writeProtocolRecord({
|
|
562
|
+
author: alice,
|
|
563
|
+
protocol,
|
|
564
|
+
protocolPath: 'community/channel/message',
|
|
565
|
+
parentContextId: channel2.message.contextId,
|
|
566
|
+
published: true,
|
|
567
|
+
dateCreated: '2025-02-03T00:00:00.000000Z',
|
|
568
|
+
});
|
|
569
|
+
const channel2Oldest = await writeProtocolRecord({
|
|
570
|
+
author: alice,
|
|
571
|
+
protocol,
|
|
572
|
+
protocolPath: 'community/channel/message',
|
|
573
|
+
parentContextId: channel2.message.contextId,
|
|
574
|
+
published: true,
|
|
575
|
+
dateCreated: '2025-02-02T00:00:00.000000Z',
|
|
576
|
+
});
|
|
577
|
+
const channel2Messages = [channel2Oldest, channel2Tie1, channel2Tie2];
|
|
578
|
+
const channel2BoundaryOccupant = channel2Messages[1].message.recordId < channel2Messages[2].message.recordId
|
|
579
|
+
? channel2Messages[1]
|
|
580
|
+
: channel2Messages[2];
|
|
581
|
+
const expectedRecordIds = [
|
|
582
|
+
channel1Messages[0].message.recordId,
|
|
583
|
+
channel2Messages[0].message.recordId,
|
|
584
|
+
channel2BoundaryOccupant.message.recordId,
|
|
585
|
+
channel1Messages[1].message.recordId,
|
|
586
|
+
];
|
|
587
|
+
const queryInput = {
|
|
588
|
+
author: alice,
|
|
589
|
+
protocol,
|
|
590
|
+
protocolPath: 'community/channel/message',
|
|
591
|
+
contextId: community.message.contextId,
|
|
592
|
+
};
|
|
593
|
+
expect((await queryProtocolRecordIds(queryInput)).recordIds).toEqual(expectedRecordIds);
|
|
594
|
+
expect((await queryProtocolRecordIds({
|
|
595
|
+
...queryInput,
|
|
596
|
+
dateSort: DateSort.CreatedDescending,
|
|
597
|
+
})).recordIds).toEqual([...expectedRecordIds].reverse());
|
|
598
|
+
expect(await countProtocolRecords(queryInput)).toBe(expectedRecordIds.length);
|
|
599
|
+
expect(await subscribeSnapshotRecordIds(queryInput)).toEqual(expectedRecordIds);
|
|
600
|
+
const page1 = await queryProtocolRecordIds({ ...queryInput, pagination: { limit: 2 } });
|
|
601
|
+
expect(page1.recordIds).toEqual(expectedRecordIds.slice(0, 2));
|
|
602
|
+
expect(page1.cursor).toBeDefined();
|
|
603
|
+
const page2 = await queryProtocolRecordIds({
|
|
604
|
+
...queryInput,
|
|
605
|
+
pagination: { limit: 2, cursor: page1.cursor },
|
|
606
|
+
});
|
|
607
|
+
expect(page2.recordIds).toEqual(expectedRecordIds.slice(2));
|
|
608
|
+
expect(page2.cursor).toBeUndefined();
|
|
609
|
+
const descendingPage1 = await queryProtocolRecordIds({
|
|
610
|
+
...queryInput,
|
|
611
|
+
dateSort: DateSort.CreatedDescending,
|
|
612
|
+
pagination: { limit: 2 },
|
|
613
|
+
});
|
|
614
|
+
expect(descendingPage1.recordIds).toEqual([...expectedRecordIds].reverse().slice(0, 2));
|
|
615
|
+
expect(descendingPage1.cursor).toBeDefined();
|
|
616
|
+
const descendingPage2 = await queryProtocolRecordIds({
|
|
617
|
+
...queryInput,
|
|
618
|
+
dateSort: DateSort.CreatedDescending,
|
|
619
|
+
pagination: { limit: 2, cursor: descendingPage1.cursor },
|
|
620
|
+
});
|
|
621
|
+
expect(descendingPage2.recordIds).toEqual([...expectedRecordIds].reverse().slice(2));
|
|
622
|
+
expect(descendingPage2.cursor).toBeUndefined();
|
|
623
|
+
// Occupancy is ranked before requester visibility. The unpublished
|
|
624
|
+
// first channel-1 occupant still consumes a slot, so the later
|
|
625
|
+
// published over-limit candidate must remain hidden anonymously.
|
|
626
|
+
const publishedFilter = {
|
|
627
|
+
contextId: community.message.contextId,
|
|
628
|
+
protocol,
|
|
629
|
+
protocolPath: 'community/channel/message',
|
|
630
|
+
published: true,
|
|
631
|
+
};
|
|
632
|
+
const anonymousQuery = await TestDataGenerator.generateRecordsQuery({
|
|
633
|
+
anonymous: true,
|
|
634
|
+
filter: publishedFilter,
|
|
635
|
+
});
|
|
636
|
+
const anonymousReply = await dwn.processMessage(alice.did, anonymousQuery.message);
|
|
637
|
+
expect(anonymousReply.status.code).toBe(200);
|
|
638
|
+
expect(anonymousReply.entries?.map((entry) => entry.recordId)).toEqual([
|
|
639
|
+
channel2Messages[0].message.recordId,
|
|
640
|
+
channel2BoundaryOccupant.message.recordId,
|
|
641
|
+
channel1Messages[1].message.recordId,
|
|
642
|
+
]);
|
|
643
|
+
const anonymousCount = await TestDataGenerator.generateRecordsCount({
|
|
644
|
+
anonymous: true,
|
|
645
|
+
filter: publishedFilter,
|
|
646
|
+
});
|
|
647
|
+
const anonymousCountReply = await dwn.processMessage(alice.did, anonymousCount.message);
|
|
648
|
+
expect(anonymousCountReply.status.code).toBe(200);
|
|
649
|
+
expect(anonymousCountReply.count).toBe(3);
|
|
650
|
+
const replayedRecordIds = [];
|
|
651
|
+
const cursorSubscribe = await TestDataGenerator.generateRecordsSubscribe({
|
|
652
|
+
author: alice,
|
|
653
|
+
cursor: cursorBeforeMessages,
|
|
654
|
+
filter: {
|
|
655
|
+
contextId: community.message.contextId,
|
|
656
|
+
protocol,
|
|
657
|
+
protocolPath: 'community/channel/message',
|
|
658
|
+
},
|
|
659
|
+
});
|
|
660
|
+
const cursorSubscribeReply = await dwn.processMessage(alice.did, cursorSubscribe.message, {
|
|
661
|
+
subscriptionHandler: (subscriptionMessage) => {
|
|
662
|
+
if (subscriptionMessage.type === 'event' && subscriptionMessage.event.message.descriptor.method === 'Write') {
|
|
663
|
+
replayedRecordIds.push(subscriptionMessage.event.message.recordId);
|
|
664
|
+
}
|
|
665
|
+
},
|
|
666
|
+
});
|
|
667
|
+
expect(cursorSubscribeReply.status.code).toBe(200);
|
|
668
|
+
await Poller.pollUntilSuccessOrTimeout(async () => {
|
|
669
|
+
expect(replayedRecordIds).toHaveLength(expectedRecordIds.length);
|
|
670
|
+
});
|
|
671
|
+
expect([...replayedRecordIds].sort()).toEqual([...expectedRecordIds].sort());
|
|
672
|
+
await cursorSubscribeReply.subscription?.close();
|
|
673
|
+
const deleteChannel1Occupant = await TestDataGenerator.generateRecordsDelete({
|
|
674
|
+
author: alice,
|
|
675
|
+
recordId: channel1Messages[0].message.recordId,
|
|
676
|
+
});
|
|
677
|
+
const deleteReply = await dwn.processMessage(alice.did, deleteChannel1Occupant.message);
|
|
678
|
+
expect(deleteReply.status.code).toBe(202);
|
|
679
|
+
const expectedAfterPromotion = [
|
|
680
|
+
channel2Messages[0].message.recordId,
|
|
681
|
+
channel2BoundaryOccupant.message.recordId,
|
|
682
|
+
channel1Messages[1].message.recordId,
|
|
683
|
+
channel1Messages[2].message.recordId,
|
|
684
|
+
];
|
|
685
|
+
expect((await queryProtocolRecordIds(queryInput)).recordIds).toEqual(expectedAfterPromotion);
|
|
686
|
+
expect((await queryProtocolRecordIds({
|
|
687
|
+
author: alice,
|
|
688
|
+
protocol,
|
|
689
|
+
protocolPath: 'community/channel/message',
|
|
690
|
+
contextId: channel2.message.contextId,
|
|
691
|
+
})).recordIds).toEqual([
|
|
692
|
+
channel2Messages[0].message.recordId,
|
|
693
|
+
channel2BoundaryOccupant.message.recordId,
|
|
694
|
+
]);
|
|
695
|
+
});
|
|
503
696
|
it('should paginate over the projected occupant set for max:N scopes', async () => {
|
|
504
697
|
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
505
698
|
const protocolDefinition = {
|
|
@@ -508,7 +701,7 @@ export function testRecordsRecordLimit() {
|
|
|
508
701
|
types: { item: {} },
|
|
509
702
|
structure: {
|
|
510
703
|
item: {
|
|
511
|
-
$recordLimit: { max: 3
|
|
704
|
+
$recordLimit: { max: 3 },
|
|
512
705
|
}
|
|
513
706
|
}
|
|
514
707
|
};
|
|
@@ -551,7 +744,7 @@ export function testRecordsRecordLimit() {
|
|
|
551
744
|
types: { status: {} },
|
|
552
745
|
structure: {
|
|
553
746
|
status: {
|
|
554
|
-
$recordLimit: { max: 1
|
|
747
|
+
$recordLimit: { max: 1 },
|
|
555
748
|
}
|
|
556
749
|
}
|
|
557
750
|
};
|
|
@@ -633,10 +826,10 @@ export function testRecordsRecordLimit() {
|
|
|
633
826
|
},
|
|
634
827
|
structure: {
|
|
635
828
|
note: {
|
|
636
|
-
$recordLimit: { max: 2
|
|
829
|
+
$recordLimit: { max: 2 },
|
|
637
830
|
},
|
|
638
831
|
profile: {
|
|
639
|
-
$recordLimit: { max: 1
|
|
832
|
+
$recordLimit: { max: 1 },
|
|
640
833
|
}
|
|
641
834
|
}
|
|
642
835
|
};
|
|
@@ -712,7 +905,7 @@ export function testRecordsRecordLimit() {
|
|
|
712
905
|
types: { blob: {} },
|
|
713
906
|
structure: {
|
|
714
907
|
blob: {
|
|
715
|
-
$recordLimit: { max: 1
|
|
908
|
+
$recordLimit: { max: 1 },
|
|
716
909
|
}
|
|
717
910
|
}
|
|
718
911
|
};
|
|
@@ -734,30 +927,6 @@ export function testRecordsRecordLimit() {
|
|
|
734
927
|
record.message.recordId,
|
|
735
928
|
]);
|
|
736
929
|
});
|
|
737
|
-
it('should reject purgeOldest strategy at write time with not-implemented error', async () => {
|
|
738
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
739
|
-
const protocolDefinition = {
|
|
740
|
-
protocol: `http://record-limit-${TestDataGenerator.randomString(12)}.xyz`,
|
|
741
|
-
published: true,
|
|
742
|
-
types: { blob: {} },
|
|
743
|
-
structure: {
|
|
744
|
-
blob: {
|
|
745
|
-
$recordLimit: { max: 1, strategy: 'purgeOldest' },
|
|
746
|
-
}
|
|
747
|
-
}
|
|
748
|
-
};
|
|
749
|
-
const protocol = protocolDefinition.protocol;
|
|
750
|
-
await installProtocol({ author: alice, definition: protocolDefinition });
|
|
751
|
-
const record = await TestDataGenerator.generateRecordsWrite({
|
|
752
|
-
author: alice,
|
|
753
|
-
recipient: alice.did,
|
|
754
|
-
protocol,
|
|
755
|
-
protocolPath: 'blob',
|
|
756
|
-
});
|
|
757
|
-
const reply = await dwn.processMessage(alice.did, record.message, { dataStream: record.dataStream });
|
|
758
|
-
expect(reply.status.code).toBe(400);
|
|
759
|
-
expect(reply.status.detail).toContain(DwnErrorCode.ProtocolAuthorizationRecordLimitStrategyNotImplemented);
|
|
760
|
-
});
|
|
761
930
|
});
|
|
762
931
|
});
|
|
763
932
|
}
|