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.
Files changed (45) hide show
  1. package/.turbo/turbo-build.log +11 -11
  2. package/CHANGELOG.md +19 -0
  3. package/dist/{chunk-OJIEP4WE.js → chunk-UBD75Z27.js} +566 -118
  4. package/dist/chunk-UBD75Z27.js.map +1 -0
  5. package/dist/index.native.js +17 -5
  6. package/dist/index.native.js.map +1 -1
  7. package/dist/index.web.js +17 -5
  8. package/dist/index.web.js.map +1 -1
  9. package/dist/testing.js +124 -33
  10. package/dist/testing.js.map +1 -1
  11. package/package.json +5 -3
  12. package/src/auth/AuthSecretStorage.ts +109 -0
  13. package/src/auth/DemoAuth.ts +188 -0
  14. package/src/auth/InMemoryKVStore.ts +25 -0
  15. package/src/auth/KvStoreContext.ts +39 -0
  16. package/src/auth/PassphraseAuth.ts +113 -0
  17. package/src/coValues/account.ts +8 -3
  18. package/src/coValues/coFeed.ts +1 -1
  19. package/src/coValues/coList.ts +1 -1
  20. package/src/coValues/coMap.ts +1 -1
  21. package/src/coValues/group.ts +9 -8
  22. package/src/coValues/interfaces.ts +14 -5
  23. package/src/exports.ts +17 -3
  24. package/src/implementation/ContextManager.ts +178 -0
  25. package/src/implementation/activeAccountContext.ts +6 -1
  26. package/src/implementation/createContext.ts +173 -149
  27. package/src/testing.ts +171 -33
  28. package/src/tests/AuthSecretStorage.test.ts +275 -0
  29. package/src/tests/ContextManager.test.ts +256 -0
  30. package/src/tests/DemoAuth.test.ts +269 -0
  31. package/src/tests/PassphraseAuth.test.ts +152 -0
  32. package/src/tests/coFeed.test.ts +44 -39
  33. package/src/tests/coList.test.ts +21 -20
  34. package/src/tests/coMap.test.ts +21 -20
  35. package/src/tests/coPlainText.test.ts +21 -20
  36. package/src/tests/coRichText.test.ts +21 -20
  37. package/src/tests/createContext.test.ts +339 -0
  38. package/src/tests/deepLoading.test.ts +41 -42
  39. package/src/tests/fixtures.ts +2050 -0
  40. package/src/tests/groupsAndAccounts.test.ts +2 -2
  41. package/src/tests/subscribe.test.ts +42 -9
  42. package/src/tests/testing.test.ts +56 -0
  43. package/src/tests/utils.ts +11 -11
  44. package/src/types.ts +54 -0
  45. package/dist/chunk-OJIEP4WE.js.map +0 -1
@@ -1,19 +1,25 @@
1
1
  import {
2
2
  Account,
3
3
  AnonymousJazzAgent,
4
+ AuthSecretStorage,
4
5
  CoFeed,
5
6
  CoList,
6
7
  CoMap,
7
8
  CoPlainText,
8
9
  CoRichText,
9
10
  CoValueBase,
11
+ DemoAuth,
10
12
  Encoders,
11
13
  FileStream,
12
14
  Group,
13
15
  ImageDefinition,
16
+ InMemoryKVStore,
14
17
  Inbox,
15
18
  InboxSender,
19
+ JazzContextManager,
20
+ KvStoreContext,
16
21
  Marks,
22
+ PassphraseAuth,
17
23
  Profile,
18
24
  SchemaUnion,
19
25
  co,
@@ -22,14 +28,14 @@ import {
22
28
  createCoValueObservable,
23
29
  createInviteLink,
24
30
  createJazzContext,
25
- ephemeralCredentialsAuth,
26
- fixedCredentialsAuth,
31
+ createJazzContextForNewAccount,
32
+ createJazzContextFromExistingCredentials,
27
33
  isControlledAccount,
28
34
  loadCoValue,
29
35
  parseInviteLink,
30
36
  randomSessionProvider,
31
37
  subscribeToCoValue
32
- } from "./chunk-OJIEP4WE.js";
38
+ } from "./chunk-UBD75Z27.js";
33
39
 
34
40
  // src/index.native.ts
35
41
  import {
@@ -39,6 +45,7 @@ import {
39
45
  export {
40
46
  Account,
41
47
  AnonymousJazzAgent,
48
+ AuthSecretStorage,
42
49
  FileStream as BinaryCoStream,
43
50
  CoFeed,
44
51
  CoList,
@@ -47,14 +54,19 @@ export {
47
54
  CoRichText,
48
55
  CoFeed as CoStream,
49
56
  CoValueBase,
57
+ DemoAuth,
50
58
  Encoders,
51
59
  FileStream,
52
60
  Group,
53
61
  ImageDefinition,
62
+ InMemoryKVStore,
54
63
  Inbox,
55
64
  InboxSender,
65
+ JazzContextManager,
66
+ KvStoreContext,
56
67
  MAX_RECOMMENDED_TX_SIZE,
57
68
  Marks,
69
+ PassphraseAuth,
58
70
  Profile,
59
71
  SchemaUnion,
60
72
  co,
@@ -64,8 +76,8 @@ export {
64
76
  createCoValueObservable,
65
77
  createInviteLink,
66
78
  createJazzContext,
67
- ephemeralCredentialsAuth,
68
- fixedCredentialsAuth,
79
+ createJazzContextForNewAccount,
80
+ createJazzContextFromExistingCredentials,
69
81
  isControlledAccount,
70
82
  loadCoValue,
71
83
  parseInviteLink,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.native.ts"],"sourcesContent":["export * from \"./exports.js\";\n\nexport {\n MAX_RECOMMENDED_TX_SIZE,\n cojsonInternals,\n} from \"cojson/native\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEA;AAAA,EACE;AAAA,EACA;AAAA,OACK;","names":[]}
1
+ {"version":3,"sources":["../src/index.native.ts"],"sourcesContent":["export * from \"./exports.js\";\n\nexport {\n MAX_RECOMMENDED_TX_SIZE,\n cojsonInternals,\n} from \"cojson/native\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEA;AAAA,EACE;AAAA,EACA;AAAA,OACK;","names":[]}
package/dist/index.web.js CHANGED
@@ -1,19 +1,25 @@
1
1
  import {
2
2
  Account,
3
3
  AnonymousJazzAgent,
4
+ AuthSecretStorage,
4
5
  CoFeed,
5
6
  CoList,
6
7
  CoMap,
7
8
  CoPlainText,
8
9
  CoRichText,
9
10
  CoValueBase,
11
+ DemoAuth,
10
12
  Encoders,
11
13
  FileStream,
12
14
  Group,
13
15
  ImageDefinition,
16
+ InMemoryKVStore,
14
17
  Inbox,
15
18
  InboxSender,
19
+ JazzContextManager,
20
+ KvStoreContext,
16
21
  Marks,
22
+ PassphraseAuth,
17
23
  Profile,
18
24
  SchemaUnion,
19
25
  co,
@@ -22,20 +28,21 @@ import {
22
28
  createCoValueObservable,
23
29
  createInviteLink,
24
30
  createJazzContext,
25
- ephemeralCredentialsAuth,
26
- fixedCredentialsAuth,
31
+ createJazzContextForNewAccount,
32
+ createJazzContextFromExistingCredentials,
27
33
  isControlledAccount,
28
34
  loadCoValue,
29
35
  parseInviteLink,
30
36
  randomSessionProvider,
31
37
  subscribeToCoValue
32
- } from "./chunk-OJIEP4WE.js";
38
+ } from "./chunk-UBD75Z27.js";
33
39
 
34
40
  // src/index.web.ts
35
41
  import { cojsonInternals, MAX_RECOMMENDED_TX_SIZE, WasmCrypto } from "cojson";
36
42
  export {
37
43
  Account,
38
44
  AnonymousJazzAgent,
45
+ AuthSecretStorage,
39
46
  FileStream as BinaryCoStream,
40
47
  CoFeed,
41
48
  CoList,
@@ -44,14 +51,19 @@ export {
44
51
  CoRichText,
45
52
  CoFeed as CoStream,
46
53
  CoValueBase,
54
+ DemoAuth,
47
55
  Encoders,
48
56
  FileStream,
49
57
  Group,
50
58
  ImageDefinition,
59
+ InMemoryKVStore,
51
60
  Inbox,
52
61
  InboxSender,
62
+ JazzContextManager,
63
+ KvStoreContext,
53
64
  MAX_RECOMMENDED_TX_SIZE,
54
65
  Marks,
66
+ PassphraseAuth,
55
67
  Profile,
56
68
  SchemaUnion,
57
69
  WasmCrypto,
@@ -62,8 +74,8 @@ export {
62
74
  createCoValueObservable,
63
75
  createInviteLink,
64
76
  createJazzContext,
65
- ephemeralCredentialsAuth,
66
- fixedCredentialsAuth,
77
+ createJazzContextForNewAccount,
78
+ createJazzContextFromExistingCredentials,
67
79
  isControlledAccount,
68
80
  loadCoValue,
69
81
  parseInviteLink,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.web.ts"],"sourcesContent":["export * from \"./exports.js\";\n\nexport { cojsonInternals, MAX_RECOMMENDED_TX_SIZE, WasmCrypto } from \"cojson\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEA,SAAS,iBAAiB,yBAAyB,kBAAkB;","names":[]}
1
+ {"version":3,"sources":["../src/index.web.ts"],"sourcesContent":["export * from \"./exports.js\";\n\nexport { cojsonInternals, MAX_RECOMMENDED_TX_SIZE, WasmCrypto } from \"cojson\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEA,SAAS,iBAAiB,yBAAyB,kBAAkB;","names":[]}
package/dist/testing.js CHANGED
@@ -1,8 +1,11 @@
1
1
  import {
2
2
  Account,
3
+ JazzContextManager,
3
4
  activeAccountContext,
4
- createAnonymousJazzContext
5
- } from "./chunk-OJIEP4WE.js";
5
+ createAnonymousJazzContext,
6
+ createJazzContext,
7
+ randomSessionProvider
8
+ } from "./chunk-UBD75Z27.js";
6
9
 
7
10
  // src/testing.ts
8
11
  import { LocalNode } from "cojson";
@@ -11,7 +14,7 @@ import { PureJSCrypto } from "cojson/crypto";
11
14
  var syncServer = { current: null };
12
15
  var TestJSCrypto = class extends PureJSCrypto {
13
16
  static async create() {
14
- if ("navigator" in globalThis && navigator.userAgent.includes("jsdom")) {
17
+ if ("navigator" in globalThis && navigator.userAgent?.includes("jsdom")) {
15
18
  const crypto = new PureJSCrypto();
16
19
  crypto.seal = (options) => `sealed_U${cojsonInternals.stableStringify(options.message)}`;
17
20
  crypto.unseal = (sealed) => JSON.parse(sealed.substring("sealed_U".length));
@@ -22,39 +25,60 @@ var TestJSCrypto = class extends PureJSCrypto {
22
25
  return new PureJSCrypto();
23
26
  }
24
27
  };
28
+ function getPeerConnectedToTestSyncServer() {
29
+ if (!syncServer.current) {
30
+ throw new Error("Sync server not initialized");
31
+ }
32
+ const [aPeer, bPeer] = cojsonInternals.connectedPeers(
33
+ Math.random().toString(),
34
+ Math.random().toString(),
35
+ {
36
+ peer1role: "server",
37
+ peer2role: "server"
38
+ }
39
+ );
40
+ syncServer.current.syncManager.addPeer(aPeer);
41
+ return bPeer;
42
+ }
43
+ var SecretSeedMap = /* @__PURE__ */ new Map();
44
+ var isMigrationActive = false;
25
45
  async function createJazzTestAccount(options) {
26
46
  const AccountSchema = options?.AccountSchema ?? Account;
27
47
  const peers = [];
28
48
  if (syncServer.current) {
29
- const [aPeer, bPeer] = cojsonInternals.connectedPeers(
30
- Math.random().toString(),
31
- Math.random().toString(),
32
- {
33
- peer1role: "server",
34
- peer2role: "server"
35
- }
36
- );
37
- syncServer.current.syncManager.addPeer(aPeer);
38
- peers.push(bPeer);
49
+ peers.push(getPeerConnectedToTestSyncServer());
39
50
  }
51
+ const crypto = await TestJSCrypto.create();
52
+ const secretSeed = crypto.newRandomSecretSeed();
40
53
  const { node } = await LocalNode.withNewlyCreatedAccount({
41
54
  creationProps: {
42
55
  name: "Test Account",
43
56
  ...options?.creationProps
44
57
  },
45
- crypto: await TestJSCrypto.create(),
58
+ initialAgentSecret: crypto.agentSecretFromSecretSeed(secretSeed),
59
+ crypto,
46
60
  peersToLoadFrom: peers,
47
61
  migration: async (rawAccount, _node, creationProps) => {
62
+ if (isMigrationActive) {
63
+ throw new Error(
64
+ "It is not possible to create multiple accounts in parallel inside the test environment."
65
+ );
66
+ }
67
+ isMigrationActive = true;
48
68
  const account2 = new AccountSchema({
49
69
  fromRaw: rawAccount
50
70
  });
51
- if (options?.isCurrentActiveAccount) {
52
- activeAccountContext.set(account2);
53
- }
71
+ const prevActiveAccount = activeAccountContext.maybeGet();
72
+ activeAccountContext.set(account2);
54
73
  await account2.applyMigration?.(creationProps);
74
+ if (!options?.isCurrentActiveAccount) {
75
+ activeAccountContext.set(prevActiveAccount);
76
+ }
77
+ isMigrationActive = false;
55
78
  }
56
79
  });
57
80
  const account = AccountSchema.fromNode(node);
81
+ SecretSeedMap.set(account.id, secretSeed);
58
82
  if (options?.isCurrentActiveAccount) {
59
83
  activeAccountContext.set(account);
60
84
  }
@@ -72,22 +96,87 @@ async function createJazzTestGuest() {
72
96
  guest: ctx.agent
73
97
  };
74
98
  }
75
- function getJazzContextShape(account) {
76
- if ("guest" in account) {
77
- return {
78
- guest: account.guest,
79
- AccountSchema: Account,
80
- logOut: () => account.guest.node.gracefulShutdown(),
81
- done: () => account.guest.node.gracefulShutdown()
82
- };
99
+ var TestJazzContextManager = class _TestJazzContextManager extends JazzContextManager {
100
+ static fromAccountOrGuest(account, props) {
101
+ if (account && "guest" in account) {
102
+ return this.fromGuest(account, props);
103
+ }
104
+ return this.fromAccount(account ?? Account.getMe(), props);
83
105
  }
84
- return {
85
- me: account,
86
- AccountSchema: account.constructor,
87
- logOut: () => account._raw.core.node.gracefulShutdown(),
88
- done: () => account._raw.core.node.gracefulShutdown()
89
- };
90
- }
106
+ static fromAccount(account, props) {
107
+ const context = new _TestJazzContextManager();
108
+ const provider = props?.isAuthenticated ? "testProvider" : "anonymous";
109
+ const storage = context.getAuthSecretStorage();
110
+ const node = account._raw.core.node;
111
+ storage.set({
112
+ accountID: account.id,
113
+ accountSecret: node.account.agentSecret,
114
+ secretSeed: SecretSeedMap.get(account.id),
115
+ provider
116
+ });
117
+ storage.isAuthenticated = Boolean(props?.isAuthenticated);
118
+ context.updateContext(
119
+ {
120
+ AccountSchema: account.constructor,
121
+ ...props
122
+ },
123
+ {
124
+ me: account,
125
+ node,
126
+ done: () => {
127
+ node.gracefulShutdown();
128
+ },
129
+ logOut: async () => {
130
+ node.gracefulShutdown();
131
+ }
132
+ }
133
+ );
134
+ return context;
135
+ }
136
+ static fromGuest({ guest }, props = {}) {
137
+ const context = new _TestJazzContextManager();
138
+ const node = guest.node;
139
+ context.updateContext(props, {
140
+ guest,
141
+ node,
142
+ done: () => {
143
+ node.gracefulShutdown();
144
+ },
145
+ logOut: async () => {
146
+ node.gracefulShutdown();
147
+ }
148
+ });
149
+ return context;
150
+ }
151
+ async createContext(props, authProps) {
152
+ this.props = props;
153
+ if (!syncServer.current) {
154
+ throw new Error(
155
+ "You need to setup a test sync server with setupJazzTestSync to use the Auth functions"
156
+ );
157
+ }
158
+ const context = await createJazzContext({
159
+ credentials: authProps?.credentials,
160
+ defaultProfileName: props.defaultProfileName,
161
+ newAccountProps: authProps?.newAccountProps,
162
+ peersToLoadFrom: [getPeerConnectedToTestSyncServer()],
163
+ crypto: await TestJSCrypto.create(),
164
+ sessionProvider: randomSessionProvider,
165
+ authSecretStorage: this.getAuthSecretStorage(),
166
+ AccountSchema: props.AccountSchema
167
+ });
168
+ this.updateContext(props, {
169
+ me: context.account,
170
+ node: context.node,
171
+ done: () => {
172
+ context.done();
173
+ },
174
+ logOut: () => {
175
+ return context.logOut();
176
+ }
177
+ });
178
+ }
179
+ };
91
180
  function linkAccounts(a, b, aRole = "server", bRole = "server") {
92
181
  const [aPeer, bPeer] = cojsonInternals.connectedPeers(b.id, a.id, {
93
182
  peer1role: aRole,
@@ -110,9 +199,11 @@ async function setupJazzTestSync() {
110
199
  return account;
111
200
  }
112
201
  export {
202
+ TestJSCrypto,
203
+ TestJazzContextManager,
113
204
  createJazzTestAccount,
114
205
  createJazzTestGuest,
115
- getJazzContextShape,
206
+ getPeerConnectedToTestSyncServer,
116
207
  linkAccounts,
117
208
  setActiveAccount,
118
209
  setupJazzTestSync
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/testing.ts"],"sourcesContent":["import { AgentSecret, CryptoProvider, LocalNode, Peer } from \"cojson\";\nimport { cojsonInternals } from \"cojson\";\nimport { PureJSCrypto } from \"cojson/crypto\";\nimport { Account, type AccountClass } from \"./exports.js\";\nimport { activeAccountContext } from \"./implementation/activeAccountContext.js\";\nimport {\n type AnonymousJazzAgent,\n type CoValueClass,\n createAnonymousJazzContext,\n} from \"./internal.js\";\n\nconst syncServer: { current: LocalNode | null } = { current: null };\n\ntype TestAccountSchema<Acc extends Account> = CoValueClass<Acc> & {\n fromNode: (typeof Account)[\"fromNode\"];\n create: (options: {\n creationProps: { name: string };\n initialAgentSecret?: AgentSecret;\n peersToLoadFrom?: Peer[];\n crypto: CryptoProvider;\n }) => Promise<Acc>;\n};\n\nclass TestJSCrypto extends PureJSCrypto {\n static async create() {\n if (\"navigator\" in globalThis && navigator.userAgent.includes(\"jsdom\")) {\n // Mocking crypto seal & encrypt to make it work with JSDom. Getting \"Error: Uint8Array expected\" there\n const crypto = new PureJSCrypto();\n\n crypto.seal = (options) =>\n `sealed_U${cojsonInternals.stableStringify(options.message)}` as any;\n crypto.unseal = (sealed) =>\n JSON.parse(sealed.substring(\"sealed_U\".length));\n crypto.encrypt = (message) =>\n `encrypted_U${cojsonInternals.stableStringify(message)}` as any;\n crypto.decryptRaw = (encrypted) =>\n encrypted.substring(\"encrypted_U\".length) as any;\n\n return crypto;\n }\n\n // For non-jsdom environments, we use the real crypto\n return new PureJSCrypto();\n }\n}\n\nexport async function createJazzTestAccount<Acc extends Account>(options?: {\n isCurrentActiveAccount?: boolean;\n AccountSchema?: CoValueClass<Acc>;\n creationProps?: Record<string, unknown>;\n}): Promise<Acc> {\n const AccountSchema = (options?.AccountSchema ??\n Account) as unknown as TestAccountSchema<Acc>;\n const peers = [];\n if (syncServer.current) {\n const [aPeer, bPeer] = cojsonInternals.connectedPeers(\n Math.random().toString(),\n Math.random().toString(),\n {\n peer1role: \"server\",\n peer2role: \"server\",\n },\n );\n syncServer.current.syncManager.addPeer(aPeer);\n peers.push(bPeer);\n }\n\n const { node } = await LocalNode.withNewlyCreatedAccount({\n creationProps: {\n name: \"Test Account\",\n ...options?.creationProps,\n },\n crypto: await TestJSCrypto.create(),\n peersToLoadFrom: peers,\n migration: async (rawAccount, _node, creationProps) => {\n const account = new AccountSchema({\n fromRaw: rawAccount,\n });\n\n if (options?.isCurrentActiveAccount) {\n activeAccountContext.set(account);\n }\n\n await account.applyMigration?.(creationProps);\n },\n });\n\n const account = AccountSchema.fromNode(node);\n\n if (options?.isCurrentActiveAccount) {\n activeAccountContext.set(account);\n }\n\n return account;\n}\n\nexport function setActiveAccount(account: Account) {\n activeAccountContext.set(account);\n}\n\nexport async function createJazzTestGuest() {\n const ctx = await createAnonymousJazzContext({\n crypto: await PureJSCrypto.create(),\n peersToLoadFrom: [],\n });\n\n return {\n guest: ctx.agent,\n };\n}\n\nexport function getJazzContextShape<Acc extends Account>(\n account: Acc | { guest: AnonymousJazzAgent },\n) {\n if (\"guest\" in account) {\n return {\n guest: account.guest,\n AccountSchema: Account,\n logOut: () => account.guest.node.gracefulShutdown(),\n done: () => account.guest.node.gracefulShutdown(),\n };\n }\n\n return {\n me: account,\n AccountSchema: account.constructor as AccountClass<Acc>,\n logOut: () => account._raw.core.node.gracefulShutdown(),\n done: () => account._raw.core.node.gracefulShutdown(),\n };\n}\n\nexport function linkAccounts(\n a: Account,\n b: Account,\n aRole: \"server\" | \"client\" = \"server\",\n bRole: \"server\" | \"client\" = \"server\",\n) {\n const [aPeer, bPeer] = cojsonInternals.connectedPeers(b.id, a.id, {\n peer1role: aRole,\n peer2role: bRole,\n });\n\n a._raw.core.node.syncManager.addPeer(aPeer);\n b._raw.core.node.syncManager.addPeer(bPeer);\n}\n\nexport async function setupJazzTestSync() {\n if (syncServer.current) {\n syncServer.current.gracefulShutdown();\n }\n\n const account = await Account.create({\n creationProps: {\n name: \"Test Account\",\n },\n crypto: await TestJSCrypto.create(),\n });\n\n syncServer.current = account._raw.core.node;\n\n return account;\n}\n"],"mappings":"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","names":["account"]}
1
+ {"version":3,"sources":["../src/testing.ts"],"sourcesContent":["import { AgentSecret, CryptoProvider, LocalNode, Peer } from \"cojson\";\nimport { cojsonInternals } from \"cojson\";\nimport { PureJSCrypto } from \"cojson/crypto\";\nimport {\n Account,\n AccountClass,\n JazzContextManagerAuthProps,\n} from \"./exports.js\";\nimport {\n JazzContextManager,\n JazzContextManagerBaseProps,\n} from \"./implementation/ContextManager.js\";\nimport { activeAccountContext } from \"./implementation/activeAccountContext.js\";\nimport {\n type AnonymousJazzAgent,\n type CoValueClass,\n createAnonymousJazzContext,\n createJazzContext,\n randomSessionProvider,\n} from \"./internal.js\";\n\nconst syncServer: { current: LocalNode | null } = { current: null };\n\ntype TestAccountSchema<Acc extends Account> = CoValueClass<Acc> & {\n fromNode: (typeof Account)[\"fromNode\"];\n create: (options: {\n creationProps: { name: string };\n initialAgentSecret?: AgentSecret;\n peersToLoadFrom?: Peer[];\n crypto: CryptoProvider;\n }) => Promise<Acc>;\n};\n\nexport class TestJSCrypto extends PureJSCrypto {\n static async create() {\n if (\"navigator\" in globalThis && navigator.userAgent?.includes(\"jsdom\")) {\n // Mocking crypto seal & encrypt to make it work with JSDom. Getting \"Error: Uint8Array expected\" there\n const crypto = new PureJSCrypto();\n\n crypto.seal = (options) =>\n `sealed_U${cojsonInternals.stableStringify(options.message)}` as any;\n crypto.unseal = (sealed) =>\n JSON.parse(sealed.substring(\"sealed_U\".length));\n crypto.encrypt = (message) =>\n `encrypted_U${cojsonInternals.stableStringify(message)}` as any;\n crypto.decryptRaw = (encrypted) =>\n encrypted.substring(\"encrypted_U\".length) as any;\n\n return crypto;\n }\n\n // For non-jsdom environments, we use the real crypto\n return new PureJSCrypto();\n }\n}\n\nexport function getPeerConnectedToTestSyncServer() {\n if (!syncServer.current) {\n throw new Error(\"Sync server not initialized\");\n }\n\n const [aPeer, bPeer] = cojsonInternals.connectedPeers(\n Math.random().toString(),\n Math.random().toString(),\n {\n peer1role: \"server\",\n peer2role: \"server\",\n },\n );\n syncServer.current.syncManager.addPeer(aPeer);\n\n return bPeer;\n}\n\nconst SecretSeedMap = new Map<string, Uint8Array>();\nlet isMigrationActive = false;\n\nexport async function createJazzTestAccount<Acc extends Account>(options?: {\n isCurrentActiveAccount?: boolean;\n AccountSchema?: CoValueClass<Acc>;\n creationProps?: Record<string, unknown>;\n}): Promise<Acc> {\n const AccountSchema = (options?.AccountSchema ??\n Account) as unknown as TestAccountSchema<Acc>;\n const peers = [];\n if (syncServer.current) {\n peers.push(getPeerConnectedToTestSyncServer());\n }\n\n const crypto = await TestJSCrypto.create();\n const secretSeed = crypto.newRandomSecretSeed();\n\n const { node } = await LocalNode.withNewlyCreatedAccount({\n creationProps: {\n name: \"Test Account\",\n ...options?.creationProps,\n },\n initialAgentSecret: crypto.agentSecretFromSecretSeed(secretSeed),\n crypto,\n peersToLoadFrom: peers,\n migration: async (rawAccount, _node, creationProps) => {\n if (isMigrationActive) {\n throw new Error(\n \"It is not possible to create multiple accounts in parallel inside the test environment.\",\n );\n }\n\n isMigrationActive = true;\n\n const account = new AccountSchema({\n fromRaw: rawAccount,\n });\n\n // We need to set the account as current because the migration\n // will probably rely on the global me\n const prevActiveAccount = activeAccountContext.maybeGet();\n activeAccountContext.set(account);\n\n await account.applyMigration?.(creationProps);\n\n if (!options?.isCurrentActiveAccount) {\n activeAccountContext.set(prevActiveAccount);\n }\n\n isMigrationActive = false;\n },\n });\n\n const account = AccountSchema.fromNode(node);\n SecretSeedMap.set(account.id, secretSeed);\n\n if (options?.isCurrentActiveAccount) {\n activeAccountContext.set(account);\n }\n\n return account;\n}\n\nexport function setActiveAccount(account: Account) {\n activeAccountContext.set(account);\n}\n\nexport async function createJazzTestGuest() {\n const ctx = await createAnonymousJazzContext({\n crypto: await PureJSCrypto.create(),\n peersToLoadFrom: [],\n });\n\n return {\n guest: ctx.agent,\n };\n}\n\nexport type TestJazzContextManagerProps<Acc extends Account> =\n JazzContextManagerBaseProps<Acc> & {\n defaultProfileName?: string;\n AccountSchema?: AccountClass<Acc>;\n isAuthenticated?: boolean;\n };\n\nexport class TestJazzContextManager<\n Acc extends Account,\n> extends JazzContextManager<Acc, TestJazzContextManagerProps<Acc>> {\n static fromAccountOrGuest<Acc extends Account>(\n account?: Acc | { guest: AnonymousJazzAgent },\n props?: TestJazzContextManagerProps<Acc>,\n ) {\n if (account && \"guest\" in account) {\n return this.fromGuest<Acc>(account, props);\n }\n\n return this.fromAccount<Acc>(account ?? (Account.getMe() as Acc), props);\n }\n\n static fromAccount<Acc extends Account>(\n account: Acc,\n props?: TestJazzContextManagerProps<Acc>,\n ) {\n const context = new TestJazzContextManager<Acc>();\n\n const provider = props?.isAuthenticated ? \"testProvider\" : \"anonymous\";\n const storage = context.getAuthSecretStorage();\n const node = account._raw.core.node;\n\n storage.set({\n accountID: account.id,\n accountSecret: node.account.agentSecret,\n secretSeed: SecretSeedMap.get(account.id),\n provider,\n });\n storage.isAuthenticated = Boolean(props?.isAuthenticated);\n\n context.updateContext(\n {\n AccountSchema: account.constructor as AccountClass<Acc>,\n ...props,\n },\n {\n me: account,\n node,\n done: () => {\n node.gracefulShutdown();\n },\n logOut: async () => {\n node.gracefulShutdown();\n },\n },\n );\n\n return context;\n }\n\n static fromGuest<Acc extends Account>(\n { guest }: { guest: AnonymousJazzAgent },\n props: TestJazzContextManagerProps<Acc> = {},\n ) {\n const context = new TestJazzContextManager<Acc>();\n const node = guest.node;\n\n context.updateContext(props, {\n guest,\n node,\n done: () => {\n node.gracefulShutdown();\n },\n logOut: async () => {\n node.gracefulShutdown();\n },\n });\n\n return context;\n }\n\n async createContext(\n props: TestJazzContextManagerProps<Acc>,\n authProps?: JazzContextManagerAuthProps,\n ) {\n this.props = props;\n\n if (!syncServer.current) {\n throw new Error(\n \"You need to setup a test sync server with setupJazzTestSync to use the Auth functions\",\n );\n }\n\n const context = await createJazzContext<Acc>({\n credentials: authProps?.credentials,\n defaultProfileName: props.defaultProfileName,\n newAccountProps: authProps?.newAccountProps,\n peersToLoadFrom: [getPeerConnectedToTestSyncServer()],\n crypto: await TestJSCrypto.create(),\n sessionProvider: randomSessionProvider,\n authSecretStorage: this.getAuthSecretStorage(),\n AccountSchema: props.AccountSchema,\n });\n\n this.updateContext(props, {\n me: context.account,\n node: context.node,\n done: () => {\n context.done();\n },\n logOut: () => {\n return context.logOut();\n },\n });\n }\n}\n\nexport function linkAccounts(\n a: Account,\n b: Account,\n aRole: \"server\" | \"client\" = \"server\",\n bRole: \"server\" | \"client\" = \"server\",\n) {\n const [aPeer, bPeer] = cojsonInternals.connectedPeers(b.id, a.id, {\n peer1role: aRole,\n peer2role: bRole,\n });\n\n a._raw.core.node.syncManager.addPeer(aPeer);\n b._raw.core.node.syncManager.addPeer(bPeer);\n}\n\nexport async function setupJazzTestSync() {\n if (syncServer.current) {\n syncServer.current.gracefulShutdown();\n }\n\n const account = await Account.create({\n creationProps: {\n name: \"Test Account\",\n },\n crypto: await TestJSCrypto.create(),\n });\n\n syncServer.current = account._raw.core.node;\n\n return account;\n}\n"],"mappings":"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","names":["account"]}
package/package.json CHANGED
@@ -23,14 +23,16 @@
23
23
  },
24
24
  "type": "module",
25
25
  "license": "MIT",
26
- "version": "0.9.23",
26
+ "version": "0.10.0",
27
27
  "dependencies": {
28
- "cojson": "0.9.23"
28
+ "@scure/bip39": "^1.3.0",
29
+ "cojson": "0.10.0"
29
30
  },
30
31
  "devDependencies": {
31
32
  "tsup": "8.3.5",
32
33
  "typescript": "~5.6.2",
33
- "vitest": "1.5.3"
34
+ "vitest": "3.0.5",
35
+ "ws": "^8.14.2"
34
36
  },
35
37
  "madge": {
36
38
  "detectiveOptions": {
@@ -0,0 +1,109 @@
1
+ import { AgentSecret } from "cojson";
2
+ import type { Account } from "../coValues/account.js";
3
+ import type { ID } from "../internal.js";
4
+ import { AuthCredentials } from "../types.js";
5
+ import KvStoreContext from "./KvStoreContext.js";
6
+
7
+ const STORAGE_KEY = "jazz-logged-in-secret";
8
+
9
+ export type AuthSetPayload = {
10
+ accountID: ID<Account>;
11
+ secretSeed?: Uint8Array;
12
+ accountSecret: AgentSecret;
13
+ provider: "anonymous" | "clerk" | "demo" | "passkey" | "passphrase" | string;
14
+ };
15
+
16
+ export class AuthSecretStorage {
17
+ private listeners: Set<(isAuthenticated: boolean) => void>;
18
+ public isAuthenticated: boolean;
19
+
20
+ constructor() {
21
+ this.listeners = new Set();
22
+ this.isAuthenticated = false;
23
+ }
24
+
25
+ async migrate() {
26
+ const kvStore = KvStoreContext.getInstance().getStorage();
27
+
28
+ if (!(await kvStore.get(STORAGE_KEY))) {
29
+ const demoAuthSecret = await kvStore.get("demo-auth-logged-in-secret");
30
+ if (demoAuthSecret) {
31
+ await kvStore.set(STORAGE_KEY, demoAuthSecret);
32
+ await kvStore.delete("demo-auth-logged-in-secret");
33
+ }
34
+
35
+ const clerkAuthSecret = await kvStore.get("jazz-clerk-auth");
36
+ if (clerkAuthSecret) {
37
+ await kvStore.set(STORAGE_KEY, clerkAuthSecret);
38
+ await kvStore.delete("jazz-clerk-auth");
39
+ }
40
+ }
41
+ }
42
+
43
+ async get(): Promise<AuthCredentials | null> {
44
+ const kvStore = KvStoreContext.getInstance().getStorage();
45
+ const data = await kvStore.get(STORAGE_KEY);
46
+
47
+ if (!data) return null;
48
+
49
+ const parsed = JSON.parse(data);
50
+
51
+ if (!parsed.accountID || !parsed.accountSecret) {
52
+ throw new Error("Invalid auth secret storage data");
53
+ }
54
+
55
+ return {
56
+ accountID: parsed.accountID,
57
+ secretSeed: parsed.secretSeed
58
+ ? new Uint8Array(parsed.secretSeed)
59
+ : undefined,
60
+ accountSecret: parsed.accountSecret,
61
+ provider: parsed.provider,
62
+ };
63
+ }
64
+
65
+ async set(payload: AuthSetPayload) {
66
+ const kvStore = KvStoreContext.getInstance().getStorage();
67
+ await kvStore.set(
68
+ STORAGE_KEY,
69
+ JSON.stringify({
70
+ accountID: payload.accountID,
71
+ secretSeed: payload.secretSeed
72
+ ? Array.from(payload.secretSeed)
73
+ : undefined,
74
+ accountSecret: payload.accountSecret,
75
+ provider: payload.provider,
76
+ }),
77
+ );
78
+ this.emitUpdate(payload);
79
+ }
80
+
81
+ getIsAuthenticated(data: AuthCredentials | null): boolean {
82
+ if (!data) return false;
83
+ return data.provider !== "anonymous";
84
+ }
85
+
86
+ onUpdate(handler: (isAuthenticated: boolean) => void) {
87
+ this.listeners.add(handler);
88
+ return () => {
89
+ this.listeners.delete(handler);
90
+ };
91
+ }
92
+
93
+ emitUpdate(data: AuthCredentials | null) {
94
+ const isAuthenticated = this.getIsAuthenticated(data);
95
+
96
+ if (this.isAuthenticated === isAuthenticated) return;
97
+
98
+ this.isAuthenticated = isAuthenticated;
99
+ for (const listener of this.listeners) {
100
+ listener(this.isAuthenticated);
101
+ }
102
+ }
103
+
104
+ async clear() {
105
+ const kvStore = KvStoreContext.getInstance().getStorage();
106
+ await kvStore.delete(STORAGE_KEY);
107
+ this.emitUpdate(null);
108
+ }
109
+ }