Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add the assign method to RawCoMap to create bulk transactions and optimize RawCoMap init #1077

Merged
merged 3 commits into from
Jan 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/tiny-trains-vanish.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"cojson": patch
---

Add the assign method to RawCoMap to create bulk transactions and optimize RawCoMap init
35 changes: 24 additions & 11 deletions packages/cojson/src/coValues/coList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ export class RawCoListView<
) {
const entry =
this.insertions[opID.sessionID]?.[opID.txIndex]?.[opID.changeIdx];

if (!entry) {
throw new Error("Missing op " + opID);
}
Expand Down Expand Up @@ -412,6 +413,14 @@ export class RawCoList<
item: Item,
after?: number,
privacy: "private" | "trusting" = "private",
) {
this.appendItems([item], after, privacy);
}

appendItems(
items: Item[],
after?: number,
privacy: "private" | "trusting" = "private",
) {
const entries = this.entries();
after =
Expand All @@ -420,7 +429,7 @@ export class RawCoList<
? entries.length - 1
: 0
: after;
let opIDBefore;
let opIDBefore: OpID | "start";
if (entries.length > 0) {
const entryBefore = entries[after];
if (!entryBefore) {
Expand All @@ -433,16 +442,20 @@ export class RawCoList<
}
opIDBefore = "start";
}
this.core.makeTransaction(
[
{
op: "app",
value: isCoValue(item) ? item.id : item,
after: opIDBefore,
},
],
privacy,
);

const changes = items.map((item) => ({
op: "app",
value: isCoValue(item) ? item.id : item,
after: opIDBefore,
}));

if (opIDBefore !== "start") {
// When added as successors we need to reverse the items
// to keep the same insertion order
changes.reverse();
}

this.core.makeTransaction(changes, privacy);

const listAfter = new RawCoList(this.core) as this;

Expand Down
20 changes: 20 additions & 0 deletions packages/cojson/src/coValues/coMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,26 @@ export class RawCoMap<
this.processNewTransactions();
}

assign(
entries: Partial<Shape>,
privacy: "private" | "trusting" = "private",
): void {
if (this.isTimeTravelEntity()) {
throw new Error("Cannot set value on a time travel entity");
}

this.core.makeTransaction(
Object.entries(entries).map(([key, value]) => ({
op: "set",
key,
value: isCoValue(value) ? value.id : value,
})),
privacy,
);

this.processNewTransactions();
}

/** Delete the given key (setting it to undefined).
*
* If `privacy` is `"private"` **(default)**, `key` is encrypted in the transaction, only readable by other members of the group this `CoMap` belongs to. Not even sync servers can see the content in plaintext.
Expand Down
10 changes: 3 additions & 7 deletions packages/cojson/src/coValues/group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -623,9 +623,7 @@ export class RawGroup<
.getCurrentContent() as M;

if (init) {
for (const [key, value] of Object.entries(init)) {
map.set(key, value, initPrivacy);
}
map.assign(init, initPrivacy);
}

return map;
Expand Down Expand Up @@ -655,10 +653,8 @@ export class RawGroup<
})
.getCurrentContent() as L;

if (init) {
for (const item of init) {
list.append(item, undefined, initPrivacy);
}
if (init?.length) {
list.appendItems(init, undefined, initPrivacy);
}

return list;
Expand Down
40 changes: 40 additions & 0 deletions packages/cojson/src/tests/coList.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,26 @@ test("Push is equivalent to append after last item", () => {
expect(content.toJSON()).toEqual(["hello", "world", "hooray"]);
});

test("appendItems add an array of items at the end of the list", () => {
const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);

const coValue = node.createCoValue({
type: "colist",
ruleset: { type: "unsafeAllowAll" },
meta: null,
...Crypto.createdNowUnique(),
});

const content = expectList(coValue.getCurrentContent());

expect(content.type).toEqual("colist");

content.append("hello", 0, "trusting");
expect(content.toJSON()).toEqual(["hello"]);
content.appendItems(["world", "hooray", "universe"], undefined, "trusting");
expect(content.toJSON()).toEqual(["hello", "world", "hooray", "universe"]);
});

test("Can push into empty list", () => {
const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);

Expand All @@ -92,3 +112,23 @@ test("Can push into empty list", () => {
content.append("hello", undefined, "trusting");
expect(content.toJSON()).toEqual(["hello"]);
});

test("init the list correctly", () => {
const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);

const group = node.createGroup();

const content = group.createList(["hello", "world", "hooray", "universe"]);

expect(content.type).toEqual("colist");
expect(content.toJSON()).toEqual(["hello", "world", "hooray", "universe"]);

content.append("hello", content.toJSON().length - 1, "trusting");
expect(content.toJSON()).toEqual([
"hello",
"world",
"hooray",
"universe",
"hello",
]);
});
32 changes: 32 additions & 0 deletions packages/cojson/src/tests/coMap.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,3 +175,35 @@ test("Can get last tx ID for a key in CoMap", () => {
content.set("hello", "C", "trusting");
expect(content.lastEditAt("hello")?.tx.txIndex).toEqual(2);
});

test("Can set items in bulk with assign", () => {
const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);

const coValue = node.createCoValue({
type: "comap",
ruleset: { type: "unsafeAllowAll" },
meta: null,
...Crypto.createdNowUnique(),
});

const content = expectMap(coValue.getCurrentContent());

expect(content.type).toEqual("comap");

content.set("key1", "set1", "trusting");

content.assign(
{
key1: "assign1",
key2: "assign2",
key3: "assign3",
},
"trusting",
);

expect(content.toJSON()).toEqual({
key1: "assign1",
key2: "assign2",
key3: "assign3",
});
});
8 changes: 5 additions & 3 deletions packages/jazz-tools/src/coValues/coList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,9 +239,11 @@ export class CoList<Item = any> extends Array<Item> implements CoValue {
}

push(...items: Item[]): number {
for (const item of toRawItems(items as Item[], this._schema[ItemsSym])) {
this._raw.append(item);
}
this._raw.appendItems(
toRawItems(items, this._schema[ItemsSym]),
undefined,
"private",
);

return this._raw.entries().length;
}
Expand Down
Loading