cojson 0.20.7 → 0.20.9
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 +26 -0
- package/dist/SyncStateManager.d.ts.map +1 -1
- package/dist/SyncStateManager.js +0 -2
- package/dist/SyncStateManager.js.map +1 -1
- package/dist/base64url.d.ts +15 -0
- package/dist/base64url.d.ts.map +1 -1
- package/dist/base64url.js +101 -5
- package/dist/base64url.js.map +1 -1
- package/dist/base64url.test.js +76 -1
- package/dist/base64url.test.js.map +1 -1
- package/dist/coValue.d.ts +2 -1
- package/dist/coValue.d.ts.map +1 -1
- package/dist/coValue.js.map +1 -1
- package/dist/coValueCore/coValueCore.d.ts +9 -11
- package/dist/coValueCore/coValueCore.d.ts.map +1 -1
- package/dist/coValueCore/coValueCore.js +92 -65
- package/dist/coValueCore/coValueCore.js.map +1 -1
- package/dist/coValueCore/verifiedState.d.ts +38 -7
- package/dist/coValueCore/verifiedState.d.ts.map +1 -1
- package/dist/coValueCore/verifiedState.js +226 -30
- package/dist/coValueCore/verifiedState.js.map +1 -1
- package/dist/coValues/binaryCoStream.d.ts +63 -0
- package/dist/coValues/binaryCoStream.d.ts.map +1 -0
- package/dist/coValues/binaryCoStream.js +125 -0
- package/dist/coValues/binaryCoStream.js.map +1 -0
- package/dist/coValues/coList.d.ts +3 -1
- package/dist/coValues/coList.d.ts.map +1 -1
- package/dist/coValues/coList.js +15 -6
- package/dist/coValues/coList.js.map +1 -1
- package/dist/coValues/coMap.d.ts +1 -1
- package/dist/coValues/coMap.d.ts.map +1 -1
- package/dist/coValues/coMap.js +2 -2
- package/dist/coValues/coMap.js.map +1 -1
- package/dist/coValues/coStream.d.ts +0 -38
- package/dist/coValues/coStream.d.ts.map +1 -1
- package/dist/coValues/coStream.js +0 -86
- package/dist/coValues/coStream.js.map +1 -1
- package/dist/coValues/group.d.ts +44 -6
- package/dist/coValues/group.d.ts.map +1 -1
- package/dist/coValues/group.js +198 -17
- package/dist/coValues/group.js.map +1 -1
- package/dist/coreToCoValue.d.ts +2 -1
- package/dist/coreToCoValue.d.ts.map +1 -1
- package/dist/coreToCoValue.js +2 -1
- package/dist/coreToCoValue.js.map +1 -1
- package/dist/crypto/NapiCrypto.d.ts +18 -24
- package/dist/crypto/NapiCrypto.d.ts.map +1 -1
- package/dist/crypto/NapiCrypto.js +98 -60
- package/dist/crypto/NapiCrypto.js.map +1 -1
- package/dist/crypto/RNCrypto.d.ts +16 -3
- package/dist/crypto/RNCrypto.d.ts.map +1 -1
- package/dist/crypto/RNCrypto.js +117 -54
- package/dist/crypto/RNCrypto.js.map +1 -1
- package/dist/crypto/WasmCrypto.d.ts +18 -24
- package/dist/crypto/WasmCrypto.d.ts.map +1 -1
- package/dist/crypto/WasmCrypto.js +100 -61
- package/dist/crypto/WasmCrypto.js.map +1 -1
- package/dist/crypto/crypto.d.ts +55 -19
- package/dist/crypto/crypto.d.ts.map +1 -1
- package/dist/crypto/crypto.js +14 -3
- package/dist/crypto/crypto.js.map +1 -1
- package/dist/exports.d.ts +7 -3
- package/dist/exports.d.ts.map +1 -1
- package/dist/exports.js +4 -2
- package/dist/exports.js.map +1 -1
- package/dist/localNode.d.ts +3 -1
- package/dist/localNode.d.ts.map +1 -1
- package/dist/localNode.js +10 -3
- package/dist/localNode.js.map +1 -1
- package/dist/media.d.ts +1 -1
- package/dist/media.d.ts.map +1 -1
- package/dist/permissions.d.ts +2 -1
- package/dist/permissions.d.ts.map +1 -1
- package/dist/permissions.js +19 -3
- package/dist/permissions.js.map +1 -1
- package/dist/storage/sqliteAsync/client.d.ts +24 -12
- package/dist/storage/sqliteAsync/client.d.ts.map +1 -1
- package/dist/storage/sqliteAsync/client.js +70 -58
- package/dist/storage/sqliteAsync/client.js.map +1 -1
- package/dist/storage/sqliteAsync/types.d.ts +1 -1
- package/dist/storage/sqliteAsync/types.d.ts.map +1 -1
- package/dist/storage/types.d.ts +1 -0
- package/dist/storage/types.d.ts.map +1 -1
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +7 -1
- package/dist/sync.js.map +1 -1
- package/dist/tests/CojsonMessageChannel.test.js +2 -2
- package/dist/tests/SQLiteClientAsync.test.d.ts +2 -0
- package/dist/tests/SQLiteClientAsync.test.d.ts.map +1 -0
- package/dist/tests/SQLiteClientAsync.test.js +64 -0
- package/dist/tests/SQLiteClientAsync.test.js.map +1 -0
- package/dist/tests/StorageApiAsync.test.js +2 -8
- package/dist/tests/StorageApiAsync.test.js.map +1 -1
- package/dist/tests/SyncStateManager.test.js +2 -2
- package/dist/tests/WasmCrypto.test.js +1 -15
- package/dist/tests/WasmCrypto.test.js.map +1 -1
- package/dist/tests/coList.test.js +24 -5
- package/dist/tests/coList.test.js.map +1 -1
- package/dist/tests/coStream.test.js +4 -3
- package/dist/tests/coStream.test.js.map +1 -1
- package/dist/tests/coValueCore.initTransaction.test.d.ts +2 -0
- package/dist/tests/coValueCore.initTransaction.test.d.ts.map +1 -0
- package/dist/tests/coValueCore.initTransaction.test.js +438 -0
- package/dist/tests/coValueCore.initTransaction.test.js.map +1 -0
- package/dist/tests/coValueCore.test.js +11 -19
- package/dist/tests/coValueCore.test.js.map +1 -1
- package/dist/tests/crypto.test.js +83 -0
- package/dist/tests/crypto.test.js.map +1 -1
- package/dist/tests/deleteCoValue.test.js +5 -5
- package/dist/tests/deleteCoValue.test.js.map +1 -1
- package/dist/tests/group.inheritance.test.js +11 -0
- package/dist/tests/group.inheritance.test.js.map +1 -1
- package/dist/tests/group.test.js +24 -1
- package/dist/tests/group.test.js.map +1 -1
- package/dist/tests/groupSealer.test.d.ts +2 -0
- package/dist/tests/groupSealer.test.d.ts.map +1 -0
- package/dist/tests/groupSealer.test.js +913 -0
- package/dist/tests/groupSealer.test.js.map +1 -0
- package/dist/tests/setup.js +5 -0
- package/dist/tests/setup.js.map +1 -1
- package/dist/tests/sync.auth.test.js +10 -10
- package/dist/tests/sync.concurrentLoad.test.js +12 -12
- package/dist/tests/sync.deleted.test.js +8 -8
- package/dist/tests/sync.garbageCollection.test.js +10 -10
- package/dist/tests/sync.invite.test.js +12 -12
- package/dist/tests/sync.known.test.js +2 -2
- package/dist/tests/sync.load.test.js +107 -107
- package/dist/tests/sync.mesh.test.js +164 -46
- package/dist/tests/sync.mesh.test.js.map +1 -1
- package/dist/tests/sync.multipleServers.test.js +43 -43
- package/dist/tests/sync.peerReconciliation.test.js +29 -29
- package/dist/tests/sync.sharding.test.js +3 -3
- package/dist/tests/sync.storage.test.js +104 -104
- package/dist/tests/sync.storage.test.js.map +1 -1
- package/dist/tests/sync.storageAsync.test.js +56 -56
- package/dist/tests/sync.upload.test.js +22 -22
- package/dist/tests/testStorage.d.ts +2 -0
- package/dist/tests/testStorage.d.ts.map +1 -1
- package/dist/tests/testStorage.js +30 -6
- package/dist/tests/testStorage.js.map +1 -1
- package/dist/typeUtils/isCoValue.js +1 -1
- package/dist/typeUtils/isCoValue.js.map +1 -1
- package/package.json +4 -4
- package/src/SyncStateManager.ts +0 -2
- package/src/base64url.test.ts +89 -1
- package/src/base64url.ts +134 -6
- package/src/coValue.ts +2 -1
- package/src/coValueCore/coValueCore.ts +126 -84
- package/src/coValueCore/verifiedState.ts +335 -53
- package/src/coValues/binaryCoStream.ts +217 -0
- package/src/coValues/coList.ts +21 -8
- package/src/coValues/coMap.ts +3 -0
- package/src/coValues/coStream.ts +0 -170
- package/src/coValues/group.ts +270 -21
- package/src/coreToCoValue.ts +2 -1
- package/src/crypto/NapiCrypto.ts +198 -95
- package/src/crypto/RNCrypto.ts +229 -102
- package/src/crypto/WasmCrypto.ts +201 -95
- package/src/crypto/crypto.ts +118 -45
- package/src/exports.ts +11 -5
- package/src/localNode.ts +17 -1
- package/src/media.ts +1 -1
- package/src/permissions.ts +30 -7
- package/src/storage/sqliteAsync/client.ts +136 -115
- package/src/storage/sqliteAsync/types.ts +3 -1
- package/src/storage/types.ts +4 -0
- package/src/sync.ts +10 -1
- package/src/tests/CojsonMessageChannel.test.ts +2 -2
- package/src/tests/SQLiteClientAsync.test.ts +75 -0
- package/src/tests/StorageApiAsync.test.ts +4 -9
- package/src/tests/SyncStateManager.test.ts +2 -2
- package/src/tests/WasmCrypto.test.ts +1 -25
- package/src/tests/coList.test.ts +39 -5
- package/src/tests/coStream.test.ts +4 -5
- package/src/tests/coValueCore.initTransaction.test.ts +836 -0
- package/src/tests/coValueCore.test.ts +11 -22
- package/src/tests/crypto.test.ts +107 -0
- package/src/tests/deleteCoValue.test.ts +5 -5
- package/src/tests/group.inheritance.test.ts +16 -0
- package/src/tests/group.test.ts +29 -1
- package/src/tests/groupSealer.test.ts +1473 -0
- package/src/tests/setup.ts +6 -0
- package/src/tests/sync.auth.test.ts +10 -10
- package/src/tests/sync.concurrentLoad.test.ts +12 -12
- package/src/tests/sync.deleted.test.ts +8 -8
- package/src/tests/sync.garbageCollection.test.ts +10 -10
- package/src/tests/sync.invite.test.ts +12 -12
- package/src/tests/sync.known.test.ts +2 -2
- package/src/tests/sync.load.test.ts +107 -107
- package/src/tests/sync.mesh.test.ts +189 -46
- package/src/tests/sync.multipleServers.test.ts +43 -43
- package/src/tests/sync.peerReconciliation.test.ts +29 -29
- package/src/tests/sync.sharding.test.ts +3 -3
- package/src/tests/sync.storage.test.ts +104 -104
- package/src/tests/sync.storageAsync.test.ts +56 -56
- package/src/tests/sync.upload.test.ts +22 -22
- package/src/tests/testStorage.ts +39 -9
- package/src/typeUtils/isCoValue.ts +1 -1
- package/dist/coValueCore/SessionMap.d.ts +0 -55
- package/dist/coValueCore/SessionMap.d.ts.map +0 -1
- package/dist/coValueCore/SessionMap.js +0 -206
- package/dist/coValueCore/SessionMap.js.map +0 -1
- package/dist/tests/coreWasm.test.d.ts +0 -2
- package/dist/tests/coreWasm.test.d.ts.map +0 -1
- package/dist/tests/coreWasm.test.js +0 -203
- package/dist/tests/coreWasm.test.js.map +0 -1
- package/src/coValueCore/SessionMap.ts +0 -394
- package/src/tests/coreWasm.test.ts +0 -452
package/src/base64url.test.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { expect, test } from "vitest";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
base64URLtoBytes,
|
|
4
|
+
bytesToBase64url,
|
|
5
|
+
bytesToBase64,
|
|
6
|
+
} from "./base64url.js";
|
|
3
7
|
|
|
4
8
|
const txt = new TextEncoder();
|
|
5
9
|
|
|
@@ -31,3 +35,87 @@ test("Test our Base64 URL encoding and decoding", () => {
|
|
|
31
35
|
"V2hhdCBkb2VzIDIgKyAyLjEgZXF1YWw_PyB-IDQ=",
|
|
32
36
|
);
|
|
33
37
|
});
|
|
38
|
+
|
|
39
|
+
test("Single special bytes: 0x00 and 0xFF", () => {
|
|
40
|
+
const zero = new Uint8Array([0x00]);
|
|
41
|
+
const encoded0 = bytesToBase64url(zero);
|
|
42
|
+
expect(base64URLtoBytes(encoded0)).toEqual(zero);
|
|
43
|
+
|
|
44
|
+
const ff = new Uint8Array([0xff]);
|
|
45
|
+
const encodedFF = bytesToBase64url(ff);
|
|
46
|
+
expect(base64URLtoBytes(encodedFF)).toEqual(ff);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("All 256 byte values round-trip", () => {
|
|
50
|
+
const allBytes = new Uint8Array(256);
|
|
51
|
+
for (let i = 0; i < 256; i++) allBytes[i] = i;
|
|
52
|
+
const encoded = bytesToBase64url(allBytes);
|
|
53
|
+
expect(base64URLtoBytes(encoded)).toEqual(allBytes);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("Decoding without padding", () => {
|
|
57
|
+
expect(base64URLtoBytes("Zg")).toEqual(base64URLtoBytes("Zg=="));
|
|
58
|
+
expect(base64URLtoBytes("Zm8")).toEqual(base64URLtoBytes("Zm8="));
|
|
59
|
+
expect(base64URLtoBytes("Zm9vYg")).toEqual(base64URLtoBytes("Zm9vYg=="));
|
|
60
|
+
expect(base64URLtoBytes("Zm9vYmE")).toEqual(base64URLtoBytes("Zm9vYmE="));
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("Larger binary data round-trip (4KB)", () => {
|
|
64
|
+
const size = 4096;
|
|
65
|
+
const data = new Uint8Array(size);
|
|
66
|
+
// Deterministic pseudo-random fill
|
|
67
|
+
let seed = 42;
|
|
68
|
+
for (let i = 0; i < size; i++) {
|
|
69
|
+
seed = (seed * 1103515245 + 12345) & 0x7fffffff;
|
|
70
|
+
data[i] = seed & 0xff;
|
|
71
|
+
}
|
|
72
|
+
const encoded = bytesToBase64url(data);
|
|
73
|
+
expect(base64URLtoBytes(encoded)).toEqual(data);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("URL-safe characters: - and _ instead of + and /", () => {
|
|
77
|
+
// 0xFB, 0xEF, 0xBE → standard base64 "++--", base64url "----"
|
|
78
|
+
const bytes1 = new Uint8Array([0xfb, 0xef, 0xbe]);
|
|
79
|
+
const encoded1 = bytesToBase64url(bytes1);
|
|
80
|
+
expect(encoded1).not.toContain("+");
|
|
81
|
+
expect(encoded1).not.toContain("/");
|
|
82
|
+
expect(encoded1).toContain("-");
|
|
83
|
+
expect(base64URLtoBytes(encoded1)).toEqual(bytes1);
|
|
84
|
+
|
|
85
|
+
// 0xFF, 0xFF, 0xFE → standard base64 "///+", base64url "___-"
|
|
86
|
+
const bytes2 = new Uint8Array([0xff, 0xff, 0xfe]);
|
|
87
|
+
const encoded2 = bytesToBase64url(bytes2);
|
|
88
|
+
expect(encoded2).not.toContain("+");
|
|
89
|
+
expect(encoded2).not.toContain("/");
|
|
90
|
+
expect(encoded2).toContain("_");
|
|
91
|
+
expect(encoded2).toContain("-");
|
|
92
|
+
expect(base64URLtoBytes(encoded2)).toEqual(bytes2);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("bytesToBase64 produces standard base64 with + and /", () => {
|
|
96
|
+
// 0xFB, 0xEF, 0xBE → standard base64 should contain "-" characters that become "+"
|
|
97
|
+
const bytes1 = new Uint8Array([0xfb, 0xef, 0xbe]);
|
|
98
|
+
const encoded1 = bytesToBase64(bytes1);
|
|
99
|
+
expect(encoded1).not.toContain("-");
|
|
100
|
+
expect(encoded1).not.toContain("_");
|
|
101
|
+
expect(encoded1).toContain("+");
|
|
102
|
+
|
|
103
|
+
// 0xFF, 0xFF, 0xFE → standard base64 "///+"
|
|
104
|
+
const bytes2 = new Uint8Array([0xff, 0xff, 0xfe]);
|
|
105
|
+
const encoded2 = bytesToBase64(bytes2);
|
|
106
|
+
expect(encoded2).not.toContain("-");
|
|
107
|
+
expect(encoded2).not.toContain("_");
|
|
108
|
+
expect(encoded2).toContain("/");
|
|
109
|
+
expect(encoded2).toContain("+");
|
|
110
|
+
expect(encoded2).toEqual("///+");
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test("bytesToBase64 RFC test vectors", () => {
|
|
114
|
+
expect(bytesToBase64(new Uint8Array([]))).toEqual("");
|
|
115
|
+
expect(bytesToBase64(txt.encode("f"))).toEqual("Zg==");
|
|
116
|
+
expect(bytesToBase64(txt.encode("fo"))).toEqual("Zm8=");
|
|
117
|
+
expect(bytesToBase64(txt.encode("foo"))).toEqual("Zm9v");
|
|
118
|
+
expect(bytesToBase64(txt.encode("foob"))).toEqual("Zm9vYg==");
|
|
119
|
+
expect(bytesToBase64(txt.encode("fooba"))).toEqual("Zm9vYmE=");
|
|
120
|
+
expect(bytesToBase64(txt.encode("foobar"))).toEqual("Zm9vYmFy");
|
|
121
|
+
});
|
package/src/base64url.ts
CHANGED
|
@@ -1,7 +1,104 @@
|
|
|
1
1
|
const encoder = new TextEncoder();
|
|
2
2
|
const decoder = new TextDecoder();
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
// Check for native base64 support (available in modern runtimes)
|
|
5
|
+
const hasNativeBase64 =
|
|
6
|
+
typeof (Uint8Array.prototype as unknown as { toBase64?: unknown })
|
|
7
|
+
.toBase64 === "function" &&
|
|
8
|
+
typeof (Uint8Array as unknown as { fromBase64?: unknown }).fromBase64 ===
|
|
9
|
+
"function";
|
|
10
|
+
|
|
11
|
+
// Native implementation hooks for React Native (set via setNativeBase64Implementation)
|
|
12
|
+
let nativeBytesToBase64url: ((bytes: ArrayBuffer) => string) | undefined;
|
|
13
|
+
let nativeBase64urlToBytes: ((base64: string) => ArrayBuffer) | undefined;
|
|
14
|
+
let nativeBytesToBase64: ((bytes: ArrayBuffer) => string) | undefined;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Set native base64 implementation for React Native.
|
|
18
|
+
* Called by RNCrypto.create() to register native Rust implementations.
|
|
19
|
+
* @internal
|
|
20
|
+
*/
|
|
21
|
+
export function setNativeBase64Implementation(impl: {
|
|
22
|
+
bytesToBase64url: (bytes: ArrayBuffer) => string;
|
|
23
|
+
base64urlToBytes: (base64: string) => ArrayBuffer;
|
|
24
|
+
bytesToBase64: (bytes: ArrayBuffer) => string;
|
|
25
|
+
}): void {
|
|
26
|
+
nativeBytesToBase64url = impl.bytesToBase64url;
|
|
27
|
+
nativeBase64urlToBytes = impl.base64urlToBytes;
|
|
28
|
+
nativeBytesToBase64 = impl.bytesToBase64;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Convert Uint8Array to ArrayBuffer, handling views correctly.
|
|
33
|
+
*/
|
|
34
|
+
function toArrayBuffer(view: Uint8Array): ArrayBuffer {
|
|
35
|
+
if (
|
|
36
|
+
view.byteOffset === 0 &&
|
|
37
|
+
view.byteLength === view.buffer.byteLength &&
|
|
38
|
+
view.buffer instanceof ArrayBuffer
|
|
39
|
+
) {
|
|
40
|
+
return view.buffer;
|
|
41
|
+
}
|
|
42
|
+
const buffer = new ArrayBuffer(view.byteLength);
|
|
43
|
+
new Uint8Array(buffer).set(view);
|
|
44
|
+
return buffer;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function base64URLtoBytes(base64: string): Uint8Array {
|
|
48
|
+
// Use React Native native implementation if available
|
|
49
|
+
if (nativeBase64urlToBytes) {
|
|
50
|
+
return new Uint8Array(nativeBase64urlToBytes(base64));
|
|
51
|
+
}
|
|
52
|
+
// Use browser native implementation if available
|
|
53
|
+
if (hasNativeBase64) {
|
|
54
|
+
return (
|
|
55
|
+
Uint8Array as unknown as {
|
|
56
|
+
fromBase64: (s: string, opts: { alphabet: string }) => Uint8Array;
|
|
57
|
+
}
|
|
58
|
+
).fromBase64(base64, { alphabet: "base64url" });
|
|
59
|
+
}
|
|
60
|
+
return base64URLtoBytesFallback(base64);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function bytesToBase64url(bytes: Uint8Array): string {
|
|
64
|
+
// Use React Native native implementation if available
|
|
65
|
+
if (nativeBytesToBase64url) {
|
|
66
|
+
return nativeBytesToBase64url(toArrayBuffer(bytes));
|
|
67
|
+
}
|
|
68
|
+
// Use browser native implementation if available
|
|
69
|
+
if (hasNativeBase64) {
|
|
70
|
+
return (
|
|
71
|
+
bytes as unknown as {
|
|
72
|
+
toBase64: (opts: { alphabet: string }) => string;
|
|
73
|
+
}
|
|
74
|
+
).toBase64({ alphabet: "base64url" });
|
|
75
|
+
}
|
|
76
|
+
return bytesToBase64urlFallback(bytes);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Encode bytes to standard base64 (not URL-safe).
|
|
81
|
+
* Use this for data URLs and other contexts requiring standard base64.
|
|
82
|
+
*/
|
|
83
|
+
export function bytesToBase64(bytes: Uint8Array): string {
|
|
84
|
+
// Use React Native native implementation if available
|
|
85
|
+
if (nativeBytesToBase64) {
|
|
86
|
+
return nativeBytesToBase64(toArrayBuffer(bytes));
|
|
87
|
+
}
|
|
88
|
+
// Use browser native implementation if available
|
|
89
|
+
if (hasNativeBase64) {
|
|
90
|
+
return (
|
|
91
|
+
bytes as unknown as {
|
|
92
|
+
toBase64: () => string;
|
|
93
|
+
}
|
|
94
|
+
).toBase64();
|
|
95
|
+
}
|
|
96
|
+
return bytesToBase64Fallback(bytes);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// --- Fallback implementations ---
|
|
100
|
+
|
|
101
|
+
function base64URLtoBytesFallback(base64: string): Uint8Array {
|
|
5
102
|
base64 = base64.replace(/=/g, "");
|
|
6
103
|
const n = base64.length;
|
|
7
104
|
const rem = n % 4;
|
|
@@ -24,8 +121,7 @@ export function base64URLtoBytes(base64: string) {
|
|
|
24
121
|
return new Uint8Array(encoded.buffer, 0, m);
|
|
25
122
|
}
|
|
26
123
|
|
|
27
|
-
|
|
28
|
-
// const before = performance.now();
|
|
124
|
+
function bytesToBase64urlFallback(bytes: Uint8Array): string {
|
|
29
125
|
const m = bytes.length;
|
|
30
126
|
const k = m % 3;
|
|
31
127
|
const n = Math.floor(m / 3) * 4 + (k && k + 1);
|
|
@@ -47,16 +143,48 @@ export function bytesToBase64url(bytes: Uint8Array) {
|
|
|
47
143
|
return base64;
|
|
48
144
|
}
|
|
49
145
|
|
|
50
|
-
|
|
146
|
+
function bytesToBase64Fallback(bytes: Uint8Array): string {
|
|
147
|
+
const m = bytes.length;
|
|
148
|
+
const k = m % 3;
|
|
149
|
+
const n = Math.floor(m / 3) * 4 + (k && k + 1);
|
|
150
|
+
const N = Math.ceil(m / 3) * 4;
|
|
151
|
+
const encoded = new Uint8Array(N);
|
|
152
|
+
|
|
153
|
+
for (let i = 0, j = 0; j < m; i += 4, j += 3) {
|
|
154
|
+
const y = (bytes[j]! << 16) + (bytes[j + 1]! << 8) + (bytes[j + 2]! | 0);
|
|
155
|
+
encoded[i] = encodeLookupStd[y >> 18]!;
|
|
156
|
+
encoded[i + 1] = encodeLookupStd[(y >> 12) & 0x3f]!;
|
|
157
|
+
encoded[i + 2] = encodeLookupStd[(y >> 6) & 0x3f]!;
|
|
158
|
+
encoded[i + 3] = encodeLookupStd[y & 0x3f]!;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
let base64 = decoder.decode(new Uint8Array(encoded.buffer, 0, n));
|
|
162
|
+
if (k === 1) base64 += "==";
|
|
163
|
+
if (k === 2) base64 += "=";
|
|
164
|
+
|
|
165
|
+
return base64;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// base64url alphabet (RFC 4648 §5)
|
|
169
|
+
const alphabetUrl =
|
|
51
170
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
52
171
|
|
|
172
|
+
// Standard base64 alphabet (RFC 4648 §4)
|
|
173
|
+
const alphabetStd =
|
|
174
|
+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
175
|
+
|
|
53
176
|
const lookup = new Uint8Array(128);
|
|
54
|
-
for (const [i, a] of Array.from(
|
|
177
|
+
for (const [i, a] of Array.from(alphabetUrl).entries()) {
|
|
55
178
|
lookup[a.charCodeAt(0)] = i;
|
|
56
179
|
}
|
|
57
180
|
lookup["=".charCodeAt(0)] = 0;
|
|
58
181
|
|
|
59
182
|
const encodeLookup = new Uint8Array(64);
|
|
60
|
-
for (const [i, a] of Array.from(
|
|
183
|
+
for (const [i, a] of Array.from(alphabetUrl).entries()) {
|
|
61
184
|
encodeLookup[i] = a.charCodeAt(0);
|
|
62
185
|
}
|
|
186
|
+
|
|
187
|
+
const encodeLookupStd = new Uint8Array(64);
|
|
188
|
+
for (const [i, a] of Array.from(alphabetStd).entries()) {
|
|
189
|
+
encodeLookupStd[i] = a.charCodeAt(0);
|
|
190
|
+
}
|
package/src/coValue.ts
CHANGED
|
@@ -3,7 +3,8 @@ import { RawProfile as Profile, RawAccount } from "./coValues/account.js";
|
|
|
3
3
|
import { RawCoList } from "./coValues/coList.js";
|
|
4
4
|
import { RawCoMap } from "./coValues/coMap.js";
|
|
5
5
|
import { RawCoPlainText } from "./coValues/coPlainText.js";
|
|
6
|
-
import { RawBinaryCoStream
|
|
6
|
+
import { RawBinaryCoStream } from "./coValues/binaryCoStream.js";
|
|
7
|
+
import { RawCoStream } from "./coValues/coStream.js";
|
|
7
8
|
import { RawGroup } from "./coValues/group.js";
|
|
8
9
|
import { RawCoID } from "./ids.js";
|
|
9
10
|
import { JsonObject, JsonValue } from "./jsonValue.js";
|
|
@@ -42,7 +42,6 @@ import {
|
|
|
42
42
|
Uniqueness,
|
|
43
43
|
VerifiedState,
|
|
44
44
|
} from "./verifiedState.js";
|
|
45
|
-
import { SessionMap } from "./SessionMap.js";
|
|
46
45
|
import {
|
|
47
46
|
MergeCommit,
|
|
48
47
|
BranchPointerCommit,
|
|
@@ -65,45 +64,6 @@ import {
|
|
|
65
64
|
} from "../knownState.js";
|
|
66
65
|
import { safeParseJSON } from "../jsonStringify.js";
|
|
67
66
|
|
|
68
|
-
export type ValidationValue =
|
|
69
|
-
| { isOk: true }
|
|
70
|
-
| {
|
|
71
|
-
isOk: false;
|
|
72
|
-
message: string;
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
function validateUniqueness(uniqueness: Uniqueness): ValidationValue {
|
|
76
|
-
if (typeof uniqueness === "number" && !Number.isInteger(uniqueness)) {
|
|
77
|
-
return {
|
|
78
|
-
isOk: false,
|
|
79
|
-
message: "Uniqueness cannot be a non-integer number, got " + uniqueness,
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (Array.isArray(uniqueness)) {
|
|
84
|
-
return {
|
|
85
|
-
isOk: false,
|
|
86
|
-
message: "Uniqueness cannot be an array, got " + uniqueness,
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if (typeof uniqueness === "object" && uniqueness !== null) {
|
|
91
|
-
for (let [key, value] of Object.entries(uniqueness)) {
|
|
92
|
-
if (typeof value !== "string") {
|
|
93
|
-
return {
|
|
94
|
-
isOk: false,
|
|
95
|
-
message:
|
|
96
|
-
"Uniqueness object values must be a string, got " +
|
|
97
|
-
value +
|
|
98
|
-
" for key " +
|
|
99
|
-
key,
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
return { isOk: true };
|
|
105
|
-
}
|
|
106
|
-
|
|
107
67
|
export function idforHeader(
|
|
108
68
|
header: CoValueHeader,
|
|
109
69
|
crypto: CryptoProvider,
|
|
@@ -137,13 +97,17 @@ export class VerifiedTransaction {
|
|
|
137
97
|
changes: JsonValue[] | undefined;
|
|
138
98
|
// The decoded meta information of the transaction
|
|
139
99
|
meta: JsonObject | undefined;
|
|
140
|
-
isValidated: boolean = false;
|
|
141
100
|
// Whether the transaction is valid, as per membership rules
|
|
142
101
|
isValid: boolean = false;
|
|
143
102
|
// The error message that caused the transaction to be invalid
|
|
144
103
|
validationErrorMessage: string | undefined = undefined;
|
|
145
104
|
// The previous verified transaction for the same session
|
|
146
105
|
previous: VerifiedTransaction | undefined;
|
|
106
|
+
// Transaction processing stage:
|
|
107
|
+
// - "to-validate": Transaction is pending validation on permissions checks
|
|
108
|
+
// - "validated": Transaction has been validated but not yet applied to content
|
|
109
|
+
// - "processed": Transaction has been validated and applied to content
|
|
110
|
+
stage: "to-validate" | "validated" | "processed" = "to-validate";
|
|
147
111
|
|
|
148
112
|
constructor(
|
|
149
113
|
coValueId: RawCoID,
|
|
@@ -214,18 +178,31 @@ export class VerifiedTransaction {
|
|
|
214
178
|
return Boolean(this.isValid && this.changes);
|
|
215
179
|
}
|
|
216
180
|
|
|
181
|
+
isProcessable(includeInvalidMetaTransactions: boolean): this is {
|
|
182
|
+
changes: JsonValue[];
|
|
183
|
+
} {
|
|
184
|
+
return Boolean(
|
|
185
|
+
this.changes && (includeInvalidMetaTransactions || this.isValid),
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
217
189
|
markValid() {
|
|
190
|
+
const validityChanged = this.isValid === false;
|
|
218
191
|
this.isValid = true;
|
|
219
192
|
this.validationErrorMessage = undefined;
|
|
220
193
|
|
|
221
|
-
if (
|
|
222
|
-
this.
|
|
194
|
+
if (this.stage === "to-validate") {
|
|
195
|
+
this.stage = "validated";
|
|
196
|
+
this.dispatchTransaction(this);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (this.stage === "processed" && validityChanged) {
|
|
223
200
|
this.dispatchTransaction(this);
|
|
224
201
|
}
|
|
225
202
|
}
|
|
226
203
|
|
|
227
204
|
markInvalid(errorMessage: string, attributes?: Record<string, JsonValue>) {
|
|
228
|
-
this.
|
|
205
|
+
const validityChanged = this.isValid === true;
|
|
229
206
|
this.isValid = false;
|
|
230
207
|
|
|
231
208
|
this.validationErrorMessage = errorMessage;
|
|
@@ -237,6 +214,22 @@ export class VerifiedTransaction {
|
|
|
237
214
|
...attributes,
|
|
238
215
|
});
|
|
239
216
|
}
|
|
217
|
+
|
|
218
|
+
if (this.stage === "processed" && validityChanged) {
|
|
219
|
+
this.dispatchTransaction(this);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (this.stage === "to-validate") {
|
|
223
|
+
this.stage = "validated";
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
markAsProcessed() {
|
|
228
|
+
this.stage = "processed";
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
markAsToValidate() {
|
|
232
|
+
this.stage = "to-validate";
|
|
240
233
|
}
|
|
241
234
|
}
|
|
242
235
|
|
|
@@ -586,36 +579,34 @@ export class CoValueCore {
|
|
|
586
579
|
streamingKnownState?: KnownStateSessions,
|
|
587
580
|
skipVerify?: boolean,
|
|
588
581
|
) {
|
|
589
|
-
if (
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
errorMessage: validation.message,
|
|
595
|
-
});
|
|
596
|
-
return false;
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
const expectedId = idforHeader(header, this.node.crypto);
|
|
582
|
+
if (this._verified?.sessionCount) {
|
|
583
|
+
throw new Error(
|
|
584
|
+
"CoValueCore: provideHeader called on coValue with verified sessions present!",
|
|
585
|
+
);
|
|
586
|
+
}
|
|
600
587
|
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
588
|
+
// Create VerifiedState - Rust validates uniqueness and id match unless skipVerify is true
|
|
589
|
+
try {
|
|
590
|
+
this._verified = new VerifiedState(
|
|
591
|
+
this.id,
|
|
592
|
+
this.node.crypto,
|
|
593
|
+
header,
|
|
594
|
+
streamingKnownState,
|
|
595
|
+
skipVerify,
|
|
596
|
+
);
|
|
597
|
+
} catch (e) {
|
|
598
|
+
// Rust validation failed (invalid uniqueness or id mismatch)
|
|
599
|
+
logger.error("Header validation failed", {
|
|
600
|
+
id: this.id,
|
|
601
|
+
header,
|
|
602
|
+
error: e instanceof Error ? e.message : String(e),
|
|
603
|
+
});
|
|
604
|
+
return false;
|
|
604
605
|
}
|
|
605
606
|
|
|
607
|
+
// Only add dependencies after successful validation
|
|
606
608
|
this.addDependencyFromHeader(header);
|
|
607
609
|
|
|
608
|
-
if (this._verified?.sessions.size) {
|
|
609
|
-
throw new Error(
|
|
610
|
-
"CoValueCore: provideHeader called on coValue with verified sessions present!",
|
|
611
|
-
);
|
|
612
|
-
}
|
|
613
|
-
this._verified = new VerifiedState(
|
|
614
|
-
this.id,
|
|
615
|
-
this.node.crypto,
|
|
616
|
-
header,
|
|
617
|
-
new SessionMap(this.id, this.node.crypto, streamingKnownState),
|
|
618
|
-
);
|
|
619
610
|
// Clean up if transitioning from garbageCollected/onlyKnownState
|
|
620
611
|
if (this.isAvailable()) {
|
|
621
612
|
this.cleanupLastKnownState();
|
|
@@ -651,7 +642,7 @@ export class CoValueCore {
|
|
|
651
642
|
*/
|
|
652
643
|
knownStateWithStreaming(): CoValueKnownState {
|
|
653
644
|
if (this.verified) {
|
|
654
|
-
return this.verified.
|
|
645
|
+
return this.verified.knownStateWithStreaming();
|
|
655
646
|
}
|
|
656
647
|
|
|
657
648
|
return this.knownState();
|
|
@@ -660,16 +651,12 @@ export class CoValueCore {
|
|
|
660
651
|
/**
|
|
661
652
|
* Returns the known state of the CoValue
|
|
662
653
|
*
|
|
663
|
-
* The return value identity is going to be stable as long as the CoValue is not modified.
|
|
664
|
-
*
|
|
665
|
-
* On change the knownState is invalidated and a new object is returned.
|
|
666
|
-
*
|
|
667
654
|
* For garbageCollected/onlyKnownState CoValues, returns the cached knownState.
|
|
668
655
|
*/
|
|
669
656
|
knownState(): CoValueKnownState {
|
|
670
657
|
// 1. If we have verified content in memory, use that (authoritative)
|
|
671
658
|
if (this.verified) {
|
|
672
|
-
return this.verified.
|
|
659
|
+
return this.verified.knownState();
|
|
673
660
|
}
|
|
674
661
|
|
|
675
662
|
// 2. If we have last known state (GC'd or onlyKnownState), use that
|
|
@@ -730,7 +717,7 @@ export class CoValueCore {
|
|
|
730
717
|
|
|
731
718
|
return {
|
|
732
719
|
sessionID,
|
|
733
|
-
txIndex: this.verified.
|
|
720
|
+
txIndex: this.verified.getTransactionsCount(sessionID) || 0,
|
|
734
721
|
};
|
|
735
722
|
}
|
|
736
723
|
|
|
@@ -761,8 +748,7 @@ export class CoValueCore {
|
|
|
761
748
|
let deleteTransaction: Transaction | undefined = undefined;
|
|
762
749
|
|
|
763
750
|
if (isDeleteSessionID(sessionID)) {
|
|
764
|
-
const txCount =
|
|
765
|
-
this.verified.sessions.get(sessionID)?.transactions.length ?? 0;
|
|
751
|
+
const txCount = this.verified.getTransactionsCount(sessionID) ?? 0;
|
|
766
752
|
if (txCount > 0 || newTransactions.length > 1) {
|
|
767
753
|
return {
|
|
768
754
|
value: true,
|
|
@@ -1051,6 +1037,20 @@ export class CoValueCore {
|
|
|
1051
1037
|
}
|
|
1052
1038
|
}
|
|
1053
1039
|
|
|
1040
|
+
#isContentRebuildScheduled = false;
|
|
1041
|
+
scheduleContentRebuild() {
|
|
1042
|
+
if (!this._cachedContent || this.#isContentRebuildScheduled) {
|
|
1043
|
+
return;
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
this.#isContentRebuildScheduled = true;
|
|
1047
|
+
|
|
1048
|
+
queueMicrotask(() => {
|
|
1049
|
+
this.#isContentRebuildScheduled = false;
|
|
1050
|
+
this._cachedContent?.rebuildFromCore();
|
|
1051
|
+
});
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
1054
|
#isNotifyUpdatePaused = false;
|
|
1055
1055
|
pauseNotifyUpdate() {
|
|
1056
1056
|
this.#isNotifyUpdatePaused = true;
|
|
@@ -1319,7 +1319,7 @@ export class CoValueCore {
|
|
|
1319
1319
|
// Store the validity of the transactions before resetting the parsed transactions
|
|
1320
1320
|
const validityBeforeReset = new Array<boolean>(verifiedTransactions.length);
|
|
1321
1321
|
this.verifiedTransactions.forEach((transaction, index) => {
|
|
1322
|
-
transaction.
|
|
1322
|
+
transaction.markAsToValidate();
|
|
1323
1323
|
validityBeforeReset[index] = transaction.isValidTransactionWithChanges();
|
|
1324
1324
|
});
|
|
1325
1325
|
|
|
@@ -1327,6 +1327,7 @@ export class CoValueCore {
|
|
|
1327
1327
|
this.toProcessTransactions = [];
|
|
1328
1328
|
this.toDecryptTransactions = [];
|
|
1329
1329
|
this.toParseMetaTransactions = [];
|
|
1330
|
+
this.#fwwWinners.clear();
|
|
1330
1331
|
|
|
1331
1332
|
this.parseNewTransactions(false);
|
|
1332
1333
|
|
|
@@ -1378,7 +1379,7 @@ export class CoValueCore {
|
|
|
1378
1379
|
|
|
1379
1380
|
const isBranched = this.isBranched();
|
|
1380
1381
|
|
|
1381
|
-
for (const [sessionID, sessionLog] of this.verified.
|
|
1382
|
+
for (const [sessionID, sessionLog] of this.verified.sessionEntries()) {
|
|
1382
1383
|
const count = this.verifiedTransactionsKnownSessions[sessionID] ?? 0;
|
|
1383
1384
|
|
|
1384
1385
|
for (
|
|
@@ -1427,11 +1428,16 @@ export class CoValueCore {
|
|
|
1427
1428
|
}
|
|
1428
1429
|
|
|
1429
1430
|
dispatchTransaction = (transaction: VerifiedTransaction) => {
|
|
1430
|
-
if (
|
|
1431
|
+
if (transaction.stage === "to-validate") {
|
|
1431
1432
|
this.toValidateTransactions.push(transaction);
|
|
1432
1433
|
return;
|
|
1433
1434
|
}
|
|
1434
1435
|
|
|
1436
|
+
if (transaction.stage === "processed") {
|
|
1437
|
+
this.scheduleContentRebuild();
|
|
1438
|
+
return;
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1435
1441
|
if (transaction.changes) {
|
|
1436
1442
|
this.toProcessTransactions.push(transaction);
|
|
1437
1443
|
} else {
|
|
@@ -1451,6 +1457,8 @@ export class CoValueCore {
|
|
|
1451
1457
|
this.toValidateTransactions = [];
|
|
1452
1458
|
}
|
|
1453
1459
|
|
|
1460
|
+
#fwwWinners: Map<string, VerifiedTransaction> = new Map();
|
|
1461
|
+
|
|
1454
1462
|
/**
|
|
1455
1463
|
* Parses the meta information of a transaction, and set the branchStart and mergeCommits.
|
|
1456
1464
|
*/
|
|
@@ -1489,6 +1497,30 @@ export class CoValueCore {
|
|
|
1489
1497
|
this.mergeCommits.push(mergeCommit);
|
|
1490
1498
|
}
|
|
1491
1499
|
|
|
1500
|
+
if ("fww" in transaction.meta) {
|
|
1501
|
+
const fwwKey = transaction.meta.fww as string;
|
|
1502
|
+
const currentWinner = this.#fwwWinners.get(fwwKey);
|
|
1503
|
+
|
|
1504
|
+
// First-writer-wins: keep the transaction with the smallest madeAt
|
|
1505
|
+
// compareTransactions returns < 0 if transaction is earlier than currentWinner
|
|
1506
|
+
if (
|
|
1507
|
+
!currentWinner ||
|
|
1508
|
+
this.compareTransactions(transaction, currentWinner) < 0
|
|
1509
|
+
) {
|
|
1510
|
+
if (currentWinner) {
|
|
1511
|
+
currentWinner.markInvalid(
|
|
1512
|
+
`Transaction is not the first writer for fww key "${fwwKey}"`,
|
|
1513
|
+
);
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
this.#fwwWinners.set(fwwKey, transaction);
|
|
1517
|
+
} else {
|
|
1518
|
+
transaction.markInvalid(
|
|
1519
|
+
`Transaction is not the first writer for fww key "${fwwKey}"`,
|
|
1520
|
+
);
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1492
1524
|
// Check if the transaction has been merged from a branch
|
|
1493
1525
|
if ("mi" in transaction.meta) {
|
|
1494
1526
|
const meta = transaction.meta as MergedTransactionMetadata;
|
|
@@ -1570,7 +1602,7 @@ export class CoValueCore {
|
|
|
1570
1602
|
from?: CoValueKnownState["sessions"];
|
|
1571
1603
|
to?: CoValueKnownState["sessions"];
|
|
1572
1604
|
knownTransactions?: Record<RawCoID, number>;
|
|
1573
|
-
|
|
1605
|
+
includeInvalidMetaTransactions?: boolean;
|
|
1574
1606
|
// If true, the branch source transactions will be skipped. Used to gather the transactions for the merge operation.
|
|
1575
1607
|
skipBranchSource?: boolean;
|
|
1576
1608
|
}): DecryptedTransaction[] {
|
|
@@ -1589,6 +1621,11 @@ export class CoValueCore {
|
|
|
1589
1621
|
|
|
1590
1622
|
const knownTransactions = options?.knownTransactions?.[this.id] ?? 0;
|
|
1591
1623
|
|
|
1624
|
+
// Include invalid transactions in the result (only transactions invalidated by metadata parsing are included e.g. init transactions)
|
|
1625
|
+
// permission errors are still not included
|
|
1626
|
+
const includeInvalidMetaTransactions =
|
|
1627
|
+
options?.includeInvalidMetaTransactions ?? false;
|
|
1628
|
+
|
|
1592
1629
|
for (
|
|
1593
1630
|
let i = knownTransactions;
|
|
1594
1631
|
i < this.toProcessTransactions.length;
|
|
@@ -1596,7 +1633,7 @@ export class CoValueCore {
|
|
|
1596
1633
|
) {
|
|
1597
1634
|
const transaction = this.toProcessTransactions[i]!;
|
|
1598
1635
|
|
|
1599
|
-
if (!transaction.
|
|
1636
|
+
if (!transaction.isProcessable(includeInvalidMetaTransactions)) {
|
|
1600
1637
|
continue;
|
|
1601
1638
|
}
|
|
1602
1639
|
|
|
@@ -1613,6 +1650,7 @@ export class CoValueCore {
|
|
|
1613
1650
|
continue;
|
|
1614
1651
|
}
|
|
1615
1652
|
|
|
1653
|
+
transaction.markAsProcessed();
|
|
1616
1654
|
matchingTransactions.push(transaction);
|
|
1617
1655
|
}
|
|
1618
1656
|
|
|
@@ -1623,6 +1661,7 @@ export class CoValueCore {
|
|
|
1623
1661
|
// If this is a branch, we load the valid transactions from the source
|
|
1624
1662
|
if (source && this.branchStart && !options?.skipBranchSource) {
|
|
1625
1663
|
const sourceTransactions = source.getValidTransactions({
|
|
1664
|
+
includeInvalidMetaTransactions,
|
|
1626
1665
|
knownTransactions: options?.knownTransactions,
|
|
1627
1666
|
to: this.branchStart,
|
|
1628
1667
|
ignorePrivateTransactions: options?.ignorePrivateTransactions ?? false,
|
|
@@ -1781,6 +1820,9 @@ export class CoValueCore {
|
|
|
1781
1820
|
|
|
1782
1821
|
// The transactions that have already been processed, used for the incremental builds of the content views
|
|
1783
1822
|
knownTransactions?: Record<RawCoID, number>;
|
|
1823
|
+
|
|
1824
|
+
// Whether to include invalid transactions in the result (only transactions invalidated by metadata parsing are included e.g. init transactions)
|
|
1825
|
+
includeInvalidMetaTransactions?: boolean;
|
|
1784
1826
|
}): DecryptedTransaction[] {
|
|
1785
1827
|
const allTransactions = this.getValidTransactions(options);
|
|
1786
1828
|
|
|
@@ -1903,7 +1945,7 @@ export class CoValueCore {
|
|
|
1903
1945
|
}
|
|
1904
1946
|
|
|
1905
1947
|
getTx(txID: TransactionID): Transaction | undefined {
|
|
1906
|
-
return this.verified?.
|
|
1948
|
+
return this.verified?.getSession(txID.sessionID)?.transactions[
|
|
1907
1949
|
txID.txIndex
|
|
1908
1950
|
];
|
|
1909
1951
|
}
|