From 1bd27f848ecec63fdcf7e4df805c26c7540aa03b Mon Sep 17 00:00:00 2001 From: Anselm Date: Wed, 2 Aug 2023 16:25:04 +0100 Subject: [PATCH 1/3] Rename CoValue -> CoValueContent --- src/coValue.ts | 6 +++--- src/index.ts | 6 +++--- src/jsonValue.ts | 4 ++-- src/multilog.ts | 8 ++++---- src/permissions.ts | 4 ++-- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/coValue.ts b/src/coValue.ts index 51c6f4007..ebe9e9808 100644 --- a/src/coValue.ts +++ b/src/coValue.ts @@ -1,11 +1,11 @@ import { JsonAtom, JsonObject, JsonValue } from "./jsonValue"; import { MultiLog, MultiLogID, TransactionID } from "./multilog"; -export type CoValueID = MultiLogID & { +export type CoValueID = MultiLogID & { readonly __type: T; }; -export type CoValue = +export type CoValueContent = | CoMap<{[key: string]: JsonValue}, JsonValue> | CoList | MultiStream @@ -230,7 +230,7 @@ export class Static { } } -export function expectMap(content: CoValue): CoMap<{ [key: string]: string }, {}> { +export function expectMap(content: CoValueContent): CoMap<{ [key: string]: string }, {}> { if (content.type !== "comap") { throw new Error("Expected map"); } diff --git a/src/index.ts b/src/index.ts index 709cc2e15..453614bc3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,13 +1,13 @@ -import { CoValue } from "./coValue"; +import { CoValueContent } from "./coValue"; import { JsonValue } from "./jsonValue"; import { MultiLog } from "./multilog"; import { LocalNode } from "./node"; -type Value = JsonValue | CoValue; +type Value = JsonValue | CoValueContent; export { JsonValue, - CoValue, + CoValueContent as CoValue, Value, LocalNode, MultiLog diff --git a/src/jsonValue.ts b/src/jsonValue.ts index fd37d8859..9a535263c 100644 --- a/src/jsonValue.ts +++ b/src/jsonValue.ts @@ -1,6 +1,6 @@ -import { CoValueID, CoValue } from "./coValue"; +import { CoValueID, CoValueContent } from "./coValue"; export type JsonAtom = string | number | boolean | null; -export type JsonValue = JsonAtom | JsonArray | JsonObject | CoValueID; +export type JsonValue = JsonAtom | JsonArray | JsonObject | CoValueID; export type JsonArray = JsonValue[]; export type JsonObject = { [key: string]: JsonValue; }; diff --git a/src/multilog.ts b/src/multilog.ts index a7282aa76..105017178 100644 --- a/src/multilog.ts +++ b/src/multilog.ts @@ -1,5 +1,5 @@ import { randomBytes } from "@noble/hashes/utils"; -import { CoList, CoMap, CoValue, Static, MultiStream } from "./coValue"; +import { CoList, CoMap, CoValueContent, Static, MultiStream } from "./coValue"; import { Encrypted, Hash, @@ -36,7 +36,7 @@ import { MultiLogKnownState, NewContentMessage } from "./sync"; export type MultiLogID = `coval_${string}`; export type MultiLogHeader = { - type: CoValue["type"]; + type: CoValueContent["type"]; ruleset: RulesetDef; meta: JsonValue; }; @@ -94,7 +94,7 @@ export class MultiLog { node: LocalNode; header: MultiLogHeader; sessions: { [key: SessionID]: SessionLog }; - content?: CoValue; + content?: CoValueContent; constructor(header: MultiLogHeader, node: LocalNode) { this.id = multilogIDforHeader(header); @@ -260,7 +260,7 @@ export class MultiLog { ); } - getCurrentContent(): CoValue { + getCurrentContent(): CoValueContent { if (this.content) { return this.content; } diff --git a/src/permissions.ts b/src/permissions.ts index 9d1ce83b7..85ab519bc 100644 --- a/src/permissions.ts +++ b/src/permissions.ts @@ -1,4 +1,4 @@ -import { CoMap, CoValue, MapOpPayload } from "./coValue"; +import { CoMap, CoValueContent, MapOpPayload } from "./coValue"; import { JsonValue } from "./jsonValue"; import { Encrypted, @@ -215,7 +215,7 @@ export type TeamContent = { [key: AgentID]: Role } & { }; }; -export function expectTeamContent(content: CoValue): CoMap { +export function expectTeamContent(content: CoValueContent): CoMap { if (content.type !== "comap") { throw new Error("Expected map"); } From 8a145941dbbfa8e979e894ca84d75ef220b917dc Mon Sep 17 00:00:00 2001 From: Anselm Date: Wed, 2 Aug 2023 16:27:59 +0100 Subject: [PATCH 2/3] Rename CoValueContent to ContentType --- README.md | 2 +- src/{coValue.test.ts => contentType.test.ts} | 0 src/{coValue.ts => contentType.ts} | 16 ++++++++-------- src/index.ts | 6 +++--- src/jsonValue.ts | 4 ++-- src/multilog.test.ts | 6 +++--- src/multilog.ts | 12 ++++++------ src/node.ts | 2 +- src/permissions.test.ts | 2 +- src/permissions.ts | 4 ++-- src/sync.test.ts | 2 +- 11 files changed, 28 insertions(+), 28 deletions(-) rename src/{coValue.test.ts => contentType.test.ts} (100%) rename src/{coValue.ts => contentType.ts} (93%) diff --git a/README.md b/README.md index 23145acbc..6b38c12ad 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ THIS IS WORK IN PROGRESS - Team (`AgentID` → `Role`) - CoList (`Immutable[]`, addressable positions, insertAfter semantics) - Agent (`{signatoryID, recipientID}[]`) -- MultiStream (independent per-session streams of `Immutable`s) +- CoStream (independent per-session streams of `Immutable`s) - Static (single addressable `Immutable`) ## Implementation Abstractions diff --git a/src/coValue.test.ts b/src/contentType.test.ts similarity index 100% rename from src/coValue.test.ts rename to src/contentType.test.ts diff --git a/src/coValue.ts b/src/contentType.ts similarity index 93% rename from src/coValue.ts rename to src/contentType.ts index ebe9e9808..c936dc950 100644 --- a/src/coValue.ts +++ b/src/contentType.ts @@ -1,14 +1,14 @@ import { JsonAtom, JsonObject, JsonValue } from "./jsonValue"; import { MultiLog, MultiLogID, TransactionID } from "./multilog"; -export type CoValueID = MultiLogID & { +export type CoValueID = MultiLogID & { readonly __type: T; }; -export type CoValueContent = +export type ContentType = | CoMap<{[key: string]: JsonValue}, JsonValue> | CoList - | MultiStream + | CoStream | Static; type MapOp = { @@ -204,12 +204,12 @@ export class CoList { } } -export class MultiStream { - id: CoValueID>; - type: "multistream" = "multistream"; +export class CoStream { + id: CoValueID>; + type: "costream" = "costream"; constructor(multilog: MultiLog) { - this.id = multilog.id as CoValueID>; + this.id = multilog.id as CoValueID>; } toJSON(): JsonObject { @@ -230,7 +230,7 @@ export class Static { } } -export function expectMap(content: CoValueContent): CoMap<{ [key: string]: string }, {}> { +export function expectMap(content: ContentType): CoMap<{ [key: string]: string }, {}> { if (content.type !== "comap") { throw new Error("Expected map"); } diff --git a/src/index.ts b/src/index.ts index 453614bc3..cc2ad60c9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,13 +1,13 @@ -import { CoValueContent } from "./coValue"; +import { ContentType } from "./contentType"; import { JsonValue } from "./jsonValue"; import { MultiLog } from "./multilog"; import { LocalNode } from "./node"; -type Value = JsonValue | CoValueContent; +type Value = JsonValue | ContentType; export { JsonValue, - CoValueContent as CoValue, + ContentType as CoValue, Value, LocalNode, MultiLog diff --git a/src/jsonValue.ts b/src/jsonValue.ts index 9a535263c..67bc29e0c 100644 --- a/src/jsonValue.ts +++ b/src/jsonValue.ts @@ -1,6 +1,6 @@ -import { CoValueID, CoValueContent } from "./coValue"; +import { CoValueID, ContentType } from "./contentType"; export type JsonAtom = string | number | boolean | null; -export type JsonValue = JsonAtom | JsonArray | JsonObject | CoValueID; +export type JsonValue = JsonAtom | JsonArray | JsonObject | CoValueID; export type JsonArray = JsonValue[]; export type JsonObject = { [key: string]: JsonValue; }; diff --git a/src/multilog.test.ts b/src/multilog.test.ts index 09fa2aa0f..66aad04bf 100644 --- a/src/multilog.test.ts +++ b/src/multilog.test.ts @@ -18,7 +18,7 @@ test("Can create multilog with new agent credentials and add transaction to it", ); const multilog = node.createMultiLog({ - type: "multistream", + type: "costream", ruleset: { type: "unsafeAllowAll" }, meta: null, }); @@ -58,7 +58,7 @@ test("transactions with wrong signature are rejected", () => { ); const multilog = node.createMultiLog({ - type: "multistream", + type: "costream", ruleset: { type: "unsafeAllowAll" }, meta: null, }); @@ -97,7 +97,7 @@ test("transactions with correctly signed, but wrong hash are rejected", () => { ); const multilog = node.createMultiLog({ - type: "multistream", + type: "costream", ruleset: { type: "unsafeAllowAll" }, meta: null, }); diff --git a/src/multilog.ts b/src/multilog.ts index 105017178..f6a2aaf60 100644 --- a/src/multilog.ts +++ b/src/multilog.ts @@ -1,5 +1,5 @@ import { randomBytes } from "@noble/hashes/utils"; -import { CoList, CoMap, CoValueContent, Static, MultiStream } from "./coValue"; +import { CoList, CoMap, ContentType, Static, CoStream } from "./contentType"; import { Encrypted, Hash, @@ -36,7 +36,7 @@ import { MultiLogKnownState, NewContentMessage } from "./sync"; export type MultiLogID = `coval_${string}`; export type MultiLogHeader = { - type: CoValueContent["type"]; + type: ContentType["type"]; ruleset: RulesetDef; meta: JsonValue; }; @@ -94,7 +94,7 @@ export class MultiLog { node: LocalNode; header: MultiLogHeader; sessions: { [key: SessionID]: SessionLog }; - content?: CoValueContent; + content?: ContentType; constructor(header: MultiLogHeader, node: LocalNode) { this.id = multilogIDforHeader(header); @@ -260,7 +260,7 @@ export class MultiLog { ); } - getCurrentContent(): CoValueContent { + getCurrentContent(): ContentType { if (this.content) { return this.content; } @@ -269,8 +269,8 @@ export class MultiLog { this.content = new CoMap(this); } else if (this.header.type === "colist") { this.content = new CoList(this); - } else if (this.header.type === "multistream") { - this.content = new MultiStream(this); + } else if (this.header.type === "costream") { + this.content = new CoStream(this); } else if (this.header.type === "static") { this.content = new Static(this); } else { diff --git a/src/node.ts b/src/node.ts index c824702c5..db0b4649e 100644 --- a/src/node.ts +++ b/src/node.ts @@ -1,4 +1,4 @@ -import { CoMap } from "./coValue"; +import { CoMap } from "./contentType"; import { newRandomKeySecret, seal } from "./crypto"; import { MultiLogID, diff --git a/src/permissions.test.ts b/src/permissions.test.ts index 041e5a0ac..0f21679a2 100644 --- a/src/permissions.test.ts +++ b/src/permissions.test.ts @@ -6,7 +6,7 @@ import { newRandomSessionID, } from "./multilog"; import { LocalNode } from "./node"; -import { expectMap } from "./coValue"; +import { expectMap } from "./contentType"; import { expectTeamContent } from "./permissions"; import { getRecipientID, diff --git a/src/permissions.ts b/src/permissions.ts index 85ab519bc..9dc10cef0 100644 --- a/src/permissions.ts +++ b/src/permissions.ts @@ -1,4 +1,4 @@ -import { CoMap, CoValueContent, MapOpPayload } from "./coValue"; +import { CoMap, ContentType, MapOpPayload } from "./contentType"; import { JsonValue } from "./jsonValue"; import { Encrypted, @@ -215,7 +215,7 @@ export type TeamContent = { [key: AgentID]: Role } & { }; }; -export function expectTeamContent(content: CoValueContent): CoMap { +export function expectTeamContent(content: ContentType): CoMap { if (content.type !== "comap") { throw new Error("Expected map"); } diff --git a/src/sync.test.ts b/src/sync.test.ts index 1c7edbea3..56f2c758a 100644 --- a/src/sync.test.ts +++ b/src/sync.test.ts @@ -7,7 +7,7 @@ import { } from "./multilog"; import { LocalNode } from "./node"; import { Peer, SyncMessage } from "./sync"; -import { MapOpPayload, expectMap } from "./coValue"; +import { MapOpPayload, expectMap } from "./contentType"; test( "Node replies with initial tx and header to empty subscribe", From 25dd2e9447821caeaa6b7f85f0e49f34e8ae23ac Mon Sep 17 00:00:00 2001 From: Anselm Date: Wed, 2 Aug 2023 16:36:31 +0100 Subject: [PATCH 3/3] Rename MultiLog to CoValue --- README.md | 4 +- src/{multilog.test.ts => coValue.test.ts} | 24 +-- src/{multilog.ts => coValue.ts} | 46 +++--- src/contentType.test.ts | 30 ++-- src/contentType.ts | 42 ++--- src/crypto.ts | 14 +- src/index.ts | 6 +- src/node.ts | 190 +++++++++++----------- src/permissions.test.ts | 56 +++---- src/permissions.ts | 56 +++---- src/sync.test.ts | 124 +++++++------- src/sync.ts | 32 ++-- 12 files changed, 312 insertions(+), 312 deletions(-) rename src/{multilog.test.ts => coValue.test.ts} (84%) rename src/{multilog.ts => coValue.ts} (92%) diff --git a/README.md b/README.md index 6b38c12ad..bd08d7c76 100644 --- a/README.md +++ b/README.md @@ -34,13 +34,13 @@ THIS IS WORK IN PROGRESS - Static (single addressable `Immutable`) ## Implementation Abstractions -- MultiLog +- CoValue - Session Logs - Transactions - Private (encrypted) transactions - Trusting (unencrypted) transactions - Rulesets -- CoValue Types +- CoValue Content Types - LocalNode - Peers - AgentCredentials diff --git a/src/multilog.test.ts b/src/coValue.test.ts similarity index 84% rename from src/multilog.test.ts rename to src/coValue.test.ts index 66aad04bf..f1af174cf 100644 --- a/src/multilog.test.ts +++ b/src/coValue.test.ts @@ -1,23 +1,23 @@ import { expect, test } from "bun:test"; import { - MultiLog, + CoValue, Transaction, getAgent, getAgentID, newRandomAgentCredential, newRandomSessionID, -} from "./multilog"; +} from "./coValue"; import { LocalNode } from "./node"; import { sign } from "./crypto"; -test("Can create multilog with new agent credentials and add transaction to it", () => { +test("Can create coValue with new agent credentials and add transaction to it", () => { const agentCredential = newRandomAgentCredential(); const node = new LocalNode( agentCredential, newRandomSessionID(getAgentID(getAgent(agentCredential))) ); - const multilog = node.createMultiLog({ + const coValue = node.createCoValue({ type: "costream", ruleset: { type: "unsafeAllowAll" }, meta: null, @@ -33,13 +33,13 @@ test("Can create multilog with new agent credentials and add transaction to it", ], }; - const { expectedNewHash } = multilog.expectedNewHashAfter( + const { expectedNewHash } = coValue.expectedNewHashAfter( node.ownSessionID, [transaction] ); expect( - multilog.tryAddTransactions( + coValue.tryAddTransactions( node.ownSessionID, [transaction], expectedNewHash, @@ -57,7 +57,7 @@ test("transactions with wrong signature are rejected", () => { newRandomSessionID(getAgentID(getAgent(agentCredential))) ); - const multilog = node.createMultiLog({ + const coValue = node.createCoValue({ type: "costream", ruleset: { type: "unsafeAllowAll" }, meta: null, @@ -73,13 +73,13 @@ test("transactions with wrong signature are rejected", () => { ], }; - const { expectedNewHash } = multilog.expectedNewHashAfter( + const { expectedNewHash } = coValue.expectedNewHashAfter( node.ownSessionID, [transaction] ); expect( - multilog.tryAddTransactions( + coValue.tryAddTransactions( node.ownSessionID, [transaction], expectedNewHash, @@ -96,7 +96,7 @@ test("transactions with correctly signed, but wrong hash are rejected", () => { newRandomSessionID(getAgentID(getAgent(agentCredential))) ); - const multilog = node.createMultiLog({ + const coValue = node.createCoValue({ type: "costream", ruleset: { type: "unsafeAllowAll" }, meta: null, @@ -112,7 +112,7 @@ test("transactions with correctly signed, but wrong hash are rejected", () => { ], }; - const { expectedNewHash } = multilog.expectedNewHashAfter( + const { expectedNewHash } = coValue.expectedNewHashAfter( node.ownSessionID, [ { @@ -128,7 +128,7 @@ test("transactions with correctly signed, but wrong hash are rejected", () => { ); expect( - multilog.tryAddTransactions( + coValue.tryAddTransactions( node.ownSessionID, [transaction], expectedNewHash, diff --git a/src/multilog.ts b/src/coValue.ts similarity index 92% rename from src/multilog.ts rename to src/coValue.ts index f6a2aaf60..6bb8e06a4 100644 --- a/src/multilog.ts +++ b/src/coValue.ts @@ -31,17 +31,17 @@ import { expectTeamContent, } from "./permissions"; import { LocalNode } from "./node"; -import { MultiLogKnownState, NewContentMessage } from "./sync"; +import { CoValueKnownState, NewContentMessage } from "./sync"; -export type MultiLogID = `coval_${string}`; +export type RawCoValueID = `coval_${string}`; -export type MultiLogHeader = { +export type CoValueHeader = { type: ContentType["type"]; ruleset: RulesetDef; meta: JsonValue; }; -function multilogIDforHeader(header: MultiLogHeader): MultiLogID { +function coValueIDforHeader(header: CoValueHeader): RawCoValueID { const hash = shortHash(header); return `coval_${hash.slice("shortHash_".length)}`; } @@ -69,7 +69,7 @@ export type PrivateTransaction = { keyUsed: KeyID; encryptedChanges: Encrypted< JsonValue[], - { in: MultiLogID; tx: TransactionID } + { in: RawCoValueID; tx: TransactionID } >; }; @@ -89,15 +89,15 @@ export type DecryptedTransaction = { export type TransactionID = { sessionID: SessionID; txIndex: number }; -export class MultiLog { - id: MultiLogID; +export class CoValue { + id: RawCoValueID; node: LocalNode; - header: MultiLogHeader; + header: CoValueHeader; sessions: { [key: SessionID]: SessionLog }; content?: ContentType; - constructor(header: MultiLogHeader, node: LocalNode) { - this.id = multilogIDforHeader(header); + constructor(header: CoValueHeader, node: LocalNode) { + this.id = coValueIDforHeader(header); this.header = header; this.sessions = {}; this.node = node; @@ -106,18 +106,18 @@ export class MultiLog { testWithDifferentCredentials( agentCredential: AgentCredential, ownSessionID: SessionID - ): MultiLog { + ): CoValue { const newNode = this.node.testWithDifferentCredentials( agentCredential, ownSessionID ); - return newNode.expectMultiLogLoaded(this.id); + return newNode.expectCoValueLoaded(this.id); } - knownState(): MultiLogKnownState { + knownState(): CoValueKnownState { return { - multilogID: this.id, + coValueID: this.id, header: true, sessions: Object.fromEntries( Object.entries(this.sessions).map(([k, v]) => [ @@ -187,7 +187,7 @@ export class MultiLog { this.content = undefined; - this.node.syncMultiLog(this); + this.node.syncCoValue(this); const _ = this.getCurrentContent(); @@ -274,7 +274,7 @@ export class MultiLog { } else if (this.header.type === "static") { this.content = new Static(this); } else { - throw new Error(`Unknown multilog type ${this.header.type}`); + throw new Error(`Unknown coValue type ${this.header.type}`); } return this.content; @@ -333,7 +333,7 @@ export class MultiLog { }; } else if (this.header.ruleset.type === "ownedByTeam") { return this.node - .expectMultiLogLoaded(this.header.ruleset.team) + .expectCoValueLoaded(this.header.ruleset.team) .getCurrentReadKey(); } else { throw new Error( @@ -411,7 +411,7 @@ export class MultiLog { ); } else if (this.header.ruleset.type === "ownedByTeam") { return this.node - .expectMultiLogLoaded(this.header.ruleset.team) + .expectCoValueLoaded(this.header.ruleset.team) .getReadKey(keyID); } else { throw new Error( @@ -424,10 +424,10 @@ export class MultiLog { return this.sessions[txID.sessionID]?.transactions[txID.txIndex]; } - newContentSince(knownState: MultiLogKnownState | undefined): NewContentMessage | undefined { + newContentSince(knownState: CoValueKnownState | undefined): NewContentMessage | undefined { const newContent: NewContentMessage = { action: "newContent", - multilogID: this.id, + coValueID: this.id, header: knownState?.header ? undefined : this.header, newContent: Object.fromEntries( Object.entries(this.sessions) @@ -483,7 +483,7 @@ export function getAgent(agentCredential: AgentCredential) { }; } -export function getAgentMultilogHeader(agent: Agent): MultiLogHeader { +export function getAgentCoValueHeader(agent: Agent): CoValueHeader { return { type: "comap", ruleset: { @@ -496,12 +496,12 @@ export function getAgentMultilogHeader(agent: Agent): MultiLogHeader { } export function getAgentID(agent: Agent): AgentID { - return `agent_${multilogIDforHeader(getAgentMultilogHeader(agent)).slice( + return `agent_${coValueIDforHeader(getAgentCoValueHeader(agent)).slice( "coval_".length )}`; } -export function agentIDasMultiLogID(agentID: AgentID): MultiLogID { +export function agentIDAsCoValueID(agentID: AgentID): RawCoValueID { return `coval_${agentID.substring("agent_".length)}`; } diff --git a/src/contentType.test.ts b/src/contentType.test.ts index a437a99c8..b0adfabf2 100644 --- a/src/contentType.test.ts +++ b/src/contentType.test.ts @@ -5,7 +5,7 @@ import { getAgentID, newRandomAgentCredential, newRandomSessionID, -} from "./multilog"; +} from "./coValue"; import { LocalNode } from "./node"; test("Empty COJSON Map works", () => { @@ -15,13 +15,13 @@ test("Empty COJSON Map works", () => { newRandomSessionID(getAgentID(getAgent(agentCredential))) ); - const multilog = node.createMultiLog({ + const coValue = node.createCoValue({ type: "comap", ruleset: { type: "unsafeAllowAll" }, meta: null, }); - const content = multilog.getCurrentContent(); + const content = coValue.getCurrentContent(); if (content.type !== "comap") { throw new Error("Expected map"); @@ -39,13 +39,13 @@ test("Can insert and delete Map entries in edit()", () => { newRandomSessionID(getAgentID(getAgent(agentCredential))) ); - const multilog = node.createMultiLog({ + const coValue = node.createCoValue({ type: "comap", ruleset: { type: "unsafeAllowAll" }, meta: null, }); - const content = multilog.getCurrentContent(); + const content = coValue.getCurrentContent(); if (content.type !== "comap") { throw new Error("Expected map"); @@ -71,13 +71,13 @@ test("Can get map entry values at different points in time", () => { newRandomSessionID(getAgentID(getAgent(agentCredential))) ); - const multilog = node.createMultiLog({ + const coValue = node.createCoValue({ type: "comap", ruleset: { type: "unsafeAllowAll" }, meta: null, }); - const content = multilog.getCurrentContent(); + const content = coValue.getCurrentContent(); if (content.type !== "comap") { throw new Error("Expected map"); @@ -110,13 +110,13 @@ test("Can get all historic values of key", () => { newRandomSessionID(getAgentID(getAgent(agentCredential))) ); - const multilog = node.createMultiLog({ + const coValue = node.createCoValue({ type: "comap", ruleset: { type: "unsafeAllowAll" }, meta: null, }); - const content = multilog.getCurrentContent(); + const content = coValue.getCurrentContent(); if (content.type !== "comap") { throw new Error("Expected map"); @@ -139,22 +139,22 @@ test("Can get all historic values of key", () => { { txID: txA, value: "A", - at: txA && multilog.getTx(txA)?.madeAt, + at: txA && coValue.getTx(txA)?.madeAt, }, { txID: txB, value: "B", - at: txB && multilog.getTx(txB)?.madeAt, + at: txB && coValue.getTx(txB)?.madeAt, }, { txID: txDel, value: undefined, - at: txDel && multilog.getTx(txDel)?.madeAt, + at: txDel && coValue.getTx(txDel)?.madeAt, }, { txID: txC, value: "C", - at: txC && multilog.getTx(txC)?.madeAt, + at: txC && coValue.getTx(txC)?.madeAt, }, ]); }); @@ -167,13 +167,13 @@ test("Can get last tx ID for a key", () => { newRandomSessionID(getAgentID(getAgent(agentCredential))) ); - const multilog = node.createMultiLog({ + const coValue = node.createCoValue({ type: "comap", ruleset: { type: "unsafeAllowAll" }, meta: null, }); - const content = multilog.getCurrentContent(); + const content = coValue.getCurrentContent(); if (content.type !== "comap") { throw new Error("Expected map"); diff --git a/src/contentType.ts b/src/contentType.ts index c936dc950..4331d9975 100644 --- a/src/contentType.ts +++ b/src/contentType.ts @@ -1,7 +1,7 @@ import { JsonAtom, JsonObject, JsonValue } from "./jsonValue"; -import { MultiLog, MultiLogID, TransactionID } from "./multilog"; +import { CoValue, RawCoValueID, TransactionID } from "./coValue"; -export type CoValueID = MultiLogID & { +export type CoValueID = RawCoValueID & { readonly __type: T; }; @@ -37,22 +37,22 @@ export class CoMap< MM extends {[key: string]: JsonValue} = {[KK in K]: M[KK]} > { id: CoValueID>; - multiLog: MultiLog; + coValue: CoValue; type: "comap" = "comap"; ops: {[KK in K]?: MapOp[]}; - constructor(multiLog: MultiLog) { - this.id = multiLog.id as CoValueID>; - this.multiLog = multiLog; + constructor(coValue: CoValue) { + this.id = coValue.id as CoValueID>; + this.coValue = coValue; this.ops = {}; - this.fillOpsFromMultilog(); + this.fillOpsFromCoValue(); } - protected fillOpsFromMultilog() { + protected fillOpsFromCoValue() { this.ops = {}; - for (const { txID, changes, madeAt } of this.multiLog.getValidSortedTransactions()) { + for (const { txID, changes, madeAt } of this.coValue.getValidSortedTransactions()) { for (const [changeIdx, changeUntyped] of ( changes ).entries()) { @@ -154,9 +154,9 @@ export class CoMap< } edit(changer: (editable: WriteableCoMap) => void): CoMap { - const editable = new WriteableCoMap(this.multiLog); + const editable = new WriteableCoMap(this.coValue); changer(editable); - return new CoMap(this.multiLog); + return new CoMap(this.coValue); } } @@ -168,7 +168,7 @@ export class WriteableCoMap< MM extends {[key: string]: JsonValue} = {[KK in K]: M[KK]} > extends CoMap { set(key: KK, value: M[KK], privacy: "private" | "trusting" = "private"): void { - this.multiLog.makeTransaction([ + this.coValue.makeTransaction([ { op: "insert", key, @@ -176,18 +176,18 @@ export class WriteableCoMap< }, ], privacy); - this.fillOpsFromMultilog(); + this.fillOpsFromCoValue(); } delete(key: K, privacy: "private" | "trusting" = "private"): void { - this.multiLog.makeTransaction([ + this.coValue.makeTransaction([ { op: "delete", key, }, ], privacy); - this.fillOpsFromMultilog(); + this.fillOpsFromCoValue(); } } @@ -195,8 +195,8 @@ export class CoList { id: CoValueID>; type: "colist" = "colist"; - constructor(multilog: MultiLog) { - this.id = multilog.id as CoValueID>; + constructor(coValue: CoValue) { + this.id = coValue.id as CoValueID>; } toJSON(): JsonObject { @@ -208,8 +208,8 @@ export class CoStream { id: CoValueID>; type: "costream" = "costream"; - constructor(multilog: MultiLog) { - this.id = multilog.id as CoValueID>; + constructor(coValue: CoValue) { + this.id = coValue.id as CoValueID>; } toJSON(): JsonObject { @@ -221,8 +221,8 @@ export class Static { id: CoValueID>; type: "static" = "static"; - constructor(multilog: MultiLog) { - this.id = multilog.id as CoValueID>; + constructor(coValue: CoValue) { + this.id = coValue.id as CoValueID>; } toJSON(): JsonObject { diff --git a/src/crypto.ts b/src/crypto.ts index b5ba53ece..e1c7820b9 100644 --- a/src/crypto.ts +++ b/src/crypto.ts @@ -5,7 +5,7 @@ import { base58, base64url } from "@scure/base"; import stableStringify from "fast-json-stable-stringify"; import { blake3 } from "@noble/hashes/blake3"; import { randomBytes } from "@noble/ciphers/webcrypto/utils"; -import { MultiLogID, SessionID, TransactionID } from "./multilog"; +import { RawCoValueID, SessionID, TransactionID } from "./coValue"; export type SignatorySecret = `signatorySecret_z${string}`; export type SignatoryID = `signatory_z${string}`; @@ -72,7 +72,7 @@ export function seal( message: T, from: RecipientSecret, to: Set, - nOnceMaterial: { in: MultiLogID; tx: TransactionID } + nOnceMaterial: { in: RawCoValueID; tx: TransactionID } ): SealedSet { const nOnce = blake3( textEncoder.encode(stableStringify(nOnceMaterial)) @@ -113,7 +113,7 @@ export function openAs( sealedSet: SealedSet, recipient: RecipientSecret, from: RecipientID, - nOnceMaterial: { in: MultiLogID; tx: TransactionID } + nOnceMaterial: { in: RawCoValueID; tx: TransactionID } ): T | undefined { const nOnce = blake3( textEncoder.encode(stableStringify(nOnceMaterial)) @@ -219,8 +219,8 @@ function encrypt( export function encryptForTransaction( value: T, keySecret: KeySecret, - nOnceMaterial: { in: MultiLogID; tx: TransactionID } -): Encrypted { + nOnceMaterial: { in: RawCoValueID; tx: TransactionID } +): Encrypted { return encrypt(value, keySecret, nOnceMaterial); } @@ -273,9 +273,9 @@ function decrypt( } export function decryptForTransaction( - encrypted: Encrypted, + encrypted: Encrypted, keySecret: KeySecret, - nOnceMaterial: { in: MultiLogID; tx: TransactionID } + nOnceMaterial: { in: RawCoValueID; tx: TransactionID } ): T | undefined { return decrypt(encrypted, keySecret, nOnceMaterial); } diff --git a/src/index.ts b/src/index.ts index cc2ad60c9..9ddcd6bca 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,14 +1,14 @@ import { ContentType } from "./contentType"; import { JsonValue } from "./jsonValue"; -import { MultiLog } from "./multilog"; +import { CoValue } from "./coValue"; import { LocalNode } from "./node"; type Value = JsonValue | ContentType; export { JsonValue, - ContentType as CoValue, + ContentType, Value, LocalNode, - MultiLog + CoValue } diff --git a/src/node.ts b/src/node.ts index db0b4649e..7acf2db6c 100644 --- a/src/node.ts +++ b/src/node.ts @@ -1,19 +1,19 @@ import { CoMap } from "./contentType"; import { newRandomKeySecret, seal } from "./crypto"; import { - MultiLogID, - MultiLog, + RawCoValueID, + CoValue, AgentCredential, AgentID, SessionID, Agent, getAgent, getAgentID, - getAgentMultilogHeader, - MultiLogHeader, + getAgentCoValueHeader, + CoValueHeader, agentIDfromSessionID, - agentIDasMultiLogID, -} from "./multilog"; + agentIDAsCoValueID, +} from "./coValue"; import { Team, expectTeamContent } from "./permissions"; import { NewContentMessage, @@ -31,7 +31,7 @@ import { } from "./sync"; export class LocalNode { - multilogs: { [key: MultiLogID]: MultilogState } = {}; + coValues: { [key: RawCoValueID]: CoValueState } = {}; peers: { [key: PeerID]: PeerState } = {}; agentCredential: AgentCredential; agentID: AgentID; @@ -46,35 +46,35 @@ export class LocalNode { this.knownAgents[agentID] = agent; this.ownSessionID = ownSessionID; - const agentMultilog = new MultiLog(getAgentMultilogHeader(agent), this); - this.multilogs[agentMultilog.id] = { + const agentCoValue = new CoValue(getAgentCoValueHeader(agent), this); + this.coValues[agentCoValue.id] = { state: "loaded", - multilog: agentMultilog, + coValue: agentCoValue, }; } - createMultiLog(header: MultiLogHeader): MultiLog { - const multilog = new MultiLog(header, this); - this.multilogs[multilog.id] = { state: "loaded", multilog }; + createCoValue(header: CoValueHeader): CoValue { + const coValue = new CoValue(header, this); + this.coValues[coValue.id] = { state: "loaded", coValue: coValue }; - this.syncMultiLog(multilog); + this.syncCoValue(coValue); - return multilog; + return coValue; } - loadMultiLog(id: MultiLogID): Promise { - let entry = this.multilogs[id]; + loadCoValue(id: RawCoValueID): Promise { + let entry = this.coValues[id]; if (!entry) { entry = newLoadingState(); - this.multilogs[id] = entry; + this.coValues[id] = entry; for (const peer of Object.values(this.peers)) { peer.outgoing .write({ action: "subscribe", knownState: { - multilogID: id, + coValueID: id, header: false, sessions: {}, }, @@ -85,26 +85,26 @@ export class LocalNode { } } if (entry.state === "loaded") { - return Promise.resolve(entry.multilog); + return Promise.resolve(entry.coValue); } return entry.done; } - expectMultiLogLoaded(id: MultiLogID, expectation?: string): MultiLog { - const entry = this.multilogs[id]; + expectCoValueLoaded(id: RawCoValueID, expectation?: string): CoValue { + const entry = this.coValues[id]; if (!entry) { throw new Error( - `${expectation ? expectation + ": " : ""}Unknown multilog ${id}` + `${expectation ? expectation + ": " : ""}Unknown CoValue ${id}` ); } if (entry.state === "loading") { throw new Error( `${ expectation ? expectation + ": " : "" - }Multilog ${id} not yet loaded` + }CoValue ${id} not yet loaded` ); } - return entry.multilog; + return entry.coValue; } addKnownAgent(agent: Agent) { @@ -113,13 +113,13 @@ export class LocalNode { } createTeam(): Team { - const teamMultilog = this.createMultiLog({ + const teamCoValue = this.createCoValue({ type: "comap", ruleset: { type: "team", initialAdmin: this.agentID }, meta: null, }); - let teamContent = expectTeamContent(teamMultilog.getCurrentContent()); + let teamContent = expectTeamContent(teamCoValue.getCurrentContent()); teamContent = teamContent.edit((editable) => { editable.set(this.agentID, "admin", "trusting"); @@ -130,8 +130,8 @@ export class LocalNode { this.agentCredential.recipientSecret, new Set([getAgent(this.agentCredential).recipientID]), { - in: teamMultilog.id, - tx: teamMultilog.nextTransactionID(), + in: teamCoValue.id, + tx: teamCoValue.nextTransactionID(), } ); @@ -156,7 +156,7 @@ export class LocalNode { this.peers[peer.id] = peerState; if (peer.role === "server") { - for (const entry of Object.values(this.multilogs)) { + for (const entry of Object.values(this.coValues)) { if (entry.state === "loading") { continue; } @@ -164,15 +164,15 @@ export class LocalNode { peerState.outgoing .write({ action: "subscribe", - knownState: entry.multilog.knownState(), + knownState: entry.coValue.knownState(), }) .catch((e) => { // TODO: handle error console.error("Error writing to peer", e); }); - peerState.optimisticKnownStates[entry.multilog.id] = { - multilogID: entry.multilog.id, + peerState.optimisticKnownStates[entry.coValue.id] = { + coValueID: entry.coValue.id, header: false, sessions: {}, }; @@ -217,20 +217,20 @@ export class LocalNode { handleSubscribe( msg: SubscribeMessage, peer: PeerState, - asDependencyOf?: MultiLogID + asDependencyOf?: RawCoValueID ): SyncMessage[] { - const entry = this.multilogs[msg.knownState.multilogID]; + const entry = this.coValues[msg.knownState.coValueID]; if (!entry || entry.state === "loading") { if (!entry) { - this.multilogs[msg.knownState.multilogID] = newLoadingState(); + this.coValues[msg.knownState.coValueID] = newLoadingState(); } return [ { action: "subscribeResponse", knownState: { - multilogID: msg.knownState.multilogID, + coValueID: msg.knownState.coValueID, header: false, sessions: {}, }, @@ -238,39 +238,39 @@ export class LocalNode { ]; } - peer.optimisticKnownStates[entry.multilog.id] = - entry.multilog.knownState(); + peer.optimisticKnownStates[entry.coValue.id] = + entry.coValue.knownState(); - const newContent = entry.multilog.newContentSince(msg.knownState); + const newContent = entry.coValue.newContentSince(msg.knownState); - const dependedOnMultilogs = - entry.multilog.header.ruleset.type === "team" - ? expectTeamContent(entry.multilog.getCurrentContent()) + const dependedOnCoValues = + entry.coValue.header.ruleset.type === "team" + ? expectTeamContent(entry.coValue.getCurrentContent()) .keys() .filter((k): k is AgentID => k.startsWith("agent_")) - .map((agent) => agentIDasMultiLogID(agent)) - : entry.multilog.header.ruleset.type === "ownedByTeam" - ? [entry.multilog.header.ruleset.team] + .map((agent) => agentIDAsCoValueID(agent)) + : entry.coValue.header.ruleset.type === "ownedByTeam" + ? [entry.coValue.header.ruleset.team] : []; return [ - ...dependedOnMultilogs.flatMap((multilogID) => + ...dependedOnCoValues.flatMap((coValueID) => this.handleSubscribe( { action: "subscribe", knownState: { - multilogID, + coValueID, header: false, sessions: {}, }, }, peer, - asDependencyOf || msg.knownState.multilogID + asDependencyOf || msg.knownState.coValueID ) ), { action: "subscribeResponse", - knownState: entry.multilog.knownState(), + knownState: entry.coValue.knownState(), asDependencyOf, }, ...(newContent ? [newContent] : []), @@ -281,70 +281,70 @@ export class LocalNode { msg: SubscribeResponseMessage, peer: PeerState ): SyncMessage[] { - let entry = this.multilogs[msg.knownState.multilogID]; + let entry = this.coValues[msg.knownState.coValueID]; if (!entry) { if (msg.asDependencyOf) { - if (this.multilogs[msg.asDependencyOf]) { + if (this.coValues[msg.asDependencyOf]) { entry = newLoadingState(); - this.multilogs[msg.knownState.multilogID] = entry; + this.coValues[msg.knownState.coValueID] = entry; } } else { throw new Error( - "Expected multilog entry to be created, missing subscribe?" + "Expected coValue entry to be created, missing subscribe?" ); } } if (entry.state === "loading") { - peer.optimisticKnownStates[msg.knownState.multilogID] = + peer.optimisticKnownStates[msg.knownState.coValueID] = msg.knownState; return []; } - const newContent = entry.multilog.newContentSince(msg.knownState); - peer.optimisticKnownStates[msg.knownState.multilogID] = - combinedKnownStates(msg.knownState, entry.multilog.knownState()); + const newContent = entry.coValue.newContentSince(msg.knownState); + peer.optimisticKnownStates[msg.knownState.coValueID] = + combinedKnownStates(msg.knownState, entry.coValue.knownState()); return newContent ? [newContent] : []; } handleNewContent(msg: NewContentMessage): SyncMessage[] { - let entry = this.multilogs[msg.multilogID]; + let entry = this.coValues[msg.coValueID]; if (!entry) { throw new Error( - "Expected multilog entry to be created, missing subscribe?" + "Expected coValue entry to be created, missing subscribe?" ); } - let resolveAfterDone: ((multilog: MultiLog) => void) | undefined; + let resolveAfterDone: ((coValue: CoValue) => void) | undefined; if (entry.state === "loading") { if (!msg.header) { throw new Error("Expected header to be sent in first message"); } - const multilog = new MultiLog(msg.header, this); + const coValue = new CoValue(msg.header, this); resolveAfterDone = entry.resolve; entry = { state: "loaded", - multilog, + coValue: coValue, }; - this.multilogs[msg.multilogID] = entry; + this.coValues[msg.coValueID] = entry; } - const multilog = entry.multilog; + const coValue = entry.coValue; let invalidStateAssumed = false; for (const sessionID of Object.keys(msg.newContent) as SessionID[]) { const ourKnownTxIdx = - multilog.sessions[sessionID]?.transactions.length; + coValue.sessions[sessionID]?.transactions.length; const theirFirstNewTxIdx = msg.newContent[sessionID].after; if ((ourKnownTxIdx || 0) < theirFirstNewTxIdx) { @@ -361,7 +361,7 @@ export class LocalNode { alreadyKnownOffset ); - const success = multilog.tryAddTransactions( + const success = coValue.tryAddTransactions( sessionID, newTransactions, msg.newContent[sessionID].lastHash, @@ -375,14 +375,14 @@ export class LocalNode { } if (resolveAfterDone) { - resolveAfterDone(multilog); + resolveAfterDone(coValue); } return invalidStateAssumed ? [ { action: "wrongAssumedKnownState", - knownState: multilog.knownState(), + knownState: coValue.knownState(), }, ] : []; @@ -392,12 +392,12 @@ export class LocalNode { msg: WrongAssumedKnownStateMessage, peer: PeerState ): SyncMessage[] { - const multilog = this.expectMultiLogLoaded(msg.knownState.multilogID); + const coValue = this.expectCoValueLoaded(msg.knownState.coValueID); - peer.optimisticKnownStates[msg.knownState.multilogID] = - combinedKnownStates(msg.knownState, multilog.knownState()); + peer.optimisticKnownStates[msg.knownState.coValueID] = + combinedKnownStates(msg.knownState, coValue.knownState()); - const newContent = multilog.newContentSince(msg.knownState); + const newContent = coValue.newContentSince(msg.knownState); return newContent ? [newContent] : []; } @@ -406,28 +406,28 @@ export class LocalNode { throw new Error("Method not implemented."); } - async syncMultiLog(multilog: MultiLog) { + async syncCoValue(coValue: CoValue) { for (const peer of Object.values(this.peers)) { const optimisticKnownState = - peer.optimisticKnownStates[multilog.id]; + peer.optimisticKnownStates[coValue.id]; if (optimisticKnownState || peer.role === "server") { const newContent = - multilog.newContentSince(optimisticKnownState); + coValue.newContentSince(optimisticKnownState); - peer.optimisticKnownStates[multilog.id] = peer - .optimisticKnownStates[multilog.id] + peer.optimisticKnownStates[coValue.id] = peer + .optimisticKnownStates[coValue.id] ? combinedKnownStates( - peer.optimisticKnownStates[multilog.id], - multilog.knownState() + peer.optimisticKnownStates[coValue.id], + coValue.knownState() ) - : multilog.knownState(); + : coValue.knownState(); if (!optimisticKnownState && peer.role === "server") { // auto-subscribe await peer.outgoing.write({ action: "subscribe", - knownState: multilog.knownState(), + knownState: coValue.knownState(), }); } @@ -444,21 +444,21 @@ export class LocalNode { ): LocalNode { const newNode = new LocalNode(agentCredential, ownSessionID); - newNode.multilogs = Object.fromEntries( - Object.entries(this.multilogs) + newNode.coValues = Object.fromEntries( + Object.entries(this.coValues) .map(([id, entry]) => { if (entry.state === "loading") { return undefined; } - const newMultilog = new MultiLog( - entry.multilog.header, + const newCoValue = new CoValue( + entry.coValue.header, newNode ); - newMultilog.sessions = entry.multilog.sessions; + newCoValue.sessions = entry.coValue.sessions; - return [id, { state: "loaded", multilog: newMultilog }]; + return [id, { state: "loaded", coValue: newCoValue }]; }) .filter((x): x is Exclude => !!x) ); @@ -472,18 +472,18 @@ export class LocalNode { } } -type MultilogState = +type CoValueState = | { state: "loading"; - done: Promise; - resolve: (multilog: MultiLog) => void; + done: Promise; + resolve: (coValue: CoValue) => void; } - | { state: "loaded"; multilog: MultiLog }; + | { state: "loaded"; coValue: CoValue }; -function newLoadingState(): MultilogState { - let resolve: (multilog: MultiLog) => void; +function newLoadingState(): CoValueState { + let resolve: (coValue: CoValue) => void; - const promise = new Promise((r) => { + const promise = new Promise((r) => { resolve = r; }); diff --git a/src/permissions.test.ts b/src/permissions.test.ts index 0f21679a2..3d26fb40c 100644 --- a/src/permissions.test.ts +++ b/src/permissions.test.ts @@ -4,7 +4,7 @@ import { getAgentID, newRandomAgentCredential, newRandomSessionID, -} from "./multilog"; +} from "./coValue"; import { LocalNode } from "./node"; import { expectMap } from "./contentType"; import { expectTeamContent } from "./permissions"; @@ -44,7 +44,7 @@ function newTeam() { const node = new LocalNode(admin, newRandomSessionID(adminID)); - const team = node.createMultiLog({ + const team = node.createCoValue({ type: "comap", ruleset: { type: "team", initialAdmin: adminID }, meta: null, @@ -351,7 +351,7 @@ test("Admins can add readers to a team, who can't add admins, writers, or reader test("Admins can write to an object that is owned by their team", () => { const { node, team } = newTeam(); - const childObject = node.createMultiLog({ + const childObject = node.createCoValue({ type: "comap", ruleset: { type: "ownedByTeam", team: team.id }, meta: null, @@ -393,7 +393,7 @@ test("Writers can write to an object that is owned by their team", () => { expect(editable.get(writerID)).toEqual("writer"); }); - const childObject = node.createMultiLog({ + const childObject = node.createCoValue({ type: "comap", ruleset: { type: "ownedByTeam", team: team.id }, meta: null, @@ -431,7 +431,7 @@ test("Writers can write to an object that is owned by their team (high level)", const childObject = team.createMap(); let childObjectAsWriter = expectMap( - childObject.multiLog + childObject.coValue .testWithDifferentCredentials(writer, newRandomSessionID(writerID)) .getCurrentContent() ); @@ -455,7 +455,7 @@ test("Readers can not write to an object that is owned by their team", () => { expect(editable.get(readerID)).toEqual("reader"); }); - const childObject = node.createMultiLog({ + const childObject = node.createCoValue({ type: "comap", ruleset: { type: "ownedByTeam", team: team.id }, meta: null, @@ -493,7 +493,7 @@ test("Readers can not write to an object that is owned by their team (high level const childObject = team.createMap(); let childObjectAsReader = expectMap( - childObject.multiLog + childObject.coValue .testWithDifferentCredentials(reader, newRandomSessionID(readerID)) .getCurrentContent() ); @@ -530,7 +530,7 @@ test("Admins can set team read key and then use it to create and read private tr expect(team.getCurrentReadKey().secret).toEqual(readKey); }); - const childObject = node.createMultiLog({ + const childObject = node.createCoValue({ type: "comap", ruleset: { type: "ownedByTeam", team: team.id }, meta: null, @@ -588,7 +588,7 @@ test("Admins can set team read key and then writers can use it to create and rea editable.set("readKey", { keyID: readKeyID, revelation }, "trusting"); }); - const childObject = node.createMultiLog({ + const childObject = node.createCoValue({ type: "comap", ruleset: { type: "ownedByTeam", team: team.id }, meta: null, @@ -628,7 +628,7 @@ test("Admins can set team read key and then writers can use it to create and rea const childObject = team.createMap(); let childObjectAsWriter = expectMap( - childObject.multiLog + childObject.coValue .testWithDifferentCredentials(writer, newRandomSessionID(writerID)) .getCurrentContent() ); @@ -669,7 +669,7 @@ test("Admins can set team read key and then use it to create private transaction editable.set("readKey", { keyID: readKeyID, revelation }, "trusting"); }); - const childObject = node.createMultiLog({ + const childObject = node.createCoValue({ type: "comap", ruleset: { type: "ownedByTeam", team: team.id }, meta: null, @@ -711,7 +711,7 @@ test("Admins can set team read key and then use it to create private transaction expect(editable.get("foo")).toEqual("bar"); }); - const childContentAsReader = expectMap(childObject.multiLog.testWithDifferentCredentials( + const childContentAsReader = expectMap(childObject.coValue.testWithDifferentCredentials( reader, newRandomSessionID(readerID) ).getCurrentContent()); @@ -769,7 +769,7 @@ test("Admins can set team read key and then use it to create private transaction ); }); - const childObject = node.createMultiLog({ + const childObject = node.createCoValue({ type: "comap", ruleset: { type: "ownedByTeam", team: team.id }, meta: null, @@ -827,7 +827,7 @@ test("Admins can set team read key and then use it to create private transaction expect(editable.get("foo")).toEqual("bar"); }); - const childContentAsReader1 = expectMap(childObject.multiLog.testWithDifferentCredentials( + const childContentAsReader1 = expectMap(childObject.coValue.testWithDifferentCredentials( reader1, newRandomSessionID(reader1ID) ).getCurrentContent()); @@ -836,7 +836,7 @@ test("Admins can set team read key and then use it to create private transaction team.addMember(reader2ID, "reader"); - const childContentAsReader2 = expectMap(childObject.multiLog.testWithDifferentCredentials( + const childContentAsReader2 = expectMap(childObject.coValue.testWithDifferentCredentials( reader2, newRandomSessionID(reader2ID) ).getCurrentContent()); @@ -869,7 +869,7 @@ test("Admins can set team read key, make a private transaction in an owned objec expect(team.getCurrentReadKey().secret).toEqual(readKey); }); - const childObject = node.createMultiLog({ + const childObject = node.createCoValue({ type: "comap", ruleset: { type: "ownedByTeam", team: team.id }, meta: null, @@ -923,7 +923,7 @@ test("Admins can set team read key, make a private transaction in an owned objec let childObject = team.createMap(); - const firstReadKey = childObject.multiLog.getCurrentReadKey(); + const firstReadKey = childObject.coValue.getCurrentReadKey(); childObject = childObject.edit((editable) => { editable.set("foo", "bar", "private"); @@ -934,7 +934,7 @@ test("Admins can set team read key, make a private transaction in an owned objec team.rotateReadKey(); - expect(childObject.multiLog.getCurrentReadKey()).not.toEqual(firstReadKey); + expect(childObject.coValue.getCurrentReadKey()).not.toEqual(firstReadKey); childObject = childObject.edit((editable) => { editable.set("foo2", "bar2", "private"); @@ -948,7 +948,7 @@ test("Admins can set team read key, make a private transaction in an owned objec test("Admins can set team read key, make a private transaction in an owned object, rotate the read key, add a reader, make another private transaction in the owned object, and both can be read by the reader", () => { const { node, team, admin, adminID } = newTeam(); - const childObject = node.createMultiLog({ + const childObject = node.createCoValue({ type: "comap", ruleset: { type: "ownedByTeam", team: team.id }, meta: null, @@ -1052,7 +1052,7 @@ test("Admins can set team read key, make a private transaction in an owned objec let childObject = team.createMap(); - const firstReadKey = childObject.multiLog.getCurrentReadKey(); + const firstReadKey = childObject.coValue.getCurrentReadKey(); childObject = childObject.edit((editable) => { editable.set("foo", "bar", "private"); @@ -1063,7 +1063,7 @@ test("Admins can set team read key, make a private transaction in an owned objec team.rotateReadKey(); - expect(childObject.multiLog.getCurrentReadKey()).not.toEqual(firstReadKey); + expect(childObject.coValue.getCurrentReadKey()).not.toEqual(firstReadKey); const reader = newRandomAgentCredential(); const readerID = getAgentID(getAgent(reader)); @@ -1077,7 +1077,7 @@ test("Admins can set team read key, make a private transaction in an owned objec expect(editable.get("foo2")).toEqual("bar2"); }); - const childContentAsReader = expectMap(childObject.multiLog.testWithDifferentCredentials( + const childContentAsReader = expectMap(childObject.coValue.testWithDifferentCredentials( reader, newRandomSessionID(readerID) ).getCurrentContent()); @@ -1090,7 +1090,7 @@ test("Admins can set team read key, make a private transaction in an owned objec test("Admins can set team read rey, make a private transaction in an owned object, rotate the read key, add two readers, rotate the read key again to kick out one reader, make another private transaction in the owned object, and only the remaining reader can read both transactions", () => { const { node, team, admin, adminID } = newTeam(); - const childObject = node.createMultiLog({ + const childObject = node.createCoValue({ type: "comap", ruleset: { type: "ownedByTeam", team: team.id }, meta: null, @@ -1192,7 +1192,7 @@ test("Admins can set team read rey, make a private transaction in an owned objec expect(editable.get("foo2")).toEqual("bar2"); }); - // TODO: make sure these instances of multilogs sync between each other so this isn't necessary? + // TODO: make sure these instances of coValues sync between each other so this isn't necessary? childObjectAsReader = childObject.testWithDifferentCredentials( reader, newRandomSessionID(readerID) @@ -1228,7 +1228,7 @@ test("Admins can set team read rey, make a private transaction in an owned objec team.rotateReadKey(); - const secondReadKey = childObject.multiLog.getCurrentReadKey(); + const secondReadKey = childObject.coValue.getCurrentReadKey(); const reader = newRandomAgentCredential(); const readerID = getAgentID(getAgent(reader)); @@ -1251,14 +1251,14 @@ test("Admins can set team read rey, make a private transaction in an owned objec team.removeMember(readerID); - expect(childObject.multiLog.getCurrentReadKey()).not.toEqual(secondReadKey); + expect(childObject.coValue.getCurrentReadKey()).not.toEqual(secondReadKey); childObject = childObject.edit((editable) => { editable.set("foo3", "bar3", "private"); expect(editable.get("foo3")).toEqual("bar3"); }); - const childContentAsReader2 = expectMap(childObject.multiLog.testWithDifferentCredentials( + const childContentAsReader2 = expectMap(childObject.coValue.testWithDifferentCredentials( reader2, newRandomSessionID(reader2ID) ).getCurrentContent()); @@ -1267,7 +1267,7 @@ test("Admins can set team read rey, make a private transaction in an owned objec expect(childContentAsReader2.get("foo2")).toEqual("bar2"); expect(childContentAsReader2.get("foo3")).toEqual("bar3"); - expect(() => childObject.multiLog.testWithDifferentCredentials( + expect(() => childObject.coValue.testWithDifferentCredentials( reader, newRandomSessionID(readerID) ).getCurrentContent()).toThrow(/readKey (.+?) not revealed for (.+?)/); diff --git a/src/permissions.ts b/src/permissions.ts index 9dc10cef0..224d568cd 100644 --- a/src/permissions.ts +++ b/src/permissions.ts @@ -15,19 +15,19 @@ import { import { AgentCredential, AgentID, - MultiLog, - MultiLogID, + CoValue, + RawCoValueID, SessionID, Transaction, TransactionID, TrustingTransaction, agentIDfromSessionID, -} from "./multilog"; +} from "./coValue"; import { LocalNode } from "."; export type PermissionsDef = - | { type: "team"; initialAdmin: AgentID; parentTeams?: MultiLogID[] } - | { type: "ownedByTeam"; team: MultiLogID } + | { type: "team"; initialAdmin: AgentID; parentTeams?: RawCoValueID[] } + | { type: "ownedByTeam"; team: RawCoValueID } | { type: "agent"; initialSignatoryID: SignatoryID; @@ -38,11 +38,11 @@ export type PermissionsDef = export type Role = "reader" | "writer" | "admin" | "revoked"; export function determineValidTransactions( - multilog: MultiLog + coValue: CoValue ): { txID: TransactionID; tx: Transaction }[] { - if (multilog.header.ruleset.type === "team") { + if (coValue.header.ruleset.type === "team") { const allTrustingTransactionsSorted = Object.entries( - multilog.sessions + coValue.sessions ).flatMap(([sessionID, sessionLog]) => { return sessionLog.transactions .map((tx, txIndex) => ({ sessionID, txIndex, tx })) @@ -64,7 +64,7 @@ export function determineValidTransactions( return a.tx.madeAt - b.tx.madeAt; }); - const initialAdmin = multilog.header.ruleset.initialAdmin; + const initialAdmin = coValue.header.ruleset.initialAdmin; if (!initialAdmin) { throw new Error("Team must have initialAdmin"); @@ -153,10 +153,10 @@ export function determineValidTransactions( } return validTransactions; - } else if (multilog.header.ruleset.type === "ownedByTeam") { + } else if (coValue.header.ruleset.type === "ownedByTeam") { const teamContent = - multilog.node.expectMultiLogLoaded( - multilog.header.ruleset.team, + coValue.node.expectCoValueLoaded( + coValue.header.ruleset.team, "Determining valid transaction in owned object but its team wasn't loaded" ).getCurrentContent(); @@ -164,7 +164,7 @@ export function determineValidTransactions( throw new Error("Team must be a map"); } - return Object.entries(multilog.sessions).flatMap( + return Object.entries(coValue.sessions).flatMap( ([sessionID, sessionLog]) => { const transactor = agentIDfromSessionID(sessionID as SessionID); return sessionLog.transactions @@ -185,8 +185,8 @@ export function determineValidTransactions( })); } ); - } else if (multilog.header.ruleset.type === "unsafeAllowAll") { - return Object.entries(multilog.sessions).flatMap( + } else if (coValue.header.ruleset.type === "unsafeAllowAll") { + return Object.entries(coValue.sessions).flatMap( ([sessionID, sessionLog]) => { return sessionLog.transactions.map((tx, txIndex) => ({ txID: { sessionID: sessionID as SessionID, txIndex }, @@ -194,11 +194,11 @@ export function determineValidTransactions( })); } ); - } else if (multilog.header.ruleset.type === "agent") { + } else if (coValue.header.ruleset.type === "agent") { // TODO return []; } else { - throw new Error("Unknown ruleset type " + (multilog.header.ruleset as any).type); + throw new Error("Unknown ruleset type " + (coValue.header.ruleset as any).type); } } @@ -232,7 +232,7 @@ export class Team { this.node = node; } - get id(): MultiLogID { + get id(): RawCoValueID { return this.teamMap.id; } @@ -249,15 +249,15 @@ export class Team { throw new Error("Failed to set role"); } - const currentReadKey = this.teamMap.multiLog.getCurrentReadKey(); + const currentReadKey = this.teamMap.coValue.getCurrentReadKey(); const revelation = seal( currentReadKey.secret, - this.teamMap.multiLog.node.agentCredential.recipientSecret, + this.teamMap.coValue.node.agentCredential.recipientSecret, new Set([agent.recipientID]), { - in: this.teamMap.multiLog.id, - tx: this.teamMap.multiLog.nextTransactionID(), + in: this.teamMap.coValue.id, + tx: this.teamMap.coValue.nextTransactionID(), } ); @@ -281,21 +281,21 @@ export class Team { } }) as AgentID[]; - const currentReadKey = this.teamMap.multiLog.getCurrentReadKey(); + const currentReadKey = this.teamMap.coValue.getCurrentReadKey(); const newReadKey = newRandomKeySecret(); const newReadKeyRevelation = seal( newReadKey.secret, - this.teamMap.multiLog.node.agentCredential.recipientSecret, + this.teamMap.coValue.node.agentCredential.recipientSecret, new Set( currentlyPermittedReaders.map( (reader) => this.node.knownAgents[reader].recipientID ) ), { - in: this.teamMap.multiLog.id, - tx: this.teamMap.multiLog.nextTransactionID(), + in: this.teamMap.coValue.id, + tx: this.teamMap.coValue.nextTransactionID(), } ); @@ -329,7 +329,7 @@ export class Team { meta?: M ): CoMap { return this.node - .createMultiLog({ + .createCoValue({ type: "comap", ruleset: { type: "ownedByTeam", @@ -346,7 +346,7 @@ export class Team { ): Team { return new Team( expectTeamContent( - this.teamMap.multiLog + this.teamMap.coValue .testWithDifferentCredentials(credential, sessionId) .getCurrentContent() ), diff --git a/src/sync.test.ts b/src/sync.test.ts index 56f2c758a..2698d1224 100644 --- a/src/sync.test.ts +++ b/src/sync.test.ts @@ -4,7 +4,7 @@ import { getAgentID, newRandomAgentCredential, newRandomSessionID, -} from "./multilog"; +} from "./coValue"; import { LocalNode } from "./node"; import { Peer, SyncMessage } from "./sync"; import { MapOpPayload, expectMap } from "./contentType"; @@ -40,7 +40,7 @@ test( await writer.write({ action: "subscribe", knownState: { - multilogID: map.multiLog.id, + coValueID: map.coValue.id, header: false, sessions: {}, }, @@ -57,14 +57,14 @@ test( expect(subscribeResponseMsg.value).toEqual({ action: "subscribeResponse", - knownState: map.multiLog.knownState(), + knownState: map.coValue.knownState(), } satisfies SyncMessage); const newContentMsg = await reader.read(); expect(newContentMsg.value).toEqual({ action: "newContent", - multilogID: map.multiLog.id, + coValueID: map.coValue.id, header: { type: "comap", ruleset: { type: "ownedByTeam", team: team.id }, @@ -76,7 +76,7 @@ test( newTransactions: [ { privacy: "trusting", - madeAt: map.multiLog.sessions[node.ownSessionID] + madeAt: map.coValue.sessions[node.ownSessionID] .transactions[0].madeAt, changes: [ { @@ -88,9 +88,9 @@ test( }, ], lastHash: - map.multiLog.sessions[node.ownSessionID].lastHash!, + map.coValue.sessions[node.ownSessionID].lastHash!, lastSignature: - map.multiLog.sessions[node.ownSessionID].lastSignature!, + map.coValue.sessions[node.ownSessionID].lastSignature!, }, }, } satisfies SyncMessage); @@ -128,7 +128,7 @@ test("Node replies with only new tx to subscribe with some known state", async ( await writer.write({ action: "subscribe", knownState: { - multilogID: map.multiLog.id, + coValueID: map.coValue.id, header: true, sessions: { [node.ownSessionID]: 1, @@ -147,14 +147,14 @@ test("Node replies with only new tx to subscribe with some known state", async ( expect(mapSubscribeResponseMsg.value).toEqual({ action: "subscribeResponse", - knownState: map.multiLog.knownState(), + knownState: map.coValue.knownState(), } satisfies SyncMessage); const mapNewContentMsg = await reader.read(); expect(mapNewContentMsg.value).toEqual({ action: "newContent", - multilogID: map.multiLog.id, + coValueID: map.coValue.id, header: undefined, newContent: { [node.ownSessionID]: { @@ -162,7 +162,7 @@ test("Node replies with only new tx to subscribe with some known state", async ( newTransactions: [ { privacy: "trusting", - madeAt: map.multiLog.sessions[node.ownSessionID] + madeAt: map.coValue.sessions[node.ownSessionID] .transactions[1].madeAt, changes: [ { @@ -173,15 +173,15 @@ test("Node replies with only new tx to subscribe with some known state", async ( ], }, ], - lastHash: map.multiLog.sessions[node.ownSessionID].lastHash!, + lastHash: map.coValue.sessions[node.ownSessionID].lastHash!, lastSignature: - map.multiLog.sessions[node.ownSessionID].lastSignature!, + map.coValue.sessions[node.ownSessionID].lastSignature!, }, }, } satisfies SyncMessage); }); -test.skip("TODO: node only replies with new tx to subscribe with some known state, even in the depended on multilogs", () => {}); +test.skip("TODO: node only replies with new tx to subscribe with some known state, even in the depended on coValues", () => {}); test("After subscribing, node sends own known state and new txs to peer", async () => { const admin = newRandomAgentCredential(); @@ -208,7 +208,7 @@ test("After subscribing, node sends own known state and new txs to peer", async await writer.write({ action: "subscribe", knownState: { - multilogID: map.multiLog.id, + coValueID: map.coValue.id, header: false, sessions: { [node.ownSessionID]: 0, @@ -227,15 +227,15 @@ test("After subscribing, node sends own known state and new txs to peer", async expect(mapSubscribeResponseMsg.value).toEqual({ action: "subscribeResponse", - knownState: map.multiLog.knownState(), + knownState: map.coValue.knownState(), } satisfies SyncMessage); const mapNewContentHeaderOnlyMsg = await reader.read(); expect(mapNewContentHeaderOnlyMsg.value).toEqual({ action: "newContent", - multilogID: map.multiLog.id, - header: map.multiLog.header, + coValueID: map.coValue.id, + header: map.coValue.header, newContent: {}, } satisfies SyncMessage); @@ -247,14 +247,14 @@ test("After subscribing, node sends own known state and new txs to peer", async expect(mapEditMsg1.value).toEqual({ action: "newContent", - multilogID: map.multiLog.id, + coValueID: map.coValue.id, newContent: { [node.ownSessionID]: { after: 0, newTransactions: [ { privacy: "trusting", - madeAt: map.multiLog.sessions[node.ownSessionID] + madeAt: map.coValue.sessions[node.ownSessionID] .transactions[0].madeAt, changes: [ { @@ -265,9 +265,9 @@ test("After subscribing, node sends own known state and new txs to peer", async ], }, ], - lastHash: map.multiLog.sessions[node.ownSessionID].lastHash!, + lastHash: map.coValue.sessions[node.ownSessionID].lastHash!, lastSignature: - map.multiLog.sessions[node.ownSessionID].lastSignature!, + map.coValue.sessions[node.ownSessionID].lastSignature!, }, }, } satisfies SyncMessage); @@ -280,14 +280,14 @@ test("After subscribing, node sends own known state and new txs to peer", async expect(mapEditMsg2.value).toEqual({ action: "newContent", - multilogID: map.multiLog.id, + coValueID: map.coValue.id, newContent: { [node.ownSessionID]: { after: 1, newTransactions: [ { privacy: "trusting", - madeAt: map.multiLog.sessions[node.ownSessionID] + madeAt: map.coValue.sessions[node.ownSessionID] .transactions[1].madeAt, changes: [ { @@ -298,9 +298,9 @@ test("After subscribing, node sends own known state and new txs to peer", async ], }, ], - lastHash: map.multiLog.sessions[node.ownSessionID].lastHash!, + lastHash: map.coValue.sessions[node.ownSessionID].lastHash!, lastSignature: - map.multiLog.sessions[node.ownSessionID].lastSignature!, + map.coValue.sessions[node.ownSessionID].lastSignature!, }, }, } satisfies SyncMessage); @@ -335,7 +335,7 @@ test("Client replies with known new content to subscribeResponse from server", a await writer.write({ action: "subscribeResponse", knownState: { - multilogID: map.multiLog.id, + coValueID: map.coValue.id, header: false, sessions: { [node.ownSessionID]: 0, @@ -349,15 +349,15 @@ test("Client replies with known new content to subscribeResponse from server", a expect(msg1.value).toEqual({ action: "newContent", - multilogID: map.multiLog.id, - header: map.multiLog.header, + coValueID: map.coValue.id, + header: map.coValue.header, newContent: { [node.ownSessionID]: { after: 0, newTransactions: [ { privacy: "trusting", - madeAt: map.multiLog.sessions[node.ownSessionID] + madeAt: map.coValue.sessions[node.ownSessionID] .transactions[0].madeAt, changes: [ { @@ -368,9 +368,9 @@ test("Client replies with known new content to subscribeResponse from server", a ], }, ], - lastHash: map.multiLog.sessions[node.ownSessionID].lastHash!, + lastHash: map.coValue.sessions[node.ownSessionID].lastHash!, lastSignature: - map.multiLog.sessions[node.ownSessionID].lastSignature!, + map.coValue.sessions[node.ownSessionID].lastSignature!, }, }, } satisfies SyncMessage); @@ -401,7 +401,7 @@ test("No matter the optimistic known state, node respects invalid known state me await writer.write({ action: "subscribe", knownState: { - multilogID: map.multiLog.id, + coValueID: map.coValue.id, header: false, sessions: { [node.ownSessionID]: 0, @@ -432,7 +432,7 @@ test("No matter the optimistic known state, node respects invalid known state me await writer.write({ action: "wrongAssumedKnownState", knownState: { - multilogID: map.multiLog.id, + coValueID: map.coValue.id, header: true, sessions: { [node.ownSessionID]: 1, @@ -444,7 +444,7 @@ test("No matter the optimistic known state, node respects invalid known state me expect(newContentAfterWrongAssumedState.value).toEqual({ action: "newContent", - multilogID: map.multiLog.id, + coValueID: map.coValue.id, header: undefined, newContent: { [node.ownSessionID]: { @@ -452,7 +452,7 @@ test("No matter the optimistic known state, node respects invalid known state me newTransactions: [ { privacy: "trusting", - madeAt: map.multiLog.sessions[node.ownSessionID] + madeAt: map.coValue.sessions[node.ownSessionID] .transactions[1].madeAt, changes: [ { @@ -463,15 +463,15 @@ test("No matter the optimistic known state, node respects invalid known state me ], }, ], - lastHash: map.multiLog.sessions[node.ownSessionID].lastHash!, + lastHash: map.coValue.sessions[node.ownSessionID].lastHash!, lastSignature: - map.multiLog.sessions[node.ownSessionID].lastSignature!, + map.coValue.sessions[node.ownSessionID].lastSignature!, }, }, } satisfies SyncMessage); }); -test("If we add a peer, but it never subscribes to a multilog, it won't get any messages", async () => { +test("If we add a peer, but it never subscribes to a coValue, it won't get any messages", async () => { const admin = newRandomAgentCredential(); const adminID = getAgentID(getAgent(admin)); @@ -500,7 +500,7 @@ test("If we add a peer, but it never subscribes to a multilog, it won't get any await shouldNotResolve(reader.read(), { timeout: 50 }); }); -test("If we add a server peer, all updates to all multilogs are sent to it, even if it doesn't subscribe", async () => { +test("If we add a server peer, all updates to all coValues are sent to it, even if it doesn't subscribe", async () => { const admin = newRandomAgentCredential(); const adminID = getAgentID(getAgent(admin)); @@ -533,7 +533,7 @@ test("If we add a server peer, all updates to all multilogs are sent to it, even expect(subscribeMsg.value).toEqual({ action: "subscribe", knownState: { - multilogID: map.multiLog.id, + coValueID: map.coValue.id, header: true, sessions: {}, }, @@ -543,15 +543,15 @@ test("If we add a server peer, all updates to all multilogs are sent to it, even expect(newContentMsg.value).toEqual({ action: "newContent", - multilogID: map.multiLog.id, - header: map.multiLog.header, + coValueID: map.coValue.id, + header: map.coValue.header, newContent: { [node.ownSessionID]: { after: 0, newTransactions: [ { privacy: "trusting", - madeAt: map.multiLog.sessions[node.ownSessionID] + madeAt: map.coValue.sessions[node.ownSessionID] .transactions[0].madeAt, changes: [ { @@ -562,15 +562,15 @@ test("If we add a server peer, all updates to all multilogs are sent to it, even ], }, ], - lastHash: map.multiLog.sessions[node.ownSessionID].lastHash!, + lastHash: map.coValue.sessions[node.ownSessionID].lastHash!, lastSignature: - map.multiLog.sessions[node.ownSessionID].lastSignature!, + map.coValue.sessions[node.ownSessionID].lastSignature!, }, }, } satisfies SyncMessage); }); -test("If we add a server peer, newly created multilogs are auto-subscribed to", async () => { +test("If we add a server peer, newly created coValues are auto-subscribed to", async () => { const admin = newRandomAgentCredential(); const adminID = getAgentID(getAgent(admin)); @@ -600,22 +600,22 @@ test("If we add a server peer, newly created multilogs are auto-subscribed to", expect(msg1.value).toEqual({ action: "subscribe", - knownState: map.multiLog.knownState(), + knownState: map.coValue.knownState(), } satisfies SyncMessage); const msg2 = await reader.read(); expect(msg2.value).toEqual({ action: "newContent", - multilogID: map.multiLog.id, - header: map.multiLog.header, + coValueID: map.coValue.id, + header: map.coValue.header, newContent: {}, } satisfies SyncMessage); }); test.skip("TODO: when receiving a subscribe response that is behind our optimistic state (due to already sent content), we ignore it", () => {}); -test("When we connect a new server peer, we try to sync all existing multilogs to it", async () => { +test("When we connect a new server peer, we try to sync all existing coValues to it", async () => { const admin = newRandomAgentCredential(); const adminID = getAgentID(getAgent(admin)); @@ -642,14 +642,14 @@ test("When we connect a new server peer, we try to sync all existing multilogs t expect(teamSubscribeMessage.value).toEqual({ action: "subscribe", - knownState: team.teamMap.multiLog.knownState(), + knownState: team.teamMap.coValue.knownState(), } satisfies SyncMessage); const secondMessage = await reader.read(); expect(secondMessage.value).toEqual({ action: "subscribe", - knownState: map.multiLog.knownState(), + knownState: map.coValue.knownState(), } satisfies SyncMessage); }); @@ -678,7 +678,7 @@ test("When receiving a subscribe with a known state that is ahead of our own, pe await writer.write({ action: "subscribe", knownState: { - multilogID: map.multiLog.id, + coValueID: map.coValue.id, header: true, sessions: { [node.ownSessionID]: 1, @@ -696,11 +696,11 @@ test("When receiving a subscribe with a known state that is ahead of our own, pe expect(mapSubscribeResponse.value).toEqual({ action: "subscribeResponse", - knownState: map.multiLog.knownState(), + knownState: map.coValue.knownState(), } satisfies SyncMessage); }); -test("When replaying creation and transactions of a multilog as new content, the receiving peer integrates this information", async () => { +test("When replaying creation and transactions of a coValue as new content, the receiving peer integrates this information", async () => { const admin = newRandomAgentCredential(); const adminID = getAgentID(getAgent(admin)); @@ -752,7 +752,7 @@ test("When replaying creation and transactions of a multilog as new content, the await writer2.write(teamSubscribeMsg.value); const teamSubscribeResponseMsg = await reader2.read(); - expect(node2.multilogs[team.teamMap.multiLog.id]?.state).toEqual("loading"); + expect(node2.coValues[team.teamMap.coValue.id]?.state).toEqual("loading"); const writer1 = inTx1.getWriter(); @@ -765,7 +765,7 @@ test("When replaying creation and transactions of a multilog as new content, the const _mapSubscribeResponseMsg = await reader2.read(); await writer2.write(mapNewContentMsg.value); - expect(node2.multilogs[map.multiLog.id]?.state).toEqual("loading"); + expect(node2.coValues[map.coValue.id]?.state).toEqual("loading"); await writer2.write(mapEditMsg.value); @@ -773,12 +773,12 @@ test("When replaying creation and transactions of a multilog as new content, the expect( expectMap( - node2.expectMultiLogLoaded(map.multiLog.id).getCurrentContent() + node2.expectCoValueLoaded(map.coValue.id).getCurrentContent() ).get("hello") ).toEqual("world"); }); -test("When loading a multilog on one node, the server node it is requested from replies with all the necessary depended on multilogs to make it work", async () => { +test("When loading a coValue on one node, the server node it is requested from replies with all the necessary depended on coValues to make it work", async () => { const admin = newRandomAgentCredential(); const adminID = getAgentID(getAgent(admin)); @@ -798,11 +798,11 @@ test("When loading a multilog on one node, the server node it is requested from node1.addPeer(node2asPeer); node2.addPeer(node1asPeer); - await node2.loadMultiLog(map.multiLog.id); + await node2.loadCoValue(map.coValue.id); expect( expectMap( - node2.expectMultiLogLoaded(map.multiLog.id).getCurrentContent() + node2.expectCoValueLoaded(map.coValue.id).getCurrentContent() ).get("hello") ).toEqual("world"); }); diff --git a/src/sync.ts b/src/sync.ts index 276d84bbe..3a8bd02e3 100644 --- a/src/sync.ts +++ b/src/sync.ts @@ -1,8 +1,8 @@ import { Hash, Signature } from "./crypto"; -import { MultiLogHeader, MultiLogID, SessionID, Transaction } from "./multilog"; +import { CoValueHeader, RawCoValueID, SessionID, Transaction } from "./coValue"; -export type MultiLogKnownState = { - multilogID: MultiLogID; +export type CoValueKnownState = { + coValueID: RawCoValueID; header: boolean; sessions: { [sessionID: SessionID]: number }; }; @@ -16,19 +16,19 @@ export type SyncMessage = export type SubscribeMessage = { action: "subscribe"; - knownState: MultiLogKnownState; + knownState: CoValueKnownState; }; export type SubscribeResponseMessage = { action: "subscribeResponse"; - knownState: MultiLogKnownState; - asDependencyOf?: MultiLogID; + knownState: CoValueKnownState; + asDependencyOf?: RawCoValueID; }; export type NewContentMessage = { action: "newContent"; - multilogID: MultiLogID; - header?: MultiLogHeader; + coValueID: RawCoValueID; + header?: CoValueHeader; newContent: { [sessionID: SessionID]: SessionNewContent; }; @@ -44,12 +44,12 @@ export type SessionNewContent = { export type WrongAssumedKnownStateMessage = { action: "wrongAssumedKnownState"; - knownState: MultiLogKnownState; + knownState: CoValueKnownState; }; export type UnsubscribeMessage = { action: "unsubscribe"; - multilogID: MultiLogID; + coValueID: RawCoValueID; }; export type PeerID = string; @@ -63,15 +63,15 @@ export interface Peer { export interface PeerState { id: PeerID; - optimisticKnownStates: { [multilogID: MultiLogID]: MultiLogKnownState }; + optimisticKnownStates: { [coValueID: RawCoValueID]: CoValueKnownState }; incoming: ReadableStream; outgoing: WritableStreamDefaultWriter; role: "peer" | "server" | "client"; } export function weAreStrictlyAhead( - ourKnownState: MultiLogKnownState, - theirKnownState: MultiLogKnownState + ourKnownState: CoValueKnownState, + theirKnownState: CoValueKnownState ): boolean { if (theirKnownState.header && !ourKnownState.header) { return false; @@ -94,8 +94,8 @@ export function weAreStrictlyAhead( return true; } -export function combinedKnownStates(stateA: MultiLogKnownState, stateB: MultiLogKnownState): MultiLogKnownState { - const sessionStates: MultiLogKnownState["sessions"] = {}; +export function combinedKnownStates(stateA: CoValueKnownState, stateB: CoValueKnownState): CoValueKnownState { + const sessionStates: CoValueKnownState["sessions"] = {}; const allSessions = new Set([...Object.keys(stateA.sessions), ...Object.keys(stateB.sessions)] as SessionID[]); @@ -107,7 +107,7 @@ export function combinedKnownStates(stateA: MultiLogKnownState, stateB: MultiLog } return { - multilogID: stateA.multilogID, + coValueID: stateA.coValueID, header: stateA.header || stateB.header, sessions: sessionStates, };