jazz-tools 0.18.23 → 0.18.25
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/.svelte-kit/__package__/Provider.svelte +2 -0
- package/.svelte-kit/__package__/Provider.svelte.d.ts.map +1 -1
- package/.turbo/turbo-build.log +65 -65
- package/CHANGELOG.md +25 -0
- package/dist/browser/index.js +9 -9
- package/dist/browser/index.js.map +1 -1
- package/dist/{chunk-D5L6ES2M.js → chunk-DOCEAUVD.js} +72 -35
- package/dist/chunk-DOCEAUVD.js.map +1 -0
- package/dist/{chunk-BOMSRY5H.js → chunk-M2HGBOXS.js} +2 -2
- package/dist/chunk-M2HGBOXS.js.map +1 -0
- package/dist/index.js +2 -2
- package/dist/inspector/{custom-element-XDJT5T57.js → custom-element-A7UAELEG.js} +3 -1
- package/dist/inspector/{custom-element-XDJT5T57.js.map → custom-element-A7UAELEG.js.map} +1 -1
- package/dist/inspector/index.js +2 -0
- package/dist/inspector/index.js.map +1 -1
- package/dist/inspector/register-custom-element.js +1 -1
- package/dist/inspector/viewer/use-open-inspector.d.ts.map +1 -1
- package/dist/inspector/viewer/use-page-path.d.ts.map +1 -1
- package/dist/react/hooks.d.ts +1 -1
- package/dist/react/hooks.d.ts.map +1 -1
- package/dist/react/index.d.ts +2 -1
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +17 -3
- package/dist/react/index.js.map +1 -1
- package/dist/react/provider.d.ts +2 -1
- package/dist/react/provider.d.ts.map +1 -1
- package/dist/react/ssr.js +1 -1
- package/dist/react/ssr.js.map +1 -1
- package/dist/react-core/hooks.d.ts +13 -0
- package/dist/react-core/hooks.d.ts.map +1 -1
- package/dist/react-core/index.d.ts +2 -0
- package/dist/react-core/index.d.ts.map +1 -1
- package/dist/react-core/index.js +104 -0
- package/dist/react-core/index.js.map +1 -1
- package/dist/react-core/subscription-provider.d.ts +27 -0
- package/dist/react-core/subscription-provider.d.ts.map +1 -0
- package/dist/react-core/tests/subscription.bench.d.ts +2 -0
- package/dist/react-core/tests/subscription.bench.d.ts.map +1 -0
- package/dist/react-core/tests/useSubscriptionSelector.test.d.ts +2 -0
- package/dist/react-core/tests/useSubscriptionSelector.test.d.ts.map +1 -0
- package/dist/react-core/types.d.ts +10 -0
- package/dist/react-core/types.d.ts.map +1 -0
- package/dist/react-native-core/hooks.d.ts +1 -1
- package/dist/react-native-core/hooks.d.ts.map +1 -1
- package/dist/react-native-core/index.d.ts +1 -0
- package/dist/react-native-core/index.d.ts.map +1 -1
- package/dist/react-native-core/index.js +25 -12
- package/dist/react-native-core/index.js.map +1 -1
- package/dist/react-native-core/provider.d.ts +2 -1
- package/dist/react-native-core/provider.d.ts.map +1 -1
- package/dist/svelte/Provider.svelte +2 -0
- package/dist/svelte/Provider.svelte.d.ts.map +1 -1
- package/dist/testing.js +4 -4
- package/dist/testing.js.map +1 -1
- package/dist/tools/auth/AuthSecretStorage.d.ts +3 -1
- package/dist/tools/auth/AuthSecretStorage.d.ts.map +1 -1
- package/dist/tools/coValues/account.d.ts +3 -3
- package/dist/tools/coValues/account.d.ts.map +1 -1
- package/dist/tools/coValues/group.d.ts +20 -1
- package/dist/tools/coValues/group.d.ts.map +1 -1
- package/dist/tools/coValues/interfaces.d.ts.map +1 -1
- package/dist/tools/implementation/ContextManager.d.ts +1 -0
- package/dist/tools/implementation/ContextManager.d.ts.map +1 -1
- package/dist/tools/implementation/createContext.d.ts +7 -7
- package/dist/tools/implementation/createContext.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/schemaTypes/AccountSchema.d.ts +3 -8
- package/dist/tools/implementation/zodSchema/schemaTypes/AccountSchema.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/schemaTypes/GroupSchema.d.ts +5 -0
- package/dist/tools/implementation/zodSchema/schemaTypes/GroupSchema.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/zodCo.d.ts.map +1 -1
- package/dist/tools/ssr.js +1 -1
- package/dist/tools/testing.d.ts.map +1 -1
- package/dist/worker/index.js +3 -3
- package/dist/worker/index.js.map +1 -1
- package/package.json +5 -4
- package/src/better-auth/database-adapter/tests/sync-utils.ts +1 -1
- package/src/browser/createBrowserContext.ts +9 -9
- package/src/browser/tests/utils.ts +2 -2
- package/src/inspector/viewer/use-open-inspector.ts +1 -0
- package/src/inspector/viewer/use-page-path.ts +1 -0
- package/src/react/hooks.tsx +3 -0
- package/src/react/index.ts +9 -0
- package/src/react/provider.tsx +3 -0
- package/src/react-core/hooks.ts +37 -5
- package/src/react-core/index.ts +2 -0
- package/src/react-core/subscription-provider.tsx +144 -0
- package/src/react-core/tests/subscription.bench.tsx +319 -0
- package/src/react-core/tests/useSubscriptionSelector.test.ts +250 -0
- package/src/react-core/types.ts +19 -0
- package/src/react-native-core/hooks.tsx +3 -0
- package/src/react-native-core/index.ts +6 -0
- package/src/react-native-core/platform.ts +9 -9
- package/src/react-native-core/provider.tsx +3 -1
- package/src/svelte/Provider.svelte +2 -0
- package/src/tools/auth/AuthSecretStorage.ts +16 -9
- package/src/tools/coValues/account.ts +5 -5
- package/src/tools/coValues/group.ts +33 -0
- package/src/tools/coValues/interfaces.ts +4 -1
- package/src/tools/implementation/ContextManager.ts +7 -3
- package/src/tools/implementation/createContext.ts +12 -12
- package/src/tools/implementation/zodSchema/schemaTypes/AccountSchema.ts +5 -5
- package/src/tools/implementation/zodSchema/schemaTypes/GroupSchema.ts +7 -1
- package/src/tools/ssr/ssr.ts +1 -1
- package/src/tools/testing.ts +3 -3
- package/src/tools/tests/AuthSecretStorage.test.ts +59 -1
- package/src/tools/tests/ContextManager.test.ts +11 -1
- package/src/tools/tests/account.test.ts +16 -0
- package/src/tools/tests/coPlainText.test.ts +2 -2
- package/src/tools/tests/createContext.test.ts +19 -19
- package/src/tools/tests/deepLoading.test.ts +2 -2
- package/src/tools/tests/group.test.ts +59 -0
- package/src/tools/tests/inbox.test.ts +2 -2
- package/src/tools/tests/utils.ts +3 -3
- package/src/worker/index.ts +3 -3
- package/dist/chunk-BOMSRY5H.js.map +0 -1
- package/dist/chunk-D5L6ES2M.js.map +0 -1
@@ -19,10 +19,12 @@
|
|
19
19
|
let props: JazzContextManagerProps<S> & {
|
20
20
|
children?: Snippet;
|
21
21
|
enableSSR?: boolean;
|
22
|
+
authSecretStorageKey?: string;
|
22
23
|
} = $props();
|
23
24
|
|
24
25
|
const contextManager = new JazzBrowserContextManager<S>({
|
25
26
|
useAnonymousFallback: props.enableSSR,
|
27
|
+
authSecretStorageKey: props.authSecretStorageKey,
|
26
28
|
});
|
27
29
|
|
28
30
|
const ctx = $state<JazzContext<InstanceOfSchema<S>>>({ current: undefined });
|
@@ -3,6 +3,7 @@ import type { Account } from "../coValues/account.js";
|
|
3
3
|
import type { ID } from "../internal.js";
|
4
4
|
import { AuthCredentials } from "../types.js";
|
5
5
|
import KvStoreContext from "./KvStoreContext.js";
|
6
|
+
import { z } from "zod/v4";
|
6
7
|
|
7
8
|
const STORAGE_KEY = "jazz-logged-in-secret";
|
8
9
|
|
@@ -24,20 +25,26 @@ export class AuthSecretStorage {
|
|
24
25
|
private listeners: Set<(isAuthenticated: boolean) => void>;
|
25
26
|
public isAuthenticated: boolean;
|
26
27
|
|
27
|
-
constructor() {
|
28
|
+
constructor(private storageKey: string = STORAGE_KEY) {
|
29
|
+
z.string().nonempty().parse(storageKey);
|
30
|
+
|
28
31
|
this.listeners = new Set();
|
29
32
|
this.isAuthenticated = false;
|
30
33
|
}
|
31
34
|
|
35
|
+
getStorageKey(): string {
|
36
|
+
return this.storageKey;
|
37
|
+
}
|
38
|
+
|
32
39
|
async migrate() {
|
33
40
|
const kvStore = KvStoreContext.getInstance().getStorage();
|
34
41
|
|
35
|
-
if (!(await kvStore.get(
|
42
|
+
if (!(await kvStore.get(this.storageKey))) {
|
36
43
|
const demoAuthSecret = await kvStore.get("demo-auth-logged-in-secret");
|
37
44
|
if (demoAuthSecret) {
|
38
45
|
const parsed = JSON.parse(demoAuthSecret);
|
39
46
|
await kvStore.set(
|
40
|
-
|
47
|
+
this.storageKey,
|
41
48
|
JSON.stringify({
|
42
49
|
accountID: parsed.accountID,
|
43
50
|
accountSecret: parsed.accountSecret,
|
@@ -51,7 +58,7 @@ export class AuthSecretStorage {
|
|
51
58
|
if (clerkAuthSecret) {
|
52
59
|
const parsed = JSON.parse(clerkAuthSecret);
|
53
60
|
await kvStore.set(
|
54
|
-
|
61
|
+
this.storageKey,
|
55
62
|
JSON.stringify({
|
56
63
|
accountID: parsed.accountID,
|
57
64
|
accountSecret: parsed.secret,
|
@@ -62,14 +69,14 @@ export class AuthSecretStorage {
|
|
62
69
|
}
|
63
70
|
}
|
64
71
|
|
65
|
-
const value = await kvStore.get(
|
72
|
+
const value = await kvStore.get(this.storageKey);
|
66
73
|
|
67
74
|
if (value) {
|
68
75
|
const parsed = JSON.parse(value);
|
69
76
|
|
70
77
|
if ("secret" in parsed) {
|
71
78
|
await kvStore.set(
|
72
|
-
|
79
|
+
this.storageKey,
|
73
80
|
JSON.stringify({
|
74
81
|
accountID: parsed.accountID,
|
75
82
|
secretSeed: parsed.secretSeed,
|
@@ -83,7 +90,7 @@ export class AuthSecretStorage {
|
|
83
90
|
|
84
91
|
async get(): Promise<AuthCredentials | null> {
|
85
92
|
const kvStore = KvStoreContext.getInstance().getStorage();
|
86
|
-
const data = await kvStore.get(
|
93
|
+
const data = await kvStore.get(this.storageKey);
|
87
94
|
|
88
95
|
if (!data) return null;
|
89
96
|
|
@@ -106,7 +113,7 @@ export class AuthSecretStorage {
|
|
106
113
|
async setWithoutNotify(payload: AuthSetPayload) {
|
107
114
|
const kvStore = KvStoreContext.getInstance().getStorage();
|
108
115
|
await kvStore.set(
|
109
|
-
|
116
|
+
this.storageKey,
|
110
117
|
JSON.stringify({
|
111
118
|
accountID: payload.accountID,
|
112
119
|
secretSeed: payload.secretSeed
|
@@ -148,7 +155,7 @@ export class AuthSecretStorage {
|
|
148
155
|
|
149
156
|
async clearWithoutNotify() {
|
150
157
|
const kvStore = KvStoreContext.getInstance().getStorage();
|
151
|
-
await kvStore.delete(
|
158
|
+
await kvStore.delete(this.storageKey);
|
152
159
|
}
|
153
160
|
|
154
161
|
async clear() {
|
@@ -120,13 +120,13 @@ export class Account extends CoValueBase implements CoValue {
|
|
120
120
|
*
|
121
121
|
* @param valueID The ID of the `CoValue` or `Group` to accept the invite to.
|
122
122
|
* @param inviteSecret The secret of the invite to accept.
|
123
|
-
* @param coValueClass The class of the `CoValue` or `Group` to accept the invite to.
|
123
|
+
* @param coValueClass [Group] The class of the `CoValue` or `Group` to accept the invite to.
|
124
124
|
* @returns The loaded `CoValue` or `Group`.
|
125
125
|
*/
|
126
126
|
async acceptInvite<S extends CoValueClassOrSchema>(
|
127
127
|
valueID: string,
|
128
128
|
inviteSecret: InviteSecret,
|
129
|
-
coValueClass
|
129
|
+
coValueClass?: S,
|
130
130
|
): Promise<Resolved<InstanceOrPrimitiveOfSchema<S>, true> | null> {
|
131
131
|
if (!this.$jazz.isLocalNodeOwner) {
|
132
132
|
throw new Error("Only a controlled account can accept invites");
|
@@ -138,7 +138,7 @@ export class Account extends CoValueBase implements CoValue {
|
|
138
138
|
);
|
139
139
|
|
140
140
|
return loadCoValue(
|
141
|
-
coValueClassFromCoValueClassOrSchema(coValueClass),
|
141
|
+
coValueClassFromCoValueClassOrSchema(coValueClass ?? Group),
|
142
142
|
valueID,
|
143
143
|
{
|
144
144
|
loadAs: this,
|
@@ -212,7 +212,7 @@ export class Account extends CoValueBase implements CoValue {
|
|
212
212
|
options: {
|
213
213
|
creationProps: { name: string };
|
214
214
|
initialAgentSecret?: AgentSecret;
|
215
|
-
|
215
|
+
peers?: Peer[];
|
216
216
|
crypto: CryptoProvider;
|
217
217
|
},
|
218
218
|
): Promise<A> {
|
@@ -256,7 +256,7 @@ export class Account extends CoValueBase implements CoValue {
|
|
256
256
|
const account = await this.create<A>({
|
257
257
|
creationProps: options.creationProps,
|
258
258
|
crypto: as.$jazz.localNode.crypto,
|
259
|
-
|
259
|
+
peers: [connectedPeers[0]],
|
260
260
|
});
|
261
261
|
|
262
262
|
await account.$jazz.waitForAllCoValuesSync();
|
@@ -3,6 +3,7 @@ import {
|
|
3
3
|
type AccountRole,
|
4
4
|
type AgentID,
|
5
5
|
type Everyone,
|
6
|
+
type InviteSecret,
|
6
7
|
type RawAccountID,
|
7
8
|
type RawGroup,
|
8
9
|
type Role,
|
@@ -299,6 +300,29 @@ export class Group extends CoValueBase implements CoValue {
|
|
299
300
|
const { options, listener } = parseSubscribeRestArgs(args);
|
300
301
|
return subscribeToCoValueWithoutMe<G, R>(this, id, options, listener);
|
301
302
|
}
|
303
|
+
|
304
|
+
/** @category Invites
|
305
|
+
* Creates a group invite
|
306
|
+
* @param id The ID of the group to create an invite for
|
307
|
+
* @param options Optional configuration
|
308
|
+
* @param options.role The role to grant to the accepter of the invite. Defaults to 'reader'
|
309
|
+
* @param options.loadAs The account to use when loading the group. Defaults to the current account
|
310
|
+
* @returns An invite secret, (a string starting with "inviteSecret_"). Can be
|
311
|
+
* accepted using `Account.acceptInvite()`
|
312
|
+
*/
|
313
|
+
static async createInvite<G extends Group>(
|
314
|
+
this: CoValueClass<G>,
|
315
|
+
id: ID<G>,
|
316
|
+
options?: { role?: AccountRole; loadAs?: Account },
|
317
|
+
): Promise<InviteSecret> {
|
318
|
+
const group = await loadCoValueWithoutMe(this, id, {
|
319
|
+
loadAs: options?.loadAs,
|
320
|
+
});
|
321
|
+
if (!group) {
|
322
|
+
throw new Error(`Group with id ${id} not found`);
|
323
|
+
}
|
324
|
+
return group.$jazz.createInvite(options?.role ?? "reader");
|
325
|
+
}
|
302
326
|
}
|
303
327
|
|
304
328
|
export class GroupJazzApi<G extends Group> extends CoValueJazzApi<G> {
|
@@ -350,6 +374,15 @@ export class GroupJazzApi<G extends Group> extends CoValueJazzApi<G> {
|
|
350
374
|
return subscribeToExistingCoValue(this.group, options, listener);
|
351
375
|
}
|
352
376
|
|
377
|
+
/**
|
378
|
+
* Create an invite to this group
|
379
|
+
*
|
380
|
+
* @category Invites
|
381
|
+
*/
|
382
|
+
createInvite(role: AccountRole = "reader"): InviteSecret {
|
383
|
+
return this.raw.createInvite(role);
|
384
|
+
}
|
385
|
+
|
353
386
|
/**
|
354
387
|
* Wait for the `Group` to be uploaded to the other peers.
|
355
388
|
*
|
@@ -301,7 +301,10 @@ export function subscribeToCoValue<
|
|
301
301
|
if (value.type === "unavailable") {
|
302
302
|
options.onUnavailable?.();
|
303
303
|
|
304
|
-
|
304
|
+
// Don't log unavailable errors when `loadUnique` or `upsertUnique` are used
|
305
|
+
if (!options.skipRetry) {
|
306
|
+
console.error(value.toString());
|
307
|
+
}
|
305
308
|
} else if (value.type === "unauthorized") {
|
306
309
|
options.onUnauthorized?.();
|
307
310
|
|
@@ -45,7 +45,7 @@ type PlatformSpecificContext<Acc extends Account> =
|
|
45
45
|
|
46
46
|
function getAnonymousFallback() {
|
47
47
|
const context = createAnonymousJazzContext({
|
48
|
-
|
48
|
+
peers: [],
|
49
49
|
crypto: new PureJSCrypto(),
|
50
50
|
});
|
51
51
|
|
@@ -71,13 +71,17 @@ export class JazzContextManager<
|
|
71
71
|
protected value: JazzContextType<Acc> | undefined;
|
72
72
|
protected context: PlatformSpecificContext<Acc> | undefined;
|
73
73
|
protected props: P | undefined;
|
74
|
-
protected authSecretStorage
|
74
|
+
protected authSecretStorage;
|
75
75
|
protected keepContextOpen = false;
|
76
76
|
contextPromise: Promise<void> | undefined;
|
77
77
|
protected authenticatingAccountID: string | null = null;
|
78
78
|
|
79
|
-
constructor(opts?: {
|
79
|
+
constructor(opts?: {
|
80
|
+
useAnonymousFallback?: boolean;
|
81
|
+
authSecretStorageKey?: string;
|
82
|
+
}) {
|
80
83
|
KvStoreContext.getInstance().initialize(this.getKvStore());
|
84
|
+
this.authSecretStorage = new AuthSecretStorage(opts?.authSecretStorageKey);
|
81
85
|
|
82
86
|
if (opts?.useAnonymousFallback) {
|
83
87
|
this.value = getAnonymousFallback();
|
@@ -90,7 +90,7 @@ export async function createJazzContextFromExistingCredentials<
|
|
90
90
|
| CoreAccountSchema,
|
91
91
|
>({
|
92
92
|
credentials,
|
93
|
-
|
93
|
+
peers,
|
94
94
|
crypto,
|
95
95
|
storage,
|
96
96
|
AccountSchema: PropsAccountSchema,
|
@@ -99,7 +99,7 @@ export async function createJazzContextFromExistingCredentials<
|
|
99
99
|
asActiveAccount,
|
100
100
|
}: {
|
101
101
|
credentials: Credentials;
|
102
|
-
|
102
|
+
peers: Peer[];
|
103
103
|
crypto: CryptoProvider;
|
104
104
|
AccountSchema?: S;
|
105
105
|
sessionProvider: SessionProvider;
|
@@ -122,7 +122,7 @@ export async function createJazzContextFromExistingCredentials<
|
|
122
122
|
accountID: credentials.accountID as unknown as CoID<RawAccount>,
|
123
123
|
accountSecret: credentials.secret,
|
124
124
|
sessionID: sessionID,
|
125
|
-
|
125
|
+
peers: peers,
|
126
126
|
crypto: crypto,
|
127
127
|
storage,
|
128
128
|
migration: async (rawAccount, _node, creationProps) => {
|
@@ -162,7 +162,7 @@ export async function createJazzContextForNewAccount<
|
|
162
162
|
>({
|
163
163
|
creationProps,
|
164
164
|
initialAgentSecret,
|
165
|
-
|
165
|
+
peers,
|
166
166
|
crypto,
|
167
167
|
AccountSchema: PropsAccountSchema,
|
168
168
|
onLogOut,
|
@@ -170,7 +170,7 @@ export async function createJazzContextForNewAccount<
|
|
170
170
|
}: {
|
171
171
|
creationProps: { name: string };
|
172
172
|
initialAgentSecret?: AgentSecret;
|
173
|
-
|
173
|
+
peers: Peer[];
|
174
174
|
crypto: CryptoProvider;
|
175
175
|
AccountSchema?: S;
|
176
176
|
onLogOut?: () => Promise<void>;
|
@@ -184,7 +184,7 @@ export async function createJazzContextForNewAccount<
|
|
184
184
|
|
185
185
|
const { node } = await LocalNode.withNewlyCreatedAccount({
|
186
186
|
creationProps,
|
187
|
-
|
187
|
+
peers,
|
188
188
|
crypto,
|
189
189
|
initialAgentSecret,
|
190
190
|
storage,
|
@@ -219,7 +219,7 @@ export async function createJazzContext<
|
|
219
219
|
>(options: {
|
220
220
|
credentials?: AuthCredentials;
|
221
221
|
newAccountProps?: NewAccountProps;
|
222
|
-
|
222
|
+
peers: Peer[];
|
223
223
|
crypto: CryptoProvider;
|
224
224
|
defaultProfileName?: string;
|
225
225
|
AccountSchema?: S;
|
@@ -243,7 +243,7 @@ export async function createJazzContext<
|
|
243
243
|
accountID: credentials.accountID,
|
244
244
|
secret: credentials.accountSecret,
|
245
245
|
},
|
246
|
-
|
246
|
+
peers: options.peers,
|
247
247
|
crypto,
|
248
248
|
AccountSchema: options.AccountSchema,
|
249
249
|
sessionProvider: options.sessionProvider,
|
@@ -267,7 +267,7 @@ export async function createJazzContext<
|
|
267
267
|
context = await createJazzContextForNewAccount({
|
268
268
|
creationProps,
|
269
269
|
initialAgentSecret,
|
270
|
-
|
270
|
+
peers: options.peers,
|
271
271
|
crypto,
|
272
272
|
AccountSchema: options.AccountSchema,
|
273
273
|
onLogOut: async () => {
|
@@ -293,11 +293,11 @@ export async function createJazzContext<
|
|
293
293
|
}
|
294
294
|
|
295
295
|
export function createAnonymousJazzContext({
|
296
|
-
|
296
|
+
peers,
|
297
297
|
crypto,
|
298
298
|
storage,
|
299
299
|
}: {
|
300
|
-
|
300
|
+
peers: Peer[];
|
301
301
|
crypto: CryptoProvider;
|
302
302
|
storage?: StorageAPI;
|
303
303
|
}): JazzContextWithAgent {
|
@@ -309,7 +309,7 @@ export function createAnonymousJazzContext({
|
|
309
309
|
crypto,
|
310
310
|
);
|
311
311
|
|
312
|
-
for (const peer of
|
312
|
+
for (const peer of peers) {
|
313
313
|
node.syncManager.addPeer(peer);
|
314
314
|
}
|
315
315
|
|
@@ -1,10 +1,10 @@
|
|
1
|
-
import { CryptoProvider } from "cojson";
|
2
1
|
import {
|
3
2
|
Account,
|
4
3
|
AccountCreationProps,
|
5
4
|
BranchDefinition,
|
6
5
|
Group,
|
7
6
|
RefsToResolveStrict,
|
7
|
+
Simplify,
|
8
8
|
} from "../../../internal.js";
|
9
9
|
import { AnonymousJazzAgent } from "../../anonymousJazzAgent.js";
|
10
10
|
import { InstanceOrPrimitiveOfSchema } from "../typeConverters/InstanceOrPrimitiveOfSchema.js";
|
@@ -48,10 +48,9 @@ export interface AccountSchema<
|
|
48
48
|
> {
|
49
49
|
builtin: "Account";
|
50
50
|
|
51
|
-
create: (
|
52
|
-
|
53
|
-
|
54
|
-
}) => Promise<AccountInstance<Shape>>;
|
51
|
+
create: (
|
52
|
+
options: Simplify<Parameters<(typeof Account)["create"]>[0]>,
|
53
|
+
) => Promise<AccountInstance<Shape>>;
|
55
54
|
|
56
55
|
load: <R extends ResolveQuery<AccountSchema<Shape>>>(
|
57
56
|
id: string,
|
@@ -61,6 +60,7 @@ export interface AccountSchema<
|
|
61
60
|
},
|
62
61
|
) => Promise<Loaded<AccountSchema<Shape>, R> | null>;
|
63
62
|
|
63
|
+
/** @internal */
|
64
64
|
createAs: (
|
65
65
|
as: Account,
|
66
66
|
options: {
|
@@ -14,6 +14,7 @@ import {
|
|
14
14
|
import { CoreCoValueSchema } from "./CoValueSchema.js";
|
15
15
|
import { coOptionalDefiner } from "../zodCo.js";
|
16
16
|
import { CoOptionalSchema } from "./CoOptionalSchema.js";
|
17
|
+
import type { AccountRole, InviteSecret } from "cojson";
|
17
18
|
|
18
19
|
export interface CoreGroupSchema extends CoreCoValueSchema {
|
19
20
|
builtin: "Group";
|
@@ -51,7 +52,12 @@ export class GroupSchema implements CoreGroupSchema {
|
|
51
52
|
): Promise<Group | null> {
|
52
53
|
return Group.load(id, options);
|
53
54
|
}
|
54
|
-
|
55
|
+
createInvite<G extends Group>(
|
56
|
+
id: ID<G>,
|
57
|
+
options?: { role?: AccountRole; loadAs?: Account },
|
58
|
+
): Promise<InviteSecret> {
|
59
|
+
return Group.createInvite(id, options);
|
60
|
+
}
|
55
61
|
subscribe<G extends Group, const R extends RefsToResolve<G>>(
|
56
62
|
id: ID<G>,
|
57
63
|
listener: (value: Resolved<G, R>, unsubscribe: () => void) => void,
|
package/src/tools/ssr/ssr.ts
CHANGED
@@ -5,7 +5,7 @@ import { createAnonymousJazzContext } from "jazz-tools";
|
|
5
5
|
export function createSSRJazzAgent(opts: { peer: string }) {
|
6
6
|
const ssrNode = createAnonymousJazzContext({
|
7
7
|
crypto: new PureJSCrypto(),
|
8
|
-
|
8
|
+
peers: [],
|
9
9
|
});
|
10
10
|
|
11
11
|
const wsPeer = new WebSocketPeerWithReconnection({
|
package/src/tools/testing.ts
CHANGED
@@ -112,7 +112,7 @@ export async function createJazzTestAccount<
|
|
112
112
|
},
|
113
113
|
initialAgentSecret: crypto.agentSecretFromSecretSeed(secretSeed),
|
114
114
|
crypto,
|
115
|
-
|
115
|
+
peers: peers,
|
116
116
|
migration: async (rawAccount, _node, creationProps) => {
|
117
117
|
if (isMigrationActive) {
|
118
118
|
throw new Error(
|
@@ -185,7 +185,7 @@ export function runWithoutActiveAccount<Result>(
|
|
185
185
|
export async function createJazzTestGuest() {
|
186
186
|
const ctx = await createAnonymousJazzContext({
|
187
187
|
crypto: await PureJSCrypto.create(),
|
188
|
-
|
188
|
+
peers: [],
|
189
189
|
});
|
190
190
|
|
191
191
|
return {
|
@@ -318,7 +318,7 @@ export class TestJazzContextManager<
|
|
318
318
|
credentials: authProps?.credentials,
|
319
319
|
defaultProfileName: props.defaultProfileName,
|
320
320
|
newAccountProps: authProps?.newAccountProps,
|
321
|
-
|
321
|
+
peers: [getPeerConnectedToTestSyncServer()],
|
322
322
|
crypto: await TestJSCrypto.create(),
|
323
323
|
sessionProvider: randomSessionProvider,
|
324
324
|
authSecretStorage: this.getAuthSecretStorage(),
|
@@ -1,6 +1,6 @@
|
|
1
1
|
// @vitest-environment happy-dom
|
2
2
|
|
3
|
-
import { beforeEach, describe, expect, it, vi } from "vitest";
|
3
|
+
import { afterAll, beforeEach, describe, expect, it, vi } from "vitest";
|
4
4
|
import { AuthSecretStorage } from "../auth/AuthSecretStorage";
|
5
5
|
import { InMemoryKVStore } from "../auth/InMemoryKVStore.js";
|
6
6
|
import KvStoreContext from "../auth/KvStoreContext";
|
@@ -428,4 +428,62 @@ describe("AuthSecretStorage", () => {
|
|
428
428
|
});
|
429
429
|
});
|
430
430
|
});
|
431
|
+
|
432
|
+
describe("configurable secret storage key", () => {
|
433
|
+
const KEY = "test-auth-secret";
|
434
|
+
beforeEach(() => {
|
435
|
+
kvStore.clearAll();
|
436
|
+
authSecretStorage = new AuthSecretStorage(KEY);
|
437
|
+
});
|
438
|
+
|
439
|
+
afterAll(() => {
|
440
|
+
// Restore the default storage key
|
441
|
+
authSecretStorage = new AuthSecretStorage();
|
442
|
+
});
|
443
|
+
|
444
|
+
it("should throw an error if the storage key is empty", () => {
|
445
|
+
expect(() => new AuthSecretStorage("")).toThrow(
|
446
|
+
"Too small: expected string to have >=1 characters",
|
447
|
+
);
|
448
|
+
});
|
449
|
+
|
450
|
+
it("should use the configured storage key on get", async () => {
|
451
|
+
const credentials = {
|
452
|
+
accountID: "test123",
|
453
|
+
secretSeed: [1, 2, 3],
|
454
|
+
accountSecret: "secret123",
|
455
|
+
provider: "anonymous",
|
456
|
+
};
|
457
|
+
await kvStore.set(KEY, JSON.stringify(credentials));
|
458
|
+
|
459
|
+
const result = await authSecretStorage.get();
|
460
|
+
|
461
|
+
expect(result).toEqual({
|
462
|
+
accountID: "test123",
|
463
|
+
secretSeed: new Uint8Array([1, 2, 3]),
|
464
|
+
accountSecret: "secret123",
|
465
|
+
provider: "anonymous",
|
466
|
+
});
|
467
|
+
});
|
468
|
+
|
469
|
+
it("should use the configured storage key on set", async () => {
|
470
|
+
const payload = {
|
471
|
+
accountID: "test123" as ID<Account>,
|
472
|
+
secretSeed: new Uint8Array([1, 2, 3]),
|
473
|
+
accountSecret:
|
474
|
+
"secret123" as `sealerSecret_z${string}/signerSecret_z${string}`,
|
475
|
+
provider: "passphrase",
|
476
|
+
};
|
477
|
+
|
478
|
+
await authSecretStorage.set(payload);
|
479
|
+
|
480
|
+
const stored = JSON.parse((await kvStore.get(KEY))!);
|
481
|
+
expect(stored).toEqual({
|
482
|
+
accountID: "test123",
|
483
|
+
secretSeed: [1, 2, 3],
|
484
|
+
accountSecret: "secret123",
|
485
|
+
provider: "passphrase",
|
486
|
+
});
|
487
|
+
});
|
488
|
+
});
|
431
489
|
});
|
@@ -56,7 +56,7 @@ class TestJazzContextManager<Acc extends Account> extends JazzContextManager<
|
|
56
56
|
credentials: authProps?.credentials,
|
57
57
|
defaultProfileName: props.defaultProfileName,
|
58
58
|
newAccountProps: authProps?.newAccountProps,
|
59
|
-
|
59
|
+
peers: [getPeerConnectedToTestSyncServer()],
|
60
60
|
crypto: Crypto,
|
61
61
|
sessionProvider: randomSessionProvider,
|
62
62
|
authSecretStorage: this.getAuthSecretStorage(),
|
@@ -541,6 +541,16 @@ describe("ContextManager", () => {
|
|
541
541
|
).rejects.toThrow("Props required");
|
542
542
|
});
|
543
543
|
|
544
|
+
describe("configurable storage key", () => {
|
545
|
+
test("uses the configured storage key", async () => {
|
546
|
+
const KEY = "test-auth-secret";
|
547
|
+
const manager = new TestJazzContextManager<Account>({
|
548
|
+
authSecretStorageKey: KEY,
|
549
|
+
});
|
550
|
+
expect(manager.getAuthSecretStorage().getStorageKey()).toBe(KEY);
|
551
|
+
});
|
552
|
+
});
|
553
|
+
|
544
554
|
describe("Race condition handling", () => {
|
545
555
|
test("prevents concurrent authentication attempts", async () => {
|
546
556
|
const account = await createJazzTestAccount();
|
@@ -409,3 +409,19 @@ describe("account.toJSON", () => {
|
|
409
409
|
});
|
410
410
|
});
|
411
411
|
});
|
412
|
+
|
413
|
+
describe("accepting invites", () => {
|
414
|
+
test("accepting an invite to a Group", async () => {
|
415
|
+
const account = await createJazzTestAccount({
|
416
|
+
isCurrentActiveAccount: true,
|
417
|
+
});
|
418
|
+
const group = co.group().create();
|
419
|
+
const invite = group.$jazz.createInvite("reader");
|
420
|
+
const newAccount = await createJazzTestAccount({
|
421
|
+
isCurrentActiveAccount: true,
|
422
|
+
});
|
423
|
+
expect(group.getRoleOf(newAccount.$jazz.id)).toBeUndefined();
|
424
|
+
await newAccount.acceptInvite(group.$jazz.id, invite);
|
425
|
+
expect(group.getRoleOf(newAccount.$jazz.id)).toBe("reader");
|
426
|
+
});
|
427
|
+
});
|
@@ -205,7 +205,7 @@ describe("CoPlainText", () => {
|
|
205
205
|
secret: me.$jazz.localNode.getCurrentAgent().agentSecret,
|
206
206
|
},
|
207
207
|
sessionProvider: randomSessionProvider,
|
208
|
-
|
208
|
+
peers: [initialAsPeer],
|
209
209
|
crypto: Crypto,
|
210
210
|
asActiveAccount: true,
|
211
211
|
});
|
@@ -237,7 +237,7 @@ describe("CoPlainText", () => {
|
|
237
237
|
secret: me.$jazz.localNode.getCurrentAgent().agentSecret,
|
238
238
|
},
|
239
239
|
sessionProvider: randomSessionProvider,
|
240
|
-
|
240
|
+
peers: [initialAsPeer],
|
241
241
|
crypto: Crypto,
|
242
242
|
asActiveAccount: true,
|
243
243
|
});
|