Compare commits
31 Commits
jazz-bette
...
jazz-inspe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0088aa8b25 | ||
|
|
35a66df1e4 | ||
|
|
3b2fa64a82 | ||
|
|
3d1027f278 | ||
|
|
cc78386163 | ||
|
|
c240eed6a4 | ||
|
|
2a9b7f5d52 | ||
|
|
f2fbd29de5 | ||
|
|
c960176a2a | ||
|
|
fd4bae4cc1 | ||
|
|
ae32b7c19b | ||
|
|
83986c6699 | ||
|
|
59a4e2cee3 | ||
|
|
d2a971c86c | ||
|
|
aae9ef49da | ||
|
|
58c5ee5c73 | ||
|
|
f1d6097ee6 | ||
|
|
3172a61543 | ||
|
|
25324c28d9 | ||
|
|
f74cb4885a | ||
|
|
4130213d82 | ||
|
|
c36a26e669 | ||
|
|
c8b33ad7f1 | ||
|
|
a79489683e | ||
|
|
1251fb89b1 | ||
|
|
cdfc10557a | ||
|
|
6e0481114c | ||
|
|
f7e157d2f5 | ||
|
|
ce83f101c7 | ||
|
|
6efdfef386 | ||
|
|
6528d350ec |
@@ -1,5 +1,30 @@
|
||||
# betterauth
|
||||
|
||||
## 0.1.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3d1027f]
|
||||
- Updated dependencies [c240eed]
|
||||
- jazz-tools@0.14.2
|
||||
- jazz-betterauth-server-plugin@0.14.2
|
||||
- jazz-inspector@0.14.2
|
||||
- jazz-react@0.14.2
|
||||
- jazz-react-auth-betterauth@0.14.2
|
||||
- jazz-betterauth-client-plugin@0.14.2
|
||||
|
||||
## 0.1.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [cdfc105]
|
||||
- jazz-tools@0.14.1
|
||||
- jazz-betterauth-server-plugin@0.14.1
|
||||
- jazz-inspector@0.14.1
|
||||
- jazz-react@0.14.1
|
||||
- jazz-react-auth-betterauth@0.14.1
|
||||
- jazz-betterauth-client-plugin@0.14.1
|
||||
|
||||
## 0.1.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "betterauth",
|
||||
"version": "0.1.5",
|
||||
"version": "0.1.7",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
# chat-rn-expo-clerk
|
||||
|
||||
## 1.0.126
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3d1027f]
|
||||
- Updated dependencies [c240eed]
|
||||
- jazz-tools@0.14.2
|
||||
- jazz-expo@0.14.2
|
||||
- jazz-react-native-media-images@0.14.2
|
||||
|
||||
## 1.0.125
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [cdfc105]
|
||||
- jazz-tools@0.14.1
|
||||
- jazz-expo@0.14.1
|
||||
- jazz-react-native-media-images@0.14.1
|
||||
|
||||
## 1.0.124
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "chat-rn-expo-clerk",
|
||||
"main": "index.js",
|
||||
"version": "1.0.124",
|
||||
"version": "1.0.126",
|
||||
"scripts": {
|
||||
"build": "expo export -p ios",
|
||||
"start": "expo start",
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
# chat-rn-expo
|
||||
|
||||
## 1.0.113
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3d1027f]
|
||||
- Updated dependencies [c240eed]
|
||||
- jazz-tools@0.14.2
|
||||
- jazz-expo@0.14.2
|
||||
|
||||
## 1.0.112
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [cdfc105]
|
||||
- jazz-tools@0.14.1
|
||||
- jazz-expo@0.14.1
|
||||
|
||||
## 1.0.111
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "chat-rn-expo",
|
||||
"version": "1.0.111",
|
||||
"version": "1.0.113",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "tsc --noEmit && expo export -p ios",
|
||||
|
||||
@@ -1,5 +1,25 @@
|
||||
# chat-rn
|
||||
|
||||
## 1.0.121
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3d1027f]
|
||||
- Updated dependencies [c240eed]
|
||||
- jazz-tools@0.14.2
|
||||
- jazz-react-native@0.14.2
|
||||
|
||||
## 1.0.120
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [c8b33ad]
|
||||
- Updated dependencies [cdfc105]
|
||||
- cojson@0.14.1
|
||||
- jazz-tools@0.14.1
|
||||
- cojson-transport-ws@0.14.1
|
||||
- jazz-react-native@0.14.1
|
||||
|
||||
## 1.0.119
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "chat-rn",
|
||||
"version": "1.0.119",
|
||||
"version": "1.0.121",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"android": "react-native run-android",
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
# chat-vue
|
||||
|
||||
## 0.0.104
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3d1027f]
|
||||
- Updated dependencies [c240eed]
|
||||
- jazz-tools@0.14.2
|
||||
- jazz-browser@0.14.2
|
||||
- jazz-vue@0.14.2
|
||||
|
||||
## 0.0.103
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [cdfc105]
|
||||
- jazz-tools@0.14.1
|
||||
- jazz-browser@0.14.1
|
||||
- jazz-vue@0.14.1
|
||||
|
||||
## 0.0.102
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "chat-vue",
|
||||
"version": "0.0.102",
|
||||
"version": "0.0.104",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
# jazz-example-chat
|
||||
|
||||
## 0.0.202
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3d1027f]
|
||||
- Updated dependencies [c240eed]
|
||||
- jazz-tools@0.14.2
|
||||
- jazz-inspector@0.14.2
|
||||
- jazz-react@0.14.2
|
||||
|
||||
## 0.0.201
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [cdfc105]
|
||||
- jazz-tools@0.14.1
|
||||
- jazz-inspector@0.14.1
|
||||
- jazz-react@0.14.1
|
||||
|
||||
## 0.0.200
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-example-chat",
|
||||
"private": true,
|
||||
"version": "0.0.200",
|
||||
"version": "0.0.202",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
# minimal-auth-clerk
|
||||
|
||||
## 0.0.101
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3d1027f]
|
||||
- Updated dependencies [c240eed]
|
||||
- jazz-tools@0.14.2
|
||||
- jazz-react@0.14.2
|
||||
- jazz-react-auth-clerk@0.14.2
|
||||
|
||||
## 0.0.100
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [cdfc105]
|
||||
- jazz-tools@0.14.1
|
||||
- jazz-react@0.14.1
|
||||
- jazz-react-auth-clerk@0.14.1
|
||||
|
||||
## 0.0.99
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "clerk",
|
||||
"private": true,
|
||||
"version": "0.0.99",
|
||||
"version": "0.0.101",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
# file-share-svelte
|
||||
|
||||
## 0.0.85
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3d1027f]
|
||||
- Updated dependencies [c240eed]
|
||||
- jazz-tools@0.14.2
|
||||
- jazz-inspector-element@0.14.2
|
||||
- jazz-svelte@0.14.2
|
||||
|
||||
## 0.0.84
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [cdfc105]
|
||||
- jazz-tools@0.14.1
|
||||
- jazz-svelte@0.14.1
|
||||
- jazz-inspector-element@0.14.1
|
||||
|
||||
## 0.0.83
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "file-share-svelte",
|
||||
"version": "0.0.83",
|
||||
"version": "0.0.85",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
# jazz-tailwind-demo-auth-starter
|
||||
|
||||
## 0.0.41
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3d1027f]
|
||||
- Updated dependencies [c240eed]
|
||||
- jazz-tools@0.14.2
|
||||
- jazz-inspector@0.14.2
|
||||
- jazz-react@0.14.2
|
||||
|
||||
## 0.0.40
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [cdfc105]
|
||||
- jazz-tools@0.14.1
|
||||
- jazz-inspector@0.14.1
|
||||
- jazz-react@0.14.1
|
||||
|
||||
## 0.0.39
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "filestream",
|
||||
"private": true,
|
||||
"version": "0.0.39",
|
||||
"version": "0.0.41",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
# form
|
||||
|
||||
## 0.1.42
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3d1027f]
|
||||
- Updated dependencies [c240eed]
|
||||
- jazz-tools@0.14.2
|
||||
- jazz-react@0.14.2
|
||||
|
||||
## 0.1.41
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [cdfc105]
|
||||
- jazz-tools@0.14.1
|
||||
- jazz-react@0.14.1
|
||||
|
||||
## 0.1.40
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "form",
|
||||
"private": true,
|
||||
"version": "0.1.40",
|
||||
"version": "0.1.42",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -80,6 +80,7 @@ export const JazzAccount = co
|
||||
const draft = DraftBubbleTeaOrder.create(
|
||||
{
|
||||
addOns: ListOfBubbleTeaAddOns.create([], account),
|
||||
instructions: co.plainText().create("", account),
|
||||
},
|
||||
account,
|
||||
);
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
# image-upload
|
||||
|
||||
## 0.0.98
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3d1027f]
|
||||
- Updated dependencies [c240eed]
|
||||
- jazz-tools@0.14.2
|
||||
- jazz-react@0.14.2
|
||||
|
||||
## 0.0.97
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [cdfc105]
|
||||
- jazz-tools@0.14.1
|
||||
- jazz-react@0.14.1
|
||||
|
||||
## 0.0.96
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "image-upload",
|
||||
"private": true,
|
||||
"version": "0.0.96",
|
||||
"version": "0.0.98",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
# jazz-example-inspector
|
||||
|
||||
## 0.0.151
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-inspector@0.14.2
|
||||
|
||||
## 0.0.150
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [c8b33ad]
|
||||
- cojson@0.14.1
|
||||
- cojson-transport-ws@0.14.1
|
||||
- jazz-inspector@0.14.1
|
||||
|
||||
## 0.0.149
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-inspector-app",
|
||||
"private": true,
|
||||
"version": "0.0.149",
|
||||
"version": "0.0.151",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
# multi-cursors
|
||||
|
||||
## 0.0.94
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3d1027f]
|
||||
- Updated dependencies [c240eed]
|
||||
- jazz-tools@0.14.2
|
||||
- jazz-react@0.14.2
|
||||
|
||||
## 0.0.93
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [cdfc105]
|
||||
- jazz-tools@0.14.1
|
||||
- jazz-react@0.14.1
|
||||
|
||||
## 0.0.92
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "multi-cursors",
|
||||
"private": true,
|
||||
"version": "0.0.92",
|
||||
"version": "0.0.94",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
# multiauth
|
||||
|
||||
## 0.0.42
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3d1027f]
|
||||
- Updated dependencies [c240eed]
|
||||
- jazz-tools@0.14.2
|
||||
- jazz-react@0.14.2
|
||||
- jazz-react-auth-clerk@0.14.2
|
||||
|
||||
## 0.0.41
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [cdfc105]
|
||||
- jazz-tools@0.14.1
|
||||
- jazz-react@0.14.1
|
||||
- jazz-react-auth-clerk@0.14.1
|
||||
|
||||
## 0.0.40
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "multiauth",
|
||||
"private": true,
|
||||
"version": "0.0.40",
|
||||
"version": "0.0.42",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
# jazz-example-musicplayer
|
||||
|
||||
## 0.0.123
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3d1027f]
|
||||
- Updated dependencies [c240eed]
|
||||
- jazz-tools@0.14.2
|
||||
- jazz-inspector@0.14.2
|
||||
- jazz-react@0.14.2
|
||||
|
||||
## 0.0.122
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [cdfc105]
|
||||
- jazz-tools@0.14.1
|
||||
- jazz-inspector@0.14.1
|
||||
- jazz-react@0.14.1
|
||||
|
||||
## 0.0.121
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-example-music-player",
|
||||
"private": true,
|
||||
"version": "0.0.121",
|
||||
"version": "0.0.123",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
# organization
|
||||
|
||||
## 0.0.94
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3d1027f]
|
||||
- Updated dependencies [c240eed]
|
||||
- jazz-tools@0.14.2
|
||||
- jazz-react@0.14.2
|
||||
|
||||
## 0.0.93
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [cdfc105]
|
||||
- jazz-tools@0.14.1
|
||||
- jazz-react@0.14.1
|
||||
|
||||
## 0.0.92
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "organization",
|
||||
"private": true,
|
||||
"version": "0.0.92",
|
||||
"version": "0.0.94",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useAccount, useCoState } from "jazz-react";
|
||||
import { Account, Group, ID, Loaded } from "jazz-tools";
|
||||
import { useAccount } from "jazz-react";
|
||||
import { Account, Group, Loaded } from "jazz-tools";
|
||||
import { Organization } from "../schema.ts";
|
||||
|
||||
export function OrganizationMembers({
|
||||
@@ -12,7 +12,7 @@ export function OrganizationMembers({
|
||||
{group.members.map((member) => (
|
||||
<MemberItem
|
||||
key={member.id}
|
||||
accountId={member.account.id}
|
||||
account={member.account}
|
||||
role={member.role}
|
||||
group={group}
|
||||
/>
|
||||
@@ -22,18 +22,13 @@ export function OrganizationMembers({
|
||||
}
|
||||
|
||||
function MemberItem({
|
||||
accountId,
|
||||
account,
|
||||
role,
|
||||
group,
|
||||
}: { accountId: ID<Account>; role: string; group: Group }) {
|
||||
const account = useCoState(Account, accountId, {
|
||||
resolve: {
|
||||
profile: true,
|
||||
},
|
||||
});
|
||||
}: { account: Account; role: string; group: Group }) {
|
||||
const { me } = useAccount();
|
||||
|
||||
const canRemoveMember = group.myRole() === "admin" && accountId !== me?.id;
|
||||
const canRemoveMember = group.myRole() === "admin" && account.id !== me?.id;
|
||||
|
||||
function handleRemoveMember() {
|
||||
if (canRemoveMember && account) {
|
||||
@@ -44,7 +39,7 @@ function MemberItem({
|
||||
return (
|
||||
<div className="px-4 py-5 sm:px-6 flex justify-between items-center">
|
||||
<div>
|
||||
<strong className="font-medium">{account?.profile.name}</strong> ({role}
|
||||
<strong className="font-medium">{account.profile?.name}</strong> ({role}
|
||||
)
|
||||
</div>
|
||||
{canRemoveMember && (
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
# passkey-svelte
|
||||
|
||||
## 0.0.89
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3d1027f]
|
||||
- Updated dependencies [c240eed]
|
||||
- jazz-tools@0.14.2
|
||||
- jazz-svelte@0.14.2
|
||||
|
||||
## 0.0.88
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [cdfc105]
|
||||
- jazz-tools@0.14.1
|
||||
- jazz-svelte@0.14.1
|
||||
|
||||
## 0.0.87
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "passkey-svelte",
|
||||
"version": "0.0.87",
|
||||
"version": "0.0.89",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
# minimal-auth-passkey
|
||||
|
||||
## 0.0.99
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3d1027f]
|
||||
- Updated dependencies [c240eed]
|
||||
- jazz-tools@0.14.2
|
||||
- jazz-react@0.14.2
|
||||
|
||||
## 0.0.98
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [cdfc105]
|
||||
- jazz-tools@0.14.1
|
||||
- jazz-react@0.14.1
|
||||
|
||||
## 0.0.97
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "passkey",
|
||||
"private": true,
|
||||
"version": "0.0.97",
|
||||
"version": "0.0.99",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
# passphrase
|
||||
|
||||
## 0.0.96
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3d1027f]
|
||||
- Updated dependencies [c240eed]
|
||||
- jazz-tools@0.14.2
|
||||
- jazz-react@0.14.2
|
||||
|
||||
## 0.0.95
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [cdfc105]
|
||||
- jazz-tools@0.14.1
|
||||
- jazz-react@0.14.1
|
||||
|
||||
## 0.0.94
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "passphrase",
|
||||
"private": true,
|
||||
"version": "0.0.94",
|
||||
"version": "0.0.96",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
# jazz-password-manager
|
||||
|
||||
## 0.0.120
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3d1027f]
|
||||
- Updated dependencies [c240eed]
|
||||
- jazz-tools@0.14.2
|
||||
- jazz-react@0.14.2
|
||||
|
||||
## 0.0.119
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [cdfc105]
|
||||
- jazz-tools@0.14.1
|
||||
- jazz-react@0.14.1
|
||||
|
||||
## 0.0.118
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-password-manager",
|
||||
"private": true,
|
||||
"version": "0.0.118",
|
||||
"version": "0.0.120",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
# jazz-example-pets
|
||||
|
||||
## 0.0.218
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3d1027f]
|
||||
- Updated dependencies [c240eed]
|
||||
- jazz-tools@0.14.2
|
||||
- jazz-react@0.14.2
|
||||
|
||||
## 0.0.217
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [cdfc105]
|
||||
- jazz-tools@0.14.1
|
||||
- jazz-react@0.14.1
|
||||
|
||||
## 0.0.216
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-example-pets",
|
||||
"private": true,
|
||||
"version": "0.0.216",
|
||||
"version": "0.0.218",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
# reactions
|
||||
|
||||
## 0.0.98
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3d1027f]
|
||||
- Updated dependencies [c240eed]
|
||||
- jazz-tools@0.14.2
|
||||
- jazz-react@0.14.2
|
||||
|
||||
## 0.0.97
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [cdfc105]
|
||||
- jazz-tools@0.14.1
|
||||
- jazz-react@0.14.1
|
||||
|
||||
## 0.0.96
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "reactions",
|
||||
"private": true,
|
||||
"version": "0.0.96",
|
||||
"version": "0.0.98",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
# richtext-tiptap
|
||||
|
||||
## 0.1.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3d1027f]
|
||||
- Updated dependencies [c240eed]
|
||||
- jazz-tools@0.14.2
|
||||
- jazz-react@0.14.2
|
||||
- jazz-richtext-tiptap@0.1.11
|
||||
|
||||
## 0.1.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [cdfc105]
|
||||
- jazz-tools@0.14.1
|
||||
- jazz-react@0.14.1
|
||||
- jazz-richtext-tiptap@0.1.10
|
||||
|
||||
## 0.1.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "richtext-tiptap",
|
||||
"private": true,
|
||||
"version": "0.1.9",
|
||||
"version": "0.1.11",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
# richtext
|
||||
|
||||
## 0.0.88
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3d1027f]
|
||||
- Updated dependencies [c240eed]
|
||||
- jazz-tools@0.14.2
|
||||
- jazz-react@0.14.2
|
||||
- jazz-richtext-prosemirror@0.1.22
|
||||
|
||||
## 0.0.87
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [cdfc105]
|
||||
- jazz-tools@0.14.1
|
||||
- jazz-react@0.14.1
|
||||
- jazz-richtext-prosemirror@0.1.21
|
||||
|
||||
## 0.0.86
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "richtext",
|
||||
"private": true,
|
||||
"version": "0.0.86",
|
||||
"version": "0.0.88",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
# todo-vue
|
||||
|
||||
## 0.0.102
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3d1027f]
|
||||
- Updated dependencies [c240eed]
|
||||
- jazz-tools@0.14.2
|
||||
- jazz-browser@0.14.2
|
||||
- jazz-vue@0.14.2
|
||||
|
||||
## 0.0.101
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [cdfc105]
|
||||
- jazz-tools@0.14.1
|
||||
- jazz-browser@0.14.1
|
||||
- jazz-vue@0.14.1
|
||||
|
||||
## 0.0.100
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "todo-vue",
|
||||
"version": "0.0.100",
|
||||
"version": "0.0.102",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
# jazz-example-todo
|
||||
|
||||
## 0.0.217
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3d1027f]
|
||||
- Updated dependencies [c240eed]
|
||||
- jazz-tools@0.14.2
|
||||
- jazz-react@0.14.2
|
||||
|
||||
## 0.0.216
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [cdfc105]
|
||||
- jazz-tools@0.14.1
|
||||
- jazz-react@0.14.1
|
||||
|
||||
## 0.0.215
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-example-todo",
|
||||
"private": true,
|
||||
"version": "0.0.215",
|
||||
"version": "0.0.217",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
# version-history
|
||||
|
||||
## 0.0.96
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3d1027f]
|
||||
- Updated dependencies [c240eed]
|
||||
- jazz-tools@0.14.2
|
||||
- jazz-inspector@0.14.2
|
||||
- jazz-react@0.14.2
|
||||
|
||||
## 0.0.95
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [cdfc105]
|
||||
- jazz-tools@0.14.1
|
||||
- jazz-inspector@0.14.1
|
||||
- jazz-react@0.14.1
|
||||
|
||||
## 0.0.94
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "version-history",
|
||||
"private": true,
|
||||
"version": "0.0.94",
|
||||
"version": "0.0.96",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -87,3 +87,14 @@ html.dark {
|
||||
}
|
||||
|
||||
@import "@shikijs/twoslash/style-rich.css";
|
||||
|
||||
/* fix for https://github.com/garden-co/jazz/issues/2257*/
|
||||
.twoslash .twoslash-popup-container {
|
||||
display: none;
|
||||
}
|
||||
.twoslash .twoslash-hover:hover .twoslash-popup-container,
|
||||
.twoslash .twoslash-error-hover:hover .twoslash-popup-container,
|
||||
.twoslash .twoslash-query-presisted .twoslash-popup-container,
|
||||
.twoslash .twoslash-query-line .twoslash-popup-container {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
@@ -4,12 +4,13 @@ import { SideNav, SideNavBody, SideNavHeader } from "@/components/SideNav";
|
||||
import { SideNavSection } from "@/components/SideNavSection";
|
||||
import { FrameworkSelect } from "@/components/docs/FrameworkSelect";
|
||||
import { docNavigationItems } from "@/content/docs/docNavigationItems";
|
||||
import { DocNavigationSection } from "@/content/docs/docNavigationItemsTypes";
|
||||
import { useFramework } from "@/lib/use-framework";
|
||||
import React from "react";
|
||||
|
||||
export function DocNav() {
|
||||
const framework = useFramework();
|
||||
const items = docNavigationItems.map((headerItem) => {
|
||||
const items = (docNavigationItems as DocNavigationSection[]).map((headerItem) => {
|
||||
return {
|
||||
...headerItem,
|
||||
items: headerItem.items
|
||||
|
||||
@@ -2,6 +2,7 @@ import {
|
||||
docNavigationItems,
|
||||
flatItemsWithNavLinks,
|
||||
} from "@/content/docs/docNavigationItems";
|
||||
import { DocNavigationItem } from "@/content/docs/docNavigationItemsTypes";
|
||||
import { Icon } from "@garden-co/design-system/src/components/atoms/Icon";
|
||||
import { Separator } from "@garden-co/design-system/src/components/atoms/Separator";
|
||||
import Link from "next/link";
|
||||
@@ -18,7 +19,7 @@ export function PreviousNextLinks({ slug, framework }: PreviousNextLinksProps) {
|
||||
? `/docs/${framework}/${slug.join("/")}`
|
||||
: `/docs/${framework}`;
|
||||
return currentPath === itemPath;
|
||||
});
|
||||
}) as DocNavigationItem;
|
||||
|
||||
if (
|
||||
currentItem?.excludeFromNavigation ||
|
||||
|
||||
@@ -4,22 +4,22 @@ export const metadata = {
|
||||
|
||||
import { CodeGroup } from "@/components/forMdx";
|
||||
|
||||
# Creating and updating CoValues in a form
|
||||
# How to write autosaving forms to create and update CoValues
|
||||
|
||||
Normally, we implement forms using
|
||||
[the onSubmit handler](https://react.dev/reference/react-dom/components/form#handle-form-submission-on-the-client),
|
||||
or by making [a controlled form with useState](https://christinakozanian.medium.com/building-controlled-forms-with-usestate-in-react-f9053ad255a0),
|
||||
or by using special libraries like [react-hook-form](https://www.react-hook-form.com).
|
||||
This guide shows you a simple and powerful way to implement forms for creating and updating CoValues.
|
||||
|
||||
In Jazz, we can do something simpler and more powerful, because CoValues give us reactive,
|
||||
persisted state which we can use to directly edit live objects, and represent auto-saved drafts.
|
||||
We'll build:
|
||||
1. An update form that saves changes as you make them, removing the need for a save button.
|
||||
2. A create form that autosaves your changes into a draft, so you can come back to it later.
|
||||
|
||||
[See the full example here.](https://github.com/garden-co/jazz/tree/main/examples/form)
|
||||
|
||||
**Note**: If you do need a save button on your update form, this guide is not for you. Another option
|
||||
is to use [react-hook-form](https://www.react-hook-form.com), which you can see in [this example](https://github.com/garden-co/jazz/tree/main/examples/password-manager).
|
||||
|
||||
## Updating a CoValue
|
||||
|
||||
To update a CoValue, we simply assign the new value directly as changes happen. These changes are synced to the server, so
|
||||
we don't need to handle form submissions either.
|
||||
To update a CoValue, we simply assign the new value directly as changes happen. These changes are synced to the server.
|
||||
|
||||
<CodeGroup>
|
||||
```tsx
|
||||
@@ -31,7 +31,7 @@ we don't need to handle form submissions either.
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
This means we can write update forms in fewer lines of code.
|
||||
It's that simple!
|
||||
|
||||
## Creating a CoValue
|
||||
|
||||
|
||||
@@ -4,10 +4,9 @@ export const metadata = {
|
||||
|
||||
import { CodeGroup } from "@/components/forMdx";
|
||||
|
||||
# Sharing data through Organizations
|
||||
# How to share data between users through Organizations
|
||||
|
||||
Organizations are a way to share a set of data between users.
|
||||
Different apps have different names for this concept, such as "teams" or "workspaces".
|
||||
This guide shows you how to share a set of CoValues between users. Different apps have different names for this concept, such as "teams" or "workspaces".
|
||||
|
||||
We'll use the term Organization.
|
||||
|
||||
@@ -104,12 +103,16 @@ This schema now allows users to create `Organization`s and add `Project`s to the
|
||||
|
||||
[See the schema for the example app here.](https://github.com/garden-co/jazz/blob/main/examples/organization/src/schema.ts)
|
||||
|
||||
## Adding other users to an Organization
|
||||
## Adding members to an Organization
|
||||
|
||||
To give users access to an `Organization`, you can either send them an invite link, or
|
||||
add their `Account` manually.
|
||||
Here are different ways to add members to an `Organization`.
|
||||
|
||||
### Adding users through invite links
|
||||
- Send users an invite link.
|
||||
- [The user requests to join.](/docs/groups/sharing#requesting-invites)
|
||||
|
||||
This guide and the example app show you the first method.
|
||||
|
||||
### Adding members through invite links
|
||||
|
||||
Here's how you can generate an [invite link](/docs/groups/sharing#invites).
|
||||
|
||||
@@ -180,6 +183,7 @@ export function AcceptInvitePage() {
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### Adding users through their Account ID
|
||||
## Further reading
|
||||
|
||||
...more on this coming soon
|
||||
- [Allowing users to request an invite to join a Group](/docs/groups/sharing#requesting-invites)
|
||||
- [Groups as permission scopes](/docs/groups/intro#adding-group-members-by-id)
|
||||
|
||||
@@ -1,27 +1,5 @@
|
||||
import { Framework } from "../framework";
|
||||
|
||||
export type DoneStatus =
|
||||
| number // represents percentage done
|
||||
| Partial<Record<Framework, number>>;
|
||||
|
||||
export type DocNavigationItem = {
|
||||
name: string;
|
||||
href: string;
|
||||
done: DoneStatus;
|
||||
framework?: Framework;
|
||||
next?: DocNavigationItem | null;
|
||||
previous?: DocNavigationItem | null;
|
||||
excludeFromNavigation?: boolean;
|
||||
};
|
||||
|
||||
export type DocNavigationSection = {
|
||||
name: string;
|
||||
items: DocNavigationItem[];
|
||||
collapse?: boolean;
|
||||
prefix?: string;
|
||||
};
|
||||
|
||||
export const docNavigationItems: DocNavigationSection[] = [
|
||||
/** @satisfies {DocNavigationSection[]} */
|
||||
export const docNavigationItems = [
|
||||
{
|
||||
// welcome to jazz
|
||||
name: "Getting started",
|
||||
@@ -81,6 +59,7 @@ export const docNavigationItems: DocNavigationSection[] = [
|
||||
react: 100,
|
||||
"react-native": 100,
|
||||
"react-native-expo": 100,
|
||||
svelte: 100,
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -159,7 +138,7 @@ export const docNavigationItems: DocNavigationSection[] = [
|
||||
name: "0.9.2 - Local persistence on React Native Expo",
|
||||
href: "/docs/upgrade/react-native-local-persistence",
|
||||
done: 100,
|
||||
framework: Framework.ReactNativeExpo,
|
||||
framework: "react-native-expo",
|
||||
excludeFromNavigation: true,
|
||||
},
|
||||
// {
|
||||
@@ -308,7 +287,7 @@ export const docNavigationItems: DocNavigationSection[] = [
|
||||
name: "Design patterns",
|
||||
items: [
|
||||
{
|
||||
name: "Form",
|
||||
name: "Autosaving forms",
|
||||
href: "/docs/design-patterns/form",
|
||||
done: 100,
|
||||
},
|
||||
22
homepage/homepage/content/docs/docNavigationItemsTypes.ts
Normal file
22
homepage/homepage/content/docs/docNavigationItemsTypes.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Framework } from "../framework";
|
||||
|
||||
export type DoneStatus =
|
||||
| number // represents percentage done
|
||||
| Partial<Record<Framework, number>>;
|
||||
|
||||
export type DocNavigationItem = {
|
||||
name: string;
|
||||
href: string;
|
||||
done: DoneStatus;
|
||||
framework?: Framework;
|
||||
next?: DocNavigationItem | null;
|
||||
previous?: DocNavigationItem | null;
|
||||
excludeFromNavigation?: boolean;
|
||||
};
|
||||
|
||||
export type DocNavigationSection = {
|
||||
name: string;
|
||||
items: DocNavigationItem[];
|
||||
collapse?: boolean;
|
||||
prefix?: string;
|
||||
};
|
||||
@@ -0,0 +1,162 @@
|
||||
export const metadata = {
|
||||
description:
|
||||
"Configure your JazzProvider - the core component that connects your app to Jazz, handling sync, storage, account schema, and auth.",
|
||||
};
|
||||
|
||||
import { CodeGroup } from "@/components/forMdx";
|
||||
|
||||
# Providers
|
||||
|
||||
`<JazzProvider />` is the core component that connects your Svelte application to Jazz. It handles:
|
||||
|
||||
- **Data Synchronization**: Manages connections to peers and the Jazz cloud
|
||||
- **Local Storage**: Persists data locally between app sessions
|
||||
- **Schema Types**: Provides APIs for the [AccountSchema](/docs/schemas/accounts-and-migrations)
|
||||
- **Authentication**: Connects your authentication system to Jazz
|
||||
|
||||
Our [File Share example app](https://github.com/garden-co/jazz/blob/main/examples/file-share-svelte/src/routes/%2Blayout.svelte) provides an implementation of JazzProvider with authentication and real-time data sync.
|
||||
|
||||
## Setting up the Provider
|
||||
|
||||
The `<JazzProvider />` accepts several configuration options:
|
||||
|
||||
<CodeGroup>
|
||||
```svelte
|
||||
<!-- src/routes/+layout.svelte -->
|
||||
<script lang="ts" module>
|
||||
// Register the Account schema so `useAccount` returns our custom `MyAppAccount`
|
||||
declare module 'jazz-svelte' {
|
||||
interface Register {
|
||||
Account: MyAppAccount;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { JazzProvider } from "jazz-svelte";
|
||||
import { MyAppAccount } from "$lib/schema";
|
||||
let { children } = $props();
|
||||
</script>
|
||||
|
||||
<JazzProvider
|
||||
sync={{
|
||||
peer: "wss://cloud.jazz.tools/?key=your-api-key",
|
||||
when: "always" // When to sync: "always", "never", or "signedUp"
|
||||
}}
|
||||
AccountSchema={MyAppAccount}
|
||||
>
|
||||
{@render children()}
|
||||
</JazzProvider>
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
## Provider Options
|
||||
|
||||
### Sync Options
|
||||
|
||||
The `sync` property configures how your application connects to the Jazz network:
|
||||
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
// @filename: src/routes/layout.svelte
|
||||
|
||||
// ---cut---
|
||||
import { type SyncConfig } from "jazz-tools";
|
||||
|
||||
const syncConfig: SyncConfig = {
|
||||
// Connection to Jazz Cloud or your own sync server
|
||||
peer: "wss://cloud.jazz.tools/?key=your-api-key",
|
||||
|
||||
// When to sync: "always" (default), "never", or "signedUp"
|
||||
when: "always",
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
|
||||
See [Authentication States](/docs/authentication/authentication-states#controlling-sync-for-different-authentication-states) for more details on how the `when` property affects synchronization based on authentication state.
|
||||
|
||||
### Account Schema
|
||||
|
||||
The `AccountSchema` property defines your application's account structure:
|
||||
|
||||
<CodeGroup>
|
||||
```svelte
|
||||
<!-- src/routes/+layout.svelte -->>
|
||||
<script lang="ts" module>
|
||||
// Register the Account schema so `useAccount` returns our custom `MyAppAccount`
|
||||
declare module 'jazz-svelte' {
|
||||
interface Register {
|
||||
Account: MyAppAccount;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { JazzProvider } from "jazz-svelte";
|
||||
import { MyAppAccount } from "$lib/schema";
|
||||
let { children } = $props();
|
||||
</script>
|
||||
|
||||
<JazzProvider
|
||||
sync={syncConfig}
|
||||
AccountSchema={MyAppAccount}
|
||||
>
|
||||
{@render children()}
|
||||
</JazzProvider>
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### Additional Options
|
||||
|
||||
The provider accepts these additional options:
|
||||
|
||||
<CodeGroup>
|
||||
```svelte
|
||||
<!-- src/routes/+layout.svelte -->
|
||||
<script lang="ts">
|
||||
import { JazzProvider } from "jazz-svelte";
|
||||
import { syncConfig } from "$lib/syncConfig";
|
||||
let { children } = $props();
|
||||
|
||||
// Enable guest mode for account-less access
|
||||
const guestMode = false;
|
||||
|
||||
// Default name for new user profiles
|
||||
const defaultProfileName = "New User";
|
||||
|
||||
// Handle user logout
|
||||
const onLogOut = () => {
|
||||
console.log("User logged out");
|
||||
};
|
||||
|
||||
// Handle anonymous account data when user logs in to existing account
|
||||
const onAnonymousAccountDiscarded = (account) => {
|
||||
console.log("Anonymous account discarded", account.id);
|
||||
// Migrate data here
|
||||
return Promise.resolve();
|
||||
};
|
||||
</script>
|
||||
|
||||
<JazzProvider
|
||||
sync={syncConfig}
|
||||
{guestMode}
|
||||
{defaultProfileName}
|
||||
{onLogOut}
|
||||
{onAnonymousAccountDiscarded}
|
||||
>
|
||||
{@render children}
|
||||
</JazzProvider>
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
See [Authentication States](/docs/authentication/authentication-states) for more information on authentication states, guest mode, and handling anonymous accounts.
|
||||
|
||||
## Authentication
|
||||
|
||||
`<JazzProvider />` works with various authentication methods to enable users to access their data across multiple devices. For a complete guide to authentication, see our [Authentication Overview](/docs/authentication/overview).
|
||||
|
||||
## Need Help?
|
||||
|
||||
If you have questions about configuring the Jazz Provider for your specific use case, [join our Discord community](https://discord.gg/utDMjHYg42) for help.
|
||||
@@ -1,19 +1,207 @@
|
||||
import { ContentByFramework } from '@/components/forMdx'
|
||||
import { ContentByFramework, CodeGroup } from '@/components/forMdx'
|
||||
|
||||
# Jazz 0.14.0 Introducing Zod-based schemas
|
||||
|
||||
**Note:** This is a huge release with many breaking changes.
|
||||
We're excited to move from our own schema syntax to using Zod v4.
|
||||
|
||||
This is the first step in a series of releases to make Jazz more familiar and to make CoValues look more like regular data structures.
|
||||
|
||||
**Note: This is a huge release that we're still cleaning up and documenting.**
|
||||
|
||||
<small className="leading-tight">
|
||||
We're still in the process of:
|
||||
- updating all our docs
|
||||
- double-checking all our framework bindings
|
||||
- particularly React Native might still have issues
|
||||
- completing all the details of this upgrade guide
|
||||
</small>
|
||||
|
||||
Thanks for your patience!
|
||||
**Note: React Native is currently broken based on an [underlying Zod v4 issue](https://github.com/colinhacks/zod/issues/4148).**
|
||||
|
||||
If you see something broken, please let us know on [Discord](https://discord.gg/utDMjHYg42) and check back in a couple hours.
|
||||
|
||||
Thanks for your patience!
|
||||
|
||||
## Overview:
|
||||
|
||||
TODO (check back in a couple minutes)
|
||||
So far, Jazz has relied on our own idiosyncratic schema definition syntax where you had to extend classes and be careful to use `co.ref` for references.
|
||||
|
||||
<CodeGroup>
|
||||
```ts
|
||||
// BEFORE
|
||||
import { co, CoMap, CoList, CoPlainText, ImageDefinition } from "jazz-tools";
|
||||
|
||||
export class Message extends CoMap {
|
||||
text = co.ref(CoPlainText);
|
||||
image = co.optional.ref(ImageDefinition);
|
||||
important = co.boolean;
|
||||
}
|
||||
|
||||
export class Chat extends CoList.Of(co.ref(Message)) {}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
While this had certain ergonomic benefits it relied on unclean hacks to work.
|
||||
|
||||
In addition, many of our adopters expressed a preference for avoiding class syntax, and LLMs consistently expected to be able to use Zod.
|
||||
|
||||
For this reason, we completely overhauled how you define and use CoValue schemas:
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
// AFTER
|
||||
import { co, z } from "jazz-tools";
|
||||
|
||||
export const Message = co.map({
|
||||
text: co.plainText(),
|
||||
image: z.optional(co.image()),
|
||||
important: z.boolean(),
|
||||
});
|
||||
|
||||
export const Chat = co.list(Message);
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
## Major breaking changes
|
||||
|
||||
### Schema definitions
|
||||
|
||||
You now define CoValue schemas using two new exports from `jazz-tools`:
|
||||
|
||||
- a new `co` definer that mirrors Zod's object/record/array syntax to define CoValue types
|
||||
- `co.map()`, `co.record()`, `co.list()`, `co.feed()`
|
||||
- `co.account()`, `co.profile()`
|
||||
- `co.plainText()`, `co.richText()`,
|
||||
- `co.fileStream()`, `co.image()`
|
||||
- see the updated [Defining CoValue Schemas](/docs/schemas/covalues)
|
||||
- `z` re-exported from Zod v4
|
||||
- primitives like `z.string()`, `z.number()`, `z.literal()`
|
||||
- **note**: additional constraints like `z.min()` and `z.max()` are not yet enforced, we'll add validation in future releases
|
||||
- complex types like `z.object()` and `z.array()` to define JSON-like fields without internal collaboration
|
||||
- combinators like `z.optional()` and `z.discriminatedUnion()`
|
||||
- these also work on CoValue types!
|
||||
- see the updated [Docs on Primitive Fields](/docs/schemas/covalues#primitive-fields),
|
||||
[Docs on Optional References](/docs/schemas/covalues#optional-references)
|
||||
and [Docs on Unions of CoMaps](/docs/schemas/covalues#unions-of-comaps-declaration)
|
||||
|
||||
Similar to Zod v4's new object syntax, recursive and mutually recursive types are now [much easier to express](/docs/react/schemas/covalues#recursive-references).
|
||||
|
||||
### How to pass loaded CoValues
|
||||
|
||||
Calls to `useCoState()` work just the same, but they return a slightly different type than before.
|
||||
|
||||
And while you can still read from the type just as before...
|
||||
|
||||
<CodeGroup>
|
||||
```tsx twoslash
|
||||
import React from "react";
|
||||
// ---cut---
|
||||
import { z, co } from "jazz-tools";
|
||||
import { useCoState } from "jazz-react";
|
||||
|
||||
const Pet = co.map({
|
||||
name: z.string(),
|
||||
age: z.number(),
|
||||
});
|
||||
|
||||
const Person = co.map({
|
||||
name: z.string(),
|
||||
age: z.number(),
|
||||
pets: co.list(Pet),
|
||||
});
|
||||
|
||||
function MyComponent({ id }: { id: string }) {
|
||||
const person = useCoState(Person, id);
|
||||
|
||||
return person && <div>{person.name}</div>;
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
...you now need to specify the type differently **when passing CoValues as a parameter** or
|
||||
**whenever you need to refer to the type of a loaded CoValue instance:**
|
||||
|
||||
<CodeGroup>
|
||||
```tsx twoslash
|
||||
import React from "react";
|
||||
// ---cut---
|
||||
import { z, co, Loaded } from "jazz-tools"; // [!code ++]
|
||||
import { useCoState } from "jazz-react";
|
||||
|
||||
const Pet = co.map({
|
||||
name: z.string(),
|
||||
age: z.number(),
|
||||
});
|
||||
|
||||
const Person = co.map({
|
||||
name: z.string(),
|
||||
age: z.number(),
|
||||
pets: co.list(Pet),
|
||||
});
|
||||
|
||||
function MyComponent({ id }: { id: string }) {
|
||||
const person = useCoState(Person, id);
|
||||
|
||||
return person && <PersonName person={person} />;
|
||||
}
|
||||
|
||||
function PersonName({ person }: { person: Loaded<typeof Person> }) { // [!code ++]
|
||||
return <div>{person.name}</div>;
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
`Loaded` can also take a second argument to specify the loading depth of the expected CoValue, mirroring the `resolve` options for `useCoState`, `load`, `subscribe`, etc.
|
||||
|
||||
<CodeGroup>
|
||||
```tsx twoslash
|
||||
import React from "react";
|
||||
// ---cut---
|
||||
import { z, co, Loaded } from "jazz-tools";
|
||||
import { useCoState } from "jazz-react";
|
||||
|
||||
const Pet = co.map({
|
||||
name: z.string(),
|
||||
age: z.number(),
|
||||
});
|
||||
|
||||
const Person = co.map({
|
||||
name: z.string(),
|
||||
age: z.number(),
|
||||
pets: co.list(Pet),
|
||||
});
|
||||
|
||||
function MyComponent({ id }: { id: string }) {
|
||||
const personWithPets = useCoState(Person, id, {
|
||||
resolve: { pets: { $each: true } } // [!code ++]
|
||||
});
|
||||
|
||||
return personWithPets && <PersonAndFirstPetName person={personWithPets} />;
|
||||
}
|
||||
|
||||
function PersonAndFirstPetName({ person }: {
|
||||
person: Loaded<typeof Person, { pets: { $each: true } }> // [!code ++]
|
||||
}) {
|
||||
return <div>{person.name} & {person.pets[0].name}</div>;
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### Defining migrations
|
||||
|
||||
TODO
|
||||
|
||||
### Defining Schema helper methods
|
||||
|
||||
TODO
|
||||
|
||||
### Removing AccountSchema registration
|
||||
|
||||
TODO
|
||||
|
||||
## Minor breaking changes
|
||||
|
||||
### `_refs` and `_edits` are now potentially null
|
||||
|
||||
TODO
|
||||
|
||||
### `members` and `by` now return basic `Account`s
|
||||
@@ -1,6 +1,6 @@
|
||||
import { CodeGroup, ContentByFramework } from "@/components/forMdx";
|
||||
|
||||
export const metadata = {
|
||||
export const metadata = {
|
||||
description: "Learn how to subscribe to CoValues, specify loading depths, and handle loading states and inaccessible data."
|
||||
};
|
||||
|
||||
@@ -26,27 +26,26 @@ If you're using React in your project, check out our [React hooks](/docs/react/u
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
import { ID, CoMap, coField } from "jazz-tools";
|
||||
const taskId = "co_123" as ID<Task>;
|
||||
import { co, z } from "jazz-tools";
|
||||
const taskId = "co_123";
|
||||
// ---cut-before---
|
||||
class Task extends CoMap {
|
||||
title = coField.string;
|
||||
description = coField.string;
|
||||
status = coField.literal("todo", "in-progress", "completed");
|
||||
assignedTo = coField.optional.string;
|
||||
}
|
||||
const Task = co.map({
|
||||
title: z.string(),
|
||||
description: z.string(),
|
||||
status: z.literal(["todo", "in-progress", "completed"]),
|
||||
assignedTo: z.optional(z.string()),
|
||||
});
|
||||
|
||||
// ...
|
||||
|
||||
// Subscribe to a Task by ID
|
||||
const unsubscribe = Task.subscribe(taskId, (updatedTask) => {
|
||||
const unsubscribe = Task.subscribe(taskId, {}, (updatedTask) => {
|
||||
console.log("Task updated:", updatedTask.title);
|
||||
console.log("New status:", updatedTask.status);
|
||||
});
|
||||
|
||||
// Clean up when you're done
|
||||
unsubscribe();
|
||||
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
@@ -54,14 +53,14 @@ If you already have a CoValue instance, you can subscribe to it by calling its `
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
import { ID, CoMap, coField } from "jazz-tools";
|
||||
import { co, z } from "jazz-tools";
|
||||
|
||||
class Task extends CoMap {
|
||||
title = coField.string;
|
||||
description = coField.string;
|
||||
status = coField.literal("todo", "in-progress", "completed");
|
||||
assignedTo = coField.optional.string;
|
||||
}
|
||||
const Task = co.map({
|
||||
title: z.string(),
|
||||
description: z.string(),
|
||||
status: z.literal(["todo", "in-progress", "completed"]),
|
||||
assignedTo: z.optional(z.string()),
|
||||
});
|
||||
const otherProps = {} as any;
|
||||
// ---cut-before---
|
||||
const task = Task.create({
|
||||
@@ -88,22 +87,20 @@ Jazz provides a `useCoState` hook that provides a convenient way to subscribe to
|
||||
<CodeGroup>
|
||||
```tsx twoslash
|
||||
import React from "react";
|
||||
import { ID, CoMap, coField, CoList } from "jazz-tools";
|
||||
|
||||
class Task extends CoMap {
|
||||
title = coField.string;
|
||||
status = coField.literal("todo", "in-progress", "completed");
|
||||
}
|
||||
class Project extends CoMap {
|
||||
name = coField.string;
|
||||
tasks = coField.ref(ListOfTasks);
|
||||
}
|
||||
class ListOfTasks extends CoList.Of(coField.ref(Task)) {}
|
||||
import { co, z, ID, Loaded } from "jazz-tools";
|
||||
|
||||
const Task = co.map({
|
||||
title: z.string(),
|
||||
status: z.literal(["todo", "in-progress", "completed"]),
|
||||
});
|
||||
const Project = co.map({
|
||||
name: z.string(),
|
||||
tasks: co.list(Task),
|
||||
});
|
||||
// ---cut-before---
|
||||
import { useCoState } from "jazz-react";
|
||||
|
||||
function GardenPlanner({ projectId }: { projectId: ID<Project> }) {
|
||||
function GardenPlanner({ projectId }: { projectId: string }) {
|
||||
// Subscribe to a project and its tasks
|
||||
const project = useCoState(Project, projectId, {
|
||||
resolve: {
|
||||
@@ -125,7 +122,7 @@ function GardenPlanner({ projectId }: { projectId: ID<Project> }) {
|
||||
);
|
||||
}
|
||||
|
||||
function TaskList({ tasks }: { tasks: Task[] }) {
|
||||
function TaskList({ tasks }: { tasks: Loaded<typeof Task>[] }) {
|
||||
return (
|
||||
<ul>
|
||||
{tasks.map((task) => (
|
||||
@@ -152,37 +149,38 @@ Like `useCoState`, you can specify a resolve query to also subscribe to CoValues
|
||||
<CodeGroup>
|
||||
```tsx twoslash
|
||||
import React from "react";
|
||||
import { ID, CoMap, coField, CoList, Account } from "jazz-tools";
|
||||
class Task extends CoMap {
|
||||
title = coField.string;
|
||||
}
|
||||
class ListOfTasks extends CoList.Of(coField.ref(Task)) {}
|
||||
class Project extends CoMap {
|
||||
name = coField.string;
|
||||
tasks = coField.ref(ListOfTasks);
|
||||
}
|
||||
class ListOfProjects extends CoList.Of(coField.ref(Project)) {}
|
||||
import { co, z } from "jazz-tools";
|
||||
const Task = co.map({
|
||||
title: z.string(),
|
||||
});
|
||||
|
||||
const Project = co.map({
|
||||
name: z.string(),
|
||||
tasks: co.list(Task),
|
||||
});
|
||||
|
||||
const AccountRoot = co.map({
|
||||
myProjects: co.list(Project),
|
||||
});
|
||||
|
||||
const MyAppAccount = co.account({
|
||||
root: AccountRoot,
|
||||
profile: co.profile(),
|
||||
});
|
||||
|
||||
class AccountRoot extends CoMap {
|
||||
myProjects = coField.ref(ListOfProjects);
|
||||
}
|
||||
class MyAppAccount extends Account {
|
||||
root = coField.ref(AccountRoot);
|
||||
}
|
||||
declare module "jazz-react" { interface Register { Account: MyAppAccount; } }
|
||||
// ---cut-before---
|
||||
import { useAccount } from "jazz-react";
|
||||
|
||||
function ProjectList() {
|
||||
const { me } = useAccount({
|
||||
const { me } = useAccount(MyAppAccount, {
|
||||
resolve: {
|
||||
profile: true,
|
||||
root: {
|
||||
myProjects: {
|
||||
$each: {
|
||||
tasks: true
|
||||
}
|
||||
}
|
||||
tasks: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -191,17 +189,20 @@ function ProjectList() {
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
|
||||
return <div>
|
||||
<h1>{me.profile.name}'s projects</h1>
|
||||
<ul>
|
||||
{me.root.myProjects.map(project => (
|
||||
<li key={project.id}>
|
||||
{project.name} ({project.tasks.length} tasks)
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
return (
|
||||
<div>
|
||||
<h1>{me.profile.name}'s projects</h1>
|
||||
<ul>
|
||||
{me.root.myProjects.map((project) => (
|
||||
<li key={project.id}>
|
||||
{project.name} ({project.tasks.length} tasks)
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
@@ -219,14 +220,14 @@ This allows you to handle loading, error, and success states in your application
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
import { ID, CoMap, coField } from "jazz-tools";
|
||||
class Task extends CoMap {
|
||||
title = coField.string;
|
||||
}
|
||||
import { co, z, Loaded } from "jazz-tools";
|
||||
const Task = co.map({
|
||||
title: z.string(),
|
||||
});
|
||||
|
||||
const taskId = "co_123" as ID<Task>;
|
||||
const taskId = "co_123";
|
||||
// ---cut-before---
|
||||
Task.subscribe(taskId, (task) => {
|
||||
Task.subscribe(taskId, {}, (task: Loaded<typeof Task>) => {
|
||||
if (task === undefined) {
|
||||
console.log("Task is loading...");
|
||||
} else if (task === null) {
|
||||
@@ -248,27 +249,25 @@ Resolve queries let you declare exactly which references to load and how deep to
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
import { ID, CoMap, coField, CoList } from "jazz-tools";
|
||||
const projectId = "co_123" as ID<Project>;
|
||||
import { co, z, CoListSchema } from "jazz-tools";
|
||||
const projectId = "co_123";
|
||||
|
||||
// ---cut-before---
|
||||
class Project extends CoMap {
|
||||
name = coField.string;
|
||||
tasks = coField.ref(ListOfTasks);
|
||||
owner = coField.ref(TeamMember);
|
||||
}
|
||||
const TeamMember = co.map({
|
||||
name: z.string(),
|
||||
});
|
||||
|
||||
class Task extends CoMap {
|
||||
title = coField.string;
|
||||
subtasks = coField.ref(ListOfTasks);
|
||||
assignee = coField.optional.ref(TeamMember);
|
||||
}
|
||||
const Task = co.map({
|
||||
title: z.string(),
|
||||
assignee: z.optional(TeamMember),
|
||||
get subtasks(): CoListSchema<typeof Task> { return co.list(Task) },
|
||||
});
|
||||
|
||||
class TeamMember extends CoMap {
|
||||
name = coField.string;
|
||||
}
|
||||
|
||||
class ListOfTasks extends CoList.Of(coField.ref(Task)) {}
|
||||
const Project = co.map({
|
||||
name: z.string(),
|
||||
tasks: co.list(Task),
|
||||
owner: TeamMember,
|
||||
});
|
||||
|
||||
// Load just the project, not its references
|
||||
const project = await Project.load(projectId);
|
||||
@@ -349,28 +348,26 @@ When loading data with references, the load operation will fail if one of the re
|
||||
When a user tries to load a reference they don't have access to:
|
||||
|
||||
<CodeGroup>
|
||||
```typescript twoslash
|
||||
import { ID, CoMap, coField, CoList } from "jazz-tools";
|
||||
```ts twoslash
|
||||
import { co, z, CoListSchema } from "jazz-tools";
|
||||
|
||||
class Project extends CoMap {
|
||||
name = coField.string;
|
||||
tasks = coField.ref(ListOfTasks);
|
||||
owner = coField.ref(TeamMember);
|
||||
}
|
||||
const TeamMember = co.map({
|
||||
name: z.string(),
|
||||
});
|
||||
|
||||
class Task extends CoMap {
|
||||
title = coField.string;
|
||||
subtasks = coField.ref(ListOfTasks);
|
||||
assignee = coField.optional.ref(TeamMember);
|
||||
}
|
||||
const Task = co.map({
|
||||
title: z.string(),
|
||||
assignee: z.optional(TeamMember),
|
||||
get subtasks(): CoListSchema<typeof Task> { return co.list(Task) },
|
||||
});
|
||||
|
||||
class TeamMember extends CoMap {
|
||||
name = coField.string;
|
||||
}
|
||||
const Project = co.map({
|
||||
name: z.string(),
|
||||
tasks: co.list(Task),
|
||||
owner: TeamMember,
|
||||
});
|
||||
|
||||
class ListOfTasks extends CoList.Of(coField.ref(Task)) {}
|
||||
|
||||
const taskId = "co_123" as ID<Task>;
|
||||
const taskId = "co_123";
|
||||
|
||||
// ---cut-before---
|
||||
// If assignee is not accessible to the user:
|
||||
@@ -390,28 +387,26 @@ The behavior is the same for optional and required references.
|
||||
When a list contains references to items the user can't access:
|
||||
|
||||
<CodeGroup>
|
||||
```typescript twoslash
|
||||
import { ID, CoMap, coField, CoList } from "jazz-tools";
|
||||
```ts twoslash
|
||||
import { co, z, CoListSchema } from "jazz-tools";
|
||||
|
||||
class Project extends CoMap {
|
||||
name = coField.string;
|
||||
tasks = coField.ref(ListOfTasks);
|
||||
owner = coField.ref(TeamMember);
|
||||
}
|
||||
const TeamMember = co.map({
|
||||
name: z.string(),
|
||||
});
|
||||
|
||||
class Task extends CoMap {
|
||||
title = coField.string;
|
||||
subtasks = coField.ref(ListOfTasks);
|
||||
assignee = coField.optional.ref(TeamMember);
|
||||
}
|
||||
const Task = co.map({
|
||||
title: z.string(),
|
||||
assignee: z.optional(TeamMember),
|
||||
get subtasks(): CoListSchema<typeof Task> { return co.list(Task) },
|
||||
});
|
||||
|
||||
class TeamMember extends CoMap {
|
||||
name = coField.string;
|
||||
}
|
||||
const Project = co.map({
|
||||
name: z.string(),
|
||||
tasks: co.list(Task),
|
||||
owner: TeamMember,
|
||||
});
|
||||
|
||||
class ListOfTasks extends CoList.Of(coField.ref(Task)) {}
|
||||
|
||||
const projectId = "co_123" as ID<Project>;
|
||||
const projectId = "co_123";
|
||||
// ---cut-before---
|
||||
// If any item in the list is not accessible:
|
||||
const project = await Project.load(projectId, {
|
||||
@@ -428,28 +423,26 @@ If any item in a list is inaccessible to the user, the entire load operation wil
|
||||
When trying to load an object with an inaccessible reference without directly resolving it:
|
||||
|
||||
<CodeGroup>
|
||||
```typescript twoslash
|
||||
import { ID, CoMap, coField, CoList } from "jazz-tools";
|
||||
```ts twoslash
|
||||
import { co, z, CoListSchema } from "jazz-tools";
|
||||
|
||||
class Project extends CoMap {
|
||||
name = coField.string;
|
||||
tasks = coField.ref(ListOfTasks);
|
||||
owner = coField.ref(TeamMember);
|
||||
}
|
||||
const TeamMember = co.map({
|
||||
name: z.string(),
|
||||
});
|
||||
|
||||
class Task extends CoMap {
|
||||
title = coField.string;
|
||||
subtasks = coField.ref(ListOfTasks);
|
||||
assignee = coField.optional.ref(TeamMember);
|
||||
}
|
||||
const Task = co.map({
|
||||
title: z.string(),
|
||||
assignee: z.optional(TeamMember),
|
||||
get subtasks(): CoListSchema<typeof Task> { return co.list(Task) },
|
||||
});
|
||||
|
||||
class TeamMember extends CoMap {
|
||||
name = coField.string;
|
||||
}
|
||||
const Project = co.map({
|
||||
name: z.string(),
|
||||
tasks: co.list(Task),
|
||||
owner: TeamMember,
|
||||
});
|
||||
|
||||
class ListOfTasks extends CoList.Of(coField.ref(Task)) {}
|
||||
|
||||
const projectId = "co_123" as ID<Project>;
|
||||
const projectId = "co_123";
|
||||
// ---cut-before---
|
||||
const project = await Project.load(projectId, {
|
||||
resolve: true
|
||||
@@ -474,21 +467,24 @@ This is especially useful when in your app access to these items might be revoke
|
||||
This way the inaccessible items are replaced with `null` in the returned list.
|
||||
|
||||
<CodeGroup>
|
||||
```typescript twoslash
|
||||
import { ID, CoMap, CoList, co, Group, Account } from "jazz-tools";
|
||||
import { assert } from "vitest";
|
||||
```ts twoslash
|
||||
import { co, z, CoListSchema, Group } from "jazz-tools";
|
||||
import { createJazzTestAccount } from "jazz-tools/testing";
|
||||
|
||||
class Person extends CoMap {
|
||||
name = co.string;
|
||||
}
|
||||
class Friends extends CoList.Of(co.ref(Person)) {}
|
||||
const me = await createJazzTestAccount();
|
||||
const account2 = await createJazzTestAccount();
|
||||
|
||||
const privateGroup = Group.create();
|
||||
const publicGroup = Group.create();
|
||||
const me = {} as unknown as Account;
|
||||
const Person = co.map({
|
||||
name: z.string(),
|
||||
});
|
||||
|
||||
const Friends = co.list(Person);
|
||||
|
||||
const privateGroup = Group.create({ owner: account2 });
|
||||
const publicGroup = Group.create({ owner: me });
|
||||
|
||||
// ---cut-before---
|
||||
const source = Friends.create(
|
||||
const source = co.list(Person).create(
|
||||
[
|
||||
Person.create(
|
||||
{
|
||||
@@ -506,7 +502,7 @@ const source = Friends.create(
|
||||
publicGroup,
|
||||
);
|
||||
|
||||
const friends = await Friends.load(source.id, {
|
||||
const friends = await co.list(Person).load(source.id, {
|
||||
resolve: {
|
||||
$each: { $onError: null }
|
||||
},
|
||||
@@ -515,47 +511,47 @@ const friends = await Friends.load(source.id, {
|
||||
|
||||
// Thanks to $onError catching the errors, the list is loaded
|
||||
// because we have access to friends
|
||||
friends // => Friends
|
||||
|
||||
assert(friends);
|
||||
console.log(friends); // Person[]
|
||||
|
||||
// Jane is null because we lack access rights
|
||||
// and we have used $onError to catch the error on the list items
|
||||
friends[0] // => null
|
||||
console.log(friends?.[0]); // null
|
||||
|
||||
// Alice is not null because we have access
|
||||
// the type is nullable because we have used $onError
|
||||
friends[1] // => Person
|
||||
console.log(friends?.[1]); // Person
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
The `$onError` works as a "catch" clause option to block any error in the resolved childs.
|
||||
The `$onError` works as a "catch" clause option to block any error in the resolved children.
|
||||
|
||||
<CodeGroup>
|
||||
```typescript twoslash
|
||||
import { ID, CoMap, CoList, co, Group, Account } from "jazz-tools";
|
||||
import { assert } from "vitest";
|
||||
```ts twoslash
|
||||
import { createJazzTestAccount } from "jazz-tools/testing";
|
||||
const me = await createJazzTestAccount();
|
||||
const account2 = await createJazzTestAccount();
|
||||
|
||||
class Person extends CoMap {
|
||||
name = co.string;
|
||||
dog = co.ref(Dog);
|
||||
}
|
||||
class Dog extends CoMap {
|
||||
name = co.string;
|
||||
}
|
||||
class Friends extends CoList.Of(co.ref(Person)) {}
|
||||
import { Group, co, z } from "jazz-tools";
|
||||
|
||||
class User extends CoMap {
|
||||
name = co.string;
|
||||
friends = co.ref(Friends);
|
||||
}
|
||||
const Dog = co.map({
|
||||
name: z.string(),
|
||||
});
|
||||
|
||||
const privateGroup = Group.create();
|
||||
const publicGroup = Group.create();
|
||||
const me = {} as unknown as Account;
|
||||
const Person = co.map({
|
||||
name: z.string(),
|
||||
dog: Dog,
|
||||
});
|
||||
|
||||
const User = co.map({
|
||||
name: z.string(),
|
||||
friends: co.list(Person),
|
||||
});
|
||||
|
||||
const privateGroup = Group.create({ owner: account2 });
|
||||
const publicGroup = Group.create({ owner: me });
|
||||
|
||||
// ---cut-before---
|
||||
const source = Friends.create(
|
||||
const source = co.list(Person).create(
|
||||
[
|
||||
Person.create(
|
||||
{
|
||||
@@ -571,47 +567,47 @@ const source = Friends.create(
|
||||
publicGroup,
|
||||
);
|
||||
|
||||
const friends = await Friends.load(source.id, {
|
||||
const friends = await co.list(Person).load(source.id, {
|
||||
resolve: {
|
||||
$each: { dog: true, $onError: null }
|
||||
},
|
||||
loadAs: me,
|
||||
});
|
||||
|
||||
assert(friends);
|
||||
|
||||
// Jane is null because we don't have access to Rex
|
||||
// and we have used $onError to catch the error on the list items
|
||||
friends[0] // => null
|
||||
console.log(friends?.[0]); // null
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
We can actually use `$onError` everywhere in the resolve query, so we can use it to catch the error on dog:
|
||||
|
||||
<CodeGroup>
|
||||
```typescript twoslash
|
||||
import { ID, CoMap, CoList, co, Group, Account } from "jazz-tools";
|
||||
import { assert } from "vitest";
|
||||
```ts twoslash
|
||||
import { createJazzTestAccount } from "jazz-tools/testing";
|
||||
const me = await createJazzTestAccount();
|
||||
const account2 = await createJazzTestAccount();
|
||||
|
||||
class Person extends CoMap {
|
||||
name = co.string;
|
||||
dog = co.ref(Dog);
|
||||
}
|
||||
class Dog extends CoMap {
|
||||
name = co.string;
|
||||
}
|
||||
class Friends extends CoList.Of(co.ref(Person)) {}
|
||||
import { co, z, Group } from "jazz-tools";
|
||||
|
||||
class User extends CoMap {
|
||||
name = co.string;
|
||||
friends = co.ref(Friends);
|
||||
}
|
||||
const Dog = co.map({
|
||||
name: z.string(),
|
||||
});
|
||||
|
||||
const privateGroup = Group.create();
|
||||
const publicGroup = Group.create();
|
||||
const me = {} as unknown as Account;
|
||||
const Person = co.map({
|
||||
name: z.string(),
|
||||
dog: Dog,
|
||||
});
|
||||
|
||||
const source = Friends.create(
|
||||
const User = co.map({
|
||||
name: z.string(),
|
||||
friends: co.list(Person),
|
||||
});
|
||||
|
||||
const privateGroup = Group.create({ owner: account2 });
|
||||
const publicGroup = Group.create({ owner: me });
|
||||
|
||||
const source = co.list(Person).create(
|
||||
[
|
||||
Person.create(
|
||||
{
|
||||
@@ -628,71 +624,71 @@ const source = Friends.create(
|
||||
);
|
||||
|
||||
// ---cut-before---
|
||||
const friends = await Friends.load(source.id, {
|
||||
const friends = await co.list(Person).load(source.id, {
|
||||
resolve: {
|
||||
$each: { dog: { $onError: null } }
|
||||
},
|
||||
loadAs: me,
|
||||
});
|
||||
|
||||
assert(friends);
|
||||
|
||||
// Jane now is not-nullable at type level because
|
||||
// we have moved $onError down to the dog field
|
||||
//
|
||||
// This also means that if we don't have access to Jane
|
||||
// the entire friends list will be null
|
||||
friends[0] // => Person
|
||||
console.log(friends?.[0]); // => Person
|
||||
|
||||
// Jane's dog is null because we don't have access to Rex
|
||||
// and we have used $onError to catch the error
|
||||
friends[0].dog // => null
|
||||
console.log(friends?.[0]?.dog); // => null
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
## Type Safety with Resolved Type
|
||||
## Type Safety with Loaded Type
|
||||
|
||||
Jazz provides the `Resolved` type to help you define and enforce the structure of deeply loaded data in your application. This makes it easier to ensure that components receive the data they expect with proper TypeScript validation.
|
||||
Jazz provides the `Loaded` type to help you define and enforce the structure of deeply loaded data in your application. This makes it easier to ensure that components receive the data they expect with proper TypeScript validation.
|
||||
|
||||
The `Resolved` type is especially useful when passing data between components, as it guarantees that all necessary nested data has been loaded:
|
||||
The `Loaded` type is especially useful when passing data between components, as it guarantees that all necessary nested data has been loaded:
|
||||
|
||||
<ContentByFramework framework="react">
|
||||
<CodeGroup>
|
||||
```tsx twoslash
|
||||
import { CoListSchema, Loaded, co, z } from "jazz-tools";
|
||||
import React from "react";
|
||||
import { ID, CoMap, coField, CoList, Resolved } from "jazz-tools";
|
||||
|
||||
class Project extends CoMap {
|
||||
name = coField.string;
|
||||
tasks = coField.ref(ListOfTasks);
|
||||
owner = coField.ref(TeamMember);
|
||||
}
|
||||
const TeamMember = co.map({
|
||||
name: z.string(),
|
||||
});
|
||||
|
||||
class Task extends CoMap {
|
||||
title = coField.string;
|
||||
subtasks = coField.ref(ListOfTasks);
|
||||
assignee = coField.optional.ref(TeamMember);
|
||||
}
|
||||
|
||||
class TeamMember extends CoMap {
|
||||
name = coField.string;
|
||||
}
|
||||
|
||||
class ListOfTasks extends CoList.Of(coField.ref(Task)) {}
|
||||
const Task = co.map({
|
||||
title: z.string(),
|
||||
assignee: z.optional(TeamMember),
|
||||
get subtasks(): CoListSchema<typeof Task> {
|
||||
return co.list(Task);
|
||||
},
|
||||
});
|
||||
|
||||
const Project = co.map({
|
||||
name: z.string(),
|
||||
tasks: co.list(Task),
|
||||
owner: TeamMember,
|
||||
});
|
||||
|
||||
// ---cut-before---
|
||||
// Define a type that includes resolved nested data
|
||||
type ProjectWithTasks = Resolved<Project, {
|
||||
tasks: { $each: true }
|
||||
}>;
|
||||
// Define a type that includes loaded nested data
|
||||
type ProjectWithTasks = Loaded<
|
||||
typeof Project,
|
||||
{
|
||||
tasks: { $each: true };
|
||||
}
|
||||
>;
|
||||
|
||||
// Component that expects a fully resolved project
|
||||
// Component that expects a fully loaded project
|
||||
function TaskList({ project }: { project: ProjectWithTasks }) {
|
||||
// TypeScript knows tasks are loaded, so this is type-safe
|
||||
return (
|
||||
<ul>
|
||||
{project.tasks.map(task => (
|
||||
{project.tasks.map((task) => (
|
||||
<li key={task.id}>{task.title}</li>
|
||||
))}
|
||||
</ul>
|
||||
@@ -700,26 +696,30 @@ function TaskList({ project }: { project: ProjectWithTasks }) {
|
||||
}
|
||||
|
||||
// For more complex resolutions
|
||||
type FullyLoadedProject = Resolved<Project, {
|
||||
tasks: {
|
||||
$each: {
|
||||
subtasks: true,
|
||||
assignee: true
|
||||
}
|
||||
},
|
||||
owner: true
|
||||
}>;
|
||||
type FullyLoadedProject = Loaded<
|
||||
typeof Project,
|
||||
{
|
||||
tasks: {
|
||||
$each: {
|
||||
subtasks: true;
|
||||
assignee: true;
|
||||
};
|
||||
};
|
||||
owner: true;
|
||||
}
|
||||
>;
|
||||
|
||||
// Function that requires deeply resolved data
|
||||
// Function that requires deeply loaded data
|
||||
function processProject(project: FullyLoadedProject) {
|
||||
// Safe access to all resolved properties
|
||||
// Safe access to all loaded properties
|
||||
console.log(`Project ${project.name} owned by ${project.owner.name}`);
|
||||
|
||||
project.tasks.forEach(task => {
|
||||
project.tasks.forEach((task) => {
|
||||
console.log(`Task: ${task.title}, Assigned to: ${task.assignee?.name}`);
|
||||
console.log(`Subtasks: ${task.subtasks.length}`);
|
||||
});
|
||||
}
|
||||
|
||||
```
|
||||
</CodeGroup>
|
||||
</ContentByFramework>
|
||||
@@ -727,59 +727,62 @@ function processProject(project: FullyLoadedProject) {
|
||||
<ContentByFramework framework="vanilla">
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
import { ID, CoMap, coField, CoList, Resolved } from "jazz-tools";
|
||||
import { CoListSchema, Loaded, co, z } from "jazz-tools";
|
||||
|
||||
class Project extends CoMap {
|
||||
name = coField.string;
|
||||
tasks = coField.ref(ListOfTasks);
|
||||
owner = coField.ref(TeamMember);
|
||||
}
|
||||
const TeamMember = co.map({
|
||||
name: z.string(),
|
||||
});
|
||||
|
||||
class Task extends CoMap {
|
||||
title = coField.string;
|
||||
subtasks = coField.ref(ListOfTasks);
|
||||
assignee = coField.optional.ref(TeamMember);
|
||||
}
|
||||
|
||||
class TeamMember extends CoMap {
|
||||
name = coField.string;
|
||||
}
|
||||
|
||||
class ListOfTasks extends CoList.Of(coField.ref(Task)) {}
|
||||
const Task = co.map({
|
||||
title: z.string(),
|
||||
assignee: z.optional(TeamMember),
|
||||
get subtasks(): CoListSchema<typeof Task> {
|
||||
return co.list(Task);
|
||||
},
|
||||
});
|
||||
|
||||
const Project = co.map({
|
||||
name: z.string(),
|
||||
tasks: co.list(Task),
|
||||
owner: TeamMember,
|
||||
});
|
||||
|
||||
// ---cut-before---
|
||||
// Define a type that includes resolved nested data
|
||||
type ProjectWithTasks = Resolved<Project, {
|
||||
tasks: { $each: true }
|
||||
}>;
|
||||
// Define a type that includes loaded nested data
|
||||
type ProjectWithTasks = Loaded<
|
||||
typeof Project,
|
||||
{
|
||||
tasks: { $each: true };
|
||||
}
|
||||
>;
|
||||
|
||||
// Function that expects resolved data
|
||||
async function taskList({project}: {project: ProjectWithTasks}) {
|
||||
// Function that expects loaded data
|
||||
async function taskList({ project }: { project: ProjectWithTasks }) {
|
||||
// TypeScript knows tasks are loaded, so this is type-safe
|
||||
return project.tasks
|
||||
.map(task => task.title)
|
||||
.join(`\n - `);
|
||||
return project.tasks.map((task) => task.title).join(`\n - `);
|
||||
}
|
||||
|
||||
// For more complex resolutions
|
||||
type FullyLoadedProject = Resolved<Project, {
|
||||
tasks: {
|
||||
$each: {
|
||||
title: true,
|
||||
subtasks: true,
|
||||
assignee: true
|
||||
}
|
||||
},
|
||||
owner: true
|
||||
}>;
|
||||
type FullyLoadedProject = Loaded<
|
||||
typeof Project,
|
||||
{
|
||||
tasks: {
|
||||
$each: {
|
||||
title: true;
|
||||
subtasks: true;
|
||||
assignee: true;
|
||||
};
|
||||
};
|
||||
owner: true;
|
||||
}
|
||||
>;
|
||||
|
||||
// Function that requires deeply resolved data
|
||||
// Function that requires deeply loaded data
|
||||
function processProject(project: FullyLoadedProject) {
|
||||
// Safe access to all resolved properties
|
||||
// Safe access to all loaded properties
|
||||
console.log(`Project ${project.name} owned by ${project.owner.name}`);
|
||||
|
||||
project.tasks.forEach(task => {
|
||||
project.tasks.forEach((task) => {
|
||||
console.log(`Task: ${task.title}, Assigned to: ${task.assignee?.name}`);
|
||||
console.log(`Subtasks: ${task.subtasks.length}`);
|
||||
});
|
||||
@@ -788,7 +791,7 @@ function processProject(project: FullyLoadedProject) {
|
||||
</CodeGroup>
|
||||
</ContentByFramework>
|
||||
|
||||
Using the `Resolved` type helps catch errors at compile time rather than runtime, ensuring that your components and functions receive data with the proper resolution depth. This is especially useful for larger applications where data is passed between many components.
|
||||
Using the `Loaded` type helps catch errors at compile time rather than runtime, ensuring that your components and functions receive data with the proper resolution depth. This is especially useful for larger applications where data is passed between many components.
|
||||
|
||||
## Ensuring Data is Loaded
|
||||
|
||||
@@ -796,29 +799,29 @@ Sometimes you need to make sure data is loaded before proceeding with an operati
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
import { ID, CoMap, coField, CoList, Resolved } from "jazz-tools";
|
||||
import { CoListSchema, Loaded, co, z } from "jazz-tools";
|
||||
|
||||
class Project extends CoMap {
|
||||
name = coField.string;
|
||||
tasks = coField.ref(ListOfTasks);
|
||||
owner = coField.ref(TeamMember);
|
||||
}
|
||||
const TeamMember = co.map({
|
||||
name: z.string(),
|
||||
});
|
||||
|
||||
class Task extends CoMap {
|
||||
title = coField.string;
|
||||
status = coField.literal("todo", "in-progress", "completed");
|
||||
subtasks = coField.ref(ListOfTasks);
|
||||
assignee = coField.optional.ref(TeamMember);
|
||||
}
|
||||
const Task = co.map({
|
||||
title: z.string(),
|
||||
status: z.literal(["todo", "in-progress", "completed"]),
|
||||
assignee: z.string().optional(),
|
||||
get subtasks(): CoListSchema<typeof Task> {
|
||||
return co.list(Task);
|
||||
},
|
||||
});
|
||||
|
||||
class TeamMember extends CoMap {
|
||||
name = coField.string;
|
||||
}
|
||||
|
||||
class ListOfTasks extends CoList.Of(coField.ref(Task)) {}
|
||||
const Project = co.map({
|
||||
name: z.string(),
|
||||
tasks: co.list(Task),
|
||||
owner: TeamMember,
|
||||
});
|
||||
|
||||
// ---cut-before---
|
||||
async function completeAllTasks(projectId: ID<Project>) {
|
||||
async function completeAllTasks(projectId: string) {
|
||||
// Ensure the project is loaded
|
||||
const project = await Project.load(projectId, { resolve: true });
|
||||
if (!project) return;
|
||||
@@ -827,13 +830,13 @@ async function completeAllTasks(projectId: ID<Project>) {
|
||||
const loadedProject = await project.ensureLoaded({
|
||||
resolve: {
|
||||
tasks: {
|
||||
$each: true
|
||||
}
|
||||
}
|
||||
$each: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Now we can safely access and modify tasks
|
||||
loadedProject.tasks.forEach(task => {
|
||||
loadedProject.tasks.forEach((task) => {
|
||||
task.status = "completed";
|
||||
});
|
||||
}
|
||||
@@ -848,4 +851,4 @@ async function completeAllTasks(projectId: ID<Project>) {
|
||||
2. **Use framework integrations**: They handle subscription lifecycle automatically
|
||||
3. **Clean up subscriptions**: Always store and call the unsubscribe function when you're done
|
||||
4. **Handle all loading states**: Check for undefined (loading), null (not found), and success states
|
||||
5. **Use the Resolved type**: Add compile-time type safety for components that require specific resolution patterns
|
||||
5. **Use the Loaded type**: Add compile-time type safety for components that require specific resolution patterns
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { docNavigationItems } from "../../content/docs/docNavigationItems.ts";
|
||||
import { docNavigationItems } from "../../content/docs/docNavigationItems.js";
|
||||
|
||||
// Transform docNavigationItems into the format we need
|
||||
function transformNavItems() {
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# cojson-storage-indexeddb
|
||||
|
||||
## 0.14.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [c8b33ad]
|
||||
- cojson@0.14.1
|
||||
- cojson-storage@0.14.1
|
||||
|
||||
## 0.14.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cojson-storage-indexeddb",
|
||||
"version": "0.14.0",
|
||||
"version": "0.14.1",
|
||||
"main": "dist/index.js",
|
||||
"type": "module",
|
||||
"types": "dist/index.d.ts",
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# cojson-storage-sqlite
|
||||
|
||||
## 0.14.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [c8b33ad]
|
||||
- cojson@0.14.1
|
||||
- cojson-storage@0.14.1
|
||||
|
||||
## 0.14.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"name": "cojson-storage-sqlite",
|
||||
"type": "module",
|
||||
"version": "0.14.0",
|
||||
"version": "0.14.1",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"better-sqlite3": "^11.7.0",
|
||||
"cojson": "workspace:0.14.0",
|
||||
"cojson": "workspace:0.14.1",
|
||||
"cojson-storage": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# cojson-storage
|
||||
|
||||
## 0.14.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [c8b33ad]
|
||||
- cojson@0.14.1
|
||||
|
||||
## 0.14.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cojson-storage",
|
||||
"version": "0.14.0",
|
||||
"version": "0.14.1",
|
||||
"main": "dist/index.js",
|
||||
"type": "module",
|
||||
"types": "dist/index.d.ts",
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# cojson-transport-nodejs-ws
|
||||
|
||||
## 0.14.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [c8b33ad]
|
||||
- cojson@0.14.1
|
||||
|
||||
## 0.14.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "cojson-transport-ws",
|
||||
"type": "module",
|
||||
"version": "0.14.0",
|
||||
"version": "0.14.1",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# cojson
|
||||
|
||||
## 0.14.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- c8b33ad: Force sync of the group after acceptInvite
|
||||
|
||||
## 0.14.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
},
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"version": "0.14.0",
|
||||
"version": "0.14.1",
|
||||
"devDependencies": {
|
||||
"@opentelemetry/sdk-metrics": "^2.0.0",
|
||||
"typescript": "catalog:"
|
||||
|
||||
@@ -451,7 +451,9 @@ export class RawGroup<
|
||||
}
|
||||
|
||||
getCurrentReadKeyId() {
|
||||
if (this.myRole() === "writeOnly") {
|
||||
const myRole = this.myRole();
|
||||
|
||||
if (myRole === "writeOnly") {
|
||||
const accountId = this.core.node.getCurrentAgent().id;
|
||||
|
||||
const key = this.get(`writeKeyFor_${accountId}`) as KeyID;
|
||||
@@ -469,6 +471,16 @@ export class RawGroup<
|
||||
return key;
|
||||
}
|
||||
|
||||
if (!myRole) {
|
||||
const accountId = this.core.node.getCurrentAgent().id;
|
||||
|
||||
const key = this.get(`writeKeyFor_${accountId}`) as KeyID;
|
||||
|
||||
if (key) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
return this.get("readKey");
|
||||
}
|
||||
|
||||
@@ -670,22 +682,24 @@ export class RawGroup<
|
||||
);
|
||||
}
|
||||
|
||||
const value = role === "inherit" ? "extend" : role;
|
||||
|
||||
this.set(`parent_${parent.id}`, value, "trusting");
|
||||
parent.set(`child_${this.id}`, "extend", "trusting");
|
||||
|
||||
if (
|
||||
parent.myRole() !== "admin" &&
|
||||
parent.myRole() !== "writer" &&
|
||||
parent.myRole() !== "reader" &&
|
||||
parent.myRole() !== "writeOnly"
|
||||
) {
|
||||
throw new Error(
|
||||
"To extend a group, the current account must be a member of the parent group",
|
||||
// Create a writeOnly key in the parent group to be able to reveal the current child key to the parent group
|
||||
parent.internalCreateWriteOnlyKeyForMember(
|
||||
this.core.node.getCurrentAgent().id,
|
||||
this.core.node.getCurrentAgent().currentAgentID(),
|
||||
);
|
||||
}
|
||||
|
||||
const value = role === "inherit" ? "extend" : role;
|
||||
|
||||
this.set(`parent_${parent.id}`, value, "trusting");
|
||||
parent.set(`child_${this.id}`, "extend", "trusting");
|
||||
|
||||
const { id: parentReadKeyID, secret: parentReadKeySecret } =
|
||||
parent.core.getCurrentReadKey();
|
||||
if (!parentReadKeySecret) {
|
||||
|
||||
@@ -548,6 +548,7 @@ export class LocalNode {
|
||||
group.processNewTransactions();
|
||||
|
||||
group.core.notifyUpdate("immediate");
|
||||
this.syncManager.requestCoValueSync(group.core);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
|
||||
@@ -345,18 +345,6 @@ function determineValidTransactionsForGroup(
|
||||
validTransactions.push({ txID: { sessionID, txIndex }, tx });
|
||||
continue;
|
||||
} else if (isChildExtension(change.key)) {
|
||||
if (
|
||||
memberState[transactor] !== "admin" &&
|
||||
memberState[transactor] !== "writer" &&
|
||||
memberState[transactor] !== "reader" &&
|
||||
memberState[transactor] !== "writeOnly"
|
||||
) {
|
||||
logPermissionError(
|
||||
"Only admins, writers, readers and writeOnly can set child extensions",
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
validTransactions.push({ txID: { sessionID, txIndex }, tx });
|
||||
continue;
|
||||
} else if (isWriteKeyForMember(change.key)) {
|
||||
|
||||
@@ -157,6 +157,29 @@ describe("extend", () => {
|
||||
|
||||
expect(childGroup.roleOf(node2.accountID)).toEqual(undefined);
|
||||
});
|
||||
|
||||
test("should be possible to extend a group without having membership in the parent group", async () => {
|
||||
const { node1, node2, node3 } = await createThreeConnectedNodes(
|
||||
"server",
|
||||
"server",
|
||||
"server",
|
||||
);
|
||||
|
||||
const parentGroup = node1.node.createGroup();
|
||||
const childGroup = node2.node.createGroup();
|
||||
|
||||
const alice = await loadCoValueOrFail(node1.node, node3.accountID);
|
||||
parentGroup.addMember(alice, "writer");
|
||||
|
||||
const parentGroupOnNode2 = await loadCoValueOrFail(
|
||||
node2.node,
|
||||
parentGroup.id,
|
||||
);
|
||||
|
||||
childGroup.extend(parentGroupOnNode2);
|
||||
|
||||
expect(childGroup.roleOf(alice.id)).toBe("writer");
|
||||
});
|
||||
});
|
||||
|
||||
describe("unextend", () => {
|
||||
@@ -191,6 +214,78 @@ describe("unextend", () => {
|
||||
expect(childGroup.roleOf(alice.id)).toBe(undefined);
|
||||
});
|
||||
|
||||
test("should work when the account has no access to the parent group but owns the writeKey", async () => {
|
||||
const { node1, node2, node3 } = await createThreeConnectedNodes(
|
||||
"server",
|
||||
"server",
|
||||
"server",
|
||||
);
|
||||
|
||||
const parentGroup = node1.node.createGroup();
|
||||
const childGroup = node2.node.createGroup();
|
||||
|
||||
const alice = await loadCoValueOrFail(node1.node, node3.accountID);
|
||||
parentGroup.addMember(alice, "writer");
|
||||
|
||||
const parentGroupOnNode2 = await loadCoValueOrFail(
|
||||
node2.node,
|
||||
parentGroup.id,
|
||||
);
|
||||
|
||||
childGroup.extend(parentGroupOnNode2);
|
||||
|
||||
expect(childGroup.roleOf(alice.id)).toBe("writer");
|
||||
|
||||
// `childGroup` no longer has `parentGroup`'s members
|
||||
await childGroup.revokeExtend(parentGroup);
|
||||
expect(childGroup.roleOf(alice.id)).toBe(undefined);
|
||||
|
||||
const map = childGroup.createMap();
|
||||
map.set("test", "Hello!");
|
||||
|
||||
const mapOnAlice = await loadCoValueOrFail(node3.node, map.id);
|
||||
|
||||
expect(mapOnAlice.get("test")).toEqual(undefined);
|
||||
});
|
||||
|
||||
test("should work when the account has no access to the parent group and not owns the writeKey", async () => {
|
||||
const {
|
||||
node1: bobNode,
|
||||
node2: johnNode,
|
||||
node3: aliceNode,
|
||||
} = await createThreeConnectedNodes("server", "server", "server");
|
||||
|
||||
const parentGroup = bobNode.node.createGroup();
|
||||
const childGroup = johnNode.node.createGroup();
|
||||
|
||||
const parentGroupOnJohn = await loadCoValueOrFail(
|
||||
johnNode.node,
|
||||
parentGroup.id,
|
||||
);
|
||||
|
||||
childGroup.extend(parentGroupOnJohn);
|
||||
|
||||
const bob = await loadCoValueOrFail(johnNode.node, bobNode.accountID);
|
||||
const alice = await loadCoValueOrFail(johnNode.node, aliceNode.accountID);
|
||||
childGroup.addMember(alice, "admin");
|
||||
|
||||
const childGroupOnAlice = await loadCoValueOrFail(
|
||||
aliceNode.node,
|
||||
childGroup.id,
|
||||
);
|
||||
|
||||
// `childGroup` no longer has `parentGroup`'s members
|
||||
await childGroupOnAlice.revokeExtend(parentGroup);
|
||||
expect(childGroupOnAlice.roleOf(bob.id)).toBe(undefined);
|
||||
|
||||
const map = childGroupOnAlice.createMap();
|
||||
map.set("test", "Hello!");
|
||||
|
||||
const mapOnBob = await loadCoValueOrFail(bobNode.node, map.id);
|
||||
|
||||
expect(mapOnBob.get("test")).toEqual(undefined);
|
||||
});
|
||||
|
||||
test("should do nothing if applied to a group that is not extended", async () => {
|
||||
const { node1, node2, node3 } = await createThreeConnectedNodes(
|
||||
"server",
|
||||
|
||||
@@ -323,6 +323,10 @@ describe("Group invites", () => {
|
||||
|
||||
expect(groupOnMemberNode.roleOf(member.accountID)).toEqual("reader");
|
||||
|
||||
await waitFor(() => {
|
||||
expect(group.roleOf(member.accountID)).toEqual("reader");
|
||||
});
|
||||
|
||||
// Verify read access is restored
|
||||
const personOnMemberNode = await loadCoValueOrFail(member.node, person.id);
|
||||
expect(personOnMemberNode.get("name")).toEqual("John Doe");
|
||||
|
||||
@@ -1982,40 +1982,6 @@ test("Writers, readers and writeOnly can set child extensions", () => {
|
||||
expect(groupAsReader.get(`child_${childGroup.id}`)).toEqual("extend");
|
||||
});
|
||||
|
||||
test("Invitees can not set child extensions", () => {
|
||||
const { group, node } = newGroupHighLevel();
|
||||
const childGroup = node.createGroup();
|
||||
|
||||
const adminInvite = createAccountInNode(node);
|
||||
const writerInvite = createAccountInNode(node);
|
||||
const readerInvite = createAccountInNode(node);
|
||||
|
||||
group.addMember(adminInvite, "adminInvite");
|
||||
group.addMember(writerInvite, "writerInvite");
|
||||
group.addMember(readerInvite, "readerInvite");
|
||||
|
||||
const groupAsAdminInvite = expectGroup(
|
||||
group.core.contentInClonedNodeWithDifferentAccount(adminInvite),
|
||||
);
|
||||
|
||||
groupAsAdminInvite.set(`child_${childGroup.id}`, "extend", "trusting");
|
||||
expect(groupAsAdminInvite.get(`child_${childGroup.id}`)).toBeUndefined();
|
||||
|
||||
const groupAsWriterInvite = expectGroup(
|
||||
group.core.contentInClonedNodeWithDifferentAccount(writerInvite),
|
||||
);
|
||||
|
||||
groupAsWriterInvite.set(`child_${childGroup.id}`, "extend", "trusting");
|
||||
expect(groupAsWriterInvite.get(`child_${childGroup.id}`)).toBeUndefined();
|
||||
|
||||
const groupAsReaderInvite = expectGroup(
|
||||
group.core.contentInClonedNodeWithDifferentAccount(readerInvite),
|
||||
);
|
||||
|
||||
groupAsReaderInvite.set(`child_${childGroup.id}`, "extend", "trusting");
|
||||
expect(groupAsReaderInvite.get(`child_${childGroup.id}`)).toBeUndefined();
|
||||
});
|
||||
|
||||
test("Member roles are inherited by child groups (except invites)", () => {
|
||||
const { group, node, admin } = newGroupHighLevel();
|
||||
const parentGroup = node.createGroup();
|
||||
|
||||
@@ -56,6 +56,10 @@ describe("invitations sync", () => {
|
||||
"invite-consumer -> server | KNOWN Group sessions: header/5",
|
||||
"server -> invite-consumer | CONTENT Map header: true new: After: 0 New: 1",
|
||||
"invite-consumer -> server | KNOWN Map sessions: header/1",
|
||||
"invite-consumer -> server | CONTENT Group header: false new: After: 0 New: 2",
|
||||
"server -> invite-consumer | KNOWN Group sessions: header/7",
|
||||
"server -> invite-provider | CONTENT Group header: false new: After: 0 New: 2",
|
||||
"invite-provider -> server | KNOWN Group sessions: header/7",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -104,6 +108,10 @@ describe("invitations sync", () => {
|
||||
"invite-consumer -> server | KNOWN Group sessions: header/7",
|
||||
"server -> invite-consumer | CONTENT Map header: true new: After: 0 New: 1",
|
||||
"invite-consumer -> server | KNOWN Map sessions: header/1",
|
||||
"invite-consumer -> server | CONTENT Group header: false new: After: 0 New: 2",
|
||||
"server -> invite-consumer | KNOWN Group sessions: header/9",
|
||||
"server -> invite-provider | CONTENT Group header: false new: After: 0 New: 2",
|
||||
"invite-provider -> server | KNOWN Group sessions: header/9",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -148,15 +156,15 @@ describe("invitations sync", () => {
|
||||
"invite-consumer -> server | LOAD ParentGroup sessions: empty",
|
||||
"server -> invite-consumer | CONTENT ParentGroup header: true new: After: 0 New: 6",
|
||||
"invite-consumer -> server | KNOWN ParentGroup sessions: header/6",
|
||||
"invite-consumer -> server | LOAD Map sessions: empty",
|
||||
"server -> invite-consumer | CONTENT Group header: true new: After: 0 New: 5",
|
||||
"invite-consumer -> server | KNOWN Group sessions: header/5",
|
||||
"server -> invite-consumer | CONTENT Map header: true new: After: 0 New: 1",
|
||||
"invite-consumer -> server | CONTENT ParentGroup header: false new: After: 0 New: 2",
|
||||
"server -> invite-consumer | KNOWN ParentGroup sessions: header/8",
|
||||
"server -> invite-provider | CONTENT ParentGroup header: false new: After: 0 New: 2",
|
||||
"invite-consumer -> server | KNOWN Map sessions: header/1",
|
||||
"invite-consumer -> server | LOAD Map sessions: empty",
|
||||
"invite-provider -> server | KNOWN ParentGroup sessions: header/8",
|
||||
"server -> invite-consumer | CONTENT Group header: true new: After: 0 New: 5",
|
||||
"invite-consumer -> server | KNOWN Group sessions: header/5",
|
||||
"server -> invite-consumer | CONTENT Map header: true new: After: 0 New: 1",
|
||||
"invite-consumer -> server | KNOWN Map sessions: header/1",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
@@ -1,5 +1,26 @@
|
||||
# jazz-auth-betterauth
|
||||
|
||||
## 0.14.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3d1027f]
|
||||
- Updated dependencies [c240eed]
|
||||
- jazz-tools@0.14.2
|
||||
- jazz-browser@0.14.2
|
||||
- jazz-betterauth-client-plugin@0.14.2
|
||||
|
||||
## 0.14.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [c8b33ad]
|
||||
- Updated dependencies [cdfc105]
|
||||
- cojson@0.14.1
|
||||
- jazz-tools@0.14.1
|
||||
- jazz-browser@0.14.1
|
||||
- jazz-betterauth-client-plugin@0.14.1
|
||||
|
||||
## 0.14.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-auth-betterauth",
|
||||
"version": "0.14.0",
|
||||
"version": "0.14.2",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.ts",
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
# jazz-auth-clerk
|
||||
|
||||
## 0.14.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3d1027f]
|
||||
- Updated dependencies [c240eed]
|
||||
- jazz-tools@0.14.2
|
||||
- jazz-browser@0.14.2
|
||||
|
||||
## 0.14.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [c8b33ad]
|
||||
- Updated dependencies [cdfc105]
|
||||
- cojson@0.14.1
|
||||
- jazz-tools@0.14.1
|
||||
- jazz-browser@0.14.1
|
||||
|
||||
## 0.14.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
{
|
||||
"name": "jazz-auth-clerk",
|
||||
"version": "0.14.0",
|
||||
"version": "0.14.2",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cojson": "workspace:0.14.0",
|
||||
"jazz-browser": "workspace:0.14.0",
|
||||
"jazz-tools": "workspace:0.14.0"
|
||||
"cojson": "workspace:0.14.1",
|
||||
"jazz-browser": "workspace:0.14.2",
|
||||
"jazz-tools": "workspace:0.14.2"
|
||||
},
|
||||
"scripts": {
|
||||
"format-and-lint": "biome check .",
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# jazz-betterauth-client-plugin
|
||||
|
||||
## 0.14.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-betterauth-server-plugin@0.14.2
|
||||
|
||||
## 0.14.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-betterauth-server-plugin@0.14.1
|
||||
|
||||
## 0.14.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-betterauth-client-plugin",
|
||||
"version": "0.14.0",
|
||||
"version": "0.14.2",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.ts",
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
# jazz-betterauth-server-plugin
|
||||
|
||||
## 0.14.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3d1027f]
|
||||
- Updated dependencies [c240eed]
|
||||
- jazz-tools@0.14.2
|
||||
- jazz-browser@0.14.2
|
||||
|
||||
## 0.14.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [c8b33ad]
|
||||
- Updated dependencies [cdfc105]
|
||||
- cojson@0.14.1
|
||||
- jazz-tools@0.14.1
|
||||
- jazz-browser@0.14.1
|
||||
|
||||
## 0.14.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-betterauth-server-plugin",
|
||||
"version": "0.14.0",
|
||||
"version": "0.14.2",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.ts",
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
# jazz-browser-media-images
|
||||
|
||||
## 0.14.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3d1027f]
|
||||
- Updated dependencies [c240eed]
|
||||
- jazz-tools@0.14.2
|
||||
- jazz-browser@0.14.2
|
||||
|
||||
## 0.14.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [cdfc105]
|
||||
- jazz-tools@0.14.1
|
||||
- jazz-browser@0.14.1
|
||||
|
||||
## 0.14.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-browser-media-images",
|
||||
"version": "0.14.0",
|
||||
"version": "0.14.2",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
@@ -8,8 +8,8 @@
|
||||
"dependencies": {
|
||||
"@types/image-blob-reduce": "^4.1.1",
|
||||
"image-blob-reduce": "^4.1.0",
|
||||
"jazz-browser": "workspace:0.14.0",
|
||||
"jazz-tools": "workspace:0.14.0",
|
||||
"jazz-browser": "workspace:0.14.2",
|
||||
"jazz-tools": "workspace:0.14.2",
|
||||
"pica": "^9.0.1"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
# jazz-browser
|
||||
|
||||
## 0.14.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3d1027f]
|
||||
- Updated dependencies [c240eed]
|
||||
- jazz-tools@0.14.2
|
||||
|
||||
## 0.14.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [c8b33ad]
|
||||
- Updated dependencies [cdfc105]
|
||||
- cojson@0.14.1
|
||||
- jazz-tools@0.14.1
|
||||
- cojson-storage-indexeddb@0.14.1
|
||||
- cojson-transport-ws@0.14.1
|
||||
|
||||
## 0.14.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-browser",
|
||||
"version": "0.14.0",
|
||||
"version": "0.14.2",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
|
||||
@@ -1,5 +1,29 @@
|
||||
# jazz-browser
|
||||
|
||||
## 0.14.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3d1027f]
|
||||
- Updated dependencies [c240eed]
|
||||
- jazz-tools@0.14.2
|
||||
- jazz-auth-clerk@0.14.2
|
||||
- jazz-react-core@0.14.2
|
||||
- jazz-react-native-core@0.14.2
|
||||
|
||||
## 0.14.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [c8b33ad]
|
||||
- Updated dependencies [cdfc105]
|
||||
- cojson@0.14.1
|
||||
- jazz-tools@0.14.1
|
||||
- cojson-transport-ws@0.14.1
|
||||
- jazz-auth-clerk@0.14.1
|
||||
- jazz-react-core@0.14.1
|
||||
- jazz-react-native-core@0.14.1
|
||||
|
||||
## 0.14.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-expo",
|
||||
"version": "0.14.0",
|
||||
"version": "0.14.2",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
# jazz-inspector-element
|
||||
|
||||
## 0.14.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3d1027f]
|
||||
- Updated dependencies [c240eed]
|
||||
- jazz-tools@0.14.2
|
||||
- jazz-inspector@0.14.2
|
||||
|
||||
## 0.14.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [cdfc105]
|
||||
- jazz-tools@0.14.1
|
||||
- jazz-inspector@0.14.1
|
||||
|
||||
## 0.14.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-inspector-element",
|
||||
"version": "0.14.0",
|
||||
"version": "0.14.2",
|
||||
"type": "module",
|
||||
"main": "./dist/main.js",
|
||||
"types": "./dist/main.d.ts",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user