jazz-tools 0.18.6 → 0.18.7

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 (84) hide show
  1. package/.turbo/turbo-build.log +49 -49
  2. package/CHANGELOG.md +17 -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-CFAY3FMQ.js} +70 -21
  7. package/dist/chunk-CFAY3FMQ.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 +80 -16
  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/CoValueBase.d.ts +14 -0
  46. package/dist/tools/coValues/CoValueBase.d.ts.map +1 -1
  47. package/dist/tools/coValues/coMap.d.ts +0 -12
  48. package/dist/tools/coValues/coMap.d.ts.map +1 -1
  49. package/dist/tools/implementation/createContext.d.ts +2 -1
  50. package/dist/tools/implementation/createContext.d.ts.map +1 -1
  51. package/dist/tools/tests/utils.d.ts.map +1 -1
  52. package/dist/worker/index.d.ts +4 -0
  53. package/dist/worker/index.d.ts.map +1 -1
  54. package/dist/worker/index.js +4 -2
  55. package/dist/worker/index.js.map +1 -1
  56. package/package.json +6 -4
  57. package/src/better-auth/auth/server.ts +8 -4
  58. package/src/better-auth/auth/tests/server.test.ts +2 -2
  59. package/src/inspector/index.tsx +1 -1
  60. package/src/inspector/ui/button.tsx +15 -1
  61. package/src/inspector/ui/heading.tsx +7 -2
  62. package/src/inspector/ui/input.tsx +6 -2
  63. package/src/inspector/ui/modal.tsx +158 -0
  64. package/src/inspector/viewer/delete-local-data.tsx +101 -0
  65. package/src/inspector/viewer/new-app.tsx +3 -1
  66. package/src/react/hooks.tsx +1 -0
  67. package/src/react/index.ts +1 -0
  68. package/src/react-core/hooks.ts +162 -0
  69. package/src/react-core/tests/useCoStateWithSelector.test.ts +149 -0
  70. package/src/react-native-core/hooks.tsx +1 -0
  71. package/src/tools/coValues/CoValueBase.ts +32 -0
  72. package/src/tools/coValues/coList.ts +35 -0
  73. package/src/tools/coValues/coMap.ts +0 -18
  74. package/src/tools/implementation/createContext.ts +9 -2
  75. package/src/tools/tests/coList.test.ts +41 -0
  76. package/src/tools/tests/coPlainText.test.ts +24 -0
  77. package/src/tools/tests/createContext.test.ts +24 -0
  78. package/src/tools/tests/deepLoading.test.ts +2 -0
  79. package/src/tools/tests/patterns/requestToJoin.test.ts +14 -6
  80. package/src/tools/tests/utils.ts +1 -0
  81. package/src/worker/index.ts +6 -0
  82. package/dist/chunk-45VKEOXG.js.map +0 -1
  83. package/dist/inspector/custom-element-IBHKHN27.js.map +0 -1
  84. /package/src/inspector/viewer/{inpsector-button.tsx → inspector-button.tsx} +0 -0
@@ -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();
@@ -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
+ });
@@ -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>;