jazz-tools 0.7.0-alpha.4 → 0.7.0-alpha.42

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. package/.eslintrc.cjs +3 -10
  2. package/.prettierrc.js +9 -0
  3. package/.turbo/turbo-build.log +3 -19
  4. package/.turbo/turbo-lint.log +4 -0
  5. package/.turbo/turbo-test.log +140 -0
  6. package/CHANGELOG.md +252 -0
  7. package/README.md +10 -2
  8. package/dist/coValues/account.js +104 -50
  9. package/dist/coValues/account.js.map +1 -1
  10. package/dist/coValues/coList.js +165 -112
  11. package/dist/coValues/coList.js.map +1 -1
  12. package/dist/coValues/coMap.js +243 -163
  13. package/dist/coValues/coMap.js.map +1 -1
  14. package/dist/coValues/coStream.js +256 -73
  15. package/dist/coValues/coStream.js.map +1 -1
  16. package/dist/coValues/deepLoading.js +57 -0
  17. package/dist/coValues/deepLoading.js.map +1 -0
  18. package/dist/coValues/extensions/imageDef.js +14 -8
  19. package/dist/coValues/extensions/imageDef.js.map +1 -1
  20. package/dist/coValues/group.js +49 -38
  21. package/dist/coValues/group.js.map +1 -1
  22. package/dist/coValues/interfaces.js +66 -26
  23. package/dist/coValues/interfaces.js.map +1 -1
  24. package/dist/implementation/devtoolsFormatters.js +114 -0
  25. package/dist/implementation/devtoolsFormatters.js.map +1 -0
  26. package/dist/implementation/refs.js +60 -19
  27. package/dist/implementation/refs.js.map +1 -1
  28. package/dist/implementation/schema.js +44 -1
  29. package/dist/implementation/schema.js.map +1 -1
  30. package/dist/implementation/subscriptionScope.js +19 -1
  31. package/dist/implementation/subscriptionScope.js.map +1 -1
  32. package/dist/implementation/symbols.js +5 -0
  33. package/dist/implementation/symbols.js.map +1 -0
  34. package/dist/index.js +4 -5
  35. package/dist/index.js.map +1 -1
  36. package/dist/internal.js +4 -1
  37. package/dist/internal.js.map +1 -1
  38. package/dist/tests/coList.test.js +51 -52
  39. package/dist/tests/coList.test.js.map +1 -1
  40. package/dist/tests/coMap.test.js +196 -75
  41. package/dist/tests/coMap.test.js.map +1 -1
  42. package/dist/tests/coStream.test.js +95 -85
  43. package/dist/tests/coStream.test.js.map +1 -1
  44. package/dist/tests/deepLoading.test.js +188 -0
  45. package/dist/tests/deepLoading.test.js.map +1 -0
  46. package/dist/tests/groupsAndAccounts.test.js +83 -0
  47. package/dist/tests/groupsAndAccounts.test.js.map +1 -0
  48. package/package.json +17 -9
  49. package/src/coValues/account.ts +184 -153
  50. package/src/coValues/coList.ts +220 -173
  51. package/src/coValues/coMap.ts +322 -312
  52. package/src/coValues/coStream.ts +397 -135
  53. package/src/coValues/deepLoading.ts +215 -0
  54. package/src/coValues/extensions/imageDef.ts +16 -17
  55. package/src/coValues/group.ts +95 -111
  56. package/src/coValues/interfaces.ts +217 -115
  57. package/src/implementation/devtoolsFormatters.ts +110 -0
  58. package/src/implementation/inspect.ts +1 -1
  59. package/src/implementation/refs.ts +91 -38
  60. package/src/implementation/schema.ts +87 -46
  61. package/src/implementation/subscriptionScope.ts +44 -12
  62. package/src/implementation/symbols.ts +11 -0
  63. package/src/index.ts +13 -9
  64. package/src/internal.ts +6 -2
  65. package/src/tests/coList.test.ts +67 -66
  66. package/src/tests/coMap.test.ts +226 -123
  67. package/src/tests/coStream.test.ts +141 -131
  68. package/src/tests/deepLoading.test.ts +301 -0
  69. package/src/tests/groupsAndAccounts.test.ts +91 -0
@@ -0,0 +1,301 @@
1
+ const Crypto = await WasmCrypto.create();
2
+ import { expect, describe, test, expectTypeOf } from "vitest";
3
+ import { connectedPeers } from "cojson/src/streamUtils.js";
4
+ import {
5
+ Account,
6
+ CoList,
7
+ CoMap,
8
+ CoStream,
9
+ SessionID,
10
+ WasmCrypto,
11
+ co,
12
+ Profile,
13
+ isControlledAccount,
14
+ ID,
15
+ } from "../index.js";
16
+ import { newRandomSessionID } from "cojson/src/coValueCore.js";
17
+
18
+ class TestMap extends CoMap {
19
+ list = co.ref(TestList);
20
+ optionalRef? = co.ref(InnermostMap);
21
+ }
22
+
23
+ class TestList extends CoList.Of(co.ref(() => InnerMap)) {}
24
+
25
+ class InnerMap extends CoMap {
26
+ stream = co.ref(TestStream);
27
+ }
28
+
29
+ class TestStream extends CoStream.Of(co.ref(() => InnermostMap)) {}
30
+
31
+ class InnermostMap extends CoMap {
32
+ value = co.string;
33
+ }
34
+
35
+ describe("Deep loading with depth arg", async () => {
36
+ const me = await Account.create({
37
+ creationProps: { name: "Hermes Puggington" },
38
+ crypto: Crypto,
39
+ });
40
+
41
+ const [initialAsPeer, secondPeer] = connectedPeers("initial", "second", {
42
+ peer1role: "server",
43
+ peer2role: "client",
44
+ });
45
+ if (!isControlledAccount(me)) {
46
+ throw "me is not a controlled account";
47
+ }
48
+ me._raw.core.node.syncManager.addPeer(secondPeer);
49
+ const meOnSecondPeer = await Account.become({
50
+ accountID: me.id,
51
+ accountSecret: me._raw.agentSecret,
52
+ peersToLoadFrom: [initialAsPeer],
53
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
54
+ sessionID: newRandomSessionID(me.id as any),
55
+ crypto: Crypto,
56
+ });
57
+
58
+ test("loading a deeply nested object will wait until all required refs are loaded", async () => {
59
+ const map = TestMap.create(
60
+ {
61
+ list: TestList.create(
62
+ [
63
+ InnerMap.create(
64
+ {
65
+ stream: TestStream.create(
66
+ [
67
+ InnermostMap.create(
68
+ { value: "hello" },
69
+ { owner: me },
70
+ ),
71
+ ],
72
+ { owner: me },
73
+ ),
74
+ },
75
+ { owner: me },
76
+ ),
77
+ ],
78
+ { owner: me },
79
+ ),
80
+ },
81
+ { owner: me },
82
+ );
83
+
84
+ const map1 = await TestMap.load(map.id, meOnSecondPeer, {});
85
+ expectTypeOf(map1).toEqualTypeOf<TestMap | undefined>();
86
+ if (map1 === undefined) {
87
+ throw new Error("map1 is undefined");
88
+ }
89
+ expect(map1.list).toBe(null);
90
+
91
+ const map2 = await TestMap.load(map.id, meOnSecondPeer, { list: [] });
92
+ expectTypeOf(map2).toEqualTypeOf<
93
+ | (TestMap & {
94
+ list: TestList;
95
+ })
96
+ | undefined
97
+ >();
98
+ if (map2 === undefined) {
99
+ throw new Error("map2 is undefined");
100
+ }
101
+ expect(map2.list).not.toBe(null);
102
+ expect(map2.list[0]).toBe(null);
103
+
104
+ const map3 = await TestMap.load(map.id, meOnSecondPeer, { list: [{}] });
105
+ expectTypeOf(map3).toEqualTypeOf<
106
+ | (TestMap & {
107
+ list: TestList & InnerMap[];
108
+ })
109
+ | undefined
110
+ >();
111
+ if (map3 === undefined) {
112
+ throw new Error("map3 is undefined");
113
+ }
114
+ expect(map3.list[0]).not.toBe(null);
115
+ expect(map3.list[0]?.stream).toBe(null);
116
+
117
+ const map3a = await TestMap.load(map.id, meOnSecondPeer, {
118
+ optionalRef: {},
119
+ });
120
+ expectTypeOf(map3a).toEqualTypeOf<
121
+ | (TestMap & {
122
+ optionalRef: InnermostMap | undefined;
123
+ })
124
+ | undefined
125
+ >();
126
+
127
+ const map4 = await TestMap.load(map.id, meOnSecondPeer, {
128
+ list: [{ stream: [] }],
129
+ });
130
+ expectTypeOf(map4).toEqualTypeOf<
131
+ | (TestMap & {
132
+ list: TestList & (InnerMap & { stream: TestStream })[];
133
+ })
134
+ | undefined
135
+ >();
136
+ if (map4 === undefined) {
137
+ throw new Error("map4 is undefined");
138
+ }
139
+ expect(map4.list[0]?.stream).not.toBe(null);
140
+ // TODO: we should expect null here, but apparently we don't even have the id/ref?
141
+ expect(map4.list[0]?.stream?.[me.id]?.value).not.toBeDefined();
142
+ expect(map4.list[0]?.stream?.byMe?.value).not.toBeDefined();
143
+
144
+ const map5 = await TestMap.load(map.id, meOnSecondPeer, {
145
+ list: [{ stream: [{}] }],
146
+ });
147
+ type ExpectedMap5 =
148
+ | (TestMap & {
149
+ list: TestList &
150
+ (InnerMap & {
151
+ stream: TestStream & {
152
+ byMe?: { value: InnermostMap };
153
+ inCurrentSession?: { value: InnermostMap };
154
+ perSession: {
155
+ [sessionID: SessionID]: {
156
+ value: InnermostMap;
157
+ };
158
+ };
159
+ } & {
160
+ [key: ID<Account>]: { value: InnermostMap };
161
+ };
162
+ })[];
163
+ })
164
+ | undefined;
165
+
166
+ expectTypeOf(map5).toEqualTypeOf<ExpectedMap5>();
167
+ if (map5 === undefined) {
168
+ throw new Error("map5 is undefined");
169
+ }
170
+ expect(map5.list[0]?.stream?.[me.id]?.value).not.toBe(null);
171
+ expect(map5.list[0]?.stream?.byMe?.value).not.toBe(null);
172
+ });
173
+ });
174
+
175
+ class CustomProfile extends Profile {
176
+ stream = co.ref(TestStream);
177
+ }
178
+
179
+ class CustomAccount extends Account {
180
+ profile = co.ref(CustomProfile);
181
+ root = co.ref(TestMap);
182
+
183
+ async migrate(creationProps?: { name: string } | undefined) {
184
+ if (creationProps) {
185
+ this.profile = CustomProfile.create(
186
+ {
187
+ name: creationProps.name,
188
+ stream: TestStream.create([], { owner: this }),
189
+ },
190
+ { owner: this },
191
+ );
192
+ this.root = TestMap.create(
193
+ { list: TestList.create([], { owner: this }) },
194
+ { owner: this },
195
+ );
196
+ }
197
+
198
+ const thisLoaded = await CustomAccount.load(this, {
199
+ profile: { stream: [] },
200
+ root: { list: [] },
201
+ });
202
+ expectTypeOf(thisLoaded).toEqualTypeOf<
203
+ | (CustomAccount & {
204
+ profile: CustomProfile & {
205
+ stream: TestStream;
206
+ };
207
+ root: TestMap & {
208
+ list: TestList;
209
+ };
210
+ })
211
+ | undefined
212
+ >();
213
+ }
214
+ }
215
+
216
+ test("Deep loading within account", async () => {
217
+ const me = await CustomAccount.create({
218
+ creationProps: { name: "Hermes Puggington" },
219
+ crypto: Crypto,
220
+ });
221
+
222
+ const meLoaded = await CustomAccount.load(me, {
223
+ profile: { stream: [] },
224
+ root: { list: [] },
225
+ });
226
+ expectTypeOf(meLoaded).toEqualTypeOf<
227
+ | (CustomAccount & {
228
+ profile: CustomProfile & {
229
+ stream: TestStream;
230
+ };
231
+ root: TestMap & {
232
+ list: TestList;
233
+ };
234
+ })
235
+ | undefined
236
+ >();
237
+ if (meLoaded === undefined) {
238
+ throw new Error("meLoaded is undefined");
239
+ }
240
+ expect(meLoaded.profile.stream).not.toBe(null);
241
+ expect(meLoaded.root.list).not.toBe(null);
242
+ });
243
+
244
+ class RecordLike extends CoMap.Record(co.ref(TestMap)) {}
245
+
246
+ test("Deep loading a record-like coMap", async () => {
247
+ const me = await Account.create({
248
+ creationProps: { name: "Hermes Puggington" },
249
+ crypto: Crypto,
250
+ });
251
+
252
+ const [initialAsPeer, secondPeer] = connectedPeers("initial", "second", {
253
+ peer1role: "server",
254
+ peer2role: "client",
255
+ });
256
+ if (!isControlledAccount(me)) {
257
+ throw "me is not a controlled account";
258
+ }
259
+ me._raw.core.node.syncManager.addPeer(secondPeer);
260
+ const meOnSecondPeer = await Account.become({
261
+ accountID: me.id,
262
+ accountSecret: me._raw.agentSecret,
263
+ peersToLoadFrom: [initialAsPeer],
264
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
265
+ sessionID: newRandomSessionID(me.id as any),
266
+ crypto: Crypto,
267
+ });
268
+
269
+ const record = RecordLike.create(
270
+ {
271
+ key1: TestMap.create(
272
+ { list: TestList.create([], { owner: me }) },
273
+ { owner: me },
274
+ ),
275
+ key2: TestMap.create(
276
+ { list: TestList.create([], { owner: me }) },
277
+ { owner: me },
278
+ ),
279
+ },
280
+ { owner: me },
281
+ );
282
+
283
+ const recordLoaded = await RecordLike.load(record.id, meOnSecondPeer, [
284
+ { list: [{}] },
285
+ ]);
286
+ expectTypeOf(recordLoaded).toEqualTypeOf<
287
+ | (RecordLike & {
288
+ [key: string]: TestMap & {
289
+ list: TestList & InnerMap[];
290
+ };
291
+ })
292
+ | undefined
293
+ >();
294
+ if (recordLoaded === undefined) {
295
+ throw new Error("recordLoaded is undefined");
296
+ }
297
+ expect(recordLoaded.key1?.list).not.toBe(null);
298
+ expect(recordLoaded.key1?.list).not.toBe(undefined);
299
+ expect(recordLoaded.key2?.list).not.toBe(null);
300
+ expect(recordLoaded.key2?.list).not.toBe(undefined);
301
+ });
@@ -0,0 +1,91 @@
1
+ import { expect, describe, test } from "vitest";
2
+ import { Account, CoMap, co, Group, WasmCrypto } from "../index.js";
3
+
4
+ const Crypto = await WasmCrypto.create();
5
+
6
+ describe("Custom accounts and groups", async () => {
7
+ class CustomProfile extends CoMap {
8
+ name = co.string;
9
+ color = co.string;
10
+ }
11
+
12
+ class CustomAccount extends Account {
13
+ profile = co.ref(CustomProfile);
14
+ root = co.ref(CoMap);
15
+
16
+ migrate(creationProps?: { name: string }) {
17
+ if (creationProps) {
18
+ const profileGroup = Group.create({ owner: this });
19
+ profileGroup.addMember("everyone", "reader");
20
+ this.profile = CustomProfile.create(
21
+ { name: creationProps.name, color: "blue" },
22
+ { owner: this },
23
+ );
24
+ }
25
+ }
26
+ }
27
+
28
+ class CustomGroup extends Group {
29
+ profile = co.null;
30
+ root = co.null;
31
+ [co.members] = co.ref(CustomAccount);
32
+
33
+ get nMembers() {
34
+ return this.members.length;
35
+ }
36
+ }
37
+
38
+ test("Custom account and group", async () => {
39
+ const me = await CustomAccount.create({
40
+ creationProps: { name: "Hermes Puggington" },
41
+ crypto: Crypto,
42
+ });
43
+
44
+ expect(me.profile).toBeDefined();
45
+ expect(me.profile?.name).toBe("Hermes Puggington");
46
+ expect(me.profile?.color).toBe("blue");
47
+
48
+ const group = new CustomGroup({ owner: me });
49
+ group.addMember("everyone", "reader");
50
+
51
+ expect(group.members).toMatchObject([
52
+ { id: me.id, role: "admin" },
53
+ { id: "everyone", role: "reader" },
54
+ ]);
55
+
56
+ expect(group.nMembers).toBe(2);
57
+
58
+ await new Promise<void>((resolve) => {
59
+ CustomGroup.subscribe(group, {}, (update) => {
60
+ const meAsMember = update.members.find((member) => {
61
+ return member.id === me.id && member.account?.profile;
62
+ });
63
+ if (meAsMember) {
64
+ expect(meAsMember.account?.profile?.name).toBe(
65
+ "Hermes Puggington",
66
+ );
67
+ expect(meAsMember.account?.profile?.color).toBe("blue");
68
+ resolve();
69
+ }
70
+ });
71
+ });
72
+
73
+ class MyMap extends CoMap {
74
+ name = co.string;
75
+ }
76
+
77
+ const map = MyMap.create({ name: "test" }, { owner: group });
78
+
79
+ const meAsCastMember = map._owner
80
+ .as(CustomGroup)
81
+ .members.find((member) => member.id === me.id);
82
+ expect(meAsCastMember?.account?.profile?.name).toBe(
83
+ "Hermes Puggington",
84
+ );
85
+ expect(meAsCastMember?.account?.profile?.color).toBe("blue");
86
+
87
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
88
+ expect((map._owner as any).nMembers).toBeUndefined();
89
+ expect(map._owner.as(CustomGroup).nMembers).toBe(2);
90
+ });
91
+ });