jazz-tools 0.10.7 → 0.10.8

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/dist/index.js CHANGED
@@ -35,7 +35,7 @@ import {
35
35
  parseInviteLink,
36
36
  randomSessionProvider,
37
37
  subscribeToCoValue
38
- } from "./chunk-TSEO4KAO.js";
38
+ } from "./chunk-6OJCOJJ6.js";
39
39
 
40
40
  // src/index.ts
41
41
  import { MAX_RECOMMENDED_TX_SIZE, cojsonInternals } from "cojson";
package/dist/testing.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  createAnonymousJazzContext,
6
6
  createJazzContext,
7
7
  randomSessionProvider
8
- } from "./chunk-TSEO4KAO.js";
8
+ } from "./chunk-6OJCOJJ6.js";
9
9
 
10
10
  // src/testing.ts
11
11
  import { LocalNode } from "cojson";
package/package.json CHANGED
@@ -17,10 +17,10 @@
17
17
  },
18
18
  "type": "module",
19
19
  "license": "MIT",
20
- "version": "0.10.7",
20
+ "version": "0.10.8",
21
21
  "dependencies": {
22
22
  "@scure/bip39": "^1.3.0",
23
- "cojson": "0.10.7"
23
+ "cojson": "0.10.8"
24
24
  },
25
25
  "devDependencies": {
26
26
  "tsup": "8.3.5",
@@ -31,9 +31,32 @@ export function fulfillsDepth(depth: any, value: CoValue): boolean {
31
31
  [key: string]: any;
32
32
  _refs: { [key: string]: Ref<CoValue> | undefined };
33
33
  };
34
- if (!map._refs[key] && map._schema[key].optional) {
35
- continue;
34
+
35
+ if (map._raw.get(key) === undefined) {
36
+ if (!map._schema?.[key]) {
37
+ if (map._schema?.[ItemsSym]) {
38
+ // CoMap.Record
39
+ if (map._schema[ItemsSym].optional) {
40
+ continue;
41
+ } else {
42
+ throw new Error(
43
+ `The ref ${key} requested on ${map.constructor.name} is missing`,
44
+ );
45
+ }
46
+ } else {
47
+ throw new Error(
48
+ `The ref ${key} requested on ${map.constructor.name} is not defined in the schema`,
49
+ );
50
+ }
51
+ } else if (map._schema[key].optional) {
52
+ continue;
53
+ } else {
54
+ throw new Error(
55
+ `The ref ${key} on ${map.constructor.name} is required but missing`,
56
+ );
57
+ }
36
58
  }
59
+
37
60
  if (!map[key]) {
38
61
  return false;
39
62
  }
@@ -295,6 +295,7 @@ export function subscribeToCoValue<V extends CoValue, Depth>(
295
295
  .then((value) => subscribe(value))
296
296
  .catch((e) => {
297
297
  console.error("Failed to load / subscribe to CoValue", e);
298
+ onUnavailable?.();
298
299
  });
299
300
  }
300
301
 
@@ -1,5 +1,5 @@
1
1
  import { WasmCrypto } from "cojson/crypto/WasmCrypto";
2
- import { describe, expect, expectTypeOf, test } from "vitest";
2
+ import { describe, expect, expectTypeOf, test, vi } from "vitest";
3
3
  import { Group, randomSessionProvider } from "../exports.js";
4
4
  import {
5
5
  Account,
@@ -10,7 +10,7 @@ import {
10
10
  createJazzContextFromExistingCredentials,
11
11
  isControlledAccount,
12
12
  } from "../index.js";
13
- import { setupTwoNodes } from "./utils.js";
13
+ import { setupTwoNodes, waitFor } from "./utils.js";
14
14
 
15
15
  const connectedPeers = cojsonInternals.connectedPeers;
16
16
 
@@ -1,6 +1,6 @@
1
1
  import { cojsonInternals } from "cojson";
2
2
  import { WasmCrypto } from "cojson/crypto/WasmCrypto";
3
- import { describe, expect, expectTypeOf, test } from "vitest";
3
+ import { describe, expect, expectTypeOf, test, vi } from "vitest";
4
4
  import {
5
5
  Account,
6
6
  CoFeed,
@@ -14,6 +14,7 @@ import {
14
14
  isControlledAccount,
15
15
  } from "../index.js";
16
16
  import { randomSessionProvider } from "../internal.js";
17
+ import { waitFor } from "./utils.js";
17
18
 
18
19
  const Crypto = await WasmCrypto.create();
19
20
  const { connectedPeers } = cojsonInternals;
@@ -303,3 +304,86 @@ test("Deep loading a record-like coMap", async () => {
303
304
  expect(recordLoaded.key2?.list).not.toBe(null);
304
305
  expect(recordLoaded.key2?.list).not.toBe(undefined);
305
306
  });
307
+
308
+ test("doesn't break on Map.Record key deletion when the key is referenced in the depth", async () => {
309
+ class JazzProfile extends CoMap {
310
+ firstName = co.string;
311
+ }
312
+
313
+ class JazzySnapStore extends CoMap.Record(co.ref(JazzProfile)) {}
314
+
315
+ const me = await Account.create({
316
+ creationProps: { name: "Tester McTesterson" },
317
+ crypto: Crypto,
318
+ });
319
+
320
+ const snapStore = JazzySnapStore.create(
321
+ {
322
+ profile1: JazzProfile.create({ firstName: "John" }, { owner: me }),
323
+ profile2: JazzProfile.create({ firstName: "John" }, { owner: me }),
324
+ },
325
+ { owner: me },
326
+ );
327
+
328
+ const spy = vi.fn();
329
+ const unsub = snapStore.subscribe({ profile1: {}, profile2: {} }, spy);
330
+
331
+ await waitFor(() => expect(spy).toHaveBeenCalled());
332
+
333
+ spy.mockClear();
334
+ delete snapStore.profile1;
335
+
336
+ expect(Object.keys(snapStore)).toEqual(["profile2"]);
337
+
338
+ unsub();
339
+
340
+ await expect(
341
+ snapStore.ensureLoaded({
342
+ profile1: {},
343
+ }),
344
+ ).rejects.toThrow("Failed to deeply load CoValue " + snapStore.id);
345
+ });
346
+
347
+ test("throw when calling ensureLoaded on a ref that's required but missing", async () => {
348
+ class JazzProfile extends CoMap {
349
+ firstName = co.string;
350
+ }
351
+
352
+ class JazzRoot extends CoMap {
353
+ profile = co.ref(JazzProfile);
354
+ }
355
+
356
+ const me = await Account.create({
357
+ creationProps: { name: "Tester McTesterson" },
358
+ crypto: Crypto,
359
+ });
360
+
361
+ const root = JazzRoot.create(
362
+ // @ts-expect-error missing required ref
363
+ {},
364
+ { owner: me },
365
+ );
366
+
367
+ await expect(
368
+ root.ensureLoaded({
369
+ profile: {},
370
+ }),
371
+ ).rejects.toThrow("Failed to deeply load CoValue " + root.id);
372
+ });
373
+
374
+ test("throw when calling ensureLoaded on a ref that is not defined in the schema", async () => {
375
+ class JazzRoot extends CoMap {}
376
+
377
+ const me = await Account.create({
378
+ creationProps: { name: "Tester McTesterson" },
379
+ crypto: Crypto,
380
+ });
381
+
382
+ const root = JazzRoot.create({}, { owner: me });
383
+
384
+ await expect(
385
+ root.ensureLoaded({
386
+ profile: {},
387
+ }),
388
+ ).rejects.toThrow("Failed to deeply load CoValue " + root.id);
389
+ });
@@ -150,6 +150,45 @@ describe("subscribeToCoValue", () => {
150
150
  );
151
151
  });
152
152
 
153
+ it("shouldn't fire updates after unsubscribing", async () => {
154
+ const { me, meOnSecondPeer } = await setupAccount();
155
+
156
+ const chatRoom = createChatRoom(me, "General");
157
+ const updateFn = vi.fn();
158
+
159
+ const { messages } = await chatRoom.ensureLoaded({ messages: [{}] });
160
+
161
+ messages.push(createMessage(me, "Hello"));
162
+
163
+ const unsubscribe = subscribeToCoValue(
164
+ ChatRoom,
165
+ chatRoom.id,
166
+ meOnSecondPeer,
167
+ {
168
+ messages: [{}],
169
+ },
170
+ updateFn,
171
+ );
172
+
173
+ await waitFor(() => {
174
+ expect(updateFn).toHaveBeenCalled();
175
+ });
176
+
177
+ unsubscribe();
178
+ chatRoom.name = "Lounge";
179
+ messages.push(createMessage(me, "Hello 2"));
180
+
181
+ await new Promise((resolve) => setTimeout(resolve, 100));
182
+
183
+ expect(updateFn).toHaveBeenCalledTimes(1);
184
+ expect(updateFn).toHaveBeenCalledWith(
185
+ expect.objectContaining({
186
+ id: chatRoom.id,
187
+ }),
188
+ expect.any(Function),
189
+ );
190
+ });
191
+
153
192
  it("should fire updates when a ref entity is updates", async () => {
154
193
  const { me, meOnSecondPeer } = await setupAccount();
155
194