jazz-tools 0.9.8 → 0.9.10-jazz-bridge-preview.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,33 +1,161 @@
1
+ import { connectedPeers } from "cojson/src/streamUtils.js";
1
2
  import { describe, expect, test } from "vitest";
2
- import { Account, CoPlainText, WasmCrypto } from "../index.web.js";
3
+ import {
4
+ Account,
5
+ CoPlainText,
6
+ WasmCrypto,
7
+ cojsonInternals,
8
+ createJazzContext,
9
+ fixedCredentialsAuth,
10
+ isControlledAccount,
11
+ } from "../index.web.js";
12
+ import { randomSessionProvider } from "../internal.js";
3
13
 
4
14
  const Crypto = await WasmCrypto.create();
5
15
 
6
- describe("Simple CoPlainText operations", async () => {
7
- const me = await Account.create({
8
- creationProps: { name: "Hermes Puggington" },
9
- crypto: Crypto,
16
+ describe("CoPlainText", () => {
17
+ const initNodeAndText = async () => {
18
+ const me = await Account.create({
19
+ creationProps: { name: "Hermes Puggington" },
20
+ crypto: Crypto,
21
+ });
22
+
23
+ const text = CoPlainText.create("hello world", { owner: me });
24
+
25
+ return { me, text };
26
+ };
27
+
28
+ describe("Simple CoPlainText operations", async () => {
29
+ const { me, text } = await initNodeAndText();
30
+
31
+ test("Construction", () => {
32
+ expect(text + "").toEqual("hello world");
33
+ });
34
+
35
+ describe("Mutation", () => {
36
+ test("insertion", () => {
37
+ const text = CoPlainText.create("hello world", { owner: me });
38
+
39
+ text.insertAfter(5, " cruel");
40
+ expect(text + "").toEqual("hello cruel world");
41
+
42
+ text.insertAfter(0, "Hello, ");
43
+ expect(text + "").toEqual("Hello, hello cruel world");
44
+ });
45
+
46
+ test("deletion", () => {
47
+ const text = CoPlainText.create("hello world", { owner: me });
48
+
49
+ text.deleteRange({ from: 3, to: 8 });
50
+ expect(text + "").toEqual("helrld");
51
+ });
52
+ });
53
+
54
+ describe("Position operations", () => {
55
+ test("idxBefore returns index before a position", () => {
56
+ const text = CoPlainText.create("hello world", { owner: me });
57
+
58
+ // Get position at index 5 (between "hello" and " world")
59
+ const pos = text.posBefore(5);
60
+ expect(pos).toBeDefined();
61
+
62
+ // Verify idxBefore returns the index before the position (4)
63
+ // This makes sense as the position is between characters,
64
+ // and idxBefore returns the index of the last character before that position
65
+ const idx = text.idxBefore(pos!);
66
+ expect(idx).toBe(4); // Index of 'o' in "hello"
67
+ });
68
+
69
+ test("idxAfter returns index after a position", () => {
70
+ const text = CoPlainText.create("hello world", { owner: me });
71
+
72
+ // Get position at index 5 (between "hello" and " world")
73
+ const pos = text.posBefore(5);
74
+ expect(pos).toBeDefined();
75
+
76
+ // Verify idxAfter returns the index after the position (5)
77
+ // This makes sense as the position is between characters,
78
+ // and idxAfter returns the index of the first character after that position
79
+ const idx = text.idxAfter(pos!);
80
+ expect(idx).toBe(5); // Index of ' ' in "hello world"
81
+ });
82
+ });
10
83
  });
11
84
 
12
- const text = CoPlainText.create("hello world", { owner: me });
85
+ describe("Loading and availability", () => {
86
+ test("can load text across peers", async () => {
87
+ const { me, text } = await initNodeAndText();
88
+ const id = text.id;
89
+
90
+ // Set up peer connections
91
+ const [initialAsPeer, secondPeer] = connectedPeers("initial", "second", {
92
+ peer1role: "server",
93
+ peer2role: "client",
94
+ });
95
+
96
+ if (!isControlledAccount(me)) {
97
+ throw "me is not a controlled account";
98
+ }
99
+ me._raw.core.node.syncManager.addPeer(secondPeer);
100
+ const { account: meOnSecondPeer } = await createJazzContext({
101
+ auth: fixedCredentialsAuth({
102
+ accountID: me.id,
103
+ secret: me._raw.agentSecret,
104
+ }),
105
+ sessionProvider: randomSessionProvider,
106
+ peersToLoadFrom: [initialAsPeer],
107
+ crypto: Crypto,
108
+ });
13
109
 
14
- test("Construction", () => {
15
- expect(text + "").toEqual("hello world");
110
+ // Load the text on the second peer
111
+ const loaded = await CoPlainText.load(id, meOnSecondPeer);
112
+ expect(loaded).toBeDefined();
113
+ expect(loaded!.toString()).toBe("hello world");
114
+ });
16
115
  });
17
116
 
18
- describe("Mutation", () => {
19
- test("insertion", () => {
20
- const text = CoPlainText.create("hello world", { owner: me });
117
+ test("Subscription & auto-resolution", async () => {
118
+ const { me, text } = await initNodeAndText();
119
+
120
+ // Set up peer connections
121
+ const [initialAsPeer, secondPeer] = connectedPeers("initial", "second", {
122
+ peer1role: "server",
123
+ peer2role: "client",
124
+ });
21
125
 
22
- text.insertAfter(5, " cruel");
23
- expect(text + "").toEqual("hello cruel world");
126
+ if (!isControlledAccount(me)) {
127
+ throw "me is not a controlled account";
128
+ }
129
+ me._raw.core.node.syncManager.addPeer(secondPeer);
130
+ const { account: meOnSecondPeer } = await createJazzContext({
131
+ auth: fixedCredentialsAuth({
132
+ accountID: me.id,
133
+ secret: me._raw.agentSecret,
134
+ }),
135
+ sessionProvider: randomSessionProvider,
136
+ peersToLoadFrom: [initialAsPeer],
137
+ crypto: Crypto,
24
138
  });
25
139
 
26
- test("deletion", () => {
27
- const text = CoPlainText.create("hello world", { owner: me });
140
+ const queue = new cojsonInternals.Channel();
28
141
 
29
- text.deleteRange({ from: 3, to: 8 });
30
- expect(text + "").toEqual("helrld");
142
+ // Subscribe to text updates
143
+ CoPlainText.subscribe(text.id, meOnSecondPeer, (subscribedText) => {
144
+ void queue.push(subscribedText);
31
145
  });
146
+
147
+ // Initial subscription should give us the text
148
+ const update1 = (await queue.next()).value;
149
+ expect(update1.toString()).toBe("hello world");
150
+
151
+ // When we make a change, we should get an update
152
+ text.insertAfter(5, " beautiful");
153
+ const update2 = (await queue.next()).value;
154
+ expect(update2.toString()).toBe("hello beautiful world");
155
+
156
+ // When we make another change, we should get another update
157
+ update2.deleteRange({ from: 5, to: 15 }); // Delete " beautiful"
158
+ const update3 = (await queue.next()).value;
159
+ expect(update3.toString()).toBe("hello world");
32
160
  });
33
161
  });