Compare commits

...

5 Commits

Author SHA1 Message Date
Anselm
6a147c2d28 Pre-release 2024-09-06 20:39:14 +01:00
Anselm
c2b62a0fee Pre-release 2024-09-06 16:49:42 +01:00
Anselm
301458e713 Make anonymous auth work better 2024-09-06 16:48:22 +01:00
Anselm
1a979b64b3 Implement guest auth 2024-09-05 18:37:24 +01:00
Anselm
8b7b57fe2c Move newRandomSessionID to CryptoProvider 2024-09-04 11:41:02 +01:00
72 changed files with 1047 additions and 568 deletions

View File

@@ -0,0 +1,5 @@
---
"cojson": patch
---
Move session generation to crypto provider

View File

@@ -9,6 +9,8 @@
"jazz-tools",
"jazz-browser",
"jazz-browser-media-images",
"jazz-browser-auth-clerk",
"jazz-react-auth-clerk",
"jazz-react",
"jazz-nodejs",
"jazz-run",

View File

@@ -1,29 +1,32 @@
{
"mode": "pre",
"tag": "unique",
"tag": "guest-auth",
"initialVersions": {
"jazz-example-chat": "0.0.82-new-auth.1",
"jazz-example-chat-clerk": "0.0.80-new-auth.1",
"jazz-inspector": "0.0.59",
"jazz-example-pets": "0.0.100-new-auth.1",
"jazz-example-todo": "0.0.99-new-auth.1",
"cojson": "0.7.34",
"cojson-storage-indexeddb": "0.7.34",
"cojson-storage-sqlite": "0.7.34",
"cojson-transport-ws": "0.7.34",
"jazz-example-chat": "0.0.82-unique.2",
"jazz-example-chat-clerk": "0.0.80-unique.2",
"jazz-inspector": "0.0.60-unique.0",
"jazz-example-pets": "0.0.100-unique.2",
"jazz-example-todo": "0.0.99-unique.2",
"cojson": "0.7.35-unique.2",
"cojson-storage-indexeddb": "0.7.35-unique.2",
"cojson-storage-sqlite": "0.7.35-unique.2",
"cojson-transport-ws": "0.7.35-unique.2",
"hash-slash": "0.2.0",
"jazz-browser": "0.7.35-new-auth.0",
"jazz-browser-auth-clerk": "0.7.33-new-auth.0",
"jazz-browser-media-images": "0.7.35-new-auth.0",
"jazz-nodejs": "0.7.35-new-auth.0",
"jazz-react": "0.7.35-new-auth.1",
"jazz-react-auth-clerk": "0.7.33-new-auth.1",
"jazz-run": "0.7.35-new-auth.0",
"jazz-tools": "0.7.35-new-auth.0"
"jazz-browser": "0.7.35-unique.2",
"jazz-browser-auth-clerk": "0.7.33-unique.1",
"jazz-browser-media-images": "0.7.35-unique.2",
"jazz-nodejs": "0.7.35-unique.2",
"jazz-react": "0.7.35-unique.2",
"jazz-react-auth-clerk": "0.7.33-unique.2",
"jazz-run": "0.7.35-unique.2",
"jazz-tools": "0.7.35-unique.2"
},
"changesets": [
"beige-hats-judge",
"dirty-plants-sniff",
"shiny-peaches-grab",
"small-students-buy",
"small-tables-appear",
"smart-mice-camp",
"twelve-lobsters-pull"
]

View File

@@ -0,0 +1,7 @@
---
"jazz-browser": patch
"jazz-react": patch
"jazz-tools": patch
---
Make anonymous auth work better

View File

@@ -0,0 +1,7 @@
---
"jazz-browser": patch
"jazz-react": patch
"jazz-tools": patch
---
Implement guest auth without account

View File

@@ -1,5 +1,33 @@
# jazz-example-chat
## 0.0.80-guest-auth.5
### Patch Changes
- Updated dependencies
- cojson@0.7.35-guest-auth.5
- jazz-react@0.7.35-guest-auth.5
- jazz-react-auth-clerk@0.7.35-guest-auth.5
- jazz-tools@0.7.35-guest-auth.5
## 0.0.80-guest-auth.4
### Patch Changes
- Updated dependencies
- jazz-react@0.7.35-guest-auth.4
- jazz-tools@0.7.35-guest-auth.4
- jazz-react-auth-clerk@0.7.35-guest-auth.4
## 0.0.80-guest-auth.3
### Patch Changes
- Updated dependencies
- jazz-react@0.7.35-guest-auth.3
- jazz-tools@0.7.35-guest-auth.3
- jazz-react-auth-clerk@0.7.33-guest-auth.3
## 0.0.80-unique.2
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-chat-clerk",
"private": true,
"version": "0.0.80-unique.2",
"version": "0.0.80-guest-auth.5",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,12 +1,17 @@
import { CoMap, CoList, co, Group, ID } from "jazz-tools";
import { createJazzReactApp } from "jazz-react";
import { JazzClerkAuth } from "jazz-react-auth-clerk";
import { useJazzClerkAuth } from "jazz-react-auth-clerk";
import { createRoot } from "react-dom/client";
import { useIframeHashRouter } from "hash-slash";
import { ChatScreen } from "./chatScreen.tsx";
import { StrictMode } from "react";
import { ClerkProvider, SignedIn, useAuth } from "@clerk/clerk-react";
import {
ClerkProvider,
SignInButton,
useAuth,
useClerk,
} from "@clerk/clerk-react";
export class Message extends CoMap {
text = co.string;
@@ -17,6 +22,29 @@ export class Chat extends CoList.Of(co.ref(Message)) {}
const Jazz = createJazzReactApp();
export const { useAccount, useCoState } = Jazz;
function AuthAndJazz({ children }: { children: React.ReactNode }) {
const clerk = useClerk();
const [auth, state] = useJazzClerkAuth(clerk);
return (
<>
{state.errors.map((error) => (
<div key={error}>{error}</div>
))}
{auth ? (
<Jazz.Provider
auth={auth}
peer="wss://mesh.jazz.tools/?key=chat-example-jazz-clerk@gcmp.io"
>
{children}
</Jazz.Provider>
) : (
<SignInButton />
)}
</>
);
}
function App() {
const { signOut } = useAuth();
const { me } = useAccount();
@@ -49,14 +77,9 @@ createRoot(document.getElementById("root")!).render(
publishableKey={import.meta.env.VITE_CLERK_PUBLISHABLE_KEY}
afterSignOutUrl="/"
>
<JazzClerkAuth>
<SignedIn>
<Jazz.Provider peer="wss://mesh.jazz.tools/?key=chat-example-jazz-clerk@gcmp.io">
<App />
</Jazz.Provider>
</SignedIn>
<JazzClerkAuth.BasicUI appName="Chat" />
</JazzClerkAuth>
<AuthAndJazz>
<App />
</AuthAndJazz>
</ClerkProvider>
</StrictMode>
);

View File

@@ -1,5 +1,30 @@
# jazz-example-chat
## 0.0.82-guest-auth.5
### Patch Changes
- Updated dependencies
- cojson@0.7.35-guest-auth.5
- jazz-react@0.7.35-guest-auth.5
- jazz-tools@0.7.35-guest-auth.5
## 0.0.82-guest-auth.4
### Patch Changes
- Updated dependencies
- jazz-react@0.7.35-guest-auth.4
- jazz-tools@0.7.35-guest-auth.4
## 0.0.82-guest-auth.3
### Patch Changes
- Updated dependencies
- jazz-react@0.7.35-guest-auth.3
- jazz-tools@0.7.35-guest-auth.3
## 0.0.82-unique.2
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-chat",
"private": true,
"version": "0.0.82-unique.2",
"version": "0.0.82-guest-auth.5",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,5 +1,5 @@
import { CoMap, CoList, co, Group, ID } from "jazz-tools";
import { createJazzReactApp, DemoAuth } from "jazz-react";
import { createJazzReactApp, DemoAuthBasicUI, useDemoAuth } from "jazz-react";
import { createRoot } from "react-dom/client";
import { useIframeHashRouter } from "hash-slash";
import { ChatScreen } from "./chatScreen.tsx";
@@ -28,7 +28,8 @@ function App() {
return (
<div className="flex flex-col items-center justify-between w-screen h-screen p-2 dark:bg-black dark:text-white">
<div className="rounded mb-5 px-2 py-1 text-sm self-end">
{me?.profile?.name} · {/*<button onClick={logOut}>Log Out</button>*/}
{me?.profile?.name} ·{" "}
{/*<button onClick={logOut}>Log Out</button>*/}
</div>
{useIframeHashRouter().route({
"/": () => createChat() as never,
@@ -38,13 +39,27 @@ function App() {
);
}
function AuthAndJazz({ children }: { children: React.ReactNode }) {
const [auth, state] = useDemoAuth();
return (
<Jazz.Provider
auth={auth}
peer="wss://mesh.jazz.tools/?key=chat-example-jazz@gcmp.io"
>
{state.state === "signedIn" ? (
children
) : (
<DemoAuthBasicUI appName="Jazz Chat" state={state} />
)}
</Jazz.Provider>
);
}
createRoot(document.getElementById("root")!).render(
<StrictMode>
<DemoAuth>
<DemoAuth.BasicUI appName="Jazz Chat" />
<Jazz.Provider peer="wss://mesh.jazz.tools/?key=chat-example-jazz@gcmp.io">
<App />
</Jazz.Provider>
</DemoAuth>
<AuthAndJazz>
<App />
</AuthAndJazz>
</StrictMode>
);

View File

@@ -1,5 +1,13 @@
# jazz-example-chat
## 0.0.60-guest-auth.1
### Patch Changes
- Updated dependencies
- cojson@0.7.35-guest-auth.5
- cojson-transport-ws@0.7.35-guest-auth.5
## 0.0.60-unique.0
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-inspector",
"private": true,
"version": "0.0.60-unique.0",
"version": "0.0.60-guest-auth.1",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -6,7 +6,6 @@ import {
RawAccount,
AgentSecret,
RawAccountID,
cojsonInternals,
WasmCrypto,
} from "cojson";
import { createWebSocketPeer } from "cojson-transport-ws";
@@ -69,7 +68,7 @@ export default function CoJsonViewerApp() {
const node = await LocalNode.withLoadedAccount({
accountID: currentAccount.id,
accountSecret: currentAccount.secret,
sessionID: cojsonInternals.newRandomSessionID(
sessionID: crypto.newRandomSessionID(
currentAccount.id,
),
peersToLoadFrom: [wsPeer],

View File

@@ -1,5 +1,31 @@
# jazz-example-pets
## 0.0.100-guest-auth.5
### Patch Changes
- jazz-react@0.7.35-guest-auth.5
- jazz-tools@0.7.35-guest-auth.5
- jazz-browser-media-images@0.7.35-guest-auth.5
## 0.0.100-guest-auth.4
### Patch Changes
- Updated dependencies
- jazz-react@0.7.35-guest-auth.4
- jazz-tools@0.7.35-guest-auth.4
- jazz-browser-media-images@0.7.35-guest-auth.4
## 0.0.100-guest-auth.3
### Patch Changes
- Updated dependencies
- jazz-react@0.7.35-guest-auth.3
- jazz-tools@0.7.35-guest-auth.3
- jazz-browser-media-images@0.7.35-guest-auth.3
## 0.0.100-unique.2
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-pets",
"private": true,
"version": "0.0.100-unique.2",
"version": "0.0.100-guest-auth.5",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -3,20 +3,20 @@ import ReactDOM from "react-dom/client";
import { Link, RouterProvider, createHashRouter } from "react-router-dom";
import "./index.css";
import { createJazzReactApp, PasskeyAuth, usePasskeyAuth } from "jazz-react";
import {
Button,
ThemeProvider,
TitleAndLogo,
} from "./basicComponents/index.ts";
createJazzReactApp,
PasskeyAuthBasicUI,
usePasskeyAuth,
} from "jazz-react";
import { ThemeProvider, TitleAndLogo } from "./basicComponents/index.ts";
import { NewPetPostForm } from "./3_NewPetPostForm.tsx";
import { RatePetPostUI } from "./4_RatePetPostUI.tsx";
import { PetAccount, PetPost } from "./1_schema.ts";
/** Walkthrough: The top-level provider `<WithJazz/>`
/** Walkthrough: The top-level provider `<Jazz.Provider/>`
*
* This shows how to use the top-level provider `<WithJazz/>`,
* This shows how to use the top-level provider `<Jazz.Provider/>`,
* which provides the rest of the app with a `LocalNode` (used through `useJazz` later),
* based on `LocalAuth` that uses PassKeys (aka WebAuthn) to store a user's account secret
* - no backend needed. */
@@ -27,17 +27,31 @@ const Jazz = createJazzReactApp({ AccountSchema: PetAccount });
// eslint-disable-next-line react-refresh/only-export-components
export const { useAccount, useCoState, useAcceptInvite } = Jazz;
function JazzAndAuth({ children }: { children: React.ReactNode }) {
const [passkeyAuth, passKeyState] = usePasskeyAuth({ appName });
return (
<Jazz.Provider
auth={passkeyAuth}
peer="wss://mesh.jazz.tools/?key=pets-example-jazz@gcmp.io"
>
{passKeyState.state === "signedIn" ? (
children
) : (
<PasskeyAuthBasicUI state={passKeyState} />
)}
</Jazz.Provider>
);
}
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<ThemeProvider>
<TitleAndLogo name={appName} />
<div className="flex flex-col h-full items-center justify-start gap-10 pt-10 pb-10 px-5">
<PasskeyAuth appName={appName}>
<Jazz.Provider peer="wss://mesh.jazz.tools/?key=pets-example-jazz@gcmp.io">
<App />
</Jazz.Provider>
<PasskeyAuth.BasicUI />
</PasskeyAuth>
<JazzAndAuth>
<App />
</JazzAndAuth>
</div>
</ThemeProvider>
</React.StrictMode>,
@@ -51,8 +65,6 @@ ReactDOM.createRoot(document.getElementById("root")!).render(
*/
export default function App() {
const {state: passKeyState} = usePasskeyAuth();
const router = createHashRouter([
{
path: "/",
@@ -81,7 +93,7 @@ export default function App() {
<>
<RouterProvider router={router} />
{passKeyState.state === "signedIn" && (
{/* {passKeyState.state === "signedIn" && (
<Button
onClick={() =>
router.navigate("/").then(passKeyState.logOut)
@@ -90,7 +102,7 @@ export default function App() {
>
Log Out
</Button>
)}
)} */}
</>
);
}

View File

@@ -1,5 +1,28 @@
# jazz-example-todo
## 0.0.99-guest-auth.5
### Patch Changes
- jazz-react@0.7.35-guest-auth.5
- jazz-tools@0.7.35-guest-auth.5
## 0.0.99-guest-auth.4
### Patch Changes
- Updated dependencies
- jazz-react@0.7.35-guest-auth.4
- jazz-tools@0.7.35-guest-auth.4
## 0.0.99-guest-auth.3
### Patch Changes
- Updated dependencies
- jazz-react@0.7.35-guest-auth.3
- jazz-tools@0.7.35-guest-auth.3
## 0.0.99-unique.2
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-todo",
"private": true,
"version": "0.0.99-unique.2",
"version": "0.0.99-guest-auth.5",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -6,7 +6,7 @@ import {
} from "react-router-dom";
import "./index.css";
import { createJazzReactApp, PasskeyAuth, usePasskeyAuth } from "jazz-react";
import { createJazzReactApp, PasskeyAuthBasicUI, usePasskeyAuth } from "jazz-react";
import {
Button,
@@ -36,17 +36,31 @@ const Jazz = createJazzReactApp<TodoAccount>({
// eslint-disable-next-line react-refresh/only-export-components
export const { useAccount, useCoState, useAcceptInvite } = Jazz;
function JazzAndAuth({ children }: { children: React.ReactNode }) {
const [passkeyAuth, passKeyState] = usePasskeyAuth({ appName });
return (
<Jazz.Provider
auth={passkeyAuth}
peer="wss://mesh.jazz.tools/?key=todo-example-jazz@gcmp.io"
>
{passKeyState.state === "signedIn" ? (
children
) : (
<PasskeyAuthBasicUI state={passKeyState} />
)}
</Jazz.Provider>
);
}
ReactDOM.createRoot(document.getElementById("root")!).render(
// <React.StrictMode>
<ThemeProvider>
<TitleAndLogo name={appName} />
<div className="flex flex-col h-full items-center justify-start gap-10 pt-10 pb-10 px-5">
<PasskeyAuth appName={appName}>
<Jazz.Provider peer="wss://mesh.jazz.tools/?key=todo-example-jazz@gcmp.io">
<App />
</Jazz.Provider>
<PasskeyAuth.BasicUI />
</PasskeyAuth>
<JazzAndAuth>
<App />
</JazzAndAuth>
</div>
</ThemeProvider>,
// </React.StrictMode>
@@ -61,7 +75,6 @@ ReactDOM.createRoot(document.getElementById("root")!).render(
*/
export default function App() {
// logOut logs out the AuthProvider passed to `<Jazz.Provider/>` above.
const { state: passKeyState } = usePasskeyAuth();
const router = createHashRouter([
{
@@ -90,7 +103,7 @@ export default function App() {
<>
<RouterProvider router={router} />
{passKeyState.state === "signedIn" && (
{/* {passKeyState.state === "signedIn" && (
<Button
onClick={() =>
router.navigate("/").then(passKeyState.logOut)
@@ -99,7 +112,7 @@ export default function App() {
>
Log Out
</Button>
)}
)} */}
</>
);
}

View File

@@ -1,5 +1,12 @@
# cojson-storage-indexeddb
## 0.7.35-guest-auth.5
### Patch Changes
- Updated dependencies
- cojson@0.7.35-guest-auth.5
## 0.7.35-unique.2
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "cojson-storage-indexeddb",
"version": "0.7.35-unique.2",
"version": "0.7.35-guest-auth.5",
"main": "dist/index.js",
"type": "module",
"types": "src/index.ts",

View File

@@ -3,7 +3,6 @@ import {
ControlledAgent,
LocalNode,
WasmCrypto,
cojsonInternals,
} from "cojson";
import { IDBStorage } from "./index.js";
@@ -14,7 +13,7 @@ test.skip("Should be able to initialize and load from empty DB", async () => {
const node = new LocalNode(
new ControlledAgent(agentSecret, Crypto),
cojsonInternals.newRandomSessionID(Crypto.getAgentID(agentSecret)),
Crypto.newRandomSessionID(Crypto.getAgentID(agentSecret)),
Crypto,
);
@@ -34,7 +33,7 @@ test("Should be able to sync data to database and then load that from a new node
const node1 = new LocalNode(
new ControlledAgent(agentSecret, Crypto),
cojsonInternals.newRandomSessionID(Crypto.getAgentID(agentSecret)),
Crypto.newRandomSessionID(Crypto.getAgentID(agentSecret)),
Crypto,
);
@@ -54,7 +53,7 @@ test("Should be able to sync data to database and then load that from a new node
const node2 = new LocalNode(
new ControlledAgent(agentSecret, Crypto),
cojsonInternals.newRandomSessionID(Crypto.getAgentID(agentSecret)),
Crypto.newRandomSessionID(Crypto.getAgentID(agentSecret)),
Crypto,
);

View File

@@ -1,5 +1,12 @@
# cojson-storage-sqlite
## 0.7.35-guest-auth.5
### Patch Changes
- Updated dependencies
- cojson@0.7.35-guest-auth.5
## 0.7.35-unique.2
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "cojson-storage-sqlite",
"type": "module",
"version": "0.7.35-unique.2",
"version": "0.7.35-guest-auth.5",
"main": "dist/index.js",
"types": "src/index.ts",
"license": "MIT",

View File

@@ -1,5 +1,12 @@
# cojson-transport-nodejs-ws
## 0.7.35-guest-auth.5
### Patch Changes
- Updated dependencies
- cojson@0.7.35-guest-auth.5
## 0.7.35-unique.2
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "cojson-transport-ws",
"type": "module",
"version": "0.7.35-unique.2",
"version": "0.7.35-guest-auth.5",
"main": "dist/index.js",
"types": "src/index.ts",
"license": "MIT",

View File

@@ -1,5 +1,11 @@
# cojson
## 0.7.35-guest-auth.5
### Patch Changes
- Move session generation to crypto provider
## 0.7.35-unique.2
### Patch Changes

View File

@@ -5,7 +5,7 @@
"types": "src/index.ts",
"type": "module",
"license": "MIT",
"version": "0.7.35-unique.2",
"version": "0.7.35-guest-auth.5",
"devDependencies": {
"@types/jest": "^29.5.3",
"@typescript-eslint/eslint-plugin": "^6.2.1",

View File

@@ -10,7 +10,6 @@ import {
SignerID,
} from "./crypto/crypto.js";
import { JsonObject, JsonValue } from "./jsonValue.js";
import { base58 } from "@scure/base";
import {
PermissionsDef as RulesetDef,
determineValidTransactions,
@@ -19,7 +18,7 @@ import {
import { RawGroup } from "./coValues/group.js";
import { LocalNode, ResolveAccountAgentError } from "./localNode.js";
import { CoValueKnownState, NewContentMessage } from "./sync.js";
import { AgentID, RawCoID, SessionID, TransactionID } from "./ids.js";
import { RawCoID, SessionID, TransactionID } from "./ids.js";
import { RawAccountID, ControlledAccountOrAgent } from "./coValues/account.js";
import { Stringified, parseJSON, stableStringify } from "./jsonStringify.js";
import { coreToCoValue } from "./coreToCoValue.js";
@@ -53,13 +52,6 @@ export function idforHeader(
return `co_z${hash.slice("shortHash_z".length)}`;
}
export function newRandomSessionID(accountID: RawAccountID | AgentID): SessionID {
return `${accountID}_session_z${base58.encode(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(globalThis as any).crypto.getRandomValues(new Uint8Array(8)),
)}`;
}
type SessionLog = {
transactions: Transaction[];
lastHash?: Hash;

View File

@@ -133,7 +133,6 @@ export class RawControlledAccount<Meta extends AccountMeta = AccountMeta>
}
}
/** @hidden */
export class ControlledAgent implements ControlledAccountOrAgent {
constructor(
public agentSecret: AgentSecret,

View File

@@ -2,6 +2,7 @@ import { JsonValue } from "../jsonValue.js";
import { base58 } from "@scure/base";
import { AgentID, RawCoID, TransactionID } from "../ids.js";
import { Stringified, parseJSON, stableStringify } from "../jsonStringify.js";
import { RawAccountID, SessionID } from "../index.js";
export type SignerSecret = `signerSecret_z${string}`;
export type SignerID = `signer_z${string}`;
@@ -290,6 +291,10 @@ export abstract class CryptoProvider<Blake3State = any> {
}),
)}`;
}
newRandomSessionID(accountID: RawAccountID | AgentID): SessionID {
return `${accountID}_session_z${base58.encode(this.randomBytes(8))}`;
}
}
export type Hash = `hash_z${string}`;

View File

@@ -1,7 +1,6 @@
import {
CoValueCore,
type CoValueUniqueness,
newRandomSessionID,
MAX_RECOMMENDED_TX_SIZE,
idforHeader,
} from "./coValueCore.js";
@@ -65,7 +64,6 @@ import { FileSystem } from "./storage/FileSystem.js";
/** @hidden */
export const cojsonInternals = {
newRandomSessionID,
connectedPeers,
rawCoIDtoBytes,
rawCoIDfromBytes,

View File

@@ -3,7 +3,6 @@ import {
CoValueCore,
CoValueHeader,
CoValueUniqueness,
newRandomSessionID,
} from "./coValueCore.js";
import {
InviteSecret,
@@ -89,7 +88,7 @@ export class LocalNode {
const throwawayAgent = crypto.newRandomAgentSecret();
const setupNode = new LocalNode(
new ControlledAgent(throwawayAgent, crypto),
newRandomSessionID(crypto.getAgentID(throwawayAgent)),
crypto.newRandomSessionID(crypto.getAgentID(throwawayAgent)),
crypto,
);
@@ -97,7 +96,7 @@ export class LocalNode {
const nodeWithAccount = account.core.node.testWithDifferentAccount(
account,
newRandomSessionID(account.id),
crypto.newRandomSessionID(account.id),
);
const accountOnNodeWithAccount =
@@ -183,7 +182,7 @@ export class LocalNode {
}): Promise<LocalNode> {
const loadingNode = new LocalNode(
new ControlledAgent(accountSecret, crypto),
newRandomSessionID(accountID),
crypto.newRandomSessionID(accountID),
crypto,
);
@@ -207,7 +206,7 @@ export class LocalNode {
// since this is all synchronous, we can just swap out nodes for the SyncManager
const node = loadingNode.testWithDifferentAccount(
controlledAccount,
sessionID || newRandomSessionID(accountID),
sessionID || crypto.newRandomSessionID(accountID),
);
node.syncManager = loadingNode.syncManager;
node.syncManager.local = node;
@@ -430,7 +429,7 @@ export class LocalNode {
group.core
.testWithDifferentAccount(
new ControlledAgent(inviteAgentSecret, this.crypto),
newRandomSessionID(inviteAgentID),
this.crypto.newRandomSessionID(inviteAgentID),
)
.getCurrentContent(),
);
@@ -500,7 +499,7 @@ export class LocalNode {
)
.testWithDifferentAccount(
new ControlledAgent(agentSecret, this.crypto),
newRandomSessionID(accountAgentID),
this.crypto.newRandomSessionID(accountAgentID),
)
.getCurrentContent(),
);

View File

@@ -1,5 +1,4 @@
import { expect, test } from "vitest";
import { newRandomSessionID } from "../coValueCore.js";
import { LocalNode } from "../localNode.js";
import { connectedPeers } from "../streamUtils.js";
import { WasmCrypto } from "../crypto/WasmCrypto.js";
@@ -65,7 +64,7 @@ test("Can create account with one node, and then load it on another", async () =
const node2 = await LocalNode.withLoadedAccount({
accountID,
accountSecret,
sessionID: newRandomSessionID(accountID),
sessionID: Crypto.newRandomSessionID(accountID),
peersToLoadFrom: [node1asPeer],
crypto: Crypto,
});

View File

@@ -1,5 +1,4 @@
import { expect, test } from "vitest";
import { newRandomSessionID } from "../coValueCore.js";
import { expectMap } from "../coValue.js";
import {
newGroup,
@@ -27,7 +26,7 @@ test("Added admin can add a third admin to a group", () => {
groupCore
.testWithDifferentAccount(
otherAdmin,
newRandomSessionID(otherAdmin.id),
Crypto.newRandomSessionID(otherAdmin.id),
)
.getCurrentContent(),
);
@@ -47,7 +46,7 @@ test("Added adming can add a third admin to a group (high level)", () => {
group.core
.testWithDifferentAccount(
otherAdmin,
newRandomSessionID(otherAdmin.id),
Crypto.newRandomSessionID(otherAdmin.id),
)
.getCurrentContent(),
);
@@ -73,7 +72,7 @@ test("Admins can't demote other admins in a group", () => {
groupCore
.testWithDifferentAccount(
otherAdmin,
newRandomSessionID(otherAdmin.id),
Crypto.newRandomSessionID(otherAdmin.id),
)
.getCurrentContent(),
);
@@ -89,7 +88,7 @@ test("Admins can't demote other admins in a group (high level)", () => {
group.core
.testWithDifferentAccount(
otherAdmin,
newRandomSessionID(otherAdmin.id),
Crypto.newRandomSessionID(otherAdmin.id),
)
.getCurrentContent(),
);
@@ -114,7 +113,7 @@ test("Admins an add writers to a group, who can't add admins, writers, or reader
const groupAsWriter = expectGroup(
groupCore
.testWithDifferentAccount(writer, newRandomSessionID(writer.id))
.testWithDifferentAccount(writer, Crypto.newRandomSessionID(writer.id))
.getCurrentContent(),
);
@@ -142,7 +141,7 @@ test("Admins an add writers to a group, who can't add admins, writers, or reader
const groupAsWriter = expectGroup(
group.core
.testWithDifferentAccount(writer, newRandomSessionID(writer.id))
.testWithDifferentAccount(writer, Crypto.newRandomSessionID(writer.id))
.getCurrentContent(),
);
@@ -174,7 +173,7 @@ test("Admins can add readers to a group, who can't add admins, writers, or reade
const groupAsReader = expectGroup(
groupCore
.testWithDifferentAccount(reader, newRandomSessionID(reader.id))
.testWithDifferentAccount(reader, Crypto.newRandomSessionID(reader.id))
.getCurrentContent(),
);
@@ -203,7 +202,7 @@ test("Admins can add readers to a group, who can't add admins, writers, or reade
const groupAsReader = expectGroup(
group.core
.testWithDifferentAccount(reader, newRandomSessionID(reader.id))
.testWithDifferentAccount(reader, Crypto.newRandomSessionID(reader.id))
.getCurrentContent(),
);
@@ -267,7 +266,7 @@ test("Writers can write to an object that is owned by their group", () => {
const childObjectAsWriter = childObject.testWithDifferentAccount(
writer,
newRandomSessionID(writer.id),
Crypto.newRandomSessionID(writer.id),
);
const childContentAsWriter = expectMap(
@@ -289,7 +288,7 @@ test("Writers can write to an object that is owned by their group (high level)",
const childObjectAsWriter = expectMap(
childObject.core
.testWithDifferentAccount(writer, newRandomSessionID(writer.id))
.testWithDifferentAccount(writer, Crypto.newRandomSessionID(writer.id))
.getCurrentContent(),
);
@@ -315,7 +314,7 @@ test("Readers can not write to an object that is owned by their group", () => {
const childObjectAsReader = childObject.testWithDifferentAccount(
reader,
newRandomSessionID(reader.id),
Crypto.newRandomSessionID(reader.id),
);
const childContentAsReader = expectMap(
@@ -337,7 +336,7 @@ test("Readers can not write to an object that is owned by their group (high leve
const childObjectAsReader = expectMap(
childObject.core
.testWithDifferentAccount(reader, newRandomSessionID(reader.id))
.testWithDifferentAccount(reader, Crypto.newRandomSessionID(reader.id))
.getCurrentContent(),
);
@@ -442,7 +441,7 @@ test("Admins can set group read key and then writers can use it to create and re
const childObjectAsWriter = childObject.testWithDifferentAccount(
writer,
newRandomSessionID(writer.id),
Crypto.newRandomSessionID(writer.id),
);
expect(childObject.getCurrentReadKey().secret).toEqual(readKey);
@@ -466,7 +465,7 @@ test("Admins can set group read key and then writers can use it to create and re
const childObjectAsWriter = expectMap(
childObject.core
.testWithDifferentAccount(writer, newRandomSessionID(writer.id))
.testWithDifferentAccount(writer, Crypto.newRandomSessionID(writer.id))
.getCurrentContent(),
);
@@ -526,7 +525,7 @@ test("Admins can set group read key and then use it to create private transactio
const childObjectAsReader = childObject.testWithDifferentAccount(
reader,
newRandomSessionID(reader.id),
Crypto.newRandomSessionID(reader.id),
);
expect(childObjectAsReader.getCurrentReadKey().secret).toEqual(readKey);
@@ -552,7 +551,7 @@ test("Admins can set group read key and then use it to create private transactio
const childContentAsReader = expectMap(
childObject.core
.testWithDifferentAccount(reader, newRandomSessionID(reader.id))
.testWithDifferentAccount(reader, Crypto.newRandomSessionID(reader.id))
.getCurrentContent(),
);
@@ -613,7 +612,7 @@ test("Admins can set group read key and then use it to create private transactio
const childObjectAsReader1 = childObject.testWithDifferentAccount(
reader1,
newRandomSessionID(reader1.id),
Crypto.newRandomSessionID(reader1.id),
);
expect(childObjectAsReader1.getCurrentReadKey().secret).toEqual(readKey);
@@ -638,7 +637,7 @@ test("Admins can set group read key and then use it to create private transactio
const childObjectAsReader2 = childObject.testWithDifferentAccount(
reader2,
newRandomSessionID(reader2.id),
Crypto.newRandomSessionID(reader2.id),
);
expect(childObjectAsReader2.getCurrentReadKey().secret).toEqual(readKey);
@@ -666,7 +665,7 @@ test("Admins can set group read key and then use it to create private transactio
const childContentAsReader1 = expectMap(
childObject.core
.testWithDifferentAccount(reader1, newRandomSessionID(reader1.id))
.testWithDifferentAccount(reader1, Crypto.newRandomSessionID(reader1.id))
.getCurrentContent(),
);
@@ -676,7 +675,7 @@ test("Admins can set group read key and then use it to create private transactio
const childContentAsReader2 = expectMap(
childObject.core
.testWithDifferentAccount(reader2, newRandomSessionID(reader2.id))
.testWithDifferentAccount(reader2, Crypto.newRandomSessionID(reader2.id))
.getCurrentContent(),
);
@@ -845,7 +844,7 @@ test("Admins can set group read key, make a private transaction in an owned obje
const childObjectAsReader = childObject.testWithDifferentAccount(
reader,
newRandomSessionID(reader.id),
Crypto.newRandomSessionID(reader.id),
);
expect(childObjectAsReader.getCurrentReadKey().secret).toEqual(readKey2);
@@ -883,7 +882,7 @@ test("Admins can set group read key, make a private transaction in an owned obje
const childContentAsReader = expectMap(
childObject.core
.testWithDifferentAccount(reader, newRandomSessionID(reader.id))
.testWithDifferentAccount(reader, Crypto.newRandomSessionID(reader.id))
.getCurrentContent(),
);
@@ -959,7 +958,7 @@ test("Admins can set group read rey, make a private transaction in an owned obje
let childObjectAsReader = childObject.testWithDifferentAccount(
reader,
newRandomSessionID(reader.id),
Crypto.newRandomSessionID(reader.id),
);
expect(
@@ -968,7 +967,7 @@ test("Admins can set group read rey, make a private transaction in an owned obje
let childObjectAsReader2 = childObject.testWithDifferentAccount(
reader,
newRandomSessionID(reader.id),
Crypto.newRandomSessionID(reader.id),
);
expect(
@@ -1024,11 +1023,11 @@ test("Admins can set group read rey, make a private transaction in an owned obje
// TODO: make sure these instances of coValues sync between each other so this isn't necessary?
childObjectAsReader = childObject.testWithDifferentAccount(
reader,
newRandomSessionID(reader.id),
Crypto.newRandomSessionID(reader.id),
);
childObjectAsReader2 = childObject.testWithDifferentAccount(
reader2,
newRandomSessionID(reader2.id),
Crypto.newRandomSessionID(reader2.id),
);
expect(
@@ -1072,7 +1071,7 @@ test("Admins can set group read rey, make a private transaction in an owned obje
const childContentAsReader2 = expectMap(
childObject.core
.testWithDifferentAccount(reader2, newRandomSessionID(reader2.id))
.testWithDifferentAccount(reader2, Crypto.newRandomSessionID(reader2.id))
.getCurrentContent(),
);
@@ -1083,7 +1082,7 @@ test("Admins can set group read rey, make a private transaction in an owned obje
expect(
expectMap(
childObject.core
.testWithDifferentAccount(reader, newRandomSessionID(reader.id))
.testWithDifferentAccount(reader, Crypto.newRandomSessionID(reader.id))
.getCurrentContent(),
).get("foo3"),
).toBeUndefined();
@@ -1151,7 +1150,7 @@ test("Admins can create an adminInvite, which can add an admin", () => {
groupCore
.testWithDifferentAccount(
new ControlledAgent(inviteSecret, Crypto),
newRandomSessionID(inviteID),
Crypto.newRandomSessionID(inviteID),
)
.getCurrentContent(),
);
@@ -1198,7 +1197,7 @@ test("Admins can create an adminInvite, which can add an admin (high-level)", as
const nodeAsInvitedAdmin = node.testWithDifferentAccount(
new ControlledAgent(invitedAdminSecret, Crypto),
newRandomSessionID(invitedAdminID),
Crypto.newRandomSessionID(invitedAdminID),
);
await nodeAsInvitedAdmin.acceptInvite(group.id, inviteSecret);
@@ -1261,7 +1260,7 @@ test("Admins can create a writerInvite, which can add a writer", () => {
groupCore
.testWithDifferentAccount(
new ControlledAgent(inviteSecret, Crypto),
newRandomSessionID(inviteID),
Crypto.newRandomSessionID(inviteID),
)
.getCurrentContent(),
);
@@ -1308,7 +1307,7 @@ test("Admins can create a writerInvite, which can add a writer (high-level)", as
const nodeAsInvitedWriter = node.testWithDifferentAccount(
new ControlledAgent(invitedWriterSecret, Crypto),
newRandomSessionID(invitedWriterID),
Crypto.newRandomSessionID(invitedWriterID),
);
await nodeAsInvitedWriter.acceptInvite(group.id, inviteSecret);
@@ -1364,7 +1363,7 @@ test("Admins can create a readerInvite, which can add a reader", () => {
groupCore
.testWithDifferentAccount(
new ControlledAgent(inviteSecret, Crypto),
newRandomSessionID(inviteID),
Crypto.newRandomSessionID(inviteID),
)
.getCurrentContent(),
);
@@ -1401,7 +1400,7 @@ test("Admins can create a readerInvite, which can add a reader (high-level)", as
const nodeAsInvitedReader = node.testWithDifferentAccount(
new ControlledAgent(invitedReaderSecret, Crypto),
newRandomSessionID(invitedReaderID),
Crypto.newRandomSessionID(invitedReaderID),
);
await nodeAsInvitedReader.acceptInvite(group.id, inviteSecret);
@@ -1457,7 +1456,7 @@ test("WriterInvites can not invite admins", () => {
groupCore
.testWithDifferentAccount(
new ControlledAgent(inviteSecret, Crypto),
newRandomSessionID(inviteID),
Crypto.newRandomSessionID(inviteID),
)
.getCurrentContent(),
);
@@ -1511,7 +1510,7 @@ test("ReaderInvites can not invite admins", () => {
groupCore
.testWithDifferentAccount(
new ControlledAgent(inviteSecret, Crypto),
newRandomSessionID(inviteID),
Crypto.newRandomSessionID(inviteID),
)
.getCurrentContent(),
);
@@ -1565,7 +1564,7 @@ test("ReaderInvites can not invite writers", () => {
groupCore
.testWithDifferentAccount(
new ControlledAgent(inviteSecret, Crypto),
newRandomSessionID(inviteID),
Crypto.newRandomSessionID(inviteID),
)
.getCurrentContent(),
);
@@ -1610,7 +1609,7 @@ test("Can give read permission to 'everyone'", () => {
childObject
.testWithDifferentAccount(
newAccount,
newRandomSessionID(newAccount.currentAgentID()._unsafeUnwrap()),
Crypto.newRandomSessionID(newAccount.currentAgentID()._unsafeUnwrap()),
)
.getCurrentContent(),
);
@@ -1639,7 +1638,7 @@ test("Can give read permissions to 'everyone' (high-level)", async () => {
childObject.core
.testWithDifferentAccount(
new ControlledAgent(Crypto.newRandomAgentSecret(), Crypto),
newRandomSessionID(newAccount.currentAgentID()._unsafeUnwrap()),
Crypto.newRandomSessionID(newAccount.currentAgentID()._unsafeUnwrap()),
)
.getCurrentContent(),
);
@@ -1680,7 +1679,7 @@ test("Can give write permission to 'everyone'", async () => {
childObject
.testWithDifferentAccount(
newAccount,
newRandomSessionID(newAccount.currentAgentID()._unsafeUnwrap()),
Crypto.newRandomSessionID(newAccount.currentAgentID()._unsafeUnwrap()),
)
.getCurrentContent(),
);
@@ -1715,7 +1714,7 @@ test("Can give write permissions to 'everyone' (high-level)", async () => {
childObject.core
.testWithDifferentAccount(
newAccount,
newRandomSessionID(newAccount.currentAgentID()._unsafeUnwrap()),
Crypto.newRandomSessionID(newAccount.currentAgentID()._unsafeUnwrap()),
)
.getCurrentContent(),
);

View File

@@ -9,7 +9,6 @@ import { RawAccountID } from "../coValues/account.js";
import { stableStringify } from "../jsonStringify.js";
import { WasmCrypto } from "../crypto/WasmCrypto.js";
import { expectMap } from "../coValue.js";
import { newRandomSessionID } from "../coValueCore.js";
const Crypto = await WasmCrypto.create();
@@ -713,7 +712,7 @@ test.skip("When replaying creation and transactions of a coValue as new content,
crashOnClose: true,
});
const node2 = new LocalNode(admin, newRandomSessionID(admin.id), Crypto);
const node2 = new LocalNode(admin, Crypto.newRandomSessionID(admin.id), Crypto);
const [inRx2, inTx2] = newQueuePair();
const [outRx2, outTx2] = newQueuePair();
@@ -864,7 +863,7 @@ test("Can sync a coValue through a server to another client", async () => {
client1.syncManager.addPeer(serverAsPeerForClient1);
server.syncManager.addPeer(client1AsPeer);
const client2 = new LocalNode(admin, newRandomSessionID(admin.id), Crypto);
const client2 = new LocalNode(admin, Crypto.newRandomSessionID(admin.id), Crypto);
const [serverAsPeerForClient2, client2AsPeer] = connectedPeers(
"serverFor2",
@@ -916,7 +915,7 @@ test("Can sync a coValue with private transactions through a server to another c
client1.syncManager.addPeer(serverAsPeer);
server.syncManager.addPeer(client1AsPeer);
const client2 = new LocalNode(admin, newRandomSessionID(admin.id), Crypto);
const client2 = new LocalNode(admin, client1.crypto.newRandomSessionID(admin.id), Crypto);
const [serverAsOtherPeer, client2AsPeer] = await connectedPeers(
"server",
@@ -1064,7 +1063,7 @@ test("If we start loading a coValue before connecting to a peer that has it, it
const map = group.createMap();
map.set("hello", "world", "trusting");
const node2 = new LocalNode(admin, newRandomSessionID(admin.id), Crypto);
const node2 = new LocalNode(admin, Crypto.newRandomSessionID(admin.id), Crypto);
const [node1asPeer, node2asPeer] = await connectedPeers("peer1", "peer2", {
peer1role: "server",

View File

@@ -1,5 +1,4 @@
import { expect } from "vitest";
import { newRandomSessionID } from "../coValueCore.js";
import { LocalNode } from "../localNode.js";
import { expectGroup } from "../typeUtils/expectGroup.js";
import { ControlledAgent } from "../coValues/account.js";
@@ -14,7 +13,7 @@ export function randomAnonymousAccountAndSessionID(): [
] {
const agentSecret = Crypto.newRandomAgentSecret();
const sessionID = newRandomSessionID(Crypto.getAgentID(agentSecret));
const sessionID = Crypto.newRandomSessionID(Crypto.getAgentID(agentSecret));
return [new ControlledAgent(agentSecret, Crypto), sessionID];
}

View File

@@ -1,5 +1,30 @@
# jazz-browser-media-images
## 0.7.35-guest-auth.5
### Patch Changes
- Updated dependencies
- cojson@0.7.35-guest-auth.5
- jazz-browser@0.7.35-guest-auth.5
- jazz-tools@0.7.35-guest-auth.5
## 0.7.35-guest-auth.4
### Patch Changes
- Updated dependencies
- jazz-browser@0.7.35-guest-auth.4
- jazz-tools@0.7.35-guest-auth.4
## 0.7.33-guest-auth.2
### Patch Changes
- Updated dependencies
- jazz-browser@0.7.35-guest-auth.3
- jazz-tools@0.7.35-guest-auth.3
## 0.7.33-unique.1
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "jazz-browser-auth-clerk",
"version": "0.7.33-unique.1",
"version": "0.7.35-guest-auth.5",
"type": "module",
"main": "dist/index.js",
"types": "src/index.ts",
@@ -9,8 +9,7 @@
"peerDependencies": {
"cojson": "workspace:*",
"jazz-tools": "workspace:*",
"jazz-browser": "workspace:*",
"@clerk/types": "^3.65.3"
"jazz-browser": "workspace:*"
},
"scripts": {
"lint": "eslint . --ext ts,tsx",

View File

@@ -1,11 +1,24 @@
import { Account, AuthMethod, AuthResult, ID } from "jazz-tools";
import type { LoadedClerk } from '@clerk/types';
import { AgentSecret } from "cojson";
export type MinimalClerkClient = {
user: {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
unsafeMetadata: Record<string, any>;
fullName: string | null;
username: string | null;
id: string;
update: (args: {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
unsafeMetadata: Record<string, any>;
}) => Promise<unknown>;
} | null | undefined;
}
export class BrowserClerkAuth implements AuthMethod {
constructor(
public driver: BrowserClerkAuth.Driver,
private readonly clerkClient: LoadedClerk,
private readonly clerkClient: MinimalClerkClient
) {}
async start(): Promise<AuthResult> {

View File

@@ -1,5 +1,28 @@
# jazz-browser-media-images
## 0.7.35-guest-auth.5
### Patch Changes
- jazz-browser@0.7.35-guest-auth.5
- jazz-tools@0.7.35-guest-auth.5
## 0.7.35-guest-auth.4
### Patch Changes
- Updated dependencies
- jazz-browser@0.7.35-guest-auth.4
- jazz-tools@0.7.35-guest-auth.4
## 0.7.35-guest-auth.3
### Patch Changes
- Updated dependencies
- jazz-browser@0.7.35-guest-auth.3
- jazz-tools@0.7.35-guest-auth.3
## 0.7.35-unique.2
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "jazz-browser-media-images",
"version": "0.7.35-unique.2",
"version": "0.7.35-guest-auth.5",
"type": "module",
"main": "dist/index.js",
"types": "src/index.ts",

View File

@@ -1,5 +1,31 @@
# jazz-browser
## 0.7.35-guest-auth.5
### Patch Changes
- Updated dependencies
- cojson@0.7.35-guest-auth.5
- cojson-storage-indexeddb@0.7.35-guest-auth.5
- cojson-transport-ws@0.7.35-guest-auth.5
- jazz-tools@0.7.35-guest-auth.5
## 0.7.35-guest-auth.4
### Patch Changes
- Make anonymous auth work better
- Updated dependencies
- jazz-tools@0.7.35-guest-auth.4
## 0.7.35-guest-auth.3
### Patch Changes
- Implement guest auth without account
- Updated dependencies
- jazz-tools@0.7.35-guest-auth.3
## 0.7.35-unique.2
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "jazz-browser",
"version": "0.7.35-unique.2",
"version": "0.7.35-guest-auth.5",
"type": "module",
"main": "dist/index.js",
"types": "src/index.ts",

View File

@@ -11,14 +11,15 @@ import {
CryptoProvider,
AuthMethod,
createJazzContext,
AnonymousJazzAgent,
} from "jazz-tools";
import { RawAccountID, LSMStorage } from "cojson";
import { OPFSFilesystem } from "./OPFSFilesystem.js";
import { IDBStorage } from "cojson-storage-indexeddb";
import { createWebSocketPeer } from "cojson-transport-ws";
export { BrowserDemoAuth } from './auth/DemoAuth.js'
export { BrowserPasskeyAuth } from './auth/PasskeyAuth.js'
export { BrowserPassphraseAuth } from './auth/PassphraseAuth.js'
export { BrowserDemoAuth } from "./auth/DemoAuth.js";
export { BrowserPasskeyAuth } from "./auth/PasskeyAuth.js";
export { BrowserPassphraseAuth } from "./auth/PassphraseAuth.js";
/** @category Context Creation */
export type BrowserContext<Acc extends Account> = {
@@ -27,65 +28,96 @@ export type BrowserContext<Acc extends Account> = {
done: () => void;
};
/** @category Context Creation */
export async function createJazzBrowserContext<Acc extends Account>({
auth,
AccountSchema = Account as unknown as CoValueClass<Acc> & {
fromNode: (typeof Account)["fromNode"];
},
peer: peerAddr,
reconnectionTimeout: initialReconnectionTimeout = 500,
storage = "indexedDB",
crypto: customCrypto,
}: {
export type BrowserGuestContext = {
guest: AnonymousJazzAgent;
done: () => void;
};
export type BrowserContextOptions<Acc extends Account> = {
auth: AuthMethod;
AccountSchema: CoValueClass<Acc> & {
fromNode: (typeof Account)["fromNode"];
};
} & BaseBrowserContextOptions;
export type BaseBrowserContextOptions = {
peer: `wss://${string}` | `ws://${string}`;
reconnectionTimeout?: number;
storage?: "indexedDB" | "singleTabOPFS";
crypto?: CryptoProvider;
}): Promise<BrowserContext<Acc>> {
const crypto = customCrypto || (await WasmCrypto.create());
};
/** @category Context Creation */
export async function createJazzBrowserContext<Acc extends Account>(
options: BrowserContextOptions<Acc>,
): Promise<BrowserContext<Acc>>;
export async function createJazzBrowserContext(
options: BaseBrowserContextOptions,
): Promise<BrowserGuestContext>;
export async function createJazzBrowserContext<Acc extends Account>(
options: BrowserContextOptions<Acc> | BaseBrowserContextOptions,
): Promise<BrowserContext<Acc> | BrowserGuestContext>;
export async function createJazzBrowserContext<Acc extends Account>(
options: BrowserContextOptions<Acc> | BaseBrowserContextOptions,
): Promise<BrowserContext<Acc> | BrowserGuestContext> {
const crypto = options.crypto || (await WasmCrypto.create());
const firstWsPeer = createWebSocketPeer({
websocket: new WebSocket(peerAddr),
id: peerAddr + "@" + new Date().toISOString(),
websocket: new WebSocket(options.peer),
id: options.peer + "@" + new Date().toISOString(),
role: "server",
});
let shouldTryToReconnect = true;
let currentReconnectionTimeout = initialReconnectionTimeout;
let currentReconnectionTimeout = options.reconnectionTimeout || 500;
function onOnline() {
console.log("Online, resetting reconnection timeout");
currentReconnectionTimeout = initialReconnectionTimeout;
currentReconnectionTimeout = options.reconnectionTimeout || 500;
}
window.addEventListener("online", onOnline);
const { account, done } = await createJazzContext({
AccountSchema,
auth,
crypto: await WasmCrypto.create(),
peersToLoadFrom: [
storage === "indexedDB"
? await IDBStorage.asPeer()
: await LSMStorage.asPeer({
fs: new OPFSFilesystem(crypto),
// trace: true,
}),
firstWsPeer,
],
sessionProvider: provideBroswerLockSession,
});
const context =
"auth" in options
? await createJazzContext({
AccountSchema: options.AccountSchema,
auth: options.auth,
crypto: await WasmCrypto.create(),
peersToLoadFrom: [
options.storage === "singleTabOPFS"
? await LSMStorage.asPeer({
fs: new OPFSFilesystem(crypto),
// trace: true,
})
: await IDBStorage.asPeer(),
firstWsPeer,
],
sessionProvider: provideBroswerLockSession,
})
: await createJazzContext({
crypto: await WasmCrypto.create(),
peersToLoadFrom: [
options.storage === "singleTabOPFS"
? await LSMStorage.asPeer({
fs: new OPFSFilesystem(crypto),
// trace: true,
})
: await IDBStorage.asPeer(),
firstWsPeer,
],
});
const node =
"account" in context
? context.account._raw.core.node
: context.agent.node;
async function websocketReconnectLoop() {
while (shouldTryToReconnect) {
if (
Object.keys(account._raw.core.node.syncManager.peers).some(
(peerId) => peerId.includes(peerAddr),
Object.keys(node.syncManager.peers).some((peerId) =>
peerId.includes(options.peer),
)
) {
// TODO: this might drain battery, use listeners instead
@@ -114,10 +146,10 @@ export async function createJazzBrowserContext<Acc extends Account>({
);
});
account._raw.core.node.syncManager.addPeer(
node.syncManager.addPeer(
createWebSocketPeer({
websocket: new WebSocket(peerAddr),
id: peerAddr + "@" + new Date().toISOString(),
websocket: new WebSocket(options.peer),
id: options.peer + "@" + new Date().toISOString(),
role: "server",
}),
);
@@ -127,14 +159,23 @@ export async function createJazzBrowserContext<Acc extends Account>({
void websocketReconnectLoop();
return {
me: account,
done: () => {
shouldTryToReconnect = false;
window.removeEventListener("online", onOnline);
done();
},
};
return "account" in context
? {
me: context.account,
done: () => {
shouldTryToReconnect = false;
window.removeEventListener("online", onOnline);
context.done();
},
}
: {
guest: context.agent,
done: () => {
shouldTryToReconnect = false;
window.removeEventListener("online", onOnline);
context.done();
},
};
}
/** @category Auth Providers */
@@ -142,7 +183,10 @@ export type SessionProvider = (
accountID: ID<Account> | AgentID,
) => Promise<SessionID>;
export function provideBroswerLockSession(accountID: ID<Account> | AgentID) {
export function provideBroswerLockSession(
accountID: ID<Account> | AgentID,
crypto: CryptoProvider,
) {
let sessionDone!: () => void;
const donePromise = new Promise<void>((resolve) => {
sessionDone = resolve;
@@ -166,7 +210,7 @@ export function provideBroswerLockSession(accountID: ID<Account> | AgentID) {
const sessionID =
localStorage[accountID + "_" + idx] ||
cojsonInternals.newRandomSessionID(
crypto.newRandomSessionID(
accountID as RawAccountID | AgentID,
);
localStorage[accountID + "_" + idx] = sessionID;

View File

@@ -1,5 +1,28 @@
# jazz-autosub
## 0.7.35-guest-auth.5
### Patch Changes
- Updated dependencies
- cojson@0.7.35-guest-auth.5
- cojson-transport-ws@0.7.35-guest-auth.5
- jazz-tools@0.7.35-guest-auth.5
## 0.7.35-guest-auth.4
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.35-guest-auth.4
## 0.7.35-guest-auth.3
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.35-guest-auth.3
## 0.7.35-unique.2
### Patch Changes

View File

@@ -5,7 +5,7 @@
"types": "src/index.ts",
"type": "module",
"license": "MIT",
"version": "0.7.35-unique.2",
"version": "0.7.35-guest-auth.5",
"dependencies": {
"cojson": "workspace:*",
"cojson-transport-ws": "workspace:*",

View File

@@ -1,5 +1,33 @@
# jazz-browser-media-images
## 0.7.35-guest-auth.5
### Patch Changes
- Updated dependencies
- cojson@0.7.35-guest-auth.5
- jazz-browser-auth-clerk@0.7.35-guest-auth.5
- jazz-react@0.7.35-guest-auth.5
- jazz-tools@0.7.35-guest-auth.5
## 0.7.35-guest-auth.4
### Patch Changes
- Updated dependencies
- jazz-react@0.7.35-guest-auth.4
- jazz-tools@0.7.35-guest-auth.4
- jazz-browser-auth-clerk@0.7.35-guest-auth.4
## 0.7.33-guest-auth.3
### Patch Changes
- Updated dependencies
- jazz-react@0.7.35-guest-auth.3
- jazz-tools@0.7.35-guest-auth.3
- jazz-browser-auth-clerk@0.7.33-guest-auth.2
## 0.7.33-unique.2
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "jazz-react-auth-clerk",
"version": "0.7.33-unique.2",
"version": "0.7.35-guest-auth.5",
"type": "module",
"main": "dist/index.js",
"types": "src/index.tsx",
@@ -11,7 +11,6 @@
"jazz-tools": "workspace:*",
"jazz-react": "workspace:*",
"jazz-browser-auth-clerk": "workspace:*",
"@clerk/clerk-react": "^4.0.0",
"react": "^18.2.0"
},
"scripts": {

View File

@@ -1,86 +1,29 @@
import { SignedOut, SignInButton, useClerk } from "@clerk/clerk-react";
import { BrowserClerkAuth } from "jazz-browser-auth-clerk";
import { AuthMethodCtx } from "jazz-react";
import { useState, useMemo, ReactNode, useContext, createContext } from "react";
import { BrowserClerkAuth, MinimalClerkClient } from "jazz-browser-auth-clerk";
import { useState, useMemo } from "react";
export const JazzClerkAuthCtx = createContext<{
errors: string[];
}>({
errors: [],
});
export function JazzClerkAuth({ children }: { children: ReactNode }) {
const clerk = useClerk();
const [errors, setErrors] = useState<string[]>([]);
export function useJazzClerkAuth(clerk: MinimalClerkClient & {
signOut: () => Promise<unknown>;
}) {
const [state, setState] = useState<{ errors: string[] }>({ errors: [] });
const authMethod = useMemo(() => {
return new BrowserClerkAuth(
{
onError: (error) => {
void clerk.signOut();
setErrors((errors) => [...errors, error.toString()]);
if (clerk.user) {
return new BrowserClerkAuth(
{
onError: (error) => {
void clerk.signOut();
setState((state) => ({
...state,
errors: [...state.errors, error.toString()],
}));
},
},
},
clerk,
);
}, [clerk]);
clerk,
);
} else {
return undefined;
}
}, [clerk.user]);
return (
<JazzClerkAuthCtx.Provider value={{ errors }}>
<AuthMethodCtx.Provider value={authMethod}>
{children}
</AuthMethodCtx.Provider>
</JazzClerkAuthCtx.Provider>
);
}
const JazzClerkBasicUI = ({ appName }: { appName: string }) => {
const { errors } = useContext(JazzClerkAuthCtx);
const darkMode =
typeof window !== "undefined"
? window.matchMedia("(prefers-color-scheme: dark)").matches
: false;
return (
<SignedOut>
<div
style={{
width: "100vw",
height: "100vh",
display: "flex",
alignItems: "center",
justifyContent: "center",
...(darkMode ? { background: "#000" } : {}),
}}
>
<div
style={{
width: "18rem",
display: "flex",
flexDirection: "column",
gap: "2rem",
}}
>
<h1
style={{
color: darkMode ? "#fff" : "#000",
textAlign: "center",
}}
>
{appName}
</h1>
<SignInButton />
{errors.map((error) => (
<div key={error} style={{ color: "red" }}>
{error}
</div>
))}
</div>
</div>
</SignedOut>
);
};
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace JazzClerkAuth {
export const BasicUI = JazzClerkBasicUI;
return [authMethod, state] as const;
}

View File

@@ -1,5 +1,32 @@
# jazz-react
## 0.7.35-guest-auth.5
### Patch Changes
- Updated dependencies
- cojson@0.7.35-guest-auth.5
- jazz-browser@0.7.35-guest-auth.5
- jazz-tools@0.7.35-guest-auth.5
## 0.7.35-guest-auth.4
### Patch Changes
- Make anonymous auth work better
- Updated dependencies
- jazz-browser@0.7.35-guest-auth.4
- jazz-tools@0.7.35-guest-auth.4
## 0.7.35-guest-auth.3
### Patch Changes
- Implement guest auth without account
- Updated dependencies
- jazz-browser@0.7.35-guest-auth.3
- jazz-tools@0.7.35-guest-auth.3
## 0.7.35-unique.2
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "jazz-react",
"version": "0.7.35-unique.2",
"version": "0.7.35-guest-auth.5",
"type": "module",
"main": "dist/index.js",
"types": "src/index.ts",

View File

@@ -1,10 +1,9 @@
import { createContext, ReactNode, useContext, useMemo, useState } from "react";
import { useMemo, useState } from "react";
import { BrowserDemoAuth } from "jazz-browser";
import { Account, ID } from "jazz-tools";
import { AgentSecret } from "cojson";
import { AuthMethodCtx } from "./auth.js";
type DemoAuthState =
type DemoAuthState = (
| {
state: "uninitialized";
}
@@ -20,28 +19,23 @@ type DemoAuthState =
| {
state: "signedIn";
logOut: () => void;
};
const DemoAuthStateCtx = createContext<{
state: DemoAuthState;
}
) & {
errors: string[];
}>({
state: { state: "uninitialized" },
errors: [],
});
};
/** @category Auth Providers */
export function DemoAuth({
children,
export function useDemoAuth({
seedAccounts,
}: {
children: ReactNode;
seedAccounts?: {
[name: string]: { accountID: ID<Account>; accountSecret: AgentSecret };
};
}) {
const [errors, setErrors] = useState<string[]>([]);
const [state, setState] = useState<DemoAuthState>({ state: "loading" });
} = {}) {
const [state, setState] = useState<DemoAuthState>({
state: "loading",
errors: [],
});
const authMethod = useMemo(() => {
return new BrowserDemoAuth(
@@ -52,30 +46,33 @@ export function DemoAuth({
signUp,
existingUsers,
logInAs,
errors: [],
});
},
onSignedIn: ({ logOut }) => {
setState({ state: "signedIn", logOut });
setState({ state: "signedIn", logOut, errors: [] });
},
onError: (error) => {
setErrors((errors) => [...errors, error.toString()]);
setState((current) => ({
...current,
errors: [...current.errors, error.toString()],
}));
},
},
seedAccounts,
);
}, [seedAccounts]);
return (
<DemoAuthStateCtx.Provider value={{ state, errors }}>
<AuthMethodCtx.Provider value={authMethod}>
{children}
</AuthMethodCtx.Provider>
</DemoAuthStateCtx.Provider>
);
return [authMethod, state] as const;
}
const DemoAuthBasicUI = ({ appName }: { appName: string }) => {
const { state, errors } = useContext(DemoAuthStateCtx);
export const DemoAuthBasicUI = ({
appName,
state,
}: {
appName: string;
state: DemoAuthState;
}) => {
const [username, setUsername] = useState<string>("");
const darkMode =
typeof window !== "undefined"
@@ -112,7 +109,7 @@ const DemoAuthBasicUI = ({ appName }: { appName: string }) => {
>
{appName}
</h1>
{errors.map((error) => (
{state.errors.map((error) => (
<div key={error} style={{ color: "red" }}>
{error}
</div>
@@ -186,9 +183,3 @@ const DemoAuthBasicUI = ({ appName }: { appName: string }) => {
</div>
);
};
/** @category Auth Providers */
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace DemoAuth {
export const BasicUI = DemoAuthBasicUI;
}

View File

@@ -1,8 +1,7 @@
import { useMemo, useState, ReactNode, createContext, useContext } from "react";
import { useMemo, useState } from "react";
import { BrowserPasskeyAuth } from "jazz-browser";
import { AuthMethodCtx } from "./auth.js";
export type PasskeyAuthState =
export type PasskeyAuthState = (
| { state: "uninitialized" }
| { state: "loading" }
| {
@@ -10,29 +9,22 @@ export type PasskeyAuthState =
logIn: () => void;
signUp: (username: string) => void;
}
| { state: "signedIn"; logOut: () => void };
const PasskeyAuthStateCtx = createContext<{
state: PasskeyAuthState;
| { state: "signedIn"; logOut: () => void }
) & {
errors: string[];
}>({
state: { state: "uninitialized" },
errors: [],
});
};
/** @category Auth Providers */
export function PasskeyAuth({
children,
export function usePasskeyAuth({
appName,
appHostname,
}: {
children: ReactNode;
appName: string;
appHostname?: string;
}) {
const [errors, setErrors] = useState<string[]>([]);
const [state, setState] = useState<PasskeyAuthState>({
state: "loading",
errors: [],
});
const authMethod = useMemo(() => {
@@ -43,6 +35,7 @@ export function PasskeyAuth({
state: "ready",
logIn: next.logIn,
signUp: next.signUp,
errors: [],
});
},
onSignedIn(next) {
@@ -50,12 +43,16 @@ export function PasskeyAuth({
state: "signedIn",
logOut: () => {
next.logOut();
setState({ state: "loading" });
setState({ state: "loading", errors: [] });
},
errors: [],
});
},
onError(error) {
setErrors((errors) => [...errors, error.toString()]);
setState((state) => ({
...state,
errors: [...state.errors, error.toString()],
}));
},
},
appName,
@@ -63,21 +60,10 @@ export function PasskeyAuth({
);
}, [appName, appHostname]);
return (
<PasskeyAuthStateCtx.Provider value={{ state, errors }}>
<AuthMethodCtx.Provider value={authMethod}>
{children}
</AuthMethodCtx.Provider>
</PasskeyAuthStateCtx.Provider>
);
return [authMethod, state] as const;
}
export const usePasskeyAuth = () => {
return useContext(PasskeyAuthStateCtx);
};
const PasskeyAuthBasicUI = () => {
const { state, errors } = usePasskeyAuth();
export const PasskeyAuthBasicUI = ({ state }: { state: PasskeyAuthState }) => {
const [username, setUsername] = useState<string>("");
if (state.state !== "ready") {
@@ -104,9 +90,9 @@ const PasskeyAuthBasicUI = () => {
gap: "2rem",
}}
>
{errors.length > 0 && (
{state.errors.length > 0 && (
<div style={{ color: "red" }}>
{errors.map((error, index) => (
{state.errors.map((error, index) => (
<div key={index}>{error}</div>
))}
</div>
@@ -164,14 +150,3 @@ const PasskeyAuthBasicUI = () => {
</div>
);
};
/** @category Auth Providers */
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace PasskeyAuth {
export type Component = (props: {
loading: boolean;
logIn: () => void;
signUp: (username: string) => void;
}) => ReactNode;
export const BasicUI = PasskeyAuthBasicUI;
}

View File

@@ -1,44 +1,34 @@
import { useMemo, useState, ReactNode, createContext, useContext } from "react";
import { useMemo, useState } from "react";
import { BrowserPassphraseAuth } from "jazz-browser";
import { generateMnemonic } from "@scure/bip39";
import { cojsonInternals } from "cojson";
import { AuthMethodCtx } from "./auth.js";
export type PassphraseAuthState =
| { state: "uninitialized" }
(| { state: "uninitialized" }
| { state: "loading" }
| {
state: "ready";
logIn: (passphrase: string) => void;
signUp: (username: string, passphrase: string) => void;
generateRandomPassphrase: () => string;
}
| { state: "signedIn"; logOut: () => void };
const PassphraseAuthStateCtx = createContext<{
state: PassphraseAuthState;
| { state: "signedIn"; logOut: () => void }) & {
errors: string[];
generateRandomPassphrase: () => string;
}>({
state: { state: "uninitialized" },
errors: [],
generateRandomPassphrase: () => "",
});
};
/** @category Auth Providers */
export function PassphraseAuth({
children,
export function usePassphraseAuth({
appName,
appHostname,
wordlist,
}: {
children: ReactNode;
appName: string;
appHostname?: string;
wordlist: string[];
}) {
const [errors, setErrors] = useState<string[]>([]);
const [state, setState] = useState<PassphraseAuthState>({
state: "loading",
errors: [],
});
const generateRandomPassphrase = () => {
@@ -56,6 +46,8 @@ export function PassphraseAuth({
state: "ready",
logIn: next.logIn,
signUp: next.signUp,
generateRandomPassphrase,
errors: [],
});
},
onSignedIn(next) {
@@ -63,12 +55,13 @@ export function PassphraseAuth({
state: "signedIn",
logOut: () => {
next.logOut();
setState({ state: "loading" });
setState({ state: "loading", errors: [] });
},
errors: [],
});
},
onError(error) {
setErrors((errors) => [...errors, error.toString()]);
setState((state) => ({ ...state, errors: [...state.errors, error.toString()] }));
},
},
wordlist,
@@ -77,17 +70,10 @@ export function PassphraseAuth({
);
}, [appName, appHostname, wordlist]);
return (
<PassphraseAuthStateCtx.Provider value={{ state, errors, generateRandomPassphrase }}>
<AuthMethodCtx.Provider value={authMethod}>
{children}
</AuthMethodCtx.Provider>
</PassphraseAuthStateCtx.Provider>
);
return [authMethod, state] as const;
}
const PassphraseAuthBasicUI = () => {
const { state, errors, generateRandomPassphrase } = useContext(PassphraseAuthStateCtx);
export const PassphraseAuthBasicUI = (state: PassphraseAuthState) => {
const [username, setUsername] = useState<string>("");
const [passphrase, setPassphrase] = useState<string>("");
const [loginPassphrase, setLoginPassphrase] = useState<string>("");
@@ -116,9 +102,9 @@ const PassphraseAuthBasicUI = () => {
gap: "2rem",
}}
>
{errors.length > 0 && (
{state.errors.length > 0 && (
<div style={{ color: "red" }}>
{errors.map((error, index) => (
{state.errors.map((error, index) => (
<div key={index}>{error}</div>
))}
</div>
@@ -153,7 +139,7 @@ const PassphraseAuthBasicUI = () => {
<button
type="button"
onClick={(e) => {
setPassphrase(generateRandomPassphrase());
setPassphrase(state.generateRandomPassphrase());
e.preventDefault();
}}
style={{
@@ -229,10 +215,4 @@ const PassphraseAuthBasicUI = () => {
</div>
</div>
);
};
/** @category Auth Providers */
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace PassphraseAuth {
export const BasicUI = PassphraseAuthBasicUI;
}
};

View File

@@ -1,13 +1,3 @@
import React from "react";
import { AuthMethod } from "jazz-tools";
export type AuthState = "loading" | "ready" | "signedIn";
/** @category Auth Methods */
export const AuthMethodCtx = React.createContext<AuthMethod | undefined>(
undefined,
);
export { DemoAuth } from "./DemoAuth.js";
export { PasskeyAuth, usePasskeyAuth } from "./PasskeyAuth.js";
export { PassphraseAuth } from "./PassphraseAuth.js";
export { useDemoAuth, DemoAuthBasicUI } from "./DemoAuth.js";
export { usePasskeyAuth, PasskeyAuthBasicUI } from "./PasskeyAuth.js";
export { usePassphraseAuth, PassphraseAuthBasicUI } from "./PassphraseAuth.js";

View File

@@ -1,5 +1,7 @@
import React, { useContext, useEffect, useState } from "react";
import React, { useEffect, useState } from "react";
import {
BrowserContext,
BrowserGuestContext,
consumeInviteLinkFromWindowLocation,
createJazzBrowserContext,
} from "jazz-browser";
@@ -7,6 +9,8 @@ import {
import {
Account,
AccountClass,
AnonymousJazzAgent,
AuthMethod,
CoValue,
CoValueClass,
DeeplyLoaded,
@@ -14,41 +18,48 @@ import {
ID,
subscribeToCoValue,
} from "jazz-tools";
import { AuthMethodCtx } from "./auth/auth.js";
/** @category Context & Hooks */
export function createJazzReactApp<Acc extends Account>({
AccountSchema = Account as unknown as AccountClass<Acc>,
}: {
AccountSchema?: AccountClass<Acc>;
} = {}): JazzReactContext<Acc> {
const JazzContext = React.createContext<Acc | undefined>(undefined);
} = {}): JazzReactApp<Acc> {
const JazzContext = React.createContext<
BrowserContext<Acc> | BrowserGuestContext | undefined
>(undefined);
function Provider({
children,
auth,
peer,
storage,
}: {
children: React.ReactNode;
auth: AuthMethod | "guest";
peer: `wss://${string}` | `ws://${string}`;
storage?: "indexedDB" | "singleTabOPFS";
}) {
const [me, setMe] = useState<Acc | undefined>();
const auth = useContext(AuthMethodCtx);
if (!auth) {
throw new Error("Jazz.Provider must be used within an Auth Method Provider");
}
const [ctx, setCtx] = useState<
BrowserContext<Acc> | BrowserGuestContext | undefined
>();
useEffect(() => {
const promiseWithDoneCallback = createJazzBrowserContext<Acc>({
AccountSchema,
auth,
peer,
storage,
}).then(({ me, done }) => {
setMe(me);
return done;
const promiseWithDoneCallback = createJazzBrowserContext<Acc>(
auth === "guest"
? {
peer,
storage,
}
: {
AccountSchema,
auth: auth,
peer,
storage,
},
).then((context) => {
setCtx(context);
return context.done;
});
return () => {
@@ -56,10 +67,14 @@ export function createJazzReactApp<Acc extends Account>({
};
}, [AccountSchema, auth, peer, storage]);
return <JazzContext.Provider value={me}>{children}</JazzContext.Provider>;
return (
<JazzContext.Provider value={ctx}>
{ctx && children}
</JazzContext.Provider>
);
}
function useAccount(): { me: Acc | undefined };
function useAccount(): { me: Acc };
function useAccount<D extends DepthsIn<Acc>>(
depth: D,
): { me: DeeplyLoaded<Acc, D> | undefined };
@@ -68,17 +83,59 @@ export function createJazzReactApp<Acc extends Account>({
): { me: Acc | DeeplyLoaded<Acc, D> | undefined } {
const context = React.useContext(JazzContext);
if (!context) {
throw new Error("useAccount must be used within a JazzProvider");
}
if (!("me" in context)) {
throw new Error(
"useAccount can't be used in a JazzProvider with auth === 'guest' - consider using useAccountOrGuest()",
);
}
const me = useCoState<Acc, D>(
context?.constructor as CoValueClass<Acc>,
context?.id,
context?.me.constructor as CoValueClass<Acc>,
context?.me.id,
depth,
);
return {
me: depth === undefined ? me || context : me,
me: depth === undefined ? me || context.me : me,
};
}
function useAccountOrGuest(): { me: Acc | AnonymousJazzAgent };
function useAccountOrGuest<D extends DepthsIn<Acc>>(
depth: D,
): { me: DeeplyLoaded<Acc, D> | undefined | AnonymousJazzAgent };
function useAccountOrGuest<D extends DepthsIn<Acc>>(
depth?: D,
): { me: Acc | DeeplyLoaded<Acc, D> | undefined | AnonymousJazzAgent } {
const context = React.useContext(JazzContext);
if (!context) {
throw new Error(
"useAccountOrGuest must be used within a JazzProvider",
);
}
const contextMe = "me" in context ? context.me : undefined;
const me = useCoState<Acc, D>(
contextMe?.constructor as CoValueClass<Acc>,
contextMe?.id,
depth,
);
if ("me" in context) {
return {
me: depth === undefined ? me || context.me : me,
};
} else {
return { me: context.guest };
}
}
function useCoState<V extends CoValue, D>(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Schema: CoValueClass<V>,
@@ -88,15 +145,25 @@ export function createJazzReactApp<Acc extends Account>({
const [state, setState] = useState<{
value: DeeplyLoaded<V, D> | undefined;
}>({ value: undefined });
const me = React.useContext(JazzContext);
const context = React.useContext(JazzContext);
if (!context) {
throw new Error("useCoState must be used within a JazzProvider");
}
useEffect(() => {
if (!id || !me) return;
if (!id) return;
return subscribeToCoValue(Schema, id, me, depth, (value) => {
setState({ value });
});
}, [Schema, id, me]);
return subscribeToCoValue(
Schema,
id,
"me" in context ? context.me : context.guest,
depth,
(value) => {
setState({ value });
},
);
}, [Schema, id, context]);
return state.value;
}
@@ -110,12 +177,25 @@ export function createJazzReactApp<Acc extends Account>({
onAccept: (projectID: ID<V>) => void;
forValueHint?: string;
}): void {
const me = React.useContext(JazzContext);
const context = React.useContext(JazzContext);
if (!context) {
throw new Error(
"useAcceptInvite must be used within a JazzProvider",
);
}
if (!("me" in context)) {
throw new Error(
"useAcceptInvite can't be used in a JazzProvider with auth === 'guest'.",
);
}
useEffect(() => {
if (!me) return;
if (!context) return;
const result = consumeInviteLinkFromWindowLocation({
as: me,
as: context.me,
invitedObjectSchema,
forValueHint,
});
@@ -131,23 +211,25 @@ export function createJazzReactApp<Acc extends Account>({
return {
Provider,
useAccount,
useAccountOrGuest,
useCoState,
useAcceptInvite,
};
}
/** @category Context & Hooks */
export interface JazzReactContext<Acc extends Account> {
export interface JazzReactApp<Acc extends Account> {
/** @category Provider Component */
Provider: React.FC<{
children: React.ReactNode;
auth: AuthMethod | "guest";
peer: `wss://${string}` | `ws://${string}`;
storage?: "indexedDB" | "singleTabOPFS";
}>;
/** @category Hooks */
useAccount(): {
me: Acc | undefined;
me: Acc;
};
/** @category Hooks */
useAccount<D extends DepthsIn<Acc>>(
@@ -156,6 +238,15 @@ export interface JazzReactContext<Acc extends Account> {
me: DeeplyLoaded<Acc, D> | undefined;
};
/** @category Hooks */
useAccountOrGuest(): {
me: Acc | AnonymousJazzAgent;
};
useAccountOrGuest<D extends DepthsIn<Acc>>(
depth: D,
): {
me: DeeplyLoaded<Acc, D> | undefined | AnonymousJazzAgent;
};
/** @category Hooks */
useCoState<V extends CoValue, D>(
// eslint-disable-next-line @typescript-eslint/no-explicit-any

View File

@@ -1,5 +1,28 @@
# jazz-autosub
## 0.7.35-guest-auth.5
### Patch Changes
- Updated dependencies
- cojson@0.7.35-guest-auth.5
- cojson-transport-ws@0.7.35-guest-auth.5
- jazz-tools@0.7.35-guest-auth.5
## 0.7.35-guest-auth.4
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.35-guest-auth.4
## 0.7.35-guest-auth.3
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.35-guest-auth.3
## 0.7.35-unique.2
### Patch Changes

View File

@@ -3,7 +3,7 @@
"bin": "./dist/index.js",
"type": "module",
"license": "MIT",
"version": "0.7.35-unique.2",
"version": "0.7.35-guest-auth.5",
"scripts": {
"lint": "eslint . --ext ts,tsx",
"format": "prettier --write './src/**/*.{ts,tsx}'",

View File

@@ -1,5 +1,24 @@
# jazz-autosub
## 0.7.35-guest-auth.5
### Patch Changes
- Updated dependencies
- cojson@0.7.35-guest-auth.5
## 0.7.35-guest-auth.4
### Patch Changes
- Make anonymous auth work better
## 0.7.35-guest-auth.3
### Patch Changes
- Implement guest auth without account
## 0.7.35-unique.2
### Patch Changes

View File

@@ -5,7 +5,7 @@
"types": "./src/index.ts",
"type": "module",
"license": "MIT",
"version": "0.7.35-unique.2",
"version": "0.7.35-guest-auth.5",
"dependencies": {
"cojson": "workspace:*",
"fast-check": "^3.17.2"

View File

@@ -1,5 +1,5 @@
import { LocalNode, cojsonInternals } from "cojson";
import type {
import {
AgentSecret,
CoID,
CryptoProvider,
@@ -21,6 +21,7 @@ import {
RefIfCoValue,
DeeplyLoaded,
DepthsIn,
AnonymousJazzAgent,
} from "../internal.js";
import {
Group,
@@ -67,8 +68,13 @@ export class Account extends CoValueBase implements CoValue {
get _owner(): Account {
return this as Account;
}
get _loadedAs(): Account {
return this.isMe ? this : Account.fromNode(this._raw.core.node);
get _loadedAs(): Account | AnonymousJazzAgent {
if (this.isMe) return this;
if (this._raw.core.node.account instanceof RawAccount) {
return Account.fromRaw(this._raw.core.node.account);
}
return new AnonymousJazzAgent(this._raw.core.node);
}
declare profile: Profile | null;

View File

@@ -14,6 +14,7 @@ import type {
} from "../internal.js";
import {
Account,
AnonymousJazzAgent,
Group,
ItemsSym,
Ref,
@@ -163,7 +164,10 @@ export class CoList<Item = any> extends Array<Item> implements CoValue {
}
get _loadedAs() {
return Account.fromNode(this._raw.core.node);
if (this._raw.core.node.account instanceof RawAccount) {
return Account.fromRaw(this._raw.core.node.account);
}
return new AnonymousJazzAgent(this._raw.core.node);
}
static get [Symbol.species]() {

View File

@@ -15,6 +15,7 @@ import type {
DepthsIn,
DeeplyLoaded,
CoValueClass,
AnonymousJazzAgent,
} from "../internal.js";
import {
Account,
@@ -194,11 +195,6 @@ export class CoMap extends CoValueBase implements CoValue {
};
}
/** @internal */
get _loadedAs() {
return Account.fromNode(this._raw.core.node);
}
/** @internal */
constructor(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -445,9 +441,9 @@ export class CoMap extends CoValueBase implements CoValue {
static findUnique<M extends CoMap>(
this: CoValueClass<M>,
unique: CoValueUniqueness['uniqueness'],
unique: CoValueUniqueness["uniqueness"],
ownerID: ID<Account> | ID<Group>,
as: Account | Group,
as: Account | Group | AnonymousJazzAgent,
) {
const header = {
type: "comap" as const,
@@ -458,10 +454,9 @@ export class CoMap extends CoValueBase implements CoValue {
meta: null,
uniqueness: unique,
};
return cojsonInternals.idforHeader(
header,
as._raw.core.crypto,
) as ID<M>;
const crypto =
as._type === "Anonymous" ? as.node.crypto : as._raw.core.crypto;
return cojsonInternals.idforHeader(header, crypto) as ID<M>;
}
/**

View File

@@ -20,6 +20,7 @@ import type {
CoValueClass,
DeeplyLoaded,
DepthsIn,
AnonymousJazzAgent,
} from "../internal.js";
import {
ItemsSym,
@@ -77,13 +78,21 @@ export class CoStream<Item = any> extends CoValueBase implements CoValue {
[key: ID<Account>]: CoStreamEntry<Item>;
get byMe(): CoStreamEntry<Item> | undefined {
return this[this._loadedAs.id];
if (this._loadedAs._type === "Account") {
return this[this._loadedAs.id];
} else {
return undefined;
}
}
perSession!: {
[key: SessionID]: CoStreamEntry<Item>;
};
get inCurrentSession(): CoStreamEntry<Item> | undefined {
return this.perSession[this._loadedAs.sessionID!];
if (this._loadedAs._type === "Account") {
return this.perSession[this._loadedAs.sessionID!];
} else {
return undefined;
}
}
constructor(
@@ -233,7 +242,7 @@ function entryFromRawEntry<Item>(
at: Date;
value: JsonValue;
},
loadedAs: Account,
loadedAs: Account | AnonymousJazzAgent,
accountID: ID<Account> | undefined,
itemField: Schema,
): Omit<CoStreamEntry<Item>, "all"> {

View File

@@ -8,6 +8,7 @@ import {
Ref,
inspect,
subscriptionsScopes,
AnonymousJazzAgent,
} from "../internal.js";
import { fulfillsDepth } from "./deepLoading.js";
@@ -35,7 +36,7 @@ export interface CoValue {
/** @category Internals */
_raw: RawCoValue;
/** @internal */
readonly _loadedAs: Account;
readonly _loadedAs: Account | AnonymousJazzAgent;
/** @category Stringifying & Inspection */
// eslint-disable-next-line @typescript-eslint/no-explicit-any
toJSON(key?: string, seenAbove?: ID<CoValue>[]): any[] | object | string;
@@ -86,7 +87,10 @@ export class CoValueBase implements CoValue {
/** @private */
get _loadedAs() {
return Account.fromNode(this._raw.core.node);
if (this._raw.core.node.account instanceof RawAccount) {
return Account.fromRaw(this._raw.core.node.account);
}
return new AnonymousJazzAgent(this._raw.core.node);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -134,7 +138,7 @@ export class CoValueBase implements CoValue {
export function loadCoValue<V extends CoValue, Depth>(
cls: CoValueClass<V>,
id: ID<V>,
as: Account,
as: Account | AnonymousJazzAgent,
depth: Depth & DepthsIn<V>,
): Promise<DeeplyLoaded<V, Depth> | undefined> {
return new Promise((resolve) => {
@@ -170,7 +174,7 @@ export function ensureCoValueLoaded<V extends CoValue, Depth>(
export function subscribeToCoValue<V extends CoValue, Depth>(
cls: CoValueClass<V>,
id: ID<V>,
as: Account,
as: Account | AnonymousJazzAgent,
depth: Depth & DepthsIn<V>,
listener: (value: DeeplyLoaded<V, Depth>) => void,
onUnavailable?: () => void,

View File

@@ -1,7 +1,7 @@
import {
AgentSecret,
CoID,
cojsonInternals,
ControlledAgent,
CryptoProvider,
LocalNode,
Peer,
@@ -48,38 +48,75 @@ export const fixedCredentialsAuth = (credentials: {
};
};
export async function randomSessionProvider(accountID: ID<Account>) {
export async function randomSessionProvider(
accountID: ID<Account>,
crypto: CryptoProvider,
) {
return {
sessionID: cojsonInternals.newRandomSessionID(
sessionID: crypto.newRandomSessionID(
accountID as unknown as RawAccountID,
),
sessionDone: () => {},
};
}
export async function createJazzContext<Acc extends Account>({
AccountSchema = Account as unknown as AccountClass<Acc>,
auth,
sessionProvider,
peersToLoadFrom,
crypto,
}: {
type ContextParamsWithAuth<Acc extends Account> = {
AccountSchema?: AccountClass<Acc>;
auth: AuthMethod;
sessionProvider: (
accountID: ID<Account>,
crypto: CryptoProvider,
) => Promise<{ sessionID: SessionID; sessionDone: () => void }>;
} & BaseContextParams;
type BaseContextParams = {
peersToLoadFrom: Peer[];
crypto: CryptoProvider;
}): Promise<{ account: Acc; done: () => void }> {
};
export async function createJazzContext<Acc extends Account>({
AccountSchema,
auth,
sessionProvider,
peersToLoadFrom,
crypto,
}: ContextParamsWithAuth<Acc>): Promise<{ account: Acc; done: () => void }>;
export async function createJazzContext({
peersToLoadFrom,
crypto,
}: BaseContextParams): Promise<{ agent: AnonymousJazzAgent; done: () => void }>;
export async function createJazzContext<Acc extends Account>(
options: ContextParamsWithAuth<Acc> | BaseContextParams,
): Promise<
| { account: Acc; done: () => void }
| { agent: AnonymousJazzAgent; done: () => void }
>
export async function createJazzContext<Acc extends Account>(
options: ContextParamsWithAuth<Acc> | BaseContextParams,
): Promise<
| { account: Acc; done: () => void }
| { agent: AnonymousJazzAgent; done: () => void }
> {
// eslint-disable-next-line no-constant-condition
while (true) {
if (!("auth" in options)) {
return createAnonymousJazzContext({
peersToLoadFrom: options.peersToLoadFrom,
crypto: options.crypto,
});
}
const { auth, sessionProvider, peersToLoadFrom, crypto } = options;
const AccountSchema =
options.AccountSchema ?? (Account as unknown as AccountClass<Acc>);
const authResult = await auth.start(crypto);
if (authResult.type === "existing") {
try {
const { sessionID, sessionDone } = await sessionProvider(
authResult.credentials.accountID,
crypto,
);
try {
@@ -160,3 +197,34 @@ export async function createJazzContext<Acc extends Account>({
}
}
}
export class AnonymousJazzAgent {
_type = "Anonymous" as const;
constructor(public node: LocalNode) {}
}
export async function createAnonymousJazzContext({
peersToLoadFrom,
crypto,
}: {
peersToLoadFrom: Peer[];
crypto: CryptoProvider;
}): Promise<{ agent: AnonymousJazzAgent; done: () => void }> {
const agentSecret = crypto.newRandomAgentSecret();
const rawAgent = new ControlledAgent(agentSecret, crypto);
const node = new LocalNode(
rawAgent,
crypto.newRandomSessionID(rawAgent.id),
crypto,
);
for (const peer of peersToLoadFrom) {
node.syncManager.addPeer(peer);
}
return {
agent: new AnonymousJazzAgent(node),
done: () => {},
};
}

View File

@@ -1,6 +1,7 @@
import type { CoID, RawCoValue } from "cojson";
import type {
Account,
AnonymousJazzAgent,
CoValue,
ID,
RefEncoded,
@@ -19,7 +20,7 @@ const TRACE_ACCESSES = false;
export class Ref<out V extends CoValue> {
constructor(
readonly id: ID<V>,
readonly controlledAccount: Account,
readonly controlledAccount: Account | AnonymousJazzAgent,
readonly schema: RefEncoded<V>,
) {
if (!isRefEncoded(schema)) {
@@ -28,9 +29,11 @@ export class Ref<out V extends CoValue> {
}
get value() {
const raw = this.controlledAccount._raw.core.node.getLoaded(
this.id as unknown as CoID<RawCoValue>,
);
const node =
"node" in this.controlledAccount
? this.controlledAccount.node
: this.controlledAccount._raw.core.node;
const raw = node.getLoaded(this.id as unknown as CoID<RawCoValue>);
if (raw) {
let value = refCache.get(raw);
if (value) {
@@ -48,7 +51,11 @@ export class Ref<out V extends CoValue> {
private async loadHelper(options?: {
onProgress: (p: number) => void;
}): Promise<V | "unavailable"> {
const raw = await this.controlledAccount._raw.core.node.load(
const node =
"node" in this.controlledAccount
? this.controlledAccount.node
: this.controlledAccount._raw.core.node;
const raw = await node.load(
this.id as unknown as CoID<RawCoValue>,
options?.onProgress,
);
@@ -117,7 +124,7 @@ export class Ref<out V extends CoValue> {
export function makeRefs<Keys extends string | number>(
getIdForKey: (key: Keys) => ID<CoValue> | undefined,
getKeysWithIds: () => Keys[],
controlledAccount: Account,
controlledAccount: Account | AnonymousJazzAgent,
refSchemaForKey: (key: Keys) => RefEncoded<CoValue>,
): { [K in Keys]: Ref<CoValue> } & {
[Symbol.iterator]: () => IterableIterator<Ref<CoValue>>;

View File

@@ -5,6 +5,7 @@ import type {
ID,
CoValueClass,
CoValueFromRaw,
AnonymousJazzAgent,
} from "../internal.js";
export const subscriptionsScopes = new WeakMap<
@@ -17,7 +18,7 @@ const TRACE_INVALIDATIONS = false;
export class SubscriptionScope<Root extends CoValue> {
scopeID: string = `scope-${Math.random().toString(36).slice(2)}`;
subscriber: Account;
subscriber: Account | AnonymousJazzAgent;
entries = new Map<
ID<CoValue>,
| { state: "loading"; immediatelyUnsub?: boolean }
@@ -89,32 +90,34 @@ export class SubscriptionScope<Root extends CoValue> {
immediatelyUnsub: false,
} as const;
this.entries.set(accessedOrSetId, loadingEntry);
void this.subscriber._raw.core.node
.loadCoValueCore(accessedOrSetId)
.then((core) => {
if (
loadingEntry.state === "loading" &&
loadingEntry.immediatelyUnsub
) {
return;
}
if (core !== "unavailable") {
const entry = {
state: "loaded" as const,
rawUnsub: () => {}, // placeholder
};
this.entries.set(accessedOrSetId, entry);
const node =
this.subscriber._type === "Account"
? this.subscriber._raw.core.node
: this.subscriber.node;
void node.loadCoValueCore(accessedOrSetId).then((core) => {
if (
loadingEntry.state === "loading" &&
loadingEntry.immediatelyUnsub
) {
return;
}
if (core !== "unavailable") {
const entry = {
state: "loaded" as const,
rawUnsub: () => {}, // placeholder
};
this.entries.set(accessedOrSetId, entry);
const rawUnsub = core.subscribe((rawUpdate) => {
// console.log("ref update", this.scopeID, accessedOrSetId, JSON.stringify(rawUpdate))
if (!rawUpdate) return;
this.invalidate(accessedOrSetId);
this.scheduleUpdate();
});
const rawUnsub = core.subscribe((rawUpdate) => {
// console.log("ref update", this.scopeID, accessedOrSetId, JSON.stringify(rawUpdate))
if (!rawUpdate) return;
this.invalidate(accessedOrSetId);
this.scheduleUpdate();
});
entry.rawUnsub = rawUnsub;
}
});
entry.rawUnsub = rawUnsub;
}
});
}
}

View File

@@ -34,4 +34,6 @@ export {
type AuthResult,
createJazzContext,
fixedCredentialsAuth,
AnonymousJazzAgent,
createAnonymousJazzContext,
} from "./internal.js";

49
pnpm-lock.yaml generated
View File

@@ -671,9 +671,6 @@ importers:
packages/jazz-browser-auth-clerk:
dependencies:
'@clerk/types':
specifier: ^3.65.3
version: 3.65.3
cojson:
specifier: workspace:*
version: link:../cojson
@@ -765,9 +762,6 @@ importers:
packages/jazz-react-auth-clerk:
dependencies:
'@clerk/clerk-react':
specifier: ^4.0.0
version: 4.32.3(react@18.2.0)
cojson:
specifier: workspace:*
version: link:../cojson
@@ -928,12 +922,6 @@ packages:
'@changesets/write@0.3.1':
resolution: {integrity: sha512-SyGtMXzH3qFqlHKcvFY2eX+6b0NGiFcNav8AFsYwy5l8hejOeoeTDemu5Yjmke2V5jpzY+pBvM0vCCQ3gdZpfw==}
'@clerk/clerk-react@4.32.3':
resolution: {integrity: sha512-qLKazE3ERUcQwzIaRzLH7BszConlTRsDZuW5hDrFA4T3KNv5ReaFP7W2nW+dvfCz1UC6RC3E4622k1fmfXC6+A==}
engines: {node: '>=14'}
peerDependencies:
react: '>=16'
'@clerk/clerk-react@5.4.1':
resolution: {integrity: sha512-r0P5kEFWh3Cyl+PrNWM7oW/+0aPaB0Zp80MzQLTvpSSGgemYoDHgybmc2Twc4rS0GOJ455y5uSB1smTh0G+ztw==}
engines: {node: '>=18.17.0'}
@@ -941,14 +929,6 @@ packages:
react: '>=18 || >=19.0.0-beta'
react-dom: '>=18 || >=19.0.0-beta'
'@clerk/shared@1.4.1':
resolution: {integrity: sha512-3rlZy0Hadnb1dw6x+4MGEC7dpZLlIVY3mZTwWRRS4CILWowVAccwfW84paN2XNlM12lJgMc+w66WNdw19XFtpg==}
peerDependencies:
react: '>=16'
peerDependenciesMeta:
react:
optional: true
'@clerk/shared@2.5.1':
resolution: {integrity: sha512-RslzZvjolG8ToZlegV8XPEkINQ4XM23lO2xS6ZtJEeGzRzchkpEBUOYywjG6aGtJDByWBY2OxyrPaa5rwx1XEg==}
engines: {node: '>=18.17.0'}
@@ -961,10 +941,6 @@ packages:
react-dom:
optional: true
'@clerk/types@3.65.3':
resolution: {integrity: sha512-q2BvCBK3H9SxLyoSsybAFTcbW6s50JPY8JACFqzenBbByOyolgIXPJNE0MStUUnNc7lRrPDkoLuZZf71X7JqIw==}
engines: {node: '>=14'}
'@clerk/types@4.13.1':
resolution: {integrity: sha512-AfFTA1Du0pog62CSl23Y8NNDN+iHwXdUQAp/sQ/LbE+/wYBOMSaV5d+cS5F4HX+/uzXgKsc3BpCNI/Th3CLNHg==}
engines: {node: '>=18.17.0'}
@@ -3324,10 +3300,6 @@ packages:
js-cookie@2.2.1:
resolution: {integrity: sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==}
js-cookie@3.0.1:
resolution: {integrity: sha512-+0rgsUXZu4ncpPxRL+lNEptWMOWl9etvPHc/koSRp6MPwpRYAhmk0dUG00J4bxVV3r9uUzfo24wW0knS07SKSw==}
engines: {node: '>=12'}
js-cookie@3.0.5:
resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==}
engines: {node: '>=14'}
@@ -5171,13 +5143,6 @@ snapshots:
human-id: 1.0.2
prettier: 2.8.8
'@clerk/clerk-react@4.32.3(react@18.2.0)':
dependencies:
'@clerk/shared': 1.4.1(react@18.2.0)
'@clerk/types': 3.65.3
react: 18.2.0
tslib: 2.4.1
'@clerk/clerk-react@5.4.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
dependencies:
'@clerk/shared': 2.5.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
@@ -5186,14 +5151,6 @@ snapshots:
react-dom: 18.2.0(react@18.2.0)
tslib: 2.4.1
'@clerk/shared@1.4.1(react@18.2.0)':
dependencies:
glob-to-regexp: 0.4.1
js-cookie: 3.0.1
swr: 2.2.0(react@18.2.0)
optionalDependencies:
react: 18.2.0
'@clerk/shared@2.5.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
dependencies:
'@clerk/types': 4.13.1
@@ -5205,10 +5162,6 @@ snapshots:
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
'@clerk/types@3.65.3':
dependencies:
csstype: 3.1.1
'@clerk/types@4.13.1':
dependencies:
csstype: 3.1.1
@@ -7727,8 +7680,6 @@ snapshots:
js-cookie@2.2.1: {}
js-cookie@3.0.1: {}
js-cookie@3.0.5: {}
js-tokens@4.0.0: {}