cojson 0.18.0 → 0.18.2
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 +14 -0
- package/dist/coValueCore/branching.d.ts +36 -0
- package/dist/coValueCore/branching.d.ts.map +1 -0
- package/dist/coValueCore/branching.js +122 -0
- package/dist/coValueCore/branching.js.map +1 -0
- package/dist/coValueCore/coValueCore.d.ts +71 -5
- package/dist/coValueCore/coValueCore.d.ts.map +1 -1
- package/dist/coValueCore/coValueCore.js +162 -53
- package/dist/coValueCore/coValueCore.js.map +1 -1
- package/dist/coValueCore/decodeTransactionChangesAndMeta.d.ts +3 -0
- package/dist/coValueCore/decodeTransactionChangesAndMeta.d.ts.map +1 -0
- package/dist/coValueCore/decodeTransactionChangesAndMeta.js +59 -0
- package/dist/coValueCore/decodeTransactionChangesAndMeta.js.map +1 -0
- package/dist/coValueCore/utils.d.ts.map +1 -1
- package/dist/coValueCore/utils.js +3 -0
- package/dist/coValueCore/utils.js.map +1 -1
- package/dist/coValueCore/verifiedState.d.ts.map +1 -1
- package/dist/coValues/coList.d.ts +3 -3
- package/dist/coValues/coList.d.ts.map +1 -1
- package/dist/coValues/coList.js +4 -7
- package/dist/coValues/coList.js.map +1 -1
- package/dist/coValues/coMap.d.ts +3 -3
- package/dist/coValues/coMap.d.ts.map +1 -1
- package/dist/coValues/coMap.js +6 -6
- package/dist/coValues/coMap.js.map +1 -1
- package/dist/coValues/coStream.d.ts +3 -3
- package/dist/coValues/coStream.d.ts.map +1 -1
- package/dist/coValues/coStream.js +4 -4
- package/dist/coValues/coStream.js.map +1 -1
- package/dist/ids.d.ts.map +1 -1
- package/dist/ids.js.map +1 -1
- package/dist/jsonStringify.d.ts +1 -0
- package/dist/jsonStringify.d.ts.map +1 -1
- package/dist/jsonStringify.js +8 -0
- package/dist/jsonStringify.js.map +1 -1
- package/dist/localNode.d.ts +6 -0
- package/dist/localNode.d.ts.map +1 -1
- package/dist/localNode.js +27 -0
- package/dist/localNode.js.map +1 -1
- package/dist/permissions.d.ts +2 -7
- package/dist/permissions.d.ts.map +1 -1
- package/dist/permissions.js +75 -71
- package/dist/permissions.js.map +1 -1
- package/dist/tests/branching.test.d.ts +2 -0
- package/dist/tests/branching.test.d.ts.map +1 -0
- package/dist/tests/branching.test.js +287 -0
- package/dist/tests/branching.test.js.map +1 -0
- package/dist/tests/coValueCore.test.js +2 -3
- package/dist/tests/coValueCore.test.js.map +1 -1
- package/dist/tests/group.removeMember.test.js +63 -116
- package/dist/tests/group.removeMember.test.js.map +1 -1
- package/dist/tests/sync.load.test.js +36 -0
- package/dist/tests/sync.load.test.js.map +1 -1
- package/dist/tests/sync.storage.test.js +39 -3
- package/dist/tests/sync.storage.test.js.map +1 -1
- package/dist/tests/sync.upload.test.js +39 -3
- package/dist/tests/sync.upload.test.js.map +1 -1
- package/package.json +2 -2
- package/src/coValueCore/branching.ts +198 -0
- package/src/coValueCore/coValueCore.ts +255 -72
- package/src/coValueCore/decodeTransactionChangesAndMeta.ts +81 -0
- package/src/coValueCore/utils.ts +4 -0
- package/src/coValueCore/verifiedState.ts +1 -1
- package/src/coValues/coList.ts +8 -10
- package/src/coValues/coMap.ts +8 -11
- package/src/coValues/coStream.ts +7 -8
- package/src/ids.ts +4 -1
- package/src/jsonStringify.ts +8 -0
- package/src/localNode.ts +40 -0
- package/src/permissions.ts +83 -90
- package/src/tests/branching.test.ts +425 -0
- package/src/tests/coValueCore.test.ts +2 -3
- package/src/tests/group.removeMember.test.ts +116 -214
- package/src/tests/sync.load.test.ts +48 -0
- package/src/tests/sync.storage.test.ts +54 -3
- package/src/tests/sync.upload.test.ts +53 -3
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { AvailableCoValueCore, VerifiedTransaction } from "./coValueCore.js";
|
|
2
|
+
import { safeParseJSON } from "../jsonStringify.js";
|
|
3
|
+
|
|
4
|
+
export function decodeTransactionChangesAndMeta(
|
|
5
|
+
coValue: AvailableCoValueCore,
|
|
6
|
+
transaction: VerifiedTransaction,
|
|
7
|
+
ignorePrivateTransactions: boolean,
|
|
8
|
+
) {
|
|
9
|
+
if (!transaction.isValid) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (transaction.tx.privacy === "private" && ignorePrivateTransactions) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const needsChagesParsing =
|
|
18
|
+
!transaction.hasInvalidChanges && !transaction.changes;
|
|
19
|
+
const needsMetaParsing =
|
|
20
|
+
!transaction.hasInvalidMeta && !transaction.meta && transaction.tx.meta;
|
|
21
|
+
|
|
22
|
+
if (!needsChagesParsing && !needsMetaParsing) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (transaction.tx.privacy === "private") {
|
|
27
|
+
const readKey = coValue.getReadKey(transaction.tx.keyUsed);
|
|
28
|
+
|
|
29
|
+
if (!readKey) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (needsChagesParsing) {
|
|
34
|
+
const changes = coValue.verified.decryptTransaction(
|
|
35
|
+
transaction.txID.sessionID,
|
|
36
|
+
transaction.txID.txIndex,
|
|
37
|
+
readKey,
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
if (!changes) {
|
|
41
|
+
transaction.hasInvalidChanges = true;
|
|
42
|
+
} else {
|
|
43
|
+
transaction.changes = changes;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (needsMetaParsing) {
|
|
48
|
+
const meta = coValue.verified.decryptTransactionMeta(
|
|
49
|
+
transaction.txID.sessionID,
|
|
50
|
+
transaction.txID.txIndex,
|
|
51
|
+
readKey,
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
if (!meta) {
|
|
55
|
+
transaction.hasInvalidMeta = true;
|
|
56
|
+
} else {
|
|
57
|
+
transaction.meta = meta;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
} else {
|
|
61
|
+
if (needsChagesParsing) {
|
|
62
|
+
const changes = safeParseJSON(transaction.tx.changes);
|
|
63
|
+
|
|
64
|
+
if (!changes) {
|
|
65
|
+
transaction.hasInvalidChanges = true;
|
|
66
|
+
} else {
|
|
67
|
+
transaction.changes = changes;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (needsMetaParsing && transaction.tx.meta) {
|
|
72
|
+
const meta = safeParseJSON(transaction.tx.meta);
|
|
73
|
+
|
|
74
|
+
if (!meta) {
|
|
75
|
+
transaction.hasInvalidMeta = true;
|
|
76
|
+
} else {
|
|
77
|
+
transaction.meta = meta;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
package/src/coValueCore/utils.ts
CHANGED
|
@@ -21,6 +21,7 @@ import { CoValueKnownState, NewContentMessage } from "../sync.js";
|
|
|
21
21
|
import { TryAddTransactionsError } from "./coValueCore.js";
|
|
22
22
|
import { SessionLog, SessionMap } from "./SessionMap.js";
|
|
23
23
|
import { ControlledAccountOrAgent } from "../coValues/account.js";
|
|
24
|
+
import { logger } from "../logger.js";
|
|
24
25
|
|
|
25
26
|
export type CoValueHeader = {
|
|
26
27
|
type: AnyRawCoValue["type"];
|
|
@@ -40,7 +41,6 @@ export type PrivateTransaction = {
|
|
|
40
41
|
encryptedChanges: Encrypted<JsonValue[], { in: RawCoID; tx: TransactionID }>;
|
|
41
42
|
meta?: Encrypted<JsonObject, { in: RawCoID; tx: TransactionID }>;
|
|
42
43
|
};
|
|
43
|
-
|
|
44
44
|
export type TrustingTransaction = {
|
|
45
45
|
privacy: "trusting";
|
|
46
46
|
madeAt: number;
|
package/src/coValues/coList.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { accountOrAgentIDfromSessionID } from "../typeUtils/accountOrAgentIDfrom
|
|
|
10
10
|
import { isCoValue } from "../typeUtils/isCoValue.js";
|
|
11
11
|
import { RawAccountID } from "./account.js";
|
|
12
12
|
import { RawGroup } from "./group.js";
|
|
13
|
+
import { Transaction } from "../coValueCore/verifiedState.js";
|
|
13
14
|
|
|
14
15
|
export type OpID = TransactionID & { changeIdx: number };
|
|
15
16
|
|
|
@@ -86,8 +87,12 @@ export class RawCoList<
|
|
|
86
87
|
opID: OpID;
|
|
87
88
|
}[];
|
|
88
89
|
/** @internal */
|
|
89
|
-
|
|
90
|
-
|
|
90
|
+
knownTransactions: Set<Transaction>;
|
|
91
|
+
|
|
92
|
+
get totalValidTransactions() {
|
|
93
|
+
return this.knownTransactions.size;
|
|
94
|
+
}
|
|
95
|
+
|
|
91
96
|
lastValidTransaction: number | undefined;
|
|
92
97
|
|
|
93
98
|
/** @internal */
|
|
@@ -99,7 +104,7 @@ export class RawCoList<
|
|
|
99
104
|
this.deletionsByInsertion = {};
|
|
100
105
|
this.afterStart = [];
|
|
101
106
|
this.beforeEnd = [];
|
|
102
|
-
this.knownTransactions =
|
|
107
|
+
this.knownTransactions = new Set<Transaction>();
|
|
103
108
|
|
|
104
109
|
this.processNewTransactions();
|
|
105
110
|
}
|
|
@@ -114,7 +119,6 @@ export class RawCoList<
|
|
|
114
119
|
return;
|
|
115
120
|
}
|
|
116
121
|
|
|
117
|
-
this.totalValidTransactions += transactions.length;
|
|
118
122
|
let lastValidTransaction: number | undefined = undefined;
|
|
119
123
|
let oldestValidTransaction: number | undefined = undefined;
|
|
120
124
|
this._cachedEntries = undefined;
|
|
@@ -126,11 +130,6 @@ export class RawCoList<
|
|
|
126
130
|
madeAt,
|
|
127
131
|
);
|
|
128
132
|
|
|
129
|
-
this.knownTransactions[txID.sessionID] = Math.max(
|
|
130
|
-
this.knownTransactions[txID.sessionID] ?? 0,
|
|
131
|
-
txID.txIndex,
|
|
132
|
-
);
|
|
133
|
-
|
|
134
133
|
for (const [changeIdx, changeUntyped] of changes.entries()) {
|
|
135
134
|
const change = changeUntyped as ListOpPayload<Item>;
|
|
136
135
|
|
|
@@ -624,7 +623,6 @@ export class RawCoList<
|
|
|
624
623
|
this.afterStart = listAfter.afterStart;
|
|
625
624
|
this.beforeEnd = listAfter.beforeEnd;
|
|
626
625
|
this.insertions = listAfter.insertions;
|
|
627
|
-
this.totalValidTransactions = listAfter.totalValidTransactions;
|
|
628
626
|
this.lastValidTransaction = listAfter.lastValidTransaction;
|
|
629
627
|
this.knownTransactions = listAfter.knownTransactions;
|
|
630
628
|
this.deletionsByInsertion = listAfter.deletionsByInsertion;
|
package/src/coValues/coMap.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { accountOrAgentIDfromSessionID } from "../typeUtils/accountOrAgentIDfrom
|
|
|
10
10
|
import { isCoValue } from "../typeUtils/isCoValue.js";
|
|
11
11
|
import { RawAccountID } from "./account.js";
|
|
12
12
|
import type { RawGroup } from "./group.js";
|
|
13
|
+
import { Transaction } from "../coValueCore/verifiedState.js";
|
|
13
14
|
|
|
14
15
|
type MapOp<K extends string, V extends JsonValue | undefined> = {
|
|
15
16
|
txID: TransactionID;
|
|
@@ -57,7 +58,7 @@ export class RawCoMapView<
|
|
|
57
58
|
[Key in keyof Shape & string]?: MapOp<Key, Shape[Key]>[];
|
|
58
59
|
};
|
|
59
60
|
/** @internal */
|
|
60
|
-
knownTransactions:
|
|
61
|
+
knownTransactions: Set<Transaction>;
|
|
61
62
|
|
|
62
63
|
/** @internal */
|
|
63
64
|
ignorePrivateTransactions: boolean;
|
|
@@ -66,7 +67,9 @@ export class RawCoMapView<
|
|
|
66
67
|
/** @category 6. Meta */
|
|
67
68
|
readonly _shape!: Shape;
|
|
68
69
|
|
|
69
|
-
totalValidTransactions
|
|
70
|
+
get totalValidTransactions() {
|
|
71
|
+
return this.knownTransactions.size;
|
|
72
|
+
}
|
|
70
73
|
|
|
71
74
|
/** @internal */
|
|
72
75
|
constructor(
|
|
@@ -83,7 +86,7 @@ export class RawCoMapView<
|
|
|
83
86
|
options?.ignorePrivateTransactions ?? false;
|
|
84
87
|
this.ops = {};
|
|
85
88
|
this.latest = {};
|
|
86
|
-
this.knownTransactions =
|
|
89
|
+
this.knownTransactions = new Set<Transaction>();
|
|
87
90
|
|
|
88
91
|
this.processNewTransactions();
|
|
89
92
|
}
|
|
@@ -113,7 +116,7 @@ export class RawCoMapView<
|
|
|
113
116
|
NonNullable<(typeof ops)[keyof typeof ops]>
|
|
114
117
|
>();
|
|
115
118
|
|
|
116
|
-
for (const { txID, changes, madeAt,
|
|
119
|
+
for (const { txID, changes, madeAt, tx } of newValidTransactions) {
|
|
117
120
|
if (madeAt > this.latestTxMadeAt) {
|
|
118
121
|
this.latestTxMadeAt = madeAt;
|
|
119
122
|
}
|
|
@@ -128,7 +131,7 @@ export class RawCoMapView<
|
|
|
128
131
|
madeAt,
|
|
129
132
|
changeIdx,
|
|
130
133
|
change,
|
|
131
|
-
trusting,
|
|
134
|
+
trusting: tx.privacy === "trusting",
|
|
132
135
|
};
|
|
133
136
|
|
|
134
137
|
const entries = ops[change.key];
|
|
@@ -140,15 +143,9 @@ export class RawCoMapView<
|
|
|
140
143
|
entries.push(entry);
|
|
141
144
|
changedEntries.set(change.key, entries);
|
|
142
145
|
}
|
|
143
|
-
this.knownTransactions[txID.sessionID] = Math.max(
|
|
144
|
-
this.knownTransactions[txID.sessionID] ?? 0,
|
|
145
|
-
txID.txIndex,
|
|
146
|
-
);
|
|
147
146
|
}
|
|
148
147
|
}
|
|
149
148
|
|
|
150
|
-
this.totalValidTransactions += newValidTransactions.length;
|
|
151
|
-
|
|
152
149
|
for (const entries of changedEntries.values()) {
|
|
153
150
|
entries.sort(this.core.compareTransactions);
|
|
154
151
|
}
|
package/src/coValues/coStream.ts
CHANGED
|
@@ -13,6 +13,7 @@ import { isAccountID } from "../typeUtils/isAccountID.js";
|
|
|
13
13
|
import { isCoValue } from "../typeUtils/isCoValue.js";
|
|
14
14
|
import { RawAccountID } from "./account.js";
|
|
15
15
|
import { RawGroup } from "./group.js";
|
|
16
|
+
import { Transaction } from "../coValueCore/verifiedState.js";
|
|
16
17
|
|
|
17
18
|
export type BinaryStreamInfo = {
|
|
18
19
|
mimeType: string;
|
|
@@ -58,15 +59,18 @@ export class RawCoStreamView<
|
|
|
58
59
|
[key: SessionID]: CoStreamItem<Item>[];
|
|
59
60
|
};
|
|
60
61
|
/** @internal */
|
|
61
|
-
knownTransactions:
|
|
62
|
-
totalValidTransactions = 0;
|
|
62
|
+
knownTransactions: Set<Transaction>;
|
|
63
63
|
readonly _item!: Item;
|
|
64
64
|
|
|
65
|
+
get totalValidTransactions() {
|
|
66
|
+
return this.knownTransactions.size;
|
|
67
|
+
}
|
|
68
|
+
|
|
65
69
|
constructor(core: AvailableCoValueCore) {
|
|
66
70
|
this.id = core.id as CoID<this>;
|
|
67
71
|
this.core = core;
|
|
68
72
|
this.items = {};
|
|
69
|
-
this.knownTransactions =
|
|
73
|
+
this.knownTransactions = new Set<Transaction>();
|
|
70
74
|
this.processNewTransactions();
|
|
71
75
|
}
|
|
72
76
|
|
|
@@ -113,7 +117,6 @@ export class RawCoStreamView<
|
|
|
113
117
|
}
|
|
114
118
|
|
|
115
119
|
for (const { txID, madeAt, changes } of newValidTransactions) {
|
|
116
|
-
this.totalValidTransactions++;
|
|
117
120
|
for (const changeUntyped of changes) {
|
|
118
121
|
const change = changeUntyped as Item;
|
|
119
122
|
let entries = this.items[txID.sessionID];
|
|
@@ -124,10 +127,6 @@ export class RawCoStreamView<
|
|
|
124
127
|
entries.push({ value: change, madeAt, tx: txID });
|
|
125
128
|
changeEntries.add(entries);
|
|
126
129
|
}
|
|
127
|
-
this.knownTransactions[txID.sessionID] = Math.max(
|
|
128
|
-
this.knownTransactions[txID.sessionID] ?? 0,
|
|
129
|
-
txID.txIndex,
|
|
130
|
-
);
|
|
131
130
|
}
|
|
132
131
|
|
|
133
132
|
for (const entries of changeEntries) {
|
package/src/ids.ts
CHANGED
|
@@ -20,7 +20,10 @@ export function rawCoIDfromBytes(bytes: Uint8Array): RawCoID {
|
|
|
20
20
|
return `co_z${base58.encode(bytes.slice(0, shortHashLength))}` as RawCoID;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
export type TransactionID = {
|
|
23
|
+
export type TransactionID = {
|
|
24
|
+
sessionID: SessionID;
|
|
25
|
+
txIndex: number;
|
|
26
|
+
};
|
|
24
27
|
|
|
25
28
|
export type AgentID = `sealer_z${string}/signer_z${string}`;
|
|
26
29
|
|
package/src/jsonStringify.ts
CHANGED
|
@@ -66,3 +66,11 @@ export function stableStringify<T>(
|
|
|
66
66
|
export function parseJSON<T>(json: Stringified<T>): T {
|
|
67
67
|
return JSON.parse(json);
|
|
68
68
|
}
|
|
69
|
+
|
|
70
|
+
export function safeParseJSON<T>(json: Stringified<T>): T | undefined {
|
|
71
|
+
try {
|
|
72
|
+
return JSON.parse(json);
|
|
73
|
+
} catch (e) {
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
}
|
package/src/localNode.ts
CHANGED
|
@@ -457,6 +457,46 @@ export class LocalNode {
|
|
|
457
457
|
return core.getCurrentContent() as T;
|
|
458
458
|
}
|
|
459
459
|
|
|
460
|
+
/**
|
|
461
|
+
* Loads a branch from a group coValue, creating a new one if it doesn't exist.
|
|
462
|
+
*
|
|
463
|
+
* Returns "unavailable" in case of errors or missing source.
|
|
464
|
+
*/
|
|
465
|
+
async checkoutBranch<T extends RawCoValue>(
|
|
466
|
+
id: CoID<T>,
|
|
467
|
+
branch: string,
|
|
468
|
+
branchOwnerID?: RawCoID,
|
|
469
|
+
): Promise<T | "unavailable"> {
|
|
470
|
+
const source = await this.loadCoValueCore(id);
|
|
471
|
+
|
|
472
|
+
if (!source.isAvailable()) {
|
|
473
|
+
return "unavailable";
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
const header = source.verified.header;
|
|
477
|
+
|
|
478
|
+
// Group and account coValues can't have branches
|
|
479
|
+
if (header.ruleset.type !== "ownedByGroup") {
|
|
480
|
+
logger.error("Can't checkout a branch from a group coValue", {
|
|
481
|
+
id,
|
|
482
|
+
});
|
|
483
|
+
return "unavailable";
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
const ownerID = branchOwnerID || header.ruleset.group;
|
|
487
|
+
|
|
488
|
+
const branchID = source.getBranchId(branch, ownerID);
|
|
489
|
+
|
|
490
|
+
// Passing skipRetry to true because otherwise creating a new branch would always take 1 retry delay
|
|
491
|
+
let branchCoValue = await this.loadCoValueCore(branchID, undefined, true);
|
|
492
|
+
|
|
493
|
+
if (!branchCoValue.isAvailable()) {
|
|
494
|
+
branchCoValue = source.createBranch(branch, ownerID);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
return branchCoValue.getCurrentContent() as T;
|
|
498
|
+
}
|
|
499
|
+
|
|
460
500
|
getLoaded<T extends RawCoValue>(id: CoID<T>): T | undefined {
|
|
461
501
|
const coValue = this.getCoValue(id);
|
|
462
502
|
|