@enbox/dwn-sdk-js 0.0.3 → 0.0.5

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 (134) hide show
  1. package/dist/browser.mjs +135 -0
  2. package/dist/browser.mjs.map +7 -0
  3. package/dist/esm/generated/precompiled-validators.js +640 -510
  4. package/dist/esm/generated/precompiled-validators.js.map +1 -1
  5. package/dist/esm/src/core/auth.js +6 -1
  6. package/dist/esm/src/core/auth.js.map +1 -1
  7. package/dist/esm/src/core/dwn-error.js +3 -0
  8. package/dist/esm/src/core/dwn-error.js.map +1 -1
  9. package/dist/esm/src/core/protocol-authorization.js +4 -0
  10. package/dist/esm/src/core/protocol-authorization.js.map +1 -1
  11. package/dist/esm/src/dwn.js +14 -0
  12. package/dist/esm/src/dwn.js.map +1 -1
  13. package/dist/esm/src/handlers/protocols-configure.js.map +1 -1
  14. package/dist/esm/src/handlers/records-delete.js +13 -0
  15. package/dist/esm/src/handlers/records-delete.js.map +1 -1
  16. package/dist/esm/src/handlers/records-subscribe.js +121 -66
  17. package/dist/esm/src/handlers/records-subscribe.js.map +1 -1
  18. package/dist/esm/src/handlers/records-write.js +1 -1
  19. package/dist/esm/src/handlers/records-write.js.map +1 -1
  20. package/dist/esm/src/index.js +1 -1
  21. package/dist/esm/src/index.js.map +1 -1
  22. package/dist/esm/src/interfaces/protocols-configure.js.map +1 -1
  23. package/dist/esm/src/interfaces/records-delete.js +1 -0
  24. package/dist/esm/src/interfaces/records-delete.js.map +1 -1
  25. package/dist/esm/src/interfaces/records-subscribe.js +2 -0
  26. package/dist/esm/src/interfaces/records-subscribe.js.map +1 -1
  27. package/dist/esm/src/interfaces/records-write.js +28 -45
  28. package/dist/esm/src/interfaces/records-write.js.map +1 -1
  29. package/dist/esm/src/jose/jws/general/verifier.js +9 -1
  30. package/dist/esm/src/jose/jws/general/verifier.js.map +1 -1
  31. package/dist/esm/src/smt/smt-utils.js +1 -1
  32. package/dist/esm/src/smt/smt-utils.js.map +1 -1
  33. package/dist/esm/src/types/records-types.js.map +1 -1
  34. package/dist/esm/src/utils/encryption.js +221 -78
  35. package/dist/esm/src/utils/encryption.js.map +1 -1
  36. package/dist/esm/src/utils/hd-key.js +6 -7
  37. package/dist/esm/src/utils/hd-key.js.map +1 -1
  38. package/dist/esm/src/utils/protocols.js +12 -10
  39. package/dist/esm/src/utils/protocols.js.map +1 -1
  40. package/dist/esm/src/utils/records.js +33 -44
  41. package/dist/esm/src/utils/records.js.map +1 -1
  42. package/dist/esm/tests/features/protocol-composition.spec.js +26 -21
  43. package/dist/esm/tests/features/protocol-composition.spec.js.map +1 -1
  44. package/dist/esm/tests/features/records-tags.spec.js +5 -5
  45. package/dist/esm/tests/features/records-tags.spec.js.map +1 -1
  46. package/dist/esm/tests/handlers/records-delete.spec.js +120 -2
  47. package/dist/esm/tests/handlers/records-delete.spec.js.map +1 -1
  48. package/dist/esm/tests/handlers/records-read.spec.js +25 -26
  49. package/dist/esm/tests/handlers/records-read.spec.js.map +1 -1
  50. package/dist/esm/tests/handlers/records-subscribe.spec.js +103 -0
  51. package/dist/esm/tests/handlers/records-subscribe.spec.js.map +1 -1
  52. package/dist/esm/tests/handlers/records-write.spec.js +124 -10
  53. package/dist/esm/tests/handlers/records-write.spec.js.map +1 -1
  54. package/dist/esm/tests/interfaces/messages-get.spec.js +3 -2
  55. package/dist/esm/tests/interfaces/messages-get.spec.js.map +1 -1
  56. package/dist/esm/tests/interfaces/records-write.spec.js +43 -34
  57. package/dist/esm/tests/interfaces/records-write.spec.js.map +1 -1
  58. package/dist/esm/tests/scenarios/end-to-end-tests.spec.js +4 -4
  59. package/dist/esm/tests/scenarios/end-to-end-tests.spec.js.map +1 -1
  60. package/dist/esm/tests/utils/encryption-callbacks.spec.js +21 -24
  61. package/dist/esm/tests/utils/encryption-callbacks.spec.js.map +1 -1
  62. package/dist/esm/tests/utils/encryption.spec.js +69 -66
  63. package/dist/esm/tests/utils/encryption.spec.js.map +1 -1
  64. package/dist/esm/tests/utils/filters.spec.js +1 -0
  65. package/dist/esm/tests/utils/filters.spec.js.map +1 -1
  66. package/dist/esm/tests/utils/test-data-generator.js +28 -7
  67. package/dist/esm/tests/utils/test-data-generator.js.map +1 -1
  68. package/dist/esm/tests/validation/json-schemas/protocols/protocols-configure.spec.js +1 -1
  69. package/dist/esm/tests/validation/json-schemas/protocols/protocols-configure.spec.js.map +1 -1
  70. package/dist/types/generated/precompiled-validators.d.ts.map +1 -1
  71. package/dist/types/src/core/auth.d.ts +3 -1
  72. package/dist/types/src/core/auth.d.ts.map +1 -1
  73. package/dist/types/src/core/dwn-error.d.ts +3 -0
  74. package/dist/types/src/core/dwn-error.d.ts.map +1 -1
  75. package/dist/types/src/core/protocol-authorization.d.ts.map +1 -1
  76. package/dist/types/src/dwn.d.ts +12 -0
  77. package/dist/types/src/dwn.d.ts.map +1 -1
  78. package/dist/types/src/handlers/protocols-configure.d.ts.map +1 -1
  79. package/dist/types/src/handlers/records-delete.d.ts.map +1 -1
  80. package/dist/types/src/handlers/records-subscribe.d.ts +17 -28
  81. package/dist/types/src/handlers/records-subscribe.d.ts.map +1 -1
  82. package/dist/types/src/index.d.ts +4 -4
  83. package/dist/types/src/index.d.ts.map +1 -1
  84. package/dist/types/src/interfaces/records-delete.d.ts +4 -0
  85. package/dist/types/src/interfaces/records-delete.d.ts.map +1 -1
  86. package/dist/types/src/interfaces/records-subscribe.d.ts +4 -1
  87. package/dist/types/src/interfaces/records-subscribe.d.ts.map +1 -1
  88. package/dist/types/src/interfaces/records-write.d.ts +23 -53
  89. package/dist/types/src/interfaces/records-write.d.ts.map +1 -1
  90. package/dist/types/src/jose/jws/general/verifier.d.ts.map +1 -1
  91. package/dist/types/src/types/encryption-types.d.ts +9 -8
  92. package/dist/types/src/types/encryption-types.d.ts.map +1 -1
  93. package/dist/types/src/types/protocols-types.d.ts +65 -16
  94. package/dist/types/src/types/protocols-types.d.ts.map +1 -1
  95. package/dist/types/src/types/records-types.d.ts +7 -26
  96. package/dist/types/src/types/records-types.d.ts.map +1 -1
  97. package/dist/types/src/utils/encryption.d.ts +157 -28
  98. package/dist/types/src/utils/encryption.d.ts.map +1 -1
  99. package/dist/types/src/utils/hd-key.d.ts +2 -3
  100. package/dist/types/src/utils/hd-key.d.ts.map +1 -1
  101. package/dist/types/src/utils/protocols.d.ts.map +1 -1
  102. package/dist/types/src/utils/records.d.ts +3 -4
  103. package/dist/types/src/utils/records.d.ts.map +1 -1
  104. package/dist/types/tests/features/protocol-composition.spec.d.ts.map +1 -1
  105. package/dist/types/tests/handlers/records-delete.spec.d.ts.map +1 -1
  106. package/dist/types/tests/handlers/records-read.spec.d.ts.map +1 -1
  107. package/dist/types/tests/handlers/records-subscribe.spec.d.ts.map +1 -1
  108. package/dist/types/tests/handlers/records-write.spec.d.ts.map +1 -1
  109. package/dist/types/tests/utils/test-data-generator.d.ts +7 -0
  110. package/dist/types/tests/utils/test-data-generator.d.ts.map +1 -1
  111. package/package.json +10 -21
  112. package/src/core/auth.ts +12 -1
  113. package/src/core/dwn-error.ts +3 -0
  114. package/src/core/protocol-authorization.ts +8 -0
  115. package/src/dwn.ts +15 -0
  116. package/src/handlers/protocols-configure.ts +4 -4
  117. package/src/handlers/records-delete.ts +12 -0
  118. package/src/handlers/records-subscribe.ts +174 -75
  119. package/src/handlers/records-write.ts +1 -1
  120. package/src/index.ts +4 -4
  121. package/src/interfaces/protocols-configure.ts +5 -5
  122. package/src/interfaces/records-delete.ts +9 -3
  123. package/src/interfaces/records-subscribe.ts +6 -1
  124. package/src/interfaces/records-write.ts +33 -105
  125. package/src/jose/jws/general/verifier.ts +11 -1
  126. package/src/smt/smt-utils.ts +1 -1
  127. package/src/types/encryption-types.ts +9 -8
  128. package/src/types/protocols-types.ts +72 -18
  129. package/src/types/records-types.ts +7 -29
  130. package/src/utils/encryption.ts +346 -88
  131. package/src/utils/hd-key.ts +9 -10
  132. package/src/utils/protocols.ts +15 -13
  133. package/src/utils/records.ts +47 -55
  134. package/dist/bundles/dwn.js +0 -151
@@ -4,7 +4,7 @@ import type { GenericMessageReply } from '../types/message-types.js';
4
4
  import type { MessageStore } from '../types//message-store.js';
5
5
  import type { MethodHandler } from '../types/method-handler.js';
6
6
  import type { StateIndex } from '../types/state-index.js';
7
- import type { ProtocolDefinition, ProtocolsConfigureMessage } from '../types/protocols-types.js';
7
+ import type { ProtocolDefinition, ProtocolRuleSet, ProtocolsConfigureMessage } from '../types/protocols-types.js';
8
8
 
9
9
  import { authenticate } from '../core/auth.js';
10
10
  import { Message } from '../core/message.js';
@@ -187,7 +187,7 @@ export class ProtocolsConfigureHandler implements MethodHandler {
187
187
  }
188
188
 
189
189
  // Walk the structure and validate all $ref paths and cross-protocol role references
190
- ProtocolsConfigureHandler.validateRefsAndRolesRecursively(definition.structure, '', referencedDefinitions);
190
+ ProtocolsConfigureHandler.validateRefsAndRolesRecursively(definition.structure as ProtocolRuleSet, '', referencedDefinitions);
191
191
  }
192
192
 
193
193
  /**
@@ -218,7 +218,7 @@ export class ProtocolsConfigureHandler implements MethodHandler {
218
218
  * - Cross-protocol `role` references point to valid `$role: true` paths in the referenced protocol
219
219
  */
220
220
  private static validateRefsAndRolesRecursively(
221
- ruleSet: { [key: string]: any },
221
+ ruleSet: ProtocolRuleSet,
222
222
  protocolPath: string,
223
223
  referencedDefinitions: Map<string, ProtocolDefinition>
224
224
  ): void {
@@ -227,7 +227,7 @@ export class ProtocolsConfigureHandler implements MethodHandler {
227
227
  continue;
228
228
  }
229
229
 
230
- const childRuleSet = ruleSet[key];
230
+ const childRuleSet = ruleSet[key] as ProtocolRuleSet;
231
231
  const childProtocolPath = protocolPath === '' ? key : `${protocolPath}/${key}`;
232
232
 
233
233
  // Validate $ref path exists in the referenced protocol
@@ -9,9 +9,11 @@ import { authenticate } from '../core/auth.js';
9
9
  import { DwnInterfaceName } from '../enums/dwn-interface-method.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';
14
15
  import { RecordsDelete } from '../interfaces/records-delete.js';
16
+ import { RecordsGrantAuthorization } from '../core/records-grant-authorization.js';
15
17
  import { RecordsWrite } from '../interfaces/records-write.js';
16
18
  import { ResumableTaskName } from '../core/resumable-task-manager.js';
17
19
  import { DwnError, DwnErrorCode } from '../core/dwn-error.js';
@@ -112,6 +114,16 @@ export class RecordsDeleteHandler implements MethodHandler {
112
114
 
113
115
  if (recordsDelete.author === tenant) {
114
116
  return;
117
+ } else if (recordsDelete.author !== undefined && recordsDelete.signaturePayload!.permissionGrantId !== undefined) {
118
+ const permissionGrant = await PermissionsProtocol.fetchGrant(tenant, messageStore, recordsDelete.signaturePayload!.permissionGrantId);
119
+ await RecordsGrantAuthorization.authorizeDelete({
120
+ recordsDeleteMessage : recordsDelete.message,
121
+ recordsWriteToDelete : recordsWrite.message,
122
+ expectedGrantor : tenant,
123
+ expectedGrantee : recordsDelete.author,
124
+ permissionGrant,
125
+ messageStore,
126
+ });
115
127
  } else if (recordsWrite.message.descriptor.protocol !== undefined) {
116
128
  await ProtocolAuthorization.authorizeDelete(tenant, recordsDelete, recordsWrite, messageStore);
117
129
  } else {
@@ -1,17 +1,21 @@
1
1
  import type { DidResolver } from '@enbox/dids';
2
- import type { Filter } from '../types/query-types.js';
2
+ import type { MessageSort } from '../types/message-types.js';
3
3
  import type { MessageStore } from '../types//message-store.js';
4
4
  import type { MethodHandler } from '../types/method-handler.js';
5
5
  import type { EventListener, EventStream } from '../types/subscriptions.js';
6
- import type { RecordEvent, RecordsSubscribeMessage, RecordsSubscribeReply, RecordSubscriptionHandler } from '../types/records-types.js';
6
+ import type { Filter, PaginationCursor } from '../types/query-types.js';
7
+ import type { RecordEvent, RecordsQueryReplyEntry, RecordsSubscribeMessage, RecordsSubscribeReply, RecordSubscriptionHandler } from '../types/records-types.js';
7
8
 
8
9
  import { authenticate } from '../core/auth.js';
10
+ import { DateSort } from '../types/records-types.js';
9
11
  import { FilterUtility } from '../utils/filter.js';
10
12
  import { Message } from '../core/message.js';
11
13
  import { messageReplyFromError } from '../core/message-reply.js';
12
14
  import { ProtocolAuthorization } from '../core/protocol-authorization.js';
13
15
  import { Records } from '../utils/records.js';
14
16
  import { RecordsSubscribe } from '../interfaces/records-subscribe.js';
17
+ import { RecordsWrite } from '../interfaces/records-write.js';
18
+ import { SortDirection } from '../types/query-types.js';
15
19
  import { DwnError, DwnErrorCode } from '../core/dwn-error.js';
16
20
  import { DwnInterfaceName, DwnMethodName } from '../enums/dwn-interface-method.js';
17
21
 
@@ -42,11 +46,13 @@ export class RecordsSubscribeHandler implements MethodHandler {
42
46
  return messageReplyFromError(e, 400);
43
47
  }
44
48
 
45
- let filters:Filter[] = [];
49
+ let eventFilters: Filter[] = [];
50
+ let queryFilters: Filter[] = [];
51
+
46
52
  // if this is an anonymous subscribe and the filter supports published records, subscribe to only published records
47
53
  if (Records.filterIncludesPublishedRecords(recordsSubscribe.message.descriptor.filter) && recordsSubscribe.author === undefined) {
48
- // build filters for a stream of published records
49
- filters = [ RecordsSubscribeHandler.buildPublishedRecordsFilter(recordsSubscribe) ];
54
+ eventFilters = [RecordsSubscribeHandler.buildPublishedEventFilter(recordsSubscribe)];
55
+ queryFilters = [RecordsSubscribeHandler.buildPublishedQueryFilter(recordsSubscribe)];
50
56
  // delete the undefined authorization property else the code will encounter the following IPLD issue when attempting to generate CID:
51
57
  // Error: `undefined` is not supported by the IPLD Data Model and cannot be encoded
52
58
  delete message.authorization;
@@ -60,138 +66,231 @@ export class RecordsSubscribeHandler implements MethodHandler {
60
66
  }
61
67
 
62
68
  if (recordsSubscribe.author === tenant) {
63
- // if the subscribe author is the tenant, filter as owner.
64
- filters = await RecordsSubscribeHandler.filterAsOwner(recordsSubscribe);
69
+ eventFilters = RecordsSubscribeHandler.buildOwnerEventFilters(recordsSubscribe);
70
+ queryFilters = RecordsSubscribeHandler.buildOwnerQueryFilters(recordsSubscribe);
65
71
  } else {
66
- // otherwise build filters based on published records, permissions, or protocol rules
67
- filters = await RecordsSubscribeHandler.filterAsNonOwner(recordsSubscribe);
72
+ eventFilters = RecordsSubscribeHandler.buildNonOwnerEventFilters(recordsSubscribe);
73
+ queryFilters = RecordsSubscribeHandler.buildNonOwnerQueryFilters(recordsSubscribe);
68
74
  }
69
75
  }
70
76
 
77
+ // Step 1: Register event listener FIRST to ensure no events are missed between query and subscribe
71
78
  const listener: EventListener = (eventTenant, event, eventIndexes):void => {
72
- if (tenant === eventTenant && FilterUtility.matchAnyFilter(eventIndexes, filters)) {
73
- // the filters check for interface and method
74
- // if matched the message is either a `RecordsWriteMessage` or `RecordsDeleteMessage` so we cast the event to a `RecordEvent`
79
+ if (tenant === eventTenant && FilterUtility.matchAnyFilter(eventIndexes, eventFilters)) {
75
80
  subscriptionHandler(event as RecordEvent);
76
81
  }
77
82
  };
78
83
 
79
84
  const messageCid = await Message.getCid(message);
80
85
  const subscription = await this.eventStream.subscribe(tenant, messageCid, listener);
86
+
87
+ // Step 2: Query for initial snapshot of matching records
88
+ let entries: RecordsQueryReplyEntry[];
89
+ let cursor: PaginationCursor | undefined;
90
+ try {
91
+ const { dateSort, pagination } = recordsSubscribe.message.descriptor;
92
+ const messageSort = RecordsSubscribeHandler.convertDateSort(dateSort);
93
+ const queryResult = await this.messageStore.query(tenant, queryFilters, messageSort, pagination);
94
+ entries = queryResult.messages as RecordsQueryReplyEntry[];
95
+ cursor = queryResult.cursor;
96
+
97
+ // attach initialWrite for non-initial writes
98
+ for (const entry of entries) {
99
+ if (!await RecordsWrite.isInitialWrite(entry)) {
100
+ const initialWriteResult = await this.messageStore.query(
101
+ tenant,
102
+ [{ recordId: entry.recordId, isLatestBaseState: false, method: DwnMethodName.Write }]
103
+ );
104
+ const initialWrite = initialWriteResult.messages[0] as RecordsQueryReplyEntry;
105
+ delete initialWrite.encodedData;
106
+ entry.initialWrite = initialWrite;
107
+ }
108
+ }
109
+ } catch (error) {
110
+ // if the query fails, close the subscription and return the error
111
+ await subscription.close();
112
+ return messageReplyFromError(error, 500);
113
+ }
114
+
115
+ // Step 3: Return subscription + initial entries + cursor
81
116
  return {
82
117
  status: { code: 200, detail: 'OK' },
83
- subscription
118
+ subscription,
119
+ entries,
120
+ cursor
84
121
  };
85
122
  }
86
123
 
87
124
  /**
88
- * Subscribe to records as the owner of the DWN with no additional filtering.
125
+ * Convert an incoming DateSort to a sort type accepted by MessageStore.
126
+ * Defaults to `dateCreated` ascending if no sort is supplied.
89
127
  */
90
- private static async filterAsOwner(RecordsSubscribe: RecordsSubscribe): Promise<Filter[]> {
91
- const { filter } = RecordsSubscribe.message.descriptor;
128
+ private static convertDateSort(dateSort?: DateSort): MessageSort {
129
+ switch (dateSort) {
130
+ case DateSort.CreatedAscending:
131
+ return { dateCreated: SortDirection.Ascending };
132
+ case DateSort.CreatedDescending:
133
+ return { dateCreated: SortDirection.Descending };
134
+ case DateSort.PublishedAscending:
135
+ return { datePublished: SortDirection.Ascending };
136
+ case DateSort.PublishedDescending:
137
+ return { datePublished: SortDirection.Descending };
138
+ case DateSort.UpdatedAscending:
139
+ return { messageTimestamp: SortDirection.Ascending };
140
+ case DateSort.UpdatedDescending:
141
+ return { messageTimestamp: SortDirection.Descending };
142
+ default:
143
+ return { dateCreated: SortDirection.Ascending };
144
+ }
145
+ }
146
+
147
+ // =============================================
148
+ // Event filters (for live subscription)
149
+ // These match Write+Delete and do NOT use isLatestBaseState
150
+ // =============================================
92
151
 
93
- const subscribeFilter = {
152
+ /**
153
+ * Build event filters for owner: all matching Write+Delete events.
154
+ */
155
+ private static buildOwnerEventFilters(recordsSubscribe: RecordsSubscribe): Filter[] {
156
+ const { filter } = recordsSubscribe.message.descriptor;
157
+ return [{
94
158
  ...Records.convertFilter(filter),
95
159
  interface : DwnInterfaceName.Records,
96
- method : [ DwnMethodName.Write, DwnMethodName.Delete ], // we filter for both write and delete so that subscriber can update state.
97
- };
98
-
99
- return [ subscribeFilter ];
160
+ method : [DwnMethodName.Write, DwnMethodName.Delete],
161
+ }];
100
162
  }
101
163
 
102
164
  /**
103
- * Creates filters in order to subscribe to records as a non-owner.
104
- *
105
- * Filters can support emitting messages for both published and unpublished records,
106
- * as well as explicitly only published or only unpublished records.
107
- *
108
- * A) BOTH published and unpublished:
109
- * 1. published records; and
110
- * 2. unpublished records intended for the subscription author (where `recipient` is the subscription author); and
111
- * 3. unpublished records authorized by a protocol rule.
112
- *
113
- * B) PUBLISHED:
114
- * 1. only published records;
115
- *
116
- * C) UNPUBLISHED:
117
- * 1. unpublished records intended for the subscription author (where `recipient` is the subscription author); and
118
- * 2. unpublished records authorized by a protocol rule.
165
+ * Build event filters for non-owner with visibility rules.
119
166
  */
120
- private static async filterAsNonOwner(
121
- recordsSubscribe: RecordsSubscribe
122
- ): Promise<Filter[]> {
123
- const filters:Filter[] = [];
167
+ private static buildNonOwnerEventFilters(recordsSubscribe: RecordsSubscribe): Filter[] {
168
+ const filters: Filter[] = [];
124
169
  const { filter } = recordsSubscribe.message.descriptor;
125
170
  if (Records.filterIncludesPublishedRecords(filter)) {
126
- filters.push(RecordsSubscribeHandler.buildPublishedRecordsFilter(recordsSubscribe));
171
+ filters.push(RecordsSubscribeHandler.buildPublishedEventFilter(recordsSubscribe));
127
172
  }
128
173
 
129
174
  if (Records.filterIncludesUnpublishedRecords(filter)) {
130
175
  if (Records.shouldBuildUnpublishedAuthorFilter(filter, recordsSubscribe.author!)) {
131
- filters.push(RecordsSubscribeHandler.buildUnpublishedRecordsBySubscribeAuthorFilter(recordsSubscribe));
176
+ filters.push({
177
+ ...Records.convertFilter(filter),
178
+ author : recordsSubscribe.author!,
179
+ interface : DwnInterfaceName.Records,
180
+ method : [DwnMethodName.Write, DwnMethodName.Delete],
181
+ published : false,
182
+ });
132
183
  }
133
184
 
134
185
  if (Records.shouldProtocolAuthorize(recordsSubscribe.signaturePayload!)) {
135
- filters.push(RecordsSubscribeHandler.buildUnpublishedProtocolAuthorizedRecordsFilter(recordsSubscribe));
186
+ filters.push({
187
+ ...Records.convertFilter(filter),
188
+ interface : DwnInterfaceName.Records,
189
+ method : [DwnMethodName.Write, DwnMethodName.Delete],
190
+ published : false,
191
+ });
136
192
  }
137
193
 
138
194
  if (Records.shouldBuildUnpublishedRecipientFilter(filter, recordsSubscribe.author!)) {
139
- filters.push(RecordsSubscribeHandler.buildUnpublishedRecordsForSubscribeAuthorFilter(recordsSubscribe));
195
+ filters.push({
196
+ ...Records.convertFilter(filter),
197
+ interface : DwnInterfaceName.Records,
198
+ method : [DwnMethodName.Write, DwnMethodName.Delete],
199
+ recipient : recordsSubscribe.author!,
200
+ published : false,
201
+ });
140
202
  }
141
203
  }
142
204
  return filters;
143
205
  }
144
206
 
145
207
  /**
146
- * Creates a filter for all published records matching the subscribe
208
+ * Build a published-only event filter (Write+Delete).
147
209
  */
148
- private static buildPublishedRecordsFilter(recordsSubscribe: RecordsSubscribe): Filter {
210
+ private static buildPublishedEventFilter(recordsSubscribe: RecordsSubscribe): Filter {
149
211
  return {
150
212
  ...Records.convertFilter(recordsSubscribe.message.descriptor.filter),
151
213
  interface : DwnInterfaceName.Records,
152
- method : [ DwnMethodName.Write, DwnMethodName.Delete ],
214
+ method : [DwnMethodName.Write, DwnMethodName.Delete],
153
215
  published : true,
154
216
  };
155
217
  }
156
218
 
219
+ // =============================================
220
+ // Query filters (for initial snapshot)
221
+ // These match Write only and use isLatestBaseState: true
222
+ // =============================================
223
+
157
224
  /**
158
- * Creates a filter for unpublished records that are intended for the subscribe author (where `recipient` is the author).
225
+ * Build query filters for owner: latest writes matching the filter.
159
226
  */
160
- private static buildUnpublishedRecordsForSubscribeAuthorFilter(recordsSubscribe: RecordsSubscribe): Filter {
161
- // include records where recipient is subscribe author
162
- return {
163
- ...Records.convertFilter(recordsSubscribe.message.descriptor.filter),
164
- interface : DwnInterfaceName.Records,
165
- method : [ DwnMethodName.Write, DwnMethodName.Delete ],
166
- recipient : recordsSubscribe.author!,
167
- published : false
168
- };
227
+ private static buildOwnerQueryFilters(recordsSubscribe: RecordsSubscribe): Filter[] {
228
+ const { dateSort, filter } = recordsSubscribe.message.descriptor;
229
+ return [{
230
+ ...Records.convertFilter(filter, dateSort),
231
+ interface : DwnInterfaceName.Records,
232
+ method : DwnMethodName.Write,
233
+ isLatestBaseState : true,
234
+ }];
169
235
  }
170
236
 
171
237
  /**
172
- * Creates a filter for unpublished records that are within the specified protocol.
173
- * Validation that `protocol` and other required protocol-related fields occurs before this method.
238
+ * Build query filters for non-owner with visibility rules.
174
239
  */
175
- private static buildUnpublishedProtocolAuthorizedRecordsFilter(recordsSubscribe: RecordsSubscribe): Filter {
176
- return {
177
- ...Records.convertFilter(recordsSubscribe.message.descriptor.filter),
178
- interface : DwnInterfaceName.Records,
179
- method : [ DwnMethodName.Write, DwnMethodName.Delete ],
180
- published : false
181
- };
240
+ private static buildNonOwnerQueryFilters(recordsSubscribe: RecordsSubscribe): Filter[] {
241
+ const filters: Filter[] = [];
242
+ const { dateSort, filter } = recordsSubscribe.message.descriptor;
243
+ if (Records.filterIncludesPublishedRecords(filter)) {
244
+ filters.push(RecordsSubscribeHandler.buildPublishedQueryFilter(recordsSubscribe));
245
+ }
246
+
247
+ if (Records.filterIncludesUnpublishedRecords(filter)) {
248
+ if (Records.shouldBuildUnpublishedAuthorFilter(filter, recordsSubscribe.author!)) {
249
+ filters.push({
250
+ ...Records.convertFilter(filter, dateSort),
251
+ author : recordsSubscribe.author!,
252
+ interface : DwnInterfaceName.Records,
253
+ method : DwnMethodName.Write,
254
+ isLatestBaseState : true,
255
+ published : false,
256
+ });
257
+ }
258
+
259
+ if (Records.shouldProtocolAuthorize(recordsSubscribe.signaturePayload!)) {
260
+ filters.push({
261
+ ...Records.convertFilter(filter, dateSort),
262
+ interface : DwnInterfaceName.Records,
263
+ method : DwnMethodName.Write,
264
+ isLatestBaseState : true,
265
+ published : false,
266
+ });
267
+ }
268
+
269
+ if (Records.shouldBuildUnpublishedRecipientFilter(filter, recordsSubscribe.author!)) {
270
+ filters.push({
271
+ ...Records.convertFilter(filter, dateSort),
272
+ interface : DwnInterfaceName.Records,
273
+ method : DwnMethodName.Write,
274
+ recipient : recordsSubscribe.author!,
275
+ isLatestBaseState : true,
276
+ published : false,
277
+ });
278
+ }
279
+ }
280
+ return filters;
182
281
  }
183
282
 
184
283
  /**
185
- * Creates a filter for only unpublished records where the author is the same as the subscribe author.
284
+ * Build a published-only query filter (latest writes).
186
285
  */
187
- private static buildUnpublishedRecordsBySubscribeAuthorFilter(recordsSubscribe: RecordsSubscribe): Filter {
188
- // include records where author is the same as the subscribe author
286
+ private static buildPublishedQueryFilter(recordsSubscribe: RecordsSubscribe): Filter {
287
+ const { dateSort, filter } = recordsSubscribe.message.descriptor;
189
288
  return {
190
- ...Records.convertFilter(recordsSubscribe.message.descriptor.filter),
191
- author : recordsSubscribe.author!,
192
- interface : DwnInterfaceName.Records,
193
- method : [ DwnMethodName.Write, DwnMethodName.Delete ],
194
- published : false
289
+ ...Records.convertFilter(filter, dateSort),
290
+ interface : DwnInterfaceName.Records,
291
+ method : DwnMethodName.Write,
292
+ published : true,
293
+ isLatestBaseState : true,
195
294
  };
196
295
  }
197
296
 
@@ -53,7 +53,7 @@ export class RecordsWriteHandler implements MethodHandler {
53
53
 
54
54
  // authentication & authorization
55
55
  try {
56
- await authenticate(message.authorization, this.didResolver);
56
+ await authenticate(message.authorization, this.didResolver, message.attestation);
57
57
  await RecordsWriteHandler.authorizeRecordsWrite(tenant, recordsWrite, this.messageStore);
58
58
  } catch (e) {
59
59
  return messageReplyFromError(e, 401);
package/src/index.ts CHANGED
@@ -4,8 +4,8 @@ export type { EventListener, EventStream, EventSubscription, MessageEvent, Subsc
4
4
  export type { AuthorizationModel, Descriptor, DelegatedGrantRecordsWriteMessage, GenericMessage, GenericMessageReply, GenericSignaturePayload, MessageSort, MessageSubscription, Pagination, QueryResultEntry, Status } from './types/message-types.js';
5
5
  export type { MessagesFilter, MessagesReadMessage as MessagesReadMessage, MessagesReadReply as MessagesReadReply, MessagesReadReplyEntry as MessagesReadReplyEntry, MessagesReadDescriptor, MessagesSubscribeDescriptor, MessagesSubscribeMessage, MessagesSubscribeReply, MessageSubscriptionHandler, MessagesSubscribeMessageOptions, MessagesSyncAction, MessagesSyncDescriptor, 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
- export type { ProtocolsConfigureDescriptor, ProtocolDefinition, ProtocolTypes, ProtocolRuleSet, ProtocolsQueryFilter, ProtocolsConfigureMessage, ProtocolsQueryMessage, ProtocolsQueryReply, ProtocolActionRule, ProtocolPathEncryption, ProtocolsQueryDescriptor, ProtocolType, ProtocolUses } from './types/protocols-types.js';
8
- export type { DataEncodedRecordsWriteMessage, EncryptedKey, EncryptionProperty, RecordsCountDescriptor, RecordsCountMessage, RecordsCountReply, RecordsDeleteMessage, RecordsFilter, RecordsQueryMessage, RecordsQueryReply, RecordsQueryReplyEntry, RecordsReadMessage, RecordsReadReply, RecordsSubscribeDescriptor, RecordsSubscribeMessage, RecordsSubscribeReply, RecordSubscriptionHandler, RecordsWriteDescriptor, RecordsWriteTags, RecordsWriteTagValue, RecordsWriteMessage, RecordsWriteSignaturePayload, RecordsDeleteDescriptor, RecordsQueryDescriptor, RecordsReadDescriptor, RecordsSubscribeMessageOptions, RecordsWriteMessageOptions, InternalRecordsWriteMessage, RecordEvent, RecordsWriteTagsFilter } from './types/records-types.js';
7
+ export type { ProtocolsConfigureDescriptor, ProtocolDefinition, ProtocolTypes, ProtocolRuleSet, ProtocolsQueryFilter, ProtocolsConfigureMessage, ProtocolsQueryMessage, ProtocolsQueryReply, ProtocolActionRule, ProtocolPathEncryption, ProtocolsQueryDescriptor, ProtocolSizeDefinition, ProtocolTagsDefinition, ProtocolTagSchema, ProtocolType, ProtocolUses } from './types/protocols-types.js';
8
+ export type { DataEncodedRecordsWriteMessage, RecordsCountDescriptor, RecordsCountMessage, RecordsCountReply, RecordsDeleteMessage, RecordsFilter, RecordsQueryMessage, RecordsQueryReply, RecordsQueryReplyEntry, RecordsReadMessage, RecordsReadReply, RecordsSubscribeDescriptor, RecordsSubscribeMessage, RecordsSubscribeReply, RecordSubscriptionHandler, RecordsWriteDescriptor, RecordsWriteTags, RecordsWriteTagValue, RecordsWriteMessage, RecordsWriteSignaturePayload, RecordsDeleteDescriptor, RecordsQueryDescriptor, RecordsReadDescriptor, RecordsSubscribeMessageOptions, RecordsWriteMessageOptions, InternalRecordsWriteMessage, RecordEvent, RecordsWriteTagsFilter } from './types/records-types.js';
9
9
  export type { GeneralJws, SignatureEntry } from './types/jws-types.js';
10
10
  export { authenticate } from './core/auth.js';
11
11
  export { AllowAllTenantGate } from './core/tenant-gate.js';
@@ -28,8 +28,8 @@ export { DwnInterfaceName, DwnMethodName } from './enums/dwn-interface-method.js
28
28
  export { Encoder } from './utils/encoder.js';
29
29
  export { MessagesSubscribe as MessagesSubscribe } from './interfaces/messages-subscribe.js';
30
30
  export type { MessagesSubscribeOptions as MessagesSubscribeOptions } from './interfaces/messages-subscribe.js';
31
- export { Encryption, EncryptionAlgorithm } from './utils/encryption.js';
32
- export type { EciesEncryptionOutput, EciesEncryptionInput } from './utils/encryption.js';
31
+ export { Encryption, ContentEncryptionAlgorithm, KeyAgreementAlgorithm } from './utils/encryption.js';
32
+ export type { JweEncryption, JweRecipient, JweRecipientHeader, JweProtectedHeader, JweKeyUnwrapPayload } from './utils/encryption.js';
33
33
  export { RecordsWrite } from './interfaces/records-write.js';
34
34
  export type { EncryptionInput, KeyEncryptionInput, RecordsWriteOptions, CreateFromOptions } from './interfaces/records-write.js';
35
35
  export { executeUnlessAborted } from './utils/abort.js';
@@ -143,11 +143,11 @@ export class ProtocolsConfigure extends AbstractMessage<ProtocolsConfigureMessag
143
143
  const recordTypes = Object.keys(definition.types);
144
144
 
145
145
  // gather all roles (local roles only — cross-protocol roles are validated by alias existence)
146
- const roles = ProtocolsConfigure.fetchAllRolePathsRecursively('', definition.structure, []);
146
+ const roles = ProtocolsConfigure.fetchAllRolePathsRecursively('', definition.structure as ProtocolRuleSet, []);
147
147
 
148
148
  // validate the entire rule set structure recursively
149
149
  ProtocolsConfigure.validateRuleSetRecursively({
150
- ruleSet : definition.structure,
150
+ ruleSet : definition.structure as ProtocolRuleSet,
151
151
  ruleSetProtocolPath : '',
152
152
  recordTypes,
153
153
  roles,
@@ -172,7 +172,7 @@ export class ProtocolsConfigure extends AbstractMessage<ProtocolsConfigureMessag
172
172
  continue;
173
173
  }
174
174
 
175
- const childRuleSet = ruleSet[recordType];
175
+ const childRuleSet = ruleSet[recordType] as ProtocolRuleSet;
176
176
 
177
177
  let childRuleSetProtocolPath;
178
178
  if (ruleSetProtocolPath === '') {
@@ -233,7 +233,7 @@ export class ProtocolsConfigure extends AbstractMessage<ProtocolsConfigureMessag
233
233
  for (const tag in tagProperties) {
234
234
  const tagSchemaDefinition = tagProperties[tag];
235
235
 
236
- if (!ajv.validateSchema(tagSchemaDefinition)) {
236
+ if (!ajv.validateSchema(tagSchemaDefinition as Record<string, unknown>)) {
237
237
  const schemaError = ajv.errorsText(ajv.errors, { dataVar: `${ruleSetProtocolPath}/$tags/${tag}` });
238
238
  throw new DwnError(DwnErrorCode.ProtocolsConfigureInvalidTagSchema, `tags schema validation error: ${schemaError}`);
239
239
  }
@@ -376,7 +376,7 @@ export class ProtocolsConfigure extends AbstractMessage<ProtocolsConfigureMessag
376
376
  continue;
377
377
  }
378
378
 
379
- const childRuleSet = ruleSet[recordType];
379
+ const childRuleSet = ruleSet[recordType] as ProtocolRuleSet;
380
380
 
381
381
  // A structure key whose rule set has `$ref` does not need to be in the local `types` map —
382
382
  // the type comes from the referenced protocol. All other keys must be in `types`.
@@ -23,6 +23,11 @@ export type RecordsDeleteOptions = {
23
23
  */
24
24
  prune?: boolean
25
25
 
26
+ /**
27
+ * The ID of the permission grant authorizing this delete.
28
+ */
29
+ permissionGrantId?: string;
30
+
26
31
  /**
27
32
  * The delegated grant to sign on behalf of the logical author, which is the grantor (`grantedBy`) of the delegated grant.
28
33
  */
@@ -64,9 +69,10 @@ export class RecordsDelete extends AbstractMessage<RecordsDeleteMessage> {
64
69
 
65
70
  const authorization = await Message.createAuthorization({
66
71
  descriptor,
67
- signer : options.signer,
68
- protocolRole : options.protocolRole,
69
- delegatedGrant : options.delegatedGrant
72
+ signer : options.signer,
73
+ protocolRole : options.protocolRole,
74
+ permissionGrantId : options.permissionGrantId,
75
+ delegatedGrant : options.delegatedGrant
70
76
  });
71
77
  const message: RecordsDeleteMessage = { descriptor, authorization };
72
78
 
@@ -1,6 +1,7 @@
1
1
  import type { MessageSigner } from '../types/signer.js';
2
2
  import type { MessageStore } from '../types/message-store.js';
3
- import type { DataEncodedRecordsWriteMessage, RecordsFilter, RecordsSubscribeDescriptor, RecordsSubscribeMessage } from '../types/records-types.js';
3
+ import type { Pagination } from '../types/message-types.js';
4
+ import type { DataEncodedRecordsWriteMessage, DateSort, RecordsFilter, RecordsSubscribeDescriptor, RecordsSubscribeMessage } from '../types/records-types.js';
4
5
 
5
6
  import { AbstractMessage } from '../core/abstract-message.js';
6
7
  import { Message } from '../core/message.js';
@@ -16,6 +17,8 @@ import { validateProtocolUrlNormalized, validateSchemaUrlNormalized } from '../u
16
17
  export type RecordsSubscribeOptions = {
17
18
  messageTimestamp?: string;
18
19
  filter: RecordsFilter;
20
+ dateSort?: DateSort;
21
+ pagination?: Pagination;
19
22
  signer?: MessageSigner;
20
23
  protocolRole?: string;
21
24
 
@@ -63,6 +66,8 @@ export class RecordsSubscribe extends AbstractMessage<RecordsSubscribeMessage> {
63
66
  method : DwnMethodName.Subscribe,
64
67
  messageTimestamp : options.messageTimestamp ?? Time.getCurrentTimestamp(),
65
68
  filter : Records.normalizeFilter(options.filter),
69
+ dateSort : options.dateSort,
70
+ pagination : options.pagination,
66
71
  };
67
72
 
68
73
  // delete all descriptor properties that are `undefined` else the code will encounter the following IPLD issue when attempting to generate CID: