jazz-tools 0.10.13 → 0.10.15

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-GSIV52ZH.js";
38
+ } from "./chunk-5USJBXLW.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-GSIV52ZH.js";
8
+ } from "./chunk-5USJBXLW.js";
9
9
 
10
10
  // src/testing.ts
11
11
  import { LocalNode } from "cojson";
@@ -152,8 +152,7 @@ var TestJazzContextManager = class _TestJazzContextManager extends JazzContextMa
152
152
  });
153
153
  return context;
154
154
  }
155
- async createContext(props, authProps) {
156
- this.props = props;
155
+ async getNewContext(props, authProps) {
157
156
  if (!syncServer.current) {
158
157
  throw new Error(
159
158
  "You need to setup a test sync server with setupJazzTestSync to use the Auth functions"
@@ -169,20 +168,16 @@ var TestJazzContextManager = class _TestJazzContextManager extends JazzContextMa
169
168
  authSecretStorage: this.getAuthSecretStorage(),
170
169
  AccountSchema: props.AccountSchema
171
170
  });
172
- await this.updateContext(
173
- props,
174
- {
175
- me: context.account,
176
- node: context.node,
177
- done: () => {
178
- context.done();
179
- },
180
- logOut: () => {
181
- return context.logOut();
182
- }
171
+ return {
172
+ me: context.account,
173
+ node: context.node,
174
+ done: () => {
175
+ context.done();
183
176
  },
184
- authProps
185
- );
177
+ logOut: () => {
178
+ return context.logOut();
179
+ }
180
+ };
186
181
  }
187
182
  };
188
183
  function linkAccounts(a, b, aRole = "server", bRole = "server") {
@@ -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/dist/crypto/PureJSCrypto\";\nimport {\n Account,\n AccountClass,\n AuthCredentials,\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 const credentials = {\n accountID: account.id,\n accountSecret: node.account.agentSecret,\n secretSeed: SecretSeedMap.get(account.id),\n provider,\n } satisfies AuthCredentials;\n\n storage.set(credentials);\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 await storage.clear();\n node.gracefulShutdown();\n },\n },\n {\n credentials,\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 await this.updateContext(\n props,\n {\n me: context.account,\n node: context.node,\n done: () => {\n context.done();\n },\n logOut: () => {\n return context.logOut();\n },\n },\n authProps,\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"]}
1
+ {"version":3,"sources":["../src/testing.ts"],"sourcesContent":["import { AgentSecret, CryptoProvider, LocalNode, Peer } from \"cojson\";\nimport { cojsonInternals } from \"cojson\";\nimport { PureJSCrypto } from \"cojson/dist/crypto/PureJSCrypto\";\nimport {\n Account,\n AccountClass,\n AuthCredentials,\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 const credentials = {\n accountID: account.id,\n accountSecret: node.account.agentSecret,\n secretSeed: SecretSeedMap.get(account.id),\n provider,\n } satisfies AuthCredentials;\n\n storage.set(credentials);\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 await storage.clear();\n node.gracefulShutdown();\n },\n },\n {\n credentials,\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 getNewContext(\n props: TestJazzContextManagerProps<Acc>,\n authProps?: JazzContextManagerAuthProps,\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 return {\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
@@ -17,10 +17,10 @@
17
17
  },
18
18
  "type": "module",
19
19
  "license": "MIT",
20
- "version": "0.10.13",
20
+ "version": "0.10.15",
21
21
  "dependencies": {
22
22
  "@scure/bip39": "^1.3.0",
23
- "cojson": "0.10.8"
23
+ "cojson": "0.10.15"
24
24
  },
25
25
  "devDependencies": {
26
26
  "tsup": "8.3.5",
@@ -16,7 +16,6 @@ export type AuthSetPayload = {
16
16
  export class AuthSecretStorage {
17
17
  private listeners: Set<(isAuthenticated: boolean) => void>;
18
18
  public isAuthenticated: boolean;
19
- notify = false;
20
19
 
21
20
  constructor() {
22
21
  this.listeners = new Set();
@@ -97,7 +96,7 @@ export class AuthSecretStorage {
97
96
  };
98
97
  }
99
98
 
100
- async set(payload: AuthSetPayload) {
99
+ async setWithoutNotify(payload: AuthSetPayload) {
101
100
  const kvStore = KvStoreContext.getInstance().getStorage();
102
101
  await kvStore.set(
103
102
  STORAGE_KEY,
@@ -110,10 +109,11 @@ export class AuthSecretStorage {
110
109
  provider: payload.provider,
111
110
  }),
112
111
  );
112
+ }
113
113
 
114
- if (this.notify) {
115
- this.emitUpdate(payload);
116
- }
114
+ async set(payload: AuthSetPayload) {
115
+ this.setWithoutNotify(payload);
116
+ this.emitUpdate(payload);
117
117
  }
118
118
 
119
119
  getIsAuthenticated(data: AuthCredentials | null): boolean {
@@ -139,12 +139,13 @@ export class AuthSecretStorage {
139
139
  }
140
140
  }
141
141
 
142
- async clear() {
142
+ async clearWithoutNotify() {
143
143
  const kvStore = KvStoreContext.getInstance().getStorage();
144
144
  await kvStore.delete(STORAGE_KEY);
145
+ }
145
146
 
146
- if (this.notify) {
147
- this.emitUpdate(null);
148
- }
147
+ async clear() {
148
+ await this.clearWithoutNotify();
149
+ this.emitUpdate(null);
149
150
  }
150
151
  }
@@ -43,11 +43,8 @@ export class JazzContextManager<
43
43
  protected context: PlatformSpecificContext<Acc> | undefined;
44
44
  protected props: P | undefined;
45
45
  protected authSecretStorage = new AuthSecretStorage();
46
- protected authSecretStorageWithNotify = Object.assign(
47
- Object.create(this.authSecretStorage),
48
- { notify: true },
49
- );
50
- protected authenticating = false;
46
+ protected keepContextOpen = false;
47
+ protected contextPromise: Promise<void> | undefined;
51
48
 
52
49
  constructor() {
53
50
  KvStoreContext.getInstance().initialize(this.getKvStore());
@@ -58,6 +55,33 @@ export class JazzContextManager<
58
55
  }
59
56
 
60
57
  async createContext(props: P, authProps?: JazzContextManagerAuthProps) {
58
+ // We need to store the props here to block the double effect execution
59
+ // on React. Otherwise when calling propsChanged this.props is undefined.
60
+ this.props = props;
61
+
62
+ // Avoid race condition between the previous context and the new one
63
+ const { promise, resolve } = createResolvablePromise<void>();
64
+
65
+ const prevPromise = this.contextPromise;
66
+ this.contextPromise = promise;
67
+
68
+ await prevPromise;
69
+
70
+ try {
71
+ const result = await this.getNewContext(props, authProps);
72
+ await this.updateContext(props, result, authProps);
73
+
74
+ resolve();
75
+ } catch (error) {
76
+ resolve();
77
+ throw error;
78
+ }
79
+ }
80
+
81
+ async getNewContext(
82
+ props: P,
83
+ authProps?: JazzContextManagerAuthProps,
84
+ ): Promise<PlatformSpecificContext<Acc>> {
61
85
  props;
62
86
  authProps;
63
87
  throw new Error("Not implemented");
@@ -68,9 +92,9 @@ export class JazzContextManager<
68
92
  context: PlatformSpecificContext<Acc>,
69
93
  authProps?: JazzContextManagerAuthProps,
70
94
  ) {
71
- // When authenticating we don't want to close the previous context
95
+ // When keepContextOpen we don't want to close the previous context
72
96
  // because we might need to handle the onAnonymousAccountDiscarded callback
73
- if (!this.authenticating) {
97
+ if (!this.keepContextOpen) {
74
98
  this.context?.done();
75
99
  }
76
100
 
@@ -103,9 +127,7 @@ export class JazzContextManager<
103
127
  }
104
128
 
105
129
  getAuthSecretStorage() {
106
- // External updates of the auth secret storage are notified by default (e.g. when registering a new Auth provider)
107
- // We skip internal notify to ensure that the isAuthenticated changes are notified along with the context updates
108
- return this.authSecretStorageWithNotify;
130
+ return this.authSecretStorage;
109
131
  }
110
132
 
111
133
  logOut = async () => {
@@ -126,6 +148,18 @@ export class JazzContextManager<
126
148
  this.context.done();
127
149
  };
128
150
 
151
+ shouldMigrateAnonymousAccount = async () => {
152
+ if (!this.props?.onAnonymousAccountDiscarded) {
153
+ return false;
154
+ }
155
+
156
+ const prevCredentials = await this.authSecretStorage.get();
157
+ const wasAnonymous =
158
+ this.authSecretStorage.getIsAuthenticated(prevCredentials) === false;
159
+
160
+ return wasAnonymous;
161
+ };
162
+
129
163
  /**
130
164
  * Authenticates the user with the given credentials
131
165
  */
@@ -135,16 +169,15 @@ export class JazzContextManager<
135
169
  }
136
170
 
137
171
  const prevContext = this.context;
138
- const prevCredentials = await this.authSecretStorage.get();
139
- const wasAnonymous =
140
- this.authSecretStorage.getIsAuthenticated(prevCredentials) === false;
172
+ const migratingAnonymousAccount =
173
+ await this.shouldMigrateAnonymousAccount();
141
174
 
142
- this.authenticating = true;
175
+ this.keepContextOpen = migratingAnonymousAccount;
143
176
  await this.createContext(this.props, { credentials }).finally(() => {
144
- this.authenticating = false;
177
+ this.keepContextOpen = false;
145
178
  });
146
179
 
147
- if (wasAnonymous) {
180
+ if (migratingAnonymousAccount) {
148
181
  await this.handleAnonymousAccountMigration(prevContext);
149
182
  }
150
183
  };
@@ -158,21 +191,20 @@ export class JazzContextManager<
158
191
  }
159
192
 
160
193
  const prevContext = this.context;
161
- const prevCredentials = await this.authSecretStorage.get();
162
- const wasAnonymous =
163
- this.authSecretStorage.getIsAuthenticated(prevCredentials) === false;
194
+ const migratingAnonymousAccount =
195
+ await this.shouldMigrateAnonymousAccount();
164
196
 
165
- this.authenticating = true;
197
+ this.keepContextOpen = migratingAnonymousAccount;
166
198
  await this.createContext(this.props, {
167
199
  newAccountProps: {
168
200
  secret: accountSecret,
169
201
  creationProps,
170
202
  },
171
203
  }).finally(() => {
172
- this.authenticating = false;
204
+ this.keepContextOpen = false;
173
205
  });
174
206
 
175
- if (wasAnonymous) {
207
+ if (migratingAnonymousAccount) {
176
208
  await this.handleAnonymousAccountMigration(prevContext);
177
209
  }
178
210
 
@@ -241,3 +273,13 @@ export class JazzContextManager<
241
273
  }
242
274
  }
243
275
  }
276
+
277
+ function createResolvablePromise<T>() {
278
+ let resolve!: (value: T) => void;
279
+
280
+ const promise = new Promise<T>((res) => {
281
+ resolve = res;
282
+ });
283
+
284
+ return { promise, resolve };
285
+ }
@@ -219,7 +219,7 @@ export async function createJazzContext<Acc extends Account>(options: {
219
219
  AccountSchema: options.AccountSchema,
220
220
  sessionProvider: options.sessionProvider,
221
221
  onLogOut: () => {
222
- authSecretStorage.clear();
222
+ authSecretStorage.clearWithoutNotify();
223
223
  },
224
224
  });
225
225
  } else {
@@ -240,12 +240,12 @@ export async function createJazzContext<Acc extends Account>(options: {
240
240
  crypto,
241
241
  AccountSchema: options.AccountSchema,
242
242
  onLogOut: async () => {
243
- await authSecretStorage.clear();
243
+ await authSecretStorage.clearWithoutNotify();
244
244
  },
245
245
  });
246
246
 
247
247
  if (!options.newAccountProps) {
248
- await authSecretStorage.set({
248
+ await authSecretStorage.setWithoutNotify({
249
249
  accountID: context.account.id,
250
250
  secretSeed,
251
251
  accountSecret: context.node.account.agentSecret,
package/src/testing.ts CHANGED
@@ -237,12 +237,10 @@ export class TestJazzContextManager<
237
237
  return context;
238
238
  }
239
239
 
240
- async createContext(
240
+ async getNewContext(
241
241
  props: TestJazzContextManagerProps<Acc>,
242
242
  authProps?: JazzContextManagerAuthProps,
243
243
  ) {
244
- this.props = props;
245
-
246
244
  if (!syncServer.current) {
247
245
  throw new Error(
248
246
  "You need to setup a test sync server with setupJazzTestSync to use the Auth functions",
@@ -260,20 +258,16 @@ export class TestJazzContextManager<
260
258
  AccountSchema: props.AccountSchema,
261
259
  });
262
260
 
263
- await this.updateContext(
264
- props,
265
- {
266
- me: context.account,
267
- node: context.node,
268
- done: () => {
269
- context.done();
270
- },
271
- logOut: () => {
272
- return context.logOut();
273
- },
261
+ return {
262
+ me: context.account,
263
+ node: context.node,
264
+ done: () => {
265
+ context.done();
274
266
  },
275
- authProps,
276
- );
267
+ logOut: () => {
268
+ return context.logOut();
269
+ },
270
+ };
277
271
  }
278
272
  }
279
273
 
@@ -257,11 +257,10 @@ describe("AuthSecretStorage", () => {
257
257
  });
258
258
  });
259
259
 
260
- describe("notify=true", () => {
260
+ describe("notify", () => {
261
261
  beforeEach(() => {
262
262
  kvStore.clearAll();
263
263
  authSecretStorage = new AuthSecretStorage();
264
- authSecretStorage.notify = true;
265
264
  });
266
265
 
267
266
  describe("set", () => {
@@ -337,7 +336,7 @@ describe("AuthSecretStorage", () => {
337
336
  });
338
337
  });
339
338
 
340
- describe("notify=false", () => {
339
+ describe("without notify", () => {
341
340
  beforeEach(() => {
342
341
  kvStore.clearAll();
343
342
  authSecretStorage = new AuthSecretStorage();
@@ -348,7 +347,7 @@ describe("AuthSecretStorage", () => {
348
347
  const handler = vi.fn();
349
348
  authSecretStorage.onUpdate(handler);
350
349
 
351
- await authSecretStorage.set({
350
+ await authSecretStorage.setWithoutNotify({
352
351
  accountID: "test123" as ID<Account>,
353
352
  accountSecret:
354
353
  "secret123" as `sealerSecret_z${string}/signerSecret_z${string}`,
@@ -365,7 +364,7 @@ describe("AuthSecretStorage", () => {
365
364
  });
366
365
 
367
366
  it("should return false for anonymous credentials", async () => {
368
- await authSecretStorage.set({
367
+ await authSecretStorage.setWithoutNotify({
369
368
  accountID: "test123" as ID<Account>,
370
369
  accountSecret:
371
370
  "secret123" as `sealerSecret_z${string}/signerSecret_z${string}`,
@@ -376,7 +375,7 @@ describe("AuthSecretStorage", () => {
376
375
  });
377
376
 
378
377
  it("should return true for non-anonymous credentials", async () => {
379
- await authSecretStorage.set({
378
+ await authSecretStorage.setWithoutNotify({
380
379
  accountID: "test123" as ID<Account>,
381
380
  accountSecret:
382
381
  "secret123" as `sealerSecret_z${string}/signerSecret_z${string}`,
@@ -395,7 +394,7 @@ describe("AuthSecretStorage", () => {
395
394
  });
396
395
 
397
396
  it("should return true when the provider is missing", async () => {
398
- await authSecretStorage.set({
397
+ await authSecretStorage.setWithoutNotify({
399
398
  accountID: "test123" as ID<Account>,
400
399
  accountSecret:
401
400
  "secret123" as `sealerSecret_z${string}/signerSecret_z${string}`,
@@ -414,7 +413,7 @@ describe("AuthSecretStorage", () => {
414
413
 
415
414
  describe("clear", () => {
416
415
  it("should not emit update event when clearing", async () => {
417
- await authSecretStorage.set({
416
+ await authSecretStorage.setWithoutNotify({
418
417
  accountID: "test123" as ID<Account>,
419
418
  accountSecret:
420
419
  "secret123" as `sealerSecret_z${string}/signerSecret_z${string}`,
@@ -424,7 +423,7 @@ describe("AuthSecretStorage", () => {
424
423
  const handler = vi.fn();
425
424
  authSecretStorage.onUpdate(handler);
426
425
 
427
- await authSecretStorage.clear();
426
+ await authSecretStorage.clearWithoutNotify();
428
427
 
429
428
  expect(handler).not.toHaveBeenCalled();
430
429
  });
@@ -35,7 +35,7 @@ class TestJazzContextManager<Acc extends Account> extends JazzContextManager<
35
35
  AccountSchema?: AccountClass<Acc>;
36
36
  }
37
37
  > {
38
- async createContext(
38
+ async getNewContext(
39
39
  props: JazzContextManagerBaseProps<Acc> & {
40
40
  defaultProfileName?: string;
41
41
  AccountSchema?: AccountClass<Acc>;
@@ -53,20 +53,16 @@ class TestJazzContextManager<Acc extends Account> extends JazzContextManager<
53
53
  AccountSchema: props.AccountSchema,
54
54
  });
55
55
 
56
- await this.updateContext(
57
- props,
58
- {
59
- me: context.account,
60
- node: context.node,
61
- done: () => {
62
- context.done();
63
- },
64
- logOut: async () => {
65
- await context.logOut();
66
- },
56
+ return {
57
+ me: context.account,
58
+ node: context.node,
59
+ done: () => {
60
+ context.done();
67
61
  },
68
- authProps,
69
- );
62
+ logOut: async () => {
63
+ await context.logOut();
64
+ },
65
+ };
70
66
  }
71
67
  }
72
68
 
@@ -127,6 +123,23 @@ describe("ContextManager", () => {
127
123
  expect(getCurrentValue().me.id).toBe(credentials.accountID);
128
124
  });
129
125
 
126
+ test("handles race conditions on the context creation", async () => {
127
+ const account = await createJazzTestAccount();
128
+
129
+ manager.createContext({});
130
+
131
+ const credentials = {
132
+ accountID: account.id,
133
+ accountSecret: account._raw.core.node.account.agentSecret,
134
+ provider: "test",
135
+ };
136
+
137
+ // Authenticate without waiting for the previous context to be created
138
+ await manager.authenticate(credentials);
139
+
140
+ expect(getCurrentValue().me.id).toBe(credentials.accountID);
141
+ });
142
+
130
143
  test("calls onLogOut callback when logging out", async () => {
131
144
  const onLogOut = vi.fn();
132
145
  await manager.createContext({ onLogOut });
@@ -1,8 +1,7 @@
1
1
  // @vitest-environment happy-dom
2
2
 
3
- import { mnemonicToEntropy } from "@scure/bip39";
4
3
  import { AgentSecret } from "cojson";
5
- import { PureJSCrypto } from "cojson/src/crypto/PureJSCrypto";
4
+ import { PureJSCrypto } from "cojson/crypto/PureJSCrypto";
6
5
  import {
7
6
  Account,
8
7
  AuthSecretStorage,
@@ -303,6 +302,10 @@ describe("PassphraseAuth with TestJazzContextManager", () => {
303
302
  // Verify account was created
304
303
  expect(accountId).toBeDefined();
305
304
 
305
+ await contextManager
306
+ .getCurrentValue()
307
+ ?.node.syncManager.waitForAllCoValuesSync();
308
+
306
309
  // Verify we can log in with the passphrase
307
310
  await contextManager.logOut();
308
311
  await passphraseAuth.logIn(passphrase);
package/tsconfig.json CHANGED
@@ -12,5 +12,5 @@
12
12
  "esModuleInterop": true
13
13
  },
14
14
  "include": ["./src/**/*.ts"],
15
- "exclude": ["./node_modules", "./src/tests"]
15
+ "exclude": ["./node_modules"]
16
16
  }