@enbox/dwn-sdk-js 0.4.16 → 0.4.18
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 +978 -840
- 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/records-collection.js +200 -0
- package/dist/esm/src/handlers/records-collection.js.map +1 -0
- package/dist/esm/src/handlers/records-count.js +26 -195
- package/dist/esm/src/handlers/records-count.js.map +1 -1
- package/dist/esm/src/handlers/records-query.js +19 -373
- package/dist/esm/src/handlers/records-query.js.map +1 -1
- package/dist/esm/src/handlers/records-subscribe.js +21 -338
- 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/interfaces/records-count.js +1 -1
- package/dist/esm/src/interfaces/records-count.js.map +1 -1
- package/dist/esm/src/interfaces/records-query.js +1 -1
- package/dist/esm/src/interfaces/records-query.js.map +1 -1
- package/dist/esm/src/interfaces/records-subscribe.js +3 -1
- package/dist/esm/src/interfaces/records-subscribe.js.map +1 -1
- package/dist/esm/src/store/index-level.js +210 -21
- package/dist/esm/src/store/index-level.js.map +1 -1
- package/dist/esm/src/store/message-store-level.js +192 -21
- 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 +4 -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 +73 -202
- package/dist/esm/src/utils/record-limit-occupancy.js.map +1 -1
- package/dist/esm/src/utils/records.js +19 -52
- package/dist/esm/src/utils/records.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 +149 -70
- package/dist/esm/tests/features/records-nested-query-scope.spec.js.map +1 -1
- package/dist/esm/tests/features/records-record-limit.spec.js +380 -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/records-count.spec.js +52 -1
- package/dist/esm/tests/handlers/records-count.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-subscribe.spec.js +76 -1
- package/dist/esm/tests/handlers/records-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 +208 -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 +76 -1
- 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/records-collection.d.ts +32 -0
- package/dist/types/src/handlers/records-collection.d.ts.map +1 -0
- package/dist/types/src/handlers/records-count.d.ts +0 -34
- package/dist/types/src/handlers/records-count.d.ts.map +1 -1
- package/dist/types/src/handlers/records-query.d.ts +0 -61
- package/dist/types/src/handlers/records-query.d.ts.map +1 -1
- package/dist/types/src/handlers/records-subscribe.d.ts +1 -37
- 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/interfaces/records-subscribe.d.ts.map +1 -1
- package/dist/types/src/store/index-level.d.ts +61 -0
- package/dist/types/src/store/index-level.d.ts.map +1 -1
- package/dist/types/src/store/message-store-level.d.ts +24 -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 +28 -2
- package/dist/types/src/types/message-store.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 +3 -2
- 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.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 +6 -24
- package/dist/types/src/utils/records.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/records-count.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-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/records-collection.ts +269 -0
- package/src/handlers/records-count.ts +38 -232
- package/src/handlers/records-query.ts +22 -444
- package/src/handlers/records-subscribe.ts +26 -397
- package/src/handlers/records-write.ts +17 -32
- package/src/index.ts +3 -3
- package/src/interfaces/protocols-configure.ts +4 -13
- package/src/interfaces/records-count.ts +1 -1
- package/src/interfaces/records-query.ts +1 -1
- package/src/interfaces/records-subscribe.ts +5 -2
- package/src/store/index-level.ts +323 -28
- package/src/store/message-store-level.ts +251 -25
- package/src/store/storage-controller.ts +1 -3
- package/src/types/message-store.ts +30 -2
- package/src/types/protocols-types.ts +7 -26
- package/src/types/query-types.ts +10 -2
- package/src/types/records-types.ts +3 -2
- package/src/utils/filter.ts +40 -3
- package/src/utils/grant-key-coverage.ts +4 -7
- package/src/utils/messages.ts +4 -14
- package/src/utils/permission-scope.ts +4 -11
- package/src/utils/record-limit-occupancy.ts +108 -293
- package/src/utils/records.ts +22 -57
|
@@ -1,23 +1,22 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { RecordsCollectionVisibility } from './records-collection.js';
|
|
2
2
|
import type { EventSubscription, ProgressGapInfo, ProgressToken, SubscriptionEvent, SubscriptionListener, SubscriptionMessage } from '../types/subscriptions.js';
|
|
3
3
|
import type { Filter, PaginationCursor } from '../types/query-types.js';
|
|
4
4
|
import type { HandlerDependencies, MethodHandler } from '../types/method-handler.js';
|
|
5
5
|
import type { RecordsQueryReplyEntry, RecordsSubscribeMessage, RecordsSubscribeReply } from '../types/records-types.js';
|
|
6
6
|
|
|
7
7
|
import { attachInitialWrites } from '../utils/initial-write-attachment.js';
|
|
8
|
-
import { authenticate } from '../core/auth.js';
|
|
9
|
-
import { DateSort } from '../types/records-types.js';
|
|
10
8
|
import { EncryptionControl } from '../core/encryption-control.js';
|
|
9
|
+
import { isRecordLimitOccupant } from '../utils/record-limit-occupancy.js';
|
|
11
10
|
import { Message } from '../core/message.js';
|
|
12
11
|
import { messageReplyFromError } from '../core/message-reply.js';
|
|
13
|
-
import { ProtocolAuthorization } from '../core/protocol-authorization.js';
|
|
14
12
|
import { Records } from '../utils/records.js';
|
|
15
|
-
import { RecordsGrantAuthorization } from '../core/records-grant-authorization.js';
|
|
16
13
|
import { RecordsSubscribe } from '../interfaces/records-subscribe.js';
|
|
17
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
buildRecordsEventFilters,
|
|
16
|
+
queryVisibleRecordsPage,
|
|
17
|
+
resolveRecordsCollectionVisibility,
|
|
18
|
+
} from './records-collection.js';
|
|
18
19
|
import { DwnError, DwnErrorCode } from '../core/dwn-error.js';
|
|
19
|
-
import { DwnInterfaceName, DwnMethodName } from '../enums/dwn-interface-method.js';
|
|
20
|
-
import { isRecordLimitOccupant, queryRecordsWithRecordLimitOccupancy } from '../utils/record-limit-occupancy.js';
|
|
21
20
|
|
|
22
21
|
type ProjectedRecordsSubscriptionHandler = {
|
|
23
22
|
listener: SubscriptionListener;
|
|
@@ -47,16 +46,15 @@ export class RecordsSubscribeHandler implements MethodHandler {
|
|
|
47
46
|
let recordsSubscribe: RecordsSubscribe;
|
|
48
47
|
try {
|
|
49
48
|
recordsSubscribe = await RecordsSubscribe.parse(message);
|
|
50
|
-
} catch (
|
|
51
|
-
return messageReplyFromError(
|
|
49
|
+
} catch (error) {
|
|
50
|
+
return messageReplyFromError(error, 400);
|
|
52
51
|
}
|
|
53
52
|
|
|
54
|
-
const requester = Message.getRequester(recordsSubscribe.message);
|
|
55
53
|
const filterResolution = await this.resolveSubscriptionFilters(tenant, message, recordsSubscribe);
|
|
56
54
|
if ('errorReply' in filterResolution) {
|
|
57
55
|
return filterResolution.errorReply;
|
|
58
56
|
}
|
|
59
|
-
const { eventFilters,
|
|
57
|
+
const { eventFilters, visibility } = filterResolution;
|
|
60
58
|
|
|
61
59
|
const messageCid = await Message.getCid(message);
|
|
62
60
|
const { cursor: eventLogCursor } = recordsSubscribe.message.descriptor;
|
|
@@ -78,12 +76,12 @@ export class RecordsSubscribeHandler implements MethodHandler {
|
|
|
78
76
|
|
|
79
77
|
// ---- No cursor: existing behavior (initial snapshot from MessageStore) ----
|
|
80
78
|
return this.handleSnapshotSubscription(
|
|
81
|
-
tenant, messageCid, recordsSubscribe,
|
|
79
|
+
tenant, messageCid, recordsSubscribe, eventFilters, visibility, projectedSubscriptionHandler
|
|
82
80
|
);
|
|
83
81
|
}
|
|
84
82
|
|
|
85
83
|
/**
|
|
86
|
-
* Resolves the event
|
|
84
|
+
* Resolves the event filters and visibility for the subscription, performing authentication and
|
|
87
85
|
* authorization when the request is not an anonymous published-records-only subscribe.
|
|
88
86
|
* Returns the resolved filters, or an `errorReply` if authentication/authorization failed.
|
|
89
87
|
*/
|
|
@@ -91,35 +89,21 @@ export class RecordsSubscribeHandler implements MethodHandler {
|
|
|
91
89
|
tenant: string,
|
|
92
90
|
message: RecordsSubscribeMessage,
|
|
93
91
|
recordsSubscribe: RecordsSubscribe,
|
|
94
|
-
): Promise<{ eventFilters: Filter[];
|
|
95
|
-
|
|
96
|
-
if (Records.filterIncludesPublishedRecords(recordsSubscribe.message.descriptor.filter) && recordsSubscribe.author === undefined) {
|
|
97
|
-
const eventFilters = [RecordsSubscribeHandler.buildPublishedEventFilter(recordsSubscribe)];
|
|
98
|
-
const queryFilters = [RecordsSubscribeHandler.buildPublishedQueryFilter(recordsSubscribe)];
|
|
99
|
-
// delete the undefined authorization property else the code will encounter the following IPLD issue when attempting to generate CID:
|
|
100
|
-
// Error: `undefined` is not supported by the IPLD Data Model and cannot be encoded
|
|
101
|
-
delete message.authorization;
|
|
102
|
-
return { eventFilters, queryFilters };
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// authentication and authorization
|
|
92
|
+
): Promise<{ eventFilters: Filter[]; visibility: RecordsCollectionVisibility } | { errorReply: RecordsSubscribeReply }> {
|
|
93
|
+
let visibility: RecordsCollectionVisibility;
|
|
106
94
|
try {
|
|
107
|
-
await
|
|
108
|
-
await RecordsSubscribeHandler.authorizeRecordsSubscribe(tenant, recordsSubscribe, this.deps);
|
|
95
|
+
visibility = await resolveRecordsCollectionVisibility(tenant, recordsSubscribe, this.deps);
|
|
109
96
|
} catch (error) {
|
|
110
97
|
return { errorReply: messageReplyFromError(error, 401) };
|
|
111
98
|
}
|
|
112
99
|
|
|
113
|
-
if (
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
queryFilters : RecordsSubscribeHandler.buildOwnerQueryFilters(recordsSubscribe),
|
|
117
|
-
};
|
|
100
|
+
if (visibility === 'published') {
|
|
101
|
+
// Remove the undefined property before computing the subscription CID because IPLD cannot encode it.
|
|
102
|
+
delete message.authorization;
|
|
118
103
|
}
|
|
119
|
-
|
|
120
104
|
return {
|
|
121
|
-
eventFilters
|
|
122
|
-
|
|
105
|
+
eventFilters: buildRecordsEventFilters(recordsSubscribe, visibility),
|
|
106
|
+
visibility,
|
|
123
107
|
};
|
|
124
108
|
}
|
|
125
109
|
|
|
@@ -167,9 +151,8 @@ export class RecordsSubscribeHandler implements MethodHandler {
|
|
|
167
151
|
tenant: string,
|
|
168
152
|
messageCid: string,
|
|
169
153
|
recordsSubscribe: RecordsSubscribe,
|
|
170
|
-
requester: string | undefined,
|
|
171
154
|
eventFilters: Filter[],
|
|
172
|
-
|
|
155
|
+
visibility: RecordsCollectionVisibility,
|
|
173
156
|
projectedSubscriptionHandler: ProjectedRecordsSubscriptionHandler,
|
|
174
157
|
): Promise<RecordsSubscribeReply> {
|
|
175
158
|
// Step 1: Register event listener FIRST to ensure no events are missed between query and subscribe
|
|
@@ -182,16 +165,12 @@ export class RecordsSubscribeHandler implements MethodHandler {
|
|
|
182
165
|
let entries: RecordsQueryReplyEntry[];
|
|
183
166
|
let paginationCursor: PaginationCursor | undefined;
|
|
184
167
|
try {
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
const queryResult = await this.queryRecordsWithVisibleControlFiltering(
|
|
168
|
+
const queryResult = await queryVisibleRecordsPage({
|
|
169
|
+
deps : this.deps,
|
|
188
170
|
tenant,
|
|
189
|
-
recordsSubscribe,
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
messageSort,
|
|
193
|
-
pagination,
|
|
194
|
-
);
|
|
171
|
+
request : recordsSubscribe,
|
|
172
|
+
visibility,
|
|
173
|
+
});
|
|
195
174
|
|
|
196
175
|
// attach the retained initial write to every entry that is not itself an initial write
|
|
197
176
|
entries = await attachInitialWrites({
|
|
@@ -216,29 +195,6 @@ export class RecordsSubscribeHandler implements MethodHandler {
|
|
|
216
195
|
};
|
|
217
196
|
}
|
|
218
197
|
|
|
219
|
-
/**
|
|
220
|
-
* Convert an incoming DateSort to a sort type accepted by MessageStore.
|
|
221
|
-
* Defaults to `dateCreated` ascending if no sort is supplied.
|
|
222
|
-
*/
|
|
223
|
-
private static convertDateSort(dateSort?: DateSort): MessageSort {
|
|
224
|
-
switch (dateSort) {
|
|
225
|
-
case DateSort.CreatedAscending:
|
|
226
|
-
return { dateCreated: SortDirection.Ascending };
|
|
227
|
-
case DateSort.CreatedDescending:
|
|
228
|
-
return { dateCreated: SortDirection.Descending };
|
|
229
|
-
case DateSort.PublishedAscending:
|
|
230
|
-
return { datePublished: SortDirection.Ascending };
|
|
231
|
-
case DateSort.PublishedDescending:
|
|
232
|
-
return { datePublished: SortDirection.Descending };
|
|
233
|
-
case DateSort.UpdatedAscending:
|
|
234
|
-
return { messageTimestamp: SortDirection.Ascending };
|
|
235
|
-
case DateSort.UpdatedDescending:
|
|
236
|
-
return { messageTimestamp: SortDirection.Descending };
|
|
237
|
-
default:
|
|
238
|
-
return { dateCreated: SortDirection.Ascending };
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
198
|
private static createRecordLimitOccupancyGuard(input: {
|
|
243
199
|
deps: HandlerDependencies;
|
|
244
200
|
eventFilters: Filter[];
|
|
@@ -357,331 +313,4 @@ export class RecordsSubscribeHandler implements MethodHandler {
|
|
|
357
313
|
};
|
|
358
314
|
}
|
|
359
315
|
|
|
360
|
-
// =============================================
|
|
361
|
-
// Event filters (for live subscription)
|
|
362
|
-
// These match Write+Delete and do NOT use isLatestBaseState
|
|
363
|
-
// =============================================
|
|
364
|
-
|
|
365
|
-
/**
|
|
366
|
-
* Build event filters for owner: all matching Write+Delete events.
|
|
367
|
-
*/
|
|
368
|
-
private static buildOwnerEventFilters(recordsSubscribe: RecordsSubscribe): Filter[] {
|
|
369
|
-
const { filter } = recordsSubscribe.message.descriptor;
|
|
370
|
-
return [{
|
|
371
|
-
...Records.convertFilter(filter),
|
|
372
|
-
interface : DwnInterfaceName.Records,
|
|
373
|
-
method : [DwnMethodName.Write, DwnMethodName.Delete],
|
|
374
|
-
}];
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
/**
|
|
378
|
-
* Build event filters for non-owner with visibility rules.
|
|
379
|
-
*/
|
|
380
|
-
private static buildNonOwnerEventFilters(recordsSubscribe: RecordsSubscribe): Filter[] {
|
|
381
|
-
const filters: Filter[] = [];
|
|
382
|
-
const { filter } = recordsSubscribe.message.descriptor;
|
|
383
|
-
if (Records.filterIncludesPublishedRecords(filter)) {
|
|
384
|
-
filters.push(RecordsSubscribeHandler.buildPublishedEventFilter(recordsSubscribe));
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
if (Records.filterIncludesUnpublishedRecords(filter)) {
|
|
388
|
-
if (EncryptionControl.isExactAudienceFilter(filter)) {
|
|
389
|
-
filters.push({
|
|
390
|
-
...Records.convertFilter(filter),
|
|
391
|
-
interface : DwnInterfaceName.Records,
|
|
392
|
-
method : [DwnMethodName.Write, DwnMethodName.Delete],
|
|
393
|
-
published : false,
|
|
394
|
-
});
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
if (Records.shouldBuildUnpublishedAuthorFilter(filter, recordsSubscribe.author!)) {
|
|
398
|
-
filters.push({
|
|
399
|
-
...Records.convertFilter(filter),
|
|
400
|
-
author : recordsSubscribe.author!,
|
|
401
|
-
interface : DwnInterfaceName.Records,
|
|
402
|
-
method : [DwnMethodName.Write, DwnMethodName.Delete],
|
|
403
|
-
published : false,
|
|
404
|
-
});
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
if (Records.shouldProtocolAuthorize(recordsSubscribe.signaturePayload!)) {
|
|
408
|
-
filters.push({
|
|
409
|
-
...Records.convertFilter(filter),
|
|
410
|
-
interface : DwnInterfaceName.Records,
|
|
411
|
-
method : [DwnMethodName.Write, DwnMethodName.Delete],
|
|
412
|
-
published : false,
|
|
413
|
-
});
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
if (Message.getPermissionGrantId(recordsSubscribe.signaturePayload!) !== undefined) {
|
|
417
|
-
filters.push({
|
|
418
|
-
...Records.convertFilter(filter),
|
|
419
|
-
interface : DwnInterfaceName.Records,
|
|
420
|
-
method : [DwnMethodName.Write, DwnMethodName.Delete],
|
|
421
|
-
published : false,
|
|
422
|
-
});
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
if (Records.shouldBuildUnpublishedRecipientFilter(filter, recordsSubscribe.author!)) {
|
|
426
|
-
filters.push({
|
|
427
|
-
...Records.convertFilter(filter),
|
|
428
|
-
interface : DwnInterfaceName.Records,
|
|
429
|
-
method : [DwnMethodName.Write, DwnMethodName.Delete],
|
|
430
|
-
recipient : recordsSubscribe.author!,
|
|
431
|
-
published : false,
|
|
432
|
-
});
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
return filters;
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
/**
|
|
439
|
-
* Build a published-only event filter (Write+Delete).
|
|
440
|
-
*/
|
|
441
|
-
private static buildPublishedEventFilter(recordsSubscribe: RecordsSubscribe): Filter {
|
|
442
|
-
return {
|
|
443
|
-
...Records.convertFilter(recordsSubscribe.message.descriptor.filter),
|
|
444
|
-
interface : DwnInterfaceName.Records,
|
|
445
|
-
method : [DwnMethodName.Write, DwnMethodName.Delete],
|
|
446
|
-
published : true,
|
|
447
|
-
};
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
// =============================================
|
|
451
|
-
// Query filters (for initial snapshot)
|
|
452
|
-
// These match Write only and use isLatestBaseState: true
|
|
453
|
-
// =============================================
|
|
454
|
-
|
|
455
|
-
/**
|
|
456
|
-
* Build query filters for owner: latest writes matching the filter.
|
|
457
|
-
*/
|
|
458
|
-
private static buildOwnerQueryFilters(recordsSubscribe: RecordsSubscribe): Filter[] {
|
|
459
|
-
const { dateSort, filter } = recordsSubscribe.message.descriptor;
|
|
460
|
-
return [{
|
|
461
|
-
...Records.convertFilter(filter, dateSort),
|
|
462
|
-
interface : DwnInterfaceName.Records,
|
|
463
|
-
method : DwnMethodName.Write,
|
|
464
|
-
isLatestBaseState : true,
|
|
465
|
-
}];
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
/**
|
|
469
|
-
* Build query filters for non-owner with visibility rules.
|
|
470
|
-
*/
|
|
471
|
-
private static buildNonOwnerQueryFilters(recordsSubscribe: RecordsSubscribe): Filter[] {
|
|
472
|
-
const filters: Filter[] = [];
|
|
473
|
-
const { dateSort, filter } = recordsSubscribe.message.descriptor;
|
|
474
|
-
if (Records.filterIncludesPublishedRecords(filter)) {
|
|
475
|
-
filters.push(RecordsSubscribeHandler.buildPublishedQueryFilter(recordsSubscribe));
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
if (Records.filterIncludesUnpublishedRecords(filter)) {
|
|
479
|
-
if (EncryptionControl.isExactAudienceFilter(filter)) {
|
|
480
|
-
filters.push(Records.buildUnpublishedControlRecordsFilter(filter, dateSort));
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
if (Records.shouldBuildUnpublishedAuthorFilter(filter, recordsSubscribe.author!)) {
|
|
484
|
-
filters.push({
|
|
485
|
-
...Records.convertFilter(filter, dateSort),
|
|
486
|
-
author : recordsSubscribe.author!,
|
|
487
|
-
interface : DwnInterfaceName.Records,
|
|
488
|
-
method : DwnMethodName.Write,
|
|
489
|
-
isLatestBaseState : true,
|
|
490
|
-
published : false,
|
|
491
|
-
});
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
if (Records.shouldProtocolAuthorize(recordsSubscribe.signaturePayload!)) {
|
|
495
|
-
filters.push({
|
|
496
|
-
...Records.convertFilter(filter, dateSort),
|
|
497
|
-
interface : DwnInterfaceName.Records,
|
|
498
|
-
method : DwnMethodName.Write,
|
|
499
|
-
isLatestBaseState : true,
|
|
500
|
-
published : false,
|
|
501
|
-
});
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
if (Message.getPermissionGrantId(recordsSubscribe.signaturePayload!) !== undefined) {
|
|
505
|
-
filters.push({
|
|
506
|
-
...Records.convertFilter(filter, dateSort),
|
|
507
|
-
interface : DwnInterfaceName.Records,
|
|
508
|
-
method : DwnMethodName.Write,
|
|
509
|
-
isLatestBaseState : true,
|
|
510
|
-
published : false,
|
|
511
|
-
});
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
if (Records.shouldBuildUnpublishedRecipientFilter(filter, recordsSubscribe.author!)) {
|
|
515
|
-
filters.push({
|
|
516
|
-
...Records.convertFilter(filter, dateSort),
|
|
517
|
-
interface : DwnInterfaceName.Records,
|
|
518
|
-
method : DwnMethodName.Write,
|
|
519
|
-
recipient : recordsSubscribe.author!,
|
|
520
|
-
isLatestBaseState : true,
|
|
521
|
-
published : false,
|
|
522
|
-
});
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
return filters;
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
/**
|
|
529
|
-
* Build a published-only query filter (latest writes).
|
|
530
|
-
*/
|
|
531
|
-
private static buildPublishedQueryFilter(recordsSubscribe: RecordsSubscribe): Filter {
|
|
532
|
-
const { dateSort, filter } = recordsSubscribe.message.descriptor;
|
|
533
|
-
return {
|
|
534
|
-
...Records.convertFilter(filter, dateSort),
|
|
535
|
-
interface : DwnInterfaceName.Records,
|
|
536
|
-
method : DwnMethodName.Write,
|
|
537
|
-
published : true,
|
|
538
|
-
isLatestBaseState : true,
|
|
539
|
-
};
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
/**
|
|
543
|
-
* @param messageStore Used to check if the grant has been revoked.
|
|
544
|
-
*/
|
|
545
|
-
public static async authorizeRecordsSubscribe(
|
|
546
|
-
tenant: string,
|
|
547
|
-
recordsSubscribe: RecordsSubscribe,
|
|
548
|
-
deps: HandlerDependencies,
|
|
549
|
-
): Promise<void> {
|
|
550
|
-
|
|
551
|
-
if (Message.isSignedByAuthorDelegate(recordsSubscribe.message) &&
|
|
552
|
-
!EncryptionControl.filterTargetsOnlyControlRecords(recordsSubscribe.message.descriptor.filter)) {
|
|
553
|
-
await recordsSubscribe.authorizeDelegate(deps.validationStateReader);
|
|
554
|
-
} else if (EncryptionControl.filterTargetsOnlyControlRecords(recordsSubscribe.message.descriptor.filter)) {
|
|
555
|
-
await EncryptionControl.authorizeControlReadRequest({
|
|
556
|
-
tenant,
|
|
557
|
-
incomingMessage : recordsSubscribe.message,
|
|
558
|
-
requester : Message.getRequester(recordsSubscribe.message),
|
|
559
|
-
validationStateReader : deps.validationStateReader,
|
|
560
|
-
});
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
const permissionGrantId = Message.getPermissionGrantId(recordsSubscribe.signaturePayload!);
|
|
564
|
-
if (permissionGrantId !== undefined) {
|
|
565
|
-
const permissionGrant = await deps.validationStateReader.fetchGrant(tenant, permissionGrantId);
|
|
566
|
-
await RecordsGrantAuthorization.authorizeQueryOrSubscribe({
|
|
567
|
-
incomingMessage : recordsSubscribe.message,
|
|
568
|
-
expectedGrantor : tenant,
|
|
569
|
-
expectedGrantee : recordsSubscribe.author!,
|
|
570
|
-
permissionGrant,
|
|
571
|
-
validationStateReader : deps.validationStateReader,
|
|
572
|
-
});
|
|
573
|
-
return;
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
// NOTE: not all RecordsSubscribe messages require protocol authorization even if the filter includes protocol-related fields,
|
|
577
|
-
// this is because we dynamically filter out records that the caller is not authorized to see.
|
|
578
|
-
// Currently only run protocol authorization if message deliberately invokes a protocol role.
|
|
579
|
-
if (Records.shouldProtocolAuthorize(recordsSubscribe.signaturePayload!)) {
|
|
580
|
-
await ProtocolAuthorization.authorizeQueryOrSubscribe(tenant, recordsSubscribe, deps.validationStateReader);
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
private async filterControlRecordsForNonOwner(
|
|
585
|
-
tenant: string,
|
|
586
|
-
recordsSubscribe: RecordsSubscribe,
|
|
587
|
-
requester: string | undefined,
|
|
588
|
-
recordsWrites: RecordsQueryReplyEntry[],
|
|
589
|
-
): Promise<RecordsQueryReplyEntry[]> {
|
|
590
|
-
return EncryptionControl.filterVisibleControlRecords({
|
|
591
|
-
tenant,
|
|
592
|
-
incomingMessage : recordsSubscribe.message,
|
|
593
|
-
requester,
|
|
594
|
-
recordsWriteMessages : recordsWrites,
|
|
595
|
-
validationStateReader : this.deps.validationStateReader,
|
|
596
|
-
});
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
private async queryRecordsWithVisibleControlFiltering(
|
|
600
|
-
tenant: string,
|
|
601
|
-
recordsSubscribe: RecordsSubscribe,
|
|
602
|
-
requester: string | undefined,
|
|
603
|
-
filters: Filter[],
|
|
604
|
-
messageSort: MessageSort,
|
|
605
|
-
pagination: { cursor?: PaginationCursor; limit?: number } | undefined,
|
|
606
|
-
): Promise<{ messages: RecordsQueryReplyEntry[], cursor?: PaginationCursor }> {
|
|
607
|
-
const controlFilters = Records.buildControlRecordsFilters(filters);
|
|
608
|
-
const currentAudienceRecordIdCache = new Map<string, string | undefined>();
|
|
609
|
-
if (controlFilters.length === 0) {
|
|
610
|
-
const result = await queryRecordsWithRecordLimitOccupancy({
|
|
611
|
-
messageStore : this.deps.messageStore,
|
|
612
|
-
validationStateReader : this.deps.validationStateReader,
|
|
613
|
-
tenant,
|
|
614
|
-
filters,
|
|
615
|
-
messageSort,
|
|
616
|
-
pagination,
|
|
617
|
-
messageTimestamp : recordsSubscribe.message.descriptor.messageTimestamp,
|
|
618
|
-
});
|
|
619
|
-
return EncryptionControl.projectCurrentAudienceRecordPage({
|
|
620
|
-
messageStore: this.deps.messageStore,
|
|
621
|
-
tenant,
|
|
622
|
-
filters,
|
|
623
|
-
currentAudienceRecordIdCache,
|
|
624
|
-
result,
|
|
625
|
-
});
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
if (pagination?.limit === undefined || pagination.limit <= 0) {
|
|
629
|
-
const result = await queryRecordsWithRecordLimitOccupancy({
|
|
630
|
-
messageStore : this.deps.messageStore,
|
|
631
|
-
validationStateReader : this.deps.validationStateReader,
|
|
632
|
-
tenant,
|
|
633
|
-
filters,
|
|
634
|
-
messageSort,
|
|
635
|
-
pagination,
|
|
636
|
-
messageTimestamp : recordsSubscribe.message.descriptor.messageTimestamp,
|
|
637
|
-
});
|
|
638
|
-
const projectedResult = await EncryptionControl.projectCurrentAudienceRecordPage({
|
|
639
|
-
messageStore: this.deps.messageStore,
|
|
640
|
-
tenant,
|
|
641
|
-
filters,
|
|
642
|
-
currentAudienceRecordIdCache,
|
|
643
|
-
result,
|
|
644
|
-
});
|
|
645
|
-
return {
|
|
646
|
-
messages : await this.filterControlRecordsForNonOwner(tenant, recordsSubscribe, requester, projectedResult.messages),
|
|
647
|
-
cursor : projectedResult.cursor,
|
|
648
|
-
};
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
const visibleMessages: RecordsQueryReplyEntry[] = [];
|
|
652
|
-
let cursor = pagination.cursor;
|
|
653
|
-
let nextCursor: PaginationCursor | undefined;
|
|
654
|
-
// Keeps visible-page pagination stable until #1100 moves control visibility into indexed store filters.
|
|
655
|
-
do {
|
|
656
|
-
const remainingLimit = pagination.limit - visibleMessages.length;
|
|
657
|
-
const result = await queryRecordsWithRecordLimitOccupancy({
|
|
658
|
-
messageStore : this.deps.messageStore,
|
|
659
|
-
validationStateReader : this.deps.validationStateReader,
|
|
660
|
-
tenant,
|
|
661
|
-
filters,
|
|
662
|
-
messageSort,
|
|
663
|
-
pagination : { ...pagination, cursor, limit: remainingLimit },
|
|
664
|
-
messageTimestamp : recordsSubscribe.message.descriptor.messageTimestamp,
|
|
665
|
-
});
|
|
666
|
-
const projectedResult = await EncryptionControl.projectCurrentAudienceRecordPage({
|
|
667
|
-
messageStore: this.deps.messageStore,
|
|
668
|
-
tenant,
|
|
669
|
-
filters,
|
|
670
|
-
currentAudienceRecordIdCache,
|
|
671
|
-
result,
|
|
672
|
-
});
|
|
673
|
-
const filteredMessages = await this.filterControlRecordsForNonOwner(
|
|
674
|
-
tenant,
|
|
675
|
-
recordsSubscribe,
|
|
676
|
-
requester,
|
|
677
|
-
projectedResult.messages,
|
|
678
|
-
);
|
|
679
|
-
visibleMessages.push(...filteredMessages);
|
|
680
|
-
nextCursor = projectedResult.cursor;
|
|
681
|
-
cursor = projectedResult.cursor;
|
|
682
|
-
} while (visibleMessages.length < pagination.limit && cursor !== undefined);
|
|
683
|
-
|
|
684
|
-
return { messages: visibleMessages, cursor: nextCursor };
|
|
685
|
-
}
|
|
686
|
-
|
|
687
316
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { CoreProtocol } from '../core/core-protocol.js';
|
|
2
2
|
import type { ProgressToken } from '../types/subscriptions.js';
|
|
3
|
+
import type { ProtocolRuleSet } from '../types/protocols-types.js';
|
|
3
4
|
import type { GenericMessage, GenericMessageReply } from '../types/message-types.js';
|
|
4
5
|
import type { HandlerDependencies, MethodHandler } from '../types/method-handler.js';
|
|
5
6
|
import type { RecordsQueryReplyEntry, RecordsWriteMessage } from '../types/records-types.js';
|
|
@@ -64,8 +65,13 @@ export class RecordsWriteHandler implements MethodHandler {
|
|
|
64
65
|
return messageReplyFromError(e, 400);
|
|
65
66
|
}
|
|
66
67
|
|
|
68
|
+
let protocolRuleSet: ProtocolRuleSet;
|
|
67
69
|
try {
|
|
68
|
-
await ProtocolAuthorization.validateReferentialIntegrity(
|
|
70
|
+
protocolRuleSet = await ProtocolAuthorization.validateReferentialIntegrity(
|
|
71
|
+
tenant,
|
|
72
|
+
recordsWrite,
|
|
73
|
+
this.deps.validationStateReader,
|
|
74
|
+
);
|
|
69
75
|
} catch (e) {
|
|
70
76
|
return messageReplyFromError(e, 400);
|
|
71
77
|
}
|
|
@@ -87,7 +93,7 @@ export class RecordsWriteHandler implements MethodHandler {
|
|
|
87
93
|
// messageTimestamp is <= the most recent squash record at the same path and parent context.
|
|
88
94
|
// The squash record acts as a temporal floor — no record older than the latest squash can exist.
|
|
89
95
|
try {
|
|
90
|
-
await this.enforceSquashBackstop(tenant, message);
|
|
96
|
+
await this.enforceSquashBackstop(tenant, message, protocolRuleSet);
|
|
91
97
|
} catch (e) {
|
|
92
98
|
return messageReplyFromError(e, 409);
|
|
93
99
|
}
|
|
@@ -445,38 +451,17 @@ export class RecordsWriteHandler implements MethodHandler {
|
|
|
445
451
|
* and there exists a squash record at the same protocol path and parent context whose
|
|
446
452
|
* `messageTimestamp` is >= the incoming message's `messageTimestamp`, reject with 409.
|
|
447
453
|
*
|
|
454
|
+
* The rule set is the exact policy already resolved by referential-integrity validation. Reusing
|
|
455
|
+
* it prevents a second store lookup from observing different state or failing open.
|
|
456
|
+
*
|
|
448
457
|
* This check only applies to protocol-based records at `$squash: true` paths.
|
|
449
458
|
*/
|
|
450
|
-
private async enforceSquashBackstop(
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
// Fetch the protocol definition active at the incoming message timestamp to check if $squash is enabled at this path.
|
|
457
|
-
// The reader resolves core protocols (e.g. permissions) from the registry.
|
|
458
|
-
let protocolDefinition;
|
|
459
|
-
try {
|
|
460
|
-
protocolDefinition = await this.deps.validationStateReader.fetchProtocolDefinition(
|
|
461
|
-
tenant,
|
|
462
|
-
message.descriptor.protocol,
|
|
463
|
-
message.descriptor.messageTimestamp,
|
|
464
|
-
);
|
|
465
|
-
} catch (error) {
|
|
466
|
-
// If the protocol definition can't be found, skip the backstop check.
|
|
467
|
-
// Authorization will handle the missing protocol error later.
|
|
468
|
-
console.warn(`enforceSquashBackstop: failed to fetch protocol definition for '${message.descriptor.protocol}':`, error);
|
|
469
|
-
return;
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
// Walk the structure to find the rule set for this protocol path
|
|
473
|
-
const pathSegments = message.descriptor.protocolPath.split('/');
|
|
474
|
-
let ruleSet = protocolDefinition.structure[pathSegments[0]];
|
|
475
|
-
for (let i = 1; i < pathSegments.length && ruleSet !== undefined; i++) {
|
|
476
|
-
ruleSet = ruleSet[pathSegments[i]] as typeof ruleSet;
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
if (ruleSet?.$squash !== true) {
|
|
459
|
+
private async enforceSquashBackstop(
|
|
460
|
+
tenant: string,
|
|
461
|
+
message: RecordsWriteMessage,
|
|
462
|
+
ruleSet: ProtocolRuleSet,
|
|
463
|
+
): Promise<void> {
|
|
464
|
+
if (ruleSet.$squash !== true) {
|
|
480
465
|
return;
|
|
481
466
|
}
|
|
482
467
|
|
package/src/index.ts
CHANGED
|
@@ -9,9 +9,8 @@ export { StoreValidationStateReader } from './core/validation-state-reader.js';
|
|
|
9
9
|
export type { RecordedValidationRead } from './core/recording-validation-state-reader.js';
|
|
10
10
|
export { RecordingValidationStateReader } from './core/recording-validation-state-reader.js';
|
|
11
11
|
export type { MessagesFilter, MessagesQueryDescriptor, MessagesQueryMessage, MessagesQueryReply, MessagesQueryReplyEntry, MessagesReadMessage, MessagesReadReply, MessagesReadReplyEntry, MessagesReadDescriptor, MessagesSubscribeDescriptor, MessagesSubscribeMessage, MessagesSubscribeReply, MessagesSubscribeMessageOptions } from './types/messages-types.js';
|
|
12
|
-
export type { GT, LT, Filter, FilterValue, KeyValues, EqualFilter, OneOfFilter, RangeFilter, RangeCriterion, PaginationCursor, QueryOptions, RangeValue, StartsWithFilter } from './types/query-types.js';
|
|
12
|
+
export type { GT, LT, Filter, FilterValue, KeyValues, EqualFilter, OneOfFilter, RangeFilter, RangeCriterion, PaginationCursor, QueryOptions, RangeValue, StartsWithFilter, SubtreeFilter } from './types/query-types.js';
|
|
13
13
|
export type { ProtocolsConfigureDescriptor, ProtocolDefinition, ProtocolTypes, ProtocolRuleSet, ProtocolsQueryFilter, ProtocolsConfigureMessage, ProtocolsQueryMessage, ProtocolsQueryReply, ProtocolActionRule, ProtocolDeliveryStrategy, ProtocolKeyAgreement, ProtocolsQueryDescriptor, ProtocolRecordLimitDefinition, ProtocolSizeDefinition, ProtocolTagsDefinition, ProtocolTagSchema, ProtocolType, ProtocolUses } from './types/protocols-types.js';
|
|
14
|
-
export { ProtocolRecordLimitStrategy } from './types/protocols-types.js';
|
|
15
14
|
export type { DataEncodedRecordsWriteMessage, RecordsCountDescriptor, RecordsCountMessage, RecordsCountReply, RecordsDeleteMessage, RecordsFilter, RecordsQueryMessage, RecordsQueryReply, RecordsQueryReplyEntry, RecordsReadMessage, RecordsReadReply, RecordsSubscribeDescriptor, RecordsSubscribeMessage, RecordsSubscribeReply, RecordsWriteDescriptor, RecordsWriteTags, RecordsWriteTagValue, RecordsWriteMessage, RecordsWriteSignaturePayload, RecordsDeleteDescriptor, RecordsQueryDescriptor, RecordsReadDescriptor, RecordsSubscribeMessageOptions, RecordsWriteMessageOptions, InternalRecordsWriteMessage, RecordEvent, RecordsWriteTagsFilter } from './types/records-types.js';
|
|
16
15
|
export type { GeneralJws, SignatureEntry } from './types/jws-types.js';
|
|
17
16
|
export { authenticate } from './core/auth.js';
|
|
@@ -53,6 +52,7 @@ export { DwnError, DwnErrorCode } from './core/dwn-error.js';
|
|
|
53
52
|
export type { DwnErrorInfo } from './core/dwn-error.js';
|
|
54
53
|
export { DwnInterfaceName, DwnMethodName } from './enums/dwn-interface-method.js';
|
|
55
54
|
export { Encoder } from './utils/encoder.js';
|
|
55
|
+
export { assertValidSubtreeFilters, isSubtreeFilter } from './utils/filter.js';
|
|
56
56
|
export { MessagesSubscribe } from './interfaces/messages-subscribe.js';
|
|
57
57
|
export type { MessagesSubscribeOptions } from './interfaces/messages-subscribe.js';
|
|
58
58
|
export { Encryption, ContentEncryptionAlgorithm, KeyAgreementAlgorithm, ROLE_AUDIENCE_DERIVATION_SCHEME, SEAL_DERIVATION_SCHEME } from './utils/encryption.js';
|
|
@@ -82,7 +82,7 @@ export type { MessagesReadOptions } from './interfaces/messages-read.js';
|
|
|
82
82
|
export { MessagesQuery } from './interfaces/messages-query.js';
|
|
83
83
|
export type { MessagesQueryOptions } from './interfaces/messages-query.js';
|
|
84
84
|
export type { UnionMessageReply } from './core/message-reply.js';
|
|
85
|
-
export type { MessageStore, MessageStoreLatestStateTransition, MessageStoreOptions, MessageStorePutResult } from './types/message-store.js';
|
|
85
|
+
export type { MessageStore, MessageStoreLatestStateTransition, MessageStoreOptions, MessageStorePutResult, MessageStoreQueryOptions, RecordLimitOccupancy } from './types/message-store.js';
|
|
86
86
|
export { Replication } from './utils/replication.js';
|
|
87
87
|
export type { MessageInterface } from './types/message-interface.js';
|
|
88
88
|
export { PermissionGrant } from './protocols/permission-grant.js';
|
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
|
|
10
10
|
import { AbstractMessage } from '../core/abstract-message.js';
|
|
11
11
|
import { DwnConstant } from '../core/dwn-constant.js';
|
|
12
|
+
import { FilterUtility } from '../utils/filter.js';
|
|
12
13
|
import { Message } from '../core/message.js';
|
|
13
14
|
import { PermissionGrant } from '../protocols/permission-grant.js';
|
|
14
15
|
import { ProtocolsGrantAuthorization } from '../core/protocols-grant-authorization.js';
|
|
@@ -18,7 +19,7 @@ import { DwnError, DwnErrorCode } from '../core/dwn-error.js';
|
|
|
18
19
|
import { DwnInterfaceName, DwnMethodName } from '../enums/dwn-interface-method.js';
|
|
19
20
|
import { getRoleAudienceContextId, getRuleSetAtPath, isCrossProtocolRef, parseCrossProtocolRef } from '../utils/protocols.js';
|
|
20
21
|
import { normalizeProtocolUrl, normalizeSchemaUrl, validateProtocolUrlNormalized, validateSchemaUrlNormalized } from '../utils/url.js';
|
|
21
|
-
import { ProtocolAction, ProtocolActor
|
|
22
|
+
import { ProtocolAction, ProtocolActor } from '../types/protocols-types.js';
|
|
22
23
|
|
|
23
24
|
export type ProtocolsConfigureOptions = {
|
|
24
25
|
messageTimestamp?: string;
|
|
@@ -300,7 +301,7 @@ export class ProtocolsConfigure extends AbstractMessage<ProtocolsConfigureMessag
|
|
|
300
301
|
|
|
301
302
|
// Validate $recordLimit
|
|
302
303
|
if (ruleSet.$recordLimit !== undefined) {
|
|
303
|
-
const { max
|
|
304
|
+
const { max } = ruleSet.$recordLimit;
|
|
304
305
|
|
|
305
306
|
if (!Number.isInteger(max) || max < 1) {
|
|
306
307
|
throw new DwnError(
|
|
@@ -315,15 +316,6 @@ export class ProtocolsConfigure extends AbstractMessage<ProtocolsConfigureMessag
|
|
|
315
316
|
`Invalid $recordLimit.max value ${max} at protocol path '${ruleSetProtocolPath}': must be <= ${DwnConstant.maxRecordLimit}.`
|
|
316
317
|
);
|
|
317
318
|
}
|
|
318
|
-
|
|
319
|
-
const validStrategies = Object.values(ProtocolRecordLimitStrategy) as string[];
|
|
320
|
-
if (!validStrategies.includes(strategy as string)) {
|
|
321
|
-
throw new DwnError(
|
|
322
|
-
DwnErrorCode.ProtocolsConfigureInvalidRecordLimit,
|
|
323
|
-
`Invalid $recordLimit.strategy '${strategy}' at protocol path '${ruleSetProtocolPath}': ` +
|
|
324
|
-
`must be one of ${validStrategies.join(', ')}.`
|
|
325
|
-
);
|
|
326
|
-
}
|
|
327
319
|
}
|
|
328
320
|
|
|
329
321
|
if (ruleSet.$tags !== undefined) {
|
|
@@ -412,8 +404,7 @@ export class ProtocolsConfigure extends AbstractMessage<ProtocolsConfigureMessag
|
|
|
412
404
|
ProtocolsConfigure.validateCrossProtocolAlias(actionRule.of, uses, ruleSetProtocolPath, 'of');
|
|
413
405
|
} else {
|
|
414
406
|
// Local `of`: must be self or ancestor
|
|
415
|
-
const isSelfOrAncestor =
|
|
416
|
-
|| ruleSetProtocolPath.startsWith(actionRule.of + '/');
|
|
407
|
+
const isSelfOrAncestor = FilterUtility.matchesSubtree(actionRule.of, ruleSetProtocolPath);
|
|
417
408
|
if (!isSelfOrAncestor) {
|
|
418
409
|
throw new DwnError(
|
|
419
410
|
DwnErrorCode.ProtocolsConfigureInvalidActionOfNotAnAncestor,
|
|
@@ -38,7 +38,7 @@ export class RecordsCount extends AbstractMessage<RecordsCountMessage> {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
await Records.validateDelegatedGrantReferentialIntegrity(message, signaturePayload);
|
|
41
|
-
Records.
|
|
41
|
+
Records.validateNestedProtocolPathScope(
|
|
42
42
|
message.descriptor.filter,
|
|
43
43
|
DwnErrorCode.RecordsCountNestedProtocolPathContextIdInvalid,
|
|
44
44
|
'RecordsCount'
|