Compare commits
47 Commits
jazz-auth-
...
cojson-sto
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a98d6aaf2 | ||
|
|
4ea1a63a0a | ||
|
|
41a4c3bc95 | ||
|
|
60d0027f9d | ||
|
|
748c2ff751 | ||
|
|
70938b0ab3 | ||
|
|
f2f5b55dbf | ||
|
|
3c3acae803 | ||
|
|
896ee3460f | ||
|
|
9b9bf44e2b | ||
|
|
392aa88d95 | ||
|
|
7ce82cd934 | ||
|
|
0c8158b91c | ||
|
|
25c56146f5 | ||
|
|
c564fbb02e | ||
|
|
12481e14c2 | ||
|
|
fd2d247ff5 | ||
|
|
9e9ea029b2 | ||
|
|
a0da272dcd | ||
|
|
72fbcc3262 | ||
|
|
f4c8cc858b | ||
|
|
0ab4d7a20d | ||
|
|
4cbda689c4 | ||
|
|
771b0ed914 | ||
|
|
79913c3136 | ||
|
|
43d3511d15 | ||
|
|
928ef14086 | ||
|
|
048dd7def0 | ||
|
|
873b146d15 | ||
|
|
ab1798c7bd | ||
|
|
26ae69a242 | ||
|
|
21ad3767b9 | ||
|
|
a9383516c1 | ||
|
|
bffc516c68 | ||
|
|
9e7c0d9887 | ||
|
|
99b44d5780 | ||
|
|
02db5f3b1d | ||
|
|
1949a5fcd9 | ||
|
|
dcd3b022cc | ||
|
|
a7b837c7e1 | ||
|
|
88ebcf58ab | ||
|
|
f379a168be | ||
|
|
bde6ac7d45 | ||
|
|
239da90c9f | ||
|
|
972791e7a8 | ||
|
|
6b662b0efe | ||
|
|
a8b3ec7bb0 |
@@ -1,5 +1,18 @@
|
||||
# passkey-svelte
|
||||
|
||||
## 0.0.109
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [43d3511]
|
||||
- jazz-tools@0.16.3
|
||||
|
||||
## 0.0.108
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-tools@0.16.2
|
||||
|
||||
## 0.0.107
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "chat-svelte",
|
||||
"version": "0.0.107",
|
||||
"version": "0.0.109",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
BubbleTeaOrder,
|
||||
DraftBubbleTeaOrder,
|
||||
JazzAccount,
|
||||
ListOfBubbleTeaAddOns,
|
||||
validateDraftOrder,
|
||||
} from "./schema.ts";
|
||||
|
||||
@@ -34,7 +33,7 @@ export function CreateOrder() {
|
||||
|
||||
// reset the draft
|
||||
me.root.draft = DraftBubbleTeaOrder.create({
|
||||
addOns: ListOfBubbleTeaAddOns.create([]),
|
||||
addOns: [],
|
||||
});
|
||||
|
||||
router.navigate("/");
|
||||
|
||||
@@ -73,15 +73,9 @@ export const JazzAccount = co
|
||||
})
|
||||
.withMigration((account) => {
|
||||
if (!account.root) {
|
||||
const orders = co.list(BubbleTeaOrder).create([], account);
|
||||
const draft = DraftBubbleTeaOrder.create(
|
||||
{
|
||||
addOns: ListOfBubbleTeaAddOns.create([], account),
|
||||
instructions: co.plainText().create("", account),
|
||||
},
|
||||
account.root = AccountRoot.create(
|
||||
{ draft: { addOns: [], instructions: "" }, orders: [] },
|
||||
account,
|
||||
);
|
||||
|
||||
account.root = AccountRoot.create({ draft, orders }, account);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -41,7 +41,9 @@ export function Footer({
|
||||
</Link>
|
||||
</div>
|
||||
<p className="col-span-full sm:col-span-6 md:col-span-4 text-sm sm:text-base">
|
||||
Playful software for serious problems.
|
||||
Computers are magic.
|
||||
<br />
|
||||
Time to make them less complex.
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid gap-y-8 grid-cols-12">
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { ChatDemoSection } from "@/components/home/ChatDemoSection";
|
||||
import { CollaborationFeaturesSection } from "@/components/home/CollaborationFeaturesSection";
|
||||
import { ComingSoonSection } from "@/components/home/ComingSoonSection";
|
||||
import { EarlyAdopterSection } from "@/components/home/EarlyAdopterSection";
|
||||
import { EncryptionSection } from "@/components/home/EncryptionSection";
|
||||
import { FeaturesSection } from "@/components/home/FeaturesSection";
|
||||
@@ -15,9 +14,9 @@ import { Testimonial } from "@garden-co/design-system/src/components/molecules/T
|
||||
export default function Home() {
|
||||
return (
|
||||
<>
|
||||
<HeroSection />
|
||||
|
||||
<HeroSection />
|
||||
<div className="container flex flex-col gap-12 lg:gap-20">
|
||||
|
||||
<GetStartedSnippetSelect />
|
||||
<SupportedEnvironmentsSection />
|
||||
<HowJazzWorksSection />
|
||||
@@ -54,8 +53,6 @@ export default function Home() {
|
||||
|
||||
<FeaturesSection />
|
||||
|
||||
<ComingSoonSection />
|
||||
|
||||
<EarlyAdopterSection />
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
import CoPlainTextDescription from "@/app/(others)/(home)/coValueDescriptions/coPlainTextDescription.mdx";
|
||||
import CursorsAndCaretsDescription from "@/app/(others)/(home)/toolkit/cursorsAndCarets.mdx";
|
||||
import TwoWaySyncDescription from "@/app/(others)/(home)/toolkit/twoWaySync.mdx";
|
||||
import VideoPresenceCallsDescription from "@/app/(others)/(home)/toolkit/videoPresenceCalls.mdx";
|
||||
import { CodeRef } from "@garden-co/design-system/src/components/atoms/CodeRef";
|
||||
import { P } from "@garden-co/design-system/src/components/atoms/Paragraph";
|
||||
import { FeatureCard } from "@garden-co/design-system/src/components/molecules/FeatureCard";
|
||||
import { GappedGrid } from "@garden-co/design-system/src/components/molecules/GappedGrid";
|
||||
import { Prose } from "@garden-co/design-system/src/components/molecules/Prose";
|
||||
import { SectionHeader } from "@garden-co/design-system/src/components/molecules/SectionHeader";
|
||||
|
||||
export function ComingSoonSection() {
|
||||
return (
|
||||
<div>
|
||||
<SectionHeader title="More features coming soon" />
|
||||
|
||||
<GappedGrid cols={4}>
|
||||
<FeatureCard className="p-4" label={<h3>Cursors & carets</h3>}>
|
||||
<P>Ready-made spatial presence.</P>
|
||||
<Prose size="sm">
|
||||
<CursorsAndCaretsDescription />
|
||||
</Prose>
|
||||
</FeatureCard>
|
||||
|
||||
<FeatureCard className="p-4" label={<h3>Two-way sync to your DB</h3>}>
|
||||
<P>Add Jazz to an existing app.</P>
|
||||
<Prose size="sm">
|
||||
<TwoWaySyncDescription />
|
||||
</Prose>
|
||||
</FeatureCard>
|
||||
|
||||
<FeatureCard className="p-4" label={<h3>Video presence & calls</h3>}>
|
||||
<P>Stream and record audio & video.</P>
|
||||
<Prose size="sm">
|
||||
<VideoPresenceCallsDescription />
|
||||
</Prose>
|
||||
</FeatureCard>
|
||||
|
||||
<FeatureCard
|
||||
className="p-4"
|
||||
label={
|
||||
<h3>
|
||||
<CodeRef>CoPlainText</CodeRef> & <CodeRef>CoRichText</CodeRef>
|
||||
</h3>
|
||||
}
|
||||
>
|
||||
<Prose size="sm">
|
||||
<CoPlainTextDescription />
|
||||
</Prose>
|
||||
</FeatureCard>
|
||||
</GappedGrid>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -16,81 +16,70 @@ const features: Array<{
|
||||
title: string;
|
||||
icon: IconName;
|
||||
}> = [
|
||||
{
|
||||
title: "Instant updates",
|
||||
icon: "instant",
|
||||
},
|
||||
{
|
||||
title: "Real-time sync",
|
||||
icon: "devices",
|
||||
},
|
||||
{
|
||||
title: "Multiplayer",
|
||||
icon: "spatialPresence",
|
||||
},
|
||||
{
|
||||
title: "File uploads",
|
||||
icon: "upload",
|
||||
},
|
||||
{
|
||||
title: "Social features",
|
||||
icon: "social",
|
||||
},
|
||||
{
|
||||
title: "Permissions",
|
||||
icon: "permissions",
|
||||
},
|
||||
{
|
||||
title: "E2E encryption",
|
||||
icon: "encryption",
|
||||
},
|
||||
{
|
||||
title: "Authentication",
|
||||
icon: "auth",
|
||||
},
|
||||
];
|
||||
{
|
||||
title: "Instant updates",
|
||||
icon: "instant",
|
||||
},
|
||||
{
|
||||
title: "Real-time sync",
|
||||
icon: "devices",
|
||||
},
|
||||
{
|
||||
title: "Multiplayer",
|
||||
icon: "spatialPresence",
|
||||
},
|
||||
{
|
||||
title: "File uploads",
|
||||
icon: "upload",
|
||||
},
|
||||
{
|
||||
title: "Social features",
|
||||
icon: "social",
|
||||
},
|
||||
{
|
||||
title: "Permissions",
|
||||
icon: "permissions",
|
||||
},
|
||||
{
|
||||
title: "E2E encryption",
|
||||
icon: "encryption",
|
||||
},
|
||||
{
|
||||
title: "Authentication",
|
||||
icon: "auth",
|
||||
},
|
||||
];
|
||||
|
||||
export function HeroSection() {
|
||||
return (
|
||||
<div className="container grid items-center gap-x-8 gap-y-12 my-12 md:my-16 lg:my-24 lg:gap-x-10 lg:grid-cols-12">
|
||||
<div className="container grid items-center gap-x-8 gap-y-12 mt-12 md:mt-16 lg:mt-24 mb-12 lg:gap-x-10 lg:grid-cols-12">
|
||||
<div className="flex flex-col justify-center gap-5 lg:col-span-11 lg:gap-8">
|
||||
<Kicker>Toolkit for backendless apps</Kicker>
|
||||
<Kicker>Reactive, distributed, secure</Kicker>
|
||||
<H1>
|
||||
<span className="inline-block text-highlight">
|
||||
{marketingCopy.headline}
|
||||
</span>
|
||||
</H1>
|
||||
|
||||
<Prose size="lg" className="text-pretty max-w-2xl dark:text-stone-200">
|
||||
<Prose size="lg" className="text-pretty max-w-2xl dark:text-stone-200 prose-p:leading-normal">
|
||||
<p>
|
||||
Jazz gives you data without needing a database — plus auth,
|
||||
permissions, files and multiplayer without needing a backend.
|
||||
Jazz is a new kind of database that's distributed across your frontend, containers, serverless functions and its own storage cloud.
|
||||
</p>
|
||||
<p>It syncs structured data, files and LLM streams instantly.<br/>It looks like local reactive JSON state.</p>
|
||||
<p>And you get auth, orgs & teams, real-time multiplayer, edit histories, permissions, E2E encryption and offline-support out of the box.</p>
|
||||
<p>
|
||||
Do everything right from the frontend and ship better apps, faster.
|
||||
This lets you get rid of 90% of the traditional backend, and most of your frontend state juggling.
|
||||
You'll ship better apps, faster.
|
||||
</p>
|
||||
<p>
|
||||
Open source. Self-host or use{" "}
|
||||
<p className="text-base">
|
||||
Self-host or use{" "}
|
||||
<Link className="text-reset" href="/cloud">
|
||||
Jazz Cloud
|
||||
</Link>{" "}
|
||||
for zero-config magic.
|
||||
for a zero-deploy globally-scaled DB.
|
||||
<br/>Open source (MIT)
|
||||
</p>
|
||||
</Prose>
|
||||
|
||||
<div className="grid grid-cols-2 gap-2 max-w-3xl sm:grid-cols-4 sm:gap-4">
|
||||
{features.map(({ title, icon }) => (
|
||||
<div
|
||||
key={title}
|
||||
className="flex text-xs sm:text-sm gap-2 items-center"
|
||||
>
|
||||
<span className="p-1.5 rounded-lg bg-primary-transparent">
|
||||
<Icon size="xs" name={icon} intent="primary" />
|
||||
</span>
|
||||
<p>{title}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -83,7 +83,7 @@ export function HowJazzWorksSection() {
|
||||
<div className="grid gap-3">
|
||||
<Kicker>How it works</Kicker>
|
||||
|
||||
<H2>Build entire apps using only client-side code</H2>
|
||||
<H2>Build entire apps with collaborative state</H2>
|
||||
</div>
|
||||
<GappedGrid>
|
||||
<Step
|
||||
|
||||
@@ -53,7 +53,7 @@ export function LocalFirstFeaturesSection() {
|
||||
return (
|
||||
<div>
|
||||
<SectionHeader
|
||||
title="The best of all worlds"
|
||||
title="Local-first state with global sync"
|
||||
slogan={
|
||||
<>
|
||||
<p>
|
||||
|
||||
@@ -9,7 +9,7 @@ export default function ProblemStatementSection() {
|
||||
<div className="grid gap-4 lg:gap-8">
|
||||
<SectionHeader
|
||||
className="sm:text-center sm:mx-auto"
|
||||
title={"Powered by the first “flat stack”"}
|
||||
title={"A database that does what's actually needed"}
|
||||
slogan="A perspective shift worth 10,000 hours"
|
||||
/>
|
||||
|
||||
@@ -41,8 +41,7 @@ export default function ProblemStatementSection() {
|
||||
<Prose>
|
||||
<p>
|
||||
For each new app you tackle a{" "}
|
||||
<strong>mess of moving parts and infra worries.</strong> Or, you
|
||||
haven't even tried because "you're not full-stack".
|
||||
<strong>mess of moving parts and infra worries.</strong> Your backend is responsible for shuffling data around in a myriad of ways.
|
||||
</p>
|
||||
<p>
|
||||
Want to build a <strong>modern app</strong> with multiplayer or
|
||||
@@ -68,7 +67,7 @@ export default function ProblemStatementSection() {
|
||||
<strong>With users & permissions built-in.</strong>
|
||||
</p>
|
||||
<p>
|
||||
With completely <strong>app-independent infra,</strong> you get to
|
||||
With a <strong>DB and infra made for modern apps</strong> you get to
|
||||
focus on <strong>building the app your users want.</strong> You'll
|
||||
notice that <strong>90% of the work is now the UI.</strong>
|
||||
</p>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { BunLogo } from "@/components/icons/BunLogo";
|
||||
import { CloudflareWorkerLogo } from "@/components/icons/CloudflareWorkerLogo";
|
||||
import { VercelLogo } from "@/components/icons/VercelLogo";
|
||||
import { ExpoLogo } from "@/components/icons/ExpoLogo";
|
||||
import { JavascriptLogo } from "@/components/icons/JavascriptLogo";
|
||||
import { NodejsLogo } from "@/components/icons/NodejsLogo";
|
||||
@@ -44,14 +45,18 @@ const serverWorkers = [
|
||||
icon: NodejsLogo,
|
||||
href: "/docs/react/server-workers",
|
||||
},
|
||||
{
|
||||
name: "Cloudflare Workers",
|
||||
icon: CloudflareWorkerLogo,
|
||||
},
|
||||
{
|
||||
name: "Bun",
|
||||
icon: BunLogo,
|
||||
},
|
||||
{
|
||||
name: "Vercel",
|
||||
icon: VercelLogo,
|
||||
},
|
||||
{
|
||||
name: "CF Workers",
|
||||
icon: CloudflareWorkerLogo,
|
||||
}
|
||||
];
|
||||
|
||||
export function SupportedEnvironmentsSection() {
|
||||
|
||||
16
homepage/homepage/components/icons/VercelLogo.tsx
Normal file
16
homepage/homepage/components/icons/VercelLogo.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import React from "react";
|
||||
import type { SVGProps } from "react";
|
||||
|
||||
export function VercelLogo(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
width="1.5em"
|
||||
height="1.5em"
|
||||
viewBox="0 0 76 65"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="M37.5274 0L75.0548 65H0L37.5274 0Z" fill="currentColor" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -54,7 +54,7 @@ function AuthStateIndicator() {
|
||||
const isGuest = agent._type !== "Account"
|
||||
|
||||
// Anonymous authentication: has an account but not fully authenticated
|
||||
const isAnonymous = agent._type === "Account" && !isAuthenticated;
|
||||
const isAnonymous = agent._type === "Account" && !isAuthenticated;
|
||||
return (
|
||||
<div>
|
||||
{isGuest && <span>Guest Mode</span>}
|
||||
|
||||
@@ -31,7 +31,7 @@ export const Organization = co.map({
|
||||
name: z.string(),
|
||||
|
||||
// shared data between users of each organization
|
||||
projects: co.list(Project),
|
||||
projects: co.list(Project),
|
||||
});
|
||||
|
||||
export const ListOfOrganizations = co.list(Organization);
|
||||
@@ -115,7 +115,7 @@ import * as React from "react";
|
||||
import { useAcceptInvite, useAccount } from "jazz-tools/react";
|
||||
import { co, z } from "jazz-tools";
|
||||
|
||||
const Project = z.object({
|
||||
const Project = co.map({
|
||||
name: z.string(),
|
||||
});
|
||||
|
||||
|
||||
@@ -205,6 +205,101 @@ console.log(containingGroup.getParentGroups()); // [addedGroup]
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
## Group hierarchy on CoValue creation
|
||||
|
||||
When creating CoValues that contain other CoValues using plain JSON objects, Jazz not only creates
|
||||
the necessary CoValues automatically but it will also manage their group ownership.
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
import { co, z } from "jazz-tools";
|
||||
|
||||
// ---cut---
|
||||
const Task = co.plainText();
|
||||
const Column = co.list(Task);
|
||||
const Board = co.map({
|
||||
title: z.string(),
|
||||
columns: co.list(Column),
|
||||
});
|
||||
|
||||
const board = Board.create({
|
||||
title: "My board",
|
||||
columns: [
|
||||
["Task 1.1", "Task 1.2"],
|
||||
["Task 2.1", "Task 2.2"],
|
||||
],
|
||||
});
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
For each created column and task CoValue, Jazz also creates a new group as its owner and
|
||||
adds the referencing CoValue's owner as a member of that group. This means permissions for nested CoValues
|
||||
are inherited from the CoValue that references them, but can also be modified independently for each CoValue
|
||||
if needed.
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
import { co, z, Group, Account } from "jazz-tools";
|
||||
|
||||
const alice = {} as unknown as Account;
|
||||
const bob = {} as unknown as Account;
|
||||
const Task = co.plainText();
|
||||
const Column = co.list(Task);
|
||||
const Board = co.map({
|
||||
title: z.string(),
|
||||
columns: co.list(Column),
|
||||
});
|
||||
// ---cut---
|
||||
const writeAccess = Group.create();
|
||||
writeAccess.addMember(bob, "writer");
|
||||
|
||||
// Give Bob write access to the board, columns and tasks
|
||||
const board = Board.create({
|
||||
title: "My board",
|
||||
columns: [
|
||||
["Task 1.1", "Task 1.2"],
|
||||
["Task 2.1", "Task 2.2"],
|
||||
],
|
||||
}, writeAccess);
|
||||
|
||||
// Give Alice read access to one specific task
|
||||
const task = board.columns[0][0];
|
||||
const taskGroup = task._owner.castAs(Group);
|
||||
taskGroup.addMember(alice, "reader");
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
If you prefer to manage permissions differently, you can always create CoValues explicitly:
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
import { co, Group, z, Account } from "jazz-tools";
|
||||
|
||||
const bob = {} as unknown as Account;
|
||||
const Task = co.plainText();
|
||||
const Column = co.list(Task);
|
||||
const Board = co.map({
|
||||
title: z.string(),
|
||||
columns: co.list(Column),
|
||||
});
|
||||
|
||||
// ---cut---
|
||||
const writeAccess = Group.create();
|
||||
writeAccess.addMember(bob, "writer");
|
||||
const readAccess = Group.create();
|
||||
readAccess.addMember(bob, "reader");
|
||||
|
||||
// Give Bob read access to the board and write access to the columns and tasks
|
||||
const board = Board.create({
|
||||
title: "My board",
|
||||
columns: co.list(Column).create([
|
||||
["Task 1.1", "Task 1.2"],
|
||||
["Task 2.1", "Task 2.2"],
|
||||
], writeAccess),
|
||||
}, readAccess);
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
## Example: Team Hierarchy
|
||||
|
||||
Here's a practical example of using group inheritance for team permissions:
|
||||
|
||||
@@ -7,9 +7,11 @@ export const metadata = {
|
||||
|
||||
# Learn some <span className="sr-only">Jazz</span> <JazzLogo className="h-[41px] -ml-0.5 -mt-[3px] inline" />
|
||||
|
||||
**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.
|
||||
**Jazz is a new kind of database** that's **distributed** across your frontend, containers, serverless functions and its own storage cloud.
|
||||
|
||||
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.
|
||||
It syncs structured data, files and LLM streams instantly, and looks like local reactive JSON state.
|
||||
|
||||
It also provides auth, orgs & teams, real-time multiplayer, edit histories, permissions, E2E encryption and offline-support out of the box.
|
||||
|
||||
---
|
||||
|
||||
@@ -19,7 +21,7 @@ You can use [`create-jazz-app`](/docs/tools/create-jazz-app) to create a new Jaz
|
||||
|
||||
<CodeGroup>
|
||||
```sh
|
||||
npx create-jazz-app@latest --api-key you@example.com
|
||||
npx create-jazz-app@latest --api-key you@example.com
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
@@ -30,21 +32,10 @@ Or you can follow this [React step-by-step guide](/docs/react/guide) where we wa
|
||||
|
||||
</ContentByFramework> */}
|
||||
|
||||
## Why Jazz is different
|
||||
|
||||
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)
|
||||
2. **Connect to storage 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
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ export const metadata = {
|
||||
};
|
||||
|
||||
import { CodeGroup, ComingSoon } from "@/components/forMdx";
|
||||
import { Alert } from "@garden-co/design-system/src/components/atoms/Alert";
|
||||
|
||||
# Defining schemas: CoValues
|
||||
|
||||
@@ -80,6 +81,40 @@ const project = TodoProject.create(
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
When creating CoValues that contain other CoValues, you can pass in a plain JSON object.
|
||||
Jazz will automatically create the CoValues for you.
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
// @filename: schema.ts
|
||||
import { co, z, CoMap, CoList } from "jazz-tools";
|
||||
|
||||
export const ListOfTasks = co.list(z.string());
|
||||
|
||||
export const TodoProject = co.map({
|
||||
title: z.string(),
|
||||
tasks: ListOfTasks,
|
||||
});
|
||||
|
||||
// @filename: app.ts
|
||||
// ---cut---
|
||||
// app.ts
|
||||
import { Group } from "jazz-tools";
|
||||
import { TodoProject, ListOfTasks } from "./schema";
|
||||
|
||||
const group = Group.create().makePublic();
|
||||
const project = TodoProject.create({
|
||||
title: "New Project",
|
||||
tasks: [], // Permissions are inherited, so the tasks list will also be public
|
||||
}, group);
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
<Alert variant="info" className="flex gap-2 items-center my-4">
|
||||
To learn more about how permissions work when creating nested CoValues with plain JSON objects,
|
||||
refer to [Group hierarchy on CoValue creation](/docs/groups/inheritance#group-hierarchy-on-covalue-creation).
|
||||
</Alert>
|
||||
|
||||
## Types of CoValues
|
||||
|
||||
### `CoMap` (declaration)
|
||||
@@ -320,6 +355,10 @@ const Company = co.map({
|
||||
</CodeGroup>
|
||||
|
||||
#### Optional References
|
||||
You can make schema fields optional using either `z.optional()` or `co.optional()`, depending on the type of value:
|
||||
|
||||
- Use `z.optional()` for primitive Zod values like `z.string()`, `z.number()`, or `z.boolean()`
|
||||
- Use `co.optional()` for CoValues like `co.map()`, `co.list()`, or `co.record()`
|
||||
|
||||
You can make references optional with `co.optional()`:
|
||||
|
||||
@@ -331,7 +370,8 @@ const Pet = co.map({
|
||||
});
|
||||
// ---cut---
|
||||
const Person = co.map({
|
||||
pet: co.optional(Pet),
|
||||
age: z.optional(z.number()), // primitive
|
||||
pet: co.optional(Pet), // CoValue
|
||||
});
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Alert } from "@garden-co/design-system/src/components/atoms/Alert";
|
||||
|
||||
# Inbox API with Server Workers
|
||||
|
||||
The Inbox API provides a message-based communication system for Server Workers in Jazz.
|
||||
The Inbox API provides a message-based communication system for Server Workers in Jazz.
|
||||
|
||||
It works on top of the Jazz APIs and uses sync to transfer messages between the client and the server.
|
||||
|
||||
@@ -154,8 +154,8 @@ function EventComponent({ event }: { event: Event }) {
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
The `sendInboxMessage` API returns a Promise that waits for the message to be handled by a Worker.
|
||||
A message is considered to be handled when the Promise returned by `inbox.subscribe` resolves.
|
||||
The `sendInboxMessage` API returns a Promise that waits for the message to be handled by a Worker.
|
||||
A message is considered to be handled when the Promise returned by `inbox.subscribe` resolves.
|
||||
The value returned will be the id of the CoValue returned in the `inbox.subscribe` resolved promise.
|
||||
|
||||
|
||||
@@ -163,4 +163,4 @@ The value returned will be the id of the CoValue returned in the `inbox.subscrib
|
||||
|
||||
Multi-region deployments are not supported when using the Inbox API.
|
||||
|
||||
If you need to split the workload across multiple regions, you can use the [HTTP API](./http-requests.mdx) instead.
|
||||
If you need to split the workload across multiple regions, you can use the [HTTP API](./http-requests) instead.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export const marketingCopy = {
|
||||
headline: "Whip up an app",
|
||||
headline: "Smooth database.",
|
||||
description:
|
||||
"Jazz gives you data without needing a database — plus auth, permissions, files and multiplayer without needing a backend. Do everything right from the frontend and ship better apps, faster.",
|
||||
"Jazz is a database that's distributed across your frontend, containers and functions. It syncs structured data, files and LLM streams instantly and looks like local reactive JSON state.",
|
||||
};
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# cojson-storage-indexeddb
|
||||
|
||||
## 0.16.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cojson@0.16.3
|
||||
|
||||
## 0.16.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cojson@0.16.2
|
||||
|
||||
## 0.16.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cojson-storage-indexeddb",
|
||||
"version": "0.16.1",
|
||||
"version": "0.16.3",
|
||||
"main": "dist/index.js",
|
||||
"type": "module",
|
||||
"types": "dist/index.d.ts",
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# cojson-storage-sqlite
|
||||
|
||||
## 0.16.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cojson@0.16.3
|
||||
|
||||
## 0.16.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cojson@0.16.2
|
||||
|
||||
## 0.16.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "cojson-storage-sqlite",
|
||||
"type": "module",
|
||||
"version": "0.16.1",
|
||||
"version": "0.16.3",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# cojson-transport-nodejs-ws
|
||||
|
||||
## 0.16.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cojson@0.16.3
|
||||
|
||||
## 0.16.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cojson@0.16.2
|
||||
|
||||
## 0.16.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "cojson-transport-ws",
|
||||
"type": "module",
|
||||
"version": "0.16.1",
|
||||
"version": "0.16.3",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# cojson
|
||||
|
||||
## 0.16.3
|
||||
|
||||
## 0.16.2
|
||||
|
||||
## 0.16.1
|
||||
|
||||
## 0.16.0
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
},
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"version": "0.16.1",
|
||||
"version": "0.16.3",
|
||||
"devDependencies": {
|
||||
"@opentelemetry/sdk-metrics": "^2.0.0",
|
||||
"libsql": "^0.5.13",
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
# jazz-auth-betterauth
|
||||
|
||||
## 0.16.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [43d3511]
|
||||
- jazz-tools@0.16.3
|
||||
- jazz-betterauth-client-plugin@0.16.3
|
||||
- cojson@0.16.3
|
||||
|
||||
## 0.16.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cojson@0.16.2
|
||||
- jazz-betterauth-client-plugin@0.16.2
|
||||
- jazz-tools@0.16.2
|
||||
|
||||
## 0.16.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-auth-betterauth",
|
||||
"version": "0.16.1",
|
||||
"version": "0.16.3",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.ts",
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# jazz-betterauth-client-plugin
|
||||
|
||||
## 0.16.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-betterauth-server-plugin@0.16.3
|
||||
|
||||
## 0.16.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-betterauth-server-plugin@0.16.2
|
||||
|
||||
## 0.16.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-betterauth-client-plugin",
|
||||
"version": "0.16.1",
|
||||
"version": "0.16.3",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.ts",
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
# jazz-betterauth-server-plugin
|
||||
|
||||
## 0.16.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [43d3511]
|
||||
- jazz-tools@0.16.3
|
||||
- cojson@0.16.3
|
||||
|
||||
## 0.16.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cojson@0.16.2
|
||||
- jazz-tools@0.16.2
|
||||
|
||||
## 0.16.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-betterauth-server-plugin",
|
||||
"version": "0.16.1",
|
||||
"version": "0.16.3",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.ts",
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
# jazz-react-auth-betterauth
|
||||
|
||||
## 0.16.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [43d3511]
|
||||
- jazz-tools@0.16.3
|
||||
- jazz-auth-betterauth@0.16.3
|
||||
- jazz-betterauth-client-plugin@0.16.3
|
||||
- cojson@0.16.3
|
||||
|
||||
## 0.16.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cojson@0.16.2
|
||||
- jazz-auth-betterauth@0.16.2
|
||||
- jazz-betterauth-client-plugin@0.16.2
|
||||
- jazz-tools@0.16.2
|
||||
|
||||
## 0.16.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-react-auth-betterauth",
|
||||
"version": "0.16.1",
|
||||
"version": "0.16.3",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.tsx",
|
||||
|
||||
@@ -1,5 +1,25 @@
|
||||
# jazz-run
|
||||
|
||||
## 0.16.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [43d3511]
|
||||
- jazz-tools@0.16.3
|
||||
- cojson@0.16.3
|
||||
- cojson-storage-sqlite@0.16.3
|
||||
- cojson-transport-ws@0.16.3
|
||||
|
||||
## 0.16.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 239da90: Fix jazz-run package.json exports
|
||||
- cojson@0.16.2
|
||||
- cojson-storage-sqlite@0.16.2
|
||||
- cojson-transport-ws@0.16.2
|
||||
- jazz-tools@0.16.2
|
||||
|
||||
## 0.16.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -3,15 +3,15 @@
|
||||
"bin": "./dist/index.js",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"version": "0.16.1",
|
||||
"version": "0.16.3",
|
||||
"exports": {
|
||||
"./startSyncServer": {
|
||||
"import": "./dist/startSyncServer.js",
|
||||
"types": "./dist/startSyncServer.d.ts"
|
||||
"types": "./dist/startSyncServer.d.ts",
|
||||
"default": "./dist/startSyncServer.js"
|
||||
},
|
||||
"./createWorkerAccount": {
|
||||
"import": "./dist/createWorkerAccount.js",
|
||||
"types": "./dist/createWorkerAccount.d.ts"
|
||||
"types": "./dist/createWorkerAccount.d.ts",
|
||||
"default": "./dist/createWorkerAccount.js"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
@@ -28,11 +28,11 @@
|
||||
"@effect/printer-ansi": "^0.34.5",
|
||||
"@effect/schema": "^0.71.1",
|
||||
"@effect/typeclass": "^0.25.5",
|
||||
"cojson": "workspace:0.16.1",
|
||||
"cojson-storage-sqlite": "workspace:0.16.1",
|
||||
"cojson-transport-ws": "workspace:0.16.1",
|
||||
"cojson": "workspace:0.16.3",
|
||||
"cojson-storage-sqlite": "workspace:0.16.3",
|
||||
"cojson-transport-ws": "workspace:0.16.3",
|
||||
"effect": "^3.6.5",
|
||||
"jazz-tools": "workspace:0.16.1",
|
||||
"jazz-tools": "workspace:0.16.3",
|
||||
"ws": "^8.14.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
# jazz-tools
|
||||
|
||||
## 0.16.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 43d3511: Streamlined CoValue creation:
|
||||
- CoValues can be created with plain JSON objects. Nested CoValues will be automatically created when necessary.
|
||||
- Optional fields can be ommited (i.e. it's no longer necessary to provide an explicit `undefined` value).
|
||||
- cojson@0.16.3
|
||||
- cojson-storage-indexeddb@0.16.3
|
||||
- cojson-transport-ws@0.16.3
|
||||
|
||||
## 0.16.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cojson@0.16.2
|
||||
- cojson-storage-indexeddb@0.16.2
|
||||
- cojson-transport-ws@0.16.2
|
||||
|
||||
## 0.16.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -139,7 +139,7 @@
|
||||
},
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"version": "0.16.1",
|
||||
"version": "0.16.3",
|
||||
"dependencies": {
|
||||
"@manuscripts/prosemirror-recreate-steps": "^0.1.4",
|
||||
"@scure/base": "1.2.1",
|
||||
|
||||
@@ -141,13 +141,102 @@ function useCoValueSubscription<
|
||||
return subscription.subscription;
|
||||
}
|
||||
|
||||
/**
|
||||
* React hook for subscribing to CoValues and handling loading states.
|
||||
*
|
||||
* This hook provides a convenient way to subscribe to CoValues and automatically
|
||||
* handles the subscription lifecycle (subscribe on mount, unsubscribe on unmount).
|
||||
* It also supports deep loading of nested CoValues through resolve queries.
|
||||
*
|
||||
* @returns The loaded CoValue, or `undefined` if loading, or `null` if not found/not accessible
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* // Deep loading with resolve queries
|
||||
* const Project = co.map({
|
||||
* name: z.string(),
|
||||
* tasks: co.list(Task),
|
||||
* owner: TeamMember,
|
||||
* });
|
||||
*
|
||||
* function ProjectView({ projectId }: { projectId: string }) {
|
||||
* const project = useCoState(Project, projectId, {
|
||||
* resolve: {
|
||||
* tasks: { $each: true },
|
||||
* owner: true,
|
||||
* },
|
||||
* });
|
||||
*
|
||||
* if (!project) {
|
||||
* return project === null
|
||||
* ? "Project not found or not accessible"
|
||||
* : "Loading project...";
|
||||
* }
|
||||
*
|
||||
* return (
|
||||
* <div>
|
||||
* <h1>{project.name}</h1>
|
||||
* <p>Owner: {project.owner.name}</p>
|
||||
* <ul>
|
||||
* {project.tasks.map((task) => (
|
||||
* <li key={task.id}>{task.title}</li>
|
||||
* ))}
|
||||
* </ul>
|
||||
* </div>
|
||||
* );
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* // Using with optional references and error handling
|
||||
* const Task = co.map({
|
||||
* title: z.string(),
|
||||
* assignee: co.optional(TeamMember),
|
||||
* subtasks: co.list(Task),
|
||||
* });
|
||||
*
|
||||
* function TaskDetail({ taskId }: { taskId: string }) {
|
||||
* const task = useCoState(Task, taskId, {
|
||||
* resolve: {
|
||||
* assignee: true,
|
||||
* subtasks: { $each: { $onError: null } },
|
||||
* },
|
||||
* });
|
||||
*
|
||||
* if (!task) {
|
||||
* return task === null
|
||||
* ? "Task not found or not accessible"
|
||||
* : "Loading task...";
|
||||
* }
|
||||
*
|
||||
* return (
|
||||
* <div>
|
||||
* <h2>{task.title}</h2>
|
||||
* {task.assignee && <p>Assigned to: {task.assignee.name}</p>}
|
||||
* <ul>
|
||||
* {task.subtasks.map((subtask, index) => (
|
||||
* subtask ? <li key={subtask.id}>{subtask.title}</li> : <li key={index}>Inaccessible subtask</li>
|
||||
* ))}
|
||||
* </ul>
|
||||
* </div>
|
||||
* );
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* For more examples, see the [subscription and deep loading](https://jazz.tools/docs/react/using-covalues/subscription-and-loading) documentation.
|
||||
*/
|
||||
export function useCoState<
|
||||
S extends CoValueClassOrSchema,
|
||||
const R extends ResolveQuery<S> = true,
|
||||
>(
|
||||
/** The CoValue schema or class constructor */
|
||||
Schema: S,
|
||||
/** The ID of the CoValue to subscribe to. If `undefined`, returns `null` */
|
||||
id: string | undefined,
|
||||
/** Optional configuration for the subscription */
|
||||
options?: {
|
||||
/** Resolve query to specify which nested CoValues to load */
|
||||
resolve?: ResolveQueryStrict<S, R>;
|
||||
},
|
||||
): Loaded<S, R> | undefined | null {
|
||||
@@ -229,12 +318,66 @@ function useAccountSubscription<
|
||||
return subscription.subscription;
|
||||
}
|
||||
|
||||
/**
|
||||
* React hook for accessing the current user's account and authentication state.
|
||||
*
|
||||
* This hook provides access to the current user's account profile and root data,
|
||||
* along with authentication utilities. It automatically handles subscription to
|
||||
* the user's account data and provides a logout function.
|
||||
*
|
||||
* @returns An object containing:
|
||||
* - `me`: The loaded account data, or `undefined` if loading, or `null` if not authenticated
|
||||
* - `agent`: The current agent (anonymous or authenticated user). Can be used as `loadAs` parameter for load and subscribe methods.
|
||||
* - `logOut`: Function to log out the current user
|
||||
|
||||
* @example
|
||||
* ```tsx
|
||||
* // Deep loading with resolve queries
|
||||
* function ProjectListWithDetails() {
|
||||
* const { me } = useAccount(MyAppAccount, {
|
||||
* resolve: {
|
||||
* profile: true,
|
||||
* root: {
|
||||
* myProjects: {
|
||||
* $each: {
|
||||
* tasks: true,
|
||||
* },
|
||||
* },
|
||||
* },
|
||||
* },
|
||||
* });
|
||||
*
|
||||
* if (!me) {
|
||||
* return me === null
|
||||
* ? <div>Failed to load your projects</div>
|
||||
* : <div>Loading...</div>;
|
||||
* }
|
||||
*
|
||||
* return (
|
||||
* <div>
|
||||
* <h1>{me.profile.name}'s projects</h1>
|
||||
* <ul>
|
||||
* {me.root.myProjects.map((project) => (
|
||||
* <li key={project.id}>
|
||||
* {project.name} ({project.tasks.length} tasks)
|
||||
* </li>
|
||||
* ))}
|
||||
* </ul>
|
||||
* </div>
|
||||
* );
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
export function useAccount<
|
||||
A extends AccountClass<Account> | AnyAccountSchema,
|
||||
R extends ResolveQuery<A> = true,
|
||||
>(
|
||||
/** The account schema to use. Defaults to the base Account schema */
|
||||
AccountSchema: A = Account as unknown as A,
|
||||
/** Optional configuration for the subscription */
|
||||
options?: {
|
||||
/** Resolve query to specify which nested CoValues to load from the account */
|
||||
resolve?: ResolveQueryStrict<A, R>;
|
||||
},
|
||||
): {
|
||||
|
||||
@@ -34,6 +34,7 @@ import {
|
||||
coField,
|
||||
ensureCoValueLoaded,
|
||||
inspect,
|
||||
instantiateRefEncodedWithInit,
|
||||
isRefEncoded,
|
||||
loadCoValueWithoutMe,
|
||||
parseCoValueCreateOptions,
|
||||
@@ -278,7 +279,16 @@ export class CoFeed<out Item = any> extends CoValueBase implements CoValue {
|
||||
} else if ("encoded" in itemDescriptor) {
|
||||
this._raw.push(itemDescriptor.encoded.encode(item));
|
||||
} else if (isRefEncoded(itemDescriptor)) {
|
||||
this._raw.push((item as unknown as CoValue).id);
|
||||
let refId = (item as unknown as CoValue).id;
|
||||
if (!refId) {
|
||||
const coValue = instantiateRefEncodedWithInit(
|
||||
itemDescriptor,
|
||||
item,
|
||||
this._owner,
|
||||
);
|
||||
refId = coValue.id;
|
||||
}
|
||||
this._raw.push(refId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,9 +428,7 @@ export class CoFeed<out Item = any> extends CoValueBase implements CoValue {
|
||||
*
|
||||
* @category Subscription & Loading
|
||||
*/
|
||||
waitForSync(options?: {
|
||||
timeout?: number;
|
||||
}) {
|
||||
waitForSync(options?: { timeout?: number }) {
|
||||
return this._raw.core.waitForSync(options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import {
|
||||
coValuesCache,
|
||||
ensureCoValueLoaded,
|
||||
inspect,
|
||||
instantiateRefEncodedWithInit,
|
||||
isRefEncoded,
|
||||
loadCoValueWithoutMe,
|
||||
makeRefs,
|
||||
@@ -240,7 +241,7 @@ export class CoList<out Item = any> extends Array<Item> implements CoValue {
|
||||
const { owner } = parseCoValueCreateOptions(options);
|
||||
const instance = new this({ init: items, owner });
|
||||
const raw = owner._raw.createList(
|
||||
toRawItems(items, instance._schema[ItemsSym]),
|
||||
toRawItems(items, instance._schema[ItemsSym], owner),
|
||||
);
|
||||
|
||||
Object.defineProperties(instance, {
|
||||
@@ -256,7 +257,7 @@ export class CoList<out Item = any> extends Array<Item> implements CoValue {
|
||||
|
||||
push(...items: Item[]): number {
|
||||
this._raw.appendItems(
|
||||
toRawItems(items, this._schema[ItemsSym]),
|
||||
toRawItems(items, this._schema[ItemsSym], this._owner),
|
||||
undefined,
|
||||
"private",
|
||||
);
|
||||
@@ -265,7 +266,11 @@ export class CoList<out Item = any> extends Array<Item> implements CoValue {
|
||||
}
|
||||
|
||||
unshift(...items: Item[]): number {
|
||||
for (const item of toRawItems(items as Item[], this._schema[ItemsSym])) {
|
||||
for (const item of toRawItems(
|
||||
items as Item[],
|
||||
this._schema[ItemsSym],
|
||||
this._owner,
|
||||
)) {
|
||||
this._raw.prepend(item);
|
||||
}
|
||||
|
||||
@@ -306,7 +311,11 @@ export class CoList<out Item = any> extends Array<Item> implements CoValue {
|
||||
this._raw.delete(idxToDelete);
|
||||
}
|
||||
|
||||
const rawItems = toRawItems(items as Item[], this._schema[ItemsSym]);
|
||||
const rawItems = toRawItems(
|
||||
items as Item[],
|
||||
this._schema[ItemsSym],
|
||||
this._owner,
|
||||
);
|
||||
|
||||
// If there are no items to insert, return the deleted items
|
||||
if (rawItems.length === 0) {
|
||||
@@ -551,23 +560,36 @@ export class CoList<out Item = any> extends Array<Item> implements CoValue {
|
||||
* Convert an array of items to a raw array of items.
|
||||
* @param items - The array of items to convert.
|
||||
* @param itemDescriptor - The descriptor of the items.
|
||||
* @param owner - The owner of the CoList.
|
||||
* @returns The raw array of items.
|
||||
*/
|
||||
function toRawItems<Item>(items: Item[], itemDescriptor: Schema) {
|
||||
const rawItems =
|
||||
itemDescriptor === "json"
|
||||
? (items as JsonValue[])
|
||||
: "encoded" in itemDescriptor
|
||||
? items?.map((e) => itemDescriptor.encoded.encode(e))
|
||||
: isRefEncoded(itemDescriptor)
|
||||
? items?.map((v) => {
|
||||
if (!v) return null;
|
||||
|
||||
return (v as unknown as CoValue).id;
|
||||
})
|
||||
: (() => {
|
||||
throw new Error("Invalid element descriptor");
|
||||
})();
|
||||
function toRawItems<Item>(
|
||||
items: Item[],
|
||||
itemDescriptor: Schema,
|
||||
owner: Account | Group,
|
||||
) {
|
||||
let rawItems: JsonValue[] = [];
|
||||
if (itemDescriptor === "json") {
|
||||
rawItems = items as JsonValue[];
|
||||
} else if ("encoded" in itemDescriptor) {
|
||||
rawItems = items?.map((e) => itemDescriptor.encoded.encode(e));
|
||||
} else if (isRefEncoded(itemDescriptor)) {
|
||||
rawItems = items?.map((value) => {
|
||||
if (value == null) return null;
|
||||
let refId = (value as unknown as CoValue).id;
|
||||
if (!refId) {
|
||||
const coValue = instantiateRefEncodedWithInit(
|
||||
itemDescriptor,
|
||||
value,
|
||||
owner,
|
||||
);
|
||||
refId = coValue.id;
|
||||
}
|
||||
return refId;
|
||||
});
|
||||
} else {
|
||||
throw new Error("Invalid element descriptor");
|
||||
}
|
||||
return rawItems;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ import {
|
||||
activeAccountContext,
|
||||
ensureCoValueLoaded,
|
||||
inspect,
|
||||
instantiateRefEncodedWithInit,
|
||||
isRefEncoded,
|
||||
loadCoValueWithoutMe,
|
||||
makeRefs,
|
||||
@@ -424,8 +425,17 @@ export class CoMap extends CoValueBase implements CoValue {
|
||||
if (descriptor === "json") {
|
||||
rawInit[key] = initValue as JsonValue;
|
||||
} else if (isRefEncoded(descriptor)) {
|
||||
if (initValue) {
|
||||
rawInit[key] = (initValue as unknown as CoValue).id;
|
||||
if (initValue != null) {
|
||||
let refId = (initValue as unknown as CoValue).id;
|
||||
if (!refId) {
|
||||
const coValue = instantiateRefEncodedWithInit(
|
||||
descriptor,
|
||||
initValue,
|
||||
owner,
|
||||
);
|
||||
refId = coValue.id;
|
||||
}
|
||||
rawInit[key] = refId;
|
||||
}
|
||||
} else if ("encoded" in descriptor) {
|
||||
rawInit[key] = descriptor.encoded.encode(
|
||||
|
||||
@@ -145,7 +145,7 @@ export class Inbox {
|
||||
|
||||
for (const [sessionID, items] of Object.entries(stream.items) as [
|
||||
SessionID,
|
||||
CoStreamItem<CoID<InboxMessage<InstanceOfSchema<M>, O>>>[],
|
||||
CoStreamItem<CoID<InboxMessage<NonNullable<InstanceOfSchema<M>>, O>>>[],
|
||||
][]) {
|
||||
const accountID = getAccountIDfromSessionID(sessionID);
|
||||
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
import { JsonValue, RawCoMap } from "cojson";
|
||||
import {
|
||||
Account,
|
||||
AnonymousJazzAgent,
|
||||
CoMapInit,
|
||||
CoValue,
|
||||
CoValueBase,
|
||||
CoValueClass,
|
||||
CoValueFromRaw,
|
||||
Group,
|
||||
ID,
|
||||
Resolved,
|
||||
Simplify,
|
||||
SubscribeListenerOptions,
|
||||
SubscribeRestArgs,
|
||||
loadCoValueWithoutMe,
|
||||
@@ -20,6 +24,10 @@ import {
|
||||
export type SchemaUnionConcreteSubclass<V extends CoValue> =
|
||||
typeof SchemaUnion & CoValueClass<V>;
|
||||
|
||||
export type SchemaUnionDiscriminator<V extends CoValue> = (discriminable: {
|
||||
get(key: string): JsonValue | undefined;
|
||||
}) => CoValueClass<V> & CoValueFromRaw<V>;
|
||||
|
||||
/**
|
||||
* SchemaUnion allows you to create union types of CoValues that can be discriminated at runtime.
|
||||
*
|
||||
@@ -89,28 +97,45 @@ export abstract class SchemaUnion extends CoValueBase implements CoValue {
|
||||
* @category Declaration
|
||||
**/
|
||||
static Of<V extends CoValue>(
|
||||
discriminator: (raw: V["_raw"]) => CoValueClass<V> & CoValueFromRaw<V>,
|
||||
discriminator: SchemaUnionDiscriminator<V>,
|
||||
): SchemaUnionConcreteSubclass<V> {
|
||||
return class SchemaUnionClass extends SchemaUnion {
|
||||
static override create<V extends CoValue>(
|
||||
this: CoValueClass<V>,
|
||||
init: Simplify<CoMapInit<V>>,
|
||||
owner: Account | Group,
|
||||
): V {
|
||||
const ResolvedClass = discriminator(new Map(Object.entries(init)));
|
||||
// @ts-expect-error - create is a static method in the CoMap class
|
||||
return ResolvedClass.create(init, owner);
|
||||
}
|
||||
|
||||
static override fromRaw<T extends CoValue>(
|
||||
this: CoValueClass<T> & CoValueFromRaw<T>,
|
||||
raw: T["_raw"],
|
||||
): T {
|
||||
const ResolvedClass = discriminator(
|
||||
raw as V["_raw"],
|
||||
raw as RawCoMap,
|
||||
) as unknown as CoValueClass<T> & CoValueFromRaw<T>;
|
||||
return ResolvedClass.fromRaw(raw);
|
||||
}
|
||||
} as unknown as SchemaUnionConcreteSubclass<V>;
|
||||
}
|
||||
|
||||
static create<V extends CoValue>(
|
||||
this: CoValueClass<V>,
|
||||
init: Simplify<CoMapInit<V>>,
|
||||
owner: Account | Group,
|
||||
): V {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance from raw data. This is called internally and should not be used directly.
|
||||
* Use {@link SchemaUnion.Of} to create a union type instead.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
// @ts-ignore
|
||||
static fromRaw<V extends CoValue>(this: CoValueClass<V>, raw: V["_raw"]): V {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
@@ -107,7 +107,6 @@ export {
|
||||
type CoreAccountSchema as AnyAccountSchema,
|
||||
type ResolveQuery,
|
||||
type ResolveQueryStrict,
|
||||
type InitFor,
|
||||
} from "./internal.js";
|
||||
|
||||
export {
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import type { JsonValue, RawCoValue } from "cojson";
|
||||
import { CojsonInternalTypes } from "cojson";
|
||||
import {
|
||||
Account,
|
||||
type CoValue,
|
||||
type CoValueClass,
|
||||
CoValueFromRaw,
|
||||
Group,
|
||||
ItemsSym,
|
||||
JazzToolsSymbol,
|
||||
SchemaInit,
|
||||
isCoValueClass,
|
||||
} from "../internal.js";
|
||||
@@ -140,7 +141,7 @@ export function isRefEncoded<V extends CoValue>(
|
||||
);
|
||||
}
|
||||
|
||||
export function instantiateRefEncoded<V extends CoValue>(
|
||||
export function instantiateRefEncodedFromRaw<V extends CoValue>(
|
||||
schema: RefEncoded<V>,
|
||||
raw: RawCoValue,
|
||||
): V {
|
||||
@@ -151,6 +152,31 @@ export function instantiateRefEncoded<V extends CoValue>(
|
||||
).fromRaw(raw);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new CoValue of the given ref type, using the provided init values.
|
||||
*
|
||||
* @param schema - The schema of the CoValue to create.
|
||||
* @param init - The init values to use to create the CoValue.
|
||||
* @param parentOwner - The owner of the referencing CoValue. Will be used
|
||||
* as the parent group of the created CoValue's group
|
||||
* @returns The created CoValue.
|
||||
*/
|
||||
export function instantiateRefEncodedWithInit<V extends CoValue>(
|
||||
schema: RefEncoded<V>,
|
||||
init: any,
|
||||
parentOwner: Account | Group,
|
||||
): V {
|
||||
if (!isCoValueClass<V>(schema.ref)) {
|
||||
throw Error(
|
||||
`Cannot automatically create CoValue from value: ${JSON.stringify(init)}. Use the CoValue schema's create() method instead.`,
|
||||
);
|
||||
}
|
||||
const owner = Group.create();
|
||||
owner.addMember(parentOwner.castAs(Group));
|
||||
// @ts-expect-error - create is a static method in all CoValue classes
|
||||
return schema.ref.create(init, owner);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type Schema = JsonEncoded | RefEncoded<CoValue> | EncodedAs<any>;
|
||||
|
||||
|
||||
@@ -6,21 +6,16 @@ import {
|
||||
RefsToResolve,
|
||||
RefsToResolveStrict,
|
||||
Resolved,
|
||||
Simplify,
|
||||
SubscribeListenerOptions,
|
||||
coOptionalDefiner,
|
||||
} from "../../../internal.js";
|
||||
import { AnonymousJazzAgent } from "../../anonymousJazzAgent.js";
|
||||
import { CoFieldInit } from "../typeConverters/CoFieldInit.js";
|
||||
import { CoFeedInit } from "../typeConverters/CoFieldInit.js";
|
||||
import { InstanceOrPrimitiveOfSchema } from "../typeConverters/InstanceOrPrimitiveOfSchema.js";
|
||||
import { InstanceOrPrimitiveOfSchemaCoValuesNullable } from "../typeConverters/InstanceOrPrimitiveOfSchemaCoValuesNullable.js";
|
||||
import { CoOptionalSchema } from "./CoOptionalSchema.js";
|
||||
import { CoreCoValueSchema } from "./CoValueSchema.js";
|
||||
|
||||
type CoFeedInit<T extends AnyZodOrCoValueSchema> = Simplify<
|
||||
Array<CoFieldInit<T>>
|
||||
>;
|
||||
|
||||
export class CoFeedSchema<T extends AnyZodOrCoValueSchema>
|
||||
implements CoreCoFeedSchema<T>
|
||||
{
|
||||
@@ -36,7 +31,7 @@ export class CoFeedSchema<T extends AnyZodOrCoValueSchema>
|
||||
init: CoFeedInit<T>,
|
||||
options?: { owner: Account | Group } | Account | Group,
|
||||
): CoFeedInstance<T> {
|
||||
return this.coValueClass.create(init, options) as CoFeedInstance<T>;
|
||||
return this.coValueClass.create(init as any, options) as CoFeedInstance<T>;
|
||||
}
|
||||
|
||||
load<const R extends RefsToResolve<CoFeedInstanceCoValuesNullable<T>> = true>(
|
||||
|
||||
@@ -5,22 +5,17 @@ import {
|
||||
RefsToResolve,
|
||||
RefsToResolveStrict,
|
||||
Resolved,
|
||||
Simplify,
|
||||
SubscribeListenerOptions,
|
||||
coOptionalDefiner,
|
||||
} from "../../../internal.js";
|
||||
import { AnonymousJazzAgent } from "../../anonymousJazzAgent.js";
|
||||
import { CoFieldInit } from "../typeConverters/CoFieldInit.js";
|
||||
import { CoListInit } from "../typeConverters/CoFieldInit.js";
|
||||
import { InstanceOrPrimitiveOfSchema } from "../typeConverters/InstanceOrPrimitiveOfSchema.js";
|
||||
import { InstanceOrPrimitiveOfSchemaCoValuesNullable } from "../typeConverters/InstanceOrPrimitiveOfSchemaCoValuesNullable.js";
|
||||
import { AnyZodOrCoValueSchema } from "../zodSchema.js";
|
||||
import { CoOptionalSchema } from "./CoOptionalSchema.js";
|
||||
import { CoreCoValueSchema } from "./CoValueSchema.js";
|
||||
|
||||
type CoListInit<T extends AnyZodOrCoValueSchema> = Simplify<
|
||||
Array<CoFieldInit<T>>
|
||||
>;
|
||||
|
||||
export class CoListSchema<T extends AnyZodOrCoValueSchema>
|
||||
implements CoreCoListSchema<T>
|
||||
{
|
||||
@@ -36,7 +31,7 @@ export class CoListSchema<T extends AnyZodOrCoValueSchema>
|
||||
items: CoListInit<T>,
|
||||
options?: { owner: Account | Group } | Account | Group,
|
||||
): CoListInstance<T> {
|
||||
return this.coValueClass.create(items, options) as CoListInstance<T>;
|
||||
return this.coValueClass.create(items as any, options) as CoListInstance<T>;
|
||||
}
|
||||
|
||||
load<const R extends RefsToResolve<CoListInstanceCoValuesNullable<T>> = true>(
|
||||
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
DiscriminableCoValueSchemaDefinition,
|
||||
DiscriminableCoreCoValueSchema,
|
||||
Group,
|
||||
PartialOnUndefined,
|
||||
RefsToResolve,
|
||||
RefsToResolveStrict,
|
||||
Resolved,
|
||||
@@ -17,7 +16,7 @@ import {
|
||||
} from "../../../internal.js";
|
||||
import { AnonymousJazzAgent } from "../../anonymousJazzAgent.js";
|
||||
import { removeGetters } from "../../schemaUtils.js";
|
||||
import { CoFieldInit } from "../typeConverters/CoFieldInit.js";
|
||||
import { CoMapSchemaInit } from "../typeConverters/CoFieldInit.js";
|
||||
import { InstanceOrPrimitiveOfSchema } from "../typeConverters/InstanceOrPrimitiveOfSchema.js";
|
||||
import { InstanceOrPrimitiveOfSchemaCoValuesNullable } from "../typeConverters/InstanceOrPrimitiveOfSchemaCoValuesNullable.js";
|
||||
import { z } from "../zodReExport.js";
|
||||
@@ -30,7 +29,7 @@ export interface CoMapSchema<
|
||||
Owner extends Account | Group = Account | Group,
|
||||
> extends CoreCoMapSchema<Shape, CatchAll> {
|
||||
create: (
|
||||
init: Simplify<CoMapSchemaInit<Shape>>,
|
||||
init: CoMapSchemaInit<Shape>,
|
||||
options?:
|
||||
| {
|
||||
owner: Owner;
|
||||
@@ -227,13 +226,6 @@ export function enrichCoMapSchema<
|
||||
return coValueSchema;
|
||||
}
|
||||
|
||||
// Due to a TS limitation with types that contain known properties and
|
||||
// an index signature, we cannot accept catchall properties on creation
|
||||
export type CoMapSchemaInit<Shape extends z.core.$ZodLooseShape> =
|
||||
PartialOnUndefined<{
|
||||
[key in keyof Shape]: CoFieldInit<Shape[key]>;
|
||||
}>;
|
||||
|
||||
export interface CoMapSchemaDefinition<
|
||||
Shape extends z.core.$ZodLooseShape = z.core.$ZodLooseShape,
|
||||
CatchAll extends AnyZodOrCoValueSchema | unknown = unknown,
|
||||
|
||||
@@ -1,13 +1,78 @@
|
||||
import { NotNull } from "../../../internal.js";
|
||||
import {
|
||||
CoDiscriminatedUnionSchema,
|
||||
CoValueClass,
|
||||
CoreCoFeedSchema,
|
||||
CoreCoListSchema,
|
||||
CoreCoMapSchema,
|
||||
CoreCoRecordSchema,
|
||||
CorePlainTextSchema,
|
||||
PartialOnUndefined,
|
||||
Simplify,
|
||||
} from "../../../internal.js";
|
||||
import { CoreCoOptionalSchema } from "../schemaTypes/CoOptionalSchema.js";
|
||||
import { CoreCoValueSchema } from "../schemaTypes/CoValueSchema.js";
|
||||
import { CoreRichTextSchema } from "../schemaTypes/RichTextSchema.js";
|
||||
import { z } from "../zodReExport.js";
|
||||
import { AnyZodOrCoValueSchema } from "../zodSchema.js";
|
||||
import { InstanceOrPrimitiveOfSchemaCoValuesNullable } from "./InstanceOrPrimitiveOfSchemaCoValuesNullable.js";
|
||||
import { AnyZodOrCoValueSchema, Loaded } from "../zodSchema.js";
|
||||
import { TypeOfZodSchema } from "./TypeOfZodSchema.js";
|
||||
|
||||
/**
|
||||
* Returns the type of the value that should be used to initialize a coField
|
||||
* of the given schema.
|
||||
* The type of value that can be used to initialize a CoField of the given schema.
|
||||
*
|
||||
* For CoValue fields, this can be either a shallowly-loaded CoValue instance
|
||||
* or a JSON object that will be used to create the CoValue.
|
||||
*/
|
||||
export type CoFieldInit<T extends AnyZodOrCoValueSchema> =
|
||||
T extends z.core.$ZodNullable
|
||||
? InstanceOrPrimitiveOfSchemaCoValuesNullable<T>
|
||||
: NotNull<InstanceOrPrimitiveOfSchemaCoValuesNullable<T>>;
|
||||
export type CoFieldInit<S extends CoValueClass | AnyZodOrCoValueSchema> =
|
||||
S extends CoreCoValueSchema
|
||||
?
|
||||
| Loaded<S>
|
||||
| (S extends CoreCoRecordSchema<infer K, infer V>
|
||||
? CoMapSchemaInit<{ [key in z.output<K> & string]: V }>
|
||||
: S extends CoreCoMapSchema<infer Shape>
|
||||
? CoMapSchemaInit<Shape>
|
||||
: S extends CoreCoListSchema<infer T>
|
||||
? CoListInit<T>
|
||||
: S extends CoreCoFeedSchema<infer T>
|
||||
? CoFeedInit<T>
|
||||
: S extends CorePlainTextSchema | CoreRichTextSchema
|
||||
? string
|
||||
: S extends CoreCoOptionalSchema<infer T>
|
||||
? CoFieldInit<T> | undefined
|
||||
: S extends CoDiscriminatedUnionSchema<infer Members>
|
||||
? CoFieldInit<Members[number]>
|
||||
: never)
|
||||
: S extends z.core.$ZodType
|
||||
? TypeOfZodSchema<S>
|
||||
: S extends CoValueClass
|
||||
? InstanceType<S>
|
||||
: never;
|
||||
|
||||
// Due to a TS limitation with types that contain known properties and
|
||||
// an index signature, we cannot accept catchall properties on creation
|
||||
export type CoMapSchemaInit<Shape extends z.core.$ZodLooseShape> = Simplify<
|
||||
{
|
||||
/**
|
||||
* Cannot use {@link PartialOnUndefined} because evaluating CoFieldInit<Shape[Key]>
|
||||
* to know if the value can be undefined does not work with recursive types.
|
||||
*/
|
||||
[Key in keyof Shape as Shape[Key] extends
|
||||
| CoreCoOptionalSchema
|
||||
| z.core.$ZodOptional
|
||||
? never
|
||||
: Key]: CoFieldInit<Shape[Key]>;
|
||||
} & {
|
||||
[Key in keyof Shape as Shape[Key] extends
|
||||
| CoreCoOptionalSchema
|
||||
| z.core.$ZodOptional
|
||||
? Key
|
||||
: never]?: CoFieldInit<Shape[Key]>;
|
||||
}
|
||||
>;
|
||||
|
||||
export type CoListInit<T extends AnyZodOrCoValueSchema> = Simplify<
|
||||
ReadonlyArray<CoFieldInit<T>>
|
||||
>;
|
||||
|
||||
export type CoFeedInit<T extends AnyZodOrCoValueSchema> = Simplify<
|
||||
ReadonlyArray<CoFieldInit<T>>
|
||||
>;
|
||||
|
||||
@@ -27,15 +27,20 @@ export type InstanceOfSchema<S extends CoValueClass | AnyZodOrCoValueSchema> =
|
||||
S extends CoreCoValueSchema
|
||||
? S extends CoreAccountSchema<infer Shape>
|
||||
? {
|
||||
[key in keyof Shape]: InstanceOrPrimitiveOfSchema<Shape[key]>;
|
||||
-readonly [key in keyof Shape]: InstanceOrPrimitiveOfSchema<
|
||||
Shape[key]
|
||||
>;
|
||||
} & Account
|
||||
: S extends CoreCoRecordSchema<infer K, infer V>
|
||||
? {
|
||||
[key in z.output<K> & string]: InstanceOrPrimitiveOfSchema<V>;
|
||||
-readonly [key in z.output<K> &
|
||||
string]: InstanceOrPrimitiveOfSchema<V>;
|
||||
} & CoMap
|
||||
: S extends CoreCoMapSchema<infer Shape, infer CatchAll>
|
||||
? {
|
||||
[key in keyof Shape]: InstanceOrPrimitiveOfSchema<Shape[key]>;
|
||||
-readonly [key in keyof Shape]: InstanceOrPrimitiveOfSchema<
|
||||
Shape[key]
|
||||
>;
|
||||
} & (CatchAll extends AnyZodOrCoValueSchema
|
||||
? {
|
||||
[key: string]: InstanceOrPrimitiveOfSchema<CatchAll>;
|
||||
@@ -53,7 +58,7 @@ export type InstanceOfSchema<S extends CoValueClass | AnyZodOrCoValueSchema> =
|
||||
: S extends CoreFileStreamSchema
|
||||
? FileStream
|
||||
: S extends CoreCoOptionalSchema<infer T>
|
||||
? InstanceOrPrimitiveOfSchema<T>
|
||||
? InstanceOrPrimitiveOfSchema<T> | undefined
|
||||
: S extends CoDiscriminatedUnionSchema<infer Members>
|
||||
? InstanceOrPrimitiveOfSchema<Members[number]>
|
||||
: never
|
||||
|
||||
@@ -29,7 +29,7 @@ export type InstanceOfSchemaCoValuesNullable<
|
||||
? S extends CoreAccountSchema<infer Shape>
|
||||
?
|
||||
| ({
|
||||
[key in keyof Shape]: InstanceOrPrimitiveOfSchemaCoValuesNullable<
|
||||
-readonly [key in keyof Shape]: InstanceOrPrimitiveOfSchemaCoValuesNullable<
|
||||
Shape[key]
|
||||
>;
|
||||
} & Account)
|
||||
@@ -37,14 +37,14 @@ export type InstanceOfSchemaCoValuesNullable<
|
||||
: S extends CoreCoRecordSchema<infer K, infer V>
|
||||
?
|
||||
| ({
|
||||
[key in z.output<K> &
|
||||
-readonly [key in z.output<K> &
|
||||
string]: InstanceOrPrimitiveOfSchemaCoValuesNullable<V>;
|
||||
} & CoMap)
|
||||
| null
|
||||
: S extends CoreCoMapSchema<infer Shape, infer CatchAll>
|
||||
?
|
||||
| ({
|
||||
[key in keyof Shape]: InstanceOrPrimitiveOfSchemaCoValuesNullable<
|
||||
-readonly [key in keyof Shape]: InstanceOrPrimitiveOfSchemaCoValuesNullable<
|
||||
Shape[key]
|
||||
>;
|
||||
} & (CatchAll extends AnyZodOrCoValueSchema
|
||||
|
||||
@@ -1,122 +1,11 @@
|
||||
import { JsonValue } from "cojson";
|
||||
import {
|
||||
Account,
|
||||
AnyZodOrCoValueSchema,
|
||||
CoDiscriminatedUnionSchema,
|
||||
CoFeed,
|
||||
CoList,
|
||||
CoMap,
|
||||
CoPlainText,
|
||||
CoRichText,
|
||||
CoValueClass,
|
||||
CoreAccountSchema,
|
||||
CoreCoRecordSchema,
|
||||
FileStream,
|
||||
Profile,
|
||||
InstanceOfSchema,
|
||||
} from "../../../internal.js";
|
||||
import { CoreCoFeedSchema } from "../schemaTypes/CoFeedSchema.js";
|
||||
import { CoreCoListSchema } from "../schemaTypes/CoListSchema.js";
|
||||
import { CoreCoMapSchema } from "../schemaTypes/CoMapSchema.js";
|
||||
import { CoreCoOptionalSchema } from "../schemaTypes/CoOptionalSchema.js";
|
||||
import { CoreCoValueSchema } from "../schemaTypes/CoValueSchema.js";
|
||||
import { CoreFileStreamSchema } from "../schemaTypes/FileStreamSchema.js";
|
||||
import { CorePlainTextSchema } from "../schemaTypes/PlainTextSchema.js";
|
||||
import { CoreRichTextSchema } from "../schemaTypes/RichTextSchema.js";
|
||||
import { z } from "../zodReExport.js";
|
||||
import { TypeOfZodSchema } from "./TypeOfZodSchema.js";
|
||||
|
||||
export type InstanceOrPrimitiveOfSchema<
|
||||
S extends CoValueClass | AnyZodOrCoValueSchema,
|
||||
> = S extends CoreCoValueSchema
|
||||
? S extends CoreAccountSchema<infer Shape>
|
||||
? {
|
||||
-readonly [key in keyof Shape]: InstanceOrPrimitiveOfSchema<Shape[key]>;
|
||||
} & { profile: Profile } & Account
|
||||
: S extends CoreCoRecordSchema<infer K, infer V>
|
||||
? {
|
||||
-readonly [key in z.output<K> &
|
||||
string]: InstanceOrPrimitiveOfSchema<V>;
|
||||
} & CoMap
|
||||
: S extends CoreCoMapSchema<infer Shape, infer CatchAll>
|
||||
? {
|
||||
-readonly [key in keyof Shape]: InstanceOrPrimitiveOfSchema<
|
||||
Shape[key]
|
||||
>;
|
||||
} & (CatchAll extends AnyZodOrCoValueSchema
|
||||
? {
|
||||
[key: string]: InstanceOrPrimitiveOfSchema<CatchAll>;
|
||||
}
|
||||
: {}) &
|
||||
CoMap
|
||||
: S extends CoreCoListSchema<infer T>
|
||||
? CoList<InstanceOrPrimitiveOfSchema<T>>
|
||||
: S extends CoreCoFeedSchema<infer T>
|
||||
? CoFeed<InstanceOrPrimitiveOfSchema<T>>
|
||||
: S extends CorePlainTextSchema
|
||||
? CoPlainText
|
||||
: S extends CoreRichTextSchema
|
||||
? CoRichText
|
||||
: S extends CoreFileStreamSchema
|
||||
? FileStream
|
||||
: S extends CoreCoOptionalSchema<infer T>
|
||||
? InstanceOrPrimitiveOfSchema<T> | undefined
|
||||
: S extends CoDiscriminatedUnionSchema<infer Members>
|
||||
? InstanceOrPrimitiveOfSchema<Members[number]>
|
||||
: never
|
||||
: S extends z.core.$ZodType
|
||||
? S extends z.core.$ZodOptional<infer Inner extends z.core.$ZodType>
|
||||
? InstanceOrPrimitiveOfSchema<Inner> | undefined
|
||||
: S extends z.core.$ZodNullable<infer Inner extends z.core.$ZodType>
|
||||
? InstanceOrPrimitiveOfSchema<Inner> | null
|
||||
: S extends z.ZodJSONSchema
|
||||
? JsonValue
|
||||
: S extends z.core.$ZodUnion<infer Members extends z.core.$ZodType[]>
|
||||
? InstanceOrPrimitiveOfSchema<Members[number]>
|
||||
: // primitives below here - we manually traverse to ensure we only allow what we can handle
|
||||
S extends z.core.$ZodObject<infer Shape>
|
||||
? {
|
||||
-readonly [key in keyof Shape]: InstanceOrPrimitiveOfSchema<
|
||||
Shape[key]
|
||||
>;
|
||||
}
|
||||
: S extends z.core.$ZodArray<infer Item extends z.core.$ZodType>
|
||||
? InstanceOrPrimitiveOfSchema<Item>[]
|
||||
: S extends z.core.$ZodTuple<
|
||||
infer Items extends readonly z.core.$ZodType[]
|
||||
>
|
||||
? {
|
||||
[key in keyof Items]: InstanceOrPrimitiveOfSchema<
|
||||
Items[key]
|
||||
>;
|
||||
}
|
||||
: S extends z.core.$ZodString
|
||||
? string
|
||||
: S extends z.core.$ZodNumber
|
||||
? number
|
||||
: S extends z.core.$ZodBoolean
|
||||
? boolean
|
||||
: S extends z.core.$ZodLiteral<infer Literal>
|
||||
? Literal
|
||||
: S extends z.core.$ZodDate
|
||||
? Date
|
||||
: S extends z.core.$ZodEnum<infer Enum>
|
||||
? Enum[keyof Enum]
|
||||
: S extends z.core.$ZodTemplateLiteral<
|
||||
infer pattern
|
||||
>
|
||||
? pattern
|
||||
: S extends z.core.$ZodReadonly<
|
||||
infer Inner extends z.core.$ZodType
|
||||
>
|
||||
? InstanceOrPrimitiveOfSchema<Inner>
|
||||
: S extends z.core.$ZodDefault<
|
||||
infer Default extends z.core.$ZodType
|
||||
>
|
||||
? InstanceOrPrimitiveOfSchema<Default>
|
||||
: S extends z.core.$ZodCatch<
|
||||
infer Catch extends z.core.$ZodType
|
||||
>
|
||||
? InstanceOrPrimitiveOfSchema<Catch>
|
||||
: never
|
||||
: S extends CoValueClass
|
||||
? InstanceType<S>
|
||||
: never;
|
||||
> = S extends z.core.$ZodType ? TypeOfZodSchema<S> : InstanceOfSchema<S>;
|
||||
|
||||
@@ -1,137 +1,13 @@
|
||||
import { JsonValue } from "cojson";
|
||||
import {
|
||||
Account,
|
||||
AnyZodOrCoValueSchema,
|
||||
CoDiscriminatedUnionSchema,
|
||||
CoFeed,
|
||||
CoList,
|
||||
CoMap,
|
||||
CoPlainText,
|
||||
CoRichText,
|
||||
CoValueClass,
|
||||
CoreAccountSchema,
|
||||
CoreCoRecordSchema,
|
||||
FileStream,
|
||||
InstanceOrPrimitiveOfSchema,
|
||||
Profile,
|
||||
InstanceOfSchemaCoValuesNullable,
|
||||
} from "../../../internal.js";
|
||||
import { CoreCoFeedSchema } from "../schemaTypes/CoFeedSchema.js";
|
||||
import { CoreCoListSchema } from "../schemaTypes/CoListSchema.js";
|
||||
import { CoreCoMapSchema } from "../schemaTypes/CoMapSchema.js";
|
||||
import { CoreCoOptionalSchema } from "../schemaTypes/CoOptionalSchema.js";
|
||||
import { CoreCoValueSchema } from "../schemaTypes/CoValueSchema.js";
|
||||
import { CoreFileStreamSchema } from "../schemaTypes/FileStreamSchema.js";
|
||||
import { CorePlainTextSchema } from "../schemaTypes/PlainTextSchema.js";
|
||||
import { CoreRichTextSchema } from "../schemaTypes/RichTextSchema.js";
|
||||
import { z } from "../zodReExport.js";
|
||||
import { TypeOfZodSchema } from "./TypeOfZodSchema.js";
|
||||
|
||||
export type InstanceOrPrimitiveOfSchemaCoValuesNullable<
|
||||
S extends CoValueClass | AnyZodOrCoValueSchema,
|
||||
> = S extends CoreCoValueSchema
|
||||
? S extends CoreAccountSchema<infer Shape>
|
||||
?
|
||||
| ({
|
||||
-readonly [key in keyof Shape]: InstanceOrPrimitiveOfSchemaCoValuesNullable<
|
||||
Shape[key]
|
||||
>;
|
||||
} & { profile: Profile | null } & Account)
|
||||
| null
|
||||
: S extends CoreCoRecordSchema<infer K, infer V>
|
||||
?
|
||||
| ({
|
||||
-readonly [key in z.output<K> &
|
||||
string]: InstanceOrPrimitiveOfSchemaCoValuesNullable<V>;
|
||||
} & CoMap)
|
||||
| null
|
||||
: S extends CoreCoMapSchema<infer Shape, infer CatchAll>
|
||||
?
|
||||
| ({
|
||||
-readonly [key in keyof Shape]: InstanceOrPrimitiveOfSchemaCoValuesNullable<
|
||||
Shape[key]
|
||||
>;
|
||||
} & (CatchAll extends AnyZodOrCoValueSchema
|
||||
? {
|
||||
[
|
||||
key: string
|
||||
]: InstanceOrPrimitiveOfSchemaCoValuesNullable<CatchAll>;
|
||||
}
|
||||
: {}) &
|
||||
CoMap)
|
||||
| null
|
||||
: S extends CoreCoListSchema<infer T>
|
||||
? CoList<InstanceOrPrimitiveOfSchemaCoValuesNullable<T>> | null
|
||||
: S extends CoreCoFeedSchema<infer T>
|
||||
? CoFeed<InstanceOrPrimitiveOfSchemaCoValuesNullable<T>> | null
|
||||
: S extends CorePlainTextSchema
|
||||
? CoPlainText | null
|
||||
: S extends CoreRichTextSchema
|
||||
? CoRichText | null
|
||||
: S extends CoreFileStreamSchema
|
||||
? FileStream | null
|
||||
: S extends CoreCoOptionalSchema<infer T>
|
||||
? InstanceOrPrimitiveOfSchemaCoValuesNullable<T> | undefined
|
||||
: S extends CoDiscriminatedUnionSchema<infer Members>
|
||||
? InstanceOrPrimitiveOfSchemaCoValuesNullable<
|
||||
Members[number]
|
||||
>
|
||||
: never
|
||||
: S extends z.core.$ZodType
|
||||
? S extends z.core.$ZodOptional<infer Inner extends z.core.$ZodType>
|
||||
? InstanceOrPrimitiveOfSchemaCoValuesNullable<Inner> | undefined
|
||||
: S extends z.core.$ZodNullable<infer Inner extends z.core.$ZodType>
|
||||
? InstanceOrPrimitiveOfSchemaCoValuesNullable<Inner> | null
|
||||
: S extends z.ZodJSONSchema
|
||||
? JsonValue
|
||||
: S extends z.core.$ZodUnion<
|
||||
infer Members extends readonly z.core.$ZodType[]
|
||||
>
|
||||
? InstanceOrPrimitiveOfSchemaCoValuesNullable<Members[number]>
|
||||
: // primitives below here - we manually traverse to ensure we only allow what we can handle
|
||||
S extends z.core.$ZodObject<infer Shape>
|
||||
? {
|
||||
-readonly [key in keyof Shape]: InstanceOrPrimitiveOfSchema<
|
||||
Shape[key]
|
||||
>;
|
||||
}
|
||||
: S extends z.core.$ZodArray<infer Item extends z.core.$ZodType>
|
||||
? InstanceOrPrimitiveOfSchema<Item>[]
|
||||
: S extends z.core.$ZodTuple<
|
||||
infer Items extends z.core.$ZodType[]
|
||||
>
|
||||
? {
|
||||
[key in keyof Items]: InstanceOrPrimitiveOfSchema<
|
||||
Items[key]
|
||||
>;
|
||||
}
|
||||
: S extends z.core.$ZodString
|
||||
? string
|
||||
: S extends z.core.$ZodNumber
|
||||
? number
|
||||
: S extends z.core.$ZodBoolean
|
||||
? boolean
|
||||
: S extends z.core.$ZodLiteral<infer Literal>
|
||||
? Literal
|
||||
: S extends z.core.$ZodDate
|
||||
? Date
|
||||
: S extends z.core.$ZodEnum<infer Enum>
|
||||
? Enum[keyof Enum]
|
||||
: S extends z.core.$ZodTemplateLiteral<
|
||||
infer pattern
|
||||
>
|
||||
? pattern
|
||||
: S extends z.core.$ZodReadonly<
|
||||
infer Inner extends z.core.$ZodType
|
||||
>
|
||||
? InstanceOrPrimitiveOfSchema<Inner>
|
||||
: S extends z.core.$ZodDefault<
|
||||
infer Default extends z.core.$ZodType
|
||||
>
|
||||
? InstanceOrPrimitiveOfSchema<Default>
|
||||
: S extends z.core.$ZodCatch<
|
||||
infer Catch extends z.core.$ZodType
|
||||
>
|
||||
? InstanceOrPrimitiveOfSchema<Catch>
|
||||
: never
|
||||
: S extends CoValueClass
|
||||
? InstanceType<S> | null
|
||||
: never;
|
||||
> = S extends z.core.$ZodType
|
||||
? TypeOfZodSchema<S>
|
||||
: InstanceOfSchemaCoValuesNullable<S>;
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
import { JsonValue } from "cojson";
|
||||
import { PartialOnUndefined } from "../../../internal.js";
|
||||
import { z } from "../zodReExport.js";
|
||||
|
||||
// Copied from https://github.com/colinhacks/zod/blob/7e7e3461aceecf3633e158df50d6bc852e7cdf45/packages/zod/src/v4/core/schemas.ts#L1591,
|
||||
// since this type is not exported by Zod
|
||||
type OptionalInSchema = {
|
||||
_zod: {
|
||||
optin: "optional";
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Get type from Zod schema definition.
|
||||
*
|
||||
* Similar to `z.infer`, but we manually traverse Zod types to ensure we only allow what we can handle
|
||||
*/
|
||||
export type TypeOfZodSchema<S extends z.core.$ZodType> =
|
||||
S extends z.core.$ZodOptional<infer Inner extends z.core.$ZodType>
|
||||
? TypeOfZodSchema<Inner> | undefined
|
||||
: S extends z.core.$ZodNullable<infer Inner extends z.core.$ZodType>
|
||||
? TypeOfZodSchema<Inner> | null
|
||||
: S extends z.ZodJSONSchema
|
||||
? JsonValue
|
||||
: S extends z.core.$ZodUnion<
|
||||
infer Members extends readonly z.core.$ZodType[]
|
||||
>
|
||||
? TypeOfZodSchema<Members[number]>
|
||||
: S extends z.core.$ZodObject<infer Shape>
|
||||
? /**
|
||||
* Cannot use {@link PartialOnUndefined} because evaluating TypeOfZodSchema<Shape[key]>
|
||||
* to know if the value can be undefined does not work with recursive types.
|
||||
*/
|
||||
{
|
||||
-readonly [key in keyof Shape as Shape[key] extends OptionalInSchema
|
||||
? never
|
||||
: key]: TypeOfZodSchema<Shape[key]>;
|
||||
} & {
|
||||
-readonly [key in keyof Shape as Shape[key] extends OptionalInSchema
|
||||
? key
|
||||
: never]?: TypeOfZodSchema<Shape[key]>;
|
||||
}
|
||||
: S extends z.core.$ZodArray<infer Item extends z.core.$ZodType>
|
||||
? TypeOfZodSchema<Item>[]
|
||||
: S extends z.core.$ZodTuple<
|
||||
infer Items extends readonly z.core.$ZodType[]
|
||||
>
|
||||
? {
|
||||
[key in keyof Items]: TypeOfZodSchema<Items[key]>;
|
||||
}
|
||||
: S extends z.core.$ZodString
|
||||
? string
|
||||
: S extends z.core.$ZodNumber
|
||||
? number
|
||||
: S extends z.core.$ZodBoolean
|
||||
? boolean
|
||||
: S extends z.core.$ZodLiteral<infer Literal>
|
||||
? Literal
|
||||
: S extends z.core.$ZodDate
|
||||
? Date
|
||||
: S extends z.core.$ZodEnum<infer Enum>
|
||||
? Enum[keyof Enum]
|
||||
: S extends z.core.$ZodTemplateLiteral<
|
||||
infer pattern
|
||||
>
|
||||
? pattern
|
||||
: S extends z.core.$ZodReadonly<
|
||||
infer Inner extends z.core.$ZodType
|
||||
>
|
||||
? TypeOfZodSchema<Inner>
|
||||
: S extends z.core.$ZodDefault<
|
||||
infer Default extends z.core.$ZodType
|
||||
>
|
||||
? TypeOfZodSchema<Default>
|
||||
: S extends z.core.$ZodCatch<
|
||||
infer Catch extends z.core.$ZodType
|
||||
>
|
||||
? TypeOfZodSchema<Catch>
|
||||
: never;
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
CoreCoMapSchema,
|
||||
DiscriminableCoValueSchemas,
|
||||
DiscriminableCoreCoValueSchema,
|
||||
SchemaUnionDiscriminator,
|
||||
} from "../../internal.js";
|
||||
import {
|
||||
hydrateCoreCoValueSchema,
|
||||
@@ -56,21 +57,17 @@ export function schemaUnionDiscriminatorFor(
|
||||
}
|
||||
}
|
||||
|
||||
const determineSchema = (_raw: RawCoMap | RawAccount | RawCoList) => {
|
||||
if (_raw instanceof RawCoList) {
|
||||
throw new Error(
|
||||
"co.discriminatedUnion() of collaborative types is not supported for CoLists",
|
||||
);
|
||||
}
|
||||
|
||||
const determineSchema: SchemaUnionDiscriminator<CoMap> = (
|
||||
discriminable,
|
||||
) => {
|
||||
for (const option of availableOptions) {
|
||||
let match = true;
|
||||
|
||||
for (const key of Object.keys(discriminatorMap)) {
|
||||
const discriminatorDef = (option as CoreCoMapSchema).getDefinition()
|
||||
.shape[key as string];
|
||||
.shape[key];
|
||||
|
||||
const discriminatorValue = (_raw as RawCoMap).get(key as string);
|
||||
const discriminatorValue = discriminable.get(key);
|
||||
|
||||
if (discriminatorValue && typeof discriminatorValue === "object") {
|
||||
throw new Error("Discriminator must be a primitive value");
|
||||
|
||||
@@ -23,11 +23,7 @@ import {
|
||||
} from "./schemaTypes/CoDiscriminatedUnionSchema.js";
|
||||
import { CoFeedSchema, CoreCoFeedSchema } from "./schemaTypes/CoFeedSchema.js";
|
||||
import { CoListSchema, CoreCoListSchema } from "./schemaTypes/CoListSchema.js";
|
||||
import {
|
||||
CoMapSchema,
|
||||
CoMapSchemaInit,
|
||||
CoreCoMapSchema,
|
||||
} from "./schemaTypes/CoMapSchema.js";
|
||||
import { CoMapSchema, CoreCoMapSchema } from "./schemaTypes/CoMapSchema.js";
|
||||
import {
|
||||
CoOptionalSchema,
|
||||
CoreCoOptionalSchema,
|
||||
@@ -84,8 +80,8 @@ export type CoValueSchemaFromCoreSchema<S extends CoreCoValueSchema> =
|
||||
export type CoValueClassFromAnySchema<S extends CoValueClassOrSchema> =
|
||||
S extends CoValueClass<any>
|
||||
? S
|
||||
: CoValueClass<InstanceOfSchema<S>> &
|
||||
CoValueFromRaw<InstanceOfSchema<S>> &
|
||||
: CoValueClass<NonNullable<InstanceOfSchema<S>>> &
|
||||
CoValueFromRaw<NonNullable<InstanceOfSchema<S>>> &
|
||||
(S extends CoreAccountSchema ? AccountClassEssentials : {});
|
||||
|
||||
type AccountClassEssentials = {
|
||||
@@ -122,9 +118,3 @@ export type ResolveQueryStrict<
|
||||
T extends CoValueClassOrSchema,
|
||||
R extends ResolveQuery<T>,
|
||||
> = RefsToResolveStrict<NonNullable<InstanceOfSchemaCoValuesNullable<T>>, R>;
|
||||
|
||||
export type InitFor<T extends CoValueClassOrSchema> = T extends CoreCoMapSchema<
|
||||
infer Shape
|
||||
>
|
||||
? Simplify<CoMapSchemaInit<Shape>>
|
||||
: never;
|
||||
|
||||
@@ -45,6 +45,7 @@ export * from "./implementation/zodSchema/typeConverters/InstanceOrPrimitiveOfSc
|
||||
export * from "./implementation/zodSchema/typeConverters/InstanceOrPrimitiveOfSchemaCoValuesNullable.js";
|
||||
export * from "./implementation/zodSchema/typeConverters/InstanceOfSchema.js";
|
||||
export * from "./implementation/zodSchema/typeConverters/InstanceOfSchemaCoValuesNullable.js";
|
||||
export * from "./implementation/zodSchema/typeConverters/CoFieldInit.js";
|
||||
export * from "./implementation/zodSchema/runtimeConverters/coValueSchemaTransformation.js";
|
||||
export * from "./implementation/zodSchema/runtimeConverters/schemaFieldToCoFieldDef.js";
|
||||
export * from "./coValues/extensions/imageDef.js";
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
type ID,
|
||||
type RefEncoded,
|
||||
type RefsToResolve,
|
||||
instantiateRefEncoded,
|
||||
instantiateRefEncodedFromRaw,
|
||||
isRefEncoded,
|
||||
} from "../internal.js";
|
||||
import { applyCoValueMigrations } from "../lib/migration.js";
|
||||
@@ -75,7 +75,9 @@ export class SubscriptionScope<D extends CoValue> {
|
||||
}
|
||||
|
||||
this.migrating = true;
|
||||
applyCoValueMigrations(instantiateRefEncoded(this.schema, value));
|
||||
applyCoValueMigrations(
|
||||
instantiateRefEncodedFromRaw(this.schema, value),
|
||||
);
|
||||
this.migrated = true;
|
||||
this.handleUpdate(lastUpdate);
|
||||
return;
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
CoValue,
|
||||
RefEncoded,
|
||||
coValueClassFromCoValueClassOrSchema,
|
||||
instantiateRefEncoded,
|
||||
instantiateRefEncodedFromRaw,
|
||||
} from "../internal.js";
|
||||
import { coValuesCache } from "../lib/cache.js";
|
||||
import { SubscriptionScope } from "./SubscriptionScope.js";
|
||||
@@ -26,7 +26,7 @@ export function createCoValue<D extends CoValue>(
|
||||
raw: RawCoValue,
|
||||
subscriptionScope: SubscriptionScope<D>,
|
||||
) {
|
||||
const freshValueInstance = instantiateRefEncoded(ref, raw);
|
||||
const freshValueInstance = instantiateRefEncodedFromRaw(ref, raw);
|
||||
|
||||
Object.defineProperty(freshValueInstance, "_subscriptionScope", {
|
||||
value: subscriptionScope,
|
||||
|
||||
@@ -52,6 +52,46 @@ describe("Simple CoFeed operations", async () => {
|
||||
expect(stream.perSession[me.sessionID]?.value).toEqual("milk");
|
||||
});
|
||||
|
||||
describe("Create CoFeed with a reference", () => {
|
||||
let me: Account;
|
||||
|
||||
beforeEach(async () => {
|
||||
await setupJazzTestSync();
|
||||
me = await createJazzTestAccount({
|
||||
isCurrentActiveAccount: true,
|
||||
creationProps: { name: "Hermes Puggington" },
|
||||
});
|
||||
});
|
||||
|
||||
test("using a CoValue", () => {
|
||||
const Text = co.plainText();
|
||||
const TextStream = co.feed(Text);
|
||||
|
||||
const stream = TextStream.create([Text.create("milk")], { owner: me });
|
||||
|
||||
const coValue = stream.perAccount[me.id]?.value;
|
||||
expect(coValue?.toString()).toEqual("milk");
|
||||
});
|
||||
|
||||
describe("using JSON", () => {
|
||||
test("automatically creates CoValues for nested objects", () => {
|
||||
const Text = co.plainText();
|
||||
const TextStream = co.feed(Text);
|
||||
|
||||
const stream = TextStream.create(["milk"], { owner: me });
|
||||
|
||||
const coValue = stream.perAccount[me.id]?.value;
|
||||
expect(coValue?.toString()).toEqual("milk");
|
||||
});
|
||||
|
||||
test("can create a coPlainText from an empty string", () => {
|
||||
const Schema = co.feed(co.plainText());
|
||||
const feed = Schema.create([""]);
|
||||
expect(feed.perAccount[me.id]?.value?.toString()).toBe("");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test("Construction with nullable values", () => {
|
||||
const NullableTestStream = co.feed(z.string().nullable());
|
||||
const stream = NullableTestStream.create(["milk", null], { owner: me });
|
||||
|
||||
@@ -52,6 +52,48 @@ describe("Simple CoList operations", async () => {
|
||||
expect(list[2]).toBe("c");
|
||||
});
|
||||
|
||||
test("create CoList with reference using CoValue", () => {
|
||||
const Dog = co.map({
|
||||
name: z.string(),
|
||||
});
|
||||
const Person = co.map({
|
||||
pets: co.list(Dog),
|
||||
});
|
||||
|
||||
const person = Person.create({
|
||||
pets: [Dog.create({ name: "Rex" }), Dog.create({ name: "Fido" })],
|
||||
});
|
||||
|
||||
expect(person.pets.length).toEqual(2);
|
||||
expect(person.pets[0]?.name).toEqual("Rex");
|
||||
expect(person.pets[1]?.name).toEqual("Fido");
|
||||
});
|
||||
|
||||
describe("create CoList with reference using JSON", () => {
|
||||
test("automatically creates CoValues for nested objects", () => {
|
||||
const Dog = co.map({
|
||||
name: z.string(),
|
||||
});
|
||||
const Person = co.map({
|
||||
pets: co.list(Dog),
|
||||
});
|
||||
|
||||
const person = Person.create({
|
||||
pets: [{ name: "Rex" }, { name: "Fido" }],
|
||||
});
|
||||
|
||||
expect(person.pets.length).toEqual(2);
|
||||
expect(person.pets[0]?.name).toEqual("Rex");
|
||||
expect(person.pets[1]?.name).toEqual("Fido");
|
||||
});
|
||||
|
||||
test("can create a coPlainText from an empty string", () => {
|
||||
const Schema = co.list(co.plainText());
|
||||
const list = Schema.create([""]);
|
||||
expect(list[0]?.toString()).toBe("");
|
||||
});
|
||||
});
|
||||
|
||||
test("list with nullable content", () => {
|
||||
const List = co.list(z.string().nullable());
|
||||
const list = List.create(["a", "b", "c", null]);
|
||||
|
||||
@@ -54,7 +54,7 @@ describe("CoMap", async () => {
|
||||
expectTypeOf(john._owner).toEqualTypeOf<Account | Group>();
|
||||
});
|
||||
|
||||
test("CoMap with reference", () => {
|
||||
test("create CoMap with reference using CoValue", () => {
|
||||
const Dog = co.map({
|
||||
name: z.string(),
|
||||
breed: z.string(),
|
||||
@@ -85,6 +85,46 @@ describe("CoMap", async () => {
|
||||
matches(person);
|
||||
});
|
||||
|
||||
test("create CoMap with reference using JSON", () => {
|
||||
const Dog = co.map({
|
||||
name: z.string(),
|
||||
breed: z.string(),
|
||||
});
|
||||
const Person = co.map({
|
||||
dog1: Dog,
|
||||
dog2: Dog,
|
||||
get friend() {
|
||||
return Person.optional();
|
||||
},
|
||||
});
|
||||
|
||||
const person = Person.create({
|
||||
// @ts-expect-error - breed is missing
|
||||
dog1: { name: "Rex", items },
|
||||
// @ts-expect-error - Object literal may only specify known properties
|
||||
dog2: { name: "Fido", breed: "Labrador", extra: "extra" },
|
||||
friend: {
|
||||
dog1: {
|
||||
name: "Rex",
|
||||
breed: "Labrador",
|
||||
},
|
||||
dog2: { name: "Fido", breed: "Labrador" },
|
||||
},
|
||||
});
|
||||
|
||||
type ExpectedType = {
|
||||
dog1: Loaded<typeof Dog>;
|
||||
dog2: Loaded<typeof Dog>;
|
||||
friend: Loaded<typeof Person> | undefined;
|
||||
};
|
||||
|
||||
function matches(value: ExpectedType) {
|
||||
return value;
|
||||
}
|
||||
|
||||
matches(person);
|
||||
});
|
||||
|
||||
test("CoMap with optional reference", () => {
|
||||
const Dog = co.map({
|
||||
name: z.string(),
|
||||
|
||||
@@ -117,10 +117,9 @@ describe("CoMap", async () => {
|
||||
expect(emptyMap.color).toEqual(undefined);
|
||||
});
|
||||
|
||||
test("CoMap with reference", () => {
|
||||
test("create CoMap with reference using CoValue", () => {
|
||||
const Dog = co.map({
|
||||
name: z.string(),
|
||||
breed: z.string(),
|
||||
});
|
||||
|
||||
const Person = co.map({
|
||||
@@ -132,11 +131,89 @@ describe("CoMap", async () => {
|
||||
const person = Person.create({
|
||||
name: "John",
|
||||
age: 20,
|
||||
dog: Dog.create({ name: "Rex", breed: "Labrador" }),
|
||||
dog: Dog.create({ name: "Rex" }),
|
||||
});
|
||||
|
||||
expect(person.dog?.name).toEqual("Rex");
|
||||
expect(person.dog?.breed).toEqual("Labrador");
|
||||
});
|
||||
|
||||
describe("create CoMap with references using JSON", () => {
|
||||
const Dog = co.map({
|
||||
type: z.literal("dog"),
|
||||
name: z.string(),
|
||||
});
|
||||
const Cat = co.map({
|
||||
type: z.literal("cat"),
|
||||
name: z.string(),
|
||||
});
|
||||
|
||||
const Person = co.map({
|
||||
name: co.plainText(),
|
||||
bio: co.richText(),
|
||||
dog: Dog,
|
||||
get friends() {
|
||||
return co.list(Person);
|
||||
},
|
||||
reactions: co.feed(co.plainText()),
|
||||
pet: co.discriminatedUnion("type", [Dog, Cat]),
|
||||
});
|
||||
|
||||
let person: ReturnType<typeof Person.create>;
|
||||
|
||||
beforeEach(() => {
|
||||
person = Person.create({
|
||||
name: "John",
|
||||
bio: "I am a software engineer",
|
||||
dog: { type: "dog", name: "Rex" },
|
||||
friends: [
|
||||
{
|
||||
name: "Jane",
|
||||
bio: "I am a mechanical engineer",
|
||||
dog: { type: "dog", name: "Fido" },
|
||||
friends: [],
|
||||
reactions: [],
|
||||
pet: { type: "dog", name: "Fido" },
|
||||
},
|
||||
],
|
||||
reactions: ["👎", "👍"],
|
||||
pet: { type: "cat", name: "Whiskers" },
|
||||
});
|
||||
});
|
||||
|
||||
it("automatically creates CoValues for each CoValue reference", () => {
|
||||
expect(person.name.toString()).toEqual("John");
|
||||
expect(person.bio.toString()).toEqual("I am a software engineer");
|
||||
expect(person.dog?.name).toEqual("Rex");
|
||||
expect(person.friends.length).toEqual(1);
|
||||
expect(person.friends[0]?.name.toString()).toEqual("Jane");
|
||||
expect(person.friends[0]?.bio.toString()).toEqual(
|
||||
"I am a mechanical engineer",
|
||||
);
|
||||
expect(person.friends[0]?.dog.name).toEqual("Fido");
|
||||
expect(person.friends[0]?.friends.length).toEqual(0);
|
||||
expect(person.reactions.byMe?.value?.toString()).toEqual("👍");
|
||||
expect(person.pet.name).toEqual("Whiskers");
|
||||
});
|
||||
|
||||
it("creates a group for each new CoValue that is a child of the referencing CoValue's owner", () => {
|
||||
for (const value of Object.values(person)) {
|
||||
expect(
|
||||
value._owner.getParentGroups().map((group: Group) => group.id),
|
||||
).toContain(person._owner.id);
|
||||
}
|
||||
const friend = person.friends[0]!;
|
||||
for (const value of Object.values(friend)) {
|
||||
expect(
|
||||
value._owner.getParentGroups().map((group: Group) => group.id),
|
||||
).toContain(friend._owner.id);
|
||||
}
|
||||
});
|
||||
|
||||
it("can create a coPlainText from an empty string", () => {
|
||||
const Schema = co.map({ text: co.plainText() });
|
||||
const map = Schema.create({ text: "" });
|
||||
expect(map.text.toString()).toBe("");
|
||||
});
|
||||
});
|
||||
|
||||
test("CoMap with self reference", () => {
|
||||
@@ -209,7 +286,7 @@ describe("CoMap", async () => {
|
||||
const Person = co.map({
|
||||
name: z.string(),
|
||||
age: z.number(),
|
||||
get friend(): co.Optional<typeof Person> {
|
||||
get friend() {
|
||||
return co.optional(Person);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -316,6 +316,60 @@ describe("Group inheritance", () => {
|
||||
expect(group.getRoleOf(bob.id)).toBe("reader");
|
||||
expect(group.getRoleOf(alice.id)).toBe(undefined);
|
||||
});
|
||||
|
||||
describe("when creating nested CoValues from a JSON object", () => {
|
||||
const Task = co.plainText();
|
||||
const Column = co.list(Task);
|
||||
const Board = co.map({
|
||||
title: z.string(),
|
||||
columns: co.list(Column),
|
||||
});
|
||||
|
||||
let board: ReturnType<typeof Board.create>;
|
||||
|
||||
beforeEach(async () => {
|
||||
const me = co.account().getMe();
|
||||
const writeAccess = Group.create();
|
||||
writeAccess.addMember(me, "writer");
|
||||
|
||||
board = Board.create(
|
||||
{
|
||||
title: "My board",
|
||||
columns: [
|
||||
["Task 1.1", "Task 1.2"],
|
||||
["Task 2.1", "Task 2.2"],
|
||||
],
|
||||
},
|
||||
writeAccess,
|
||||
);
|
||||
});
|
||||
|
||||
test("nested CoValues inherit permissions from the referencing CoValue", async () => {
|
||||
const me = co.account().getMe();
|
||||
const task = board.columns[0]![0]!;
|
||||
|
||||
const boardAsWriter = await Board.load(board.id, { loadAs: me });
|
||||
expect(boardAsWriter?.title).toEqual("My board");
|
||||
const taskAsWriter = await Task.load(task.id, { loadAs: me });
|
||||
expect(taskAsWriter?.toString()).toEqual("Task 1.1");
|
||||
});
|
||||
|
||||
test("nested CoValues inherit permissions from the referencing CoValue", async () => {
|
||||
const me = co.account().getMe();
|
||||
const reader = await co.account().createAs(me, {
|
||||
creationProps: { name: "Reader" },
|
||||
});
|
||||
|
||||
const task = board.columns[0]![0]!;
|
||||
const taskGroup = task._owner.castAs(Group);
|
||||
taskGroup.addMember(reader, "reader");
|
||||
|
||||
const taskAsReader = await Task.load(task.id, { loadAs: reader });
|
||||
expect(taskAsReader?.toString()).toEqual("Task 1.1");
|
||||
const boardAsReader = await Board.load(board.id, { loadAs: reader });
|
||||
expect(boardAsReader).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Group.getRoleOf", () => {
|
||||
|
||||
@@ -98,6 +98,22 @@ describe("co.map and Zod schema compatibility", () => {
|
||||
expect(map.updatedAt).toEqual("Test");
|
||||
});
|
||||
|
||||
it("should handle nested optional fields", async () => {
|
||||
const RecursiveZodSchema = z.object({
|
||||
get optionalField() {
|
||||
return RecursiveZodSchema.optional();
|
||||
},
|
||||
});
|
||||
const CoMapSchema = co.map({ field: RecursiveZodSchema });
|
||||
|
||||
const map = CoMapSchema.create({
|
||||
field: { optionalField: { optionalField: {} } },
|
||||
});
|
||||
expect(
|
||||
map.field.optionalField!.optionalField!.optionalField,
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should handle literal fields", async () => {
|
||||
const schema = co.map({
|
||||
status: z.literal("active"),
|
||||
|
||||
24
pnpm-lock.yaml
generated
24
pnpm-lock.yaml
generated
@@ -1842,19 +1842,19 @@ importers:
|
||||
specifier: ^0.25.5
|
||||
version: 0.25.8(effect@3.11.9)
|
||||
cojson:
|
||||
specifier: workspace:0.16.1
|
||||
specifier: workspace:0.16.3
|
||||
version: link:../cojson
|
||||
cojson-storage-sqlite:
|
||||
specifier: workspace:0.16.1
|
||||
specifier: workspace:0.16.3
|
||||
version: link:../cojson-storage-sqlite
|
||||
cojson-transport-ws:
|
||||
specifier: workspace:0.16.1
|
||||
specifier: workspace:0.16.3
|
||||
version: link:../cojson-transport-ws
|
||||
effect:
|
||||
specifier: ^3.6.5
|
||||
version: 3.11.9
|
||||
jazz-tools:
|
||||
specifier: workspace:0.16.1
|
||||
specifier: workspace:0.16.3
|
||||
version: link:../jazz-tools
|
||||
ws:
|
||||
specifier: ^8.14.2
|
||||
@@ -2182,6 +2182,15 @@ importers:
|
||||
|
||||
tests/browser-integration:
|
||||
dependencies:
|
||||
'@testing-library/jest-dom':
|
||||
specifier: 6.6.3
|
||||
version: 6.6.3
|
||||
'@testing-library/react':
|
||||
specifier: 16.2.0
|
||||
version: 16.2.0(@testing-library/dom@10.4.0)(@types/react-dom@19.1.0(@types/react@19.1.0))(@types/react@19.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
'@vitejs/plugin-react-swc':
|
||||
specifier: ^3.10.1
|
||||
version: 3.10.1(@swc/helpers@0.5.17)(vite@6.3.5(@types/node@22.16.5)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.37.0)(tsx@4.20.3)(yaml@2.6.1))
|
||||
cojson:
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/cojson
|
||||
@@ -2197,6 +2206,12 @@ importers:
|
||||
jazz-tools:
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/jazz-tools
|
||||
react:
|
||||
specifier: 19.1.0
|
||||
version: 19.1.0
|
||||
react-dom:
|
||||
specifier: 19.1.0
|
||||
version: 19.1.0(react@19.1.0)
|
||||
devDependencies:
|
||||
typescript:
|
||||
specifier: 'catalog:'
|
||||
@@ -12406,6 +12421,7 @@ packages:
|
||||
source-map@0.8.0-beta.0:
|
||||
resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==}
|
||||
engines: {node: '>= 8'}
|
||||
deprecated: The work that was done in this beta branch won't be included in future versions
|
||||
|
||||
sourcemap-codec@1.4.8:
|
||||
resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==}
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
# jazz-react-tailwind-starter
|
||||
|
||||
## 0.0.140
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [43d3511]
|
||||
- jazz-tools@0.16.3
|
||||
|
||||
## 0.0.139
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-tools@0.16.2
|
||||
|
||||
## 0.0.138
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-react-passkey-auth-starter",
|
||||
"private": true,
|
||||
"version": "0.0.138",
|
||||
"version": "0.0.140",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
# svelte-passkey-auth
|
||||
|
||||
## 0.0.114
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [43d3511]
|
||||
- jazz-tools@0.16.3
|
||||
|
||||
## 0.0.113
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-tools@0.16.2
|
||||
|
||||
## 0.0.112
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "svelte-passkey-auth",
|
||||
"version": "0.0.112",
|
||||
"version": "0.0.114",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -15,6 +15,11 @@
|
||||
"cojson-storage-indexeddb": "workspace:*",
|
||||
"cojson-storage-sqlite": "workspace:*",
|
||||
"cojson-transport-ws": "workspace:*",
|
||||
"jazz-tools": "workspace:*"
|
||||
"jazz-tools": "workspace:*",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"@vitejs/plugin-react-swc": "^3.10.1",
|
||||
"@testing-library/react": "16.2.0",
|
||||
"@testing-library/jest-dom": "6.6.3"
|
||||
}
|
||||
}
|
||||
|
||||
179
tests/browser-integration/src/logout.react.test.tsx
Normal file
179
tests/browser-integration/src/logout.react.test.tsx
Normal file
@@ -0,0 +1,179 @@
|
||||
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
||||
import { commands } from "@vitest/browser/context";
|
||||
import { AuthSecretStorage, co, z } from "jazz-tools";
|
||||
import { JazzReactProvider, useAccount, useCoState } from "jazz-tools/react";
|
||||
import { afterAll, afterEach, describe, expect, test } from "vitest";
|
||||
import { createAccountContext, startSyncServer } from "./testUtils";
|
||||
|
||||
// Define a simple account schema for testing
|
||||
const TestMap = co.map({ count: co.map({ value: z.number() }) });
|
||||
|
||||
const TestAccount = co
|
||||
.account({
|
||||
profile: co.map({ name: z.string() }),
|
||||
root: TestMap,
|
||||
})
|
||||
.withMigration((account) => {
|
||||
if (!account.root) {
|
||||
account.root = TestMap.create(
|
||||
{ count: TestMap.shape.count.create({ value: 0 }) },
|
||||
{ owner: account },
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// React component that uses Jazz hooks for testing logout behavior
|
||||
function TestLogoutComponent({
|
||||
onLogout,
|
||||
}: {
|
||||
onLogout?: () => void;
|
||||
}) {
|
||||
const { logOut, me } = useAccount(TestAccount, {
|
||||
resolve: {
|
||||
profile: true,
|
||||
},
|
||||
});
|
||||
|
||||
const root = useCoState(TestAccount.shape.root, me?.root?.id, {
|
||||
resolve: {
|
||||
count: true,
|
||||
},
|
||||
});
|
||||
|
||||
const handleLogout = () => {
|
||||
logOut();
|
||||
onLogout?.();
|
||||
};
|
||||
|
||||
if (me && root) {
|
||||
return (
|
||||
<div>
|
||||
<p data-testid="user-name">Welcome, {me.profile.name}</p>
|
||||
<p data-testid="root-value">{root.count.value}</p>
|
||||
<button
|
||||
data-testid="increment-button"
|
||||
onClick={() => {
|
||||
root.count.value++;
|
||||
}}
|
||||
>
|
||||
Increment
|
||||
</button>
|
||||
<button data-testid="logout-button" onClick={handleLogout}>
|
||||
Logout
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p data-testid="not-authenticated">Not authenticated</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
afterAll(async () => {
|
||||
await commands.cleanup();
|
||||
});
|
||||
|
||||
describe("Jazz logout behavior in React apps", () => {
|
||||
afterEach(async () => {
|
||||
await new AuthSecretStorage().clear();
|
||||
});
|
||||
|
||||
test("should update the profile state on logout", async () => {
|
||||
const syncServer = await startSyncServer();
|
||||
let logoutCallbackCalled = false;
|
||||
|
||||
// Create an authenticated account context
|
||||
await createAccountContext({
|
||||
defaultProfileName: "John Doe",
|
||||
sync: {
|
||||
peer: syncServer.url,
|
||||
},
|
||||
storage: "indexedDB",
|
||||
AccountSchema: TestAccount,
|
||||
});
|
||||
|
||||
// Render the React component with Jazz provider
|
||||
const { unmount } = render(
|
||||
<JazzReactProvider
|
||||
sync={{ peer: syncServer.url }}
|
||||
storage="indexedDB"
|
||||
AccountSchema={TestAccount}
|
||||
defaultProfileName="Anonymous user"
|
||||
>
|
||||
<TestLogoutComponent
|
||||
onLogout={() => {
|
||||
logoutCallbackCalled = true;
|
||||
}}
|
||||
/>
|
||||
</JazzReactProvider>,
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId("user-name")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
expect(screen.getByTestId("user-name")).toHaveTextContent(
|
||||
"Welcome, John Doe",
|
||||
);
|
||||
|
||||
const logoutButton = screen.getByTestId("logout-button");
|
||||
fireEvent.click(logoutButton);
|
||||
|
||||
expect(logoutCallbackCalled).toBe(true);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId("user-name")).toHaveTextContent(
|
||||
"Welcome, Anonymous user",
|
||||
);
|
||||
});
|
||||
|
||||
unmount();
|
||||
});
|
||||
|
||||
test("should reset nested co-state on logout", async () => {
|
||||
const syncServer = await startSyncServer();
|
||||
let logoutCallbackCalled = false;
|
||||
|
||||
render(
|
||||
<JazzReactProvider
|
||||
sync={{ peer: syncServer.url }}
|
||||
storage="indexedDB"
|
||||
AccountSchema={TestAccount}
|
||||
defaultProfileName="Anonymous user"
|
||||
>
|
||||
<TestLogoutComponent
|
||||
onLogout={() => {
|
||||
logoutCallbackCalled = true;
|
||||
}}
|
||||
/>
|
||||
</JazzReactProvider>,
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId("root-value")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
expect(screen.getByTestId("root-value")).toHaveTextContent("0");
|
||||
|
||||
const incrementButton = screen.getByTestId("increment-button");
|
||||
fireEvent.click(incrementButton);
|
||||
|
||||
expect(screen.getByTestId("root-value")).toHaveTextContent("1");
|
||||
|
||||
fireEvent.click(incrementButton);
|
||||
|
||||
expect(screen.getByTestId("root-value")).toHaveTextContent("2");
|
||||
|
||||
const logoutButton = screen.getByTestId("logout-button");
|
||||
fireEvent.click(logoutButton);
|
||||
|
||||
expect(logoutCallbackCalled).toBe(true);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId("root-value")).toHaveTextContent("0");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -9,7 +9,8 @@
|
||||
"sourceMap": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"outDir": "dist"
|
||||
"outDir": "dist",
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import react from "@vitejs/plugin-react-swc";
|
||||
import { defineProject } from "vitest/config";
|
||||
import { customCommands } from "./src/commands";
|
||||
|
||||
export default defineProject({
|
||||
plugins: [react()],
|
||||
test: {
|
||||
name: "browser-integration-tests",
|
||||
browser: {
|
||||
|
||||
Reference in New Issue
Block a user