@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
|
@@ -6,6 +6,29 @@ export interface MessageStoreOptions {
|
|
|
6
6
|
signal?: AbortSignal;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Read-time `$recordLimit` occupancy policy for one concrete protocol path.
|
|
11
|
+
*
|
|
12
|
+
* Stores partition matching latest RecordsWrites by `parentId`, rank each
|
|
13
|
+
* partition by `dateCreated` then `recordId` ascending, and apply `max`
|
|
14
|
+
* before the caller's filters, sort, and pagination.
|
|
15
|
+
*/
|
|
16
|
+
export type RecordLimitOccupancy = {
|
|
17
|
+
protocol: string;
|
|
18
|
+
protocolPath: string;
|
|
19
|
+
/**
|
|
20
|
+
* Root of the candidate context subtree. An exact-record request supplies
|
|
21
|
+
* its direct-parent context here so sibling candidates still consume slots.
|
|
22
|
+
* Omitted for root protocol paths.
|
|
23
|
+
*/
|
|
24
|
+
contextId?: string;
|
|
25
|
+
max: number;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export interface MessageStoreQueryOptions extends MessageStoreOptions {
|
|
29
|
+
recordLimit?: RecordLimitOccupancy;
|
|
30
|
+
}
|
|
31
|
+
|
|
9
32
|
/**
|
|
10
33
|
* Result of a {@link MessageStore.put} operation.
|
|
11
34
|
*/
|
|
@@ -98,7 +121,7 @@ export interface MessageStore {
|
|
|
98
121
|
filters: Filter[],
|
|
99
122
|
messageSort?: MessageSort,
|
|
100
123
|
pagination?: Pagination,
|
|
101
|
-
options?:
|
|
124
|
+
options?: MessageStoreQueryOptions
|
|
102
125
|
): Promise<{ messages: GenericMessage[], cursor?: PaginationCursor}>;
|
|
103
126
|
|
|
104
127
|
/**
|
|
@@ -109,7 +132,7 @@ export interface MessageStore {
|
|
|
109
132
|
tenant: string,
|
|
110
133
|
filters: Filter[],
|
|
111
134
|
messageSort?: MessageSort,
|
|
112
|
-
options?:
|
|
135
|
+
options?: MessageStoreQueryOptions
|
|
113
136
|
): Promise<number>;
|
|
114
137
|
|
|
115
138
|
/**
|
|
@@ -87,6 +87,20 @@ export type MessagesSubscribeMessage = {
|
|
|
87
87
|
|
|
88
88
|
export type MessagesSubscribeReply = GenericMessageReply & {
|
|
89
89
|
subscription?: MessageSubscription;
|
|
90
|
+
/**
|
|
91
|
+
* Feed fingerprint over the subscription's filter scopes, observed after the
|
|
92
|
+
* subscription became active. Present only when the filters map onto
|
|
93
|
+
* fingerprint domains and the message store exposes the replication feed.
|
|
94
|
+
*/
|
|
95
|
+
fingerprint?: string;
|
|
96
|
+
/**
|
|
97
|
+
* High-water progress token of the tenant's replication log, observed after
|
|
98
|
+
* the subscription became active — the position-zero anchor when the log is
|
|
99
|
+
* empty. NOT a delivery cursor: adopting it as a checkpoint is sound only
|
|
100
|
+
* after verifying `fingerprint` against the local feed, since resuming from
|
|
101
|
+
* it skips every earlier log position.
|
|
102
|
+
*/
|
|
103
|
+
head?: ProgressToken;
|
|
90
104
|
/** Present when status.code is 410 — structured gap metadata. */
|
|
91
105
|
error?: { code: 'ProgressGap' } & ProgressGapInfo;
|
|
92
106
|
};
|
|
@@ -149,33 +149,15 @@ export type ProtocolSizeDefinition = {
|
|
|
149
149
|
};
|
|
150
150
|
|
|
151
151
|
/**
|
|
152
|
-
*
|
|
153
|
-
*/
|
|
154
|
-
export enum ProtocolRecordLimitStrategy {
|
|
155
|
-
/** Reject the incoming write with an error. The author must delete old records first. */
|
|
156
|
-
Reject = 'reject',
|
|
157
|
-
/** Automatically delete the oldest record(s) (by `dateCreated`) to make room for the new one. */
|
|
158
|
-
PurgeOldest = 'purgeOldest',
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Limits the number of records at a given protocol path within the same parent context.
|
|
152
|
+
* Limits the visible records at a given protocol path within the same parent context.
|
|
163
153
|
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
* Only initial writes (new records) count toward the limit; updates to existing records do not.
|
|
154
|
+
* Root-level records form one group for the protocol path. Nested records are grouped by
|
|
155
|
+
* direct parent context. Occupants are selected deterministically at read time; valid writes
|
|
156
|
+
* outside the visible population remain stored and may become visible later.
|
|
168
157
|
*/
|
|
169
158
|
export type ProtocolRecordLimitDefinition = {
|
|
170
|
-
/** Maximum number of records
|
|
159
|
+
/** Maximum number of visible records at this path within the same parent context. Must be >= 1. */
|
|
171
160
|
max: number;
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Strategy when the limit is reached.
|
|
175
|
-
* Currently only `'reject'` is implemented. Future strategies (e.g. `'purgeOldest'`)
|
|
176
|
-
* are defined in the wire format but not yet enforced.
|
|
177
|
-
*/
|
|
178
|
-
strategy: ProtocolRecordLimitStrategy | `${ProtocolRecordLimitStrategy}` | (string & {});
|
|
179
161
|
};
|
|
180
162
|
|
|
181
163
|
/**
|
|
@@ -287,9 +269,8 @@ export type ProtocolRuleSet = {
|
|
|
287
269
|
$tags?: ProtocolTagsDefinition;
|
|
288
270
|
|
|
289
271
|
/**
|
|
290
|
-
* If
|
|
291
|
-
* parent context
|
|
292
|
-
* whether the write is rejected or the oldest record is purged.
|
|
272
|
+
* If `$recordLimit` is set, reads expose at most `max` records at this protocol path
|
|
273
|
+
* within each direct parent context.
|
|
293
274
|
*/
|
|
294
275
|
$recordLimit?: ProtocolRecordLimitDefinition;
|
|
295
276
|
|
package/src/types/query-types.ts
CHANGED
|
@@ -37,7 +37,15 @@ export type StartsWithFilter = {
|
|
|
37
37
|
startsWith: string;
|
|
38
38
|
};
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Selects one hierarchical path and every proper `/`-delimited descendant.
|
|
42
|
+
* Supported only for the `contextId` and `protocolPath` indexes.
|
|
43
|
+
*/
|
|
44
|
+
export type SubtreeFilter = {
|
|
45
|
+
subtree: string;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export type FilterValue = EqualFilter | OneOfFilter | RangeFilter | SubtreeFilter;
|
|
41
49
|
|
|
42
50
|
export type Filter = {
|
|
43
51
|
[property: string]: FilterValue;
|
|
@@ -58,4 +66,4 @@ export type RangeCriterion = {
|
|
|
58
66
|
export type PaginationCursor = {
|
|
59
67
|
messageCid: string;
|
|
60
68
|
value: string | number;
|
|
61
|
-
};
|
|
69
|
+
};
|
|
@@ -154,7 +154,8 @@ export type RecordsFilter = {
|
|
|
154
154
|
published?: boolean;
|
|
155
155
|
|
|
156
156
|
/**
|
|
157
|
-
*
|
|
157
|
+
* Selects the exact context and its proper `/`-delimited descendants.
|
|
158
|
+
* Prefix-sharing sibling contexts are excluded.
|
|
158
159
|
*/
|
|
159
160
|
contextId?: string;
|
|
160
161
|
schema?: string;
|
package/src/utils/filter.ts
CHANGED
|
@@ -1,4 +1,30 @@
|
|
|
1
|
-
import type { EqualFilter, Filter, FilterValue, KeyValues, OneOfFilter, RangeCriterion, RangeFilter, RangeValue } from '../types/query-types.js';
|
|
1
|
+
import type { EqualFilter, Filter, FilterValue, KeyValues, OneOfFilter, RangeCriterion, RangeFilter, RangeValue, SubtreeFilter } from '../types/query-types.js';
|
|
2
|
+
|
|
3
|
+
const SUBTREE_FILTER_PROPERTIES = new Set(['contextId', 'protocolPath']);
|
|
4
|
+
|
|
5
|
+
/** Returns whether the value is a well-formed hierarchical subtree filter. */
|
|
6
|
+
export function isSubtreeFilter(filter: FilterValue): filter is SubtreeFilter {
|
|
7
|
+
return typeof filter === 'object' &&
|
|
8
|
+
filter !== null &&
|
|
9
|
+
!Array.isArray(filter) &&
|
|
10
|
+
Object.keys(filter).length === 1 &&
|
|
11
|
+
typeof (filter as Partial<SubtreeFilter>).subtree === 'string';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Rejects subtree filters for indexes without hierarchical path semantics.
|
|
16
|
+
*
|
|
17
|
+
* @throws {TypeError} If a subtree filter targets an index other than `contextId` or `protocolPath`.
|
|
18
|
+
*/
|
|
19
|
+
export function assertValidSubtreeFilters(filters: Filter[]): void {
|
|
20
|
+
for (const filter of filters) {
|
|
21
|
+
for (const property in filter) {
|
|
22
|
+
if (isSubtreeFilter(filter[property]) && !SUBTREE_FILTER_PROPERTIES.has(property)) {
|
|
23
|
+
throw new TypeError(`SubtreeFilter is not supported for index '${property}'.`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
2
28
|
|
|
3
29
|
/**
|
|
4
30
|
* A Utility class to help match indexes against filters.
|
|
@@ -84,7 +110,9 @@ export class FilterUtility {
|
|
|
84
110
|
if (this.matchOneOf(filterValue, indexValue)) {
|
|
85
111
|
return true;
|
|
86
112
|
}
|
|
87
|
-
} else if (this.
|
|
113
|
+
} else if (isSubtreeFilter(filterValue) && this.matchesSubtree(filterValue.subtree, indexValue)) {
|
|
114
|
+
return true;
|
|
115
|
+
} else if (this.isRangeFilter(filterValue) && this.matchRange(filterValue, indexValue as RangeValue)) {
|
|
88
116
|
// `filterValue` is a `RangeFilter`
|
|
89
117
|
// range filters cannot range over booleans
|
|
90
118
|
return true;
|
|
@@ -181,6 +209,15 @@ export class FilterUtility {
|
|
|
181
209
|
};
|
|
182
210
|
}
|
|
183
211
|
|
|
212
|
+
/**
|
|
213
|
+
* Returns whether a candidate is the selected hierarchical path or one of
|
|
214
|
+
* its proper `/`-delimited descendants.
|
|
215
|
+
*/
|
|
216
|
+
static matchesSubtree(subtree: string, candidate: unknown): candidate is string {
|
|
217
|
+
return typeof candidate === 'string' &&
|
|
218
|
+
(candidate === subtree || candidate.startsWith(`${subtree}/`));
|
|
219
|
+
}
|
|
220
|
+
|
|
184
221
|
}
|
|
185
222
|
|
|
186
223
|
export class FilterSelector {
|
|
@@ -238,4 +275,4 @@ export class FilterSelector {
|
|
|
238
275
|
singlePropertyFilter[firstRemainingProperty] = filter[firstRemainingProperty];
|
|
239
276
|
return singlePropertyFilter;
|
|
240
277
|
}
|
|
241
|
-
}
|
|
278
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { PermissionScope, RecordsPermissionScope } from '../types/permission-types.js';
|
|
2
2
|
import type { ProtocolDefinition, ProtocolRuleSet } from '../types/protocols-types.js';
|
|
3
3
|
|
|
4
|
+
import { FilterUtility } from './filter.js';
|
|
4
5
|
import { KeyDerivationScheme } from './hd-key.js';
|
|
5
6
|
import { ProtocolAction } from '../types/protocols-types.js';
|
|
6
7
|
import { DwnInterfaceName, DwnMethodName } from '../enums/dwn-interface-method.js';
|
|
@@ -171,7 +172,7 @@ function readGrantKeyScopeCoversDeliveredScope(input: {
|
|
|
171
172
|
return false;
|
|
172
173
|
}
|
|
173
174
|
|
|
174
|
-
if (
|
|
175
|
+
if (FilterUtility.matchesSubtree(grantProtocolPath, deliveredProtocolPath)) {
|
|
175
176
|
return true;
|
|
176
177
|
}
|
|
177
178
|
|
|
@@ -206,7 +207,7 @@ function writeGrantKeyScopeCoversDeliveredScope(input: {
|
|
|
206
207
|
return true;
|
|
207
208
|
}
|
|
208
209
|
|
|
209
|
-
return
|
|
210
|
+
return FilterUtility.matchesSubtree(grantProtocolPath, deliveredProtocolPath);
|
|
210
211
|
}
|
|
211
212
|
|
|
212
213
|
function getGrantKeyReadRolePathsForScope(protocolDefinition: ProtocolDefinition, scopeProtocolPath: string): string[] {
|
|
@@ -257,7 +258,7 @@ function collectReadRolePaths(protocolDefinition: ProtocolDefinition, ruleSet: P
|
|
|
257
258
|
|
|
258
259
|
function getGrantKeyRolePathsCoveredByScope(protocolDefinition: ProtocolDefinition, scopeProtocolPath?: string): string[] {
|
|
259
260
|
return collectRolePaths(protocolDefinition, protocolDefinition.structure as ProtocolRuleSet)
|
|
260
|
-
.filter((rolePath) => scopeProtocolPath === undefined ||
|
|
261
|
+
.filter((rolePath) => scopeProtocolPath === undefined || FilterUtility.matchesSubtree(scopeProtocolPath, rolePath));
|
|
261
262
|
}
|
|
262
263
|
|
|
263
264
|
function collectRolePaths(protocolDefinition: ProtocolDefinition, ruleSet: ProtocolRuleSet, parentPath: string[] = []): string[] {
|
|
@@ -285,10 +286,6 @@ function isGrantKeyLocalRolePath(protocolDefinition: ProtocolDefinition, protoco
|
|
|
285
286
|
return ruleSet?.$role === true && ruleSet.$keyAgreement !== undefined;
|
|
286
287
|
}
|
|
287
288
|
|
|
288
|
-
function isBoundaryAwareSubtree(scopePath: string, candidatePath: string): boolean {
|
|
289
|
-
return `${candidatePath}/`.startsWith(`${scopePath}/`);
|
|
290
|
-
}
|
|
291
|
-
|
|
292
289
|
function getGrantKeyScopeCacheKey(scope: GrantKeyProtocolPathScope): string {
|
|
293
290
|
return JSON.stringify([scope.protocol, scope.protocolPath]);
|
|
294
291
|
}
|
package/src/utils/messages.ts
CHANGED
|
@@ -6,6 +6,7 @@ import type { MessagesFilter } from '../types/messages-types.js';
|
|
|
6
6
|
import { FilterUtility } from './filter.js';
|
|
7
7
|
import { normalizeProtocolUrl } from './url.js';
|
|
8
8
|
import { Records } from './records.js';
|
|
9
|
+
import { Replication } from './replication.js';
|
|
9
10
|
import { isEmptyObject, removeUndefinedProperties } from '@enbox/common';
|
|
10
11
|
|
|
11
12
|
type StoredMessageWithEncodedData = GenericMessage & { encodedData?: string };
|
|
@@ -122,6 +123,50 @@ export class Messages {
|
|
|
122
123
|
return metadataFilter;
|
|
123
124
|
}
|
|
124
125
|
|
|
126
|
+
/**
|
|
127
|
+
* Maps a filter set onto the replication fingerprint domains it covers, or
|
|
128
|
+
* `undefined` when the filters do not correspond to fingerprint domains.
|
|
129
|
+
* An empty filter set covers the global domain; a set of protocol-only
|
|
130
|
+
* filters covers each protocol's domain plus its tagged core-protocol
|
|
131
|
+
* domains. Any other filter shape has no domain mapping.
|
|
132
|
+
*/
|
|
133
|
+
public static computeFingerprintScopes(filters: MessagesFilter[]): string[] | undefined {
|
|
134
|
+
if (filters.length === 0) {
|
|
135
|
+
return [Replication.globalDomain];
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const protocols = new Set<string>();
|
|
139
|
+
for (const filter of filters) {
|
|
140
|
+
const keys = Object.keys(filter);
|
|
141
|
+
if (keys.length !== 1 || typeof filter.protocol !== 'string') {
|
|
142
|
+
return undefined;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
protocols.add(filter.protocol);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Fail closed for filter sets naming a core protocol directly: the core
|
|
149
|
+
// protocols contribute no tagged domains for themselves, so the domain
|
|
150
|
+
// set would not span everything such filters enumerate — two feeds could
|
|
151
|
+
// fingerprint identically while differing in enumerable messages. No
|
|
152
|
+
// fingerprint beats one whose coverage is known-incomplete.
|
|
153
|
+
for (const protocol of protocols) {
|
|
154
|
+
if (Replication.isCoreProtocolUri(protocol)) {
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const scopes: string[] = [];
|
|
160
|
+
for (const protocol of protocols) {
|
|
161
|
+
scopes.push(
|
|
162
|
+
Replication.protocolDomain(protocol),
|
|
163
|
+
...Replication.taggedCoreProtocolDomains(protocol, protocols),
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return scopes;
|
|
168
|
+
}
|
|
169
|
+
|
|
125
170
|
/**
|
|
126
171
|
* Returns a copy of a RecordsWrite message without inline encodedData, and the
|
|
127
172
|
* detached encodedData value for wire surfaces that carry data beside the message.
|
|
@@ -151,26 +196,16 @@ export class Messages {
|
|
|
151
196
|
delete filterCopy.dateUpdated;
|
|
152
197
|
}
|
|
153
198
|
|
|
154
|
-
// Convert protocolPathPrefix into a protocolPath
|
|
155
|
-
// The range gte: prefix, lt: prefix + '/\uffff' matches:
|
|
156
|
-
// - exact: 'post' (prefix itself)
|
|
157
|
-
// - children: 'post/attachment', 'post/comment', etc.
|
|
158
|
-
// - NOT siblings: 'poster', 'postfix' (excluded because '/' < any alphanumeric)
|
|
199
|
+
// Convert protocolPathPrefix into a segment-aware protocolPath subtree filter.
|
|
159
200
|
if (protocolPathPrefix !== undefined) {
|
|
160
201
|
delete (filterCopy as any).protocolPathPrefix;
|
|
161
|
-
filterCopy.protocolPath = {
|
|
162
|
-
gte : protocolPathPrefix,
|
|
163
|
-
lt : protocolPathPrefix + '/\uffff',
|
|
164
|
-
};
|
|
202
|
+
filterCopy.protocolPath = { subtree: protocolPathPrefix };
|
|
165
203
|
}
|
|
166
204
|
|
|
167
|
-
// Convert contextIdPrefix into
|
|
205
|
+
// Convert contextIdPrefix into the same segment-aware subtree filter.
|
|
168
206
|
if (contextIdPrefix !== undefined) {
|
|
169
207
|
delete (filterCopy as any).contextIdPrefix;
|
|
170
|
-
filterCopy.contextId = {
|
|
171
|
-
gte : contextIdPrefix,
|
|
172
|
-
lt : contextIdPrefix + '/\uffff',
|
|
173
|
-
};
|
|
208
|
+
filterCopy.contextId = { subtree: contextIdPrefix };
|
|
174
209
|
}
|
|
175
210
|
|
|
176
211
|
return filterCopy;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { FilterUtility } from './filter.js';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Minimal protocol-bound scope shape used by permission grant authorization.
|
|
3
5
|
*
|
|
@@ -38,22 +40,13 @@ export class PermissionScopeMatcher {
|
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
if (scope.protocolPath !== undefined) {
|
|
41
|
-
return
|
|
43
|
+
return FilterUtility.matchesSubtree(scope.protocolPath, target.protocolPath);
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
if (scope.contextId !== undefined) {
|
|
45
|
-
return
|
|
47
|
+
return FilterUtility.matchesSubtree(scope.contextId, target.contextId);
|
|
46
48
|
}
|
|
47
49
|
|
|
48
50
|
return true;
|
|
49
51
|
}
|
|
50
|
-
|
|
51
|
-
private static matchesContextId(scopeContextId: string, candidateContextId: unknown): boolean {
|
|
52
|
-
return PermissionScopeMatcher.matchesSubtree(scopeContextId, candidateContextId);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
private static matchesSubtree(scopeValue: string, candidateValue: unknown): boolean {
|
|
56
|
-
return typeof candidateValue === 'string' &&
|
|
57
|
-
(candidateValue === scopeValue || candidateValue.startsWith(scopeValue + '/'));
|
|
58
|
-
}
|
|
59
52
|
}
|