jazz-tools 0.13.17 → 0.13.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +7 -7
- package/CHANGELOG.md +11 -0
- package/dist/{chunk-PYBQOYML.js → chunk-ITSHLDQB.js} +729 -595
- package/dist/chunk-ITSHLDQB.js.map +1 -0
- package/dist/coValues/account.d.ts +5 -4
- package/dist/coValues/account.d.ts.map +1 -1
- package/dist/coValues/coFeed.d.ts +1 -0
- package/dist/coValues/coFeed.d.ts.map +1 -1
- package/dist/coValues/coList.d.ts +1 -0
- package/dist/coValues/coList.d.ts.map +1 -1
- package/dist/coValues/coMap.d.ts +4 -2
- package/dist/coValues/coMap.d.ts.map +1 -1
- package/dist/coValues/coPlainText.d.ts +2 -2
- package/dist/coValues/coPlainText.d.ts.map +1 -1
- package/dist/coValues/deepLoading.d.ts +1 -9
- package/dist/coValues/deepLoading.d.ts.map +1 -1
- package/dist/coValues/extensions/imageDef.d.ts.map +1 -1
- package/dist/coValues/group.d.ts.map +1 -1
- package/dist/coValues/inbox.d.ts.map +1 -1
- package/dist/coValues/interfaces.d.ts +4 -1
- package/dist/coValues/interfaces.d.ts.map +1 -1
- package/dist/implementation/createContext.d.ts.map +1 -1
- package/dist/implementation/refs.d.ts +5 -10
- package/dist/implementation/refs.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/internal.d.ts +1 -1
- package/dist/internal.d.ts.map +1 -1
- package/dist/subscribe/CoValueCoreSubscription.d.ts +14 -0
- package/dist/subscribe/CoValueCoreSubscription.d.ts.map +1 -0
- package/dist/subscribe/JazzError.d.ts +16 -0
- package/dist/subscribe/JazzError.d.ts.map +1 -0
- package/dist/subscribe/SubscriptionScope.d.ts +42 -0
- package/dist/subscribe/SubscriptionScope.d.ts.map +1 -0
- package/dist/subscribe/index.d.ts +21 -0
- package/dist/subscribe/index.d.ts.map +1 -0
- package/dist/subscribe/types.d.ts +12 -0
- package/dist/subscribe/types.d.ts.map +1 -0
- package/dist/subscribe/utils.d.ts +10 -0
- package/dist/subscribe/utils.d.ts.map +1 -0
- package/dist/testing.js +2 -2
- package/dist/testing.js.map +1 -1
- package/dist/tests/coMap.record.test.d.ts +2 -0
- package/dist/tests/coMap.record.test.d.ts.map +1 -0
- package/dist/tests/utils.d.ts +2 -2
- package/dist/tests/utils.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/coValues/account.ts +43 -31
- package/src/coValues/coFeed.ts +11 -7
- package/src/coValues/coList.ts +13 -17
- package/src/coValues/coMap.ts +72 -80
- package/src/coValues/coPlainText.ts +13 -2
- package/src/coValues/deepLoading.ts +4 -277
- package/src/coValues/extensions/imageDef.ts +1 -7
- package/src/coValues/group.ts +7 -6
- package/src/coValues/inbox.ts +4 -11
- package/src/coValues/interfaces.ts +54 -111
- package/src/implementation/createContext.ts +3 -4
- package/src/implementation/invites.ts +2 -2
- package/src/implementation/refs.ts +30 -121
- package/src/internal.ts +1 -2
- package/src/subscribe/CoValueCoreSubscription.ts +71 -0
- package/src/subscribe/JazzError.ts +48 -0
- package/src/subscribe/SubscriptionScope.ts +523 -0
- package/src/subscribe/index.ts +82 -0
- package/src/subscribe/types.ts +7 -0
- package/src/subscribe/utils.ts +36 -0
- package/src/testing.ts +1 -1
- package/src/tests/ContextManager.test.ts +13 -9
- package/src/tests/coFeed.test.ts +6 -6
- package/src/tests/coList.test.ts +304 -115
- package/src/tests/coMap.record.test.ts +325 -0
- package/src/tests/coMap.test.ts +718 -645
- package/src/tests/coPlainText.test.ts +2 -2
- package/src/tests/createContext.test.ts +8 -8
- package/src/tests/deepLoading.test.ts +8 -34
- package/src/tests/groupsAndAccounts.test.ts +6 -4
- package/src/tests/subscribe.test.ts +357 -42
- package/src/tests/utils.ts +8 -6
- package/dist/chunk-PYBQOYML.js.map +0 -1
- package/dist/implementation/subscriptionScope.d.ts +0 -34
- package/dist/implementation/subscriptionScope.d.ts.map +0 -1
- package/src/implementation/subscriptionScope.ts +0 -165
package/src/tests/coMap.test.ts
CHANGED
@@ -1,420 +1,423 @@
|
|
1
1
|
import { WasmCrypto } from "cojson/crypto/WasmCrypto";
|
2
|
-
import { describe, expect, expectTypeOf, test, vi } from "vitest";
|
3
|
-
import { Group, randomSessionProvider } from "../exports.js";
|
4
2
|
import {
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
3
|
+
assert,
|
4
|
+
beforeEach,
|
5
|
+
describe,
|
6
|
+
expect,
|
7
|
+
expectTypeOf,
|
8
|
+
it,
|
9
|
+
test,
|
10
|
+
vi,
|
11
|
+
} from "vitest";
|
12
|
+
import { Group, Resolved, subscribeToCoValue } from "../exports.js";
|
13
|
+
import { Account, CoMap, Encoders, co, cojsonInternals } from "../index.js";
|
14
|
+
import { createJazzTestAccount, setupJazzTestSync } from "../testing.js";
|
15
|
+
import { setupTwoNodes, waitFor } from "./utils.js";
|
14
16
|
|
15
17
|
const { connectedPeers } = cojsonInternals;
|
16
18
|
|
17
19
|
const Crypto = await WasmCrypto.create();
|
18
20
|
|
19
|
-
|
20
|
-
|
21
|
-
_height = co.number;
|
22
|
-
birthday = co.Date;
|
23
|
-
name? = co.string;
|
24
|
-
nullable = co.optional.encoded<string | undefined>({
|
25
|
-
encode: (value: string | undefined) => value || null,
|
26
|
-
decode: (value: unknown) => (value as string) || undefined,
|
27
|
-
});
|
28
|
-
optionalDate = co.optional.Date;
|
29
|
-
|
30
|
-
get roughColor() {
|
31
|
-
return this.color + "ish";
|
32
|
-
}
|
33
|
-
}
|
21
|
+
beforeEach(async () => {
|
22
|
+
await setupJazzTestSync();
|
34
23
|
|
35
|
-
|
36
|
-
|
24
|
+
await createJazzTestAccount({
|
25
|
+
isCurrentActiveAccount: true,
|
37
26
|
creationProps: { name: "Hermes Puggington" },
|
38
|
-
crypto: Crypto,
|
39
27
|
});
|
28
|
+
});
|
40
29
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
"
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
30
|
+
describe("CoMap", async () => {
|
31
|
+
describe("init", () => {
|
32
|
+
test("create a CoMap with basic property access", () => {
|
33
|
+
class Person extends CoMap {
|
34
|
+
color = co.string;
|
35
|
+
_height = co.number;
|
36
|
+
birthday = co.Date;
|
37
|
+
name = co.string;
|
38
|
+
nullable = co.optional.encoded<string | undefined>({
|
39
|
+
encode: (value: string | undefined) => value || null,
|
40
|
+
decode: (value: unknown) => (value as string) || undefined,
|
41
|
+
});
|
42
|
+
optionalDate = co.optional.Date;
|
43
|
+
|
44
|
+
get roughColor() {
|
45
|
+
return this.color + "ish";
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
const birthday = new Date("1989-11-27");
|
50
|
+
|
51
|
+
const john = Person.create({
|
52
|
+
color: "red",
|
53
|
+
_height: 10,
|
54
|
+
birthday,
|
55
|
+
name: "John",
|
56
|
+
});
|
66
57
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
58
|
+
expect(john.color).toEqual("red");
|
59
|
+
expect(john.roughColor).toEqual("redish");
|
60
|
+
expect(john._height).toEqual(10);
|
61
|
+
expect(john.birthday).toEqual(birthday);
|
62
|
+
expect(john._raw.get("birthday")).toEqual(birthday.toISOString());
|
63
|
+
expect(Object.keys(john)).toEqual([
|
64
|
+
"color",
|
65
|
+
"_height",
|
66
|
+
"birthday",
|
67
|
+
"name",
|
68
|
+
]);
|
69
|
+
});
|
72
70
|
|
73
|
-
|
74
|
-
|
71
|
+
test("property existence", () => {
|
72
|
+
class Person extends CoMap {
|
73
|
+
name = co.string;
|
74
|
+
}
|
75
75
|
|
76
|
-
|
77
|
-
const group = Group.create(me);
|
78
|
-
const map = TestMap.create(
|
79
|
-
{ color: "red", _height: 10, birthday: birthday },
|
80
|
-
group,
|
81
|
-
);
|
76
|
+
const john = Person.create({ name: "John" });
|
82
77
|
|
83
|
-
|
84
|
-
|
78
|
+
expect("name" in john).toEqual(true);
|
79
|
+
expect("age" in john).toEqual(false);
|
80
|
+
});
|
85
81
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
_height: 10,
|
91
|
-
birthday: birthday,
|
92
|
-
name: "Hermes",
|
93
|
-
extra: "extra",
|
94
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
95
|
-
} as any,
|
96
|
-
{ owner: me },
|
97
|
-
);
|
82
|
+
test("create a CoMap with an account as owner", () => {
|
83
|
+
class Person extends CoMap {
|
84
|
+
name = co.string;
|
85
|
+
}
|
98
86
|
|
99
|
-
|
100
|
-
});
|
87
|
+
const john = Person.create({ name: "John" }, Account.getMe());
|
101
88
|
|
102
|
-
|
103
|
-
|
89
|
+
expect(john.name).toEqual("John");
|
90
|
+
expect(john._raw.get("name")).toEqual("John");
|
91
|
+
});
|
104
92
|
|
105
|
-
|
106
|
-
|
107
|
-
|
93
|
+
test("create a CoMap with a group as owner", () => {
|
94
|
+
class Person extends CoMap {
|
95
|
+
name = co.string;
|
96
|
+
}
|
108
97
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
98
|
+
const john = Person.create({ name: "John" }, Group.create());
|
99
|
+
|
100
|
+
expect(john.name).toEqual("John");
|
101
|
+
expect(john._raw.get("name")).toEqual("John");
|
102
|
+
});
|
103
|
+
|
104
|
+
test("Empty schema", () => {
|
105
|
+
const emptyMap = CoMap.create({});
|
106
|
+
|
107
|
+
// @ts-expect-error
|
108
|
+
expect(emptyMap.color).toEqual(undefined);
|
109
|
+
});
|
110
|
+
|
111
|
+
test("setting date as undefined should throw", () => {
|
112
|
+
class Person extends CoMap {
|
113
|
+
color = co.string;
|
114
|
+
_height = co.number;
|
115
|
+
birthday = co.Date;
|
116
|
+
name = co.string;
|
117
|
+
nullable = co.optional.encoded<string | undefined>({
|
118
|
+
encode: (value: string | undefined) => value || null,
|
119
|
+
decode: (value: unknown) => (value as string) || undefined,
|
120
|
+
});
|
121
|
+
optionalDate = co.optional.Date;
|
122
|
+
|
123
|
+
get roughColor() {
|
124
|
+
return this.color + "ish";
|
125
|
+
}
|
126
|
+
}
|
127
|
+
|
128
|
+
expect(() =>
|
129
|
+
Person.create({
|
113
130
|
color: "red",
|
114
131
|
_height: 10,
|
132
|
+
name: "John",
|
115
133
|
birthday: undefined!,
|
116
|
-
},
|
117
|
-
|
118
|
-
|
119
|
-
).toThrow();
|
120
|
-
});
|
134
|
+
}),
|
135
|
+
).toThrow();
|
136
|
+
});
|
121
137
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
138
|
+
test("CoMap with reference", () => {
|
139
|
+
class Dog extends CoMap {
|
140
|
+
name = co.string;
|
141
|
+
breed = co.string;
|
142
|
+
}
|
143
|
+
|
144
|
+
class Person extends CoMap {
|
145
|
+
name = co.string;
|
146
|
+
age = co.number;
|
147
|
+
dog = co.ref(Dog);
|
148
|
+
}
|
149
|
+
|
150
|
+
const person = Person.create({
|
151
|
+
name: "John",
|
152
|
+
age: 20,
|
153
|
+
dog: Dog.create({ name: "Rex", breed: "Labrador" }),
|
154
|
+
});
|
127
155
|
|
128
|
-
|
156
|
+
expect(person.dog?.name).toEqual("Rex");
|
157
|
+
expect(person.dog?.breed).toEqual("Labrador");
|
158
|
+
});
|
129
159
|
|
130
|
-
|
160
|
+
test("CoMap with self reference", () => {
|
161
|
+
class Person extends CoMap {
|
162
|
+
name = co.string;
|
163
|
+
age = co.number;
|
164
|
+
friend = co.optional.ref(Person);
|
165
|
+
}
|
166
|
+
|
167
|
+
const person = Person.create({
|
168
|
+
name: "John",
|
169
|
+
age: 20,
|
170
|
+
friend: Person.create({ name: "Jane", age: 21 }),
|
171
|
+
});
|
131
172
|
|
132
|
-
|
133
|
-
|
134
|
-
id: map.id,
|
135
|
-
color: "red",
|
136
|
-
height: 10,
|
173
|
+
expect(person.friend?.name).toEqual("Jane");
|
174
|
+
expect(person.friend?.age).toEqual(21);
|
137
175
|
});
|
138
|
-
});
|
139
176
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
}
|
177
|
+
test("toJSON should not fail when there is a key in the raw value not represented in the schema", () => {
|
178
|
+
class Person extends CoMap {
|
179
|
+
name = co.string;
|
180
|
+
age = co.number;
|
181
|
+
}
|
146
182
|
|
147
|
-
|
183
|
+
const person = Person.create({ name: "John", age: 20 });
|
148
184
|
|
149
|
-
|
185
|
+
person._raw.set("extra", "extra");
|
150
186
|
|
151
|
-
|
152
|
-
_type: "CoMap",
|
153
|
-
id: map.id,
|
154
|
-
color: "red",
|
155
|
-
height: 10,
|
156
|
-
nested: {
|
187
|
+
expect(person.toJSON()).toEqual({
|
157
188
|
_type: "CoMap",
|
158
|
-
id:
|
159
|
-
|
160
|
-
|
161
|
-
}
|
189
|
+
id: person.id,
|
190
|
+
name: "John",
|
191
|
+
age: 20,
|
192
|
+
});
|
162
193
|
});
|
163
|
-
});
|
164
194
|
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
195
|
+
test("toJSON should handle references", () => {
|
196
|
+
class Person extends CoMap {
|
197
|
+
name = co.string;
|
198
|
+
age = co.number;
|
199
|
+
friend = co.optional.ref(Person);
|
200
|
+
}
|
201
|
+
|
202
|
+
const person = Person.create({
|
203
|
+
name: "John",
|
204
|
+
age: 20,
|
205
|
+
friend: Person.create({ name: "Jane", age: 21 }),
|
206
|
+
});
|
171
207
|
|
172
|
-
|
208
|
+
expect(person.toJSON()).toEqual({
|
209
|
+
_type: "CoMap",
|
210
|
+
id: person.id,
|
211
|
+
name: "John",
|
212
|
+
age: 20,
|
213
|
+
friend: {
|
214
|
+
_type: "CoMap",
|
215
|
+
id: person.friend?.id,
|
216
|
+
name: "Jane",
|
217
|
+
age: 21,
|
218
|
+
},
|
219
|
+
});
|
220
|
+
});
|
173
221
|
|
174
|
-
|
222
|
+
test("toJSON should handle circular references", () => {
|
223
|
+
class Person extends CoMap {
|
224
|
+
name = co.string;
|
225
|
+
age = co.number;
|
226
|
+
friend = co.optional.ref(Person);
|
227
|
+
}
|
175
228
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
height: 10,
|
181
|
-
nested: {
|
182
|
-
_circular: map.id,
|
183
|
-
},
|
184
|
-
});
|
185
|
-
});
|
229
|
+
const person = Person.create({
|
230
|
+
name: "John",
|
231
|
+
age: 20,
|
232
|
+
});
|
186
233
|
|
187
|
-
|
188
|
-
const map = TestMap.create(
|
189
|
-
{
|
190
|
-
color: "red",
|
191
|
-
_height: 10,
|
192
|
-
birthday: new Date(),
|
193
|
-
},
|
194
|
-
{ owner: me },
|
195
|
-
);
|
234
|
+
person.friend = person;
|
196
235
|
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
236
|
+
expect(person.toJSON()).toEqual({
|
237
|
+
_type: "CoMap",
|
238
|
+
id: person.id,
|
239
|
+
name: "John",
|
240
|
+
age: 20,
|
241
|
+
friend: {
|
242
|
+
_circular: person.id,
|
243
|
+
},
|
244
|
+
});
|
203
245
|
});
|
204
|
-
});
|
205
246
|
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
247
|
+
test("testing toJSON on a CoMap with a Date field", () => {
|
248
|
+
class Person extends CoMap {
|
249
|
+
name = co.string;
|
250
|
+
age = co.number;
|
251
|
+
birthday = co.Date;
|
252
|
+
}
|
253
|
+
|
254
|
+
const birthday = new Date();
|
255
|
+
|
256
|
+
const john = Person.create({
|
257
|
+
name: "John",
|
258
|
+
age: 20,
|
211
259
|
birthday,
|
212
|
-
|
213
|
-
},
|
214
|
-
{ owner: me },
|
215
|
-
);
|
216
|
-
expect(map.optionalDate).toBeUndefined();
|
217
|
-
});
|
260
|
+
});
|
218
261
|
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
map.birthday = newBirthday;
|
226
|
-
expect(map.birthday).toEqual(newBirthday);
|
227
|
-
expect(map._raw.get("birthday")).toEqual(newBirthday.toISOString());
|
228
|
-
|
229
|
-
Object.assign(map, { color: "green", _height: 20 });
|
230
|
-
expect(map.color).toEqual("green");
|
231
|
-
expect(map._raw.get("color")).toEqual("green");
|
232
|
-
expect(map._height).toEqual(20);
|
233
|
-
expect(map._raw.get("_height")).toEqual(20);
|
234
|
-
|
235
|
-
map.nullable = "not null";
|
236
|
-
map.nullable = undefined;
|
237
|
-
delete map.nullable;
|
238
|
-
map.nullable = undefined;
|
239
|
-
|
240
|
-
map.name = "Secret name";
|
241
|
-
expect(map.name).toEqual("Secret name");
|
242
|
-
map.name = undefined;
|
243
|
-
expect(map.name).toEqual(undefined);
|
244
|
-
expect(Object.keys(map)).toContain("name");
|
245
|
-
delete map.name;
|
246
|
-
expect(map.name).toEqual(undefined);
|
247
|
-
expect(Object.keys(map)).not.toContain("name");
|
248
|
-
|
249
|
-
expect(map._edits).toMatchObject({
|
250
|
-
_height: {
|
251
|
-
by: { id: me.id },
|
252
|
-
value: 20,
|
253
|
-
},
|
254
|
-
birthday: {
|
255
|
-
by: { id: me.id },
|
256
|
-
value: newBirthday,
|
257
|
-
},
|
258
|
-
color: {
|
259
|
-
by: { id: me.id },
|
260
|
-
value: "green",
|
261
|
-
all: [
|
262
|
-
expect.objectContaining({
|
263
|
-
by: { _type: "Account", id: me.id },
|
264
|
-
value: "red",
|
265
|
-
}),
|
266
|
-
expect.objectContaining({
|
267
|
-
by: { _type: "Account", id: me.id },
|
268
|
-
value: "blue",
|
269
|
-
}),
|
270
|
-
expect.objectContaining({
|
271
|
-
by: { _type: "Account", id: me.id },
|
272
|
-
value: "green",
|
273
|
-
}),
|
274
|
-
],
|
275
|
-
},
|
276
|
-
nullable: {
|
277
|
-
by: { id: me.id },
|
278
|
-
value: null,
|
279
|
-
all: [
|
280
|
-
expect.objectContaining({
|
281
|
-
by: { _type: "Account", id: me.id },
|
282
|
-
value: null,
|
283
|
-
}),
|
284
|
-
expect.objectContaining({
|
285
|
-
by: { _type: "Account", id: me.id },
|
286
|
-
value: "not null",
|
287
|
-
}),
|
288
|
-
expect.objectContaining({
|
289
|
-
by: { _type: "Account", id: me.id },
|
290
|
-
value: null,
|
291
|
-
}),
|
292
|
-
expect.objectContaining({
|
293
|
-
by: { _type: "Account", id: me.id },
|
294
|
-
value: undefined,
|
295
|
-
}),
|
296
|
-
expect.objectContaining({
|
297
|
-
by: { _type: "Account", id: me.id },
|
298
|
-
value: null,
|
299
|
-
}),
|
300
|
-
],
|
301
|
-
},
|
262
|
+
expect(john.toJSON()).toMatchObject({
|
263
|
+
name: "John",
|
264
|
+
age: 20,
|
265
|
+
birthday: birthday.toISOString(),
|
266
|
+
_type: "CoMap",
|
267
|
+
id: john.id,
|
302
268
|
});
|
269
|
+
});
|
303
270
|
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
value: "red",
|
320
|
-
}),
|
321
|
-
expect.objectContaining({
|
322
|
-
by: { _type: "Account", id: me.id },
|
323
|
-
value: "blue",
|
324
|
-
}),
|
325
|
-
expect.objectContaining({
|
326
|
-
by: { _type: "Account", id: me.id },
|
327
|
-
value: "green",
|
328
|
-
}),
|
329
|
-
],
|
330
|
-
},
|
331
|
-
nullable: {
|
332
|
-
by: { id: me.id },
|
333
|
-
value: null,
|
334
|
-
},
|
271
|
+
test("setting optional date as undefined should not throw", () => {
|
272
|
+
class Person extends CoMap {
|
273
|
+
name = co.string;
|
274
|
+
age = co.number;
|
275
|
+
birthday = co.optional.Date;
|
276
|
+
}
|
277
|
+
|
278
|
+
const john = Person.create({
|
279
|
+
name: "John",
|
280
|
+
age: 20,
|
281
|
+
});
|
282
|
+
|
283
|
+
expect(john.toJSON()).toMatchObject({
|
284
|
+
name: "John",
|
285
|
+
age: 20,
|
335
286
|
});
|
336
287
|
});
|
337
|
-
});
|
338
288
|
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
{
|
345
|
-
owner: me,
|
346
|
-
},
|
347
|
-
);
|
289
|
+
it("should disallow extra properties", () => {
|
290
|
+
class Person extends CoMap {
|
291
|
+
name = co.string;
|
292
|
+
age = co.number;
|
293
|
+
}
|
348
294
|
|
349
|
-
expect
|
350
|
-
|
295
|
+
// @ts-expect-error - x is not a valid property
|
296
|
+
const john = Person.create({ name: "John", age: 30, x: 1 });
|
297
|
+
|
298
|
+
expect(john.toJSON()).toEqual({
|
299
|
+
_type: "CoMap",
|
300
|
+
id: john.id,
|
301
|
+
name: "John",
|
302
|
+
age: 30,
|
303
|
+
});
|
351
304
|
});
|
352
305
|
});
|
353
306
|
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
307
|
+
describe("Mutation", () => {
|
308
|
+
test("change a primitive value", () => {
|
309
|
+
class Person extends CoMap {
|
310
|
+
name = co.string;
|
311
|
+
age = co.number;
|
312
|
+
}
|
313
|
+
|
314
|
+
const john = Person.create({ name: "John", age: 20 });
|
315
|
+
|
316
|
+
john.name = "Jane";
|
317
|
+
|
318
|
+
expect(john.name).toEqual("Jane");
|
319
|
+
expect(john.age).toEqual(20);
|
320
|
+
});
|
321
|
+
|
322
|
+
test("delete an optional value", () => {
|
323
|
+
class Person extends CoMap {
|
324
|
+
name = co.string;
|
325
|
+
age = co.optional.number;
|
326
|
+
}
|
327
|
+
|
328
|
+
const john = Person.create({ name: "John", age: 20 });
|
329
|
+
|
330
|
+
delete john.age;
|
331
|
+
|
332
|
+
expect(john.name).toEqual("John");
|
333
|
+
expect(john.age).toEqual(undefined);
|
358
334
|
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
335
|
+
expect(john.toJSON()).toEqual({
|
336
|
+
_type: "CoMap",
|
337
|
+
id: john.id,
|
338
|
+
name: "John",
|
339
|
+
});
|
340
|
+
});
|
341
|
+
|
342
|
+
test("update a reference", () => {
|
343
|
+
class Dog extends CoMap {
|
344
|
+
name = co.string;
|
345
|
+
}
|
346
|
+
|
347
|
+
class Person extends CoMap {
|
348
|
+
name = co.string;
|
349
|
+
age = co.number;
|
350
|
+
dog = co.ref(Dog);
|
351
|
+
}
|
352
|
+
|
353
|
+
const john = Person.create({
|
354
|
+
name: "John",
|
355
|
+
age: 20,
|
356
|
+
dog: Dog.create({ name: "Rex" }),
|
357
|
+
});
|
358
|
+
|
359
|
+
john.dog = Dog.create({ name: "Fido" });
|
360
|
+
|
361
|
+
expect(john.dog?.name).toEqual("Fido");
|
362
|
+
});
|
363
|
+
|
364
|
+
test("changes should be listed in _edits", () => {
|
365
|
+
class Person extends CoMap {
|
366
|
+
name = co.string;
|
367
|
+
age = co.number;
|
368
|
+
}
|
369
|
+
|
370
|
+
const john = Person.create({ name: "John", age: 20 });
|
371
|
+
|
372
|
+
const me = Account.getMe();
|
373
|
+
|
374
|
+
john.age = 21;
|
375
|
+
|
376
|
+
expect(john._edits.age.all).toEqual([
|
377
|
+
{
|
378
|
+
by: expect.objectContaining({ _type: "Account", id: me.id }),
|
379
|
+
value: 20,
|
380
|
+
key: "age",
|
381
|
+
ref: undefined,
|
382
|
+
madeAt: expect.any(Date),
|
383
|
+
},
|
363
384
|
{
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
{ owner: me },
|
370
|
-
),
|
385
|
+
by: expect.objectContaining({ _type: "Account", id: me.id }),
|
386
|
+
value: 21,
|
387
|
+
key: "age",
|
388
|
+
ref: undefined,
|
389
|
+
madeAt: expect.any(Date),
|
371
390
|
},
|
372
|
-
|
373
|
-
),
|
374
|
-
},
|
375
|
-
{ owner: me },
|
376
|
-
);
|
377
|
-
|
378
|
-
describe("Recursive CoMap", () => {
|
379
|
-
test("Construction", () => {
|
380
|
-
expect(recursiveMap.name).toEqual("first");
|
381
|
-
expect(recursiveMap.next?.name).toEqual("second");
|
382
|
-
expect(recursiveMap.next?.next?.name).toEqual("third");
|
391
|
+
]);
|
383
392
|
});
|
384
393
|
});
|
385
394
|
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
395
|
+
test("Enum of maps", () => {
|
396
|
+
class MapWithEnumOfMaps extends CoMap {
|
397
|
+
name = co.string;
|
398
|
+
child = co.ref<typeof ChildA | typeof ChildB>((raw) =>
|
399
|
+
raw.get("type") === "a" ? ChildA : ChildB,
|
400
|
+
);
|
401
|
+
}
|
392
402
|
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
403
|
+
class ChildA extends CoMap {
|
404
|
+
type = co.literal("a");
|
405
|
+
value = co.number;
|
406
|
+
}
|
397
407
|
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
408
|
+
class ChildB extends CoMap {
|
409
|
+
type = co.literal("b");
|
410
|
+
value = co.string;
|
411
|
+
}
|
402
412
|
|
403
|
-
|
404
|
-
{
|
413
|
+
const mapWithEnum = MapWithEnumOfMaps.create({
|
405
414
|
name: "enum",
|
406
|
-
child: ChildA.create(
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
{ owner: me },
|
412
|
-
),
|
413
|
-
},
|
414
|
-
{ owner: me },
|
415
|
-
);
|
415
|
+
child: ChildA.create({
|
416
|
+
type: "a",
|
417
|
+
value: 5,
|
418
|
+
}),
|
419
|
+
});
|
416
420
|
|
417
|
-
test("Enum of maps", () => {
|
418
421
|
expect(mapWithEnum.name).toEqual("enum");
|
419
422
|
expect(mapWithEnum.child?.type).toEqual("a");
|
420
423
|
expect(mapWithEnum.child?.value).toEqual(5);
|
@@ -423,393 +426,464 @@ describe("Simple CoMap operations", async () => {
|
|
423
426
|
});
|
424
427
|
|
425
428
|
describe("CoMap resolution", async () => {
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
name = co.string;
|
432
|
-
twiceNested = co.ref(TwiceNestedMap);
|
429
|
+
test("loading a locally available map with deep resolve", async () => {
|
430
|
+
class Dog extends CoMap {
|
431
|
+
name = co.string;
|
432
|
+
breed = co.string;
|
433
|
+
}
|
433
434
|
|
434
|
-
|
435
|
-
|
435
|
+
class Person extends CoMap {
|
436
|
+
name = co.string;
|
437
|
+
age = co.number;
|
438
|
+
dog = co.ref(Dog);
|
436
439
|
}
|
437
|
-
}
|
438
440
|
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
441
|
+
const person = Person.create({
|
442
|
+
name: "John",
|
443
|
+
age: 20,
|
444
|
+
dog: Dog.create({ name: "Rex", breed: "Labrador" }),
|
445
|
+
});
|
446
|
+
|
447
|
+
const loadedPerson = await Person.load(person.id, {
|
448
|
+
resolve: {
|
449
|
+
dog: true,
|
450
|
+
},
|
451
|
+
});
|
452
|
+
|
453
|
+
assert(loadedPerson);
|
454
|
+
expect(loadedPerson.dog.name).toEqual("Rex");
|
455
|
+
});
|
456
|
+
|
457
|
+
test("loading a locally available map using autoload for the refs", async () => {
|
458
|
+
class Dog extends CoMap {
|
459
|
+
name = co.string;
|
460
|
+
breed = co.string;
|
461
|
+
}
|
443
462
|
|
444
|
-
|
445
|
-
|
463
|
+
class Person extends CoMap {
|
464
|
+
name = co.string;
|
465
|
+
age = co.number;
|
466
|
+
dog = co.ref(Dog);
|
446
467
|
}
|
447
|
-
}
|
448
468
|
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
469
|
+
const person = Person.create({
|
470
|
+
name: "John",
|
471
|
+
age: 20,
|
472
|
+
dog: Dog.create({ name: "Rex", breed: "Labrador" }),
|
453
473
|
});
|
454
474
|
|
455
|
-
const
|
475
|
+
const loadedPerson = await Person.load(person.id);
|
476
|
+
|
477
|
+
assert(loadedPerson);
|
478
|
+
expect(loadedPerson.dog?.name).toEqual("Rex");
|
479
|
+
});
|
480
|
+
|
481
|
+
test("loading a remotely available map with deep resolve", async () => {
|
482
|
+
class Dog extends CoMap {
|
483
|
+
name = co.string;
|
484
|
+
breed = co.string;
|
485
|
+
}
|
486
|
+
|
487
|
+
class Person extends CoMap {
|
488
|
+
name = co.string;
|
489
|
+
age = co.number;
|
490
|
+
dog = co.ref(Dog);
|
491
|
+
}
|
492
|
+
|
493
|
+
const group = Group.create();
|
494
|
+
group.addMember("everyone", "writer");
|
495
|
+
|
496
|
+
const person = Person.create(
|
456
497
|
{
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
{
|
461
|
-
name: "nested",
|
462
|
-
twiceNested: TwiceNestedMap.create(
|
463
|
-
{ taste: "sour" },
|
464
|
-
{ owner: me },
|
465
|
-
),
|
466
|
-
},
|
467
|
-
{ owner: me },
|
468
|
-
),
|
498
|
+
name: "John",
|
499
|
+
age: 20,
|
500
|
+
dog: Dog.create({ name: "Rex", breed: "Labrador" }, group),
|
469
501
|
},
|
470
|
-
|
502
|
+
group,
|
471
503
|
);
|
472
504
|
|
473
|
-
|
474
|
-
};
|
505
|
+
const userB = await createJazzTestAccount();
|
475
506
|
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
507
|
+
const loadedPerson = await Person.load(person.id, {
|
508
|
+
resolve: {
|
509
|
+
dog: true,
|
510
|
+
},
|
511
|
+
loadAs: userB,
|
512
|
+
});
|
480
513
|
|
481
|
-
|
482
|
-
expect(
|
483
|
-
expect(map.height).toEqual(10);
|
484
|
-
expect(map.nested?.name).toEqual("nested");
|
485
|
-
expect(map.nested?._fancyName).toEqual("Sir nested");
|
486
|
-
expect(map.nested?.id).toBeDefined();
|
487
|
-
expect(map.nested?.twiceNested?.taste).toEqual("sour");
|
514
|
+
assert(loadedPerson);
|
515
|
+
expect(loadedPerson.dog.name).toEqual("Rex");
|
488
516
|
});
|
489
517
|
|
490
|
-
test("
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
});
|
518
|
+
test("loading a remotely available map using autoload for the refs", async () => {
|
519
|
+
class Dog extends CoMap {
|
520
|
+
name = co.string;
|
521
|
+
breed = co.string;
|
522
|
+
}
|
496
523
|
|
497
|
-
|
498
|
-
|
524
|
+
class Person extends CoMap {
|
525
|
+
name = co.string;
|
526
|
+
age = co.number;
|
527
|
+
dog = co.ref(Dog);
|
499
528
|
}
|
500
|
-
me._raw.core.node.syncManager.addPeer(secondPeer);
|
501
|
-
const { account: meOnSecondPeer } =
|
502
|
-
await createJazzContextFromExistingCredentials({
|
503
|
-
credentials: {
|
504
|
-
accountID: me.id,
|
505
|
-
secret: me._raw.agentSecret,
|
506
|
-
},
|
507
|
-
sessionProvider: randomSessionProvider,
|
508
|
-
peersToLoadFrom: [initialAsPeer],
|
509
|
-
crypto: Crypto,
|
510
|
-
});
|
511
529
|
|
512
|
-
const
|
530
|
+
const group = Group.create();
|
531
|
+
group.addMember("everyone", "writer");
|
513
532
|
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
533
|
+
const person = Person.create(
|
534
|
+
{
|
535
|
+
name: "John",
|
536
|
+
age: 20,
|
537
|
+
dog: Dog.create({ name: "Rex", breed: "Labrador" }, group),
|
538
|
+
},
|
539
|
+
group,
|
540
|
+
);
|
519
541
|
|
520
|
-
const
|
521
|
-
|
542
|
+
const userB = await createJazzTestAccount();
|
543
|
+
const loadedPerson = await Person.load(person.id, {
|
544
|
+
loadAs: userB,
|
522
545
|
});
|
523
546
|
|
524
|
-
|
525
|
-
expect(
|
526
|
-
expect(loadedMap?._refs.nested?.value).toEqual(loadedNestedMap);
|
527
|
-
expect(loadedMap?.nested?.twiceNested?.taste).toEqual(undefined);
|
547
|
+
assert(loadedPerson);
|
548
|
+
expect(loadedPerson.dog).toBe(null);
|
528
549
|
|
529
|
-
|
530
|
-
map.nested!.twiceNested!.id,
|
531
|
-
{ loadAs: meOnSecondPeer },
|
532
|
-
);
|
550
|
+
await waitFor(() => expect(loadedPerson.dog).toBeTruthy());
|
533
551
|
|
534
|
-
expect(
|
535
|
-
|
536
|
-
|
537
|
-
|
552
|
+
expect(loadedPerson.dog?.name).toEqual("Rex");
|
553
|
+
});
|
554
|
+
|
555
|
+
test("accessing the value refs", async () => {
|
556
|
+
class Dog extends CoMap {
|
557
|
+
name = co.string;
|
558
|
+
breed = co.string;
|
559
|
+
}
|
560
|
+
|
561
|
+
class Person extends CoMap {
|
562
|
+
name = co.string;
|
563
|
+
age = co.number;
|
564
|
+
dog = co.ref(Dog);
|
565
|
+
}
|
538
566
|
|
539
|
-
const
|
567
|
+
const group = Group.create();
|
568
|
+
group.addMember("everyone", "writer");
|
569
|
+
|
570
|
+
const person = Person.create(
|
540
571
|
{
|
541
|
-
name: "
|
542
|
-
|
543
|
-
|
544
|
-
{ owner: meOnSecondPeer },
|
545
|
-
),
|
572
|
+
name: "John",
|
573
|
+
age: 20,
|
574
|
+
dog: Dog.create({ name: "Rex", breed: "Labrador" }, group),
|
546
575
|
},
|
547
|
-
|
576
|
+
group,
|
548
577
|
);
|
549
578
|
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
579
|
+
const userB = await createJazzTestAccount();
|
580
|
+
const loadedPerson = await Person.load(person.id, {
|
581
|
+
loadAs: userB,
|
582
|
+
});
|
583
|
+
|
584
|
+
assert(loadedPerson);
|
585
|
+
|
586
|
+
expect(loadedPerson._refs.dog.id).toBe(person.dog!.id);
|
587
|
+
|
588
|
+
const dog = await loadedPerson._refs.dog.load();
|
589
|
+
|
590
|
+
assert(dog);
|
591
|
+
|
592
|
+
expect(dog.name).toEqual("Rex");
|
556
593
|
});
|
557
594
|
|
558
|
-
async
|
559
|
-
|
595
|
+
test("subscription on a locally available map with deep resolve", async () => {
|
596
|
+
class Dog extends CoMap {
|
597
|
+
name = co.string;
|
598
|
+
breed = co.string;
|
599
|
+
}
|
600
|
+
|
601
|
+
class Person extends CoMap {
|
602
|
+
name = co.string;
|
603
|
+
age = co.number;
|
604
|
+
dog = co.ref(Dog);
|
605
|
+
}
|
560
606
|
|
561
|
-
const
|
562
|
-
|
563
|
-
|
607
|
+
const person = Person.create({
|
608
|
+
name: "John",
|
609
|
+
age: 20,
|
610
|
+
dog: Dog.create({ name: "Rex", breed: "Labrador" }),
|
564
611
|
});
|
565
612
|
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
secret: me._raw.agentSecret,
|
613
|
+
const updates: Resolved<Person, { dog: true }>[] = [];
|
614
|
+
const spy = vi.fn((person) => updates.push(person));
|
615
|
+
|
616
|
+
Person.subscribe(
|
617
|
+
person.id,
|
618
|
+
{
|
619
|
+
resolve: {
|
620
|
+
dog: true,
|
575
621
|
},
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
});
|
622
|
+
},
|
623
|
+
spy,
|
624
|
+
);
|
580
625
|
|
581
|
-
|
626
|
+
expect(spy).not.toHaveBeenCalled();
|
582
627
|
|
583
|
-
await
|
628
|
+
await waitFor(() => expect(spy).toHaveBeenCalled());
|
584
629
|
|
585
|
-
|
586
|
-
// Read to property to trigger loading
|
587
|
-
subscribedMap.nested?.twiceNested?.taste;
|
588
|
-
void queue.push(subscribedMap);
|
589
|
-
});
|
630
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
590
631
|
|
591
|
-
|
592
|
-
|
632
|
+
expect(updates[0]?.dog.name).toEqual("Rex");
|
633
|
+
|
634
|
+
person.dog!.name = "Fido";
|
593
635
|
|
594
|
-
|
595
|
-
const { queue } = await setupTest();
|
636
|
+
await waitFor(() => expect(spy).toHaveBeenCalledTimes(2));
|
596
637
|
|
597
|
-
|
598
|
-
expect(update1.nested).toEqual(null);
|
638
|
+
expect(updates[1]?.dog.name).toEqual("Fido");
|
599
639
|
|
600
|
-
|
601
|
-
expect(update2.nested?.name).toEqual("nested");
|
640
|
+
expect(spy).toHaveBeenCalledTimes(2);
|
602
641
|
});
|
603
642
|
|
604
|
-
test("
|
605
|
-
|
643
|
+
test("subscription on a locally available map with autoload", async () => {
|
644
|
+
class Dog extends CoMap {
|
645
|
+
name = co.string;
|
646
|
+
breed = co.string;
|
647
|
+
}
|
606
648
|
|
607
|
-
|
608
|
-
|
609
|
-
|
649
|
+
class Person extends CoMap {
|
650
|
+
name = co.string;
|
651
|
+
age = co.number;
|
652
|
+
dog = co.ref(Dog);
|
653
|
+
}
|
610
654
|
|
611
|
-
|
655
|
+
const person = Person.create({
|
656
|
+
name: "John",
|
657
|
+
age: 20,
|
658
|
+
dog: Dog.create({ name: "Rex", breed: "Labrador" }),
|
659
|
+
});
|
612
660
|
|
613
|
-
|
614
|
-
const
|
615
|
-
expect(update3.nested?.name).toEqual("nestedUpdated");
|
661
|
+
const updates: Person[] = [];
|
662
|
+
const spy = vi.fn((person) => updates.push(person));
|
616
663
|
|
617
|
-
|
618
|
-
expect(oldTwiceNested?.taste).toEqual("sour");
|
619
|
-
});
|
664
|
+
Person.subscribe(person.id, {}, spy);
|
620
665
|
|
621
|
-
|
622
|
-
const { meOnSecondPeer, queue } = await setupTest();
|
666
|
+
expect(spy).not.toHaveBeenCalled();
|
623
667
|
|
624
|
-
|
625
|
-
await queue.next();
|
626
|
-
await queue.next();
|
668
|
+
await waitFor(() => expect(spy).toHaveBeenCalled());
|
627
669
|
|
628
|
-
|
670
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
629
671
|
|
630
|
-
|
631
|
-
{
|
632
|
-
taste: "sweet",
|
633
|
-
},
|
634
|
-
{ owner: meOnSecondPeer },
|
635
|
-
);
|
672
|
+
expect(updates[0]?.dog?.name).toEqual("Rex");
|
636
673
|
|
637
|
-
|
638
|
-
{
|
639
|
-
name: "newNested",
|
640
|
-
twiceNested: newTwiceNested,
|
641
|
-
},
|
642
|
-
{ owner: meOnSecondPeer },
|
643
|
-
);
|
674
|
+
person.dog!.name = "Fido";
|
644
675
|
|
645
|
-
|
676
|
+
await waitFor(() => expect(spy).toHaveBeenCalledTimes(2));
|
646
677
|
|
647
|
-
|
648
|
-
const update4 = (await queue.next()).value;
|
678
|
+
expect(updates[1]?.dog?.name).toEqual("Fido");
|
649
679
|
|
650
|
-
expect(
|
651
|
-
expect(update4.nested?.twiceNested?.taste).toEqual("sweet");
|
680
|
+
expect(spy).toHaveBeenCalledTimes(2);
|
652
681
|
});
|
653
682
|
|
654
|
-
test("
|
655
|
-
|
683
|
+
test("subscription on a locally available map with syncResolution", async () => {
|
684
|
+
class Dog extends CoMap {
|
685
|
+
name = co.string;
|
686
|
+
breed = co.string;
|
687
|
+
}
|
656
688
|
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
689
|
+
class Person extends CoMap {
|
690
|
+
name = co.string;
|
691
|
+
age = co.number;
|
692
|
+
dog = co.ref(Dog);
|
693
|
+
}
|
661
694
|
|
662
|
-
const
|
663
|
-
|
664
|
-
|
665
|
-
|
695
|
+
const person = Person.create({
|
696
|
+
name: "John",
|
697
|
+
age: 20,
|
698
|
+
dog: Dog.create({ name: "Rex", breed: "Labrador" }),
|
699
|
+
});
|
700
|
+
|
701
|
+
const updates: Person[] = [];
|
702
|
+
const spy = vi.fn((person) => updates.push(person));
|
666
703
|
|
667
|
-
|
704
|
+
subscribeToCoValue(
|
705
|
+
Person,
|
706
|
+
person.id,
|
668
707
|
{
|
669
|
-
|
670
|
-
|
708
|
+
syncResolution: true,
|
709
|
+
loadAs: Account.getMe(),
|
671
710
|
},
|
672
|
-
|
711
|
+
spy,
|
673
712
|
);
|
674
713
|
|
675
|
-
|
714
|
+
expect(spy).toHaveBeenCalled();
|
715
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
716
|
+
|
717
|
+
expect(updates[0]?.dog?.name).toEqual("Rex");
|
718
|
+
|
719
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
720
|
+
|
721
|
+
person.dog!.name = "Fido";
|
676
722
|
|
677
|
-
|
678
|
-
await queue.next();
|
679
|
-
await queue.next();
|
723
|
+
expect(spy).toHaveBeenCalledTimes(2);
|
680
724
|
|
681
|
-
|
682
|
-
const update5 = (await queue.next()).value;
|
683
|
-
expect(update5.nested?.twiceNested?.taste).toEqual("salty");
|
725
|
+
expect(updates[1]?.dog?.name).toEqual("Fido");
|
684
726
|
|
685
|
-
|
686
|
-
const update6 = (await queue.next()).value;
|
687
|
-
expect(update6.nested?.twiceNested?.taste).toEqual("umami");
|
727
|
+
expect(spy).toHaveBeenCalledTimes(2);
|
688
728
|
});
|
689
729
|
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
730
|
+
test("subscription on a remotely available map with deep resolve", async () => {
|
731
|
+
class Dog extends CoMap {
|
732
|
+
name = co.string;
|
733
|
+
breed = co.string;
|
734
|
+
}
|
694
735
|
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
}
|
736
|
+
class Person extends CoMap {
|
737
|
+
name = co.string;
|
738
|
+
age = co.number;
|
739
|
+
dog = co.ref(Dog);
|
740
|
+
}
|
741
|
+
|
742
|
+
const group = Group.create();
|
743
|
+
group.addMember("everyone", "writer");
|
700
744
|
|
701
|
-
const
|
745
|
+
const person = Person.create(
|
702
746
|
{
|
703
|
-
|
747
|
+
name: "John",
|
748
|
+
age: 20,
|
749
|
+
dog: Dog.create({ name: "Rex", breed: "Labrador" }, group),
|
704
750
|
},
|
705
|
-
|
751
|
+
group,
|
706
752
|
);
|
707
753
|
|
708
|
-
|
709
|
-
expect(mapWithout.nested).toEqual(undefined);
|
754
|
+
const userB = await createJazzTestAccount();
|
710
755
|
|
711
|
-
const
|
756
|
+
const updates: Resolved<Person, { dog: true }>[] = [];
|
757
|
+
const spy = vi.fn((person) => updates.push(person));
|
758
|
+
|
759
|
+
Person.subscribe(
|
760
|
+
person.id,
|
712
761
|
{
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
twiceNested: TwiceNestedMap.create(
|
718
|
-
{ taste: "sour" },
|
719
|
-
{ owner: me },
|
720
|
-
),
|
721
|
-
},
|
722
|
-
{ owner: me },
|
723
|
-
),
|
762
|
+
resolve: {
|
763
|
+
dog: true,
|
764
|
+
},
|
765
|
+
loadAs: userB,
|
724
766
|
},
|
725
|
-
|
767
|
+
spy,
|
726
768
|
);
|
727
769
|
|
728
|
-
expect(
|
729
|
-
|
730
|
-
expect(
|
731
|
-
|
770
|
+
expect(spy).not.toHaveBeenCalled();
|
771
|
+
|
772
|
+
await waitFor(() => expect(spy).toHaveBeenCalled());
|
773
|
+
|
774
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
775
|
+
|
776
|
+
expect(updates[0]?.dog.name).toEqual("Rex");
|
777
|
+
|
778
|
+
person.dog!.name = "Fido";
|
779
|
+
|
780
|
+
await waitFor(() => expect(spy).toHaveBeenCalledTimes(2));
|
781
|
+
|
782
|
+
expect(updates[1]?.dog.name).toEqual("Fido");
|
783
|
+
|
784
|
+
expect(spy).toHaveBeenCalledTimes(2);
|
732
785
|
});
|
733
786
|
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
interface TestRecord extends Record<string, number> {}
|
787
|
+
test("subscription on a remotely available map with autoload", async () => {
|
788
|
+
class Dog extends CoMap {
|
789
|
+
name = co.string;
|
790
|
+
breed = co.string;
|
791
|
+
}
|
740
792
|
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
}
|
793
|
+
class Person extends CoMap {
|
794
|
+
name = co.string;
|
795
|
+
age = co.number;
|
796
|
+
dog = co.ref(Dog);
|
797
|
+
}
|
798
|
+
|
799
|
+
const group = Group.create();
|
800
|
+
group.addMember("everyone", "writer");
|
746
801
|
|
747
|
-
const
|
802
|
+
const person = Person.create(
|
748
803
|
{
|
749
|
-
|
750
|
-
|
804
|
+
name: "John",
|
805
|
+
age: 20,
|
806
|
+
dog: Dog.create({ name: "Rex", breed: "Labrador" }, group),
|
751
807
|
},
|
752
|
-
|
808
|
+
group,
|
753
809
|
);
|
754
810
|
|
755
|
-
|
756
|
-
|
757
|
-
expect(record.other).toEqual(3);
|
758
|
-
expect(record._raw.get("other")).toEqual(3);
|
759
|
-
expect(Object.keys(record)).toEqual(["height", "other"]);
|
760
|
-
expect(record.toJSON()).toMatchObject({
|
761
|
-
_type: "CoMap",
|
762
|
-
height: 5,
|
763
|
-
id: expect.any(String),
|
764
|
-
other: 3,
|
765
|
-
});
|
766
|
-
});
|
811
|
+
const updates: Person[] = [];
|
812
|
+
const spy = vi.fn((person) => updates.push(person));
|
767
813
|
|
768
|
-
|
814
|
+
const userB = await createJazzTestAccount();
|
769
815
|
|
770
|
-
|
771
|
-
|
772
|
-
creationProps: { name: "Hermes Puggington" },
|
773
|
-
crypto: Crypto,
|
774
|
-
});
|
775
|
-
|
776
|
-
const record = TestRecord2.create(
|
816
|
+
Person.subscribe(
|
817
|
+
person.id,
|
777
818
|
{
|
778
|
-
|
779
|
-
other: 3,
|
819
|
+
loadAs: userB,
|
780
820
|
},
|
781
|
-
|
821
|
+
spy,
|
782
822
|
);
|
783
823
|
|
784
|
-
expect(
|
785
|
-
|
786
|
-
expect(
|
787
|
-
|
788
|
-
expect(
|
824
|
+
expect(spy).not.toHaveBeenCalled();
|
825
|
+
|
826
|
+
await waitFor(() => expect(spy).toHaveBeenCalled());
|
827
|
+
|
828
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
829
|
+
|
830
|
+
expect(updates[0]?.dog?.name).toEqual("Rex");
|
831
|
+
|
832
|
+
person.dog!.name = "Fido";
|
833
|
+
|
834
|
+
await waitFor(() => expect(spy).toHaveBeenCalledTimes(2));
|
835
|
+
|
836
|
+
expect(updates[1]?.dog?.name).toEqual("Fido");
|
837
|
+
|
838
|
+
expect(spy).toHaveBeenCalledTimes(2);
|
789
839
|
});
|
790
840
|
|
791
|
-
|
841
|
+
test("replacing nested object triggers updates", async () => {
|
842
|
+
class Dog extends CoMap {
|
843
|
+
name = co.string;
|
844
|
+
breed = co.string;
|
845
|
+
}
|
792
846
|
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
847
|
+
class Person extends CoMap {
|
848
|
+
name = co.string;
|
849
|
+
age = co.number;
|
850
|
+
dog = co.ref(Dog);
|
851
|
+
}
|
852
|
+
|
853
|
+
const person = Person.create({
|
854
|
+
name: "John",
|
855
|
+
age: 20,
|
856
|
+
dog: Dog.create({ name: "Rex", breed: "Labrador" }),
|
797
857
|
});
|
798
858
|
|
799
|
-
const
|
859
|
+
const updates: Resolved<Person, { dog: true }>[] = [];
|
860
|
+
const spy = vi.fn((person) => updates.push(person));
|
861
|
+
|
862
|
+
Person.subscribe(
|
863
|
+
person.id,
|
800
864
|
{
|
801
|
-
|
802
|
-
|
865
|
+
resolve: {
|
866
|
+
dog: true,
|
867
|
+
},
|
803
868
|
},
|
804
|
-
|
869
|
+
spy,
|
805
870
|
);
|
806
871
|
|
807
|
-
expect(
|
808
|
-
|
809
|
-
expect(
|
810
|
-
|
811
|
-
expect(
|
812
|
-
|
872
|
+
expect(spy).not.toHaveBeenCalled();
|
873
|
+
|
874
|
+
await waitFor(() => expect(spy).toHaveBeenCalled());
|
875
|
+
|
876
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
877
|
+
|
878
|
+
expect(updates[0]?.dog.name).toEqual("Rex");
|
879
|
+
|
880
|
+
person.dog!.name = "Fido";
|
881
|
+
|
882
|
+
await waitFor(() => expect(spy).toHaveBeenCalledTimes(2));
|
883
|
+
|
884
|
+
expect(updates[1]?.dog.name).toEqual("Fido");
|
885
|
+
|
886
|
+
expect(spy).toHaveBeenCalledTimes(2);
|
813
887
|
});
|
814
888
|
});
|
815
889
|
|
@@ -1134,16 +1208,16 @@ describe("CoMap Typescript validation", async () => {
|
|
1134
1208
|
|
1135
1209
|
describe("Creating and finding unique CoMaps", async () => {
|
1136
1210
|
test("Creating and finding unique CoMaps", async () => {
|
1137
|
-
const
|
1138
|
-
creationProps: { name: "Tester McTesterson" },
|
1139
|
-
crypto: Crypto,
|
1140
|
-
});
|
1211
|
+
const group = Group.create();
|
1141
1212
|
|
1142
|
-
|
1143
|
-
|
1144
|
-
|
1213
|
+
class Person extends CoMap {
|
1214
|
+
name = co.string;
|
1215
|
+
_height = co.number;
|
1216
|
+
birthday = co.Date;
|
1217
|
+
color = co.string;
|
1218
|
+
}
|
1145
1219
|
|
1146
|
-
const alice =
|
1220
|
+
const alice = Person.create(
|
1147
1221
|
{
|
1148
1222
|
name: "Alice",
|
1149
1223
|
_height: 100,
|
@@ -1153,8 +1227,7 @@ describe("Creating and finding unique CoMaps", async () => {
|
|
1153
1227
|
{ owner: group, unique: { name: "Alice" } },
|
1154
1228
|
);
|
1155
1229
|
|
1156
|
-
const foundAlice =
|
1157
|
-
|
1230
|
+
const foundAlice = Person.findUnique({ name: "Alice" }, group.id);
|
1158
1231
|
expect(foundAlice).toEqual(alice.id);
|
1159
1232
|
});
|
1160
1233
|
});
|