Compare commits

..

3 Commits

Author SHA1 Message Date
Guido D'Orsi
70437048fb Revert "Fix Error - 'ErrorLoadingCoValueCore'"
This reverts commit 0d43b2edbc.
2024-11-22 11:34:28 +01:00
Guido D'Orsi
72af643286 Revert "fix(coValueState): once a coValue is loaded send the known state to the rest of the peers"
This reverts commit a5559529ae.
2024-11-22 11:28:21 +01:00
Guido D'Orsi
35e2827bb7 Revert "Send empty known state on all states except available"
This reverts commit db5227b463.
2024-11-22 11:28:10 +01:00
130 changed files with 843 additions and 2846 deletions

View File

@@ -1,44 +1,5 @@
# @jazz-e2e/binarycostream
## 0.0.109
### Patch Changes
- Updated dependencies [df42b2b]
- Updated dependencies [df42b2b]
- cojson@0.8.32
- jazz-tools@0.8.32
- jazz-react@0.8.32
## 0.0.108
### Patch Changes
- Updated dependencies [e511d6d]
- cojson@0.8.31
- jazz-react@0.8.31
- jazz-tools@0.8.31
## 0.0.107
### Patch Changes
- Updated dependencies [0a2fae3]
- Updated dependencies [99cda2f]
- cojson@0.8.30
- jazz-react@0.8.30
- jazz-tools@0.8.30
## 0.0.106
### Patch Changes
- Updated dependencies [dcc9c2e]
- Updated dependencies [699553f]
- cojson@0.8.29
- jazz-react@0.8.29
- jazz-tools@0.8.29
## 0.0.105
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "@jazz-e2e/filestream",
"private": true,
"version": "0.0.109",
"version": "0.0.105",
"type": "module",
"scripts": {
"dev": "vite",
@@ -13,11 +13,11 @@
"test:ui": "playwright test --ui"
},
"dependencies": {
"cojson": "workspace:0.8.32",
"cojson": "workspace:0.8.28",
"hash-slash": "workspace:0.2.1",
"is-ci": "^3.0.1",
"jazz-react": "workspace:0.8.32",
"jazz-tools": "workspace:0.8.32",
"jazz-react": "workspace:0.8.28",
"jazz-tools": "workspace:0.8.28",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},

View File

@@ -1,44 +1,5 @@
# @jazz-e2e/covalues
## 0.0.108
### Patch Changes
- Updated dependencies [df42b2b]
- Updated dependencies [df42b2b]
- cojson@0.8.32
- jazz-tools@0.8.32
- jazz-react@0.8.32
## 0.0.107
### Patch Changes
- Updated dependencies [e511d6d]
- cojson@0.8.31
- jazz-react@0.8.31
- jazz-tools@0.8.31
## 0.0.106
### Patch Changes
- Updated dependencies [0a2fae3]
- Updated dependencies [99cda2f]
- cojson@0.8.30
- jazz-react@0.8.30
- jazz-tools@0.8.30
## 0.0.105
### Patch Changes
- Updated dependencies [dcc9c2e]
- Updated dependencies [699553f]
- cojson@0.8.29
- jazz-react@0.8.29
- jazz-tools@0.8.29
## 0.0.104
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "@jazz-e2e/covalues",
"private": true,
"version": "0.0.108",
"version": "0.0.104",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,38 +1,5 @@
# jazz-example-book-shelf
## 0.1.24
### Patch Changes
- Updated dependencies [df42b2b]
- jazz-tools@0.8.32
- jazz-react@0.8.32
- jazz-browser-media-images@0.8.32
## 0.1.23
### Patch Changes
- jazz-react@0.8.31
- jazz-tools@0.8.31
- jazz-browser-media-images@0.8.31
## 0.1.22
### Patch Changes
- jazz-react@0.8.30
- jazz-tools@0.8.30
- jazz-browser-media-images@0.8.30
## 0.1.21
### Patch Changes
- jazz-react@0.8.29
- jazz-tools@0.8.29
- jazz-browser-media-images@0.8.29
## 0.1.20
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "jazz-example-book-shelf",
"version": "0.1.24",
"version": "0.1.20",
"private": true,
"scripts": {
"dev": "next dev",
@@ -11,9 +11,9 @@
},
"dependencies": {
"clsx": "^2.0.0",
"jazz-browser-media-images": "workspace:0.8.32",
"jazz-react": "workspace:0.8.32",
"jazz-tools": "workspace:0.8.32",
"jazz-browser-media-images": "workspace:0.8.28",
"jazz-react": "workspace:0.8.28",
"jazz-tools": "workspace:0.8.28",
"next": "14.2.5",
"react": "^18.2.0",
"react-dom": "^18.2.0"

View File

@@ -1,49 +1,5 @@
# jazz-example-chat
## 0.0.108
### Patch Changes
- Updated dependencies [df42b2b]
- Updated dependencies [1a4bda0]
- Updated dependencies [df42b2b]
- cojson@0.8.32
- jazz-react-auth-clerk@0.8.32
- jazz-tools@0.8.32
- jazz-react@0.8.32
## 0.0.107
### Patch Changes
- Updated dependencies [e511d6d]
- cojson@0.8.31
- jazz-react@0.8.31
- jazz-react-auth-clerk@0.8.31
- jazz-tools@0.8.31
## 0.0.106
### Patch Changes
- Updated dependencies [0a2fae3]
- Updated dependencies [99cda2f]
- cojson@0.8.30
- jazz-react@0.8.30
- jazz-react-auth-clerk@0.8.30
- jazz-tools@0.8.30
## 0.0.105
### Patch Changes
- Updated dependencies [dcc9c2e]
- Updated dependencies [699553f]
- cojson@0.8.29
- jazz-react@0.8.29
- jazz-react-auth-clerk@0.8.29
- jazz-tools@0.8.29
## 0.0.104
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-chat-clerk",
"private": true,
"version": "0.0.108",
"version": "0.0.104",
"type": "module",
"scripts": {
"dev": "vite",
@@ -17,11 +17,11 @@
"@radix-ui/react-toast": "^1.1.4",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"cojson": "workspace:0.8.32",
"cojson": "workspace:0.8.28",
"hash-slash": "workspace:0.2.1",
"jazz-react": "workspace:0.8.32",
"jazz-react-auth-clerk": "workspace:0.8.32",
"jazz-tools": "workspace:0.8.32",
"jazz-react": "workspace:0.8.28",
"jazz-react-auth-clerk": "workspace:0.8.28",
"jazz-tools": "workspace:0.8.28",
"lucide-react": "^0.274.0",
"qrcode": "^1.5.3",
"react": "^18.2.0",

View File

@@ -1,43 +1,5 @@
# chat-rn-clerk
## 1.0.24
### Patch Changes
- Updated dependencies [1a4bda0]
- Updated dependencies [df42b2b]
- jazz-react-auth-clerk@0.8.32
- jazz-tools@0.8.32
- jazz-react-native@0.8.32
- jazz-react-native-media-images@0.8.24
## 1.0.23
### Patch Changes
- jazz-react-auth-clerk@0.8.31
- jazz-react-native@0.8.31
- jazz-tools@0.8.31
- jazz-react-native-media-images@0.8.23
## 1.0.22
### Patch Changes
- jazz-react-auth-clerk@0.8.30
- jazz-react-native@0.8.30
- jazz-tools@0.8.30
- jazz-react-native-media-images@0.8.22
## 1.0.21
### Patch Changes
- jazz-react-native@0.8.29
- jazz-react-auth-clerk@0.8.29
- jazz-tools@0.8.29
- jazz-react-native-media-images@0.8.21
## 1.0.20
### Patch Changes

View File

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

View File

@@ -1,34 +1,5 @@
# chat-rn
## 1.0.23
### Patch Changes
- Updated dependencies [df42b2b]
- jazz-tools@0.8.32
- jazz-react-native@0.8.32
## 1.0.22
### Patch Changes
- jazz-react-native@0.8.31
- jazz-tools@0.8.31
## 1.0.21
### Patch Changes
- jazz-react-native@0.8.30
- jazz-tools@0.8.30
## 1.0.20
### Patch Changes
- jazz-react-native@0.8.29
- jazz-tools@0.8.29
## 1.0.19
### Patch Changes

View File

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

View File

@@ -1,38 +1,5 @@
# chat-vue
## 0.0.15
### Patch Changes
- Updated dependencies [df42b2b]
- jazz-tools@0.8.32
- jazz-browser@0.8.32
- jazz-vue@0.8.20
## 0.0.14
### Patch Changes
- jazz-browser@0.8.31
- jazz-tools@0.8.31
- jazz-vue@0.8.19
## 0.0.13
### Patch Changes
- jazz-browser@0.8.30
- jazz-tools@0.8.30
- jazz-vue@0.8.18
## 0.0.12
### Patch Changes
- jazz-browser@0.8.29
- jazz-tools@0.8.29
- jazz-vue@0.8.17
## 0.0.11
### Patch Changes

View File

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

View File

@@ -1,44 +1,5 @@
# jazz-example-chat
## 0.0.110
### Patch Changes
- Updated dependencies [df42b2b]
- Updated dependencies [df42b2b]
- cojson@0.8.32
- jazz-tools@0.8.32
- jazz-react@0.8.32
## 0.0.109
### Patch Changes
- Updated dependencies [e511d6d]
- cojson@0.8.31
- jazz-react@0.8.31
- jazz-tools@0.8.31
## 0.0.108
### Patch Changes
- Updated dependencies [0a2fae3]
- Updated dependencies [99cda2f]
- cojson@0.8.30
- jazz-react@0.8.30
- jazz-tools@0.8.30
## 0.0.107
### Patch Changes
- Updated dependencies [dcc9c2e]
- Updated dependencies [699553f]
- cojson@0.8.29
- jazz-react@0.8.29
- jazz-tools@0.8.29
## 0.0.106
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-chat",
"private": true,
"version": "0.0.110",
"version": "0.0.106",
"type": "module",
"scripts": {
"dev": "vite",
@@ -18,10 +18,10 @@
"@radix-ui/react-toast": "^1.1.4",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"cojson": "workspace:0.8.32",
"cojson": "workspace:0.8.28",
"hash-slash": "workspace:0.2.1",
"jazz-react": "workspace:0.8.32",
"jazz-tools": "workspace:0.8.32",
"jazz-react": "workspace:0.8.28",
"jazz-tools": "workspace:0.8.28",
"lucide-react": "^0.274.0",
"qrcode": "^1.5.3",
"react": "^18.2.0",

View File

@@ -1,39 +1,5 @@
# jazz-example-inspector
## 0.0.80
### Patch Changes
- Updated dependencies [df42b2b]
- cojson@0.8.32
- cojson-transport-ws@0.8.32
## 0.0.79
### Patch Changes
- Updated dependencies [e511d6d]
- cojson@0.8.31
- cojson-transport-ws@0.8.31
## 0.0.78
### Patch Changes
- Updated dependencies [0a2fae3]
- Updated dependencies [99cda2f]
- cojson@0.8.30
- cojson-transport-ws@0.8.30
## 0.0.77
### Patch Changes
- Updated dependencies [dcc9c2e]
- Updated dependencies [699553f]
- cojson-transport-ws@0.8.29
- cojson@0.8.29
## 0.0.76
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-inspector",
"private": true,
"version": "0.0.80",
"version": "0.0.76",
"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",
"cojson": "workspace:0.8.32",
"cojson-transport-ws": "workspace:0.8.32",
"cojson": "workspace:0.8.28",
"cojson-transport-ws": "workspace:0.8.28",
"hash-slash": "workspace:0.2.1",
"lucide-react": "^0.274.0",
"qrcode": "^1.5.3",

View File

@@ -1,39 +1,5 @@
# minimal-auth-clerk
## 0.0.9
### Patch Changes
- Updated dependencies [1a4bda0]
- Updated dependencies [df42b2b]
- jazz-react-auth-clerk@0.8.32
- jazz-tools@0.8.32
- jazz-react@0.8.32
## 0.0.8
### Patch Changes
- jazz-react@0.8.31
- jazz-react-auth-clerk@0.8.31
- jazz-tools@0.8.31
## 0.0.7
### Patch Changes
- jazz-react@0.8.30
- jazz-react-auth-clerk@0.8.30
- jazz-tools@0.8.30
## 0.0.6
### Patch Changes
- jazz-react@0.8.29
- jazz-react-auth-clerk@0.8.29
- jazz-tools@0.8.29
## 0.0.5
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "minimal-auth-clerk",
"private": true,
"version": "0.0.9",
"version": "0.0.5",
"type": "module",
"scripts": {
"dev": "vite",
@@ -14,7 +14,7 @@
"@clerk/clerk-react": "^5.4.1",
"jazz-tools": "workspace:*",
"jazz-react": "workspace:*",
"jazz-react-auth-clerk": "workspace:0.8.32",
"jazz-react-auth-clerk": "workspace:0.8.28",
"react": "^18.3.1",
"react-dom": "^18.3.1"
},

View File

@@ -1,34 +1,5 @@
# minimal-auth-passkey
## 0.0.8
### Patch Changes
- Updated dependencies [df42b2b]
- jazz-tools@0.8.32
- jazz-react@0.8.32
## 0.0.7
### Patch Changes
- jazz-react@0.8.31
- jazz-tools@0.8.31
## 0.0.6
### Patch Changes
- jazz-react@0.8.30
- jazz-tools@0.8.30
## 0.0.5
### Patch Changes
- jazz-react@0.8.29
- jazz-tools@0.8.29
## 0.0.4
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "minimal-auth-passkey",
"private": true,
"version": "0.0.8",
"version": "0.0.4",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,34 +1,5 @@
# jazz-example-musicplayer
## 0.0.30
### Patch Changes
- Updated dependencies [df42b2b]
- jazz-tools@0.8.32
- jazz-react@0.8.32
## 0.0.29
### Patch Changes
- jazz-react@0.8.31
- jazz-tools@0.8.31
## 0.0.28
### Patch Changes
- jazz-react@0.8.30
- jazz-tools@0.8.30
## 0.0.27
### Patch Changes
- jazz-react@0.8.29
- jazz-tools@0.8.29
## 0.0.26
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-music-player",
"private": true,
"version": "0.0.30",
"version": "0.0.26",
"type": "module",
"scripts": {
"dev": "vite",
@@ -18,8 +18,8 @@
"@radix-ui/react-toast": "^1.1.4",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"jazz-react": "workspace:0.8.32",
"jazz-tools": "workspace:0.8.32",
"jazz-react": "workspace:0.8.28",
"jazz-tools": "workspace:0.8.28",
"lucide-react": "^0.274.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",

View File

@@ -1,38 +1,5 @@
# jazz-example-onboarding
## 0.0.11
### Patch Changes
- Updated dependencies [df42b2b]
- jazz-tools@0.8.32
- jazz-react@0.8.32
- jazz-browser-media-images@0.8.32
## 0.0.10
### Patch Changes
- jazz-react@0.8.31
- jazz-tools@0.8.31
- jazz-browser-media-images@0.8.31
## 0.0.9
### Patch Changes
- jazz-react@0.8.30
- jazz-tools@0.8.30
- jazz-browser-media-images@0.8.30
## 0.0.8
### Patch Changes
- jazz-react@0.8.29
- jazz-tools@0.8.29
- jazz-browser-media-images@0.8.29
## 0.0.7
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-onboarding",
"private": true,
"version": "0.0.11",
"version": "0.0.7",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,34 +1,5 @@
# jazz-password-manager
## 0.0.29
### Patch Changes
- Updated dependencies [df42b2b]
- jazz-tools@0.8.32
- jazz-react@0.8.32
## 0.0.28
### Patch Changes
- jazz-react@0.8.31
- jazz-tools@0.8.31
## 0.0.27
### Patch Changes
- jazz-react@0.8.30
- jazz-tools@0.8.30
## 0.0.26
### Patch Changes
- jazz-react@0.8.29
- jazz-tools@0.8.29
## 0.0.25
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-password-manager",
"private": true,
"version": "0.0.29",
"version": "0.0.25",
"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.8.32",
"jazz-tools": "workspace:0.8.32",
"jazz-react": "workspace:0.8.28",
"jazz-tools": "workspace:0.8.28",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hook-form": "^7.41.5",

View File

@@ -1,38 +1,5 @@
# jazz-example-pets
## 0.0.127
### Patch Changes
- Updated dependencies [df42b2b]
- jazz-tools@0.8.32
- jazz-react@0.8.32
- jazz-browser-media-images@0.8.32
## 0.0.126
### Patch Changes
- jazz-react@0.8.31
- jazz-tools@0.8.31
- jazz-browser-media-images@0.8.31
## 0.0.125
### Patch Changes
- jazz-react@0.8.30
- jazz-tools@0.8.30
- jazz-browser-media-images@0.8.30
## 0.0.124
### Patch Changes
- jazz-react@0.8.29
- jazz-tools@0.8.29
- jazz-browser-media-images@0.8.29
## 0.0.123
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-pets",
"private": true,
"version": "0.0.127",
"version": "0.0.123",
"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.8.32",
"jazz-react": "workspace:0.8.32",
"jazz-tools": "workspace:0.8.32",
"jazz-browser-media-images": "workspace:0.8.28",
"jazz-react": "workspace:0.8.28",
"jazz-tools": "workspace:0.8.28",
"lucide-react": "^0.274.0",
"qrcode": "^1.5.3",
"react": "^18.2.0",
@@ -41,7 +41,7 @@
"@vitejs/plugin-react-swc": "^3.3.2",
"autoprefixer": "^10.4.14",
"is-ci": "^3.0.1",
"jazz-run": "workspace:0.8.32",
"jazz-run": "workspace:0.8.28",
"postcss": "^8.4.27",
"tailwindcss": "3.3.2",
"typescript": "^5.3.3",

View File

@@ -1,38 +1,5 @@
# todo-vue
## 0.0.13
### Patch Changes
- Updated dependencies [df42b2b]
- jazz-tools@0.8.32
- jazz-browser@0.8.32
- jazz-vue@0.8.20
## 0.0.12
### Patch Changes
- jazz-browser@0.8.31
- jazz-tools@0.8.31
- jazz-vue@0.8.19
## 0.0.11
### Patch Changes
- jazz-browser@0.8.30
- jazz-tools@0.8.30
- jazz-vue@0.8.18
## 0.0.10
### Patch Changes
- jazz-browser@0.8.29
- jazz-tools@0.8.29
- jazz-vue@0.8.17
## 0.0.9
### Patch Changes

View File

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

View File

@@ -1,34 +1,5 @@
# jazz-example-todo
## 0.0.126
### Patch Changes
- Updated dependencies [df42b2b]
- jazz-tools@0.8.32
- jazz-react@0.8.32
## 0.0.125
### Patch Changes
- jazz-react@0.8.31
- jazz-tools@0.8.31
## 0.0.124
### Patch Changes
- jazz-react@0.8.30
- jazz-tools@0.8.30
## 0.0.123
### Patch Changes
- jazz-react@0.8.29
- jazz-tools@0.8.29
## 0.0.122
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-todo",
"private": true,
"version": "0.0.126",
"version": "0.0.122",
"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.8.32",
"jazz-tools": "workspace:0.8.32",
"jazz-react": "workspace:0.8.28",
"jazz-tools": "workspace:0.8.28",
"lucide-react": "^0.274.0",
"qrcode": "^1.5.3",
"react": "^18.2.0",

View File

@@ -12,7 +12,6 @@
"dependencies": {
"@evilmartians/harmony": "^1.0.0",
"@headlessui/react": "^2.2.0",
"@icons-pack/react-simple-icons": "^9.1.0",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"lucide-react": "^0.436.0",
@@ -20,7 +19,6 @@
"next-themes": "^0.2.1",
"react": "^18",
"react-dom": "^18",
"resend": "^4.0.0",
"tailwind-merge": "^1.14.0",
"tailwindcss-animate": "^1.0.7"
},

View File

@@ -1,7 +1,5 @@
import { clsx } from "clsx";
import { LucideIcon } from "lucide-react";
import Link from "next/link";
import { Spinner } from "./Spinner";
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
children: React.ReactNode;
@@ -9,19 +7,6 @@ interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
size?: "sm" | "md" | "lg";
href?: string;
newTab?: boolean;
icon?: LucideIcon;
loading?: boolean;
loadingText?: string;
}
function ButtonIcon({ icon: Icon, loading }: ButtonProps) {
if (!Icon) return null;
const className = "size-5";
if (loading) return <Spinner className={className} />;
return <Icon strokeWidth={1.5} className={className} />;
}
export function Button(props: ButtonProps) {
@@ -33,7 +18,6 @@ export function Button(props: ButtonProps) {
href,
disabled,
newTab,
loadingText,
} = props;
const sizeClasses = {
@@ -53,7 +37,6 @@ export function Button(props: ButtonProps) {
const classNames = clsx(
className,
"inline-flex items-center justify-center gap-2 rounded-lg text-center transition-colors",
"disabled:pointer-events-none disabled:opacity-70",
sizeClasses[size],
variantClasses[variant],
disabled && "opacity-50 cursor-not-allowed pointer-events-none",
@@ -66,21 +49,14 @@ export function Button(props: ButtonProps) {
target={newTab ? "_blank" : undefined}
className={classNames}
>
<ButtonIcon {...props} />
{children}
</Link>
);
}
return (
<button
{...props}
disabled={props.disabled || props.loading}
className={classNames}
>
<ButtonIcon {...props} />
{props.loading && props.loadingText ? props.loadingText : children}
<button {...props} className={classNames}>
{children}
</button>
);
}

View File

@@ -1,12 +0,0 @@
import { clsx } from "clsx";
export function Card({
children,
className,
}: { children: React.ReactNode; className?: string }) {
return (
<div className={clsx(className, "border rounded-xl shadow-sm")}>
{children}
</div>
);
}

View File

@@ -1,16 +1,16 @@
import clsx from "clsx";
import { ReactNode } from "react";
import { Card } from "../atoms/Card";
export function GridCard(props: { children: ReactNode; className?: string }) {
return (
<Card
<div
className={clsx(
"col-span-2 p-4 [&>h4]:mt-0 [&>h3]:mt-0 [&>:last-child]:mb-0",
"border rounded-xl shadow-sm",
props.className,
)}
>
{props.children}
</Card>
</div>
);
}

View File

@@ -1,26 +0,0 @@
import clsx from "clsx";
export function Spinner({ className }: { className?: string }) {
return (
<svg
className={clsx(className, "animate-spin")}
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle
className="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
stroke-width="4"
></circle>
<path
className="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
></path>
</svg>
);
}

View File

@@ -1,36 +0,0 @@
import clsx from "clsx";
import { LucideIcon } from "lucide-react";
import { Card } from "../atoms/Card";
import { Prose } from "./Prose";
export function FeatureCard({
label,
icon: Icon,
explanation,
children,
className,
}: {
label: React.ReactNode;
icon?: LucideIcon;
explanation?: React.ReactNode;
children?: React.ReactNode;
className?: string;
}) {
return (
<Card className={clsx(className, "p-4")}>
{Icon && (
<Icon
className="size-8 text-blue p-1.5 rounded-lg bg-blue-50 dark:text-blue-500 dark:bg-stone-900 mb-2.5 md:size-10"
strokeWidth={1.5}
strokeLinecap="butt"
size={80}
/>
)}
<div className="text-stone-900 font-medium md:text-base dark:text-stone-100 mb-2">
{label}
</div>
{explanation && <Prose>{explanation}</Prose>}
{children}
</Card>
);
}

View File

@@ -6,23 +6,15 @@ export function GappedGrid({
children,
className,
title,
cols = 3,
}: {
children: ReactNode;
className?: string;
title?: string;
cols?: 3 | 4;
}) {
const colsClassName =
cols === 3
? "md:grid-cols-4 lg:grid-cols-6"
: "sm:grid-cols-2 lg:grid-cols-4";
return (
<div
className={clsx(
"grid grid-cols-2 gap-4 lg:gap-8",
colsClassName,
"grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-4 lg:gap-8",
"items-stretch",
className,
)}

View File

@@ -0,0 +1,37 @@
import clsx from "clsx";
import { LucideIcon } from "lucide-react";
import { Prose } from "./Prose";
export function LabelledFeatureIcon({
label,
icon: Icon,
explanation,
className,
}: {
label: string;
icon: LucideIcon;
explanation: React.ReactNode;
className?: string;
}) {
return (
<div
className={clsx(
className,
"text-base",
"rounded-xl",
"border p-4 shadow-sm",
)}
>
<Icon
className="size-8 text-blue p-1.5 rounded-lg bg-blue-50 dark:text-blue-500 dark:bg-stone-900 mb-2.5 md:size-10"
strokeWidth={1.5}
strokeLinecap="butt"
size={80}
/>
<div className="text-stone-900 font-medium md:text-base dark:text-stone-100 mb-2">
{label}
</div>
<Prose>{explanation}</Prose>
</div>
);
}

View File

@@ -5,8 +5,6 @@ import Link from "next/link";
import { usePathname } from "next/navigation";
import { ReactNode } from "react";
import { ThemeToggle } from "../molecules/ThemeToggle";
import { NewsletterForm } from "./NewsletterForm";
import { SocialLinks, SocialLinksProps } from "./SocialLinks";
type FooterSection = {
title: string;
@@ -21,7 +19,6 @@ type FooterProps = {
logo: ReactNode;
companyName: string;
sections: FooterSection[];
socials: SocialLinksProps;
};
function Copyright({
@@ -38,47 +35,38 @@ function Copyright({
);
}
export function Footer({ logo, companyName, sections, socials }: FooterProps) {
export function Footer({ logo, companyName, sections }: FooterProps) {
return (
<footer className="w-full border-t py-8 mt-12 md:mt-20">
<div className="container grid gap-8 md:gap-12">
<div className=" grid gap-y-8 grid-cols-12">
<div className="flex flex-col gap-6 justify-between col-span-full md:col-span-7">
{logo}
<NewsletterForm />
</div>
<footer className="w-full border-t bg-stone-100 mt-12 md:mt-20 dark:bg-stone-925">
<div className="container py-8 md:py-16 grid gap-y-8 grid-cols-12">
<div className="flex flex-col justify-between col-span-full md:col-span-4">
{logo}
{sections.map((section, index) => (
<div
key={index}
className="flex flex-col gap-2 text-sm col-span-6 md:col-span-2"
>
<h2 className="font-medium">{section.title}</h2>
{section.links.map((link, linkIndex) => (
<FooterLink
key={linkIndex}
href={link.href}
newTab={link.newTab}
>
{link.label}
</FooterLink>
))}
</div>
))}
<div className="hidden md:flex justify-end items-end md:col-span-1">
<ThemeToggle />
</div>
<Copyright className="hidden md:block" companyName={companyName} />
</div>
<div className="flex flex-col justify-between gap-y-6 gap-3 md:flex-row">
<Copyright companyName={companyName} />
{sections.map((section, index) => (
<div
key={index}
className="flex flex-col gap-2 text-sm col-span-4 sm:col-span-4 md:col-span-2"
>
<h2 className="font-medium">{section.title}</h2>
{section.links.map((link, linkIndex) => (
<FooterLink key={linkIndex} href={link.href} newTab={link.newTab}>
{link.label}
</FooterLink>
))}
</div>
))}
<SocialLinks
{...socials}
className="order-first md:order-last"
></SocialLinks>
<div className="hidden md:flex justify-end items-end md:col-span-2">
<ThemeToggle />
</div>
<Copyright
className="col-span-full md:hidden"
companyName={companyName}
/>
</div>
</footer>
);

View File

@@ -14,7 +14,6 @@ import { usePathname } from "next/navigation";
import { ReactNode, useEffect, useLayoutEffect, useRef, useState } from "react";
import { BreadCrumb } from "../molecules/Breadcrumb";
import { ThemeToggle } from "../molecules/ThemeToggle";
import { SocialLinks, SocialLinksProps } from "./SocialLinks";
type NavItemProps = {
href: string;
@@ -31,7 +30,6 @@ type NavProps = {
items: NavItemProps[];
docNav?: ReactNode;
cta?: ReactNode;
socials?: SocialLinksProps;
};
function NavItem({
@@ -112,7 +110,7 @@ function NavItem({
);
}
export function MobileNav({ mainLogo, items, docNav, cta, socials }: NavProps) {
export function MobileNav({ mainLogo, items, docNav, cta }: NavProps) {
const [menuOpen, setMenuOpen] = useState(false);
const [searchOpen, setSearchOpen] = useState(false);
const searchRef = useRef<HTMLInputElement>(null);
@@ -172,8 +170,13 @@ export function MobileNav({ mainLogo, items, docNav, cta, socials }: NavProps) {
>
{mainLogo}
</NavLinkLogo>
<SocialLinks className="px-2 gap-2" {...socials} />
{items
.filter((item) => "icon" in item)
.map((item, i) => (
<NavLinkLogo key={i} href={item.href} newTab={item.newTab}>
{item.icon}
</NavLinkLogo>
))}
</div>
{pathname.startsWith("/docs") && docNav && (
@@ -307,8 +310,6 @@ export function Nav(props: NavProps) {
/>
))}
<SocialLinks {...props.socials} />
{cta}
</PopoverGroup>
</div>

View File

@@ -1,48 +0,0 @@
import { SiDiscord, SiGithub, SiX } from "@icons-pack/react-simple-icons";
import { clsx } from "clsx";
export interface SocialLinksProps {
github?: string;
x?: string;
discord?: string;
}
const socials = [
{
name: "Github",
icon: SiGithub,
key: "github",
},
{
name: "Discord",
icon: SiDiscord,
key: "discord",
},
{
name: "X",
icon: SiX,
key: "x",
},
];
export function SocialLinks(props: SocialLinksProps & { className?: string }) {
return (
<div className={clsx(props.className, "flex gap-6 ")}>
{socials.map(
(social) =>
props[social.key as keyof SocialLinksProps] && (
<a
key={social.key}
href={props[social.key as keyof SocialLinksProps]}
target="_blank"
rel="noreferrer"
className="flex items-center gap-2 hover:text-stone-900 hover:dark:text-white"
>
<social.icon className="w-5" />
<span className="sr-only">{social.name}</span>
</a>
),
)}
</div>
);
}

View File

@@ -1,5 +1,4 @@
import { Prose } from "@components/molecules/Prose";
import { NewsletterForm } from "@components/organisms/NewsletterForm";
export default function Home() {
return (
@@ -64,12 +63,6 @@ export default function Home() {
</Prose>
</div>
</div>
<h2>Newsletter Subscription Form</h2>
<div className="p-3 border">
<NewsletterForm />
</div>
</main>
);
}

View File

@@ -1,7 +1,7 @@
import { NewsletterForm } from "@/components/NewsletterForm";
import { Posts } from "@/components/blog/Posts";
import { HeroHeader } from "gcmp-design-system/src/app/components/molecules/HeroHeader";
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
import { NewsletterForm } from "gcmp-design-system/src/app/components/organisms/NewsletterForm";
import Link from "next/link";
export const metadata = {

View File

@@ -69,15 +69,6 @@ const team: Array<TeamMember> = [
github: "marinoska",
image: "marina.jpeg",
},
{
name: "Giordano Ricci",
titles: ["Full-Stack Dev", "DevOps"],
location: "Lisbon, Portugal ",
linkedin: "giordanoricci",
github: "Elfo404",
website: "https://giordanoricci.com",
image: "gio.jpg",
},
];
function SocialLink({

View File

@@ -1,37 +1,30 @@
"use client";
import { CheckIcon, MailIcon } from "lucide-react";
import { subscribe } from "@/app/actions/resend";
import { Button } from "gcmp-design-system/src/app/components/atoms/Button";
import { Input } from "gcmp-design-system/src/app/components/molecules/Input";
import { CheckIcon } from "lucide-react";
import { useState } from "react";
import { ErrorResponse } from "resend";
import { subscribe } from "../../../actions/resend";
import { Button } from "../atoms/Button";
import { Input } from "../molecules/Input";
export function NewsletterForm() {
const [email, setEmail] = useState("");
// const [subscribed, setSubscribed] = useState(false);
const [subscribed, setSubscribed] = useState(false);
const [error, setError] = useState<ErrorResponse | undefined>();
const [state, setState] = useState<"ready" | "loading" | "success" | "error">(
"ready",
);
const submit = async (e: React.FormEvent) => {
e.preventDefault();
setState("loading");
const res = await subscribe(email);
if (res.error) {
setError(res.error);
setState("error");
} else {
setState("success");
setSubscribed(true);
}
};
if (state === "success") {
if (subscribed) {
return (
<div className="flex gap-3 items-center">
<CheckIcon className="text-green-500" size={16} />
@@ -40,12 +33,12 @@ export function NewsletterForm() {
);
}
if (state === "error" && error?.message) {
if (error) {
return <p className="text-red-700">Error: {error.message}</p>;
}
return (
<form action="" onSubmit={submit} className="flex gap-x-4 w-120 max-w-md">
<form action="" onSubmit={submit} className="flex gap-x-4 w-120 max-w-xl">
<Input
id="email-address"
name="email"
@@ -58,13 +51,7 @@ export function NewsletterForm() {
className="flex-1 label:sr-only"
label="Email address"
/>
<Button
type="submit"
variant="secondary"
loadingText="Subscribing..."
loading={state === "loading"}
icon={MailIcon}
>
<Button type="submit" variant="secondary">
Subscribe
</Button>
</form>

View File

@@ -28,6 +28,7 @@
"next": "14.2.15",
"react": "^18",
"react-dom": "^18",
"resend": "^4.0.0",
"shiki": "^0.14.6",
"shiki-twoslash": "^3.1.2",
"tailwind-merge": "^1.14.0",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

View File

@@ -15,7 +15,7 @@ export default function Layout({
}) {
return (
<DocsLayout nav={<ApiNav />}>
<Prose className="py-8 [&_*]:scroll-mt-[8rem]">{children}</Prose>
<Prose className="py-8">{children}</Prose>
</DocsLayout>
);
}

View File

@@ -1,111 +0,0 @@
import { CodeGroup } from "@/components/forMdx";
# Authentication methods
Jazz supports a variety of authentication methods, which you can use to authenticate users in your app.
- Passphrase (built-in)
- Passkey (built-in)
- Clerk ([React](https://www.npmjs.com/package/jazz-react-auth-clerk) and [vanilla](https://www.npmjs.com/package/jazz-browser-auth-clerk) packages)
## Passphrase
Passphrase authentication allows users to create a new account or log in with an existing one by providing a passphrase.
Passphrase authentication is supported out of the box and imported from `jazz-react`.
### How to use
1. Setup up Jazz as described in the [React setup guide](/docs/project-setup/react).
2. Use the `usePassphraseAuth` hook to authenticate.
<CodeGroup>
```ts
import { usePassphraseAuth } from "jazz-react";
// ...
const [passphraseAuth, passphraseState] = usePassphraseAuth({ appName });
```
</CodeGroup>
## Passkey
Passkey authentication allows users to create a new account or log in with an existing one by providing a passkey.
Passkey authentication is supported out of the box.
We have a [minimal example of a passkey authentication setup](https://github.com/garden-co/jazz/tree/main/examples/minimal-auth-passkey).
### How to use
1. Setup up Jazz as described in the [React setup guide](/docs/project-setup/react).
2. Use the `usePasskeyAuth` hook to authenticate.
<CodeGroup>
```ts
import { usePasskeyAuth } from "jazz-react";
// ...
const [passkeyAuth, passkeyState] = usePasskeyAuth({ appName });
```
</CodeGroup>
## Clerk
We have a React package `jazz-react-auth-clerk` to add Clerk authentication to your app.
We have a [minimal example of a Clerk authentication setup](https://github.com/garden-co/jazz/tree/main/examples/minimal-auth-clerk).
### How to use
1. Setup up Jazz as described in the [React setup guide](/docs/project-setup/react).
2. Install Clerk as described in the [Clerk docs](https://clerk.com/docs/components/overview).
3. Then add the appropriate package to your project (e.g. `jazz-react-auth-clerk`).
<CodeGroup>
```bash
pnpm install jazz-react-auth-clerk
```
</CodeGroup>
4. Provide a Clerk instance to the `useJazzClerkAuth` hook.
<CodeGroup>
```tsx
import { useClerk, SignInButton } from "@clerk/clerk-react";
import { useJazzClerkAuth } from "jazz-react-auth-clerk";
// ...
function JazzAndAuth({ children }: { children: React.ReactNode }) {
const clerk = useClerk();
const [auth, state] = useJazzClerkAuth(clerk);
return (
<>
{clerk.user && auth ? (
<Jazz.Provider auth={auth}></Jazz.Provider>
) : (
<SignInButton />
)}
</>
);
}
createRoot(document.getElementById("root")!).render(
<StrictMode>
<ClerkProvider publishableKey={PUBLISHABLE_KEY} afterSignOutUrl="/">
<JazzAndAuth>
<App />
</JazzAndAuth>
</ClerkProvider>
</StrictMode>,
);
```
</CodeGroup>

View File

@@ -70,11 +70,7 @@ function RenderPackageChild({
return child.getAllSignatures().map((signature, i) => {
const paramTypes = printParamsWithTypes(signature);
return (
<div
key={i}
id={child.name}
className="not-prose mt-4 p-3 rounded bg-stone-50 dark:bg-stone-925"
>
<div key={i} id={child.name} className="not-prose mt-4">
{
<Highlight hide={[0, 2]}>
{`function \n${printSimpleSignature(child, signature) + ":"}\n {}`}
@@ -175,7 +171,10 @@ function RenderClassOrInterface({
</div>
)}
{classOrInterface.categories?.map((category) => (
<div className="flex flex-col mt-6 first:mt-0" key={category.title}>
<div
className="flex flex-col divide-y divide-stone-200 dark:divide-stone-900"
key={category.title}
>
<PropCategory
name={category.title}
description={renderSummary(

View File

@@ -80,15 +80,15 @@ export function ClassOrInterface({
className="inline-flex items-center gap-2 lg:-ml-[22px]"
>
<LinkIcon size={14} className="hidden lg:inline" />
<h3 className="text-lg lg:text-xl">
<h3>
<Highlight>
{(isInterface ? "interface " : "class ") + name + typeParameters}
</Highlight>
</h3>
</Link>
</div>
<div className="flex flex-col gap-5 mt-5">
{doc && <div>{doc}</div>}
<div className="flex flex-col gap-5">
<div>{doc}</div>
<div>{children}</div>
</div>
</div>
@@ -119,7 +119,7 @@ export function PropDecl({
example?: ReactNode;
}) {
return (
<div className="text-sm flex flex-col gap-3 my-2 p-3 rounded bg-stone-50 dark:bg-stone-925">
<div className="text-sm py-3 flex flex-col gap-5">
{(name || type) && (
<div>
{name && <Highlight>{name + ":"}</Highlight>}
@@ -157,7 +157,7 @@ export function FnDecl({
example: ReactNode;
}) {
return (
<div className="text-sm flex flex-col gap-3 my-2 p-3 rounded bg-stone-50 dark:bg-stone-925">
<div className="text-sm py-3 flex flex-col gap-5">
<div className="flex flex-col gap-2">
<div>
{<Highlight>{signature + ":"}</Highlight>}{" "}
@@ -201,7 +201,7 @@ export function PropCategory({
}) {
return (
<>
<div className="col-span-6 py-3 font-display font-semibold text-lg text-stone-900 dark:text-white">
<div className="col-span-6 uppercase font-medium tracking-widest text-stone-500 text-xs py-3">
{name}
</div>
{description && <PropDecl doc={description} example={example} />}

View File

@@ -1,4 +1,3 @@
import { socials } from "@/lib/socials";
import { GcmpLogo } from "gcmp-design-system/src/app/components/atoms/logos/GcmpLogo";
import { Footer } from "gcmp-design-system/src/app/components/organisms/Footer";
@@ -7,7 +6,6 @@ export function JazzFooter() {
<Footer
logo={<GcmpLogo monochrome className="w-32" />}
companyName="Garden Computing, Inc."
socials={socials}
sections={[
{
title: "Resources",
@@ -17,6 +15,26 @@ export function JazzFooter() {
{ href: "/docs", label: "Docs" },
],
},
{
title: "Community",
links: [
{
href: "https://github.com/gardencmp/jazz",
label: "GitHub",
newTab: true,
},
{
href: "https://discord.gg/utDMjHYg42",
label: "Discord",
newTab: true,
},
{
href: "https://x.com/jazz_tools",
label: "X",
newTab: true,
},
],
},
{
title: "News",
links: [

View File

@@ -1,6 +1,5 @@
"use client";
import { Card } from "gcmp-design-system/src/app/components/atoms/Card";
import { H3 } from "gcmp-design-system/src/app/components/atoms/Headings";
import { GappedGrid } from "gcmp-design-system/src/app/components/molecules/GappedGrid";
import { SectionHeader } from "gcmp-design-system/src/app/components/molecules/SectionHeader";
@@ -23,7 +22,7 @@ function Iframe(
const { src, user } = props;
return (
<Card className="relative col-span-2 w-full overflow-hidden lg:col-span-2 dark:bg-black">
<div className="relative col-span-2 w-full border rounded-xl shadow-sm overflow-hidden lg:col-span-2 dark:bg-black">
<iframe
{...props}
src={src}
@@ -32,7 +31,7 @@ function Iframe(
height="390"
allowFullScreen
/>
</Card>
</div>
);
}

View File

@@ -1,4 +1,3 @@
import { Card } from "gcmp-design-system/src/app/components/atoms/Card";
import { GappedGrid } from "gcmp-design-system/src/app/components/molecules/GappedGrid";
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
import { SectionHeader } from "gcmp-design-system/src/app/components/molecules/SectionHeader";
@@ -43,8 +42,8 @@ export function CollaborationFeaturesSection() {
<GappedGrid>
{data.map(({ title, description, codeSample: CodeSample }) => (
<Card
className="col-span-2 pt-4 px-4 flex flex-col gap-3"
<div
className="col-span-2 border rounded-xl shadow-sm pt-4 px-4 flex flex-col gap-3"
key={title}
>
<div>
@@ -58,7 +57,7 @@ export function CollaborationFeaturesSection() {
<pre className="flex-1 text-sm border-t border-x rounded-t-lg bg-stone-50 dark:bg-stone-925">
<CodeSample />
</pre>
</Card>
</div>
))}
</GappedGrid>
</div>

View File

@@ -3,8 +3,9 @@ import CursorsAndCaretsDescription from "@/app/(home)/toolkit/cursorsAndCarets.m
import TwoWaySyncDescription from "@/app/(home)/toolkit/twoWaySync.mdx";
import VideoPresenceCallsDescription from "@/app/(home)/toolkit/videoPresenceCalls.mdx";
import { CodeRef } from "gcmp-design-system/src/app/components/atoms/CodeRef";
import { GridCard } from "gcmp-design-system/src/app/components/atoms/GridCard";
import { H3 } from "gcmp-design-system/src/app/components/atoms/Headings";
import { P } from "gcmp-design-system/src/app/components/atoms/Paragraph";
import { FeatureCard } from "gcmp-design-system/src/app/components/molecules/FeatureCard";
import { GappedGrid } from "gcmp-design-system/src/app/components/molecules/GappedGrid";
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
import { SectionHeader } from "gcmp-design-system/src/app/components/molecules/SectionHeader";
@@ -14,40 +15,39 @@ export function ComingSoonSection() {
<div>
<SectionHeader title="More features coming soon" />
<GappedGrid cols={4}>
<FeatureCard className="p-4" label={<h3>Cursors & carets</h3>}>
<P>Ready-made spatial presence.</P>
<GappedGrid>
<GridCard>
<H3>Cursors & carets</H3>
<P className="text-lg">Ready-made spatial presence.</P>
<Prose size="sm">
<CursorsAndCaretsDescription />
</Prose>
</FeatureCard>
</GridCard>
<FeatureCard className="p-4" label={<h3>Two-way sync to your DB</h3>}>
<P>Add Jazz to an existing app.</P>
<GridCard>
<H3>Two-way sync to your DB</H3>
<P className="text-lg">Add Jazz to an existing app.</P>
<Prose size="sm">
<TwoWaySyncDescription />
</Prose>
</FeatureCard>
</GridCard>
<FeatureCard className="p-4" label={<h3>Video presence & calls</h3>}>
<P>Stream and record audio & video.</P>
<GridCard>
<H3>Video presence & calls</H3>
<P className="text-lg">Stream and record audio & video.</P>
<Prose size="sm">
<VideoPresenceCallsDescription />
</Prose>
</FeatureCard>
</GridCard>
<FeatureCard
className="p-4"
label={
<h3>
<CodeRef>CoPlainText</CodeRef> & <CodeRef>CoRichText</CodeRef>
</h3>
}
>
<GridCard>
<H3>
<CodeRef>CoPlainText</CodeRef> & <CodeRef>CoRichText</CodeRef>
</H3>
<Prose size="sm">
<CoPlainTextDescription />
</Prose>
</FeatureCard>
</GridCard>
</GappedGrid>
</div>
);

View File

@@ -1,11 +1,10 @@
import { Button } from "gcmp-design-system/src/app/components/atoms/Button";
import { Card } from "gcmp-design-system/src/app/components/atoms/Card";
import { H2 } from "gcmp-design-system/src/app/components/atoms/Headings";
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
export function EarlyAdopterSection() {
return (
<Card className="p-4 md:py-16">
<div className="border rounded-xl shadow-sm p-4 md:py-16">
<div className="lg:max-w-3xl md:text-center mx-auto space-y-6">
<p className="uppercase text-blue tracking-widest text-sm font-medium dark:text-stone-400">
Become an early adopter
@@ -32,6 +31,6 @@ export function EarlyAdopterSection() {
</Button>
</div>
</div>
</Card>
</div>
);
}

View File

@@ -1,4 +1,3 @@
import { Card } from "gcmp-design-system/src/app/components/atoms/Card";
import { H3 } from "gcmp-design-system/src/app/components/atoms/Headings";
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
import { LockKeyholeIcon } from "lucide-react";
@@ -65,7 +64,7 @@ function Illustration() {
export function EncryptionSection() {
return (
<Card className="overflow-hidden dark:bg-stone-925">
<div className="border rounded-xl bg-white shadow-sm overflow-hidden dark:bg-stone-925">
<div className="flex grid md:grid-cols-3 md:gap-3">
<div className="md:col-span-2 px-4 pb-4 md:p-8">
<H3 className="mb-0 text-balance">
@@ -87,6 +86,6 @@ export function EncryptionSection() {
<Illustration />
</div>
</Card>
</div>
);
}

View File

@@ -1,7 +1,6 @@
import { ServerWorkersDiagram } from "@/components/home/ServerWorkersDiagram";
import { ClerkLogo } from "@/components/icons/ClerkLogo";
import { Button } from "gcmp-design-system/src/app/components/atoms/Button";
import { Card } from "gcmp-design-system/src/app/components/atoms/Card";
import { H3 } from "gcmp-design-system/src/app/components/atoms/Headings";
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
import { SectionHeader } from "gcmp-design-system/src/app/components/molecules/SectionHeader";
@@ -125,7 +124,10 @@ export function FeaturesSection() {
<div className="grid grid-cols-2 sm:grid-cols-4 md:grid-cols-6 gap-4 lg:gap-8">
{features.map(({ title, icon: Icon, description, illustration }) => (
<Card key={title} className="col-span-2 overflow-hidden">
<div
key={title}
className="col-span-2 border rounded-xl shadow-sm overflow-hidden"
>
<div className="h-48 flex w-full items-center justify-center">
{illustration}
</div>
@@ -135,7 +137,7 @@ export function FeaturesSection() {
</h3>
<Prose size="sm">{description}</Prose>
</div>
</Card>
</div>
))}
<div className="border p-4 sm:p-8 shadow-sm rounded-xl col-span-2 sm:col-span-4 space-y-5">

View File

@@ -1,5 +1,4 @@
import { clsx } from "clsx";
import { Card } from "gcmp-design-system/src/app/components/atoms/Card";
import { H2 } from "gcmp-design-system/src/app/components/atoms/Headings";
import { GappedGrid } from "gcmp-design-system/src/app/components/molecules/GappedGrid";
import CodeStepAction from "./CodeStepAction.mdx";
@@ -49,10 +48,10 @@ function Step({
className?: string;
}) {
return (
<Card
<div
className={clsx(
className,
"overflow-hidden flex flex-col gap-6",
"rounded-lg overflow-hidden shadow-sm flex flex-col gap-6 border",
"pt-4 sm:pt-6",
"col-span-2 lg:col-span-3",
)}
@@ -71,7 +70,7 @@ function Step({
<p className="max-w-md">{description}</p>
</div>
<div className="flex-1 pl-4 sm:pl-12">{children}</div>
</Card>
</div>
);
}

View File

@@ -1,5 +1,4 @@
import { FeatureCard } from "gcmp-design-system/src/app/components/molecules/FeatureCard";
import { GappedGrid } from "gcmp-design-system/src/app/components/molecules/GappedGrid";
import { LabelledFeatureIcon } from "gcmp-design-system/src/app/components/molecules/LabelledFeatureIcon";
import { SectionHeader } from "gcmp-design-system/src/app/components/molecules/SectionHeader";
import {
GaugeIcon,
@@ -65,16 +64,16 @@ export function LocalFirstFeaturesSection() {
</>
}
/>
<GappedGrid cols={4}>
<div className="grid sm:grid-cols-2 lg:grid-cols-4 gap-4 lg:gap-8">
{features.map(({ title, icon: Icon, description }) => (
<FeatureCard
<LabelledFeatureIcon
label={title}
icon={Icon}
explanation={description}
key={title}
></FeatureCard>
></LabelledFeatureIcon>
))}
</GappedGrid>
</div>
</div>
);
}

View File

@@ -1,4 +1,5 @@
import { socials } from "@/lib/socials";
import { SiDiscord, SiGithub, SiX } from "@icons-pack/react-simple-icons";
import { Button } from "gcmp-design-system/src/app/components/atoms/Button";
import { JazzLogo } from "gcmp-design-system/src/app/components/atoms/logos/JazzLogo";
import { Nav } from "gcmp-design-system/src/app/components/organisms/Nav";
import { BookTextIcon, BoxIcon, CodeIcon } from "lucide-react";
@@ -67,8 +68,25 @@ export function JazzNav() {
href: "https://github.com/gardencmp/jazz/releases",
newTab: true,
},
{
title: "GitHub",
href: "https://github.com/gardencmp/jazz",
newTab: true,
icon: <SiGithub className="w-5" />,
},
{
title: "Discord",
href: "https://discord.gg/utDMjHYg42",
newTab: true,
icon: <SiDiscord className="w-5" />,
},
{
title: "X",
href: "https://x.com/jazz_tools",
newTab: true,
icon: <SiX className="w-5" />,
},
]}
socials={socials}
docNav={<DocNav className="block h-auto" />}
/>
);

View File

@@ -111,27 +111,12 @@ export const docNavigationItems = [
],
},
{
name: "Authentication methods",
name: "Authentication",
items: [
{
name: "Overview",
name: "Auth methods overview",
href: "/docs/authentication/auth-methods",
done: 80,
},
{
name: "Passphrase",
href: "/docs/authentication/auth-methods#passphrase",
done: 20,
},
{
name: "Passkey",
href: "/docs/authentication/auth-methods#passkey",
done: 20,
},
{
name: "Clerk",
href: "/docs/authentication/auth-methods#clerk",
done: 20,
done: 0,
},
{
name: "Writing your own",

View File

@@ -1,5 +0,0 @@
export const socials = {
github: "https://github.com/gardencmp/jazz",
discord: "https://discord.gg/utDMjHYg42",
x: "https://x.com/jazz_tools",
};

View File

@@ -10,6 +10,10 @@ const nextConfig = {
// Configure `pageExtensions`` to include MDX files
pageExtensions: ["js", "jsx", "mdx", "ts", "tsx"],
transpilePackages: ["gcmp-design-system"],
// Optionally, add any other Next.js config below
experimental: {
serverActions: true,
},
};
const withMDX = createMDX({

View File

@@ -5,8 +5,7 @@
"type": "module",
"scripts": {
"dev": "NODE_OPTIONS=--max-old-space-size=8192 next dev",
"build:generate-docs": "node genDocs.mjs --build",
"build": "pnpm run build:generate-docs && next build",
"build": "node genDocs.mjs --build && next build",
"start": "next start",
"format-and-lint": "biome check .",
"format-and-lint:fix": "biome check . --write"
@@ -28,7 +27,7 @@
"mdast-util-from-markdown": "^2.0.0",
"mdast-util-mdx": "^3.0.0",
"micromark-extension-mdxjs": "^3.0.0",
"next": "14.2.15",
"next": "13.5.4",
"qrcode": "^1.5.4",
"react": "^18",
"react-dom": "^18",

493
homepage/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,35 +1,5 @@
# cojson-storage-indexeddb
## 0.8.32
### Patch Changes
- Updated dependencies [df42b2b]
- cojson@0.8.32
## 0.8.31
### Patch Changes
- Updated dependencies [e511d6d]
- cojson@0.8.31
## 0.8.30
### Patch Changes
- Updated dependencies [0a2fae3]
- Updated dependencies [99cda2f]
- cojson@0.8.30
## 0.8.29
### Patch Changes
- Updated dependencies [dcc9c2e]
- Updated dependencies [699553f]
- cojson@0.8.29
## 0.8.28
### Patch Changes

View File

@@ -1,12 +1,12 @@
{
"name": "cojson-storage-indexeddb",
"version": "0.8.32",
"version": "0.8.28",
"main": "dist/index.js",
"type": "module",
"types": "src/index.ts",
"license": "MIT",
"dependencies": {
"cojson": "workspace:0.8.32"
"cojson": "workspace:0.8.28"
},
"devDependencies": {
"@vitest/browser": "^0.34.1",

View File

@@ -703,7 +703,7 @@ function getDependedOnCoValues(
coValueRow?.header.ruleset.group,
...new Set(
newContentPieces.flatMap((piece) =>
Object.keys(piece.new)
Object.keys(piece)
.map((sessionID) =>
cojsonInternals.accountOrAgentIDfromSessionID(
sessionID as SessionID,

View File

@@ -1,35 +1,5 @@
# cojson-storage-sqlite
## 0.8.32
### Patch Changes
- Updated dependencies [df42b2b]
- cojson@0.8.32
## 0.8.31
### Patch Changes
- Updated dependencies [e511d6d]
- cojson@0.8.31
## 0.8.30
### Patch Changes
- Updated dependencies [0a2fae3]
- Updated dependencies [99cda2f]
- cojson@0.8.30
## 0.8.29
### Patch Changes
- Updated dependencies [dcc9c2e]
- Updated dependencies [699553f]
- cojson@0.8.29
## 0.8.28
### Patch Changes

View File

@@ -1,13 +1,13 @@
{
"name": "cojson-storage-sqlite",
"type": "module",
"version": "0.8.32",
"version": "0.8.28",
"main": "dist/index.js",
"types": "src/index.ts",
"license": "MIT",
"dependencies": {
"better-sqlite3": "^8.5.2",
"cojson": "workspace:0.8.32",
"cojson": "workspace:0.8.28",
"typescript": "^5.3.3"
},
"devDependencies": {

View File

@@ -1,36 +1,5 @@
# cojson-transport-nodejs-ws
## 0.8.32
### Patch Changes
- Updated dependencies [df42b2b]
- cojson@0.8.32
## 0.8.31
### Patch Changes
- Updated dependencies [e511d6d]
- cojson@0.8.31
## 0.8.30
### Patch Changes
- Updated dependencies [0a2fae3]
- Updated dependencies [99cda2f]
- cojson@0.8.30
## 0.8.29
### Patch Changes
- dcc9c2e: Clear out the queues when closing a Peer
- Updated dependencies [dcc9c2e]
- Updated dependencies [699553f]
- cojson@0.8.29
## 0.8.28
### Patch Changes

View File

@@ -1,12 +1,12 @@
{
"name": "cojson-transport-ws",
"type": "module",
"version": "0.8.32",
"version": "0.8.28",
"main": "dist/index.js",
"types": "src/index.ts",
"license": "MIT",
"dependencies": {
"cojson": "workspace:0.8.32",
"cojson": "workspace:0.8.28",
"typescript": "^5.3.3"
},
"scripts": {

View File

@@ -59,7 +59,6 @@ function createOutgoingMessagesManager(
websocket: AnyWebSocket,
batchingByDefault: boolean,
) {
let closed = false;
const outgoingMessages = new BatchedOutgoingMessages((messages) => {
if (websocket.readyState === 1) {
websocket.send(messages);
@@ -69,10 +68,6 @@ function createOutgoingMessagesManager(
let batchingEnabled = batchingByDefault;
async function sendMessage(msg: SyncMessage) {
if (closed) {
return Promise.reject(new Error("WebSocket closed"));
}
if (websocket.readyState !== 1) {
await waitForWebSocketOpen(websocket);
}
@@ -103,7 +98,6 @@ function createOutgoingMessagesManager(
batchingEnabled = enabled;
},
close() {
closed = true;
outgoingMessages.close();
},
};

View File

@@ -153,23 +153,6 @@ describe("createWebSocketPeer", () => {
expect(mockWebSocket.close).toHaveBeenCalled();
});
test("should return a rejection if a message is sent after the peer is closed", async () => {
const { peer } = setup();
peer.outgoing.close();
const message: SyncMessage = {
action: "known",
id: "co_ztest",
header: false,
sessions: {},
};
await expect(peer.outgoing.push(message)).rejects.toThrow(
"WebSocket closed",
);
});
describe("batchingByDefault = true", () => {
test("should batch outgoing messages", async () => {
const { peer, mockWebSocket } = setup();

View File

@@ -1,31 +1,5 @@
# cojson
## 0.8.32
### Patch Changes
- df42b2b: Catch hard-to-debug errors when trying to get edits at a CoMap key called "constructor"
## 0.8.31
### Patch Changes
- e511d6d: Performance: make the isUploaded check on the SyncStateManager lazy
## 0.8.30
### Patch Changes
- 0a2fae3: More optimised way to get knownState
- 99cda2f: Reduce noise on peer close and increase the load timeout
## 0.8.29
### Patch Changes
- dcc9c2e: Clear out the queues when closing a Peer
- 699553f: Restore offline support and improve loading perfromance when values are cached
## 0.8.28
### Patch Changes

View File

@@ -19,7 +19,7 @@
},
"type": "module",
"license": "MIT",
"version": "0.8.32",
"version": "0.8.28",
"devDependencies": {
"@types/jest": "^29.5.3",
"typescript": "^5.3.3",

View File

@@ -97,10 +97,6 @@ export class PeerState {
}
pushOutgoingMessage(msg: SyncMessage) {
if (this.closed) {
return Promise.resolve();
}
const promise = this.queue.push(msg);
void this.processQueue();
@@ -118,17 +114,8 @@ export class PeerState {
return this.peer.incoming;
}
private closeQueue() {
let entry: QueueEntry | undefined;
while ((entry = this.queue.pull())) {
// Using resolve here to avoid unnecessary noise in the logs
entry.resolve();
}
}
gracefulShutdown() {
console.debug("Gracefully closing", this.id);
this.closeQueue();
this.peer.outgoing.close();
this.closed = true;
}

View File

@@ -6,31 +6,29 @@ import {
emptyKnownState,
} from "./sync.js";
export type SyncStateGetter = {
isUploaded: boolean;
};
export type GlobalSyncStateListenerCallback = (
peerId: PeerID,
knownState: CoValueKnownState,
sync: SyncStateGetter,
) => void;
export type PeerSyncStateListenerCallback = (
knownState: CoValueKnownState,
sync: SyncStateGetter,
) => void;
export class SyncStateSubscriptionManager {
constructor(private syncManager: SyncManager) {}
private listeners = new Set<GlobalSyncStateListenerCallback>();
private listenersByPeers = new Map<
PeerID,
Set<PeerSyncStateListenerCallback>
private listeners = new Set<
(
peerId: PeerID,
knownState: CoValueKnownState,
uploadCompleted: boolean,
) => void
>();
subscribeToUpdates(listener: GlobalSyncStateListenerCallback) {
private listenersByPeers = new Map<
PeerID,
Set<(knownState: CoValueKnownState, uploadCompleted: boolean) => void>
>();
subscribeToUpdates(
listener: (
peerId: PeerID,
knownState: CoValueKnownState,
uploadCompleted: boolean,
) => void,
) {
this.listeners.add(listener);
return () => {
@@ -40,7 +38,7 @@ export class SyncStateSubscriptionManager {
subscribeToPeerUpdates(
peerId: PeerID,
listener: PeerSyncStateListenerCallback,
listener: (knownState: CoValueKnownState, uploadCompleted: boolean) => void,
) {
const listeners = this.listenersByPeers.get(peerId) ?? new Set();
@@ -70,29 +68,19 @@ export class SyncStateSubscriptionManager {
}
const knownState = peer.knownStates.get(id) ?? emptyKnownState(id);
// Build a lazy sync state object to process the isUploaded info
// only when requested
const syncState = {} as SyncStateGetter;
const getIsUploaded = simpleMemoize(() =>
this.getIsCoValueFullyUploadedIntoPeer(peerId, id),
const fullyUploadedIntoPeer = this.getIsCoValueFullyUploadedIntoPeer(
peerId,
id,
);
Object.defineProperties(syncState, {
isUploaded: {
enumerable: true,
get: getIsUploaded,
},
});
for (const listener of this.listeners) {
listener(peerId, knownState, syncState);
listener(peerId, knownState, fullyUploadedIntoPeer);
}
if (!peerListeners) return;
for (const listener of peerListeners) {
listener(knownState, syncState);
listener(knownState, fullyUploadedIntoPeer);
}
}
@@ -134,8 +122,3 @@ function getIsUploadCompleted(
return true;
}
function simpleMemoize<T>(fn: () => T): () => T {
let value: T | undefined;
return () => value ?? (value = fn());
}

View File

@@ -155,16 +155,15 @@ export class CoValueCore {
/** @internal */
knownStateUncached(): CoValueKnownState {
const sessions: CoValueKnownState["sessions"] = {};
for (const [sessionID, sessionLog] of this.sessionLogs.entries()) {
sessions[sessionID] = sessionLog.transactions.length;
}
return {
id: this.id,
header: true,
sessions,
sessions: Object.fromEntries(
[...this.sessionLogs.entries()].map(([k, v]) => [
k,
v.transactions.length,
]),
),
};
}

View File

@@ -4,7 +4,6 @@ import { RawCoID } from "./ids.js";
import { PeerID } from "./sync.js";
export const CO_VALUE_LOADING_MAX_RETRIES = 5;
export const CO_VALUE_LOADING_TIMEOUT = 30_000;
export class CoValueUnknownState {
type = "unknown" as const;
@@ -265,53 +264,15 @@ async function loadCoValueFromPeers(
peers: PeerState[],
) {
for (const peer of peers) {
if (peer.closed) {
continue;
}
if (coValueEntry.state.type === "available") {
/**
* We don't need to wait for the message to be delivered here.
*
* This way when the coValue becomes available because it's cached we don't wait for the server
* peer to consume the messages queue before moving forward.
*/
peer
.pushOutgoingMessage({
action: "load",
...coValueEntry.state.coValue.knownState(),
})
.catch((err) => {
console.error(`Failed to push load message to peer ${peer.id}`, err);
});
} else {
/**
* We only wait for the load state to be resolved.
*/
peer
.pushOutgoingMessage({
action: "load",
id: coValueEntry.id,
header: false,
sessions: {},
})
.catch((err) => {
console.error(`Failed to push load message to peer ${peer.id}`, err);
});
}
await peer.pushOutgoingMessage({
action: "load",
id: coValueEntry.id,
header: false,
sessions: {},
});
if (coValueEntry.state.type === "loading") {
const timeout = setTimeout(() => {
if (coValueEntry.state.type === "loading") {
console.error("Failed to load coValue from peer", peer.id);
coValueEntry.dispatch({
type: "not-found-in-peer",
peerId: peer.id,
});
}
}, CO_VALUE_LOADING_TIMEOUT);
await coValueEntry.state.waitForPeer(peer.id);
clearTimeout(timeout);
}
}
}

View File

@@ -107,10 +107,6 @@ export class RawCoMapView<
timeFilteredOps<K extends keyof Shape & string>(
key: K,
): MapOp<K, Shape[K]>[] | undefined {
if (key === "constructor") {
return undefined;
}
if (this.atTimeFilter) {
return this.ops[key]?.filter((op) => op.madeAt <= this.atTimeFilter!);
} else {

View File

@@ -451,17 +451,12 @@ export class SyncManager {
dependencyEntry.state.type === "available" ||
dependencyEntry.state.type === "loading"
) {
this.local
.loadCoValueCore(
msg.id,
peer.role === "storage" ? undefined : peer.id,
)
.catch((e) => {
console.error(
`Error loading coValue ${msg.id} to create loading state, as dependency of ${msg.asDependencyOf}`,
e,
);
});
this.local.loadCoValueCore(msg.id, peer.id).catch((e) => {
console.error(
`Error loading coValue ${msg.id} to create loading state, as dependency of ${msg.asDependencyOf}`,
e,
);
});
} else {
throw new Error(
"Expected coValue dependency entry to be created, missing subscribe?",
@@ -718,8 +713,8 @@ export class SyncManager {
const unsubscribe =
this.syncStateSubscriptionManager.subscribeToPeerUpdates(
peerId,
(knownState, syncState) => {
if (syncState.isUploaded && knownState.id === id) {
(knownState, uploadCompleted) => {
if (uploadCompleted && knownState.id === id) {
resolve(true);
unsubscribe?.();
}

View File

@@ -57,36 +57,6 @@ describe("PeerState", () => {
consoleSpy.mockRestore();
});
test("should empty the queue when closing", async () => {
const { mockPeer, peerState } = setup();
mockPeer.outgoing.push = vi.fn().mockImplementation((message) => {
return new Promise<void>((resolve) => {
setTimeout(resolve, 100);
});
});
const message1 = peerState.pushOutgoingMessage({
action: "content",
id: "co_z1",
new: {},
priority: CO_VALUE_PRIORITY.HIGH,
});
const message2 = peerState.pushOutgoingMessage({
action: "content",
id: "co_z1",
new: {},
priority: CO_VALUE_PRIORITY.HIGH,
});
peerState.gracefulShutdown();
await Promise.allSettled([message1, message2]);
await expect(message1).resolves.toBe(undefined);
await expect(message2).resolves.toBe(undefined);
});
test("should schedule outgoing messages based on their priority", async () => {
const { peerState } = setup();

View File

@@ -1,8 +1,4 @@
import { describe, expect, onTestFinished, test, vi } from "vitest";
import {
GlobalSyncStateListenerCallback,
PeerSyncStateListenerCallback,
} from "../SyncStateSubscriptionManager.js";
import { connectedPeers } from "../streamUtils.js";
import { emptyKnownState } from "../sync.js";
import { createTestNode, waitFor } from "./testUtils.js";
@@ -33,7 +29,7 @@ describe("SyncStateSubscriptionManager", () => {
const subscriptionManager = client.syncManager.syncStateSubscriptionManager;
const updateSpy: GlobalSyncStateListenerCallback = vi.fn();
const updateSpy = vi.fn();
const unsubscribe = subscriptionManager.subscribeToUpdates(updateSpy);
await client.syncManager.actuallySyncCoValue(map.core);
@@ -41,7 +37,7 @@ describe("SyncStateSubscriptionManager", () => {
expect(updateSpy).toHaveBeenCalledWith(
"jazzCloudConnection",
emptyKnownState(map.core.id),
{ isUploaded: false },
false,
);
await waitFor(() => {
@@ -56,7 +52,7 @@ describe("SyncStateSubscriptionManager", () => {
client.syncManager.peers["jazzCloudConnection"]!.knownStates.get(
map.core.id,
)!,
{ isUploaded: true },
true,
);
// Cleanup
@@ -94,8 +90,8 @@ describe("SyncStateSubscriptionManager", () => {
const subscriptionManager = client.syncManager.syncStateSubscriptionManager;
const updateToJazzCloudSpy: PeerSyncStateListenerCallback = vi.fn();
const updateToStorageSpy: PeerSyncStateListenerCallback = vi.fn();
const updateToJazzCloudSpy = vi.fn();
const updateToStorageSpy = vi.fn();
const unsubscribe1 = subscriptionManager.subscribeToPeerUpdates(
"jazzCloudConnection",
updateToJazzCloudSpy,
@@ -114,7 +110,7 @@ describe("SyncStateSubscriptionManager", () => {
expect(updateToJazzCloudSpy).toHaveBeenCalledWith(
emptyKnownState(map.core.id),
{ isUploaded: false },
false,
);
await waitFor(() => {
@@ -128,12 +124,12 @@ describe("SyncStateSubscriptionManager", () => {
client.syncManager.peers["jazzCloudConnection"]!.knownStates.get(
map.core.id,
)!,
{ isUploaded: true },
true,
);
expect(updateToStorageSpy).toHaveBeenLastCalledWith(
emptyKnownState(map.core.id),
{ isUploaded: false },
false,
);
});

View File

@@ -24,7 +24,7 @@ describe("CoValueState", () => {
});
test("should create available state", async () => {
const mockCoValue = createMockCoValueCore(mockCoValueId);
const mockCoValue = { id: mockCoValueId } as CoValueCore;
const state = CoValueState.Available(mockCoValue);
expect(state.id).toBe(mockCoValueId);
@@ -34,7 +34,7 @@ describe("CoValueState", () => {
});
test("should handle found action", async () => {
const mockCoValue = createMockCoValueCore(mockCoValueId);
const mockCoValue = { id: mockCoValueId } as CoValueCore;
const state = CoValueState.Loading(mockCoValueId, ["peer1", "peer2"]);
const stateValuePromise = state.getCoValue();
@@ -232,7 +232,7 @@ describe("CoValueState", () => {
setTimeout(() => {
state.dispatch({
type: "available",
coValue: createMockCoValueCore(mockCoValueId),
coValue: { id: mockCoValueId } as CoValueCore,
});
}, 100);
}
@@ -285,7 +285,7 @@ describe("CoValueState", () => {
state.dispatch({
type: "available",
coValue: createMockCoValueCore(mockCoValueId),
coValue: { id: mockCoValueId } as CoValueCore,
});
await loadPromise;
@@ -311,7 +311,7 @@ describe("CoValueState", () => {
if (run > 2) {
state.dispatch({
type: "available",
coValue: createMockCoValueCore(mockCoValueId),
coValue: { id: mockCoValueId } as CoValueCore,
});
}
state.dispatch({
@@ -338,129 +338,6 @@ describe("CoValueState", () => {
vi.useRealTimers();
});
test("should start sending the known state to peers when available", async () => {
vi.useFakeTimers();
const mockCoValue = createMockCoValueCore(mockCoValueId);
const peer1 = createMockPeerState(
{
id: "peer1",
role: "storage",
},
async () => {
state.dispatch({
type: "available",
coValue: mockCoValue,
});
},
);
const peer2 = createMockPeerState(
{
id: "peer1",
role: "server",
},
async () => {
state.dispatch({
type: "not-found-in-peer",
peerId: "peer2",
});
},
);
const state = CoValueState.Unknown(mockCoValueId);
const loadPromise = state.loadFromPeers([peer1, peer2]);
for (let i = 0; i < CO_VALUE_LOADING_MAX_RETRIES; i++) {
await vi.runAllTimersAsync();
}
await loadPromise;
expect(peer1.pushOutgoingMessage).toHaveBeenCalledTimes(1);
expect(peer2.pushOutgoingMessage).toHaveBeenCalledTimes(1);
expect(peer2.pushOutgoingMessage).toHaveBeenCalledWith({
action: "load",
...mockCoValue.knownState(),
});
expect(state.state.type).toBe("available");
await expect(state.getCoValue()).resolves.toEqual({ id: mockCoValueId });
vi.useRealTimers();
});
test("should skip closed peers", async () => {
vi.useFakeTimers();
const mockCoValue = createMockCoValueCore(mockCoValueId);
const peer1 = createMockPeerState(
{
id: "peer1",
role: "storage",
},
async () => {
return new Promise(() => {});
},
);
const peer2 = createMockPeerState(
{
id: "peer1",
role: "server",
},
async () => {
state.dispatch({
type: "available",
coValue: mockCoValue,
});
},
);
peer1.closed = true;
const state = CoValueState.Unknown(mockCoValueId);
const loadPromise = state.loadFromPeers([peer1, peer2]);
for (let i = 0; i < CO_VALUE_LOADING_MAX_RETRIES; i++) {
await vi.runAllTimersAsync();
}
await loadPromise;
expect(peer1.pushOutgoingMessage).toHaveBeenCalledTimes(0);
expect(peer2.pushOutgoingMessage).toHaveBeenCalledTimes(1);
expect(state.state.type).toBe("available");
await expect(state.getCoValue()).resolves.toEqual({ id: mockCoValueId });
vi.useRealTimers();
});
test("should not be stuck in loading state when not getting a response", async () => {
vi.useFakeTimers();
const peer1 = createMockPeerState(
{
id: "peer1",
role: "server",
},
async () => {},
);
const state = CoValueState.Unknown(mockCoValueId);
const loadPromise = state.loadFromPeers([peer1]);
for (let i = 0; i < CO_VALUE_LOADING_MAX_RETRIES * 2; i++) {
await vi.runAllTimersAsync();
}
await loadPromise;
expect(peer1.pushOutgoingMessage).toHaveBeenCalledTimes(5);
expect(state.state.type).toBe("unavailable");
await expect(state.getCoValue()).resolves.toEqual("unavailable");
vi.useRealTimers();
});
});
function createMockPeerState(
@@ -483,18 +360,3 @@ function createMockPeerState(
return peerState;
}
function createMockCoValueCore(mockCoValueId: string) {
// Setting the knownState as part of the prototype to simplify
// the equality checks
const mockCoValue = Object.create({
knownState: vi.fn().mockReturnValue({
id: mockCoValueId,
header: true,
sessions: {},
}),
});
mockCoValue.id = mockCoValueId;
return mockCoValue as unknown as CoValueCore;
}

View File

@@ -1,45 +1,5 @@
# jazz-browser-media-images
## 0.8.32
### Patch Changes
- 1a4bda0: Document usage in readme
- Updated dependencies [df42b2b]
- Updated dependencies [df42b2b]
- cojson@0.8.32
- jazz-tools@0.8.32
- jazz-browser@0.8.32
## 0.8.31
### Patch Changes
- Updated dependencies [e511d6d]
- cojson@0.8.31
- jazz-browser@0.8.31
- jazz-tools@0.8.31
## 0.8.30
### Patch Changes
- Updated dependencies [0a2fae3]
- Updated dependencies [99cda2f]
- cojson@0.8.30
- jazz-browser@0.8.30
- jazz-tools@0.8.30
## 0.8.29
### Patch Changes
- Updated dependencies [dcc9c2e]
- Updated dependencies [699553f]
- cojson@0.8.29
- jazz-browser@0.8.29
- jazz-tools@0.8.29
## 0.8.28
### Patch Changes

View File

@@ -2,30 +2,40 @@
This package provides a [Clerk-based](https://clerk.com/) authentication strategy for Jazz.
Looking for a React integration? Check out [`jazz-react-auth-clerk`](https://www.npmjs.com/package/jazz-react-auth-clerk).
## Usage
`BrowserClerkAuth` is a class that provides a `JazzAuth` object. Provide a Clerk instance to `BrowserClerkAuth`, and it will return the appropriate `JazzAuth` object. Once authenticated, authentication will persist across page reloads, even if the device is offline.
`useJazzClerkAuth` is a hook that returns a `JazzAuth` object and a `JazzAuthState` object. Provide a Clerk instance to `useJazzClerkAuth`, and it will return the appropriate `JazzAuth` object. Once authenticated, authentication will persist across page reloads, even if the device is offline.
From [the example chat app](https://github.com/gardencmp/jazz/tree/main/examples/chat-clerk):
```ts
import { BrowserClerkAuth } from "jazz-browser-auth-clerk";
```typescript
import { ClerkProvider, SignInButton, useClerk } from "@clerk/clerk-react";
import { useJazzClerkAuth } from "jazz-react-auth-clerk";
// ...
const Jazz = createJazzReactApp();
export const { useAccount, useCoState } = Jazz;
const auth = new BrowserClerkAuth(
{
onError: (error) => {
void clerk.signOut();
setState((state) => ({
...state,
errors: [...state.errors, error.toString()],
}));
},
},
clerk,
);
function JazzAndAuth({ 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://cloud.jazz.tools/?key=chat-example-jazz-clerk@gcmp.io"
>
{children}
</Jazz.Provider>
) : (
<SignInButton />
)}
</>
);
}
```

View File

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

View File

@@ -1,34 +1,5 @@
# jazz-browser-media-images
## 0.8.32
### Patch Changes
- Updated dependencies [df42b2b]
- jazz-tools@0.8.32
- jazz-browser@0.8.32
## 0.8.31
### Patch Changes
- jazz-browser@0.8.31
- jazz-tools@0.8.31
## 0.8.30
### Patch Changes
- jazz-browser@0.8.30
- jazz-tools@0.8.30
## 0.8.29
### Patch Changes
- jazz-browser@0.8.29
- jazz-tools@0.8.29
## 0.8.28
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "jazz-browser-media-images",
"version": "0.8.32",
"version": "0.8.28",
"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.8.32",
"jazz-tools": "workspace:0.8.32",
"jazz-browser": "workspace:0.8.28",
"jazz-tools": "workspace:0.8.28",
"pica": "^9.0.1",
"typescript": "^5.3.3"
},

View File

@@ -1,48 +1,5 @@
# jazz-browser
## 0.8.32
### Patch Changes
- Updated dependencies [df42b2b]
- Updated dependencies [df42b2b]
- cojson@0.8.32
- jazz-tools@0.8.32
- cojson-storage-indexeddb@0.8.32
- cojson-transport-ws@0.8.32
## 0.8.31
### Patch Changes
- Updated dependencies [e511d6d]
- cojson@0.8.31
- cojson-storage-indexeddb@0.8.31
- cojson-transport-ws@0.8.31
- jazz-tools@0.8.31
## 0.8.30
### Patch Changes
- Updated dependencies [0a2fae3]
- Updated dependencies [99cda2f]
- cojson@0.8.30
- cojson-storage-indexeddb@0.8.30
- cojson-transport-ws@0.8.30
- jazz-tools@0.8.30
## 0.8.29
### Patch Changes
- Updated dependencies [dcc9c2e]
- Updated dependencies [699553f]
- cojson-transport-ws@0.8.29
- cojson@0.8.29
- cojson-storage-indexeddb@0.8.29
- jazz-tools@0.8.29
## 0.8.28
### Patch Changes

View File

@@ -1,16 +1,16 @@
{
"name": "jazz-browser",
"version": "0.8.32",
"version": "0.8.28",
"type": "module",
"main": "dist/index.js",
"types": "src/index.ts",
"license": "MIT",
"dependencies": {
"@scure/bip39": "^1.3.0",
"cojson": "workspace:0.8.32",
"cojson-storage-indexeddb": "workspace:0.8.32",
"cojson-transport-ws": "workspace:0.8.32",
"jazz-tools": "workspace:0.8.32",
"cojson": "workspace:0.8.28",
"cojson-storage-indexeddb": "workspace:0.8.28",
"cojson-transport-ws": "workspace:0.8.28",
"jazz-tools": "workspace:0.8.28",
"typescript": "^5.3.3"
},
"scripts": {

View File

@@ -8,19 +8,6 @@ type StorageData = {
const localStorageKey = "demo-auth-logged-in-secret";
/**
* `BrowserDemoAuth` provides a `JazzAuth` object for demo authentication.
*
* Demo authentication is useful for quickly testing your app, as it allows you to create new accounts and log in as existing ones. The authentication persists across page reloads, but as the data is stored in `localStorage`, it will be lost when the browser is closed.
*
* ```
* import { BrowserDemoAuth } from "jazz-browser";
*
* const auth = new BrowserDemoAuth(driver);
* ```
*
* @category Auth Providers
*/
export class BrowserDemoAuth implements AuthMethod {
constructor(
public driver: BrowserDemoAuth.Driver,
@@ -50,9 +37,6 @@ export class BrowserDemoAuth implements AuthMethod {
}
}
/**
* @returns A `JazzAuth` object
*/
async start() {
if (localStorage["demo-auth-logged-in-secret"]) {
const localStorageData = JSON.parse(
@@ -145,7 +129,7 @@ export class BrowserDemoAuth implements AuthMethod {
}
}
/** @internal */
/** @category Auth Providers */
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace BrowserDemoAuth {
export interface Driver {

View File

@@ -13,17 +13,6 @@ type LocalStorageData = {
const localStorageKey = "jazz-logged-in-secret";
/**
* `BrowserPasskeyAuth` provides a `JazzAuth` object for passkey authentication.
*
* ```ts
* import { BrowserPasskeyAuth } from "jazz-browser";
*
* const auth = new BrowserPasskeyAuth(driver, appName);
* ```
*
* @category Auth Providers
*/
export class BrowserPasskeyAuth implements AuthMethod {
constructor(
public driver: BrowserPasskeyAuth.Driver,
@@ -40,9 +29,6 @@ export class BrowserPasskeyAuth implements AuthMethod {
this.driver.onError(error);
}
/**
* @returns A `JazzAuth` object
*/
async start(crypto: CryptoProvider): Promise<AuthResult> {
if (localStorage[localStorageKey]) {
const localStorageData = JSON.parse(
@@ -185,7 +171,7 @@ export class BrowserPasskeyAuth implements AuthMethod {
}
}
/** @internal */
/** @category Auth Providers */
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace BrowserPasskeyAuth {
export interface Driver {

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