Compare commits

..

26 Commits

Author SHA1 Message Date
Anselm Eickhoff
44497fd058 Merge pull request #668 from gardencmp/changeset-release/main
Version Packages
2024-11-01 10:20:07 +00:00
github-actions[bot]
a18c187818 Version Packages 2024-11-01 10:04:27 +00:00
Anselm Eickhoff
f49949cc25 Merge pull request #653 from gardencmp/feature/react-provider-dev-mode
fix: ensure that the react provider doesn't run twice on dev
2024-11-01 10:03:09 +00:00
Anselm
3cc6aee7e6 Add changeset 2024-11-01 09:54:11 +00:00
Anselm Eickhoff
c80763100e Merge pull request #651 from gardencmp/gudorsi-jazz-248-storage-peer
feat: introduce the storage peer role
2024-11-01 09:50:14 +00:00
Anselm
6ed75ebb35 Add changeset 2024-11-01 09:49:30 +00:00
Anselm Eickhoff
30d7734124 Merge pull request #663 from gardencmp/trishalim-jazz-443
Close mobile nav after clicking a link
2024-11-01 09:41:10 +00:00
Anselm Eickhoff
bca8b44189 Merge pull request #664 from gardencmp/trishalim-jazz-445
Docs: create separate pages for nextjs and react native
2024-11-01 09:31:11 +00:00
Anselm Eickhoff
36a069c90f Merge pull request #666 from gardencmp/feature/jazz-run-integration-test
chore(jazz-run): extract commands business logic and add tests
2024-11-01 09:29:12 +00:00
Guido D'Orsi
757b37e8ed feat: introduce the storage peer role 2024-10-31 23:18:47 +01:00
Guido D'Orsi
46b1163058 chore(jazz-run): split commands and core logic and add tests 2024-10-31 23:09:42 +01:00
Trisha Lim
8c261b0409 Create separate pages for nextjs and react native 2024-10-31 19:36:19 +00:00
Trisha Lim
fdacde57dd Lint fix 2024-10-31 19:10:38 +00:00
Trisha Lim
817dd7dde1 Close mobile nav after clicking a link 2024-10-31 19:07:11 +00:00
Anselm Eickhoff
6279dd1467 Merge pull request #662 from gardencmp/trishalim-jazz-446
Add yellow dot as legend for docs that are WIP
2024-10-31 17:30:18 +00:00
Trisha Lim
abd4b94392 Add legend in side nav to indicate WIP docs 2024-10-31 17:24:03 +00:00
Trisha Lim
68c6ee77c0 Remove % progress in docs, add '/coming-soon' page 2024-10-31 17:18:33 +00:00
Anselm Eickhoff
d2b2812428 Merge pull request #658 from gardencmp/fix/sticky-nav
Fix nav is not sticking to top
2024-10-31 16:29:52 +00:00
Anselm Eickhoff
fe214cc3c2 Merge pull request #657 from gardencmp/fix/h4-style
Fix styling for h4 in prose
2024-10-31 16:29:39 +00:00
Anselm Eickhoff
2cca9506ad Merge pull request #656 from gardencmp/react-native-docs
React native docs
2024-10-31 16:29:06 +00:00
Trisha Lim
17e69aff8f Add space between main content and footer 2024-10-31 14:22:33 +00:00
Trisha Lim
c45331e645 Fix nav is not sticking to top 2024-10-31 14:21:13 +00:00
Trisha Lim
4337001526 Fix styling for h4 in prose 2024-10-31 13:52:23 +00:00
pax-k
9eef1ec031 fix(docs): layout centering 2024-10-31 15:33:54 +02:00
pax-k
9d31bbc3aa chore(docs): added react native docs 2024-10-31 15:20:01 +02:00
Guido D'Orsi
911add3dea fix: ensure that the react provider doesn't run twice on dev 2024-10-31 11:19:48 +01:00
97 changed files with 1353 additions and 669 deletions

View File

@@ -1,5 +1,15 @@
# @jazz-e2e/binarycostream
## 0.0.90
### Patch Changes
- Updated dependencies [6ed75eb]
- Updated dependencies [3cc6aee]
- cojson@0.8.12
- jazz-react@0.8.12
- jazz-tools@0.8.12
## 0.0.89
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "@jazz-e2e/binarycostream",
"private": true,
"version": "0.0.89",
"version": "0.0.90",
"type": "module",
"scripts": {
"dev": "vite",
@@ -14,11 +14,11 @@
"*.{js,jsx,mdx,json}": "prettier --write"
},
"dependencies": {
"cojson": "workspace:0.8.11",
"cojson": "workspace:0.8.12",
"hash-slash": "workspace:0.2.1",
"is-ci": "^3.0.1",
"jazz-react": "workspace:0.8.11",
"jazz-tools": "workspace:0.8.11",
"jazz-react": "workspace:0.8.12",
"jazz-tools": "workspace:0.8.12",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},

View File

@@ -27,10 +27,11 @@ export default defineConfig({
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
baseURL: isCI ? "http://localhost:4173/" : "http://localhost:5173",
baseURL: "http://localhost:5173/",
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: "on-first-retry",
permissions: ["clipboard-read", "clipboard-write"],
},
/* Configure projects for major browsers */
@@ -42,8 +43,11 @@ export default defineConfig({
],
/* Run your local dev server before starting the tests */
webServer: isCI ? {
command: "pnpm preview",
url: "http://localhost:4173/",
} : undefined,
webServer: [
{
command: "pnpm preview --port 5173",
url: "http://localhost:5173/",
reuseExistingServer: !isCI,
},
],
});

View File

@@ -1,5 +1,15 @@
# @jazz-e2e/covalues
## 0.0.89
### Patch Changes
- Updated dependencies [6ed75eb]
- Updated dependencies [3cc6aee]
- cojson@0.8.12
- jazz-react@0.8.12
- jazz-tools@0.8.12
## 0.0.88
### Patch Changes

View File

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

View File

@@ -1,5 +1,14 @@
# jazz-example-book-shelf
## 0.1.5
### Patch Changes
- Updated dependencies [3cc6aee]
- jazz-react@0.8.12
- jazz-tools@0.8.12
- jazz-browser-media-images@0.8.12
## 0.1.4
### Patch Changes

View File

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

View File

@@ -1,5 +1,16 @@
# jazz-example-chat
## 0.0.89
### Patch Changes
- Updated dependencies [6ed75eb]
- Updated dependencies [3cc6aee]
- cojson@0.8.12
- jazz-react@0.8.12
- jazz-react-auth-clerk@0.8.12
- jazz-tools@0.8.12
## 0.0.88
### Patch Changes

View File

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

View File

@@ -1,5 +1,14 @@
# chat-rn-clerk
## 1.0.5
### Patch Changes
- jazz-react-auth-clerk@0.8.12
- jazz-react-native@0.8.12
- jazz-tools@0.8.12
- jazz-react-native-media-images@0.8.8
## 1.0.4
### Patch Changes

View File

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

View File

@@ -1,5 +1,12 @@
# chat-rn
## 1.0.7
### Patch Changes
- jazz-react-native@0.8.12
- jazz-tools@0.8.12
## 1.0.6
### Patch Changes

View File

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

View File

@@ -1,5 +1,15 @@
# jazz-example-chat
## 0.0.91
### Patch Changes
- Updated dependencies [6ed75eb]
- Updated dependencies [3cc6aee]
- cojson@0.8.12
- jazz-react@0.8.12
- jazz-tools@0.8.12
## 0.0.90
### Patch Changes

View File

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

View File

@@ -27,7 +27,7 @@ export default defineConfig({
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
baseURL: isCI ? "http://localhost:4173/" : "http://localhost:5173",
baseURL: "http://localhost:5173/",
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: "on-first-retry",
@@ -43,8 +43,11 @@ export default defineConfig({
],
/* Run your local dev server before starting the tests */
webServer: isCI ? {
command: "pnpm preview",
url: "http://localhost:4173/",
} : undefined,
webServer: [
{
command: "pnpm preview --port 5173",
url: "http://localhost:5173/",
reuseExistingServer: !isCI,
},
],
});

View File

@@ -1,5 +1,13 @@
# jazz-example-inspector
## 0.0.67
### Patch Changes
- Updated dependencies [6ed75eb]
- cojson@0.8.12
- cojson-transport-ws@0.8.12
## 0.0.66
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-inspector",
"private": true,
"version": "0.0.66",
"version": "0.0.67",
"type": "module",
"scripts": {
"dev": "vite",
@@ -15,8 +15,8 @@
"@radix-ui/react-toast": "^1.1.4",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"cojson": "workspace:0.8.11",
"cojson-transport-ws": "workspace:0.8.11",
"cojson": "workspace:0.8.12",
"cojson-transport-ws": "workspace:0.8.12",
"hash-slash": "workspace:0.2.1",
"lucide-react": "^0.274.0",
"qrcode": "^1.5.3",

View File

@@ -1,5 +1,13 @@
# jazz-example-musicplayer
## 0.0.11
### Patch Changes
- Updated dependencies [3cc6aee]
- jazz-react@0.8.12
- jazz-tools@0.8.12
## 0.0.10
### Patch Changes

View File

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

View File

@@ -1,5 +1,13 @@
# jazz-password-manager
## 0.0.10
### Patch Changes
- Updated dependencies [3cc6aee]
- jazz-react@0.8.12
- jazz-tools@0.8.12
## 0.0.9
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-password-manager",
"private": true,
"version": "0.0.9",
"version": "0.0.10",
"type": "module",
"scripts": {
"dev": "vite",
@@ -11,8 +11,8 @@
"clean-install": "rm -rf node_modules pnpm-lock.yaml && pnpm install"
},
"dependencies": {
"jazz-react": "workspace:0.8.11",
"jazz-tools": "workspace:0.8.11",
"jazz-react": "workspace:0.8.12",
"jazz-tools": "workspace:0.8.12",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hook-form": "^7.41.5",

View File

@@ -1,5 +1,14 @@
# jazz-example-pets
## 0.0.108
### Patch Changes
- Updated dependencies [3cc6aee]
- jazz-react@0.8.12
- jazz-tools@0.8.12
- jazz-browser-media-images@0.8.12
## 0.0.107
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-pets",
"private": true,
"version": "0.0.107",
"version": "0.0.108",
"type": "module",
"scripts": {
"dev": "vite",
@@ -23,9 +23,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.11",
"jazz-react": "workspace:0.8.11",
"jazz-tools": "workspace:0.8.11",
"jazz-browser-media-images": "workspace:0.8.12",
"jazz-react": "workspace:0.8.12",
"jazz-tools": "workspace:0.8.12",
"lucide-react": "^0.274.0",
"qrcode": "^1.5.3",
"react": "^18.2.0",
@@ -50,7 +50,7 @@
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.3",
"is-ci": "^3.0.1",
"jazz-run": "workspace:0.8.11",
"jazz-run": "workspace:0.8.12",
"postcss": "^8.4.27",
"tailwindcss": "3.3.2",
"typescript": "^5.3.3",

View File

@@ -1,5 +1,13 @@
# jazz-example-todo
## 0.0.107
### Patch Changes
- Updated dependencies [3cc6aee]
- jazz-react@0.8.12
- jazz-tools@0.8.12
## 0.0.106
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-todo",
"private": true,
"version": "0.0.106",
"version": "0.0.107",
"type": "module",
"scripts": {
"dev": "vite",
@@ -20,8 +20,8 @@
"@radix-ui/react-toast": "^1.1.4",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"jazz-react": "workspace:0.8.11",
"jazz-tools": "workspace:0.8.11",
"jazz-react": "workspace:0.8.12",
"jazz-tools": "workspace:0.8.12",
"lucide-react": "^0.274.0",
"qrcode": "^1.5.3",
"react": "^18.2.0",

View File

@@ -1,5 +1,11 @@
import clsx from "clsx";
export function P({ children, className }: { children: React.ReactNode, className?: string }) {
export function P({
children,
className,
}: {
children: React.ReactNode;
className?: string;
}) {
return <p className={clsx(className, "mb-4")}>{children}</p>;
}

View File

@@ -22,7 +22,7 @@ export function ThemeToggle({ className }: { className?: string }) {
"md:p-2 md:rounded-full md:border",
"text-stone-400 hover:text-stone-900 dark:text-stone-400 dark:hover:text-white",
"md:hover:bg-stone-200 md:dark:hover:bg-stone-900",
"transition-colors"
"transition-colors",
)}
aria-label={
mounted ? `Switch to ${otherTheme} theme` : "Toggle theme"

View File

@@ -37,7 +37,7 @@ function Copyright({
export function Footer({ logo, companyName, sections }: FooterProps) {
return (
<footer className="w-full border-t bg-stone-100 dark:bg-stone-925">
<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}

View File

@@ -2,7 +2,7 @@
import { MenuIcon, XIcon } from "lucide-react";
import { usePathname } from "next/navigation";
import { ReactNode, useLayoutEffect, useRef, useState } from "react";
import { ReactNode, useEffect, useLayoutEffect, useRef, useState } from "react";
import { BreadCrumb } from "../molecules/Breadcrumb";
import clsx from "clsx";
import Link from "next/link";
@@ -35,6 +35,10 @@ export function Nav({
const pathname = usePathname();
useEffect(() => {
setMenuOpen(false);
}, [pathname]);
return (
<>
<nav

View File

@@ -135,7 +135,7 @@ const config = {
"--tw-prose-invert-code": stonePalette[50],
"--tw-prose-links": theme("colors.blue.DEFAULT"),
"--tw-prose-invert-links": theme("colors.blue.500"),
maxWidth: theme("screens.4xl"),
maxWidth: null,
strong: {
color: "var(--tw-prose-bold)",
fontWeight: theme("fontWeight.medium"),
@@ -167,10 +167,10 @@ const config = {
fontSize: theme("fontSize.2xl"),
},
h4: {
textTransform: "uppercase",
letterSpacing: theme("letterSpacing.widest"),
fontWeight: theme("fontWeight.medium"),
fontSize: theme("fontSize.sm"),
fontFamily: theme("fontFamily.display"),
letterSpacing: theme("letterSpacing.tight"),
fontWeight: theme("fontWeight.semibold"),
fontSize: theme("fontSize.xl"),
},
'code::before': {
content: 'none',

View File

@@ -83,7 +83,7 @@ export default function RootLayout({
manrope.variable,
commitMono.variable,
inter.className,
"h-full flex flex-col items-center",
"min-h-full flex flex-col items-center",
"bg-white text-stone-700 dark:text-stone-400 dark:bg-stone-950",
].join(" ")}
>
@@ -96,10 +96,10 @@ export default function RootLayout({
disableTransitionOnChange
>
<GcmpNav />
<main className="flex flex-1 flex-col w-full">
<main className="flex-1 w-full">
{children}
</main>
<footer className="py-8 md:py-16 text-sm flex justify-between gap-3 w-full container">
<footer className="py-8 text-sm flex justify-between gap-3 w-full container mt-12 md:mt-20">
<p>©2024 Garden Computing, Inc.</p>
<ThemeToggle className="hidden md:block"/>

View File

@@ -210,7 +210,7 @@ export default function Home() {
<BeforeAfterJazz />
<div className="container flex flex-col gap-12 py-12 lg:gap-20 lg:py-20">
<div className="container flex flex-col gap-12 mt-12 lg:gap-20 lg:mt-20">
<HowItWorks />
<Testimonial name="Serious Adopter #4" role="Technical Founder">

View File

@@ -88,11 +88,17 @@ export default function Page() {
</CardHeading>
<CardBody>
Get help from our{" "}
<Link href="https://discord.gg/utDMjHYg42" className="underline">
<Link
href="https://discord.gg/utDMjHYg42"
className="underline"
>
Discord
</Link>
, or open an issue on{" "}
<Link href="https://github.com/gardencmp/jazz" className="underline">
<Link
href="https://github.com/gardencmp/jazz"
className="underline"
>
GitHub
</Link>
.

View File

@@ -0,0 +1,9 @@
# The documentation for this feature is not yet available
Grayed out pages on the sidebar indicate that the documentation is not yet available.
This feature has already been released, but the documentation is still a work in progress.
If you don't find what you're looking for, [please ask for help in the Discord](https://discord.gg/utDMjHYg42).
We have a whole team of developers who are ready to help you get started.

View File

@@ -5,25 +5,59 @@ export default function Page() {
<Prose>
<h1>Welcome to the Jazz documentation.</h1>
<p>
The Jazz docs are currently heavily work in progress, sorry about that!
The Jazz docs are currently heavily work in progress, sorry
about that!
</p>
<p>The best ways to get started are:</p>
<ul>
<li>Quickstart (incomplete)<ol>
<li><a href="/docs/sync-and-storage">Sync & Storage Setup</a></li>
<li><a href="/docs/project-setup/react">React Project Setup</a></li>
<li><a href="/docs/schemas/covalues">CoValue Basics & Schema Definition</a></li>
<li><span className="opacity-50">Creating Covalues</span></li>
<li><span className="opacity-50">Using Covalues</span></li>
</ol></li>
<li>The step-by-step <a href="/docs/guide">Guide</a> (incomplete)</li>
<li>
Quickstart (incomplete)
<ol>
<li>
<a href="/docs/sync-and-storage">
Sync & Storage Setup
</a>
</li>
<li>
<a href="/docs/project-setup/react">
React Project Setup
</a>
</li>
<li>
<a href="/docs/schemas/covalues">
CoValue Basics & Schema Definition
</a>
</li>
<li>
<span className="opacity-50">
Creating Covalues
</span>
</li>
<li>
<span className="opacity-50">Using Covalues</span>
</li>
</ol>
</li>
<li>
The step-by-step <a href="/docs/guide">Guide</a>{" "}
(incomplete)
</li>
</ul>
<p>Also make sure to:</p>
<ul>
<li>Find an <a href="/docs/examples">example app with code</a> most similar to what you want to build</li>
<li>Check out the <a href="/docs/api-reference">API Reference</a> (incomplete)</li>
<li>
Find an <a href="/docs/examples">example app with code</a>{" "}
most similar to what you want to build
</li>
<li>
Check out the{" "}
<a href="/docs/api-reference">API Reference</a> (incomplete)
</li>
</ul>
<p>And the best way to get help is to join the <a href="https://discord.gg/utDMjHYg42">Discord</a>!</p>
<p>
And the best way to get help is to join the{" "}
<a href="https://discord.gg/utDMjHYg42">Discord</a>!
</p>
</Prose>
);
}

View File

@@ -77,7 +77,7 @@ export default function Page() {
<div
className={clsx(
"col-span-12 md:col-span-8 lg:col-span-9",
"lg:flex lg:gap-5",
"flex justify-center lg:gap-5",
)}
>
<Prose className="overflow-x-hidden lg:flex-1">

View File

@@ -0,0 +1,75 @@
import { CodeGroup } from "@/components/forMdx";
# Next.js
## <span id="next-csr">Client-side only</span>
The easiest way to use Jazz with Next.JS is to only use it on the client side. You can ensure this by:
- marking the `jazz.tsx` file as `"use client"`
<CodeGroup>
{/* prettier-ignore */}
```tsx
"use client"
import { createJazzReactApp } from "jazz-react";// old
const Jazz = createJazzReactApp();// old
export const { useAccount, useCoState } = Jazz;// old
import { PasskeyAuthBasicUI, usePasskeyAuth } from "jazz-react";// old
function JazzAndAuth({ children }: { children: React.ReactNode }) {// old
const [passkeyAuth, passKeyState] = usePasskeyAuth({ appName });// old
return (// old
<>// old
<Jazz.Provider// old
auth={passkeyAuth}// old
peer="wss://mesh.jazz.tools/?key=you@example.com"// old
>// old
{children}// old
</Jazz.Provider>// old
<PasskeyAuthBasicUI state={passKeyState} />// old
</>// old
);// old
}// old
```
</CodeGroup>
- marking any file with components where you use Jazz hooks (such as `useAccount` or `useCoState`) as `"use client"`
## <span id="next-ssr">SSR use (experimental)</span>
Pure SSR use of Jazz is basically just using jazz-nodejs (see [Node.JS / Server Workers](/docs/project-setup/server-side)) inside Server Components.
Instead of using hooks as you would on the client, you await promises returned by `CoValue.load(...)` inside your Server Components.
TODO: code example
This should work well for cases like rendering publicly-readable information, since the worker account will be able to load them.
In the future, it will be possible to use trusted auth methods (such as Clerk, Auth0, etc.) that let you act as the same Jazz user both on the client and on the server, letting you use SSR even for data private to that user.
## <span id="next-ssr-plus-csr">SSR + client-side (experimental)</span>
You can combine the two approaches by creating
1. A pure "rendering" component that renders an already-loaded CoValue (in JSON-ified form)
TODO: code example
2. A "hydrating" component (with `"use client"`) that
- expects a pre-loaded CoValue as a prop (in JSON-ified form)
- uses one of the client-side Jazz hooks (such as `useAccount` or `useCoState`) to subscribe to that same CoValue
- passing the client-side subscribed state to the "rendering" component, with the pre-loaded CoValue as a fallback until the client receives the first subscribed state
TODO: code example
3. A "pre-loading" Server Component that
- pre-loads the CoValue by awaiting it's `load(...)` method (as described above)
- renders the "hydrating" component, passing the pre-loaded CoValue as a prop
TODO: code example

View File

@@ -0,0 +1,35 @@
import NextGuide from "./next.mdx";
import { TableOfContents } from "@/components/docs/TableOfContents";
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
import { clsx } from "clsx";
const navItems = [
{
name: "Client-side only",
href: "/docs/project-setup/react#next-csr",
},
{
name: "SSR use 🧪",
href: "/docs/project-setup/react#next-ssr",
},
{
name: "SSR + client-side 🧪",
href: "/docs/project-setup/react#next-ssr-plus-csr",
},
];
export default function Page() {
return (
<div
className={clsx(
"col-span-12 md:col-span-8 lg:col-span-9",
"flex justify-center lg:gap-5",
)}
>
<Prose className="overflow-x-hidden lg:flex-1">
<NextGuide />
</Prose>
<TableOfContents className="w-48 shrink-0" items={navItems} />
</div>
);
}

View File

@@ -0,0 +1,30 @@
import ReactNativeGuide from "./react-native.mdx";
import { TableOfContents } from "@/components/docs/TableOfContents";
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
import { clsx } from "clsx";
const navItems = [
{
name: "Setup",
href: "/docs/project-setup/react#react-native-setup",
},
{
name: "Using Jazz",
href: "/docs/project-setup/react#react-native-using-jazz",
},
];
export default function Page() {
return (
<div
className={clsx(
"col-span-12 md:col-span-8 lg:col-span-9",
"flex justify-center lg:gap-5",
)}
>
<Prose className="overflow-x-hidden lg:flex-1">
<ReactNativeGuide />
</Prose>
<TableOfContents className="w-48 shrink-0" items={navItems} />
</div>
);
}

View File

@@ -0,0 +1,267 @@
import { CodeGroup } from "@/components/forMdx";
# <span id="react-native">React Native</span>
Jazz requires an [Expo development build](https://docs.expo.dev/develop/development-builds/introduction/) using [Expo Prebuild](https://docs.expo.dev/workflow/prebuild/) for native code. It is **not compatible** with Expo Go. Jazz also supports the [New Architecture](https://docs.expo.dev/guides/new-architecture/).
Tested with:
<CodeGroup>
```json
"expo": "~51.0.0",
"react-native": "~0.74.5",
"react": "^18.2.0",
```
</CodeGroup>
## <span id="react-native-setup">Setup</span>
### Create a New Project
(skip this step if you already have one)
<CodeGroup>
```bash
npx create-expo-app -e with-router-tailwind my-jazz-app
cd my-jazz-app
npx expo prebuild
```
</CodeGroup>
### Install dependencies
<CodeGroup>
```bash
npx expo install expo-linking expo-secure-store expo-file-system @react-native-community/netinfo @bam.tech/react-native-image-resizer
npm i -S react-native-polyfill-globals react-native-url-polyfill web-streams-polyfill@3.2.1 base-64 text-encoding react-native-fetch-api react-native-get-random-values buffer
npm i -D @babel/plugin-transform-class-static-block
npm i -S jazz-tools jazz-react-native jazz-react-native-media-images
```
</CodeGroup>
### Fix Incompatible Dependencies
<CodeGroup>
```bash
npx expo install --fix
```
</CodeGroup>
### Install Pods
<CodeGroup>
```bash
npx pod-install
```
</CodeGroup>
### Configure Metro
#### Regular Repositories
If you are not working within a monorepo, create a new file metro.config.js in the root of your project with the following content:
<CodeGroup>
```ts
const { getDefaultConfig } = require("expo/metro-config");
const config = getDefaultConfig(projectRoot);
config.resolver.unstable_enablePackageExports = true; // important setting
config.resolver.sourceExts = ["mjs", "js", "json", "ts", "tsx"];
config.resolver.requireCycleIgnorePatterns = [/(^|\/|\\)node_modules($|\/|\\)/];
module.exports = config;
```
</CodeGroup>
If you created the project using the command `npx create-expo-app -e with-router-tailwind my-jazz-app`, then `metro.config.js` is already present. In that case, simply add this setting to the existing file:
<CodeGroup>
```ts
config.resolver.unstable_enablePackageExports = true
```
</CodeGroup>
#### Monorepos
For monorepos, use the following metro.config.js:
<CodeGroup>
```ts
const { getDefaultConfig } = require("expo/metro-config");
const { FileStore } = require("metro-cache");
const path = require("path");
// eslint-disable-next-line no-undef
const projectRoot = __dirname;
const workspaceRoot = path.resolve(projectRoot, "../..");
const config = getDefaultConfig(projectRoot);
config.watchFolders = [workspaceRoot];
config.resolver.nodeModulesPaths = [
path.resolve(projectRoot, "node_modules"),
path.resolve(workspaceRoot, "node_modules"),
];
config.resolver.sourceExts = ["mjs", "js", "json", "ts", "tsx"];
config.resolver.unstable_enablePackageExports = true;
config.resolver.requireCycleIgnorePatterns = [/(^|\/|\\)node_modules($|\/|\\)/];
config.cacheStores = [
new FileStore({
root: path.join(projectRoot, "node_modules", ".cache", "metro"),
}),
];
module.exports = config;
```
</CodeGroup>
### Additional Monorepo Configuration (for pnpm users)
- Add node-linker=hoisted to the root .npmrc (create this file if it doesnt exist).
- Add the following to the root package.json:
<CodeGroup>
```json
"pnpm": {
"peerDependencyRules": {
"ignoreMissing": [
"@babel/*",
"expo-modules-*",
"typescript"
]
}
}
```
</CodeGroup>
For more information, refer to [this](https://github.com/byCedric/expo-monorepo-example#pnpm-workarounds) Expo monorepo example.
### Configure Babel
Add `@babel/plugin-transform-class-static-block` to the array of Babel plugins inside `babel.config.js`:
<CodeGroup>
```ts
module.exports = function (api) {
api.cache(true);
return {
presets: ["babel-preset-expo"],
plugins: [
"nativewind/babel",
"@babel/plugin-transform-class-static-block",
],
};
};
```
</CodeGroup>
### Add Polyfills
Create a file `polyfills.js` at the project root with the following content:
<CodeGroup>
```ts
import "react-native-polyfill-globals/auto";
import "@azure/core-asynciterator-polyfill";
import { ReadableStream } from "web-streams-polyfill/ponyfill/es6";
import { polyfillGlobal } from "react-native/Libraries/Utilities/PolyfillFunctions";
import { Buffer } from "buffer";
polyfillGlobal("Buffer", () => Buffer);
polyfillGlobal("ReadableStream", () => ReadableStream);
```
</CodeGroup>
Update `index.js` based on whether you are using expo-router or not:
#### If using `expo-router`
<CodeGroup>
```ts
import "./polyfills";
import "expo-router/entry";
```
</CodeGroup>
#### Without `expo-router`
<CodeGroup>
```ts
import "./polyfills";
import { registerRootComponent } from "expo";
import App from "./src/App";
registerRootComponent(App);
```
</CodeGroup>
Lastly, ensure that the `"main"` field in your `package.json` points to `index.js`:
<CodeGroup>
```json
"main": "index.js",
```
</CodeGroup>
## <span id="react-native-using-jazz">Using Jazz</span>
### `createJazzRNApp()`
Create a file `jazz.tsx` with the following contents:
<CodeGroup>
```tsx
import { createJazzRNApp } from "jazz-react-native";
export const Jazz = createJazzRNApp();
export const { useAccount, useCoState, useAcceptInvite } = Jazz;
```
</CodeGroup>
You can optionally pass a custom `kvStore` and `AccountSchema` to `createJazzRNApp()`, otherwise, it defaults to `ExpoSecureStoreAdapter` and `Account`.
### Choosing an Auth Method
Refer to the Jazz + React Native demo projects for implementing authentication:
- [DemoAuth Example](https://github.com/gardencmp/jazz/tree/main/examples/chat-rn)
- [ClerkAuth Example](https://github.com/gardencmp/jazz/tree/main/examples/chat-rn-clerk)
In the demos, you'll find details on:
- Using Jazz.Provider with your chosen authentication method
- Defining a Jazz schema
- Creating and subscribing to covalues
- Handling invites
### Working with Images
To work with images in Jazz, import the `createImage` function from [`jazz-react-native-media-images`](https://github.com/gardencmp/jazz/tree/main/packages/jazz-react-native-media-images).
<CodeGroup>
```tsx
import { createImage } from "jazz-react-native-media-images";
const base64ImageDataURI = "data:image/png;base64,...";
const image = await createImage(base64ImageDataURI, {
owner: newPetPost._owner,
maxSize: 2048, // optional: specify maximum image size
});
someCovalue.image = image;
```
</CodeGroup>
For a complete implementation, please refer to [this](https://github.com/gardencmp/jazz/blob/main/examples/pets/src/3_NewPetPostForm.tsx) demo.
### Running Your App
<CodeGroup>
```bash
npx expo run:ios
npx expo run:android
```
</CodeGroup>

View File

@@ -1,50 +1,18 @@
import ReactGuide from "./react.mdx"
import { TableOfContents } from "@/components/docs/TableOfContents";
import ReactGuide from "./react.mdx";
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
import { clsx } from "clsx";
const navItems = [
{
name: "React",
href: "/docs/project-setup/react#react",
},
{
name: "Next.JS",
href: "/docs/project-setup/react#next",
items: [
{
name: "Client-side only",
href: "/docs/project-setup/react#next-csr",
},
{
name: "SSR use 🧪",
href: "/docs/project-setup/react#next-ssr",
},
{
name: "SSR + client-side 🧪",
href: "/docs/project-setup/react#next-ssr-plus-csr",
},
]
},
{
name: "React Native",
href: "/docs/project-setup/react#react-native"
}
]
export default function Page() {
return (
<div
className={clsx(
"col-span-12 md:col-span-8 lg:col-span-9",
"lg:flex lg:gap-5",
"flex justify-center lg:gap-5",
)}
>
<Prose className="overflow-x-hidden lg:flex-1">
<ReactGuide />
</Prose>
<TableOfContents className="w-48 shrink-0" items={navItems} />
</div>
);
}

View File

@@ -1,6 +1,6 @@
import { CodeGroup } from "@/components/forMdx";
# React
# <span id="react">React</span>
Currently, the recommended pattern to set up a React app with Jazz is to create a separate file (for example, called `jazz.tsx`) in which:
@@ -80,82 +80,3 @@ ReactDOM.createRoot(document.getElementById("root")!).render( // old
);// old
```
</CodeGroup>
# Next.JS
## Client-side only
The easiest way to use Jazz with Next.JS is to only use it on the client side. You can ensure this by:
- marking the `jazz.tsx` file as `"use client"`
<CodeGroup>
{/* prettier-ignore */}
```tsx
"use client"
import { createJazzReactApp } from "jazz-react";// old
const Jazz = createJazzReactApp();// old
export const { useAccount, useCoState } = Jazz;// old
import { PasskeyAuthBasicUI, usePasskeyAuth } from "jazz-react";// old
function JazzAndAuth({ children }: { children: React.ReactNode }) {// old
const [passkeyAuth, passKeyState] = usePasskeyAuth({ appName });// old
return (// old
<>// old
<Jazz.Provider// old
auth={passkeyAuth}// old
peer="wss://mesh.jazz.tools/?key=you@example.com"// old
>// old
{children}// old
</Jazz.Provider>// old
<PasskeyAuthBasicUI state={passKeyState} />// old
</>// old
);// old
}// old
```
</CodeGroup>
- marking any file with components where you use Jazz hooks (such as `useAccount` or `useCoState`) as `"use client"`
## SSR use (experimental)
Pure SSR use of Jazz is basically just using jazz-nodejs (see [Node.JS / Server Workers](/docs/project-setup/server-side)) inside Server Components.
Instead of using hooks as you would on the client, you await promises returned by `CoValue.load(...)` inside your Server Components.
TODO: code example
This should work well for cases like rendering publicly-readable information, since the worker account will be able to load them.
In the future, it will be possible to use trusted auth methods (such as Clerk, Auth0, etc.) that let you act as the same Jazz user both on the client and on the server, letting you use SSR even for data private to that user.
## SSR + client-side (experimental)
You can combine the two approaches by creating
1. A pure "rendering" component that renders an already-loaded CoValue (in JSON-ified form)
TODO: code example
2. A "hydrating" component (with `"use client"`) that
- expects a pre-loaded CoValue as a prop (in JSON-ified form)
- uses one of the client-side Jazz hooks (such as `useAccount` or `useCoState`) to subscribe to that same CoValue
- passing the client-side subscribed state to the "rendering" component, with the pre-loaded CoValue as a fallback until the client receives the first subscribed state
TODO: code example
3. A "pre-loading" Server Component that
- pre-loads the CoValue by awaiting it's `load(...)` method (as described above)
- renders the "hydrating" component, passing the pre-loaded CoValue as a prop
TODO: code example
# React Native
TODO

View File

@@ -1,4 +1,4 @@
import ServerGuide from "./server-side.mdx"
import ServerGuide from "./server-side.mdx";
import { TableOfContents } from "@/components/docs/TableOfContents";
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
import { clsx } from "clsx";
@@ -19,16 +19,15 @@ const navItems = [
{
name: "Using CoValues instead of Requests",
href: "/docs/project-setup/server-side#covalues-instead-of-requests",
}
]
},
];
export default function Page() {
return (
<div
className={clsx(
"col-span-12 md:col-span-8 lg:col-span-9",
"lg:flex lg:gap-5",
"flex justify-center lg:gap-5",
)}
>
<Prose className="overflow-x-hidden lg:flex-1">

View File

@@ -1,4 +1,4 @@
import CoValuesGuide from "./covalues.mdx"
import CoValuesGuide from "./covalues.mdx";
import { TableOfContents } from "@/components/docs/TableOfContents";
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
import { clsx } from "clsx";
@@ -26,9 +26,9 @@ const navItems = [
},
{
name: "Computed Fields, Methods & Constructors ",
href: "/docs/schemas/covalues#custom-fields"
}
]
href: "/docs/schemas/covalues#custom-fields",
},
],
},
{
name: "CoMaps",
@@ -42,7 +42,7 @@ const navItems = [
name: "Dict/Record-like CoMaps",
href: "/docs/schemas/covalues#comaps-dict-like",
},
]
],
},
{
name: "CoLists",
@@ -55,16 +55,15 @@ const navItems = [
{
name: "BinaryCoStreams",
href: "/docs/schemas/covalues#binarycostreams",
}
]
},
];
export default function Page() {
return (
<div
className={clsx(
"col-span-12 md:col-span-8 lg:col-span-9",
"lg:flex lg:gap-5",
"flex justify-center lg:gap-5",
)}
>
<Prose className="overflow-x-hidden lg:flex-1">

View File

@@ -1,4 +1,4 @@
import SyncAndStorage from "./sync-and-storage.mdx"
import SyncAndStorage from "./sync-and-storage.mdx";
import { TableOfContents } from "@/components/docs/TableOfContents";
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
import { clsx } from "clsx";
@@ -12,7 +12,7 @@ const navItems = [
name: "Free Public Alpha",
href: "/docs/sync-and-storage#free-public-alpha",
},
]
],
},
{
name: "Running your own sync server",
@@ -26,17 +26,16 @@ const navItems = [
name: "Source code",
href: "/docs/sync-and-storage#source-code",
},
]
}
]
],
},
];
export default function Page() {
return (
<div
className={clsx(
"col-span-12 md:col-span-8 lg:col-span-9",
"lg:flex lg:gap-5",
"flex justify-center lg:gap-5",
)}
>
<Prose className="overflow-x-hidden lg:flex-1">

View File

@@ -83,7 +83,7 @@ export default function RootLayout({
manrope.variable,
commitMono.variable,
inter.className,
"h-full flex flex-col items-center [&_*]:scroll-mt-[5rem]",
"min-h-full flex flex-col items-center [&_*]:scroll-mt-[5rem]",
"bg-white text-stone-700 dark:text-stone-400 dark:bg-stone-950",
].join(" ")}
>
@@ -96,7 +96,7 @@ export default function RootLayout({
disableTransitionOnChange
>
<JazzNav />
<main className="flex flex-col w-full">{children}</main>
<main className="flex-1 w-full">{children}</main>
<JazzFooter />
</ThemeProvider>
</body>

View File

@@ -20,6 +20,11 @@ export function SideNav({
}) {
return (
<div className={clsx(className, "text-sm space-y-5")}>
<div className="flex items-center gap-2">
<span className="inline-block size-2 rounded-full bg-yellow-400"></span>{" "}
Documentation coming soon
</div>
{items.map(({ name, href, items }) => (
<div key={name}>
<SideNavHeader href={href}>{name}</SideNavHeader>
@@ -27,23 +32,29 @@ export function SideNav({
items.map(({ name, href, items, done }) => (
<ul key={name}>
<li>
<SideNavItem href={done === 0 ? undefined : href}>
<SideNavItem
href={
done === 0
? "/docs/coming-soon"
: href
}
>
{done == 0 && (
<span className="mr-1.5 inline-block size-2 rounded-full bg-yellow-400"></span>
)}
<span
className={
done === 0 ? "opacity-50" : ""
done === 0
? "text-stone-400 dark:text-stone-600"
: ""
}
>
{name}
</span>{" "}
{done === undefined ? (
""
) : (
<span className="text-xs opacity-50">
({done}%)
</span>
)}
</span>
</SideNavItem>
</li>
{items && items?.length > 0 && (
<ul className="pl-4">
{items.map(({ name, href }) => (

View File

@@ -11,7 +11,10 @@ export function SideNavItem({
children: ReactNode;
className?: string;
}) {
const classes = clsx(className, "py-1 block hover:transition-colors");
const classes = clsx(
className,
"py-1 flex items-center hover:transition-colors",
);
if (href) {
return (

View File

@@ -14,6 +14,8 @@ export function TableOfContents({
items: NavItem[];
className?: string;
}) {
if (!items.length) return null;
return (
<div
className={clsx(

View File

@@ -13,12 +13,14 @@ export function DocNav({ className }: { className?: string }) {
return (
<SideNav
items={docNavigationItems}
className={clsx(twMerge(
"pr-3 md:col-span-4 lg:col-span-3",
"sticky align-start top-[4.75rem] h-[calc(100vh-8rem)] overflow-y-auto overflow-x-hidden",
"hidden md:block",
className
))}
className={clsx(
twMerge(
"pr-3 md:col-span-4 lg:col-span-3",
"sticky align-start top-[4.75rem] h-[calc(100vh-8rem)] overflow-y-auto overflow-x-hidden",
"hidden md:block",
className,
),
)}
>
<div>
<SideNavHeader href="/docs/api-reference">

View File

@@ -1,7 +1,4 @@
import {
ArrowDownIcon,
ArrowRightIcon,
} from "lucide-react";
import { ArrowDownIcon, ArrowRightIcon } from "lucide-react";
import { DiagramBeforeJazz } from "@/components/DiagramBeforeJazz";
import { DiagramAfterJazz } from "@/components/DiagramAfterJazz";
import { SectionHeader } from "gcmp-design-system/src/app/components/molecules/SectionHeader";
@@ -33,10 +30,10 @@ export default function BeforeAfterJazz() {
The sad truth is...
</p>
<p>
<strong>
Every stack reinvents how users and machines
share state.
</strong>
<strong>
Every stack reinvents how users and machines
share state.
</strong>
</p>
</Prose>
<div className="relative flex items-center flex-1">

View File

@@ -48,7 +48,7 @@ export function JazzNav() {
icon: <SiTwitter className="w-5" />,
},
]}
docNav={<DocNav className="block h-auto"/>}
docNav={<DocNav className="block h-auto" />}
/>
);
}

View File

@@ -12,7 +12,7 @@ export const docNavigationItems = [
{
name: "Guide",
href: "/docs/guide",
done: 50
done: 50,
},
],
},
@@ -26,10 +26,18 @@ export const docNavigationItems = [
done: 100,
},
{
name: "React / Next.js / React Native",
name: "React",
href: "/docs/project-setup/react",
done: 80,
},
{
name: "React Native",
href: "/docs/project-setup/react-native",
},
{
name: "Next.js",
href: "/docs/project-setup/next",
},
{
name: "Node.JS / Server Workers",
href: "/docs/project-setup/server-side",
@@ -43,12 +51,12 @@ export const docNavigationItems = [
{
name: "CoValues",
href: "/docs/schemas/covalues",
done: 20
done: 20,
},
{
name: "Accounts & Migrations",
href: "/docs/schemas/accounts",
done: 0
done: 0,
},
],
},
@@ -58,22 +66,22 @@ export const docNavigationItems = [
{
name: "Creation & Ownership",
href: "/docs/covalues/creation",
done: 0
done: 0,
},
{
name: "Subscribing & Deep Loading",
href: "/docs/covalues/reading",
done: 0
done: 0,
},
{
name: "Updating & Deleting",
href: "/docs/covalues/updating",
done: 0
done: 0,
},
{
name: "Metadata & Time-travel",
href: "/docs/covalues/metadata",
done: 0
done: 0,
},
],
},
@@ -83,17 +91,17 @@ export const docNavigationItems = [
{
name: "Groups as Permission Scopes",
href: "/docs/groups/intro",
done: 0
done: 0,
},
{
name: "Public Sharing & Invites",
href: "/docs/groups/sharing",
done: 0
done: 0,
},
{
name: "Group Inheritance",
href: "/docs/groups/inheritance",
done: 0
done: 0,
},
],
},
@@ -103,12 +111,12 @@ export const docNavigationItems = [
{
name: "Auth Methods Overview",
href: "/docs/authentication/auth-methods",
done: 0
done: 0,
},
{
name: "Writing Your Own",
href: "/docs/authentication/writing-your-own",
done: 0
done: 0,
},
],
},
@@ -118,35 +126,12 @@ export const docNavigationItems = [
{
name: "Example apps",
href: "/docs/examples",
done: 30
done: 30,
},
{
name: "Jazz under the hood",
href: "/docs/jazz-under-the-hood",
done: 0
},
],
},
];
export const docNavigationItemsOld = [
{
// welcome to jazz
name: "Getting started",
href: "/docs/guide",
items: [
{
name: "Guide",
href: "/docs/guide",
},
],
},
{
name: "Resources",
items: [
{
name: "Example apps",
href: "/docs/examples",
done: 0,
},
],
},

View File

@@ -29,7 +29,7 @@ const config = {
function highlightPlugin() {
return async function transformer(tree) {
const highlighter = await getHighlighter({
langs: ["typescript", "bash", "tsx"],
langs: ["typescript", "bash", "tsx", "json"],
theme: "css-variables", // use the theme
});
@@ -47,7 +47,7 @@ function highlightPlugin() {
node.type = "html";
node.value = `<code class="not-prose py-2 flex flex-col leading-relaxed">${lines
.map((line) => {
let lineClassName = ''
let lineClassName = "";
const isSubduedLine = line.some((token) =>
token.content.includes("// old"),
@@ -58,19 +58,17 @@ function highlightPlugin() {
if (!isBinnedLine) {
lineNo++;
}
if (isBinnedLine) {
lineClassName = 'bg-red-100 dark:bg-red-800'
lineClassName = "bg-red-100 dark:bg-red-800";
}
return (
`<span class="block px-3 min-h-[1em] ${lineClassName}" style="${isBinnedLine ? "user-select: none" : ""}">` +
line
.map(
(token) => {
return `<span style="color: ${token.color};${isSubduedLine ? "opacity: 0.4;" : ""}">${escape(token.content.replace("// old", "").replace("// *bin*", ""))}</span>`
}
)
.map((token) => {
return `<span style="color: ${token.color};${isSubduedLine ? "opacity: 0.4;" : ""}">${escape(token.content.replace("// old", "").replace("// *bin*", ""))}</span>`;
})
.join("") +
"</span>"
);

View File

@@ -1,5 +1,13 @@
# cojson-storage-indexeddb
## 0.8.12
### Patch Changes
- 6ed75eb: Introduce "storage" peer role
- Updated dependencies [6ed75eb]
- cojson@0.8.12
## 0.8.11
### Patch Changes

View File

@@ -1,12 +1,12 @@
{
"name": "cojson-storage-indexeddb",
"version": "0.8.11",
"version": "0.8.12",
"main": "dist/index.js",
"type": "module",
"types": "src/index.ts",
"license": "MIT",
"dependencies": {
"cojson": "workspace:0.8.11",
"cojson": "workspace:0.8.12",
"typescript": "^5.3.3"
},
"devDependencies": {

View File

@@ -94,7 +94,7 @@ export class IDBStorage {
"storage",
{
peer1role: "client",
peer2role: "server",
peer2role: "storage",
trace,
crashOnClose: true,
},

View File

@@ -1,5 +1,13 @@
# cojson-storage-sqlite
## 0.8.12
### Patch Changes
- 6ed75eb: Introduce "storage" peer role
- Updated dependencies [6ed75eb]
- cojson@0.8.12
## 0.8.11
### Patch Changes

View File

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

View File

@@ -94,7 +94,7 @@ export class SQLiteStorage {
const [localNodeAsPeer, storageAsPeer] = cojsonInternals.connectedPeers(
localNodeName,
"storage",
{ peer1role: "client", peer2role: "server", trace, crashOnClose: true },
{ peer1role: "client", peer2role: "storage", trace, crashOnClose: true },
);
await SQLiteStorage.open(

View File

@@ -1,5 +1,12 @@
# cojson-transport-nodejs-ws
## 0.8.12
### Patch Changes
- Updated dependencies [6ed75eb]
- cojson@0.8.12
## 0.8.11
### Patch Changes

View File

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

View File

@@ -1,5 +1,11 @@
# cojson
## 0.8.12
### Patch Changes
- 6ed75eb: Introduce "storage" peer role
## 0.8.11
### Patch Changes

View File

@@ -19,7 +19,7 @@
},
"type": "module",
"license": "MIT",
"version": "0.8.11",
"version": "0.8.12",
"devDependencies": {
"@types/jest": "^29.5.3",
"@typescript-eslint/eslint-plugin": "^6.2.1",

View File

@@ -29,6 +29,10 @@ export class PeerState {
return this.peer.crashOnClose;
}
isServerOrStoragePeer() {
return this.peer.role === "server" || this.peer.role === "storage";
}
/**
* We set as default priority HIGH to handle all the messages without a
* priority property as HIGH priority.

View File

@@ -281,7 +281,7 @@ export class LocalNode {
if (!entry) {
const peersToWaitFor = new Set(
Object.values(this.syncManager.peers)
.filter((peer) => peer.role === "server")
.filter((peer) => peer.isServerOrStoragePeer())
.map((peer) => peer.id),
);
if (options.dontWaitFor) peersToWaitFor.delete(options.dontWaitFor);

View File

@@ -560,7 +560,7 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
"storage",
{
peer1role: "client",
peer2role: "server",
peer2role: "storage",
trace,
crashOnClose: true,
},

View File

@@ -75,7 +75,7 @@ export interface Peer {
id: PeerID;
incoming: IncomingSyncStream;
outgoing: OutgoingSyncQueue;
role: "peer" | "server" | "client";
role: "peer" | "server" | "client" | "storage";
priority?: number;
crashOnClose: boolean;
}
@@ -129,7 +129,7 @@ export class SyncManager {
async loadFromPeers(id: RawCoID, forPeer?: PeerID) {
const eligiblePeers = this.peersInPriorityOrder().filter(
(peer) => peer.id !== forPeer && peer.role === "server",
(peer) => peer.id !== forPeer && peer.isServerOrStoragePeer(),
);
const coValueEntry = this.local.coValues[id];
@@ -305,7 +305,7 @@ export class SyncManager {
const peerState = new PeerState(peer);
this.peers[peer.id] = peerState;
if (peer.role === "server") {
if (peerState.isServerOrStoragePeer()) {
const initialSync = async () => {
for (const id of Object.keys(
this.local.coValues,
@@ -393,7 +393,7 @@ export class SyncManager {
// special case: we should be able to solve this much more neatly
// with an explicit state machine in the future
const eligiblePeers = this.peersInPriorityOrder().filter(
(other) => other.id !== peer.id && other.role === "server",
(other) => other.id !== peer.id && other.isServerOrStoragePeer(),
);
if (eligiblePeers.length === 0) {
if (msg.header || Object.keys(msg.sessions).length > 0) {
@@ -698,7 +698,7 @@ export class SyncManager {
coValue.id,
peer,
);
} else if (peer.role === "server") {
} else if (peer.isServerOrStoragePeer()) {
await this.subscribeToIncludingDependencies(coValue.id, peer);
await this.sendNewContentIncludingDependencies(
coValue.id,

View File

@@ -1,5 +1,14 @@
# jazz-browser-media-images
## 0.8.12
### Patch Changes
- Updated dependencies [6ed75eb]
- cojson@0.8.12
- jazz-browser@0.8.12
- jazz-tools@0.8.12
## 0.8.11
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "jazz-browser-auth-clerk",
"version": "0.8.11",
"version": "0.8.12",
"type": "module",
"main": "dist/index.js",
"types": "src/index.ts",
@@ -10,9 +10,9 @@
"*.{js,jsx,mdx,json}": "prettier --write"
},
"dependencies": {
"cojson": "workspace:0.8.11",
"jazz-browser": "workspace:0.8.11",
"jazz-tools": "workspace:0.8.11"
"cojson": "workspace:0.8.12",
"jazz-browser": "workspace:0.8.12",
"jazz-tools": "workspace:0.8.12"
},
"scripts": {
"lint": "eslint . --ext ts,tsx",

View File

@@ -1,5 +1,12 @@
# jazz-browser-media-images
## 0.8.12
### Patch Changes
- jazz-browser@0.8.12
- jazz-tools@0.8.12
## 0.8.11
### Patch Changes

View File

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

View File

@@ -1,5 +1,15 @@
# jazz-browser
## 0.8.12
### Patch Changes
- Updated dependencies [6ed75eb]
- cojson-storage-indexeddb@0.8.12
- cojson@0.8.12
- cojson-transport-ws@0.8.12
- jazz-tools@0.8.12
## 0.8.11
### Patch Changes

View File

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

View File

@@ -1,5 +1,14 @@
# jazz-autosub
## 0.8.12
### Patch Changes
- Updated dependencies [6ed75eb]
- cojson@0.8.12
- cojson-transport-ws@0.8.12
- jazz-tools@0.8.12
## 0.8.11
### Patch Changes

View File

@@ -5,11 +5,11 @@
"types": "src/index.ts",
"type": "module",
"license": "MIT",
"version": "0.8.11",
"version": "0.8.12",
"dependencies": {
"cojson": "workspace:0.8.11",
"cojson-transport-ws": "workspace:0.8.11",
"jazz-tools": "workspace:0.8.11",
"cojson": "workspace:0.8.12",
"cojson-transport-ws": "workspace:0.8.12",
"jazz-tools": "workspace:0.8.12",
"ws": "^8.14.2"
},
"devDependencies": {

View File

@@ -1,5 +1,16 @@
# jazz-browser-media-images
## 0.8.12
### Patch Changes
- Updated dependencies [6ed75eb]
- Updated dependencies [3cc6aee]
- cojson@0.8.12
- jazz-react@0.8.12
- jazz-browser-auth-clerk@0.8.12
- jazz-tools@0.8.12
## 0.8.11
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "jazz-react-auth-clerk",
"version": "0.8.11",
"version": "0.8.12",
"type": "module",
"main": "dist/index.js",
"types": "src/index.tsx",
@@ -10,10 +10,10 @@
"*.{js,jsx,mdx,json}": "prettier --write"
},
"dependencies": {
"cojson": "workspace:0.8.11",
"jazz-browser-auth-clerk": "workspace:0.8.11",
"jazz-react": "workspace:0.8.11",
"jazz-tools": "workspace:0.8.11"
"cojson": "workspace:0.8.12",
"jazz-browser-auth-clerk": "workspace:0.8.12",
"jazz-react": "workspace:0.8.12",
"jazz-tools": "workspace:0.8.12"
},
"peerDependencies": {
"react": "^18.2.0"

View File

@@ -1,5 +1,11 @@
# jazz-browser-media-images
## 0.8.8
### Patch Changes
- jazz-tools@0.8.12
## 0.8.7
### Patch Changes

View File

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

View File

@@ -1,5 +1,14 @@
# jazz-browser
## 0.8.12
### Patch Changes
- Updated dependencies [6ed75eb]
- cojson@0.8.12
- cojson-transport-ws@0.8.12
- jazz-tools@0.8.12
## 0.8.11
### Patch Changes

View File

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

View File

@@ -1,5 +1,15 @@
# jazz-react
## 0.8.12
### Patch Changes
- 3cc6aee: Temporary fix to prevent double creation of Jazz context in React StrictMode
- Updated dependencies [6ed75eb]
- cojson@0.8.12
- jazz-browser@0.8.12
- jazz-tools@0.8.12
## 0.8.11
### Patch Changes

View File

@@ -1,15 +1,15 @@
{
"name": "jazz-react",
"version": "0.8.11",
"version": "0.8.12",
"type": "module",
"main": "dist/index.js",
"types": "src/index.ts",
"license": "MIT",
"dependencies": {
"@scure/bip39": "^1.3.0",
"cojson": "workspace:0.8.11",
"jazz-browser": "workspace:0.8.11",
"jazz-tools": "workspace:0.8.11",
"cojson": "workspace:0.8.12",
"jazz-browser": "workspace:0.8.12",
"jazz-tools": "workspace:0.8.12",
"typescript": "^5.3.3"
},
"devDependencies": {

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useState } from "react";
import React, { useEffect, useRef, useState } from "react";
import {
BrowserContext,
BrowserGuestContext,
@@ -46,34 +46,74 @@ export function createJazzReactApp<Acc extends Account>({
const [sessionCount, setSessionCount] = useState(0);
const effectExecuted = useRef(false);
effectExecuted.current = false;
useEffect(() => {
const promiseWithDoneCallback = createJazzBrowserContext<Acc>(
auth === "guest"
? {
peer,
storage,
}
: {
AccountSchema,
auth: auth,
peer,
storage,
},
).then((context) => {
// Avoid double execution of the effect in development mode for easier debugging.
if (process.env.NODE_ENV === "development") {
if (effectExecuted.current) {
return;
}
effectExecuted.current = true;
// In development mode we don't return a cleanup function because otherwise
// the double effect execution would mark the context as done immediately.
//
// So we mark it as done in the subsequent execution.
const previousContext = ctx;
if (previousContext) {
previousContext.done();
}
}
async function createContext() {
const currentContext = await createJazzBrowserContext<Acc>(
auth === "guest"
? {
peer,
storage,
}
: {
AccountSchema,
auth,
peer,
storage,
},
);
const logOut = () => {
currentContext.logOut();
setCtx(undefined);
setSessionCount(sessionCount + 1);
if (process.env.NODE_ENV === "development") {
// In development mode we don't return a cleanup function
// so we mark the context as done here.
currentContext.done();
}
};
setCtx({
...context,
logOut: () => {
context.logOut();
setCtx(undefined);
setSessionCount(sessionCount + 1);
},
...currentContext,
logOut,
});
return context.done;
});
return currentContext;
}
const promise = createContext();
// In development mode we don't return a cleanup function because otherwise
// the double effect execution would mark the context as done immediately.
if (process.env.NODE_ENV === "development") {
return;
}
return () => {
void promiseWithDoneCallback.then((done) => done());
};
void promise.then((context) => context.done());
}
}, [AccountSchema, auth, peer, storage, sessionCount]);
return (

View File

@@ -1,5 +1,15 @@
# jazz-run
## 0.8.12
### Patch Changes
- Updated dependencies [6ed75eb]
- cojson-storage-sqlite@0.8.12
- cojson@0.8.12
- cojson-transport-ws@0.8.12
- jazz-tools@0.8.12
## 0.8.11
### Patch Changes

View File

@@ -3,7 +3,7 @@
"bin": "./dist/index.js",
"type": "module",
"license": "MIT",
"version": "0.8.11",
"version": "0.8.12",
"scripts": {
"lint": "eslint . --ext ts,tsx",
"format": "prettier --write './src/**/*.{ts,tsx}'",
@@ -17,11 +17,11 @@
"@effect/printer-ansi": "^0.34.5",
"@effect/schema": "^0.71.1",
"@effect/typeclass": "^0.25.5",
"cojson": "workspace:0.8.11",
"cojson-storage-sqlite": "workspace:0.8.11",
"cojson-transport-ws": "workspace:0.8.11",
"cojson": "workspace:0.8.12",
"cojson-storage-sqlite": "workspace:0.8.12",
"cojson-transport-ws": "workspace:0.8.12",
"effect": "^3.6.5",
"jazz-tools": "workspace:0.8.11",
"jazz-tools": "workspace:0.8.12",
"ws": "^8.14.2"
},
"devDependencies": {

View File

@@ -1,125 +0,0 @@
import { Command, Options } from "@effect/cli";
import { Console, Effect } from "effect";
import { createWebSocketPeer } from "cojson-transport-ws";
import { WebSocket } from "ws";
import {
Account,
Peer,
WasmCrypto,
createJazzContext,
isControlledAccount,
} from "jazz-tools";
import { fixedCredentialsAuth, randomSessionProvider } from "jazz-tools";
import { CoValueCore } from "cojson";
const name = Options.text("name").pipe(Options.withAlias("n"));
const peer = Options.text("peer")
.pipe(Options.withAlias("p"))
.pipe(Options.withDefault("wss://cloud.jazz.tools"));
const accountCreate = Command.make(
"create",
{ name, peer },
({ name, peer: peerAddr }) => {
return Effect.gen(function* () {
const crypto = yield* Effect.promise(() => WasmCrypto.create());
const peer = createWebSocketPeer({
id: "upstream",
websocket: new WebSocket(peerAddr),
role: "server",
});
const account: Account = yield* Effect.promise(async () =>
Account.create({
creationProps: { name },
peersToLoadFrom: [peer],
crypto,
}),
);
if (!isControlledAccount(account)) {
throw new Error("account is not a controlled account");
}
const accountCoValue = account._raw.core;
const accountProfileCoValue = account.profile!._raw.core;
const syncManager = account._raw.core.node.syncManager;
yield* Effect.promise(() =>
syncManager.syncCoValue(accountCoValue),
);
yield* Effect.promise(() =>
syncManager.syncCoValue(accountProfileCoValue),
);
yield* Effect.promise(() => Promise.all([
waitForSync(account, peer, accountCoValue),
waitForSync(account, peer, accountProfileCoValue),
]));
// Spawn a second peer to double check that the account is fully synced
const peer2 = createWebSocketPeer({
id: "upstream2",
websocket: new WebSocket(peerAddr),
role: "server",
});
yield* Effect.promise(async () =>
createJazzContext({
auth: fixedCredentialsAuth({
accountID: account.id,
secret: account._raw.agentSecret,
}),
sessionProvider: randomSessionProvider,
peersToLoadFrom: [peer2],
crypto,
}),
);
yield* Console.log(`# Credentials for Jazz account "${name}":
JAZZ_WORKER_ACCOUNT=${account.id}
JAZZ_WORKER_SECRET=${account._raw.agentSecret}
`);
});
},
);
const accountBase = Command.make("account");
export const account = accountBase.pipe(Command.withSubcommands([accountCreate]));
function waitForSync(account: Account, peer: Peer, coValue: CoValueCore) {
const syncManager = account._raw.core.node.syncManager;
const peerState = syncManager.peers[peer.id];
return new Promise((resolve) => {
const unsubscribe = peerState?.optimisticKnownStates.subscribe((id, peerKnownState) => {
if (id !== coValue.id) return;
const knownState = coValue.knownState();
const synced = isEqualSession(knownState.sessions, peerKnownState.sessions);
if (synced) {
resolve(true);
unsubscribe?.();
}
});
});
}
function isEqualSession(a: Record<string, number>, b: Record<string, number>) {
const keysA = Object.keys(a);
const keysB = Object.keys(b);
if (keysA.length !== keysB.length) {
return false;
}
for (const sessionId of keysA) {
if (a[sessionId] !== b[sessionId]) {
return false;
}
}
return true;
}

View File

@@ -0,0 +1,114 @@
import { createWebSocketPeer } from "cojson-transport-ws";
import { WebSocket } from "ws";
import {
Account,
Peer,
WasmCrypto,
createJazzContext,
isControlledAccount,
} from "jazz-tools";
import { fixedCredentialsAuth, randomSessionProvider } from "jazz-tools";
import { CoValueCore } from "cojson";
export const createWorkerAccount = async ({
name,
peer: peerAddr,
}: {
name: string;
peer: string;
}) => {
const crypto = await WasmCrypto.create();
const peer = createWebSocketPeer({
id: "upstream",
websocket: new WebSocket(peerAddr),
role: "server",
});
const account = await Account.create({
creationProps: { name },
peersToLoadFrom: [peer],
crypto,
});
if (!isControlledAccount(account)) {
throw new Error("account is not a controlled account");
}
const accountCoValue = account._raw.core;
const accountProfileCoValue = account.profile!._raw.core;
const syncManager = account._raw.core.node.syncManager;
await Promise.all([
syncManager.syncCoValue(accountCoValue),
syncManager.syncCoValue(accountProfileCoValue),
]);
await Promise.all([
waitForSync(account, peer, accountCoValue),
waitForSync(account, peer, accountProfileCoValue),
]);
// Spawn a second peer to double check that the account is fully synced
const peer2 = createWebSocketPeer({
id: "upstream2",
websocket: new WebSocket(peerAddr),
role: "server",
});
await createJazzContext({
auth: fixedCredentialsAuth({
accountID: account.id,
secret: account._raw.agentSecret,
}),
sessionProvider: randomSessionProvider,
peersToLoadFrom: [peer2],
crypto,
});
return {
accountId: account.id,
agentSecret: account._raw.agentSecret,
};
};
function waitForSync(account: Account, peer: Peer, coValue: CoValueCore) {
const syncManager = account._raw.core.node.syncManager;
const peerState = syncManager.peers[peer.id];
return new Promise((resolve) => {
const unsubscribe = peerState?.optimisticKnownStates.subscribe(
(id, peerKnownState) => {
if (id !== coValue.id) return;
const knownState = coValue.knownState();
const synced = isEqualSession(
knownState.sessions,
peerKnownState.sessions,
);
if (synced) {
resolve(true);
unsubscribe?.();
}
},
);
});
}
function isEqualSession(a: Record<string, number>, b: Record<string, number>) {
const keysA = Object.keys(a);
const keysB = Object.keys(b);
if (keysA.length !== keysB.length) {
return false;
}
for (const sessionId of keysA) {
if (a[sessionId] !== b[sessionId]) {
return false;
}
}
return true;
}

View File

@@ -1,18 +1,86 @@
#!/usr/bin/env node
/* istanbul ignore file -- @preserve */
import { Command } from "@effect/cli";
import { Command, Options } from "@effect/cli";
import { NodeContext, NodeRuntime } from "@effect/platform-node";
import { Effect } from "effect";
import { account } from "./accountCreate.js";
import { startSync } from "./startSync.js";
import { Console, Effect } from "effect";
import { createWorkerAccount } from "./createWorkerAccount.js";
import { startSyncServer } from "./startSyncServer.js";
const jazzTools = Command.make("jazz-tools");
const command = jazzTools.pipe(Command.withSubcommands([account, startSync]));
const nameOption = Options.text("name").pipe(Options.withAlias("n"));
const peerOption = Options.text("peer")
.pipe(Options.withAlias("p"))
.pipe(Options.withDefault("wss://cloud.jazz.tools"));
const createAccountCommand = Command.make(
"create",
{ name: nameOption, peer: peerOption },
({ name, peer }) => {
return Effect.gen(function* () {
const { accountId, agentSecret } = yield* Effect.promise(() =>
createWorkerAccount({ name, peer }),
);
yield* Console.log(`# Credentials for Jazz account "${name}":
JAZZ_WORKER_ACCOUNT=${accountId}
JAZZ_WORKER_SECRET=${agentSecret}
`);
});
},
);
const accountCommand = Command.make("account").pipe(
Command.withSubcommands([createAccountCommand]),
);
const portOption = Options.text("port")
.pipe(Options.withAlias("p"))
.pipe(
Options.withDescription(
"Select a different port for the WebSocket server. Default is 4200",
),
)
.pipe(Options.withDefault("4200"));
const inMemoryOption = Options.boolean("in-memory").pipe(
Options.withDescription("Use an in-memory storage instead of file-based"),
);
const dbOption = Options.file("db")
.pipe(
Options.withDescription(
"The path to the file where to store the data. Default is 'sync-db/storage.db'",
),
)
.pipe(Options.withDefault("sync-db/storage.db"));
export const startSyncServerCommand = Command.make(
"sync",
{ port: portOption, inMemory: inMemoryOption, db: dbOption },
({ port, inMemory, db }) => {
return Effect.gen(function* () {
yield* Effect.promise(() =>
startSyncServer({ port, inMemory, db }),
);
Console.log(
`COJSON sync server listening on ws://127.0.0.1:${port}`,
);
// Keep the server up
yield* Effect.never;
});
},
);
const command = jazzTools.pipe(
Command.withSubcommands([accountCommand, startSyncServerCommand]),
);
const cli = Command.run(command, {
name: "Jazz CLI Tools",
version: "v0.7.0",
version: "v0.8.11",
});
Effect.suspend(() => cli(process.argv)).pipe(

View File

@@ -1,125 +0,0 @@
/* istanbul ignore file -- @preserve */
import { Command, Options } from "@effect/cli";
import { ControlledAgent, LocalNode, WasmCrypto } from "cojson";
import { WebSocketServer } from "ws";
import { createServer } from "http";
import { createWebSocketPeer } from "cojson-transport-ws";
import { Effect } from "effect";
import { SQLiteStorage } from "cojson-storage-sqlite";
import { dirname } from "node:path";
import { mkdir } from "node:fs/promises";
const port = Options.text("port")
.pipe(Options.withAlias("p"))
.pipe(
Options.withDescription(
"Select a different port for the WebSocket server. Default is 4200",
),
)
.pipe(Options.withDefault("4200"));
const inMemory = Options.boolean("in-memory").pipe(
Options.withDescription("Use an in-memory storage instead of file-based"),
);
const db = Options.file("db")
.pipe(
Options.withDescription(
"The path to the file where to store the data. Default is 'sync-db/storage.db'",
),
)
.pipe(Options.withDefault("sync-db/storage.db"));
export const startSync = Command.make(
"sync",
{ port, inMemory, db },
({ port, inMemory, db }) => {
return Effect.gen(function* () {
const crypto = yield* Effect.promise(() => WasmCrypto.create());
const server = createServer((req, res) => {
if (req.url === "/health") {
res.writeHead(200);
res.end("ok");
}
});
const wss = new WebSocketServer({ noServer: true });
console.log("COJSON sync server listening on port " + port);
const agentSecret = crypto.newRandomAgentSecret();
const agentID = crypto.getAgentID(agentSecret);
const localNode = new LocalNode(
new ControlledAgent(agentSecret, crypto),
crypto.newRandomSessionID(agentID),
crypto,
);
if (!inMemory) {
yield* Effect.promise(() =>
mkdir(dirname(db), { recursive: true }),
);
const storage = yield* Effect.promise(() =>
SQLiteStorage.asPeer({ filename: db }),
);
localNode.syncManager.addPeer(storage);
}
wss.on("connection", function connection(ws, req) {
// ping/pong for the connection liveness
const pinging = setInterval(() => {
ws.send(
JSON.stringify({
type: "ping",
time: Date.now(),
dc: "unknown",
}),
);
}, 1500);
ws.on("close", () => {
clearInterval(pinging);
});
const clientAddress =
(req.headers["x-forwarded-for"] as string | undefined)
?.split(",")[0]
?.trim() || req.socket.remoteAddress;
const clientId = clientAddress + "@" + new Date().toISOString();
localNode.syncManager.addPeer(
createWebSocketPeer({
id: clientId,
role: "client",
// eslint-disable-next-line @typescript-eslint/no-explicit-any
websocket: ws as any, // TODO: fix types
expectPings: false,
batchingByDefault: false
}),
);
ws.on("error", (e) =>
console.error(`Error on connection ${clientId}:`, e),
);
});
server.on("upgrade", function upgrade(req, socket, head) {
if (req.url !== "/health") {
wss.handleUpgrade(req, socket, head, function done(ws) {
wss.emit("connection", ws, req);
});
}
});
server.listen(parseInt(port));
// Keep the server up
yield* Effect.never;
});
},
);

View File

@@ -0,0 +1,95 @@
import { ControlledAgent, LocalNode, WasmCrypto } from "cojson";
import { WebSocketServer } from "ws";
import { createServer } from "http";
import { createWebSocketPeer } from "cojson-transport-ws";
import { SQLiteStorage } from "cojson-storage-sqlite";
import { dirname } from "node:path";
import { mkdir } from "node:fs/promises";
export const startSyncServer = async ({
port,
inMemory,
db,
}: {
port: string | undefined;
inMemory: boolean;
db: string;
}) => {
const crypto = await WasmCrypto.create();
const server = createServer((req, res) => {
if (req.url === "/health") {
res.writeHead(200);
res.end("ok");
}
});
const wss = new WebSocketServer({ noServer: true });
const agentSecret = crypto.newRandomAgentSecret();
const agentID = crypto.getAgentID(agentSecret);
const localNode = new LocalNode(
new ControlledAgent(agentSecret, crypto),
crypto.newRandomSessionID(agentID),
crypto,
);
if (!inMemory) {
await mkdir(dirname(db), { recursive: true })
const storage = await SQLiteStorage.asPeer({ filename: db });
localNode.syncManager.addPeer(storage);
}
wss.on("connection", function connection(ws, req) {
// ping/pong for the connection liveness
const pinging = setInterval(() => {
ws.send(
JSON.stringify({
type: "ping",
time: Date.now(),
dc: "unknown",
}),
);
}, 1500);
ws.on("close", () => {
clearInterval(pinging);
});
const clientAddress =
(req.headers["x-forwarded-for"] as string | undefined)
?.split(",")[0]
?.trim() || req.socket.remoteAddress;
const clientId = clientAddress + "@" + new Date().toISOString();
localNode.syncManager.addPeer(
createWebSocketPeer({
id: clientId,
role: "client",
websocket: ws,
expectPings: false,
batchingByDefault: false,
}),
);
ws.on("error", (e) =>
console.error(`Error on connection ${clientId}:`, e),
);
});
server.on("upgrade", function upgrade(req, socket, head) {
if (req.url !== "/health") {
wss.handleUpgrade(req, socket, head, function done(ws) {
wss.emit("connection", ws, req);
});
}
});
server.listen(port ? parseInt(port) : undefined);
return server;
};

View File

@@ -0,0 +1,32 @@
import { describe, onTestFinished, it, expect } from "vitest";
import { startSyncServer } from "../startSyncServer.js";
import { createWorkerAccount } from "../createWorkerAccount.js";
describe("createWorkerAccount - integration tests", () => {
it("should create a worker account using the local sync server", async () => {
// Pass port: undefined to let the server choose a random port
const server = await startSyncServer({ port: undefined, inMemory: true, db: '' });
onTestFinished(() => {
server.close()
});
const address = server.address();
if (typeof address !== 'object' || address === null) {
throw new Error('Server address is not an object');
}
const { accountId, agentSecret } = await createWorkerAccount({ name: "test", peer: `ws://localhost:${address.port}` });
expect(accountId).toBeDefined();
expect(agentSecret).toBeDefined();
});
it("should create a worker account using the Jazz cloud", async () => {
const { accountId, agentSecret } = await createWorkerAccount({ name: "test", peer: `wss://cloud.jazz.tools` });
expect(accountId).toBeDefined();
expect(agentSecret).toBeDefined();
});
});

View File

@@ -1,5 +1,12 @@
# jazz-tools
## 0.8.12
### Patch Changes
- Updated dependencies [6ed75eb]
- cojson@0.8.12
## 0.8.11
### Patch Changes

View File

@@ -19,7 +19,7 @@
},
"type": "module",
"license": "MIT",
"version": "0.8.11",
"version": "0.8.12",
"dependencies": {
"cojson": "workspace:*",
"fast-check": "^3.17.2"

108
pnpm-lock.yaml generated
View File

@@ -40,7 +40,7 @@ importers:
e2e/BinaryCoStream:
dependencies:
cojson:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../../packages/cojson
hash-slash:
specifier: workspace:0.2.1
@@ -49,10 +49,10 @@ importers:
specifier: ^3.0.1
version: 3.0.1
jazz-react:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../../packages/jazz-react
jazz-tools:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../../packages/jazz-tools
react:
specifier: 18.3.1
@@ -144,13 +144,13 @@ importers:
specifier: ^2.0.0
version: 2.0.0
jazz-browser-media-images:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../../packages/jazz-browser-media-images
jazz-react:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../../packages/jazz-react
jazz-tools:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../../packages/jazz-tools
next:
specifier: 14.2.5
@@ -217,16 +217,16 @@ importers:
specifier: ^2.0.0
version: 2.0.0
cojson:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../../packages/cojson
hash-slash:
specifier: workspace:0.2.1
version: link:../../packages/hash-slash
jazz-react:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../../packages/jazz-react
jazz-tools:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../../packages/jazz-tools
lucide-react:
specifier: ^0.274.0
@@ -329,19 +329,19 @@ importers:
specifier: ^2.0.0
version: 2.0.0
cojson:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../../packages/cojson
hash-slash:
specifier: workspace:0.2.1
version: link:../../packages/hash-slash
jazz-react:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../../packages/jazz-react
jazz-react-auth-clerk:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../../packages/jazz-react-auth-clerk
jazz-tools:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../../packages/jazz-tools
lucide-react:
specifier: ^0.274.0
@@ -722,10 +722,10 @@ importers:
specifier: ^2.0.0
version: 2.0.0
cojson:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../../packages/cojson
cojson-transport-ws:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../../packages/cojson-transport-ws
hash-slash:
specifier: workspace:0.2.1
@@ -822,10 +822,10 @@ importers:
specifier: ^2.0.0
version: 2.0.0
jazz-react:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../../packages/jazz-react
jazz-tools:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../../packages/jazz-tools
lucide-react:
specifier: ^0.274.0
@@ -898,10 +898,10 @@ importers:
examples/password-manager:
dependencies:
jazz-react:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../../packages/jazz-react
jazz-tools:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../../packages/jazz-tools
react:
specifier: 18.3.1
@@ -977,13 +977,13 @@ importers:
specifier: ^2.0.0
version: 2.0.0
jazz-browser-media-images:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../../packages/jazz-browser-media-images
jazz-react:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../../packages/jazz-react
jazz-tools:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../../packages/jazz-tools
lucide-react:
specifier: ^0.274.0
@@ -1053,7 +1053,7 @@ importers:
specifier: ^3.0.1
version: 3.0.1
jazz-run:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../../packages/jazz-run
postcss:
specifier: ^8.4.27
@@ -1091,10 +1091,10 @@ importers:
specifier: ^2.0.0
version: 2.0.0
jazz-react:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../../packages/jazz-react
jazz-tools:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../../packages/jazz-tools
lucide-react:
specifier: ^0.274.0
@@ -1228,7 +1228,7 @@ importers:
packages/cojson-storage-indexeddb:
dependencies:
cojson:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../cojson
typescript:
specifier: ^5.3.3
@@ -1253,7 +1253,7 @@ importers:
specifier: ^8.5.2
version: 8.7.0
cojson:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../cojson
typescript:
specifier: ^5.3.3
@@ -1266,7 +1266,7 @@ importers:
packages/cojson-transport-ws:
dependencies:
cojson:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../cojson
typescript:
specifier: ^5.3.3
@@ -1294,16 +1294,16 @@ importers:
specifier: ^1.3.0
version: 1.3.0
cojson:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../cojson
cojson-storage-indexeddb:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../cojson-storage-indexeddb
cojson-transport-ws:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../cojson-transport-ws
jazz-tools:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../jazz-tools
typescript:
specifier: ^5.3.3
@@ -1312,13 +1312,13 @@ importers:
packages/jazz-browser-auth-clerk:
dependencies:
cojson:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../cojson
jazz-browser:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../jazz-browser
jazz-tools:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../jazz-tools
devDependencies:
typescript:
@@ -1334,10 +1334,10 @@ importers:
specifier: ^4.1.0
version: 4.1.0
jazz-browser:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../jazz-browser
jazz-tools:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../jazz-tools
pica:
specifier: ^9.0.1
@@ -1353,13 +1353,13 @@ importers:
packages/jazz-nodejs:
dependencies:
cojson:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../cojson
cojson-transport-ws:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../cojson-transport-ws
jazz-tools:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../jazz-tools
ws:
specifier: ^8.14.2
@@ -1378,13 +1378,13 @@ importers:
specifier: ^1.3.0
version: 1.3.0
cojson:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../cojson
jazz-browser:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../jazz-browser
jazz-tools:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../jazz-tools
typescript:
specifier: ^5.3.3
@@ -1403,16 +1403,16 @@ importers:
packages/jazz-react-auth-clerk:
dependencies:
cojson:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../cojson
jazz-browser-auth-clerk:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../jazz-browser-auth-clerk
jazz-react:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../jazz-react
jazz-tools:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../jazz-tools
react:
specifier: 18.3.1
@@ -1496,19 +1496,19 @@ importers:
specifier: ^0.25.5
version: 0.25.5(effect@3.6.5)
cojson:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../cojson
cojson-storage-sqlite:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../cojson-storage-sqlite
cojson-transport-ws:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../cojson-transport-ws
effect:
specifier: ^3.6.5
version: 3.6.5
jazz-tools:
specifier: workspace:0.8.11
specifier: workspace:0.8.12
version: link:../jazz-tools
ws:
specifier: ^8.14.2
@@ -15740,7 +15740,7 @@ snapshots:
eslint: 8.57.1
eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@6.15.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(eslint@8.57.1))(eslint@8.57.1)
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@6.15.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@6.15.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1)
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@6.15.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
eslint-plugin-jsx-a11y: 6.10.0(eslint@8.57.1)
eslint-plugin-react: 7.37.1(eslint@8.57.1)
eslint-plugin-react-hooks: 4.6.0(eslint@8.57.1)
@@ -15779,7 +15779,7 @@ snapshots:
is-bun-module: 1.2.1
is-glob: 4.0.3
optionalDependencies:
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@6.15.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@6.15.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1)
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@6.15.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
transitivePeerDependencies:
- '@typescript-eslint/parser'
- eslint-import-resolver-node
@@ -15836,7 +15836,7 @@ snapshots:
- supports-color
- typescript
eslint-plugin-import@2.31.0(@typescript-eslint/parser@6.15.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@6.15.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1):
eslint-plugin-import@2.31.0(@typescript-eslint/parser@6.15.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1):
dependencies:
'@rtsao/scc': 1.1.0
array-includes: 3.1.8