cojson 0.4.8 → 0.5.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/CHANGELOG.md +7 -0
- package/dist/coValueCore.js +112 -45
- package/dist/coValueCore.js.map +1 -1
- package/dist/coValues/coList.js +22 -6
- package/dist/coValues/coList.js.map +1 -1
- package/dist/coValues/coMap.js +1 -2
- package/dist/coValues/coMap.js.map +1 -1
- package/dist/coValues/coStream.js +12 -8
- package/dist/coValues/coStream.js.map +1 -1
- package/dist/crypto.js +57 -31
- package/dist/crypto.js.map +1 -1
- package/dist/localNode.js +59 -16
- package/dist/localNode.js.map +1 -1
- package/dist/permissions.js +5 -3
- package/dist/permissions.js.map +1 -1
- package/dist/streamUtils.js +34 -8
- package/dist/streamUtils.js.map +1 -1
- package/dist/sync.js +180 -46
- package/dist/sync.js.map +1 -1
- package/dist/typeUtils/accountOrAgentIDfromSessionID.js +2 -1
- package/dist/typeUtils/accountOrAgentIDfromSessionID.js.map +1 -1
- package/package.json +4 -3
- package/src/coValueCore.ts +144 -66
- package/src/coValues/coList.ts +34 -12
- package/src/coValues/coMap.ts +2 -5
- package/src/coValues/coStream.ts +36 -11
- package/src/crypto.ts +83 -41
- package/src/localNode.ts +128 -33
- package/src/permissions.ts +19 -24
- package/src/streamUtils.ts +41 -8
- package/src/sync.ts +221 -60
- package/src/tests/coValue.test.ts +3 -3
- package/src/tests/permissions.test.ts +124 -83
- package/src/tests/sync.test.ts +29 -20
- package/src/typeUtils/accountOrAgentIDfromSessionID.ts +2 -1
package/src/coValues/coStream.ts
CHANGED
|
@@ -8,7 +8,6 @@ import { Group } from "./group.js";
|
|
|
8
8
|
import { AgentID, SessionID, TransactionID } from "../ids.js";
|
|
9
9
|
import { base64URLtoBytes, bytesToBase64url } from "../base64url.js";
|
|
10
10
|
import { AccountID } from "./account.js";
|
|
11
|
-
import { parseJSON } from "../jsonStringify.js";
|
|
12
11
|
|
|
13
12
|
export type BinaryStreamInfo = {
|
|
14
13
|
mimeType: string;
|
|
@@ -84,7 +83,7 @@ export class CoStreamView<
|
|
|
84
83
|
madeAt,
|
|
85
84
|
changes,
|
|
86
85
|
} of this.core.getValidSortedTransactions()) {
|
|
87
|
-
for (const changeUntyped of
|
|
86
|
+
for (const changeUntyped of changes) {
|
|
88
87
|
const change = changeUntyped as Item;
|
|
89
88
|
let entries = this.items[txID.sessionID];
|
|
90
89
|
if (!entries) {
|
|
@@ -294,7 +293,7 @@ export class BinaryCoStreamView<
|
|
|
294
293
|
implements CoValue
|
|
295
294
|
{
|
|
296
295
|
getBinaryChunks(
|
|
297
|
-
allowUnfinished?: boolean
|
|
296
|
+
allowUnfinished?: boolean,
|
|
298
297
|
):
|
|
299
298
|
| (BinaryStreamInfo & { chunks: Uint8Array[]; finished: boolean })
|
|
300
299
|
| undefined {
|
|
@@ -319,6 +318,8 @@ export class BinaryCoStreamView<
|
|
|
319
318
|
let finished = false;
|
|
320
319
|
// let totalLength = 0;
|
|
321
320
|
|
|
321
|
+
let lastProgressUpdate = Date.now();
|
|
322
|
+
|
|
322
323
|
for (const item of items.slice(1)) {
|
|
323
324
|
if (item.type === "end") {
|
|
324
325
|
finished = true;
|
|
@@ -335,6 +336,10 @@ export class BinaryCoStreamView<
|
|
|
335
336
|
);
|
|
336
337
|
// totalLength += chunk.length;
|
|
337
338
|
chunks.push(chunk);
|
|
339
|
+
|
|
340
|
+
if (Date.now() - lastProgressUpdate > 100) {
|
|
341
|
+
lastProgressUpdate = Date.now();
|
|
342
|
+
}
|
|
338
343
|
}
|
|
339
344
|
|
|
340
345
|
// const after = performance.now();
|
|
@@ -362,36 +367,55 @@ export class BinaryCoStream<
|
|
|
362
367
|
/** @internal */
|
|
363
368
|
push(
|
|
364
369
|
item: BinaryStreamItem,
|
|
365
|
-
privacy
|
|
366
|
-
): this
|
|
370
|
+
privacy?: "private" | "trusting",
|
|
371
|
+
): this
|
|
372
|
+
push(
|
|
373
|
+
item: BinaryStreamItem,
|
|
374
|
+
privacy: "private" | "trusting",
|
|
375
|
+
returnNewStream: true
|
|
376
|
+
): this
|
|
377
|
+
push(
|
|
378
|
+
item: BinaryStreamItem,
|
|
379
|
+
privacy: "private" | "trusting",
|
|
380
|
+
returnNewStream: false
|
|
381
|
+
): void
|
|
382
|
+
push(
|
|
383
|
+
item: BinaryStreamItem,
|
|
384
|
+
privacy: "private" | "trusting" = "private",
|
|
385
|
+
returnNewStream: boolean = true
|
|
386
|
+
): this | void {
|
|
367
387
|
this.core.makeTransaction([item], privacy);
|
|
368
|
-
|
|
388
|
+
if (returnNewStream) {
|
|
389
|
+
return new BinaryCoStream(this.core) as this;
|
|
390
|
+
}
|
|
369
391
|
}
|
|
370
392
|
|
|
371
393
|
startBinaryStream(
|
|
372
394
|
settings: BinaryStreamInfo,
|
|
373
395
|
privacy: "private" | "trusting" = "private"
|
|
374
|
-
):
|
|
396
|
+
): void {
|
|
375
397
|
return this.push(
|
|
376
398
|
{
|
|
377
399
|
type: "start",
|
|
378
400
|
...settings,
|
|
379
401
|
} satisfies BinaryStreamStart,
|
|
380
|
-
privacy
|
|
402
|
+
privacy,
|
|
403
|
+
false
|
|
381
404
|
);
|
|
382
405
|
}
|
|
383
406
|
|
|
384
407
|
pushBinaryStreamChunk(
|
|
385
408
|
chunk: Uint8Array,
|
|
386
409
|
privacy: "private" | "trusting" = "private"
|
|
387
|
-
):
|
|
410
|
+
): void {
|
|
388
411
|
// const before = performance.now();
|
|
389
412
|
return this.push(
|
|
390
413
|
{
|
|
391
414
|
type: "chunk",
|
|
392
415
|
chunk: `binary_U${bytesToBase64url(chunk)}`,
|
|
393
416
|
} satisfies BinaryStreamChunk,
|
|
394
|
-
privacy
|
|
417
|
+
privacy,
|
|
418
|
+
false
|
|
395
419
|
);
|
|
396
420
|
// const after = performance.now();
|
|
397
421
|
// console.log(
|
|
@@ -405,7 +429,8 @@ export class BinaryCoStream<
|
|
|
405
429
|
{
|
|
406
430
|
type: "end",
|
|
407
431
|
} satisfies BinaryStreamEnd,
|
|
408
|
-
privacy
|
|
432
|
+
privacy,
|
|
433
|
+
true
|
|
409
434
|
);
|
|
410
435
|
}
|
|
411
436
|
|
package/src/crypto.ts
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
initBundledOnce,
|
|
3
|
+
Ed25519SigningKey,
|
|
4
|
+
Ed25519VerifyingKey,
|
|
5
|
+
X25519StaticSecret,
|
|
6
|
+
Memory,
|
|
7
|
+
Ed25519Signature,
|
|
8
|
+
X25519PublicKey,
|
|
9
|
+
} from "@hazae41/berith";
|
|
2
10
|
import { xsalsa20_poly1305, xsalsa20 } from "@noble/ciphers/salsa";
|
|
3
11
|
import { JsonValue } from "./jsonValue.js";
|
|
4
12
|
import { base58 } from "@scure/base";
|
|
@@ -10,7 +18,13 @@ import { createBLAKE3 } from "hash-wasm";
|
|
|
10
18
|
import { Stringified, parseJSON, stableStringify } from "./jsonStringify.js";
|
|
11
19
|
|
|
12
20
|
let blake3Instance: Awaited<ReturnType<typeof createBLAKE3>>;
|
|
13
|
-
let blake3HashOnce: (data: Uint8Array) => Uint8Array
|
|
21
|
+
let blake3HashOnce: (data: Uint8Array) => Uint8Array = () => {
|
|
22
|
+
throw new Error(
|
|
23
|
+
"cojson WASM dependencies not yet loaded; Make sure to import `cojsonReady` from `cojson` and await it before using any cojson functionality:\n\n" +
|
|
24
|
+
'import { cojsonReady } from "cojson";\n' +
|
|
25
|
+
"await cojsonReady;\n\n"
|
|
26
|
+
);
|
|
27
|
+
};
|
|
14
28
|
let blake3HashOnceWithContext: (
|
|
15
29
|
data: Uint8Array,
|
|
16
30
|
{ context }: { context: Uint8Array }
|
|
@@ -21,29 +35,36 @@ let blake3incrementalUpdateSLOW_WITH_DEVTOOLS: (
|
|
|
21
35
|
) => Uint8Array;
|
|
22
36
|
let blake3digestForState: (state: Uint8Array) => Uint8Array;
|
|
23
37
|
|
|
24
|
-
export const cryptoReady =
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
38
|
+
export const cryptoReady = Promise.all([
|
|
39
|
+
new Promise<void>((resolve) => {
|
|
40
|
+
createBLAKE3()
|
|
41
|
+
.then((bl3) => {
|
|
42
|
+
blake3Instance = bl3;
|
|
43
|
+
blake3HashOnce = (data) => {
|
|
44
|
+
return bl3.init().update(data).digest("binary");
|
|
45
|
+
};
|
|
46
|
+
blake3HashOnceWithContext = (data, { context }) => {
|
|
47
|
+
return bl3
|
|
48
|
+
.init()
|
|
49
|
+
.update(context)
|
|
50
|
+
.update(data)
|
|
51
|
+
.digest("binary");
|
|
52
|
+
};
|
|
53
|
+
blake3incrementalUpdateSLOW_WITH_DEVTOOLS = (state, data) => {
|
|
54
|
+
bl3.load(state).update(data);
|
|
55
|
+
return bl3.save();
|
|
56
|
+
};
|
|
57
|
+
blake3digestForState = (state) => {
|
|
58
|
+
return bl3.load(state).digest("binary");
|
|
59
|
+
};
|
|
60
|
+
resolve();
|
|
61
|
+
})
|
|
62
|
+
.catch((e) =>
|
|
63
|
+
console.error("Failed to load cryptography dependencies", e)
|
|
64
|
+
);
|
|
65
|
+
}),
|
|
66
|
+
initBundledOnce(),
|
|
67
|
+
]);
|
|
47
68
|
|
|
48
69
|
export type SignerSecret = `signerSecret_z${string}`;
|
|
49
70
|
export type SignerID = `signer_z${string}`;
|
|
@@ -59,7 +80,9 @@ const textEncoder = new TextEncoder();
|
|
|
59
80
|
const textDecoder = new TextDecoder();
|
|
60
81
|
|
|
61
82
|
export function newRandomSigner(): SignerSecret {
|
|
62
|
-
return `signerSecret_z${base58.encode(
|
|
83
|
+
return `signerSecret_z${base58.encode(
|
|
84
|
+
new Ed25519SigningKey().to_bytes().copyAndDispose()
|
|
85
|
+
)}`;
|
|
63
86
|
}
|
|
64
87
|
|
|
65
88
|
export function signerSecretToBytes(secret: SignerSecret): Uint8Array {
|
|
@@ -72,17 +95,22 @@ export function signerSecretFromBytes(bytes: Uint8Array): SignerSecret {
|
|
|
72
95
|
|
|
73
96
|
export function getSignerID(secret: SignerSecret): SignerID {
|
|
74
97
|
return `signer_z${base58.encode(
|
|
75
|
-
|
|
76
|
-
base58.decode(secret.substring("signerSecret_z".length))
|
|
98
|
+
Ed25519SigningKey.from_bytes(
|
|
99
|
+
new Memory(base58.decode(secret.substring("signerSecret_z".length)))
|
|
77
100
|
)
|
|
101
|
+
.public()
|
|
102
|
+
.to_bytes()
|
|
103
|
+
.copyAndDispose()
|
|
78
104
|
)}`;
|
|
79
105
|
}
|
|
80
106
|
|
|
81
107
|
export function sign(secret: SignerSecret, message: JsonValue): Signature {
|
|
82
|
-
const signature =
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
108
|
+
const signature = Ed25519SigningKey.from_bytes(
|
|
109
|
+
new Memory(base58.decode(secret.substring("signerSecret_z".length)))
|
|
110
|
+
)
|
|
111
|
+
.sign(new Memory(textEncoder.encode(stableStringify(message))))
|
|
112
|
+
.to_bytes()
|
|
113
|
+
.copyAndDispose();
|
|
86
114
|
return `signature_z${base58.encode(signature)}`;
|
|
87
115
|
}
|
|
88
116
|
|
|
@@ -91,15 +119,20 @@ export function verify(
|
|
|
91
119
|
message: JsonValue,
|
|
92
120
|
id: SignerID
|
|
93
121
|
): boolean {
|
|
94
|
-
return
|
|
95
|
-
base58.decode(
|
|
96
|
-
|
|
97
|
-
|
|
122
|
+
return new Ed25519VerifyingKey(
|
|
123
|
+
new Memory(base58.decode(id.substring("signer_z".length)))
|
|
124
|
+
).verify(
|
|
125
|
+
new Memory(textEncoder.encode(stableStringify(message))),
|
|
126
|
+
new Ed25519Signature(
|
|
127
|
+
new Memory(base58.decode(signature.substring("signature_z".length)))
|
|
128
|
+
)
|
|
98
129
|
);
|
|
99
130
|
}
|
|
100
131
|
|
|
101
132
|
export function newRandomSealer(): SealerSecret {
|
|
102
|
-
return `sealerSecret_z${base58.encode(
|
|
133
|
+
return `sealerSecret_z${base58.encode(
|
|
134
|
+
new X25519StaticSecret().to_bytes().copyAndDispose()
|
|
135
|
+
)}`;
|
|
103
136
|
}
|
|
104
137
|
|
|
105
138
|
export function sealerSecretToBytes(secret: SealerSecret): Uint8Array {
|
|
@@ -112,9 +145,12 @@ export function sealerSecretFromBytes(bytes: Uint8Array): SealerSecret {
|
|
|
112
145
|
|
|
113
146
|
export function getSealerID(secret: SealerSecret): SealerID {
|
|
114
147
|
return `sealer_z${base58.encode(
|
|
115
|
-
|
|
116
|
-
base58.decode(secret.substring("sealerSecret_z".length))
|
|
148
|
+
X25519StaticSecret.from_bytes(
|
|
149
|
+
new Memory(base58.decode(secret.substring("sealerSecret_z".length)))
|
|
117
150
|
)
|
|
151
|
+
.to_public()
|
|
152
|
+
.to_bytes()
|
|
153
|
+
.copyAndDispose()
|
|
118
154
|
)}`;
|
|
119
155
|
}
|
|
120
156
|
|
|
@@ -180,7 +216,10 @@ export function seal<T extends JsonValue>({
|
|
|
180
216
|
|
|
181
217
|
const plaintext = textEncoder.encode(stableStringify(message));
|
|
182
218
|
|
|
183
|
-
const sharedSecret =
|
|
219
|
+
const sharedSecret = X25519StaticSecret.from_bytes(new Memory(senderPriv))
|
|
220
|
+
.diffie_hellman(X25519PublicKey.from_bytes(new Memory(sealerPub)))
|
|
221
|
+
.to_bytes()
|
|
222
|
+
.copyAndDispose();
|
|
184
223
|
|
|
185
224
|
const sealedBytes = xsalsa20_poly1305(sharedSecret, nOnce).encrypt(
|
|
186
225
|
plaintext
|
|
@@ -205,7 +244,10 @@ export function unseal<T extends JsonValue>(
|
|
|
205
244
|
|
|
206
245
|
const sealedBytes = base64URLtoBytes(sealed.substring("sealed_U".length));
|
|
207
246
|
|
|
208
|
-
const sharedSecret =
|
|
247
|
+
const sharedSecret = X25519StaticSecret.from_bytes(new Memory(sealerPriv))
|
|
248
|
+
.diffie_hellman(X25519PublicKey.from_bytes(new Memory(senderPub)))
|
|
249
|
+
.to_bytes()
|
|
250
|
+
.copyAndDispose();
|
|
209
251
|
|
|
210
252
|
const plaintext = xsalsa20_poly1305(sharedSecret, nOnce).decrypt(
|
|
211
253
|
sealedBytes
|
package/src/localNode.ts
CHANGED
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
Group,
|
|
20
20
|
secretSeedFromInviteSecret,
|
|
21
21
|
} from "./coValues/group.js";
|
|
22
|
-
import { Peer, SyncManager } from "./sync.js";
|
|
22
|
+
import { Peer, PeerID, SyncManager } from "./sync.js";
|
|
23
23
|
import { AgentID, RawCoID, SessionID, isAgentID } from "./ids.js";
|
|
24
24
|
import { CoID } from "./coValue.js";
|
|
25
25
|
import {
|
|
@@ -153,6 +153,11 @@ export class LocalNode {
|
|
|
153
153
|
}
|
|
154
154
|
|
|
155
155
|
const account = await accountPromise;
|
|
156
|
+
|
|
157
|
+
if (account === "unavailable") {
|
|
158
|
+
throw new Error("Account unavailable from all peers");
|
|
159
|
+
}
|
|
160
|
+
|
|
156
161
|
const controlledAccount = new ControlledAccount(
|
|
157
162
|
account.core,
|
|
158
163
|
accountSecret
|
|
@@ -199,14 +204,36 @@ export class LocalNode {
|
|
|
199
204
|
}
|
|
200
205
|
|
|
201
206
|
/** @internal */
|
|
202
|
-
|
|
207
|
+
async loadCoValueCore(
|
|
208
|
+
id: RawCoID,
|
|
209
|
+
options: {
|
|
210
|
+
dontLoadFrom?: PeerID;
|
|
211
|
+
dontWaitFor?: PeerID;
|
|
212
|
+
onProgress?: (progress: number) => void;
|
|
213
|
+
} = {}
|
|
214
|
+
): Promise<CoValueCore | "unavailable"> {
|
|
203
215
|
let entry = this.coValues[id];
|
|
204
216
|
if (!entry) {
|
|
205
|
-
|
|
217
|
+
const peersToWaitFor = new Set(
|
|
218
|
+
Object.values(this.syncManager.peers)
|
|
219
|
+
.filter((peer) => peer.role === "server")
|
|
220
|
+
.map((peer) => peer.id)
|
|
221
|
+
);
|
|
222
|
+
if (options.dontWaitFor) peersToWaitFor.delete(options.dontWaitFor);
|
|
223
|
+
entry = newLoadingState(peersToWaitFor, options.onProgress);
|
|
206
224
|
|
|
207
225
|
this.coValues[id] = entry;
|
|
208
226
|
|
|
209
|
-
this.syncManager
|
|
227
|
+
this.syncManager
|
|
228
|
+
.loadFromPeers(id, options.dontLoadFrom)
|
|
229
|
+
.catch((e) => {
|
|
230
|
+
console.error(
|
|
231
|
+
"Error loading from peers",
|
|
232
|
+
id,
|
|
233
|
+
|
|
234
|
+
e
|
|
235
|
+
);
|
|
236
|
+
});
|
|
210
237
|
}
|
|
211
238
|
if (entry.state === "loaded") {
|
|
212
239
|
return Promise.resolve(entry.coValue);
|
|
@@ -221,25 +248,38 @@ export class LocalNode {
|
|
|
221
248
|
*
|
|
222
249
|
* @category 3. Low-level
|
|
223
250
|
*/
|
|
224
|
-
async load<T extends CoValue>(
|
|
225
|
-
|
|
251
|
+
async load<T extends CoValue>(
|
|
252
|
+
id: CoID<T>,
|
|
253
|
+
onProgress?: (progress: number) => void
|
|
254
|
+
): Promise<T | "unavailable"> {
|
|
255
|
+
const core = await this.loadCoValueCore(id, { onProgress });
|
|
256
|
+
|
|
257
|
+
if (core === "unavailable") {
|
|
258
|
+
return "unavailable";
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return core.getCurrentContent() as T;
|
|
226
262
|
}
|
|
227
263
|
|
|
228
264
|
/** @category 3. Low-level */
|
|
229
265
|
subscribe<T extends CoValue>(
|
|
230
266
|
id: CoID<T>,
|
|
231
|
-
callback: (update: T) => void
|
|
267
|
+
callback: (update: T | "unavailable") => void
|
|
232
268
|
): () => void {
|
|
233
269
|
let stopped = false;
|
|
234
270
|
let unsubscribe!: () => void;
|
|
235
271
|
|
|
236
|
-
console.log("Subscribing to " + id);
|
|
272
|
+
// console.log("Subscribing to " + id);
|
|
237
273
|
|
|
238
274
|
this.load(id)
|
|
239
275
|
.then((coValue) => {
|
|
240
276
|
if (stopped) {
|
|
241
277
|
return;
|
|
242
278
|
}
|
|
279
|
+
if (coValue === "unavailable") {
|
|
280
|
+
callback("unavailable");
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
243
283
|
unsubscribe = coValue.subscribe(callback);
|
|
244
284
|
})
|
|
245
285
|
.catch((e) => {
|
|
@@ -260,6 +300,12 @@ export class LocalNode {
|
|
|
260
300
|
): Promise<void> {
|
|
261
301
|
const groupOrOwnedValue = await this.load(groupOrOwnedValueID);
|
|
262
302
|
|
|
303
|
+
if (groupOrOwnedValue === "unavailable") {
|
|
304
|
+
throw new Error(
|
|
305
|
+
"Trying to accept invite: Group/owned value unavailable from all peers"
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
|
|
263
309
|
if (groupOrOwnedValue.core.header.ruleset.type === "ownedByGroup") {
|
|
264
310
|
return this.acceptInvite(
|
|
265
311
|
groupOrOwnedValue.core.header.ruleset.group as CoID<Group>,
|
|
@@ -325,7 +371,7 @@ export class LocalNode {
|
|
|
325
371
|
: "reader"
|
|
326
372
|
);
|
|
327
373
|
|
|
328
|
-
group.core.
|
|
374
|
+
group.core._sessionLogs = groupAsInvite.core.sessionLogs;
|
|
329
375
|
group.core._cachedContent = undefined;
|
|
330
376
|
|
|
331
377
|
for (const groupListener of group.core.listeners) {
|
|
@@ -400,17 +446,6 @@ export class LocalNode {
|
|
|
400
446
|
},
|
|
401
447
|
});
|
|
402
448
|
|
|
403
|
-
console.log(
|
|
404
|
-
"Creating read key",
|
|
405
|
-
getAgentSealerSecret(agentSecret),
|
|
406
|
-
getAgentSealerID(accountAgentID),
|
|
407
|
-
account.id,
|
|
408
|
-
account.core.nextTransactionID(),
|
|
409
|
-
"in session",
|
|
410
|
-
account.core.node.currentSessionID,
|
|
411
|
-
"=",
|
|
412
|
-
sealed
|
|
413
|
-
);
|
|
414
449
|
editable.set(
|
|
415
450
|
`${readKey.id}_for_${accountAgentID}`,
|
|
416
451
|
sealed,
|
|
@@ -432,16 +467,13 @@ export class LocalNode {
|
|
|
432
467
|
|
|
433
468
|
const accountOnThisNode = this.expectCoValueLoaded(account.id);
|
|
434
469
|
|
|
435
|
-
accountOnThisNode.
|
|
436
|
-
|
|
437
|
-
};
|
|
470
|
+
accountOnThisNode._sessionLogs = new Map(account.core.sessionLogs);
|
|
471
|
+
|
|
438
472
|
accountOnThisNode._cachedContent = undefined;
|
|
439
473
|
|
|
440
474
|
const profileOnThisNode = this.createCoValue(profile.core.header);
|
|
441
475
|
|
|
442
|
-
profileOnThisNode.
|
|
443
|
-
...profile.core.sessions,
|
|
444
|
-
};
|
|
476
|
+
profileOnThisNode._sessionLogs = new Map(profile.core.sessionLogs);
|
|
445
477
|
profileOnThisNode._cachedContent = undefined;
|
|
446
478
|
|
|
447
479
|
return new ControlledAccount(accountOnThisNode, agentSecret);
|
|
@@ -475,6 +507,41 @@ export class LocalNode {
|
|
|
475
507
|
return new Account(coValue).getCurrentAgentID();
|
|
476
508
|
}
|
|
477
509
|
|
|
510
|
+
async resolveAccountAgentAsync(
|
|
511
|
+
id: AccountID | AgentID,
|
|
512
|
+
expectation?: string
|
|
513
|
+
): Promise<AgentID> {
|
|
514
|
+
if (isAgentID(id)) {
|
|
515
|
+
return id;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
const coValue = await this.loadCoValueCore(id);
|
|
519
|
+
|
|
520
|
+
if (coValue === "unavailable") {
|
|
521
|
+
throw new Error(
|
|
522
|
+
`${
|
|
523
|
+
expectation ? expectation + ": " : ""
|
|
524
|
+
}Account ${id} is unavailable from all peers`
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
if (
|
|
529
|
+
coValue.header.type !== "comap" ||
|
|
530
|
+
coValue.header.ruleset.type !== "group" ||
|
|
531
|
+
!coValue.header.meta ||
|
|
532
|
+
!("type" in coValue.header.meta) ||
|
|
533
|
+
coValue.header.meta.type !== "account"
|
|
534
|
+
) {
|
|
535
|
+
throw new Error(
|
|
536
|
+
`${
|
|
537
|
+
expectation ? expectation + ": " : ""
|
|
538
|
+
}CoValue ${id} is not an account`
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
return new Account(coValue).getCurrentAgentID();
|
|
543
|
+
}
|
|
544
|
+
|
|
478
545
|
/**
|
|
479
546
|
* @deprecated use Account.createGroup() instead
|
|
480
547
|
*/
|
|
@@ -543,7 +610,7 @@ export class LocalNode {
|
|
|
543
610
|
const newCoValue = new CoValueCore(
|
|
544
611
|
entry.coValue.header,
|
|
545
612
|
newNode,
|
|
546
|
-
|
|
613
|
+
new Map(entry.coValue.sessionLogs)
|
|
547
614
|
);
|
|
548
615
|
|
|
549
616
|
newNode.coValues[coValueID as RawCoID] = {
|
|
@@ -575,16 +642,34 @@ export class LocalNode {
|
|
|
575
642
|
type CoValueState =
|
|
576
643
|
| {
|
|
577
644
|
state: "loading";
|
|
578
|
-
done: Promise<CoValueCore>;
|
|
579
|
-
resolve: (coValue: CoValueCore) => void;
|
|
645
|
+
done: Promise<CoValueCore | "unavailable">;
|
|
646
|
+
resolve: (coValue: CoValueCore | "unavailable") => void;
|
|
647
|
+
onProgress?: (progress: number) => void;
|
|
648
|
+
firstPeerState: {
|
|
649
|
+
[peerID: string]:
|
|
650
|
+
| {
|
|
651
|
+
type: "waiting";
|
|
652
|
+
done: Promise<void>;
|
|
653
|
+
resolve: () => void;
|
|
654
|
+
}
|
|
655
|
+
| { type: "available" }
|
|
656
|
+
| { type: "unavailable" };
|
|
657
|
+
};
|
|
580
658
|
}
|
|
581
|
-
| {
|
|
659
|
+
| {
|
|
660
|
+
state: "loaded";
|
|
661
|
+
coValue: CoValueCore;
|
|
662
|
+
onProgress?: (progress: number) => void;
|
|
663
|
+
};
|
|
582
664
|
|
|
583
665
|
/** @internal */
|
|
584
|
-
export function newLoadingState(
|
|
585
|
-
|
|
666
|
+
export function newLoadingState(
|
|
667
|
+
currentPeerIds: Set<PeerID>,
|
|
668
|
+
onProgress?: (progress: number) => void
|
|
669
|
+
): CoValueState {
|
|
670
|
+
let resolve: (coValue: CoValueCore | "unavailable") => void;
|
|
586
671
|
|
|
587
|
-
const promise = new Promise<CoValueCore>((r) => {
|
|
672
|
+
const promise = new Promise<CoValueCore | "unavailable">((r) => {
|
|
588
673
|
resolve = r;
|
|
589
674
|
});
|
|
590
675
|
|
|
@@ -592,5 +677,15 @@ export function newLoadingState(): CoValueState {
|
|
|
592
677
|
state: "loading",
|
|
593
678
|
done: promise,
|
|
594
679
|
resolve: resolve!,
|
|
680
|
+
onProgress,
|
|
681
|
+
firstPeerState: Object.fromEntries(
|
|
682
|
+
[...currentPeerIds].map((id) => {
|
|
683
|
+
let resolve: () => void;
|
|
684
|
+
const done = new Promise<void>((r) => {
|
|
685
|
+
resolve = r;
|
|
686
|
+
});
|
|
687
|
+
return [id, { type: "waiting", done, resolve: resolve! }];
|
|
688
|
+
})
|
|
689
|
+
),
|
|
595
690
|
};
|
|
596
691
|
}
|
package/src/permissions.ts
CHANGED
|
@@ -2,10 +2,7 @@ import { CoID } from "./coValue.js";
|
|
|
2
2
|
import { MapOpPayload } from "./coValues/coMap.js";
|
|
3
3
|
import { JsonValue } from "./jsonValue.js";
|
|
4
4
|
import { KeyID } from "./crypto.js";
|
|
5
|
-
import {
|
|
6
|
-
CoValueCore,
|
|
7
|
-
Transaction,
|
|
8
|
-
} from "./coValueCore.js";
|
|
5
|
+
import { CoValueCore, Transaction } from "./coValueCore.js";
|
|
9
6
|
import { accountOrAgentIDfromSessionID } from "./typeUtils/accountOrAgentIDfromSessionID.js";
|
|
10
7
|
import { AgentID, RawCoID, SessionID, TransactionID } from "./ids.js";
|
|
11
8
|
import { Account, AccountID, Profile } from "./coValues/account.js";
|
|
@@ -31,19 +28,19 @@ export function determineValidTransactions(
|
|
|
31
28
|
coValue: CoValueCore
|
|
32
29
|
): { txID: TransactionID; tx: Transaction }[] {
|
|
33
30
|
if (coValue.header.ruleset.type === "group") {
|
|
34
|
-
const allTransactionsSorted =
|
|
35
|
-
(
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
);
|
|
31
|
+
const allTransactionsSorted = [
|
|
32
|
+
...coValue.sessionLogs.entries(),
|
|
33
|
+
].flatMap(([sessionID, sessionLog]) => {
|
|
34
|
+
return sessionLog.transactions.map((tx, txIndex) => ({
|
|
35
|
+
sessionID,
|
|
36
|
+
txIndex,
|
|
37
|
+
tx,
|
|
38
|
+
})) as {
|
|
39
|
+
sessionID: SessionID;
|
|
40
|
+
txIndex: number;
|
|
41
|
+
tx: Transaction;
|
|
42
|
+
}[];
|
|
43
|
+
});
|
|
47
44
|
|
|
48
45
|
allTransactionsSorted.sort((a, b) => {
|
|
49
46
|
return a.tx.madeAt - b.tx.madeAt;
|
|
@@ -242,11 +239,9 @@ export function determineValidTransactions(
|
|
|
242
239
|
throw new Error("Group must be a map");
|
|
243
240
|
}
|
|
244
241
|
|
|
245
|
-
return
|
|
242
|
+
return [...coValue.sessionLogs.entries()].flatMap(
|
|
246
243
|
([sessionID, sessionLog]) => {
|
|
247
|
-
const transactor = accountOrAgentIDfromSessionID(
|
|
248
|
-
sessionID as SessionID
|
|
249
|
-
);
|
|
244
|
+
const transactor = accountOrAgentIDfromSessionID(sessionID);
|
|
250
245
|
|
|
251
246
|
return sessionLog.transactions
|
|
252
247
|
.filter((tx) => {
|
|
@@ -266,16 +261,16 @@ export function determineValidTransactions(
|
|
|
266
261
|
);
|
|
267
262
|
})
|
|
268
263
|
.map((tx, txIndex) => ({
|
|
269
|
-
txID: { sessionID: sessionID
|
|
264
|
+
txID: { sessionID: sessionID, txIndex },
|
|
270
265
|
tx,
|
|
271
266
|
}));
|
|
272
267
|
}
|
|
273
268
|
);
|
|
274
269
|
} else if (coValue.header.ruleset.type === "unsafeAllowAll") {
|
|
275
|
-
return
|
|
270
|
+
return [...coValue.sessionLogs.entries()].flatMap(
|
|
276
271
|
([sessionID, sessionLog]) => {
|
|
277
272
|
return sessionLog.transactions.map((tx, txIndex) => ({
|
|
278
|
-
txID: { sessionID: sessionID
|
|
273
|
+
txID: { sessionID: sessionID, txIndex },
|
|
279
274
|
tx,
|
|
280
275
|
}));
|
|
281
276
|
}
|