jazz-tools 0.18.6 → 0.18.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.
Files changed (91) hide show
  1. package/.turbo/turbo-build.log +55 -55
  2. package/CHANGELOG.md +28 -0
  3. package/dist/better-auth/auth/server.d.ts.map +1 -1
  4. package/dist/better-auth/auth/server.js +8 -4
  5. package/dist/better-auth/auth/server.js.map +1 -1
  6. package/dist/{chunk-45VKEOXG.js → chunk-QF3R3C4N.js} +75 -22
  7. package/dist/chunk-QF3R3C4N.js.map +1 -0
  8. package/dist/index.js +1 -1
  9. package/dist/inspector/{custom-element-IBHKHN27.js → custom-element-G6SPZEBR.js} +292 -31
  10. package/dist/inspector/custom-element-G6SPZEBR.js.map +1 -0
  11. package/dist/inspector/index.d.ts +1 -1
  12. package/dist/inspector/index.js +302 -41
  13. package/dist/inspector/index.js.map +1 -1
  14. package/dist/inspector/register-custom-element.js +1 -1
  15. package/dist/inspector/ui/button.d.ts +1 -1
  16. package/dist/inspector/ui/button.d.ts.map +1 -1
  17. package/dist/inspector/ui/heading.d.ts +2 -1
  18. package/dist/inspector/ui/heading.d.ts.map +1 -1
  19. package/dist/inspector/ui/input.d.ts.map +1 -1
  20. package/dist/inspector/ui/modal.d.ts +16 -0
  21. package/dist/inspector/ui/modal.d.ts.map +1 -0
  22. package/dist/inspector/viewer/delete-local-data.d.ts +2 -0
  23. package/dist/inspector/viewer/delete-local-data.d.ts.map +1 -0
  24. package/dist/inspector/viewer/{inpsector-button.d.ts → inspector-button.d.ts} +1 -1
  25. package/dist/inspector/viewer/{inpsector-button.d.ts.map → inspector-button.d.ts.map} +1 -1
  26. package/dist/inspector/viewer/new-app.d.ts +1 -1
  27. package/dist/inspector/viewer/new-app.d.ts.map +1 -1
  28. package/dist/react/hooks.d.ts +1 -1
  29. package/dist/react/hooks.d.ts.map +1 -1
  30. package/dist/react/index.d.ts +1 -1
  31. package/dist/react/index.d.ts.map +1 -1
  32. package/dist/react/index.js +3 -1
  33. package/dist/react/index.js.map +1 -1
  34. package/dist/react-core/hooks.d.ts +133 -0
  35. package/dist/react-core/hooks.d.ts.map +1 -1
  36. package/dist/react-core/index.js +85 -17
  37. package/dist/react-core/index.js.map +1 -1
  38. package/dist/react-core/tests/useCoStateWithSelector.test.d.ts +2 -0
  39. package/dist/react-core/tests/useCoStateWithSelector.test.d.ts.map +1 -0
  40. package/dist/react-native-core/hooks.d.ts +1 -1
  41. package/dist/react-native-core/hooks.d.ts.map +1 -1
  42. package/dist/react-native-core/index.js +3 -1
  43. package/dist/react-native-core/index.js.map +1 -1
  44. package/dist/testing.js +1 -1
  45. package/dist/tools/coValues/CoFieldInit.d.ts +5 -5
  46. package/dist/tools/coValues/CoFieldInit.d.ts.map +1 -1
  47. package/dist/tools/coValues/CoValueBase.d.ts +14 -0
  48. package/dist/tools/coValues/CoValueBase.d.ts.map +1 -1
  49. package/dist/tools/coValues/coMap.d.ts +0 -12
  50. package/dist/tools/coValues/coMap.d.ts.map +1 -1
  51. package/dist/tools/implementation/createContext.d.ts +2 -1
  52. package/dist/tools/implementation/createContext.d.ts.map +1 -1
  53. package/dist/tools/implementation/zodSchema/schemaTypes/CoMapSchema.d.ts +5 -3
  54. package/dist/tools/implementation/zodSchema/schemaTypes/CoMapSchema.d.ts.map +1 -1
  55. package/dist/tools/tests/utils.d.ts.map +1 -1
  56. package/dist/worker/index.d.ts +4 -0
  57. package/dist/worker/index.d.ts.map +1 -1
  58. package/dist/worker/index.js +4 -2
  59. package/dist/worker/index.js.map +1 -1
  60. package/package.json +6 -4
  61. package/src/better-auth/auth/server.ts +8 -4
  62. package/src/better-auth/auth/tests/server.test.ts +2 -2
  63. package/src/inspector/index.tsx +1 -1
  64. package/src/inspector/ui/button.tsx +15 -1
  65. package/src/inspector/ui/heading.tsx +7 -2
  66. package/src/inspector/ui/input.tsx +6 -2
  67. package/src/inspector/ui/modal.tsx +158 -0
  68. package/src/inspector/viewer/delete-local-data.tsx +101 -0
  69. package/src/inspector/viewer/new-app.tsx +3 -1
  70. package/src/react/hooks.tsx +1 -0
  71. package/src/react/index.ts +1 -0
  72. package/src/react-core/hooks.ts +162 -0
  73. package/src/react-core/tests/useCoStateWithSelector.test.ts +149 -0
  74. package/src/react-native-core/hooks.tsx +1 -0
  75. package/src/tools/coValues/CoFieldInit.ts +5 -5
  76. package/src/tools/coValues/CoValueBase.ts +32 -0
  77. package/src/tools/coValues/coList.ts +35 -0
  78. package/src/tools/coValues/coMap.ts +0 -18
  79. package/src/tools/implementation/createContext.ts +9 -2
  80. package/src/tools/implementation/zodSchema/schemaTypes/CoMapSchema.ts +22 -8
  81. package/src/tools/tests/coList.test.ts +41 -0
  82. package/src/tools/tests/coMap.test.ts +37 -0
  83. package/src/tools/tests/coPlainText.test.ts +24 -0
  84. package/src/tools/tests/createContext.test.ts +24 -0
  85. package/src/tools/tests/deepLoading.test.ts +2 -0
  86. package/src/tools/tests/patterns/requestToJoin.test.ts +14 -6
  87. package/src/tools/tests/utils.ts +1 -0
  88. package/src/worker/index.ts +6 -0
  89. package/dist/chunk-45VKEOXG.js.map +0 -1
  90. package/dist/inspector/custom-element-IBHKHN27.js.map +0 -1
  91. /package/src/inspector/viewer/{inpsector-button.tsx → inspector-button.tsx} +0 -0
@@ -0,0 +1,149 @@
1
+ // @vitest-environment happy-dom
2
+
3
+ import { cojsonInternals } from "cojson";
4
+ import { Account, co, Loaded, z } from "jazz-tools";
5
+ import { beforeEach, describe, expect, expectTypeOf, it } from "vitest";
6
+ import { useCoStateWithSelector } from "../index.js";
7
+ import { createJazzTestAccount, setupJazzTestSync } from "../testing.js";
8
+ import { renderHook, waitFor } from "./testUtils.js";
9
+ import { useRef } from "react";
10
+
11
+ beforeEach(async () => {
12
+ await setupJazzTestSync();
13
+
14
+ await createJazzTestAccount({
15
+ isCurrentActiveAccount: true,
16
+ });
17
+ });
18
+
19
+ cojsonInternals.setCoValueLoadingRetryDelay(300);
20
+
21
+ const useRenderCount = <T>(hook: () => T) => {
22
+ const renderCountRef = useRef(0);
23
+ const result = hook();
24
+ renderCountRef.current = renderCountRef.current + 1;
25
+ return {
26
+ renderCount: renderCountRef.current,
27
+ result,
28
+ };
29
+ };
30
+
31
+ describe("useCoStateWithSelector", () => {
32
+ it("should not re-render when a nested coValue is updated and not selected", async () => {
33
+ const TestMap = co.map({
34
+ value: z.string(),
35
+ get nested() {
36
+ return TestMap.optional();
37
+ },
38
+ });
39
+
40
+ const map = TestMap.create({
41
+ value: "1",
42
+ nested: TestMap.create({
43
+ value: "1",
44
+ }),
45
+ });
46
+
47
+ const { result } = renderHook(() =>
48
+ useRenderCount(() =>
49
+ useCoStateWithSelector(TestMap, map.$jazz.id, {
50
+ resolve: {
51
+ nested: true,
52
+ },
53
+ select: (v) => v?.value,
54
+ }),
55
+ ),
56
+ );
57
+
58
+ await waitFor(() => {
59
+ expect(result.current.result).not.toBeUndefined();
60
+ });
61
+
62
+ for (let i = 0; i < 100; i++) {
63
+ map.nested!.$jazz.set("value", `${i}`);
64
+ await Account.getMe().$jazz.waitForAllCoValuesSync();
65
+ }
66
+
67
+ expect(result.current.result).toEqual("1");
68
+ expect(result.current.renderCount).toEqual(1);
69
+ });
70
+
71
+ it("should re-render when a nested coValue is updated and selected", async () => {
72
+ const TestMap = co.map({
73
+ value: z.string(),
74
+ get nested() {
75
+ return TestMap.optional();
76
+ },
77
+ });
78
+
79
+ const map = TestMap.create({
80
+ value: "1",
81
+ nested: TestMap.create({
82
+ value: "1",
83
+ }),
84
+ });
85
+
86
+ const { result } = renderHook(() =>
87
+ useRenderCount(() =>
88
+ useCoStateWithSelector(TestMap, map.$jazz.id, {
89
+ resolve: {
90
+ nested: true,
91
+ },
92
+ select: (v) => v?.nested?.value,
93
+ }),
94
+ ),
95
+ );
96
+
97
+ await waitFor(() => {
98
+ expect(result.current.result).not.toBeUndefined();
99
+ });
100
+
101
+ for (let i = 1; i <= 100; i++) {
102
+ map.nested!.$jazz.set("value", `${i}`);
103
+ await Account.getMe().$jazz.waitForAllCoValuesSync();
104
+ }
105
+
106
+ expect(result.current.result).toEqual("100");
107
+
108
+ // skips re-render on i = 1, only re-renders on i = [2,100], so initial render + 99 renders = 100
109
+ expect(result.current.renderCount).toEqual(100);
110
+
111
+ expectTypeOf(result.current.result).toEqualTypeOf<string | undefined>();
112
+ });
113
+
114
+ it("should not re-render when equalityFn always returns true", async () => {
115
+ const TestMap = co.map({
116
+ value: z.string(),
117
+ get nested() {
118
+ return TestMap.optional();
119
+ },
120
+ });
121
+
122
+ const map = TestMap.create({
123
+ value: "1",
124
+ nested: TestMap.create({
125
+ value: "1",
126
+ }),
127
+ });
128
+
129
+ const { result } = renderHook(() =>
130
+ useRenderCount(() =>
131
+ useCoStateWithSelector(TestMap, map.$jazz.id, {
132
+ resolve: {
133
+ nested: true,
134
+ },
135
+ select: (v) => v?.nested?.value,
136
+ equalityFn: () => true,
137
+ }),
138
+ ),
139
+ );
140
+
141
+ for (let i = 1; i <= 100; i++) {
142
+ map.nested!.$jazz.set("value", `${i}`);
143
+ await Account.getMe().$jazz.waitForAllCoValuesSync();
144
+ }
145
+
146
+ expect(result.current.result).toEqual("1");
147
+ expect(result.current.renderCount).toEqual(1);
148
+ });
149
+ });
@@ -13,6 +13,7 @@ export {
13
13
  useAuthSecretStorage,
14
14
  useIsAuthenticated,
15
15
  useAccount,
16
+ useCoStateWithSelector,
16
17
  } from "jazz-tools/react-core";
17
18
 
18
19
  export function useAcceptInviteNative<S extends CoValueClassOrSchema>({
@@ -1,8 +1,8 @@
1
- import { CoFeed } from "./coFeed";
2
- import { CoList } from "./coList";
3
- import { CoMap, CoMapInit } from "./coMap";
4
- import { CoPlainText } from "./coPlainText";
5
- import { CoRichText } from "./coRichText";
1
+ import { CoFeed } from "./coFeed.js";
2
+ import { CoList } from "./coList.js";
3
+ import { CoMap, CoMapInit } from "./coMap.js";
4
+ import { CoPlainText } from "./coPlainText.js";
5
+ import { CoRichText } from "./coRichText.js";
6
6
 
7
7
  /**
8
8
  * Returns the type of values that can be used to initialize a field of the provided type.
@@ -77,4 +77,36 @@ export abstract class CoValueJazzApi<V extends CoValue> {
77
77
 
78
78
  return new AnonymousJazzAgent(this.localNode);
79
79
  }
80
+
81
+ /**
82
+ * The timestamp of the creation time of the CoValue
83
+ *
84
+ * @category Content
85
+ */
86
+ get createdAt(): number {
87
+ const createdAt = this.raw.core.verified.header.meta?.createdAt;
88
+
89
+ if (typeof createdAt === "string") {
90
+ return new Date(createdAt).getTime();
91
+ }
92
+
93
+ return this.raw.core.earliestTxMadeAt;
94
+ }
95
+
96
+ /**
97
+ * The timestamp of the last updated time of the CoValue
98
+ *
99
+ * Returns the creation time if there are no updates.
100
+ *
101
+ * @category Content
102
+ */
103
+ get lastUpdatedAt(): number {
104
+ const value = this.raw.core.latestTxMadeAt;
105
+
106
+ if (value === 0) {
107
+ return this.createdAt;
108
+ }
109
+
110
+ return value;
111
+ }
80
112
  }
@@ -971,4 +971,39 @@ const CoListProxyHandler: ProxyHandler<CoList> = {
971
971
  return Reflect.has(target, key);
972
972
  }
973
973
  },
974
+ ownKeys(target) {
975
+ const keys = Reflect.ownKeys(target);
976
+ // Add numeric indices for all entries in the list
977
+ const indexKeys = target.$jazz.raw.entries().map((_entry, i) => String(i));
978
+ keys.push(...indexKeys);
979
+ return keys;
980
+ },
981
+ getOwnPropertyDescriptor(target, key) {
982
+ if (key === TypeSym) {
983
+ // Make TypeSym non-enumerable so it doesn't show up in Object.keys()
984
+ return {
985
+ enumerable: false,
986
+ configurable: true,
987
+ writable: false,
988
+ value: target[TypeSym],
989
+ };
990
+ } else if (key in target) {
991
+ return Reflect.getOwnPropertyDescriptor(target, key);
992
+ } else if (typeof key === "string" && !isNaN(+key)) {
993
+ const index = Number(key);
994
+ if (index >= 0 && index < target.$jazz.raw.entries().length) {
995
+ return {
996
+ enumerable: true,
997
+ configurable: true,
998
+ writable: true,
999
+ };
1000
+ }
1001
+ } else if (key === "length") {
1002
+ return {
1003
+ enumerable: false,
1004
+ configurable: false,
1005
+ writable: false,
1006
+ };
1007
+ }
1008
+ },
974
1009
  };
@@ -836,24 +836,6 @@ class CoMapJazzApi<M extends CoMap> extends CoValueJazzApi<M> {
836
836
  return this.getRaw();
837
837
  }
838
838
 
839
- /**
840
- * The timestamp of the creation time of the CoMap
841
- *
842
- * @category Content
843
- */
844
- get createdAt(): number {
845
- return this.raw.earliestTxMadeAt ?? Number.MAX_SAFE_INTEGER;
846
- }
847
-
848
- /**
849
- * The timestamp of the last updated time of the CoMap
850
- *
851
- * @category Content
852
- */
853
- get lastUpdatedAt(): number {
854
- return this.raw.latestTxMadeAt;
855
- }
856
-
857
839
  /** @internal */
858
840
  get schema(): CoMapFieldSchema {
859
841
  return (this.coMap.constructor as typeof CoMap)._schema;
@@ -96,6 +96,7 @@ export async function createJazzContextFromExistingCredentials<
96
96
  AccountSchema: PropsAccountSchema,
97
97
  sessionProvider,
98
98
  onLogOut,
99
+ asActiveAccount,
99
100
  }: {
100
101
  credentials: Credentials;
101
102
  peersToLoadFrom: Peer[];
@@ -104,6 +105,7 @@ export async function createJazzContextFromExistingCredentials<
104
105
  sessionProvider: SessionProvider;
105
106
  onLogOut?: () => void;
106
107
  storage?: StorageAPI;
108
+ asActiveAccount: boolean;
107
109
  }): Promise<JazzContextWithAccount<InstanceOfSchema<S>>> {
108
110
  const { sessionID, sessionDone } = await sessionProvider(
109
111
  credentials.accountID,
@@ -125,14 +127,18 @@ export async function createJazzContextFromExistingCredentials<
125
127
  storage,
126
128
  migration: async (rawAccount, _node, creationProps) => {
127
129
  const account = AccountClass.fromRaw(rawAccount) as InstanceOfSchema<S>;
128
- activeAccountContext.set(account);
130
+ if (asActiveAccount) {
131
+ activeAccountContext.set(account);
132
+ }
129
133
 
130
134
  await account.applyMigration(creationProps);
131
135
  },
132
136
  });
133
137
 
134
138
  const account = AccountClass.fromNode(node);
135
- activeAccountContext.set(account);
139
+ if (asActiveAccount) {
140
+ activeAccountContext.set(account);
141
+ }
136
142
 
137
143
  return {
138
144
  node,
@@ -245,6 +251,7 @@ export async function createJazzContext<
245
251
  authSecretStorage.clearWithoutNotify();
246
252
  },
247
253
  storage: options.storage,
254
+ asActiveAccount: true,
248
255
  });
249
256
  } else {
250
257
  const secretSeed = options.crypto.newRandomSecretSeed();
@@ -180,7 +180,9 @@ export interface CoMapSchema<
180
180
  *
181
181
  * @returns A new CoMap schema with all fields optional.
182
182
  */
183
- partial(): CoMapSchema<PartialShape<Shape>, CatchAll, Owner>;
183
+ partial<Keys extends keyof Shape = keyof Shape>(
184
+ keys?: { [key in Keys]: true },
185
+ ): CoMapSchema<PartialShape<Shape, Keys>, CatchAll, Owner>;
184
186
  }
185
187
 
186
188
  export function createCoreCoMapSchema<
@@ -281,10 +283,17 @@ export function enrichCoMapSchema<
281
283
 
282
284
  return coMapDefiner(pickedShape);
283
285
  },
284
- partial: () => {
286
+ partial: <Keys extends keyof Shape = keyof Shape>(
287
+ keys?: { [key in Keys]: true },
288
+ ) => {
285
289
  const partialShape: Record<string, AnyZodOrCoValueSchema> = {};
286
290
 
287
291
  for (const [key, value] of Object.entries(coValueSchema.shape)) {
292
+ if (keys && !keys[key as Keys]) {
293
+ partialShape[key] = value;
294
+ continue;
295
+ }
296
+
288
297
  if (isAnyCoValueSchema(value)) {
289
298
  partialShape[key] = coOptionalDefiner(value);
290
299
  } else {
@@ -341,10 +350,15 @@ export type CoMapInstanceCoValuesNullable<Shape extends z.core.$ZodLooseShape> =
341
350
  >;
342
351
  };
343
352
 
344
- export type PartialShape<Shape extends z.core.$ZodLooseShape> = Simplify<{
345
- -readonly [key in keyof Shape]: Shape[key] extends AnyZodSchema
346
- ? z.ZodOptional<Shape[key]>
347
- : Shape[key] extends CoreCoValueSchema
348
- ? CoOptionalSchema<Shape[key]>
349
- : never;
353
+ export type PartialShape<
354
+ Shape extends z.core.$ZodLooseShape,
355
+ PartialKeys extends keyof Shape = keyof Shape,
356
+ > = Simplify<{
357
+ -readonly [key in keyof Shape]: key extends PartialKeys
358
+ ? Shape[key] extends AnyZodSchema
359
+ ? z.ZodOptional<Shape[key]>
360
+ : Shape[key] extends CoreCoValueSchema
361
+ ? CoOptionalSchema<Shape[key]>
362
+ : never
363
+ : Shape[key];
350
364
  }>;
@@ -122,6 +122,25 @@ describe("Simple CoList operations", async () => {
122
122
  expect(list[0]).toEqual("milk");
123
123
  });
124
124
 
125
+ test("CoList keys can be iterated over just like an array's", () => {
126
+ const TestList = co.list(z.string());
127
+ const list = ["a", "b", "c", "d", "e"];
128
+ const coList = TestList.create(list);
129
+ const keys = [];
130
+ for (const key in coList) {
131
+ keys.push(key);
132
+ }
133
+ expect(keys).toEqual(Object.keys(list));
134
+ expect(Object.keys(coList)).toEqual(Object.keys(list));
135
+ });
136
+
137
+ test("a CoList is structurally equal to an array", () => {
138
+ const TestList = co.list(z.string());
139
+ const list = ["a", "b", "c", "d", "e"];
140
+ const coList = TestList.create(list);
141
+ expect(coList).toEqual(list);
142
+ });
143
+
125
144
  describe("Mutation", () => {
126
145
  test("assignment", () => {
127
146
  const list = TestList.create(["bread", "butter", "onion"], {
@@ -1314,3 +1333,25 @@ describe("co.list schema", () => {
1314
1333
  expect(keywords[1]?.toString()).toEqual("world");
1315
1334
  });
1316
1335
  });
1336
+
1337
+ describe("lastUpdatedAt", () => {
1338
+ test("empty list last updated time", () => {
1339
+ const emptyList = co.list(z.number()).create([]);
1340
+
1341
+ expect(emptyList.$jazz.lastUpdatedAt).not.toEqual(0);
1342
+ expect(emptyList.$jazz.lastUpdatedAt).toEqual(emptyList.$jazz.createdAt);
1343
+ });
1344
+
1345
+ test("last update should change on push", async () => {
1346
+ const list = co.list(z.string()).create(["John"]);
1347
+
1348
+ expect(list.$jazz.lastUpdatedAt).not.toEqual(0);
1349
+
1350
+ const updatedAt = list.$jazz.lastUpdatedAt;
1351
+
1352
+ await new Promise((r) => setTimeout(r, 10));
1353
+ list.$jazz.push("Jane");
1354
+
1355
+ expect(list.$jazz.lastUpdatedAt).not.toEqual(updatedAt);
1356
+ });
1357
+ });
@@ -2646,6 +2646,43 @@ describe("co.map schema", () => {
2646
2646
  expect(draftPerson.pet).toEqual(rex);
2647
2647
  });
2648
2648
 
2649
+ test("creates a new CoMap schema by making some properties optional", () => {
2650
+ const Dog = co.map({
2651
+ name: z.string(),
2652
+ breed: z.string(),
2653
+ });
2654
+ const Person = co.map({
2655
+ name: z.string(),
2656
+ age: z.number(),
2657
+ pet: Dog,
2658
+ });
2659
+
2660
+ const DraftPerson = Person.partial({
2661
+ pet: true,
2662
+ });
2663
+
2664
+ const draftPerson = DraftPerson.create({
2665
+ name: "John",
2666
+ age: 20,
2667
+ });
2668
+
2669
+ expect(draftPerson.$jazz.has("pet")).toBe(false);
2670
+
2671
+ const rex = Dog.create({ name: "Rex", breed: "Labrador" });
2672
+ draftPerson.$jazz.set("pet", rex);
2673
+
2674
+ expect(draftPerson.pet).toEqual(rex);
2675
+
2676
+ expect(draftPerson.$jazz.has("pet")).toBe(true);
2677
+
2678
+ draftPerson.$jazz.delete("pet");
2679
+
2680
+ expect(draftPerson.$jazz.has("pet")).toBe(false);
2681
+
2682
+ // @ts-expect-error - should not allow deleting required properties
2683
+ draftPerson.$jazz.delete("age");
2684
+ });
2685
+
2649
2686
  test("the new schema includes catchall properties", () => {
2650
2687
  const Person = co
2651
2688
  .map({
@@ -183,6 +183,7 @@ describe("CoPlainText", () => {
183
183
  sessionProvider: randomSessionProvider,
184
184
  peersToLoadFrom: [initialAsPeer],
185
185
  crypto: Crypto,
186
+ asActiveAccount: true,
186
187
  });
187
188
 
188
189
  // Load the text on the second peer
@@ -214,6 +215,7 @@ describe("CoPlainText", () => {
214
215
  sessionProvider: randomSessionProvider,
215
216
  peersToLoadFrom: [initialAsPeer],
216
217
  crypto: Crypto,
218
+ asActiveAccount: true,
217
219
  });
218
220
 
219
221
  const queue = new Channel();
@@ -242,3 +244,25 @@ describe("CoPlainText", () => {
242
244
  expect(update3.toString()).toBe("hello world");
243
245
  });
244
246
  });
247
+
248
+ describe("lastUpdatedAt", () => {
249
+ test("empty text last updated time", () => {
250
+ const text = co.plainText().create("");
251
+
252
+ expect(text.$jazz.lastUpdatedAt).toEqual(text.$jazz.createdAt);
253
+ expect(text.$jazz.lastUpdatedAt).not.toEqual(0);
254
+ });
255
+
256
+ test("last update should change on push", async () => {
257
+ const text = co.plainText().create("John");
258
+
259
+ expect(text.$jazz.lastUpdatedAt).not.toEqual(0);
260
+
261
+ const updatedAt = text.$jazz.lastUpdatedAt;
262
+
263
+ await new Promise((r) => setTimeout(r, 10));
264
+ text.$jazz.applyDiff("Jane");
265
+
266
+ expect(text.$jazz.lastUpdatedAt).not.toEqual(updatedAt);
267
+ });
268
+ });
@@ -54,6 +54,7 @@ describe("createContext methods", () => {
54
54
  peersToLoadFrom: [getPeerConnectedToTestSyncServer()],
55
55
  crypto: Crypto,
56
56
  sessionProvider: randomSessionProvider,
57
+ asActiveAccount: true,
57
58
  });
58
59
 
59
60
  expect(context.node).toBeDefined();
@@ -86,6 +87,7 @@ describe("createContext methods", () => {
86
87
  crypto: Crypto,
87
88
  AccountSchema: CustomAccount,
88
89
  sessionProvider: randomSessionProvider,
90
+ asActiveAccount: true,
89
91
  });
90
92
 
91
93
  expect(context.account).toBeInstanceOf(
@@ -109,6 +111,7 @@ describe("createContext methods", () => {
109
111
  crypto: Crypto,
110
112
  sessionProvider: randomSessionProvider,
111
113
  onLogOut,
114
+ asActiveAccount: true,
112
115
  });
113
116
 
114
117
  context.logOut();
@@ -131,6 +134,7 @@ describe("createContext methods", () => {
131
134
  peersToLoadFrom: [getPeerConnectedToTestSyncServer()],
132
135
  crypto: Crypto,
133
136
  sessionProvider: randomSessionProvider,
137
+ asActiveAccount: true,
134
138
  });
135
139
 
136
140
  const loadedMap = await loadCoValueOrFail(context.node, coMap.id);
@@ -151,10 +155,30 @@ describe("createContext methods", () => {
151
155
  peersToLoadFrom: [getPeerConnectedToTestSyncServer()],
152
156
  crypto: Crypto,
153
157
  sessionProvider: randomSessionProvider,
158
+ asActiveAccount: true,
154
159
  });
155
160
 
156
161
  expect(activeAccountContext.get()).toBe(context.account);
157
162
  });
163
+
164
+ test("does not set the active account when asActiveAccount is false", async () => {
165
+ const account = await createJazzTestAccount({
166
+ isCurrentActiveAccount: false,
167
+ });
168
+
169
+ const context = await createJazzContextFromExistingCredentials({
170
+ credentials: {
171
+ accountID: account.$jazz.id,
172
+ secret: account.$jazz.localNode.getCurrentAgent().agentSecret,
173
+ },
174
+ peersToLoadFrom: [getPeerConnectedToTestSyncServer()],
175
+ crypto: Crypto,
176
+ sessionProvider: randomSessionProvider,
177
+ asActiveAccount: false,
178
+ });
179
+
180
+ expect(activeAccountContext.get()).not.toBe(context.account);
181
+ });
158
182
  });
159
183
 
160
184
  describe("createJazzContextForNewAccount", () => {
@@ -57,6 +57,7 @@ describe("Deep loading with depth arg", async () => {
57
57
  sessionProvider: randomSessionProvider,
58
58
  peersToLoadFrom: [initialAsPeer],
59
59
  crypto: Crypto,
60
+ asActiveAccount: true,
60
61
  });
61
62
 
62
63
  const ownership = { owner: me };
@@ -287,6 +288,7 @@ test("Deep loading a record-like coMap", async () => {
287
288
  sessionProvider: randomSessionProvider,
288
289
  peersToLoadFrom: [initialAsPeer],
289
290
  crypto: Crypto,
291
+ asActiveAccount: true,
290
292
  });
291
293
 
292
294
  const record = RecordLike.create(
@@ -1,6 +1,10 @@
1
1
  import { assert, describe, expect, test } from "vitest";
2
2
  import { Account, Group, co, z } from "../../exports";
3
- import { createJazzTestAccount, linkAccounts } from "../../testing";
3
+ import {
4
+ createJazzTestAccount,
5
+ linkAccounts,
6
+ setupJazzTestSync,
7
+ } from "../../testing";
4
8
 
5
9
  const RequestToJoin = co.map({
6
10
  account: Account,
@@ -24,16 +28,20 @@ const Organization = co.map({
24
28
  });
25
29
 
26
30
  async function setup() {
31
+ await setupJazzTestSync();
32
+
27
33
  const admin1 = await createJazzTestAccount();
28
34
  const admin2 = await createJazzTestAccount();
29
35
  const user1 = await createJazzTestAccount();
30
36
  const user2 = await createJazzTestAccount();
31
37
 
32
- await linkAccounts(admin1, admin2);
33
- await linkAccounts(admin1, user1);
34
- await linkAccounts(admin1, user2);
35
- await linkAccounts(admin2, user1);
36
- await linkAccounts(admin2, user2);
38
+ // TODO: with this setting the waitForAllCoValuesSync gets stuck
39
+ // https://github.com/garden-co/jazz/issues/2874
40
+ // await linkAccounts(admin1, admin2);
41
+ // await linkAccounts(admin1, user1);
42
+ // await linkAccounts(admin1, user2);
43
+ // await linkAccounts(admin2, user1);
44
+ // await linkAccounts(admin2, user2);
37
45
 
38
46
  // The organization info are public
39
47
  const adminsGroup = Group.create(admin1);
@@ -40,6 +40,7 @@ export async function setupAccount() {
40
40
  sessionProvider: randomSessionProvider,
41
41
  peersToLoadFrom: [initialAsPeer],
42
42
  crypto: Crypto,
43
+ asActiveAccount: true,
43
44
  });
44
45
 
45
46
  return { me, meOnSecondPeer };
@@ -31,6 +31,10 @@ type WorkerOptions<
31
31
  * If true, the inbox will not be loaded.
32
32
  */
33
33
  skipInboxLoad?: boolean;
34
+ /**
35
+ * If false, the worker will not set in the global account context
36
+ */
37
+ asActiveAccount?: boolean;
34
38
  };
35
39
 
36
40
  /** @category Context Creation */
@@ -45,6 +49,7 @@ export async function startWorker<
45
49
  syncServer = "wss://cloud.jazz.tools",
46
50
  AccountSchema = Account as unknown as S,
47
51
  skipInboxLoad = false,
52
+ asActiveAccount = true,
48
53
  } = options;
49
54
 
50
55
  let node: LocalNode | undefined = undefined;
@@ -90,6 +95,7 @@ export async function startWorker<
90
95
  sessionProvider: randomSessionProvider,
91
96
  peersToLoadFrom,
92
97
  crypto: options.crypto ?? (await WasmCrypto.create()),
98
+ asActiveAccount,
93
99
  });
94
100
 
95
101
  const account = context.account as InstanceOfSchema<S>;