cojson 0.8.12 → 0.8.16
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/CHANGELOG.md +89 -83
- package/dist/native/PeerKnownStates.js +1 -1
- package/dist/native/PeerKnownStates.js.map +1 -1
- package/dist/native/PeerState.js +1 -1
- package/dist/native/PeerState.js.map +1 -1
- package/dist/native/PriorityBasedMessageQueue.js +1 -10
- package/dist/native/PriorityBasedMessageQueue.js.map +1 -1
- package/dist/native/base64url.js.map +1 -1
- package/dist/native/base64url.test.js +1 -1
- package/dist/native/base64url.test.js.map +1 -1
- package/dist/native/coValue.js.map +1 -1
- package/dist/native/coValueCore.js +141 -149
- package/dist/native/coValueCore.js.map +1 -1
- package/dist/native/coValueState.js.map +1 -1
- package/dist/native/coValues/account.js +6 -6
- package/dist/native/coValues/account.js.map +1 -1
- package/dist/native/coValues/coList.js +2 -3
- package/dist/native/coValues/coList.js.map +1 -1
- package/dist/native/coValues/coMap.js +1 -1
- package/dist/native/coValues/coMap.js.map +1 -1
- package/dist/native/coValues/coStream.js +3 -5
- package/dist/native/coValues/coStream.js.map +1 -1
- package/dist/native/coValues/group.js +11 -11
- package/dist/native/coValues/group.js.map +1 -1
- package/dist/native/coreToCoValue.js +2 -2
- package/dist/native/coreToCoValue.js.map +1 -1
- package/dist/native/crypto/PureJSCrypto.js +4 -4
- package/dist/native/crypto/PureJSCrypto.js.map +1 -1
- package/dist/native/crypto/crypto.js.map +1 -1
- package/dist/native/exports.js +12 -12
- package/dist/native/exports.js.map +1 -1
- package/dist/native/ids.js.map +1 -1
- package/dist/native/jsonStringify.js.map +1 -1
- package/dist/native/localNode.js +5 -7
- package/dist/native/localNode.js.map +1 -1
- package/dist/native/permissions.js +4 -7
- package/dist/native/permissions.js.map +1 -1
- package/dist/native/priority.js.map +1 -1
- package/dist/native/storage/FileSystem.js.map +1 -1
- package/dist/native/storage/chunksAndKnownStates.js +2 -4
- package/dist/native/storage/chunksAndKnownStates.js.map +1 -1
- package/dist/native/storage/index.js +6 -15
- package/dist/native/storage/index.js.map +1 -1
- package/dist/native/streamUtils.js.map +1 -1
- package/dist/native/sync.js +2 -4
- package/dist/native/sync.js.map +1 -1
- package/dist/native/typeUtils/accountOrAgentIDfromSessionID.js.map +1 -1
- package/dist/native/typeUtils/expectGroup.js.map +1 -1
- package/dist/native/typeUtils/isAccountID.js.map +1 -1
- package/dist/native/typeUtils/isCoValue.js +1 -1
- package/dist/native/typeUtils/isCoValue.js.map +1 -1
- package/dist/web/PeerKnownStates.js +1 -1
- package/dist/web/PeerKnownStates.js.map +1 -1
- package/dist/web/PeerState.js +1 -1
- package/dist/web/PeerState.js.map +1 -1
- package/dist/web/PriorityBasedMessageQueue.js +1 -10
- package/dist/web/PriorityBasedMessageQueue.js.map +1 -1
- package/dist/web/base64url.js.map +1 -1
- package/dist/web/base64url.test.js +1 -1
- package/dist/web/base64url.test.js.map +1 -1
- package/dist/web/coValue.js.map +1 -1
- package/dist/web/coValueCore.js +141 -149
- package/dist/web/coValueCore.js.map +1 -1
- package/dist/web/coValueState.js.map +1 -1
- package/dist/web/coValues/account.js +6 -6
- package/dist/web/coValues/account.js.map +1 -1
- package/dist/web/coValues/coList.js +2 -3
- package/dist/web/coValues/coList.js.map +1 -1
- package/dist/web/coValues/coMap.js +1 -1
- package/dist/web/coValues/coMap.js.map +1 -1
- package/dist/web/coValues/coStream.js +3 -5
- package/dist/web/coValues/coStream.js.map +1 -1
- package/dist/web/coValues/group.js +11 -11
- package/dist/web/coValues/group.js.map +1 -1
- package/dist/web/coreToCoValue.js +2 -2
- package/dist/web/coreToCoValue.js.map +1 -1
- package/dist/web/crypto/PureJSCrypto.js +4 -4
- package/dist/web/crypto/PureJSCrypto.js.map +1 -1
- package/dist/web/crypto/WasmCrypto.js +5 -5
- package/dist/web/crypto/WasmCrypto.js.map +1 -1
- package/dist/web/crypto/crypto.js.map +1 -1
- package/dist/web/exports.js +12 -12
- package/dist/web/exports.js.map +1 -1
- package/dist/web/ids.js.map +1 -1
- package/dist/web/jsonStringify.js.map +1 -1
- package/dist/web/localNode.js +5 -7
- package/dist/web/localNode.js.map +1 -1
- package/dist/web/permissions.js +4 -7
- package/dist/web/permissions.js.map +1 -1
- package/dist/web/priority.js.map +1 -1
- package/dist/web/storage/FileSystem.js.map +1 -1
- package/dist/web/storage/chunksAndKnownStates.js +2 -4
- package/dist/web/storage/chunksAndKnownStates.js.map +1 -1
- package/dist/web/storage/index.js +6 -15
- package/dist/web/storage/index.js.map +1 -1
- package/dist/web/streamUtils.js.map +1 -1
- package/dist/web/sync.js +2 -4
- package/dist/web/sync.js.map +1 -1
- package/dist/web/typeUtils/accountOrAgentIDfromSessionID.js.map +1 -1
- package/dist/web/typeUtils/expectGroup.js.map +1 -1
- package/dist/web/typeUtils/isAccountID.js.map +1 -1
- package/dist/web/typeUtils/isCoValue.js +1 -1
- package/dist/web/typeUtils/isCoValue.js.map +1 -1
- package/package.json +4 -14
- package/src/PeerKnownStates.ts +91 -89
- package/src/PeerState.ts +72 -73
- package/src/PriorityBasedMessageQueue.ts +42 -49
- package/src/base64url.test.ts +24 -24
- package/src/base64url.ts +44 -45
- package/src/coValue.ts +45 -45
- package/src/coValueCore.ts +746 -785
- package/src/coValueState.ts +82 -72
- package/src/coValues/account.ts +143 -150
- package/src/coValues/coList.ts +520 -522
- package/src/coValues/coMap.ts +283 -285
- package/src/coValues/coStream.ts +320 -324
- package/src/coValues/group.ts +306 -305
- package/src/coreToCoValue.ts +28 -31
- package/src/crypto/PureJSCrypto.ts +188 -194
- package/src/crypto/WasmCrypto.ts +236 -254
- package/src/crypto/crypto.ts +302 -309
- package/src/exports.ts +116 -116
- package/src/ids.ts +9 -9
- package/src/jsonStringify.ts +46 -46
- package/src/jsonValue.ts +24 -10
- package/src/localNode.ts +635 -660
- package/src/media.ts +3 -3
- package/src/permissions.ts +272 -278
- package/src/priority.ts +21 -19
- package/src/storage/FileSystem.ts +91 -99
- package/src/storage/chunksAndKnownStates.ts +110 -115
- package/src/storage/index.ts +466 -497
- package/src/streamUtils.ts +60 -60
- package/src/sync.ts +593 -615
- package/src/tests/PeerKnownStates.test.ts +38 -34
- package/src/tests/PeerState.test.ts +101 -64
- package/src/tests/PriorityBasedMessageQueue.test.ts +91 -91
- package/src/tests/account.test.ts +59 -59
- package/src/tests/coList.test.ts +65 -65
- package/src/tests/coMap.test.ts +137 -137
- package/src/tests/coStream.test.ts +254 -257
- package/src/tests/coValueCore.test.ts +153 -156
- package/src/tests/crypto.test.ts +136 -144
- package/src/tests/cryptoImpl.test.ts +205 -197
- package/src/tests/group.test.ts +24 -24
- package/src/tests/permissions.test.ts +1306 -1371
- package/src/tests/priority.test.ts +65 -82
- package/src/tests/sync.test.ts +1300 -1291
- package/src/tests/testUtils.ts +52 -53
- package/src/typeUtils/accountOrAgentIDfromSessionID.ts +4 -4
- package/src/typeUtils/expectGroup.ts +9 -9
- package/src/typeUtils/isAccountID.ts +1 -1
- package/src/typeUtils/isCoValue.ts +9 -9
- package/tsconfig.json +4 -6
- package/tsconfig.native.json +9 -11
- package/tsconfig.web.json +4 -10
- package/.eslintrc.cjs +0 -25
- package/.prettierrc.js +0 -9
package/src/PeerState.ts
CHANGED
|
@@ -1,91 +1,90 @@
|
|
|
1
|
-
import { RawCoID } from "./ids.js";
|
|
2
1
|
import { PeerKnownStates } from "./PeerKnownStates.js";
|
|
3
|
-
import { CO_VALUE_PRIORITY } from "./priority.js";
|
|
4
2
|
import {
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
PriorityBasedMessageQueue,
|
|
4
|
+
QueueEntry,
|
|
7
5
|
} from "./PriorityBasedMessageQueue.js";
|
|
6
|
+
import { RawCoID } from "./ids.js";
|
|
7
|
+
import { CO_VALUE_PRIORITY } from "./priority.js";
|
|
8
8
|
import { Peer, SyncMessage } from "./sync.js";
|
|
9
9
|
|
|
10
10
|
export class PeerState {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
constructor(private peer: Peer) {}
|
|
12
|
+
|
|
13
|
+
readonly optimisticKnownStates = new PeerKnownStates();
|
|
14
|
+
readonly toldKnownState: Set<RawCoID> = new Set();
|
|
15
|
+
|
|
16
|
+
get id() {
|
|
17
|
+
return this.peer.id;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
get role() {
|
|
21
|
+
return this.peer.role;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
get priority() {
|
|
25
|
+
return this.peer.priority;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get crashOnClose() {
|
|
29
|
+
return this.peer.crashOnClose;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
isServerOrStoragePeer() {
|
|
33
|
+
return this.peer.role === "server" || this.peer.role === "storage";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* We set as default priority HIGH to handle all the messages without a
|
|
38
|
+
* priority property as HIGH priority.
|
|
39
|
+
*
|
|
40
|
+
* This way we consider all the non-content messsages as HIGH priority.
|
|
41
|
+
*/
|
|
42
|
+
private queue = new PriorityBasedMessageQueue(CO_VALUE_PRIORITY.HIGH);
|
|
43
|
+
private processing = false;
|
|
44
|
+
public closed = false;
|
|
45
|
+
|
|
46
|
+
async processQueue() {
|
|
47
|
+
if (this.processing) {
|
|
48
|
+
return;
|
|
18
49
|
}
|
|
19
50
|
|
|
20
|
-
|
|
21
|
-
|
|
51
|
+
this.processing = true;
|
|
52
|
+
|
|
53
|
+
let entry: QueueEntry | undefined;
|
|
54
|
+
while ((entry = this.queue.pull())) {
|
|
55
|
+
// Awaiting the push to send one message at a time
|
|
56
|
+
// This way when the peer is "under pressure" we can enqueue all
|
|
57
|
+
// the coming messages and organize them by priority
|
|
58
|
+
await this.peer.outgoing
|
|
59
|
+
.push(entry.msg)
|
|
60
|
+
.then(entry.resolve)
|
|
61
|
+
.catch(entry.reject);
|
|
22
62
|
}
|
|
23
63
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
64
|
+
this.processing = false;
|
|
65
|
+
}
|
|
27
66
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
67
|
+
pushOutgoingMessage(msg: SyncMessage) {
|
|
68
|
+
const promise = this.queue.push(msg);
|
|
31
69
|
|
|
32
|
-
|
|
33
|
-
return this.peer.role === "server" || this.peer.role === "storage";
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* We set as default priority HIGH to handle all the messages without a
|
|
38
|
-
* priority property as HIGH priority.
|
|
39
|
-
*
|
|
40
|
-
* This way we consider all the non-content messsages as HIGH priority.
|
|
41
|
-
*/
|
|
42
|
-
private queue = new PriorityBasedMessageQueue(CO_VALUE_PRIORITY.HIGH);
|
|
43
|
-
private processing = false;
|
|
44
|
-
public closed = false;
|
|
45
|
-
|
|
46
|
-
async processQueue() {
|
|
47
|
-
if (this.processing) {
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
this.processing = true;
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
let entry: QueueEntry | undefined;
|
|
55
|
-
while ((entry = this.queue.pull())) {
|
|
56
|
-
// Awaiting the push to send one message at a time
|
|
57
|
-
// This way when the peer is "under pressure" we can enqueue all
|
|
58
|
-
// the coming messages and organize them by priority
|
|
59
|
-
await this.peer.outgoing
|
|
60
|
-
.push(entry.msg)
|
|
61
|
-
.then(entry.resolve)
|
|
62
|
-
.catch(entry.reject);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
this.processing = false;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
pushOutgoingMessage(msg: SyncMessage) {
|
|
69
|
-
const promise = this.queue.push(msg);
|
|
70
|
+
void this.processQueue();
|
|
70
71
|
|
|
71
|
-
|
|
72
|
+
return promise;
|
|
73
|
+
}
|
|
72
74
|
|
|
73
|
-
|
|
75
|
+
get incoming() {
|
|
76
|
+
if (this.closed) {
|
|
77
|
+
return (async function* () {
|
|
78
|
+
yield "Disconnected" as const;
|
|
79
|
+
})();
|
|
74
80
|
}
|
|
75
81
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
return (async function* () {
|
|
79
|
-
yield "Disconnected" as const;
|
|
80
|
-
})();
|
|
81
|
-
}
|
|
82
|
+
return this.peer.incoming;
|
|
83
|
+
}
|
|
82
84
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
this.peer.outgoing.close();
|
|
89
|
-
this.closed = true;
|
|
90
|
-
}
|
|
85
|
+
gracefulShutdown() {
|
|
86
|
+
console.debug("Gracefully closing", this.id);
|
|
87
|
+
this.peer.outgoing.close();
|
|
88
|
+
this.closed = true;
|
|
89
|
+
}
|
|
91
90
|
}
|
|
@@ -2,76 +2,69 @@ import { CoValuePriority } from "./priority.js";
|
|
|
2
2
|
import { SyncMessage } from "./sync.js";
|
|
3
3
|
|
|
4
4
|
function promiseWithResolvers<R>() {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
let resolve = (_: R) => {};
|
|
6
|
+
let reject = (_: unknown) => {};
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
const promise = new Promise<R>((_resolve, _reject) => {
|
|
9
|
+
resolve = _resolve;
|
|
10
|
+
reject = _reject;
|
|
11
|
+
});
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
return {
|
|
14
|
+
promise,
|
|
15
|
+
resolve,
|
|
16
|
+
reject,
|
|
17
|
+
};
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
export type QueueEntry = {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
msg: SyncMessage;
|
|
22
|
+
promise: Promise<void>;
|
|
23
|
+
resolve: () => void;
|
|
24
|
+
reject: (_: unknown) => void;
|
|
25
25
|
};
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
28
|
* Since we have a fixed range of priority values (0-7) we can create a fixed array of queues.
|
|
29
29
|
*/
|
|
30
|
-
type Tuple<T, N extends number, A extends unknown[] = []> = A extends {
|
|
30
|
+
type Tuple<T, N extends number, A extends unknown[] = []> = A extends {
|
|
31
|
+
length: N;
|
|
32
|
+
}
|
|
33
|
+
? A
|
|
34
|
+
: Tuple<T, N, [...A, T]>;
|
|
31
35
|
type QueueTuple = Tuple<QueueEntry[], 8>;
|
|
32
36
|
|
|
33
37
|
export class PriorityBasedMessageQueue {
|
|
34
|
-
|
|
35
|
-
[],
|
|
36
|
-
[],
|
|
37
|
-
[],
|
|
38
|
-
[],
|
|
39
|
-
[],
|
|
40
|
-
[],
|
|
41
|
-
[],
|
|
42
|
-
[],
|
|
43
|
-
];
|
|
44
|
-
|
|
45
|
-
private getQueue(priority: CoValuePriority) {
|
|
46
|
-
return this.queues[priority];
|
|
47
|
-
}
|
|
38
|
+
private queues: QueueTuple = [[], [], [], [], [], [], [], []];
|
|
48
39
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
40
|
+
private getQueue(priority: CoValuePriority) {
|
|
41
|
+
return this.queues[priority];
|
|
42
|
+
}
|
|
52
43
|
|
|
53
|
-
|
|
54
|
-
const { promise, resolve, reject } = promiseWithResolvers<void>();
|
|
55
|
-
const entry: QueueEntry = { msg, promise, resolve, reject };
|
|
44
|
+
constructor(private defaultPriority: CoValuePriority) {}
|
|
56
45
|
|
|
57
|
-
|
|
58
|
-
|
|
46
|
+
public push(msg: SyncMessage) {
|
|
47
|
+
const { promise, resolve, reject } = promiseWithResolvers<void>();
|
|
48
|
+
const entry: QueueEntry = { msg, promise, resolve, reject };
|
|
59
49
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
this.getQueue(this.defaultPriority).push(entry);
|
|
63
|
-
}
|
|
50
|
+
if ("priority" in msg) {
|
|
51
|
+
const queue = this.getQueue(msg.priority);
|
|
64
52
|
|
|
65
|
-
|
|
53
|
+
queue?.push(entry);
|
|
54
|
+
} else {
|
|
55
|
+
this.getQueue(this.defaultPriority).push(entry);
|
|
66
56
|
}
|
|
67
57
|
|
|
68
|
-
|
|
69
|
-
|
|
58
|
+
return promise;
|
|
59
|
+
}
|
|
70
60
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
61
|
+
public pull() {
|
|
62
|
+
const activeQueue = this.queues.find((queue) => queue.length > 0);
|
|
74
63
|
|
|
75
|
-
|
|
64
|
+
if (!activeQueue) {
|
|
65
|
+
return undefined;
|
|
76
66
|
}
|
|
67
|
+
|
|
68
|
+
return activeQueue.shift();
|
|
69
|
+
}
|
|
77
70
|
}
|
package/src/base64url.test.ts
CHANGED
|
@@ -1,33 +1,33 @@
|
|
|
1
|
+
import { expect, test } from "vitest";
|
|
1
2
|
import { base64URLtoBytes, bytesToBase64url } from "./base64url.js";
|
|
2
|
-
import { test, expect } from "vitest";
|
|
3
3
|
|
|
4
4
|
const txt = new TextEncoder();
|
|
5
5
|
|
|
6
6
|
test("Test our Base64 URL encoding and decoding", () => {
|
|
7
|
-
|
|
7
|
+
// tests from the RFC
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
expect(base64URLtoBytes("")).toEqual(new Uint8Array([]));
|
|
10
|
+
expect(bytesToBase64url(new Uint8Array([]))).toEqual("");
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
12
|
+
expect(bytesToBase64url(txt.encode("f"))).toEqual("Zg==");
|
|
13
|
+
expect(bytesToBase64url(txt.encode("fo"))).toEqual("Zm8=");
|
|
14
|
+
expect(bytesToBase64url(txt.encode("foo"))).toEqual("Zm9v");
|
|
15
|
+
expect(bytesToBase64url(txt.encode("foob"))).toEqual("Zm9vYg==");
|
|
16
|
+
expect(bytesToBase64url(txt.encode("fooba"))).toEqual("Zm9vYmE=");
|
|
17
|
+
expect(bytesToBase64url(txt.encode("foobar"))).toEqual("Zm9vYmFy");
|
|
18
|
+
// reverse
|
|
19
|
+
expect(base64URLtoBytes("Zg==")).toEqual(txt.encode("f"));
|
|
20
|
+
expect(base64URLtoBytes("Zm8=")).toEqual(txt.encode("fo"));
|
|
21
|
+
expect(base64URLtoBytes("Zm9v")).toEqual(txt.encode("foo"));
|
|
22
|
+
expect(base64URLtoBytes("Zm9vYg==")).toEqual(txt.encode("foob"));
|
|
23
|
+
expect(base64URLtoBytes("Zm9vYmE=")).toEqual(txt.encode("fooba"));
|
|
24
|
+
expect(base64URLtoBytes("Zm9vYmFy")).toEqual(txt.encode("foobar"));
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
26
|
+
expect(base64URLtoBytes("V2hhdCBkb2VzIDIgKyAyLjEgZXF1YWw_PyB-IDQ=")).toEqual(
|
|
27
|
+
txt.encode("What does 2 + 2.1 equal?? ~ 4"),
|
|
28
|
+
);
|
|
29
|
+
// reverse
|
|
30
|
+
expect(bytesToBase64url(txt.encode("What does 2 + 2.1 equal?? ~ 4"))).toEqual(
|
|
31
|
+
"V2hhdCBkb2VzIDIgKyAyLjEgZXF1YWw_PyB-IDQ=",
|
|
32
|
+
);
|
|
33
33
|
});
|
package/src/base64url.ts
CHANGED
|
@@ -2,67 +2,66 @@ const encoder = new TextEncoder();
|
|
|
2
2
|
const decoder = new TextDecoder();
|
|
3
3
|
|
|
4
4
|
export function base64URLtoBytes(base64: string) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
base64 = base64.replace(/=/g, "");
|
|
6
|
+
const n = base64.length;
|
|
7
|
+
const rem = n % 4;
|
|
8
|
+
const k = rem && rem - 1; // how many bytes the last base64 chunk encodes
|
|
9
|
+
const m = (n >> 2) * 3 + k; // total encoded bytes
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
const encoded = new Uint8Array(n + 3);
|
|
12
|
+
encoder.encodeInto(base64 + "===", encoded);
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
14
|
+
for (let i = 0, j = 0; i < n; i += 4, j += 3) {
|
|
15
|
+
const x =
|
|
16
|
+
(lookup[encoded[i]!]! << 18) +
|
|
17
|
+
(lookup[encoded[i + 1]!]! << 12) +
|
|
18
|
+
(lookup[encoded[i + 2]!]! << 6) +
|
|
19
|
+
lookup[encoded[i + 3]!]!;
|
|
20
|
+
encoded[j] = x >> 16;
|
|
21
|
+
encoded[j + 1] = (x >> 8) & 0xff;
|
|
22
|
+
encoded[j + 2] = x & 0xff;
|
|
23
|
+
}
|
|
24
|
+
return new Uint8Array(encoded.buffer, 0, m);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
export function bytesToBase64url(bytes: Uint8Array) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
// const before = performance.now();
|
|
29
|
+
const m = bytes.length;
|
|
30
|
+
const k = m % 3;
|
|
31
|
+
const n = Math.floor(m / 3) * 4 + (k && k + 1);
|
|
32
|
+
const N = Math.ceil(m / 3) * 4;
|
|
33
|
+
const encoded = new Uint8Array(N);
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
35
|
+
for (let i = 0, j = 0; j < m; i += 4, j += 3) {
|
|
36
|
+
const y = (bytes[j]! << 16) + (bytes[j + 1]! << 8) + (bytes[j + 2]! | 0);
|
|
37
|
+
encoded[i] = encodeLookup[y >> 18]!;
|
|
38
|
+
encoded[i + 1] = encodeLookup[(y >> 12) & 0x3f]!;
|
|
39
|
+
encoded[i + 2] = encodeLookup[(y >> 6) & 0x3f]!;
|
|
40
|
+
encoded[i + 3] = encodeLookup[y & 0x3f]!;
|
|
41
|
+
}
|
|
43
42
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
43
|
+
let base64 = decoder.decode(new Uint8Array(encoded.buffer, 0, n));
|
|
44
|
+
if (k === 1) base64 += "==";
|
|
45
|
+
if (k === 2) base64 += "=";
|
|
46
|
+
// const after = performance.now();
|
|
47
|
+
// console.log(
|
|
48
|
+
// "bytesToBase64url bandwidth in MB/s for length",
|
|
49
|
+
// (1000 * bytes.length / (after - before)) / (1024 * 1024),
|
|
50
|
+
// bytes.length
|
|
51
|
+
// );
|
|
52
|
+
return base64;
|
|
54
53
|
}
|
|
55
54
|
|
|
56
55
|
const alphabet =
|
|
57
|
-
|
|
56
|
+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
58
57
|
|
|
59
58
|
const lookup = new Uint8Array(128);
|
|
60
59
|
for (const [i, a] of Array.from(alphabet).entries()) {
|
|
61
|
-
|
|
60
|
+
lookup[a.charCodeAt(0)] = i;
|
|
62
61
|
}
|
|
63
62
|
lookup["=".charCodeAt(0)] = 0;
|
|
64
63
|
|
|
65
64
|
const encodeLookup = new Uint8Array(64);
|
|
66
65
|
for (const [i, a] of Array.from(alphabet).entries()) {
|
|
67
|
-
|
|
66
|
+
encodeLookup[i] = a.charCodeAt(0);
|
|
68
67
|
}
|
package/src/coValue.ts
CHANGED
|
@@ -1,68 +1,68 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { CoValueCore } from "./coValueCore.js";
|
|
2
|
+
import { RawProfile as Profile, RawAccount } from "./coValues/account.js";
|
|
3
|
+
import { RawCoList } from "./coValues/coList.js";
|
|
3
4
|
import { RawCoMap } from "./coValues/coMap.js";
|
|
4
5
|
import { RawBinaryCoStream, RawCoStream } from "./coValues/coStream.js";
|
|
5
|
-
import { RawCoList } from "./coValues/coList.js";
|
|
6
|
-
import { CoValueCore } from "./coValueCore.js";
|
|
7
6
|
import { RawGroup } from "./coValues/group.js";
|
|
8
|
-
import {
|
|
7
|
+
import { RawCoID } from "./ids.js";
|
|
8
|
+
import { JsonObject, JsonValue } from "./jsonValue.js";
|
|
9
9
|
|
|
10
10
|
export type CoID<T extends RawCoValue> = RawCoID & {
|
|
11
|
-
|
|
11
|
+
readonly __type: T;
|
|
12
12
|
};
|
|
13
13
|
|
|
14
14
|
export interface RawCoValue {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
15
|
+
/** The `CoValue`'s (precisely typed) `CoID` */
|
|
16
|
+
id: CoID<this>;
|
|
17
|
+
core: CoValueCore;
|
|
18
|
+
/** Specifies which kind of `CoValue` this is */
|
|
19
|
+
type: string;
|
|
20
|
+
/** The `CoValue`'s (precisely typed) static metadata */
|
|
21
|
+
headerMeta: JsonObject | null;
|
|
22
|
+
/** The `Group` this `CoValue` belongs to (determining permissions) */
|
|
23
|
+
group: RawGroup;
|
|
24
|
+
/** Returns an immutable JSON presentation of this `CoValue` */
|
|
25
|
+
toJSON(): JsonValue;
|
|
26
|
+
atTime(time: number): this;
|
|
27
|
+
/** Lets you subscribe to future updates to this CoValue (whether made locally or by other users).
|
|
28
|
+
*
|
|
29
|
+
* Takes a listener function that will be called with the current state for each update.
|
|
30
|
+
*
|
|
31
|
+
* Returns an unsubscribe function.
|
|
32
|
+
*
|
|
33
|
+
* Used internally by `useTelepathicData()` for reactive updates on changes to a `CoValue`. */
|
|
34
|
+
subscribe(listener: (coValue: this) => void): () => void;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
export type AnyRawCoValue =
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
38
|
+
| RawCoMap
|
|
39
|
+
| RawGroup
|
|
40
|
+
| RawAccount
|
|
41
|
+
| Profile
|
|
42
|
+
| RawCoList
|
|
43
|
+
| RawCoStream
|
|
44
|
+
| RawBinaryCoStream;
|
|
45
45
|
|
|
46
46
|
export function expectMap(content: RawCoValue): RawCoMap {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
if (content.type !== "comap") {
|
|
48
|
+
throw new Error("Expected map");
|
|
49
|
+
}
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
return content as RawCoMap;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
export function expectList(content: RawCoValue): RawCoList {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
if (content.type !== "colist") {
|
|
56
|
+
throw new Error("Expected list");
|
|
57
|
+
}
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
return content as RawCoList;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
export function expectStream(content: RawCoValue): RawCoStream {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
if (content.type !== "costream") {
|
|
64
|
+
throw new Error("Expected stream");
|
|
65
|
+
}
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
return content as RawCoStream;
|
|
68
68
|
}
|