@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.
- package/dist/browser.mjs +8 -8
- package/dist/browser.mjs.map +4 -4
- package/dist/esm/generated/precompiled-validators.js +2591 -1435
- package/dist/esm/generated/precompiled-validators.js.map +1 -1
- package/dist/esm/src/core/constants.js +20 -0
- package/dist/esm/src/core/constants.js.map +1 -1
- package/dist/esm/src/core/dwn-error.js +24 -1
- package/dist/esm/src/core/dwn-error.js.map +1 -1
- package/dist/esm/src/core/grant-authorization.js +4 -4
- package/dist/esm/src/core/grant-authorization.js.map +1 -1
- package/dist/esm/src/core/message.js +89 -4
- package/dist/esm/src/core/message.js.map +1 -1
- package/dist/esm/src/core/messages-grant-authorization.js +147 -55
- package/dist/esm/src/core/messages-grant-authorization.js.map +1 -1
- package/dist/esm/src/core/protocol-authorization.js +76 -0
- package/dist/esm/src/core/protocol-authorization.js.map +1 -1
- package/dist/esm/src/core/records-grant-authorization.js +40 -15
- package/dist/esm/src/core/records-grant-authorization.js.map +1 -1
- package/dist/esm/src/handlers/messages-read.js +5 -5
- package/dist/esm/src/handlers/messages-read.js.map +1 -1
- package/dist/esm/src/handlers/messages-subscribe.js +109 -7
- package/dist/esm/src/handlers/messages-subscribe.js.map +1 -1
- package/dist/esm/src/handlers/messages-sync.js +341 -96
- package/dist/esm/src/handlers/messages-sync.js.map +1 -1
- package/dist/esm/src/handlers/protocols-configure.js +81 -2
- package/dist/esm/src/handlers/protocols-configure.js.map +1 -1
- package/dist/esm/src/handlers/records-count.js +30 -0
- package/dist/esm/src/handlers/records-count.js.map +1 -1
- package/dist/esm/src/handlers/records-delete.js +3 -2
- package/dist/esm/src/handlers/records-delete.js.map +1 -1
- package/dist/esm/src/handlers/records-query.js +30 -0
- package/dist/esm/src/handlers/records-query.js.map +1 -1
- package/dist/esm/src/handlers/records-read.js +3 -2
- package/dist/esm/src/handlers/records-read.js.map +1 -1
- package/dist/esm/src/handlers/records-subscribe.js +31 -0
- package/dist/esm/src/handlers/records-subscribe.js.map +1 -1
- package/dist/esm/src/handlers/records-write.js +3 -2
- package/dist/esm/src/handlers/records-write.js.map +1 -1
- package/dist/esm/src/index.js +2 -0
- package/dist/esm/src/index.js.map +1 -1
- package/dist/esm/src/interfaces/messages-read.js +6 -3
- package/dist/esm/src/interfaces/messages-read.js.map +1 -1
- package/dist/esm/src/interfaces/messages-subscribe.js +6 -3
- package/dist/esm/src/interfaces/messages-subscribe.js.map +1 -1
- package/dist/esm/src/interfaces/messages-sync.js +17 -3
- package/dist/esm/src/interfaces/messages-sync.js.map +1 -1
- package/dist/esm/src/interfaces/protocols-configure.js +5 -2
- package/dist/esm/src/interfaces/protocols-configure.js.map +1 -1
- package/dist/esm/src/interfaces/protocols-query.js +8 -4
- package/dist/esm/src/interfaces/protocols-query.js.map +1 -1
- package/dist/esm/src/interfaces/records-count.js +5 -0
- package/dist/esm/src/interfaces/records-count.js.map +1 -1
- package/dist/esm/src/interfaces/records-delete.js +6 -2
- package/dist/esm/src/interfaces/records-delete.js.map +1 -1
- package/dist/esm/src/interfaces/records-query.js +5 -0
- package/dist/esm/src/interfaces/records-query.js.map +1 -1
- package/dist/esm/src/interfaces/records-read.js +6 -3
- package/dist/esm/src/interfaces/records-read.js.map +1 -1
- package/dist/esm/src/interfaces/records-subscribe.js +5 -0
- package/dist/esm/src/interfaces/records-subscribe.js.map +1 -1
- package/dist/esm/src/interfaces/records-write.js +6 -3
- package/dist/esm/src/interfaces/records-write.js.map +1 -1
- package/dist/esm/src/protocols/permissions.js +28 -7
- package/dist/esm/src/protocols/permissions.js.map +1 -1
- package/dist/esm/src/sync/records-projection.js +228 -0
- package/dist/esm/src/sync/records-projection.js.map +1 -0
- package/dist/esm/src/types/message-types.js.map +1 -1
- package/dist/esm/src/types/permission-types.js.map +1 -1
- package/dist/esm/src/utils/permission-scope.js +37 -0
- package/dist/esm/src/utils/permission-scope.js.map +1 -0
- package/dist/esm/tests/core/grant-authorization.spec.js +26 -3
- package/dist/esm/tests/core/grant-authorization.spec.js.map +1 -1
- package/dist/esm/tests/core/records-grant-authorization.spec.js +117 -0
- package/dist/esm/tests/core/records-grant-authorization.spec.js.map +1 -0
- package/dist/esm/tests/features/permissions.spec.js +126 -0
- package/dist/esm/tests/features/permissions.spec.js.map +1 -1
- package/dist/esm/tests/handlers/messages-read.spec.js +345 -12
- package/dist/esm/tests/handlers/messages-read.spec.js.map +1 -1
- package/dist/esm/tests/handlers/messages-subscribe.spec.js +326 -9
- package/dist/esm/tests/handlers/messages-subscribe.spec.js.map +1 -1
- package/dist/esm/tests/handlers/messages-sync.spec.js +1053 -7
- package/dist/esm/tests/handlers/messages-sync.spec.js.map +1 -1
- package/dist/esm/tests/handlers/protocols-configure.spec.js +361 -0
- package/dist/esm/tests/handlers/protocols-configure.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-count.spec.js +75 -2
- package/dist/esm/tests/handlers/records-count.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-query.spec.js +73 -0
- package/dist/esm/tests/handlers/records-query.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-subscribe.spec.js +75 -1
- package/dist/esm/tests/handlers/records-subscribe.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/messages-get.spec.js +107 -5
- package/dist/esm/tests/interfaces/messages-get.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/protocols-configure.spec.js +13 -0
- package/dist/esm/tests/interfaces/protocols-configure.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/records-delete.spec.js +12 -0
- package/dist/esm/tests/interfaces/records-delete.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/records-query.spec.js +10 -0
- package/dist/esm/tests/interfaces/records-query.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/records-subscribe.spec.js +10 -0
- package/dist/esm/tests/interfaces/records-subscribe.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/records-write.spec.js +33 -0
- package/dist/esm/tests/interfaces/records-write.spec.js.map +1 -1
- package/dist/esm/tests/sync/records-projection.spec.js +245 -0
- package/dist/esm/tests/sync/records-projection.spec.js.map +1 -0
- package/dist/esm/tests/test-suite.js +2 -0
- package/dist/esm/tests/test-suite.js.map +1 -1
- package/dist/esm/tests/utils/permission-scope.spec.js +66 -0
- package/dist/esm/tests/utils/permission-scope.spec.js.map +1 -0
- package/dist/esm/tests/utils/test-data-generator.js +5 -2
- package/dist/esm/tests/utils/test-data-generator.js.map +1 -1
- package/dist/types/generated/precompiled-validators.d.ts.map +1 -1
- package/dist/types/src/core/constants.d.ts +13 -0
- package/dist/types/src/core/constants.d.ts.map +1 -1
- package/dist/types/src/core/dwn-error.d.ts +24 -1
- package/dist/types/src/core/dwn-error.d.ts.map +1 -1
- package/dist/types/src/core/grant-authorization.d.ts +1 -2
- package/dist/types/src/core/grant-authorization.d.ts.map +1 -1
- package/dist/types/src/core/message.d.ts +41 -1
- package/dist/types/src/core/message.d.ts.map +1 -1
- package/dist/types/src/core/messages-grant-authorization.d.ts +36 -4
- package/dist/types/src/core/messages-grant-authorization.d.ts.map +1 -1
- package/dist/types/src/core/protocol-authorization.d.ts +12 -0
- package/dist/types/src/core/protocol-authorization.d.ts.map +1 -1
- package/dist/types/src/core/records-grant-authorization.d.ts +6 -0
- package/dist/types/src/core/records-grant-authorization.d.ts.map +1 -1
- package/dist/types/src/handlers/messages-read.d.ts.map +1 -1
- package/dist/types/src/handlers/messages-subscribe.d.ts +2 -1
- package/dist/types/src/handlers/messages-subscribe.d.ts.map +1 -1
- package/dist/types/src/handlers/messages-sync.d.ts +31 -0
- package/dist/types/src/handlers/messages-sync.d.ts.map +1 -1
- package/dist/types/src/handlers/protocols-configure.d.ts +3 -0
- package/dist/types/src/handlers/protocols-configure.d.ts.map +1 -1
- package/dist/types/src/handlers/records-count.d.ts +4 -0
- package/dist/types/src/handlers/records-count.d.ts.map +1 -1
- package/dist/types/src/handlers/records-delete.d.ts.map +1 -1
- package/dist/types/src/handlers/records-query.d.ts +4 -0
- package/dist/types/src/handlers/records-query.d.ts.map +1 -1
- package/dist/types/src/handlers/records-read.d.ts.map +1 -1
- package/dist/types/src/handlers/records-subscribe.d.ts.map +1 -1
- package/dist/types/src/handlers/records-write.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +6 -2
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/interfaces/messages-read.d.ts +1 -1
- package/dist/types/src/interfaces/messages-read.d.ts.map +1 -1
- package/dist/types/src/interfaces/messages-subscribe.d.ts +1 -1
- package/dist/types/src/interfaces/messages-subscribe.d.ts.map +1 -1
- package/dist/types/src/interfaces/messages-sync.d.ts +4 -1
- package/dist/types/src/interfaces/messages-sync.d.ts.map +1 -1
- package/dist/types/src/interfaces/protocols-configure.d.ts.map +1 -1
- package/dist/types/src/interfaces/protocols-query.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-count.d.ts +1 -0
- package/dist/types/src/interfaces/records-count.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-delete.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-query.d.ts +1 -0
- package/dist/types/src/interfaces/records-query.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-read.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-subscribe.d.ts +1 -0
- package/dist/types/src/interfaces/records-subscribe.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-write.d.ts.map +1 -1
- package/dist/types/src/protocols/permissions.d.ts +2 -0
- package/dist/types/src/protocols/permissions.d.ts.map +1 -1
- package/dist/types/src/sync/records-projection.d.ts +98 -0
- package/dist/types/src/sync/records-projection.d.ts.map +1 -0
- package/dist/types/src/types/message-types.d.ts +1 -0
- package/dist/types/src/types/message-types.d.ts.map +1 -1
- package/dist/types/src/types/messages-types.d.ts +21 -3
- package/dist/types/src/types/messages-types.d.ts.map +1 -1
- package/dist/types/src/types/permission-types.d.ts +4 -0
- package/dist/types/src/types/permission-types.d.ts.map +1 -1
- package/dist/types/src/types/records-types.d.ts +4 -0
- package/dist/types/src/types/records-types.d.ts.map +1 -1
- package/dist/types/src/types/subscriptions.d.ts +18 -3
- package/dist/types/src/types/subscriptions.d.ts.map +1 -1
- package/dist/types/src/utils/permission-scope.d.ts +29 -0
- package/dist/types/src/utils/permission-scope.d.ts.map +1 -0
- package/dist/types/tests/core/records-grant-authorization.spec.d.ts +2 -0
- package/dist/types/tests/core/records-grant-authorization.spec.d.ts.map +1 -0
- package/dist/types/tests/features/permissions.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/messages-read.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/messages-subscribe.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/messages-sync.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/protocols-configure.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-count.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-query.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-subscribe.spec.d.ts.map +1 -1
- package/dist/types/tests/sync/records-projection.spec.d.ts +2 -0
- package/dist/types/tests/sync/records-projection.spec.d.ts.map +1 -0
- package/dist/types/tests/test-suite.d.ts.map +1 -1
- package/dist/types/tests/utils/permission-scope.spec.d.ts +2 -0
- package/dist/types/tests/utils/permission-scope.spec.d.ts.map +1 -0
- package/dist/types/tests/utils/test-data-generator.d.ts +5 -2
- package/dist/types/tests/utils/test-data-generator.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/core/constants.ts +24 -0
- package/src/core/dwn-error.ts +24 -1
- package/src/core/grant-authorization.ts +7 -5
- package/src/core/message.ts +153 -6
- package/src/core/messages-grant-authorization.ts +282 -70
- package/src/core/protocol-authorization.ts +130 -0
- package/src/core/records-grant-authorization.ts +64 -21
- package/src/handlers/messages-read.ts +7 -5
- package/src/handlers/messages-subscribe.ts +149 -9
- package/src/handlers/messages-sync.ts +593 -102
- package/src/handlers/protocols-configure.ts +103 -2
- package/src/handlers/records-count.ts +33 -0
- package/src/handlers/records-delete.ts +3 -2
- package/src/handlers/records-query.ts +33 -0
- package/src/handlers/records-read.ts +3 -2
- package/src/handlers/records-subscribe.ts +34 -0
- package/src/handlers/records-write.ts +3 -2
- package/src/index.ts +7 -3
- package/src/interfaces/messages-read.ts +8 -5
- package/src/interfaces/messages-subscribe.ts +12 -9
- package/src/interfaces/messages-sync.ts +33 -12
- package/src/interfaces/protocols-configure.ts +8 -4
- package/src/interfaces/protocols-query.ts +13 -9
- package/src/interfaces/records-count.ts +7 -0
- package/src/interfaces/records-delete.ts +9 -5
- package/src/interfaces/records-query.ts +7 -0
- package/src/interfaces/records-read.ts +6 -3
- package/src/interfaces/records-subscribe.ts +7 -0
- package/src/interfaces/records-write.ts +25 -17
- package/src/protocols/permissions.ts +47 -9
- package/src/sync/records-projection.ts +328 -0
- package/src/types/message-types.ts +1 -0
- package/src/types/messages-types.ts +23 -3
- package/src/types/permission-types.ts +5 -1
- package/src/types/records-types.ts +5 -1
- package/src/types/subscriptions.ts +19 -3
- package/src/utils/permission-scope.ts +55 -0
|
@@ -17,6 +17,7 @@ export type RecordsCountOptions = {
|
|
|
17
17
|
messageTimestamp?: string;
|
|
18
18
|
filter: RecordsFilter;
|
|
19
19
|
signer?: MessageSigner;
|
|
20
|
+
permissionGrantId?: string;
|
|
20
21
|
protocolRole?: string;
|
|
21
22
|
|
|
22
23
|
/**
|
|
@@ -60,11 +61,16 @@ export class RecordsCount extends AbstractMessage<RecordsCountMessage> {
|
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
public static async create(options: RecordsCountOptions): Promise<RecordsCount> {
|
|
64
|
+
const permissionGrantInvocation = Message.normalizePermissionGrantInvocation({
|
|
65
|
+
permissionGrantId: options.permissionGrantId,
|
|
66
|
+
});
|
|
67
|
+
|
|
63
68
|
const descriptor: RecordsCountDescriptor = {
|
|
64
69
|
interface : DwnInterfaceName.Records,
|
|
65
70
|
method : DwnMethodName.Count,
|
|
66
71
|
messageTimestamp : options.messageTimestamp ?? Time.getCurrentTimestamp(),
|
|
67
72
|
filter : Records.normalizeFilter(options.filter),
|
|
73
|
+
...permissionGrantInvocation,
|
|
68
74
|
};
|
|
69
75
|
|
|
70
76
|
// delete all descriptor properties that are `undefined` else the code will encounter the following IPLD issue when attempting to generate CID:
|
|
@@ -78,6 +84,7 @@ export class RecordsCount extends AbstractMessage<RecordsCountMessage> {
|
|
|
78
84
|
authorization = await Message.createAuthorization({
|
|
79
85
|
descriptor,
|
|
80
86
|
signer,
|
|
87
|
+
...permissionGrantInvocation,
|
|
81
88
|
protocolRole : options.protocolRole,
|
|
82
89
|
delegatedGrant : options.delegatedGrant
|
|
83
90
|
});
|
|
@@ -58,21 +58,25 @@ export class RecordsDelete extends AbstractMessage<RecordsDeleteMessage> {
|
|
|
58
58
|
public static async create(options: RecordsDeleteOptions): Promise<RecordsDelete> {
|
|
59
59
|
const recordId = options.recordId;
|
|
60
60
|
const currentTime = Time.getCurrentTimestamp();
|
|
61
|
+
const permissionGrantInvocation = Message.normalizePermissionGrantInvocation({
|
|
62
|
+
permissionGrantId: options.permissionGrantId,
|
|
63
|
+
});
|
|
61
64
|
|
|
62
65
|
const descriptor: RecordsDeleteDescriptor = {
|
|
63
66
|
interface : DwnInterfaceName.Records,
|
|
64
67
|
method : DwnMethodName.Delete,
|
|
65
68
|
messageTimestamp : options.messageTimestamp ?? currentTime,
|
|
66
69
|
recordId,
|
|
67
|
-
prune : options.prune ?? false
|
|
70
|
+
prune : options.prune ?? false,
|
|
71
|
+
...permissionGrantInvocation,
|
|
68
72
|
};
|
|
69
73
|
|
|
70
74
|
const authorization = await Message.createAuthorization({
|
|
71
75
|
descriptor,
|
|
72
|
-
signer
|
|
73
|
-
protocolRole
|
|
74
|
-
|
|
75
|
-
delegatedGrant
|
|
76
|
+
signer : options.signer,
|
|
77
|
+
protocolRole : options.protocolRole,
|
|
78
|
+
...permissionGrantInvocation,
|
|
79
|
+
delegatedGrant : options.delegatedGrant
|
|
76
80
|
});
|
|
77
81
|
const message: RecordsDeleteMessage = { descriptor, authorization };
|
|
78
82
|
|
|
@@ -21,6 +21,7 @@ export type RecordsQueryOptions = {
|
|
|
21
21
|
dateSort?: DateSort;
|
|
22
22
|
pagination?: Pagination;
|
|
23
23
|
signer?: MessageSigner;
|
|
24
|
+
permissionGrantId?: string;
|
|
24
25
|
protocolRole?: string;
|
|
25
26
|
|
|
26
27
|
/**
|
|
@@ -74,11 +75,16 @@ export class RecordsQuery extends AbstractMessage<RecordsQueryMessage> {
|
|
|
74
75
|
}
|
|
75
76
|
|
|
76
77
|
public static async create(options: RecordsQueryOptions): Promise<RecordsQuery> {
|
|
78
|
+
const permissionGrantInvocation = Message.normalizePermissionGrantInvocation({
|
|
79
|
+
permissionGrantId: options.permissionGrantId,
|
|
80
|
+
});
|
|
81
|
+
|
|
77
82
|
const descriptor: RecordsQueryDescriptor = {
|
|
78
83
|
interface : DwnInterfaceName.Records,
|
|
79
84
|
method : DwnMethodName.Query,
|
|
80
85
|
messageTimestamp : options.messageTimestamp ?? Time.getCurrentTimestamp(),
|
|
81
86
|
filter : Records.normalizeFilter(options.filter),
|
|
87
|
+
...permissionGrantInvocation,
|
|
82
88
|
dateSort : options.dateSort,
|
|
83
89
|
pagination : options.pagination,
|
|
84
90
|
};
|
|
@@ -103,6 +109,7 @@ export class RecordsQuery extends AbstractMessage<RecordsQueryMessage> {
|
|
|
103
109
|
authorization = await Message.createAuthorization({
|
|
104
110
|
descriptor,
|
|
105
111
|
signer,
|
|
112
|
+
...permissionGrantInvocation,
|
|
106
113
|
protocolRole : options.protocolRole,
|
|
107
114
|
delegatedGrant : options.delegatedGrant
|
|
108
115
|
});
|
|
@@ -64,8 +64,11 @@ export class RecordsRead extends AbstractMessage<RecordsReadMessage> {
|
|
|
64
64
|
* @throws {DwnError} when a combination of required RecordsReadOptions are missing
|
|
65
65
|
*/
|
|
66
66
|
public static async create(options: RecordsReadOptions): Promise<RecordsRead> {
|
|
67
|
-
const { filter, signer,
|
|
67
|
+
const { filter, signer, protocolRole, dateSort } = options;
|
|
68
68
|
const currentTime = Time.getCurrentTimestamp();
|
|
69
|
+
const permissionGrantInvocation = Message.normalizePermissionGrantInvocation({
|
|
70
|
+
permissionGrantId: options.permissionGrantId
|
|
71
|
+
});
|
|
69
72
|
|
|
70
73
|
if (options.filter.published === false) {
|
|
71
74
|
if (dateSort === DateSort.PublishedAscending || dateSort === DateSort.PublishedDescending) {
|
|
@@ -81,8 +84,8 @@ export class RecordsRead extends AbstractMessage<RecordsReadMessage> {
|
|
|
81
84
|
method : DwnMethodName.Read,
|
|
82
85
|
filter : Records.normalizeFilter(filter),
|
|
83
86
|
messageTimestamp : options.messageTimestamp ?? currentTime,
|
|
84
|
-
permissionGrantId,
|
|
85
87
|
dateSort,
|
|
88
|
+
...permissionGrantInvocation,
|
|
86
89
|
};
|
|
87
90
|
|
|
88
91
|
removeUndefinedProperties(descriptor);
|
|
@@ -93,7 +96,7 @@ export class RecordsRead extends AbstractMessage<RecordsReadMessage> {
|
|
|
93
96
|
authorization = await Message.createAuthorization({
|
|
94
97
|
descriptor,
|
|
95
98
|
signer,
|
|
96
|
-
|
|
99
|
+
...permissionGrantInvocation,
|
|
97
100
|
protocolRole,
|
|
98
101
|
delegatedGrant: options.delegatedGrant
|
|
99
102
|
});
|
|
@@ -21,6 +21,7 @@ export type RecordsSubscribeOptions = {
|
|
|
21
21
|
dateSort?: DateSort;
|
|
22
22
|
pagination?: Pagination;
|
|
23
23
|
signer?: MessageSigner;
|
|
24
|
+
permissionGrantId?: string;
|
|
24
25
|
protocolRole?: string;
|
|
25
26
|
|
|
26
27
|
/**
|
|
@@ -68,11 +69,16 @@ export class RecordsSubscribe extends AbstractMessage<RecordsSubscribeMessage> {
|
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
public static async create(options: RecordsSubscribeOptions): Promise<RecordsSubscribe> {
|
|
72
|
+
const permissionGrantInvocation = Message.normalizePermissionGrantInvocation({
|
|
73
|
+
permissionGrantId: options.permissionGrantId,
|
|
74
|
+
});
|
|
75
|
+
|
|
71
76
|
const descriptor: RecordsSubscribeDescriptor = {
|
|
72
77
|
interface : DwnInterfaceName.Records,
|
|
73
78
|
method : DwnMethodName.Subscribe,
|
|
74
79
|
messageTimestamp : options.messageTimestamp ?? Time.getCurrentTimestamp(),
|
|
75
80
|
filter : Records.normalizeFilter(options.filter),
|
|
81
|
+
...permissionGrantInvocation,
|
|
76
82
|
dateSort : options.dateSort,
|
|
77
83
|
pagination : options.pagination,
|
|
78
84
|
cursor : options.cursor,
|
|
@@ -89,6 +95,7 @@ export class RecordsSubscribe extends AbstractMessage<RecordsSubscribeMessage> {
|
|
|
89
95
|
authorization = await Message.createAuthorization({
|
|
90
96
|
descriptor,
|
|
91
97
|
signer,
|
|
98
|
+
...permissionGrantInvocation,
|
|
92
99
|
protocolRole : options.protocolRole,
|
|
93
100
|
delegatedGrant : options.delegatedGrant
|
|
94
101
|
});
|
|
@@ -267,7 +267,12 @@ export class RecordsWrite implements MessageInterface<RecordsWriteMessage> {
|
|
|
267
267
|
await Message.validateSignatureStructure(message.authorization.signature, message.descriptor, 'RecordsWriteSignaturePayload');
|
|
268
268
|
|
|
269
269
|
if (message.authorization.ownerSignature !== undefined) {
|
|
270
|
-
await Message.validateSignatureStructure(
|
|
270
|
+
await Message.validateSignatureStructure(
|
|
271
|
+
message.authorization.ownerSignature,
|
|
272
|
+
message.descriptor,
|
|
273
|
+
'GenericSignaturePayload',
|
|
274
|
+
{ validatePermissionGrantInvocation: false }
|
|
275
|
+
);
|
|
271
276
|
}
|
|
272
277
|
|
|
273
278
|
await validateAttestationIntegrity(message);
|
|
@@ -314,25 +319,28 @@ export class RecordsWrite implements MessageInterface<RecordsWriteMessage> {
|
|
|
314
319
|
const dataSize = options.dataSize ?? options.data!.length;
|
|
315
320
|
|
|
316
321
|
const currentTime = Time.getCurrentTimestamp();
|
|
322
|
+
const permissionGrantInvocation = Message.normalizePermissionGrantInvocation({
|
|
323
|
+
permissionGrantId: options.permissionGrantId,
|
|
324
|
+
});
|
|
317
325
|
|
|
318
326
|
const descriptor: RecordsWriteDescriptor = {
|
|
319
|
-
interface
|
|
320
|
-
method
|
|
321
|
-
protocol
|
|
322
|
-
protocolPath
|
|
323
|
-
recipient
|
|
324
|
-
schema
|
|
325
|
-
tags
|
|
326
|
-
parentId
|
|
327
|
+
interface : DwnInterfaceName.Records,
|
|
328
|
+
method : DwnMethodName.Write,
|
|
329
|
+
protocol : normalizeProtocolUrl(options.protocol),
|
|
330
|
+
protocolPath : options.protocolPath,
|
|
331
|
+
recipient : options.recipient,
|
|
332
|
+
schema : options.schema === undefined ? undefined : normalizeSchemaUrl(options.schema),
|
|
333
|
+
tags : options.tags,
|
|
334
|
+
parentId : RecordsWrite.getRecordIdFromContextId(options.parentContextId),
|
|
327
335
|
dataCid,
|
|
328
336
|
dataSize,
|
|
329
|
-
dateCreated
|
|
330
|
-
messageTimestamp
|
|
331
|
-
published
|
|
332
|
-
datePublished
|
|
333
|
-
dataFormat
|
|
334
|
-
|
|
335
|
-
|
|
337
|
+
dateCreated : options.dateCreated ?? currentTime,
|
|
338
|
+
messageTimestamp : options.messageTimestamp ?? currentTime,
|
|
339
|
+
published : options.published,
|
|
340
|
+
datePublished : options.datePublished,
|
|
341
|
+
dataFormat : options.dataFormat,
|
|
342
|
+
squash : options.squash,
|
|
343
|
+
...permissionGrantInvocation,
|
|
336
344
|
};
|
|
337
345
|
|
|
338
346
|
// generate `datePublished` if the message is to be published but `datePublished` is not given
|
|
@@ -370,7 +378,7 @@ export class RecordsWrite implements MessageInterface<RecordsWriteMessage> {
|
|
|
370
378
|
await recordsWrite.sign({
|
|
371
379
|
signer : options.signer,
|
|
372
380
|
delegatedGrant : options.delegatedGrant,
|
|
373
|
-
|
|
381
|
+
...permissionGrantInvocation,
|
|
374
382
|
protocolRole : options.protocolRole,
|
|
375
383
|
authorKeyDeliveryPublicKey : options.authorKeyDeliveryPublicKey,
|
|
376
384
|
});
|
|
@@ -14,6 +14,7 @@ import { FilterUtility } from '../utils/filter.js';
|
|
|
14
14
|
import { Message } from '../core/message.js';
|
|
15
15
|
import { PermissionGrant } from './permission-grant.js';
|
|
16
16
|
import { PermissionRequest } from './permission-request.js';
|
|
17
|
+
import { PERMISSIONS_PROTOCOL_URI } from '../core/constants.js';
|
|
17
18
|
import { Records } from '../utils/records.js';
|
|
18
19
|
import { RecordsWrite } from '../interfaces/records-write.js';
|
|
19
20
|
import { Time } from '../utils/time.js';
|
|
@@ -95,7 +96,7 @@ export class PermissionsProtocol implements CoreProtocol {
|
|
|
95
96
|
/**
|
|
96
97
|
* The URI of the DWN Permissions protocol.
|
|
97
98
|
*/
|
|
98
|
-
public static readonly uri =
|
|
99
|
+
public static readonly uri = PERMISSIONS_PROTOCOL_URI;
|
|
99
100
|
|
|
100
101
|
/**
|
|
101
102
|
* The protocol path of the `request` record.
|
|
@@ -312,12 +313,24 @@ export class PermissionsProtocol implements CoreProtocol {
|
|
|
312
313
|
dataEncodedMessage: DataEncodedRecordsWriteMessage,
|
|
313
314
|
}> {
|
|
314
315
|
|
|
316
|
+
if (this.hasConflictingSubtreeScope(options.scope)) {
|
|
317
|
+
throw new DwnError(
|
|
318
|
+
DwnErrorCode.PermissionsProtocolCreateRequestScopeContextIdProtocolPathConflict,
|
|
319
|
+
'Permission request scopes cannot have both `contextId` and `protocolPath` present'
|
|
320
|
+
);
|
|
321
|
+
}
|
|
315
322
|
if (this.isRecordPermissionScope(options.scope) && options.scope.protocol === undefined) {
|
|
316
323
|
throw new DwnError(
|
|
317
324
|
DwnErrorCode.PermissionsProtocolCreateRequestRecordsScopeMissingProtocol,
|
|
318
325
|
'Permission request for Records must have a scope with a `protocol` property'
|
|
319
326
|
);
|
|
320
327
|
}
|
|
328
|
+
if (this.hasSubtreeScope(options.scope) && !this.hasProtocolScope(options.scope)) {
|
|
329
|
+
throw new DwnError(
|
|
330
|
+
DwnErrorCode.PermissionsProtocolCreateRequestSubtreeScopeMissingProtocol,
|
|
331
|
+
'Permission request subtree scopes must have a `protocol` property'
|
|
332
|
+
);
|
|
333
|
+
}
|
|
321
334
|
|
|
322
335
|
const scope = PermissionsProtocol.normalizePermissionScope(options.scope);
|
|
323
336
|
|
|
@@ -371,12 +384,24 @@ export class PermissionsProtocol implements CoreProtocol {
|
|
|
371
384
|
dataEncodedMessage: DataEncodedRecordsWriteMessage,
|
|
372
385
|
}> {
|
|
373
386
|
|
|
387
|
+
if (this.hasConflictingSubtreeScope(options.scope)) {
|
|
388
|
+
throw new DwnError(
|
|
389
|
+
DwnErrorCode.PermissionsProtocolCreateGrantScopeContextIdProtocolPathConflict,
|
|
390
|
+
'Permission grant scopes cannot have both `contextId` and `protocolPath` present'
|
|
391
|
+
);
|
|
392
|
+
}
|
|
374
393
|
if (this.isRecordPermissionScope(options.scope) && options.scope.protocol === undefined) {
|
|
375
394
|
throw new DwnError(
|
|
376
395
|
DwnErrorCode.PermissionsProtocolCreateGrantRecordsScopeMissingProtocol,
|
|
377
396
|
'Permission grants for Records must have a scope with a `protocol` property'
|
|
378
397
|
);
|
|
379
398
|
}
|
|
399
|
+
if (this.hasSubtreeScope(options.scope) && !this.hasProtocolScope(options.scope)) {
|
|
400
|
+
throw new DwnError(
|
|
401
|
+
DwnErrorCode.PermissionsProtocolCreateGrantSubtreeScopeMissingProtocol,
|
|
402
|
+
'Permission grant subtree scopes must have a `protocol` property'
|
|
403
|
+
);
|
|
404
|
+
}
|
|
380
405
|
|
|
381
406
|
const scope = PermissionsProtocol.normalizePermissionScope(options.scope);
|
|
382
407
|
|
|
@@ -600,6 +625,16 @@ export class PermissionsProtocol implements CoreProtocol {
|
|
|
600
625
|
return 'protocol' in scope && scope.protocol !== undefined;
|
|
601
626
|
}
|
|
602
627
|
|
|
628
|
+
private static hasSubtreeScope(scope: PermissionScope): scope is PermissionScope & ({ contextId: string } | { protocolPath: string }) {
|
|
629
|
+
return ('contextId' in scope && scope.contextId !== undefined)
|
|
630
|
+
|| ('protocolPath' in scope && scope.protocolPath !== undefined);
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
private static hasConflictingSubtreeScope(scope: PermissionScope): boolean {
|
|
634
|
+
return ('contextId' in scope && scope.contextId !== undefined)
|
|
635
|
+
&& ('protocolPath' in scope && scope.protocolPath !== undefined);
|
|
636
|
+
}
|
|
637
|
+
|
|
603
638
|
/**
|
|
604
639
|
* Validates that tags must include a protocol tag that matches the scoped protocol.
|
|
605
640
|
*/
|
|
@@ -633,18 +668,21 @@ export class PermissionsProtocol implements CoreProtocol {
|
|
|
633
668
|
this.validateTags(requestOrGrant, scope.protocol);
|
|
634
669
|
}
|
|
635
670
|
|
|
636
|
-
// if the scope is not a record permission scope, no additional validation is required
|
|
637
|
-
if (!this.isRecordPermissionScope(scope)) {
|
|
638
|
-
return;
|
|
639
|
-
}
|
|
640
|
-
// otherwise this is a record permission scope, more validation needed below
|
|
641
|
-
|
|
642
671
|
// `contextId` and `protocolPath` are mutually exclusive
|
|
643
|
-
|
|
672
|
+
const hasContextId = 'contextId' in scope && scope.contextId !== undefined;
|
|
673
|
+
const hasProtocolPath = 'protocolPath' in scope && scope.protocolPath !== undefined;
|
|
674
|
+
if (hasContextId && hasProtocolPath) {
|
|
644
675
|
throw new DwnError(
|
|
645
676
|
DwnErrorCode.PermissionsProtocolValidateScopeContextIdProhibitedProperties,
|
|
646
677
|
'Permission grants cannot have both `contextId` and `protocolPath` present'
|
|
647
678
|
);
|
|
648
679
|
}
|
|
680
|
+
if ((hasContextId || hasProtocolPath) && !this.hasProtocolScope(scope)) {
|
|
681
|
+
throw new DwnError(
|
|
682
|
+
DwnErrorCode.PermissionsProtocolValidateScopeSubtreeScopeMissingProtocol,
|
|
683
|
+
'Permission grant subtree scopes must have a `protocol` property'
|
|
684
|
+
);
|
|
685
|
+
}
|
|
686
|
+
|
|
649
687
|
}
|
|
650
|
-
};
|
|
688
|
+
};
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
import type { Filter } from '../types/query-types.js';
|
|
2
|
+
import type { Hash } from '../types/smt-types.js';
|
|
3
|
+
import type { MessageStore, MessageStoreOptions } from '../types/message-store.js';
|
|
4
|
+
|
|
5
|
+
import { DwnInterfaceName } from '../enums/dwn-interface-method.js';
|
|
6
|
+
import { FilterUtility } from '../utils/filter.js';
|
|
7
|
+
import { hashToHex } from '../smt/smt-utils.js';
|
|
8
|
+
import { isRecordsPrimaryProjectionExcludedProtocol } from '../core/constants.js';
|
|
9
|
+
import { lexicographicalCompare } from '../utils/string.js';
|
|
10
|
+
import { Message } from '../core/message.js';
|
|
11
|
+
import { SMTStoreMemory } from '../smt/smt-store-memory.js';
|
|
12
|
+
import { SparseMerkleTree } from '../smt/sparse-merkle-tree.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Projection-root algorithm for record-primary scoped subsets.
|
|
16
|
+
*
|
|
17
|
+
* This version builds an on-demand SMT over latest Records primary message CIDs
|
|
18
|
+
* selected by protocol plus optional exact protocolPath or context subtree.
|
|
19
|
+
* Dependency records, protocol configs, and record data payloads are not part
|
|
20
|
+
* of this root.
|
|
21
|
+
*/
|
|
22
|
+
export const RECORDS_PROJECTION_ROOT_VERSION = 'records-primary-scope-root-v1';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* A Records primary projection scope.
|
|
26
|
+
*
|
|
27
|
+
* `protocolPath` is an exact type-path match. `contextId` is a boundary-aware
|
|
28
|
+
* subtree match: the candidate context must equal the scoped context or start
|
|
29
|
+
* with `${contextId}/`. `protocolPath` and `contextId` are mutually exclusive.
|
|
30
|
+
*/
|
|
31
|
+
export type RecordsProjectionScope = {
|
|
32
|
+
protocol: string;
|
|
33
|
+
protocolPath?: string;
|
|
34
|
+
contextId?: string;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export type RecordsProjectionInput = {
|
|
38
|
+
tenant: string;
|
|
39
|
+
messageStore: MessageStore;
|
|
40
|
+
scopes: readonly [RecordsProjectionScope, ...RecordsProjectionScope[]];
|
|
41
|
+
options?: MessageStoreOptions;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export type RecordsProjectionTreeInput = RecordsProjectionInput & {
|
|
45
|
+
prefix: boolean[];
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export type RecordsProjectionSnapshot = {
|
|
49
|
+
getRoot(): Promise<Hash>;
|
|
50
|
+
getRootHex(): Promise<string>;
|
|
51
|
+
getSubtreeHash(prefix: boolean[]): Promise<Hash>;
|
|
52
|
+
getLeaves(prefix: boolean[]): Promise<string[]>;
|
|
53
|
+
close(): Promise<void>;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export type NormalizedRecordsProjectionScope = {
|
|
57
|
+
protocol: string;
|
|
58
|
+
} | {
|
|
59
|
+
protocol: string;
|
|
60
|
+
protocolPath: string;
|
|
61
|
+
} | {
|
|
62
|
+
protocol: string;
|
|
63
|
+
contextId: string;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Computes deterministic on-demand roots for Records projections.
|
|
68
|
+
*
|
|
69
|
+
* The snapshot API performs one store enumeration and serves tree operations
|
|
70
|
+
* from that in-memory view. A future store-level snapshot/high-watermark can be
|
|
71
|
+
* threaded through the `options` input without changing the projection shape.
|
|
72
|
+
*/
|
|
73
|
+
export class RecordsProjection {
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Returns the sorted latest Records primary message CIDs covered by a scope union.
|
|
77
|
+
*/
|
|
78
|
+
public static async getPrimaryMessageCids({
|
|
79
|
+
tenant,
|
|
80
|
+
messageStore,
|
|
81
|
+
scopes,
|
|
82
|
+
options,
|
|
83
|
+
}: RecordsProjectionInput): Promise<string[]> {
|
|
84
|
+
const filters = RecordsProjection.normalizeScopes(scopes)
|
|
85
|
+
.filter(scope => !isRecordsPrimaryProjectionExcludedProtocol(scope.protocol))
|
|
86
|
+
.flatMap(scope => RecordsProjection.constructFilters(scope));
|
|
87
|
+
if (filters.length === 0) {
|
|
88
|
+
return [];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const { messages } = await messageStore.query(tenant, filters, undefined, undefined, options);
|
|
92
|
+
const messageCids = await Promise.all(messages.map(message => Message.getCid(message)));
|
|
93
|
+
|
|
94
|
+
return [...new Set(messageCids)].sort(lexicographicalCompare); // NOSONAR — projection IDs require locale-independent bytewise ordering.
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Returns the projection root hash.
|
|
99
|
+
*/
|
|
100
|
+
public static async getRoot(input: RecordsProjectionInput): Promise<Hash> {
|
|
101
|
+
return RecordsProjection.withTree(input, tree => tree.getRoot());
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Returns the projection root hash encoded as lowercase hex.
|
|
106
|
+
*/
|
|
107
|
+
public static async getRootHex(input: RecordsProjectionInput): Promise<string> {
|
|
108
|
+
return hashToHex(await RecordsProjection.getRoot(input));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Returns the subtree hash for a bit prefix within this projection.
|
|
113
|
+
*/
|
|
114
|
+
public static async getSubtreeHash(input: RecordsProjectionTreeInput): Promise<Hash> {
|
|
115
|
+
return RecordsProjection.withTree(input, tree => tree.getSubtreeHash(input.prefix));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Returns the message CIDs under a bit prefix within this projection.
|
|
120
|
+
*/
|
|
121
|
+
public static async getLeaves(input: RecordsProjectionTreeInput): Promise<string[]> {
|
|
122
|
+
return RecordsProjection.withTree(input, tree => tree.getLeaves(input.prefix));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Builds an in-memory projection tree from one primary-CID enumeration.
|
|
127
|
+
*/
|
|
128
|
+
public static async createSnapshot(input: RecordsProjectionInput): Promise<RecordsProjectionSnapshot> {
|
|
129
|
+
const tree = await RecordsProjection.createTree(input);
|
|
130
|
+
return {
|
|
131
|
+
close : () => tree.close(),
|
|
132
|
+
getLeaves : (prefix: boolean[]) => tree.getLeaves(prefix),
|
|
133
|
+
getRoot : () => tree.getRoot(),
|
|
134
|
+
getRootHex : async () => hashToHex(await tree.getRoot()),
|
|
135
|
+
getSubtreeHash : (prefix: boolean[]) => tree.getSubtreeHash(prefix),
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Normalizes a scope union into sorted, duplicate-free, subsumption-reduced entries.
|
|
141
|
+
*/
|
|
142
|
+
public static normalizeScopes(
|
|
143
|
+
scopes: readonly [RecordsProjectionScope, ...RecordsProjectionScope[]],
|
|
144
|
+
): [NormalizedRecordsProjectionScope, ...NormalizedRecordsProjectionScope[]] {
|
|
145
|
+
if (scopes.length === 0) {
|
|
146
|
+
throw new Error('RecordsProjection: scopes must contain at least one scope.');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const normalized = scopes.map(scope => RecordsProjection.normalizeScope(scope));
|
|
150
|
+
const deduped = new Map<string, NormalizedRecordsProjectionScope>();
|
|
151
|
+
for (const scope of normalized) {
|
|
152
|
+
deduped.set(RecordsProjection.scopeKey(scope), scope);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const uniqueScopes = [...deduped.values()];
|
|
156
|
+
const protocolWide = new Set(
|
|
157
|
+
uniqueScopes
|
|
158
|
+
.filter(scope => RecordsProjection.isProtocolWideScope(scope))
|
|
159
|
+
.map(scope => scope.protocol)
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
const reduced = uniqueScopes.filter(scope => {
|
|
163
|
+
if (protocolWide.has(scope.protocol)) {
|
|
164
|
+
return RecordsProjection.isProtocolWideScope(scope);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (RecordsProjection.isContextScope(scope)) {
|
|
168
|
+
return !uniqueScopes.some(candidate =>
|
|
169
|
+
candidate !== scope &&
|
|
170
|
+
RecordsProjection.isContextScope(candidate) &&
|
|
171
|
+
candidate.protocol === scope.protocol &&
|
|
172
|
+
RecordsProjection.contextIdSubsumes(candidate.contextId, scope.contextId)
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return true;
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
const result = reduced.sort(RecordsProjection.compareScopes);
|
|
180
|
+
return result as [NormalizedRecordsProjectionScope, ...NormalizedRecordsProjectionScope[]];
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
private static async withTree<T>(
|
|
184
|
+
input: RecordsProjectionInput,
|
|
185
|
+
fn: (tree: SparseMerkleTree) => Promise<T>,
|
|
186
|
+
): Promise<T> {
|
|
187
|
+
const tree = await RecordsProjection.createTree(input);
|
|
188
|
+
|
|
189
|
+
try {
|
|
190
|
+
return await fn(tree);
|
|
191
|
+
} finally {
|
|
192
|
+
await tree.close();
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
private static async createTree(input: RecordsProjectionInput): Promise<SparseMerkleTree> {
|
|
197
|
+
const tree = new SparseMerkleTree(new SMTStoreMemory());
|
|
198
|
+
await tree.initialize();
|
|
199
|
+
|
|
200
|
+
try {
|
|
201
|
+
const messageCids = await RecordsProjection.getPrimaryMessageCids(input);
|
|
202
|
+
for (const messageCid of messageCids) {
|
|
203
|
+
await tree.insert(messageCid);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return tree;
|
|
207
|
+
} catch (error) {
|
|
208
|
+
await tree.close();
|
|
209
|
+
throw error;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
private static normalizeScope(scope: RecordsProjectionScope): NormalizedRecordsProjectionScope {
|
|
214
|
+
const protocol = RecordsProjection.requireNonEmptyString(scope.protocol, 'protocol');
|
|
215
|
+
|
|
216
|
+
if (scope.protocolPath !== undefined && scope.contextId !== undefined) {
|
|
217
|
+
throw new Error('RecordsProjection: protocolPath and contextId scopes are mutually exclusive.');
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (scope.protocolPath !== undefined) {
|
|
221
|
+
return {
|
|
222
|
+
protocol,
|
|
223
|
+
protocolPath: RecordsProjection.requireNonEmptyString(scope.protocolPath, 'protocolPath'),
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (scope.contextId !== undefined) {
|
|
228
|
+
return {
|
|
229
|
+
protocol,
|
|
230
|
+
contextId: RecordsProjection.requireNonEmptyString(scope.contextId, 'contextId'),
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return { protocol };
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
private static constructFilters(scope: NormalizedRecordsProjectionScope): Filter[] {
|
|
238
|
+
const baseFilter: Filter = {
|
|
239
|
+
interface : DwnInterfaceName.Records,
|
|
240
|
+
isLatestBaseState : true,
|
|
241
|
+
protocol : scope.protocol,
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
if ('protocolPath' in scope) {
|
|
245
|
+
return [{ ...baseFilter, protocolPath: scope.protocolPath }];
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if ('contextId' in scope) {
|
|
249
|
+
const childContextPrefix = `${scope.contextId}/`;
|
|
250
|
+
return [
|
|
251
|
+
{ ...baseFilter, contextId: scope.contextId },
|
|
252
|
+
{
|
|
253
|
+
...baseFilter,
|
|
254
|
+
contextId: FilterUtility.constructPrefixFilterAsRangeFilter(childContextPrefix),
|
|
255
|
+
},
|
|
256
|
+
];
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return [baseFilter];
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
private static requireNonEmptyString(value: string, field: string): string {
|
|
263
|
+
if (typeof value !== 'string' || value.length === 0) {
|
|
264
|
+
throw new Error(`RecordsProjection: ${field} must be a non-empty string.`);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return value;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
private static scopeKey(scope: NormalizedRecordsProjectionScope): string {
|
|
271
|
+
if ('protocolPath' in scope) {
|
|
272
|
+
return `${scope.protocol}\u001fprotocolPath\u001f${scope.protocolPath}`;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if ('contextId' in scope) {
|
|
276
|
+
return `${scope.protocol}\u001fcontextId\u001f${scope.contextId}`;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return `${scope.protocol}\u001fprotocol`;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
private static isProtocolWideScope(
|
|
283
|
+
scope: NormalizedRecordsProjectionScope,
|
|
284
|
+
): scope is { protocol: string } {
|
|
285
|
+
return !('protocolPath' in scope) && !('contextId' in scope);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
private static isContextScope(
|
|
289
|
+
scope: NormalizedRecordsProjectionScope,
|
|
290
|
+
): scope is { protocol: string; contextId: string } {
|
|
291
|
+
return 'contextId' in scope;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
private static contextIdSubsumes(parentContextId: string, childContextId: string): boolean {
|
|
295
|
+
return childContextId === parentContextId ||
|
|
296
|
+
childContextId.startsWith(`${parentContextId}/`);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
private static compareScopes(
|
|
300
|
+
a: NormalizedRecordsProjectionScope,
|
|
301
|
+
b: NormalizedRecordsProjectionScope,
|
|
302
|
+
): number {
|
|
303
|
+
const protocolCompare = lexicographicalCompare(a.protocol, b.protocol);
|
|
304
|
+
if (protocolCompare !== 0) {
|
|
305
|
+
return protocolCompare;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const aRank = RecordsProjection.scopeRank(a);
|
|
309
|
+
const bRank = RecordsProjection.scopeRank(b);
|
|
310
|
+
if (aRank !== bRank) {
|
|
311
|
+
return aRank - bRank;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
return lexicographicalCompare(RecordsProjection.scopeValue(a), RecordsProjection.scopeValue(b));
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
private static scopeRank(scope: NormalizedRecordsProjectionScope): number {
|
|
318
|
+
if ('protocolPath' in scope) { return 1; }
|
|
319
|
+
if ('contextId' in scope) { return 2; }
|
|
320
|
+
return 0;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
private static scopeValue(scope: NormalizedRecordsProjectionScope): string {
|
|
324
|
+
if ('protocolPath' in scope) { return scope.protocolPath; }
|
|
325
|
+
if ('contextId' in scope) { return scope.contextId; }
|
|
326
|
+
return '';
|
|
327
|
+
}
|
|
328
|
+
}
|
|
@@ -92,6 +92,7 @@ export type DelegatedGrantRecordsWriteMessage = {
|
|
|
92
92
|
export type GenericSignaturePayload = {
|
|
93
93
|
descriptorCid: string;
|
|
94
94
|
permissionGrantId?: string;
|
|
95
|
+
permissionGrantIds?: string[];
|
|
95
96
|
|
|
96
97
|
/**
|
|
97
98
|
* Record ID of a permission grant DWN `RecordsWrite` with `delegated` set to `true`.
|