Compare commits
147 Commits
jazz-react
...
cojson-sto
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a8af6efe1a | ||
|
|
ee11b30d3a | ||
|
|
ef78d58729 | ||
|
|
40e2dd0ece | ||
|
|
b60c7c1ce0 | ||
|
|
69cd362114 | ||
|
|
526bf0a3cf | ||
|
|
439f0fe57e | ||
|
|
686433f42e | ||
|
|
3ba258e181 | ||
|
|
d6e143e4d5 | ||
|
|
3e6229da4d | ||
|
|
adfc9a6032 | ||
|
|
1a6879b1c2 | ||
|
|
172fec56f6 | ||
|
|
13892071f5 | ||
|
|
b62d75b847 | ||
|
|
beafbd3088 | ||
|
|
653d8ba69f | ||
|
|
6c23dab790 | ||
|
|
80530a4065 | ||
|
|
2ba5ea684e | ||
|
|
a8466946d3 | ||
|
|
105b240076 | ||
|
|
097dd8a646 | ||
|
|
138dd7ee8b | ||
|
|
5b17085b30 | ||
|
|
cb7bdffabe | ||
|
|
90892523da | ||
|
|
693a058890 | ||
|
|
4d20f1bcfc | ||
|
|
36bf3924ff | ||
|
|
6b6fc8b31a | ||
|
|
1649f06667 | ||
|
|
e9bd290dec | ||
|
|
9d76292db5 | ||
|
|
66373bade3 | ||
|
|
40118a0b2c | ||
|
|
86b57415b8 | ||
|
|
cdbc37a144 | ||
|
|
e996c71e83 | ||
|
|
0f704a2330 | ||
|
|
2f206e7613 | ||
|
|
761759c893 | ||
|
|
15c57e24ad | ||
|
|
580e2577c9 | ||
|
|
3804198c02 | ||
|
|
537d56a30e | ||
|
|
5524d3e0cb | ||
|
|
3d2682e3a7 | ||
|
|
0c131f3af4 | ||
|
|
fada6b0eb5 | ||
|
|
c8b94475cd | ||
|
|
162a024345 | ||
|
|
7cd195b3a4 | ||
|
|
ccad5a3c70 | ||
|
|
4268d189cb | ||
|
|
d48712986f | ||
|
|
ba2fda5de1 | ||
|
|
b1d26f23d6 | ||
|
|
e7766746d9 | ||
|
|
7c64d104a6 | ||
|
|
1870a1268a | ||
|
|
a8222368d0 | ||
|
|
72d11ce003 | ||
|
|
5fbf9770d2 | ||
|
|
707dedb33e | ||
|
|
86d42d9e49 | ||
|
|
64f01915f4 | ||
|
|
9a8fd2ce47 | ||
|
|
4dc7cdb4e6 | ||
|
|
84b993944d | ||
|
|
fa064443a0 | ||
|
|
32e9678394 | ||
|
|
b1850efd7f | ||
|
|
922e3c8244 | ||
|
|
1c063455d1 | ||
|
|
35abeba323 | ||
|
|
84f623097f | ||
|
|
73015a3438 | ||
|
|
7c3bf78fef | ||
|
|
68cb6064a5 | ||
|
|
992d5e572e | ||
|
|
c75a3042ce | ||
|
|
fa8b20899d | ||
|
|
f24cad1909 | ||
|
|
8b2df0e5e2 | ||
|
|
f93222079f | ||
|
|
514f4c9a72 | ||
|
|
d5daf060c9 | ||
|
|
604cd4e3a9 | ||
|
|
3446b38f69 | ||
|
|
de12b03d3f | ||
|
|
c3db5cf0b5 | ||
|
|
e7f2521b41 | ||
|
|
0c6cd571c9 | ||
|
|
e1d56a45f7 | ||
|
|
e40e01fce0 | ||
|
|
d70343d864 | ||
|
|
160ab768e9 | ||
|
|
4ff03f67d9 | ||
|
|
5a81adffec | ||
|
|
f4fdb3c14e | ||
|
|
d001144a87 | ||
|
|
ded473b75e | ||
|
|
602c34b0f2 | ||
|
|
77cf06945a | ||
|
|
61f39bb56f | ||
|
|
7cc51b77f3 | ||
|
|
bf303d58e3 | ||
|
|
def5c474e6 | ||
|
|
c81dca23ad | ||
|
|
b470f63f86 | ||
|
|
5c7072bf6e | ||
|
|
bd62b1342a | ||
|
|
af314e8584 | ||
|
|
8ad1878f86 | ||
|
|
2fe5cd1326 | ||
|
|
b3605c0c22 | ||
|
|
e272849026 | ||
|
|
c4fdfeaa48 | ||
|
|
3f0859c3f2 | ||
|
|
6e286bac7e | ||
|
|
235aab15b6 | ||
|
|
a440121ac9 | ||
|
|
8ad9fc57cc | ||
|
|
a763b947b8 | ||
|
|
026a26da3c | ||
|
|
c6142a1f64 | ||
|
|
f3fb2dee52 | ||
|
|
3c97e8e7f2 | ||
|
|
7996a2aa9c | ||
|
|
bf399d72c1 | ||
|
|
60fefe8158 | ||
|
|
9be66e196c | ||
|
|
04d96e52e0 | ||
|
|
5b483dac6f | ||
|
|
fadb4bf76e | ||
|
|
98a25b1fd6 | ||
|
|
6df5d72dfd | ||
|
|
b91b33e9be | ||
|
|
fd7226585b | ||
|
|
712b67b782 | ||
|
|
56c7a2dda2 | ||
|
|
e050f17945 | ||
|
|
8a60897086 | ||
|
|
06db1dd423 |
@@ -1,5 +1,32 @@
|
||||
# chat-rn-expo-clerk
|
||||
|
||||
## 1.0.112
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [439f0fe]
|
||||
- jazz-tools@0.13.20
|
||||
- jazz-expo@0.13.20
|
||||
- jazz-react-native-media-images@0.13.20
|
||||
|
||||
## 1.0.111
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [80530a4]
|
||||
- jazz-tools@0.13.19
|
||||
- jazz-expo@0.13.19
|
||||
- jazz-react-native-media-images@0.13.19
|
||||
|
||||
## 1.0.110
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [761759c]
|
||||
- jazz-tools@0.13.18
|
||||
- jazz-expo@0.13.18
|
||||
- jazz-react-native-media-images@0.13.18
|
||||
|
||||
## 1.0.109
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "chat-rn-expo-clerk",
|
||||
"main": "index.js",
|
||||
"version": "1.0.109",
|
||||
"version": "1.0.112",
|
||||
"scripts": {
|
||||
"build": "expo export -p ios",
|
||||
"start": "expo start",
|
||||
|
||||
@@ -1,5 +1,29 @@
|
||||
# chat-rn-expo
|
||||
|
||||
## 1.0.99
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [439f0fe]
|
||||
- jazz-tools@0.13.20
|
||||
- jazz-expo@0.13.20
|
||||
|
||||
## 1.0.98
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [80530a4]
|
||||
- jazz-tools@0.13.19
|
||||
- jazz-expo@0.13.19
|
||||
|
||||
## 1.0.97
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [761759c]
|
||||
- jazz-tools@0.13.18
|
||||
- jazz-expo@0.13.18
|
||||
|
||||
## 1.0.96
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "chat-rn-expo",
|
||||
"version": "1.0.96",
|
||||
"version": "1.0.99",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "expo export -p ios",
|
||||
|
||||
@@ -20,6 +20,7 @@ import { Chat, Message } from "./schema";
|
||||
export default function ChatScreen({ navigation }: { navigation: any }) {
|
||||
const { me, logOut } = useAccount();
|
||||
const [chatId, setChatId] = useState<ID<Chat>>();
|
||||
const [chatIdInput, setChatIdInput] = useState<string>();
|
||||
const loadedChat = useCoState(Chat, chatId, { resolve: { $each: true } });
|
||||
const [message, setMessage] = useState("");
|
||||
const profile = useCoState(Profile, me._refs.profile?.id, {});
|
||||
@@ -57,27 +58,11 @@ export default function ChatScreen({ navigation }: { navigation: any }) {
|
||||
};
|
||||
|
||||
const joinChat = () => {
|
||||
Alert.prompt(
|
||||
"Join Chat",
|
||||
"Enter the Chat ID (example: co_zBGEHYvRfGuT2YSBraY3njGjnde)",
|
||||
[
|
||||
{
|
||||
text: "Cancel",
|
||||
style: "cancel",
|
||||
},
|
||||
{
|
||||
text: "Join",
|
||||
onPress: (chatId) => {
|
||||
if (chatId) {
|
||||
setChatId(chatId as ID<Chat>);
|
||||
} else {
|
||||
Alert.alert("Error", "Chat ID cannot be empty.");
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
"plain-text",
|
||||
);
|
||||
if (chatIdInput) {
|
||||
setChatId(chatIdInput as ID<Chat>);
|
||||
} else {
|
||||
Alert.alert("Error", "Chat ID cannot be empty.");
|
||||
}
|
||||
};
|
||||
|
||||
const sendMessage = () => {
|
||||
@@ -160,9 +145,25 @@ export default function ChatScreen({ navigation }: { navigation: any }) {
|
||||
>
|
||||
<Text className="text-white font-semibold">Start new chat</Text>
|
||||
</TouchableOpacity>
|
||||
<Text className="text-m font-bold mt-6">Join existing chat</Text>
|
||||
<TextInput
|
||||
className="rounded h-12 p-2 m-2 mt-4 w-80 border border-gray-200 block"
|
||||
placeholder="Chat ID"
|
||||
value={chatIdInput ?? ""}
|
||||
onChangeText={(value) => {
|
||||
setChatIdInput(value);
|
||||
}}
|
||||
textAlignVertical="center"
|
||||
onSubmitEditing={() => {
|
||||
if (chatIdInput) {
|
||||
setChatId(chatIdInput as ID<Chat>);
|
||||
}
|
||||
}}
|
||||
testID="chat-id-input"
|
||||
/>
|
||||
<TouchableOpacity
|
||||
onPress={joinChat}
|
||||
className="bg-green-500 p-4 rounded-md mt-4"
|
||||
className="bg-green-500 p-4 rounded-md"
|
||||
>
|
||||
<Text className="text-white font-semibold">Join chat</Text>
|
||||
</TouchableOpacity>
|
||||
@@ -172,7 +173,6 @@ export default function ChatScreen({ navigation }: { navigation: any }) {
|
||||
<FlatList
|
||||
contentContainerStyle={{
|
||||
flexGrow: 1,
|
||||
flex: 1,
|
||||
gap: 6,
|
||||
padding: 8,
|
||||
}}
|
||||
|
||||
@@ -9,6 +9,8 @@ appId: com.jazz.chatrn
|
||||
# - tapOn: "Reload"
|
||||
|
||||
# login
|
||||
- assertVisible: "Logout"
|
||||
- tapOn: "Logout"
|
||||
- assertVisible: "Anonymous user"
|
||||
- runFlow:
|
||||
label: "Erase existing username"
|
||||
@@ -42,9 +44,11 @@ appId: com.jazz.chatrn
|
||||
# logout
|
||||
- tapOn: "Logout"
|
||||
- assertVisible: "Anonymous user"
|
||||
# This doesn't work on CI, maybe because Android has a different alert dialog
|
||||
# - tapOn: "Join chat"
|
||||
# - inputText: "co_zFs6KFyhxPw4xtw83tcEMzeHUNv" # Use a static id because maestro doesn't have access to the system clipboard
|
||||
# - pressKey: "enter"
|
||||
# - assertVisible: "boorad"
|
||||
# - assertVisible: "bro, low key, it do be like that tho"
|
||||
|
||||
# join chat
|
||||
- tapOn:
|
||||
id: "chat-id-input"
|
||||
- inputText: "co_zFs6KFyhxPw4xtw83tcEMzeHUNv" # Use a static id because maestro doesn't have access to the system clipboard
|
||||
- tapOn: "Join chat"
|
||||
- assertVisible: "boorad"
|
||||
- assertVisible: "bro, low key, it do be like that tho"
|
||||
|
||||
@@ -1,5 +1,41 @@
|
||||
# chat-rn
|
||||
|
||||
## 1.0.107
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [adfc9a6]
|
||||
- Updated dependencies [1389207]
|
||||
- Updated dependencies [d6e143e]
|
||||
- Updated dependencies [439f0fe]
|
||||
- Updated dependencies [3e6229d]
|
||||
- cojson@0.13.20
|
||||
- jazz-tools@0.13.20
|
||||
- jazz-react-native@0.13.20
|
||||
- cojson-transport-ws@0.13.20
|
||||
|
||||
## 1.0.106
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [80530a4]
|
||||
- jazz-tools@0.13.19
|
||||
- jazz-react-native@0.13.19
|
||||
|
||||
## 1.0.105
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [9089252]
|
||||
- Updated dependencies [b470f63]
|
||||
- Updated dependencies [761759c]
|
||||
- Updated dependencies [66373ba]
|
||||
- Updated dependencies [f24cad1]
|
||||
- cojson@0.13.18
|
||||
- jazz-tools@0.13.18
|
||||
- cojson-transport-ws@0.13.18
|
||||
- jazz-react-native@0.13.18
|
||||
|
||||
## 1.0.104
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "chat-rn",
|
||||
"version": "1.0.104",
|
||||
"version": "1.0.107",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"android": "react-native run-android",
|
||||
|
||||
@@ -1,5 +1,32 @@
|
||||
# chat-vue
|
||||
|
||||
## 0.0.91
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [439f0fe]
|
||||
- jazz-tools@0.13.20
|
||||
- jazz-browser@0.13.20
|
||||
- jazz-vue@0.13.20
|
||||
|
||||
## 0.0.90
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [80530a4]
|
||||
- jazz-tools@0.13.19
|
||||
- jazz-browser@0.13.19
|
||||
- jazz-vue@0.13.19
|
||||
|
||||
## 0.0.89
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [761759c]
|
||||
- jazz-tools@0.13.18
|
||||
- jazz-browser@0.13.18
|
||||
- jazz-vue@0.13.18
|
||||
|
||||
## 0.0.88
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "chat-vue",
|
||||
"version": "0.0.88",
|
||||
"version": "0.0.91",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,32 @@
|
||||
# jazz-example-chat
|
||||
|
||||
## 0.0.189
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [439f0fe]
|
||||
- jazz-tools@0.13.20
|
||||
- jazz-inspector@0.13.20
|
||||
- jazz-react@0.13.20
|
||||
|
||||
## 0.0.188
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [80530a4]
|
||||
- jazz-tools@0.13.19
|
||||
- jazz-inspector@0.13.19
|
||||
- jazz-react@0.13.19
|
||||
|
||||
## 0.0.187
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [761759c]
|
||||
- jazz-tools@0.13.18
|
||||
- jazz-inspector@0.13.18
|
||||
- jazz-react@0.13.18
|
||||
|
||||
## 0.0.186
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-example-chat",
|
||||
"private": true,
|
||||
"version": "0.0.186",
|
||||
"version": "0.0.189",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,32 @@
|
||||
# minimal-auth-clerk
|
||||
|
||||
## 0.0.88
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [439f0fe]
|
||||
- jazz-tools@0.13.20
|
||||
- jazz-react@0.13.20
|
||||
- jazz-react-auth-clerk@0.13.20
|
||||
|
||||
## 0.0.87
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [80530a4]
|
||||
- jazz-tools@0.13.19
|
||||
- jazz-react@0.13.19
|
||||
- jazz-react-auth-clerk@0.13.19
|
||||
|
||||
## 0.0.86
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [761759c]
|
||||
- jazz-tools@0.13.18
|
||||
- jazz-react@0.13.18
|
||||
- jazz-react-auth-clerk@0.13.18
|
||||
|
||||
## 0.0.85
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "clerk",
|
||||
"private": true,
|
||||
"version": "0.0.85",
|
||||
"version": "0.0.88",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,29 @@
|
||||
# file-share-svelte
|
||||
|
||||
## 0.0.71
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [439f0fe]
|
||||
- jazz-tools@0.13.20
|
||||
- jazz-svelte@0.13.20
|
||||
|
||||
## 0.0.70
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [80530a4]
|
||||
- jazz-tools@0.13.19
|
||||
- jazz-svelte@0.13.19
|
||||
|
||||
## 0.0.69
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [761759c]
|
||||
- jazz-tools@0.13.18
|
||||
- jazz-svelte@0.13.18
|
||||
|
||||
## 0.0.68
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "file-share-svelte",
|
||||
"version": "0.0.68",
|
||||
"version": "0.0.71",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
const fileId = file._refs.file.id;
|
||||
|
||||
// Load the file as a blob, can take a while
|
||||
const blob = await FileStream.loadAsBlob(fileId, me, {});
|
||||
const blob = await FileStream.loadAsBlob(fileId);
|
||||
if (!blob) {
|
||||
toast.error('Failed to download file');
|
||||
return;
|
||||
|
||||
@@ -1,48 +1,26 @@
|
||||
<script lang="ts">
|
||||
import { useAccount, useCoState } from 'jazz-svelte';
|
||||
import { SharedFile, ListOfSharedFiles } from '$lib/schema';
|
||||
import { createInviteLink } from 'jazz-svelte';
|
||||
import { FileStream } from 'jazz-tools';
|
||||
import FileItem from '$lib/components/FileItem.svelte';
|
||||
import { SvelteMap } from 'svelte/reactivity';
|
||||
import { generateTempFileId } from '$lib/utils';
|
||||
import { CloudUpload } from 'lucide-svelte';
|
||||
|
||||
const { me, logOut } = useAccount();
|
||||
|
||||
const mySharedFilesId = me?.root?._refs.sharedFiles.id;
|
||||
const sharedFiles = $derived(useCoState(ListOfSharedFiles, mySharedFilesId, [{}]));
|
||||
const sharedFiles = $derived(useCoState(ListOfSharedFiles, mySharedFilesId));
|
||||
|
||||
let fileInput: HTMLInputElement;
|
||||
|
||||
type PendingSharedFile = {
|
||||
name: string;
|
||||
id: string;
|
||||
createdAt: Date;
|
||||
};
|
||||
|
||||
// Track files that are currently uploading
|
||||
const uploadingFiles = new SvelteMap<string, PendingSharedFile>();
|
||||
|
||||
async function handleFileUpload(event: Event) {
|
||||
const input = event.target as HTMLInputElement;
|
||||
const files = input.files;
|
||||
|
||||
if (!files || !files.length || !me.root?.sharedFiles || !me.root.publicGroup) return;
|
||||
if (!files || !files.length || !me?.root?.sharedFiles || !me?.root?.publicGroup) return;
|
||||
|
||||
const file = files[0];
|
||||
const fileName = file.name;
|
||||
const createdAt = new Date();
|
||||
const fileId = generateTempFileId(fileName, createdAt);
|
||||
|
||||
const tempFile: PendingSharedFile = {
|
||||
name: fileName,
|
||||
id: fileId,
|
||||
createdAt
|
||||
};
|
||||
|
||||
// Add to uploading files
|
||||
uploadingFiles.set(fileId, tempFile);
|
||||
|
||||
try {
|
||||
const ownership = { owner: me.root.publicGroup };
|
||||
@@ -65,17 +43,10 @@
|
||||
// Add the file to the user's files list
|
||||
me.root.sharedFiles.push(sharedFile);
|
||||
} finally {
|
||||
uploadingFiles.delete(fileId);
|
||||
fileInput.value = ''; // reset input
|
||||
}
|
||||
}
|
||||
|
||||
async function shareFile(file: SharedFile) {
|
||||
const inviteLink = createInviteLink(file, 'reader');
|
||||
await navigator.clipboard.writeText(inviteLink);
|
||||
alert('Share link copied to clipboard!');
|
||||
}
|
||||
|
||||
async function deleteFile(file: SharedFile) {
|
||||
if (!me?.root?.sharedFiles || !sharedFiles.current) return;
|
||||
|
||||
@@ -127,13 +98,11 @@
|
||||
<!-- Files List -->
|
||||
<div class="space-y-4">
|
||||
{#if sharedFiles.current}
|
||||
{#if !(sharedFiles.current.length === 0 && uploadingFiles.size === 0)}
|
||||
{#each [...sharedFiles.current, ...uploadingFiles.values()] as file (generateTempFileId(file?.name, file?.createdAt))}
|
||||
{#if !(sharedFiles.current.length === 0)}
|
||||
{#each sharedFiles.current as file}
|
||||
{#if file}
|
||||
<FileItem
|
||||
{file}
|
||||
loading={uploadingFiles.has(generateTempFileId(file?.name, file?.createdAt))}
|
||||
onShare={shareFile}
|
||||
onDelete={deleteFile}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
@@ -1,5 +1,32 @@
|
||||
# jazz-tailwind-demo-auth-starter
|
||||
|
||||
## 0.0.28
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [439f0fe]
|
||||
- jazz-tools@0.13.20
|
||||
- jazz-inspector@0.13.20
|
||||
- jazz-react@0.13.20
|
||||
|
||||
## 0.0.27
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [80530a4]
|
||||
- jazz-tools@0.13.19
|
||||
- jazz-inspector@0.13.19
|
||||
- jazz-react@0.13.19
|
||||
|
||||
## 0.0.26
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [761759c]
|
||||
- jazz-tools@0.13.18
|
||||
- jazz-inspector@0.13.18
|
||||
- jazz-react@0.13.18
|
||||
|
||||
## 0.0.25
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "filestream",
|
||||
"private": true,
|
||||
"version": "0.0.25",
|
||||
"version": "0.0.28",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,29 @@
|
||||
# form
|
||||
|
||||
## 0.1.29
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [439f0fe]
|
||||
- jazz-tools@0.13.20
|
||||
- jazz-react@0.13.20
|
||||
|
||||
## 0.1.28
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [80530a4]
|
||||
- jazz-tools@0.13.19
|
||||
- jazz-react@0.13.19
|
||||
|
||||
## 0.1.27
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [761759c]
|
||||
- jazz-tools@0.13.18
|
||||
- jazz-react@0.13.18
|
||||
|
||||
## 0.1.26
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "form",
|
||||
"private": true,
|
||||
"version": "0.1.26",
|
||||
"version": "0.1.29",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,29 @@
|
||||
# image-upload
|
||||
|
||||
## 0.0.85
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [439f0fe]
|
||||
- jazz-tools@0.13.20
|
||||
- jazz-react@0.13.20
|
||||
|
||||
## 0.0.84
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [80530a4]
|
||||
- jazz-tools@0.13.19
|
||||
- jazz-react@0.13.19
|
||||
|
||||
## 0.0.83
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [761759c]
|
||||
- jazz-tools@0.13.18
|
||||
- jazz-react@0.13.18
|
||||
|
||||
## 0.0.82
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "image-upload",
|
||||
"private": true,
|
||||
"version": "0.0.82",
|
||||
"version": "0.0.85",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,35 @@
|
||||
# jazz-example-inspector
|
||||
|
||||
## 0.0.139
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [adfc9a6]
|
||||
- Updated dependencies [1389207]
|
||||
- Updated dependencies [d6e143e]
|
||||
- Updated dependencies [3e6229d]
|
||||
- cojson@0.13.20
|
||||
- cojson-transport-ws@0.13.20
|
||||
- jazz-inspector@0.13.20
|
||||
|
||||
## 0.0.138
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-inspector@0.13.19
|
||||
|
||||
## 0.0.137
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [9089252]
|
||||
- Updated dependencies [b470f63]
|
||||
- Updated dependencies [66373ba]
|
||||
- Updated dependencies [f24cad1]
|
||||
- cojson@0.13.18
|
||||
- cojson-transport-ws@0.13.18
|
||||
- jazz-inspector@0.13.18
|
||||
|
||||
## 0.0.136
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-inspector-app",
|
||||
"private": true,
|
||||
"version": "0.0.136",
|
||||
"version": "0.0.139",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -8,7 +8,10 @@ export function cn(...inputs: ClassValue[]) {
|
||||
/**
|
||||
* Given a player selections, returns the winner of the current game.
|
||||
*/
|
||||
export function determineWinner(player1Choice: string, player2Choice: string) {
|
||||
export function determineWinner(
|
||||
player1Choice: "rock" | "paper" | "scissors",
|
||||
player2Choice: "rock" | "paper" | "scissors",
|
||||
) {
|
||||
if (player1Choice === player2Choice) {
|
||||
return "draw";
|
||||
} else if (
|
||||
|
||||
@@ -10,139 +10,139 @@
|
||||
|
||||
// Import Routes
|
||||
|
||||
import { Route as rootRoute } from "./routes/__root";
|
||||
import { Route as AuthenticatedImport } from "./routes/_authenticated";
|
||||
import { Route as AuthenticatedGameGameIdImport } from "./routes/_authenticated/game.$gameId";
|
||||
import { Route as AuthenticatedWaitingRoomWaitingRoomIdImport } from "./routes/_authenticated/waiting-room.$waitingRoomId";
|
||||
import { Route as IndexImport } from "./routes/index";
|
||||
import { Route as rootRoute } from './routes/__root'
|
||||
import { Route as AuthenticatedImport } from './routes/_authenticated'
|
||||
import { Route as IndexImport } from './routes/index'
|
||||
import { Route as AuthenticatedWaitingRoomWaitingRoomIdImport } from './routes/_authenticated/waiting-room.$waitingRoomId'
|
||||
import { Route as AuthenticatedGameGameIdImport } from './routes/_authenticated/game.$gameId'
|
||||
|
||||
// Create/Update Routes
|
||||
|
||||
const AuthenticatedRoute = AuthenticatedImport.update({
|
||||
id: "/_authenticated",
|
||||
id: '/_authenticated',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any);
|
||||
} as any)
|
||||
|
||||
const IndexRoute = IndexImport.update({
|
||||
id: "/",
|
||||
path: "/",
|
||||
id: '/',
|
||||
path: '/',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any);
|
||||
} as any)
|
||||
|
||||
const AuthenticatedWaitingRoomWaitingRoomIdRoute =
|
||||
AuthenticatedWaitingRoomWaitingRoomIdImport.update({
|
||||
id: "/waiting-room/$waitingRoomId",
|
||||
path: "/waiting-room/$waitingRoomId",
|
||||
id: '/waiting-room/$waitingRoomId',
|
||||
path: '/waiting-room/$waitingRoomId',
|
||||
getParentRoute: () => AuthenticatedRoute,
|
||||
} as any);
|
||||
} as any)
|
||||
|
||||
const AuthenticatedGameGameIdRoute = AuthenticatedGameGameIdImport.update({
|
||||
id: "/game/$gameId",
|
||||
path: "/game/$gameId",
|
||||
id: '/game/$gameId',
|
||||
path: '/game/$gameId',
|
||||
getParentRoute: () => AuthenticatedRoute,
|
||||
} as any);
|
||||
} as any)
|
||||
|
||||
// Populate the FileRoutesByPath interface
|
||||
|
||||
declare module "@tanstack/react-router" {
|
||||
declare module '@tanstack/react-router' {
|
||||
interface FileRoutesByPath {
|
||||
"/": {
|
||||
id: "/";
|
||||
path: "/";
|
||||
fullPath: "/";
|
||||
preLoaderRoute: typeof IndexImport;
|
||||
parentRoute: typeof rootRoute;
|
||||
};
|
||||
"/_authenticated": {
|
||||
id: "/_authenticated";
|
||||
path: "";
|
||||
fullPath: "";
|
||||
preLoaderRoute: typeof AuthenticatedImport;
|
||||
parentRoute: typeof rootRoute;
|
||||
};
|
||||
"/_authenticated/game/$gameId": {
|
||||
id: "/_authenticated/game/$gameId";
|
||||
path: "/game/$gameId";
|
||||
fullPath: "/game/$gameId";
|
||||
preLoaderRoute: typeof AuthenticatedGameGameIdImport;
|
||||
parentRoute: typeof AuthenticatedImport;
|
||||
};
|
||||
"/_authenticated/waiting-room/$waitingRoomId": {
|
||||
id: "/_authenticated/waiting-room/$waitingRoomId";
|
||||
path: "/waiting-room/$waitingRoomId";
|
||||
fullPath: "/waiting-room/$waitingRoomId";
|
||||
preLoaderRoute: typeof AuthenticatedWaitingRoomWaitingRoomIdImport;
|
||||
parentRoute: typeof AuthenticatedImport;
|
||||
};
|
||||
'/': {
|
||||
id: '/'
|
||||
path: '/'
|
||||
fullPath: '/'
|
||||
preLoaderRoute: typeof IndexImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/_authenticated': {
|
||||
id: '/_authenticated'
|
||||
path: ''
|
||||
fullPath: ''
|
||||
preLoaderRoute: typeof AuthenticatedImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/_authenticated/game/$gameId': {
|
||||
id: '/_authenticated/game/$gameId'
|
||||
path: '/game/$gameId'
|
||||
fullPath: '/game/$gameId'
|
||||
preLoaderRoute: typeof AuthenticatedGameGameIdImport
|
||||
parentRoute: typeof AuthenticatedImport
|
||||
}
|
||||
'/_authenticated/waiting-room/$waitingRoomId': {
|
||||
id: '/_authenticated/waiting-room/$waitingRoomId'
|
||||
path: '/waiting-room/$waitingRoomId'
|
||||
fullPath: '/waiting-room/$waitingRoomId'
|
||||
preLoaderRoute: typeof AuthenticatedWaitingRoomWaitingRoomIdImport
|
||||
parentRoute: typeof AuthenticatedImport
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create and export the route tree
|
||||
|
||||
interface AuthenticatedRouteChildren {
|
||||
AuthenticatedGameGameIdRoute: typeof AuthenticatedGameGameIdRoute;
|
||||
AuthenticatedWaitingRoomWaitingRoomIdRoute: typeof AuthenticatedWaitingRoomWaitingRoomIdRoute;
|
||||
AuthenticatedGameGameIdRoute: typeof AuthenticatedGameGameIdRoute
|
||||
AuthenticatedWaitingRoomWaitingRoomIdRoute: typeof AuthenticatedWaitingRoomWaitingRoomIdRoute
|
||||
}
|
||||
|
||||
const AuthenticatedRouteChildren: AuthenticatedRouteChildren = {
|
||||
AuthenticatedGameGameIdRoute: AuthenticatedGameGameIdRoute,
|
||||
AuthenticatedWaitingRoomWaitingRoomIdRoute:
|
||||
AuthenticatedWaitingRoomWaitingRoomIdRoute,
|
||||
};
|
||||
}
|
||||
|
||||
const AuthenticatedRouteWithChildren = AuthenticatedRoute._addFileChildren(
|
||||
AuthenticatedRouteChildren,
|
||||
);
|
||||
)
|
||||
|
||||
export interface FileRoutesByFullPath {
|
||||
"/": typeof IndexRoute;
|
||||
"": typeof AuthenticatedRouteWithChildren;
|
||||
"/game/$gameId": typeof AuthenticatedGameGameIdRoute;
|
||||
"/waiting-room/$waitingRoomId": typeof AuthenticatedWaitingRoomWaitingRoomIdRoute;
|
||||
'/': typeof IndexRoute
|
||||
'': typeof AuthenticatedRouteWithChildren
|
||||
'/game/$gameId': typeof AuthenticatedGameGameIdRoute
|
||||
'/waiting-room/$waitingRoomId': typeof AuthenticatedWaitingRoomWaitingRoomIdRoute
|
||||
}
|
||||
|
||||
export interface FileRoutesByTo {
|
||||
"/": typeof IndexRoute;
|
||||
"": typeof AuthenticatedRouteWithChildren;
|
||||
"/game/$gameId": typeof AuthenticatedGameGameIdRoute;
|
||||
"/waiting-room/$waitingRoomId": typeof AuthenticatedWaitingRoomWaitingRoomIdRoute;
|
||||
'/': typeof IndexRoute
|
||||
'': typeof AuthenticatedRouteWithChildren
|
||||
'/game/$gameId': typeof AuthenticatedGameGameIdRoute
|
||||
'/waiting-room/$waitingRoomId': typeof AuthenticatedWaitingRoomWaitingRoomIdRoute
|
||||
}
|
||||
|
||||
export interface FileRoutesById {
|
||||
__root__: typeof rootRoute;
|
||||
"/": typeof IndexRoute;
|
||||
"/_authenticated": typeof AuthenticatedRouteWithChildren;
|
||||
"/_authenticated/game/$gameId": typeof AuthenticatedGameGameIdRoute;
|
||||
"/_authenticated/waiting-room/$waitingRoomId": typeof AuthenticatedWaitingRoomWaitingRoomIdRoute;
|
||||
__root__: typeof rootRoute
|
||||
'/': typeof IndexRoute
|
||||
'/_authenticated': typeof AuthenticatedRouteWithChildren
|
||||
'/_authenticated/game/$gameId': typeof AuthenticatedGameGameIdRoute
|
||||
'/_authenticated/waiting-room/$waitingRoomId': typeof AuthenticatedWaitingRoomWaitingRoomIdRoute
|
||||
}
|
||||
|
||||
export interface FileRouteTypes {
|
||||
fileRoutesByFullPath: FileRoutesByFullPath;
|
||||
fullPaths: "/" | "" | "/game/$gameId" | "/waiting-room/$waitingRoomId";
|
||||
fileRoutesByTo: FileRoutesByTo;
|
||||
to: "/" | "" | "/game/$gameId" | "/waiting-room/$waitingRoomId";
|
||||
fileRoutesByFullPath: FileRoutesByFullPath
|
||||
fullPaths: '/' | '' | '/game/$gameId' | '/waiting-room/$waitingRoomId'
|
||||
fileRoutesByTo: FileRoutesByTo
|
||||
to: '/' | '' | '/game/$gameId' | '/waiting-room/$waitingRoomId'
|
||||
id:
|
||||
| "__root__"
|
||||
| "/"
|
||||
| "/_authenticated"
|
||||
| "/_authenticated/game/$gameId"
|
||||
| "/_authenticated/waiting-room/$waitingRoomId";
|
||||
fileRoutesById: FileRoutesById;
|
||||
| '__root__'
|
||||
| '/'
|
||||
| '/_authenticated'
|
||||
| '/_authenticated/game/$gameId'
|
||||
| '/_authenticated/waiting-room/$waitingRoomId'
|
||||
fileRoutesById: FileRoutesById
|
||||
}
|
||||
|
||||
export interface RootRouteChildren {
|
||||
IndexRoute: typeof IndexRoute;
|
||||
AuthenticatedRoute: typeof AuthenticatedRouteWithChildren;
|
||||
IndexRoute: typeof IndexRoute
|
||||
AuthenticatedRoute: typeof AuthenticatedRouteWithChildren
|
||||
}
|
||||
|
||||
const rootRouteChildren: RootRouteChildren = {
|
||||
IndexRoute: IndexRoute,
|
||||
AuthenticatedRoute: AuthenticatedRouteWithChildren,
|
||||
};
|
||||
}
|
||||
|
||||
export const routeTree = rootRoute
|
||||
._addFileChildren(rootRouteChildren)
|
||||
._addFileTypes<FileRouteTypes>();
|
||||
._addFileTypes<FileRouteTypes>()
|
||||
|
||||
/* ROUTE_MANIFEST_START
|
||||
{
|
||||
|
||||
@@ -14,7 +14,7 @@ import { type ID } from "jazz-tools";
|
||||
import { Badge, CircleHelp, Scissors, ScrollText } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
const playIcon = (selection: string | undefined) => {
|
||||
const playIcon = (selection: "rock" | "paper" | "scissors" | undefined) => {
|
||||
switch (selection) {
|
||||
case "rock":
|
||||
return <Badge className="w-5 h-5" />;
|
||||
@@ -50,9 +50,9 @@ function RouteComponent() {
|
||||
const isPlayer1 = loaderGame.player1?.account?.isMe;
|
||||
const player = isPlayer1 ? "player1" : "player2";
|
||||
|
||||
const [playSelection, setPlaySelection] = useState(
|
||||
loaderGame[player]?.playSelection ?? "",
|
||||
);
|
||||
const [playSelection, setPlaySelection] = useState<
|
||||
"rock" | "paper" | "scissors" | undefined
|
||||
>(loaderGame[player]?.playSelection);
|
||||
const sendInboxMessage = experimental_useInboxSender(WORKER_ID);
|
||||
|
||||
const game = useCoState(Game, gameId as ID<Game>);
|
||||
@@ -62,7 +62,7 @@ function RouteComponent() {
|
||||
|
||||
return loaderGame.subscribe((game) => {
|
||||
if (gameCompleted && !game.outcome) {
|
||||
setPlaySelection(""); // Reset play selection when one player clicks on "Start a new game"
|
||||
setPlaySelection(undefined); // Reset play selection when one player clicks on "Start a new game"
|
||||
}
|
||||
|
||||
gameCompleted = Boolean(game.outcome);
|
||||
@@ -82,7 +82,10 @@ function RouteComponent() {
|
||||
|
||||
const opponentSelection = opponentPlayer?.playSelection;
|
||||
|
||||
const onSubmit = async (playSelection: string) => {
|
||||
const onSubmit = async (
|
||||
playSelection: "rock" | "paper" | "scissors" | undefined,
|
||||
) => {
|
||||
if (!playSelection) return;
|
||||
sendInboxMessage(
|
||||
PlayIntent.create({ type: "play", gameId, player, playSelection }),
|
||||
);
|
||||
@@ -117,7 +120,9 @@ function RouteComponent() {
|
||||
) : null}
|
||||
<CardContent>
|
||||
<div>
|
||||
{playSelection === "" ? "Make Your Selection" : "Your Selection: "}
|
||||
{playSelection === undefined
|
||||
? "Make Your Selection"
|
||||
: "Your Selection: "}
|
||||
</div>
|
||||
<CardSmall>{playIcon(playSelection)}</CardSmall>
|
||||
{gameComplete ? null : (
|
||||
@@ -148,7 +153,7 @@ function RouteComponent() {
|
||||
<div className="m-4">
|
||||
<Button
|
||||
disabled={
|
||||
playSelection === "" ||
|
||||
playSelection === undefined ||
|
||||
Boolean(currentPlayer?.playSelection)
|
||||
}
|
||||
onClick={() => onSubmit(playSelection)}
|
||||
|
||||
@@ -30,7 +30,7 @@ export class Game extends CoMap {
|
||||
|
||||
export class Player extends CoMap {
|
||||
account = co.ref(Account);
|
||||
playSelection? = co.string;
|
||||
playSelection? = co.literal("rock", "paper", "scissors");
|
||||
}
|
||||
|
||||
export class WaitingRoom extends CoMap {
|
||||
@@ -47,7 +47,7 @@ export class PlayIntent extends InboxMessage {
|
||||
type = co.literal("play");
|
||||
gameId = co.string;
|
||||
player = co.literal("player1", "player2");
|
||||
playSelection = co.string;
|
||||
playSelection = co.literal("rock", "paper", "scissors");
|
||||
}
|
||||
|
||||
export class NewGameIntent extends InboxMessage {
|
||||
|
||||
@@ -186,9 +186,9 @@ async function handlePlayIntent(_: ID<Account>, message: PlayIntent) {
|
||||
// once both players have a selection, determine the winner
|
||||
if (
|
||||
!!player1Selection &&
|
||||
player1Selection !== "" &&
|
||||
player1Selection !== undefined &&
|
||||
!!player2Selection &&
|
||||
player2Selection !== ""
|
||||
player2Selection !== undefined
|
||||
) {
|
||||
const outcome = determineWinner(player1Selection, player2Selection);
|
||||
game.outcome = outcome;
|
||||
|
||||
@@ -1,5 +1,29 @@
|
||||
# multi-cursors
|
||||
|
||||
## 0.0.81
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [439f0fe]
|
||||
- jazz-tools@0.13.20
|
||||
- jazz-react@0.13.20
|
||||
|
||||
## 0.0.80
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [80530a4]
|
||||
- jazz-tools@0.13.19
|
||||
- jazz-react@0.13.19
|
||||
|
||||
## 0.0.79
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [761759c]
|
||||
- jazz-tools@0.13.18
|
||||
- jazz-react@0.13.18
|
||||
|
||||
## 0.0.78
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "multi-cursors",
|
||||
"private": true,
|
||||
"version": "0.0.78",
|
||||
"version": "0.0.81",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,32 @@
|
||||
# multiauth
|
||||
|
||||
## 0.0.29
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [439f0fe]
|
||||
- jazz-tools@0.13.20
|
||||
- jazz-react@0.13.20
|
||||
- jazz-react-auth-clerk@0.13.20
|
||||
|
||||
## 0.0.28
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [80530a4]
|
||||
- jazz-tools@0.13.19
|
||||
- jazz-react@0.13.19
|
||||
- jazz-react-auth-clerk@0.13.19
|
||||
|
||||
## 0.0.27
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [761759c]
|
||||
- jazz-tools@0.13.18
|
||||
- jazz-react@0.13.18
|
||||
- jazz-react-auth-clerk@0.13.18
|
||||
|
||||
## 0.0.26
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "multiauth",
|
||||
"private": true,
|
||||
"version": "0.0.26",
|
||||
"version": "0.0.29",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,32 @@
|
||||
# jazz-example-musicplayer
|
||||
|
||||
## 0.0.110
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [439f0fe]
|
||||
- jazz-tools@0.13.20
|
||||
- jazz-inspector@0.13.20
|
||||
- jazz-react@0.13.20
|
||||
|
||||
## 0.0.109
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [80530a4]
|
||||
- jazz-tools@0.13.19
|
||||
- jazz-inspector@0.13.19
|
||||
- jazz-react@0.13.19
|
||||
|
||||
## 0.0.108
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [761759c]
|
||||
- jazz-tools@0.13.18
|
||||
- jazz-inspector@0.13.18
|
||||
- jazz-react@0.13.18
|
||||
|
||||
## 0.0.107
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-example-music-player",
|
||||
"private": true,
|
||||
"version": "0.0.107",
|
||||
"version": "0.0.110",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,29 @@
|
||||
# organization
|
||||
|
||||
## 0.0.81
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [439f0fe]
|
||||
- jazz-tools@0.13.20
|
||||
- jazz-react@0.13.20
|
||||
|
||||
## 0.0.80
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [80530a4]
|
||||
- jazz-tools@0.13.19
|
||||
- jazz-react@0.13.19
|
||||
|
||||
## 0.0.79
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [761759c]
|
||||
- jazz-tools@0.13.18
|
||||
- jazz-react@0.13.18
|
||||
|
||||
## 0.0.78
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "organization",
|
||||
"private": true,
|
||||
"version": "0.0.78",
|
||||
"version": "0.0.81",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,29 @@
|
||||
# passkey-svelte
|
||||
|
||||
## 0.0.75
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [439f0fe]
|
||||
- jazz-tools@0.13.20
|
||||
- jazz-svelte@0.13.20
|
||||
|
||||
## 0.0.74
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [80530a4]
|
||||
- jazz-tools@0.13.19
|
||||
- jazz-svelte@0.13.19
|
||||
|
||||
## 0.0.73
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [761759c]
|
||||
- jazz-tools@0.13.18
|
||||
- jazz-svelte@0.13.18
|
||||
|
||||
## 0.0.72
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "passkey-svelte",
|
||||
"version": "0.0.72",
|
||||
"version": "0.0.75",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,29 @@
|
||||
# minimal-auth-passkey
|
||||
|
||||
## 0.0.86
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [439f0fe]
|
||||
- jazz-tools@0.13.20
|
||||
- jazz-react@0.13.20
|
||||
|
||||
## 0.0.85
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [80530a4]
|
||||
- jazz-tools@0.13.19
|
||||
- jazz-react@0.13.19
|
||||
|
||||
## 0.0.84
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [761759c]
|
||||
- jazz-tools@0.13.18
|
||||
- jazz-react@0.13.18
|
||||
|
||||
## 0.0.83
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "passkey",
|
||||
"private": true,
|
||||
"version": "0.0.83",
|
||||
"version": "0.0.86",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,29 @@
|
||||
# passphrase
|
||||
|
||||
## 0.0.83
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [439f0fe]
|
||||
- jazz-tools@0.13.20
|
||||
- jazz-react@0.13.20
|
||||
|
||||
## 0.0.82
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [80530a4]
|
||||
- jazz-tools@0.13.19
|
||||
- jazz-react@0.13.19
|
||||
|
||||
## 0.0.81
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [761759c]
|
||||
- jazz-tools@0.13.18
|
||||
- jazz-react@0.13.18
|
||||
|
||||
## 0.0.80
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "passphrase",
|
||||
"private": true,
|
||||
"version": "0.0.80",
|
||||
"version": "0.0.83",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,29 @@
|
||||
# jazz-password-manager
|
||||
|
||||
## 0.0.107
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [439f0fe]
|
||||
- jazz-tools@0.13.20
|
||||
- jazz-react@0.13.20
|
||||
|
||||
## 0.0.106
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [80530a4]
|
||||
- jazz-tools@0.13.19
|
||||
- jazz-react@0.13.19
|
||||
|
||||
## 0.0.105
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [761759c]
|
||||
- jazz-tools@0.13.18
|
||||
- jazz-react@0.13.18
|
||||
|
||||
## 0.0.104
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-password-manager",
|
||||
"private": true,
|
||||
"version": "0.0.104",
|
||||
"version": "0.0.107",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,29 @@
|
||||
# jazz-example-pets
|
||||
|
||||
## 0.0.205
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [439f0fe]
|
||||
- jazz-tools@0.13.20
|
||||
- jazz-react@0.13.20
|
||||
|
||||
## 0.0.204
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [80530a4]
|
||||
- jazz-tools@0.13.19
|
||||
- jazz-react@0.13.19
|
||||
|
||||
## 0.0.203
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [761759c]
|
||||
- jazz-tools@0.13.18
|
||||
- jazz-react@0.13.18
|
||||
|
||||
## 0.0.202
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-example-pets",
|
||||
"private": true,
|
||||
"version": "0.0.202",
|
||||
"version": "0.0.205",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,29 @@
|
||||
# reactions
|
||||
|
||||
## 0.0.85
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [439f0fe]
|
||||
- jazz-tools@0.13.20
|
||||
- jazz-react@0.13.20
|
||||
|
||||
## 0.0.84
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [80530a4]
|
||||
- jazz-tools@0.13.19
|
||||
- jazz-react@0.13.19
|
||||
|
||||
## 0.0.83
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [761759c]
|
||||
- jazz-tools@0.13.18
|
||||
- jazz-react@0.13.18
|
||||
|
||||
## 0.0.82
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "reactions",
|
||||
"private": true,
|
||||
"version": "0.0.82",
|
||||
"version": "0.0.85",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,33 @@
|
||||
# richtext
|
||||
|
||||
## 0.0.75
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [439f0fe]
|
||||
- jazz-tools@0.13.20
|
||||
- jazz-react@0.13.20
|
||||
- jazz-richtext-prosemirror@0.1.9
|
||||
|
||||
## 0.0.74
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [80530a4]
|
||||
- jazz-tools@0.13.19
|
||||
- jazz-react@0.13.19
|
||||
- jazz-richtext-prosemirror@0.1.8
|
||||
|
||||
## 0.0.73
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [761759c]
|
||||
- Updated dependencies [133b8ab]
|
||||
- jazz-tools@0.13.18
|
||||
- jazz-richtext-prosemirror@0.1.7
|
||||
- jazz-react@0.13.18
|
||||
|
||||
## 0.0.72
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "richtext",
|
||||
"private": true,
|
||||
"version": "0.0.72",
|
||||
"version": "0.0.75",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,32 @@
|
||||
# todo-vue
|
||||
|
||||
## 0.0.89
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [439f0fe]
|
||||
- jazz-tools@0.13.20
|
||||
- jazz-browser@0.13.20
|
||||
- jazz-vue@0.13.20
|
||||
|
||||
## 0.0.88
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [80530a4]
|
||||
- jazz-tools@0.13.19
|
||||
- jazz-browser@0.13.19
|
||||
- jazz-vue@0.13.19
|
||||
|
||||
## 0.0.87
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [761759c]
|
||||
- jazz-tools@0.13.18
|
||||
- jazz-browser@0.13.18
|
||||
- jazz-vue@0.13.18
|
||||
|
||||
## 0.0.86
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "todo-vue",
|
||||
"version": "0.0.86",
|
||||
"version": "0.0.89",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,29 @@
|
||||
# jazz-example-todo
|
||||
|
||||
## 0.0.204
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [439f0fe]
|
||||
- jazz-tools@0.13.20
|
||||
- jazz-react@0.13.20
|
||||
|
||||
## 0.0.203
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [80530a4]
|
||||
- jazz-tools@0.13.19
|
||||
- jazz-react@0.13.19
|
||||
|
||||
## 0.0.202
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [761759c]
|
||||
- jazz-tools@0.13.18
|
||||
- jazz-react@0.13.18
|
||||
|
||||
## 0.0.201
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-example-todo",
|
||||
"private": true,
|
||||
"version": "0.0.201",
|
||||
"version": "0.0.204",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -11,6 +11,7 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@faker-js/faker": "^9.7.0",
|
||||
"@radix-ui/react-checkbox": "^1.0.4",
|
||||
"@radix-ui/react-slot": "^1.1.1",
|
||||
"@radix-ui/react-toast": "^1.1.4",
|
||||
|
||||
@@ -41,12 +41,9 @@ export class TodoAccount extends Account {
|
||||
*/
|
||||
migrate() {
|
||||
if (!this._refs.root) {
|
||||
this.root = TodoAccountRoot.create(
|
||||
{
|
||||
projects: ListOfProjects.create([], { owner: this }),
|
||||
},
|
||||
{ owner: this },
|
||||
);
|
||||
this.root = TodoAccountRoot.create({
|
||||
projects: ListOfProjects.create([]),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
ThemeProvider,
|
||||
TitleAndLogo,
|
||||
} from "./basicComponents/index.ts";
|
||||
import { TaskGenerator } from "./components/TaskGenerator.tsx";
|
||||
import { wordlist } from "./wordlist.ts";
|
||||
|
||||
/**
|
||||
@@ -95,6 +96,10 @@ export default function App() {
|
||||
path: "/invite/*",
|
||||
element: <p>Accepting invite...</p>,
|
||||
},
|
||||
{
|
||||
path: "/generate",
|
||||
element: <TaskGenerator />,
|
||||
},
|
||||
]);
|
||||
|
||||
// `useAcceptInvite()` is a hook that accepts an invite link from the URL hash,
|
||||
|
||||
@@ -35,7 +35,11 @@ export function ProjectTodoTable() {
|
||||
// content - whether we create edits locally, load persisted data, or receive
|
||||
// sync updates from other devices or participants!
|
||||
// It also recursively resolves and subsribes to all referenced CoValues.
|
||||
const project = useCoState(TodoProject, projectId);
|
||||
const project = useCoState(TodoProject, projectId, {
|
||||
resolve: {
|
||||
tasks: true,
|
||||
},
|
||||
});
|
||||
|
||||
// `createTask` is similar to `createProject` we saw earlier, creating a new CoMap
|
||||
// for a new task (in the same group as the project), and then
|
||||
|
||||
61
examples/todo/src/components/TaskGenerator.tsx
Normal file
61
examples/todo/src/components/TaskGenerator.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import { TodoAccount } from "@/1_schema";
|
||||
import { FormEvent, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { generateRandomProject } from "../generate";
|
||||
|
||||
export function TaskGenerator() {
|
||||
const [isGenerating, setIsGenerating] = useState(false);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(e.currentTarget);
|
||||
const numTasks = Math.max(
|
||||
1,
|
||||
parseInt(formData.get("numTasks") as string) || 1,
|
||||
);
|
||||
|
||||
setIsGenerating(true);
|
||||
const project = generateRandomProject(numTasks);
|
||||
|
||||
const { root } = await TodoAccount.getMe().ensureLoaded({
|
||||
resolve: {
|
||||
root: {
|
||||
projects: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
root.projects.push(project);
|
||||
|
||||
navigate(`/project/${project.id}`);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="p-4 border rounded-lg shadow-sm bg-white">
|
||||
<h2 className="text-lg font-semibold mb-4">Generate Random Tasks</h2>
|
||||
<form onSubmit={handleSubmit} className="flex flex-col gap-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<label htmlFor="numTasks" className="text-sm font-medium">
|
||||
Number of tasks:
|
||||
</label>
|
||||
<input
|
||||
id="numTasks"
|
||||
name="numTasks"
|
||||
type="number"
|
||||
min="1"
|
||||
defaultValue={5}
|
||||
className="w-20 px-2 py-1 border rounded"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isGenerating}
|
||||
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 disabled:bg-blue-300"
|
||||
>
|
||||
{isGenerating ? "Generating..." : "Generate Tasks"}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
25
examples/todo/src/generate.ts
Normal file
25
examples/todo/src/generate.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { faker } from "@faker-js/faker";
|
||||
import { ListOfTasks, Task, TodoProject } from "./1_schema";
|
||||
|
||||
export function generateRandomProject(numTasks: number): TodoProject {
|
||||
// Generate a random project title
|
||||
const projectTitle = faker.company.catchPhrase();
|
||||
|
||||
// Create a list of tasks
|
||||
const tasks = ListOfTasks.create([]);
|
||||
|
||||
// Generate random tasks
|
||||
for (let i = 0; i < numTasks; i++) {
|
||||
const task = Task.create({
|
||||
done: faker.datatype.boolean(),
|
||||
text: faker.lorem.sentence({ min: 3, max: 8 }),
|
||||
});
|
||||
tasks.push(task);
|
||||
}
|
||||
|
||||
// Create and return the project
|
||||
return TodoProject.create({
|
||||
title: projectTitle,
|
||||
tasks: tasks,
|
||||
});
|
||||
}
|
||||
@@ -1,5 +1,32 @@
|
||||
# version-history
|
||||
|
||||
## 0.0.83
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [439f0fe]
|
||||
- jazz-tools@0.13.20
|
||||
- jazz-inspector@0.13.20
|
||||
- jazz-react@0.13.20
|
||||
|
||||
## 0.0.82
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [80530a4]
|
||||
- jazz-tools@0.13.19
|
||||
- jazz-inspector@0.13.19
|
||||
- jazz-react@0.13.19
|
||||
|
||||
## 0.0.81
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [761759c]
|
||||
- jazz-tools@0.13.18
|
||||
- jazz-inspector@0.13.18
|
||||
- jazz-react@0.13.18
|
||||
|
||||
## 0.0.80
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "version-history",
|
||||
"private": true,
|
||||
"version": "0.0.80",
|
||||
"version": "0.0.83",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -22,7 +22,7 @@ export function JazzLogo({
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M136.179 44.8277C136.179 44.8277 136.179 44.8277 136.179 44.8276V21.168C117.931 28.5527 97.9854 32.6192 77.0897 32.6192C65.1466 32.6192 53.5138 31.2908 42.331 28.7737V51.4076C42.331 51.4076 42.331 51.4076 42.331 51.4076V81.1508C41.2955 80.4385 40.1568 79.8458 38.9405 79.3915C36.1732 78.358 33.128 78.0876 30.1902 78.6145C27.2524 79.1414 24.5539 80.4419 22.4358 82.3516C20.3178 84.2613 18.8754 86.6944 18.291 89.3433C17.7066 91.9921 18.0066 94.7377 19.1528 97.2329C20.2991 99.728 22.2403 101.861 24.7308 103.361C27.2214 104.862 30.1495 105.662 33.1448 105.662H33.1455C33.6061 105.662 33.8365 105.662 34.0314 105.659C44.5583 105.449 53.042 96.9656 53.2513 86.4386C53.2534 86.3306 53.2544 86.2116 53.2548 86.0486H53.2552V85.7149L53.2552 85.5521V82.0762L53.2552 53.1993C61.0533 54.2324 69.0092 54.7656 77.0897 54.7656C77.6696 54.7656 78.2489 54.7629 78.8276 54.7574V110.696C77.792 109.983 76.6533 109.391 75.437 108.936C72.6697 107.903 69.6246 107.632 66.6867 108.159C63.7489 108.686 61.0504 109.987 58.9323 111.896C56.8143 113.806 55.3719 116.239 54.7875 118.888C54.2032 121.537 54.5031 124.283 55.6494 126.778C56.7956 129.273 58.7368 131.405 61.2273 132.906C63.7179 134.406 66.646 135.207 69.6414 135.207C70.1024 135.207 70.3329 135.207 70.5279 135.203C81.0548 134.994 89.5385 126.51 89.7478 115.983C89.7517 115.788 89.7517 115.558 89.7517 115.097V111.621L89.7517 54.3266C101.962 53.4768 113.837 51.4075 125.255 48.2397V80.9017C124.219 80.1894 123.081 79.5966 121.864 79.1424C119.097 78.1089 116.052 77.8384 113.114 78.3653C110.176 78.8922 107.478 80.1927 105.36 82.1025C103.242 84.0122 101.799 86.4453 101.215 89.0941C100.631 91.743 100.931 94.4886 102.077 96.9837C103.223 99.4789 105.164 101.612 107.655 103.112C110.145 104.612 113.073 105.413 116.069 105.413C116.53 105.413 116.76 105.413 116.955 105.409C127.482 105.2 135.966 96.7164 136.175 86.1895C136.179 85.9945 136.179 85.764 136.179 85.3029V81.8271L136.179 44.8277Z"
|
||||
className="fill-primary"
|
||||
fill="#146AFF"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import { ImageResponse } from "next/og";
|
||||
import { JazzLogo } from "../atoms/logos/JazzLogo";
|
||||
|
||||
@@ -10,28 +8,112 @@ export const imageSize = {
|
||||
|
||||
export const imageContentType = "image/png";
|
||||
|
||||
export default async function OpenGraphImage({ title }: { title: string }) {
|
||||
const manropeSemiBold = await readFile(
|
||||
join(process.cwd(), "public/fonts/Manrope-SemiBold.ttf"),
|
||||
async function loadManropeGoogleFont() {
|
||||
const url = `https://fonts.googleapis.com/css2?family=Manrope:wght@600`;
|
||||
const css = await (await fetch(url)).text();
|
||||
const resource = css.match(
|
||||
/src: url\((.+)\) format\('(opentype|truetype)'\)/,
|
||||
);
|
||||
|
||||
if (resource) {
|
||||
const response = await fetch(resource[1]);
|
||||
if (response.status == 200) {
|
||||
return await response.arrayBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error("failed to load font data");
|
||||
}
|
||||
|
||||
export async function OpenGraphImage({
|
||||
title,
|
||||
framework,
|
||||
contents,
|
||||
topic,
|
||||
subtopic,
|
||||
}: {
|
||||
title: string;
|
||||
framework?: string;
|
||||
contents?: string[];
|
||||
topic?: string;
|
||||
subtopic?: string;
|
||||
}) {
|
||||
if (!title) {
|
||||
throw new Error(
|
||||
`No title from tocItems in opengraph-image.tsx ${framework} ${topic} ${subtopic}`,
|
||||
);
|
||||
}
|
||||
|
||||
return new ImageResponse(
|
||||
<div
|
||||
style={{
|
||||
fontSize: "7em",
|
||||
background: "white",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-start",
|
||||
flexDirection: "column",
|
||||
alignItems: "flex-start",
|
||||
justifyContent: "center",
|
||||
padding: "77px",
|
||||
letterSpacing: "-0.05em",
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
<div
|
||||
style={{ display: "flex", position: "absolute", bottom: 35, right: 45 }}
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
gap: "1rem",
|
||||
fontSize: "4rem",
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
right: 15,
|
||||
top: 10,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-start",
|
||||
fontSize: "3rem",
|
||||
color: "#888888",
|
||||
letterSpacing: "-0.03em",
|
||||
}}
|
||||
>
|
||||
<div style={{ display: "flex", color: "#BBB", paddingRight: "0.5rem" }}>
|
||||
{framework}
|
||||
</div>
|
||||
{topic && (
|
||||
<span style={{ color: "#CCC", paddingRight: "0.5rem" }}>
|
||||
/ {topic}
|
||||
</span>
|
||||
)}
|
||||
{subtopic && <span style={{ color: "#DDD" }}>/ {subtopic}</span>}
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
marginTop: "1rem",
|
||||
gap: "0.2rem",
|
||||
fontSize: "2rem",
|
||||
color: "#888888",
|
||||
letterSpacing: "-0.03em",
|
||||
}}
|
||||
>
|
||||
{contents?.map((content) => (
|
||||
<div key={content}>{content}</div>
|
||||
))}
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
position: "absolute",
|
||||
bottom: 35,
|
||||
right: 45,
|
||||
}}
|
||||
>
|
||||
<JazzLogo width={193} height={73} />
|
||||
</div>
|
||||
@@ -41,7 +123,9 @@ export default async function OpenGraphImage({ title }: { title: string }) {
|
||||
fonts: [
|
||||
{
|
||||
name: "Manrope",
|
||||
data: manropeSemiBold,
|
||||
data: await loadManropeGoogleFont(),
|
||||
style: "normal",
|
||||
weight: 600,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import { OpenGraphImage, imageSize, imageContentType } from '@garden-co/design-system/src/components/organisms/OpenGraphImage';
|
||||
import { getMdxWithToc } from '@/lib/docMdxContent';
|
||||
export const title = "Quickstart";
|
||||
export const size = imageSize;
|
||||
export const contentType = imageContentType;
|
||||
export const alt = "Quickstart";
|
||||
|
||||
export default async function Image({ params }: { params: Promise<{ framework: string, topic: string, subtopic: string }> }) {
|
||||
const { framework, topic, subtopic } = await params;
|
||||
const { tocItems } = await getMdxWithToc(framework, [topic, subtopic]);
|
||||
|
||||
const title = tocItems[0]?.value;
|
||||
|
||||
if (!title) {
|
||||
throw new Error(`No title from tocItems in opengraph-image.tsx ${framework} ${topic} ${subtopic}`);
|
||||
}
|
||||
|
||||
return OpenGraphImage({
|
||||
title: title,
|
||||
framework,
|
||||
contents: tocItems[0]?.children?.map((child) => child.value) ?? [],
|
||||
topic,
|
||||
subtopic,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { OpenGraphImage, imageSize, imageContentType } from '@garden-co/design-system/src/components/organisms/OpenGraphImage';
|
||||
import { getMdxWithToc } from '@/lib/docMdxContent';
|
||||
export const title = "Quickstart";
|
||||
export const size = imageSize;
|
||||
export const contentType = imageContentType;
|
||||
export const alt = "Quickstart";
|
||||
|
||||
export default async function Image({ params }: { params: Promise<{ framework: string, topic: string }> }) {
|
||||
const { framework, topic } = await params;
|
||||
const { tocItems } = await getMdxWithToc(framework, [topic]);
|
||||
|
||||
// console.log('tocItems', tocItems);
|
||||
|
||||
const title = tocItems[0]?.value;
|
||||
|
||||
if (!title) {
|
||||
throw new Error(`No title from tocItems in opengraph-image.tsx ${framework} ${topic}`);
|
||||
}
|
||||
|
||||
return OpenGraphImage({
|
||||
title: title,
|
||||
framework,
|
||||
contents: tocItems[0]?.children?.map((child) => child.value) ?? [],
|
||||
topic,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import { getMdxWithToc } from "@/lib/docMdxContent";
|
||||
import {
|
||||
OpenGraphImage,
|
||||
imageSize,
|
||||
imageContentType,
|
||||
} from "@garden-co/design-system/src/components/organisms/OpenGraphImage";
|
||||
|
||||
export const title = "Quickstart";
|
||||
export const size = imageSize;
|
||||
export const contentType = imageContentType;
|
||||
export const alt = "Jazz Docs | Quickstart";
|
||||
|
||||
export default async function Image({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ framework: string }>;
|
||||
}) {
|
||||
const { framework } = await params;
|
||||
const { tocItems } = await getMdxWithToc(framework, []);
|
||||
|
||||
const title = tocItems[0]?.value;
|
||||
|
||||
if (!title) {
|
||||
throw new Error(`No title from tocItems in opengraph-image.tsx ${framework}`);
|
||||
}
|
||||
|
||||
return OpenGraphImage({
|
||||
title: title,
|
||||
framework,
|
||||
contents: tocItems[0]?.children?.map((child) => child.value) ?? [],
|
||||
});
|
||||
}
|
||||
@@ -516,7 +516,7 @@ const reactExamples: Example[] = [
|
||||
tech: [tech.react],
|
||||
features: [features.serverWorker, features.inbox],
|
||||
illustration: <JazzPaperScissorsIllustration />,
|
||||
demoUrl: "https://jazz-paper-scissors.vercel.app"
|
||||
demoUrl: "https://jazz-paper-scissors.vercel.app",
|
||||
},
|
||||
{
|
||||
name: "Clerk",
|
||||
@@ -552,7 +552,7 @@ const reactExamples: Example[] = [
|
||||
tech: [tech.react],
|
||||
features: [features.inviteLink],
|
||||
illustration: <OrganizationIllustration />,
|
||||
demoUrl: "https://jazz-organization.vercel.app"
|
||||
demoUrl: "https://jazz-organization.vercel.app",
|
||||
},
|
||||
{
|
||||
name: "Version history",
|
||||
@@ -561,7 +561,7 @@ const reactExamples: Example[] = [
|
||||
"Track and restore previous versions of your data, and see who made the changes.",
|
||||
tech: [tech.react],
|
||||
illustration: <VersionHistoryIllustration />,
|
||||
demoUrl: "https://jazz-version-history.vercel.app"
|
||||
demoUrl: "https://jazz-version-history.vercel.app",
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@ pre.shiki .line {
|
||||
.twoslash-popup-code pre.shiki .line {
|
||||
display: inline;
|
||||
padding-left: 0;
|
||||
white-space: break-spaces;
|
||||
}
|
||||
|
||||
html.dark .shiki {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { marketingCopy } from "@/content/marketingCopy";
|
||||
import OpenGraphImage, {
|
||||
import {
|
||||
OpenGraphImage,
|
||||
imageSize,
|
||||
imageContentType,
|
||||
} from "@garden-co/design-system/src/components/organisms/OpenGraphImage";
|
||||
|
||||
@@ -19,8 +19,7 @@ export default function DocsLayout({
|
||||
navIcon?: IconName;
|
||||
tocItems?: TocEntry[];
|
||||
}) {
|
||||
const tableOfContentsItems =
|
||||
tocItems?.length && tocItems[0].children ? tocItems[0].children : [];
|
||||
const tableOfContentsItems = tocItems ?? [];
|
||||
|
||||
const navSections: NavSection[] = [
|
||||
{
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
import type { Toc, TocEntry } from "@stefanprobst/rehype-extract-toc";
|
||||
import { clsx } from "clsx";
|
||||
import Link from "next/link";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
|
||||
const TocList = ({
|
||||
items,
|
||||
level,
|
||||
currentId,
|
||||
}: { items: Toc; level: number; currentId: string }) => {
|
||||
|
||||
const isActive = (item: TocEntry) => {
|
||||
if (!item.id) return false;
|
||||
if (item.id === currentId) return true;
|
||||
@@ -20,7 +21,7 @@ const TocList = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<ul className="space-y-2" style={{ paddingLeft: level ? "1rem" : "0" }}>
|
||||
<ul className="space-y-2" style={{ paddingLeft: (level > 0) ? "1rem" : "0" }}>
|
||||
{items.map((item) => (
|
||||
<li key={item.id} className="space-y-2">
|
||||
{item.id && (
|
||||
@@ -57,8 +58,10 @@ export function TableOfContents({
|
||||
}) {
|
||||
const [currentId, setCurrentId] = useState<string>("");
|
||||
|
||||
const itemsUnderH1 = useMemo(() => items[0]?.children || [], [items]);
|
||||
|
||||
const getHeadings = useCallback(() => {
|
||||
return items
|
||||
return itemsUnderH1
|
||||
.flatMap((node) => {
|
||||
const headings = [node];
|
||||
if (node.children) {
|
||||
@@ -78,17 +81,17 @@ export function TableOfContents({
|
||||
return { id: item.id, top };
|
||||
})
|
||||
.filter((x): x is { id: string; top: number } => x !== null);
|
||||
}, [items]);
|
||||
}, [itemsUnderH1]);
|
||||
|
||||
useEffect(() => {
|
||||
if (items.length === 0) return;
|
||||
if (itemsUnderH1.length === 0) return;
|
||||
|
||||
const onScroll = () => {
|
||||
const headings = getHeadings();
|
||||
if (headings.length === 0) return;
|
||||
|
||||
const top = window.scrollY;
|
||||
let current = headings[0].id;
|
||||
let current = headings[0]?.id;
|
||||
|
||||
for (const heading of headings) {
|
||||
if (top >= heading.top - 500) {
|
||||
@@ -98,7 +101,7 @@ export function TableOfContents({
|
||||
}
|
||||
}
|
||||
|
||||
setCurrentId(current);
|
||||
current && setCurrentId(current);
|
||||
};
|
||||
|
||||
window.addEventListener("scroll", onScroll, { passive: true });
|
||||
@@ -107,14 +110,14 @@ export function TableOfContents({
|
||||
return () => {
|
||||
window.removeEventListener("scroll", onScroll);
|
||||
};
|
||||
}, [getHeadings, items]);
|
||||
}, [getHeadings, itemsUnderH1]);
|
||||
|
||||
if (!items.length) return null;
|
||||
if (!itemsUnderH1.length) return null;
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<p className="font-medium text-highlight mb-3">On this page</p>
|
||||
<TocList items={items} level={0} currentId={currentId} />
|
||||
<TocList items={itemsUnderH1} level={0} currentId={currentId} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
```tsx
|
||||
<JazzProvider
|
||||
sync={{ peer: "wss://cloud.jazz.tools/?key=you@example.com" }}
|
||||
sync={{
|
||||
peer: "wss://cloud.jazz.tools/?key=you@example.com"
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</JazzProvider>
|
||||
|
||||
@@ -21,7 +21,7 @@ function Code({
|
||||
<div
|
||||
className={clsx(
|
||||
className,
|
||||
"w-full h-full relative -right-2 -bottom-1 max-w-full lg:max-w-[480px] overflow-x-auto ml-auto overflow-hidden",
|
||||
"w-full h-full relative -right-2 -bottom-1 max-w-full lg:max-w-[500px] ml-auto",
|
||||
"shadow-xl shadow-blue/20",
|
||||
"rounded-tl-lg border",
|
||||
"flex-1 bg-white ring ring-4 ring-stone-400/20",
|
||||
@@ -33,7 +33,9 @@ function Code({
|
||||
{fileName}
|
||||
</span>
|
||||
</div>
|
||||
<pre className="text-xs lg:text-sm p-1 pb-2">{children}</pre>
|
||||
<pre className="text-xs lg:text-sm p-1 pb-2 overflow-x-auto">
|
||||
{children}
|
||||
</pre>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CodeGroup } from '@/components/forMdx'
|
||||
import { CodeGroup, ContentByFramework } from '@/components/forMdx'
|
||||
import { JazzIcon } from "@garden-co/design-system/src/components/atoms/logos/JazzIcon";
|
||||
|
||||
# Jazz Inspector
|
||||
@@ -9,11 +9,14 @@ For now, you can get your account credentials from the `jazz-logged-in-secret` l
|
||||
|
||||
[https://inspector.jazz.tools](https://inspector.jazz.tools)
|
||||
|
||||
## Exporting current account to Inspector from your app
|
||||
<ContentByFramework framework={["react", "svelte", "vue", "vanilla"]}>
|
||||
## Exporting current account to Inspector from your app [!framework=react,svelte,vue,vanilla]
|
||||
|
||||
In development mode, you can launch the Inspector from your Jazz app to inspect your account by pressing `Cmd+J`.
|
||||
</ContentByFramework>
|
||||
|
||||
## Embedding the Inspector widget into your app
|
||||
<ContentByFramework framework="react">
|
||||
## Embedding the Inspector widget into your app [!framework=react]
|
||||
|
||||
Alternatively, you can embed the Inspector directly into your app, so you don't need to open a separate window.
|
||||
|
||||
@@ -40,7 +43,7 @@ import { JazzInspector } from "jazz-inspector";
|
||||
|
||||
This will show the Inspector launch button on the right of your page.
|
||||
|
||||
### Positioning the Inspector button
|
||||
### Positioning the Inspector button [!framework=react]
|
||||
|
||||
You can also customize the button position with the following options:
|
||||
|
||||
@@ -67,3 +70,4 @@ For example:
|
||||
</div>
|
||||
|
||||
Check out the [music player app](https://github.com/garden-co/jazz/blob/main/examples/music-player/src/2_main.tsx) for a full example.
|
||||
</ContentByFramework>
|
||||
|
||||
@@ -4,8 +4,6 @@ import { CodeGroup } from "@/components/forMdx";
|
||||
|
||||
# Providers
|
||||
|
||||
## Introduction
|
||||
|
||||
`<JazzProvider />` is the core component that connects your Expo application to Jazz. It handles:
|
||||
|
||||
- **Data Synchronization**: Manages connections to peers and the Jazz cloud
|
||||
|
||||
@@ -4,8 +4,6 @@ import { CodeGroup } from "@/components/forMdx";
|
||||
|
||||
# Providers
|
||||
|
||||
## Introduction
|
||||
|
||||
`<JazzProvider />` is the core component that connects your React Native application to Jazz. It handles:
|
||||
|
||||
- **Data Synchronization**: Manages connections to peers and the Jazz cloud
|
||||
|
||||
@@ -4,8 +4,6 @@ import { CodeGroup } from "@/components/forMdx";
|
||||
|
||||
# Providers
|
||||
|
||||
## Introduction
|
||||
|
||||
`<JazzProvider />` is the core component that connects your React application to Jazz. It handles:
|
||||
|
||||
- **Data Synchronization**: Manages connections to peers and the Jazz cloud
|
||||
|
||||
@@ -1,99 +1,380 @@
|
||||
export const metadata = { title: "React" };
|
||||
export const metadata = { title: "Installation" };
|
||||
|
||||
import { CodeGroup } from "@/components/forMdx";
|
||||
|
||||
# <span id="react">React</span>
|
||||
# Installation and Setup
|
||||
|
||||
Wrap your application with `<JazzProvider />`, this is where you specify the sync & storage server to connect to (see [Sync and storage](/docs/react/sync-and-storage)).
|
||||
Add Jazz to your React application in minutes. This setup covers standard React apps, Next.js, and gives an overview of experimental SSR approaches.
|
||||
|
||||
Integrating Jazz with React is straightforward. You'll define data schemas that describe your application's structure, then wrap your app with a provider that handles sync and storage. The whole process takes just three steps:
|
||||
|
||||
1. [Install dependencies](#install-dependencies)
|
||||
2. [Write your schema](#write-your-schema)
|
||||
3. [Wrap your app in `<JazzProvider />`](#standard-react-setup)
|
||||
|
||||
Looking for complete examples? Check out our [example applications](/examples) for chat apps, collaborative editors, and more.
|
||||
|
||||
## Install dependencies
|
||||
|
||||
First, install the required packages:
|
||||
|
||||
<CodeGroup>
|
||||
{/* prettier-ignore */}
|
||||
```tsx
|
||||
```bash
|
||||
pnpm install jazz-react jazz-tools
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
## Write your schema
|
||||
|
||||
Define your data schema using [CoValues](/docs/schemas/covalues) from `jazz-tools`.
|
||||
|
||||
<CodeGroup>
|
||||
```tsx twoslash
|
||||
// schema.ts
|
||||
import { Account, co } from "jazz-tools";
|
||||
|
||||
export class MyAppAccount extends Account {
|
||||
name = co.string;
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
See [CoValues](/docs/schemas/covalues) for more information on how to define your schema.
|
||||
|
||||
## Standard React Setup
|
||||
|
||||
Wrap your application with `<JazzProvider />` to connect to the Jazz network and define your data schema:
|
||||
|
||||
<CodeGroup>
|
||||
```tsx twoslash
|
||||
// @filename: schema.ts
|
||||
import { Account } from "jazz-tools";
|
||||
|
||||
export class MyAppAccount extends Account {}
|
||||
// @filename: app.tsx
|
||||
import * as React from "react";
|
||||
import { createRoot } from 'react-dom/client';
|
||||
|
||||
function App() {
|
||||
return <div>Hello, world!</div>;
|
||||
}
|
||||
// ---cut---
|
||||
// app.tsx
|
||||
import { JazzProvider } from "jazz-react";
|
||||
import { MyAppAccount } from "./schema";
|
||||
|
||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||
<JazzProvider // [!code ++:6]
|
||||
sync={{ peer: "wss://cloud.jazz.tools/?key=you@example.com" }}
|
||||
AccountSchema={MyAppAccount}
|
||||
>
|
||||
<App />
|
||||
</JazzProvider>
|
||||
createRoot(document.getElementById("root")!).render(
|
||||
<JazzProvider
|
||||
sync={{ peer: "wss://cloud.jazz.tools/?key=you@example.com" }}
|
||||
AccountSchema={MyAppAccount}
|
||||
>
|
||||
<App />
|
||||
</JazzProvider>
|
||||
);
|
||||
|
||||
// [!code ++:6]
|
||||
// Register the Account schema so `useAccount` returns our custom `MyAppAccount`
|
||||
// Register your Account schema to enhance TypeScript support
|
||||
declare module "jazz-react" {
|
||||
interface Register {
|
||||
Account: MyAppAccount;
|
||||
}
|
||||
interface Register {
|
||||
Account: MyAppAccount;
|
||||
}
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
This setup handles:
|
||||
- Connection to the Jazz sync server
|
||||
- Schema registration for type-safe data handling
|
||||
- Local storage configuration
|
||||
|
||||
With this in place, you're ready to start using Jazz hooks in your components. [Learn how to access and update your data](/docs/using-covalues/subscription-and-loading#subscription-hooks).
|
||||
|
||||
## Next.js
|
||||
## Next.js Integration
|
||||
|
||||
### Client-side only
|
||||
### Client-side Only (Easiest)
|
||||
|
||||
The easiest way to use Jazz with Next.JS is to only use it on the client side. You can ensure this by:
|
||||
|
||||
- marking the Jazz provider file as `"use client"`
|
||||
The simplest approach for Next.js is client-side only integration:
|
||||
|
||||
<CodeGroup>
|
||||
{/* prettier-ignore */}
|
||||
```tsx
|
||||
"use client" // [!code ++]
|
||||
```tsx twoslash
|
||||
// @filename: schema.ts
|
||||
import { Account } from "jazz-tools";
|
||||
|
||||
export class MyAppAccount extends Account {}
|
||||
// @filename: app.tsx
|
||||
import * as React from "react";
|
||||
// ---cut---
|
||||
// app.tsx
|
||||
"use client" // Mark as client component
|
||||
|
||||
import { JazzProvider } from "jazz-react";
|
||||
import { MyAppAccount } from "./schema";
|
||||
|
||||
export function MyJazzProvider(props: { children: React.ReactNode }) {
|
||||
return (
|
||||
<JazzProvider
|
||||
sync={{ peer: "wss://cloud.jazz.tools/?key=you@example.com" }}
|
||||
AccountSchema={MyAppAccount}
|
||||
>
|
||||
{props.children}
|
||||
</JazzProvider>
|
||||
);
|
||||
export function JazzWrapper({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<JazzProvider
|
||||
sync={{ peer: "wss://cloud.jazz.tools/?key=you@example.com" }}
|
||||
AccountSchema={MyAppAccount}
|
||||
>
|
||||
{children}
|
||||
</JazzProvider>
|
||||
);
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
- marking any file with components where you use Jazz hooks (such as `useAccount` or `useCoState`) as `"use client"`
|
||||
Remember to mark any component that uses Jazz hooks with `"use client"`:
|
||||
|
||||
### SSR use (experimental)
|
||||
<CodeGroup>
|
||||
```tsx twoslash
|
||||
// @filename: schema.ts
|
||||
import { Account, co } from "jazz-tools";
|
||||
|
||||
Pure SSR use of Jazz is basically just using jazz-nodejs (see [Node.JS / Server Workers](/docs/react/project-setup/server-side)) inside Server Components.
|
||||
export class MyAppAccount extends Account {
|
||||
name = co.string;
|
||||
}
|
||||
// @filename: Profile.tsx
|
||||
import * as React from "react";
|
||||
import { MyAppAccount } from "./schema";
|
||||
|
||||
Instead of using hooks as you would on the client, you await promises returned by `CoValue.load(...)` inside your Server Components.
|
||||
declare module "jazz-react" {
|
||||
interface Register {
|
||||
Account: MyAppAccount;
|
||||
}
|
||||
}
|
||||
// ---cut---
|
||||
// Profile.tsx
|
||||
"use client"; // [!code ++]
|
||||
|
||||
TODO: code example
|
||||
import { useAccount } from "jazz-react";
|
||||
|
||||
This should work well for cases like rendering publicly-readable information, since the worker account will be able to load them.
|
||||
export function Profile() {
|
||||
const { me } = useAccount();
|
||||
|
||||
return <div>Hello, {me?.name}</div>;
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
In the future, it will be possible to use trusted auth methods (such as Clerk, Auth0, etc.) that let you act as the same Jazz user both on the client and on the server, letting you use SSR even for data private to that user.
|
||||
### SSR Support (Experimental)
|
||||
|
||||
### SSR + client-side (experimental)
|
||||
For server-side rendering, Jazz offers experimental approaches:
|
||||
|
||||
You can combine the two approaches by creating
|
||||
- Pure SSR
|
||||
- Hybrid SSR + Client Hydration
|
||||
|
||||
1. A pure "rendering" component that renders an already-loaded CoValue (in JSON-ified form)
|
||||
#### Pure SSR
|
||||
|
||||
TODO: code example
|
||||
Use Jazz in server components by directly loading data with `CoValue.load()`.
|
||||
|
||||
2. A "hydrating" component (with `"use client"`) that
|
||||
{/*
|
||||
<CodeGroup>
|
||||
```tsx twoslash
|
||||
// @errors: 18047
|
||||
// @filename: schema.ts
|
||||
import { co, CoList, CoMap } from "jazz-tools";
|
||||
|
||||
- expects a pre-loaded CoValue as a prop (in JSON-ified form)
|
||||
- uses one of the client-side Jazz hooks (such as `useAccount` or `useCoState`) to subscribe to that same CoValue
|
||||
- passing the client-side subscribed state to the "rendering" component, with the pre-loaded CoValue as a fallback until the client receives the first subscribed state
|
||||
export class MyItem extends CoMap {
|
||||
title = co.string;
|
||||
}
|
||||
|
||||
TODO: code example
|
||||
export class MyCollection extends CoList.Of(co.ref(MyItem)) {}
|
||||
// @filename: PublicData.tsx
|
||||
import * as React from "react";
|
||||
import { ID } from "jazz-tools";
|
||||
const collectionID = "co_z123" as ID<MyCollection>;
|
||||
// ---cut---
|
||||
// Server Component (no "use client" directive)
|
||||
import { MyCollection, MyItem } from "./schema";
|
||||
|
||||
3. A "pre-loading" Server Component that
|
||||
export default async function PublicData() {
|
||||
// Load data directly in the server component
|
||||
const items = await MyCollection.load(collectionID);
|
||||
|
||||
if (!items) {
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<ul>
|
||||
{items.map(item => (
|
||||
item ? <li key={item.id}>{item.title}</li> : null
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
*/}
|
||||
|
||||
- pre-loads the CoValue by awaiting it's `load(...)` method (as described above)
|
||||
- renders the "hydrating" component, passing the pre-loaded CoValue as a prop
|
||||
This works well for public data accessible to the server account.
|
||||
|
||||
TODO: code example
|
||||
#### Hybrid SSR + Client Hydration
|
||||
|
||||
For more complex cases, you can pre-render on the server and hydrate on the client:
|
||||
|
||||
1. Create a shared rendering component.
|
||||
|
||||
{/*
|
||||
<CodeGroup>
|
||||
```tsx twoslash
|
||||
// @filename: schema.ts
|
||||
import { co, CoList, CoMap } from "jazz-tools";
|
||||
|
||||
export class MyItem extends CoMap {
|
||||
title = co.string;
|
||||
}
|
||||
// @filename: ItemList.tsx
|
||||
import * as React from "react";
|
||||
import { MyItem } from "./schema";
|
||||
// ---cut---
|
||||
// ItemList.tsx - works in both server and client contexts
|
||||
export function ItemList({ items }: { items: MyItem[] }) {
|
||||
return (
|
||||
<ul>
|
||||
{items.map(item => (
|
||||
<li key={item.id}>{item.title}</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
*/}
|
||||
|
||||
2. Create a client hydration component.
|
||||
|
||||
{/*
|
||||
<CodeGroup>
|
||||
```tsx twoslash
|
||||
// @filename: schema.ts
|
||||
import { co, CoList, CoMap } from "jazz-tools";
|
||||
|
||||
export class MyItem extends CoMap {
|
||||
title = co.string;
|
||||
}
|
||||
export class MyCollection extends CoList.Of(co.ref(MyItem)) {}
|
||||
// @filename: ItemList.tsx
|
||||
import * as React from "react";
|
||||
import { MyItem } from "./schema";
|
||||
|
||||
export function ItemList({ items }: { items: MyItem[] }) {
|
||||
return (
|
||||
<ul>
|
||||
{items.map(item => (
|
||||
<li key={item.id}>{item.title}</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
// @filename: ItemListHydrator.tsx
|
||||
// ItemListHydrator.tsx
|
||||
import * as React from "react";
|
||||
import { useCoState } from "jazz-react";
|
||||
import { ID } from "jazz-tools";
|
||||
const myCollectionID = "co_z123" as ID<MyCollection>;
|
||||
// ---cut---
|
||||
"use client"
|
||||
|
||||
import { MyCollection, MyItem } from "./schema";
|
||||
import { ItemList } from "./ItemList";
|
||||
export function ItemListHydrator({ initialItems }: { initialItems: MyItem[] }) {
|
||||
// Hydrate with real-time data once client loads
|
||||
const myCollection = useCoState(MyCollection, myCollectionID);
|
||||
|
||||
// Filter out nulls for type safety
|
||||
const items = Array.from(myCollection?.values() || []).filter(
|
||||
(item): item is MyItem => !!item
|
||||
);
|
||||
|
||||
// Use server data until client data is available
|
||||
const displayItems = items || initialItems;
|
||||
|
||||
return <ItemList items={displayItems} />;
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
*/}
|
||||
|
||||
3. Create a server component that pre-loads data.
|
||||
|
||||
{/*
|
||||
<CodeGroup>
|
||||
```tsx twoslash
|
||||
// @filename: schema.ts
|
||||
import { co, CoList, CoMap } from "jazz-tools";
|
||||
|
||||
export class MyItem extends CoMap {
|
||||
title = co.string;
|
||||
}
|
||||
|
||||
export class MyCollection extends CoList.Of(co.ref(MyItem)) {}
|
||||
// @filename: ItemList.tsx
|
||||
import * as React from "react";
|
||||
import { MyItem } from "./schema";
|
||||
|
||||
export function ItemList({ items }: { items: MyItem[] }) {
|
||||
return (
|
||||
<ul>
|
||||
{items.map(item => (
|
||||
<li key={item.id}>{item.title}</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
// @filename: ItemListHydrator.tsx
|
||||
// ItemListHydrator.tsx
|
||||
import * as React from "react";
|
||||
import { useCoState } from "jazz-react";
|
||||
import { ID } from "jazz-tools";
|
||||
const myCollectionID = "co_z123" as ID<MyCollection>;
|
||||
// ---cut---
|
||||
"use client"
|
||||
|
||||
import { MyCollection, MyItem } from "./schema";
|
||||
import { ItemList } from "./ItemList";
|
||||
export function ItemListHydrator({ initialItems }: { initialItems: MyItem[] }) {
|
||||
// Hydrate with real-time data once client loads
|
||||
const myCollection = useCoState(MyCollection, myCollectionID);
|
||||
|
||||
// Filter out nulls for type safety
|
||||
const items = Array.from(myCollection?.values() || []).filter(
|
||||
(item): item is MyItem => !!item
|
||||
);
|
||||
|
||||
// Use server data until client data is available
|
||||
const displayItems = items || initialItems;
|
||||
|
||||
return <ItemList items={displayItems} />;
|
||||
}
|
||||
// @filename: ServerItemPage.tsx
|
||||
import * as React from 'react';
|
||||
import { ID } from "jazz-tools";
|
||||
import { MyCollection, MyItem } from "./schema";
|
||||
import { ItemListHydrator } from "./ItemListHydrator";
|
||||
const myCollectionID = "co_z123" as ID<MyCollection>;
|
||||
// ---cut---
|
||||
// ServerItemPage.tsx
|
||||
export default async function ServerItemPage() {
|
||||
// Pre-load data on the server
|
||||
const initialItems = await MyCollection.load(myCollectionID);
|
||||
|
||||
// Filter out nulls for type safety
|
||||
const items = Array.from(initialItems?.values() || []).filter(
|
||||
(item): item is MyItem => !!item
|
||||
);
|
||||
|
||||
// Pass to client hydrator
|
||||
return <ItemListHydrator initialItems={items} />;
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
*/}
|
||||
|
||||
This approach gives you the best of both worlds: fast initial loading with server rendering, plus real-time updates on the client.
|
||||
|
||||
## Further Reading
|
||||
|
||||
- [Schemas](/docs/schemas/covalues) - Learn about defining your data model
|
||||
- [Provider Configuration](/docs/project-setup/providers) - Learn about other configuration options for Providers
|
||||
- [Authentication](/docs/authentication/overview) - Set up user authentication
|
||||
- [Sync and Storage](/docs/sync-and-storage) - Learn about data persistence
|
||||
|
||||
@@ -12,7 +12,7 @@ As their name suggests, CoValues are inherently collaborative, meaning **multipl
|
||||
|
||||
- CoValues keep their full edit histories, from which they derive their "current state".
|
||||
- The fact that this happens in an eventually-consistent way makes them [CRDTs](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type).
|
||||
- Having the full history also means that you often don't need explicit timestamps and author info - you get this for free as part of a CoValue's [edit metadata](/docs/using-covalues/metadata).
|
||||
- Having the full history also means that you often don't need explicit timestamps and author info - you get this for free as part of a CoValue's [edit metadata](/docs/using-covalues/history).
|
||||
|
||||
CoValues model JSON with CoMaps and CoLists, but also offer CoFeeds for simple per-user value feeds, and let you represent binary data with FileStreams.
|
||||
|
||||
@@ -31,11 +31,14 @@ Even before you know the details of how your app will work, you'll probably know
|
||||
Jazz makes it quick to declare schemas, since they are simple TypeScript classes:
|
||||
|
||||
<CodeGroup>
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
```ts twoslash
|
||||
import { co, CoMap, CoList } from "jazz-tools";
|
||||
class ListOfTasks extends CoList.Of(co.string) {}
|
||||
// ---cut---
|
||||
// schema.ts
|
||||
export class TodoProject extends CoMap {
|
||||
title = co.string;
|
||||
tasks = co.ref(ListOfTasks);
|
||||
title = co.string;
|
||||
tasks = co.ref(ListOfTasks);
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
@@ -45,16 +48,28 @@ Here you can see how we extend a CoValue type and use `co` for declaring (collab
|
||||
Classes might look old-fashioned, but Jazz makes use of them being both types and values in TypeScript, letting you refer to either with a single definition and import.
|
||||
|
||||
<CodeGroup>
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
```ts twoslash
|
||||
// @filename: schema.ts
|
||||
import { co, CoMap, CoList } from "jazz-tools";
|
||||
|
||||
export class ListOfTasks extends CoList.Of(co.string) {}
|
||||
|
||||
export class TodoProject extends CoMap {
|
||||
title = co.string;
|
||||
tasks = co.ref(ListOfTasks);
|
||||
}
|
||||
// @filename: app.ts
|
||||
import { Group } from "jazz-tools";
|
||||
// ---cut---
|
||||
// app.ts
|
||||
import { TodoProject, ListOfTasks } from "./schema";
|
||||
|
||||
const project: TodoProject = TodoProject.create(
|
||||
{
|
||||
title: "New Project",
|
||||
tasks: ListOfTasks.create([], Group.create()),
|
||||
},
|
||||
Group.create()
|
||||
{
|
||||
title: "New Project",
|
||||
tasks: ListOfTasks.create([], Group.create()),
|
||||
},
|
||||
Group.create()
|
||||
);
|
||||
```
|
||||
</CodeGroup>
|
||||
@@ -63,17 +78,18 @@ const project: TodoProject = TodoProject.create(
|
||||
|
||||
### `CoMap` (declaration)
|
||||
|
||||
CoMaps are the most commonly used type of CoValue. They are the equivalent of JSON objects. (Collaborative editing follows a last-write-wins strategy per-key.)
|
||||
CoMaps are the most commonly used type of CoValue. They are the equivalent of JSON objects (Collaborative editing follows a last-write-wins strategy per-key).
|
||||
|
||||
You can either declare struct-like CoMaps:
|
||||
|
||||
<CodeGroup>
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
class Person extends CoMap {
|
||||
name = co.string;
|
||||
age = co.number;
|
||||
pet = co.optional.ref(Pet);
|
||||
```ts twoslash
|
||||
// schema.ts
|
||||
import { co, CoMap } from "jazz-tools";
|
||||
// ---cut---
|
||||
export class Task extends CoMap {
|
||||
title = co.string;
|
||||
completed = co.boolean;
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
@@ -81,8 +97,13 @@ class Person extends CoMap {
|
||||
Or record-like CoMaps (key-value pairs, where keys are always `string`):
|
||||
|
||||
<CodeGroup>
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
```ts twoslash
|
||||
import { co, CoMap } from "jazz-tools";
|
||||
class Fruit extends CoMap {
|
||||
name = co.string;
|
||||
color = co.string;
|
||||
}
|
||||
// ---cut---
|
||||
class ColorToHex extends CoMap.Record(co.string) {}
|
||||
|
||||
class ColorToFruit extends CoMap.Record(co.ref(Fruit)) {}
|
||||
@@ -90,10 +111,10 @@ class ColorToFruit extends CoMap.Record(co.ref(Fruit)) {}
|
||||
</CodeGroup>
|
||||
|
||||
|
||||
See the corresponding sections for [creating](/docs/using-covalues/creation#comap-creation),
|
||||
See the corresponding sections for [creating](/docs/using-covalues/comaps#creating-comaps),
|
||||
[subscribing/loading](/docs/using-covalues/subscription-and-loading),
|
||||
[reading from](/docs/using-covalues/reading#comap-reading) and
|
||||
[writing to](/docs/using-covalues/writing#comap-writing) CoMaps.
|
||||
[reading from](/docs/using-covalues/comaps#reading-from-comaps) and
|
||||
[updating](/docs/using-covalues/comaps#updating-comaps) CoMaps.
|
||||
|
||||
### `CoList` (declaration)
|
||||
|
||||
@@ -102,38 +123,48 @@ CoLists are ordered lists and are the equivalent of JSON arrays. (They support c
|
||||
You define them by specifying the type of the items they contain:
|
||||
|
||||
<CodeGroup>
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
```ts twoslash
|
||||
import { co, CoList, CoMap } from "jazz-tools";
|
||||
class Task extends CoMap {
|
||||
title = co.string;
|
||||
completed = co.boolean;
|
||||
}
|
||||
// ---cut---
|
||||
class ListOfColors extends CoList.Of(co.string) {}
|
||||
|
||||
class ListOfTasks extends CoList.Of(co.ref(Task)) {}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
See the corresponding sections for [creating](/docs/using-covalues/creation#colist-creation),
|
||||
See the corresponding sections for [creating](/docs/using-covalues/colists#creating-colists),
|
||||
[subscribing/loading](/docs/using-covalues/subscription-and-loading),
|
||||
[reading from](/docs/using-covalues/reading#colist-reading) and
|
||||
[writing to](/docs/using-covalues/writing#colist-writing) CoLists.
|
||||
[reading from](/docs/using-covalues/colists#reading-from-colists) and
|
||||
[updating](/docs/using-covalues/colists#updating-colists) CoLists.
|
||||
|
||||
### `CoFeed` (declaration)
|
||||
|
||||
CoFeeds are a special CoValue type that represent a feed of values for a set of users / sessions. (Each session of a user gets its own append-only feed.)
|
||||
CoFeeds are a special CoValue type that represent a feed of values for a set of users/sessions (Each session of a user gets its own append-only feed).
|
||||
|
||||
They allow easy access of the latest or all items belonging to a user or their sessions. This makes them particularly useful for user presence, reactions, notifications, etc.
|
||||
|
||||
You define them by specifying the type of feed item:
|
||||
|
||||
<CodeGroup>
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
```ts twoslash
|
||||
import { co, CoFeed, CoMap } from "jazz-tools";
|
||||
class Task extends CoMap {
|
||||
title = co.string;
|
||||
completed = co.boolean;
|
||||
}
|
||||
// ---cut---
|
||||
class FeedOfTasks extends CoFeed.Of(co.ref(Task)) {}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
See the corresponding sections for [creating](/docs/using-covalues/creation#cofeed-creation),
|
||||
See the corresponding sections for [creating](/docs/using-covalues/cofeeds#creating-cofeeds),
|
||||
[subscribing/loading](/docs/using-covalues/subscription-and-loading),
|
||||
[reading from](/docs/using-covalues/reading#cofeed-reading) and
|
||||
[writing to](/docs/using-covalues/writing#cofeed-writing) CoFeeds.
|
||||
[reading from](/docs/using-covalues/cofeeds#reading-from-cofeeds) and
|
||||
[writing to](/docs/using-covalues/cofeeds#writing-to-cofeeds) CoFeeds.
|
||||
|
||||
### `FileStream` (declaration)
|
||||
|
||||
@@ -144,21 +175,22 @@ They allow you to upload and reference files, images, etc.
|
||||
You typically don't need to declare or extend them yourself, you simply refer to the built-in `FileStream` from another CoValue:
|
||||
|
||||
<CodeGroup>
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
```ts twoslash
|
||||
import { co, CoMap } from "jazz-tools";
|
||||
// ---cut---
|
||||
import { FileStream } from "jazz-tools";
|
||||
|
||||
class UserProfile extends CoMap {
|
||||
name = co.string;
|
||||
avatar = co.ref(FileStream);
|
||||
class Document extends CoMap {
|
||||
title = co.string;
|
||||
file = co.ref(FileStream);
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
See the corresponding sections for [creating](/docs/using-covalues/creation#filestream-creation),
|
||||
See the corresponding sections for [creating](/docs/using-covalues/filestreams#creating-filestreams),
|
||||
[subscribing/loading](/docs/using-covalues/subscription-and-loading),
|
||||
[reading from](/docs/using-covalues/reading#filestream-reading) and
|
||||
[writing to](/docs/using-covalues/writing#filestream-writing) FileStreams.
|
||||
[reading from](/docs/using-covalues/filestreams#reading-from-filestreams) and
|
||||
[writing to](/docs/using-covalues/filestreams#writing-to-filestreams) FileStreams.
|
||||
|
||||
### `SchemaUnion` (declaration)
|
||||
|
||||
@@ -167,8 +199,9 @@ SchemaUnion is a helper type that allows you to load and refer to multiple subcl
|
||||
You declare them with a base class type and discriminating lambda, in which you have access to the `RawCoMap`, on which you can call `get` with the field name to get the discriminating value.
|
||||
|
||||
<CodeGroup>
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
```ts twoslash
|
||||
import { co } from "jazz-tools";
|
||||
// ---cut---
|
||||
import { SchemaUnion, CoMap } from "jazz-tools";
|
||||
|
||||
class BaseWidget extends CoMap {
|
||||
@@ -196,9 +229,9 @@ const WidgetUnion = SchemaUnion.Of<BaseWidget>((raw) => {
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
See the corresponding sections for [creating](/docs/using-covalues/creation#schemaunion-creation),
|
||||
See the corresponding sections for [creating](/docs/using-covalues/schemaunions#creating-schemaunions),
|
||||
[subscribing/loading](/docs/using-covalues/subscription-and-loading) and
|
||||
[narrowing](/docs/using-covalues/reading#schemaunion-narrowing) SchemaUnions.
|
||||
[narrowing](/docs/using-covalues/schemaunions#narrowing) SchemaUnions.
|
||||
|
||||
## CoValue field/item types
|
||||
|
||||
@@ -209,12 +242,11 @@ Now that we've seen the different types of CoValues, let's see more precisely ho
|
||||
You can declare primitive field types using the `co` declarer:
|
||||
|
||||
<CodeGroup>
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
import { co } from "jazz-tools";
|
||||
```ts twoslash
|
||||
import { co, CoMap, CoList } from "jazz-tools";
|
||||
|
||||
export class Person extends CoMap {
|
||||
title = co.string;
|
||||
title = co.string;
|
||||
}
|
||||
|
||||
export class ListOfColors extends CoList.Of(co.string) {}
|
||||
@@ -224,23 +256,25 @@ export class ListOfColors extends CoList.Of(co.string) {}
|
||||
Here's a quick overview of the primitive types you can use:
|
||||
|
||||
<CodeGroup>
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
co.string;
|
||||
co.number;
|
||||
co.boolean;
|
||||
co.null;
|
||||
co.Date;
|
||||
co.literal("waiting", "ready");
|
||||
```ts twoslash
|
||||
import { co } from "jazz-tools";
|
||||
// ---cut---
|
||||
co.string; // For simple strings
|
||||
co.number; // For numbers
|
||||
co.boolean; // For booleans
|
||||
co.null; // For null
|
||||
co.Date; // For dates
|
||||
co.literal("waiting", "ready"); // For enums
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
Finally, for more complex JSON data, that you *don't want to be collaborative internally* (but only ever update as a whole), you can use `co.json<T>()`:
|
||||
|
||||
<CodeGroup>
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
co.json<{ name: string }>();
|
||||
```ts twoslash
|
||||
import { co } from "jazz-tools";
|
||||
// ---cut---
|
||||
co.json<{ name: string }>(); // For JSON objects
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
@@ -257,13 +291,18 @@ The important caveat here is that **a referenced CoValue might or might not be l
|
||||
In Schemas, you declare Refs using the `co.ref<T>()` declarer:
|
||||
|
||||
<CodeGroup>
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
class Company extends CoMap {
|
||||
members = co.ref(ListOfPeople);
|
||||
```ts twoslash
|
||||
import { co, CoMap, CoList } from "jazz-tools";
|
||||
class Person extends CoMap {
|
||||
name = co.string;
|
||||
}
|
||||
|
||||
// ---cut---
|
||||
// schema.ts
|
||||
class ListOfPeople extends CoList.Of(co.ref(Person)) {}
|
||||
|
||||
class Company extends CoMap {
|
||||
members = co.ref(ListOfPeople);
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
@@ -272,10 +311,14 @@ class ListOfPeople extends CoList.Of(co.ref(Person)) {}
|
||||
⚠️ If you want to make a referenced CoValue field optional, you *have to* use `co.optional.ref<T>()`: ⚠️
|
||||
|
||||
<CodeGroup>
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
```ts twoslash
|
||||
import { co, CoMap } from "jazz-tools";
|
||||
class Pet extends CoMap {
|
||||
name = co.string;
|
||||
}
|
||||
// ---cut---
|
||||
class Person extends CoMap {
|
||||
pet = co.optional.ref(Pet);
|
||||
pet = co.optional.ref(Pet);
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
@@ -285,20 +328,25 @@ class Person extends CoMap {
|
||||
Since CoValue schemas are based on classes, you can easily add computed fields and methods:
|
||||
|
||||
<CodeGroup>
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
```ts twoslash
|
||||
import { co, CoMap } from "jazz-tools";
|
||||
function differenceInYears(date1: Date, date2: Date) {
|
||||
const diffTime = Math.abs(date1.getTime() - date2.getTime());
|
||||
return Math.ceil(diffTime / (1000 * 60 * 60 * 24 * 365.25));
|
||||
}
|
||||
// ---cut---
|
||||
class Person extends CoMap {
|
||||
firstName = co.string;
|
||||
lastName = co.string;
|
||||
dateOfBirth = co.Date;
|
||||
firstName = co.string;
|
||||
lastName = co.string;
|
||||
dateOfBirth = co.Date;
|
||||
|
||||
get name() {
|
||||
return `${this.firstName} ${this.lastName}`;
|
||||
}
|
||||
get name() {
|
||||
return `${this.firstName} ${this.lastName}`;
|
||||
}
|
||||
|
||||
ageAsOf(date: Date) {
|
||||
return differenceInYears(date, this.dateOfBirth);
|
||||
}
|
||||
ageAsOf(date: Date) {
|
||||
return differenceInYears(date, this.dateOfBirth);
|
||||
}
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
@@ -39,7 +39,6 @@ For files from input elements or drag-and-drop interfaces, use `createFromBlob`:
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
// @errors: 18047
|
||||
import { FileStream, Group } from "jazz-tools";
|
||||
const myGroup = Group.create();
|
||||
const progressBar: HTMLElement = document.querySelector('.progress-bar')!;
|
||||
|
||||
@@ -42,7 +42,7 @@ export async function getDocMetadata(framework: string, slug?: string[]) {
|
||||
|
||||
function DocProse({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<Prose className="overflow-x-hidden lg:overflow-x-visible lg:flex-1 pb-8 pt-[calc(61px+2rem)] md:pt-8 md:max-w-3xl mx-auto">
|
||||
<Prose className="overflow-hidden pb-8 pt-[calc(61px+2rem)] md:pt-8 md:max-w-3xl mx-auto">
|
||||
{children}
|
||||
</Prose>
|
||||
);
|
||||
@@ -55,23 +55,8 @@ export async function DocPage({
|
||||
framework: string;
|
||||
slug?: string[];
|
||||
}) {
|
||||
const slugPath = slug?.join("/");
|
||||
|
||||
try {
|
||||
const mdxSource = await getMdxSource(framework, slugPath);
|
||||
|
||||
const {
|
||||
default: Content,
|
||||
tableOfContents = [],
|
||||
headingsFrameworkVisibility = {},
|
||||
} = mdxSource;
|
||||
|
||||
// Remove items that should not be shown for the current framework
|
||||
const tocItems = (tableOfContents as Toc).filter(({ id }) =>
|
||||
id && id in headingsFrameworkVisibility
|
||||
? headingsFrameworkVisibility[id]?.includes(framework)
|
||||
: true,
|
||||
);
|
||||
const { Content, tocItems } = await getMdxWithToc(framework, slug);
|
||||
|
||||
return (
|
||||
<DocsLayout nav={<DocNav />} tocItems={tocItems}>
|
||||
@@ -93,3 +78,50 @@ export async function DocPage({
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export async function getMdxWithToc(framework: string, slug?: string[]) {
|
||||
const slugPath = slug?.join("/");
|
||||
const mdxSource = await getMdxSource(framework, slugPath);
|
||||
|
||||
const {
|
||||
default: Content,
|
||||
tableOfContents,
|
||||
headingsFrameworkVisibility,
|
||||
} = mdxSource;
|
||||
|
||||
// Remove items that should not be shown for the current framework
|
||||
const tocItems = filterTocItemsForFramework(
|
||||
tableOfContents as Toc,
|
||||
framework,
|
||||
headingsFrameworkVisibility
|
||||
);
|
||||
|
||||
return {
|
||||
Content,
|
||||
tocItems,
|
||||
};
|
||||
}
|
||||
function filterTocItemsForFramework(
|
||||
tocItems: Toc,
|
||||
framework: string,
|
||||
headingsFrameworkVisibility: Record<string, string[]>
|
||||
): Toc {
|
||||
return tocItems
|
||||
.map(item => {
|
||||
const isVisible =
|
||||
!item.id || !(item.id in headingsFrameworkVisibility) ||
|
||||
headingsFrameworkVisibility[item.id]?.includes(framework);
|
||||
|
||||
if (!isVisible) return null;
|
||||
|
||||
const filteredChildren = item.children
|
||||
? filterTocItemsForFramework(item.children, framework, headingsFrameworkVisibility)
|
||||
: [];
|
||||
|
||||
return {
|
||||
...item,
|
||||
children: filteredChildren,
|
||||
};
|
||||
})
|
||||
.filter(Boolean) as Toc;
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -1,5 +1,28 @@
|
||||
# cojson-storage-indexeddb
|
||||
|
||||
## 0.13.20
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [adfc9a6]
|
||||
- Updated dependencies [1389207]
|
||||
- Updated dependencies [d6e143e]
|
||||
- Updated dependencies [3e6229d]
|
||||
- cojson-storage@0.13.20
|
||||
- cojson@0.13.20
|
||||
|
||||
## 0.13.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [9089252]
|
||||
- Updated dependencies [b470f63]
|
||||
- Updated dependencies [8b2df0e]
|
||||
- Updated dependencies [66373ba]
|
||||
- Updated dependencies [f24cad1]
|
||||
- cojson@0.13.18
|
||||
- cojson-storage@0.13.18
|
||||
|
||||
## 0.13.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cojson-storage-indexeddb",
|
||||
"version": "0.13.17",
|
||||
"version": "0.13.20",
|
||||
"main": "dist/index.js",
|
||||
"type": "module",
|
||||
"types": "dist/index.d.ts",
|
||||
|
||||
@@ -44,10 +44,7 @@ export class IDBNode {
|
||||
}
|
||||
|
||||
static async asPeer(
|
||||
{
|
||||
trace,
|
||||
localNodeName = "local",
|
||||
}: { trace?: boolean; localNodeName?: string } | undefined = {
|
||||
{ localNodeName = "local" }: { localNodeName?: string } | undefined = {
|
||||
localNodeName: "local",
|
||||
},
|
||||
): Promise<Peer> {
|
||||
@@ -57,7 +54,6 @@ export class IDBNode {
|
||||
{
|
||||
peer1role: "client",
|
||||
peer2role: "storage",
|
||||
trace,
|
||||
crashOnClose: true,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ControlledAgent, LocalNode } from "cojson";
|
||||
import { LocalNode } from "cojson";
|
||||
import { WasmCrypto } from "cojson/crypto/WasmCrypto";
|
||||
import { expect, test } from "vitest";
|
||||
import { IDBStorage } from "../index.js";
|
||||
@@ -9,7 +9,7 @@ test("Should be able to initialize and load from empty DB", async () => {
|
||||
const agentSecret = Crypto.newRandomAgentSecret();
|
||||
|
||||
const node = new LocalNode(
|
||||
new ControlledAgent(agentSecret, Crypto),
|
||||
agentSecret,
|
||||
Crypto.newRandomSessionID(Crypto.getAgentID(agentSecret)),
|
||||
Crypto,
|
||||
);
|
||||
@@ -25,7 +25,7 @@ test("Should be able to sync data to database and then load that from a new node
|
||||
const agentSecret = Crypto.newRandomAgentSecret();
|
||||
|
||||
const node1 = new LocalNode(
|
||||
new ControlledAgent(agentSecret, Crypto),
|
||||
agentSecret,
|
||||
Crypto.newRandomSessionID(Crypto.getAgentID(agentSecret)),
|
||||
Crypto,
|
||||
);
|
||||
@@ -43,7 +43,7 @@ test("Should be able to sync data to database and then load that from a new node
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
|
||||
const node2 = new LocalNode(
|
||||
new ControlledAgent(agentSecret, Crypto),
|
||||
agentSecret,
|
||||
Crypto.newRandomSessionID(Crypto.getAgentID(agentSecret)),
|
||||
Crypto,
|
||||
);
|
||||
|
||||
@@ -1,5 +1,28 @@
|
||||
# cojson-storage-sqlite
|
||||
|
||||
## 0.13.20
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [adfc9a6]
|
||||
- Updated dependencies [1389207]
|
||||
- Updated dependencies [d6e143e]
|
||||
- Updated dependencies [3e6229d]
|
||||
- cojson-storage@0.13.20
|
||||
- cojson@0.13.20
|
||||
|
||||
## 0.13.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [9089252]
|
||||
- Updated dependencies [b470f63]
|
||||
- Updated dependencies [8b2df0e]
|
||||
- Updated dependencies [66373ba]
|
||||
- Updated dependencies [f24cad1]
|
||||
- cojson@0.13.18
|
||||
- cojson-storage@0.13.18
|
||||
|
||||
## 0.13.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"name": "cojson-storage-sqlite",
|
||||
"type": "module",
|
||||
"version": "0.13.17",
|
||||
"version": "0.13.20",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"better-sqlite3": "^11.7.0",
|
||||
"cojson": "workspace:0.13.17",
|
||||
"cojson": "workspace:0.13.20",
|
||||
"cojson-storage": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -56,17 +56,15 @@ export class SQLiteNode {
|
||||
|
||||
static async asPeer({
|
||||
filename,
|
||||
trace,
|
||||
localNodeName = "local",
|
||||
}: {
|
||||
filename: string;
|
||||
trace?: boolean;
|
||||
localNodeName?: string;
|
||||
}): Promise<Peer> {
|
||||
const [localNodeAsPeer, storageAsPeer] = cojsonInternals.connectedPeers(
|
||||
localNodeName,
|
||||
"storage",
|
||||
{ peer1role: "client", peer2role: "storage", trace, crashOnClose: true },
|
||||
{ peer1role: "client", peer2role: "storage", crashOnClose: true },
|
||||
);
|
||||
|
||||
await SQLiteNode.open(
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { assert } from "node:console";
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { unlinkSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { ControlledAgent, LocalNode } from "cojson";
|
||||
import { LocalNode } from "cojson";
|
||||
import { SyncManager } from "cojson-storage";
|
||||
import { WasmCrypto } from "cojson/crypto/WasmCrypto";
|
||||
import { expect, onTestFinished, test, vi } from "vitest";
|
||||
@@ -34,7 +33,7 @@ test("Should be able to initialize and load from empty DB", async () => {
|
||||
const agentSecret = Crypto.newRandomAgentSecret();
|
||||
|
||||
const node = new LocalNode(
|
||||
new ControlledAgent(agentSecret, Crypto),
|
||||
agentSecret,
|
||||
Crypto.newRandomSessionID(Crypto.getAgentID(agentSecret)),
|
||||
Crypto,
|
||||
);
|
||||
@@ -50,7 +49,7 @@ test("should sync and load data from storage", async () => {
|
||||
const agentSecret = Crypto.newRandomAgentSecret();
|
||||
|
||||
const node1 = new LocalNode(
|
||||
new ControlledAgent(agentSecret, Crypto),
|
||||
agentSecret,
|
||||
Crypto.newRandomSessionID(Crypto.getAgentID(agentSecret)),
|
||||
Crypto,
|
||||
);
|
||||
@@ -80,14 +79,16 @@ test("should sync and load data from storage", async () => {
|
||||
).toMatchInlineSnapshot(`
|
||||
[
|
||||
"client -> CONTENT Group header: true new: After: 0 New: 3",
|
||||
"storage -> KNOWN Group sessions: header/3",
|
||||
"client -> CONTENT Map header: true new: After: 0 New: 1",
|
||||
"storage -> KNOWN Map sessions: header/1",
|
||||
]
|
||||
`);
|
||||
|
||||
node1Sync.restore();
|
||||
|
||||
const node2 = new LocalNode(
|
||||
new ControlledAgent(agentSecret, Crypto),
|
||||
agentSecret,
|
||||
Crypto.newRandomSessionID(Crypto.getAgentID(agentSecret)),
|
||||
Crypto,
|
||||
);
|
||||
@@ -131,7 +132,7 @@ test("should load dependencies correctly (group inheritance)", async () => {
|
||||
const agentSecret = Crypto.newRandomAgentSecret();
|
||||
|
||||
const node1 = new LocalNode(
|
||||
new ControlledAgent(agentSecret, Crypto),
|
||||
agentSecret,
|
||||
Crypto.newRandomSessionID(Crypto.getAgentID(agentSecret)),
|
||||
Crypto,
|
||||
);
|
||||
@@ -165,15 +166,18 @@ test("should load dependencies correctly (group inheritance)", async () => {
|
||||
).toMatchInlineSnapshot(`
|
||||
[
|
||||
"client -> CONTENT ParentGroup header: true new: After: 0 New: 4",
|
||||
"storage -> KNOWN ParentGroup sessions: header/4",
|
||||
"client -> CONTENT Group header: true new: After: 0 New: 5",
|
||||
"storage -> KNOWN Group sessions: header/5",
|
||||
"client -> CONTENT Map header: true new: After: 0 New: 1",
|
||||
"storage -> KNOWN Map sessions: header/1",
|
||||
]
|
||||
`);
|
||||
|
||||
node1Sync.restore();
|
||||
|
||||
const node2 = new LocalNode(
|
||||
new ControlledAgent(agentSecret, Crypto),
|
||||
agentSecret,
|
||||
Crypto.newRandomSessionID(Crypto.getAgentID(agentSecret)),
|
||||
Crypto,
|
||||
);
|
||||
@@ -214,11 +218,91 @@ test("should load dependencies correctly (group inheritance)", async () => {
|
||||
`);
|
||||
});
|
||||
|
||||
test("should not send the same dependency value twice", async () => {
|
||||
const agentSecret = Crypto.newRandomAgentSecret();
|
||||
|
||||
const node1 = new LocalNode(
|
||||
agentSecret,
|
||||
Crypto.newRandomSessionID(Crypto.getAgentID(agentSecret)),
|
||||
Crypto,
|
||||
);
|
||||
|
||||
const node1Sync = trackMessages(node1);
|
||||
|
||||
const { peer, dbPath } = await createSQLiteStorage();
|
||||
|
||||
node1.syncManager.addPeer(peer);
|
||||
|
||||
const group = node1.createGroup();
|
||||
const parentGroup = node1.createGroup();
|
||||
|
||||
group.extend(parentGroup);
|
||||
|
||||
const mapFromParent = parentGroup.createMap();
|
||||
const map = group.createMap();
|
||||
|
||||
map.set("hello", "world");
|
||||
mapFromParent.set("hello", "world");
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
|
||||
node1Sync.restore();
|
||||
|
||||
const node2 = new LocalNode(
|
||||
agentSecret,
|
||||
Crypto.newRandomSessionID(Crypto.getAgentID(agentSecret)),
|
||||
Crypto,
|
||||
);
|
||||
|
||||
const node2Sync = trackMessages(node2);
|
||||
|
||||
const { peer: peer2 } = await createSQLiteStorage(dbPath);
|
||||
|
||||
node2.syncManager.addPeer(peer2);
|
||||
|
||||
await node2.load(map.id);
|
||||
await node2.load(mapFromParent.id);
|
||||
|
||||
expect(node2.expectCoValueLoaded(map.id)).toBeTruthy();
|
||||
expect(node2.expectCoValueLoaded(mapFromParent.id)).toBeTruthy();
|
||||
expect(node2.expectCoValueLoaded(group.id)).toBeTruthy();
|
||||
expect(node2.expectCoValueLoaded(parentGroup.id)).toBeTruthy();
|
||||
|
||||
expect(
|
||||
toSimplifiedMessages(
|
||||
{
|
||||
Map: map.core,
|
||||
Group: group.core,
|
||||
ParentGroup: parentGroup.core,
|
||||
MapFromParent: mapFromParent.core,
|
||||
},
|
||||
node2Sync.messages,
|
||||
),
|
||||
).toMatchInlineSnapshot(`
|
||||
[
|
||||
"client -> LOAD Map sessions: empty",
|
||||
"storage -> KNOWN ParentGroup sessions: header/4",
|
||||
"storage -> CONTENT ParentGroup header: true new: After: 0 New: 4",
|
||||
"storage -> KNOWN Group sessions: header/5",
|
||||
"storage -> CONTENT Group header: true new: After: 0 New: 5",
|
||||
"client -> KNOWN ParentGroup sessions: header/4",
|
||||
"storage -> KNOWN Map sessions: header/1",
|
||||
"storage -> CONTENT Map header: true new: After: 0 New: 1",
|
||||
"client -> KNOWN Group sessions: header/5",
|
||||
"client -> KNOWN Map sessions: header/1",
|
||||
"client -> LOAD MapFromParent sessions: empty",
|
||||
"storage -> KNOWN MapFromParent sessions: header/1",
|
||||
"storage -> CONTENT MapFromParent header: true new: After: 0 New: 1",
|
||||
"client -> KNOWN MapFromParent sessions: header/1",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test("should recover from data loss", async () => {
|
||||
const agentSecret = Crypto.newRandomAgentSecret();
|
||||
|
||||
const node1 = new LocalNode(
|
||||
new ControlledAgent(agentSecret, Crypto),
|
||||
agentSecret,
|
||||
Crypto.newRandomSessionID(Crypto.getAgentID(agentSecret)),
|
||||
Crypto,
|
||||
);
|
||||
@@ -263,17 +347,20 @@ test("should recover from data loss", async () => {
|
||||
).toMatchInlineSnapshot(`
|
||||
[
|
||||
"client -> CONTENT Group header: true new: After: 0 New: 3",
|
||||
"storage -> KNOWN Group sessions: header/3",
|
||||
"client -> CONTENT Map header: true new: After: 0 New: 1",
|
||||
"storage -> KNOWN Map sessions: header/1",
|
||||
"client -> CONTENT Map header: false new: After: 3 New: 1",
|
||||
"storage -> KNOWN CORRECTION Map sessions: header/1",
|
||||
"client -> CONTENT Map header: false new: After: 1 New: 3",
|
||||
"storage -> KNOWN Map sessions: header/4",
|
||||
]
|
||||
`);
|
||||
|
||||
node1Sync.restore();
|
||||
|
||||
const node2 = new LocalNode(
|
||||
new ControlledAgent(agentSecret, Crypto),
|
||||
agentSecret,
|
||||
Crypto.newRandomSessionID(Crypto.getAgentID(agentSecret)),
|
||||
Crypto,
|
||||
);
|
||||
|
||||
@@ -1,5 +1,27 @@
|
||||
# cojson-storage
|
||||
|
||||
## 0.13.20
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- adfc9a6: Make waitForSync work on storage peers by handling optimistic/known states
|
||||
- Updated dependencies [adfc9a6]
|
||||
- Updated dependencies [1389207]
|
||||
- Updated dependencies [d6e143e]
|
||||
- Updated dependencies [3e6229d]
|
||||
- cojson@0.13.20
|
||||
|
||||
## 0.13.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 8b2df0e: Optimized the dependency push from storage to send a given dependency only once
|
||||
- Updated dependencies [9089252]
|
||||
- Updated dependencies [b470f63]
|
||||
- Updated dependencies [66373ba]
|
||||
- Updated dependencies [f24cad1]
|
||||
- cojson@0.13.18
|
||||
|
||||
## 0.13.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cojson-storage",
|
||||
"version": "0.13.17",
|
||||
"version": "0.13.20",
|
||||
"main": "dist/index.js",
|
||||
"type": "module",
|
||||
"types": "dist/index.d.ts",
|
||||
|
||||
@@ -23,6 +23,8 @@ export class SyncManager {
|
||||
private readonly toLocalNode: OutgoingSyncQueue;
|
||||
private readonly dbClient: DBClientInterface;
|
||||
|
||||
private loadedCoValues = new Set<RawCoID>();
|
||||
|
||||
constructor(dbClient: DBClientInterface, toLocalNode: OutgoingSyncQueue) {
|
||||
this.toLocalNode = toLocalNode;
|
||||
this.dbClient = dbClient;
|
||||
@@ -149,6 +151,8 @@ export class SyncManager {
|
||||
}),
|
||||
);
|
||||
|
||||
this.loadedCoValues.add(coValueRow.id);
|
||||
|
||||
const dependedOnCoValuesList = getDependedOnCoValues({
|
||||
coValueRow,
|
||||
newContentMessages,
|
||||
@@ -167,8 +171,12 @@ export class SyncManager {
|
||||
};
|
||||
|
||||
await Promise.all(
|
||||
dependedOnCoValuesList.map((dependedOnCoValue) =>
|
||||
this.collectCoValueData(
|
||||
dependedOnCoValuesList.map((dependedOnCoValue) => {
|
||||
if (this.loadedCoValues.has(dependedOnCoValue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
return this.collectCoValueData(
|
||||
{
|
||||
id: dependedOnCoValue,
|
||||
header: false,
|
||||
@@ -176,8 +184,8 @@ export class SyncManager {
|
||||
},
|
||||
messageMap,
|
||||
asDependencyOf || coValueRow.id,
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
return messageMap;
|
||||
@@ -229,7 +237,13 @@ export class SyncManager {
|
||||
if ((sessionRow?.lastIdx || 0) < (msg.new[sessionID]?.after || 0)) {
|
||||
invalidAssumptions = true;
|
||||
} else {
|
||||
return this.putNewTxs(msg, sessionID, sessionRow, storedCoValueRowID);
|
||||
const newLastIdx = await this.putNewTxs(
|
||||
msg,
|
||||
sessionID,
|
||||
sessionRow,
|
||||
storedCoValueRowID,
|
||||
);
|
||||
ourKnown.sessions[sessionID] = newLastIdx;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -240,6 +254,11 @@ export class SyncManager {
|
||||
...ourKnown,
|
||||
isCorrection: invalidAssumptions,
|
||||
});
|
||||
} else {
|
||||
this.sendStateMessage({
|
||||
action: "known",
|
||||
...ourKnown,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,11 +321,13 @@ export class SyncManager {
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.all(
|
||||
await Promise.all(
|
||||
actuallyNewTransactions.map((newTransaction, i) =>
|
||||
this.dbClient.addTransaction(sessionRowID, nextIdx + i, newTransaction),
|
||||
),
|
||||
);
|
||||
|
||||
return newLastIdx;
|
||||
}
|
||||
|
||||
handleKnown(_msg: CojsonInternalTypes.KnownStateMessage) {
|
||||
|
||||
@@ -291,7 +291,7 @@ describe("DB sync manager", () => {
|
||||
});
|
||||
});
|
||||
|
||||
test("Saves new transaction without sending message when IDB has fewer transactions", async () => {
|
||||
test("Saves new transaction and sends an ack message as response", async () => {
|
||||
DBClient.prototype.getCoValue.mockResolvedValueOnce({
|
||||
id: coValueIdToLoad,
|
||||
header: coValueHeader,
|
||||
@@ -314,7 +314,12 @@ describe("DB sync manager", () => {
|
||||
incomingTxCount,
|
||||
);
|
||||
|
||||
expect(syncManager.sendStateMessage).not.toBeCalled();
|
||||
expect(syncManager.sendStateMessage).toBeCalledWith({
|
||||
action: "known",
|
||||
header: true,
|
||||
id: coValueIdToLoad,
|
||||
sessions: expect.any(Object),
|
||||
});
|
||||
});
|
||||
|
||||
test("Sends correction message when peer sends a message far ahead of our state due to invalid assumption", async () => {
|
||||
|
||||
@@ -1,5 +1,25 @@
|
||||
# cojson-transport-nodejs-ws
|
||||
|
||||
## 0.13.20
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [adfc9a6]
|
||||
- Updated dependencies [1389207]
|
||||
- Updated dependencies [d6e143e]
|
||||
- Updated dependencies [3e6229d]
|
||||
- cojson@0.13.20
|
||||
|
||||
## 0.13.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [9089252]
|
||||
- Updated dependencies [b470f63]
|
||||
- Updated dependencies [66373ba]
|
||||
- Updated dependencies [f24cad1]
|
||||
- cojson@0.13.18
|
||||
|
||||
## 0.13.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "cojson-transport-ws",
|
||||
"type": "module",
|
||||
"version": "0.13.17",
|
||||
"version": "0.13.20",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -26,7 +26,7 @@ describe("WebSocket Peer Integration", () => {
|
||||
// Create client node
|
||||
const clientAgent = crypto.newRandomAgentSecret();
|
||||
const clientNode = new LocalNode(
|
||||
new ControlledAgent(clientAgent, crypto),
|
||||
clientAgent,
|
||||
crypto.newRandomSessionID(crypto.getAgentID(clientAgent)),
|
||||
crypto,
|
||||
);
|
||||
@@ -66,7 +66,7 @@ describe("WebSocket Peer Integration", () => {
|
||||
test("should sync data between nodes through WebSocket connection", async () => {
|
||||
const clientAgent = crypto.newRandomAgentSecret();
|
||||
const clientNode = new LocalNode(
|
||||
new ControlledAgent(clientAgent, crypto),
|
||||
clientAgent,
|
||||
crypto.newRandomSessionID(crypto.getAgentID(clientAgent)),
|
||||
crypto,
|
||||
);
|
||||
@@ -99,7 +99,7 @@ describe("WebSocket Peer Integration", () => {
|
||||
test("should handle disconnection and cleanup", async () => {
|
||||
const clientAgent = crypto.newRandomAgentSecret();
|
||||
const clientNode = new LocalNode(
|
||||
new ControlledAgent(clientAgent, crypto),
|
||||
clientAgent,
|
||||
crypto.newRandomSessionID(crypto.getAgentID(clientAgent)),
|
||||
crypto,
|
||||
);
|
||||
@@ -134,7 +134,7 @@ describe("WebSocket Peer Integration", () => {
|
||||
test("should trigger a timeout if the server does not respond", async () => {
|
||||
const clientAgent = crypto.newRandomAgentSecret();
|
||||
const clientNode = new LocalNode(
|
||||
new ControlledAgent(clientAgent, crypto),
|
||||
clientAgent,
|
||||
crypto.newRandomSessionID(crypto.getAgentID(clientAgent)),
|
||||
crypto,
|
||||
);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user