Compare commits

...

17 Commits

Author SHA1 Message Date
Guido D'Orsi
eda1588907 Merge pull request #1527 from garden-co/changeset-release/main
Version Packages
2025-02-27 09:56:16 +01:00
github-actions[bot]
b14e0bfe24 Version Packages 2025-02-26 17:58:41 +00:00
Guido D'Orsi
87aa43b46b Merge pull request #1507 from garden-co/jazz-747-passphrase-auth-restore-support-for-rerolling-the-passphrase
feat(PasskeyAuth): support random passphrase generation
2025-02-26 18:54:42 +01:00
Guido D'Orsi
b93ce9fb7e chore: rever to use property initializers 2025-02-26 18:54:32 +01:00
Guido D'Orsi
a7d83e1c10 feat(passphrase-example): inline the UI and implement the passphrase reroll flow 2025-02-26 16:29:05 +01:00
Guido D'Orsi
76a693da15 Update packages/jazz-tools/src/tests/PassphraseAuth.test.ts
Co-authored-by: Giordano Ricci <me@giordanoricci.com>
2025-02-26 16:23:02 +01:00
pax
df2f021cfd Merge pull request #1509 from garden-co/changeset-release/main
Version Packages
2025-02-26 16:25:01 +02:00
github-actions[bot]
50b15d2d1d Version Packages 2025-02-26 13:43:02 +00:00
pax
48bf7cb188 Merge pull request #1519 from garden-co/create-jazz-app-dev-deps-handling
fix(create-jazz-app): handle workspace devDependencies
2025-02-26 15:40:53 +02:00
pax-k
e2e0af34b5 chore: changeset 2025-02-26 15:04:57 +02:00
Trisha Lim
6a5bcd3063 Fix missing og meta tags 2025-02-26 19:34:15 +07:00
pax-k
bf7e62ec76 fix(create-jazz-app): handle workspace devDependencies 2025-02-26 11:58:44 +02:00
Guido D'Orsi
71dda6b10b docs(contributing): fix the build step 2025-02-25 21:33:35 +01:00
Emil Sayahi
4612e0545e fix: type inference on useCoState (#1489)
* fix: type inference on `useCoState`

The `id` parameter should be `ID<CoValue>` so the type inference picks up the CoValue type only from the `Schema` parameter.

See: #1476

* fix: type inference on loading & subscribing API

* test: add type inference `useCoState` tests

* chore: changeset
2025-02-25 10:35:53 -05:00
Guido D'Orsi
07feedd641 feat(PasskeyAuth): support random passphrase generation 2025-02-25 15:35:48 +01:00
Guido D'Orsi
edbd567f11 Merge pull request #1491 from garden-co/remove-neverthrow-from-agent
chore: remove neverthrow from the agent/sealer id getters
2025-02-25 15:22:07 +01:00
Guido D'Orsi
221ca30790 chore: remove neverthrow from the agent/sealer id getters 2025-02-24 18:11:06 +01:00
105 changed files with 1548 additions and 306 deletions

View File

@@ -36,7 +36,7 @@ We welcome all ideas! If you have suggestions, feel free to open an issue marked
### 5. Local Setup
You'll need Node.js 20.x or 22.x installed (we're working on support for 23.x), and pnpm 9.x installed. If you're using nix, run `nix develop` to get a shell with the correct versions of everything installed.
You'll need Node.js 22.x installed (we're working on support for 23.x), and pnpm 9.x installed. If you're using nix, run `nix develop` to get a shell with the correct versions of everything installed.
1. **Clone the repository**:
```bash
@@ -54,6 +54,12 @@ You'll need Node.js 20.x or 22.x installed (we're working on support for 23.x),
cd homepage && pnpm install
```
4. **Go back to the project root**:
```bash
cd ..
```
4. **Build the packages**:
```bash

View File

@@ -1,5 +1,25 @@
# chat-rn-clerk
## 1.0.78
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-react-native@0.10.13
- jazz-react-native-auth-clerk@0.10.13
- jazz-react-native-media-images@0.10.13
## 1.0.77
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
- jazz-react-native@0.10.12
- jazz-react-native-auth-clerk@0.10.12
- jazz-react-native-media-images@0.10.12
## 1.0.76
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "chat-rn-clerk",
"main": "index.js",
"version": "1.0.76",
"version": "1.0.78",
"scripts": {
"build": "expo export -p ios",
"start": "expo start",

View File

@@ -1,5 +1,21 @@
# chat-rn
## 1.0.74
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-react-native@0.10.13
## 1.0.73
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
- jazz-react-native@0.10.12
## 1.0.72
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "chat-rn",
"version": "1.0.72",
"version": "1.0.74",
"main": "index.js",
"scripts": {
"build": "expo export -p ios",

View File

@@ -1,5 +1,23 @@
# chat-vue
## 0.0.60
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-browser@0.10.13
- jazz-vue@0.10.13
## 0.0.59
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
- jazz-vue@0.10.12
- jazz-browser@0.10.12
## 0.0.58
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "chat-vue",
"version": "0.0.58",
"version": "0.0.60",
"private": true,
"type": "module",
"scripts": {

View File

@@ -1,5 +1,23 @@
# jazz-example-chat
## 0.0.156
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-browser-media-images@0.10.13
- jazz-react@0.10.13
## 0.0.155
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
- jazz-react@0.10.12
- jazz-browser-media-images@0.10.12
## 0.0.154
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-chat",
"private": true,
"version": "0.0.154",
"version": "0.0.156",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,5 +1,23 @@
# minimal-auth-clerk
## 0.0.55
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-react@0.10.13
- jazz-react-auth-clerk@0.10.13
## 0.0.54
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
- jazz-react@0.10.12
- jazz-react-auth-clerk@0.10.12
## 0.0.53
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "clerk",
"private": true,
"version": "0.0.53",
"version": "0.0.55",
"type": "module",
"scripts": {
"dev": "vite",
@@ -13,7 +13,7 @@
"dependencies": {
"@clerk/clerk-react": "^5.4.1",
"jazz-react": "workspace:*",
"jazz-react-auth-clerk": "workspace:0.10.9",
"jazz-react-auth-clerk": "workspace:0.10.13",
"jazz-tools": "workspace:*",
"react": "^18.3.1",
"react-dom": "^18.3.1"

View File

@@ -1,5 +1,21 @@
# file-share-svelte
## 0.0.40
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-svelte@0.10.13
## 0.0.39
### Patch Changes
- Updated dependencies [4612e05]
- jazz-svelte@0.10.12
- jazz-tools@0.10.12
## 0.0.38
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "file-share-svelte",
"version": "0.0.38",
"version": "0.0.40",
"private": true,
"type": "module",
"scripts": {

View File

@@ -1,5 +1,23 @@
# form
## 0.0.51
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-browser-media-images@0.10.13
- jazz-react@0.10.13
## 0.0.50
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
- jazz-react@0.10.12
- jazz-browser-media-images@0.10.12
## 0.0.49
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "form",
"private": true,
"version": "0.0.49",
"version": "0.0.51",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,5 +1,23 @@
# image-upload
## 0.0.53
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-browser-media-images@0.10.13
- jazz-react@0.10.13
## 0.0.52
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
- jazz-react@0.10.12
- jazz-browser-media-images@0.10.12
## 0.0.51
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "image-upload",
"private": true,
"version": "0.0.51",
"version": "0.0.53",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,5 +1,23 @@
# jazz-example-musicplayer
## 0.0.77
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-inspector@0.10.10
- jazz-react@0.10.13
## 0.0.76
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
- jazz-inspector@0.10.9
- jazz-react@0.10.12
## 0.0.75
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-music-player",
"private": true,
"version": "0.0.75",
"version": "0.0.77",
"type": "module",
"scripts": {
"dev": "vite",
@@ -22,8 +22,8 @@
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"jazz-inspector": "workspace:*",
"jazz-react": "workspace:0.10.9",
"jazz-tools": "workspace:0.10.8",
"jazz-react": "workspace:0.10.13",
"jazz-tools": "workspace:0.10.13",
"lucide-react": "^0.274.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",

View File

@@ -1,5 +1,21 @@
# organization
## 0.0.49
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-react@0.10.13
## 0.0.48
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
- jazz-react@0.10.12
## 0.0.47
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "organization",
"private": true,
"version": "0.0.47",
"version": "0.0.49",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,5 +1,18 @@
# passkey-svelte
## 0.0.44
### Patch Changes
- jazz-svelte@0.10.13
## 0.0.43
### Patch Changes
- Updated dependencies [4612e05]
- jazz-svelte@0.10.12
## 0.0.42
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "passkey-svelte",
"version": "0.0.42",
"version": "0.0.44",
"type": "module",
"private": true,
"scripts": {

View File

@@ -1,5 +1,21 @@
# minimal-auth-passkey
## 0.0.54
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-react@0.10.13
## 0.0.53
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
- jazz-react@0.10.12
## 0.0.52
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "passkey",
"private": true,
"version": "0.0.52",
"version": "0.0.54",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,5 +1,21 @@
# passphrase
## 0.0.51
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-react@0.10.13
## 0.0.50
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
- jazz-react@0.10.12
## 0.0.49
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "passphrase",
"private": true,
"version": "0.0.49",
"version": "0.0.51",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -66,3 +66,72 @@ main {
margin: 0 auto;
text-align: center;
}
.auth-container {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background-color: #f3f4f6;
}
.auth-card {
background-color: white;
padding: 2rem;
border-radius: 0.5rem;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px
rgba(0, 0, 0, 0.06);
width: 28rem;
}
.auth-button-primary,
.auth-button-secondary {
width: 100%;
padding: 0.5rem 1rem;
border-radius: 0.25rem;
font-weight: bold;
cursor: pointer;
margin-bottom: 1rem;
}
.auth-button-primary {
background-color: black;
color: white;
border: none;
}
.auth-button-secondary {
background-color: white;
color: black;
border: 1px solid black;
}
.auth-heading {
color: black;
font-size: 1.5rem;
font-weight: bold;
text-align: center;
margin-bottom: 1rem;
}
.auth-textarea {
width: 100%;
padding: 0.5rem;
border: 1px solid #d1d5db;
border-radius: 0.25rem;
margin-bottom: 1rem;
box-sizing: border-box;
}
.auth-description {
font-size: 0.875rem;
color: #4b5563;
text-align: center;
margin-bottom: 1rem;
}
.auth-button-group {
display: flex;
justify-content: space-between;
gap: 1rem;
}

View File

@@ -1,10 +1,142 @@
import { JazzProvider, PassphraseAuthBasicUI } from "jazz-react";
import { StrictMode } from "react";
import { JazzProvider, usePassphraseAuth } from "jazz-react";
import { StrictMode, useState } from "react";
import { createRoot } from "react-dom/client";
import App from "./App.tsx";
import "./index.css";
import { wordlist } from "./wordlist.ts";
function PassphraseAuthBasicUI(props: {
appName: string;
wordlist: string[];
children?: React.ReactNode;
}) {
const auth = usePassphraseAuth({
wordlist: props.wordlist,
});
const [step, setStep] = useState<"initial" | "create" | "login">("initial");
const [loginPassphrase, setLoginPassphrase] = useState("");
const [isCopied, setIsCopied] = useState(false);
const [currentPassphrase, setCurrentPassphrase] = useState(() =>
auth.generateRandomPassphrase(),
);
if (auth.state === "signedIn") {
return props.children ?? null;
}
const handleCreateAccount = async () => {
setStep("create");
};
const handleLogin = () => {
setStep("login");
};
const handleReroll = () => {
const newPassphrase = auth.generateRandomPassphrase();
setCurrentPassphrase(newPassphrase);
setIsCopied(false);
};
const handleBack = () => {
setStep("initial");
setLoginPassphrase("");
};
const handleCopy = async () => {
await navigator.clipboard.writeText(auth.passphrase);
setIsCopied(true);
};
const handleLoginSubmit = async () => {
await auth.logIn(loginPassphrase);
setStep("initial");
setLoginPassphrase("");
};
const handleNext = async () => {
await auth.registerNewAccount(currentPassphrase, "My Account");
setStep("initial");
setLoginPassphrase("");
};
return (
<div className="auth-container">
<div className="auth-card">
{step === "initial" && (
<div>
<h1 className="auth-heading">{props.appName}</h1>
<button
onClick={handleCreateAccount}
className="auth-button-primary"
>
Create new account
</button>
<button onClick={handleLogin} className="auth-button-secondary">
Log in
</button>
</div>
)}
{step === "create" && (
<>
<h1 className="auth-heading">Your Passphrase</h1>
<p className="auth-description">
Please copy and store this passphrase somewhere safe. You'll need
it to log in.
</p>
<textarea
readOnly
value={currentPassphrase}
className="auth-textarea"
rows={5}
/>
<button onClick={handleCopy} className="auth-button-primary">
{isCopied ? "Copied!" : "Copy"}
</button>
<div className="auth-button-group">
<button onClick={handleBack} className="auth-button-secondary">
Back
</button>
<button onClick={handleReroll} className="auth-button-secondary">
Generate New Passphrase
</button>
<button onClick={handleNext} className="auth-button-primary">
Register
</button>
</div>
</>
)}
{step === "login" && (
<div>
<h1 className="auth-heading">Log In</h1>
<textarea
value={loginPassphrase}
onChange={(e) => setLoginPassphrase(e.target.value)}
placeholder="Enter your passphrase"
className="auth-textarea"
rows={5}
/>
<div className="auth-button-group">
<button onClick={handleBack} className="auth-button-secondary">
Back
</button>
<button
onClick={handleLoginSubmit}
className="auth-button-primary"
>
Log In
</button>
</div>
</div>
)}
</div>
</div>
);
}
function JazzAndAuth({ children }: { children: React.ReactNode }) {
return (
<JazzProvider

View File

@@ -1,5 +1,21 @@
# jazz-password-manager
## 0.0.75
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-react@0.10.13
## 0.0.74
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
- jazz-react@0.10.12
## 0.0.73
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-password-manager",
"private": true,
"version": "0.0.73",
"version": "0.0.75",
"type": "module",
"scripts": {
"dev": "vite",
@@ -12,8 +12,8 @@
"clean-install": "rm -rf node_modules pnpm-lock.yaml && pnpm install"
},
"dependencies": {
"jazz-react": "workspace:0.10.9",
"jazz-tools": "workspace:0.10.8",
"jazz-react": "workspace:0.10.13",
"jazz-tools": "workspace:0.10.13",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-hook-form": "^7.41.5",

View File

@@ -1,5 +1,23 @@
# jazz-example-pets
## 0.0.173
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-browser-media-images@0.10.13
- jazz-react@0.10.13
## 0.0.172
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
- jazz-react@0.10.12
- jazz-browser-media-images@0.10.12
## 0.0.171
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-pets",
"private": true,
"version": "0.0.171",
"version": "0.0.173",
"type": "module",
"scripts": {
"dev": "vite",
@@ -19,9 +19,9 @@
"@radix-ui/react-toast": "^1.1.4",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"jazz-browser-media-images": "workspace:0.10.9",
"jazz-react": "workspace:0.10.9",
"jazz-tools": "workspace:0.10.8",
"jazz-browser-media-images": "workspace:0.10.13",
"jazz-react": "workspace:0.10.13",
"jazz-tools": "workspace:0.10.13",
"lucide-react": "^0.274.0",
"qrcode": "^1.5.3",
"react": "^18.3.1",
@@ -41,7 +41,7 @@
"@vitejs/plugin-react-swc": "^3.3.2",
"autoprefixer": "^10.4.20",
"is-ci": "^3.0.1",
"jazz-run": "workspace:0.10.8",
"jazz-run": "workspace:0.10.13",
"postcss": "^8.4.27",
"tailwindcss": "^3.4.17",
"typescript": "~5.6.2",

View File

@@ -1,5 +1,23 @@
# reactions
## 0.0.53
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-browser-media-images@0.10.13
- jazz-react@0.10.13
## 0.0.52
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
- jazz-react@0.10.12
- jazz-browser-media-images@0.10.12
## 0.0.51
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "reactions",
"private": true,
"version": "0.0.51",
"version": "0.0.53",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,5 +1,23 @@
# todo-vue
## 0.0.58
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-browser@0.10.13
- jazz-vue@0.10.13
## 0.0.57
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
- jazz-vue@0.10.12
- jazz-browser@0.10.12
## 0.0.56
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "todo-vue",
"version": "0.0.56",
"version": "0.0.58",
"private": true,
"type": "module",
"scripts": {

View File

@@ -1,5 +1,21 @@
# jazz-example-todo
## 0.0.172
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-react@0.10.13
## 0.0.171
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
- jazz-react@0.10.12
## 0.0.170
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-todo",
"private": true,
"version": "0.0.170",
"version": "0.0.172",
"type": "module",
"scripts": {
"dev": "vite",
@@ -16,8 +16,8 @@
"@radix-ui/react-toast": "^1.1.4",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"jazz-react": "workspace:0.10.9",
"jazz-tools": "workspace:0.10.8",
"jazz-react": "workspace:0.10.13",
"jazz-tools": "workspace:0.10.13",
"lucide-react": "^0.274.0",
"qrcode": "^1.5.3",
"react": "^18.3.1",

View File

@@ -1,5 +1,21 @@
# version-history
## 0.0.50
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-react@0.10.13
## 0.0.49
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
- jazz-react@0.10.12
## 0.0.48
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "version-history",
"private": true,
"version": "0.0.48",
"version": "0.0.50",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,8 +1,16 @@
import { packages } from "@/lib/packages";
import { clsx } from "clsx";
import { Icon } from "gcmp-design-system/src/app/components/atoms/Icon";
import type { Metadata } from "next";
import Link from "next/link";
export const metadata: Metadata = {
title: "API reference",
openGraph: {
title: "API reference",
},
};
const CardHeading = ({
children,
className,

View File

@@ -1,5 +1,12 @@
import { CodeGroup, ContentByFramework, JazzLogo } from '@/components/forMdx'
export const metadata = {
title: "Learn some Jazz",
openGraph: {
title: "Learn some Jazz",
},
};
# Learn some <span className="sr-only">Jazz</span> <JazzLogo className="h-[41px] -ml-0.5 -mt-[3px] inline" />
Welcome to the Jazz documentation!

View File

@@ -6,6 +6,38 @@ import { Framework, frameworks } from "@/lib/framework";
import type { Toc } from "@stefanprobst/rehype-extract-toc";
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
async function getMdxSource(slugPath: string, framework: string) {
try {
return await import(`./${slugPath}.mdx`);
} catch (error) {
return await import(`./${slugPath}/${framework}.mdx`);
}
}
export async function generateMetadata({
params: { slug, framework },
}: { params: { slug: string[]; framework: string } }) {
const slugPath = slug.join("/");
try {
const mdxSource = await getMdxSource(slugPath, framework);
const title = mdxSource.tableOfContents?.[0].value || "Documentation";
return {
title,
openGraph: {
title,
},
};
} catch (error) {
return {
title: "Documentation",
openGraph: {
title: "Documentation",
},
};
}
}
export default async function Page({
params: { slug, framework },
}: { params: { slug: string[]; framework: string } }) {
@@ -13,13 +45,7 @@ export default async function Page({
const bodyClassName = "overflow-x-hidden lg:flex-1 py-10 max-w-3xl mx-auto";
try {
let mdxSource;
try {
mdxSource = await import(`./${slugPath}.mdx`);
} catch (error) {
mdxSource = await import(`./${slugPath}/${framework}.mdx`);
}
const mdxSource = await getMdxSource(slugPath, framework);
const { default: Content, tableOfContents } = mdxSource;
// Exclude h1 from table of contents

View File

@@ -1,25 +1,27 @@
import { Pricing } from "@/components/Pricing";
import { LatencyMap } from "@/components/cloud/latencyMap";
import { GridCard } from "gcmp-design-system/src/app/components/atoms/GridCard";
import {
H2,
H3,
H4,
} from "gcmp-design-system/src/app/components/atoms/Headings";
import { LI } from "gcmp-design-system/src/app/components/atoms/ListItem";
import { H2, H3 } from "gcmp-design-system/src/app/components/atoms/Headings";
import { P } from "gcmp-design-system/src/app/components/atoms/Paragraph";
import { GappedGrid } from "gcmp-design-system/src/app/components/molecules/GappedGrid";
import { HeroHeader } from "gcmp-design-system/src/app/components/molecules/HeroHeader";
import { UL } from "gcmp-design-system/src/app/components/molecules/List";
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
import { SectionHeader } from "gcmp-design-system/src/app/components/molecules/SectionHeader";
import type { Metadata } from "next";
import CloudPlusBackup from "./cloudPlusBackup.mdx";
import CloudPlusDIY from "./cloudPlusDIY.mdx";
import CompletelyDIY from "./completelyDIY.mdx";
export const metadata = {
title: "Jazz Cloud",
description: "Serverless sync & storage for Jazz apps.",
const title = "Jazz Cloud";
const description = "Serverless sync & storage for Jazz apps.";
export const metadata: Metadata = {
title,
description,
openGraph: {
title,
description,
},
};
export default function Cloud() {

View File

@@ -11,6 +11,20 @@ import { H2 } from "gcmp-design-system/src/app/components/atoms/Headings";
import { Icon } from "gcmp-design-system/src/app/components/atoms/Icon";
import { GappedGrid } from "gcmp-design-system/src/app/components/molecules/GappedGrid";
import { HeroHeader } from "gcmp-design-system/src/app/components/molecules/HeroHeader";
import type { Metadata } from "next";
const title = "Examples";
const description =
"Find an example app with code most similar to what you want to build.";
export const metadata: Metadata = {
title,
description,
openGraph: {
title,
description,
},
};
const MockButton = ({ children }: { children: React.ReactNode }) => (
<p className="bg-blue-100 text-blue-800 py-1 px-3 rounded-full font-medium text-xs inline-flex items-center justify-center">

View File

@@ -1,11 +1,19 @@
import { products } from "@/lib/showcase";
import { HeroHeader } from "gcmp-design-system/src/app/components/molecules/HeroHeader";
import type { Metadata } from "next";
import Image from "next/image";
import Link from "next/link";
export const metadata = {
title: "Built with Jazz",
description: "Great apps by smart people.",
const title = "Built with Jazz";
const description = "Great apps by smart people.";
export const metadata: Metadata = {
title,
description,
openGraph: {
title,
description,
},
};
export default function Page() {

View File

@@ -1,8 +1,18 @@
import { clsx } from "clsx";
import { HeroHeader } from "gcmp-design-system/src/app/components/molecules/HeroHeader";
import type { Metadata } from "next";
import dynamic from "next/dynamic";
import { Fragment } from "react";
const title = "Status";
export const metadata: Metadata = {
title,
openGraph: {
title,
},
};
const LatencyChart = dynamic(() => import("@/components/LatencyChart"), {
ssr: false,
});
@@ -133,11 +143,6 @@ const query = async () => {
return byRegion;
};
export const metadata = {
title: "Status",
description: "Great apps by smart people.",
};
export default async function Page() {
const byRegion = await query();

View File

@@ -184,9 +184,7 @@ export class CoValueCore {
this.header.meta?.type === "account"
? (this.node.currentSessionID.replace(
this.node.account.id,
this.node.account
.currentAgentID()
._unsafeUnwrap({ withStackTrace: true }),
this.node.account.currentAgentID(),
) as SessionID)
: this.node.currentSessionID;
@@ -455,9 +453,7 @@ export class CoValueCore {
this.header.meta?.type === "account"
? (this.node.currentSessionID.replace(
this.node.account.id,
this.node.account
.currentAgentID()
._unsafeUnwrap({ withStackTrace: true }),
this.node.account.currentAgentID(),
) as SessionID)
: this.node.currentSessionID;
@@ -639,9 +635,7 @@ export class CoValueCore {
// Try to find key revelation for us
const lookupAccountOrAgentID =
this.header.meta?.type === "account"
? this.node.account
.currentAgentID()
._unsafeUnwrap({ withStackTrace: true })
? this.node.account.currentAgentID()
: this.node.account.id;
const lastReadyKeyEdit = content.lastEditAt(

View File

@@ -1,4 +1,3 @@
import { Result, ok } from "neverthrow";
import { CoID, RawCoValue } from "../coValue.js";
import {
CoValueCore,
@@ -47,10 +46,11 @@ export class RawAccount<
> extends RawGroup<Meta> {
_cachedCurrentAgentID: AgentID | undefined;
currentAgentID(): Result<AgentID, InvalidAccountAgentIDError> {
currentAgentID(): AgentID {
if (this._cachedCurrentAgentID) {
return ok(this._cachedCurrentAgentID);
return this._cachedCurrentAgentID;
}
const agents = this.keys()
.filter((k): k is AgentID => k.startsWith("sealer_"))
.sort(
@@ -65,7 +65,7 @@ export class RawAccount<
this._cachedCurrentAgentID = agents[0];
return ok(agents[0]!);
return agents[0]!;
}
createInvite(_: AccountRole): InviteSecret {
@@ -77,10 +77,10 @@ export interface ControlledAccountOrAgent {
id: RawAccountID | AgentID;
agentSecret: AgentSecret;
currentAgentID: () => Result<AgentID, InvalidAccountAgentIDError>;
currentSignerID: () => Result<SignerID, InvalidAccountAgentIDError>;
currentAgentID: () => AgentID;
currentSignerID: () => SignerID;
currentSignerSecret: () => SignerSecret;
currentSealerID: () => Result<SealerID, InvalidAccountAgentIDError>;
currentSealerID: () => SealerID;
currentSealerSecret: () => SealerSecret;
}
@@ -116,17 +116,17 @@ export class RawControlledAccount<Meta extends AccountMeta = AccountMeta>
return this.core.node.acceptInvite(groupOrOwnedValueID, inviteSecret);
}
currentAgentID(): Result<AgentID, InvalidAccountAgentIDError> {
currentAgentID(): AgentID {
if (this._cachedCurrentAgentID) {
return ok(this._cachedCurrentAgentID);
return this._cachedCurrentAgentID;
}
const agentID = this.crypto.getAgentID(this.agentSecret);
this._cachedCurrentAgentID = agentID;
return ok(agentID);
return agentID;
}
currentSignerID() {
return this.currentAgentID().map((id) => this.crypto.getAgentSignerID(id));
return this.crypto.getAgentSignerID(this.currentAgentID());
}
currentSignerSecret(): SignerSecret {
@@ -134,7 +134,7 @@ export class RawControlledAccount<Meta extends AccountMeta = AccountMeta>
}
currentSealerID() {
return this.currentAgentID().map((id) => this.crypto.getAgentSealerID(id));
return this.crypto.getAgentSealerID(this.currentAgentID());
}
currentSealerSecret(): SealerSecret {
@@ -153,11 +153,11 @@ export class ControlledAgent implements ControlledAccountOrAgent {
}
currentAgentID() {
return ok(this.crypto.getAgentID(this.agentSecret));
return this.crypto.getAgentID(this.agentSecret);
}
currentSignerID() {
return this.currentAgentID().map((id) => this.crypto.getAgentSignerID(id));
return this.crypto.getAgentSignerID(this.currentAgentID());
}
currentSignerSecret(): SignerSecret {
@@ -165,7 +165,7 @@ export class ControlledAgent implements ControlledAccountOrAgent {
}
currentSealerID() {
return this.currentAgentID().map((id) => this.crypto.getAgentSealerID(id));
return this.crypto.getAgentSealerID(this.currentAgentID());
}
currentSealerSecret(): SealerSecret {

View File

@@ -251,9 +251,7 @@ export class RawGroup<
const memberKey = typeof account === "string" ? account : account.id;
const agent =
typeof account === "string"
? account
: account.currentAgentID()._unsafeUnwrap({ withStackTrace: true });
typeof account === "string" ? account : account.currentAgentID();
/**
* WriteOnly members can only see their own changes.

View File

@@ -530,7 +530,7 @@ export class LocalNode {
} satisfies UnexpectedlyNotAccountError);
}
return (coValue.getCurrentContent() as RawAccount).currentAgentID();
return ok((coValue.getCurrentContent() as RawAccount).currentAgentID());
}
resolveAccountAgentAsync(
@@ -573,7 +573,7 @@ export class LocalNode {
} satisfies UnexpectedlyNotAccountError);
}
return (coValue.getCurrentContent() as RawAccount).currentAgentID();
return ok((coValue.getCurrentContent() as RawAccount).currentAgentID());
});
}
@@ -601,9 +601,7 @@ export class LocalNode {
this.crypto.seal({
message: readKey.secret,
from: this.account.currentSealerSecret(),
to: this.account
.currentSealerID()
._unsafeUnwrap({ withStackTrace: true }),
to: this.account.currentSealerID(),
nOnceMaterial: {
in: groupCoValue.id,
tx: groupCoValue.nextTransactionID(),

View File

@@ -476,16 +476,7 @@ function agentInAccountOrMemberInGroup(
groupAtTime: RawGroup,
): RawAccountID | AgentID | undefined {
if (transactor === groupAtTime.id && groupAtTime instanceof RawAccount) {
return groupAtTime.currentAgentID().match(
(agentID) => agentID,
(e) => {
logger.error(
"Error while determining current agent ID in valid transactions",
e,
);
return undefined;
},
);
return groupAtTime.currentAgentID();
}
return transactor;
}

View File

@@ -358,7 +358,7 @@ test("Admins can set group read key and then use it to create and read private t
const revelation = Crypto.seal({
message: readKey,
from: admin.currentSealerSecret(),
to: admin.currentSealerID()._unsafeUnwrap(),
to: admin.currentSealerID(),
nOnceMaterial: {
in: groupCore.id,
tx: groupCore.nextTransactionID(),
@@ -412,7 +412,7 @@ test("Admins can set group read key and then writers can use it to create and re
const revelation1 = Crypto.seal({
message: readKey,
from: admin.currentSealerSecret(),
to: admin.currentSealerID()._unsafeUnwrap(),
to: admin.currentSealerID(),
nOnceMaterial: {
in: groupCore.id,
tx: groupCore.nextTransactionID(),
@@ -424,7 +424,7 @@ test("Admins can set group read key and then writers can use it to create and re
const revelation2 = Crypto.seal({
message: readKey,
from: admin.currentSealerSecret(),
to: writer.currentSealerID()._unsafeUnwrap(),
to: writer.currentSealerID(),
nOnceMaterial: {
in: groupCore.id,
tx: groupCore.nextTransactionID(),
@@ -491,7 +491,7 @@ test("Admins can set group read key and then use it to create private transactio
const revelation1 = Crypto.seal({
message: readKey,
from: admin.currentSealerSecret(),
to: admin.currentSealerID()._unsafeUnwrap(),
to: admin.currentSealerID(),
nOnceMaterial: {
in: groupCore.id,
tx: groupCore.nextTransactionID(),
@@ -503,7 +503,7 @@ test("Admins can set group read key and then use it to create private transactio
const revelation2 = Crypto.seal({
message: readKey,
from: admin.currentSealerSecret(),
to: reader.currentSealerID()._unsafeUnwrap(),
to: reader.currentSealerID(),
nOnceMaterial: {
in: groupCore.id,
tx: groupCore.nextTransactionID(),
@@ -578,7 +578,7 @@ test("Admins can set group read key and then use it to create private transactio
const revelation1 = Crypto.seal({
message: readKey,
from: admin.currentSealerSecret(),
to: admin.currentSealerID()._unsafeUnwrap(),
to: admin.currentSealerID(),
nOnceMaterial: {
in: groupCore.id,
tx: groupCore.nextTransactionID(),
@@ -590,7 +590,7 @@ test("Admins can set group read key and then use it to create private transactio
const revelation2 = Crypto.seal({
message: readKey,
from: admin.currentSealerSecret(),
to: reader1.currentSealerID()._unsafeUnwrap(),
to: reader1.currentSealerID(),
nOnceMaterial: {
in: groupCore.id,
tx: groupCore.nextTransactionID(),
@@ -629,7 +629,7 @@ test("Admins can set group read key and then use it to create private transactio
const revelation3 = Crypto.seal({
message: readKey,
from: admin.currentSealerSecret(),
to: reader2.currentSealerID()._unsafeUnwrap(),
to: reader2.currentSealerID(),
nOnceMaterial: {
in: groupCore.id,
tx: groupCore.nextTransactionID(),
@@ -694,7 +694,7 @@ test("Admins can set group read key, make a private transaction in an owned obje
const revelation1 = Crypto.seal({
message: readKey,
from: admin.currentSealerSecret(),
to: admin.currentSealerID()._unsafeUnwrap(),
to: admin.currentSealerID(),
nOnceMaterial: {
in: groupCore.id,
tx: groupCore.nextTransactionID(),
@@ -724,7 +724,7 @@ test("Admins can set group read key, make a private transaction in an owned obje
const revelation2 = Crypto.seal({
message: readKey2,
from: admin.currentSealerSecret(),
to: admin.currentSealerID()._unsafeUnwrap(),
to: admin.currentSealerID(),
nOnceMaterial: {
in: groupCore.id,
tx: groupCore.nextTransactionID(),
@@ -777,7 +777,7 @@ test("Admins can set group read key, make a private transaction in an owned obje
const revelation = Crypto.seal({
message: readKey,
from: admin.currentSealerSecret(),
to: admin.currentSealerID()._unsafeUnwrap(),
to: admin.currentSealerID(),
nOnceMaterial: {
in: groupCore.id,
tx: groupCore.nextTransactionID(),
@@ -804,7 +804,7 @@ test("Admins can set group read key, make a private transaction in an owned obje
const revelation2 = Crypto.seal({
message: readKey2,
from: admin.currentSealerSecret(),
to: admin.currentSealerID()._unsafeUnwrap(),
to: admin.currentSealerID(),
nOnceMaterial: {
in: groupCore.id,
tx: groupCore.nextTransactionID(),
@@ -816,7 +816,7 @@ test("Admins can set group read key, make a private transaction in an owned obje
const revelation3 = Crypto.seal({
message: readKey2,
from: admin.currentSealerSecret(),
to: reader.currentSealerID()._unsafeUnwrap(),
to: reader.currentSealerID(),
nOnceMaterial: {
in: groupCore.id,
tx: groupCore.nextTransactionID(),
@@ -912,7 +912,7 @@ test("Admins can set group read rey, make a private transaction in an owned obje
const revelation1 = Crypto.seal({
message: readKey,
from: admin.currentSealerSecret(),
to: admin.currentSealerID()._unsafeUnwrap(),
to: admin.currentSealerID(),
nOnceMaterial: {
in: groupCore.id,
tx: groupCore.nextTransactionID(),
@@ -924,7 +924,7 @@ test("Admins can set group read rey, make a private transaction in an owned obje
const revelation2 = Crypto.seal({
message: readKey,
from: admin.currentSealerSecret(),
to: reader.currentSealerID()._unsafeUnwrap(),
to: reader.currentSealerID(),
nOnceMaterial: {
in: groupCore.id,
tx: groupCore.nextTransactionID(),
@@ -936,7 +936,7 @@ test("Admins can set group read rey, make a private transaction in an owned obje
const revelation3 = Crypto.seal({
message: readKey,
from: admin.currentSealerSecret(),
to: reader2.currentSealerID()._unsafeUnwrap(),
to: reader2.currentSealerID(),
nOnceMaterial: {
in: groupCore.id,
tx: groupCore.nextTransactionID(),
@@ -982,7 +982,7 @@ test("Admins can set group read rey, make a private transaction in an owned obje
const newRevelation1 = Crypto.seal({
message: readKey2,
from: admin.currentSealerSecret(),
to: admin.currentSealerID()._unsafeUnwrap(),
to: admin.currentSealerID(),
nOnceMaterial: {
in: groupCore.id,
tx: groupCore.nextTransactionID(),
@@ -994,7 +994,7 @@ test("Admins can set group read rey, make a private transaction in an owned obje
const newRevelation2 = Crypto.seal({
message: readKey2,
from: admin.currentSealerSecret(),
to: reader2.currentSealerID()._unsafeUnwrap(),
to: reader2.currentSealerID(),
nOnceMaterial: {
in: groupCore.id,
tx: groupCore.nextTransactionID(),
@@ -1119,7 +1119,7 @@ test("Admins can create an adminInvite, which can add an admin", () => {
const revelation = Crypto.seal({
message: readKey,
from: admin.currentSealerSecret(),
to: admin.currentSealerID()._unsafeUnwrap(),
to: admin.currentSealerID(),
nOnceMaterial: {
in: groupCore.id,
tx: groupCore.nextTransactionID(),
@@ -1229,7 +1229,7 @@ test("Admins can create a writerInvite, which can add a writer", () => {
const revelation = Crypto.seal({
message: readKey,
from: admin.currentSealerSecret(),
to: admin.currentSealerID()._unsafeUnwrap(),
to: admin.currentSealerID(),
nOnceMaterial: {
in: groupCore.id,
tx: groupCore.nextTransactionID(),
@@ -1332,7 +1332,7 @@ test("Admins can create a readerInvite, which can add a reader", () => {
const revelation = Crypto.seal({
message: readKey,
from: admin.currentSealerSecret(),
to: admin.currentSealerID()._unsafeUnwrap(),
to: admin.currentSealerID(),
nOnceMaterial: {
in: groupCore.id,
tx: groupCore.nextTransactionID(),
@@ -1425,7 +1425,7 @@ test("WriterInvites can not invite admins", () => {
const revelation = Crypto.seal({
message: readKey,
from: admin.currentSealerSecret(),
to: admin.currentSealerID()._unsafeUnwrap(),
to: admin.currentSealerID(),
nOnceMaterial: {
in: groupCore.id,
tx: groupCore.nextTransactionID(),
@@ -1479,7 +1479,7 @@ test("ReaderInvites can not invite admins", () => {
const revelation = Crypto.seal({
message: readKey,
from: admin.currentSealerSecret(),
to: admin.currentSealerID()._unsafeUnwrap(),
to: admin.currentSealerID(),
nOnceMaterial: {
in: groupCore.id,
tx: groupCore.nextTransactionID(),
@@ -1533,7 +1533,7 @@ test("ReaderInvites can not invite writers", () => {
const revelation = Crypto.seal({
message: readKey,
from: admin.currentSealerSecret(),
to: admin.currentSealerID()._unsafeUnwrap(),
to: admin.currentSealerID(),
nOnceMaterial: {
in: groupCore.id,
tx: groupCore.nextTransactionID(),
@@ -1587,7 +1587,7 @@ test("WriteOnlyInvites can not invite writers", () => {
const revelation = Crypto.seal({
message: readKey,
from: admin.currentSealerSecret(),
to: admin.currentSealerID()._unsafeUnwrap(),
to: admin.currentSealerID(),
nOnceMaterial: {
in: groupCore.id,
tx: groupCore.nextTransactionID(),
@@ -1641,7 +1641,7 @@ test("WriteOnlyInvites can not invite admins", () => {
const revelation = Crypto.seal({
message: readKey,
from: admin.currentSealerSecret(),
to: admin.currentSealerID()._unsafeUnwrap(),
to: admin.currentSealerID(),
nOnceMaterial: {
in: groupCore.id,
tx: groupCore.nextTransactionID(),
@@ -1695,7 +1695,7 @@ test("WriteOnlyInvites can invite writeOnly", () => {
const revelation = Crypto.seal({
message: readKey,
from: admin.currentSealerSecret(),
to: admin.currentSealerID()._unsafeUnwrap(),
to: admin.currentSealerID(),
nOnceMaterial: {
in: groupCore.id,
tx: groupCore.nextTransactionID(),
@@ -1749,7 +1749,7 @@ test("WriteOnlyInvites can set writeKeys", () => {
const revelation = Crypto.seal({
message: readKey,
from: admin.currentSealerSecret(),
to: admin.currentSealerID()._unsafeUnwrap(),
to: admin.currentSealerID(),
nOnceMaterial: {
in: groupCore.id,
tx: groupCore.nextTransactionID(),
@@ -1800,7 +1800,7 @@ test("Invites can't override key revelations", () => {
const revelation = Crypto.seal({
message: readKey,
from: admin.currentSealerSecret(),
to: admin.currentSealerID()._unsafeUnwrap(),
to: admin.currentSealerID(),
nOnceMaterial: {
in: groupCore.id,
tx: groupCore.nextTransactionID(),
@@ -1855,7 +1855,7 @@ test("WriteOnlyInvites can't override writeKeys", () => {
const revelation = Crypto.seal({
message: readKey,
from: admin.currentSealerSecret(),
to: admin.currentSealerID()._unsafeUnwrap(),
to: admin.currentSealerID(),
nOnceMaterial: {
in: groupCore.id,
tx: groupCore.nextTransactionID(),
@@ -1929,7 +1929,7 @@ test("Can give read permission to 'everyone'", () => {
childObject
.testWithDifferentAccount(
newAccount,
Crypto.newRandomSessionID(newAccount.currentAgentID()._unsafeUnwrap()),
Crypto.newRandomSessionID(newAccount.currentAgentID()),
)
.getCurrentContent(),
);
@@ -1955,7 +1955,7 @@ test("Can give read permissions to 'everyone' (high-level)", async () => {
childObject.core
.testWithDifferentAccount(
new ControlledAgent(Crypto.newRandomAgentSecret(), Crypto),
Crypto.newRandomSessionID(newAccount.currentAgentID()._unsafeUnwrap()),
Crypto.newRandomSessionID(newAccount.currentAgentID()),
)
.getCurrentContent(),
);
@@ -1993,7 +1993,7 @@ test("Can give write permission to 'everyone'", async () => {
childObject
.testWithDifferentAccount(
newAccount,
Crypto.newRandomSessionID(newAccount.currentAgentID()._unsafeUnwrap()),
Crypto.newRandomSessionID(newAccount.currentAgentID()),
)
.getCurrentContent(),
);
@@ -2025,7 +2025,7 @@ test("Can give write permissions to 'everyone' (high-level)", async () => {
childObject.core
.testWithDifferentAccount(
newAccount,
Crypto.newRandomSessionID(newAccount.currentAgentID()._unsafeUnwrap()),
Crypto.newRandomSessionID(newAccount.currentAgentID()),
)
.getCurrentContent(),
);
@@ -2087,7 +2087,7 @@ test("Writers, readers and invitees can not set parent extensions", () => {
group.core
.testWithDifferentAccount(
adminInvite,
Crypto.newRandomSessionID(adminInvite.currentAgentID()._unsafeUnwrap()),
Crypto.newRandomSessionID(adminInvite.currentAgentID()),
)
.getCurrentContent(),
);
@@ -2099,9 +2099,7 @@ test("Writers, readers and invitees can not set parent extensions", () => {
group.core
.testWithDifferentAccount(
writerInvite,
Crypto.newRandomSessionID(
writerInvite.currentAgentID()._unsafeUnwrap(),
),
Crypto.newRandomSessionID(writerInvite.currentAgentID()),
)
.getCurrentContent(),
);
@@ -2113,9 +2111,7 @@ test("Writers, readers and invitees can not set parent extensions", () => {
group.core
.testWithDifferentAccount(
readerInvite,
Crypto.newRandomSessionID(
readerInvite.currentAgentID()._unsafeUnwrap(),
),
Crypto.newRandomSessionID(readerInvite.currentAgentID()),
)
.getCurrentContent(),
);
@@ -2209,7 +2205,7 @@ test("Invitees can not set child extensions", () => {
group.core
.testWithDifferentAccount(
adminInvite,
Crypto.newRandomSessionID(adminInvite.currentAgentID()._unsafeUnwrap()),
Crypto.newRandomSessionID(adminInvite.currentAgentID()),
)
.getCurrentContent(),
);
@@ -2221,9 +2217,7 @@ test("Invitees can not set child extensions", () => {
group.core
.testWithDifferentAccount(
writerInvite,
Crypto.newRandomSessionID(
writerInvite.currentAgentID()._unsafeUnwrap(),
),
Crypto.newRandomSessionID(writerInvite.currentAgentID()),
)
.getCurrentContent(),
);
@@ -2235,9 +2229,7 @@ test("Invitees can not set child extensions", () => {
group.core
.testWithDifferentAccount(
readerInvite,
Crypto.newRandomSessionID(
readerInvite.currentAgentID()._unsafeUnwrap(),
),
Crypto.newRandomSessionID(readerInvite.currentAgentID()),
)
.getCurrentContent(),
);
@@ -2422,7 +2414,7 @@ test("Writers, readers and invites can't reveal parent read keys to child groups
group.core
.testWithDifferentAccount(
adminInvite,
Crypto.newRandomSessionID(adminInvite.currentAgentID()._unsafeUnwrap()),
Crypto.newRandomSessionID(adminInvite.currentAgentID()),
)
.getCurrentContent(),
);
@@ -2440,9 +2432,7 @@ test("Writers, readers and invites can't reveal parent read keys to child groups
group.core
.testWithDifferentAccount(
writerInvite,
Crypto.newRandomSessionID(
writerInvite.currentAgentID()._unsafeUnwrap(),
),
Crypto.newRandomSessionID(writerInvite.currentAgentID()),
)
.getCurrentContent(),
);
@@ -2460,9 +2450,7 @@ test("Writers, readers and invites can't reveal parent read keys to child groups
group.core
.testWithDifferentAccount(
readerInvite,
Crypto.newRandomSessionID(
readerInvite.currentAgentID()._unsafeUnwrap(),
),
Crypto.newRandomSessionID(readerInvite.currentAgentID()),
)
.getCurrentContent(),
);
@@ -2941,7 +2929,7 @@ test("Can revoke read permission from 'everyone'", async () => {
childObject.core
.testWithDifferentAccount(
newAccount,
Crypto.newRandomSessionID(newAccount.currentAgentID()._unsafeUnwrap()),
Crypto.newRandomSessionID(newAccount.currentAgentID()),
)
.getCurrentContent(),
);
@@ -2963,7 +2951,7 @@ test("Can revoke read permission from 'everyone'", async () => {
childObject.core
.testWithDifferentAccount(
newAccount2,
Crypto.newRandomSessionID(newAccount2.currentAgentID()._unsafeUnwrap()),
Crypto.newRandomSessionID(newAccount2.currentAgentID()),
)
.getCurrentContent(),
);

View File

@@ -1,5 +1,11 @@
# create-jazz-app
## 0.1.12
### Patch Changes
- e2e0af3: Handle workspace devDependencies of cloned apps when using create-jazz-app
## 0.1.11
### Patch Changes

View File

@@ -5,7 +5,7 @@
"types": "src/index.ts",
"type": "module",
"license": "MIT",
"version": "0.1.11",
"version": "0.1.12",
"bin": {
"create-jazz-app": "./dist/index.js"
},

View File

@@ -136,19 +136,30 @@ async function scaffoldProject({
const packageJsonPath = `${projectName}/package.json`;
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
// Replace workspace: dependencies with latest
if (packageJson.dependencies) {
const latestVersions = await getLatestPackageVersions(
packageJson.dependencies,
);
// Helper function to update workspace dependencies
async function updateWorkspaceDependencies(
dependencyType: "dependencies" | "devDependencies",
) {
if (packageJson[dependencyType]) {
const latestVersions = await getLatestPackageVersions(
packageJson[dependencyType],
);
Object.entries(packageJson.dependencies).forEach(([pkg, version]) => {
if (typeof version === "string" && version.includes("workspace:")) {
packageJson.dependencies[pkg] = latestVersions[pkg];
}
});
Object.entries(packageJson[dependencyType]).forEach(
([pkg, version]) => {
if (typeof version === "string" && version.includes("workspace:")) {
packageJson[dependencyType][pkg] = latestVersions[pkg];
}
},
);
}
}
await Promise.all([
updateWorkspaceDependencies("dependencies"),
updateWorkspaceDependencies("devDependencies"),
]);
packageJson.name = projectName;
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
depsSpinner.succeed(chalk.green("Dependencies updated"));

View File

@@ -1,5 +1,21 @@
# jazz-browser-media-images
## 0.10.13
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-browser@0.10.13
## 0.10.12
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
- jazz-browser@0.10.12
## 0.10.9
### Patch Changes

View File

@@ -1,14 +1,14 @@
{
"name": "jazz-auth-clerk",
"version": "0.10.9",
"version": "0.10.13",
"type": "module",
"main": "dist/index.js",
"types": "src/index.ts",
"license": "MIT",
"dependencies": {
"cojson": "workspace:0.10.8",
"jazz-browser": "workspace:0.10.9",
"jazz-tools": "workspace:0.10.8"
"jazz-browser": "workspace:0.10.13",
"jazz-tools": "workspace:0.10.13"
},
"scripts": {
"format-and-lint": "biome check .",

View File

@@ -1,5 +1,21 @@
# jazz-browser-media-images
## 0.10.13
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-browser@0.10.13
## 0.10.12
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
- jazz-browser@0.10.12
## 0.10.9
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "jazz-browser-media-images",
"version": "0.10.9",
"version": "0.10.13",
"type": "module",
"main": "dist/index.js",
"types": "src/index.ts",
@@ -8,8 +8,8 @@
"dependencies": {
"@types/image-blob-reduce": "^4.1.1",
"image-blob-reduce": "^4.1.0",
"jazz-browser": "workspace:0.10.9",
"jazz-tools": "workspace:0.10.8",
"jazz-browser": "workspace:0.10.13",
"jazz-tools": "workspace:0.10.13",
"pica": "^9.0.1",
"typescript": "~5.6.2"
},

View File

@@ -1,5 +1,19 @@
# jazz-browser
## 0.10.13
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
## 0.10.12
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
## 0.10.9
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "jazz-browser",
"version": "0.10.9",
"version": "0.10.13",
"type": "module",
"main": "dist/index.js",
"types": "src/index.ts",

View File

@@ -1,5 +1,21 @@
# jazz-inspector
## 0.10.10
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-react-core@0.10.10
## 0.10.9
### Patch Changes
- Updated dependencies [4612e05]
- jazz-react-core@0.10.9
- jazz-tools@0.10.12
## 0.10.8
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "jazz-inspector",
"version": "0.10.8",
"version": "0.10.10",
"type": "module",
"main": "./dist/app.js",
"types": "./dist/app.d.ts",

View File

@@ -1,5 +1,19 @@
# jazz-autosub
## 0.10.13
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
## 0.10.12
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
## 0.10.8
### Patch Changes

View File

@@ -5,7 +5,7 @@
"types": "src/index.ts",
"type": "module",
"license": "MIT",
"version": "0.10.8",
"version": "0.10.13",
"dependencies": {
"cojson": "workspace:*",
"cojson-transport-ws": "workspace:*",

View File

@@ -1,5 +1,25 @@
# jazz-browser-media-images
## 0.10.13
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-auth-clerk@0.10.13
- jazz-browser@0.10.13
- jazz-react@0.10.13
## 0.10.12
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
- jazz-react@0.10.12
- jazz-auth-clerk@0.10.12
- jazz-browser@0.10.12
## 0.10.9
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "jazz-react-auth-clerk",
"version": "0.10.9",
"version": "0.10.13",
"type": "module",
"main": "dist/index.js",
"types": "src/index.tsx",

View File

@@ -1,5 +1,20 @@
# jazz-react-core
## 0.10.10
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
## 0.10.9
### Patch Changes
- 4612e05: Fix type inference on `useCoState`
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
## 0.10.8
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "jazz-react-core",
"version": "0.10.8",
"version": "0.10.10",
"type": "module",
"main": "dist/index.js",
"types": "src/index.ts",

View File

@@ -29,6 +29,7 @@ export function usePassphraseAuth({
return new PassphraseAuth(
context.node.crypto,
context.authenticate,
context.register,
authSecretStorage,
wordlist,
);
@@ -50,6 +51,8 @@ export function usePassphraseAuth({
state: isAuthenticated ? "signedIn" : "anonymous",
logIn: authMethod.logIn,
signUp: authMethod.signUp,
registerNewAccount: authMethod.registerNewAccount,
generateRandomPassphrase: authMethod.generateRandomPassphrase,
passphrase,
} as const;
}

View File

@@ -102,7 +102,7 @@ function useCoValueObservable<V extends CoValue, D>() {
export function useCoState<V extends CoValue, D>(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Schema: CoValueClass<V>,
id: ID<V> | undefined,
id: ID<CoValue> | undefined,
depth: D & DepthsIn<V> = [] as D & DepthsIn<V>,
): DeeplyLoaded<V, D> | undefined | null {
const contextManager = useJazzContextManager();

View File

@@ -1,8 +1,8 @@
// @vitest-environment happy-dom
import { cojsonInternals } from "cojson";
import { CoMap, co } from "jazz-tools";
import { beforeEach, describe, expect, it } from "vitest";
import { CoMap, CoValue, ID, co } from "jazz-tools";
import { beforeEach, describe, expect, expectTypeOf, it } from "vitest";
import { useCoState } from "../index.js";
import { createJazzTestAccount, setupJazzTestSync } from "../testing.js";
import { act, renderHook, waitFor } from "./testUtils.js";
@@ -157,4 +157,21 @@ describe("useCoState", () => {
expect(result.current).toBeNull();
});
});
it("should return the same type as Schema", () => {
class TestMap extends CoMap {
value = co.string;
}
const map = TestMap.create({
value: "123",
});
const { result } = renderHook(() =>
useCoState(TestMap, map.id as ID<CoValue>, []),
);
expectTypeOf(result).toEqualTypeOf<{
current: TestMap | null | undefined;
}>();
});
});

View File

@@ -1,5 +1,23 @@
# jazz-react-native-auth-clerk
## 0.10.13
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-auth-clerk@0.10.13
- jazz-react-native@0.10.13
## 0.10.12
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
- jazz-react-native@0.10.12
- jazz-auth-clerk@0.10.12
## 0.10.11
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "jazz-react-native-auth-clerk",
"version": "0.10.11",
"version": "0.10.13",
"type": "module",
"main": "dist/index.js",
"types": "src/index.tsx",

View File

@@ -1,5 +1,19 @@
# jazz-browser-media-images
## 0.10.13
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
## 0.10.12
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
## 0.10.8
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "jazz-react-native-media-images",
"version": "0.10.8",
"version": "0.10.13",
"type": "module",
"main": "dist/index.js",
"types": "src/index.ts",

View File

@@ -1,5 +1,21 @@
# jazz-browser
## 0.10.13
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-react-core@0.10.10
## 0.10.12
### Patch Changes
- Updated dependencies [4612e05]
- jazz-react-core@0.10.9
- jazz-tools@0.10.12
## 0.10.11
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "jazz-react-native",
"version": "0.10.11",
"version": "0.10.13",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",

View File

@@ -1,5 +1,23 @@
# jazz-react
## 0.10.13
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-browser@0.10.13
- jazz-react-core@0.10.10
## 0.10.12
### Patch Changes
- Updated dependencies [4612e05]
- jazz-react-core@0.10.9
- jazz-tools@0.10.12
- jazz-browser@0.10.12
## 0.10.9
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "jazz-react",
"version": "0.10.9",
"version": "0.10.13",
"type": "module",
"main": "dist/index.js",
"types": "src/index.ts",
@@ -18,9 +18,9 @@
"dependencies": {
"@scure/bip39": "^1.3.0",
"cojson": "workspace:0.10.8",
"jazz-browser": "workspace:0.10.9",
"jazz-browser": "workspace:0.10.13",
"jazz-react-core": "workspace:*",
"jazz-tools": "workspace:0.10.8"
"jazz-tools": "workspace:0.10.13"
},
"devDependencies": {
"@testing-library/dom": "^10.4.0",

View File

@@ -1,5 +1,19 @@
# jazz-run
## 0.10.13
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
## 0.10.12
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
## 0.10.8
### Patch Changes

View File

@@ -3,7 +3,7 @@
"bin": "./dist/index.js",
"type": "module",
"license": "MIT",
"version": "0.10.8",
"version": "0.10.13",
"exports": {
"./startSyncServer": {
"import": "./dist/startSyncServer.js",
@@ -32,7 +32,7 @@
"cojson-storage-sqlite": "workspace:0.10.8",
"cojson-transport-ws": "workspace:0.10.8",
"effect": "^3.6.5",
"jazz-tools": "workspace:0.10.8",
"jazz-tools": "workspace:0.10.13",
"ws": "^8.14.2"
},
"devDependencies": {

View File

@@ -1,5 +1,22 @@
# jazz-svelte
## 0.10.13
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-browser@0.10.13
## 0.10.12
### Patch Changes
- 4612e05: Fix type inference on `useCoState`
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
- jazz-browser@0.10.12
## 0.10.9
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "jazz-svelte",
"version": "0.10.9",
"version": "0.10.13",
"scripts": {
"dev": "vite dev",
"build": "vite build && npm run package",

View File

@@ -14,6 +14,7 @@ export function usePassphraseAuth({
const auth = new PassphraseAuth(
context.current.node.crypto,
context.current.authenticate,
context.current.register,
authSecretStorage,
wordlist
);
@@ -35,6 +36,8 @@ export function usePassphraseAuth({
return {
logIn: auth.logIn,
signUp: auth.signUp,
registerNewAccount: auth.registerNewAccount,
generateRandomPassphrase: auth.generateRandomPassphrase,
get passphrase() {
return passphrase;
},

View File

@@ -1,6 +1,4 @@
import {
consumeInviteLinkFromWindowLocation
} from 'jazz-browser';
import { consumeInviteLinkFromWindowLocation } from 'jazz-browser';
import type {
AnonymousJazzAgent,
AuthSecretStorage,
@@ -63,11 +61,9 @@ export function getAuthSecretStorage() {
}
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
export interface Register { }
export interface Register {}
export type RegisteredAccount = Register extends { Account: infer Acc }
? Acc
: Account;
export type RegisteredAccount = Register extends { Account: infer Acc } ? Acc : Account;
export function useAccount(): { me: RegisteredAccount; logOut: () => void };
export function useAccount<D extends DepthsIn<RegisteredAccount>>(
@@ -80,7 +76,10 @@ export function useAccount<D extends DepthsIn<RegisteredAccount>>(
*/
export function useAccount<D extends DepthsIn<RegisteredAccount>>(
depth?: D
): { me: RegisteredAccount | DeeplyLoaded<RegisteredAccount, D> | undefined | null; logOut: () => void } {
): {
me: RegisteredAccount | DeeplyLoaded<RegisteredAccount, D> | undefined | null;
logOut: () => void;
} {
const ctx = getJazzContext<RegisteredAccount>();
if (!ctx?.current) {
throw new Error('useAccount must be used within a JazzProvider');
@@ -131,7 +130,14 @@ export function useAccountOrGuest<D extends DepthsIn<RegisteredAccount>>(
*/
export function useAccountOrGuest<D extends DepthsIn<RegisteredAccount>>(
depth?: D
): { me: RegisteredAccount | DeeplyLoaded<RegisteredAccount, D> | undefined | null | AnonymousJazzAgent } {
): {
me:
| RegisteredAccount
| DeeplyLoaded<RegisteredAccount, D>
| undefined
| null
| AnonymousJazzAgent;
} {
const ctx = getJazzContext<RegisteredAccount>();
if (!ctx?.current) {
@@ -175,7 +181,7 @@ export function useAccountOrGuest<D extends DepthsIn<RegisteredAccount>>(
*/
export function useCoState<V extends CoValue, D extends DepthsIn<V> = []>(
Schema: CoValueClass<V>,
id: ID<V> | undefined,
id: ID<CoValue> | undefined,
depth: D = [] as D
): {
current?: DeeplyLoaded<V, D> | null;
@@ -267,4 +273,3 @@ export function useAcceptInvite<V extends CoValue>({
});
});
}

View File

@@ -1,8 +1,16 @@
import { render, waitFor } from "@testing-library/svelte";
import { Account, CoMap, co, cojsonInternals, type CoValue, type CoValueClass, type DepthsIn } from "jazz-tools";
import { describe, expect, it } from "vitest";
import { createJazzTestAccount, createJazzTestContext, setupJazzTestSync } from "../testing.js";
import UseCoState from "./components/useCoState.svelte";
import { render, waitFor } from '@testing-library/svelte';
import {
Account,
CoMap,
co,
cojsonInternals,
type CoValue,
type CoValueClass,
type DepthsIn
} from 'jazz-tools';
import { describe, expect, it, expectTypeOf } from 'vitest';
import { createJazzTestAccount, createJazzTestContext, setupJazzTestSync } from '../testing.js';
import UseCoState from './components/useCoState.svelte';
beforeEach(async () => {
await setupJazzTestSync();
@@ -13,30 +21,26 @@ beforeEach(() => {
cojsonInternals.CO_VALUE_LOADING_CONFIG.TIMEOUT = 1;
});
function setup<T extends CoValue>(options: {
account: Account;
map: T;
depth?: DepthsIn<T>;
}) {
function setup<T extends CoValue>(options: { account: Account; map: T; depth?: DepthsIn<T> }) {
const result = { current: undefined } as { current: T | undefined };
render(UseCoState, {
context: createJazzTestContext({ account: options.account }),
props: {
Schema: options.map.constructor as CoValueClass<T>,
id: options.map.id,
depth: options.depth ?? [],
setResult: (value: T | undefined) => {
result.current = value
},
},
});
render(UseCoState, {
context: createJazzTestContext({ account: options.account }),
props: {
Schema: options.map.constructor as CoValueClass<T>,
id: options.map.id,
depth: options.depth ?? [],
setResult: (value: T | undefined) => {
result.current = value;
}
}
});
return result;
}
describe("useCoState", () => {
it("should return the correct value", async () => {
describe('useCoState', () => {
it('should return the correct value', async () => {
class TestMap extends CoMap {
value = co.string;
}
@@ -45,20 +49,20 @@ describe("useCoState", () => {
const map = TestMap.create(
{
value: "123",
value: '123'
},
{ owner: account },
{ owner: account }
);
const result = setup({
account,
map,
map
});
expect(result.current?.value).toBe("123");
expect(result.current?.value).toBe('123');
});
it("should update the value when the coValue changes", async () => {
it('should update the value when the coValue changes', async () => {
class TestMap extends CoMap {
value = co.string;
}
@@ -67,24 +71,24 @@ describe("useCoState", () => {
const map = TestMap.create(
{
value: "123",
value: '123'
},
{ owner: account },
{ owner: account }
);
const result = setup({
account,
map,
map
});
expect(result.current?.value).toBe("123");
expect(result.current?.value).toBe('123');
map.value = "456";
map.value = '456';
expect(result.current?.value).toBe("456");
expect(result.current?.value).toBe('456');
});
it("should load nested values if requested", async () => {
it('should load nested values if requested', async () => {
class TestNestedMap extends CoMap {
value = co.string;
}
@@ -98,30 +102,30 @@ describe("useCoState", () => {
const map = TestMap.create(
{
value: "123",
value: '123',
nested: TestNestedMap.create(
{
value: "456",
value: '456'
},
{ owner: account },
),
{ owner: account }
)
},
{ owner: account },
{ owner: account }
);
const result = setup({
account,
map,
depth: {
nested: {},
},
nested: {}
}
});
expect(result.current?.value).toBe("123");
expect(result.current?.nested?.value).toBe("456");
expect(result.current?.value).toBe('123');
expect(result.current?.nested?.value).toBe('456');
});
it("should load nested values on access even if not requested", async () => {
it('should load nested values on access even if not requested', async () => {
class TestNestedMap extends CoMap {
value = co.string;
}
@@ -135,50 +139,52 @@ describe("useCoState", () => {
const map = TestMap.create(
{
value: "123",
value: '123',
nested: TestNestedMap.create(
{
value: "456",
value: '456'
},
{ owner: account },
),
{ owner: account }
)
},
{ owner: account },
{ owner: account }
);
const result = setup({
account,
map,
depth: {
nested: {},
},
nested: {}
}
});
expect(result.current?.value).toBe("123");
expect(result.current?.nested?.value).toBe("456");
expect(result.current?.value).toBe('123');
expect(result.current?.nested?.value).toBe('456');
});
it("should return null if the coValue is not found", async () => {
it('should return null if the coValue is not found', async () => {
class TestMap extends CoMap {
value = co.string;
}
const unreachableAccount = await createJazzTestAccount({
});
const unreachableAccount = await createJazzTestAccount({});
const map = TestMap.create({
value: "123",
}, unreachableAccount);
const map = TestMap.create(
{
value: '123'
},
unreachableAccount
);
unreachableAccount._raw.core.node.gracefulShutdown();
const account = await createJazzTestAccount({
isCurrentActiveAccount: true,
isCurrentActiveAccount: true
});
const result = setup({
account,
map,
map
});
expect(result.current).toBeUndefined();
@@ -187,4 +193,28 @@ describe("useCoState", () => {
expect(result.current).toBeNull();
});
});
it('should return the same type as Schema', async () => {
class TestMap extends CoMap {
value = co.string;
}
const account = await createJazzTestAccount();
const map = TestMap.create(
{
value: '123'
},
{ owner: account }
);
const result = setup({
account,
map
});
expectTypeOf(result).toEqualTypeOf<{
current: TestMap | undefined;
}>();
});
});

View File

@@ -1,5 +1,17 @@
# jazz-tools
## 0.10.13
### Patch Changes
- 07feedd: Add registerNewUser and generateRandomPassphrase methods to PasskeyAuth and accept the username param on the signUp function
## 0.10.12
### Patch Changes
- 4612e05: Fix type inference on `useCoState`
## 0.10.8
### Patch Changes

View File

@@ -17,7 +17,7 @@
},
"type": "module",
"license": "MIT",
"version": "0.10.8",
"version": "0.10.13",
"dependencies": {
"@scure/bip39": "^1.3.0",
"cojson": "workspace:*"

View File

@@ -3,7 +3,10 @@ import { entropyToMnemonic } from "@scure/bip39";
import { CryptoProvider, cojsonInternals } from "cojson";
import { Account } from "../coValues/account.js";
import type { ID } from "../internal.js";
import type { AuthenticateAccountFunction } from "../types.js";
import type {
AuthenticateAccountFunction,
RegisterAccountFunction,
} from "../types.js";
import { AuthSecretStorage } from "./AuthSecretStorage.js";
/**
@@ -23,6 +26,7 @@ export class PassphraseAuth {
constructor(
private crypto: CryptoProvider,
private authenticate: AuthenticateAccountFunction,
private register: RegisterAccountFunction,
private authSecretStorage: AuthSecretStorage,
public wordlist: string[],
) {}
@@ -61,7 +65,7 @@ export class PassphraseAuth {
this.notify();
};
signUp = async () => {
signUp = async (name?: string) => {
const credentials = await this.authSecretStorage.get();
if (!credentials || !credentials.secretSeed) {
@@ -77,9 +81,32 @@ export class PassphraseAuth {
provider: "passphrase",
});
if (name?.trim()) {
const currentAccount = await Account.getMe().ensureLoaded({
profile: {},
});
currentAccount.profile.name = name;
}
return passphrase;
};
registerNewAccount = async (passphrase: string, name: string) => {
const secretSeed = bip39.mnemonicToEntropy(passphrase, this.wordlist);
const accountSecret = this.crypto.agentSecretFromSecretSeed(secretSeed);
const accountID = await this.register(accountSecret, { name });
await this.authSecretStorage.set({
accountID,
secretSeed,
accountSecret,
provider: "passphrase",
});
return accountID;
};
getCurrentAccountPassphrase = async () => {
const credentials = await this.authSecretStorage.get();
@@ -90,6 +117,10 @@ export class PassphraseAuth {
return entropyToMnemonic(credentials.secretSeed, this.wordlist);
};
generateRandomPassphrase = () => {
return entropyToMnemonic(this.crypto.newRandomSecretSeed(), this.wordlist);
};
loadCurrentAccountPassphrase = async () => {
const passphrase = await this.getCurrentAccountPassphrase();
this.passphrase = passphrase;

View File

@@ -157,7 +157,7 @@ export class CoValueBase implements CoValue {
export function loadCoValueWithoutMe<V extends CoValue, Depth>(
cls: CoValueClass<V>,
id: ID<V>,
id: ID<CoValue>,
asOrDepth: Account | AnonymousJazzAgent | (Depth & DepthsIn<V>),
depth?: Depth & DepthsIn<V>,
) {
@@ -175,7 +175,7 @@ export function loadCoValueWithoutMe<V extends CoValue, Depth>(
export function loadCoValue<V extends CoValue, Depth>(
cls: CoValueClass<V>,
id: ID<V>,
id: ID<CoValue>,
as: Account | AnonymousJazzAgent,
depth: Depth & DepthsIn<V>,
): Promise<DeeplyLoaded<V, Depth> | undefined> {
@@ -216,7 +216,7 @@ export async function ensureCoValueLoaded<V extends CoValue, Depth>(
export function subscribeToCoValueWithoutMe<V extends CoValue, Depth>(
cls: CoValueClass<V>,
id: ID<V>,
id: ID<CoValue>,
asOrDepth: Account | AnonymousJazzAgent | (Depth & DepthsIn<V>),
depthOrListener:
| (Depth & DepthsIn<V>)
@@ -251,7 +251,7 @@ export function subscribeToCoValueWithoutMe<V extends CoValue, Depth>(
export function subscribeToCoValue<V extends CoValue, Depth>(
cls: CoValueClass<V>,
id: ID<V>,
id: ID<CoValue>,
as: Account | AnonymousJazzAgent,
depth: Depth & DepthsIn<V>,
listener: (value: DeeplyLoaded<V, Depth>, unsubscribe: () => void) => void,
@@ -263,7 +263,7 @@ export function subscribeToCoValue<V extends CoValue, Depth>(
let unsubscribed = false;
let unsubscribe: (() => void) | undefined;
function subscribe(value: V | undefined) {
function subscribe(value: CoValue | undefined) {
if (!value) {
onUnavailable && onUnavailable();
return;
@@ -313,7 +313,7 @@ export function createCoValueObservable<V extends CoValue, Depth>(options?: {
function subscribe(
cls: CoValueClass<V>,
id: ID<V>,
id: ID<CoValue>,
as: Account | AnonymousJazzAgent,
depth: Depth & DepthsIn<V>,
listener: () => void,

View File

@@ -80,6 +80,7 @@ export class JazzContextManager<
...context,
node: context.node,
authenticate: this.authenticate,
register: this.register,
logOut: this.logOut,
};
@@ -143,14 +144,59 @@ export class JazzContextManager<
this.authenticating = false;
});
if (wasAnonymous) {
await this.handleAnonymousAccountMigration(prevContext);
}
};
register = async (
accountSecret: AgentSecret,
creationProps: { name: string },
) => {
if (!this.props) {
throw new Error("Props required");
}
const prevContext = this.context;
const prevCredentials = await this.authSecretStorage.get();
const wasAnonymous =
this.authSecretStorage.getIsAuthenticated(prevCredentials) === false;
this.authenticating = true;
await this.createContext(this.props, {
newAccountProps: {
secret: accountSecret,
creationProps,
},
}).finally(() => {
this.authenticating = false;
});
if (wasAnonymous) {
await this.handleAnonymousAccountMigration(prevContext);
}
if (this.context && "me" in this.context) {
return this.context.me.id;
}
throw new Error("The registration hasn't created a new account");
};
private async handleAnonymousAccountMigration(
prevContext: PlatformSpecificContext<Acc> | undefined,
) {
if (!this.props) {
throw new Error("Props required");
}
const currentContext = this.context;
if (
prevContext &&
currentContext &&
"me" in prevContext &&
"me" in currentContext &&
wasAnonymous
"me" in currentContext
) {
// Using a direct connection to make coValue transfer almost synchronous
const [prevAccountAsPeer, currentAccountAsPeer] =
@@ -178,7 +224,7 @@ export class JazzContextManager<
}
prevContext?.done();
};
}
listeners = new Set<() => void>();
subscribe = (callback: () => void) => {

View File

@@ -338,4 +338,53 @@ describe("ContextManager", () => {
expect(me.root.transferredRoot?.value).toBe("Hello");
});
test("handles registration of new account", async () => {
const onAnonymousAccountDiscarded = vi.fn();
await manager.createContext({ onAnonymousAccountDiscarded });
const secret = Crypto.newRandomAgentSecret();
const accountId = await manager.register(secret, { name: "Test User" });
expect(accountId).toBeDefined();
const context = getCurrentValue();
expect(context.me.profile?.name).toBe("Test User");
expect(context.me.id).toBe(accountId);
});
test("calls onAnonymousAccountDiscarded when registering from anonymous user", async () => {
const onAnonymousAccountDiscarded = vi.fn();
await manager.createContext({ onAnonymousAccountDiscarded });
const anonymousAccount = getCurrentValue().me;
const secret = Crypto.newRandomAgentSecret();
await manager.register(secret, { name: "Test User" });
expect(onAnonymousAccountDiscarded).toHaveBeenCalledWith(anonymousAccount);
});
test("does not call onAnonymousAccountDiscarded when registering from authenticated user", async () => {
const onAnonymousAccountDiscarded = vi.fn();
const account = await createJazzTestAccount();
await manager.getAuthSecretStorage().set({
accountID: account.id,
accountSecret: account._raw.core.node.account.agentSecret,
provider: "test",
});
await manager.createContext({ onAnonymousAccountDiscarded });
const secret = Crypto.newRandomAgentSecret();
await manager.register(secret, { name: "New User" });
expect(onAnonymousAccountDiscarded).not.toHaveBeenCalled();
});
test("throws error when registering without props", async () => {
const secret = Crypto.newRandomAgentSecret();
await expect(
manager.register(secret, { name: "Test User" }),
).rejects.toThrow("Props required");
});
});

View File

@@ -2,6 +2,7 @@
import { mnemonicToEntropy } from "@scure/bip39";
import { AgentSecret } from "cojson";
import { PureJSCrypto } from "cojson/src/crypto/PureJSCrypto";
import {
Account,
AuthSecretStorage,
@@ -9,31 +10,41 @@ import {
InMemoryKVStore,
KvStoreContext,
} from "jazz-tools";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { assert, beforeEach, describe, expect, it, vi } from "vitest";
import { PassphraseAuth } from "../auth/PassphraseAuth";
import { createJazzTestAccount } from "../testing";
import { TestJSCrypto } from "../testing";
import {
TestJazzContextManager,
createJazzTestAccount,
setupJazzTestSync,
} from "../testing";
import { testWordlist } from "./fixtures";
// Initialize KV store for tests
KvStoreContext.getInstance().initialize(new InMemoryKVStore());
beforeEach(async () => {
await setupJazzTestSync();
});
describe("PassphraseAuth", () => {
let crypto: TestJSCrypto;
let crypto: PureJSCrypto;
let mockAuthenticate: any;
let mockRegister: any;
let authSecretStorage: AuthSecretStorage;
let passphraseAuth: PassphraseAuth;
let account: Account;
beforeEach(async () => {
// Reset storage
KvStoreContext.getInstance().getStorage().clearAll();
// Set up crypto and mocks
crypto = await TestJSCrypto.create();
crypto = await PureJSCrypto.create();
mockAuthenticate = vi.fn();
mockRegister = vi.fn();
authSecretStorage = new AuthSecretStorage();
await createJazzTestAccount({
account = await createJazzTestAccount({
isCurrentActiveAccount: true,
});
@@ -41,6 +52,7 @@ describe("PassphraseAuth", () => {
passphraseAuth = new PassphraseAuth(
crypto,
mockAuthenticate,
mockRegister,
authSecretStorage,
testWordlist,
);
@@ -121,6 +133,39 @@ describe("PassphraseAuth", () => {
"No credentials found",
);
});
it("should set account name when provided during signup", async () => {
const storageData = {
accountID: "test-account-id" as ID<Account>,
accountSecret: "test-secret" as AgentSecret,
secretSeed: new Uint8Array([
173, 58, 235, 40, 67, 188, 236, 11, 107, 237, 97, 23, 182, 49, 188,
63, 237, 52, 27, 84, 142, 66, 244, 149, 243, 114, 203, 164, 115, 239,
175, 194,
]),
provider: "anonymous",
};
await authSecretStorage.set(storageData);
const testName = "Test User";
await passphraseAuth.signUp(testName);
// Verify the account name was set
const { profile } = await account.ensureLoaded({
profile: {},
});
expect(profile.name).toBe(testName);
// Verify storage was updated correctly
const storedData = await authSecretStorage.get();
expect(storedData).toEqual({
accountID: storageData.accountID,
accountSecret: storageData.accountSecret,
secretSeed: storageData.secretSeed,
provider: "passphrase",
});
});
});
describe("getCurrentAccountPassphrase", () => {
@@ -150,3 +195,153 @@ describe("PassphraseAuth", () => {
});
});
});
// Initialize KV store for tests
KvStoreContext.getInstance().initialize(new InMemoryKVStore());
describe("PassphraseAuth with TestJazzContextManager", () => {
let crypto: PureJSCrypto;
let contextManager: TestJazzContextManager<any>;
let authSecretStorage: AuthSecretStorage;
let passphraseAuth: PassphraseAuth;
beforeEach(async () => {
// Reset storage
KvStoreContext.getInstance().getStorage().clearAll();
const account = await createJazzTestAccount({
isCurrentActiveAccount: true,
});
// Set up crypto and context manager
crypto = await PureJSCrypto.create();
contextManager = TestJazzContextManager.fromAccountOrGuest(account);
authSecretStorage = contextManager.getAuthSecretStorage();
// Create initial context
await contextManager.createContext({});
// Create PassphraseAuth instance
passphraseAuth = new PassphraseAuth(
crypto,
contextManager.authenticate,
contextManager.register,
authSecretStorage,
testWordlist,
);
});
describe("logIn", () => {
it("should successfully log in with valid passphrase", async () => {
// First sign up to create initial credentials
const passphrase = await passphraseAuth.signUp();
// Log out
await contextManager.logOut();
// Log back in with passphrase
await passphraseAuth.logIn(passphrase);
// Verify we're logged in
const context = contextManager.getCurrentValue();
assert(context && "me" in context);
// Verify storage was updated
const storedData = await authSecretStorage.get();
expect(storedData?.provider).toBe("passphrase");
});
it("should throw error with invalid passphrase", async () => {
await expect(passphraseAuth.logIn("invalid words here")).rejects.toThrow(
"Invalid passphrase",
);
});
});
describe("signUp", () => {
it("should successfully sign up new user", async () => {
expect(authSecretStorage.isAuthenticated).toBe(false);
const passphrase = await passphraseAuth.signUp();
expect(authSecretStorage.isAuthenticated).toBe(true);
// Verify passphrase format
expect(passphrase.split(" ").length).toBeGreaterThan(0);
// Verify storage was updated
const storedData = await authSecretStorage.get();
expect(storedData?.provider).toBe("passphrase");
// Verify we can log in with the passphrase
await contextManager.logOut();
await passphraseAuth.logIn(passphrase);
const context = contextManager.getCurrentValue();
assert(context && "me" in context);
expect(context.me).toBeDefined();
});
it("should throw error when no credentials found", async () => {
await authSecretStorage.clear();
await expect(passphraseAuth.signUp()).rejects.toThrow(
"No credentials found",
);
});
});
describe("registerNewAccount", () => {
it("should successfully register new account with passphrase", async () => {
expect(authSecretStorage.isAuthenticated).toBe(false);
const passphrase = passphraseAuth.generateRandomPassphrase();
const accountId = await passphraseAuth.registerNewAccount(
passphrase,
"Test User",
);
// Verify account was created
expect(accountId).toBeDefined();
// Verify we can log in with the passphrase
await contextManager.logOut();
await passphraseAuth.logIn(passphrase);
const context = contextManager.getCurrentValue();
assert(context && "me" in context);
expect(context.me.id).toBe(accountId);
expect(context.me.profile?.name).toBe("Test User");
expect(authSecretStorage.isAuthenticated).toBe(true);
const credentials = await authSecretStorage.get();
assert(credentials);
expect(credentials.accountID).toBe(accountId);
expect(credentials.provider).toBe("passphrase");
});
it("should throw error with invalid passphrase during registration", async () => {
await expect(
passphraseAuth.registerNewAccount("invalid words", "Test User"),
).rejects.toThrow();
});
});
describe("getCurrentAccountPassphrase", () => {
it("should return current user passphrase when credentials exist", async () => {
const originalPassphrase = await passphraseAuth.signUp();
const retrievedPassphrase =
await passphraseAuth.getCurrentAccountPassphrase();
expect(retrievedPassphrase).toBe(originalPassphrase);
});
it("should throw error when no credentials found", async () => {
await authSecretStorage.clear();
await expect(
passphraseAuth.getCurrentAccountPassphrase(),
).rejects.toThrow("No credentials found");
});
});
});

View File

@@ -12,6 +12,7 @@ export type AuthCredentials = {
export type AuthenticateAccountFunction = (
credentials: AuthCredentials,
) => Promise<void>;
export type RegisterAccountFunction = (
accountSecret: AgentSecret,
creationProps: { name: string },
@@ -22,6 +23,7 @@ export type JazzAuthContext<Acc extends Account> = {
me: Acc;
node: LocalNode;
authenticate: AuthenticateAccountFunction;
register: RegisterAccountFunction;
logOut: () => Promise<void>;
done: () => void;
isAuthenticated?: boolean;
@@ -31,6 +33,7 @@ export type JazzGuestContext = {
guest: AnonymousJazzAgent;
node: LocalNode;
authenticate: AuthenticateAccountFunction;
register: RegisterAccountFunction;
logOut: () => void;
done: () => void;
isAuthenticated?: boolean;

View File

@@ -1,5 +1,22 @@
# jazz-react
## 0.10.13
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-browser@0.10.13
## 0.10.12
### Patch Changes
- 4612e05: Fix type inference on `useCoState`
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
- jazz-browser@0.10.12
## 0.10.9
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "jazz-vue",
"version": "0.10.9",
"version": "0.10.13",
"type": "module",
"main": "dist/index.js",
"types": "src/index.ts",

View File

@@ -30,6 +30,7 @@ export function usePassphraseAuth({
return new PassphraseAuth(
context.value.node.crypto,
context.value.authenticate,
context.value.register,
authSecretStorage,
wordlist,
);
@@ -51,6 +52,8 @@ export function usePassphraseAuth({
state: isAuthenticated.value ? "signedIn" : "anonymous",
logIn: authMethod.value.logIn,
signUp: authMethod.value.signUp,
registerNewAccount: authMethod.value.registerNewAccount,
generateRandomPassphrase: authMethod.value.generateRandomPassphrase,
passphrase: passphrase.value,
}));
}

Some files were not shown because too many files have changed in this diff Show More