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/.turbo/turbo-build.log +6 -6
- package/CHANGELOG.md +14 -0
- package/dist/{chunk-GSIV52ZH.js → chunk-5USJBXLW.js} +55 -30
- package/dist/chunk-5USJBXLW.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/testing.js +11 -16
- package/dist/testing.js.map +1 -1
- package/package.json +2 -2
- package/src/auth/AuthSecretStorage.ts +10 -9
- package/src/implementation/ContextManager.ts +64 -22
- package/src/implementation/createContext.ts +3 -3
- package/src/testing.ts +10 -16
- package/src/tests/AuthSecretStorage.test.ts +8 -9
- package/src/tests/ContextManager.test.ts +27 -14
- package/src/tests/PassphraseAuth.test.ts +5 -2
- package/tsconfig.json +1 -1
- package/dist/chunk-GSIV52ZH.js.map +0 -1
package/dist/index.js
CHANGED
package/dist/testing.js
CHANGED
@@ -5,7 +5,7 @@ import {
|
|
5
5
|
createAnonymousJazzContext,
|
6
6
|
createJazzContext,
|
7
7
|
randomSessionProvider
|
8
|
-
} from "./chunk-
|
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
|
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
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
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
|
-
|
185
|
-
|
177
|
+
logOut: () => {
|
178
|
+
return context.logOut();
|
179
|
+
}
|
180
|
+
};
|
186
181
|
}
|
187
182
|
};
|
188
183
|
function linkAccounts(a, b, aRole = "server", bRole = "server") {
|
package/dist/testing.js.map
CHANGED
@@ -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
@@ -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
|
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
|
-
|
115
|
-
|
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
|
142
|
+
async clearWithoutNotify() {
|
143
143
|
const kvStore = KvStoreContext.getInstance().getStorage();
|
144
144
|
await kvStore.delete(STORAGE_KEY);
|
145
|
+
}
|
145
146
|
|
146
|
-
|
147
|
-
|
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
|
47
|
-
|
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
|
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.
|
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
|
-
|
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
|
139
|
-
|
140
|
-
this.authSecretStorage.getIsAuthenticated(prevCredentials) === false;
|
172
|
+
const migratingAnonymousAccount =
|
173
|
+
await this.shouldMigrateAnonymousAccount();
|
141
174
|
|
142
|
-
this.
|
175
|
+
this.keepContextOpen = migratingAnonymousAccount;
|
143
176
|
await this.createContext(this.props, { credentials }).finally(() => {
|
144
|
-
this.
|
177
|
+
this.keepContextOpen = false;
|
145
178
|
});
|
146
179
|
|
147
|
-
if (
|
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
|
162
|
-
|
163
|
-
this.authSecretStorage.getIsAuthenticated(prevCredentials) === false;
|
194
|
+
const migratingAnonymousAccount =
|
195
|
+
await this.shouldMigrateAnonymousAccount();
|
164
196
|
|
165
|
-
this.
|
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.
|
204
|
+
this.keepContextOpen = false;
|
173
205
|
});
|
174
206
|
|
175
|
-
if (
|
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.
|
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.
|
243
|
+
await authSecretStorage.clearWithoutNotify();
|
244
244
|
},
|
245
245
|
});
|
246
246
|
|
247
247
|
if (!options.newAccountProps) {
|
248
|
-
await authSecretStorage.
|
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
|
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
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
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
|
-
|
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
|
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
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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
|
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
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
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
|
-
|
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/
|
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