jazz-tools 0.9.23 → 0.10.0
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 +11 -11
- package/CHANGELOG.md +19 -0
- package/dist/{chunk-OJIEP4WE.js → chunk-UBD75Z27.js} +566 -118
- package/dist/chunk-UBD75Z27.js.map +1 -0
- package/dist/index.native.js +17 -5
- package/dist/index.native.js.map +1 -1
- package/dist/index.web.js +17 -5
- package/dist/index.web.js.map +1 -1
- package/dist/testing.js +124 -33
- package/dist/testing.js.map +1 -1
- package/package.json +5 -3
- package/src/auth/AuthSecretStorage.ts +109 -0
- package/src/auth/DemoAuth.ts +188 -0
- package/src/auth/InMemoryKVStore.ts +25 -0
- package/src/auth/KvStoreContext.ts +39 -0
- package/src/auth/PassphraseAuth.ts +113 -0
- package/src/coValues/account.ts +8 -3
- package/src/coValues/coFeed.ts +1 -1
- package/src/coValues/coList.ts +1 -1
- package/src/coValues/coMap.ts +1 -1
- package/src/coValues/group.ts +9 -8
- package/src/coValues/interfaces.ts +14 -5
- package/src/exports.ts +17 -3
- package/src/implementation/ContextManager.ts +178 -0
- package/src/implementation/activeAccountContext.ts +6 -1
- package/src/implementation/createContext.ts +173 -149
- package/src/testing.ts +171 -33
- package/src/tests/AuthSecretStorage.test.ts +275 -0
- package/src/tests/ContextManager.test.ts +256 -0
- package/src/tests/DemoAuth.test.ts +269 -0
- package/src/tests/PassphraseAuth.test.ts +152 -0
- package/src/tests/coFeed.test.ts +44 -39
- package/src/tests/coList.test.ts +21 -20
- package/src/tests/coMap.test.ts +21 -20
- package/src/tests/coPlainText.test.ts +21 -20
- package/src/tests/coRichText.test.ts +21 -20
- package/src/tests/createContext.test.ts +339 -0
- package/src/tests/deepLoading.test.ts +41 -42
- package/src/tests/fixtures.ts +2050 -0
- package/src/tests/groupsAndAccounts.test.ts +2 -2
- package/src/tests/subscribe.test.ts +42 -9
- package/src/tests/testing.test.ts +56 -0
- package/src/tests/utils.ts +11 -11
- package/src/types.ts +54 -0
- package/dist/chunk-OJIEP4WE.js.map +0 -1
@@ -0,0 +1,339 @@
|
|
1
|
+
import { AgentSecret, WasmCrypto } from "cojson";
|
2
|
+
import { beforeEach, describe, expect, test, vi } from "vitest";
|
3
|
+
import {
|
4
|
+
Account,
|
5
|
+
AnonymousJazzAgent,
|
6
|
+
AuthSecretStorage,
|
7
|
+
Credentials,
|
8
|
+
ID,
|
9
|
+
InMemoryKVStore,
|
10
|
+
KvStoreContext,
|
11
|
+
createAnonymousJazzContext,
|
12
|
+
createJazzContext,
|
13
|
+
createJazzContextForNewAccount,
|
14
|
+
createJazzContextFromExistingCredentials,
|
15
|
+
randomSessionProvider,
|
16
|
+
} from "../exports";
|
17
|
+
import { activeAccountContext } from "../implementation/activeAccountContext";
|
18
|
+
import {
|
19
|
+
createJazzTestAccount,
|
20
|
+
getPeerConnectedToTestSyncServer,
|
21
|
+
setupJazzTestSync,
|
22
|
+
} from "../testing";
|
23
|
+
import { loadCoValueOrFail } from "./utils";
|
24
|
+
|
25
|
+
const Crypto = await WasmCrypto.create();
|
26
|
+
|
27
|
+
KvStoreContext.getInstance().initialize(new InMemoryKVStore());
|
28
|
+
|
29
|
+
describe("createContext methods", () => {
|
30
|
+
let authSecretStorage: AuthSecretStorage;
|
31
|
+
|
32
|
+
beforeEach(async () => {
|
33
|
+
authSecretStorage = new AuthSecretStorage();
|
34
|
+
authSecretStorage.clear();
|
35
|
+
await setupJazzTestSync();
|
36
|
+
});
|
37
|
+
|
38
|
+
describe("createJazzContextFromExistingCredentials", () => {
|
39
|
+
test("creates context with valid credentials", async () => {
|
40
|
+
// Create an account first to get valid credentials
|
41
|
+
const account = await createJazzTestAccount({
|
42
|
+
isCurrentActiveAccount: true,
|
43
|
+
});
|
44
|
+
|
45
|
+
const credentials: Credentials = {
|
46
|
+
accountID: account.id,
|
47
|
+
secret: account._raw.core.node.account.agentSecret,
|
48
|
+
};
|
49
|
+
|
50
|
+
const context = await createJazzContextFromExistingCredentials({
|
51
|
+
credentials,
|
52
|
+
peersToLoadFrom: [getPeerConnectedToTestSyncServer()],
|
53
|
+
crypto: Crypto,
|
54
|
+
sessionProvider: randomSessionProvider,
|
55
|
+
});
|
56
|
+
|
57
|
+
expect(context.node).toBeDefined();
|
58
|
+
expect(context.account).toBeDefined();
|
59
|
+
expect(context.account.id).toBe(credentials.accountID);
|
60
|
+
expect(typeof context.done).toBe("function");
|
61
|
+
expect(typeof context.logOut).toBe("function");
|
62
|
+
});
|
63
|
+
|
64
|
+
test("handles custom account schema", async () => {
|
65
|
+
class CustomAccount extends Account {
|
66
|
+
static migration = async () => {};
|
67
|
+
}
|
68
|
+
|
69
|
+
const account = await createJazzTestAccount({
|
70
|
+
isCurrentActiveAccount: true,
|
71
|
+
});
|
72
|
+
|
73
|
+
const credentials: Credentials = {
|
74
|
+
accountID: account.id,
|
75
|
+
secret: account._raw.core.node.account.agentSecret,
|
76
|
+
};
|
77
|
+
|
78
|
+
const context = await createJazzContextFromExistingCredentials({
|
79
|
+
credentials,
|
80
|
+
peersToLoadFrom: [getPeerConnectedToTestSyncServer()],
|
81
|
+
crypto: Crypto,
|
82
|
+
AccountSchema: CustomAccount,
|
83
|
+
sessionProvider: randomSessionProvider,
|
84
|
+
});
|
85
|
+
|
86
|
+
expect(context.account).toBeInstanceOf(CustomAccount);
|
87
|
+
});
|
88
|
+
|
89
|
+
test("calls onLogOut callback when logging out", async () => {
|
90
|
+
const account = await createJazzTestAccount({
|
91
|
+
isCurrentActiveAccount: true,
|
92
|
+
});
|
93
|
+
|
94
|
+
const onLogOut = vi.fn();
|
95
|
+
|
96
|
+
const context = await createJazzContextFromExistingCredentials({
|
97
|
+
credentials: {
|
98
|
+
accountID: account.id,
|
99
|
+
secret: account._raw.core.node.account.agentSecret,
|
100
|
+
},
|
101
|
+
peersToLoadFrom: [getPeerConnectedToTestSyncServer()],
|
102
|
+
crypto: Crypto,
|
103
|
+
sessionProvider: randomSessionProvider,
|
104
|
+
onLogOut,
|
105
|
+
});
|
106
|
+
|
107
|
+
context.logOut();
|
108
|
+
expect(onLogOut).toHaveBeenCalled();
|
109
|
+
});
|
110
|
+
|
111
|
+
test("connects to provided peers", async () => {
|
112
|
+
const account = await createJazzTestAccount({
|
113
|
+
isCurrentActiveAccount: true,
|
114
|
+
});
|
115
|
+
|
116
|
+
const coMap = account._raw.createMap();
|
117
|
+
coMap.set("test", "test", "trusting");
|
118
|
+
|
119
|
+
const context = await createJazzContextFromExistingCredentials({
|
120
|
+
credentials: {
|
121
|
+
accountID: account.id,
|
122
|
+
secret: account._raw.core.node.account.agentSecret,
|
123
|
+
},
|
124
|
+
peersToLoadFrom: [getPeerConnectedToTestSyncServer()],
|
125
|
+
crypto: Crypto,
|
126
|
+
sessionProvider: randomSessionProvider,
|
127
|
+
});
|
128
|
+
|
129
|
+
const loadedMap = await loadCoValueOrFail(context.node, coMap.id);
|
130
|
+
|
131
|
+
expect(loadedMap.get("test")).toBe("test");
|
132
|
+
});
|
133
|
+
|
134
|
+
test("sets the active account", async () => {
|
135
|
+
const account = await createJazzTestAccount({
|
136
|
+
isCurrentActiveAccount: true,
|
137
|
+
});
|
138
|
+
|
139
|
+
const context = await createJazzContextFromExistingCredentials({
|
140
|
+
credentials: {
|
141
|
+
accountID: account.id,
|
142
|
+
secret: account._raw.core.node.account.agentSecret,
|
143
|
+
},
|
144
|
+
peersToLoadFrom: [getPeerConnectedToTestSyncServer()],
|
145
|
+
crypto: Crypto,
|
146
|
+
sessionProvider: randomSessionProvider,
|
147
|
+
});
|
148
|
+
|
149
|
+
expect(activeAccountContext.get()).toBe(context.account);
|
150
|
+
});
|
151
|
+
});
|
152
|
+
|
153
|
+
describe("createJazzContextForNewAccount", () => {
|
154
|
+
test("creates new account with provided props", async () => {
|
155
|
+
const context = await createJazzContextForNewAccount({
|
156
|
+
creationProps: { name: "New User" },
|
157
|
+
peersToLoadFrom: [],
|
158
|
+
crypto: Crypto,
|
159
|
+
});
|
160
|
+
|
161
|
+
expect(context.account).toBeDefined();
|
162
|
+
expect(context.account.profile?.name).toBe("New User");
|
163
|
+
});
|
164
|
+
|
165
|
+
test("uses initial agent secret when provided", async () => {
|
166
|
+
const initialSecret = Crypto.newRandomAgentSecret();
|
167
|
+
|
168
|
+
const context = await createJazzContextForNewAccount({
|
169
|
+
creationProps: { name: "New User" },
|
170
|
+
initialAgentSecret: initialSecret,
|
171
|
+
peersToLoadFrom: [],
|
172
|
+
crypto: Crypto,
|
173
|
+
});
|
174
|
+
|
175
|
+
expect(context.node.account.agentSecret).toBe(initialSecret);
|
176
|
+
});
|
177
|
+
|
178
|
+
test("handles custom account schema", async () => {
|
179
|
+
class CustomAccount extends Account {
|
180
|
+
static migration = async () => {};
|
181
|
+
}
|
182
|
+
|
183
|
+
const context = await createJazzContextForNewAccount({
|
184
|
+
creationProps: { name: "New User" },
|
185
|
+
peersToLoadFrom: [],
|
186
|
+
crypto: Crypto,
|
187
|
+
AccountSchema: CustomAccount,
|
188
|
+
});
|
189
|
+
|
190
|
+
expect(context.account).toBeInstanceOf(CustomAccount);
|
191
|
+
});
|
192
|
+
|
193
|
+
test("sets the active account to the new account", async () => {
|
194
|
+
const context = await createJazzContextForNewAccount({
|
195
|
+
creationProps: { name: "New User" },
|
196
|
+
peersToLoadFrom: [],
|
197
|
+
crypto: Crypto,
|
198
|
+
});
|
199
|
+
expect(activeAccountContext.get()).toBe(context.account);
|
200
|
+
});
|
201
|
+
});
|
202
|
+
|
203
|
+
describe("createAnonymousJazzContext", () => {
|
204
|
+
test("creates anonymous context", async () => {
|
205
|
+
const context = await createAnonymousJazzContext({
|
206
|
+
peersToLoadFrom: [],
|
207
|
+
crypto: Crypto,
|
208
|
+
});
|
209
|
+
|
210
|
+
expect(context.agent).toBeInstanceOf(AnonymousJazzAgent);
|
211
|
+
expect(typeof context.done).toBe("function");
|
212
|
+
expect(typeof context.logOut).toBe("function");
|
213
|
+
});
|
214
|
+
|
215
|
+
test("connects to provided peers", async () => {
|
216
|
+
const account = await createJazzTestAccount({
|
217
|
+
isCurrentActiveAccount: true,
|
218
|
+
});
|
219
|
+
|
220
|
+
const coMap = account._raw.createMap();
|
221
|
+
coMap.set("test", "test", "trusting");
|
222
|
+
|
223
|
+
const context = await createAnonymousJazzContext({
|
224
|
+
peersToLoadFrom: [getPeerConnectedToTestSyncServer()],
|
225
|
+
crypto: Crypto,
|
226
|
+
});
|
227
|
+
|
228
|
+
const loadedMap = await loadCoValueOrFail(context.agent.node, coMap.id);
|
229
|
+
|
230
|
+
expect(loadedMap.get("test")).toBe("test");
|
231
|
+
});
|
232
|
+
|
233
|
+
test("sets the guest mode", async () => {
|
234
|
+
await createAnonymousJazzContext({
|
235
|
+
peersToLoadFrom: [],
|
236
|
+
crypto: Crypto,
|
237
|
+
});
|
238
|
+
|
239
|
+
expect(() => activeAccountContext.get()).toThrow(
|
240
|
+
"Something that expects a full active account was called in guest mode.",
|
241
|
+
);
|
242
|
+
});
|
243
|
+
});
|
244
|
+
|
245
|
+
describe("createJazzContext", () => {
|
246
|
+
test("creates new account when no credentials exist", async () => {
|
247
|
+
const context = await createJazzContext({
|
248
|
+
peersToLoadFrom: [],
|
249
|
+
crypto: Crypto,
|
250
|
+
authSecretStorage,
|
251
|
+
sessionProvider: randomSessionProvider,
|
252
|
+
});
|
253
|
+
|
254
|
+
expect(context.account).toBeDefined();
|
255
|
+
expect(context.authSecretStorage).toBe(authSecretStorage);
|
256
|
+
});
|
257
|
+
|
258
|
+
test("uses existing credentials when available", async () => {
|
259
|
+
// First create an account and store credentials
|
260
|
+
const initialContext = await createJazzContext({
|
261
|
+
peersToLoadFrom: [getPeerConnectedToTestSyncServer()],
|
262
|
+
crypto: Crypto,
|
263
|
+
authSecretStorage,
|
264
|
+
sessionProvider: randomSessionProvider,
|
265
|
+
});
|
266
|
+
|
267
|
+
// Create new context with same storage
|
268
|
+
const newContext = await createJazzContext({
|
269
|
+
peersToLoadFrom: [getPeerConnectedToTestSyncServer()],
|
270
|
+
crypto: Crypto,
|
271
|
+
authSecretStorage,
|
272
|
+
sessionProvider: randomSessionProvider,
|
273
|
+
});
|
274
|
+
|
275
|
+
expect(newContext.account.id).toBe(initialContext.account.id);
|
276
|
+
});
|
277
|
+
|
278
|
+
test("uses provided new account props", async () => {
|
279
|
+
const context = await createJazzContext({
|
280
|
+
newAccountProps: {
|
281
|
+
creationProps: { name: "Custom User" },
|
282
|
+
},
|
283
|
+
peersToLoadFrom: [],
|
284
|
+
crypto: Crypto,
|
285
|
+
authSecretStorage,
|
286
|
+
sessionProvider: randomSessionProvider,
|
287
|
+
});
|
288
|
+
|
289
|
+
expect(context.account.profile?.name).toBe("Custom User");
|
290
|
+
});
|
291
|
+
|
292
|
+
test("uses initial agent secret when provided", async () => {
|
293
|
+
const initialSecret = Crypto.newRandomAgentSecret();
|
294
|
+
|
295
|
+
const storage = new AuthSecretStorage();
|
296
|
+
|
297
|
+
await storage.set({
|
298
|
+
accountID: "test" as ID<Account>,
|
299
|
+
secretSeed: new Uint8Array([1, 2, 3]),
|
300
|
+
accountSecret: "secret123" as AgentSecret,
|
301
|
+
provider: "passkey",
|
302
|
+
});
|
303
|
+
|
304
|
+
const context = await createJazzContext({
|
305
|
+
newAccountProps: {
|
306
|
+
secret: initialSecret,
|
307
|
+
},
|
308
|
+
peersToLoadFrom: [],
|
309
|
+
crypto: Crypto,
|
310
|
+
authSecretStorage,
|
311
|
+
sessionProvider: randomSessionProvider,
|
312
|
+
});
|
313
|
+
|
314
|
+
expect(context.node.account.agentSecret).toBe(initialSecret);
|
315
|
+
expect(await storage.get()).toEqual({
|
316
|
+
accountID: "test" as ID<Account>,
|
317
|
+
secretSeed: new Uint8Array([1, 2, 3]),
|
318
|
+
accountSecret: "secret123" as AgentSecret,
|
319
|
+
provider: "passkey",
|
320
|
+
});
|
321
|
+
});
|
322
|
+
|
323
|
+
test("handles custom account schema", async () => {
|
324
|
+
class CustomAccount extends Account {
|
325
|
+
static migration = async () => {};
|
326
|
+
}
|
327
|
+
|
328
|
+
const context = await createJazzContext({
|
329
|
+
peersToLoadFrom: [],
|
330
|
+
crypto: Crypto,
|
331
|
+
authSecretStorage,
|
332
|
+
sessionProvider: randomSessionProvider,
|
333
|
+
AccountSchema: CustomAccount,
|
334
|
+
});
|
335
|
+
|
336
|
+
expect(context.account).toBeInstanceOf(CustomAccount);
|
337
|
+
});
|
338
|
+
});
|
339
|
+
});
|
@@ -1,5 +1,5 @@
|
|
1
1
|
const Crypto = await WasmCrypto.create();
|
2
|
-
import {
|
2
|
+
import { cojsonInternals } from "cojson";
|
3
3
|
import { describe, expect, expectTypeOf, test } from "vitest";
|
4
4
|
import {
|
5
5
|
Account,
|
@@ -11,12 +11,13 @@ import {
|
|
11
11
|
SessionID,
|
12
12
|
WasmCrypto,
|
13
13
|
co,
|
14
|
-
|
15
|
-
fixedCredentialsAuth,
|
14
|
+
createJazzContextFromExistingCredentials,
|
16
15
|
isControlledAccount,
|
17
16
|
} from "../index.web.js";
|
18
17
|
import { randomSessionProvider } from "../internal.js";
|
19
18
|
|
19
|
+
const { connectedPeers } = cojsonInternals;
|
20
|
+
|
20
21
|
class TestMap extends CoMap {
|
21
22
|
list = co.ref(TestList);
|
22
23
|
optionalRef = co.ref(InnermostMap, { optional: true });
|
@@ -49,15 +50,16 @@ describe("Deep loading with depth arg", async () => {
|
|
49
50
|
throw "me is not a controlled account";
|
50
51
|
}
|
51
52
|
me._raw.core.node.syncManager.addPeer(secondPeer);
|
52
|
-
const { account: meOnSecondPeer } =
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
53
|
+
const { account: meOnSecondPeer } =
|
54
|
+
await createJazzContextFromExistingCredentials({
|
55
|
+
credentials: {
|
56
|
+
accountID: me.id,
|
57
|
+
secret: me._raw.agentSecret,
|
58
|
+
},
|
59
|
+
sessionProvider: randomSessionProvider,
|
60
|
+
peersToLoadFrom: [initialAsPeer],
|
61
|
+
crypto: Crypto,
|
62
|
+
});
|
61
63
|
|
62
64
|
test("loading a deeply nested object will wait until all required refs are loaded", async () => {
|
63
65
|
const ownership = { owner: me };
|
@@ -202,15 +204,14 @@ class CustomAccount extends Account {
|
|
202
204
|
root: { list: [] },
|
203
205
|
});
|
204
206
|
expectTypeOf(thisLoaded).toEqualTypeOf<
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
| undefined
|
207
|
+
CustomAccount & {
|
208
|
+
profile: CustomProfile & {
|
209
|
+
stream: TestStream;
|
210
|
+
};
|
211
|
+
root: TestMap & {
|
212
|
+
list: TestList;
|
213
|
+
};
|
214
|
+
}
|
214
215
|
>();
|
215
216
|
}
|
216
217
|
}
|
@@ -226,19 +227,16 @@ test("Deep loading within account", async () => {
|
|
226
227
|
root: { list: [] },
|
227
228
|
});
|
228
229
|
expectTypeOf(meLoaded).toEqualTypeOf<
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
| undefined
|
230
|
+
CustomAccount & {
|
231
|
+
profile: CustomProfile & {
|
232
|
+
stream: TestStream;
|
233
|
+
};
|
234
|
+
root: TestMap & {
|
235
|
+
list: TestList;
|
236
|
+
};
|
237
|
+
}
|
238
238
|
>();
|
239
|
-
|
240
|
-
throw new Error("meLoaded is undefined");
|
241
|
-
}
|
239
|
+
|
242
240
|
expect(meLoaded.profile.stream).not.toBe(null);
|
243
241
|
expect(meLoaded.root.list).not.toBe(null);
|
244
242
|
});
|
@@ -261,15 +259,16 @@ test("Deep loading a record-like coMap", async () => {
|
|
261
259
|
}
|
262
260
|
|
263
261
|
me._raw.core.node.syncManager.addPeer(secondPeer);
|
264
|
-
const { account: meOnSecondPeer } =
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
262
|
+
const { account: meOnSecondPeer } =
|
263
|
+
await createJazzContextFromExistingCredentials({
|
264
|
+
credentials: {
|
265
|
+
accountID: me.id,
|
266
|
+
secret: me._raw.agentSecret,
|
267
|
+
},
|
268
|
+
sessionProvider: randomSessionProvider,
|
269
|
+
peersToLoadFrom: [initialAsPeer],
|
270
|
+
crypto: Crypto,
|
271
|
+
});
|
273
272
|
|
274
273
|
const record = RecordLike.create(
|
275
274
|
{
|