Compare commits

..

29 Commits

Author SHA1 Message Date
shortdiv
d4512ff2fc fix: Use required for input instead of disabled 2025-06-12 14:56:40 -04:00
shortdiv
9b2b747f76 fix: Pull out repeated bit into a const 2025-06-12 14:01:03 -04:00
shortdiv
3f6b98cdcb fix: Prevent submitting form if account id isn't accompanied by a secret 2025-06-12 13:54:51 -04:00
Benjamin S. Leveritt
518406e23d Merge pull request #2497 from garden-co/benjamin-gco-546-throw-on-typecheck-failures-with-twoslash
Throw on typecheck failures with twoslash
2025-06-12 12:39:53 +01:00
Nikos Papadopoulos
4dcbafa058 Merge pull request #2508 from garden-co/2503-create-invitation-onclick-rather-than-pre-emptively-in-org-example
organization example app: Invitation creation moved to button click event
2025-06-12 13:06:09 +02:00
Benjamin S. Leveritt
7ae9e01848 Fix. 2025-06-12 11:27:13 +01:00
Benjamin S. Leveritt
dd9ecf660d Fix? 2025-06-12 11:24:22 +01:00
Benjamin S. Leveritt
4f849050dc Fix? 2025-06-12 11:17:33 +01:00
Benjamin S. Leveritt
681600220f Excludes homepage from catalog deps 2025-06-12 10:36:26 +01:00
Benjamin S. Leveritt
384e239ad5 Bumps next to 15 in design system 2025-06-12 10:25:34 +01:00
Benjamin S. Leveritt
54e1a09a46 Bumps next to 15 2025-06-12 10:24:07 +01:00
Benjamin S. Leveritt
392a9c5aac Moves react deps to catalogs 2025-06-12 10:22:28 +01:00
Benjamin S. Leveritt
478334eabf Merge pull request #2482 from garden-co/2480-add-more-context-to-introduction-from-the-homepage
Adds context to the introduction page
2025-06-12 08:49:10 +01:00
Benjamin S. Leveritt
479f9b0113 Adds extends to turbo configs
As they're in a workspace
2025-06-12 08:33:16 +01:00
Benjamin S. Leveritt
812622b161 Adds package manager to package.json 2025-06-12 08:25:59 +01:00
Benjamin S. Leveritt
8b35fae4b6 Adds an error suppression for old code 2025-06-12 08:20:53 +01:00
Benjamin S. Leveritt
9e2ecb0378 Bumps React to 19 2025-06-12 08:11:54 +01:00
Nikos Papadopoulos
6edd061202 removes invite link state 2025-06-11 17:07:07 +01:00
Nikos Papadopoulos
865d5385e9 moves invitation creation to button click event in organization example app 2025-06-11 16:55:18 +01:00
Benjamin S. Leveritt
a998f94789 Includes the recommendation in the error 2025-06-11 12:46:53 +01:00
Benjamin S. Leveritt
d17eecfe16 Removes z.null from docs 2025-06-11 12:19:29 +01:00
Benjamin S. Leveritt
8ebfbc86db Rethrows in production 2025-06-11 11:36:56 +01:00
Benjamin S. Leveritt
abad8e762f Changes config to throw on typecheck error 2025-06-11 11:21:49 +01:00
Benjamin S. Leveritt
037e16392e Removes list of frameworks 2025-06-11 10:55:19 +01:00
Benjamin S. Leveritt
49ac65c123 Reorders list 2025-06-11 10:55:19 +01:00
Benjamin S. Leveritt
3510fb1273 Update homepage/homepage/content/docs/index.mdx
Co-authored-by: Anselm Eickhoff <anselm.eickhoff@gmail.com>
2025-06-11 10:55:19 +01:00
Benjamin S. Leveritt
bc3efe7ca0 Tweaks 2025-06-11 10:55:19 +01:00
Benjamin S. Leveritt
3b06a7809e Adds why and how sections 2025-06-11 10:55:18 +01:00
Benjamin S. Leveritt
58aa04bb10 Adds introduction paragraph 2025-06-11 10:55:18 +01:00
19 changed files with 3150 additions and 1240 deletions

View File

@@ -22,6 +22,9 @@ jobs:
- name: Setup Source Code
uses: ./.github/actions/source-code/
- name: Install root dependencies
run: pnpm install && pnpm turbo build
- name: Install project dependencies
run: pnpm install
working-directory: ./${{ matrix.project }}

View File

@@ -54,6 +54,12 @@ export default function CoJsonViewerApp() {
const [coValueId, setCoValueId] = useState<CoID<RawCoValue> | "">("");
const { path, addPages, goToIndex, goBack, setPage } = usePagePath();
const KNOWN_ERRORS = [
"trying to load covalue with invalid id",
"account has no profile",
"invalid sealer secret format",
];
useEffect(() => {
localStorage.setItem("inspectorAccounts", JSON.stringify(accounts));
}, [accounts]);
@@ -94,7 +100,14 @@ export default function CoJsonViewerApp() {
},
});
} catch (err: any) {
if (err.toString().includes("invalid id")) {
const normalizeError = (str: string): string =>
str.toLowerCase().replace(/\s+/g, " ").trim();
const matchError: string =
KNOWN_ERRORS.filter((e) =>
normalizeError(err.message).includes(e),
)[0] || "";
const knownErrorIndex: number = KNOWN_ERRORS.indexOf(matchError);
if (knownErrorIndex >= 0) {
setAccounts(accounts.filter((acc) => acc.id !== currentAccount.id));
//remove from localStorage
localStorage.removeItem("lastSelectedAccountId");
@@ -105,9 +118,9 @@ export default function CoJsonViewerApp() {
),
);
setCurrentAccount(null);
setErrors("Trying to load covalue with invalid id");
setErrors(KNOWN_ERRORS[knownErrorIndex]);
} else {
setErrors("The account could not be loaded");
setErrors("Something went wrong, the account could not be loaded");
}
setLocalNode(null);
goToIndex(-1);
@@ -357,12 +370,14 @@ function AddAccountForm({
secret separately.
</p>
<Input
required
label="Account ID"
value={id}
placeholder="co_z1234567890abcdef123456789 or paste full JSON"
onChange={handleIdChange}
/>
<Input
required
label="Account secret"
type="password"
value={secret}

View File

@@ -7,16 +7,9 @@ import { Organization } from "../schema.ts";
export function InviteLink({
organization,
}: { organization: Loaded<typeof Organization> }) {
const [inviteLink, setInviteLink] = useState<string>();
let [copyCount, setCopyCount] = useState(0);
let copied = copyCount > 0;
useEffect(() => {
if (organization) {
setInviteLink(createInviteLink(organization, "writer"));
}
}, [organization.id]);
useEffect(() => {
if (copyCount > 0) {
let timeout = setTimeout(() => setCopyCount(0), 1000);
@@ -27,11 +20,10 @@ export function InviteLink({
}, [copyCount]);
const copyUrl = () => {
if (inviteLink) {
navigator.clipboard.writeText(inviteLink).then(() => {
setCopyCount((count) => count + 1);
});
}
const inviteLink = createInviteLink(organization, "writer");
navigator.clipboard.writeText(inviteLink).then(() => {
setCopyCount((count) => count + 1);
});
};
return (

View File

@@ -19,12 +19,12 @@
"jazz-react": "link:../../packages/jazz-react",
"jazz-tools": "link:../../packages/jazz-tools",
"lucide-react": "^0.436.0",
"next": "14.2.7",
"next": "15.2.1",
"next-themes": "^0.2.1",
"postcss": "^8",
"radix-ui": "^1.4.2",
"react": "^18",
"react-dom": "^18",
"react": "catalog:",
"react-dom": "catalog:",
"resend": "^4.0.0",
"tailwind-merge": "^1.14.0",
"tailwindcss": "^3.4.17",
@@ -34,8 +34,8 @@
"@biomejs/biome": "1.9.4",
"@csstools/postcss-oklab-function": "^3.0.6",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"@types/react": "catalog:",
"@types/react-dom": "catalog:",
"typescript": "^5.3.3"
}
}

View File

@@ -20,7 +20,8 @@
"paths": {
"@/*": ["./src/*"],
"@components/*": ["./src/components/*"]
}
},
"target": "ES2017"
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]

View File

@@ -12,5 +12,6 @@
"persistent": true,
"dependsOn": ["^build"]
}
}
},
"extends": ["//"]
}

View File

@@ -10,8 +10,15 @@ import { Metadata } from "next";
import Image from "next/image";
import { notFound } from "next/navigation";
type Params = {
params: Promise<{
slug: string;
}>;
};
export default async function Post({ params }: Params) {
const post = getPostBySlug(params.slug);
const { slug } = await params;
const post = getPostBySlug(slug);
if (!post) {
return notFound();
@@ -65,14 +72,9 @@ export default async function Post({ params }: Params) {
);
}
type Params = {
params: {
slug: string;
};
};
export function generateMetadata({ params }: Params): Metadata {
const post = getPostBySlug(params.slug);
export async function generateMetadata({ params }: Params): Promise<Metadata> {
const { slug } = await params;
const post = getPostBySlug(slug);
if (!post) {
return notFound();

View File

@@ -18,15 +18,17 @@ const membersNameToInfoMap = {
brad: "Bradley Kowalski",
};
export default function TeamMemberPage({
export default async function TeamMemberPage({
params,
}: { params: { member: string } }) {
if (!(params.member in membersNameToInfoMap)) {
}: { params: Promise<{ member: string }> }) {
const { member } = await params;
if (!(member in membersNameToInfoMap)) {
Router.push("/team");
}
const memberName =
membersNameToInfoMap[params.member as keyof typeof membersNameToInfoMap];
membersNameToInfoMap[member as keyof typeof membersNameToInfoMap];
const memberInfo = team.find(
(m: { name: string }) => m.name.toLowerCase() === memberName.toLowerCase(),
);

View File

@@ -26,10 +26,10 @@
"mdast-util-from-markdown": "^2.0.0",
"mdast-util-mdx": "^3.0.0",
"micromark-extension-mdxjs": "^3.0.0",
"next": "14.2.15",
"next": "15.2.1",
"next-themes": "^0.2.1",
"react": "^18",
"react-dom": "^18",
"react": "catalog:",
"react-dom": "catalog:",
"shiki": "^0.14.6",
"shiki-twoslash": "^3.1.2",
"tailwind-merge": "^1.14.0",
@@ -39,8 +39,8 @@
"devDependencies": {
"@biomejs/biome": "1.9.4",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"@types/react": "catalog:",
"@types/react-dom": "catalog:",
"autoprefixer": "^10",
"postcss": "^8",
"tailwindcss": "^3",

View File

@@ -6,7 +6,11 @@ export const metadata = {
# Learn some <span className="sr-only">Jazz</span> <JazzLogo className="h-[41px] -ml-0.5 -mt-[3px] inline" />
Welcome to the Jazz documentation!
**Jazz is a toolkit for building backendless apps**. You get data without needing a database — plus auth, permissions, files and multiplayer without needing a backend. Jazz lets you do everything right from the frontend and you'll ship better apps, faster.
Instead of wrestling with databases, APIs, and server infrastructure, you work with **CoValues** ("collaborative values") — your new cloud-synced building blocks that feel like local state but automatically sync across all devices and users in real-time.
---
**Note:** We just released [Jazz 0.14.0](/docs/upgrade/0-14-0) with a bunch of breaking changes and are still cleaning the docs up - see the [upgrade guide](/docs/upgrade/0-14-0) for details.
@@ -20,37 +24,44 @@ npx create-jazz-app@latest --api-key you@example.com
```
</CodeGroup>
Or set up Jazz yourself, using the following instructions for your framework of choice:
- [React](/docs/react/project-setup)
- [Next.js](/docs/react/project-setup#nextjs)
- [React Native](/docs/react-native/project-setup)
- [React Native Expo](/docs/react-native-expo/project-setup)
- [Vue](/docs/vue/project-setup)
- [Svelte](/docs/svelte/project-setup)
{/* <ContentByFramework framework="react">
Or you can follow this [React step-by-step guide](/docs/react/guide) where we walk you through building an issue tracker app.
</ContentByFramework> */}
## Example apps
## Why Jazz is different
You can also find [example apps](/examples) with code most similar to what you want to build. These apps
make use of different features such as auth, file upload, and more.
Most apps rebuild the same thing: shared state that syncs between users and devices. Jazz starts from that shared state, giving you:
- **No backend required** — Focus on building features, not infrastructure
- **Real-time sync** — Changes appear everywhere immediately
- **Multiplayer by default** — Collaboration just works
- **Local-first** — Your app works offline and feels instant
Think Figma, Notion, or Linear — but you don't need years to build a custom stack.
## How it works
1. **Define your data** with CoValues schemas
2. **Connect to sync infrastructure** (Jazz Cloud or self-hosted)
3. **Create and edit CoValues** like normal objects
4. **Get automatic sync and persistence** across all devices and users
Your UI updates instantly on every change, everywhere. It's like having reactive local state that happens to be shared with the world.
## Ready to see Jazz in action?
Have a look at our [example apps](/examples) for inspiration and to see what's possible with Jazz. From real-time chat and collaborative editors to file sharing and social features — these are just the beginning of what you can build.
## Core concepts
Learn how to structure your data using [collaborative values](/docs/schemas/covalues) — the building blocks that make Jazz apps work.
## Sync and storage
Sync and persist your data by setting up a [sync and storage infrastructure](/docs/sync-and-storage) using Jazz Cloud, or do it yourself.
Sync and persist your data by setting up [sync and storage infrastructure](/docs/sync-and-storage) using Jazz Cloud, or host it yourself.
## Collaborative values
Learn how to structure your data using [collaborative values](/docs/schemas/covalues).
## LLM Docs
## Going deeper
Get better results with AI by [importing the Jazz docs](/docs/ai-tools) into your context window.
## Get support
If you have any questions or need assistance, please don't hesitate to reach out to us on [Discord](https://discord.gg/utDMjHYg42).
We would love to help you get started.
If you have any questions or need assistance, please don't hesitate to reach out to us on [Discord](https://discord.gg/utDMjHYg42). We'd love to help you get started.

View File

@@ -253,14 +253,13 @@ Here's a quick overview of the primitive types you can use:
<CodeGroup>
```ts twoslash
import {z} from "jazz-tools";
// ---cut---
z.string(); // For simple strings
z.number(); // For numbers
import { z } from "jazz-tools";
// ---cut---
z.string(); // For simple strings
z.number(); // For numbers
z.boolean(); // For booleans
z.null(); // For null
z.date(); // For dates
z.literal(["waiting", "ready"]); // For enums
z.date(); // For dates
z.literal(["waiting", "ready"]); // For enums
```
</CodeGroup>

View File

@@ -24,7 +24,7 @@ We're introducing a new resolve API for deep loading, more friendly to TypeScrip
<CodeGroup>
```tsx twoslash
// @noErrors: 2451
// @noErrors: 2451 2769
import { CoMap, CoList, coField, Account } from "jazz-tools";
import { useAccount } from "jazz-react";
class AccountRoot extends CoMap { friends = coField.ref(ListOfAccounts); }

View File

@@ -84,18 +84,22 @@ function highlightPlugin() {
transformers: [
transformerTwoslash({
explicitTrigger: true,
throws: false, //process.env.NODE_ENV === "production",
onTwoslashError:
process.env.NODE_ENV !== "production"
? (e, code) => {
const { description, recommendation } = e;
console.error("\nTwoslash error: ");
console.log(description);
console.log(recommendation);
console.log("\nCode: \n```\n" + code + "\n```");
error = e;
}
: undefined,
throws: process.env.NODE_ENV === "production",
onTwoslashError: (e, code) => {
if (process.env.NODE_ENV === "production") {
// Re-throw to actually fail the build in production
throw e;
}
const { description, recommendation } = e;
console.error("\nTwoslash error: ");
console.log(description);
console.log(recommendation);
console.log("\nCode: \n```\n" + code + "\n```");
// In development, store the error to show inline
error = e;
},
}),
transformerNotationDiff(),
],
@@ -103,7 +107,7 @@ function highlightPlugin() {
node.type = "html";
node.value = error
? `<div style="color: red;">${error}</div>` + html
? `<div style="color: red; background: #fee; padding: 8px; border: 1px solid #fcc; margin: 8px 0;"><strong>Twoslash Error:</strong> ${error.description || error.message} ${error.recommendation}</div>` + html
: html;
node.children = [];
return SKIP;

View File

@@ -31,6 +31,7 @@
"@stefanprobst/rehype-extract-toc": "^2.2.0",
"@turf/turf": "^7.1.0",
"@types/mdx": "^2.0.8",
"@types/react-native": "^0.73.0",
"@types/topojson-client": "^3.1.5",
"@vercel/analytics": "^1.3.1",
"@vercel/speed-insights": "^1.0.12",
@@ -57,8 +58,8 @@
"next": "15.2.1",
"next-themes": "^0.2.1",
"qrcode": "^1.5.4",
"react": "^18",
"react-dom": "^18",
"react": "catalog:",
"react-dom": "catalog:",
"react-singleton-hook": "^4.0.1",
"shiki": "^3.2.1",
"tailwind-merge": "^1.14.0",
@@ -70,8 +71,8 @@
"@playwright/test": "^1.52.0",
"@types/geojson": "^7946.0.14",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"@types/react": "catalog:",
"@types/react-dom": "catalog:",
"autoprefixer": "^10",
"pagefind": "^1.3.0",
"postcss": "^8",

View File

@@ -16,5 +16,6 @@
"persistent": true,
"dependsOn": ["build"]
}
}
},
"extends": ["//"]
}

4169
homepage/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,10 @@
packages:
- "homepage"
- "design-system"
- "gcmp"
- "gcmp"
catalog:
"react": "19.0.0"
"react-dom": "19.0.0"
"@types/react": "19.0.0"
"@types/react-dom": "19.0.0"

View File

@@ -55,7 +55,6 @@
"react": "19.0.0",
"react-dom": "19.0.0",
"esbuild": "0.24.0"
},
"patchedDependencies": {
"expo-router": "patches/expo-router.patch"

View File

@@ -54,6 +54,8 @@ const packageJsonFiles = findPackageJsonFiles(rootDir);
let hasIssues = false;
for (const file of packageJsonFiles) {
if (file.includes("homepage")) continue;
const issues = checkCatalogDependencies(file);
if (issues.length > 0) {
hasIssues = true;