cojson 0.10.8 → 0.11.0
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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +20 -0
- package/dist/CoValuesStore.d.ts +12 -0
- package/dist/CoValuesStore.d.ts.map +1 -0
- package/dist/PeerKnownStates.d.ts +38 -0
- package/dist/PeerKnownStates.d.ts.map +1 -0
- package/dist/PeerState.d.ts +46 -0
- package/dist/PeerState.d.ts.map +1 -0
- package/dist/PriorityBasedMessageQueue.d.ts +18 -0
- package/dist/PriorityBasedMessageQueue.d.ts.map +1 -0
- package/dist/SyncStateManager.d.ts +20 -0
- package/dist/SyncStateManager.d.ts.map +1 -0
- package/dist/base64url.d.ts +3 -0
- package/dist/base64url.d.ts.map +1 -0
- package/dist/base64url.test.d.ts +2 -0
- package/dist/base64url.test.d.ts.map +1 -0
- package/dist/coValue.d.ts +52 -0
- package/dist/coValue.d.ts.map +1 -0
- package/dist/coValueCore.d.ts +143 -0
- package/dist/coValueCore.d.ts.map +1 -0
- package/dist/coValueCore.js +3 -9
- package/dist/coValueCore.js.map +1 -1
- package/dist/coValueState.d.ts +58 -0
- package/dist/coValueState.d.ts.map +1 -0
- package/dist/coValues/account.d.ts +69 -0
- package/dist/coValues/account.d.ts.map +1 -0
- package/dist/coValues/account.js +9 -10
- package/dist/coValues/account.js.map +1 -1
- package/dist/coValues/coList.d.ts +163 -0
- package/dist/coValues/coList.d.ts.map +1 -0
- package/dist/coValues/coMap.d.ts +142 -0
- package/dist/coValues/coMap.d.ts.map +1 -0
- package/dist/coValues/coPlainText.d.ts +33 -0
- package/dist/coValues/coPlainText.d.ts.map +1 -0
- package/dist/coValues/coStream.d.ts +109 -0
- package/dist/coValues/coStream.d.ts.map +1 -0
- package/dist/coValues/group.d.ts +143 -0
- package/dist/coValues/group.d.ts.map +1 -0
- package/dist/coValues/group.js +52 -6
- package/dist/coValues/group.js.map +1 -1
- package/dist/coreToCoValue.d.ts +15 -0
- package/dist/coreToCoValue.d.ts.map +1 -0
- package/dist/crypto/PureJSCrypto.d.ts +50 -0
- package/dist/crypto/PureJSCrypto.d.ts.map +1 -0
- package/dist/crypto/WasmCrypto.d.ts +49 -0
- package/dist/crypto/WasmCrypto.d.ts.map +1 -0
- package/dist/crypto/crypto.d.ts +142 -0
- package/dist/crypto/crypto.d.ts.map +1 -0
- package/dist/exports.d.ts +84 -0
- package/dist/exports.d.ts.map +1 -0
- package/dist/ids.d.ts +23 -0
- package/dist/ids.d.ts.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/jsonStringify.d.ts +7 -0
- package/dist/jsonStringify.d.ts.map +1 -0
- package/dist/jsonValue.d.ts +45 -0
- package/dist/jsonValue.d.ts.map +1 -0
- package/dist/localNode.d.ts +111 -0
- package/dist/localNode.d.ts.map +1 -0
- package/dist/localNode.js +3 -5
- package/dist/localNode.js.map +1 -1
- package/dist/logger.d.ts +33 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/media.d.ts +8 -0
- package/dist/media.d.ts.map +1 -0
- package/dist/permissions.d.ts +24 -0
- package/dist/permissions.d.ts.map +1 -0
- package/dist/permissions.js +5 -9
- package/dist/permissions.js.map +1 -1
- package/dist/priority.d.ts +19 -0
- package/dist/priority.d.ts.map +1 -0
- package/dist/storage/FileSystem.d.ts +37 -0
- package/dist/storage/FileSystem.d.ts.map +1 -0
- package/dist/storage/chunksAndKnownStates.d.ts +7 -0
- package/dist/storage/chunksAndKnownStates.d.ts.map +1 -0
- package/dist/storage/index.d.ts +52 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/streamUtils.d.ts +13 -0
- package/dist/streamUtils.d.ts.map +1 -0
- package/dist/sync.d.ts +97 -0
- package/dist/sync.d.ts.map +1 -0
- package/dist/tests/PeerKnownStates.test.d.ts +2 -0
- package/dist/tests/PeerKnownStates.test.d.ts.map +1 -0
- package/dist/tests/PeerKnownStates.test.js +82 -0
- package/dist/tests/PeerKnownStates.test.js.map +1 -0
- package/dist/tests/PeerState.test.d.ts +2 -0
- package/dist/tests/PeerState.test.d.ts.map +1 -0
- package/dist/tests/PeerState.test.js +188 -0
- package/dist/tests/PeerState.test.js.map +1 -0
- package/dist/tests/PriorityBasedMessageQueue.test.d.ts +2 -0
- package/dist/tests/PriorityBasedMessageQueue.test.d.ts.map +1 -0
- package/dist/tests/PriorityBasedMessageQueue.test.js +120 -0
- package/dist/tests/PriorityBasedMessageQueue.test.js.map +1 -0
- package/dist/tests/SyncStateManager.test.d.ts +2 -0
- package/dist/tests/SyncStateManager.test.d.ts.map +1 -0
- package/dist/tests/SyncStateManager.test.js +127 -0
- package/dist/tests/SyncStateManager.test.js.map +1 -0
- package/dist/tests/account.test.d.ts +2 -0
- package/dist/tests/account.test.d.ts.map +1 -0
- package/dist/tests/account.test.js +66 -0
- package/dist/tests/account.test.js.map +1 -0
- package/dist/tests/coList.test.d.ts +2 -0
- package/dist/tests/coList.test.d.ts.map +1 -0
- package/dist/tests/coList.test.js +120 -0
- package/dist/tests/coList.test.js.map +1 -0
- package/dist/tests/coMap.test.d.ts +2 -0
- package/dist/tests/coMap.test.d.ts.map +1 -0
- package/dist/tests/coMap.test.js +164 -0
- package/dist/tests/coMap.test.js.map +1 -0
- package/dist/tests/coPlainText.test.d.ts +2 -0
- package/dist/tests/coPlainText.test.d.ts.map +1 -0
- package/dist/tests/coPlainText.test.js +99 -0
- package/dist/tests/coPlainText.test.js.map +1 -0
- package/dist/tests/coStream.test.d.ts +2 -0
- package/dist/tests/coStream.test.d.ts.map +1 -0
- package/dist/tests/coStream.test.js +206 -0
- package/dist/tests/coStream.test.js.map +1 -0
- package/dist/tests/coValueCore.test.d.ts +2 -0
- package/dist/tests/coValueCore.test.d.ts.map +1 -0
- package/dist/tests/coValueCore.test.js +164 -0
- package/dist/tests/coValueCore.test.js.map +1 -0
- package/dist/tests/coValueState.test.d.ts +2 -0
- package/dist/tests/coValueState.test.d.ts.map +1 -0
- package/dist/tests/coValueState.test.js +364 -0
- package/dist/tests/coValueState.test.js.map +1 -0
- package/dist/tests/crypto.test.d.ts +2 -0
- package/dist/tests/crypto.test.d.ts.map +1 -0
- package/dist/tests/crypto.test.js +144 -0
- package/dist/tests/crypto.test.js.map +1 -0
- package/dist/tests/cryptoImpl.test.d.ts +2 -0
- package/dist/tests/cryptoImpl.test.d.ts.map +1 -0
- package/dist/tests/cryptoImpl.test.js +144 -0
- package/dist/tests/cryptoImpl.test.js.map +1 -0
- package/dist/tests/group.test.d.ts +2 -0
- package/dist/tests/group.test.d.ts.map +1 -0
- package/dist/tests/group.test.js +576 -0
- package/dist/tests/group.test.js.map +1 -0
- package/dist/tests/logger.test.d.ts +2 -0
- package/dist/tests/logger.test.d.ts.map +1 -0
- package/dist/tests/logger.test.js +118 -0
- package/dist/tests/logger.test.js.map +1 -0
- package/dist/tests/permissions.test.d.ts +2 -0
- package/dist/tests/permissions.test.d.ts.map +1 -0
- package/dist/tests/permissions.test.js +2051 -0
- package/dist/tests/permissions.test.js.map +1 -0
- package/dist/tests/priority.test.d.ts +2 -0
- package/dist/tests/priority.test.d.ts.map +1 -0
- package/dist/tests/priority.test.js +61 -0
- package/dist/tests/priority.test.js.map +1 -0
- package/dist/tests/sync.test.d.ts +2 -0
- package/dist/tests/sync.test.d.ts.map +1 -0
- package/dist/tests/sync.test.js +1548 -0
- package/dist/tests/sync.test.js.map +1 -0
- package/dist/tests/testUtils.d.ts +142 -0
- package/dist/tests/testUtils.d.ts.map +1 -0
- package/dist/tests/testUtils.js +315 -0
- package/dist/tests/testUtils.js.map +1 -0
- package/dist/typeUtils/accountOrAgentIDfromSessionID.d.ts +4 -0
- package/dist/typeUtils/accountOrAgentIDfromSessionID.d.ts.map +1 -0
- package/dist/typeUtils/expectGroup.d.ts +4 -0
- package/dist/typeUtils/expectGroup.d.ts.map +1 -0
- package/dist/typeUtils/isAccountID.d.ts +4 -0
- package/dist/typeUtils/isAccountID.d.ts.map +1 -0
- package/dist/typeUtils/isCoValue.d.ts +4 -0
- package/dist/typeUtils/isCoValue.d.ts.map +1 -0
- package/dist/utils.d.ts +5 -0
- package/dist/utils.d.ts.map +1 -0
- package/package.json +6 -6
- package/src/coValueCore.ts +3 -9
- package/src/coValues/account.ts +15 -15
- package/src/coValues/group.ts +85 -12
- package/src/jsonValue.ts +9 -5
- package/src/localNode.ts +3 -5
- package/src/permissions.ts +5 -15
- package/src/tests/coValueCore.test.ts +2 -2
- package/src/tests/group.test.ts +187 -0
- package/src/tests/permissions.test.ts +330 -57
- package/src/tests/testUtils.ts +4 -1
- package/tsconfig.json +4 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"isCoValue.d.ts","sourceRoot":"","sources":["../../src/typeUtils/isCoValue.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AAKhD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,wBAAgB,SAAS,CACvB,KAAK,EAAE,SAAS,GAAG,UAAU,GAAG,SAAS,GACxC,KAAK,IAAI,UAAU,CAOrB"}
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,UAAU,CAAC,CAAC,EAAE,OAAO,GAAG;IACtC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB,CAKA"}
|
package/package.json
CHANGED
|
@@ -2,22 +2,22 @@
|
|
|
2
2
|
"name": "cojson",
|
|
3
3
|
"module": "dist/index.js",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
|
-
"types": "
|
|
5
|
+
"types": "dist/index.d.ts",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": {
|
|
8
|
-
"types": "./
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
9
|
"default": "./dist/index.js"
|
|
10
10
|
},
|
|
11
11
|
"./dist/crypto/PureJSCrypto": {
|
|
12
|
-
"types": "./
|
|
12
|
+
"types": "./dist/crypto/PureJSCrypto.d.ts",
|
|
13
13
|
"default": "./dist/crypto/PureJSCrypto.js"
|
|
14
14
|
},
|
|
15
15
|
"./crypto/PureJSCrypto": {
|
|
16
|
-
"types": "./
|
|
16
|
+
"types": "./dist/crypto/PureJSCrypto.d.ts",
|
|
17
17
|
"default": "./dist/crypto/PureJSCrypto.js"
|
|
18
18
|
},
|
|
19
19
|
"./crypto/WasmCrypto": {
|
|
20
|
-
"types": "./
|
|
20
|
+
"types": "./dist/crypto/WasmCrypto.d.ts",
|
|
21
21
|
"default": "./dist/crypto/WasmCrypto.js"
|
|
22
22
|
},
|
|
23
23
|
"./dist/*": "./dist/*",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
},
|
|
26
26
|
"type": "module",
|
|
27
27
|
"license": "MIT",
|
|
28
|
-
"version": "0.
|
|
28
|
+
"version": "0.11.0",
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@opentelemetry/sdk-metrics": "^1.29.0",
|
|
31
31
|
"typescript": "~5.6.2",
|
package/src/coValueCore.ts
CHANGED
|
@@ -184,9 +184,7 @@ export class CoValueCore {
|
|
|
184
184
|
this.header.meta?.type === "account"
|
|
185
185
|
? (this.node.currentSessionID.replace(
|
|
186
186
|
this.node.account.id,
|
|
187
|
-
this.node.account
|
|
188
|
-
.currentAgentID()
|
|
189
|
-
._unsafeUnwrap({ withStackTrace: true }),
|
|
187
|
+
this.node.account.currentAgentID(),
|
|
190
188
|
) as SessionID)
|
|
191
189
|
: this.node.currentSessionID;
|
|
192
190
|
|
|
@@ -455,9 +453,7 @@ export class CoValueCore {
|
|
|
455
453
|
this.header.meta?.type === "account"
|
|
456
454
|
? (this.node.currentSessionID.replace(
|
|
457
455
|
this.node.account.id,
|
|
458
|
-
this.node.account
|
|
459
|
-
.currentAgentID()
|
|
460
|
-
._unsafeUnwrap({ withStackTrace: true }),
|
|
456
|
+
this.node.account.currentAgentID(),
|
|
461
457
|
) as SessionID)
|
|
462
458
|
: this.node.currentSessionID;
|
|
463
459
|
|
|
@@ -639,9 +635,7 @@ export class CoValueCore {
|
|
|
639
635
|
// Try to find key revelation for us
|
|
640
636
|
const lookupAccountOrAgentID =
|
|
641
637
|
this.header.meta?.type === "account"
|
|
642
|
-
? this.node.account
|
|
643
|
-
.currentAgentID()
|
|
644
|
-
._unsafeUnwrap({ withStackTrace: true })
|
|
638
|
+
? this.node.account.currentAgentID()
|
|
645
639
|
: this.node.account.id;
|
|
646
640
|
|
|
647
641
|
const lastReadyKeyEdit = content.lastEditAt(
|
package/src/coValues/account.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { Result, ok } from "neverthrow";
|
|
2
1
|
import { CoID, RawCoValue } from "../coValue.js";
|
|
3
2
|
import {
|
|
4
3
|
CoValueCore,
|
|
@@ -47,10 +46,11 @@ export class RawAccount<
|
|
|
47
46
|
> extends RawGroup<Meta> {
|
|
48
47
|
_cachedCurrentAgentID: AgentID | undefined;
|
|
49
48
|
|
|
50
|
-
currentAgentID():
|
|
49
|
+
currentAgentID(): AgentID {
|
|
51
50
|
if (this._cachedCurrentAgentID) {
|
|
52
|
-
return
|
|
51
|
+
return this._cachedCurrentAgentID;
|
|
53
52
|
}
|
|
53
|
+
|
|
54
54
|
const agents = this.keys()
|
|
55
55
|
.filter((k): k is AgentID => k.startsWith("sealer_"))
|
|
56
56
|
.sort(
|
|
@@ -65,7 +65,7 @@ export class RawAccount<
|
|
|
65
65
|
|
|
66
66
|
this._cachedCurrentAgentID = agents[0];
|
|
67
67
|
|
|
68
|
-
return
|
|
68
|
+
return agents[0]!;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
createInvite(_: AccountRole): InviteSecret {
|
|
@@ -77,10 +77,10 @@ export interface ControlledAccountOrAgent {
|
|
|
77
77
|
id: RawAccountID | AgentID;
|
|
78
78
|
agentSecret: AgentSecret;
|
|
79
79
|
|
|
80
|
-
currentAgentID: () =>
|
|
81
|
-
currentSignerID: () =>
|
|
80
|
+
currentAgentID: () => AgentID;
|
|
81
|
+
currentSignerID: () => SignerID;
|
|
82
82
|
currentSignerSecret: () => SignerSecret;
|
|
83
|
-
currentSealerID: () =>
|
|
83
|
+
currentSealerID: () => SealerID;
|
|
84
84
|
currentSealerSecret: () => SealerSecret;
|
|
85
85
|
}
|
|
86
86
|
|
|
@@ -116,17 +116,17 @@ export class RawControlledAccount<Meta extends AccountMeta = AccountMeta>
|
|
|
116
116
|
return this.core.node.acceptInvite(groupOrOwnedValueID, inviteSecret);
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
currentAgentID():
|
|
119
|
+
currentAgentID(): AgentID {
|
|
120
120
|
if (this._cachedCurrentAgentID) {
|
|
121
|
-
return
|
|
121
|
+
return this._cachedCurrentAgentID;
|
|
122
122
|
}
|
|
123
123
|
const agentID = this.crypto.getAgentID(this.agentSecret);
|
|
124
124
|
this._cachedCurrentAgentID = agentID;
|
|
125
|
-
return
|
|
125
|
+
return agentID;
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
currentSignerID() {
|
|
129
|
-
return this.
|
|
129
|
+
return this.crypto.getAgentSignerID(this.currentAgentID());
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
currentSignerSecret(): SignerSecret {
|
|
@@ -134,7 +134,7 @@ export class RawControlledAccount<Meta extends AccountMeta = AccountMeta>
|
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
currentSealerID() {
|
|
137
|
-
return this.
|
|
137
|
+
return this.crypto.getAgentSealerID(this.currentAgentID());
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
currentSealerSecret(): SealerSecret {
|
|
@@ -153,11 +153,11 @@ export class ControlledAgent implements ControlledAccountOrAgent {
|
|
|
153
153
|
}
|
|
154
154
|
|
|
155
155
|
currentAgentID() {
|
|
156
|
-
return
|
|
156
|
+
return this.crypto.getAgentID(this.agentSecret);
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
currentSignerID() {
|
|
160
|
-
return this.
|
|
160
|
+
return this.crypto.getAgentSignerID(this.currentAgentID());
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
currentSignerSecret(): SignerSecret {
|
|
@@ -165,7 +165,7 @@ export class ControlledAgent implements ControlledAccountOrAgent {
|
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
currentSealerID() {
|
|
168
|
-
return this.
|
|
168
|
+
return this.crypto.getAgentSealerID(this.currentAgentID());
|
|
169
169
|
}
|
|
170
170
|
|
|
171
171
|
currentSealerSecret(): SealerSecret {
|
package/src/coValues/group.ts
CHANGED
|
@@ -29,7 +29,12 @@ import { RawBinaryCoStream, RawCoStream } from "./coStream.js";
|
|
|
29
29
|
export const EVERYONE = "everyone" as const;
|
|
30
30
|
export type Everyone = "everyone";
|
|
31
31
|
|
|
32
|
-
export type ParentGroupReferenceRole =
|
|
32
|
+
export type ParentGroupReferenceRole =
|
|
33
|
+
| "revoked"
|
|
34
|
+
| "extend"
|
|
35
|
+
| "reader"
|
|
36
|
+
| "writer"
|
|
37
|
+
| "admin";
|
|
33
38
|
|
|
34
39
|
export type GroupShape = {
|
|
35
40
|
profile: CoID<RawCoMap> | null;
|
|
@@ -45,7 +50,7 @@ export type GroupShape = {
|
|
|
45
50
|
{ encryptedID: KeyID; encryptingID: KeyID }
|
|
46
51
|
>;
|
|
47
52
|
[parent: ParentGroupReference]: ParentGroupReferenceRole;
|
|
48
|
-
[child: ChildGroupReference]: "extend";
|
|
53
|
+
[child: ChildGroupReference]: "revoked" | "extend";
|
|
49
54
|
};
|
|
50
55
|
|
|
51
56
|
/** A `Group` is a scope for permissions of its members (`"reader" | "writer" | "admin"`), applying to objects owned by that group.
|
|
@@ -77,7 +82,7 @@ export class RawGroup<
|
|
|
77
82
|
*
|
|
78
83
|
* @category 1. Role reading
|
|
79
84
|
*/
|
|
80
|
-
roleOf(accountID: RawAccountID): Role | undefined {
|
|
85
|
+
roleOf(accountID: RawAccountID | typeof EVERYONE): Role | undefined {
|
|
81
86
|
return this.roleOfInternal(accountID)?.role;
|
|
82
87
|
}
|
|
83
88
|
|
|
@@ -85,15 +90,15 @@ export class RawGroup<
|
|
|
85
90
|
roleOfInternal(
|
|
86
91
|
accountID: RawAccountID | AgentID | typeof EVERYONE,
|
|
87
92
|
): { role: Role; via: CoID<RawGroup> | undefined } | undefined {
|
|
88
|
-
|
|
93
|
+
let roleHere = this.get(accountID);
|
|
89
94
|
|
|
90
95
|
if (roleHere === "revoked") {
|
|
91
|
-
|
|
96
|
+
roleHere = undefined;
|
|
92
97
|
}
|
|
93
98
|
|
|
94
99
|
let roleInfo:
|
|
95
100
|
| {
|
|
96
|
-
role:
|
|
101
|
+
role: Role;
|
|
97
102
|
via: CoID<RawGroup> | undefined;
|
|
98
103
|
}
|
|
99
104
|
| undefined = roleHere && { role: roleHere, via: undefined };
|
|
@@ -114,6 +119,12 @@ export class RawGroup<
|
|
|
114
119
|
}
|
|
115
120
|
}
|
|
116
121
|
|
|
122
|
+
if (!roleInfo && accountID !== "everyone") {
|
|
123
|
+
const everyoneRole = this.get("everyone");
|
|
124
|
+
|
|
125
|
+
if (everyoneRole) return { role: everyoneRole, via: undefined };
|
|
126
|
+
}
|
|
127
|
+
|
|
117
128
|
return roleInfo;
|
|
118
129
|
}
|
|
119
130
|
|
|
@@ -122,6 +133,11 @@ export class RawGroup<
|
|
|
122
133
|
|
|
123
134
|
for (const key of this.keys()) {
|
|
124
135
|
if (isParentGroupReference(key)) {
|
|
136
|
+
// Check if the parent group reference is revoked
|
|
137
|
+
if (this.get(key) === "revoked") {
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
|
|
125
141
|
const parent = this.core.node.expectCoValueLoaded(
|
|
126
142
|
getParentGroupId(key),
|
|
127
143
|
"Expected parent group to be loaded",
|
|
@@ -183,6 +199,11 @@ export class RawGroup<
|
|
|
183
199
|
|
|
184
200
|
for (const key of this.keys()) {
|
|
185
201
|
if (isChildGroupReference(key)) {
|
|
202
|
+
// Check if the child group reference is revoked
|
|
203
|
+
if (this.get(key) === "revoked") {
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
|
|
186
207
|
const child = this.core.node.expectCoValueLoaded(
|
|
187
208
|
getChildGroupId(key),
|
|
188
209
|
"Expected child group to be loaded",
|
|
@@ -251,9 +272,7 @@ export class RawGroup<
|
|
|
251
272
|
|
|
252
273
|
const memberKey = typeof account === "string" ? account : account.id;
|
|
253
274
|
const agent =
|
|
254
|
-
typeof account === "string"
|
|
255
|
-
? account
|
|
256
|
-
: account.currentAgentID()._unsafeUnwrap({ withStackTrace: true });
|
|
275
|
+
typeof account === "string" ? account : account.currentAgentID();
|
|
257
276
|
|
|
258
277
|
/**
|
|
259
278
|
* WriteOnly members can only see their own changes.
|
|
@@ -387,6 +406,18 @@ export class RawGroup<
|
|
|
387
406
|
});
|
|
388
407
|
}
|
|
389
408
|
|
|
409
|
+
getAllMemberKeysSet() {
|
|
410
|
+
const memberKeys = new Set(this.getMemberKeys());
|
|
411
|
+
|
|
412
|
+
for (const { group } of this.getParentGroups()) {
|
|
413
|
+
for (const key of group.getAllMemberKeysSet()) {
|
|
414
|
+
memberKeys.add(key);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
return memberKeys;
|
|
419
|
+
}
|
|
420
|
+
|
|
390
421
|
/** @internal */
|
|
391
422
|
rotateReadKey(removedMemberKey?: RawAccountID | AgentID | "everyone") {
|
|
392
423
|
const memberKeys = this.getMemberKeys().filter(
|
|
@@ -608,6 +639,43 @@ export class RawGroup<
|
|
|
608
639
|
);
|
|
609
640
|
}
|
|
610
641
|
|
|
642
|
+
async revokeExtend(parent: RawGroup) {
|
|
643
|
+
if (this.myRole() !== "admin") {
|
|
644
|
+
throw new Error(
|
|
645
|
+
"To unextend a group, the current account must be an admin in the child group",
|
|
646
|
+
);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
if (
|
|
650
|
+
parent.myRole() !== "admin" &&
|
|
651
|
+
parent.myRole() !== "writer" &&
|
|
652
|
+
parent.myRole() !== "reader" &&
|
|
653
|
+
parent.myRole() !== "writeOnly"
|
|
654
|
+
) {
|
|
655
|
+
throw new Error(
|
|
656
|
+
"To unextend a group, the current account must be a member of the parent group",
|
|
657
|
+
);
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
if (
|
|
661
|
+
!this.get(`parent_${parent.id}`) ||
|
|
662
|
+
this.get(`parent_${parent.id}`) === "revoked"
|
|
663
|
+
) {
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
// Set the parent key on the child group to `revoked`
|
|
668
|
+
this.set(`parent_${parent.id}`, "revoked", "trusting");
|
|
669
|
+
|
|
670
|
+
// Set the child key on the parent group to `revoked`
|
|
671
|
+
parent.set(`child_${this.id}`, "revoked", "trusting");
|
|
672
|
+
|
|
673
|
+
await this.loadAllChildGroups();
|
|
674
|
+
|
|
675
|
+
// Rotate the keys on the child group
|
|
676
|
+
this.rotateReadKey();
|
|
677
|
+
}
|
|
678
|
+
|
|
611
679
|
/**
|
|
612
680
|
* Strips the specified member of all roles (preventing future writes in
|
|
613
681
|
* the group and owned values) and rotates the read encryption key for that group
|
|
@@ -783,8 +851,9 @@ export class RawGroup<
|
|
|
783
851
|
|
|
784
852
|
export function isInheritableRole(
|
|
785
853
|
roleInParent: Role | undefined,
|
|
786
|
-
): roleInParent is "admin" | "writer" | "reader" {
|
|
854
|
+
): roleInParent is "revoked" | "admin" | "writer" | "reader" {
|
|
787
855
|
return (
|
|
856
|
+
roleInParent === "revoked" ||
|
|
788
857
|
roleInParent === "admin" ||
|
|
789
858
|
roleInParent === "writer" ||
|
|
790
859
|
roleInParent === "reader"
|
|
@@ -792,9 +861,13 @@ export function isInheritableRole(
|
|
|
792
861
|
}
|
|
793
862
|
|
|
794
863
|
function isMorePermissiveAndShouldInherit(
|
|
795
|
-
roleInParent: "admin" | "writer" | "reader",
|
|
796
|
-
roleInChild:
|
|
864
|
+
roleInParent: "revoked" | "admin" | "writer" | "reader",
|
|
865
|
+
roleInChild: Role | undefined,
|
|
797
866
|
) {
|
|
867
|
+
if (roleInParent === "revoked") {
|
|
868
|
+
return true;
|
|
869
|
+
}
|
|
870
|
+
|
|
798
871
|
if (roleInParent === "admin") {
|
|
799
872
|
return !roleInChild || roleInChild !== "admin";
|
|
800
873
|
}
|
package/src/jsonValue.ts
CHANGED
|
@@ -8,11 +8,15 @@ export type JsonObject = { [key: string]: JsonValue | undefined };
|
|
|
8
8
|
type AtLeastOne<T, U = { [K in keyof T]: Pick<T, K> }> = Partial<T> &
|
|
9
9
|
U[keyof U];
|
|
10
10
|
type ExcludeEmpty<T> = T extends AtLeastOne<T> ? T : never;
|
|
11
|
+
type ExcludeNull<T> = T extends null ? never : T;
|
|
12
|
+
type IsFunction<T> = T extends (...args: any[]) => any ? true : false;
|
|
13
|
+
type ContainsSymbolKeys<T> = keyof ExcludeNull<T> extends symbol ? true : false;
|
|
11
14
|
|
|
12
|
-
export type CoJsonValue<T> =
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
export type CoJsonValue<T> = IsFunction<T> extends true
|
|
16
|
+
? "Functions are not allowed"
|
|
17
|
+
: ContainsSymbolKeys<T> extends true
|
|
18
|
+
? "Only string or number keys are allowed"
|
|
19
|
+
: JsonValue | CoJsonObjectWithIndex<T> | CoJsonArray<T>;
|
|
16
20
|
export type CoJsonArray<T> = CoJsonValue<T>[] | readonly CoJsonValue<T>[];
|
|
17
21
|
|
|
18
22
|
/**
|
|
@@ -25,7 +29,7 @@ export type CoJsonArray<T> = CoJsonValue<T>[] | readonly CoJsonValue<T>[];
|
|
|
25
29
|
* Applying the ExcludeEmpty type here to make sure we don't accept functions or non-serializable values
|
|
26
30
|
*/
|
|
27
31
|
export type CoJsonObjectWithIndex<T> = ExcludeEmpty<{
|
|
28
|
-
[K in keyof T
|
|
32
|
+
[K in keyof T]: CoJsonValue1L<T[K]> | undefined;
|
|
29
33
|
}>;
|
|
30
34
|
|
|
31
35
|
/**
|
package/src/localNode.ts
CHANGED
|
@@ -530,7 +530,7 @@ export class LocalNode {
|
|
|
530
530
|
} satisfies UnexpectedlyNotAccountError);
|
|
531
531
|
}
|
|
532
532
|
|
|
533
|
-
return (coValue.getCurrentContent() as RawAccount).currentAgentID();
|
|
533
|
+
return ok((coValue.getCurrentContent() as RawAccount).currentAgentID());
|
|
534
534
|
}
|
|
535
535
|
|
|
536
536
|
resolveAccountAgentAsync(
|
|
@@ -573,7 +573,7 @@ export class LocalNode {
|
|
|
573
573
|
} satisfies UnexpectedlyNotAccountError);
|
|
574
574
|
}
|
|
575
575
|
|
|
576
|
-
return (coValue.getCurrentContent() as RawAccount).currentAgentID();
|
|
576
|
+
return ok((coValue.getCurrentContent() as RawAccount).currentAgentID());
|
|
577
577
|
});
|
|
578
578
|
}
|
|
579
579
|
|
|
@@ -601,9 +601,7 @@ export class LocalNode {
|
|
|
601
601
|
this.crypto.seal({
|
|
602
602
|
message: readKey.secret,
|
|
603
603
|
from: this.account.currentSealerSecret(),
|
|
604
|
-
to: this.account
|
|
605
|
-
.currentSealerID()
|
|
606
|
-
._unsafeUnwrap({ withStackTrace: true }),
|
|
604
|
+
to: this.account.currentSealerID(),
|
|
607
605
|
nOnceMaterial: {
|
|
608
606
|
in: groupCoValue.id,
|
|
609
607
|
tx: groupCoValue.nextTransactionID(),
|
package/src/permissions.ts
CHANGED
|
@@ -56,7 +56,7 @@ function logPermissionError(
|
|
|
56
56
|
return;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
logger.
|
|
59
|
+
logger.debug("Permission error: " + message, attributes);
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
export function determineValidTransactions(
|
|
@@ -101,8 +101,7 @@ export function determineValidTransactions(
|
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
const transactorRoleAtTxTime =
|
|
104
|
-
groupAtTime.roleOfInternal(effectiveTransactor)?.role
|
|
105
|
-
groupAtTime.roleOfInternal(EVERYONE)?.role;
|
|
104
|
+
groupAtTime.roleOfInternal(effectiveTransactor)?.role;
|
|
106
105
|
|
|
107
106
|
if (
|
|
108
107
|
transactorRoleAtTxTime !== "admin" &&
|
|
@@ -135,8 +134,8 @@ export function determineValidTransactions(
|
|
|
135
134
|
}
|
|
136
135
|
|
|
137
136
|
function isHigherRole(a: Role, b: Role | undefined) {
|
|
138
|
-
if (a === undefined) return false;
|
|
139
|
-
if (b === undefined) return true;
|
|
137
|
+
if (a === undefined || a === "revoked") return false;
|
|
138
|
+
if (b === undefined || b === "revoked") return true;
|
|
140
139
|
if (b === "admin") return false;
|
|
141
140
|
if (a === "admin") return true;
|
|
142
141
|
|
|
@@ -476,16 +475,7 @@ function agentInAccountOrMemberInGroup(
|
|
|
476
475
|
groupAtTime: RawGroup,
|
|
477
476
|
): RawAccountID | AgentID | undefined {
|
|
478
477
|
if (transactor === groupAtTime.id && groupAtTime instanceof RawAccount) {
|
|
479
|
-
return groupAtTime.currentAgentID()
|
|
480
|
-
(agentID) => agentID,
|
|
481
|
-
(e) => {
|
|
482
|
-
logger.error(
|
|
483
|
-
"Error while determining current agent ID in valid transactions",
|
|
484
|
-
e,
|
|
485
|
-
);
|
|
486
|
-
return undefined;
|
|
487
|
-
},
|
|
488
|
-
);
|
|
478
|
+
return groupAtTime.currentAgentID();
|
|
489
479
|
}
|
|
490
480
|
return transactor;
|
|
491
481
|
}
|
|
@@ -155,7 +155,7 @@ test("New transactions in a group correctly update owned values, including subsc
|
|
|
155
155
|
|
|
156
156
|
map.subscribe(listener);
|
|
157
157
|
|
|
158
|
-
expect(listener.mock.calls[0][0].get("hello")).toBe("world");
|
|
158
|
+
expect(listener.mock.calls[0]?.[0].get("hello")).toBe("world");
|
|
159
159
|
|
|
160
160
|
const resignationThatWeJustLearnedAbout = {
|
|
161
161
|
privacy: "trusting",
|
|
@@ -192,7 +192,7 @@ test("New transactions in a group correctly update owned values, including subsc
|
|
|
192
192
|
expect(manuallyAdddedTxSuccess).toBe(true);
|
|
193
193
|
|
|
194
194
|
expect(listener.mock.calls.length).toBe(2);
|
|
195
|
-
expect(listener.mock.calls[1][0].get("hello")).toBe(undefined);
|
|
195
|
+
expect(listener.mock.calls[1]?.[0].get("hello")).toBe(undefined);
|
|
196
196
|
|
|
197
197
|
expect(map.core.getValidSortedTransactions().length).toBe(0);
|
|
198
198
|
});
|
package/src/tests/group.test.ts
CHANGED
|
@@ -657,6 +657,79 @@ describe("extend", () => {
|
|
|
657
657
|
});
|
|
658
658
|
});
|
|
659
659
|
|
|
660
|
+
describe("unextend", () => {
|
|
661
|
+
test("should revoke roles", async () => {
|
|
662
|
+
const { node1, node2, node3 } = await createThreeConnectedNodes(
|
|
663
|
+
"server",
|
|
664
|
+
"server",
|
|
665
|
+
"server",
|
|
666
|
+
);
|
|
667
|
+
|
|
668
|
+
// `parentGroup` has `alice` as a writer
|
|
669
|
+
const parentGroup = node1.node.createGroup();
|
|
670
|
+
const alice = await loadCoValueOrFail(node1.node, node2.accountID);
|
|
671
|
+
parentGroup.addMember(alice, "writer");
|
|
672
|
+
// `alice`'s role in `parentGroup` is `"writer"`
|
|
673
|
+
expect(parentGroup.roleOf(alice.id)).toBe("writer");
|
|
674
|
+
|
|
675
|
+
// `childGroup` has `bob` as a reader
|
|
676
|
+
const childGroup = node1.node.createGroup();
|
|
677
|
+
const bob = await loadCoValueOrFail(node1.node, node3.accountID);
|
|
678
|
+
childGroup.addMember(bob, "reader");
|
|
679
|
+
// `bob`'s role in `childGroup` is `"reader"`
|
|
680
|
+
expect(childGroup.roleOf(bob.id)).toBe("reader");
|
|
681
|
+
|
|
682
|
+
// `childGroup` has `parentGroup`'s members (in this case, `alice` as a writer)
|
|
683
|
+
childGroup.extend(parentGroup);
|
|
684
|
+
expect(childGroup.roleOf(alice.id)).toBe("writer");
|
|
685
|
+
|
|
686
|
+
// `childGroup` no longer has `parentGroup`'s members
|
|
687
|
+
await childGroup.revokeExtend(parentGroup);
|
|
688
|
+
expect(childGroup.roleOf(bob.id)).toBe("reader");
|
|
689
|
+
expect(childGroup.roleOf(alice.id)).toBe(undefined);
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
test("should do nothing if applied to a group that is not extended", async () => {
|
|
693
|
+
const { node1, node2, node3 } = await createThreeConnectedNodes(
|
|
694
|
+
"server",
|
|
695
|
+
"server",
|
|
696
|
+
"server",
|
|
697
|
+
);
|
|
698
|
+
|
|
699
|
+
const parentGroup = node1.node.createGroup();
|
|
700
|
+
const alice = await loadCoValueOrFail(node1.node, node2.accountID);
|
|
701
|
+
parentGroup.addMember(alice, "writer");
|
|
702
|
+
const childGroup = node1.node.createGroup();
|
|
703
|
+
const bob = await loadCoValueOrFail(node1.node, node3.accountID);
|
|
704
|
+
childGroup.addMember(bob, "reader");
|
|
705
|
+
await childGroup.revokeExtend(parentGroup);
|
|
706
|
+
expect(childGroup.roleOf(bob.id)).toBe("reader");
|
|
707
|
+
expect(childGroup.roleOf(alice.id)).toBe(undefined);
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
test("should not throw if the revokeExtend is called twice", async () => {
|
|
711
|
+
const { node1, node2, node3 } = await createThreeConnectedNodes(
|
|
712
|
+
"server",
|
|
713
|
+
"server",
|
|
714
|
+
"server",
|
|
715
|
+
);
|
|
716
|
+
|
|
717
|
+
const parentGroup = node1.node.createGroup();
|
|
718
|
+
const alice = await loadCoValueOrFail(node1.node, node2.accountID);
|
|
719
|
+
parentGroup.addMember(alice, "writer");
|
|
720
|
+
const childGroup = node1.node.createGroup();
|
|
721
|
+
const bob = await loadCoValueOrFail(node1.node, node3.accountID);
|
|
722
|
+
childGroup.addMember(bob, "reader");
|
|
723
|
+
|
|
724
|
+
childGroup.extend(parentGroup);
|
|
725
|
+
|
|
726
|
+
await childGroup.revokeExtend(parentGroup);
|
|
727
|
+
await childGroup.revokeExtend(parentGroup);
|
|
728
|
+
expect(childGroup.roleOf(bob.id)).toBe("reader");
|
|
729
|
+
expect(childGroup.roleOf(alice.id)).toBe(undefined);
|
|
730
|
+
});
|
|
731
|
+
});
|
|
732
|
+
|
|
660
733
|
describe("extend with role mapping", () => {
|
|
661
734
|
test("mapping to writer should add the ability to write", async () => {
|
|
662
735
|
const { node1, node2 } = await createTwoConnectedNodes("server", "server");
|
|
@@ -842,3 +915,117 @@ describe("extend with role mapping", () => {
|
|
|
842
915
|
expect(mapOnNode2.get("test")).toEqual("Written from the admin");
|
|
843
916
|
});
|
|
844
917
|
});
|
|
918
|
+
test("roleOf should prioritize explicit account role over everyone role in same group", async () => {
|
|
919
|
+
const { node1, node2 } = await createTwoConnectedNodes("server", "server");
|
|
920
|
+
|
|
921
|
+
const group = node1.node.createGroup();
|
|
922
|
+
const account2 = await loadCoValueOrFail(node1.node, node2.accountID);
|
|
923
|
+
|
|
924
|
+
// Add both everyone and specific account
|
|
925
|
+
group.addMember("everyone", "reader");
|
|
926
|
+
group.addMember(account2, "writer");
|
|
927
|
+
|
|
928
|
+
// Should return the explicit role, not everyone's role
|
|
929
|
+
expect(group.roleOf(node2.accountID)).toEqual("writer");
|
|
930
|
+
|
|
931
|
+
// Change everyone's role
|
|
932
|
+
group.addMember("everyone", "writer");
|
|
933
|
+
|
|
934
|
+
// Should still return the explicit role
|
|
935
|
+
expect(group.roleOf(node2.accountID)).toEqual("writer");
|
|
936
|
+
});
|
|
937
|
+
|
|
938
|
+
test("roleOf should prioritize inherited everyone role over explicit account role", async () => {
|
|
939
|
+
const { node1, node2 } = await createTwoConnectedNodes("server", "server");
|
|
940
|
+
|
|
941
|
+
const parentGroup = node1.node.createGroup();
|
|
942
|
+
const childGroup = node1.node.createGroup();
|
|
943
|
+
const account2 = await loadCoValueOrFail(node1.node, node2.accountID);
|
|
944
|
+
|
|
945
|
+
// Set up inheritance
|
|
946
|
+
childGroup.extend(parentGroup);
|
|
947
|
+
|
|
948
|
+
// Add everyone to parent and account to child
|
|
949
|
+
parentGroup.addMember("everyone", "writer");
|
|
950
|
+
childGroup.addMember(account2, "reader");
|
|
951
|
+
|
|
952
|
+
// Should return the explicit role from child, not inherited everyone role
|
|
953
|
+
expect(childGroup.roleOf(node2.accountID)).toEqual("writer");
|
|
954
|
+
});
|
|
955
|
+
|
|
956
|
+
test("roleOf should use everyone role when no explicit role exists", async () => {
|
|
957
|
+
const { node1, node2 } = await createTwoConnectedNodes("server", "server");
|
|
958
|
+
|
|
959
|
+
const group = node1.node.createGroup();
|
|
960
|
+
|
|
961
|
+
// Add only everyone role
|
|
962
|
+
group.addMember("everyone", "reader");
|
|
963
|
+
|
|
964
|
+
// Should return everyone's role when no explicit role exists
|
|
965
|
+
expect(group.roleOf(node2.accountID)).toEqual("reader");
|
|
966
|
+
});
|
|
967
|
+
|
|
968
|
+
test("roleOf should inherit everyone role from parent when no explicit roles exist", async () => {
|
|
969
|
+
const { node1, node2 } = await createTwoConnectedNodes("server", "server");
|
|
970
|
+
|
|
971
|
+
const parentGroup = node1.node.createGroup();
|
|
972
|
+
const childGroup = node1.node.createGroup();
|
|
973
|
+
|
|
974
|
+
// Set up inheritance
|
|
975
|
+
childGroup.extend(parentGroup);
|
|
976
|
+
|
|
977
|
+
// Add everyone to parent only
|
|
978
|
+
parentGroup.addMember("everyone", "reader");
|
|
979
|
+
|
|
980
|
+
// Should inherit everyone's role from parent
|
|
981
|
+
expect(childGroup.roleOf(node2.accountID)).toEqual("reader");
|
|
982
|
+
});
|
|
983
|
+
|
|
984
|
+
test("roleOf should handle everyone role inheritance through multiple levels", async () => {
|
|
985
|
+
const { node1, node2 } = await createTwoConnectedNodes("server", "server");
|
|
986
|
+
|
|
987
|
+
const grandParentGroup = node1.node.createGroup();
|
|
988
|
+
const parentGroup = node1.node.createGroup();
|
|
989
|
+
const childGroup = node1.node.createGroup();
|
|
990
|
+
|
|
991
|
+
const childGroupOnNode2 = await loadCoValueOrFail(node2.node, childGroup.id);
|
|
992
|
+
|
|
993
|
+
const account2 = await loadCoValueOrFail(node1.node, node2.accountID);
|
|
994
|
+
|
|
995
|
+
// Set up inheritance chain
|
|
996
|
+
parentGroup.extend(grandParentGroup);
|
|
997
|
+
childGroup.extend(parentGroup);
|
|
998
|
+
|
|
999
|
+
// Add everyone to grandparent
|
|
1000
|
+
grandParentGroup.addMember("everyone", "writer");
|
|
1001
|
+
|
|
1002
|
+
// Should inherit everyone's role from grandparent
|
|
1003
|
+
expect(childGroup.roleOf(node2.accountID)).toEqual("writer");
|
|
1004
|
+
|
|
1005
|
+
// Add explicit role in parent
|
|
1006
|
+
parentGroup.addMember(account2, "reader");
|
|
1007
|
+
|
|
1008
|
+
// Should use parent's explicit role instead of grandparent's everyone role
|
|
1009
|
+
expect(childGroup.roleOf(node2.accountID)).toEqual("writer");
|
|
1010
|
+
|
|
1011
|
+
// Add explicit role in child
|
|
1012
|
+
childGroup.addMember(account2, "admin");
|
|
1013
|
+
await childGroup.core.waitForSync();
|
|
1014
|
+
|
|
1015
|
+
// Should use child's explicit role
|
|
1016
|
+
expect(childGroup.roleOf(node2.accountID)).toEqual("admin");
|
|
1017
|
+
|
|
1018
|
+
// Remove child's explicit role
|
|
1019
|
+
await childGroupOnNode2.removeMember(account2);
|
|
1020
|
+
await childGroupOnNode2.core.waitForSync();
|
|
1021
|
+
|
|
1022
|
+
// Should fall back to parent's explicit role
|
|
1023
|
+
expect(childGroup.roleOf(node2.accountID)).toEqual("writer");
|
|
1024
|
+
|
|
1025
|
+
// Remove parent's explicit role
|
|
1026
|
+
await parentGroup.removeMember(account2);
|
|
1027
|
+
await childGroup.core.waitForSync();
|
|
1028
|
+
|
|
1029
|
+
// Should fall back to grandparent's everyone role
|
|
1030
|
+
expect(childGroup.roleOf(node2.accountID)).toEqual("writer");
|
|
1031
|
+
});
|