@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.
Files changed (175) hide show
  1. package/dist/browser.mjs +1 -1
  2. package/dist/browser.mjs.map +4 -4
  3. package/dist/esm/generated/precompiled-validators.js +978 -840
  4. package/dist/esm/generated/precompiled-validators.js.map +1 -1
  5. package/dist/esm/json-schemas/definitions.json +5 -0
  6. package/dist/esm/src/core/dwn-constant.js +2 -2
  7. package/dist/esm/src/core/dwn-error.js +0 -1
  8. package/dist/esm/src/core/dwn-error.js.map +1 -1
  9. package/dist/esm/src/core/protocol-authorization-validation.js +1 -24
  10. package/dist/esm/src/core/protocol-authorization-validation.js.map +1 -1
  11. package/dist/esm/src/core/protocol-authorization.js +1 -5
  12. package/dist/esm/src/core/protocol-authorization.js.map +1 -1
  13. package/dist/esm/src/core/validation-state-reader.js +2 -3
  14. package/dist/esm/src/core/validation-state-reader.js.map +1 -1
  15. package/dist/esm/src/handlers/records-collection.js +200 -0
  16. package/dist/esm/src/handlers/records-collection.js.map +1 -0
  17. package/dist/esm/src/handlers/records-count.js +26 -195
  18. package/dist/esm/src/handlers/records-count.js.map +1 -1
  19. package/dist/esm/src/handlers/records-query.js +19 -373
  20. package/dist/esm/src/handlers/records-query.js.map +1 -1
  21. package/dist/esm/src/handlers/records-subscribe.js +21 -338
  22. package/dist/esm/src/handlers/records-subscribe.js.map +1 -1
  23. package/dist/esm/src/handlers/records-write.js +8 -26
  24. package/dist/esm/src/handlers/records-write.js.map +1 -1
  25. package/dist/esm/src/index.js +1 -1
  26. package/dist/esm/src/index.js.map +1 -1
  27. package/dist/esm/src/interfaces/protocols-configure.js +4 -9
  28. package/dist/esm/src/interfaces/protocols-configure.js.map +1 -1
  29. package/dist/esm/src/interfaces/records-count.js +1 -1
  30. package/dist/esm/src/interfaces/records-count.js.map +1 -1
  31. package/dist/esm/src/interfaces/records-query.js +1 -1
  32. package/dist/esm/src/interfaces/records-query.js.map +1 -1
  33. package/dist/esm/src/interfaces/records-subscribe.js +3 -1
  34. package/dist/esm/src/interfaces/records-subscribe.js.map +1 -1
  35. package/dist/esm/src/store/index-level.js +210 -21
  36. package/dist/esm/src/store/index-level.js.map +1 -1
  37. package/dist/esm/src/store/message-store-level.js +192 -21
  38. package/dist/esm/src/store/message-store-level.js.map +1 -1
  39. package/dist/esm/src/store/storage-controller.js +1 -3
  40. package/dist/esm/src/store/storage-controller.js.map +1 -1
  41. package/dist/esm/src/types/protocols-types.js +0 -10
  42. package/dist/esm/src/types/protocols-types.js.map +1 -1
  43. package/dist/esm/src/utils/filter.js +35 -1
  44. package/dist/esm/src/utils/filter.js.map +1 -1
  45. package/dist/esm/src/utils/grant-key-coverage.js +4 -6
  46. package/dist/esm/src/utils/grant-key-coverage.js.map +1 -1
  47. package/dist/esm/src/utils/messages.js +4 -14
  48. package/dist/esm/src/utils/messages.js.map +1 -1
  49. package/dist/esm/src/utils/permission-scope.js +3 -9
  50. package/dist/esm/src/utils/permission-scope.js.map +1 -1
  51. package/dist/esm/src/utils/record-limit-occupancy.js +73 -202
  52. package/dist/esm/src/utils/record-limit-occupancy.js.map +1 -1
  53. package/dist/esm/src/utils/records.js +19 -52
  54. package/dist/esm/src/utils/records.js.map +1 -1
  55. package/dist/esm/tests/core/records-grant-authorization.spec.js +13 -0
  56. package/dist/esm/tests/core/records-grant-authorization.spec.js.map +1 -1
  57. package/dist/esm/tests/core/replication-replay-property.spec.js +2 -2
  58. package/dist/esm/tests/core/replication-replay-property.spec.js.map +1 -1
  59. package/dist/esm/tests/core/validation-read-closure.spec.js +1 -1
  60. package/dist/esm/tests/core/validation-read-closure.spec.js.map +1 -1
  61. package/dist/esm/tests/dwn.spec.js +2 -2
  62. package/dist/esm/tests/dwn.spec.js.map +1 -1
  63. package/dist/esm/tests/features/records-delivery.spec.js +2 -5
  64. package/dist/esm/tests/features/records-delivery.spec.js.map +1 -1
  65. package/dist/esm/tests/features/records-nested-query-scope.spec.js +149 -70
  66. package/dist/esm/tests/features/records-nested-query-scope.spec.js.map +1 -1
  67. package/dist/esm/tests/features/records-record-limit.spec.js +380 -95
  68. package/dist/esm/tests/features/records-record-limit.spec.js.map +1 -1
  69. package/dist/esm/tests/fuzz/filter.fuzz.spec.js +19 -4
  70. package/dist/esm/tests/fuzz/filter.fuzz.spec.js.map +1 -1
  71. package/dist/esm/tests/handlers/records-count.spec.js +52 -1
  72. package/dist/esm/tests/handlers/records-count.spec.js.map +1 -1
  73. package/dist/esm/tests/handlers/records-subscribe.spec.js +76 -1
  74. package/dist/esm/tests/handlers/records-subscribe.spec.js.map +1 -1
  75. package/dist/esm/tests/handlers/records-write.spec.js +57 -3
  76. package/dist/esm/tests/handlers/records-write.spec.js.map +1 -1
  77. package/dist/esm/tests/store/index-level.spec.js +208 -1
  78. package/dist/esm/tests/store/index-level.spec.js.map +1 -1
  79. package/dist/esm/tests/store/message-store-cross-context.spec.js +46 -0
  80. package/dist/esm/tests/store/message-store-cross-context.spec.js.map +1 -0
  81. package/dist/esm/tests/store/message-store-level.spec.js +76 -1
  82. package/dist/esm/tests/store/message-store-level.spec.js.map +1 -1
  83. package/dist/esm/tests/store/message-store.spec.js +81 -0
  84. package/dist/esm/tests/store/message-store.spec.js.map +1 -1
  85. package/dist/esm/tests/utils/filters.spec.js +25 -4
  86. package/dist/esm/tests/utils/filters.spec.js.map +1 -1
  87. package/dist/esm/tests/utils/permission-scope.spec.js +4 -0
  88. package/dist/esm/tests/utils/permission-scope.spec.js.map +1 -1
  89. package/dist/esm/tests/utils/records.spec.js +12 -0
  90. package/dist/esm/tests/utils/records.spec.js.map +1 -1
  91. package/dist/esm/tests/validation/json-schemas/records/records-write.spec.js +3 -1
  92. package/dist/esm/tests/validation/json-schemas/records/records-write.spec.js.map +1 -1
  93. package/dist/types/generated/precompiled-validators.d.ts.map +1 -1
  94. package/dist/types/src/core/dwn-constant.d.ts +2 -2
  95. package/dist/types/src/core/dwn-error.d.ts +0 -1
  96. package/dist/types/src/core/dwn-error.d.ts.map +1 -1
  97. package/dist/types/src/core/protocol-authorization-validation.d.ts +0 -10
  98. package/dist/types/src/core/protocol-authorization-validation.d.ts.map +1 -1
  99. package/dist/types/src/core/protocol-authorization.d.ts.map +1 -1
  100. package/dist/types/src/core/validation-state-reader.d.ts.map +1 -1
  101. package/dist/types/src/handlers/records-collection.d.ts +32 -0
  102. package/dist/types/src/handlers/records-collection.d.ts.map +1 -0
  103. package/dist/types/src/handlers/records-count.d.ts +0 -34
  104. package/dist/types/src/handlers/records-count.d.ts.map +1 -1
  105. package/dist/types/src/handlers/records-query.d.ts +0 -61
  106. package/dist/types/src/handlers/records-query.d.ts.map +1 -1
  107. package/dist/types/src/handlers/records-subscribe.d.ts +1 -37
  108. package/dist/types/src/handlers/records-subscribe.d.ts.map +1 -1
  109. package/dist/types/src/handlers/records-write.d.ts +3 -0
  110. package/dist/types/src/handlers/records-write.d.ts.map +1 -1
  111. package/dist/types/src/index.d.ts +3 -3
  112. package/dist/types/src/index.d.ts.map +1 -1
  113. package/dist/types/src/interfaces/protocols-configure.d.ts.map +1 -1
  114. package/dist/types/src/interfaces/records-subscribe.d.ts.map +1 -1
  115. package/dist/types/src/store/index-level.d.ts +61 -0
  116. package/dist/types/src/store/index-level.d.ts.map +1 -1
  117. package/dist/types/src/store/message-store-level.d.ts +24 -6
  118. package/dist/types/src/store/message-store-level.d.ts.map +1 -1
  119. package/dist/types/src/store/storage-controller.d.ts.map +1 -1
  120. package/dist/types/src/types/message-store.d.ts +28 -2
  121. package/dist/types/src/types/message-store.d.ts.map +1 -1
  122. package/dist/types/src/types/protocols-types.d.ts +7 -24
  123. package/dist/types/src/types/protocols-types.d.ts.map +1 -1
  124. package/dist/types/src/types/query-types.d.ts +8 -1
  125. package/dist/types/src/types/query-types.d.ts.map +1 -1
  126. package/dist/types/src/types/records-types.d.ts +3 -2
  127. package/dist/types/src/types/records-types.d.ts.map +1 -1
  128. package/dist/types/src/utils/filter.d.ts +14 -1
  129. package/dist/types/src/utils/filter.d.ts.map +1 -1
  130. package/dist/types/src/utils/grant-key-coverage.d.ts.map +1 -1
  131. package/dist/types/src/utils/messages.d.ts.map +1 -1
  132. package/dist/types/src/utils/permission-scope.d.ts +0 -2
  133. package/dist/types/src/utils/permission-scope.d.ts.map +1 -1
  134. package/dist/types/src/utils/record-limit-occupancy.d.ts +25 -12
  135. package/dist/types/src/utils/record-limit-occupancy.d.ts.map +1 -1
  136. package/dist/types/src/utils/records.d.ts +6 -24
  137. package/dist/types/src/utils/records.d.ts.map +1 -1
  138. package/dist/types/tests/features/records-delivery.spec.d.ts.map +1 -1
  139. package/dist/types/tests/features/records-nested-query-scope.spec.d.ts.map +1 -1
  140. package/dist/types/tests/features/records-record-limit.spec.d.ts.map +1 -1
  141. package/dist/types/tests/handlers/records-count.spec.d.ts.map +1 -1
  142. package/dist/types/tests/handlers/records-subscribe.spec.d.ts.map +1 -1
  143. package/dist/types/tests/handlers/records-write.spec.d.ts.map +1 -1
  144. package/dist/types/tests/store/message-store-cross-context.spec.d.ts +2 -0
  145. package/dist/types/tests/store/message-store-cross-context.spec.d.ts.map +1 -0
  146. package/dist/types/tests/store/message-store.spec.d.ts.map +1 -1
  147. package/package.json +4 -4
  148. package/src/core/dwn-constant.ts +2 -2
  149. package/src/core/dwn-error.ts +0 -2
  150. package/src/core/protocol-authorization-validation.ts +1 -33
  151. package/src/core/protocol-authorization.ts +0 -7
  152. package/src/core/validation-state-reader.ts +2 -3
  153. package/src/handlers/records-collection.ts +269 -0
  154. package/src/handlers/records-count.ts +38 -232
  155. package/src/handlers/records-query.ts +22 -444
  156. package/src/handlers/records-subscribe.ts +26 -397
  157. package/src/handlers/records-write.ts +17 -32
  158. package/src/index.ts +3 -3
  159. package/src/interfaces/protocols-configure.ts +4 -13
  160. package/src/interfaces/records-count.ts +1 -1
  161. package/src/interfaces/records-query.ts +1 -1
  162. package/src/interfaces/records-subscribe.ts +5 -2
  163. package/src/store/index-level.ts +323 -28
  164. package/src/store/message-store-level.ts +251 -25
  165. package/src/store/storage-controller.ts +1 -3
  166. package/src/types/message-store.ts +30 -2
  167. package/src/types/protocols-types.ts +7 -26
  168. package/src/types/query-types.ts +10 -2
  169. package/src/types/records-types.ts +3 -2
  170. package/src/utils/filter.ts +40 -3
  171. package/src/utils/grant-key-coverage.ts +4 -7
  172. package/src/utils/messages.ts +4 -14
  173. package/src/utils/permission-scope.ts +4 -11
  174. package/src/utils/record-limit-occupancy.ts +108 -293
  175. package/src/utils/records.ts +22 -57
@@ -1,30 +1,11 @@
1
- import type { Filter, PaginationCursor } from '../types/query-types.js';
2
- import type { GenericMessage, MessageSort } from '../types/message-types.js';
1
+ import type { RecordsCollectionVisibility } from './records-collection.js';
3
2
  import type { HandlerDependencies, MethodHandler } from '../types/method-handler.js';
4
- import type { RecordsQueryMessage, RecordsQueryReply, RecordsQueryReplyEntry } from '../types/records-types.js';
3
+ import type { RecordsQueryMessage, RecordsQueryReply } from '../types/records-types.js';
5
4
 
6
5
  import { attachInitialWrites } from '../utils/initial-write-attachment.js';
7
- import { authenticate } from '../core/auth.js';
8
- import { DateSort } from '../types/records-types.js';
9
- import { EncryptionControl } from '../core/encryption-control.js';
10
- import { Message } from '../core/message.js';
11
6
  import { messageReplyFromError } from '../core/message-reply.js';
12
- import { ProtocolAuthorization } from '../core/protocol-authorization.js';
13
- import { queryRecordsWithRecordLimitOccupancy } from '../utils/record-limit-occupancy.js';
14
- import { Records } from '../utils/records.js';
15
- import { RecordsGrantAuthorization } from '../core/records-grant-authorization.js';
16
7
  import { RecordsQuery } from '../interfaces/records-query.js';
17
- import { SortDirection } from '../types/query-types.js';
18
- import { DwnInterfaceName, DwnMethodName } from '../enums/dwn-interface-method.js';
19
-
20
- type RecordsQueryProjectionInput = {
21
- tenant: string;
22
- recordsQuery: RecordsQuery;
23
- requester: string | undefined;
24
- filters: Filter[];
25
- messageSort: MessageSort;
26
- pagination?: { cursor?: PaginationCursor; limit?: number };
27
- };
8
+ import { queryVisibleRecordsPage, resolveRecordsCollectionVisibility } from './records-collection.js';
28
9
 
29
10
  export class RecordsQueryHandler implements MethodHandler {
30
11
 
@@ -37,439 +18,36 @@ export class RecordsQueryHandler implements MethodHandler {
37
18
  let recordsQuery: RecordsQuery;
38
19
  try {
39
20
  recordsQuery = await RecordsQuery.parse(message);
40
- } catch (e) {
41
- return messageReplyFromError(e, 400);
42
- }
43
-
44
- let recordsWrites: RecordsQueryReplyEntry[];
45
- let cursor: PaginationCursor | undefined;
46
- const requester = Message.getRequester(recordsQuery.message);
47
- // if this is an anonymous query and the filter supports published records, query only published records
48
- if (Records.filterIncludesPublishedRecords(recordsQuery.message.descriptor.filter) && recordsQuery.author === undefined) {
49
- const results = await this.fetchPublishedRecords(tenant, recordsQuery, requester);
50
- recordsWrites = results.messages as RecordsQueryReplyEntry[];
51
- cursor = results.cursor;
52
- } else {
53
- // authentication and authorization
54
- try {
55
- await authenticate(message.authorization!, this.deps.didResolver);
56
-
57
- await RecordsQueryHandler.authorizeRecordsQuery(tenant, recordsQuery, this.deps);
58
- } catch (e) {
59
- return messageReplyFromError(e, 401);
60
- }
61
-
62
- if (recordsQuery.author === tenant) {
63
- const results = requester === tenant
64
- ? await this.fetchRecordsAsOwner(tenant, recordsQuery)
65
- : await this.fetchRecordsAsOwnerDelegate(tenant, recordsQuery, requester);
66
- recordsWrites = results.messages as RecordsQueryReplyEntry[];
67
- cursor = results.cursor;
68
- } else {
69
- const results = await this.fetchRecordsAsNonOwner(tenant, recordsQuery, requester);
70
- recordsWrites = results.messages as RecordsQueryReplyEntry[];
71
- cursor = results.cursor;
72
- }
73
- }
74
-
75
- // attach the retained initial write to every entry that is not itself an initial write
76
- const completeRecordsWrites = await attachInitialWrites({
77
- messageStore : this.deps.messageStore,
78
- tenant,
79
- recordsWrites,
80
- operationName : 'RecordsQuery',
81
- });
82
-
83
- return {
84
- status : { code: 200, detail: 'OK' },
85
- entries : completeRecordsWrites,
86
- cursor
87
- };
88
- }
89
-
90
- /**
91
- * Convert an incoming DateSort to a sort type accepted by MessageStore
92
- * Defaults to 'dateCreated' in Descending order if no sort is supplied.
93
- *
94
- * @param dateSort the optional DateSort from the RecordsQuery message descriptor.
95
- * @returns {MessageSort} for MessageStore sorting.
96
- */
97
- private convertDateSort(dateSort?: DateSort): MessageSort {
98
- switch (dateSort) {
99
- case DateSort.CreatedAscending:
100
- return { dateCreated: SortDirection.Ascending };
101
- case DateSort.CreatedDescending:
102
- return { dateCreated: SortDirection.Descending };
103
- case DateSort.PublishedAscending:
104
- return { datePublished: SortDirection.Ascending };
105
- case DateSort.PublishedDescending:
106
- return { datePublished: SortDirection.Descending };
107
- case DateSort.UpdatedAscending:
108
- return { messageTimestamp: SortDirection.Ascending };
109
- case DateSort.UpdatedDescending:
110
- return { messageTimestamp: SortDirection.Descending };
111
- default:
112
- return { dateCreated: SortDirection.Ascending };
113
- }
114
- }
115
-
116
- /**
117
- * Fetches the records as the owner of the DWN with no additional filtering.
118
- */
119
- private async fetchRecordsAsOwner(
120
- tenant: string,
121
- recordsQuery: RecordsQuery
122
- ): Promise<{ messages: GenericMessage[], cursor?: PaginationCursor }> {
123
- const { dateSort, filter, pagination } = recordsQuery.message.descriptor;
124
- // fetch all published records matching the query
125
- const queryFilter = {
126
- ...Records.convertFilter(filter, dateSort),
127
- interface : DwnInterfaceName.Records,
128
- method : DwnMethodName.Write,
129
- isLatestBaseState : true
130
- };
131
-
132
- const messageSort = this.convertDateSort(dateSort);
133
- return this.queryRecordsWithVisibleControlFiltering({
134
- tenant,
135
- recordsQuery,
136
- requester : tenant,
137
- filters : [queryFilter],
138
- messageSort,
139
- pagination,
140
- });
141
- }
142
-
143
- private async fetchRecordsAsOwnerDelegate(
144
- tenant: string,
145
- recordsQuery: RecordsQuery,
146
- requester: string | undefined,
147
- ): Promise<{ messages: GenericMessage[], cursor?: PaginationCursor }> {
148
- const { dateSort, filter, pagination } = recordsQuery.message.descriptor;
149
- const queryFilter = {
150
- ...Records.convertFilter(filter, dateSort),
151
- interface : DwnInterfaceName.Records,
152
- method : DwnMethodName.Write,
153
- isLatestBaseState : true
154
- };
155
-
156
- const messageSort = this.convertDateSort(dateSort);
157
- return this.queryRecordsWithVisibleControlFiltering({
158
- tenant,
159
- recordsQuery,
160
- requester,
161
- filters: [queryFilter],
162
- messageSort,
163
- pagination,
164
- });
165
- }
166
-
167
- /**
168
- * Fetches the records as a non-owner.
169
- *
170
- * Filters can support returning both published and unpublished records,
171
- * as well as explicitly only published or only unpublished records.
172
- *
173
- * A) BOTH published and unpublished:
174
- * 1. published records; and
175
- * 2. unpublished records intended for the query author (where `recipient` is the query author); and
176
- * 3. unpublished records authorized by a protocol rule.
177
- *
178
- * B) PUBLISHED:
179
- * 1. only published records;
180
- *
181
- * C) UNPUBLISHED:
182
- * 1. unpublished records intended for the query author (where `recipient` is the query author); and
183
- * 2. unpublished records authorized by a protocol rule.
184
- *
185
- */
186
- private async fetchRecordsAsNonOwner(
187
- tenant: string,
188
- recordsQuery: RecordsQuery,
189
- requester: string | undefined,
190
- ): Promise<{ messages: GenericMessage[], cursor?: PaginationCursor }> {
191
- const { dateSort, pagination, filter } = recordsQuery.message.descriptor;
192
- const filters = [];
193
- if (Records.filterIncludesPublishedRecords(filter)) {
194
- filters.push(RecordsQueryHandler.buildPublishedRecordsFilter(recordsQuery));
21
+ } catch (error) {
22
+ return messageReplyFromError(error, 400);
195
23
  }
196
24
 
197
- if (Records.filterIncludesUnpublishedRecords(filter)) {
198
- if (EncryptionControl.isExactAudienceFilter(filter)) {
199
- filters.push(Records.buildUnpublishedControlRecordsFilter(filter, dateSort));
200
- }
201
-
202
- if (Records.shouldBuildUnpublishedAuthorFilter(filter, recordsQuery.author!)) {
203
- filters.push(RecordsQueryHandler.buildUnpublishedRecordsByQueryAuthorFilter(recordsQuery));
204
- }
205
-
206
- if (Records.shouldProtocolAuthorize(recordsQuery.signaturePayload!)) {
207
- filters.push(RecordsQueryHandler.buildUnpublishedProtocolAuthorizedRecordsFilter(recordsQuery));
208
- }
209
-
210
- if (Message.getPermissionGrantId(recordsQuery.signaturePayload!) !== undefined) {
211
- filters.push(RecordsQueryHandler.buildUnpublishedPermissionGrantAuthorizedRecordsFilter(recordsQuery));
212
- }
213
-
214
- if (Records.shouldBuildUnpublishedRecipientFilter(filter, recordsQuery.author!)) {
215
- filters.push(RecordsQueryHandler.buildUnpublishedRecordsForQueryAuthorFilter(recordsQuery));
216
- }
25
+ let visibility: RecordsCollectionVisibility;
26
+ try {
27
+ visibility = await resolveRecordsCollectionVisibility(tenant, recordsQuery, this.deps);
28
+ } catch (error) {
29
+ return messageReplyFromError(error, 401);
217
30
  }
218
31
 
219
- const messageSort = this.convertDateSort(dateSort);
220
- return this.queryRecordsWithVisibleControlFiltering({
32
+ const result = await queryVisibleRecordsPage({
33
+ deps : this.deps,
221
34
  tenant,
222
- recordsQuery,
223
- requester,
224
- filters,
225
- messageSort,
226
- pagination,
35
+ request : recordsQuery,
36
+ visibility,
227
37
  });
228
- }
229
38
 
230
- /**
231
- * Fetches only published records.
232
- */
233
- private async fetchPublishedRecords(
234
- tenant: string,
235
- recordsQuery: RecordsQuery,
236
- requester: string | undefined,
237
- ): Promise<{ messages: GenericMessage[], cursor?: PaginationCursor }> {
238
- const { dateSort, pagination } = recordsQuery.message.descriptor;
239
- const filter = RecordsQueryHandler.buildPublishedRecordsFilter(recordsQuery);
240
- const messageSort = this.convertDateSort(dateSort);
241
- return this.queryRecordsWithVisibleControlFiltering({
39
+ // Attach the retained initial write to every entry that is not itself an initial write.
40
+ const entries = await attachInitialWrites({
41
+ messageStore : this.deps.messageStore,
242
42
  tenant,
243
- recordsQuery,
244
- requester,
245
- filters: [filter],
246
- messageSort,
247
- pagination,
43
+ recordsWrites : result.messages,
44
+ operationName : 'RecordsQuery',
248
45
  });
249
- }
250
-
251
- private async queryRecordsWithVisibleControlFiltering(
252
- input: RecordsQueryProjectionInput
253
- ): Promise<{ messages: RecordsQueryReplyEntry[], cursor?: PaginationCursor }> {
254
- const {
255
- tenant, recordsQuery, requester, filters, messageSort, pagination
256
- } = input;
257
- const controlFilters = Records.buildControlRecordsFilters(filters);
258
- const currentAudienceRecordIdCache = new Map<string, string | undefined>();
259
- if (controlFilters.length === 0) {
260
- const result = await queryRecordsWithRecordLimitOccupancy({
261
- messageStore : this.deps.messageStore,
262
- validationStateReader : this.deps.validationStateReader,
263
- tenant,
264
- filters,
265
- messageSort,
266
- pagination,
267
- messageTimestamp : recordsQuery.message.descriptor.messageTimestamp,
268
- });
269
- return EncryptionControl.projectCurrentAudienceRecordPage({
270
- messageStore : this.deps.messageStore,
271
- tenant,
272
- filters,
273
- currentAudienceRecordIdCache,
274
- result : {
275
- messages : result.messages as RecordsQueryReplyEntry[],
276
- cursor : result.cursor,
277
- },
278
- });
279
- }
280
-
281
- if (pagination?.limit === undefined || pagination.limit <= 0) {
282
- const result = await queryRecordsWithRecordLimitOccupancy({
283
- messageStore : this.deps.messageStore,
284
- validationStateReader : this.deps.validationStateReader,
285
- tenant,
286
- filters,
287
- messageSort,
288
- pagination,
289
- messageTimestamp : recordsQuery.message.descriptor.messageTimestamp,
290
- });
291
- const projectedResult = await EncryptionControl.projectCurrentAudienceRecordPage({
292
- messageStore : this.deps.messageStore,
293
- tenant,
294
- filters,
295
- currentAudienceRecordIdCache,
296
- result : {
297
- messages : result.messages as RecordsQueryReplyEntry[],
298
- cursor : result.cursor,
299
- },
300
- });
301
- return {
302
- messages : await this.filterControlRecordsForRequester(tenant, recordsQuery, requester, projectedResult.messages),
303
- cursor : projectedResult.cursor,
304
- };
305
- }
306
-
307
- const visibleMessages: RecordsQueryReplyEntry[] = [];
308
- let cursor = pagination.cursor;
309
- let nextCursor: PaginationCursor | undefined;
310
- // Keeps visible-page pagination stable until #1100 moves control visibility into indexed store filters.
311
- do {
312
- const remainingLimit = pagination.limit - visibleMessages.length;
313
- const result = await queryRecordsWithRecordLimitOccupancy({
314
- messageStore : this.deps.messageStore,
315
- validationStateReader : this.deps.validationStateReader,
316
- tenant,
317
- filters,
318
- messageSort,
319
- pagination : { ...pagination, cursor, limit: remainingLimit },
320
- messageTimestamp : recordsQuery.message.descriptor.messageTimestamp,
321
- });
322
- const projectedResult = await EncryptionControl.projectCurrentAudienceRecordPage({
323
- messageStore : this.deps.messageStore,
324
- tenant,
325
- filters,
326
- currentAudienceRecordIdCache,
327
- result : {
328
- messages : result.messages as RecordsQueryReplyEntry[],
329
- cursor : result.cursor,
330
- },
331
- });
332
- const filteredMessages = await this.filterControlRecordsForRequester(
333
- tenant,
334
- recordsQuery,
335
- requester,
336
- projectedResult.messages,
337
- );
338
- visibleMessages.push(...filteredMessages);
339
- nextCursor = projectedResult.cursor;
340
- cursor = projectedResult.cursor;
341
- } while (visibleMessages.length < pagination.limit && cursor !== undefined);
342
46
 
343
- return { messages: visibleMessages, cursor: nextCursor };
344
- }
345
-
346
- private static buildPublishedRecordsFilter(recordsQuery: RecordsQuery): Filter {
347
- const { dateSort, filter } = recordsQuery.message.descriptor;
348
- // fetch all published records matching the query
349
47
  return {
350
- ...Records.convertFilter(filter, dateSort),
351
- interface : DwnInterfaceName.Records,
352
- method : DwnMethodName.Write,
353
- published : true,
354
- isLatestBaseState : true
48
+ status : { code: 200, detail: 'OK' },
49
+ entries,
50
+ cursor : result.cursor,
355
51
  };
356
52
  }
357
-
358
- /**
359
- * Creates a filter for unpublished records that are intended for the query author (where `recipient` is the author).
360
- */
361
- private static buildUnpublishedRecordsForQueryAuthorFilter(recordsQuery: RecordsQuery): Filter {
362
- const { dateSort, filter } = recordsQuery.message.descriptor;
363
- // include records where recipient is query author
364
- return {
365
- ...Records.convertFilter(filter, dateSort),
366
- interface : DwnInterfaceName.Records,
367
- method : DwnMethodName.Write,
368
- recipient : recordsQuery.author!,
369
- isLatestBaseState : true,
370
- published : false
371
- };
372
- }
373
-
374
- /**
375
- * Creates a filter for unpublished records that are within the specified protocol.
376
- * Validation that `protocol` and other required protocol-related fields occurs before this method.
377
- */
378
- private static buildUnpublishedProtocolAuthorizedRecordsFilter(recordsQuery: RecordsQuery): Filter {
379
- const { dateSort, filter } = recordsQuery.message.descriptor;
380
- return {
381
- ...Records.convertFilter(filter, dateSort),
382
- interface : DwnInterfaceName.Records,
383
- method : DwnMethodName.Write,
384
- isLatestBaseState : true,
385
- published : false
386
- };
387
- }
388
-
389
- /**
390
- * Creates a filter for unpublished records authorized by a permission grant.
391
- */
392
- private static buildUnpublishedPermissionGrantAuthorizedRecordsFilter(recordsQuery: RecordsQuery): Filter {
393
- const { dateSort, filter } = recordsQuery.message.descriptor;
394
- return {
395
- ...Records.convertFilter(filter, dateSort),
396
- interface : DwnInterfaceName.Records,
397
- method : DwnMethodName.Write,
398
- isLatestBaseState : true,
399
- published : false
400
- };
401
- }
402
-
403
- /**
404
- * Creates a filter for only unpublished records where the author is the same as the query author.
405
- */
406
- private static buildUnpublishedRecordsByQueryAuthorFilter(recordsQuery: RecordsQuery): Filter {
407
- const { dateSort, filter } = recordsQuery.message.descriptor;
408
- // include records where author is the same as the query author
409
- return {
410
- ...Records.convertFilter(filter, dateSort),
411
- author : recordsQuery.author!,
412
- interface : DwnInterfaceName.Records,
413
- method : DwnMethodName.Write,
414
- isLatestBaseState : true,
415
- published : false
416
- };
417
- }
418
-
419
- /**
420
- * @param messageStore Used to check if the grant has been revoked.
421
- */
422
- private static async authorizeRecordsQuery(
423
- tenant: string,
424
- recordsQuery: RecordsQuery,
425
- deps: HandlerDependencies,
426
- ): Promise<void> {
427
-
428
- if (Message.isSignedByAuthorDelegate(recordsQuery.message) &&
429
- !EncryptionControl.filterTargetsOnlyControlRecords(recordsQuery.message.descriptor.filter)) {
430
- await recordsQuery.authorizeDelegate(deps.validationStateReader);
431
- } else if (EncryptionControl.filterTargetsOnlyControlRecords(recordsQuery.message.descriptor.filter)) {
432
- await EncryptionControl.authorizeControlReadRequest({
433
- tenant,
434
- incomingMessage : recordsQuery.message,
435
- requester : Message.getRequester(recordsQuery.message),
436
- validationStateReader : deps.validationStateReader,
437
- });
438
- }
439
-
440
- const permissionGrantId = Message.getPermissionGrantId(recordsQuery.signaturePayload!);
441
- if (permissionGrantId !== undefined) {
442
- const permissionGrant = await deps.validationStateReader.fetchGrant(tenant, permissionGrantId);
443
- await RecordsGrantAuthorization.authorizeQueryOrSubscribe({
444
- incomingMessage : recordsQuery.message,
445
- expectedGrantor : tenant,
446
- expectedGrantee : recordsQuery.author!,
447
- permissionGrant,
448
- validationStateReader : deps.validationStateReader,
449
- });
450
- return;
451
- }
452
-
453
- // NOTE: not all RecordsQuery messages require protocol authorization even if the filter includes protocol-related fields,
454
- // this is because we dynamically filter out records that the caller is not authorized to see.
455
- // Currently only run protocol authorization if message deliberately invokes a protocol role.
456
- if (Records.shouldProtocolAuthorize(recordsQuery.signaturePayload!)) {
457
- await ProtocolAuthorization.authorizeQueryOrSubscribe(tenant, recordsQuery, deps.validationStateReader);
458
- }
459
- }
460
-
461
- private async filterControlRecordsForRequester(
462
- tenant: string,
463
- recordsQuery: RecordsQuery,
464
- requester: string | undefined,
465
- recordsWrites: RecordsQueryReplyEntry[],
466
- ): Promise<RecordsQueryReplyEntry[]> {
467
- return EncryptionControl.filterVisibleControlRecords({
468
- tenant,
469
- incomingMessage : recordsQuery.message,
470
- requester,
471
- recordsWriteMessages : recordsWrites,
472
- validationStateReader : this.deps.validationStateReader,
473
- });
474
- }
475
53
  }