@enbox/dwn-sdk-js 0.3.7 → 0.3.8

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 (230) hide show
  1. package/dist/browser.mjs +8 -8
  2. package/dist/browser.mjs.map +4 -4
  3. package/dist/esm/generated/precompiled-validators.js +2591 -1435
  4. package/dist/esm/generated/precompiled-validators.js.map +1 -1
  5. package/dist/esm/src/core/constants.js +20 -0
  6. package/dist/esm/src/core/constants.js.map +1 -1
  7. package/dist/esm/src/core/dwn-error.js +24 -1
  8. package/dist/esm/src/core/dwn-error.js.map +1 -1
  9. package/dist/esm/src/core/grant-authorization.js +4 -4
  10. package/dist/esm/src/core/grant-authorization.js.map +1 -1
  11. package/dist/esm/src/core/message.js +89 -4
  12. package/dist/esm/src/core/message.js.map +1 -1
  13. package/dist/esm/src/core/messages-grant-authorization.js +147 -55
  14. package/dist/esm/src/core/messages-grant-authorization.js.map +1 -1
  15. package/dist/esm/src/core/protocol-authorization.js +76 -0
  16. package/dist/esm/src/core/protocol-authorization.js.map +1 -1
  17. package/dist/esm/src/core/records-grant-authorization.js +40 -15
  18. package/dist/esm/src/core/records-grant-authorization.js.map +1 -1
  19. package/dist/esm/src/handlers/messages-read.js +5 -5
  20. package/dist/esm/src/handlers/messages-read.js.map +1 -1
  21. package/dist/esm/src/handlers/messages-subscribe.js +109 -7
  22. package/dist/esm/src/handlers/messages-subscribe.js.map +1 -1
  23. package/dist/esm/src/handlers/messages-sync.js +341 -96
  24. package/dist/esm/src/handlers/messages-sync.js.map +1 -1
  25. package/dist/esm/src/handlers/protocols-configure.js +81 -2
  26. package/dist/esm/src/handlers/protocols-configure.js.map +1 -1
  27. package/dist/esm/src/handlers/records-count.js +30 -0
  28. package/dist/esm/src/handlers/records-count.js.map +1 -1
  29. package/dist/esm/src/handlers/records-delete.js +3 -2
  30. package/dist/esm/src/handlers/records-delete.js.map +1 -1
  31. package/dist/esm/src/handlers/records-query.js +30 -0
  32. package/dist/esm/src/handlers/records-query.js.map +1 -1
  33. package/dist/esm/src/handlers/records-read.js +3 -2
  34. package/dist/esm/src/handlers/records-read.js.map +1 -1
  35. package/dist/esm/src/handlers/records-subscribe.js +31 -0
  36. package/dist/esm/src/handlers/records-subscribe.js.map +1 -1
  37. package/dist/esm/src/handlers/records-write.js +3 -2
  38. package/dist/esm/src/handlers/records-write.js.map +1 -1
  39. package/dist/esm/src/index.js +2 -0
  40. package/dist/esm/src/index.js.map +1 -1
  41. package/dist/esm/src/interfaces/messages-read.js +6 -3
  42. package/dist/esm/src/interfaces/messages-read.js.map +1 -1
  43. package/dist/esm/src/interfaces/messages-subscribe.js +6 -3
  44. package/dist/esm/src/interfaces/messages-subscribe.js.map +1 -1
  45. package/dist/esm/src/interfaces/messages-sync.js +17 -3
  46. package/dist/esm/src/interfaces/messages-sync.js.map +1 -1
  47. package/dist/esm/src/interfaces/protocols-configure.js +5 -2
  48. package/dist/esm/src/interfaces/protocols-configure.js.map +1 -1
  49. package/dist/esm/src/interfaces/protocols-query.js +8 -4
  50. package/dist/esm/src/interfaces/protocols-query.js.map +1 -1
  51. package/dist/esm/src/interfaces/records-count.js +5 -0
  52. package/dist/esm/src/interfaces/records-count.js.map +1 -1
  53. package/dist/esm/src/interfaces/records-delete.js +6 -2
  54. package/dist/esm/src/interfaces/records-delete.js.map +1 -1
  55. package/dist/esm/src/interfaces/records-query.js +5 -0
  56. package/dist/esm/src/interfaces/records-query.js.map +1 -1
  57. package/dist/esm/src/interfaces/records-read.js +6 -3
  58. package/dist/esm/src/interfaces/records-read.js.map +1 -1
  59. package/dist/esm/src/interfaces/records-subscribe.js +5 -0
  60. package/dist/esm/src/interfaces/records-subscribe.js.map +1 -1
  61. package/dist/esm/src/interfaces/records-write.js +6 -3
  62. package/dist/esm/src/interfaces/records-write.js.map +1 -1
  63. package/dist/esm/src/protocols/permissions.js +28 -7
  64. package/dist/esm/src/protocols/permissions.js.map +1 -1
  65. package/dist/esm/src/sync/records-projection.js +228 -0
  66. package/dist/esm/src/sync/records-projection.js.map +1 -0
  67. package/dist/esm/src/types/message-types.js.map +1 -1
  68. package/dist/esm/src/types/permission-types.js.map +1 -1
  69. package/dist/esm/src/utils/permission-scope.js +37 -0
  70. package/dist/esm/src/utils/permission-scope.js.map +1 -0
  71. package/dist/esm/tests/core/grant-authorization.spec.js +26 -3
  72. package/dist/esm/tests/core/grant-authorization.spec.js.map +1 -1
  73. package/dist/esm/tests/core/records-grant-authorization.spec.js +117 -0
  74. package/dist/esm/tests/core/records-grant-authorization.spec.js.map +1 -0
  75. package/dist/esm/tests/features/permissions.spec.js +126 -0
  76. package/dist/esm/tests/features/permissions.spec.js.map +1 -1
  77. package/dist/esm/tests/handlers/messages-read.spec.js +345 -12
  78. package/dist/esm/tests/handlers/messages-read.spec.js.map +1 -1
  79. package/dist/esm/tests/handlers/messages-subscribe.spec.js +326 -9
  80. package/dist/esm/tests/handlers/messages-subscribe.spec.js.map +1 -1
  81. package/dist/esm/tests/handlers/messages-sync.spec.js +1053 -7
  82. package/dist/esm/tests/handlers/messages-sync.spec.js.map +1 -1
  83. package/dist/esm/tests/handlers/protocols-configure.spec.js +361 -0
  84. package/dist/esm/tests/handlers/protocols-configure.spec.js.map +1 -1
  85. package/dist/esm/tests/handlers/records-count.spec.js +75 -2
  86. package/dist/esm/tests/handlers/records-count.spec.js.map +1 -1
  87. package/dist/esm/tests/handlers/records-query.spec.js +73 -0
  88. package/dist/esm/tests/handlers/records-query.spec.js.map +1 -1
  89. package/dist/esm/tests/handlers/records-subscribe.spec.js +75 -1
  90. package/dist/esm/tests/handlers/records-subscribe.spec.js.map +1 -1
  91. package/dist/esm/tests/interfaces/messages-get.spec.js +107 -5
  92. package/dist/esm/tests/interfaces/messages-get.spec.js.map +1 -1
  93. package/dist/esm/tests/interfaces/protocols-configure.spec.js +13 -0
  94. package/dist/esm/tests/interfaces/protocols-configure.spec.js.map +1 -1
  95. package/dist/esm/tests/interfaces/records-delete.spec.js +12 -0
  96. package/dist/esm/tests/interfaces/records-delete.spec.js.map +1 -1
  97. package/dist/esm/tests/interfaces/records-query.spec.js +10 -0
  98. package/dist/esm/tests/interfaces/records-query.spec.js.map +1 -1
  99. package/dist/esm/tests/interfaces/records-subscribe.spec.js +10 -0
  100. package/dist/esm/tests/interfaces/records-subscribe.spec.js.map +1 -1
  101. package/dist/esm/tests/interfaces/records-write.spec.js +33 -0
  102. package/dist/esm/tests/interfaces/records-write.spec.js.map +1 -1
  103. package/dist/esm/tests/sync/records-projection.spec.js +245 -0
  104. package/dist/esm/tests/sync/records-projection.spec.js.map +1 -0
  105. package/dist/esm/tests/test-suite.js +2 -0
  106. package/dist/esm/tests/test-suite.js.map +1 -1
  107. package/dist/esm/tests/utils/permission-scope.spec.js +66 -0
  108. package/dist/esm/tests/utils/permission-scope.spec.js.map +1 -0
  109. package/dist/esm/tests/utils/test-data-generator.js +5 -2
  110. package/dist/esm/tests/utils/test-data-generator.js.map +1 -1
  111. package/dist/types/generated/precompiled-validators.d.ts.map +1 -1
  112. package/dist/types/src/core/constants.d.ts +13 -0
  113. package/dist/types/src/core/constants.d.ts.map +1 -1
  114. package/dist/types/src/core/dwn-error.d.ts +24 -1
  115. package/dist/types/src/core/dwn-error.d.ts.map +1 -1
  116. package/dist/types/src/core/grant-authorization.d.ts +1 -2
  117. package/dist/types/src/core/grant-authorization.d.ts.map +1 -1
  118. package/dist/types/src/core/message.d.ts +41 -1
  119. package/dist/types/src/core/message.d.ts.map +1 -1
  120. package/dist/types/src/core/messages-grant-authorization.d.ts +36 -4
  121. package/dist/types/src/core/messages-grant-authorization.d.ts.map +1 -1
  122. package/dist/types/src/core/protocol-authorization.d.ts +12 -0
  123. package/dist/types/src/core/protocol-authorization.d.ts.map +1 -1
  124. package/dist/types/src/core/records-grant-authorization.d.ts +6 -0
  125. package/dist/types/src/core/records-grant-authorization.d.ts.map +1 -1
  126. package/dist/types/src/handlers/messages-read.d.ts.map +1 -1
  127. package/dist/types/src/handlers/messages-subscribe.d.ts +2 -1
  128. package/dist/types/src/handlers/messages-subscribe.d.ts.map +1 -1
  129. package/dist/types/src/handlers/messages-sync.d.ts +31 -0
  130. package/dist/types/src/handlers/messages-sync.d.ts.map +1 -1
  131. package/dist/types/src/handlers/protocols-configure.d.ts +3 -0
  132. package/dist/types/src/handlers/protocols-configure.d.ts.map +1 -1
  133. package/dist/types/src/handlers/records-count.d.ts +4 -0
  134. package/dist/types/src/handlers/records-count.d.ts.map +1 -1
  135. package/dist/types/src/handlers/records-delete.d.ts.map +1 -1
  136. package/dist/types/src/handlers/records-query.d.ts +4 -0
  137. package/dist/types/src/handlers/records-query.d.ts.map +1 -1
  138. package/dist/types/src/handlers/records-read.d.ts.map +1 -1
  139. package/dist/types/src/handlers/records-subscribe.d.ts.map +1 -1
  140. package/dist/types/src/handlers/records-write.d.ts.map +1 -1
  141. package/dist/types/src/index.d.ts +6 -2
  142. package/dist/types/src/index.d.ts.map +1 -1
  143. package/dist/types/src/interfaces/messages-read.d.ts +1 -1
  144. package/dist/types/src/interfaces/messages-read.d.ts.map +1 -1
  145. package/dist/types/src/interfaces/messages-subscribe.d.ts +1 -1
  146. package/dist/types/src/interfaces/messages-subscribe.d.ts.map +1 -1
  147. package/dist/types/src/interfaces/messages-sync.d.ts +4 -1
  148. package/dist/types/src/interfaces/messages-sync.d.ts.map +1 -1
  149. package/dist/types/src/interfaces/protocols-configure.d.ts.map +1 -1
  150. package/dist/types/src/interfaces/protocols-query.d.ts.map +1 -1
  151. package/dist/types/src/interfaces/records-count.d.ts +1 -0
  152. package/dist/types/src/interfaces/records-count.d.ts.map +1 -1
  153. package/dist/types/src/interfaces/records-delete.d.ts.map +1 -1
  154. package/dist/types/src/interfaces/records-query.d.ts +1 -0
  155. package/dist/types/src/interfaces/records-query.d.ts.map +1 -1
  156. package/dist/types/src/interfaces/records-read.d.ts.map +1 -1
  157. package/dist/types/src/interfaces/records-subscribe.d.ts +1 -0
  158. package/dist/types/src/interfaces/records-subscribe.d.ts.map +1 -1
  159. package/dist/types/src/interfaces/records-write.d.ts.map +1 -1
  160. package/dist/types/src/protocols/permissions.d.ts +2 -0
  161. package/dist/types/src/protocols/permissions.d.ts.map +1 -1
  162. package/dist/types/src/sync/records-projection.d.ts +98 -0
  163. package/dist/types/src/sync/records-projection.d.ts.map +1 -0
  164. package/dist/types/src/types/message-types.d.ts +1 -0
  165. package/dist/types/src/types/message-types.d.ts.map +1 -1
  166. package/dist/types/src/types/messages-types.d.ts +21 -3
  167. package/dist/types/src/types/messages-types.d.ts.map +1 -1
  168. package/dist/types/src/types/permission-types.d.ts +4 -0
  169. package/dist/types/src/types/permission-types.d.ts.map +1 -1
  170. package/dist/types/src/types/records-types.d.ts +4 -0
  171. package/dist/types/src/types/records-types.d.ts.map +1 -1
  172. package/dist/types/src/types/subscriptions.d.ts +18 -3
  173. package/dist/types/src/types/subscriptions.d.ts.map +1 -1
  174. package/dist/types/src/utils/permission-scope.d.ts +29 -0
  175. package/dist/types/src/utils/permission-scope.d.ts.map +1 -0
  176. package/dist/types/tests/core/records-grant-authorization.spec.d.ts +2 -0
  177. package/dist/types/tests/core/records-grant-authorization.spec.d.ts.map +1 -0
  178. package/dist/types/tests/features/permissions.spec.d.ts.map +1 -1
  179. package/dist/types/tests/handlers/messages-read.spec.d.ts.map +1 -1
  180. package/dist/types/tests/handlers/messages-subscribe.spec.d.ts.map +1 -1
  181. package/dist/types/tests/handlers/messages-sync.spec.d.ts.map +1 -1
  182. package/dist/types/tests/handlers/protocols-configure.spec.d.ts.map +1 -1
  183. package/dist/types/tests/handlers/records-count.spec.d.ts.map +1 -1
  184. package/dist/types/tests/handlers/records-query.spec.d.ts.map +1 -1
  185. package/dist/types/tests/handlers/records-subscribe.spec.d.ts.map +1 -1
  186. package/dist/types/tests/sync/records-projection.spec.d.ts +2 -0
  187. package/dist/types/tests/sync/records-projection.spec.d.ts.map +1 -0
  188. package/dist/types/tests/test-suite.d.ts.map +1 -1
  189. package/dist/types/tests/utils/permission-scope.spec.d.ts +2 -0
  190. package/dist/types/tests/utils/permission-scope.spec.d.ts.map +1 -0
  191. package/dist/types/tests/utils/test-data-generator.d.ts +5 -2
  192. package/dist/types/tests/utils/test-data-generator.d.ts.map +1 -1
  193. package/package.json +1 -1
  194. package/src/core/constants.ts +24 -0
  195. package/src/core/dwn-error.ts +24 -1
  196. package/src/core/grant-authorization.ts +7 -5
  197. package/src/core/message.ts +153 -6
  198. package/src/core/messages-grant-authorization.ts +282 -70
  199. package/src/core/protocol-authorization.ts +130 -0
  200. package/src/core/records-grant-authorization.ts +64 -21
  201. package/src/handlers/messages-read.ts +7 -5
  202. package/src/handlers/messages-subscribe.ts +149 -9
  203. package/src/handlers/messages-sync.ts +593 -102
  204. package/src/handlers/protocols-configure.ts +103 -2
  205. package/src/handlers/records-count.ts +33 -0
  206. package/src/handlers/records-delete.ts +3 -2
  207. package/src/handlers/records-query.ts +33 -0
  208. package/src/handlers/records-read.ts +3 -2
  209. package/src/handlers/records-subscribe.ts +34 -0
  210. package/src/handlers/records-write.ts +3 -2
  211. package/src/index.ts +7 -3
  212. package/src/interfaces/messages-read.ts +8 -5
  213. package/src/interfaces/messages-subscribe.ts +12 -9
  214. package/src/interfaces/messages-sync.ts +33 -12
  215. package/src/interfaces/protocols-configure.ts +8 -4
  216. package/src/interfaces/protocols-query.ts +13 -9
  217. package/src/interfaces/records-count.ts +7 -0
  218. package/src/interfaces/records-delete.ts +9 -5
  219. package/src/interfaces/records-query.ts +7 -0
  220. package/src/interfaces/records-read.ts +6 -3
  221. package/src/interfaces/records-subscribe.ts +7 -0
  222. package/src/interfaces/records-write.ts +25 -17
  223. package/src/protocols/permissions.ts +47 -9
  224. package/src/sync/records-projection.ts +328 -0
  225. package/src/types/message-types.ts +1 -0
  226. package/src/types/messages-types.ts +23 -3
  227. package/src/types/permission-types.ts +5 -1
  228. package/src/types/records-types.ts +5 -1
  229. package/src/types/subscriptions.ts +19 -3
  230. package/src/utils/permission-scope.ts +55 -0
@@ -1,5 +1,6 @@
1
1
  import type { GenericMessageReply } from '../types/message-types.js';
2
2
  import type { MessageStore } from '../types//message-store.js';
3
+ import type { RecordsWriteMessage } from '../types/records-types.js';
3
4
  import type { HandlerDependencies, MethodHandler } from '../types/method-handler.js';
4
5
  import type { ProtocolDefinition, ProtocolRuleSet, ProtocolsConfigureMessage } from '../types/protocols-types.js';
5
6
 
@@ -7,12 +8,34 @@ import { authenticate } from '../core/auth.js';
7
8
  import { Message } from '../core/message.js';
8
9
  import { messageReplyFromError } from '../core/message-reply.js';
9
10
  import { PermissionsProtocol } from '../protocols/permissions.js';
11
+ import { ProtocolAuthorization } from '../core/protocol-authorization.js';
10
12
  import { ProtocolsConfigure } from '../interfaces/protocols-configure.js';
11
13
  import { ProtocolsGrantAuthorization } from '../core/protocols-grant-authorization.js';
14
+ import { RecordsWrite } from '../interfaces/records-write.js';
15
+ import { StorageController } from '../store/storage-controller.js';
12
16
  import { DwnError, DwnErrorCode } from '../core/dwn-error.js';
13
17
  import { DwnInterfaceName, DwnMethodName } from '../enums/dwn-interface-method.js';
14
18
  import { getRuleSetAtPath, parseCrossProtocolRef } from '../utils/protocols.js';
15
19
 
20
+ type StoredInitialWriteConfigValidity = 'valid' | 'invalid' | 'unknown';
21
+
22
+ const STORED_INITIAL_WRITE_CONFIG_INVALID_CODES = new Set<string>([
23
+ DwnErrorCode.ProtocolAuthorizationEncryptionRequired,
24
+ DwnErrorCode.ProtocolAuthorizationIncorrectDataFormat,
25
+ DwnErrorCode.ProtocolAuthorizationInitialWriteRevalidationNotInitial,
26
+ DwnErrorCode.ProtocolAuthorizationInvalidSchema,
27
+ DwnErrorCode.ProtocolAuthorizationInvalidType,
28
+ DwnErrorCode.ProtocolAuthorizationMaxSizeInvalid,
29
+ DwnErrorCode.ProtocolAuthorizationMinSizeInvalid,
30
+ DwnErrorCode.ProtocolAuthorizationMissingRuleSet,
31
+ DwnErrorCode.ProtocolAuthorizationSquashNotEnabled,
32
+ DwnErrorCode.ProtocolAuthorizationSquashNotInitialWrite,
33
+ DwnErrorCode.ProtocolAuthorizationStoredInitialWriteActionNotAllowed,
34
+ DwnErrorCode.ProtocolAuthorizationStoredInitialWriteActionRulesNotFound,
35
+ DwnErrorCode.ProtocolAuthorizationStoredInitialWriteRoleMissingRecipient,
36
+ DwnErrorCode.ProtocolAuthorizationTagsInvalidSchema,
37
+ ]);
38
+
16
39
  export class ProtocolsConfigureHandler implements MethodHandler {
17
40
 
18
41
  constructor(private readonly deps: HandlerDependencies) { }
@@ -115,6 +138,8 @@ export class ProtocolsConfigureHandler implements MethodHandler {
115
138
  }
116
139
  }
117
140
 
141
+ await this.purgeRecordsInvalidatedByProtocolConfig(tenant, message.descriptor.definition.protocol);
142
+
118
143
  return messageReply;
119
144
  };
120
145
 
@@ -142,8 +167,9 @@ export class ProtocolsConfigureHandler implements MethodHandler {
142
167
 
143
168
  if (protocolConfigure.author === tenant) {
144
169
  return;
145
- } else if (protocolConfigure.author !== undefined && protocolConfigure.signaturePayload!.permissionGrantId !== undefined) {
146
- const permissionGrant = await PermissionsProtocol.fetchGrant(tenant, messageStore, protocolConfigure.signaturePayload!.permissionGrantId);
170
+ } else if (protocolConfigure.author !== undefined && Message.getPermissionGrantId(protocolConfigure.signaturePayload!) !== undefined) {
171
+ const permissionGrantId = Message.getPermissionGrantId(protocolConfigure.signaturePayload!)!;
172
+ const permissionGrant = await PermissionsProtocol.fetchGrant(tenant, messageStore, permissionGrantId);
147
173
  await ProtocolsGrantAuthorization.authorizeConfigure({
148
174
  protocolsConfigureMessage : protocolConfigure.message,
149
175
  expectedGrantor : tenant,
@@ -156,6 +182,81 @@ export class ProtocolsConfigureHandler implements MethodHandler {
156
182
  }
157
183
  }
158
184
 
185
+ private async purgeRecordsInvalidatedByProtocolConfig(tenant: string, protocol: string): Promise<void> {
186
+ const dataStore = this.deps.dataStore;
187
+ const stateIndex = this.deps.stateIndex;
188
+ if (dataStore === undefined || stateIndex === undefined) {
189
+ return;
190
+ }
191
+
192
+ const { messages } = await this.deps.messageStore.query(tenant, [{
193
+ interface : DwnInterfaceName.Records,
194
+ method : DwnMethodName.Write,
195
+ protocol,
196
+ }]);
197
+
198
+ const checkedRecordIds = new Set<string>();
199
+ for (const message of messages) {
200
+ const recordsWriteMessage = message as RecordsWriteMessage;
201
+ if (checkedRecordIds.has(recordsWriteMessage.recordId)) {
202
+ continue;
203
+ }
204
+
205
+ const isInitialWrite = await RecordsWrite.isInitialWrite(recordsWriteMessage);
206
+ if (!isInitialWrite) {
207
+ continue;
208
+ }
209
+
210
+ checkedRecordIds.add(recordsWriteMessage.recordId);
211
+
212
+ const validity = await this.getStoredInitialWriteConfigValidity(tenant, recordsWriteMessage);
213
+ if (validity !== 'invalid') {
214
+ continue;
215
+ }
216
+
217
+ const { messages: recordMessages } = await this.deps.messageStore.query(tenant, [{
218
+ interface : DwnInterfaceName.Records,
219
+ recordId : recordsWriteMessage.recordId,
220
+ }]);
221
+ if (recordMessages.length === 0) {
222
+ continue;
223
+ }
224
+
225
+ // A DWN cannot synthesize a valid RecordsDelete on behalf of the record author.
226
+ // This repair therefore performs a local hard purge of only the invalid initial
227
+ // record. Descendants are evaluated independently so valid child records are not
228
+ // destroyed as collateral.
229
+ await StorageController.purgeRecordMessages(
230
+ tenant, recordMessages, this.deps.messageStore, dataStore, stateIndex
231
+ );
232
+ }
233
+ }
234
+
235
+ private async getStoredInitialWriteConfigValidity(
236
+ tenant: string,
237
+ message: RecordsWriteMessage,
238
+ ): Promise<StoredInitialWriteConfigValidity> {
239
+ try {
240
+ const recordsWrite = await RecordsWrite.parse(message);
241
+ // Stored records were authenticated when admitted. Reconciliation should not make
242
+ // record retention depend on fresh DID resolution availability or mutable dependency state.
243
+ await ProtocolAuthorization.validateStoredInitialWrite(
244
+ tenant, recordsWrite, this.deps.messageStore, this.deps.coreProtocols
245
+ );
246
+ return 'valid';
247
+ } catch (error) {
248
+ if (ProtocolsConfigureHandler.isStoredInitialWriteConfigInvalidError(error)) {
249
+ return 'invalid';
250
+ }
251
+
252
+ return 'unknown';
253
+ }
254
+ }
255
+
256
+ private static isStoredInitialWriteConfigInvalidError(error: unknown): boolean {
257
+ return error instanceof DwnError && STORED_INITIAL_WRITE_CONFIG_INVALID_CODES.has(error.code);
258
+ }
259
+
159
260
  /**
160
261
  * Validates composition dependencies at install time:
161
262
  * 1. All `uses` protocols must already be installed for the tenant.
@@ -7,9 +7,11 @@ import type { RecordsCountMessage, RecordsCountReply } from '../types/records-ty
7
7
  import { authenticate } from '../core/auth.js';
8
8
  import { Message } from '../core/message.js';
9
9
  import { messageReplyFromError } from '../core/message-reply.js';
10
+ import { PermissionsProtocol } from '../protocols/permissions.js';
10
11
  import { ProtocolAuthorization } from '../core/protocol-authorization.js';
11
12
  import { Records } from '../utils/records.js';
12
13
  import { RecordsCount } from '../interfaces/records-count.js';
14
+ import { RecordsGrantAuthorization } from '../core/records-grant-authorization.js';
13
15
  import { DwnInterfaceName, DwnMethodName } from '../enums/dwn-interface-method.js';
14
16
 
15
17
  export class RecordsCountHandler implements MethodHandler {
@@ -90,6 +92,10 @@ export class RecordsCountHandler implements MethodHandler {
90
92
  filters.push(RecordsCountHandler.buildUnpublishedProtocolAuthorizedRecordsFilter(recordsCount));
91
93
  }
92
94
 
95
+ if (Message.getPermissionGrantId(recordsCount.signaturePayload!) !== undefined) {
96
+ filters.push(RecordsCountHandler.buildUnpublishedPermissionGrantAuthorizedRecordsFilter(recordsCount));
97
+ }
98
+
93
99
  if (Records.shouldBuildUnpublishedRecipientFilter(filter, recordsCount.author!)) {
94
100
  filters.push(RecordsCountHandler.buildUnpublishedRecordsForCountAuthorFilter(recordsCount));
95
101
  }
@@ -146,6 +152,20 @@ export class RecordsCountHandler implements MethodHandler {
146
152
  };
147
153
  }
148
154
 
155
+ /**
156
+ * Creates a filter for unpublished records authorized by a permission grant.
157
+ */
158
+ private static buildUnpublishedPermissionGrantAuthorizedRecordsFilter(recordsCount: RecordsCount): Filter {
159
+ const { filter } = recordsCount.message.descriptor;
160
+ return {
161
+ ...Records.convertFilter(filter),
162
+ interface : DwnInterfaceName.Records,
163
+ method : DwnMethodName.Write,
164
+ isLatestBaseState : true,
165
+ published : false
166
+ };
167
+ }
168
+
149
169
  /**
150
170
  * Creates a filter for only unpublished records where the author is the same as the count author.
151
171
  */
@@ -175,6 +195,19 @@ export class RecordsCountHandler implements MethodHandler {
175
195
  await recordsCount.authorizeDelegate(messageStore);
176
196
  }
177
197
 
198
+ const permissionGrantId = Message.getPermissionGrantId(recordsCount.signaturePayload!);
199
+ if (permissionGrantId !== undefined) {
200
+ const permissionGrant = await PermissionsProtocol.fetchGrant(tenant, messageStore, permissionGrantId);
201
+ await RecordsGrantAuthorization.authorizeQueryOrSubscribe({
202
+ incomingMessage : recordsCount.message,
203
+ expectedGrantor : tenant,
204
+ expectedGrantee : recordsCount.author!,
205
+ permissionGrant,
206
+ messageStore,
207
+ });
208
+ return;
209
+ }
210
+
178
211
  // NOTE: not all RecordsCount messages require protocol authorization even if the filter includes protocol-related fields,
179
212
  // this is because we dynamically filter out records that the caller is not authorized to see.
180
213
  // Currently only run protocol authorization if message deliberately invokes a protocol role.
@@ -110,8 +110,9 @@ export class RecordsDeleteHandler implements MethodHandler {
110
110
 
111
111
  if (recordsDelete.author === tenant) {
112
112
  return;
113
- } else if (recordsDelete.author !== undefined && recordsDelete.signaturePayload!.permissionGrantId !== undefined) {
114
- const permissionGrant = await PermissionsProtocol.fetchGrant(tenant, messageStore, recordsDelete.signaturePayload!.permissionGrantId);
113
+ } else if (recordsDelete.author !== undefined && Message.getPermissionGrantId(recordsDelete.signaturePayload!) !== undefined) {
114
+ const permissionGrantId = Message.getPermissionGrantId(recordsDelete.signaturePayload!)!;
115
+ const permissionGrant = await PermissionsProtocol.fetchGrant(tenant, messageStore, permissionGrantId);
115
116
  await RecordsGrantAuthorization.authorizeDelete({
116
117
  recordsDeleteMessage : recordsDelete.message,
117
118
  recordsWriteToDelete : recordsWrite.message,
@@ -9,8 +9,10 @@ import { authenticate } from '../core/auth.js';
9
9
  import { DateSort } from '../types/records-types.js';
10
10
  import { Message } from '../core/message.js';
11
11
  import { messageReplyFromError } from '../core/message-reply.js';
12
+ import { PermissionsProtocol } from '../protocols/permissions.js';
12
13
  import { ProtocolAuthorization } from '../core/protocol-authorization.js';
13
14
  import { Records } from '../utils/records.js';
15
+ import { RecordsGrantAuthorization } from '../core/records-grant-authorization.js';
14
16
  import { RecordsQuery } from '../interfaces/records-query.js';
15
17
  import { RecordsWrite } from '../interfaces/records-write.js';
16
18
  import { SortDirection } from '../types/query-types.js';
@@ -162,6 +164,10 @@ export class RecordsQueryHandler implements MethodHandler {
162
164
  filters.push(RecordsQueryHandler.buildUnpublishedProtocolAuthorizedRecordsFilter(recordsQuery));
163
165
  }
164
166
 
167
+ if (Message.getPermissionGrantId(recordsQuery.signaturePayload!) !== undefined) {
168
+ filters.push(RecordsQueryHandler.buildUnpublishedPermissionGrantAuthorizedRecordsFilter(recordsQuery));
169
+ }
170
+
165
171
  if (Records.shouldBuildUnpublishedRecipientFilter(filter, recordsQuery.author!)) {
166
172
  filters.push(RecordsQueryHandler.buildUnpublishedRecordsForQueryAuthorFilter(recordsQuery));
167
173
  }
@@ -226,6 +232,20 @@ export class RecordsQueryHandler implements MethodHandler {
226
232
  };
227
233
  }
228
234
 
235
+ /**
236
+ * Creates a filter for unpublished records authorized by a permission grant.
237
+ */
238
+ private static buildUnpublishedPermissionGrantAuthorizedRecordsFilter(recordsQuery: RecordsQuery): Filter {
239
+ const { dateSort, filter } = recordsQuery.message.descriptor;
240
+ return {
241
+ ...Records.convertFilter(filter, dateSort),
242
+ interface : DwnInterfaceName.Records,
243
+ method : DwnMethodName.Write,
244
+ isLatestBaseState : true,
245
+ published : false
246
+ };
247
+ }
248
+
229
249
  /**
230
250
  * Creates a filter for only unpublished records where the author is the same as the query author.
231
251
  */
@@ -256,6 +276,19 @@ export class RecordsQueryHandler implements MethodHandler {
256
276
  await recordsQuery.authorizeDelegate(messageStore);
257
277
  }
258
278
 
279
+ const permissionGrantId = Message.getPermissionGrantId(recordsQuery.signaturePayload!);
280
+ if (permissionGrantId !== undefined) {
281
+ const permissionGrant = await PermissionsProtocol.fetchGrant(tenant, messageStore, permissionGrantId);
282
+ await RecordsGrantAuthorization.authorizeQueryOrSubscribe({
283
+ incomingMessage : recordsQuery.message,
284
+ expectedGrantor : tenant,
285
+ expectedGrantee : recordsQuery.author!,
286
+ permissionGrant,
287
+ messageStore,
288
+ });
289
+ return;
290
+ }
291
+
259
292
  // NOTE: not all RecordsQuery messages require protocol authorization even if the filter includes protocol-related fields,
260
293
  // this is because we dynamically filter out records that the caller is not authorized to see.
261
294
  // Currently only run protocol authorization if message deliberately invokes a protocol role.
@@ -182,8 +182,9 @@ export class RecordsReadHandler implements MethodHandler {
182
182
  ) {
183
183
  // The recipient or author of a message may always read it
184
184
  return;
185
- } else if (recordsRead.author !== undefined && recordsRead.signaturePayload!.permissionGrantId !== undefined) {
186
- const permissionGrant = await PermissionsProtocol.fetchGrant(tenant, messageStore, recordsRead.signaturePayload!.permissionGrantId);
185
+ } else if (recordsRead.author !== undefined && Message.getPermissionGrantId(recordsRead.signaturePayload!) !== undefined) {
186
+ const permissionGrantId = Message.getPermissionGrantId(recordsRead.signaturePayload!)!;
187
+ const permissionGrant = await PermissionsProtocol.fetchGrant(tenant, messageStore, permissionGrantId);
187
188
  await RecordsGrantAuthorization.authorizeRead({
188
189
  recordsReadMessage : recordsRead.message,
189
190
  recordsWriteMessageToBeRead : matchedRecordsWrite.message,
@@ -10,8 +10,10 @@ import { authenticate } from '../core/auth.js';
10
10
  import { DateSort } from '../types/records-types.js';
11
11
  import { Message } from '../core/message.js';
12
12
  import { messageReplyFromError } from '../core/message-reply.js';
13
+ import { PermissionsProtocol } from '../protocols/permissions.js';
13
14
  import { ProtocolAuthorization } from '../core/protocol-authorization.js';
14
15
  import { Records } from '../utils/records.js';
16
+ import { RecordsGrantAuthorization } from '../core/records-grant-authorization.js';
15
17
  import { RecordsSubscribe } from '../interfaces/records-subscribe.js';
16
18
  import { RecordsWrite } from '../interfaces/records-write.js';
17
19
  import { SortDirection } from '../types/query-types.js';
@@ -218,6 +220,15 @@ export class RecordsSubscribeHandler implements MethodHandler {
218
220
  });
219
221
  }
220
222
 
223
+ if (Message.getPermissionGrantId(recordsSubscribe.signaturePayload!) !== undefined) {
224
+ filters.push({
225
+ ...Records.convertFilter(filter),
226
+ interface : DwnInterfaceName.Records,
227
+ method : [DwnMethodName.Write, DwnMethodName.Delete],
228
+ published : false,
229
+ });
230
+ }
231
+
221
232
  if (Records.shouldBuildUnpublishedRecipientFilter(filter, recordsSubscribe.author!)) {
222
233
  filters.push({
223
234
  ...Records.convertFilter(filter),
@@ -293,6 +304,16 @@ export class RecordsSubscribeHandler implements MethodHandler {
293
304
  });
294
305
  }
295
306
 
307
+ if (Message.getPermissionGrantId(recordsSubscribe.signaturePayload!) !== undefined) {
308
+ filters.push({
309
+ ...Records.convertFilter(filter, dateSort),
310
+ interface : DwnInterfaceName.Records,
311
+ method : DwnMethodName.Write,
312
+ isLatestBaseState : true,
313
+ published : false,
314
+ });
315
+ }
316
+
296
317
  if (Records.shouldBuildUnpublishedRecipientFilter(filter, recordsSubscribe.author!)) {
297
318
  filters.push({
298
319
  ...Records.convertFilter(filter, dateSort),
@@ -335,6 +356,19 @@ export class RecordsSubscribeHandler implements MethodHandler {
335
356
  await recordsSubscribe.authorizeDelegate(messageStore);
336
357
  }
337
358
 
359
+ const permissionGrantId = Message.getPermissionGrantId(recordsSubscribe.signaturePayload!);
360
+ if (permissionGrantId !== undefined) {
361
+ const permissionGrant = await PermissionsProtocol.fetchGrant(tenant, messageStore, permissionGrantId);
362
+ await RecordsGrantAuthorization.authorizeQueryOrSubscribe({
363
+ incomingMessage : recordsSubscribe.message,
364
+ expectedGrantor : tenant,
365
+ expectedGrantee : recordsSubscribe.author!,
366
+ permissionGrant,
367
+ messageStore,
368
+ });
369
+ return;
370
+ }
371
+
338
372
  // NOTE: not all RecordsSubscribe messages require protocol authorization even if the filter includes protocol-related fields,
339
373
  // this is because we dynamically filter out records that the caller is not authorized to see.
340
374
  // Currently only run protocol authorization if message deliberately invokes a protocol role.
@@ -515,8 +515,9 @@ export class RecordsWriteHandler implements MethodHandler {
515
515
  } else if (recordsWrite.author === tenant) {
516
516
  // if author is the same as the target tenant, we can directly grant access
517
517
  return;
518
- } else if (recordsWrite.author !== undefined && recordsWrite.signaturePayload!.permissionGrantId !== undefined) {
519
- const permissionGrant = await PermissionsProtocol.fetchGrant(tenant, messageStore, recordsWrite.signaturePayload!.permissionGrantId);
518
+ } else if (recordsWrite.author !== undefined && Message.getPermissionGrantId(recordsWrite.signaturePayload!) !== undefined) {
519
+ const permissionGrantId = Message.getPermissionGrantId(recordsWrite.signaturePayload!)!;
520
+ const permissionGrant = await PermissionsProtocol.fetchGrant(tenant, messageStore, permissionGrantId);
520
521
  await RecordsGrantAuthorization.authorizeWrite({
521
522
  recordsWriteMessage : recordsWrite.message,
522
523
  expectedGrantor : tenant,
package/src/index.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  // export everything that we want to be consumable
2
2
  export type { DwnConfig } from './dwn.js';
3
- export type { EventListener, EventLog, EventLogEntry, EventLogReadOptions, EventLogReadResult, EventLogSubscribeOptions, EventSubscription, MessageEvent, ProgressGapInfo, ProgressGapReason, ProgressToken, SubscriptionEose, SubscriptionEvent, SubscriptionListener, SubscriptionMessage, SubscriptionReply } from './types/subscriptions.js';
3
+ export type { EventListener, EventLog, EventLogEntry, EventLogReadOptions, EventLogReadResult, EventLogSubscribeOptions, EventSubscription, MessageEvent, ProgressGapInfo, ProgressGapReason, ProgressToken, SubscriptionEose, SubscriptionError, SubscriptionEvent, SubscriptionListener, SubscriptionMessage, SubscriptionReply } from './types/subscriptions.js';
4
4
  export type { AuthorizationModel, Descriptor, DelegatedGrantRecordsWriteMessage, GenericMessage, GenericMessageReply, GenericSignaturePayload, MessageSort, MessageSubscription, Pagination, QueryResultEntry, Status } from './types/message-types.js';
5
- export type { MessagesFilter, MessagesReadMessage, MessagesReadReply, MessagesReadReplyEntry, MessagesReadDescriptor, MessagesSubscribeDescriptor, MessagesSubscribeMessage, MessagesSubscribeReply, MessagesSubscribeMessageOptions, MessagesSyncAction, MessagesSyncDescriptor, MessagesSyncDiffEntry, MessagesSyncMessage, MessagesSyncReply } from './types/messages-types.js';
5
+ export type { MessagesFilter, MessagesReadMessage, MessagesReadReply, MessagesReadReplyEntry, MessagesReadDescriptor, MessagesSubscribeDescriptor, MessagesSubscribeMessage, MessagesSubscribeReply, MessagesSubscribeMessageOptions, MessagesSyncAction, MessagesSyncDependencyClass, MessagesSyncDependencyEntry, MessagesSyncDescriptor, MessagesSyncDiffEntry, MessagesSyncMessage, MessagesSyncReply } from './types/messages-types.js';
6
6
  export type { GT, LT, Filter, FilterValue, KeyValues, EqualFilter, OneOfFilter, RangeFilter, RangeCriterion, PaginationCursor, QueryOptions, RangeValue, StartsWithFilter } from './types/query-types.js';
7
7
  export type { ProtocolsConfigureDescriptor, ProtocolDefinition, ProtocolTypes, ProtocolRuleSet, ProtocolsQueryFilter, ProtocolsConfigureMessage, ProtocolsQueryMessage, ProtocolsQueryReply, ProtocolActionRule, ProtocolDeliveryStrategy, ProtocolPathEncryption, ProtocolsQueryDescriptor, ProtocolRecordLimitDefinition, ProtocolSizeDefinition, ProtocolTagsDefinition, ProtocolTagSchema, ProtocolType, ProtocolUses } from './types/protocols-types.js';
8
8
  export { ProtocolRecordLimitStrategy } from './types/protocols-types.js';
@@ -51,6 +51,8 @@ export { PermissionGrant } from './protocols/permission-grant.js';
51
51
  export { PermissionRequest } from './protocols/permission-request.js';
52
52
  export { PermissionsProtocol } from './protocols/permissions.js';
53
53
  export type { PermissionGrantCreateOptions, PermissionRequestCreateOptions, PermissionRevocationCreateOptions } from './protocols/permissions.js';
54
+ export { PermissionScopeMatcher } from './utils/permission-scope.js';
55
+ export type { ProtocolScope } from './utils/permission-scope.js';
54
56
  export { PrivateKeySigner } from './utils/private-key-signer.js';
55
57
  export type { PrivateKeySignerOptions } from './utils/private-key-signer.js';
56
58
  export { Protocols, parseCrossProtocolRef, isCrossProtocolRef, getRuleSetAtPath } from './utils/protocols.js';
@@ -101,8 +103,10 @@ export { SMTStoreLevel } from './smt/smt-store-level.js';
101
103
  export { SMTStoreMemory } from './smt/smt-store-memory.js';
102
104
  export type { Hash, SMTNode, SMTInternalNode, SMTLeafNode, SMTProof, SMTDiffResult, SMTNodeStore } from './types/smt-types.js';
103
105
  export { hashChildren, hashEquals, hashKey, hashLeaf, hashToHex, hexToHash, getBit, initDefaultHashes, getDefaultHashes, SMT_DEPTH, ZERO_HASH } from './smt/smt-utils.js';
106
+ export { RECORDS_PROJECTION_ROOT_VERSION, RecordsProjection } from './sync/records-projection.js';
107
+ export type { NormalizedRecordsProjectionScope, RecordsProjectionInput, RecordsProjectionScope, RecordsProjectionSnapshot, RecordsProjectionTreeInput } from './sync/records-projection.js';
104
108
 
105
109
  // test library exports
106
110
  export type { GenerateFromRecordsWriteInput, GenerateFromRecordsWriteOut, GenerateGrantCreateInput, GenerateGrantCreateOutput, GenerateMessagesReadInput, GenerateMessagesReadOutput, GenerateMessagesSubscribeInput, GenerateMessagesSubscribeOutput, GenerateProtocolsConfigureInput, GenerateProtocolsConfigureOutput, GenerateProtocolsQueryInput, GenerateProtocolsQueryOutput, GenerateRecordsCountInput, GenerateRecordsCountOutput, GenerateRecordsDeleteInput, GenerateRecordsDeleteOutput, GenerateRecordsQueryInput, GenerateRecordsQueryOutput, GenerateRecordsSubscribeInput, GenerateRecordsSubscribeOutput, GenerateRecordsWriteInput, GenerateRecordsWriteOutput, Persona } from '../tests/utils/test-data-generator.js';
107
111
  export { TestDataGenerator } from '../tests/utils/test-data-generator.js';
108
- export { Poller } from '../tests/utils/poller.js';
112
+ export { Poller } from '../tests/utils/poller.js';
@@ -12,7 +12,7 @@ export type MessagesReadOptions = {
12
12
  messageCid: string;
13
13
  signer: MessageSigner;
14
14
  messageTimestamp?: string;
15
- permissionGrantId?: string;
15
+ permissionGrantIds?: string[];
16
16
  };
17
17
 
18
18
  export class MessagesRead extends AbstractMessage<MessagesReadMessage> {
@@ -27,19 +27,22 @@ export class MessagesRead extends AbstractMessage<MessagesReadMessage> {
27
27
  }
28
28
 
29
29
  public static async create(options: MessagesReadOptions): Promise<MessagesRead> {
30
- const { signer, permissionGrantId } = options;
30
+ const { signer } = options;
31
+ const permissionGrantInvocation = Message.normalizePermissionGrantInvocation({
32
+ permissionGrantIds: options.permissionGrantIds
33
+ });
31
34
 
32
35
  const descriptor: MessagesReadDescriptor = {
33
36
  interface : DwnInterfaceName.Messages,
34
37
  method : DwnMethodName.Read,
35
38
  messageCid : options.messageCid,
36
39
  messageTimestamp : options.messageTimestamp ?? Time.getCurrentTimestamp(),
37
- ...(permissionGrantId !== undefined && { permissionGrantId }),
40
+ ...permissionGrantInvocation,
38
41
  };
39
42
  const authorization = await Message.createAuthorization({
40
43
  descriptor,
41
44
  signer,
42
- permissionGrantId,
45
+ ...permissionGrantInvocation,
43
46
  });
44
47
  const message = { descriptor, authorization };
45
48
 
@@ -61,4 +64,4 @@ export class MessagesRead extends AbstractMessage<MessagesReadMessage> {
61
64
  throw new DwnError(DwnErrorCode.MessagesReadInvalidCid, `${messageCid} is not a valid CID`);
62
65
  }
63
66
  }
64
- }
67
+ }
@@ -15,7 +15,7 @@ export type MessagesSubscribeOptions = {
15
15
  signer: MessageSigner;
16
16
  messageTimestamp?: string;
17
17
  filters?: MessagesFilter[];
18
- permissionGrantId?: string;
18
+ permissionGrantIds?: string[];
19
19
  /**
20
20
  * Progress token to resume from. When provided, catch-up events are replayed
21
21
  * from the EventLog and an EOSE marker is delivered before live events.
@@ -47,22 +47,25 @@ export class MessagesSubscribe extends AbstractMessage<MessagesSubscribeMessage>
47
47
  options: MessagesSubscribeOptions
48
48
  ): Promise<MessagesSubscribe> {
49
49
  const currentTime = Time.getCurrentTimestamp();
50
+ const permissionGrantInvocation = Message.normalizePermissionGrantInvocation({
51
+ permissionGrantIds: options.permissionGrantIds
52
+ });
50
53
 
51
54
  const descriptor: MessagesSubscribeDescriptor = {
52
- interface : DwnInterfaceName.Messages,
53
- method : DwnMethodName.Subscribe,
54
- filters : options.filters ?? [],
55
- messageTimestamp : options.messageTimestamp ?? currentTime,
56
- permissionGrantId : options.permissionGrantId,
57
- cursor : options.cursor,
55
+ interface : DwnInterfaceName.Messages,
56
+ method : DwnMethodName.Subscribe,
57
+ filters : options.filters ?? [],
58
+ messageTimestamp : options.messageTimestamp ?? currentTime,
59
+ cursor : options.cursor,
60
+ ...permissionGrantInvocation,
58
61
  };
59
62
 
60
63
  removeUndefinedProperties(descriptor);
61
- const { permissionGrantId, signer } = options;
64
+ const { signer } = options;
62
65
  const authorization = await Message.createAuthorization({
63
66
  descriptor,
64
67
  signer,
65
- permissionGrantId
68
+ ...permissionGrantInvocation
66
69
  });
67
70
 
68
71
  const message: MessagesSubscribeMessage = { descriptor, authorization };
@@ -1,20 +1,25 @@
1
1
  import type { MessageSigner } from '../types/signer.js';
2
+ import type { RecordsProjectionScope } from '../sync/records-projection.js';
2
3
  import type { MessagesSyncAction, MessagesSyncDescriptor, MessagesSyncMessage } from '../types/messages-types.js';
3
4
 
4
5
  import { AbstractMessage } from '../core/abstract-message.js';
5
6
  import { Message } from '../core/message.js';
7
+ import { RECORDS_PROJECTION_ROOT_VERSION } from '../sync/records-projection.js';
6
8
  import { removeUndefinedProperties } from '@enbox/common';
7
9
  import { Time } from '../utils/time.js';
8
10
  import { validateProtocolUrlNormalized } from '../utils/url.js';
11
+ import { DwnError, DwnErrorCode } from '../core/dwn-error.js';
9
12
  import { DwnInterfaceName, DwnMethodName } from '../enums/dwn-interface-method.js';
10
13
 
11
14
  export type MessagesSyncOptions = {
12
15
  signer : MessageSigner;
13
16
  action : MessagesSyncAction;
14
17
  protocol? : string;
18
+ projectionRootVersion?: string;
19
+ projectionScopes?: RecordsProjectionScope[];
15
20
  prefix? : string;
16
21
  messageTimestamp? : string;
17
- permissionGrantId? : string;
22
+ permissionGrantIds? : string[];
18
23
  /** For `action: 'diff'`: client's subtree hashes at `depth`. */
19
24
  hashes? : Record<string, string>;
20
25
  /** For `action: 'diff'`: bit depth at which hashes were computed. */
@@ -30,30 +35,46 @@ export class MessagesSync extends AbstractMessage<MessagesSyncMessage> {
30
35
  if (message.descriptor.protocol !== undefined) {
31
36
  validateProtocolUrlNormalized(message.descriptor.protocol);
32
37
  }
38
+ if (message.descriptor.projectionRootVersion !== undefined &&
39
+ message.descriptor.projectionRootVersion !== RECORDS_PROJECTION_ROOT_VERSION) {
40
+ throw new DwnError(
41
+ DwnErrorCode.MessagesSyncUnsupportedProjectionRootVersion,
42
+ `Unsupported projection root version ${message.descriptor.projectionRootVersion}`
43
+ );
44
+ }
45
+ for (const scope of message.descriptor.projectionScopes ?? []) {
46
+ validateProtocolUrlNormalized(scope.protocol);
47
+ }
33
48
 
34
49
  return new MessagesSync(message);
35
50
  }
36
51
 
37
52
  public static async create(options: MessagesSyncOptions): Promise<MessagesSync> {
53
+ const permissionGrantInvocation = Message.normalizePermissionGrantInvocation({
54
+ permissionGrantIds: options.permissionGrantIds
55
+ });
56
+
38
57
  const descriptor: MessagesSyncDescriptor = {
39
- interface : DwnInterfaceName.Messages,
40
- method : DwnMethodName.Sync,
41
- messageTimestamp : options.messageTimestamp ?? Time.getCurrentTimestamp(),
42
- action : options.action,
43
- protocol : options.protocol,
44
- prefix : options.prefix,
45
- permissionGrantId : options.permissionGrantId,
46
- hashes : options.hashes,
47
- depth : options.depth,
58
+ interface : DwnInterfaceName.Messages,
59
+ method : DwnMethodName.Sync,
60
+ messageTimestamp : options.messageTimestamp ?? Time.getCurrentTimestamp(),
61
+ action : options.action,
62
+ protocol : options.protocol,
63
+ projectionRootVersion : options.projectionRootVersion,
64
+ projectionScopes : options.projectionScopes,
65
+ prefix : options.prefix,
66
+ hashes : options.hashes,
67
+ depth : options.depth,
68
+ ...permissionGrantInvocation,
48
69
  };
49
70
 
50
71
  removeUndefinedProperties(descriptor);
51
72
 
52
- const { permissionGrantId, signer } = options;
73
+ const { signer } = options;
53
74
  const authorization = await Message.createAuthorization({
54
75
  descriptor,
55
76
  signer,
56
- permissionGrantId
77
+ ...permissionGrantInvocation
57
78
  });
58
79
 
59
80
  const message = { descriptor, authorization };
@@ -40,19 +40,23 @@ export class ProtocolsConfigure extends AbstractMessage<ProtocolsConfigureMessag
40
40
  }
41
41
 
42
42
  public static async create(options: ProtocolsConfigureOptions): Promise<ProtocolsConfigure> {
43
+ const permissionGrantInvocation = Message.normalizePermissionGrantInvocation({
44
+ permissionGrantId: options.permissionGrantId,
45
+ });
46
+
43
47
  const descriptor: ProtocolsConfigureDescriptor = {
44
48
  interface : DwnInterfaceName.Protocols,
45
49
  method : DwnMethodName.Configure,
46
50
  messageTimestamp : options.messageTimestamp ?? Time.getCurrentTimestamp(),
47
51
  definition : ProtocolsConfigure.normalizeDefinition(options.definition),
48
- ...(options.permissionGrantId !== undefined && { permissionGrantId: options.permissionGrantId }),
52
+ ...permissionGrantInvocation,
49
53
  };
50
54
 
51
55
  const authorization = await Message.createAuthorization({
52
56
  descriptor,
53
- signer : options.signer,
54
- delegatedGrant : options.delegatedGrant,
55
- permissionGrantId : options.permissionGrantId
57
+ signer : options.signer,
58
+ delegatedGrant : options.delegatedGrant,
59
+ ...permissionGrantInvocation
56
60
  });
57
61
  const message = { descriptor, authorization };
58
62
 
@@ -36,13 +36,16 @@ export class ProtocolsQuery extends AbstractMessage<ProtocolsQueryMessage> {
36
36
  }
37
37
 
38
38
  public static async create(options: ProtocolsQueryOptions): Promise<ProtocolsQuery> {
39
+ const permissionGrantInvocation = Message.normalizePermissionGrantInvocation({
40
+ permissionGrantId: options.permissionGrantId,
41
+ });
39
42
 
40
43
  const descriptor: ProtocolsQueryDescriptor = {
41
- interface : DwnInterfaceName.Protocols,
42
- method : DwnMethodName.Query,
43
- messageTimestamp : options.messageTimestamp ?? Time.getCurrentTimestamp(),
44
- filter : options.filter ? ProtocolsQuery.normalizeFilter(options.filter) : undefined,
45
- permissionGrantId : options.permissionGrantId,
44
+ interface : DwnInterfaceName.Protocols,
45
+ method : DwnMethodName.Query,
46
+ messageTimestamp : options.messageTimestamp ?? Time.getCurrentTimestamp(),
47
+ filter : options.filter ? ProtocolsQuery.normalizeFilter(options.filter) : undefined,
48
+ ...permissionGrantInvocation,
46
49
  };
47
50
 
48
51
  // delete all descriptor properties that are `undefined` else the code will encounter the following IPLD issue when attempting to generate CID:
@@ -54,8 +57,8 @@ export class ProtocolsQuery extends AbstractMessage<ProtocolsQueryMessage> {
54
57
  if (options.signer !== undefined) {
55
58
  authorization = await Message.createAuthorization({
56
59
  descriptor,
57
- signer : options.signer,
58
- permissionGrantId : options.permissionGrantId
60
+ signer: options.signer,
61
+ ...permissionGrantInvocation
59
62
  });
60
63
  }
61
64
 
@@ -78,8 +81,9 @@ export class ProtocolsQuery extends AbstractMessage<ProtocolsQueryMessage> {
78
81
  // if author is the same as the target tenant, we can directly grant access
79
82
  if (this.author === tenant) {
80
83
  return;
81
- } else if (this.author !== undefined && this.signaturePayload!.permissionGrantId) {
82
- const permissionGrant = await PermissionsProtocol.fetchGrant(tenant, messageStore, this.signaturePayload!.permissionGrantId);
84
+ } else if (this.author !== undefined && Message.getPermissionGrantId(this.signaturePayload!) !== undefined) {
85
+ const permissionGrantId = Message.getPermissionGrantId(this.signaturePayload!)!;
86
+ const permissionGrant = await PermissionsProtocol.fetchGrant(tenant, messageStore, permissionGrantId);
83
87
  await ProtocolsGrantAuthorization.authorizeQuery({
84
88
  expectedGrantor : tenant,
85
89
  expectedGrantee : this.author,