Compare commits
130 Commits
jazz-bette
...
cojson-sto
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
748c2ff751 | ||
|
|
70938b0ab3 | ||
|
|
3c3acae803 | ||
|
|
9b9bf44e2b | ||
|
|
392aa88d95 | ||
|
|
7ce82cd934 | ||
|
|
0c8158b91c | ||
|
|
25c56146f5 | ||
|
|
12481e14c2 | ||
|
|
0ab4d7a20d | ||
|
|
88ebcf58ab | ||
|
|
f379a168be | ||
|
|
bde6ac7d45 | ||
|
|
d1609cdd55 | ||
|
|
b71ab3168a | ||
|
|
0c8f6e5039 | ||
|
|
6a93a1b8a3 | ||
|
|
9f654a2603 | ||
|
|
dbf735d9e1 | ||
|
|
c62abefb66 | ||
|
|
1453869a46 | ||
|
|
239da90c9f | ||
|
|
972791e7a8 | ||
|
|
0c0178764e | ||
|
|
928350b821 | ||
|
|
be3fd9c696 | ||
|
|
269c028df0 | ||
|
|
e4df837138 | ||
|
|
54fe6d93ba | ||
|
|
979689c6d8 | ||
|
|
859a37868f | ||
|
|
57bd32d77e | ||
|
|
e21cbccd4b | ||
|
|
a66ab7d174 | ||
|
|
78e91f4030 | ||
|
|
7a915c198e | ||
|
|
c9b0420746 | ||
|
|
2303f3e70a | ||
|
|
a7bc9569a3 | ||
|
|
f351ba0fcd | ||
|
|
d3e554f491 | ||
|
|
b5e31456ad | ||
|
|
42d07ba7b4 | ||
|
|
b81b6ba69b | ||
|
|
1bc1759bb4 | ||
|
|
512aacdbc2 | ||
|
|
7ad843aa3e | ||
|
|
fc027a56db | ||
|
|
959a7a3927 | ||
|
|
2548085b59 | ||
|
|
b27bb3e65b | ||
|
|
d1efde468f | ||
|
|
2b61e853a7 | ||
|
|
6f79b45544 | ||
|
|
2e1ff99579 | ||
|
|
ac782674de | ||
|
|
5eb406d54d | ||
|
|
a3be832414 | ||
|
|
7ca8dd960e | ||
|
|
62c8aff73f | ||
|
|
7731109a28 | ||
|
|
dfc4286694 | ||
|
|
970ff0d813 | ||
|
|
eee221f563 | ||
|
|
2283d375ef | ||
|
|
202e763380 | ||
|
|
52bbdb37a9 | ||
|
|
f5c47feeb6 | ||
|
|
b8b0851433 | ||
|
|
2bbb07b0bf | ||
|
|
d3053955d8 | ||
|
|
f40484eca9 | ||
|
|
d581a59aa1 | ||
|
|
0ca09f75c1 | ||
|
|
e8fcd101f2 | ||
|
|
cf43fa7529 | ||
|
|
df1cdda4e8 | ||
|
|
7a60d7bb76 | ||
|
|
f8263a8358 | ||
|
|
f6da966922 | ||
|
|
8a2ab51543 | ||
|
|
466e6c44ee | ||
|
|
5bd8277161 | ||
|
|
0ec917e453 | ||
|
|
6326d0fc45 | ||
|
|
d746b1279a | ||
|
|
c09dcdfc76 | ||
|
|
4402c553b6 | ||
|
|
e76fe343da | ||
|
|
a2626a0f38 | ||
|
|
ec579bcaf7 | ||
|
|
6b662b0efe | ||
|
|
e9af90c841 | ||
|
|
2b7c6f5aa7 | ||
|
|
d73a3d9d46 | ||
|
|
8af39077a3 | ||
|
|
54bd487818 | ||
|
|
a8b3ec7bb0 | ||
|
|
a420b43029 | ||
|
|
a57268de32 | ||
|
|
6b2c4ed280 | ||
|
|
8d4e0027be | ||
|
|
a4141da1b7 | ||
|
|
c9ca5202f9 | ||
|
|
7b50a2e06d | ||
|
|
43dabccb57 | ||
|
|
b6d04f56ef | ||
|
|
628195b678 | ||
|
|
9a5d769717 | ||
|
|
e30a3f66bf | ||
|
|
6327fce933 | ||
|
|
a650da4184 | ||
|
|
6e4a94f6ce | ||
|
|
b73bec64bc | ||
|
|
50ae2f47c2 | ||
|
|
724d8e7f30 | ||
|
|
7b285ab110 | ||
|
|
01ac9b8c4c | ||
|
|
4e2e1ac73e | ||
|
|
94960c1f65 | ||
|
|
b5af58347b | ||
|
|
46a84558c5 | ||
|
|
f93566c045 | ||
|
|
d97ed603a3 | ||
|
|
8d33103182 | ||
|
|
aaa1ff978b | ||
|
|
82655ea7a7 | ||
|
|
8afe3a2e02 | ||
|
|
ae2adcbd15 | ||
|
|
eb0460d330 |
@@ -1,5 +1,35 @@
|
||||
# passkey-svelte
|
||||
|
||||
## 0.0.108
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-tools@0.16.2
|
||||
|
||||
## 0.0.107
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [c62abef]
|
||||
- jazz-tools@0.16.1
|
||||
|
||||
## 0.0.106
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 2bbb07b: Introduce a cleaner separation between Zod and CoValue schemas:
|
||||
- Zod schemas and CoValue schemas are fully separated. Zod schemas can only be composed with other Zod schemas. CoValue schemas can be composed with either Zod or other CoValue schemas.
|
||||
- `z.optional()` and `z.discriminatedUnion()` no longer work with CoValue schemas. Use `co.optional()` and `co.discriminatedUnion()` instead.
|
||||
- Internal schema access is now simpler. You no longer need to use Zod’s `.def` to access internals. Use properties like `CoMapSchema.shape`, `CoListSchema.element`, and `CoOptionalSchema.innerType` directly.
|
||||
- CoValue schema types are now namespaced under `co.`. Non-namespaced exports have been removed
|
||||
- CoMap schemas no longer incorrectly inherit from Zod. Previously, methods like `.extend()` and `.partial()` appeared available but could cause unexpected behavior. These methods are now disabled. In their place, `.optional()` has been added, and more Zod-like methods will be introduced in future releases.
|
||||
- Upgraded Zod from `3.25.28` to `3.25.76`.
|
||||
- Removed deprecated `withHelpers` method from CoValue schemas
|
||||
- Removed deprecated `createCoValueObservable` function
|
||||
- Updated dependencies [c09dcdf]
|
||||
- Updated dependencies [2bbb07b]
|
||||
- jazz-tools@0.16.0
|
||||
|
||||
## 0.0.105
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "chat-svelte",
|
||||
"version": "0.0.105",
|
||||
"version": "0.0.108",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { co, z } from 'jazz-tools';
|
||||
import { co } from 'jazz-tools';
|
||||
|
||||
export const Message = co.map({
|
||||
text: co.plainText(),
|
||||
image: z.optional(co.image())
|
||||
image: co.optional(co.image())
|
||||
});
|
||||
|
||||
export const Chat = co.list(Message);
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"lucide-react": "^0.274.0",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"zod": "3.25.28"
|
||||
"zod": "3.25.76"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.50.1",
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
DraftBubbleTeaOrder,
|
||||
JazzAccount,
|
||||
ListOfBubbleTeaAddOns,
|
||||
validateDraftOrder,
|
||||
} from "./schema.ts";
|
||||
|
||||
export function CreateOrder() {
|
||||
@@ -22,8 +23,7 @@ export function CreateOrder() {
|
||||
if (!me?.root) return;
|
||||
|
||||
const onSave = (draft: Loaded<typeof DraftBubbleTeaOrder>) => {
|
||||
// validate if the draft is a valid order
|
||||
const validation = DraftBubbleTeaOrder.validate(draft);
|
||||
const validation = validateDraftOrder(draft);
|
||||
setErrors(validation.errors);
|
||||
if (validation.errors.length > 0) {
|
||||
return;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { useAccount } from "jazz-tools/react";
|
||||
import { DraftBubbleTeaOrder, JazzAccount } from "./schema";
|
||||
import { JazzAccount, hasChanges } from "./schema";
|
||||
export function DraftIndicator() {
|
||||
const { me } = useAccount(JazzAccount, {
|
||||
resolve: { root: { draft: true } },
|
||||
});
|
||||
|
||||
if (DraftBubbleTeaOrder.hasChanges(me?.root.draft)) {
|
||||
if (hasChanges(me?.root.draft)) {
|
||||
return (
|
||||
<div className="absolute -top-1 -right-1 bg-blue-500 border-2 border-white w-3 h-3 rounded-full dark:border-stone-925">
|
||||
<span className="sr-only">You have a draft</span>
|
||||
|
||||
@@ -15,13 +15,13 @@ export const BubbleTeaBaseTeaTypes = [
|
||||
"Thai",
|
||||
] as const;
|
||||
|
||||
export const ListOfBubbleTeaAddOns = co
|
||||
.list(z.literal([...BubbleTeaAddOnTypes]))
|
||||
.withHelpers((Self) => ({
|
||||
hasChanges(list?: Loaded<typeof Self> | null) {
|
||||
return list && Object.entries(list._raw.insertions).length > 0;
|
||||
},
|
||||
}));
|
||||
export const ListOfBubbleTeaAddOns = co.list(
|
||||
z.literal([...BubbleTeaAddOnTypes]),
|
||||
);
|
||||
|
||||
function hasAddOnsChanges(list?: Loaded<typeof ListOfBubbleTeaAddOns> | null) {
|
||||
return list && Object.entries(list._raw.insertions).length > 0;
|
||||
}
|
||||
|
||||
export const BubbleTeaOrder = co.map({
|
||||
baseTea: z.literal([...BubbleTeaBaseTeaTypes]),
|
||||
@@ -31,36 +31,33 @@ export const BubbleTeaOrder = co.map({
|
||||
instructions: co.optional(co.plainText()),
|
||||
});
|
||||
|
||||
export const DraftBubbleTeaOrder = co
|
||||
.map({
|
||||
baseTea: z.optional(z.literal([...BubbleTeaBaseTeaTypes])),
|
||||
addOns: co.optional(ListOfBubbleTeaAddOns),
|
||||
deliveryDate: z.optional(z.date()),
|
||||
withMilk: z.optional(z.boolean()),
|
||||
instructions: co.optional(co.plainText()),
|
||||
})
|
||||
.withHelpers((Self) => ({
|
||||
hasChanges(order: Loaded<typeof Self> | undefined) {
|
||||
return (
|
||||
!!order &&
|
||||
(Object.keys(order._edits).length > 1 ||
|
||||
ListOfBubbleTeaAddOns.hasChanges(order.addOns))
|
||||
);
|
||||
},
|
||||
export const DraftBubbleTeaOrder = co.map({
|
||||
baseTea: z.optional(z.literal([...BubbleTeaBaseTeaTypes])),
|
||||
addOns: co.optional(ListOfBubbleTeaAddOns),
|
||||
deliveryDate: z.optional(z.date()),
|
||||
withMilk: z.optional(z.boolean()),
|
||||
instructions: co.optional(co.plainText()),
|
||||
});
|
||||
|
||||
validate(order: Loaded<typeof Self>) {
|
||||
const errors: string[] = [];
|
||||
export function validateDraftOrder(order: Loaded<typeof DraftBubbleTeaOrder>) {
|
||||
const errors: string[] = [];
|
||||
|
||||
if (!order.baseTea) {
|
||||
errors.push("Please select your preferred base tea.");
|
||||
}
|
||||
if (!order.deliveryDate) {
|
||||
errors.push("Plese select a delivery date.");
|
||||
}
|
||||
if (!order.baseTea) {
|
||||
errors.push("Please select your preferred base tea.");
|
||||
}
|
||||
if (!order.deliveryDate) {
|
||||
errors.push("Plese select a delivery date.");
|
||||
}
|
||||
|
||||
return { errors };
|
||||
},
|
||||
}));
|
||||
return { errors };
|
||||
}
|
||||
|
||||
export function hasChanges(order?: Loaded<typeof DraftBubbleTeaOrder> | null) {
|
||||
return (
|
||||
!!order &&
|
||||
(Object.keys(order._edits).length > 1 || hasAddOnsChanges(order.addOns))
|
||||
);
|
||||
}
|
||||
|
||||
/** The root is an app-specific per-user private `CoMap`
|
||||
* where you can store top-level objects for that user */
|
||||
|
||||
@@ -11,9 +11,9 @@ export const Issue = co.map({
|
||||
status: z.enum(["open", "closed"]),
|
||||
labels: co.list(z.string()),
|
||||
reactions: ReactionsList,
|
||||
file: z.optional(co.fileStream()),
|
||||
image: z.optional(co.image()),
|
||||
lead: z.optional(co.account()),
|
||||
file: co.optional(co.fileStream()),
|
||||
image: co.optional(co.image()),
|
||||
lead: co.optional(co.account()),
|
||||
});
|
||||
|
||||
export const Project = co.map({
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
"jazz-tools": "workspace:*",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"zod": "3.25.28"
|
||||
"zod": "3.25.76"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.4",
|
||||
@@ -31,4 +31,4 @@
|
||||
"vite": "^6.3.5",
|
||||
"vitest": "3.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
JazzAccount,
|
||||
Organization,
|
||||
Project,
|
||||
validateDraftOrganization,
|
||||
} from "../schema.ts";
|
||||
import { Errors } from "./Errors.tsx";
|
||||
import { OrganizationForm } from "./OrganizationForm.tsx";
|
||||
@@ -21,8 +22,7 @@ export function CreateOrganization() {
|
||||
if (!me?.root?.organizations) return;
|
||||
|
||||
const onSave = (draft: Loaded<typeof DraftOrganization>) => {
|
||||
// validate if the draft is a valid organization
|
||||
const validation = DraftOrganization.validate(draft);
|
||||
const validation = validateDraftOrganization(draft);
|
||||
setErrors(validation.errors);
|
||||
if (validation.errors.length > 0) {
|
||||
return;
|
||||
|
||||
@@ -10,24 +10,24 @@ export const Organization = co.map({
|
||||
projects: co.list(Project),
|
||||
});
|
||||
|
||||
export const DraftOrganization = co
|
||||
.map({
|
||||
name: z.optional(z.string()),
|
||||
projects: co.list(Project),
|
||||
})
|
||||
.withHelpers((Self) => ({
|
||||
validate(org: Loaded<typeof Self>) {
|
||||
const errors: string[] = [];
|
||||
export const DraftOrganization = co.map({
|
||||
name: z.optional(z.string()),
|
||||
projects: co.list(Project),
|
||||
});
|
||||
|
||||
if (!org.name) {
|
||||
errors.push("Please enter a name.");
|
||||
}
|
||||
export function validateDraftOrganization(
|
||||
org: Loaded<typeof DraftOrganization>,
|
||||
) {
|
||||
const errors: string[] = [];
|
||||
|
||||
return {
|
||||
errors,
|
||||
};
|
||||
},
|
||||
}));
|
||||
if (!org.name) {
|
||||
errors.push("Please enter a name.");
|
||||
}
|
||||
|
||||
return {
|
||||
errors,
|
||||
};
|
||||
}
|
||||
|
||||
export const JazzAccountRoot = co.map({
|
||||
organizations: co.list(Organization),
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import type { NextConfig } from "next";
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
/* config options here */
|
||||
};
|
||||
const nextConfig: NextConfig = {};
|
||||
|
||||
export default nextConfig;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { jazzServerAccount } from "@/jazzServerAccount";
|
||||
import { Game, Player, PlayerState, createGameState } from "@/schema";
|
||||
import { Game, createGameState } from "@/schema";
|
||||
import { serverApi } from "@/serverApi";
|
||||
import { Account, Group, JazzRequestError } from "jazz-tools";
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ export type Game = co.loaded<typeof Game>;
|
||||
|
||||
export const WaitingRoom = co.map({
|
||||
creator: co.account(),
|
||||
game: z.optional(Game),
|
||||
game: co.optional(Game),
|
||||
});
|
||||
export type WaitingRoom = co.loaded<typeof WaitingRoom>;
|
||||
|
||||
|
||||
@@ -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>}
|
||||
|
||||
@@ -244,7 +244,7 @@ export function CreateOrder() {
|
||||
|
||||
In a `BubbleTeaOrder`, the `name` field is required, so it would be a good idea to validate this before turning the draft into a real order.
|
||||
|
||||
Update the schema to include a `validate` helper.
|
||||
Update the schema to include a `validateDraftOrder` helper.
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
@@ -253,17 +253,17 @@ import { co, z } from "jazz-tools";
|
||||
// schema.ts
|
||||
export const DraftBubbleTeaOrder = co.map({
|
||||
name: z.optional(z.string()),
|
||||
}).withHelpers((Self) => ({ // [!code ++:11]
|
||||
validate(draft: co.loaded<typeof Self>) {
|
||||
const errors: string[] = [];
|
||||
});
|
||||
|
||||
if (!draft.name) {
|
||||
errors.push("Please enter a name.");
|
||||
}
|
||||
export function validateDraftOrder(draft: co.loaded<typeof DraftBubbleTeaOrder>) { // [!code ++:9]
|
||||
const errors: string[] = [];
|
||||
|
||||
return { errors };
|
||||
},
|
||||
}));
|
||||
if (!draft.name) {
|
||||
errors.push("Please enter a name.");
|
||||
}
|
||||
|
||||
return { errors };
|
||||
};
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
@@ -282,17 +282,17 @@ export const BubbleTeaOrder = co.map({
|
||||
|
||||
export const DraftBubbleTeaOrder = co.map({
|
||||
name: z.optional(z.string()),
|
||||
}).withHelpers((Self) => ({
|
||||
validate(draft: co.loaded<typeof Self>) {
|
||||
const errors: string[] = [];
|
||||
});
|
||||
|
||||
if (!draft.name) {
|
||||
errors.push("Please enter a name.");
|
||||
}
|
||||
export function validateDraftOrder(draft: co.loaded<typeof DraftBubbleTeaOrder>) {
|
||||
const errors: string[] = [];
|
||||
|
||||
return { errors };
|
||||
},
|
||||
}));
|
||||
if (!draft.name) {
|
||||
errors.push("Please enter a name.");
|
||||
}
|
||||
|
||||
return { errors };
|
||||
};
|
||||
|
||||
export const AccountRoot = co.map({
|
||||
draft: DraftBubbleTeaOrder,
|
||||
@@ -340,7 +340,7 @@ export function CreateOrder() {
|
||||
e.preventDefault();
|
||||
if (!draft) return;
|
||||
|
||||
const validation = DraftBubbleTeaOrder.validate(draft); // [!code ++:5]
|
||||
const validation = validateDraftOrder(draft); // [!code ++:5]
|
||||
if (validation.errors.length > 0) {
|
||||
console.log(validation.errors);
|
||||
return;
|
||||
@@ -455,17 +455,17 @@ export const BubbleTeaOrder = co.map({
|
||||
|
||||
export const DraftBubbleTeaOrder = co.map({
|
||||
name: z.optional(z.string()),
|
||||
}).withHelpers((Self) => ({
|
||||
validate(draft: co.loaded<typeof Self>) {
|
||||
const errors: string[] = [];
|
||||
});
|
||||
|
||||
if (!draft.name) {
|
||||
errors.push("Please enter a name.");
|
||||
}
|
||||
export function validateDraftOrder(draft: co.loaded<typeof DraftBubbleTeaOrder>) {
|
||||
const errors: string[] = [];
|
||||
|
||||
return { errors };
|
||||
},
|
||||
}));
|
||||
if (!draft.name) {
|
||||
errors.push("Please enter a name.");
|
||||
}
|
||||
|
||||
return { errors };
|
||||
};
|
||||
|
||||
export const AccountRoot = co.map({
|
||||
draft: DraftBubbleTeaOrder,
|
||||
@@ -485,7 +485,7 @@ export const JazzAccount = co.account({
|
||||
// @filename: CreateOrder.tsx
|
||||
import * as React from "react";
|
||||
import { useCoState, useAccount } from "jazz-tools/react";
|
||||
import { BubbleTeaOrder, DraftBubbleTeaOrder, JazzAccount } from "schema";
|
||||
import { BubbleTeaOrder, DraftBubbleTeaOrder, JazzAccount, validateDraftOrder } from "schema";
|
||||
import { co } from "jazz-tools";
|
||||
|
||||
export function OrderForm({
|
||||
@@ -527,7 +527,7 @@ export function CreateOrder() {
|
||||
const draft = me.root.draft; // [!code ++:2]
|
||||
if (!draft) return;
|
||||
|
||||
const validation = DraftBubbleTeaOrder.validate(draft);
|
||||
const validation = validateDraftOrder(draft);
|
||||
if (validation.errors.length > 0) {
|
||||
console.log(validation.errors);
|
||||
return;
|
||||
@@ -579,21 +579,21 @@ import { co, z } from "jazz-tools";
|
||||
// schema.ts
|
||||
export const DraftBubbleTeaOrder = co.map({
|
||||
name: z.optional(z.string()),
|
||||
}).withHelpers((Self) => ({
|
||||
validate(draft: co.loaded<typeof Self>) {
|
||||
const errors: string[] = [];
|
||||
});
|
||||
|
||||
if (!draft.name) {
|
||||
errors.push("Plese enter a name.");
|
||||
}
|
||||
export function validateDraftOrder(draft: co.loaded<typeof DraftBubbleTeaOrder>) {
|
||||
const errors: string[] = [];
|
||||
|
||||
return { errors };
|
||||
},
|
||||
if (!draft.name) {
|
||||
errors.push("Please enter a name.");
|
||||
}
|
||||
|
||||
hasChanges(draft?: co.loaded<typeof Self>) { // [!code ++:3]
|
||||
return draft ? Object.keys(draft._edits).length : false;
|
||||
},
|
||||
}));
|
||||
return { errors };
|
||||
};
|
||||
|
||||
export function hasChanges(draft?: co.loaded<typeof DraftBubbleTeaOrder>) { // [!code ++:3]
|
||||
return draft ? Object.keys(draft._edits).length : false;
|
||||
};
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
@@ -611,21 +611,21 @@ export const BubbleTeaOrder = co.map({
|
||||
|
||||
export const DraftBubbleTeaOrder = co.map({
|
||||
name: z.optional(z.string()),
|
||||
}).withHelpers((Self) => ({
|
||||
validate(draft: co.loaded<typeof Self>) {
|
||||
const errors: string[] = [];
|
||||
});
|
||||
|
||||
if (!draft.name) {
|
||||
errors.push("Plese enter a name.");
|
||||
}
|
||||
export function validateDraftOrder(draft: co.loaded<typeof DraftBubbleTeaOrder>) {
|
||||
const errors: string[] = [];
|
||||
|
||||
return { errors };
|
||||
},
|
||||
if (!draft.name) {
|
||||
errors.push("Please enter a name.");
|
||||
}
|
||||
|
||||
hasChanges(draft?: co.loaded<typeof Self>) { // [!code ++:3]
|
||||
return draft ? Object.keys(draft._edits).length : false;
|
||||
},
|
||||
}));
|
||||
return { errors };
|
||||
};
|
||||
|
||||
export function hasChanges(draft?: co.loaded<typeof DraftBubbleTeaOrder>) {
|
||||
return draft ? Object.keys(draft._edits).length : false;
|
||||
};
|
||||
|
||||
export const AccountRoot = co.map({
|
||||
draft: DraftBubbleTeaOrder,
|
||||
@@ -649,7 +649,7 @@ export function DraftIndicator() {
|
||||
resolve: { root: { draft: true } },
|
||||
});
|
||||
|
||||
if (DraftBubbleTeaOrder.hasChanges(me?.root.draft)) {
|
||||
if (hasChanges(me?.root.draft)) {
|
||||
return (
|
||||
<p>You have a draft</p>
|
||||
);
|
||||
|
||||
@@ -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(),
|
||||
});
|
||||
|
||||
|
||||
@@ -109,6 +109,11 @@ export const docNavigationItems = [
|
||||
// collapse: true,
|
||||
prefix: "/docs/upgrade",
|
||||
items: [
|
||||
{
|
||||
name: "0.16.0 - Cleaner separation between Zod and CoValue schemas",
|
||||
href: "/docs/upgrade/0-16-0",
|
||||
done: 100,
|
||||
},
|
||||
{
|
||||
name: "0.15.0 - Everything inside `jazz-tools`",
|
||||
href: "/docs/upgrade/0-15-0",
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ export const MyAppRoot = co.map({
|
||||
|
||||
export const MyAppProfile = co.profile({ // [!code ++:4]
|
||||
name: z.string(), // compatible with default Profile schema
|
||||
avatar: z.optional(co.image()),
|
||||
avatar: co.optional(co.image()),
|
||||
});
|
||||
|
||||
export const MyAppAccount = co.account({
|
||||
@@ -241,7 +241,7 @@ const MyAppProfile = co.profile({
|
||||
// ---cut---
|
||||
const MyAppRoot = co.map({
|
||||
myChats: co.list(Chat),
|
||||
myBookmarks: z.optional(co.list(Bookmark)), // [!code ++:1]
|
||||
myBookmarks: co.optional(co.list(Bookmark)), // [!code ++:1]
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -320,6 +320,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 +335,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>
|
||||
@@ -353,16 +358,16 @@ const Person = co.map({
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
You can use the same technique for mutually recursive references, but you'll need to help TypeScript along:
|
||||
You can use the same technique for mutually recursive references:
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
// ---cut---
|
||||
import { co, z, CoListSchema } from "jazz-tools";
|
||||
import { co, z } from "jazz-tools";
|
||||
|
||||
const Person = co.map({
|
||||
name: z.string(),
|
||||
get friends(): CoListSchema<typeof Person> {
|
||||
get friends() {
|
||||
return ListOfPeople;
|
||||
}
|
||||
});
|
||||
@@ -372,22 +377,6 @@ const ListOfPeople = co.list(Person);
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
Note: similarly, if you use modifiers like `co.optional()` you'll need to help TypeScript along:
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
import { co, z } from "jazz-tools";
|
||||
// ---cut---
|
||||
const Person = co.map({
|
||||
name: z.string(),
|
||||
get bestFriend(): z.ZodOptional<typeof Person> {
|
||||
return co.optional(Person);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
### Helper methods
|
||||
|
||||
If you find yourself repeating the same logic to access computed CoValues properties,
|
||||
|
||||
@@ -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.
|
||||
|
||||
158
homepage/homepage/content/docs/upgrade/0-16-0.mdx
Normal file
158
homepage/homepage/content/docs/upgrade/0-16-0.mdx
Normal file
@@ -0,0 +1,158 @@
|
||||
import { CodeGroup } from '@/components/forMdx'
|
||||
|
||||
# Jazz 0.16.0 - Cleaner separation between Zod and CoValue schemas
|
||||
|
||||
This release introduces a cleaner separation between Zod and CoValue schemas, improves type inference with circular references, and simplifies how you access internal schemas.
|
||||
While most applications won't require extensive refactors, some breaking changes will require action.
|
||||
|
||||
## Motivation
|
||||
|
||||
Before 0.16.0, CoValue schemas were a thin wrapper around Zod schemas. This made it easy to use Zod methods on CoValue schemas,
|
||||
but it also prevented the type checker from detecting issues when combining Zod and CoValue schemas.
|
||||
|
||||
For example, the following code would previously compile without errors, but would have severe limitations:
|
||||
|
||||
<CodeGroup>
|
||||
```tsx
|
||||
import { co, z } from "jazz-tools";
|
||||
|
||||
const Dog = co.map({
|
||||
breed: z.string(),
|
||||
});
|
||||
const Person = co.map({
|
||||
pets: z.array(Dog),
|
||||
});
|
||||
|
||||
// You can create a CoMap with a z.array field that contains another CoMap
|
||||
const map = Person.create({
|
||||
pets: [Dog.create({ breed: "Labrador" })],
|
||||
});
|
||||
|
||||
// But then you cannot eagerly load the nested CoMap, because
|
||||
// there's a plain JS object in between. So this would fail:
|
||||
Person.load(map.id, { resolve: { pets: { $each: true } } });
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
Schema composition rules are now stricter: Zod schemas can only be composed with other Zod schemas.
|
||||
CoValue schemas can be composed with either Zod or other CoValue schemas. These rules are enforced at the type level, to make it easier
|
||||
to spot errors in schema definitions and avoid possible footguns when mixing Zod and CoValue schemas.
|
||||
|
||||
Having a stricter separation between Zod and CoValue schemas also allowed us to improve type inference with circular references.
|
||||
Previously, the type checker would not be able to infer types for even simple circular references, but now it can!
|
||||
<CodeGroup>
|
||||
```tsx
|
||||
import { co, z } from "jazz-tools";
|
||||
|
||||
const Person = co.map({
|
||||
name: z.string(),
|
||||
get friends(): CoListSchema<typeof Person> { // [!code --]
|
||||
get friends() { // [!code ++]
|
||||
return co.list(Person);
|
||||
},
|
||||
});
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
There are some scenarios where recursive type inference can still fail due to TypeScript limitations, but these should be rare.
|
||||
|
||||
## Breaking changes
|
||||
|
||||
### The Account root id is now discoverable
|
||||
|
||||
In prior Jazz releases, the Account root id was stored encrypted and accessible only by the account owner.
|
||||
|
||||
This made it impossible to load the account root this way:
|
||||
|
||||
<CodeGroup>
|
||||
```tsx
|
||||
const bob = MyAppAccount.load(bobId, { resolve: { root: true }, loadAs: me });
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
So we changed Account root id to be discoverable by everyone.
|
||||
**This doesn't affect the visibility of the account root**, which still follows the permissions defined in its group.
|
||||
|
||||
For existing accounts, the change is applied the next time the user loads their account.
|
||||
|
||||
No action is required on your side, but we preferred to mark this as a breaking change because it
|
||||
minimally affects access to the account root. (e.g., if in your app the root is public, now users can access other users' root by knowing their account ID)
|
||||
|
||||
### `z.optional()` and `z.discriminatedUnion()` no longer work with CoValue schemas
|
||||
|
||||
You'll now need to use the `co.optional()` and `co.discriminatedUnion()` equivalents.
|
||||
This change may require you to update any explicitly typed cyclic references.
|
||||
|
||||
<CodeGroup>
|
||||
```tsx
|
||||
import { co, z } from "jazz-tools";
|
||||
|
||||
const Person = co.map({
|
||||
name: z.string(),
|
||||
get bestFriend(): z.ZodOptional<typeof Person> { // [!code --]
|
||||
return z.optional(Person); // [!code --]
|
||||
get bestFriend(): co.Optional<typeof Person> { // [!code ++]
|
||||
return co.optional(Person); // [!code ++]
|
||||
}
|
||||
});
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### CoValue schema types are now under the `co.` namespace
|
||||
|
||||
All CoValue schema types are now accessed via the `co.` namespace. If you're using explicit types (especially in recursive schemas), you'll need to update them accordingly.
|
||||
|
||||
<CodeGroup>
|
||||
```tsx
|
||||
import { co, z } from "jazz-tools";
|
||||
|
||||
const Person = co.map({
|
||||
name: z.string(),
|
||||
get friends(): CoListSchema<typeof Person> { // [!code --]
|
||||
get friends(): co.List<typeof Person> { // [!code ++]
|
||||
return co.list(Person);
|
||||
}
|
||||
});
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### Unsupported Zod methods have been removed from CoMap schemas
|
||||
|
||||
CoMap schemas no longer incorrectly inherit Zod methods like `.extend()` and `.partial()`. These methods previously appeared to work but could behave unpredictably. They have now been disabled.
|
||||
|
||||
We're keeping `.optional()` and plan to introduce more Zod-like methods in future releases.
|
||||
|
||||
### Internal schema access is now simpler
|
||||
|
||||
You no longer need to use Zod's `.def` to access schema internals. Instead, you can directly use methods like `CoMapSchema.shape`, `CoListSchema.element`, and `CoOptionalSchema.innerType`.
|
||||
|
||||
<CodeGroup>
|
||||
```tsx
|
||||
import { co, z } from "jazz-tools";
|
||||
|
||||
const Message = co.map({
|
||||
content: co.richText(),
|
||||
});
|
||||
|
||||
const Thread = co.map({
|
||||
messages: co.list(Message),
|
||||
});
|
||||
|
||||
const thread = Thread.create({
|
||||
messages: Thread.def.shape.messages.create([ // [!code --]
|
||||
messages: Thread.shape.messages.create([ // [!code ++]
|
||||
Message.create({
|
||||
content: co.richText().create("Hi!"),
|
||||
}),
|
||||
Message.create({
|
||||
content: co.richText().create("What's up?"),
|
||||
}),
|
||||
]),
|
||||
});
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### Removed the deprecated `withHelpers` method from CoValue schemas
|
||||
|
||||
The deprecated `withHelpers()` method has been removed from CoValue schemas. You can define helper functions manually to encapsulate CoValue-related logic.
|
||||
[Learn how to define helper methods](https://jazz.tools/docs/vanilla/schemas/covalues#helper-methods).
|
||||
@@ -343,14 +343,14 @@ CoLists can be used to create one-to-many relationships:
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
import { co, z, CoListSchema } from "jazz-tools";
|
||||
import { co, z } from "jazz-tools";
|
||||
|
||||
const Task = co.map({
|
||||
title: z.string(),
|
||||
status: z.literal(["todo", "in-progress", "complete"]),
|
||||
|
||||
get project(): z.ZodOptional<typeof Project> {
|
||||
return z.optional(Project);
|
||||
get project(): co.Optional<typeof Project> {
|
||||
return co.optional(Project);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -359,7 +359,7 @@ const ListOfTasks = co.list(Task);
|
||||
const Project = co.map({
|
||||
name: z.string(),
|
||||
|
||||
get tasks(): CoListSchema<typeof Task> {
|
||||
get tasks(): co.List<typeof Task> {
|
||||
return ListOfTasks;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -213,14 +213,14 @@ const Member = co.map({
|
||||
name: z.string(),
|
||||
});
|
||||
// ---cut---
|
||||
import { co, z, CoListSchema } from "jazz-tools";
|
||||
import { co, z } from "jazz-tools";
|
||||
|
||||
const Project = co.map({
|
||||
name: z.string(),
|
||||
startDate: z.date(),
|
||||
status: z.literal(["planning", "active", "completed"]),
|
||||
coordinator: co.optional(Member),
|
||||
get subProjects(): z.ZodOptional<CoListSchema<typeof Project>> {
|
||||
get subProjects(): co.Optional<co.List<typeof Project>> {
|
||||
return co.optional(co.list(Project));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -50,51 +50,3 @@ export type User = co.loaded<typeof User>;
|
||||
This direct linking approach offers a single source of truth. When you update a referenced CoValue, all other CoValues that point to it are automatically updated, ensuring data consistency across your application.
|
||||
|
||||
By connecting CoValues through these direct references, you can build robust and collaborative applications where data is consistent, efficient to manage, and relationships are clearly defined. The ability to link different CoValue types to the same underlying data is fundamental to building complex applications with Jazz.
|
||||
|
||||
|
||||
## Recursive references with DiscriminatedUnion
|
||||
In advanced schemas, you may want a CoValue that recursively references itself. For example, a `ReferenceItem` that contains a list of other items like `NoteItem` or `AttachmentItem`. This is common in tree-like structures such as threaded comments or nested project outlines.
|
||||
|
||||
You can model this with a Zod `z.discriminatedUnion`, but TypeScript’s type inference doesn't handle recursive unions well without a workaround.
|
||||
|
||||
Here’s how to structure your schema to avoid circular reference errors.
|
||||
|
||||
### Use this pattern for recursive discriminated unions
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
import { CoListSchema, co, z } from "jazz-tools";
|
||||
|
||||
// Recursive item modeling pattern using discriminated unions
|
||||
// First, define the non-recursive types
|
||||
export const NoteItem = co.map({
|
||||
type: z.literal("note"),
|
||||
internal: z.boolean(),
|
||||
content: co.plainText(),
|
||||
});
|
||||
|
||||
export const AttachmentItem = co.map({
|
||||
type: z.literal("attachment"),
|
||||
internal: z.boolean(),
|
||||
content: co.fileStream(),
|
||||
});
|
||||
|
||||
export const ReferenceItem = co.map({
|
||||
type: z.literal("reference"),
|
||||
internal: z.boolean(),
|
||||
content: z.string(),
|
||||
|
||||
// Workaround: declare the field type using CoListSchema and ZodDiscriminatedUnion so TS can safely recurse
|
||||
get children(): CoListSchema<z.ZodDiscriminatedUnion<[typeof NoteItem, typeof AttachmentItem, typeof ReferenceItem]>> {
|
||||
return ProjectContextItemList;
|
||||
},
|
||||
});
|
||||
|
||||
// Create the recursive union
|
||||
export const ProjectContextItem = z.discriminatedUnion("type", [NoteItem, AttachmentItem, ReferenceItem]);
|
||||
|
||||
// Final list of recursive types
|
||||
export const ProjectContextItemList = co.list(ProjectContextItem);
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
Even though this seems like a shortcut, TypeScript and Zod can't resolve the circular reference this way. Always define the discriminated union before introducing recursive links.
|
||||
|
||||
@@ -24,7 +24,7 @@ import { Group, co, z } from "jazz-tools";
|
||||
|
||||
const MyProfile = co.profile({
|
||||
name: z.string(),
|
||||
image: z.optional(co.image()),
|
||||
image: co.optional(co.image()),
|
||||
});
|
||||
|
||||
const MyAccount = co.account({
|
||||
|
||||
@@ -25,7 +25,7 @@ import { Group, co, z } from "jazz-tools";
|
||||
|
||||
const MyProfile = co.profile({
|
||||
name: z.string(),
|
||||
image: z.optional(co.image()),
|
||||
image: co.optional(co.image()),
|
||||
});
|
||||
|
||||
const MyAccount = co.account({
|
||||
|
||||
@@ -249,7 +249,7 @@ Resolve queries let you declare exactly which references to load and how deep to
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
import { co, z, CoListSchema } from "jazz-tools";
|
||||
import { co, z } from "jazz-tools";
|
||||
const projectId = "co_123";
|
||||
|
||||
// ---cut-before---
|
||||
@@ -259,8 +259,8 @@ const TeamMember = co.map({
|
||||
|
||||
const Task = co.map({
|
||||
title: z.string(),
|
||||
assignee: z.optional(TeamMember),
|
||||
get subtasks(): CoListSchema<typeof Task> { return co.list(Task) },
|
||||
assignee: co.optional(TeamMember),
|
||||
get subtasks(): co.List<typeof Task> { return co.list(Task) },
|
||||
});
|
||||
|
||||
const Project = co.map({
|
||||
@@ -349,7 +349,7 @@ When a user tries to load a reference they don't have access to:
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
import { co, z, CoListSchema } from "jazz-tools";
|
||||
import { co, z } from "jazz-tools";
|
||||
|
||||
const TeamMember = co.map({
|
||||
name: z.string(),
|
||||
@@ -357,8 +357,8 @@ const TeamMember = co.map({
|
||||
|
||||
const Task = co.map({
|
||||
title: z.string(),
|
||||
assignee: z.optional(TeamMember),
|
||||
get subtasks(): CoListSchema<typeof Task> { return co.list(Task) },
|
||||
assignee: co.optional(TeamMember),
|
||||
get subtasks(): co.List<typeof Task> { return co.list(Task) },
|
||||
});
|
||||
|
||||
const Project = co.map({
|
||||
@@ -388,7 +388,7 @@ When a list contains references to items the user can't access:
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
import { co, z, CoListSchema } from "jazz-tools";
|
||||
import { co, z } from "jazz-tools";
|
||||
|
||||
const TeamMember = co.map({
|
||||
name: z.string(),
|
||||
@@ -396,8 +396,8 @@ const TeamMember = co.map({
|
||||
|
||||
const Task = co.map({
|
||||
title: z.string(),
|
||||
assignee: z.optional(TeamMember),
|
||||
get subtasks(): CoListSchema<typeof Task> { return co.list(Task) },
|
||||
assignee: co.optional(TeamMember),
|
||||
get subtasks(): co.List<typeof Task> { return co.list(Task) },
|
||||
});
|
||||
|
||||
const Project = co.map({
|
||||
@@ -424,7 +424,7 @@ When trying to load an object with an inaccessible reference without directly re
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
import { co, z, CoListSchema } from "jazz-tools";
|
||||
import { co, z } from "jazz-tools";
|
||||
|
||||
const TeamMember = co.map({
|
||||
name: z.string(),
|
||||
@@ -432,8 +432,8 @@ const TeamMember = co.map({
|
||||
|
||||
const Task = co.map({
|
||||
title: z.string(),
|
||||
assignee: z.optional(TeamMember),
|
||||
get subtasks(): CoListSchema<typeof Task> { return co.list(Task) },
|
||||
assignee: co.optional(TeamMember),
|
||||
get subtasks(): co.List<typeof Task> { return co.list(Task) },
|
||||
});
|
||||
|
||||
const Project = co.map({
|
||||
@@ -468,7 +468,7 @@ This way the inaccessible items are replaced with `null` in the returned list.
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
import { co, z, CoListSchema, Group } from "jazz-tools";
|
||||
import { co, z, Group } from "jazz-tools";
|
||||
import { createJazzTestAccount } from "jazz-tools/testing";
|
||||
|
||||
const me = await createJazzTestAccount();
|
||||
@@ -653,7 +653,7 @@ The `co.loaded` type is especially useful when passing data between components,
|
||||
<ContentByFramework framework="react">
|
||||
<CodeGroup>
|
||||
```tsx twoslash
|
||||
import { CoListSchema, co, z } from "jazz-tools";
|
||||
import { co, z } from "jazz-tools";
|
||||
import React from "react";
|
||||
|
||||
const TeamMember = co.map({
|
||||
@@ -662,8 +662,8 @@ const TeamMember = co.map({
|
||||
|
||||
const Task = co.map({
|
||||
title: z.string(),
|
||||
assignee: z.optional(TeamMember),
|
||||
get subtasks(): CoListSchema<typeof Task> {
|
||||
assignee: co.optional(TeamMember),
|
||||
get subtasks(): co.List<typeof Task> {
|
||||
return co.list(Task);
|
||||
},
|
||||
});
|
||||
@@ -727,7 +727,7 @@ function processProject(project: FullyLoadedProject) {
|
||||
<ContentByFramework framework="vanilla">
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
import { CoListSchema, co, z } from "jazz-tools";
|
||||
import { co, z } from "jazz-tools";
|
||||
|
||||
const TeamMember = co.map({
|
||||
name: z.string(),
|
||||
@@ -735,8 +735,8 @@ const TeamMember = co.map({
|
||||
|
||||
const Task = co.map({
|
||||
title: z.string(),
|
||||
assignee: z.optional(TeamMember),
|
||||
get subtasks(): CoListSchema<typeof Task> {
|
||||
assignee: co.optional(TeamMember),
|
||||
get subtasks(): co.List<typeof Task> {
|
||||
return co.list(Task);
|
||||
},
|
||||
});
|
||||
@@ -799,7 +799,7 @@ Sometimes you need to make sure data is loaded before proceeding with an operati
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
import { CoListSchema, co, z } from "jazz-tools";
|
||||
import { co, z } from "jazz-tools";
|
||||
|
||||
const TeamMember = co.map({
|
||||
name: z.string(),
|
||||
@@ -809,7 +809,7 @@ const Task = co.map({
|
||||
title: z.string(),
|
||||
status: z.literal(["todo", "in-progress", "completed"]),
|
||||
assignee: z.string().optional(),
|
||||
get subtasks(): CoListSchema<typeof Task> {
|
||||
get subtasks(): co.List<typeof Task> {
|
||||
return co.list(Task);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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,24 @@
|
||||
# cojson-storage-indexeddb
|
||||
|
||||
## 0.16.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cojson@0.16.2
|
||||
|
||||
## 0.16.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cojson@0.16.1
|
||||
|
||||
## 0.16.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [c09dcdf]
|
||||
- cojson@0.16.0
|
||||
|
||||
## 0.15.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cojson-storage-indexeddb",
|
||||
"version": "0.15.16",
|
||||
"version": "0.16.2",
|
||||
"main": "dist/index.js",
|
||||
"type": "module",
|
||||
"types": "dist/index.d.ts",
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
# cojson-storage-sqlite
|
||||
|
||||
## 0.16.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cojson@0.16.2
|
||||
|
||||
## 0.16.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cojson@0.16.1
|
||||
|
||||
## 0.16.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [c09dcdf]
|
||||
- cojson@0.16.0
|
||||
|
||||
## 0.15.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "cojson-storage-sqlite",
|
||||
"type": "module",
|
||||
"version": "0.15.16",
|
||||
"version": "0.16.2",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
# cojson-transport-nodejs-ws
|
||||
|
||||
## 0.16.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cojson@0.16.2
|
||||
|
||||
## 0.16.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cojson@0.16.1
|
||||
|
||||
## 0.16.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [c09dcdf]
|
||||
- cojson@0.16.0
|
||||
|
||||
## 0.15.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "cojson-transport-ws",
|
||||
"type": "module",
|
||||
"version": "0.15.16",
|
||||
"version": "0.16.2",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# cojson
|
||||
|
||||
## 0.16.2
|
||||
|
||||
## 0.16.1
|
||||
|
||||
## 0.16.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- c09dcdf: Change the root attribute to be public on Account. The root content will still follow the visiblity rules specified in their group.
|
||||
|
||||
Existing accounts will be gradually migrated as they are loaded.
|
||||
|
||||
## 0.15.16
|
||||
|
||||
## 0.15.15
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
},
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"version": "0.15.16",
|
||||
"version": "0.16.2",
|
||||
"devDependencies": {
|
||||
"@opentelemetry/sdk-metrics": "^2.0.0",
|
||||
"libsql": "^0.5.13",
|
||||
|
||||
@@ -50,6 +50,7 @@ export type DecryptedTransaction = {
|
||||
txID: TransactionID;
|
||||
changes: JsonValue[];
|
||||
madeAt: number;
|
||||
trusting?: boolean;
|
||||
};
|
||||
|
||||
const readKeyCache = new WeakMap<CoValueCore, { [id: KeyID]: KeySecret }>();
|
||||
@@ -657,6 +658,7 @@ export class CoValueCore {
|
||||
txID,
|
||||
madeAt: tx.madeAt,
|
||||
changes: parseJSON(tx.changes),
|
||||
trusting: true,
|
||||
});
|
||||
} catch (e) {
|
||||
logger.error("Failed to parse trusting transaction on " + this.id, {
|
||||
|
||||
@@ -16,6 +16,7 @@ type MapOp<K extends string, V extends JsonValue | undefined> = {
|
||||
madeAt: number;
|
||||
changeIdx: number;
|
||||
change: MapOpPayload<K, V>;
|
||||
trusting?: boolean;
|
||||
};
|
||||
// TODO: add after TransactionID[] for conflicts/ordering
|
||||
|
||||
@@ -112,7 +113,7 @@ export class RawCoMapView<
|
||||
NonNullable<(typeof ops)[keyof typeof ops]>
|
||||
>();
|
||||
|
||||
for (const { txID, changes, madeAt } of newValidTransactions) {
|
||||
for (const { txID, changes, madeAt, trusting } of newValidTransactions) {
|
||||
if (madeAt > this.latestTxMadeAt) {
|
||||
this.latestTxMadeAt = madeAt;
|
||||
}
|
||||
@@ -127,6 +128,7 @@ export class RawCoMapView<
|
||||
madeAt,
|
||||
changeIdx,
|
||||
change,
|
||||
trusting,
|
||||
};
|
||||
|
||||
const entries = ops[change.key];
|
||||
|
||||
@@ -63,7 +63,7 @@ import { DisconnectedError, SyncManager, emptyKnownState } from "./sync.js";
|
||||
|
||||
type Value = JsonValue | AnyRawCoValue;
|
||||
|
||||
export { PriorityBasedMessageQueue } from "./PriorityBasedMessageQueue.js";
|
||||
export { PriorityBasedMessageQueue } from "./queue/PriorityBasedMessageQueue.js";
|
||||
import { getDependedOnCoValuesFromRawData } from "./coValueCore/utils.js";
|
||||
import {
|
||||
CO_VALUE_LOADING_CONFIG,
|
||||
|
||||
@@ -313,6 +313,15 @@ export class LocalNode {
|
||||
throw new Error("Account has no profile");
|
||||
}
|
||||
|
||||
const rootID = account.get("root");
|
||||
if (rootID) {
|
||||
const rawEntry = account.getRaw("root");
|
||||
|
||||
if (!rawEntry?.trusting) {
|
||||
account.set("root", rootID, "trusting");
|
||||
}
|
||||
}
|
||||
|
||||
// Preload the profile
|
||||
await node.load(profileID);
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { CoID } from "./coValue.js";
|
||||
import { CoValueCore } from "./coValueCore/coValueCore.js";
|
||||
import { Transaction } from "./coValueCore/verifiedState.js";
|
||||
import { RawAccount, RawAccountID, RawProfile } from "./coValues/account.js";
|
||||
import { MapOpPayload } from "./coValues/coMap.js";
|
||||
import { MapOpPayload, RawCoMap } from "./coValues/coMap.js";
|
||||
import {
|
||||
EVERYONE,
|
||||
Everyone,
|
||||
@@ -270,6 +270,7 @@ function determineValidTransactionsForGroup(
|
||||
| MapOpPayload<RawAccountID | AgentID | Everyone, Role>
|
||||
| MapOpPayload<"readKey", JsonValue>
|
||||
| MapOpPayload<"profile", CoID<RawProfile>>
|
||||
| MapOpPayload<"root", CoID<RawCoMap>>
|
||||
| MapOpPayload<`parent_${CoID<RawGroup>}`, CoID<RawGroup>>
|
||||
| MapOpPayload<`child_${CoID<RawGroup>}`, CoID<RawGroup>>;
|
||||
|
||||
@@ -297,6 +298,14 @@ function determineValidTransactionsForGroup(
|
||||
continue;
|
||||
}
|
||||
|
||||
validTransactions.push({ txID: { sessionID, txIndex }, tx });
|
||||
continue;
|
||||
} else if (change.key === "root") {
|
||||
if (memberState[transactor] !== "admin") {
|
||||
logPermissionError("Only admins can set root");
|
||||
continue;
|
||||
}
|
||||
|
||||
validTransactions.push({ txID: { sessionID, txIndex }, tx });
|
||||
continue;
|
||||
} else if (
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Counter, ValueType, metrics } from "@opentelemetry/api";
|
||||
import type { PeerState } from "./PeerState.js";
|
||||
import { LinkedList } from "./PriorityBasedMessageQueue.js";
|
||||
import { SYNC_SCHEDULER_CONFIG } from "./config.js";
|
||||
import { logger } from "./logger.js";
|
||||
import type { SyncMessage } from "./sync.js";
|
||||
import type { PeerState } from "../PeerState.js";
|
||||
import { SYNC_SCHEDULER_CONFIG } from "../config.js";
|
||||
import { logger } from "../logger.js";
|
||||
import type { SyncMessage } from "../sync.js";
|
||||
import { LinkedList } from "./LinkedList.js";
|
||||
|
||||
/**
|
||||
* A queue that schedules messages across different peers using a round-robin approach.
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Counter, ValueType, metrics } from "@opentelemetry/api";
|
||||
import { CO_VALUE_PRIORITY, type CoValuePriority } from "./priority.js";
|
||||
import type { SyncMessage } from "./sync.js";
|
||||
import type { SyncMessage } from "../sync.js";
|
||||
|
||||
/**
|
||||
* Since we have a fixed range of priority values (0-7) we can create a fixed array of queues.
|
||||
@@ -10,18 +9,16 @@ type Tuple<T, N extends number, A extends unknown[] = []> = A extends {
|
||||
}
|
||||
? A
|
||||
: Tuple<T, N, [...A, T]>;
|
||||
|
||||
type QueueTuple = Tuple<LinkedList<SyncMessage>, 3>;
|
||||
|
||||
export type QueueTuple = Tuple<LinkedList<SyncMessage>, 3>;
|
||||
type LinkedListNode<T> = {
|
||||
value: T;
|
||||
next: LinkedListNode<T> | undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* Using a linked list to make the shift operation O(1) instead of O(n)
|
||||
* as our queues can grow very large when the system is under pressure.
|
||||
*/
|
||||
|
||||
export class LinkedList<T> {
|
||||
constructor(private meter?: QueueMeter) {}
|
||||
|
||||
@@ -70,7 +67,6 @@ export class LinkedList<T> {
|
||||
return this.head === undefined;
|
||||
}
|
||||
}
|
||||
|
||||
class QueueMeter {
|
||||
private pullCounter: Counter;
|
||||
private pushCounter: Counter;
|
||||
@@ -111,52 +107,9 @@ class QueueMeter {
|
||||
this.pushCounter.add(1, this.attrs);
|
||||
}
|
||||
}
|
||||
|
||||
function meteredList<T>(
|
||||
export function meteredList<T>(
|
||||
type: "incoming" | "outgoing",
|
||||
attrs?: Record<string, string | number>,
|
||||
) {
|
||||
return new LinkedList<T>(new QueueMeter("jazz.messagequeue." + type, attrs));
|
||||
}
|
||||
|
||||
const PRIORITY_TO_QUEUE_INDEX = {
|
||||
[CO_VALUE_PRIORITY.HIGH]: 0,
|
||||
[CO_VALUE_PRIORITY.MEDIUM]: 1,
|
||||
[CO_VALUE_PRIORITY.LOW]: 2,
|
||||
} as const;
|
||||
|
||||
export class PriorityBasedMessageQueue {
|
||||
private queues: QueueTuple;
|
||||
|
||||
constructor(
|
||||
private defaultPriority: CoValuePriority,
|
||||
type: "incoming" | "outgoing",
|
||||
/**
|
||||
* Optional attributes to be added to the generated metrics.
|
||||
* By default the metrics will have the priority as an attribute.
|
||||
*/
|
||||
attrs?: Record<string, string | number>,
|
||||
) {
|
||||
this.queues = [
|
||||
meteredList(type, { priority: CO_VALUE_PRIORITY.HIGH, ...attrs }),
|
||||
meteredList(type, { priority: CO_VALUE_PRIORITY.MEDIUM, ...attrs }),
|
||||
meteredList(type, { priority: CO_VALUE_PRIORITY.LOW, ...attrs }),
|
||||
];
|
||||
}
|
||||
|
||||
private getQueue(priority: CoValuePriority) {
|
||||
return this.queues[PRIORITY_TO_QUEUE_INDEX[priority]];
|
||||
}
|
||||
|
||||
public push(msg: SyncMessage) {
|
||||
const priority = "priority" in msg ? msg.priority : this.defaultPriority;
|
||||
|
||||
this.getQueue(priority).push(msg);
|
||||
}
|
||||
|
||||
public pull() {
|
||||
const priority = this.queues.findIndex((queue) => queue.length > 0);
|
||||
|
||||
return this.queues[priority]?.shift();
|
||||
}
|
||||
}
|
||||
45
packages/cojson/src/queue/PriorityBasedMessageQueue.ts
Normal file
45
packages/cojson/src/queue/PriorityBasedMessageQueue.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { CO_VALUE_PRIORITY, type CoValuePriority } from "../priority.js";
|
||||
import type { SyncMessage } from "../sync.js";
|
||||
import { QueueTuple, meteredList } from "./LinkedList.js";
|
||||
|
||||
const PRIORITY_TO_QUEUE_INDEX = {
|
||||
[CO_VALUE_PRIORITY.HIGH]: 0,
|
||||
[CO_VALUE_PRIORITY.MEDIUM]: 1,
|
||||
[CO_VALUE_PRIORITY.LOW]: 2,
|
||||
} as const;
|
||||
|
||||
export class PriorityBasedMessageQueue {
|
||||
private queues: QueueTuple;
|
||||
|
||||
constructor(
|
||||
private defaultPriority: CoValuePriority,
|
||||
type: "incoming" | "outgoing",
|
||||
/**
|
||||
* Optional attributes to be added to the generated metrics.
|
||||
* By default the metrics will have the priority as an attribute.
|
||||
*/
|
||||
attrs?: Record<string, string | number>,
|
||||
) {
|
||||
this.queues = [
|
||||
meteredList(type, { priority: CO_VALUE_PRIORITY.HIGH, ...attrs }),
|
||||
meteredList(type, { priority: CO_VALUE_PRIORITY.MEDIUM, ...attrs }),
|
||||
meteredList(type, { priority: CO_VALUE_PRIORITY.LOW, ...attrs }),
|
||||
];
|
||||
}
|
||||
|
||||
private getQueue(priority: CoValuePriority) {
|
||||
return this.queues[PRIORITY_TO_QUEUE_INDEX[priority]];
|
||||
}
|
||||
|
||||
public push(msg: SyncMessage) {
|
||||
const priority = "priority" in msg ? msg.priority : this.defaultPriority;
|
||||
|
||||
this.getQueue(priority).push(msg);
|
||||
}
|
||||
|
||||
public pull() {
|
||||
const priority = this.queues.findIndex((queue) => queue.length > 0);
|
||||
|
||||
return this.queues[priority]?.shift();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { LinkedList } from "../PriorityBasedMessageQueue.js";
|
||||
import { logger } from "../logger.js";
|
||||
import { CoValueKnownState, NewContentMessage } from "../sync.js";
|
||||
import { LinkedList } from "./LinkedList.js";
|
||||
|
||||
type StoreQueueEntry = {
|
||||
data: NewContentMessage[];
|
||||
@@ -1,4 +1,3 @@
|
||||
import { LinkedList } from "../PriorityBasedMessageQueue.js";
|
||||
import {
|
||||
type CoValueCore,
|
||||
MAX_RECOMMENDED_TX_SIZE,
|
||||
@@ -7,12 +6,12 @@ import {
|
||||
type StorageAPI,
|
||||
} from "../exports.js";
|
||||
import { getPriorityFromHeader } from "../priority.js";
|
||||
import { StoreQueue } from "../queue/StoreQueue.js";
|
||||
import {
|
||||
CoValueKnownState,
|
||||
NewContentMessage,
|
||||
emptyKnownState,
|
||||
} from "../sync.js";
|
||||
import { StoreQueue } from "./StoreQueue.js";
|
||||
import { StorageKnownState } from "./knownState.js";
|
||||
import { collectNewTxs, getDependedOnCoValues } from "./syncUtils.js";
|
||||
import type {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Histogram, ValueType, metrics } from "@opentelemetry/api";
|
||||
import { IncomingMessagesQueue } from "./IncomingMessagesQueue.js";
|
||||
import { PeerState } from "./PeerState.js";
|
||||
import { SyncStateManager } from "./SyncStateManager.js";
|
||||
import { CoValueCore } from "./coValueCore/coValueCore.js";
|
||||
@@ -10,6 +9,7 @@ import { RawCoID, SessionID } from "./ids.js";
|
||||
import { LocalNode } from "./localNode.js";
|
||||
import { logger } from "./logger.js";
|
||||
import { CoValuePriority } from "./priority.js";
|
||||
import { IncomingMessagesQueue } from "./queue/IncomingMessagesQueue.js";
|
||||
import { accountOrAgentIDfromSessionID } from "./typeUtils/accountOrAgentIDfromSessionID.js";
|
||||
import { isAccountID } from "./typeUtils/isAccountID.js";
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
|
||||
import { IncomingMessagesQueue } from "../IncomingMessagesQueue.js";
|
||||
import { PeerState } from "../PeerState.js";
|
||||
import { IncomingMessagesQueue } from "../queue/IncomingMessagesQueue.js";
|
||||
import { ConnectedPeerChannel } from "../streamUtils.js";
|
||||
import { Peer, SyncMessage } from "../sync.js";
|
||||
import {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { beforeEach, describe, expect, it } from "vitest";
|
||||
import { LinkedList } from "../PriorityBasedMessageQueue";
|
||||
import { LinkedList } from "../queue/LinkedList.js";
|
||||
|
||||
describe("LinkedList", () => {
|
||||
let list: LinkedList<number>;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { afterEach, describe, expect, test } from "vitest";
|
||||
import { PriorityBasedMessageQueue } from "../PriorityBasedMessageQueue.js";
|
||||
import { CO_VALUE_PRIORITY } from "../priority.js";
|
||||
import { PriorityBasedMessageQueue } from "../queue/PriorityBasedMessageQueue.js";
|
||||
import type { SyncMessage } from "../sync.js";
|
||||
import {
|
||||
createTestMetricReader,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { beforeEach, describe, expect, test, vi } from "vitest";
|
||||
import { StoreQueue } from "../storage/StoreQueue.js";
|
||||
import { StoreQueue } from "../queue/StoreQueue.js";
|
||||
import type { CoValueKnownState, NewContentMessage } from "../sync.js";
|
||||
|
||||
function createMockNewContentMessage(id: string): NewContentMessage[] {
|
||||
|
||||
@@ -74,6 +74,69 @@ test("Can create account with one node, and then load it on another", async () =
|
||||
expect(map2.get("foo")).toEqual("bar");
|
||||
});
|
||||
|
||||
test("Should migrate the root from private to trusting", async () => {
|
||||
const { node, accountID, accountSecret } =
|
||||
await LocalNode.withNewlyCreatedAccount({
|
||||
creationProps: { name: "Hermes Puggington" },
|
||||
crypto: Crypto,
|
||||
});
|
||||
|
||||
const group = await node.createGroup();
|
||||
expect(group).not.toBeNull();
|
||||
|
||||
const map = group.createMap();
|
||||
map.set("foo", "bar", "private");
|
||||
expect(map.get("foo")).toEqual("bar");
|
||||
|
||||
const peers1 = connectedPeers("node1", "node2", {
|
||||
peer1role: "server",
|
||||
peer2role: "client",
|
||||
});
|
||||
|
||||
const account = await node.load(accountID);
|
||||
if (account === "unavailable") throw new Error("Account unavailable");
|
||||
|
||||
account.set("root", map.id, "private");
|
||||
|
||||
node.syncManager.addPeer(peers1[1]);
|
||||
|
||||
const node2 = await LocalNode.withLoadedAccount({
|
||||
accountID,
|
||||
accountSecret,
|
||||
sessionID: Crypto.newRandomSessionID(accountID),
|
||||
peersToLoadFrom: [peers1[0]],
|
||||
crypto: Crypto,
|
||||
});
|
||||
|
||||
const account2 = await node2.load(accountID);
|
||||
if (account2 === "unavailable") throw new Error("Account unavailable");
|
||||
|
||||
expect(account2.getRaw("root")?.trusting).toEqual(true);
|
||||
|
||||
node2.gracefulShutdown(); // Stop getting updates from node1
|
||||
|
||||
const peers2 = connectedPeers("node2", "node3", {
|
||||
peer1role: "server",
|
||||
peer2role: "client",
|
||||
});
|
||||
|
||||
node.syncManager.addPeer(peers2[1]);
|
||||
|
||||
const node3 = await LocalNode.withLoadedAccount({
|
||||
accountID,
|
||||
accountSecret,
|
||||
sessionID: Crypto.newRandomSessionID(accountID),
|
||||
peersToLoadFrom: [peers2[0]],
|
||||
crypto: Crypto,
|
||||
});
|
||||
|
||||
const account3 = await node3.load(accountID);
|
||||
if (account3 === "unavailable") throw new Error("Account unavailable");
|
||||
|
||||
expect(account3.getRaw("root")?.trusting).toEqual(true);
|
||||
expect(account3.ops).toEqual(account2.ops); // No new transactions were made
|
||||
});
|
||||
|
||||
test("throws an error if the user tried to create an invite from an account", async () => {
|
||||
const { node, accountID } = await LocalNode.withNewlyCreatedAccount({
|
||||
creationProps: { name: "Hermes Puggington" },
|
||||
|
||||
@@ -1,5 +1,32 @@
|
||||
# jazz-auth-betterauth
|
||||
|
||||
## 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
|
||||
|
||||
- Updated dependencies [c62abef]
|
||||
- jazz-tools@0.16.1
|
||||
- jazz-betterauth-client-plugin@0.16.1
|
||||
- cojson@0.16.1
|
||||
|
||||
## 0.16.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [c09dcdf]
|
||||
- Updated dependencies [2bbb07b]
|
||||
- jazz-tools@0.16.0
|
||||
- cojson@0.16.0
|
||||
- jazz-betterauth-client-plugin@0.16.0
|
||||
|
||||
## 0.15.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-auth-betterauth",
|
||||
"version": "0.15.16",
|
||||
"version": "0.16.2",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.ts",
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
# jazz-betterauth-client-plugin
|
||||
|
||||
## 0.16.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-betterauth-server-plugin@0.16.2
|
||||
|
||||
## 0.16.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-betterauth-server-plugin@0.16.1
|
||||
|
||||
## 0.16.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [2bbb07b]
|
||||
- jazz-betterauth-server-plugin@0.16.0
|
||||
|
||||
## 0.15.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-betterauth-client-plugin",
|
||||
"version": "0.15.16",
|
||||
"version": "0.16.2",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.ts",
|
||||
|
||||
@@ -1,5 +1,38 @@
|
||||
# jazz-betterauth-server-plugin
|
||||
|
||||
## 0.16.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cojson@0.16.2
|
||||
- jazz-tools@0.16.2
|
||||
|
||||
## 0.16.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [c62abef]
|
||||
- jazz-tools@0.16.1
|
||||
- cojson@0.16.1
|
||||
|
||||
## 0.16.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 2bbb07b: Introduce a cleaner separation between Zod and CoValue schemas:
|
||||
- Zod schemas and CoValue schemas are fully separated. Zod schemas can only be composed with other Zod schemas. CoValue schemas can be composed with either Zod or other CoValue schemas.
|
||||
- `z.optional()` and `z.discriminatedUnion()` no longer work with CoValue schemas. Use `co.optional()` and `co.discriminatedUnion()` instead.
|
||||
- Internal schema access is now simpler. You no longer need to use Zod’s `.def` to access internals. Use properties like `CoMapSchema.shape`, `CoListSchema.element`, and `CoOptionalSchema.innerType` directly.
|
||||
- CoValue schema types are now namespaced under `co.`. Non-namespaced exports have been removed
|
||||
- CoMap schemas no longer incorrectly inherit from Zod. Previously, methods like `.extend()` and `.partial()` appeared available but could cause unexpected behavior. These methods are now disabled. In their place, `.optional()` has been added, and more Zod-like methods will be introduced in future releases.
|
||||
- Upgraded Zod from `3.25.28` to `3.25.76`.
|
||||
- Removed deprecated `withHelpers` method from CoValue schemas
|
||||
- Removed deprecated `createCoValueObservable` function
|
||||
- Updated dependencies [c09dcdf]
|
||||
- Updated dependencies [2bbb07b]
|
||||
- jazz-tools@0.16.0
|
||||
- cojson@0.16.0
|
||||
|
||||
## 0.15.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-betterauth-server-plugin",
|
||||
"version": "0.15.16",
|
||||
"version": "0.16.2",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.ts",
|
||||
@@ -10,7 +10,7 @@
|
||||
"jazz-tools": "workspace:*",
|
||||
"better-auth": "^1.2.4",
|
||||
"better-sqlite3": "^11.9.1",
|
||||
"zod": "3.25.28"
|
||||
"zod": "3.25.76"
|
||||
},
|
||||
"scripts": {
|
||||
"format-and-lint": "biome check .",
|
||||
@@ -24,4 +24,4 @@
|
||||
"typescript": "~5.6.2",
|
||||
"@types/better-sqlite3": "^7.6.12"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,35 @@
|
||||
# jazz-react-auth-betterauth
|
||||
|
||||
## 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
|
||||
|
||||
- Updated dependencies [c62abef]
|
||||
- jazz-tools@0.16.1
|
||||
- jazz-auth-betterauth@0.16.1
|
||||
- jazz-betterauth-client-plugin@0.16.1
|
||||
- cojson@0.16.1
|
||||
|
||||
## 0.16.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [c09dcdf]
|
||||
- Updated dependencies [2bbb07b]
|
||||
- jazz-tools@0.16.0
|
||||
- cojson@0.16.0
|
||||
- jazz-auth-betterauth@0.16.0
|
||||
- jazz-betterauth-client-plugin@0.16.0
|
||||
|
||||
## 0.15.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-react-auth-betterauth",
|
||||
"version": "0.15.16",
|
||||
"version": "0.16.2",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.tsx",
|
||||
|
||||
@@ -1,5 +1,45 @@
|
||||
# jazz-run
|
||||
|
||||
## 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
|
||||
|
||||
- Updated dependencies [c62abef]
|
||||
- jazz-tools@0.16.1
|
||||
- cojson@0.16.1
|
||||
- cojson-storage-sqlite@0.16.1
|
||||
- cojson-transport-ws@0.16.1
|
||||
|
||||
## 0.16.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 2bbb07b: Introduce a cleaner separation between Zod and CoValue schemas:
|
||||
- Zod schemas and CoValue schemas are fully separated. Zod schemas can only be composed with other Zod schemas. CoValue schemas can be composed with either Zod or other CoValue schemas.
|
||||
- `z.optional()` and `z.discriminatedUnion()` no longer work with CoValue schemas. Use `co.optional()` and `co.discriminatedUnion()` instead.
|
||||
- Internal schema access is now simpler. You no longer need to use Zod’s `.def` to access internals. Use properties like `CoMapSchema.shape`, `CoListSchema.element`, and `CoOptionalSchema.innerType` directly.
|
||||
- CoValue schema types are now namespaced under `co.`. Non-namespaced exports have been removed
|
||||
- CoMap schemas no longer incorrectly inherit from Zod. Previously, methods like `.extend()` and `.partial()` appeared available but could cause unexpected behavior. These methods are now disabled. In their place, `.optional()` has been added, and more Zod-like methods will be introduced in future releases.
|
||||
- Upgraded Zod from `3.25.28` to `3.25.76`.
|
||||
- Removed deprecated `withHelpers` method from CoValue schemas
|
||||
- Removed deprecated `createCoValueObservable` function
|
||||
- Updated dependencies [c09dcdf]
|
||||
- Updated dependencies [2bbb07b]
|
||||
- jazz-tools@0.16.0
|
||||
- cojson@0.16.0
|
||||
- cojson-storage-sqlite@0.16.0
|
||||
- cojson-transport-ws@0.16.0
|
||||
|
||||
## 0.15.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -3,15 +3,15 @@
|
||||
"bin": "./dist/index.js",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"version": "0.15.16",
|
||||
"version": "0.16.2",
|
||||
"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.15.16",
|
||||
"cojson-storage-sqlite": "workspace:0.15.16",
|
||||
"cojson-transport-ws": "workspace:0.15.16",
|
||||
"cojson": "workspace:0.16.2",
|
||||
"cojson-storage-sqlite": "workspace:0.16.2",
|
||||
"cojson-transport-ws": "workspace:0.16.2",
|
||||
"effect": "^3.6.5",
|
||||
"jazz-tools": "workspace:0.15.16",
|
||||
"jazz-tools": "workspace:0.16.2",
|
||||
"ws": "^8.14.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -5,15 +5,12 @@ import { join } from "node:path";
|
||||
import {
|
||||
Account,
|
||||
AccountClass,
|
||||
AccountSchema,
|
||||
AnyAccountSchema,
|
||||
CoMap,
|
||||
CoValueFromRaw,
|
||||
Group,
|
||||
InboxSender,
|
||||
Loaded,
|
||||
co,
|
||||
coField,
|
||||
z,
|
||||
} from "jazz-tools";
|
||||
import { startWorker } from "jazz-tools/worker";
|
||||
|
||||
@@ -1,5 +1,47 @@
|
||||
# jazz-tools
|
||||
|
||||
## 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
|
||||
|
||||
- c62abef: Add support for nullable non-collaborative fields
|
||||
- cojson@0.16.1
|
||||
- cojson-storage-indexeddb@0.16.1
|
||||
- cojson-transport-ws@0.16.1
|
||||
|
||||
## 0.16.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- c09dcdf: Change the root attribute to be public on Account. The root content will still follow the visiblity rules specified in their group.
|
||||
|
||||
Existing accounts will be gradually migrated as they are loaded.
|
||||
|
||||
- 2bbb07b: Introduce a cleaner separation between Zod and CoValue schemas:
|
||||
- Zod schemas and CoValue schemas are fully separated. Zod schemas can only be composed with other Zod schemas. CoValue schemas can be composed with either Zod or other CoValue schemas.
|
||||
- `z.optional()` and `z.discriminatedUnion()` no longer work with CoValue schemas. Use `co.optional()` and `co.discriminatedUnion()` instead.
|
||||
- Internal schema access is now simpler. You no longer need to use Zod’s `.def` to access internals. Use properties like `CoMapSchema.shape`, `CoListSchema.element`, and `CoOptionalSchema.innerType` directly.
|
||||
- CoValue schema types are now namespaced under `co.`. Non-namespaced exports have been removed
|
||||
- CoMap schemas no longer incorrectly inherit from Zod. Previously, methods like `.extend()` and `.partial()` appeared available but could cause unexpected behavior. These methods are now disabled. In their place, `.optional()` has been added, and more Zod-like methods will be introduced in future releases.
|
||||
- Upgraded Zod from `3.25.28` to `3.25.76`.
|
||||
- Removed deprecated `withHelpers` method from CoValue schemas
|
||||
- Removed deprecated `createCoValueObservable` function
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [c09dcdf]
|
||||
- cojson@0.16.0
|
||||
- cojson-storage-indexeddb@0.16.0
|
||||
- cojson-transport-ws@0.16.0
|
||||
|
||||
## 0.15.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -139,7 +139,7 @@
|
||||
},
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"version": "0.15.16",
|
||||
"version": "0.16.2",
|
||||
"dependencies": {
|
||||
"@manuscripts/prosemirror-recreate-steps": "^0.1.4",
|
||||
"@scure/base": "1.2.1",
|
||||
@@ -160,7 +160,7 @@
|
||||
"prosemirror-schema-basic": "^1.2.2",
|
||||
"prosemirror-state": "^1.4.3",
|
||||
"prosemirror-transform": "^1.9.0",
|
||||
"zod": "3.25.28"
|
||||
"zod": "3.25.76"
|
||||
},
|
||||
"scripts": {
|
||||
"check": "tsc --noEmit",
|
||||
|
||||
@@ -17,7 +17,7 @@ export async function createImage(
|
||||
owner?: Group | Account;
|
||||
maxSize?: 256 | 1024 | 2048;
|
||||
},
|
||||
): Promise<Loaded<typeof ImageDefinition>> {
|
||||
): Promise<Loaded<typeof ImageDefinition, { $each: true }>> {
|
||||
// Get the original size of the image
|
||||
const { width: originalWidth, height: originalHeight } =
|
||||
await getImageSize(imageBlobOrFile);
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import {
|
||||
Account,
|
||||
CoValue,
|
||||
CoValueClass,
|
||||
CoValueOrZodSchema,
|
||||
ID,
|
||||
CoValueClassOrSchema,
|
||||
InviteSecret,
|
||||
createInviteLink as baseCreateInviteLink,
|
||||
consumeInviteLink,
|
||||
@@ -36,7 +34,7 @@ export { parseInviteLink } from "jazz-tools";
|
||||
|
||||
/** @category Invite Links */
|
||||
export async function consumeInviteLinkFromWindowLocation<
|
||||
S extends CoValueOrZodSchema,
|
||||
S extends CoValueClassOrSchema,
|
||||
>({
|
||||
as,
|
||||
forValueHint,
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
AnonymousJazzAgent,
|
||||
AnyAccountSchema,
|
||||
CoValue,
|
||||
CoValueOrZodSchema,
|
||||
CoValueClassOrSchema,
|
||||
InboxSender,
|
||||
InstanceOfSchema,
|
||||
JazzContextManager,
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
ResolveQuery,
|
||||
ResolveQueryStrict,
|
||||
SubscriptionScope,
|
||||
anySchemaToCoSchema,
|
||||
coValueClassFromCoValueClassOrSchema,
|
||||
} from "jazz-tools";
|
||||
import { JazzContext, JazzContextManagerContext } from "./provider.js";
|
||||
import { getCurrentAccountFromContextManager } from "./utils.js";
|
||||
@@ -80,7 +80,7 @@ export function useIsAuthenticated() {
|
||||
}
|
||||
|
||||
function useCoValueSubscription<
|
||||
S extends CoValueOrZodSchema,
|
||||
S extends CoValueClassOrSchema,
|
||||
const R extends ResolveQuery<S>,
|
||||
>(
|
||||
Schema: S,
|
||||
@@ -107,7 +107,7 @@ function useCoValueSubscription<
|
||||
options?.resolve ?? true,
|
||||
id,
|
||||
{
|
||||
ref: anySchemaToCoSchema(Schema),
|
||||
ref: coValueClassFromCoValueClassOrSchema(Schema),
|
||||
optional: true,
|
||||
},
|
||||
);
|
||||
@@ -142,7 +142,7 @@ function useCoValueSubscription<
|
||||
}
|
||||
|
||||
export function useCoState<
|
||||
S extends CoValueOrZodSchema,
|
||||
S extends CoValueClassOrSchema,
|
||||
const R extends ResolveQuery<S> = true,
|
||||
>(
|
||||
Schema: S,
|
||||
@@ -198,7 +198,7 @@ function useAccountSubscription<
|
||||
|
||||
const node = contextManager.getCurrentValue()!.node;
|
||||
const subscription = new SubscriptionScope<any>(node, resolve, agent.id, {
|
||||
ref: anySchemaToCoSchema(Schema),
|
||||
ref: coValueClassFromCoValueClassOrSchema(Schema),
|
||||
optional: true,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @vitest-environment happy-dom
|
||||
|
||||
import { RefsToResolve, co, z, zodSchemaToCoSchema } from "jazz-tools";
|
||||
import { RefsToResolve, co, z } from "jazz-tools";
|
||||
import { beforeEach, describe, expect, it } from "vitest";
|
||||
import { useAccount, useJazzContextManager } from "../hooks.js";
|
||||
import { useIsAuthenticated } from "../index.js";
|
||||
@@ -46,7 +46,7 @@ describe("useAccount", () => {
|
||||
});
|
||||
|
||||
const account = await createJazzTestAccount({
|
||||
AccountSchema: zodSchemaToCoSchema(AccountSchema),
|
||||
AccountSchema,
|
||||
});
|
||||
|
||||
const { result } = renderHook(
|
||||
|
||||
@@ -520,12 +520,13 @@ describe("useCoState", () => {
|
||||
const Message = co.map({
|
||||
content: CoRichText,
|
||||
});
|
||||
const Messages = co.list(Message);
|
||||
const Thread = co.map({
|
||||
messages: co.list(Message),
|
||||
messages: Messages,
|
||||
});
|
||||
|
||||
const thread = Thread.create({
|
||||
messages: Thread.def.shape.messages.create([
|
||||
messages: Messages.create([
|
||||
Message.create({
|
||||
content: CoRichText.create("Hello man!"),
|
||||
}),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useEffect } from "react";
|
||||
|
||||
import { CoValueOrZodSchema, parseInviteLink } from "jazz-tools";
|
||||
import { CoValueClassOrSchema, parseInviteLink } from "jazz-tools";
|
||||
import { useJazzContext } from "jazz-tools/react-core";
|
||||
import { Linking } from "react-native";
|
||||
|
||||
@@ -15,7 +15,7 @@ export {
|
||||
useAccount,
|
||||
} from "jazz-tools/react-core";
|
||||
|
||||
export function useAcceptInviteNative<S extends CoValueOrZodSchema>({
|
||||
export function useAcceptInviteNative<S extends CoValueClassOrSchema>({
|
||||
invitedObjectSchema,
|
||||
onAccept,
|
||||
forValueHint,
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { consumeInviteLinkFromWindowLocation } from "jazz-tools/browser";
|
||||
import { useEffect } from "react";
|
||||
|
||||
import { CoValueOrZodSchema } from "jazz-tools";
|
||||
import { CoValueClassOrSchema } from "jazz-tools";
|
||||
import { useJazzContext } from "jazz-tools/react-core";
|
||||
|
||||
export { useCoState, useAuthSecretStorage } from "jazz-tools/react-core";
|
||||
|
||||
export function useAcceptInvite<S extends CoValueOrZodSchema>({
|
||||
export function useAcceptInvite<S extends CoValueClassOrSchema>({
|
||||
invitedObjectSchema,
|
||||
onAccept,
|
||||
forValueHint,
|
||||
|
||||
@@ -2,21 +2,24 @@ import type {
|
||||
Account,
|
||||
AccountClass,
|
||||
AnyAccountSchema,
|
||||
CoValueClassOrSchema,
|
||||
CoValueFromRaw,
|
||||
CoValueOrZodSchema,
|
||||
InstanceOfSchema,
|
||||
Loaded,
|
||||
ResolveQuery,
|
||||
ResolveQueryStrict,
|
||||
} from "jazz-tools";
|
||||
import { anySchemaToCoSchema, subscribeToCoValue } from "jazz-tools";
|
||||
import {
|
||||
coValueClassFromCoValueClassOrSchema,
|
||||
subscribeToCoValue,
|
||||
} from "jazz-tools";
|
||||
import { untrack } from "svelte";
|
||||
import { createSubscriber } from "svelte/reactivity";
|
||||
import { useIsAuthenticated } from "./auth/useIsAuthenticated.svelte.js";
|
||||
import { getJazzContext } from "./jazz.svelte";
|
||||
|
||||
export class CoState<
|
||||
V extends CoValueOrZodSchema,
|
||||
V extends CoValueClassOrSchema,
|
||||
R extends ResolveQuery<V> = true,
|
||||
> {
|
||||
#value: Loaded<V, R> | undefined | null = undefined;
|
||||
@@ -47,10 +50,10 @@ export class CoState<
|
||||
const agent = "me" in ctx ? ctx.me : ctx.guest;
|
||||
|
||||
const unsubscribe = subscribeToCoValue(
|
||||
anySchemaToCoSchema(Schema),
|
||||
coValueClassFromCoValueClassOrSchema(Schema),
|
||||
id,
|
||||
{
|
||||
// @ts-expect-error The resolve query type isn't compatible with the anySchemaToCoSchema conversion
|
||||
// @ts-expect-error The resolve query type isn't compatible with the coValueClassFromCoValueClassOrSchema conversion
|
||||
resolve: options?.resolve,
|
||||
loadAs: agent,
|
||||
onUnavailable: () => {
|
||||
@@ -112,10 +115,10 @@ export class AccountCoState<
|
||||
const me = ctx.me;
|
||||
|
||||
const unsubscribe = subscribeToCoValue(
|
||||
anySchemaToCoSchema(Schema),
|
||||
coValueClassFromCoValueClassOrSchema(Schema),
|
||||
me.id,
|
||||
{
|
||||
// @ts-expect-error The resolve query type isn't compatible with the anySchemaToCoSchema conversion
|
||||
// @ts-expect-error The resolve query type isn't compatible with the coValueClassFromCoValueClassOrSchema conversion
|
||||
resolve: options?.resolve,
|
||||
loadAs: me,
|
||||
onUnavailable: () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type {
|
||||
AccountClass,
|
||||
AuthSecretStorage,
|
||||
CoValueOrZodSchema,
|
||||
CoValueClassOrSchema,
|
||||
ID,
|
||||
InstanceOfSchema,
|
||||
JazzContextType,
|
||||
@@ -66,7 +66,7 @@ export function getAuthSecretStorage() {
|
||||
* @param forValueHint - Hint for the value.
|
||||
* @returns The accept invite hook.
|
||||
*/
|
||||
export class InviteListener<V extends CoValueOrZodSchema> {
|
||||
export class InviteListener<V extends CoValueClassOrSchema> {
|
||||
constructor({
|
||||
invitedObjectSchema,
|
||||
onAccept,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ControlledAccount, RawAccount, type RawCoValue } from "cojson";
|
||||
import { z } from "../implementation/zodSchema/zodReExport.js";
|
||||
import { CoreCoValueSchema } from "../implementation/zodSchema/schemaTypes/CoValueSchema.js";
|
||||
import {
|
||||
AnonymousJazzAgent,
|
||||
CoValue,
|
||||
@@ -8,13 +8,14 @@ import {
|
||||
ID,
|
||||
RegisteredSchemas,
|
||||
accessChildById,
|
||||
anySchemaToCoSchema,
|
||||
coValueClassFromCoValueClassOrSchema,
|
||||
coValuesCache,
|
||||
inspect,
|
||||
isCoValueSchema,
|
||||
} from "../internal.js";
|
||||
import type {
|
||||
Account,
|
||||
CoValueClassOrSchema,
|
||||
Group,
|
||||
InstanceOfSchemaCoValuesNullable,
|
||||
} from "../internal.js";
|
||||
@@ -46,9 +47,9 @@ export class CoValueBase implements CoValue {
|
||||
|
||||
if (agent instanceof ControlledAccount) {
|
||||
return coValuesCache.get(agent.account, () =>
|
||||
anySchemaToCoSchema(RegisteredSchemas["Account"]).fromRaw(
|
||||
agent.account,
|
||||
),
|
||||
coValueClassFromCoValueClassOrSchema(
|
||||
RegisteredSchemas["Account"],
|
||||
).fromRaw(agent.account),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -81,24 +82,11 @@ export class CoValueBase implements CoValue {
|
||||
}
|
||||
|
||||
/** @category Type Helpers */
|
||||
castAs<
|
||||
S extends
|
||||
| CoValueClass
|
||||
| z.core.$ZodType
|
||||
| (z.core.$ZodObject<any, any> & {
|
||||
builtin: "Account";
|
||||
migration?: (account: any, creationProps?: { name: string }) => void;
|
||||
})
|
||||
| (z.core.$ZodCustom<any, any> & { builtin: "FileStream" })
|
||||
| (z.core.$ZodCustom<any, any> & {
|
||||
builtin: "CoFeed";
|
||||
element: z.core.$ZodType;
|
||||
}),
|
||||
>(
|
||||
castAs<S extends CoValueClassOrSchema>(
|
||||
schema: S,
|
||||
): S extends CoValueClass
|
||||
? InstanceType<S>
|
||||
: S extends z.core.$ZodType
|
||||
: S extends CoreCoValueSchema
|
||||
? NonNullable<InstanceOfSchemaCoValuesNullable<S>>
|
||||
: never {
|
||||
const cl = isCoValueSchema(schema) ? schema.getCoValueClass() : schema;
|
||||
|
||||
@@ -16,12 +16,12 @@ import {
|
||||
} from "cojson";
|
||||
import {
|
||||
AnonymousJazzAgent,
|
||||
AnyAccountSchema,
|
||||
type CoMap,
|
||||
type CoValue,
|
||||
CoValueBase,
|
||||
CoValueClass,
|
||||
CoValueOrZodSchema,
|
||||
CoValueClassOrSchema,
|
||||
CoreAccountSchema,
|
||||
type Group,
|
||||
ID,
|
||||
InstanceOfSchema,
|
||||
@@ -40,7 +40,7 @@ import {
|
||||
SubscribeRestArgs,
|
||||
accessChildByKey,
|
||||
activeAccountContext,
|
||||
anySchemaToCoSchema,
|
||||
coValueClassFromCoValueClassOrSchema,
|
||||
coValuesCache,
|
||||
createInboxRoot,
|
||||
ensureCoValueLoaded,
|
||||
@@ -254,7 +254,7 @@ export class Account extends CoValueBase implements CoValue {
|
||||
return value._owner.getRoleOf(this.id) === "admin";
|
||||
}
|
||||
|
||||
async acceptInvite<S extends CoValueOrZodSchema>(
|
||||
async acceptInvite<S extends CoValueClassOrSchema>(
|
||||
valueID: string,
|
||||
inviteSecret: InviteSecret,
|
||||
coValueClass: S,
|
||||
@@ -268,9 +268,13 @@ export class Account extends CoValueBase implements CoValue {
|
||||
inviteSecret,
|
||||
);
|
||||
|
||||
return loadCoValue(anySchemaToCoSchema(coValueClass), valueID, {
|
||||
loadAs: this,
|
||||
}) as Resolved<InstanceOrPrimitiveOfSchema<S>, true> | null;
|
||||
return loadCoValue(
|
||||
coValueClassFromCoValueClassOrSchema(coValueClass),
|
||||
valueID,
|
||||
{
|
||||
loadAs: this,
|
||||
},
|
||||
) as Resolved<InstanceOrPrimitiveOfSchema<S>, true> | null;
|
||||
}
|
||||
|
||||
/** @private */
|
||||
@@ -447,9 +451,7 @@ export class Account extends CoValueBase implements CoValue {
|
||||
*
|
||||
* @category Subscription & Loading
|
||||
*/
|
||||
waitForSync(options?: {
|
||||
timeout?: number;
|
||||
}) {
|
||||
waitForSync(options?: { timeout?: number }) {
|
||||
return this._raw.core.waitForSync(options);
|
||||
}
|
||||
|
||||
@@ -458,9 +460,7 @@ export class Account extends CoValueBase implements CoValue {
|
||||
*
|
||||
* @category Subscription & Loading
|
||||
*/
|
||||
waitForAllCoValuesSync(options?: {
|
||||
timeout?: number;
|
||||
}) {
|
||||
waitForAllCoValuesSync(options?: { timeout?: number }) {
|
||||
return this._raw.core.node.syncManager.waitForAllCoValuesSync(
|
||||
options?.timeout,
|
||||
);
|
||||
@@ -502,7 +502,11 @@ export const AccountAndGroupProxyHandler: ProxyHandler<Account | Group> = {
|
||||
return true;
|
||||
} else if (key === "root") {
|
||||
if (value) {
|
||||
target._raw.set("root", value.id as unknown as CoID<RawCoMap>);
|
||||
target._raw.set(
|
||||
"root",
|
||||
value.id as unknown as CoID<RawCoMap>,
|
||||
"trusting",
|
||||
);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
|
||||
@@ -12,12 +12,10 @@ import type {
|
||||
import { MAX_RECOMMENDED_TX_SIZE, cojsonInternals } from "cojson";
|
||||
import type {
|
||||
AnonymousJazzAgent,
|
||||
AnyAccountSchema,
|
||||
CoValue,
|
||||
CoValueClass,
|
||||
Group,
|
||||
ID,
|
||||
InstanceOfSchema,
|
||||
RefsToResolve,
|
||||
RefsToResolveStrict,
|
||||
Resolved,
|
||||
@@ -31,10 +29,8 @@ import {
|
||||
CoValueBase,
|
||||
ItemsSym,
|
||||
Ref,
|
||||
RegisteredSchemas,
|
||||
SchemaInit,
|
||||
accessChildById,
|
||||
anySchemaToCoSchema,
|
||||
coField,
|
||||
ensureCoValueLoaded,
|
||||
inspect,
|
||||
|
||||
@@ -3,13 +3,11 @@ import { ControlledAccount, RawAccount } from "cojson";
|
||||
import { calcPatch } from "fast-myers-diff";
|
||||
import type {
|
||||
Account,
|
||||
AnyAccountSchema,
|
||||
CoValue,
|
||||
CoValueClass,
|
||||
CoValueFromRaw,
|
||||
Group,
|
||||
ID,
|
||||
InstanceOfSchema,
|
||||
RefEncoded,
|
||||
RefsToResolve,
|
||||
RefsToResolveStrict,
|
||||
@@ -26,8 +24,8 @@ import {
|
||||
RegisteredSchemas,
|
||||
SchemaInit,
|
||||
accessChildByKey,
|
||||
anySchemaToCoSchema,
|
||||
coField,
|
||||
coValueClassFromCoValueClassOrSchema,
|
||||
coValuesCache,
|
||||
ensureCoValueLoaded,
|
||||
inspect,
|
||||
@@ -117,9 +115,9 @@ export class CoList<out Item = any> extends Array<Item> implements CoValue {
|
||||
/** @category Collaboration */
|
||||
get _owner(): Account | Group {
|
||||
return this._raw.group instanceof RawAccount
|
||||
? anySchemaToCoSchema(RegisteredSchemas["Account"]).fromRaw(
|
||||
this._raw.group,
|
||||
)
|
||||
? coValueClassFromCoValueClassOrSchema(
|
||||
RegisteredSchemas["Account"],
|
||||
).fromRaw(this._raw.group)
|
||||
: RegisteredSchemas["Group"].fromRaw(this._raw.group);
|
||||
}
|
||||
|
||||
@@ -175,9 +173,9 @@ export class CoList<out Item = any> extends Array<Item> implements CoValue {
|
||||
|
||||
if (agent instanceof ControlledAccount) {
|
||||
return coValuesCache.get(agent.account, () =>
|
||||
anySchemaToCoSchema(RegisteredSchemas["Account"]).fromRaw(
|
||||
agent.account,
|
||||
),
|
||||
coValueClassFromCoValueClassOrSchema(
|
||||
RegisteredSchemas["Account"],
|
||||
).fromRaw(agent.account),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,12 +14,14 @@ import type {
|
||||
CoValueClass,
|
||||
Group,
|
||||
ID,
|
||||
PartialOnUndefined,
|
||||
RefEncoded,
|
||||
RefIfCoValue,
|
||||
RefsToResolve,
|
||||
RefsToResolveStrict,
|
||||
Resolved,
|
||||
Schema,
|
||||
Simplify,
|
||||
SubscribeListenerOptions,
|
||||
SubscribeRestArgs,
|
||||
} from "../internal.js";
|
||||
@@ -54,12 +56,6 @@ type CoMapEdit<V> = {
|
||||
|
||||
type LastAndAllCoMapEdits<V> = CoMapEdit<V> & { all: CoMapEdit<V>[] };
|
||||
|
||||
export type Simplify<A> = {
|
||||
[K in keyof A]: A[K];
|
||||
} extends infer B
|
||||
? B
|
||||
: never;
|
||||
|
||||
/**
|
||||
* CoMaps are collaborative versions of plain objects, mapping string-like keys to values.
|
||||
*
|
||||
@@ -783,13 +779,9 @@ type ForceRequiredRef<V> = V extends InstanceType<CoValueClass> | null
|
||||
? V | null
|
||||
: V;
|
||||
|
||||
export type CoMapInit<Map extends object> = {
|
||||
[Key in CoKeys<Map> as undefined extends Map[Key]
|
||||
? never
|
||||
: Key]: ForceRequiredRef<Map[Key]>;
|
||||
} & {
|
||||
[Key in CoKeys<Map>]?: ForceRequiredRef<Map[Key]>;
|
||||
};
|
||||
export type CoMapInit<Map extends object> = PartialOnUndefined<{
|
||||
[Key in CoKeys<Map>]: ForceRequiredRef<Map[Key]>;
|
||||
}>;
|
||||
|
||||
// TODO: cache handlers per descriptor for performance?
|
||||
const CoMapProxyHandler: ProxyHandler<CoMap> = {
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
Resolved,
|
||||
SubscribeListenerOptions,
|
||||
SubscribeRestArgs,
|
||||
anySchemaToCoSchema,
|
||||
coValueClassFromCoValueClassOrSchema,
|
||||
parseCoValueCreateOptions,
|
||||
} from "../internal.js";
|
||||
import {
|
||||
@@ -47,9 +47,9 @@ export class CoPlainText extends String implements CoValue {
|
||||
|
||||
if (agent instanceof ControlledAccount) {
|
||||
return coValuesCache.get(agent.account, () =>
|
||||
anySchemaToCoSchema(RegisteredSchemas["Account"]).fromRaw(
|
||||
agent.account,
|
||||
),
|
||||
coValueClassFromCoValueClassOrSchema(
|
||||
RegisteredSchemas["Account"],
|
||||
).fromRaw(agent.account),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,10 @@ import { CoFeedEntry } from "./coFeed.js";
|
||||
import { type CoKeys } from "./coMap.js";
|
||||
import { type CoValue, type ID } from "./interfaces.js";
|
||||
|
||||
type NotNull<T> = Exclude<T, null>;
|
||||
/**
|
||||
* Similar to {@link NonNullable}, but removes only `null` and preserves `undefined`.
|
||||
*/
|
||||
export type NotNull<T> = Exclude<T, null>;
|
||||
|
||||
/**
|
||||
* Used to check if T is a union type.
|
||||
|
||||
@@ -8,9 +8,9 @@ const ImageDefinitionBase = coMapDefiner({
|
||||
}).catchall(coFileStreamDefiner());
|
||||
|
||||
/** @category Media */
|
||||
export const ImageDefinition = ImageDefinitionBase.withHelpers((Self) => ({
|
||||
export const ImageDefinition = Object.assign({}, ImageDefinitionBase, {
|
||||
highestResAvailable(
|
||||
imageDef: Loaded<typeof Self>,
|
||||
imageDef: ImageDefinition,
|
||||
options?: {
|
||||
maxWidth?: number;
|
||||
targetWidth?: number;
|
||||
@@ -56,5 +56,5 @@ export const ImageDefinition = ImageDefinitionBase.withHelpers((Self) => ({
|
||||
}
|
||||
);
|
||||
},
|
||||
}));
|
||||
});
|
||||
export type ImageDefinition = Loaded<typeof ImageDefinition>;
|
||||
|
||||
@@ -3,11 +3,11 @@ import { CoStreamItem, RawCoStream } from "cojson";
|
||||
import {
|
||||
type Account,
|
||||
CoValue,
|
||||
CoValueOrZodSchema,
|
||||
CoValueClassOrSchema,
|
||||
ID,
|
||||
InstanceOfSchema,
|
||||
activeAccountContext,
|
||||
anySchemaToCoSchema,
|
||||
coValueClassFromCoValueClassOrSchema,
|
||||
loadCoValue,
|
||||
} from "../internal.js";
|
||||
import { isCoValueId } from "../lib/id.js";
|
||||
@@ -109,7 +109,7 @@ export class Inbox {
|
||||
this.failed = failed;
|
||||
}
|
||||
|
||||
subscribe<M extends CoValueOrZodSchema, O extends CoValue | undefined>(
|
||||
subscribe<M extends CoValueClassOrSchema, O extends CoValue | undefined>(
|
||||
Schema: M,
|
||||
callback: (
|
||||
message: InstanceOfSchema<M>,
|
||||
@@ -172,7 +172,7 @@ export class Inbox {
|
||||
}
|
||||
|
||||
return loadCoValue(
|
||||
anySchemaToCoSchema(Schema),
|
||||
coValueClassFromCoValueClassOrSchema(Schema),
|
||||
message.get("payload")!,
|
||||
{
|
||||
loadAs: account,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import {
|
||||
CoValueCore,
|
||||
type CoValueUniqueness,
|
||||
type CojsonInternalTypes,
|
||||
type RawCoValue,
|
||||
@@ -9,7 +8,7 @@ import { AvailableCoValueCore } from "cojson/dist/coValueCore/coValueCore.js";
|
||||
import {
|
||||
Account,
|
||||
AnonymousJazzAgent,
|
||||
CoValueOrZodSchema,
|
||||
CoValueClassOrSchema,
|
||||
type Group,
|
||||
Loaded,
|
||||
RefsToResolve,
|
||||
@@ -21,8 +20,7 @@ import {
|
||||
SubscriptionScope,
|
||||
type SubscriptionValue,
|
||||
activeAccountContext,
|
||||
anySchemaToCoSchema,
|
||||
getSubscriptionScope,
|
||||
coValueClassFromCoValueClassOrSchema,
|
||||
inspect,
|
||||
} from "../internal.js";
|
||||
|
||||
@@ -312,71 +310,6 @@ export function subscribeToCoValue<
|
||||
return unsubscribe;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Used for the React integration in the past, but we moved to use SubscriptionScope directly.
|
||||
*
|
||||
* Going to be removed in the next minor version.
|
||||
*/
|
||||
export function createCoValueObservable<
|
||||
S extends CoValueOrZodSchema,
|
||||
const R extends ResolveQuery<S>,
|
||||
>(initialValue: undefined | null = undefined) {
|
||||
let currentValue: Loaded<S, R> | undefined | null = initialValue;
|
||||
let subscriberCount = 0;
|
||||
|
||||
function subscribe(
|
||||
cls: S,
|
||||
id: string,
|
||||
options: {
|
||||
loadAs: Account | AnonymousJazzAgent;
|
||||
resolve?: ResolveQueryStrict<S, R>;
|
||||
onUnavailable?: () => void;
|
||||
onUnauthorized?: () => void;
|
||||
syncResolution?: boolean;
|
||||
},
|
||||
listener: () => void,
|
||||
) {
|
||||
subscriberCount++;
|
||||
|
||||
const unsubscribe = subscribeToCoValue(
|
||||
anySchemaToCoSchema(cls),
|
||||
id,
|
||||
{
|
||||
loadAs: options.loadAs,
|
||||
resolve: options.resolve as any,
|
||||
onUnavailable: () => {
|
||||
currentValue = null;
|
||||
options.onUnavailable?.();
|
||||
},
|
||||
onUnauthorized: () => {
|
||||
currentValue = null;
|
||||
options.onUnauthorized?.();
|
||||
},
|
||||
syncResolution: options.syncResolution,
|
||||
},
|
||||
(value) => {
|
||||
currentValue = value as Loaded<S, R>;
|
||||
listener();
|
||||
},
|
||||
);
|
||||
|
||||
return () => {
|
||||
unsubscribe();
|
||||
subscriberCount--;
|
||||
if (subscriberCount === 0) {
|
||||
currentValue = undefined;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const observable = {
|
||||
getCurrentValue: () => currentValue,
|
||||
subscribe,
|
||||
};
|
||||
|
||||
return observable;
|
||||
}
|
||||
|
||||
export function subscribeToExistingCoValue<
|
||||
V extends CoValue,
|
||||
const R extends RefsToResolve<V>,
|
||||
@@ -515,7 +448,7 @@ export function parseGroupCreateOptions(
|
||||
* ```
|
||||
*/
|
||||
export async function exportCoValue<
|
||||
S extends CoValueOrZodSchema | CoValueClass<CoValue>,
|
||||
S extends CoValueClassOrSchema,
|
||||
const R extends ResolveQuery<S>,
|
||||
>(
|
||||
cls: S,
|
||||
@@ -537,7 +470,7 @@ export async function exportCoValue<
|
||||
resolve as any,
|
||||
id,
|
||||
{
|
||||
ref: anySchemaToCoSchema(cls),
|
||||
ref: coValueClassFromCoValueClassOrSchema(cls),
|
||||
optional: false,
|
||||
},
|
||||
options.skipRetry,
|
||||
|
||||
@@ -8,19 +8,19 @@ import {
|
||||
} from "cojson";
|
||||
import z from "zod/v4";
|
||||
import {
|
||||
AnyCoMapSchema,
|
||||
AnyCoSchema,
|
||||
AnyZodOrCoValueSchema,
|
||||
CoMap,
|
||||
CoMapInitZod,
|
||||
CoMapSchema,
|
||||
CoMapSchemaInit,
|
||||
CoValueClass,
|
||||
CoreCoMapSchema,
|
||||
Group,
|
||||
Loaded,
|
||||
ResolveQuery,
|
||||
ResolveQueryStrict,
|
||||
Simplify,
|
||||
anySchemaToCoSchema,
|
||||
coMapDefiner,
|
||||
coValueClassFromCoValueClassOrSchema,
|
||||
exportCoValue,
|
||||
importContentPieces,
|
||||
loadCoValue,
|
||||
@@ -28,7 +28,7 @@ import {
|
||||
import { isCoValueId } from "../lib/id.js";
|
||||
import { Account } from "./account.js";
|
||||
|
||||
type MessageShape = Record<string, z.core.$ZodType | AnyCoSchema>;
|
||||
type MessageShape = Record<string, AnyZodOrCoValueSchema>;
|
||||
|
||||
type RequestSchemaDefinition<
|
||||
S extends MessageShape,
|
||||
@@ -93,11 +93,11 @@ type AsNullablePayload<T extends MessageShape> = T extends Record<string, never>
|
||||
? undefined
|
||||
: never;
|
||||
type MessageValuePayload<T extends MessageShape> =
|
||||
| Simplify<CoMapInitZod<T>>
|
||||
| Simplify<CoMapSchemaInit<T>>
|
||||
| AsNullablePayload<T>;
|
||||
|
||||
function createMessageEnvelope<S extends MessageShape>(
|
||||
schema: AnyCoMapSchema,
|
||||
schema: CoreCoMapSchema,
|
||||
value: MessageValuePayload<S>,
|
||||
owner: Account,
|
||||
sharedWith: Account | Group,
|
||||
@@ -111,7 +111,7 @@ function createMessageEnvelope<S extends MessageShape>(
|
||||
group.addMember(sharedWith, "reader");
|
||||
}
|
||||
|
||||
// @ts-expect-error - AnyCoMapSchema doesn't have static methods
|
||||
// @ts-expect-error - CoreCoMapSchema doesn't have static methods
|
||||
return schema.create(value ?? {}, group);
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ async function serializeMessagePayload({
|
||||
}: {
|
||||
// Skipping type validation here to avoid excessive type complexity that affects the typecheck performance
|
||||
type: "request" | "response";
|
||||
schema: AnyCoMapSchema;
|
||||
schema: CoreCoMapSchema;
|
||||
resolve: any;
|
||||
value: any;
|
||||
owner: Account;
|
||||
@@ -198,7 +198,7 @@ async function handleMessagePayload({
|
||||
}: {
|
||||
type: "request" | "response";
|
||||
// Skipping type validation here to avoid excessive type complexity that affects the typecheck performance
|
||||
schema: AnyCoMapSchema;
|
||||
schema: CoreCoMapSchema;
|
||||
resolve: any;
|
||||
request: unknown;
|
||||
loadAs: Account;
|
||||
@@ -290,7 +290,9 @@ async function handleMessagePayload({
|
||||
throw new JazzRequestError("Creator account not found", 400);
|
||||
}
|
||||
|
||||
const coSchema = anySchemaToCoSchema(schema) as CoValueClass<CoMap>;
|
||||
const coSchema = coValueClassFromCoValueClassOrSchema(
|
||||
schema,
|
||||
) as CoValueClass<CoMap>;
|
||||
const value = await loadCoValue<CoMap, true>(coSchema, requestData.id, {
|
||||
resolve,
|
||||
loadAs,
|
||||
@@ -317,13 +319,13 @@ function parseSchemaAndResolve<
|
||||
if ("schema" in options) {
|
||||
return {
|
||||
// Using a type cast to reduce the type complexity
|
||||
schema: coMapDefiner(options.schema) as AnyCoMapSchema,
|
||||
schema: coMapDefiner(options.schema) as CoreCoMapSchema,
|
||||
resolve: options.resolve as any,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
schema: coMapDefiner(options) as AnyCoMapSchema,
|
||||
schema: coMapDefiner(options) as CoreCoMapSchema,
|
||||
resolve: true as any,
|
||||
};
|
||||
}
|
||||
@@ -335,11 +337,11 @@ class HttpRoute<
|
||||
ResponseResolve extends ResolveQuery<CoMapSchema<ResponseShape>> = any,
|
||||
> {
|
||||
private requestDefinition: {
|
||||
schema: AnyCoMapSchema;
|
||||
schema: CoreCoMapSchema;
|
||||
resolve: any;
|
||||
};
|
||||
private responseDefinition: {
|
||||
schema: AnyCoMapSchema;
|
||||
schema: CoreCoMapSchema;
|
||||
resolve: any;
|
||||
};
|
||||
private url: string;
|
||||
|
||||
@@ -6,8 +6,6 @@ import {
|
||||
CoValueClass,
|
||||
CoValueFromRaw,
|
||||
ID,
|
||||
RefsToResolve,
|
||||
RefsToResolveStrict,
|
||||
Resolved,
|
||||
SubscribeListenerOptions,
|
||||
SubscribeRestArgs,
|
||||
@@ -16,6 +14,12 @@ import {
|
||||
subscribeToCoValueWithoutMe,
|
||||
} from "../internal.js";
|
||||
|
||||
/**
|
||||
* Extends `SchemaUnion` with a non-abstract constructor.
|
||||
*/
|
||||
export type SchemaUnionConcreteSubclass<V extends CoValue> =
|
||||
typeof SchemaUnion & CoValueClass<V>;
|
||||
|
||||
/**
|
||||
* SchemaUnion allows you to create union types of CoValues that can be discriminated at runtime.
|
||||
*
|
||||
@@ -86,7 +90,7 @@ export abstract class SchemaUnion extends CoValueBase implements CoValue {
|
||||
**/
|
||||
static Of<V extends CoValue>(
|
||||
discriminator: (raw: V["_raw"]) => CoValueClass<V> & CoValueFromRaw<V>,
|
||||
): CoValueClass<V> & typeof SchemaUnion {
|
||||
): SchemaUnionConcreteSubclass<V> {
|
||||
return class SchemaUnionClass extends SchemaUnion {
|
||||
static override fromRaw<T extends CoValue>(
|
||||
this: CoValueClass<T> & CoValueFromRaw<T>,
|
||||
@@ -97,7 +101,7 @@ export abstract class SchemaUnion extends CoValueBase implements CoValue {
|
||||
) as unknown as CoValueClass<T> & CoValueFromRaw<T>;
|
||||
return ResolvedClass.fromRaw(raw);
|
||||
}
|
||||
} as unknown as CoValueClass<V> & typeof SchemaUnion;
|
||||
} as unknown as SchemaUnionConcreteSubclass<V>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -35,7 +35,6 @@ export type {
|
||||
TextPos,
|
||||
AccountClass,
|
||||
AccountCreationProps,
|
||||
WithHelpers,
|
||||
} from "./internal.js";
|
||||
|
||||
export {
|
||||
@@ -49,7 +48,6 @@ export {
|
||||
CoRichText,
|
||||
Account,
|
||||
isControlledAccount,
|
||||
createCoValueObservable,
|
||||
loadCoValue,
|
||||
subscribeToCoValue,
|
||||
ImageDefinition,
|
||||
@@ -99,26 +97,17 @@ export {
|
||||
export type * from "./types.js";
|
||||
|
||||
export {
|
||||
zodSchemaToCoSchema,
|
||||
anySchemaToCoSchema,
|
||||
coValueClassFromCoValueClassOrSchema,
|
||||
type InstanceOfSchema,
|
||||
type InstanceOfSchemaCoValuesNullable,
|
||||
type CoValueOrZodSchema,
|
||||
type CoValueClassOrSchema,
|
||||
type Loaded,
|
||||
type BaseAccountShape,
|
||||
type DefaultAccountShape,
|
||||
type AccountSchema,
|
||||
type AnyAccountSchema,
|
||||
type CoListSchema,
|
||||
type CoMapSchema,
|
||||
type CoFeedSchema,
|
||||
type PlainTextSchema,
|
||||
type FileStreamSchema,
|
||||
type CoreAccountSchema as AnyAccountSchema,
|
||||
type ResolveQuery,
|
||||
type ResolveQueryStrict,
|
||||
type InitFor,
|
||||
type CoRecordSchema,
|
||||
type CoProfileSchema,
|
||||
} from "./internal.js";
|
||||
|
||||
export {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import {
|
||||
AgentSecret,
|
||||
CoID,
|
||||
ControlledAgent,
|
||||
CryptoProvider,
|
||||
LocalNode,
|
||||
Peer,
|
||||
@@ -14,12 +13,11 @@ import { AuthSecretStorage } from "../auth/AuthSecretStorage.js";
|
||||
import { type Account, type AccountClass } from "../coValues/account.js";
|
||||
import { RegisteredSchemas } from "../coValues/registeredSchemas.js";
|
||||
import {
|
||||
type AccountSchema,
|
||||
type AnyAccountSchema,
|
||||
CoValueFromRaw,
|
||||
type CoreAccountSchema,
|
||||
type ID,
|
||||
type InstanceOfSchema,
|
||||
anySchemaToCoSchema,
|
||||
coValueClassFromCoValueClassOrSchema,
|
||||
} from "../internal.js";
|
||||
import { AuthCredentials, NewAccountProps } from "../types.js";
|
||||
import { activeAccountContext } from "./activeAccountContext.js";
|
||||
@@ -89,7 +87,7 @@ export type JazzContext<Acc extends Account> =
|
||||
export async function createJazzContextFromExistingCredentials<
|
||||
S extends
|
||||
| (AccountClass<Account> & CoValueFromRaw<Account>)
|
||||
| AnyAccountSchema,
|
||||
| CoreAccountSchema,
|
||||
>({
|
||||
credentials,
|
||||
peersToLoadFrom,
|
||||
@@ -115,7 +113,8 @@ export async function createJazzContextFromExistingCredentials<
|
||||
const CurrentAccountSchema =
|
||||
PropsAccountSchema ?? (RegisteredSchemas["Account"] as unknown as S);
|
||||
|
||||
const AccountClass = anySchemaToCoSchema(CurrentAccountSchema);
|
||||
const AccountClass =
|
||||
coValueClassFromCoValueClassOrSchema(CurrentAccountSchema);
|
||||
|
||||
const node = await LocalNode.withLoadedAccount({
|
||||
accountID: credentials.accountID as unknown as CoID<RawAccount>,
|
||||
@@ -153,7 +152,7 @@ export async function createJazzContextFromExistingCredentials<
|
||||
export async function createJazzContextForNewAccount<
|
||||
S extends
|
||||
| (AccountClass<Account> & CoValueFromRaw<Account>)
|
||||
| AnyAccountSchema,
|
||||
| CoreAccountSchema,
|
||||
>({
|
||||
creationProps,
|
||||
initialAgentSecret,
|
||||
@@ -174,7 +173,8 @@ export async function createJazzContextForNewAccount<
|
||||
const CurrentAccountSchema =
|
||||
PropsAccountSchema ?? (RegisteredSchemas["Account"] as unknown as S);
|
||||
|
||||
const AccountClass = anySchemaToCoSchema(CurrentAccountSchema);
|
||||
const AccountClass =
|
||||
coValueClassFromCoValueClassOrSchema(CurrentAccountSchema);
|
||||
|
||||
const { node } = await LocalNode.withNewlyCreatedAccount({
|
||||
creationProps,
|
||||
@@ -209,7 +209,7 @@ export async function createJazzContextForNewAccount<
|
||||
export async function createJazzContext<
|
||||
S extends
|
||||
| (AccountClass<Account> & CoValueFromRaw<Account>)
|
||||
| AnyAccountSchema,
|
||||
| CoreAccountSchema,
|
||||
>(options: {
|
||||
credentials?: AuthCredentials;
|
||||
newAccountProps?: NewAccountProps;
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Account } from "../coValues/account.js";
|
||||
import type {
|
||||
CoValue,
|
||||
CoValueClass,
|
||||
CoValueOrZodSchema,
|
||||
CoValueClassOrSchema,
|
||||
ID,
|
||||
} from "../internal.js";
|
||||
|
||||
@@ -68,7 +68,7 @@ export function parseInviteLink(inviteURL: string):
|
||||
}
|
||||
|
||||
/** @category Invite Links */
|
||||
export function consumeInviteLink<S extends CoValueOrZodSchema>({
|
||||
export function consumeInviteLink<S extends CoValueClassOrSchema>({
|
||||
inviteURL,
|
||||
as = Account.getMe(),
|
||||
forValueHint,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user