Compare commits
7 Commits
jazz-brows
...
jazz-brows
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10b372da6e | ||
|
|
b556a36db3 | ||
|
|
4d71ab8aac | ||
|
|
f1297c613b | ||
|
|
1ee5c2b3c8 | ||
|
|
f14337f862 | ||
|
|
b8f4571474 |
@@ -2,9 +2,9 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<link rel="icon" type="image/png" href="/jazz-logo.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite + React + TS</title>
|
||||
<title>Jazz Todo List Example</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-example-todo",
|
||||
"private": true,
|
||||
"version": "0.0.10",
|
||||
"version": "0.0.12",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -15,13 +15,14 @@
|
||||
"@radix-ui/react-toast": "^1.1.4",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.0.0",
|
||||
"jazz-react": "^0.0.16",
|
||||
"jazz-react-auth-local": "^0.0.13",
|
||||
"jazz-react": "^0.1.0",
|
||||
"jazz-react-auth-local": "^0.1.0",
|
||||
"lucide-react": "^0.265.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"tailwind-merge": "^1.14.0",
|
||||
"tailwindcss-animate": "^1.0.6"
|
||||
"tailwindcss-animate": "^1.0.6",
|
||||
"uniqolor": "^1.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.2.15",
|
||||
|
||||
BIN
examples/todo/public/jazz-logo.png
Normal file
BIN
examples/todo/public/jazz-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.3 KiB |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -20,6 +20,7 @@ import { SubmittableInput } from "./components/SubmittableInput";
|
||||
import { createInviteLink } from "jazz-react";
|
||||
import { useToast } from "./components/ui/use-toast";
|
||||
import { Skeleton } from "./components/ui/skeleton";
|
||||
import uniqolor from "uniqolor";
|
||||
|
||||
type TaskContent = { done: boolean; text: string };
|
||||
type Task = CoMap<TaskContent>;
|
||||
@@ -38,8 +39,8 @@ function App() {
|
||||
|
||||
const createList = useCallback(
|
||||
(title: string) => {
|
||||
const listTeam = localNode.createTeam();
|
||||
const list = listTeam.createMap<TodoListContent>();
|
||||
const listGroup = localNode.createGroup();
|
||||
const list = listGroup.createMap<TodoListContent>();
|
||||
|
||||
list.edit((list) => {
|
||||
list.set("title", title);
|
||||
@@ -72,7 +73,7 @@ function App() {
|
||||
}, [localNode]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full items-center justify-start gap-10 pt-10 md:pt-[30vh] pb-10 px-5">
|
||||
<div className="flex flex-col h-full items-center justify-start gap-10 pt-10 pb-10 px-5">
|
||||
{listId ? (
|
||||
<TodoList listId={listId} />
|
||||
) : (
|
||||
@@ -100,24 +101,18 @@ export function TodoList({ listId }: { listId: CoID<TodoList> }) {
|
||||
|
||||
const createTask = (text: string) => {
|
||||
if (!list) return;
|
||||
let task = list.coValue.getTeam().createMap<TaskContent>();
|
||||
const task = list.coValue.getGroup().createMap<TaskContent>();
|
||||
|
||||
task = task.edit((task) => {
|
||||
task.edit((task) => {
|
||||
task.set("text", text);
|
||||
task.set("done", false);
|
||||
});
|
||||
|
||||
console.log("Created task", task.id, task.toJSON());
|
||||
|
||||
const listAfter = list.edit((list) => {
|
||||
list.edit((list) => {
|
||||
list.set(task.id, true);
|
||||
});
|
||||
|
||||
console.log("Updated list", listAfter.toJSON());
|
||||
};
|
||||
|
||||
const { toast } = useToast();
|
||||
|
||||
return (
|
||||
<div className="max-w-full w-4xl">
|
||||
<div className="flex justify-between items-center gap-4 mb-4">
|
||||
@@ -131,25 +126,7 @@ export function TodoList({ listId }: { listId: CoID<TodoList> }) {
|
||||
<Skeleton className="mt-1 w-[200px] h-[1em] rounded-full" />
|
||||
)}
|
||||
</h1>
|
||||
{list && list.coValue.getTeam().myRole() === "admin" && <Button
|
||||
size="sm"
|
||||
className="py-0"
|
||||
disabled={!list}
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
if (list) {
|
||||
const inviteLink = createInviteLink(list, "writer");
|
||||
navigator.clipboard.writeText(inviteLink).then(() =>
|
||||
toast({
|
||||
description:
|
||||
"Copied invite link to clipboard!",
|
||||
})
|
||||
);
|
||||
}
|
||||
}}
|
||||
>
|
||||
Invite
|
||||
</Button>}
|
||||
{list && <InviteButton list={list} />}
|
||||
</div>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
@@ -218,11 +195,56 @@ function TaskRow({ taskId }: { taskId: CoID<Task> }) {
|
||||
function NameBadge({ accountID }: { accountID?: AccountID }) {
|
||||
const profile = useProfile({ accountID });
|
||||
|
||||
const theme = window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||
? "dark"
|
||||
: "light";
|
||||
|
||||
const brightColor = uniqolor(accountID || "", { lightness: 80 }).color;
|
||||
const darkColor = uniqolor(accountID || "", { lightness: 20 }).color;
|
||||
|
||||
return (
|
||||
<span className="rounded-full bg-neutral-200 dark:bg-neutral-600 py-0.5 px-2 text-xs text-neutral-500 dark:text-neutral-300">
|
||||
<span
|
||||
className="rounded-full py-0.5 px-2 text-xs"
|
||||
style={{
|
||||
color: theme == "light" ? darkColor : brightColor,
|
||||
background: theme == "light" ? brightColor : darkColor,
|
||||
}}
|
||||
>
|
||||
{profile?.get("name") || "..."}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
function InviteButton({ list }: { list: TodoList }) {
|
||||
const [existingInviteLink, setExistingInviteLink] = useState<string>();
|
||||
const { toast } = useToast();
|
||||
|
||||
return (
|
||||
list.coValue.getGroup().myRole() === "admin" && (
|
||||
<Button
|
||||
size="sm"
|
||||
className="py-0"
|
||||
disabled={!list}
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
let inviteLink = existingInviteLink;
|
||||
if (list && !inviteLink) {
|
||||
inviteLink = createInviteLink(list, "writer");
|
||||
setExistingInviteLink(inviteLink);
|
||||
}
|
||||
if (inviteLink) {
|
||||
navigator.clipboard.writeText(inviteLink).then(() =>
|
||||
toast({
|
||||
description: "Copied invite link to clipboard!",
|
||||
})
|
||||
);
|
||||
}
|
||||
}}
|
||||
>
|
||||
Invite
|
||||
</Button>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
|
||||
@@ -11,9 +11,10 @@ import { Toaster } from "./components/ui/toaster.tsx";
|
||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||
<React.StrictMode>
|
||||
<ThemeProvider>
|
||||
<div className="flex items-center gap-2 justify-center mt-5"><img src="jazz-logo.png" className="h-5"/> Jazz Todo List Example</div>
|
||||
<WithJazz
|
||||
auth={LocalAuth({
|
||||
appName: "Todo List Example",
|
||||
appName: "Jazz Todo List Example",
|
||||
Component: PrettyAuthComponent,
|
||||
})}
|
||||
syncAddress={
|
||||
|
||||
@@ -27,7 +27,7 @@ THIS IS WORK IN PROGRESS
|
||||
|
||||
### `Collaborative` Values
|
||||
- CoMap (`string` → `Immutable`, last-writer-wins per key)
|
||||
- Team (`AgentID` → `Role`)
|
||||
- Group (`AgentID` → `Role`)
|
||||
- CoList (`Immutable[]`, addressable positions, insertAfter semantics)
|
||||
- Agent (`{signerID, sealerID}[]`)
|
||||
- CoStream (independent per-session streams of `Immutable`s)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"types": "dist/index.d.ts",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"version": "0.0.23",
|
||||
"version": "0.1.0",
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.5.3",
|
||||
"@typescript-eslint/eslint-plugin": "^6.2.1",
|
||||
|
||||
@@ -19,14 +19,14 @@ test("Can create a node while creating a new account with profile", async () =>
|
||||
);
|
||||
});
|
||||
|
||||
test("A node with an account can create teams and and objects within them", async () => {
|
||||
test("A node with an account can create groups and and objects within them", async () => {
|
||||
const { node, accountID } =
|
||||
LocalNode.withNewlyCreatedAccount("Hermes Puggington");
|
||||
|
||||
const team = await node.createTeam();
|
||||
expect(team).not.toBeNull();
|
||||
const group = await node.createGroup();
|
||||
expect(group).not.toBeNull();
|
||||
|
||||
let map = team.createMap();
|
||||
let map = group.createMap();
|
||||
map = map.edit((edit) => {
|
||||
edit.set("foo", "bar", "private");
|
||||
expect(edit.get("foo")).toEqual("bar");
|
||||
@@ -41,10 +41,10 @@ test("Can create account with one node, and then load it on another", async () =
|
||||
const { node, accountID, accountSecret } =
|
||||
LocalNode.withNewlyCreatedAccount("Hermes Puggington");
|
||||
|
||||
const team = await node.createTeam();
|
||||
expect(team).not.toBeNull();
|
||||
const group = await node.createGroup();
|
||||
expect(group).not.toBeNull();
|
||||
|
||||
let map = team.createMap();
|
||||
let map = group.createMap();
|
||||
map = map.edit((edit) => {
|
||||
edit.set("foo", "bar", "private");
|
||||
expect(edit.get("foo")).toEqual("bar");
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
} from "./crypto.js";
|
||||
import { AgentID } from "./ids.js";
|
||||
import { CoMap, LocalNode } from "./index.js";
|
||||
import { Team, TeamContent } from "./team.js";
|
||||
import { Group, GroupContent } from "./group.js";
|
||||
|
||||
export function accountHeaderForInitialAgentSecret(
|
||||
agentSecret: AgentSecret
|
||||
@@ -22,7 +22,7 @@ export function accountHeaderForInitialAgentSecret(
|
||||
const agent = getAgentID(agentSecret);
|
||||
return {
|
||||
type: "comap",
|
||||
ruleset: { type: "team", initialAdmin: agent },
|
||||
ruleset: { type: "group", initialAdmin: agent },
|
||||
meta: {
|
||||
type: "account",
|
||||
},
|
||||
@@ -31,13 +31,13 @@ export function accountHeaderForInitialAgentSecret(
|
||||
};
|
||||
}
|
||||
|
||||
export class Account extends Team {
|
||||
export class Account extends Group {
|
||||
get id(): AccountID {
|
||||
return this.teamMap.id as AccountID;
|
||||
return this.groupMap.id as AccountID;
|
||||
}
|
||||
|
||||
getCurrentAgentID(): AgentID {
|
||||
const agents = this.teamMap
|
||||
const agents = this.groupMap
|
||||
.keys()
|
||||
.filter((k): k is AgentID => k.startsWith("sealer_"));
|
||||
|
||||
@@ -70,10 +70,10 @@ export class ControlledAccount
|
||||
|
||||
constructor(
|
||||
agentSecret: AgentSecret,
|
||||
teamMap: CoMap<AccountContent, AccountMeta>,
|
||||
groupMap: CoMap<AccountContent, AccountMeta>,
|
||||
node: LocalNode
|
||||
) {
|
||||
super(teamMap, node);
|
||||
super(groupMap, node);
|
||||
|
||||
this.agentSecret = agentSecret;
|
||||
}
|
||||
@@ -133,7 +133,7 @@ export class AnonymousControlledAccount
|
||||
}
|
||||
}
|
||||
|
||||
export type AccountContent = TeamContent & { profile: CoID<Profile> };
|
||||
export type AccountContent = GroupContent & { profile: CoID<Profile> };
|
||||
export type AccountMeta = { type: "account" };
|
||||
export type AccountID = CoID<CoMap<AccountContent, AccountMeta>>;
|
||||
|
||||
|
||||
@@ -125,17 +125,17 @@ test("transactions with correctly signed, but wrong hash are rejected", () => {
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
test("New transactions in a team correctly update owned values, including subscriptions", async () => {
|
||||
test("New transactions in a group correctly update owned values, including subscriptions", async () => {
|
||||
const [account, sessionID] = randomAnonymousAccountAndSessionID();
|
||||
const node = new LocalNode(account, sessionID);
|
||||
|
||||
const team = node.createTeam();
|
||||
const group = node.createGroup();
|
||||
|
||||
const timeBeforeEdit = Date.now();
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
||||
|
||||
let map = team.createMap();
|
||||
let map = group.createMap();
|
||||
|
||||
let mapAfterEdit = map.edit((map) => {
|
||||
map.set("hello", "world");
|
||||
@@ -159,7 +159,7 @@ test("New transactions in a team correctly update owned values, including subscr
|
||||
]
|
||||
} satisfies Transaction;
|
||||
|
||||
const { expectedNewHash } = team.teamMap.coValue.expectedNewHashAfter(sessionID, [
|
||||
const { expectedNewHash } = group.groupMap.coValue.expectedNewHashAfter(sessionID, [
|
||||
resignationThatWeJustLearnedAbout,
|
||||
]);
|
||||
|
||||
@@ -170,7 +170,7 @@ test("New transactions in a team correctly update owned values, including subscr
|
||||
|
||||
expect(map.coValue.getValidSortedTransactions().length).toBe(1);
|
||||
|
||||
const manuallyAdddedTxSuccess = team.teamMap.coValue.tryAddTransactions(node.ownSessionID, [resignationThatWeJustLearnedAbout], expectedNewHash, signature);
|
||||
const manuallyAdddedTxSuccess = group.groupMap.coValue.tryAddTransactions(node.ownSessionID, [resignationThatWeJustLearnedAbout], expectedNewHash, signature);
|
||||
|
||||
expect(manuallyAdddedTxSuccess).toBe(true);
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ import {
|
||||
determineValidTransactions,
|
||||
isKeyForKeyField,
|
||||
} from "./permissions.js";
|
||||
import { Team, expectTeamContent } from "./team.js";
|
||||
import { Group, expectGroupContent } from "./group.js";
|
||||
import { LocalNode } from "./node.js";
|
||||
import { CoValueKnownState, NewContentMessage } from "./sync.js";
|
||||
import { RawCoID, SessionID, TransactionID } from "./ids.js";
|
||||
@@ -106,10 +106,10 @@ export class CoValue {
|
||||
this._sessions = internalInitSessions;
|
||||
this.node = node;
|
||||
|
||||
if (header.ruleset.type == "ownedByTeam") {
|
||||
if (header.ruleset.type == "ownedByGroup") {
|
||||
this.node
|
||||
.expectCoValueLoaded(header.ruleset.team)
|
||||
.subscribe((_teamUpdate) => {
|
||||
.expectCoValueLoaded(header.ruleset.group)
|
||||
.subscribe((_groupUpdate) => {
|
||||
this._cachedContent = undefined;
|
||||
const newContent = this.getCurrentContent();
|
||||
for (const listener of this.listeners) {
|
||||
@@ -385,8 +385,8 @@ export class CoValue {
|
||||
}
|
||||
|
||||
getCurrentReadKey(): { secret: KeySecret | undefined; id: KeyID } {
|
||||
if (this.header.ruleset.type === "team") {
|
||||
const content = expectTeamContent(this.getCurrentContent());
|
||||
if (this.header.ruleset.type === "group") {
|
||||
const content = expectGroupContent(this.getCurrentContent());
|
||||
|
||||
const currentKeyId = content.get("readKey");
|
||||
|
||||
@@ -400,20 +400,20 @@ export class CoValue {
|
||||
secret: secret,
|
||||
id: currentKeyId,
|
||||
};
|
||||
} else if (this.header.ruleset.type === "ownedByTeam") {
|
||||
} else if (this.header.ruleset.type === "ownedByGroup") {
|
||||
return this.node
|
||||
.expectCoValueLoaded(this.header.ruleset.team)
|
||||
.expectCoValueLoaded(this.header.ruleset.group)
|
||||
.getCurrentReadKey();
|
||||
} else {
|
||||
throw new Error(
|
||||
"Only teams or values owned by teams have read secrets"
|
||||
"Only groups or values owned by groups have read secrets"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
getReadKey(keyID: KeyID): KeySecret | undefined {
|
||||
if (this.header.ruleset.type === "team") {
|
||||
const content = expectTeamContent(this.getCurrentContent());
|
||||
if (this.header.ruleset.type === "group") {
|
||||
const content = expectGroupContent(this.getCurrentContent());
|
||||
|
||||
// Try to find key revelation for us
|
||||
|
||||
@@ -477,26 +477,26 @@ export class CoValue {
|
||||
}
|
||||
|
||||
return undefined;
|
||||
} else if (this.header.ruleset.type === "ownedByTeam") {
|
||||
} else if (this.header.ruleset.type === "ownedByGroup") {
|
||||
return this.node
|
||||
.expectCoValueLoaded(this.header.ruleset.team)
|
||||
.expectCoValueLoaded(this.header.ruleset.group)
|
||||
.getReadKey(keyID);
|
||||
} else {
|
||||
throw new Error(
|
||||
"Only teams or values owned by teams have read secrets"
|
||||
"Only groups or values owned by groups have read secrets"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
getTeam(): Team {
|
||||
if (this.header.ruleset.type !== "ownedByTeam") {
|
||||
throw new Error("Only values owned by teams have teams");
|
||||
getGroup(): Group {
|
||||
if (this.header.ruleset.type !== "ownedByGroup") {
|
||||
throw new Error("Only values owned by groups have groups");
|
||||
}
|
||||
|
||||
return new Team(
|
||||
expectTeamContent(
|
||||
return new Group(
|
||||
expectGroupContent(
|
||||
this.node
|
||||
.expectCoValueLoaded(this.header.ruleset.team)
|
||||
.expectCoValueLoaded(this.header.ruleset.group)
|
||||
.getCurrentContent()
|
||||
),
|
||||
this.node
|
||||
@@ -553,12 +553,12 @@ export class CoValue {
|
||||
}
|
||||
|
||||
getDependedOnCoValues(): RawCoID[] {
|
||||
return this.header.ruleset.type === "team"
|
||||
? expectTeamContent(this.getCurrentContent())
|
||||
return this.header.ruleset.type === "group"
|
||||
? expectGroupContent(this.getCurrentContent())
|
||||
.keys()
|
||||
.filter((k): k is AccountID => k.startsWith("co_"))
|
||||
: this.header.ruleset.type === "ownedByTeam"
|
||||
? [this.header.ruleset.team]
|
||||
: this.header.ruleset.type === "ownedByGroup"
|
||||
? [this.header.ruleset.group]
|
||||
: [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
import { Role } from "./permissions.js";
|
||||
import { base58 } from "@scure/base";
|
||||
|
||||
export type TeamContent = {
|
||||
export type GroupContent = {
|
||||
profile: CoID<Profile> | null;
|
||||
[key: AccountIDOrAgentID]: Role;
|
||||
readKey: KeyID;
|
||||
@@ -36,34 +36,34 @@ export type TeamContent = {
|
||||
>;
|
||||
};
|
||||
|
||||
export function expectTeamContent(
|
||||
export function expectGroupContent(
|
||||
content: ContentType
|
||||
): CoMap<TeamContent, JsonObject | null> {
|
||||
): CoMap<GroupContent, JsonObject | null> {
|
||||
if (content.type !== "comap") {
|
||||
throw new Error("Expected map");
|
||||
}
|
||||
|
||||
return content as CoMap<TeamContent, JsonObject | null>;
|
||||
return content as CoMap<GroupContent, JsonObject | null>;
|
||||
}
|
||||
|
||||
export class Team {
|
||||
teamMap: CoMap<TeamContent, JsonObject | null>;
|
||||
export class Group {
|
||||
groupMap: CoMap<GroupContent, JsonObject | null>;
|
||||
node: LocalNode;
|
||||
|
||||
constructor(
|
||||
teamMap: CoMap<TeamContent, JsonObject | null>,
|
||||
groupMap: CoMap<GroupContent, JsonObject | null>,
|
||||
node: LocalNode
|
||||
) {
|
||||
this.teamMap = teamMap;
|
||||
this.groupMap = groupMap;
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
get id(): CoID<CoMap<TeamContent, JsonObject | null>> {
|
||||
return this.teamMap.id;
|
||||
get id(): CoID<CoMap<GroupContent, JsonObject | null>> {
|
||||
return this.groupMap.id;
|
||||
}
|
||||
|
||||
roleOf(accountID: AccountIDOrAgentID): Role | undefined {
|
||||
return this.teamMap.get(accountID);
|
||||
return this.groupMap.get(accountID);
|
||||
}
|
||||
|
||||
myRole(): Role | undefined {
|
||||
@@ -71,8 +71,8 @@ export class Team {
|
||||
}
|
||||
|
||||
addMember(accountID: AccountIDOrAgentID, role: Role) {
|
||||
this.teamMap = this.teamMap.edit((map) => {
|
||||
const currentReadKey = this.teamMap.coValue.getCurrentReadKey();
|
||||
this.groupMap = this.groupMap.edit((map) => {
|
||||
const currentReadKey = this.groupMap.coValue.getCurrentReadKey();
|
||||
|
||||
if (!currentReadKey.secret) {
|
||||
throw new Error("Can't add member without read key secret");
|
||||
@@ -80,7 +80,7 @@ export class Team {
|
||||
|
||||
const agent = this.node.resolveAccountAgent(
|
||||
accountID,
|
||||
"Expected to know agent to add them to team"
|
||||
"Expected to know agent to add them to group"
|
||||
);
|
||||
|
||||
map.set(accountID, role, "trusting");
|
||||
@@ -93,11 +93,11 @@ export class Team {
|
||||
`${currentReadKey.id}_for_${accountID}`,
|
||||
seal(
|
||||
currentReadKey.secret,
|
||||
this.teamMap.coValue.node.account.currentSealerSecret(),
|
||||
this.groupMap.coValue.node.account.currentSealerSecret(),
|
||||
getAgentSealerID(agent),
|
||||
{
|
||||
in: this.teamMap.coValue.id,
|
||||
tx: this.teamMap.coValue.nextTransactionID(),
|
||||
in: this.groupMap.coValue.id,
|
||||
tx: this.groupMap.coValue.nextTransactionID(),
|
||||
}
|
||||
),
|
||||
"trusting"
|
||||
@@ -117,9 +117,9 @@ export class Team {
|
||||
}
|
||||
|
||||
rotateReadKey() {
|
||||
const currentlyPermittedReaders = this.teamMap.keys().filter((key) => {
|
||||
const currentlyPermittedReaders = this.groupMap.keys().filter((key) => {
|
||||
if (key.startsWith("co_") || isAgentID(key)) {
|
||||
const role = this.teamMap.get(key);
|
||||
const role = this.groupMap.get(key);
|
||||
return (
|
||||
role === "admin" || role === "writer" || role === "reader"
|
||||
);
|
||||
@@ -128,7 +128,7 @@ export class Team {
|
||||
}
|
||||
}) as AccountIDOrAgentID[];
|
||||
|
||||
const maybeCurrentReadKey = this.teamMap.coValue.getCurrentReadKey();
|
||||
const maybeCurrentReadKey = this.groupMap.coValue.getCurrentReadKey();
|
||||
|
||||
if (!maybeCurrentReadKey.secret) {
|
||||
throw new Error(
|
||||
@@ -143,7 +143,7 @@ export class Team {
|
||||
|
||||
const newReadKey = newRandomKeySecret();
|
||||
|
||||
this.teamMap = this.teamMap.edit((map) => {
|
||||
this.groupMap = this.groupMap.edit((map) => {
|
||||
for (const readerID of currentlyPermittedReaders) {
|
||||
const reader = this.node.resolveAccountAgent(
|
||||
readerID,
|
||||
@@ -154,11 +154,11 @@ export class Team {
|
||||
`${newReadKey.id}_for_${readerID}`,
|
||||
seal(
|
||||
newReadKey.secret,
|
||||
this.teamMap.coValue.node.account.currentSealerSecret(),
|
||||
this.groupMap.coValue.node.account.currentSealerSecret(),
|
||||
getAgentSealerID(reader),
|
||||
{
|
||||
in: this.teamMap.coValue.id,
|
||||
tx: this.teamMap.coValue.nextTransactionID(),
|
||||
in: this.groupMap.coValue.id,
|
||||
tx: this.groupMap.coValue.nextTransactionID(),
|
||||
}
|
||||
),
|
||||
"trusting"
|
||||
@@ -179,7 +179,7 @@ export class Team {
|
||||
}
|
||||
|
||||
removeMember(accountID: AccountIDOrAgentID) {
|
||||
this.teamMap = this.teamMap.edit((map) => {
|
||||
this.groupMap = this.groupMap.edit((map) => {
|
||||
map.set(accountID, "revoked", "trusting");
|
||||
});
|
||||
|
||||
@@ -194,8 +194,8 @@ export class Team {
|
||||
.createCoValue({
|
||||
type: "comap",
|
||||
ruleset: {
|
||||
type: "ownedByTeam",
|
||||
team: this.teamMap.id,
|
||||
type: "ownedByGroup",
|
||||
group: this.groupMap.id,
|
||||
},
|
||||
meta: meta || null,
|
||||
...createdNowUnique(),
|
||||
@@ -206,10 +206,10 @@ export class Team {
|
||||
testWithDifferentAccount(
|
||||
account: GeneralizedControlledAccount,
|
||||
sessionId: SessionID
|
||||
): Team {
|
||||
return new Team(
|
||||
expectTeamContent(
|
||||
this.teamMap.coValue
|
||||
): Group {
|
||||
return new Group(
|
||||
expectGroupContent(
|
||||
this.groupMap.coValue
|
||||
.testWithDifferentAccount(account, sessionId)
|
||||
.getCurrentContent()
|
||||
),
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
import { connectedPeers } from "./streamUtils.js";
|
||||
import { AnonymousControlledAccount, ControlledAccount } from "./account.js";
|
||||
import { rawCoIDtoBytes, rawCoIDfromBytes } from "./ids.js";
|
||||
import { Team, expectTeamContent } from "./team.js"
|
||||
import { Group, expectGroupContent } from "./group.js"
|
||||
|
||||
import type { SessionID, AgentID } from "./ids.js";
|
||||
import type { CoID, ContentType } from "./contentType.js";
|
||||
@@ -27,7 +27,7 @@ import type {
|
||||
ProfileContent,
|
||||
Profile,
|
||||
} from "./account.js";
|
||||
import type { InviteSecret } from "./team.js";
|
||||
import type { InviteSecret } from "./group.js";
|
||||
|
||||
type Value = JsonValue | ContentType;
|
||||
|
||||
@@ -44,7 +44,7 @@ export const cojsonInternals = {
|
||||
agentSecretFromSecretSeed,
|
||||
secretSeedLength,
|
||||
shortHashLength,
|
||||
expectTeamContent
|
||||
expectGroupContent
|
||||
};
|
||||
|
||||
export {
|
||||
@@ -53,7 +53,7 @@ export {
|
||||
CoMap,
|
||||
AnonymousControlledAccount,
|
||||
ControlledAccount,
|
||||
Team
|
||||
Group
|
||||
};
|
||||
|
||||
export type {
|
||||
|
||||
@@ -12,11 +12,11 @@ import {
|
||||
import { CoValue, CoValueHeader, newRandomSessionID } from "./coValue.js";
|
||||
import {
|
||||
InviteSecret,
|
||||
Team,
|
||||
TeamContent,
|
||||
expectTeamContent,
|
||||
Group,
|
||||
GroupContent,
|
||||
expectGroupContent,
|
||||
secretSeedFromInviteSecret,
|
||||
} from "./team.js";
|
||||
} from "./group.js";
|
||||
import { Peer, SyncManager } from "./sync.js";
|
||||
import { AgentID, RawCoID, SessionID, isAgentID } from "./ids.js";
|
||||
import { CoID, ContentType } from "./contentType.js";
|
||||
@@ -151,23 +151,23 @@ export class LocalNode {
|
||||
}
|
||||
|
||||
async acceptInvite<T extends ContentType>(
|
||||
teamOrOwnedValueID: CoID<T>,
|
||||
groupOrOwnedValueID: CoID<T>,
|
||||
inviteSecret: InviteSecret
|
||||
): Promise<void> {
|
||||
const teamOrOwnedValue = await this.load(teamOrOwnedValueID);
|
||||
const groupOrOwnedValue = await this.load(groupOrOwnedValueID);
|
||||
|
||||
if (teamOrOwnedValue.coValue.header.ruleset.type === "ownedByTeam") {
|
||||
if (groupOrOwnedValue.coValue.header.ruleset.type === "ownedByGroup") {
|
||||
return this.acceptInvite(
|
||||
teamOrOwnedValue.coValue.header.ruleset.team as CoID<
|
||||
CoMap<TeamContent>
|
||||
groupOrOwnedValue.coValue.header.ruleset.group as CoID<
|
||||
CoMap<GroupContent>
|
||||
>,
|
||||
inviteSecret
|
||||
);
|
||||
} else if (teamOrOwnedValue.coValue.header.ruleset.type !== "team") {
|
||||
throw new Error("Can only accept invites to teams");
|
||||
} else if (groupOrOwnedValue.coValue.header.ruleset.type !== "group") {
|
||||
throw new Error("Can only accept invites to groups");
|
||||
}
|
||||
|
||||
const team = new Team(expectTeamContent(teamOrOwnedValue), this);
|
||||
const group = new Group(expectGroupContent(groupOrOwnedValue), this);
|
||||
|
||||
const inviteAgentSecret = agentSecretFromSecretSeed(
|
||||
secretSeedFromInviteSecret(inviteSecret)
|
||||
@@ -175,8 +175,8 @@ export class LocalNode {
|
||||
const inviteAgentID = getAgentID(inviteAgentSecret);
|
||||
|
||||
const invitationRole = await new Promise((resolve, reject) => {
|
||||
team.teamMap.subscribe((teamMap) => {
|
||||
const role = teamMap.get(inviteAgentID);
|
||||
group.groupMap.subscribe((groupMap) => {
|
||||
const role = groupMap.get(inviteAgentID);
|
||||
if (role) {
|
||||
resolve(role);
|
||||
}
|
||||
@@ -194,7 +194,7 @@ export class LocalNode {
|
||||
throw new Error("No invitation found");
|
||||
}
|
||||
|
||||
const existingRole = team.teamMap.get(this.account.id);
|
||||
const existingRole = group.groupMap.get(this.account.id);
|
||||
|
||||
if (
|
||||
existingRole === "admin" ||
|
||||
@@ -204,12 +204,12 @@ export class LocalNode {
|
||||
return;
|
||||
}
|
||||
|
||||
const teamAsInvite = team.testWithDifferentAccount(
|
||||
const groupAsInvite = group.testWithDifferentAccount(
|
||||
new AnonymousControlledAccount(inviteAgentSecret),
|
||||
newRandomSessionID(inviteAgentID)
|
||||
);
|
||||
|
||||
teamAsInvite.addMember(
|
||||
groupAsInvite.addMember(
|
||||
this.account.id,
|
||||
invitationRole === "adminInvite"
|
||||
? "admin"
|
||||
@@ -218,8 +218,12 @@ export class LocalNode {
|
||||
: "reader"
|
||||
);
|
||||
|
||||
team.teamMap.coValue._sessions = teamAsInvite.teamMap.coValue.sessions;
|
||||
team.teamMap.coValue._cachedContent = undefined;
|
||||
group.groupMap.coValue._sessions = groupAsInvite.groupMap.coValue.sessions;
|
||||
group.groupMap.coValue._cachedContent = undefined;
|
||||
|
||||
for (const groupListener of group.groupMap.coValue.listeners) {
|
||||
groupListener(group.groupMap.coValue.getCurrentContent());
|
||||
}
|
||||
}
|
||||
|
||||
expectCoValueLoaded(id: RawCoID, expectation?: string): CoValue {
|
||||
@@ -241,7 +245,7 @@ export class LocalNode {
|
||||
|
||||
expectProfileLoaded(id: AccountID, expectation?: string): Profile {
|
||||
const account = this.expectCoValueLoaded(id, expectation);
|
||||
const profileID = expectTeamContent(account.getCurrentContent()).get(
|
||||
const profileID = expectGroupContent(account.getCurrentContent()).get(
|
||||
"profile"
|
||||
);
|
||||
if (!profileID) {
|
||||
@@ -268,12 +272,12 @@ export class LocalNode {
|
||||
newRandomSessionID(getAgentID(agentSecret))
|
||||
);
|
||||
|
||||
const accountAsTeam = new Team(
|
||||
expectTeamContent(account.getCurrentContent()),
|
||||
const accountAsGroup = new Group(
|
||||
expectGroupContent(account.getCurrentContent()),
|
||||
account.node
|
||||
);
|
||||
|
||||
accountAsTeam.teamMap.edit((editable) => {
|
||||
accountAsGroup.groupMap.edit((editable) => {
|
||||
editable.set(getAgentID(agentSecret), "admin", "trusting");
|
||||
|
||||
const readKey = newRandomKeySecret();
|
||||
@@ -301,7 +305,7 @@ export class LocalNode {
|
||||
account.node
|
||||
);
|
||||
|
||||
const profile = accountAsTeam.createMap<ProfileContent, ProfileMeta>({
|
||||
const profile = accountAsGroup.createMap<ProfileContent, ProfileMeta>({
|
||||
type: "profile",
|
||||
});
|
||||
|
||||
@@ -309,13 +313,13 @@ export class LocalNode {
|
||||
editable.set("name", name, "trusting");
|
||||
});
|
||||
|
||||
accountAsTeam.teamMap.edit((editable) => {
|
||||
accountAsGroup.groupMap.edit((editable) => {
|
||||
editable.set("profile", profile.id, "trusting");
|
||||
});
|
||||
|
||||
const accountOnThisNode = this.expectCoValueLoaded(account.id);
|
||||
|
||||
accountOnThisNode._sessions = {...accountAsTeam.teamMap.coValue.sessions};
|
||||
accountOnThisNode._sessions = {...accountAsGroup.groupMap.coValue.sessions};
|
||||
accountOnThisNode._cachedContent = undefined;
|
||||
|
||||
return controlledAccount;
|
||||
@@ -330,7 +334,7 @@ export class LocalNode {
|
||||
|
||||
if (
|
||||
coValue.header.type !== "comap" ||
|
||||
coValue.header.ruleset.type !== "team" ||
|
||||
coValue.header.ruleset.type !== "group" ||
|
||||
!coValue.header.meta ||
|
||||
!("type" in coValue.header.meta) ||
|
||||
coValue.header.meta.type !== "account"
|
||||
@@ -343,22 +347,22 @@ export class LocalNode {
|
||||
}
|
||||
|
||||
return new Account(
|
||||
coValue.getCurrentContent() as CoMap<TeamContent, AccountMeta>,
|
||||
coValue.getCurrentContent() as CoMap<GroupContent, AccountMeta>,
|
||||
this
|
||||
).getCurrentAgentID();
|
||||
}
|
||||
|
||||
createTeam(): Team {
|
||||
const teamCoValue = this.createCoValue({
|
||||
createGroup(): Group {
|
||||
const groupCoValue = this.createCoValue({
|
||||
type: "comap",
|
||||
ruleset: { type: "team", initialAdmin: this.account.id },
|
||||
ruleset: { type: "group", initialAdmin: this.account.id },
|
||||
meta: null,
|
||||
...createdNowUnique(),
|
||||
});
|
||||
|
||||
let teamContent = expectTeamContent(teamCoValue.getCurrentContent());
|
||||
let groupContent = expectGroupContent(groupCoValue.getCurrentContent());
|
||||
|
||||
teamContent = teamContent.edit((editable) => {
|
||||
groupContent = groupContent.edit((editable) => {
|
||||
editable.set(this.account.id, "admin", "trusting");
|
||||
|
||||
const readKey = newRandomKeySecret();
|
||||
@@ -370,8 +374,8 @@ export class LocalNode {
|
||||
this.account.currentSealerSecret(),
|
||||
this.account.currentSealerID(),
|
||||
{
|
||||
in: teamCoValue.id,
|
||||
tx: teamCoValue.nextTransactionID(),
|
||||
in: groupCoValue.id,
|
||||
tx: groupCoValue.nextTransactionID(),
|
||||
}
|
||||
),
|
||||
"trusting"
|
||||
@@ -380,7 +384,7 @@ export class LocalNode {
|
||||
editable.set("readKey", readKey.id, "trusting");
|
||||
});
|
||||
|
||||
return new Team(teamContent, this);
|
||||
return new Group(groupContent, this);
|
||||
}
|
||||
|
||||
testWithDifferentAccount(
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,8 +17,8 @@ import {
|
||||
} from "./account.js";
|
||||
|
||||
export type PermissionsDef =
|
||||
| { type: "team"; initialAdmin: AccountIDOrAgentID }
|
||||
| { type: "ownedByTeam"; team: RawCoID }
|
||||
| { type: "group"; initialAdmin: AccountIDOrAgentID }
|
||||
| { type: "ownedByGroup"; group: RawCoID }
|
||||
| { type: "unsafeAllowAll" };
|
||||
|
||||
export type Role =
|
||||
@@ -33,7 +33,7 @@ export type Role =
|
||||
export function determineValidTransactions(
|
||||
coValue: CoValue
|
||||
): { txID: TransactionID; tx: Transaction }[] {
|
||||
if (coValue.header.ruleset.type === "team") {
|
||||
if (coValue.header.ruleset.type === "group") {
|
||||
const allTrustingTransactionsSorted = Object.entries(
|
||||
coValue.sessions
|
||||
).flatMap(([sessionID, sessionLog]) => {
|
||||
@@ -43,7 +43,7 @@ export function determineValidTransactions(
|
||||
if (tx.privacy === "trusting") {
|
||||
return true;
|
||||
} else {
|
||||
console.warn("Unexpected private transaction in Team");
|
||||
console.warn("Unexpected private transaction in Group");
|
||||
return false;
|
||||
}
|
||||
}) as {
|
||||
@@ -60,7 +60,7 @@ export function determineValidTransactions(
|
||||
const initialAdmin = coValue.header.ruleset.initialAdmin;
|
||||
|
||||
if (!initialAdmin) {
|
||||
throw new Error("Team must have initialAdmin");
|
||||
throw new Error("Group must have initialAdmin");
|
||||
}
|
||||
|
||||
const memberState: { [agent: AccountIDOrAgentID]: Role } = {};
|
||||
@@ -81,12 +81,12 @@ export function determineValidTransactions(
|
||||
| MapOpPayload<"readKey", JsonValue>
|
||||
| MapOpPayload<"profile", CoID<Profile>>;
|
||||
if (tx.changes.length !== 1) {
|
||||
console.warn("Team transaction must have exactly one change");
|
||||
console.warn("Group transaction must have exactly one change");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (change.op !== "set") {
|
||||
console.warn("Team transaction must set a role or readKey");
|
||||
console.warn("Group transaction must set a role or readKey");
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ export function determineValidTransactions(
|
||||
change.value !== "writerInvite" &&
|
||||
change.value !== "readerInvite"
|
||||
) {
|
||||
console.warn("Team transaction must set a valid role");
|
||||
console.warn("Group transaction must set a valid role");
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -176,7 +176,7 @@ export function determineValidTransactions(
|
||||
}
|
||||
} else {
|
||||
console.warn(
|
||||
"Team transaction must be made by current admin or invite"
|
||||
"Group transaction must be made by current admin or invite"
|
||||
);
|
||||
continue;
|
||||
}
|
||||
@@ -189,16 +189,16 @@ export function determineValidTransactions(
|
||||
}
|
||||
|
||||
return validTransactions;
|
||||
} else if (coValue.header.ruleset.type === "ownedByTeam") {
|
||||
const teamContent = coValue.node
|
||||
} else if (coValue.header.ruleset.type === "ownedByGroup") {
|
||||
const groupContent = coValue.node
|
||||
.expectCoValueLoaded(
|
||||
coValue.header.ruleset.team,
|
||||
"Determining valid transaction in owned object but its team wasn't loaded"
|
||||
coValue.header.ruleset.group,
|
||||
"Determining valid transaction in owned object but its group wasn't loaded"
|
||||
)
|
||||
.getCurrentContent();
|
||||
|
||||
if (teamContent.type !== "comap") {
|
||||
throw new Error("Team must be a map");
|
||||
if (groupContent.type !== "comap") {
|
||||
throw new Error("Group must be a map");
|
||||
}
|
||||
|
||||
return Object.entries(coValue.sessions).flatMap(
|
||||
@@ -208,7 +208,7 @@ export function determineValidTransactions(
|
||||
);
|
||||
return sessionLog.transactions
|
||||
.filter((tx) => {
|
||||
const transactorRoleAtTxTime = teamContent.getAtTime(
|
||||
const transactorRoleAtTxTime = groupContent.getAtTime(
|
||||
transactor,
|
||||
tx.madeAt
|
||||
);
|
||||
|
||||
@@ -3,7 +3,7 @@ import { LocalNode } from "./node.js";
|
||||
import { Peer, PeerID, SyncMessage } from "./sync.js";
|
||||
import { expectMap } from "./contentType.js";
|
||||
import { MapOpPayload } from "./contentTypes/coMap.js";
|
||||
import { Team } from "./team.js";
|
||||
import { Group } from "./group.js";
|
||||
import {
|
||||
ReadableStream,
|
||||
WritableStream,
|
||||
@@ -23,9 +23,9 @@ test("Node replies with initial tx and header to empty subscribe", async () => {
|
||||
const [admin, session] = randomAnonymousAccountAndSessionID();
|
||||
const node = new LocalNode(admin, session);
|
||||
|
||||
const team = node.createTeam();
|
||||
const group = node.createGroup();
|
||||
|
||||
const map = team.createMap();
|
||||
const map = group.createMap();
|
||||
|
||||
map.edit((editable) => {
|
||||
editable.set("hello", "world", "trusting");
|
||||
@@ -53,7 +53,7 @@ test("Node replies with initial tx and header to empty subscribe", async () => {
|
||||
const reader = outRx.getReader();
|
||||
|
||||
// expect((await reader.read()).value).toMatchObject(admStateEx(admin.id));
|
||||
expect((await reader.read()).value).toMatchObject(teamStateEx(team));
|
||||
expect((await reader.read()).value).toMatchObject(groupStateEx(group));
|
||||
|
||||
const mapTellKnownStateMsg = await reader.read();
|
||||
expect(mapTellKnownStateMsg.value).toEqual({
|
||||
@@ -62,7 +62,7 @@ test("Node replies with initial tx and header to empty subscribe", async () => {
|
||||
} satisfies SyncMessage);
|
||||
|
||||
// expect((await reader.read()).value).toMatchObject(admContEx(admin.id));
|
||||
expect((await reader.read()).value).toMatchObject(teamContentEx(team));
|
||||
expect((await reader.read()).value).toMatchObject(groupContentEx(group));
|
||||
|
||||
const newContentMsg = await reader.read();
|
||||
|
||||
@@ -71,7 +71,7 @@ test("Node replies with initial tx and header to empty subscribe", async () => {
|
||||
id: map.coValue.id,
|
||||
header: {
|
||||
type: "comap",
|
||||
ruleset: { type: "ownedByTeam", team: team.id },
|
||||
ruleset: { type: "ownedByGroup", group: group.id },
|
||||
meta: null,
|
||||
createdAt: map.coValue.header.createdAt,
|
||||
uniqueness: map.coValue.header.uniqueness,
|
||||
@@ -104,9 +104,9 @@ test("Node replies with only new tx to subscribe with some known state", async (
|
||||
const [admin, session] = randomAnonymousAccountAndSessionID();
|
||||
const node = new LocalNode(admin, session);
|
||||
|
||||
const team = node.createTeam();
|
||||
const group = node.createGroup();
|
||||
|
||||
const map = team.createMap();
|
||||
const map = group.createMap();
|
||||
|
||||
map.edit((editable) => {
|
||||
editable.set("hello", "world", "trusting");
|
||||
@@ -137,7 +137,7 @@ test("Node replies with only new tx to subscribe with some known state", async (
|
||||
const reader = outRx.getReader();
|
||||
|
||||
// expect((await reader.read()).value).toMatchObject(admStateEx(admin.id));
|
||||
expect((await reader.read()).value).toMatchObject(teamStateEx(team));
|
||||
expect((await reader.read()).value).toMatchObject(groupStateEx(group));
|
||||
|
||||
const mapTellKnownStateMsg = await reader.read();
|
||||
expect(mapTellKnownStateMsg.value).toEqual({
|
||||
@@ -146,7 +146,7 @@ test("Node replies with only new tx to subscribe with some known state", async (
|
||||
} satisfies SyncMessage);
|
||||
|
||||
// expect((await reader.read()).value).toMatchObject(admContEx(admin.id));
|
||||
expect((await reader.read()).value).toMatchObject(teamContentEx(team));
|
||||
expect((await reader.read()).value).toMatchObject(groupContentEx(group));
|
||||
|
||||
const mapNewContentMsg = await reader.read();
|
||||
|
||||
@@ -186,9 +186,9 @@ test("After subscribing, node sends own known state and new txs to peer", async
|
||||
const [admin, session] = randomAnonymousAccountAndSessionID();
|
||||
const node = new LocalNode(admin, session);
|
||||
|
||||
const team = node.createTeam();
|
||||
const group = node.createGroup();
|
||||
|
||||
const map = team.createMap();
|
||||
const map = group.createMap();
|
||||
|
||||
const [inRx, inTx] = newStreamPair<SyncMessage>();
|
||||
const [outRx, outTx] = newStreamPair<SyncMessage>();
|
||||
@@ -214,7 +214,7 @@ test("After subscribing, node sends own known state and new txs to peer", async
|
||||
const reader = outRx.getReader();
|
||||
|
||||
// expect((await reader.read()).value).toMatchObject(admStateEx(admin.id));
|
||||
expect((await reader.read()).value).toMatchObject(teamStateEx(team));
|
||||
expect((await reader.read()).value).toMatchObject(groupStateEx(group));
|
||||
|
||||
const mapTellKnownStateMsg = await reader.read();
|
||||
expect(mapTellKnownStateMsg.value).toEqual({
|
||||
@@ -223,7 +223,7 @@ test("After subscribing, node sends own known state and new txs to peer", async
|
||||
} satisfies SyncMessage);
|
||||
|
||||
// expect((await reader.read()).value).toMatchObject(admContEx(admin.id));
|
||||
expect((await reader.read()).value).toMatchObject(teamContentEx(team));
|
||||
expect((await reader.read()).value).toMatchObject(groupContentEx(group));
|
||||
|
||||
const mapNewContentHeaderOnlyMsg = await reader.read();
|
||||
|
||||
@@ -303,9 +303,9 @@ test("Client replies with known new content to tellKnownState from server", asyn
|
||||
const [admin, session] = randomAnonymousAccountAndSessionID();
|
||||
const node = new LocalNode(admin, session);
|
||||
|
||||
const team = node.createTeam();
|
||||
const group = node.createGroup();
|
||||
|
||||
const map = team.createMap();
|
||||
const map = group.createMap();
|
||||
|
||||
map.edit((editable) => {
|
||||
editable.set("hello", "world", "trusting");
|
||||
@@ -323,7 +323,7 @@ test("Client replies with known new content to tellKnownState from server", asyn
|
||||
|
||||
const reader = outRx.getReader();
|
||||
|
||||
// expect((await reader.read()).value).toMatchObject(teamStateEx(team));
|
||||
// expect((await reader.read()).value).toMatchObject(groupStateEx(group));
|
||||
|
||||
const writer = inTx.getWriter();
|
||||
|
||||
@@ -337,7 +337,7 @@ test("Client replies with known new content to tellKnownState from server", asyn
|
||||
});
|
||||
|
||||
// expect((await reader.read()).value).toMatchObject(admStateEx(admin.id));
|
||||
expect((await reader.read()).value).toMatchObject(teamStateEx(team));
|
||||
expect((await reader.read()).value).toMatchObject(groupStateEx(group));
|
||||
|
||||
const mapTellKnownStateMsg = await reader.read();
|
||||
expect(mapTellKnownStateMsg.value).toEqual({
|
||||
@@ -346,7 +346,7 @@ test("Client replies with known new content to tellKnownState from server", asyn
|
||||
} satisfies SyncMessage);
|
||||
|
||||
// expect((await reader.read()).value).toMatchObject(admContEx(admin.id));
|
||||
expect((await reader.read()).value).toMatchObject(teamContentEx(team));
|
||||
expect((await reader.read()).value).toMatchObject(groupContentEx(group));
|
||||
|
||||
const mapNewContentMsg = await reader.read();
|
||||
|
||||
@@ -382,9 +382,9 @@ test("No matter the optimistic known state, node respects invalid known state me
|
||||
const [admin, session] = randomAnonymousAccountAndSessionID();
|
||||
const node = new LocalNode(admin, session);
|
||||
|
||||
const team = node.createTeam();
|
||||
const group = node.createGroup();
|
||||
|
||||
const map = team.createMap();
|
||||
const map = group.createMap();
|
||||
|
||||
const [inRx, inTx] = newStreamPair<SyncMessage>();
|
||||
const [outRx, outTx] = newStreamPair<SyncMessage>();
|
||||
@@ -410,7 +410,7 @@ test("No matter the optimistic known state, node respects invalid known state me
|
||||
const reader = outRx.getReader();
|
||||
|
||||
// expect((await reader.read()).value).toMatchObject(admStateEx(admin.id));
|
||||
expect((await reader.read()).value).toMatchObject(teamStateEx(team));
|
||||
expect((await reader.read()).value).toMatchObject(groupStateEx(group));
|
||||
|
||||
const mapTellKnownStateMsg = await reader.read();
|
||||
expect(mapTellKnownStateMsg.value).toEqual({
|
||||
@@ -419,7 +419,7 @@ test("No matter the optimistic known state, node respects invalid known state me
|
||||
} satisfies SyncMessage);
|
||||
|
||||
// expect((await reader.read()).value).toMatchObject(admContEx(admin.id));
|
||||
expect((await reader.read()).value).toMatchObject(teamContentEx(team));
|
||||
expect((await reader.read()).value).toMatchObject(groupContentEx(group));
|
||||
|
||||
const mapNewContentHeaderOnlyMsg = await reader.read();
|
||||
|
||||
@@ -485,9 +485,9 @@ test("If we add a peer, but it never subscribes to a coValue, it won't get any m
|
||||
const [admin, session] = randomAnonymousAccountAndSessionID();
|
||||
const node = new LocalNode(admin, session);
|
||||
|
||||
const team = node.createTeam();
|
||||
const group = node.createGroup();
|
||||
|
||||
const map = team.createMap();
|
||||
const map = group.createMap();
|
||||
|
||||
const [inRx, _inTx] = newStreamPair<SyncMessage>();
|
||||
const [outRx, outTx] = newStreamPair<SyncMessage>();
|
||||
@@ -514,9 +514,9 @@ test("If we add a server peer, all updates to all coValues are sent to it, even
|
||||
const [admin, session] = randomAnonymousAccountAndSessionID();
|
||||
const node = new LocalNode(admin, session);
|
||||
|
||||
const team = node.createTeam();
|
||||
const group = node.createGroup();
|
||||
|
||||
const map = team.createMap();
|
||||
const map = group.createMap();
|
||||
|
||||
const [inRx, _inTx] = newStreamPair<SyncMessage>();
|
||||
const [outRx, outTx] = newStreamPair<SyncMessage>();
|
||||
@@ -535,7 +535,7 @@ test("If we add a server peer, all updates to all coValues are sent to it, even
|
||||
// });
|
||||
expect((await reader.read()).value).toMatchObject({
|
||||
action: "load",
|
||||
id: team.teamMap.coValue.id,
|
||||
id: group.groupMap.coValue.id,
|
||||
});
|
||||
|
||||
const mapSubscribeMsg = await reader.read();
|
||||
@@ -552,7 +552,7 @@ test("If we add a server peer, all updates to all coValues are sent to it, even
|
||||
});
|
||||
|
||||
// expect((await reader.read()).value).toMatchObject(admContEx(admin.id));
|
||||
expect((await reader.read()).value).toMatchObject(teamContentEx(team));
|
||||
expect((await reader.read()).value).toMatchObject(groupContentEx(group));
|
||||
|
||||
const mapNewContentMsg = await reader.read();
|
||||
|
||||
@@ -588,7 +588,7 @@ test("If we add a server peer, newly created coValues are auto-subscribed to", a
|
||||
const [admin, session] = randomAnonymousAccountAndSessionID();
|
||||
const node = new LocalNode(admin, session);
|
||||
|
||||
const team = node.createTeam();
|
||||
const group = node.createGroup();
|
||||
|
||||
const [inRx, _inTx] = newStreamPair<SyncMessage>();
|
||||
const [outRx, outTx] = newStreamPair<SyncMessage>();
|
||||
@@ -607,10 +607,10 @@ test("If we add a server peer, newly created coValues are auto-subscribed to", a
|
||||
// });
|
||||
expect((await reader.read()).value).toMatchObject({
|
||||
action: "load",
|
||||
id: team.teamMap.coValue.id,
|
||||
id: group.groupMap.coValue.id,
|
||||
});
|
||||
|
||||
const map = team.createMap();
|
||||
const map = group.createMap();
|
||||
|
||||
const mapSubscribeMsg = await reader.read();
|
||||
|
||||
@@ -620,7 +620,7 @@ test("If we add a server peer, newly created coValues are auto-subscribed to", a
|
||||
} satisfies SyncMessage);
|
||||
|
||||
// expect((await reader.read()).value).toMatchObject(admContEx(adminID));
|
||||
expect((await reader.read()).value).toMatchObject(teamContentEx(team));
|
||||
expect((await reader.read()).value).toMatchObject(groupContentEx(group));
|
||||
|
||||
const mapContentMsg = await reader.read();
|
||||
|
||||
@@ -640,9 +640,9 @@ test("When we connect a new server peer, we try to sync all existing coValues to
|
||||
const [admin, session] = randomAnonymousAccountAndSessionID();
|
||||
const node = new LocalNode(admin, session);
|
||||
|
||||
const team = node.createTeam();
|
||||
const group = node.createGroup();
|
||||
|
||||
const map = team.createMap();
|
||||
const map = group.createMap();
|
||||
|
||||
const [inRx, _inTx] = newStreamPair<SyncMessage>();
|
||||
const [outRx, outTx] = newStreamPair<SyncMessage>();
|
||||
@@ -657,11 +657,11 @@ test("When we connect a new server peer, we try to sync all existing coValues to
|
||||
const reader = outRx.getReader();
|
||||
|
||||
// const _adminSubscribeMessage = await reader.read();
|
||||
const teamSubscribeMessage = await reader.read();
|
||||
const groupSubscribeMessage = await reader.read();
|
||||
|
||||
expect(teamSubscribeMessage.value).toEqual({
|
||||
expect(groupSubscribeMessage.value).toEqual({
|
||||
action: "load",
|
||||
...team.teamMap.coValue.knownState(),
|
||||
...group.groupMap.coValue.knownState(),
|
||||
} satisfies SyncMessage);
|
||||
|
||||
const secondMessage = await reader.read();
|
||||
@@ -676,9 +676,9 @@ test("When receiving a subscribe with a known state that is ahead of our own, pe
|
||||
const [admin, session] = randomAnonymousAccountAndSessionID();
|
||||
const node = new LocalNode(admin, session);
|
||||
|
||||
const team = node.createTeam();
|
||||
const group = node.createGroup();
|
||||
|
||||
const map = team.createMap();
|
||||
const map = group.createMap();
|
||||
|
||||
const [inRx, inTx] = newStreamPair<SyncMessage>();
|
||||
const [outRx, outTx] = newStreamPair<SyncMessage>();
|
||||
@@ -704,7 +704,7 @@ test("When receiving a subscribe with a known state that is ahead of our own, pe
|
||||
const reader = outRx.getReader();
|
||||
|
||||
// expect((await reader.read()).value).toMatchObject(admStateEx(admin.id));
|
||||
expect((await reader.read()).value).toMatchObject(teamStateEx(team));
|
||||
expect((await reader.read()).value).toMatchObject(groupStateEx(group));
|
||||
const mapTellKnownState = await reader.read();
|
||||
|
||||
expect(mapTellKnownState.value).toEqual({
|
||||
@@ -719,7 +719,7 @@ test.skip("When replaying creation and transactions of a coValue as new content,
|
||||
|
||||
const node1 = new LocalNode(admin, session);
|
||||
|
||||
const team = node1.createTeam();
|
||||
const group = node1.createGroup();
|
||||
|
||||
const [inRx1, inTx1] = newStreamPair<SyncMessage>();
|
||||
const [outRx1, outTx1] = newStreamPair<SyncMessage>();
|
||||
@@ -754,40 +754,40 @@ test.skip("When replaying creation and transactions of a coValue as new content,
|
||||
action: "load",
|
||||
id: admin.id,
|
||||
});
|
||||
const teamSubscribeMsg = await from1.read();
|
||||
expect(teamSubscribeMsg.value).toMatchObject({
|
||||
const groupSubscribeMsg = await from1.read();
|
||||
expect(groupSubscribeMsg.value).toMatchObject({
|
||||
action: "load",
|
||||
id: team.teamMap.coValue.id,
|
||||
id: group.groupMap.coValue.id,
|
||||
});
|
||||
|
||||
await to2.write(adminSubscribeMessage.value!);
|
||||
await to2.write(teamSubscribeMsg.value!);
|
||||
await to2.write(groupSubscribeMsg.value!);
|
||||
|
||||
// const adminTellKnownStateMsg = await from2.read();
|
||||
// expect(adminTellKnownStateMsg.value).toMatchObject(admStateEx(admin.id));
|
||||
|
||||
const teamTellKnownStateMsg = await from2.read();
|
||||
expect(teamTellKnownStateMsg.value).toMatchObject(teamStateEx(team));
|
||||
const groupTellKnownStateMsg = await from2.read();
|
||||
expect(groupTellKnownStateMsg.value).toMatchObject(groupStateEx(group));
|
||||
|
||||
expect(
|
||||
node2.sync.peers["test1"]!.optimisticKnownStates[
|
||||
team.teamMap.coValue.id
|
||||
group.groupMap.coValue.id
|
||||
]
|
||||
).toBeDefined();
|
||||
|
||||
// await to1.write(adminTellKnownStateMsg.value!);
|
||||
await to1.write(teamTellKnownStateMsg.value!);
|
||||
await to1.write(groupTellKnownStateMsg.value!);
|
||||
|
||||
// const adminContentMsg = await from1.read();
|
||||
// expect(adminContentMsg.value).toMatchObject(admContEx(admin.id));
|
||||
|
||||
const teamContentMsg = await from1.read();
|
||||
expect(teamContentMsg.value).toMatchObject(teamContentEx(team));
|
||||
const groupContentMsg = await from1.read();
|
||||
expect(groupContentMsg.value).toMatchObject(groupContentEx(group));
|
||||
|
||||
// await to2.write(adminContentMsg.value!);
|
||||
await to2.write(teamContentMsg.value!);
|
||||
await to2.write(groupContentMsg.value!);
|
||||
|
||||
const map = team.createMap();
|
||||
const map = group.createMap();
|
||||
|
||||
const mapSubscriptionMsg = await from1.read();
|
||||
expect(mapSubscriptionMsg.value).toMatchObject({
|
||||
@@ -840,9 +840,9 @@ test.skip("When loading a coValue on one node, the server node it is requested f
|
||||
|
||||
const node1 = new LocalNode(admin, session);
|
||||
|
||||
const team = node1.createTeam();
|
||||
const group = node1.createGroup();
|
||||
|
||||
const map = team.createMap();
|
||||
const map = group.createMap();
|
||||
map.edit((editable) => {
|
||||
editable.set("hello", "world", "trusting");
|
||||
});
|
||||
@@ -868,9 +868,9 @@ test("Can sync a coValue through a server to another client", async () => {
|
||||
|
||||
const client1 = new LocalNode(admin, session);
|
||||
|
||||
const team = client1.createTeam();
|
||||
const group = client1.createGroup();
|
||||
|
||||
const map = team.createMap();
|
||||
const map = group.createMap();
|
||||
map.edit((editable) => {
|
||||
editable.set("hello", "world", "trusting");
|
||||
});
|
||||
@@ -910,9 +910,9 @@ test("Can sync a coValue with private transactions through a server to another c
|
||||
|
||||
const client1 = new LocalNode(admin, session);
|
||||
|
||||
const team = client1.createTeam();
|
||||
const group = client1.createGroup();
|
||||
|
||||
const map = team.createMap();
|
||||
const map = group.createMap();
|
||||
map.edit((editable) => {
|
||||
editable.set("hello", "world", "private");
|
||||
});
|
||||
@@ -952,7 +952,7 @@ test("When a peer's incoming/readable stream closes, we remove the peer", async
|
||||
const [admin, session] = randomAnonymousAccountAndSessionID();
|
||||
const node = new LocalNode(admin, session);
|
||||
|
||||
const team = node.createTeam();
|
||||
const group = node.createGroup();
|
||||
|
||||
const [inRx, inTx] = newStreamPair<SyncMessage>();
|
||||
const [outRx, outTx] = newStreamPair<SyncMessage>();
|
||||
@@ -971,10 +971,10 @@ test("When a peer's incoming/readable stream closes, we remove the peer", async
|
||||
// });
|
||||
expect((await reader.read()).value).toMatchObject({
|
||||
action: "load",
|
||||
id: team.teamMap.coValue.id,
|
||||
id: group.groupMap.coValue.id,
|
||||
});
|
||||
|
||||
const map = team.createMap();
|
||||
const map = group.createMap();
|
||||
|
||||
const mapSubscribeMsg = await reader.read();
|
||||
|
||||
@@ -984,7 +984,7 @@ test("When a peer's incoming/readable stream closes, we remove the peer", async
|
||||
} satisfies SyncMessage);
|
||||
|
||||
// expect((await reader.read()).value).toMatchObject(admContEx(admin.id));
|
||||
expect((await reader.read()).value).toMatchObject(teamContentEx(team));
|
||||
expect((await reader.read()).value).toMatchObject(groupContentEx(group));
|
||||
|
||||
const mapContentMsg = await reader.read();
|
||||
|
||||
@@ -1006,7 +1006,7 @@ test("When a peer's outgoing/writable stream closes, we remove the peer", async
|
||||
const [admin, session] = randomAnonymousAccountAndSessionID();
|
||||
const node = new LocalNode(admin, session);
|
||||
|
||||
const team = node.createTeam();
|
||||
const group = node.createGroup();
|
||||
|
||||
const [inRx, inTx] = newStreamPair<SyncMessage>();
|
||||
const [outRx, outTx] = newStreamPair<SyncMessage>();
|
||||
@@ -1025,10 +1025,10 @@ test("When a peer's outgoing/writable stream closes, we remove the peer", async
|
||||
// });
|
||||
expect((await reader.read()).value).toMatchObject({
|
||||
action: "load",
|
||||
id: team.teamMap.coValue.id,
|
||||
id: group.groupMap.coValue.id,
|
||||
});
|
||||
|
||||
const map = team.createMap();
|
||||
const map = group.createMap();
|
||||
|
||||
const mapSubscribeMsg = await reader.read();
|
||||
|
||||
@@ -1038,7 +1038,7 @@ test("When a peer's outgoing/writable stream closes, we remove the peer", async
|
||||
} satisfies SyncMessage);
|
||||
|
||||
// expect((await reader.read()).value).toMatchObject(admContEx(admin.id));
|
||||
expect((await reader.read()).value).toMatchObject(teamContentEx(team));
|
||||
expect((await reader.read()).value).toMatchObject(groupContentEx(group));
|
||||
|
||||
const mapContentMsg = await reader.read();
|
||||
|
||||
@@ -1066,9 +1066,9 @@ test("If we start loading a coValue before connecting to a peer that has it, it
|
||||
|
||||
const node1 = new LocalNode(admin, session);
|
||||
|
||||
const team = node1.createTeam();
|
||||
const group = node1.createGroup();
|
||||
|
||||
const map = team.createMap();
|
||||
const map = group.createMap();
|
||||
map.edit((editable) => {
|
||||
editable.set("hello", "world", "trusting");
|
||||
});
|
||||
@@ -1096,10 +1096,10 @@ test("If we start loading a coValue before connecting to a peer that has it, it
|
||||
);
|
||||
});
|
||||
|
||||
function teamContentEx(team: Team) {
|
||||
function groupContentEx(group: Group) {
|
||||
return {
|
||||
action: "content",
|
||||
id: team.teamMap.coValue.id,
|
||||
id: group.groupMap.coValue.id,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1110,10 +1110,10 @@ function admContEx(adminID: AccountID) {
|
||||
};
|
||||
}
|
||||
|
||||
function teamStateEx(team: Team) {
|
||||
function groupStateEx(group: Group) {
|
||||
return {
|
||||
action: "known",
|
||||
id: team.teamMap.coValue.id,
|
||||
id: group.groupMap.coValue.id,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { AgentSecret, createdNowUnique, getAgentID, newRandomAgentSecret } from "./crypto.js";
|
||||
import { newRandomSessionID } from "./coValue.js";
|
||||
import { LocalNode } from "./node.js";
|
||||
import { expectTeamContent } from "./team.js";
|
||||
import { expectGroupContent } from "./group.js";
|
||||
import { AnonymousControlledAccount } from "./account.js";
|
||||
import { SessionID } from "./ids.js";
|
||||
|
||||
@@ -13,69 +13,69 @@ export function randomAnonymousAccountAndSessionID(): [AnonymousControlledAccoun
|
||||
return [new AnonymousControlledAccount(agentSecret), sessionID];
|
||||
}
|
||||
|
||||
export function newTeam() {
|
||||
export function newGroup() {
|
||||
const [admin, sessionID] = randomAnonymousAccountAndSessionID();
|
||||
|
||||
const node = new LocalNode(admin, sessionID);
|
||||
|
||||
const team = node.createCoValue({
|
||||
const group = node.createCoValue({
|
||||
type: "comap",
|
||||
ruleset: { type: "team", initialAdmin: admin.id },
|
||||
ruleset: { type: "group", initialAdmin: admin.id },
|
||||
meta: null,
|
||||
...createdNowUnique(),
|
||||
});
|
||||
|
||||
const teamContent = expectTeamContent(team.getCurrentContent());
|
||||
const groupContent = expectGroupContent(group.getCurrentContent());
|
||||
|
||||
teamContent.edit((editable) => {
|
||||
groupContent.edit((editable) => {
|
||||
editable.set(admin.id, "admin", "trusting");
|
||||
expect(editable.get(admin.id)).toEqual("admin");
|
||||
});
|
||||
|
||||
return { node, team, admin };
|
||||
return { node, group, admin };
|
||||
}
|
||||
|
||||
export function teamWithTwoAdmins() {
|
||||
const { team, admin, node } = newTeam();
|
||||
export function groupWithTwoAdmins() {
|
||||
const { group, admin, node } = newGroup();
|
||||
|
||||
const otherAdmin = node.createAccount("otherAdmin");
|
||||
|
||||
let content = expectTeamContent(team.getCurrentContent());
|
||||
let content = expectGroupContent(group.getCurrentContent());
|
||||
|
||||
content.edit((editable) => {
|
||||
editable.set(otherAdmin.id, "admin", "trusting");
|
||||
expect(editable.get(otherAdmin.id)).toEqual("admin");
|
||||
});
|
||||
|
||||
content = expectTeamContent(team.getCurrentContent());
|
||||
content = expectGroupContent(group.getCurrentContent());
|
||||
|
||||
if (content.type !== "comap") {
|
||||
throw new Error("Expected map");
|
||||
}
|
||||
|
||||
expect(content.get(otherAdmin.id)).toEqual("admin");
|
||||
return { team, admin, otherAdmin, node };
|
||||
return { group, admin, otherAdmin, node };
|
||||
}
|
||||
|
||||
export function newTeamHighLevel() {
|
||||
export function newGroupHighLevel() {
|
||||
const [admin, sessionID] = randomAnonymousAccountAndSessionID();
|
||||
|
||||
|
||||
const node = new LocalNode(admin, sessionID);
|
||||
|
||||
const team = node.createTeam();
|
||||
const group = node.createGroup();
|
||||
|
||||
return { admin, node, team };
|
||||
return { admin, node, group };
|
||||
}
|
||||
|
||||
export function teamWithTwoAdminsHighLevel() {
|
||||
const { admin, node, team } = newTeamHighLevel();
|
||||
export function groupWithTwoAdminsHighLevel() {
|
||||
const { admin, node, group } = newGroupHighLevel();
|
||||
|
||||
const otherAdmin = node.createAccount("otherAdmin");
|
||||
|
||||
team.addMember(otherAdmin.id, "admin");
|
||||
group.addMember(otherAdmin.id, "admin");
|
||||
|
||||
return { admin, node, team, otherAdmin };
|
||||
return { admin, node, group, otherAdmin };
|
||||
}
|
||||
|
||||
export function shouldNotResolve<T>(
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "jazz-browser-auth-local",
|
||||
"version": "0.0.6",
|
||||
"version": "0.1.0",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"jazz-browser": "^0.0.6",
|
||||
"jazz-browser": "^0.1.0",
|
||||
"typescript": "^5.1.6"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "jazz-browser",
|
||||
"version": "0.0.6",
|
||||
"version": "0.1.0",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cojson": "^0.0.23",
|
||||
"jazz-storage-indexeddb": "^0.0.10",
|
||||
"cojson": "^0.1.0",
|
||||
"jazz-storage-indexeddb": "^0.1.0",
|
||||
"typescript": "^5.1.6"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
SyncMessage,
|
||||
Peer,
|
||||
ContentType,
|
||||
Team,
|
||||
Group,
|
||||
CoID,
|
||||
} from "cojson";
|
||||
import { ReadableStream, WritableStream } from "isomorphic-streams";
|
||||
@@ -247,24 +247,24 @@ export function createInviteLink(
|
||||
const node = coValue.node;
|
||||
let currentCoValue = coValue;
|
||||
|
||||
while (currentCoValue.header.ruleset.type === "ownedByTeam") {
|
||||
while (currentCoValue.header.ruleset.type === "ownedByGroup") {
|
||||
currentCoValue = node.expectCoValueLoaded(
|
||||
currentCoValue.header.ruleset.team
|
||||
currentCoValue.header.ruleset.group
|
||||
);
|
||||
}
|
||||
|
||||
if (currentCoValue.header.ruleset.type !== "team") {
|
||||
throw new Error("Can't create invite link for object without team");
|
||||
if (currentCoValue.header.ruleset.type !== "group") {
|
||||
throw new Error("Can't create invite link for object without group");
|
||||
}
|
||||
|
||||
const team = new Team(
|
||||
cojsonInternals.expectTeamContent(currentCoValue.getCurrentContent()),
|
||||
const group = new Group(
|
||||
cojsonInternals.expectGroupContent(currentCoValue.getCurrentContent()),
|
||||
node
|
||||
);
|
||||
|
||||
const inviteSecret = team.createInvite(role);
|
||||
const inviteSecret = group.createInvite(role);
|
||||
|
||||
return `${baseURL}#invitedTo=${value.id}&inviteSecret=${inviteSecret}`;
|
||||
return `${baseURL}#invitedTo=${value.id}&${inviteSecret}`;
|
||||
}
|
||||
|
||||
export function parseInviteLink(inviteURL: string):
|
||||
@@ -278,8 +278,7 @@ export function parseInviteLink(inviteURL: string):
|
||||
.split("&")[0]
|
||||
?.replace(/^#invitedTo=/, "") as CoID<ContentType>;
|
||||
const inviteSecret = url.hash
|
||||
.split("&")[1]
|
||||
?.replace(/^inviteSecret=/, "") as InviteSecret;
|
||||
.split("&")[1] as InviteSecret;
|
||||
if (!valueID || !inviteSecret) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "jazz-react-auth-local",
|
||||
"version": "0.0.13",
|
||||
"version": "0.1.0",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"jazz-browser-auth-local": "^0.0.6",
|
||||
"jazz-react": "^0.0.16",
|
||||
"jazz-browser-auth-local": "^0.1.0",
|
||||
"jazz-react": "^0.1.0",
|
||||
"typescript": "^5.1.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "jazz-react",
|
||||
"version": "0.0.16",
|
||||
"version": "0.1.0",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cojson": "^0.0.23",
|
||||
"jazz-browser": "^0.0.6",
|
||||
"cojson": "^0.1.0",
|
||||
"jazz-browser": "^0.1.0",
|
||||
"typescript": "^5.1.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -106,7 +106,6 @@ export function useTelepathicState<T extends ContentType>(id?: CoID<T>) {
|
||||
// "Got update",
|
||||
// id,
|
||||
// newState.toJSON(),
|
||||
// newState.coValue.sessions
|
||||
// );
|
||||
setState(newState as T);
|
||||
});
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "jazz-storage-indexeddb",
|
||||
"version": "0.0.10",
|
||||
"version": "0.1.0",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cojson": "^0.0.23",
|
||||
"cojson": "^0.1.0",
|
||||
"typescript": "^5.1.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -16,7 +16,7 @@ test.skip("Should be able to initialize and load from empty DB", async () => {
|
||||
|
||||
console.log("yay!");
|
||||
|
||||
const _team = node.createTeam();
|
||||
const _group = node.createGroup();
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
|
||||
@@ -39,9 +39,9 @@ test("Should be able to sync data to database and then load that from a new node
|
||||
|
||||
console.log("yay!");
|
||||
|
||||
const team = node1.createTeam();
|
||||
const group = node1.createGroup();
|
||||
|
||||
const map = team.createMap();
|
||||
const map = group.createMap();
|
||||
|
||||
map.edit((m) => {
|
||||
m.set("hello", "world");
|
||||
|
||||
@@ -205,7 +205,7 @@ export class IDBStorage {
|
||||
}
|
||||
|
||||
const dependedOnCoValues =
|
||||
coValueRow?.header.ruleset.type === "team"
|
||||
coValueRow?.header.ruleset.type === "group"
|
||||
? Object.values(newContent.new).flatMap((sessionEntry) =>
|
||||
sessionEntry.newTransactions.flatMap((tx) => {
|
||||
if (tx.privacy !== "trusting") return [];
|
||||
@@ -226,8 +226,8 @@ export class IDBStorage {
|
||||
);
|
||||
})
|
||||
)
|
||||
: coValueRow?.header.ruleset.type === "ownedByTeam"
|
||||
? [coValueRow?.header.ruleset.team]
|
||||
: coValueRow?.header.ruleset.type === "ownedByGroup"
|
||||
? [coValueRow?.header.ruleset.group]
|
||||
: [];
|
||||
|
||||
for (const dependedOnCoValue of dependedOnCoValues) {
|
||||
|
||||
@@ -7744,6 +7744,11 @@ unbzip2-stream@1.4.3:
|
||||
buffer "^5.2.1"
|
||||
through "^2.3.8"
|
||||
|
||||
uniqolor@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/uniqolor/-/uniqolor-1.1.0.tgz#7519f81133cd54a1f4a59c33c81dbe04a3ad155d"
|
||||
integrity sha512-j2XyokF24fsj+L5u6fbu4rM3RQc6VWJuAngYM2k0ZdG3yiVxt0smLkps2GmQIYqK8VkELGdM9vFU/HfOkK/zoQ==
|
||||
|
||||
unique-filename@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-3.0.0.tgz#48ba7a5a16849f5080d26c760c86cf5cf05770ea"
|
||||
|
||||
Reference in New Issue
Block a user