@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
|
@@ -5,13 +5,13 @@ import type { ValidationStateReader } from '../types/validation-state-reader.js'
|
|
|
5
5
|
import type { ProtocolActionRule, ProtocolDefinition, ProtocolRuleSet, ProtocolType, ProtocolTypes } from '../types/protocols-types.js';
|
|
6
6
|
|
|
7
7
|
import { KeyDerivationScheme } from '../utils/hd-key.js';
|
|
8
|
+
import { ProtocolAction } from '../types/protocols-types.js';
|
|
8
9
|
import { Records } from '../utils/records.js';
|
|
9
10
|
import { validateProtocolTags } from '../utils/protocol-tags.js';
|
|
10
11
|
import { DwnError, DwnErrorCode } from './dwn-error.js';
|
|
11
12
|
import { Encryption, ROLE_AUDIENCE_DERIVATION_SCHEME } from '../utils/encryption.js';
|
|
12
13
|
import { ENCRYPTION_PROTOCOL_GRANT_KEY_PATH, ENCRYPTION_PROTOCOL_URI } from './constants.js';
|
|
13
14
|
import { getRoleAudienceContextId, getRuleSetAtPath, getTypeName, parseCrossProtocolRef } from '../utils/protocols.js';
|
|
14
|
-
import { ProtocolAction, ProtocolRecordLimitStrategy } from '../types/protocols-types.js';
|
|
15
15
|
|
|
16
16
|
type ResolvedRoleAudience = {
|
|
17
17
|
contextId: string;
|
|
@@ -526,38 +526,6 @@ export async function verifyAsRoleRecordIfNeeded(
|
|
|
526
526
|
}
|
|
527
527
|
}
|
|
528
528
|
|
|
529
|
-
/**
|
|
530
|
-
* Verifies that the `$recordLimit` strategy is supported for a new record creation.
|
|
531
|
-
*
|
|
532
|
-
* This check only applies to initial writes (new records). Updates to existing records do not
|
|
533
|
-
* affect record-limit occupancy. The supported `reject` strategy admits every candidate and
|
|
534
|
-
* projects the visible occupant set at read time, so validation stays independent of arrival order.
|
|
535
|
-
*
|
|
536
|
-
* @throws {DwnError} with `ProtocolAuthorizationRecordLimitStrategyNotImplemented` if strategy is not implemented.
|
|
537
|
-
*/
|
|
538
|
-
export async function verifyRecordLimit(
|
|
539
|
-
incomingMessage: RecordsWrite,
|
|
540
|
-
ruleSet: ProtocolRuleSet,
|
|
541
|
-
): Promise<void> {
|
|
542
|
-
if (ruleSet.$recordLimit === undefined) {
|
|
543
|
-
return;
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
// Only initial writes can introduce a new record-limit candidate.
|
|
547
|
-
const isInitialWrite = await incomingMessage.isInitialWrite();
|
|
548
|
-
if (!isInitialWrite) {
|
|
549
|
-
return;
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
const { strategy } = ruleSet.$recordLimit;
|
|
553
|
-
if (strategy !== ProtocolRecordLimitStrategy.Reject) {
|
|
554
|
-
throw new DwnError(
|
|
555
|
-
DwnErrorCode.ProtocolAuthorizationRecordLimitStrategyNotImplemented,
|
|
556
|
-
`record limit strategy '${strategy}' is not yet implemented.`
|
|
557
|
-
);
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
|
|
561
529
|
/**
|
|
562
530
|
* Verifies that a `RecordsWrite` with `squash: true` is eligible:
|
|
563
531
|
* 1. The protocol rule set at the record's `protocolPath` must have `$squash: true`.
|
|
@@ -19,7 +19,6 @@ import {
|
|
|
19
19
|
verifyAsRoleRecordIfNeeded,
|
|
20
20
|
verifyImmutability,
|
|
21
21
|
verifyProtocolPathAndContextId,
|
|
22
|
-
verifyRecordLimit,
|
|
23
22
|
verifySizeLimit,
|
|
24
23
|
verifySquashEligibility,
|
|
25
24
|
verifyTagsIfNeeded,
|
|
@@ -88,9 +87,6 @@ export class ProtocolAuthorization {
|
|
|
88
87
|
// Verify squash eligibility — ensure squash writes are at $squash: true paths and are initial writes
|
|
89
88
|
await verifySquashEligibility(incomingMessage, ruleSet);
|
|
90
89
|
|
|
91
|
-
// Verify record count limit
|
|
92
|
-
await verifyRecordLimit(incomingMessage, ruleSet);
|
|
93
|
-
|
|
94
90
|
return ruleSet;
|
|
95
91
|
}
|
|
96
92
|
|
|
@@ -142,9 +138,6 @@ export class ProtocolAuthorization {
|
|
|
142
138
|
verifyTagsIfNeeded(incomingMessage, ruleSet);
|
|
143
139
|
await verifySquashEligibility(incomingMessage, ruleSet);
|
|
144
140
|
ProtocolAuthorization.verifyStoredInitialWriteCreateAction(tenant, incomingMessage, ruleSet);
|
|
145
|
-
|
|
146
|
-
// `verifyRecordLimit()` is intentionally not replayed here. It only checks strategy
|
|
147
|
-
// support for new record candidates; read-time occupancy projection decides visibility.
|
|
148
141
|
}
|
|
149
142
|
|
|
150
143
|
/**
|
|
@@ -8,7 +8,6 @@ import type { DataEncodedRecordsWriteMessage, RecordsWriteMessage } from '../typ
|
|
|
8
8
|
import type { ProtocolDefinition, ProtocolsConfigureMessage } from '../types/protocols-types.js';
|
|
9
9
|
|
|
10
10
|
import { ENCRYPTION_CONTROL_AUDIENCE_PATH } from './constants.js';
|
|
11
|
-
import { FilterUtility } from '../utils/filter.js';
|
|
12
11
|
import { PermissionGrant } from '../protocols/permission-grant.js';
|
|
13
12
|
import { PermissionsProtocol } from '../protocols/permissions.js';
|
|
14
13
|
import { RecordsWrite } from '../interfaces/records-write.js';
|
|
@@ -313,7 +312,7 @@ export class StoreValidationStateReader implements ValidationStateReader {
|
|
|
313
312
|
};
|
|
314
313
|
|
|
315
314
|
if (input.contextIdPrefix !== undefined) {
|
|
316
|
-
filter.contextId =
|
|
315
|
+
filter.contextId = { subtree: input.contextIdPrefix };
|
|
317
316
|
}
|
|
318
317
|
|
|
319
318
|
const { messages } = await this.messageStore.query(
|
|
@@ -370,7 +369,7 @@ export class StoreValidationStateReader implements ValidationStateReader {
|
|
|
370
369
|
}
|
|
371
370
|
|
|
372
371
|
if (input.contextIdPrefix !== undefined) {
|
|
373
|
-
filter.contextId =
|
|
372
|
+
filter.contextId = { subtree: input.contextIdPrefix };
|
|
374
373
|
}
|
|
375
374
|
|
|
376
375
|
return filter;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { EventLogEntry, ProgressGapInfo
|
|
1
|
+
import type { EventLogEntry, ProgressGapInfo } from '../types/subscriptions.js';
|
|
2
2
|
import type { Filter, KeyValues } from '../types/query-types.js';
|
|
3
3
|
import type { HandlerDependencies, MethodHandler } from '../types/method-handler.js';
|
|
4
4
|
import type { MessagesFilter, MessagesQueryMessage, MessagesQueryReply, MessagesQueryReplyEntry } from '../types/messages-types.js';
|
|
@@ -31,7 +31,7 @@ export class MessagesQueryHandler implements MethodHandler {
|
|
|
31
31
|
return messageReplyFromError(e, 401);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
const replicationFeedReader =
|
|
34
|
+
const replicationFeedReader = Replication.asFeedReader(this.deps.messageStore);
|
|
35
35
|
if (replicationFeedReader === undefined) {
|
|
36
36
|
return {
|
|
37
37
|
status: {
|
|
@@ -56,7 +56,7 @@ export class MessagesQueryHandler implements MethodHandler {
|
|
|
56
56
|
drained : result.drained,
|
|
57
57
|
};
|
|
58
58
|
|
|
59
|
-
const fingerprintScopes =
|
|
59
|
+
const fingerprintScopes = Messages.computeFingerprintScopes(message.descriptor.filters);
|
|
60
60
|
if (fingerprintScopes !== undefined) {
|
|
61
61
|
reply.fingerprint = await replicationFeedReader.fingerprint(tenant, fingerprintScopes);
|
|
62
62
|
}
|
|
@@ -87,18 +87,6 @@ export class MessagesQueryHandler implements MethodHandler {
|
|
|
87
87
|
});
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
private static asReplicationFeedReader(candidate: unknown): ReplicationFeedReader | undefined {
|
|
91
|
-
const partial = candidate as Partial<ReplicationFeedReader>;
|
|
92
|
-
if (
|
|
93
|
-
typeof partial.logRead === 'function' &&
|
|
94
|
-
typeof partial.logBounds === 'function' &&
|
|
95
|
-
typeof partial.fingerprint === 'function' &&
|
|
96
|
-
typeof partial.epoch === 'function'
|
|
97
|
-
) {
|
|
98
|
-
return partial as ReplicationFeedReader;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
90
|
private static convertFilters(filters: MessagesFilter[], deps: HandlerDependencies): Filter[] | undefined {
|
|
103
91
|
if (filters.length === 0) {
|
|
104
92
|
return undefined;
|
|
@@ -155,32 +143,6 @@ export class MessagesQueryHandler implements MethodHandler {
|
|
|
155
143
|
return indexes.isLatestBaseState === true || indexes.isLatestBaseState === 'true';
|
|
156
144
|
}
|
|
157
145
|
|
|
158
|
-
private static computeFingerprintScopes(filters: MessagesFilter[]): string[] | undefined {
|
|
159
|
-
if (filters.length === 0) {
|
|
160
|
-
return [Replication.globalDomain];
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
const protocols = new Set<string>();
|
|
164
|
-
for (const filter of filters) {
|
|
165
|
-
const keys = Object.keys(filter);
|
|
166
|
-
if (keys.length !== 1 || typeof filter.protocol !== 'string') {
|
|
167
|
-
return undefined;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
protocols.add(filter.protocol);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
const scopes: string[] = [];
|
|
174
|
-
for (const protocol of protocols) {
|
|
175
|
-
scopes.push(
|
|
176
|
-
Replication.protocolDomain(protocol),
|
|
177
|
-
...Replication.taggedCoreProtocolDomains(protocol, protocols),
|
|
178
|
-
);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
return scopes;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
146
|
private static getProgressGapInfo(error: DwnError): ProgressGapInfo | undefined {
|
|
185
147
|
const gapInfo = (error as DwnError & { gapInfo?: ProgressGapInfo }).gapInfo;
|
|
186
148
|
return gapInfo;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { PermissionGrant } from '../protocols/permission-grant.js';
|
|
2
|
-
import type { EventSubscription, ProgressGapInfo, SubscriptionEvent, SubscriptionListener, SubscriptionMessage } from '../types/subscriptions.js';
|
|
2
|
+
import type { EventSubscription, ProgressGapInfo, ProgressToken, ReplicationFeedReader, SubscriptionEvent, SubscriptionListener, SubscriptionMessage } from '../types/subscriptions.js';
|
|
3
3
|
import type { HandlerDependencies, MethodHandler } from '../types/method-handler.js';
|
|
4
|
-
import type { MessagesSubscribeMessage, MessagesSubscribeReply } from '../types/messages-types.js';
|
|
4
|
+
import type { MessagesFilter, MessagesSubscribeMessage, MessagesSubscribeReply } from '../types/messages-types.js';
|
|
5
5
|
|
|
6
6
|
import { authenticate } from '../core/auth.js';
|
|
7
7
|
import { Message } from '../core/message.js';
|
|
@@ -9,6 +9,7 @@ import { messageReplyFromError } from '../core/message-reply.js';
|
|
|
9
9
|
import { Messages } from '../utils/messages.js';
|
|
10
10
|
import { MessagesGrantAuthorization } from '../core/messages-grant-authorization.js';
|
|
11
11
|
import { MessagesSubscribe } from '../interfaces/messages-subscribe.js';
|
|
12
|
+
import { Replication } from '../utils/replication.js';
|
|
12
13
|
import { Time } from '../utils/time.js';
|
|
13
14
|
import { DwnError, DwnErrorCode } from '../core/dwn-error.js';
|
|
14
15
|
|
|
@@ -79,10 +80,20 @@ export class MessagesSubscribeHandler implements MethodHandler {
|
|
|
79
80
|
});
|
|
80
81
|
await guardedHandler.setSubscription(subscription);
|
|
81
82
|
|
|
82
|
-
|
|
83
|
+
const reply: MessagesSubscribeReply = {
|
|
83
84
|
status: { code: 200, detail: 'OK' },
|
|
84
85
|
subscription,
|
|
85
86
|
};
|
|
87
|
+
try {
|
|
88
|
+
await MessagesSubscribeHandler.attachFeedSnapshot(reply, tenant, filters, this.deps);
|
|
89
|
+
} catch {
|
|
90
|
+
// Best-effort enrichment: the subscription is live and correct
|
|
91
|
+
// without the snapshot — consumers feature-detect the fields. A
|
|
92
|
+
// failure reply here would orphan the just-installed listener, since
|
|
93
|
+
// close handles are registered only from successful replies.
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return reply;
|
|
86
97
|
} catch (error) {
|
|
87
98
|
if (error instanceof DwnError && error.code === DwnErrorCode.EventLogProgressGap) {
|
|
88
99
|
const gapInfo = (error as any).gapInfo as ProgressGapInfo | undefined;
|
|
@@ -95,6 +106,59 @@ export class MessagesSubscribeHandler implements MethodHandler {
|
|
|
95
106
|
}
|
|
96
107
|
}
|
|
97
108
|
|
|
109
|
+
/**
|
|
110
|
+
* Attaches the replication feed's `head` token and scope `fingerprint` to a
|
|
111
|
+
* successful subscribe reply, when the message store exposes the feed.
|
|
112
|
+
*
|
|
113
|
+
* Ordering is load-bearing: both values are observed AFTER the subscription
|
|
114
|
+
* became active, so every event past `head` is either replayable from it or
|
|
115
|
+
* already flowing on the live stream — a caller that verifies `fingerprint`
|
|
116
|
+
* against its local feed may adopt `head` as a checkpoint without a delivery
|
|
117
|
+
* gap. The head is captured before the fingerprint, mirroring MessagesQuery's
|
|
118
|
+
* cursor-then-fingerprint order: an event landing between the two captures
|
|
119
|
+
* makes the fingerprint read ahead of the head, which a consumer observes as
|
|
120
|
+
* a mismatch (one redundant reconcile), never as a silently skipped event.
|
|
121
|
+
*/
|
|
122
|
+
private static async attachFeedSnapshot(
|
|
123
|
+
reply: MessagesSubscribeReply,
|
|
124
|
+
tenant: string,
|
|
125
|
+
filters: MessagesFilter[],
|
|
126
|
+
deps: HandlerDependencies,
|
|
127
|
+
): Promise<void> {
|
|
128
|
+
const feedReader = Replication.asFeedReader(deps.messageStore);
|
|
129
|
+
if (feedReader === undefined) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Build the complete snapshot before attaching anything: a partial
|
|
134
|
+
// snapshot (a head without its fingerprint) would read to consumers as
|
|
135
|
+
// a fingerprint-less server rather than as a failed capture.
|
|
136
|
+
const bounds = await feedReader.logBounds(tenant);
|
|
137
|
+
const head = bounds?.latest ?? await MessagesSubscribeHandler.buildAnchorToken(tenant, feedReader);
|
|
138
|
+
|
|
139
|
+
const fingerprintScopes = Messages.computeFingerprintScopes(filters);
|
|
140
|
+
const fingerprint = fingerprintScopes === undefined
|
|
141
|
+
? undefined
|
|
142
|
+
: await feedReader.fingerprint(tenant, fingerprintScopes);
|
|
143
|
+
|
|
144
|
+
reply.head = head;
|
|
145
|
+
if (fingerprint !== undefined) {
|
|
146
|
+
reply.fingerprint = fingerprint;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** Builds the position-zero anchor token for a tenant log with no events. */
|
|
151
|
+
private static async buildAnchorToken(
|
|
152
|
+
tenant: string,
|
|
153
|
+
feedReader: ReplicationFeedReader,
|
|
154
|
+
): Promise<ProgressToken> {
|
|
155
|
+
return {
|
|
156
|
+
streamId : await Replication.deriveStreamId(tenant),
|
|
157
|
+
epoch : await feedReader.epoch(),
|
|
158
|
+
position : '0',
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
98
162
|
private static async authorizeMessagesSubscribe(
|
|
99
163
|
tenant: string,
|
|
100
164
|
messagesSubscribe: MessagesSubscribe,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Filter } from '../types/query-types.js';
|
|
2
|
+
import type { RecordLimitOccupancy } from '../types/message-store.js';
|
|
2
3
|
import type { HandlerDependencies, MethodHandler } from '../types/method-handler.js';
|
|
3
4
|
import type { RecordsCountMessage, RecordsCountReply } from '../types/records-types.js';
|
|
4
5
|
|
|
@@ -10,7 +11,11 @@ import { ProtocolAuthorization } from '../core/protocol-authorization.js';
|
|
|
10
11
|
import { Records } from '../utils/records.js';
|
|
11
12
|
import { RecordsCount } from '../interfaces/records-count.js';
|
|
12
13
|
import { RecordsGrantAuthorization } from '../core/records-grant-authorization.js';
|
|
13
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
countRecordsWithRecordLimitOccupancy,
|
|
16
|
+
queryRecordsWithRecordLimitOccupancy,
|
|
17
|
+
resolveRecordLimitOccupancy,
|
|
18
|
+
} from '../utils/record-limit-occupancy.js';
|
|
14
19
|
import { DwnInterfaceName, DwnMethodName } from '../enums/dwn-interface-method.js';
|
|
15
20
|
|
|
16
21
|
export class RecordsCountHandler implements MethodHandler {
|
|
@@ -71,7 +76,8 @@ export class RecordsCountHandler implements MethodHandler {
|
|
|
71
76
|
isLatestBaseState : true
|
|
72
77
|
};
|
|
73
78
|
|
|
74
|
-
|
|
79
|
+
const recordLimit = await this.resolveRecordLimit(tenant, recordsCount);
|
|
80
|
+
return this.countProjectedRecords(tenant, [countFilter], recordLimit);
|
|
75
81
|
}
|
|
76
82
|
|
|
77
83
|
private async countRecordsAsOwnerDelegate(
|
|
@@ -142,13 +148,16 @@ export class RecordsCountHandler implements MethodHandler {
|
|
|
142
148
|
return this.countProjectedRecordsForRequester(tenant, recordsCount, requester, [filter]);
|
|
143
149
|
}
|
|
144
150
|
|
|
145
|
-
private async countProjectedRecords(
|
|
151
|
+
private async countProjectedRecords(
|
|
152
|
+
tenant: string,
|
|
153
|
+
filters: Filter[],
|
|
154
|
+
recordLimit: RecordLimitOccupancy | undefined,
|
|
155
|
+
): Promise<number> {
|
|
146
156
|
const totalCount = await countRecordsWithRecordLimitOccupancy({
|
|
147
|
-
messageStore
|
|
148
|
-
validationStateReader : this.deps.validationStateReader,
|
|
157
|
+
messageStore: this.deps.messageStore,
|
|
149
158
|
tenant,
|
|
150
159
|
filters,
|
|
151
|
-
|
|
160
|
+
recordLimit,
|
|
152
161
|
});
|
|
153
162
|
|
|
154
163
|
const audienceFilters = EncryptionControl.buildAudienceRecordFilters(filters);
|
|
@@ -178,23 +187,23 @@ export class RecordsCountHandler implements MethodHandler {
|
|
|
178
187
|
requester: string | undefined,
|
|
179
188
|
filters: Filter[],
|
|
180
189
|
): Promise<number> {
|
|
190
|
+
const recordLimit = await this.resolveRecordLimit(tenant, recordsCount);
|
|
181
191
|
const controlFilters = Records.buildControlRecordsFilters(filters);
|
|
182
192
|
if (controlFilters.length === 0) {
|
|
183
|
-
return this.countProjectedRecords(tenant,
|
|
193
|
+
return this.countProjectedRecords(tenant, filters, recordLimit);
|
|
184
194
|
}
|
|
185
195
|
|
|
186
|
-
const totalCount = await this.countProjectedRecords(tenant,
|
|
187
|
-
const controlCount = await this.countProjectedRecords(tenant,
|
|
196
|
+
const totalCount = await this.countProjectedRecords(tenant, filters, recordLimit);
|
|
197
|
+
const controlCount = await this.countProjectedRecords(tenant, controlFilters, recordLimit);
|
|
188
198
|
if (controlCount === 0) {
|
|
189
199
|
return totalCount;
|
|
190
200
|
}
|
|
191
201
|
|
|
192
202
|
const { messages } = await queryRecordsWithRecordLimitOccupancy({
|
|
193
|
-
messageStore
|
|
194
|
-
validationStateReader : this.deps.validationStateReader,
|
|
203
|
+
messageStore : this.deps.messageStore,
|
|
195
204
|
tenant,
|
|
196
|
-
filters
|
|
197
|
-
|
|
205
|
+
filters : controlFilters,
|
|
206
|
+
recordLimit,
|
|
198
207
|
});
|
|
199
208
|
const projectedMessages = await EncryptionControl.projectCurrentAudienceRecords({
|
|
200
209
|
messageStore : this.deps.messageStore,
|
|
@@ -212,6 +221,18 @@ export class RecordsCountHandler implements MethodHandler {
|
|
|
212
221
|
return totalCount - controlCount + visibleMessages.length;
|
|
213
222
|
}
|
|
214
223
|
|
|
224
|
+
private async resolveRecordLimit(
|
|
225
|
+
tenant: string,
|
|
226
|
+
recordsCount: RecordsCount,
|
|
227
|
+
): Promise<RecordLimitOccupancy | undefined> {
|
|
228
|
+
return resolveRecordLimitOccupancy({
|
|
229
|
+
validationStateReader : this.deps.validationStateReader,
|
|
230
|
+
tenant,
|
|
231
|
+
recordsFilter : recordsCount.message.descriptor.filter,
|
|
232
|
+
messageTimestamp : recordsCount.message.descriptor.messageTimestamp,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
215
236
|
private static buildPublishedRecordsFilter(recordsCount: RecordsCount): Filter {
|
|
216
237
|
const { filter } = recordsCount.message.descriptor;
|
|
217
238
|
return {
|
|
@@ -10,12 +10,12 @@ import { EncryptionControl } from '../core/encryption-control.js';
|
|
|
10
10
|
import { Message } from '../core/message.js';
|
|
11
11
|
import { messageReplyFromError } from '../core/message-reply.js';
|
|
12
12
|
import { ProtocolAuthorization } from '../core/protocol-authorization.js';
|
|
13
|
-
import { queryRecordsWithRecordLimitOccupancy } from '../utils/record-limit-occupancy.js';
|
|
14
13
|
import { Records } from '../utils/records.js';
|
|
15
14
|
import { RecordsGrantAuthorization } from '../core/records-grant-authorization.js';
|
|
16
15
|
import { RecordsQuery } from '../interfaces/records-query.js';
|
|
17
16
|
import { SortDirection } from '../types/query-types.js';
|
|
18
17
|
import { DwnInterfaceName, DwnMethodName } from '../enums/dwn-interface-method.js';
|
|
18
|
+
import { queryRecordsWithRecordLimitOccupancy, resolveRecordLimitOccupancy } from '../utils/record-limit-occupancy.js';
|
|
19
19
|
|
|
20
20
|
type RecordsQueryProjectionInput = {
|
|
21
21
|
tenant: string;
|
|
@@ -254,17 +254,22 @@ export class RecordsQueryHandler implements MethodHandler {
|
|
|
254
254
|
const {
|
|
255
255
|
tenant, recordsQuery, requester, filters, messageSort, pagination
|
|
256
256
|
} = input;
|
|
257
|
+
const recordLimit = await resolveRecordLimitOccupancy({
|
|
258
|
+
validationStateReader : this.deps.validationStateReader,
|
|
259
|
+
tenant,
|
|
260
|
+
recordsFilter : recordsQuery.message.descriptor.filter,
|
|
261
|
+
messageTimestamp : recordsQuery.message.descriptor.messageTimestamp,
|
|
262
|
+
});
|
|
257
263
|
const controlFilters = Records.buildControlRecordsFilters(filters);
|
|
258
264
|
const currentAudienceRecordIdCache = new Map<string, string | undefined>();
|
|
259
265
|
if (controlFilters.length === 0) {
|
|
260
266
|
const result = await queryRecordsWithRecordLimitOccupancy({
|
|
261
|
-
messageStore
|
|
262
|
-
validationStateReader : this.deps.validationStateReader,
|
|
267
|
+
messageStore: this.deps.messageStore,
|
|
263
268
|
tenant,
|
|
264
269
|
filters,
|
|
270
|
+
recordLimit,
|
|
265
271
|
messageSort,
|
|
266
272
|
pagination,
|
|
267
|
-
messageTimestamp : recordsQuery.message.descriptor.messageTimestamp,
|
|
268
273
|
});
|
|
269
274
|
return EncryptionControl.projectCurrentAudienceRecordPage({
|
|
270
275
|
messageStore : this.deps.messageStore,
|
|
@@ -280,13 +285,12 @@ export class RecordsQueryHandler implements MethodHandler {
|
|
|
280
285
|
|
|
281
286
|
if (pagination?.limit === undefined || pagination.limit <= 0) {
|
|
282
287
|
const result = await queryRecordsWithRecordLimitOccupancy({
|
|
283
|
-
messageStore
|
|
284
|
-
validationStateReader : this.deps.validationStateReader,
|
|
288
|
+
messageStore: this.deps.messageStore,
|
|
285
289
|
tenant,
|
|
286
290
|
filters,
|
|
291
|
+
recordLimit,
|
|
287
292
|
messageSort,
|
|
288
293
|
pagination,
|
|
289
|
-
messageTimestamp : recordsQuery.message.descriptor.messageTimestamp,
|
|
290
294
|
});
|
|
291
295
|
const projectedResult = await EncryptionControl.projectCurrentAudienceRecordPage({
|
|
292
296
|
messageStore : this.deps.messageStore,
|
|
@@ -311,13 +315,12 @@ export class RecordsQueryHandler implements MethodHandler {
|
|
|
311
315
|
do {
|
|
312
316
|
const remainingLimit = pagination.limit - visibleMessages.length;
|
|
313
317
|
const result = await queryRecordsWithRecordLimitOccupancy({
|
|
314
|
-
messageStore
|
|
315
|
-
validationStateReader : this.deps.validationStateReader,
|
|
318
|
+
messageStore : this.deps.messageStore,
|
|
316
319
|
tenant,
|
|
317
320
|
filters,
|
|
321
|
+
recordLimit,
|
|
318
322
|
messageSort,
|
|
319
|
-
pagination
|
|
320
|
-
messageTimestamp : recordsQuery.message.descriptor.messageTimestamp,
|
|
323
|
+
pagination : { ...pagination, cursor, limit: remainingLimit },
|
|
321
324
|
});
|
|
322
325
|
const projectedResult = await EncryptionControl.projectCurrentAudienceRecordPage({
|
|
323
326
|
messageStore : this.deps.messageStore,
|
|
@@ -17,7 +17,11 @@ import { RecordsSubscribe } from '../interfaces/records-subscribe.js';
|
|
|
17
17
|
import { SortDirection } from '../types/query-types.js';
|
|
18
18
|
import { DwnError, DwnErrorCode } from '../core/dwn-error.js';
|
|
19
19
|
import { DwnInterfaceName, DwnMethodName } from '../enums/dwn-interface-method.js';
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
isRecordLimitOccupant,
|
|
22
|
+
queryRecordsWithRecordLimitOccupancy,
|
|
23
|
+
resolveRecordLimitOccupancy,
|
|
24
|
+
} from '../utils/record-limit-occupancy.js';
|
|
21
25
|
|
|
22
26
|
type ProjectedRecordsSubscriptionHandler = {
|
|
23
27
|
listener: SubscriptionListener;
|
|
@@ -604,17 +608,22 @@ export class RecordsSubscribeHandler implements MethodHandler {
|
|
|
604
608
|
messageSort: MessageSort,
|
|
605
609
|
pagination: { cursor?: PaginationCursor; limit?: number } | undefined,
|
|
606
610
|
): Promise<{ messages: RecordsQueryReplyEntry[], cursor?: PaginationCursor }> {
|
|
611
|
+
const recordLimit = await resolveRecordLimitOccupancy({
|
|
612
|
+
validationStateReader : this.deps.validationStateReader,
|
|
613
|
+
tenant,
|
|
614
|
+
recordsFilter : recordsSubscribe.message.descriptor.filter,
|
|
615
|
+
messageTimestamp : recordsSubscribe.message.descriptor.messageTimestamp,
|
|
616
|
+
});
|
|
607
617
|
const controlFilters = Records.buildControlRecordsFilters(filters);
|
|
608
618
|
const currentAudienceRecordIdCache = new Map<string, string | undefined>();
|
|
609
619
|
if (controlFilters.length === 0) {
|
|
610
620
|
const result = await queryRecordsWithRecordLimitOccupancy({
|
|
611
|
-
messageStore
|
|
612
|
-
validationStateReader : this.deps.validationStateReader,
|
|
621
|
+
messageStore: this.deps.messageStore,
|
|
613
622
|
tenant,
|
|
614
623
|
filters,
|
|
624
|
+
recordLimit,
|
|
615
625
|
messageSort,
|
|
616
626
|
pagination,
|
|
617
|
-
messageTimestamp : recordsSubscribe.message.descriptor.messageTimestamp,
|
|
618
627
|
});
|
|
619
628
|
return EncryptionControl.projectCurrentAudienceRecordPage({
|
|
620
629
|
messageStore: this.deps.messageStore,
|
|
@@ -627,13 +636,12 @@ export class RecordsSubscribeHandler implements MethodHandler {
|
|
|
627
636
|
|
|
628
637
|
if (pagination?.limit === undefined || pagination.limit <= 0) {
|
|
629
638
|
const result = await queryRecordsWithRecordLimitOccupancy({
|
|
630
|
-
messageStore
|
|
631
|
-
validationStateReader : this.deps.validationStateReader,
|
|
639
|
+
messageStore: this.deps.messageStore,
|
|
632
640
|
tenant,
|
|
633
641
|
filters,
|
|
642
|
+
recordLimit,
|
|
634
643
|
messageSort,
|
|
635
644
|
pagination,
|
|
636
|
-
messageTimestamp : recordsSubscribe.message.descriptor.messageTimestamp,
|
|
637
645
|
});
|
|
638
646
|
const projectedResult = await EncryptionControl.projectCurrentAudienceRecordPage({
|
|
639
647
|
messageStore: this.deps.messageStore,
|
|
@@ -655,13 +663,12 @@ export class RecordsSubscribeHandler implements MethodHandler {
|
|
|
655
663
|
do {
|
|
656
664
|
const remainingLimit = pagination.limit - visibleMessages.length;
|
|
657
665
|
const result = await queryRecordsWithRecordLimitOccupancy({
|
|
658
|
-
messageStore
|
|
659
|
-
validationStateReader : this.deps.validationStateReader,
|
|
666
|
+
messageStore : this.deps.messageStore,
|
|
660
667
|
tenant,
|
|
661
668
|
filters,
|
|
669
|
+
recordLimit,
|
|
662
670
|
messageSort,
|
|
663
|
-
pagination
|
|
664
|
-
messageTimestamp : recordsSubscribe.message.descriptor.messageTimestamp,
|
|
671
|
+
pagination : { ...pagination, cursor, limit: remainingLimit },
|
|
665
672
|
});
|
|
666
673
|
const projectedResult = await EncryptionControl.projectCurrentAudienceRecordPage({
|
|
667
674
|
messageStore: this.deps.messageStore,
|
|
@@ -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';
|