jazz-tools 0.19.12 → 0.19.13
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 +50 -50
- package/CHANGELOG.md +10 -0
- package/dist/browser/createBrowserContext.d.ts +1 -5
- package/dist/browser/createBrowserContext.d.ts.map +1 -1
- package/dist/browser/index.js +124 -47
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/provideBrowserLockSession/BrowserSessionProvider.d.ts +12 -0
- package/dist/browser/provideBrowserLockSession/BrowserSessionProvider.d.ts.map +1 -0
- package/dist/browser/provideBrowserLockSession/BrowserSessionProvider.test.d.ts +2 -0
- package/dist/browser/provideBrowserLockSession/BrowserSessionProvider.test.d.ts.map +1 -0
- package/dist/browser/provideBrowserLockSession/SessionIDStorage.d.ts +6 -0
- package/dist/browser/provideBrowserLockSession/SessionIDStorage.d.ts.map +1 -0
- package/dist/browser/provideBrowserLockSession/index.d.ts +4 -0
- package/dist/browser/provideBrowserLockSession/index.d.ts.map +1 -0
- package/dist/{chunk-AGF4HEDH.js → chunk-GAPMDNJY.js} +437 -82
- package/dist/chunk-GAPMDNJY.js.map +1 -0
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/react-native/index.js +41 -12
- package/dist/react-native/index.js.map +1 -1
- package/dist/react-native-core/ReactNativeSessionProvider.d.ts +11 -0
- package/dist/react-native-core/ReactNativeSessionProvider.d.ts.map +1 -0
- package/dist/react-native-core/index.js +41 -12
- package/dist/react-native-core/index.js.map +1 -1
- package/dist/react-native-core/platform.d.ts +2 -8
- package/dist/react-native-core/platform.d.ts.map +1 -1
- package/dist/react-native-core/tests/ReactNativeSessionProvider.test.d.ts +2 -0
- package/dist/react-native-core/tests/ReactNativeSessionProvider.test.d.ts.map +1 -0
- package/dist/testing.js +4 -3
- package/dist/testing.js.map +1 -1
- package/dist/tools/coValues/account.d.ts.map +1 -1
- package/dist/tools/coValues/coFeed.d.ts +2 -2
- package/dist/tools/coValues/coFeed.d.ts.map +1 -1
- package/dist/tools/coValues/coList.d.ts +1 -2
- package/dist/tools/coValues/coList.d.ts.map +1 -1
- package/dist/tools/coValues/coMap.d.ts.map +1 -1
- package/dist/tools/coValues/coVector.d.ts.map +1 -1
- package/dist/tools/coValues/group.d.ts +5 -1
- package/dist/tools/coValues/group.d.ts.map +1 -1
- package/dist/tools/coValues/interfaces.d.ts +2 -1
- package/dist/tools/coValues/interfaces.d.ts.map +1 -1
- package/dist/tools/exports.d.ts +2 -2
- package/dist/tools/exports.d.ts.map +1 -1
- package/dist/tools/implementation/createContext.d.ts +21 -11
- package/dist/tools/implementation/createContext.d.ts.map +1 -1
- package/dist/tools/implementation/schema.d.ts +14 -6
- package/dist/tools/implementation/schema.d.ts.map +1 -1
- package/dist/tools/implementation/schemaUtils.d.ts +1 -1
- package/dist/tools/implementation/schemaUtils.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/runtimeConverters/schemaFieldToCoFieldDef.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/schemaPermissions.d.ts +99 -0
- package/dist/tools/implementation/zodSchema/schemaPermissions.d.ts.map +1 -0
- package/dist/tools/implementation/zodSchema/schemaTypes/CoFeedSchema.d.ts +11 -0
- package/dist/tools/implementation/zodSchema/schemaTypes/CoFeedSchema.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/schemaTypes/CoListSchema.d.ts +11 -0
- package/dist/tools/implementation/zodSchema/schemaTypes/CoListSchema.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/schemaTypes/CoMapSchema.d.ts +15 -1
- package/dist/tools/implementation/zodSchema/schemaTypes/CoMapSchema.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/schemaTypes/CoRecordSchema.d.ts +10 -0
- package/dist/tools/implementation/zodSchema/schemaTypes/CoRecordSchema.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/schemaTypes/CoVectorSchema.d.ts +9 -0
- package/dist/tools/implementation/zodSchema/schemaTypes/CoVectorSchema.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/schemaTypes/FileStreamSchema.d.ts +13 -1
- package/dist/tools/implementation/zodSchema/schemaTypes/FileStreamSchema.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/schemaTypes/PlainTextSchema.d.ts +10 -0
- package/dist/tools/implementation/zodSchema/schemaTypes/PlainTextSchema.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/schemaTypes/RichTextSchema.d.ts +6 -0
- package/dist/tools/implementation/zodSchema/schemaTypes/RichTextSchema.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/unionUtils.d.ts +12 -1
- package/dist/tools/implementation/zodSchema/unionUtils.d.ts.map +1 -1
- package/dist/tools/internal.d.ts +1 -0
- package/dist/tools/internal.d.ts.map +1 -1
- package/dist/tools/testing.d.ts.map +1 -1
- package/dist/tools/tests/schema.withPermissions.test.d.ts +2 -0
- package/dist/tools/tests/schema.withPermissions.test.d.ts.map +1 -0
- package/dist/worker/index.js +2 -2
- package/dist/worker/index.js.map +1 -1
- package/package.json +4 -4
- package/src/browser/createBrowserContext.ts +3 -62
- package/src/browser/provideBrowserLockSession/BrowserSessionProvider.test.ts +406 -0
- package/src/browser/provideBrowserLockSession/BrowserSessionProvider.ts +132 -0
- package/src/browser/provideBrowserLockSession/SessionIDStorage.ts +33 -0
- package/src/browser/provideBrowserLockSession/index.ts +11 -0
- package/src/react-native-core/ReactNativeSessionProvider.ts +52 -0
- package/src/react-native-core/platform.ts +5 -30
- package/src/react-native-core/tests/ReactNativeSessionProvider.test.ts +124 -0
- package/src/tools/coValues/account.ts +4 -0
- package/src/tools/coValues/coFeed.ts +8 -3
- package/src/tools/coValues/coList.ts +6 -3
- package/src/tools/coValues/coMap.ts +10 -0
- package/src/tools/coValues/coVector.ts +2 -1
- package/src/tools/coValues/group.ts +6 -4
- package/src/tools/coValues/interfaces.ts +19 -7
- package/src/tools/exports.ts +3 -1
- package/src/tools/implementation/createContext.ts +43 -15
- package/src/tools/implementation/schema.ts +23 -13
- package/src/tools/implementation/schemaUtils.ts +1 -1
- package/src/tools/implementation/zodSchema/runtimeConverters/schemaFieldToCoFieldDef.ts +105 -4
- package/src/tools/implementation/zodSchema/schemaPermissions.ts +188 -0
- package/src/tools/implementation/zodSchema/schemaTypes/CoFeedSchema.ts +46 -3
- package/src/tools/implementation/zodSchema/schemaTypes/CoListSchema.ts +46 -3
- package/src/tools/implementation/zodSchema/schemaTypes/CoMapSchema.ts +50 -13
- package/src/tools/implementation/zodSchema/schemaTypes/CoRecordSchema.ts +14 -0
- package/src/tools/implementation/zodSchema/schemaTypes/CoVectorSchema.ts +24 -1
- package/src/tools/implementation/zodSchema/schemaTypes/FileStreamSchema.ts +51 -4
- package/src/tools/implementation/zodSchema/schemaTypes/PlainTextSchema.ts +25 -1
- package/src/tools/implementation/zodSchema/schemaTypes/RichTextSchema.ts +21 -1
- package/src/tools/implementation/zodSchema/unionUtils.ts +72 -20
- package/src/tools/internal.ts +1 -0
- package/src/tools/testing.ts +3 -1
- package/src/tools/tests/ContextManager.test.ts +2 -1
- package/src/tools/tests/coPlainText.test.ts +2 -2
- package/src/tools/tests/createContext.test.ts +79 -1
- package/src/tools/tests/deepLoading.test.ts +25 -2
- package/src/tools/tests/schema.resolved.test.ts +10 -0
- package/src/tools/tests/schema.withPermissions.test.ts +859 -0
- package/src/tools/tests/utils.ts +2 -2
- package/src/worker/index.ts +2 -2
- package/dist/chunk-AGF4HEDH.js.map +0 -1
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { AgentID, CryptoProvider, RawAccountID, SessionID } from "cojson";
|
|
2
|
+
import { ID, Account, SessionProvider } from "jazz-tools";
|
|
3
|
+
import { SessionIDStorage } from "./SessionIDStorage";
|
|
4
|
+
|
|
5
|
+
export class BrowserSessionProvider implements SessionProvider {
|
|
6
|
+
async acquireSession(
|
|
7
|
+
accountID: ID<Account> | AgentID,
|
|
8
|
+
crypto: CryptoProvider,
|
|
9
|
+
): Promise<{ sessionID: SessionID; sessionDone: () => void }> {
|
|
10
|
+
const { sessionPromise, resolveSession } = createSessionLockPromise();
|
|
11
|
+
|
|
12
|
+
// Get the list of sessions for the account, to try to acquire an existing session
|
|
13
|
+
const sessionsList = SessionIDStorage.getSessionsList(accountID);
|
|
14
|
+
|
|
15
|
+
for (const [index, sessionID] of sessionsList.entries()) {
|
|
16
|
+
const sessionAcquired = await tryToAcquireSession(
|
|
17
|
+
sessionID,
|
|
18
|
+
sessionPromise,
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
if (sessionAcquired) {
|
|
22
|
+
console.log("Using existing session", sessionID, "at index", index); // This log is used in the e2e tests to verify the correctness of the feature
|
|
23
|
+
return {
|
|
24
|
+
sessionID,
|
|
25
|
+
sessionDone: resolveSession,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const newSessionID = crypto.newRandomSessionID(
|
|
31
|
+
accountID as RawAccountID | AgentID,
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
// Acquire exclusively the session to store the new session ID for reuse in future sessions
|
|
35
|
+
await lockAndStoreSession(accountID, newSessionID, sessionPromise);
|
|
36
|
+
|
|
37
|
+
console.log("Created new session", newSessionID); // This log is used in the e2e tests to verify the correctness of the feature
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
sessionID: newSessionID,
|
|
41
|
+
sessionDone: resolveSession,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async persistSession(
|
|
46
|
+
accountID: ID<Account> | AgentID,
|
|
47
|
+
sessionID: SessionID,
|
|
48
|
+
): Promise<{ sessionDone: () => void }> {
|
|
49
|
+
const { sessionPromise, resolveSession } = createSessionLockPromise();
|
|
50
|
+
|
|
51
|
+
// Store the session id for future use and lock it until the session is done
|
|
52
|
+
await lockAndStoreSession(accountID, sessionID, sessionPromise);
|
|
53
|
+
|
|
54
|
+
console.log("Stored new session", sessionID);
|
|
55
|
+
|
|
56
|
+
return { sessionDone: resolveSession };
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function lockAndStoreSession(
|
|
61
|
+
accountID: ID<Account> | AgentID,
|
|
62
|
+
sessionID: SessionID,
|
|
63
|
+
sessionPromise: Promise<void>,
|
|
64
|
+
) {
|
|
65
|
+
const sessionAcquired = await tryToAcquireSession(sessionID, sessionPromise);
|
|
66
|
+
|
|
67
|
+
if (!sessionAcquired) {
|
|
68
|
+
// This should never happen because the session has been randomly generated
|
|
69
|
+
throw new Error("Couldn't get lock on new session");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// We don't need to wait for this to finish, we only need to acquire the lock on the new session
|
|
73
|
+
storeSessionID(accountID, sessionID);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function tryToAcquireSession(
|
|
77
|
+
sessionID: SessionID,
|
|
78
|
+
sessionDonePromise: Promise<void>,
|
|
79
|
+
) {
|
|
80
|
+
return new Promise<boolean>((resolve) => {
|
|
81
|
+
// Acquire exclusively the session if available
|
|
82
|
+
navigator.locks.request(
|
|
83
|
+
`load_session_${sessionID}`,
|
|
84
|
+
{ mode: "exclusive", ifAvailable: true },
|
|
85
|
+
async (lock) => {
|
|
86
|
+
if (!lock) {
|
|
87
|
+
resolve(false); // Session already in use
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
resolve(true); // Session is available
|
|
92
|
+
|
|
93
|
+
// Return the promise to lock the session until sessionDone is called
|
|
94
|
+
return sessionDonePromise;
|
|
95
|
+
},
|
|
96
|
+
);
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function storeSessionID(
|
|
101
|
+
accountID: ID<Account> | AgentID,
|
|
102
|
+
sessionID: SessionID,
|
|
103
|
+
) {
|
|
104
|
+
return navigator.locks.request(
|
|
105
|
+
`store_session_${accountID}`,
|
|
106
|
+
{ mode: "exclusive" },
|
|
107
|
+
async (lock) => {
|
|
108
|
+
if (!lock) {
|
|
109
|
+
console.error("Couldn't get lock to store session ID", accountID);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const sessionsList = SessionIDStorage.getSessionsList(accountID);
|
|
113
|
+
SessionIDStorage.storeSessionID(
|
|
114
|
+
accountID,
|
|
115
|
+
sessionID,
|
|
116
|
+
sessionsList.length,
|
|
117
|
+
);
|
|
118
|
+
},
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function createSessionLockPromise() {
|
|
123
|
+
let resolveSession: () => void;
|
|
124
|
+
const sessionPromise = new Promise<void>((resolve) => {
|
|
125
|
+
resolveSession = resolve;
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
sessionPromise,
|
|
130
|
+
resolveSession: resolveSession!,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { SessionID } from "cojson";
|
|
2
|
+
|
|
3
|
+
function getSessionKey(accountID: string, index: number) {
|
|
4
|
+
return accountID + "_" + index;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export class SessionIDStorage {
|
|
8
|
+
static getSessionsList(accountID: string) {
|
|
9
|
+
let sessionsList: SessionID[] = [];
|
|
10
|
+
let i = 0;
|
|
11
|
+
let lastSessionID: SessionID | null;
|
|
12
|
+
|
|
13
|
+
do {
|
|
14
|
+
lastSessionID = localStorage.getItem(
|
|
15
|
+
getSessionKey(accountID, i),
|
|
16
|
+
) as SessionID | null;
|
|
17
|
+
if (lastSessionID) {
|
|
18
|
+
sessionsList.push(lastSessionID);
|
|
19
|
+
}
|
|
20
|
+
i++;
|
|
21
|
+
} while (lastSessionID);
|
|
22
|
+
|
|
23
|
+
return sessionsList;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
static storeSessionID(
|
|
27
|
+
accountID: string,
|
|
28
|
+
sessionID: SessionID,
|
|
29
|
+
index: number,
|
|
30
|
+
) {
|
|
31
|
+
localStorage.setItem(getSessionKey(accountID, index), sessionID);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { MockSessionProvider } from "jazz-tools";
|
|
2
|
+
import { BrowserSessionProvider } from "./BrowserSessionProvider";
|
|
3
|
+
|
|
4
|
+
export function getBrowserLockSessionProvider() {
|
|
5
|
+
if (typeof navigator === "undefined" || !navigator.locks?.request) {
|
|
6
|
+
// Fallback to random session ID for each tab session
|
|
7
|
+
return new MockSessionProvider();
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
return new BrowserSessionProvider();
|
|
11
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CryptoProvider,
|
|
3
|
+
KvStoreContext,
|
|
4
|
+
SessionID,
|
|
5
|
+
SessionProvider,
|
|
6
|
+
} from "jazz-tools";
|
|
7
|
+
import { AgentID, RawAccountID } from "cojson";
|
|
8
|
+
|
|
9
|
+
export class ReactNativeSessionProvider implements SessionProvider {
|
|
10
|
+
async acquireSession(
|
|
11
|
+
accountID: string,
|
|
12
|
+
crypto: CryptoProvider,
|
|
13
|
+
): Promise<{ sessionID: SessionID; sessionDone: () => void }> {
|
|
14
|
+
const kvStore = KvStoreContext.getInstance().getStorage();
|
|
15
|
+
const existingSession = await kvStore.get(accountID as string);
|
|
16
|
+
|
|
17
|
+
if (existingSession) {
|
|
18
|
+
console.log("Using existing session", existingSession);
|
|
19
|
+
return Promise.resolve({
|
|
20
|
+
sessionID: existingSession as SessionID,
|
|
21
|
+
sessionDone: () => {},
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// We need to provide this for backwards compatibility with the old session provider
|
|
26
|
+
// With the current session provider we should never get here because:
|
|
27
|
+
// - New accounts provide their session and go through the persistSession method
|
|
28
|
+
// - Existing accounts should already have a session
|
|
29
|
+
const newSessionID = crypto.newRandomSessionID(
|
|
30
|
+
accountID as RawAccountID | AgentID,
|
|
31
|
+
);
|
|
32
|
+
await kvStore.set(accountID, newSessionID);
|
|
33
|
+
|
|
34
|
+
console.error("Created new session", newSessionID);
|
|
35
|
+
|
|
36
|
+
return Promise.resolve({
|
|
37
|
+
sessionID: newSessionID,
|
|
38
|
+
sessionDone: () => {},
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async persistSession(
|
|
43
|
+
accountID: string,
|
|
44
|
+
sessionID: SessionID,
|
|
45
|
+
): Promise<{ sessionDone: () => void }> {
|
|
46
|
+
const kvStore = KvStoreContext.getInstance().getStorage();
|
|
47
|
+
await kvStore.set(accountID, sessionID);
|
|
48
|
+
return Promise.resolve({
|
|
49
|
+
sessionDone: () => {},
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -1,25 +1,22 @@
|
|
|
1
1
|
import NetInfo from "@react-native-community/netinfo";
|
|
2
|
-
import { LocalNode, Peer,
|
|
2
|
+
import { LocalNode, Peer, getSqliteStorageAsync } from "cojson";
|
|
3
3
|
import { PureJSCrypto } from "cojson/dist/crypto/PureJSCrypto"; // Importing from dist to not rely on the exports field
|
|
4
4
|
import {
|
|
5
5
|
Account,
|
|
6
6
|
AccountClass,
|
|
7
|
-
AgentID,
|
|
8
7
|
AnyAccountSchema,
|
|
9
8
|
AuthCredentials,
|
|
10
9
|
AuthSecretStorage,
|
|
11
10
|
CoValue,
|
|
12
11
|
CoValueFromRaw,
|
|
13
|
-
CryptoProvider,
|
|
14
|
-
ID,
|
|
15
12
|
NewAccountProps,
|
|
16
|
-
SessionID,
|
|
17
13
|
SyncConfig,
|
|
18
14
|
createInviteLink as baseCreateInviteLink,
|
|
19
15
|
createAnonymousJazzContext,
|
|
20
16
|
createJazzContext,
|
|
21
17
|
} from "jazz-tools";
|
|
22
18
|
import { KvStore, KvStoreContext } from "./storage/kv-store-context.js";
|
|
19
|
+
import { ReactNativeSessionProvider } from "./ReactNativeSessionProvider.js";
|
|
23
20
|
|
|
24
21
|
import { SQLiteDatabaseDriverAsync } from "cojson";
|
|
25
22
|
import { WebSocketPeerWithReconnection } from "cojson-transport-ws";
|
|
@@ -200,6 +197,8 @@ export async function createJazzReactNativeContext<
|
|
|
200
197
|
handleAuthUpdate(authSecretStorage.isAuthenticated);
|
|
201
198
|
}
|
|
202
199
|
|
|
200
|
+
const sessionProvider = new ReactNativeSessionProvider();
|
|
201
|
+
|
|
203
202
|
const context = await createJazzContext({
|
|
204
203
|
credentials: options.credentials,
|
|
205
204
|
newAccountProps: options.newAccountProps,
|
|
@@ -207,7 +206,7 @@ export async function createJazzReactNativeContext<
|
|
|
207
206
|
crypto,
|
|
208
207
|
defaultProfileName: options.defaultProfileName,
|
|
209
208
|
AccountSchema: options.AccountSchema,
|
|
210
|
-
sessionProvider
|
|
209
|
+
sessionProvider,
|
|
211
210
|
authSecretStorage: options.authSecretStorage,
|
|
212
211
|
storage,
|
|
213
212
|
});
|
|
@@ -233,30 +232,6 @@ export async function createJazzReactNativeContext<
|
|
|
233
232
|
};
|
|
234
233
|
}
|
|
235
234
|
|
|
236
|
-
/** @category Auth Providers */
|
|
237
|
-
export type SessionProvider = (
|
|
238
|
-
accountID: ID<Account> | AgentID,
|
|
239
|
-
) => Promise<SessionID>;
|
|
240
|
-
|
|
241
|
-
export async function provideLockSession(
|
|
242
|
-
accountID: ID<Account> | AgentID,
|
|
243
|
-
crypto: CryptoProvider,
|
|
244
|
-
) {
|
|
245
|
-
const sessionDone = () => {};
|
|
246
|
-
|
|
247
|
-
const kvStore = KvStoreContext.getInstance().getStorage();
|
|
248
|
-
|
|
249
|
-
const sessionID =
|
|
250
|
-
((await kvStore.get(accountID)) as SessionID) ||
|
|
251
|
-
crypto.newRandomSessionID(accountID as RawAccountID | AgentID);
|
|
252
|
-
await kvStore.set(accountID, sessionID);
|
|
253
|
-
|
|
254
|
-
return Promise.resolve({
|
|
255
|
-
sessionID,
|
|
256
|
-
sessionDone,
|
|
257
|
-
});
|
|
258
|
-
}
|
|
259
|
-
|
|
260
235
|
/** @category Invite Links */
|
|
261
236
|
export function createInviteLink<C extends CoValue>(
|
|
262
237
|
value: C,
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { WasmCrypto } from "cojson/crypto/WasmCrypto";
|
|
2
|
+
import { SessionID } from "cojson";
|
|
3
|
+
import { beforeEach, describe, expect, test } from "vitest";
|
|
4
|
+
import { InMemoryKVStore } from "jazz-tools";
|
|
5
|
+
import { KvStoreContext, type KvStore } from "jazz-tools";
|
|
6
|
+
import { ReactNativeSessionProvider } from "../ReactNativeSessionProvider.js";
|
|
7
|
+
import { createJazzTestAccount } from "jazz-tools/testing";
|
|
8
|
+
import type { CryptoProvider } from "jazz-tools";
|
|
9
|
+
|
|
10
|
+
// Initialize KV store for tests
|
|
11
|
+
const kvStore = new InMemoryKVStore() as KvStore;
|
|
12
|
+
KvStoreContext.getInstance().initialize(kvStore);
|
|
13
|
+
|
|
14
|
+
const Crypto = await WasmCrypto.create();
|
|
15
|
+
|
|
16
|
+
describe("ReactNativeSessionProvider", () => {
|
|
17
|
+
let sessionProvider: ReactNativeSessionProvider;
|
|
18
|
+
let account: Awaited<ReturnType<typeof createJazzTestAccount>>;
|
|
19
|
+
|
|
20
|
+
beforeEach(async () => {
|
|
21
|
+
// Clear KV store
|
|
22
|
+
kvStore.clearAll();
|
|
23
|
+
|
|
24
|
+
// Create new session provider instance
|
|
25
|
+
sessionProvider = new ReactNativeSessionProvider();
|
|
26
|
+
|
|
27
|
+
// Create test account
|
|
28
|
+
account = await createJazzTestAccount({
|
|
29
|
+
isCurrentActiveAccount: true,
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe("acquireSession", () => {
|
|
34
|
+
test("creates new session when none exists", async () => {
|
|
35
|
+
const accountID = account.$jazz.id;
|
|
36
|
+
|
|
37
|
+
// Verify no session exists
|
|
38
|
+
const existingSessionBefore = await kvStore.get(accountID);
|
|
39
|
+
expect(existingSessionBefore).toBeNull();
|
|
40
|
+
|
|
41
|
+
// Acquire session
|
|
42
|
+
const result = await sessionProvider.acquireSession(
|
|
43
|
+
accountID,
|
|
44
|
+
Crypto as CryptoProvider,
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
// Verify a new session ID is generated
|
|
48
|
+
expect(result.sessionID).toBeDefined();
|
|
49
|
+
|
|
50
|
+
// Verify the session is stored in KvStore
|
|
51
|
+
const storedSession = await kvStore.get(accountID);
|
|
52
|
+
expect(storedSession).toBeDefined();
|
|
53
|
+
expect(storedSession).toBe(result.sessionID);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("returns existing session when one exists", async () => {
|
|
57
|
+
const accountID = account.$jazz.id;
|
|
58
|
+
const existingSessionID = "existing-session-id" as SessionID;
|
|
59
|
+
|
|
60
|
+
// Pre-populate KvStore with a session ID
|
|
61
|
+
await kvStore.set(accountID, existingSessionID);
|
|
62
|
+
|
|
63
|
+
// Verify session exists before calling acquireSession
|
|
64
|
+
const sessionBefore = await kvStore.get(accountID);
|
|
65
|
+
expect(sessionBefore).toBe(existingSessionID);
|
|
66
|
+
|
|
67
|
+
// Acquire session
|
|
68
|
+
const result = await sessionProvider.acquireSession(
|
|
69
|
+
accountID,
|
|
70
|
+
Crypto as CryptoProvider,
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
// Verify the existing session ID is returned (not a new one)
|
|
74
|
+
expect(result.sessionID).toBe(existingSessionID);
|
|
75
|
+
|
|
76
|
+
// Verify no new session is created (same value still in store)
|
|
77
|
+
const sessionAfter = await kvStore.get(accountID);
|
|
78
|
+
expect(sessionAfter).toBe(existingSessionID);
|
|
79
|
+
expect(sessionAfter).toBe(result.sessionID);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
describe("persistSession", () => {
|
|
84
|
+
test("stores session ID correctly", async () => {
|
|
85
|
+
const accountID = account.$jazz.id;
|
|
86
|
+
const sessionID = "test-session-id" as SessionID;
|
|
87
|
+
|
|
88
|
+
// Verify no session exists before
|
|
89
|
+
const sessionBefore = await kvStore.get(accountID);
|
|
90
|
+
expect(sessionBefore).toBeNull();
|
|
91
|
+
|
|
92
|
+
// Persist session
|
|
93
|
+
await sessionProvider.persistSession(accountID, sessionID);
|
|
94
|
+
|
|
95
|
+
// Verify the session ID is stored in KvStore
|
|
96
|
+
const storedSession = await kvStore.get(accountID);
|
|
97
|
+
expect(storedSession).toBeDefined();
|
|
98
|
+
|
|
99
|
+
// Verify the stored value matches the provided session ID
|
|
100
|
+
expect(storedSession).toBe(sessionID);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("overwrites existing session", async () => {
|
|
104
|
+
const accountID = account.$jazz.id;
|
|
105
|
+
const initialSessionID = "initial-session-id" as SessionID;
|
|
106
|
+
const newSessionID = "new-session-id" as SessionID;
|
|
107
|
+
|
|
108
|
+
// Store an initial session ID
|
|
109
|
+
await kvStore.set(accountID, initialSessionID);
|
|
110
|
+
|
|
111
|
+
// Verify initial session is stored
|
|
112
|
+
const sessionBefore = await kvStore.get(accountID);
|
|
113
|
+
expect(sessionBefore).toBe(initialSessionID);
|
|
114
|
+
|
|
115
|
+
// Persist a different session ID
|
|
116
|
+
await sessionProvider.persistSession(accountID, newSessionID);
|
|
117
|
+
|
|
118
|
+
// Verify the new session ID replaces the old one
|
|
119
|
+
const sessionAfter = await kvStore.get(accountID);
|
|
120
|
+
expect(sessionAfter).toBe(newSessionID);
|
|
121
|
+
expect(sessionAfter).not.toBe(initialSessionID);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
});
|
|
@@ -466,10 +466,14 @@ class AccountJazzApi<A extends Account> extends CoValueJazzApi<A> {
|
|
|
466
466
|
| undefined;
|
|
467
467
|
if (!refId) {
|
|
468
468
|
const descriptor = this.schema[key];
|
|
469
|
+
const newOwnerStrategy = descriptor.permissions?.newInlineOwnerStrategy;
|
|
470
|
+
const onCreate = descriptor.permissions?.onCreate;
|
|
469
471
|
const coValue = instantiateRefEncodedWithInit(
|
|
470
472
|
descriptor,
|
|
471
473
|
value,
|
|
472
474
|
accountOrGroupToGroup(this.account),
|
|
475
|
+
newOwnerStrategy,
|
|
476
|
+
onCreate,
|
|
473
477
|
);
|
|
474
478
|
refId = coValue.$jazz.id as CoID<RawCoMap>;
|
|
475
479
|
}
|
|
@@ -32,8 +32,6 @@ import {
|
|
|
32
32
|
SubscribeRestArgs,
|
|
33
33
|
TypeSym,
|
|
34
34
|
BranchDefinition,
|
|
35
|
-
} from "../internal.js";
|
|
36
|
-
import {
|
|
37
35
|
Account,
|
|
38
36
|
CoValueBase,
|
|
39
37
|
CoValueJazzApi,
|
|
@@ -379,10 +377,15 @@ export class CoFeedJazzApi<F extends CoFeed> extends CoValueJazzApi<F> {
|
|
|
379
377
|
} else if (isRefEncoded(itemDescriptor)) {
|
|
380
378
|
let refId = (item as unknown as CoValue).$jazz?.id;
|
|
381
379
|
if (!refId) {
|
|
380
|
+
const newOwnerStrategy =
|
|
381
|
+
itemDescriptor.permissions?.newInlineOwnerStrategy;
|
|
382
|
+
const onCreate = itemDescriptor.permissions?.onCreate;
|
|
382
383
|
const coValue = instantiateRefEncodedWithInit(
|
|
383
384
|
itemDescriptor,
|
|
384
385
|
item,
|
|
385
386
|
this.owner,
|
|
387
|
+
newOwnerStrategy,
|
|
388
|
+
onCreate,
|
|
386
389
|
);
|
|
387
390
|
refId = coValue.$jazz.id;
|
|
388
391
|
}
|
|
@@ -720,6 +723,7 @@ export class FileStream extends CoValueBase implements CoValue {
|
|
|
720
723
|
*
|
|
721
724
|
* @param options - Configuration options for the new FileStream
|
|
722
725
|
* @param options.owner - The Account or Group that will own this FileStream and control access rights
|
|
726
|
+
* @param schemaConfiguration - Internal schema configuration
|
|
723
727
|
*
|
|
724
728
|
* @example
|
|
725
729
|
* ```typescript
|
|
@@ -743,7 +747,8 @@ export class FileStream extends CoValueBase implements CoValue {
|
|
|
743
747
|
this: CoValueClass<S>,
|
|
744
748
|
options?: { owner?: Account | Group } | Account | Group,
|
|
745
749
|
) {
|
|
746
|
-
|
|
750
|
+
const { owner } = parseCoValueCreateOptions(options);
|
|
751
|
+
return new this({ owner });
|
|
747
752
|
}
|
|
748
753
|
|
|
749
754
|
getMetadata(): BinaryStreamInfo | undefined {
|
|
@@ -26,8 +26,6 @@ import {
|
|
|
26
26
|
getIdFromHeader,
|
|
27
27
|
internalLoadUnique,
|
|
28
28
|
CoValueLoadingState,
|
|
29
|
-
} from "../internal.js";
|
|
30
|
-
import {
|
|
31
29
|
AnonymousJazzAgent,
|
|
32
30
|
ItemsSym,
|
|
33
31
|
Ref,
|
|
@@ -172,8 +170,8 @@ export class CoList<out Item = any>
|
|
|
172
170
|
| Account
|
|
173
171
|
| Group,
|
|
174
172
|
) {
|
|
175
|
-
const { owner, uniqueness } = parseCoValueCreateOptions(options);
|
|
176
173
|
const instance = new this();
|
|
174
|
+
const { owner, uniqueness } = parseCoValueCreateOptions(options);
|
|
177
175
|
|
|
178
176
|
Object.defineProperties(instance, {
|
|
179
177
|
$jazz: {
|
|
@@ -918,10 +916,15 @@ function toRawItems<Item>(
|
|
|
918
916
|
}
|
|
919
917
|
let refId = (value as unknown as CoValue).$jazz?.id;
|
|
920
918
|
if (!refId) {
|
|
919
|
+
const newOwnerStrategy =
|
|
920
|
+
itemDescriptor.permissions?.newInlineOwnerStrategy;
|
|
921
|
+
const onCreate = itemDescriptor.permissions?.onCreate;
|
|
921
922
|
const coValue = instantiateRefEncodedWithInit(
|
|
922
923
|
itemDescriptor,
|
|
923
924
|
value,
|
|
924
925
|
owner,
|
|
926
|
+
newOwnerStrategy,
|
|
927
|
+
onCreate,
|
|
925
928
|
);
|
|
926
929
|
refId = coValue.$jazz.id;
|
|
927
930
|
}
|
|
@@ -298,10 +298,15 @@ export class CoMap extends CoValueBase implements CoValue {
|
|
|
298
298
|
if (initValue != null) {
|
|
299
299
|
let refId = (initValue as unknown as CoValue).$jazz?.id;
|
|
300
300
|
if (!refId) {
|
|
301
|
+
const newOwnerStrategy =
|
|
302
|
+
descriptor.permissions?.newInlineOwnerStrategy;
|
|
303
|
+
const onCreate = descriptor.permissions?.onCreate;
|
|
301
304
|
const coValue = instantiateRefEncodedWithInit(
|
|
302
305
|
descriptor,
|
|
303
306
|
initValue,
|
|
304
307
|
owner,
|
|
308
|
+
newOwnerStrategy,
|
|
309
|
+
onCreate,
|
|
305
310
|
);
|
|
306
311
|
refId = coValue.$jazz.id;
|
|
307
312
|
}
|
|
@@ -610,10 +615,15 @@ class CoMapJazzApi<M extends CoMap> extends CoValueJazzApi<M> {
|
|
|
610
615
|
this.raw.set(key, null);
|
|
611
616
|
} else {
|
|
612
617
|
if (!refId) {
|
|
618
|
+
const newOwnerStrategy =
|
|
619
|
+
descriptor.permissions?.newInlineOwnerStrategy;
|
|
620
|
+
const onCreate = descriptor.permissions?.onCreate;
|
|
613
621
|
const coValue = instantiateRefEncodedWithInit(
|
|
614
622
|
descriptor,
|
|
615
623
|
value,
|
|
616
624
|
this.owner,
|
|
625
|
+
newOwnerStrategy,
|
|
626
|
+
onCreate,
|
|
617
627
|
);
|
|
618
628
|
refId = coValue.$jazz.id;
|
|
619
629
|
}
|
|
@@ -129,7 +129,8 @@ export class CoVector
|
|
|
129
129
|
);
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
-
const
|
|
132
|
+
const { owner } = parseCoValueCreateOptions(options);
|
|
133
|
+
const coVector = new this({ owner });
|
|
133
134
|
coVector.setVectorData(vectorAsFloat32Array);
|
|
134
135
|
|
|
135
136
|
const byteArray = CoVector.toByteArray(vectorAsFloat32Array);
|
|
@@ -48,6 +48,11 @@ type GroupMember = {
|
|
|
48
48
|
account: Account;
|
|
49
49
|
};
|
|
50
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Roles that can be granted to a group member.
|
|
53
|
+
*/
|
|
54
|
+
export type GroupRole = "reader" | "writer" | "admin" | "manager";
|
|
55
|
+
|
|
51
56
|
/** @category Identity & Permissions */
|
|
52
57
|
export class Group extends CoValueBase implements CoValue {
|
|
53
58
|
declare [TypeSym]: "Group";
|
|
@@ -107,10 +112,7 @@ export class Group extends CoValueBase implements CoValue {
|
|
|
107
112
|
* @param member The group that will gain access to this group.
|
|
108
113
|
* @param role The role all members of the parent group should have in this group.
|
|
109
114
|
*/
|
|
110
|
-
addMember(
|
|
111
|
-
member: Group,
|
|
112
|
-
role?: "reader" | "writer" | "admin" | "manager" | "inherit",
|
|
113
|
-
): void;
|
|
115
|
+
addMember(member: Group, role?: GroupRole | "inherit"): void;
|
|
114
116
|
addMember(
|
|
115
117
|
member: Group | Account,
|
|
116
118
|
role: "reader" | "writer" | "admin" | "manager",
|
|
@@ -11,10 +11,11 @@ import {
|
|
|
11
11
|
CoValueClassOrSchema,
|
|
12
12
|
CoValueLoadingState,
|
|
13
13
|
NotLoadedCoValueState,
|
|
14
|
-
|
|
14
|
+
Group,
|
|
15
15
|
Loaded,
|
|
16
16
|
Inaccessible,
|
|
17
17
|
MaybeLoaded,
|
|
18
|
+
OnCreateCallback,
|
|
18
19
|
Settled,
|
|
19
20
|
RefsToResolve,
|
|
20
21
|
RefsToResolveStrict,
|
|
@@ -424,6 +425,7 @@ export function parseCoValueCreateOptions(
|
|
|
424
425
|
| {
|
|
425
426
|
owner?: Account | Group;
|
|
426
427
|
unique?: CoValueUniqueness["uniqueness"];
|
|
428
|
+
onCreate?: OnCreateCallback;
|
|
427
429
|
}
|
|
428
430
|
| Account
|
|
429
431
|
| Group
|
|
@@ -432,16 +434,22 @@ export function parseCoValueCreateOptions(
|
|
|
432
434
|
owner: Group;
|
|
433
435
|
uniqueness?: CoValueUniqueness;
|
|
434
436
|
} {
|
|
437
|
+
const onCreate =
|
|
438
|
+
options && "onCreate" in options ? options.onCreate : undefined;
|
|
435
439
|
const Group = RegisteredSchemas["Group"];
|
|
436
|
-
|
|
437
440
|
if (!options) {
|
|
438
|
-
|
|
441
|
+
const owner = Group.create();
|
|
442
|
+
onCreate?.(owner);
|
|
443
|
+
return { owner, uniqueness: undefined };
|
|
439
444
|
}
|
|
440
445
|
|
|
441
446
|
if (TypeSym in options) {
|
|
442
447
|
if (options[TypeSym] === "Account") {
|
|
443
|
-
|
|
448
|
+
const owner = accountOrGroupToGroup(options);
|
|
449
|
+
onCreate?.(owner);
|
|
450
|
+
return { owner, uniqueness: undefined };
|
|
444
451
|
} else if (options[TypeSym] === "Group") {
|
|
452
|
+
onCreate?.(options);
|
|
445
453
|
return { owner: options, uniqueness: undefined };
|
|
446
454
|
}
|
|
447
455
|
}
|
|
@@ -450,10 +458,14 @@ export function parseCoValueCreateOptions(
|
|
|
450
458
|
? { uniqueness: options.unique }
|
|
451
459
|
: undefined;
|
|
452
460
|
|
|
461
|
+
const owner = options.owner
|
|
462
|
+
? accountOrGroupToGroup(options.owner)
|
|
463
|
+
: Group.create();
|
|
464
|
+
|
|
465
|
+
onCreate?.(owner);
|
|
466
|
+
|
|
453
467
|
const opts = {
|
|
454
|
-
owner
|
|
455
|
-
? accountOrGroupToGroup(options.owner)
|
|
456
|
-
: Group.create(),
|
|
468
|
+
owner,
|
|
457
469
|
uniqueness,
|
|
458
470
|
};
|
|
459
471
|
return opts;
|
package/src/tools/exports.ts
CHANGED
|
@@ -66,6 +66,7 @@ export {
|
|
|
66
66
|
createUnloadedCoValue,
|
|
67
67
|
unstable_loadUnique,
|
|
68
68
|
getUnloadedCoValueWithoutId,
|
|
69
|
+
setDefaultSchemaPermissions,
|
|
69
70
|
} from "./internal.js";
|
|
70
71
|
|
|
71
72
|
export {
|
|
@@ -100,7 +101,8 @@ export {
|
|
|
100
101
|
createJazzContextFromExistingCredentials,
|
|
101
102
|
createJazzContextForNewAccount,
|
|
102
103
|
createJazzContext,
|
|
103
|
-
|
|
104
|
+
SessionProvider,
|
|
105
|
+
MockSessionProvider,
|
|
104
106
|
type AuthResult,
|
|
105
107
|
type Credentials,
|
|
106
108
|
type JazzContextWithAccount,
|