Compare commits
44 Commits
cojson@0.1
...
jazz-react
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eda1588907 | ||
|
|
b14e0bfe24 | ||
|
|
87aa43b46b | ||
|
|
b93ce9fb7e | ||
|
|
a7d83e1c10 | ||
|
|
76a693da15 | ||
|
|
df2f021cfd | ||
|
|
50b15d2d1d | ||
|
|
48bf7cb188 | ||
|
|
e2e0af34b5 | ||
|
|
6a5bcd3063 | ||
|
|
bf7e62ec76 | ||
|
|
71dda6b10b | ||
|
|
4612e0545e | ||
|
|
07feedd641 | ||
|
|
edbd567f11 | ||
|
|
4d8bb9cdb8 | ||
|
|
1971448f5d | ||
|
|
0e861e7df8 | ||
|
|
6e3f1efcd0 | ||
|
|
eb87d10783 | ||
|
|
5a54e4aa50 | ||
|
|
16b0a22ded | ||
|
|
d469d68771 | ||
|
|
c068d7a369 | ||
|
|
25088ed5db | ||
|
|
221ca30790 | ||
|
|
caa6c147c8 | ||
|
|
4da51e8f9c | ||
|
|
a2076b179b | ||
|
|
3fa276c18d | ||
|
|
1928519d39 | ||
|
|
f4fa80b782 | ||
|
|
782df5d4b8 | ||
|
|
9db20ad630 | ||
|
|
3405d8f275 | ||
|
|
0a64dca0cd | ||
|
|
4d0b9b1bf1 | ||
|
|
403d61c8e8 | ||
|
|
0685c1cd5f | ||
|
|
834203f270 | ||
|
|
f01bc19257 | ||
|
|
6405a77aa2 | ||
|
|
ace6486075 |
@@ -36,7 +36,7 @@ We welcome all ideas! If you have suggestions, feel free to open an issue marked
|
||||
|
||||
### 5. Local Setup
|
||||
|
||||
You'll need Node.js 20.x or 22.x installed (we're working on support for 23.x), and pnpm 9.x installed. If you're using nix, run `nix develop` to get a shell with the correct versions of everything installed.
|
||||
You'll need Node.js 22.x installed (we're working on support for 23.x), and pnpm 9.x installed. If you're using nix, run `nix develop` to get a shell with the correct versions of everything installed.
|
||||
|
||||
1. **Clone the repository**:
|
||||
```bash
|
||||
@@ -54,6 +54,12 @@ You'll need Node.js 20.x or 22.x installed (we're working on support for 23.x),
|
||||
cd homepage && pnpm install
|
||||
```
|
||||
|
||||
4. **Go back to the project root**:
|
||||
|
||||
```bash
|
||||
cd ..
|
||||
```
|
||||
|
||||
4. **Build the packages**:
|
||||
|
||||
```bash
|
||||
|
||||
@@ -1,5 +1,47 @@
|
||||
# chat-rn-clerk
|
||||
|
||||
## 1.0.78
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [07feedd]
|
||||
- jazz-tools@0.10.13
|
||||
- jazz-react-native@0.10.13
|
||||
- jazz-react-native-auth-clerk@0.10.13
|
||||
- jazz-react-native-media-images@0.10.13
|
||||
|
||||
## 1.0.77
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [4612e05]
|
||||
- jazz-tools@0.10.12
|
||||
- jazz-react-native@0.10.12
|
||||
- jazz-react-native-auth-clerk@0.10.12
|
||||
- jazz-react-native-media-images@0.10.12
|
||||
|
||||
## 1.0.76
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5a54e4a]
|
||||
- jazz-react-native@0.10.11
|
||||
- jazz-react-native-auth-clerk@0.10.11
|
||||
|
||||
## 1.0.75
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3405d8f]
|
||||
- jazz-react-native@0.10.10
|
||||
- jazz-react-native-auth-clerk@0.10.10
|
||||
|
||||
## 1.0.74
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-native-auth-clerk@0.10.9
|
||||
|
||||
## 1.0.73
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "chat-rn-clerk",
|
||||
"main": "index.js",
|
||||
"version": "1.0.73",
|
||||
"version": "1.0.78",
|
||||
"scripts": {
|
||||
"build": "expo export -p ios",
|
||||
"start": "expo start",
|
||||
|
||||
@@ -1,5 +1,35 @@
|
||||
# chat-rn
|
||||
|
||||
## 1.0.74
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [07feedd]
|
||||
- jazz-tools@0.10.13
|
||||
- jazz-react-native@0.10.13
|
||||
|
||||
## 1.0.73
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [4612e05]
|
||||
- jazz-tools@0.10.12
|
||||
- jazz-react-native@0.10.12
|
||||
|
||||
## 1.0.72
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5a54e4a]
|
||||
- jazz-react-native@0.10.11
|
||||
|
||||
## 1.0.71
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3405d8f]
|
||||
- jazz-react-native@0.10.10
|
||||
|
||||
## 1.0.70
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "chat-rn",
|
||||
"version": "1.0.70",
|
||||
"version": "1.0.74",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "expo export -p ios",
|
||||
@@ -35,8 +35,6 @@
|
||||
"react": "^18.3.1",
|
||||
"react-native": "~0.76.3",
|
||||
"react-native-get-random-values": "^1.11.0",
|
||||
"react-native-nitro-modules": "0.21.0",
|
||||
"react-native-quick-crypto": "1.0.0-beta.12",
|
||||
"react-native-safe-area-context": "4.12.0",
|
||||
"react-native-screens": "4.1.0",
|
||||
"react-native-url-polyfill": "^2.0.0",
|
||||
|
||||
@@ -9,7 +9,7 @@ import * as Linking from "expo-linking";
|
||||
import React, { StrictMode, useEffect, useState } from "react";
|
||||
import HandleInviteScreen from "./invite";
|
||||
|
||||
import { JazzProvider, RNQuickCrypto } from "jazz-react-native";
|
||||
import { JazzProvider } from "jazz-react-native";
|
||||
import { apiKey } from "./apiKey";
|
||||
import ChatScreen from "./chat";
|
||||
|
||||
@@ -50,7 +50,6 @@ function App() {
|
||||
sync={{
|
||||
peer: `wss://cloud.jazz.tools/?key=${apiKey}`,
|
||||
}}
|
||||
CryptoProvider={RNQuickCrypto}
|
||||
>
|
||||
<NavigationContainer linking={linking} ref={navigationRef}>
|
||||
<Stack.Navigator initialRouteName={initialRoute}>
|
||||
|
||||
@@ -1,5 +1,31 @@
|
||||
# chat-vue
|
||||
|
||||
## 0.0.60
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [07feedd]
|
||||
- jazz-tools@0.10.13
|
||||
- jazz-browser@0.10.13
|
||||
- jazz-vue@0.10.13
|
||||
|
||||
## 0.0.59
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [4612e05]
|
||||
- jazz-tools@0.10.12
|
||||
- jazz-vue@0.10.12
|
||||
- jazz-browser@0.10.12
|
||||
|
||||
## 0.0.58
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [834203f]
|
||||
- jazz-browser@0.10.9
|
||||
- jazz-vue@0.10.9
|
||||
|
||||
## 0.0.57
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "chat-vue",
|
||||
"version": "0.0.57",
|
||||
"version": "0.0.60",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,30 @@
|
||||
# jazz-example-chat
|
||||
|
||||
## 0.0.156
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [07feedd]
|
||||
- jazz-tools@0.10.13
|
||||
- jazz-browser-media-images@0.10.13
|
||||
- jazz-react@0.10.13
|
||||
|
||||
## 0.0.155
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [4612e05]
|
||||
- jazz-tools@0.10.12
|
||||
- jazz-react@0.10.12
|
||||
- jazz-browser-media-images@0.10.12
|
||||
|
||||
## 0.0.154
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-browser-media-images@0.10.9
|
||||
- jazz-react@0.10.9
|
||||
|
||||
## 0.0.153
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-example-chat",
|
||||
"private": true,
|
||||
"version": "0.0.153",
|
||||
"version": "0.0.156",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,30 @@
|
||||
# minimal-auth-clerk
|
||||
|
||||
## 0.0.55
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [07feedd]
|
||||
- jazz-tools@0.10.13
|
||||
- jazz-react@0.10.13
|
||||
- jazz-react-auth-clerk@0.10.13
|
||||
|
||||
## 0.0.54
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [4612e05]
|
||||
- jazz-tools@0.10.12
|
||||
- jazz-react@0.10.12
|
||||
- jazz-react-auth-clerk@0.10.12
|
||||
|
||||
## 0.0.53
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react@0.10.9
|
||||
- jazz-react-auth-clerk@0.10.9
|
||||
|
||||
## 0.0.52
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "clerk",
|
||||
"private": true,
|
||||
"version": "0.0.52",
|
||||
"version": "0.0.55",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -13,7 +13,7 @@
|
||||
"dependencies": {
|
||||
"@clerk/clerk-react": "^5.4.1",
|
||||
"jazz-react": "workspace:*",
|
||||
"jazz-react-auth-clerk": "workspace:0.10.8",
|
||||
"jazz-react-auth-clerk": "workspace:0.10.13",
|
||||
"jazz-tools": "workspace:*",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1"
|
||||
|
||||
@@ -1,5 +1,27 @@
|
||||
# file-share-svelte
|
||||
|
||||
## 0.0.40
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [07feedd]
|
||||
- jazz-tools@0.10.13
|
||||
- jazz-svelte@0.10.13
|
||||
|
||||
## 0.0.39
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [4612e05]
|
||||
- jazz-svelte@0.10.12
|
||||
- jazz-tools@0.10.12
|
||||
|
||||
## 0.0.38
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.10.9
|
||||
|
||||
## 0.0.37
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "file-share-svelte",
|
||||
"version": "0.0.37",
|
||||
"version": "0.0.40",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,30 @@
|
||||
# form
|
||||
|
||||
## 0.0.51
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [07feedd]
|
||||
- jazz-tools@0.10.13
|
||||
- jazz-browser-media-images@0.10.13
|
||||
- jazz-react@0.10.13
|
||||
|
||||
## 0.0.50
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [4612e05]
|
||||
- jazz-tools@0.10.12
|
||||
- jazz-react@0.10.12
|
||||
- jazz-browser-media-images@0.10.12
|
||||
|
||||
## 0.0.49
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-browser-media-images@0.10.9
|
||||
- jazz-react@0.10.9
|
||||
|
||||
## 0.0.48
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "form",
|
||||
"private": true,
|
||||
"version": "0.0.48",
|
||||
"version": "0.0.51",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,30 @@
|
||||
# image-upload
|
||||
|
||||
## 0.0.53
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [07feedd]
|
||||
- jazz-tools@0.10.13
|
||||
- jazz-browser-media-images@0.10.13
|
||||
- jazz-react@0.10.13
|
||||
|
||||
## 0.0.52
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [4612e05]
|
||||
- jazz-tools@0.10.12
|
||||
- jazz-react@0.10.12
|
||||
- jazz-browser-media-images@0.10.12
|
||||
|
||||
## 0.0.51
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-browser-media-images@0.10.9
|
||||
- jazz-react@0.10.9
|
||||
|
||||
## 0.0.50
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "image-upload",
|
||||
"private": true,
|
||||
"version": "0.0.50",
|
||||
"version": "0.0.53",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,29 @@
|
||||
# jazz-example-musicplayer
|
||||
|
||||
## 0.0.77
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [07feedd]
|
||||
- jazz-tools@0.10.13
|
||||
- jazz-inspector@0.10.10
|
||||
- jazz-react@0.10.13
|
||||
|
||||
## 0.0.76
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [4612e05]
|
||||
- jazz-tools@0.10.12
|
||||
- jazz-inspector@0.10.9
|
||||
- jazz-react@0.10.12
|
||||
|
||||
## 0.0.75
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react@0.10.9
|
||||
|
||||
## 0.0.74
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-example-music-player",
|
||||
"private": true,
|
||||
"version": "0.0.74",
|
||||
"version": "0.0.77",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -22,8 +22,8 @@
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.0.0",
|
||||
"jazz-inspector": "workspace:*",
|
||||
"jazz-react": "workspace:0.10.8",
|
||||
"jazz-tools": "workspace:0.10.8",
|
||||
"jazz-react": "workspace:0.10.13",
|
||||
"jazz-tools": "workspace:0.10.13",
|
||||
"lucide-react": "^0.274.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
|
||||
@@ -1,5 +1,27 @@
|
||||
# organization
|
||||
|
||||
## 0.0.49
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [07feedd]
|
||||
- jazz-tools@0.10.13
|
||||
- jazz-react@0.10.13
|
||||
|
||||
## 0.0.48
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [4612e05]
|
||||
- jazz-tools@0.10.12
|
||||
- jazz-react@0.10.12
|
||||
|
||||
## 0.0.47
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react@0.10.9
|
||||
|
||||
## 0.0.46
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "organization",
|
||||
"private": true,
|
||||
"version": "0.0.46",
|
||||
"version": "0.0.49",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
# passkey-svelte
|
||||
|
||||
## 0.0.44
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.10.13
|
||||
|
||||
## 0.0.43
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [4612e05]
|
||||
- jazz-svelte@0.10.12
|
||||
|
||||
## 0.0.42
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.10.9
|
||||
|
||||
## 0.0.41
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "passkey-svelte",
|
||||
"version": "0.0.41",
|
||||
"version": "0.0.44",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,27 @@
|
||||
# minimal-auth-passkey
|
||||
|
||||
## 0.0.54
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [07feedd]
|
||||
- jazz-tools@0.10.13
|
||||
- jazz-react@0.10.13
|
||||
|
||||
## 0.0.53
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [4612e05]
|
||||
- jazz-tools@0.10.12
|
||||
- jazz-react@0.10.12
|
||||
|
||||
## 0.0.52
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react@0.10.9
|
||||
|
||||
## 0.0.51
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "passkey",
|
||||
"private": true,
|
||||
"version": "0.0.51",
|
||||
"version": "0.0.54",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,27 @@
|
||||
# passphrase
|
||||
|
||||
## 0.0.51
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [07feedd]
|
||||
- jazz-tools@0.10.13
|
||||
- jazz-react@0.10.13
|
||||
|
||||
## 0.0.50
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [4612e05]
|
||||
- jazz-tools@0.10.12
|
||||
- jazz-react@0.10.12
|
||||
|
||||
## 0.0.49
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react@0.10.9
|
||||
|
||||
## 0.0.48
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "passphrase",
|
||||
"private": true,
|
||||
"version": "0.0.48",
|
||||
"version": "0.0.51",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -66,3 +66,72 @@ main {
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.auth-container {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #f3f4f6;
|
||||
}
|
||||
|
||||
.auth-card {
|
||||
background-color: white;
|
||||
padding: 2rem;
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px
|
||||
rgba(0, 0, 0, 0.06);
|
||||
width: 28rem;
|
||||
}
|
||||
|
||||
.auth-button-primary,
|
||||
.auth-button-secondary {
|
||||
width: 100%;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 0.25rem;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.auth-button-primary {
|
||||
background-color: black;
|
||||
color: white;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.auth-button-secondary {
|
||||
background-color: white;
|
||||
color: black;
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
.auth-heading {
|
||||
color: black;
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.auth-textarea {
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
border: 1px solid #d1d5db;
|
||||
border-radius: 0.25rem;
|
||||
margin-bottom: 1rem;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.auth-description {
|
||||
font-size: 0.875rem;
|
||||
color: #4b5563;
|
||||
text-align: center;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.auth-button-group {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,142 @@
|
||||
import { JazzProvider, PassphraseAuthBasicUI } from "jazz-react";
|
||||
import { StrictMode } from "react";
|
||||
import { JazzProvider, usePassphraseAuth } from "jazz-react";
|
||||
import { StrictMode, useState } from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import App from "./App.tsx";
|
||||
import "./index.css";
|
||||
import { wordlist } from "./wordlist.ts";
|
||||
|
||||
function PassphraseAuthBasicUI(props: {
|
||||
appName: string;
|
||||
wordlist: string[];
|
||||
children?: React.ReactNode;
|
||||
}) {
|
||||
const auth = usePassphraseAuth({
|
||||
wordlist: props.wordlist,
|
||||
});
|
||||
|
||||
const [step, setStep] = useState<"initial" | "create" | "login">("initial");
|
||||
const [loginPassphrase, setLoginPassphrase] = useState("");
|
||||
const [isCopied, setIsCopied] = useState(false);
|
||||
const [currentPassphrase, setCurrentPassphrase] = useState(() =>
|
||||
auth.generateRandomPassphrase(),
|
||||
);
|
||||
|
||||
if (auth.state === "signedIn") {
|
||||
return props.children ?? null;
|
||||
}
|
||||
|
||||
const handleCreateAccount = async () => {
|
||||
setStep("create");
|
||||
};
|
||||
|
||||
const handleLogin = () => {
|
||||
setStep("login");
|
||||
};
|
||||
|
||||
const handleReroll = () => {
|
||||
const newPassphrase = auth.generateRandomPassphrase();
|
||||
setCurrentPassphrase(newPassphrase);
|
||||
setIsCopied(false);
|
||||
};
|
||||
|
||||
const handleBack = () => {
|
||||
setStep("initial");
|
||||
setLoginPassphrase("");
|
||||
};
|
||||
|
||||
const handleCopy = async () => {
|
||||
await navigator.clipboard.writeText(auth.passphrase);
|
||||
setIsCopied(true);
|
||||
};
|
||||
|
||||
const handleLoginSubmit = async () => {
|
||||
await auth.logIn(loginPassphrase);
|
||||
setStep("initial");
|
||||
setLoginPassphrase("");
|
||||
};
|
||||
|
||||
const handleNext = async () => {
|
||||
await auth.registerNewAccount(currentPassphrase, "My Account");
|
||||
setStep("initial");
|
||||
setLoginPassphrase("");
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="auth-container">
|
||||
<div className="auth-card">
|
||||
{step === "initial" && (
|
||||
<div>
|
||||
<h1 className="auth-heading">{props.appName}</h1>
|
||||
<button
|
||||
onClick={handleCreateAccount}
|
||||
className="auth-button-primary"
|
||||
>
|
||||
Create new account
|
||||
</button>
|
||||
<button onClick={handleLogin} className="auth-button-secondary">
|
||||
Log in
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{step === "create" && (
|
||||
<>
|
||||
<h1 className="auth-heading">Your Passphrase</h1>
|
||||
<p className="auth-description">
|
||||
Please copy and store this passphrase somewhere safe. You'll need
|
||||
it to log in.
|
||||
</p>
|
||||
<textarea
|
||||
readOnly
|
||||
value={currentPassphrase}
|
||||
className="auth-textarea"
|
||||
rows={5}
|
||||
/>
|
||||
<button onClick={handleCopy} className="auth-button-primary">
|
||||
{isCopied ? "Copied!" : "Copy"}
|
||||
</button>
|
||||
<div className="auth-button-group">
|
||||
<button onClick={handleBack} className="auth-button-secondary">
|
||||
Back
|
||||
</button>
|
||||
<button onClick={handleReroll} className="auth-button-secondary">
|
||||
Generate New Passphrase
|
||||
</button>
|
||||
<button onClick={handleNext} className="auth-button-primary">
|
||||
Register
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{step === "login" && (
|
||||
<div>
|
||||
<h1 className="auth-heading">Log In</h1>
|
||||
<textarea
|
||||
value={loginPassphrase}
|
||||
onChange={(e) => setLoginPassphrase(e.target.value)}
|
||||
placeholder="Enter your passphrase"
|
||||
className="auth-textarea"
|
||||
rows={5}
|
||||
/>
|
||||
<div className="auth-button-group">
|
||||
<button onClick={handleBack} className="auth-button-secondary">
|
||||
Back
|
||||
</button>
|
||||
<button
|
||||
onClick={handleLoginSubmit}
|
||||
className="auth-button-primary"
|
||||
>
|
||||
Log In
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function JazzAndAuth({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<JazzProvider
|
||||
|
||||
@@ -1,5 +1,27 @@
|
||||
# jazz-password-manager
|
||||
|
||||
## 0.0.75
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [07feedd]
|
||||
- jazz-tools@0.10.13
|
||||
- jazz-react@0.10.13
|
||||
|
||||
## 0.0.74
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [4612e05]
|
||||
- jazz-tools@0.10.12
|
||||
- jazz-react@0.10.12
|
||||
|
||||
## 0.0.73
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react@0.10.9
|
||||
|
||||
## 0.0.72
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-password-manager",
|
||||
"private": true,
|
||||
"version": "0.0.72",
|
||||
"version": "0.0.75",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -12,8 +12,8 @@
|
||||
"clean-install": "rm -rf node_modules pnpm-lock.yaml && pnpm install"
|
||||
},
|
||||
"dependencies": {
|
||||
"jazz-react": "workspace:0.10.8",
|
||||
"jazz-tools": "workspace:0.10.8",
|
||||
"jazz-react": "workspace:0.10.13",
|
||||
"jazz-tools": "workspace:0.10.13",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-hook-form": "^7.41.5",
|
||||
|
||||
@@ -1,5 +1,30 @@
|
||||
# jazz-example-pets
|
||||
|
||||
## 0.0.173
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [07feedd]
|
||||
- jazz-tools@0.10.13
|
||||
- jazz-browser-media-images@0.10.13
|
||||
- jazz-react@0.10.13
|
||||
|
||||
## 0.0.172
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [4612e05]
|
||||
- jazz-tools@0.10.12
|
||||
- jazz-react@0.10.12
|
||||
- jazz-browser-media-images@0.10.12
|
||||
|
||||
## 0.0.171
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-browser-media-images@0.10.9
|
||||
- jazz-react@0.10.9
|
||||
|
||||
## 0.0.170
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-example-pets",
|
||||
"private": true,
|
||||
"version": "0.0.170",
|
||||
"version": "0.0.173",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -19,9 +19,9 @@
|
||||
"@radix-ui/react-toast": "^1.1.4",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.0.0",
|
||||
"jazz-browser-media-images": "workspace:0.10.8",
|
||||
"jazz-react": "workspace:0.10.8",
|
||||
"jazz-tools": "workspace:0.10.8",
|
||||
"jazz-browser-media-images": "workspace:0.10.13",
|
||||
"jazz-react": "workspace:0.10.13",
|
||||
"jazz-tools": "workspace:0.10.13",
|
||||
"lucide-react": "^0.274.0",
|
||||
"qrcode": "^1.5.3",
|
||||
"react": "^18.3.1",
|
||||
@@ -41,7 +41,7 @@
|
||||
"@vitejs/plugin-react-swc": "^3.3.2",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"is-ci": "^3.0.1",
|
||||
"jazz-run": "workspace:0.10.8",
|
||||
"jazz-run": "workspace:0.10.13",
|
||||
"postcss": "^8.4.27",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"typescript": "~5.6.2",
|
||||
|
||||
@@ -1,5 +1,30 @@
|
||||
# reactions
|
||||
|
||||
## 0.0.53
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [07feedd]
|
||||
- jazz-tools@0.10.13
|
||||
- jazz-browser-media-images@0.10.13
|
||||
- jazz-react@0.10.13
|
||||
|
||||
## 0.0.52
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [4612e05]
|
||||
- jazz-tools@0.10.12
|
||||
- jazz-react@0.10.12
|
||||
- jazz-browser-media-images@0.10.12
|
||||
|
||||
## 0.0.51
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-browser-media-images@0.10.9
|
||||
- jazz-react@0.10.9
|
||||
|
||||
## 0.0.50
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "reactions",
|
||||
"private": true,
|
||||
"version": "0.0.50",
|
||||
"version": "0.0.53",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,31 @@
|
||||
# todo-vue
|
||||
|
||||
## 0.0.58
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [07feedd]
|
||||
- jazz-tools@0.10.13
|
||||
- jazz-browser@0.10.13
|
||||
- jazz-vue@0.10.13
|
||||
|
||||
## 0.0.57
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [4612e05]
|
||||
- jazz-tools@0.10.12
|
||||
- jazz-vue@0.10.12
|
||||
- jazz-browser@0.10.12
|
||||
|
||||
## 0.0.56
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [834203f]
|
||||
- jazz-browser@0.10.9
|
||||
- jazz-vue@0.10.9
|
||||
|
||||
## 0.0.55
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "todo-vue",
|
||||
"version": "0.0.55",
|
||||
"version": "0.0.58",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,27 @@
|
||||
# jazz-example-todo
|
||||
|
||||
## 0.0.172
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [07feedd]
|
||||
- jazz-tools@0.10.13
|
||||
- jazz-react@0.10.13
|
||||
|
||||
## 0.0.171
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [4612e05]
|
||||
- jazz-tools@0.10.12
|
||||
- jazz-react@0.10.12
|
||||
|
||||
## 0.0.170
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react@0.10.9
|
||||
|
||||
## 0.0.169
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-example-todo",
|
||||
"private": true,
|
||||
"version": "0.0.169",
|
||||
"version": "0.0.172",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -16,8 +16,8 @@
|
||||
"@radix-ui/react-toast": "^1.1.4",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.0.0",
|
||||
"jazz-react": "workspace:0.10.8",
|
||||
"jazz-tools": "workspace:0.10.8",
|
||||
"jazz-react": "workspace:0.10.13",
|
||||
"jazz-tools": "workspace:0.10.13",
|
||||
"lucide-react": "^0.274.0",
|
||||
"qrcode": "^1.5.3",
|
||||
"react": "^18.3.1",
|
||||
|
||||
@@ -1,5 +1,27 @@
|
||||
# version-history
|
||||
|
||||
## 0.0.50
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [07feedd]
|
||||
- jazz-tools@0.10.13
|
||||
- jazz-react@0.10.13
|
||||
|
||||
## 0.0.49
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [4612e05]
|
||||
- jazz-tools@0.10.12
|
||||
- jazz-react@0.10.12
|
||||
|
||||
## 0.0.48
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react@0.10.9
|
||||
|
||||
## 0.0.47
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "version-history",
|
||||
"private": true,
|
||||
"version": "0.0.47",
|
||||
"version": "0.0.50",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
import { packages } from "@/lib/packages";
|
||||
import { clsx } from "clsx";
|
||||
import { Icon } from "gcmp-design-system/src/app/components/atoms/Icon";
|
||||
import type { Metadata } from "next";
|
||||
import Link from "next/link";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "API reference",
|
||||
openGraph: {
|
||||
title: "API reference",
|
||||
},
|
||||
};
|
||||
|
||||
const CardHeading = ({
|
||||
children,
|
||||
className,
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import { CodeGroup, ContentByFramework, JazzLogo } from '@/components/forMdx'
|
||||
|
||||
export const metadata = {
|
||||
title: "Learn some Jazz",
|
||||
openGraph: {
|
||||
title: "Learn some Jazz",
|
||||
},
|
||||
};
|
||||
|
||||
# Learn some <span className="sr-only">Jazz</span> <JazzLogo className="h-[41px] -ml-0.5 -mt-[3px] inline" />
|
||||
|
||||
Welcome to the Jazz documentation!
|
||||
|
||||
@@ -6,6 +6,38 @@ import { Framework, frameworks } from "@/lib/framework";
|
||||
import type { Toc } from "@stefanprobst/rehype-extract-toc";
|
||||
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
|
||||
|
||||
async function getMdxSource(slugPath: string, framework: string) {
|
||||
try {
|
||||
return await import(`./${slugPath}.mdx`);
|
||||
} catch (error) {
|
||||
return await import(`./${slugPath}/${framework}.mdx`);
|
||||
}
|
||||
}
|
||||
|
||||
export async function generateMetadata({
|
||||
params: { slug, framework },
|
||||
}: { params: { slug: string[]; framework: string } }) {
|
||||
const slugPath = slug.join("/");
|
||||
try {
|
||||
const mdxSource = await getMdxSource(slugPath, framework);
|
||||
const title = mdxSource.tableOfContents?.[0].value || "Documentation";
|
||||
|
||||
return {
|
||||
title,
|
||||
openGraph: {
|
||||
title,
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
title: "Documentation",
|
||||
openGraph: {
|
||||
title: "Documentation",
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default async function Page({
|
||||
params: { slug, framework },
|
||||
}: { params: { slug: string[]; framework: string } }) {
|
||||
@@ -13,13 +45,7 @@ export default async function Page({
|
||||
const bodyClassName = "overflow-x-hidden lg:flex-1 py-10 max-w-3xl mx-auto";
|
||||
|
||||
try {
|
||||
let mdxSource;
|
||||
try {
|
||||
mdxSource = await import(`./${slugPath}.mdx`);
|
||||
} catch (error) {
|
||||
mdxSource = await import(`./${slugPath}/${framework}.mdx`);
|
||||
}
|
||||
|
||||
const mdxSource = await getMdxSource(slugPath, framework);
|
||||
const { default: Content, tableOfContents } = mdxSource;
|
||||
|
||||
// Exclude h1 from table of contents
|
||||
|
||||
@@ -1,25 +1,27 @@
|
||||
import { Pricing } from "@/components/Pricing";
|
||||
import { LatencyMap } from "@/components/cloud/latencyMap";
|
||||
import { GridCard } from "gcmp-design-system/src/app/components/atoms/GridCard";
|
||||
import {
|
||||
H2,
|
||||
H3,
|
||||
H4,
|
||||
} from "gcmp-design-system/src/app/components/atoms/Headings";
|
||||
import { LI } from "gcmp-design-system/src/app/components/atoms/ListItem";
|
||||
import { H2, H3 } from "gcmp-design-system/src/app/components/atoms/Headings";
|
||||
import { P } from "gcmp-design-system/src/app/components/atoms/Paragraph";
|
||||
import { GappedGrid } from "gcmp-design-system/src/app/components/molecules/GappedGrid";
|
||||
import { HeroHeader } from "gcmp-design-system/src/app/components/molecules/HeroHeader";
|
||||
import { UL } from "gcmp-design-system/src/app/components/molecules/List";
|
||||
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
|
||||
import { SectionHeader } from "gcmp-design-system/src/app/components/molecules/SectionHeader";
|
||||
import type { Metadata } from "next";
|
||||
import CloudPlusBackup from "./cloudPlusBackup.mdx";
|
||||
import CloudPlusDIY from "./cloudPlusDIY.mdx";
|
||||
import CompletelyDIY from "./completelyDIY.mdx";
|
||||
|
||||
export const metadata = {
|
||||
title: "Jazz Cloud",
|
||||
description: "Serverless sync & storage for Jazz apps.",
|
||||
const title = "Jazz Cloud";
|
||||
const description = "Serverless sync & storage for Jazz apps.";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title,
|
||||
description,
|
||||
openGraph: {
|
||||
title,
|
||||
description,
|
||||
},
|
||||
};
|
||||
|
||||
export default function Cloud() {
|
||||
|
||||
@@ -11,6 +11,20 @@ import { H2 } from "gcmp-design-system/src/app/components/atoms/Headings";
|
||||
import { Icon } from "gcmp-design-system/src/app/components/atoms/Icon";
|
||||
import { GappedGrid } from "gcmp-design-system/src/app/components/molecules/GappedGrid";
|
||||
import { HeroHeader } from "gcmp-design-system/src/app/components/molecules/HeroHeader";
|
||||
import type { Metadata } from "next";
|
||||
|
||||
const title = "Examples";
|
||||
const description =
|
||||
"Find an example app with code most similar to what you want to build.";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title,
|
||||
description,
|
||||
openGraph: {
|
||||
title,
|
||||
description,
|
||||
},
|
||||
};
|
||||
|
||||
const MockButton = ({ children }: { children: React.ReactNode }) => (
|
||||
<p className="bg-blue-100 text-blue-800 py-1 px-3 rounded-full font-medium text-xs inline-flex items-center justify-center">
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
import { products } from "@/lib/showcase";
|
||||
import { HeroHeader } from "gcmp-design-system/src/app/components/molecules/HeroHeader";
|
||||
import type { Metadata } from "next";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
|
||||
export const metadata = {
|
||||
title: "Built with Jazz",
|
||||
description: "Great apps by smart people.",
|
||||
const title = "Built with Jazz";
|
||||
const description = "Great apps by smart people.";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title,
|
||||
description,
|
||||
openGraph: {
|
||||
title,
|
||||
description,
|
||||
},
|
||||
};
|
||||
|
||||
export default function Page() {
|
||||
|
||||
@@ -1,8 +1,18 @@
|
||||
import { clsx } from "clsx";
|
||||
import { HeroHeader } from "gcmp-design-system/src/app/components/molecules/HeroHeader";
|
||||
import type { Metadata } from "next";
|
||||
import dynamic from "next/dynamic";
|
||||
import { Fragment } from "react";
|
||||
|
||||
const title = "Status";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title,
|
||||
openGraph: {
|
||||
title,
|
||||
},
|
||||
};
|
||||
|
||||
const LatencyChart = dynamic(() => import("@/components/LatencyChart"), {
|
||||
ssr: false,
|
||||
});
|
||||
@@ -133,11 +143,6 @@ const query = async () => {
|
||||
return byRegion;
|
||||
};
|
||||
|
||||
export const metadata = {
|
||||
title: "Status",
|
||||
description: "Great apps by smart people.",
|
||||
};
|
||||
|
||||
export default async function Page() {
|
||||
const byRegion = await query();
|
||||
|
||||
|
||||
@@ -184,9 +184,7 @@ export class CoValueCore {
|
||||
this.header.meta?.type === "account"
|
||||
? (this.node.currentSessionID.replace(
|
||||
this.node.account.id,
|
||||
this.node.account
|
||||
.currentAgentID()
|
||||
._unsafeUnwrap({ withStackTrace: true }),
|
||||
this.node.account.currentAgentID(),
|
||||
) as SessionID)
|
||||
: this.node.currentSessionID;
|
||||
|
||||
@@ -455,9 +453,7 @@ export class CoValueCore {
|
||||
this.header.meta?.type === "account"
|
||||
? (this.node.currentSessionID.replace(
|
||||
this.node.account.id,
|
||||
this.node.account
|
||||
.currentAgentID()
|
||||
._unsafeUnwrap({ withStackTrace: true }),
|
||||
this.node.account.currentAgentID(),
|
||||
) as SessionID)
|
||||
: this.node.currentSessionID;
|
||||
|
||||
@@ -639,9 +635,7 @@ export class CoValueCore {
|
||||
// Try to find key revelation for us
|
||||
const lookupAccountOrAgentID =
|
||||
this.header.meta?.type === "account"
|
||||
? this.node.account
|
||||
.currentAgentID()
|
||||
._unsafeUnwrap({ withStackTrace: true })
|
||||
? this.node.account.currentAgentID()
|
||||
: this.node.account.id;
|
||||
|
||||
const lastReadyKeyEdit = content.lastEditAt(
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { Result, ok } from "neverthrow";
|
||||
import { CoID, RawCoValue } from "../coValue.js";
|
||||
import {
|
||||
CoValueCore,
|
||||
@@ -47,10 +46,11 @@ export class RawAccount<
|
||||
> extends RawGroup<Meta> {
|
||||
_cachedCurrentAgentID: AgentID | undefined;
|
||||
|
||||
currentAgentID(): Result<AgentID, InvalidAccountAgentIDError> {
|
||||
currentAgentID(): AgentID {
|
||||
if (this._cachedCurrentAgentID) {
|
||||
return ok(this._cachedCurrentAgentID);
|
||||
return this._cachedCurrentAgentID;
|
||||
}
|
||||
|
||||
const agents = this.keys()
|
||||
.filter((k): k is AgentID => k.startsWith("sealer_"))
|
||||
.sort(
|
||||
@@ -65,7 +65,7 @@ export class RawAccount<
|
||||
|
||||
this._cachedCurrentAgentID = agents[0];
|
||||
|
||||
return ok(agents[0]!);
|
||||
return agents[0]!;
|
||||
}
|
||||
|
||||
createInvite(_: AccountRole): InviteSecret {
|
||||
@@ -77,10 +77,10 @@ export interface ControlledAccountOrAgent {
|
||||
id: RawAccountID | AgentID;
|
||||
agentSecret: AgentSecret;
|
||||
|
||||
currentAgentID: () => Result<AgentID, InvalidAccountAgentIDError>;
|
||||
currentSignerID: () => Result<SignerID, InvalidAccountAgentIDError>;
|
||||
currentAgentID: () => AgentID;
|
||||
currentSignerID: () => SignerID;
|
||||
currentSignerSecret: () => SignerSecret;
|
||||
currentSealerID: () => Result<SealerID, InvalidAccountAgentIDError>;
|
||||
currentSealerID: () => SealerID;
|
||||
currentSealerSecret: () => SealerSecret;
|
||||
}
|
||||
|
||||
@@ -116,17 +116,17 @@ export class RawControlledAccount<Meta extends AccountMeta = AccountMeta>
|
||||
return this.core.node.acceptInvite(groupOrOwnedValueID, inviteSecret);
|
||||
}
|
||||
|
||||
currentAgentID(): Result<AgentID, InvalidAccountAgentIDError> {
|
||||
currentAgentID(): AgentID {
|
||||
if (this._cachedCurrentAgentID) {
|
||||
return ok(this._cachedCurrentAgentID);
|
||||
return this._cachedCurrentAgentID;
|
||||
}
|
||||
const agentID = this.crypto.getAgentID(this.agentSecret);
|
||||
this._cachedCurrentAgentID = agentID;
|
||||
return ok(agentID);
|
||||
return agentID;
|
||||
}
|
||||
|
||||
currentSignerID() {
|
||||
return this.currentAgentID().map((id) => this.crypto.getAgentSignerID(id));
|
||||
return this.crypto.getAgentSignerID(this.currentAgentID());
|
||||
}
|
||||
|
||||
currentSignerSecret(): SignerSecret {
|
||||
@@ -134,7 +134,7 @@ export class RawControlledAccount<Meta extends AccountMeta = AccountMeta>
|
||||
}
|
||||
|
||||
currentSealerID() {
|
||||
return this.currentAgentID().map((id) => this.crypto.getAgentSealerID(id));
|
||||
return this.crypto.getAgentSealerID(this.currentAgentID());
|
||||
}
|
||||
|
||||
currentSealerSecret(): SealerSecret {
|
||||
@@ -153,11 +153,11 @@ export class ControlledAgent implements ControlledAccountOrAgent {
|
||||
}
|
||||
|
||||
currentAgentID() {
|
||||
return ok(this.crypto.getAgentID(this.agentSecret));
|
||||
return this.crypto.getAgentID(this.agentSecret);
|
||||
}
|
||||
|
||||
currentSignerID() {
|
||||
return this.currentAgentID().map((id) => this.crypto.getAgentSignerID(id));
|
||||
return this.crypto.getAgentSignerID(this.currentAgentID());
|
||||
}
|
||||
|
||||
currentSignerSecret(): SignerSecret {
|
||||
@@ -165,7 +165,7 @@ export class ControlledAgent implements ControlledAccountOrAgent {
|
||||
}
|
||||
|
||||
currentSealerID() {
|
||||
return this.currentAgentID().map((id) => this.crypto.getAgentSealerID(id));
|
||||
return this.crypto.getAgentSealerID(this.currentAgentID());
|
||||
}
|
||||
|
||||
currentSealerSecret(): SealerSecret {
|
||||
|
||||
@@ -251,9 +251,7 @@ export class RawGroup<
|
||||
|
||||
const memberKey = typeof account === "string" ? account : account.id;
|
||||
const agent =
|
||||
typeof account === "string"
|
||||
? account
|
||||
: account.currentAgentID()._unsafeUnwrap({ withStackTrace: true });
|
||||
typeof account === "string" ? account : account.currentAgentID();
|
||||
|
||||
/**
|
||||
* WriteOnly members can only see their own changes.
|
||||
|
||||
@@ -530,7 +530,7 @@ export class LocalNode {
|
||||
} satisfies UnexpectedlyNotAccountError);
|
||||
}
|
||||
|
||||
return (coValue.getCurrentContent() as RawAccount).currentAgentID();
|
||||
return ok((coValue.getCurrentContent() as RawAccount).currentAgentID());
|
||||
}
|
||||
|
||||
resolveAccountAgentAsync(
|
||||
@@ -573,7 +573,7 @@ export class LocalNode {
|
||||
} satisfies UnexpectedlyNotAccountError);
|
||||
}
|
||||
|
||||
return (coValue.getCurrentContent() as RawAccount).currentAgentID();
|
||||
return ok((coValue.getCurrentContent() as RawAccount).currentAgentID());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -601,9 +601,7 @@ export class LocalNode {
|
||||
this.crypto.seal({
|
||||
message: readKey.secret,
|
||||
from: this.account.currentSealerSecret(),
|
||||
to: this.account
|
||||
.currentSealerID()
|
||||
._unsafeUnwrap({ withStackTrace: true }),
|
||||
to: this.account.currentSealerID(),
|
||||
nOnceMaterial: {
|
||||
in: groupCoValue.id,
|
||||
tx: groupCoValue.nextTransactionID(),
|
||||
|
||||
@@ -476,16 +476,7 @@ function agentInAccountOrMemberInGroup(
|
||||
groupAtTime: RawGroup,
|
||||
): RawAccountID | AgentID | undefined {
|
||||
if (transactor === groupAtTime.id && groupAtTime instanceof RawAccount) {
|
||||
return groupAtTime.currentAgentID().match(
|
||||
(agentID) => agentID,
|
||||
(e) => {
|
||||
logger.error(
|
||||
"Error while determining current agent ID in valid transactions",
|
||||
e,
|
||||
);
|
||||
return undefined;
|
||||
},
|
||||
);
|
||||
return groupAtTime.currentAgentID();
|
||||
}
|
||||
return transactor;
|
||||
}
|
||||
|
||||
@@ -358,7 +358,7 @@ test("Admins can set group read key and then use it to create and read private t
|
||||
const revelation = Crypto.seal({
|
||||
message: readKey,
|
||||
from: admin.currentSealerSecret(),
|
||||
to: admin.currentSealerID()._unsafeUnwrap(),
|
||||
to: admin.currentSealerID(),
|
||||
nOnceMaterial: {
|
||||
in: groupCore.id,
|
||||
tx: groupCore.nextTransactionID(),
|
||||
@@ -412,7 +412,7 @@ test("Admins can set group read key and then writers can use it to create and re
|
||||
const revelation1 = Crypto.seal({
|
||||
message: readKey,
|
||||
from: admin.currentSealerSecret(),
|
||||
to: admin.currentSealerID()._unsafeUnwrap(),
|
||||
to: admin.currentSealerID(),
|
||||
nOnceMaterial: {
|
||||
in: groupCore.id,
|
||||
tx: groupCore.nextTransactionID(),
|
||||
@@ -424,7 +424,7 @@ test("Admins can set group read key and then writers can use it to create and re
|
||||
const revelation2 = Crypto.seal({
|
||||
message: readKey,
|
||||
from: admin.currentSealerSecret(),
|
||||
to: writer.currentSealerID()._unsafeUnwrap(),
|
||||
to: writer.currentSealerID(),
|
||||
nOnceMaterial: {
|
||||
in: groupCore.id,
|
||||
tx: groupCore.nextTransactionID(),
|
||||
@@ -491,7 +491,7 @@ test("Admins can set group read key and then use it to create private transactio
|
||||
const revelation1 = Crypto.seal({
|
||||
message: readKey,
|
||||
from: admin.currentSealerSecret(),
|
||||
to: admin.currentSealerID()._unsafeUnwrap(),
|
||||
to: admin.currentSealerID(),
|
||||
nOnceMaterial: {
|
||||
in: groupCore.id,
|
||||
tx: groupCore.nextTransactionID(),
|
||||
@@ -503,7 +503,7 @@ test("Admins can set group read key and then use it to create private transactio
|
||||
const revelation2 = Crypto.seal({
|
||||
message: readKey,
|
||||
from: admin.currentSealerSecret(),
|
||||
to: reader.currentSealerID()._unsafeUnwrap(),
|
||||
to: reader.currentSealerID(),
|
||||
nOnceMaterial: {
|
||||
in: groupCore.id,
|
||||
tx: groupCore.nextTransactionID(),
|
||||
@@ -578,7 +578,7 @@ test("Admins can set group read key and then use it to create private transactio
|
||||
const revelation1 = Crypto.seal({
|
||||
message: readKey,
|
||||
from: admin.currentSealerSecret(),
|
||||
to: admin.currentSealerID()._unsafeUnwrap(),
|
||||
to: admin.currentSealerID(),
|
||||
nOnceMaterial: {
|
||||
in: groupCore.id,
|
||||
tx: groupCore.nextTransactionID(),
|
||||
@@ -590,7 +590,7 @@ test("Admins can set group read key and then use it to create private transactio
|
||||
const revelation2 = Crypto.seal({
|
||||
message: readKey,
|
||||
from: admin.currentSealerSecret(),
|
||||
to: reader1.currentSealerID()._unsafeUnwrap(),
|
||||
to: reader1.currentSealerID(),
|
||||
nOnceMaterial: {
|
||||
in: groupCore.id,
|
||||
tx: groupCore.nextTransactionID(),
|
||||
@@ -629,7 +629,7 @@ test("Admins can set group read key and then use it to create private transactio
|
||||
const revelation3 = Crypto.seal({
|
||||
message: readKey,
|
||||
from: admin.currentSealerSecret(),
|
||||
to: reader2.currentSealerID()._unsafeUnwrap(),
|
||||
to: reader2.currentSealerID(),
|
||||
nOnceMaterial: {
|
||||
in: groupCore.id,
|
||||
tx: groupCore.nextTransactionID(),
|
||||
@@ -694,7 +694,7 @@ test("Admins can set group read key, make a private transaction in an owned obje
|
||||
const revelation1 = Crypto.seal({
|
||||
message: readKey,
|
||||
from: admin.currentSealerSecret(),
|
||||
to: admin.currentSealerID()._unsafeUnwrap(),
|
||||
to: admin.currentSealerID(),
|
||||
nOnceMaterial: {
|
||||
in: groupCore.id,
|
||||
tx: groupCore.nextTransactionID(),
|
||||
@@ -724,7 +724,7 @@ test("Admins can set group read key, make a private transaction in an owned obje
|
||||
const revelation2 = Crypto.seal({
|
||||
message: readKey2,
|
||||
from: admin.currentSealerSecret(),
|
||||
to: admin.currentSealerID()._unsafeUnwrap(),
|
||||
to: admin.currentSealerID(),
|
||||
nOnceMaterial: {
|
||||
in: groupCore.id,
|
||||
tx: groupCore.nextTransactionID(),
|
||||
@@ -777,7 +777,7 @@ test("Admins can set group read key, make a private transaction in an owned obje
|
||||
const revelation = Crypto.seal({
|
||||
message: readKey,
|
||||
from: admin.currentSealerSecret(),
|
||||
to: admin.currentSealerID()._unsafeUnwrap(),
|
||||
to: admin.currentSealerID(),
|
||||
nOnceMaterial: {
|
||||
in: groupCore.id,
|
||||
tx: groupCore.nextTransactionID(),
|
||||
@@ -804,7 +804,7 @@ test("Admins can set group read key, make a private transaction in an owned obje
|
||||
const revelation2 = Crypto.seal({
|
||||
message: readKey2,
|
||||
from: admin.currentSealerSecret(),
|
||||
to: admin.currentSealerID()._unsafeUnwrap(),
|
||||
to: admin.currentSealerID(),
|
||||
nOnceMaterial: {
|
||||
in: groupCore.id,
|
||||
tx: groupCore.nextTransactionID(),
|
||||
@@ -816,7 +816,7 @@ test("Admins can set group read key, make a private transaction in an owned obje
|
||||
const revelation3 = Crypto.seal({
|
||||
message: readKey2,
|
||||
from: admin.currentSealerSecret(),
|
||||
to: reader.currentSealerID()._unsafeUnwrap(),
|
||||
to: reader.currentSealerID(),
|
||||
nOnceMaterial: {
|
||||
in: groupCore.id,
|
||||
tx: groupCore.nextTransactionID(),
|
||||
@@ -912,7 +912,7 @@ test("Admins can set group read rey, make a private transaction in an owned obje
|
||||
const revelation1 = Crypto.seal({
|
||||
message: readKey,
|
||||
from: admin.currentSealerSecret(),
|
||||
to: admin.currentSealerID()._unsafeUnwrap(),
|
||||
to: admin.currentSealerID(),
|
||||
nOnceMaterial: {
|
||||
in: groupCore.id,
|
||||
tx: groupCore.nextTransactionID(),
|
||||
@@ -924,7 +924,7 @@ test("Admins can set group read rey, make a private transaction in an owned obje
|
||||
const revelation2 = Crypto.seal({
|
||||
message: readKey,
|
||||
from: admin.currentSealerSecret(),
|
||||
to: reader.currentSealerID()._unsafeUnwrap(),
|
||||
to: reader.currentSealerID(),
|
||||
nOnceMaterial: {
|
||||
in: groupCore.id,
|
||||
tx: groupCore.nextTransactionID(),
|
||||
@@ -936,7 +936,7 @@ test("Admins can set group read rey, make a private transaction in an owned obje
|
||||
const revelation3 = Crypto.seal({
|
||||
message: readKey,
|
||||
from: admin.currentSealerSecret(),
|
||||
to: reader2.currentSealerID()._unsafeUnwrap(),
|
||||
to: reader2.currentSealerID(),
|
||||
nOnceMaterial: {
|
||||
in: groupCore.id,
|
||||
tx: groupCore.nextTransactionID(),
|
||||
@@ -982,7 +982,7 @@ test("Admins can set group read rey, make a private transaction in an owned obje
|
||||
const newRevelation1 = Crypto.seal({
|
||||
message: readKey2,
|
||||
from: admin.currentSealerSecret(),
|
||||
to: admin.currentSealerID()._unsafeUnwrap(),
|
||||
to: admin.currentSealerID(),
|
||||
nOnceMaterial: {
|
||||
in: groupCore.id,
|
||||
tx: groupCore.nextTransactionID(),
|
||||
@@ -994,7 +994,7 @@ test("Admins can set group read rey, make a private transaction in an owned obje
|
||||
const newRevelation2 = Crypto.seal({
|
||||
message: readKey2,
|
||||
from: admin.currentSealerSecret(),
|
||||
to: reader2.currentSealerID()._unsafeUnwrap(),
|
||||
to: reader2.currentSealerID(),
|
||||
nOnceMaterial: {
|
||||
in: groupCore.id,
|
||||
tx: groupCore.nextTransactionID(),
|
||||
@@ -1119,7 +1119,7 @@ test("Admins can create an adminInvite, which can add an admin", () => {
|
||||
const revelation = Crypto.seal({
|
||||
message: readKey,
|
||||
from: admin.currentSealerSecret(),
|
||||
to: admin.currentSealerID()._unsafeUnwrap(),
|
||||
to: admin.currentSealerID(),
|
||||
nOnceMaterial: {
|
||||
in: groupCore.id,
|
||||
tx: groupCore.nextTransactionID(),
|
||||
@@ -1229,7 +1229,7 @@ test("Admins can create a writerInvite, which can add a writer", () => {
|
||||
const revelation = Crypto.seal({
|
||||
message: readKey,
|
||||
from: admin.currentSealerSecret(),
|
||||
to: admin.currentSealerID()._unsafeUnwrap(),
|
||||
to: admin.currentSealerID(),
|
||||
nOnceMaterial: {
|
||||
in: groupCore.id,
|
||||
tx: groupCore.nextTransactionID(),
|
||||
@@ -1332,7 +1332,7 @@ test("Admins can create a readerInvite, which can add a reader", () => {
|
||||
const revelation = Crypto.seal({
|
||||
message: readKey,
|
||||
from: admin.currentSealerSecret(),
|
||||
to: admin.currentSealerID()._unsafeUnwrap(),
|
||||
to: admin.currentSealerID(),
|
||||
nOnceMaterial: {
|
||||
in: groupCore.id,
|
||||
tx: groupCore.nextTransactionID(),
|
||||
@@ -1425,7 +1425,7 @@ test("WriterInvites can not invite admins", () => {
|
||||
const revelation = Crypto.seal({
|
||||
message: readKey,
|
||||
from: admin.currentSealerSecret(),
|
||||
to: admin.currentSealerID()._unsafeUnwrap(),
|
||||
to: admin.currentSealerID(),
|
||||
nOnceMaterial: {
|
||||
in: groupCore.id,
|
||||
tx: groupCore.nextTransactionID(),
|
||||
@@ -1479,7 +1479,7 @@ test("ReaderInvites can not invite admins", () => {
|
||||
const revelation = Crypto.seal({
|
||||
message: readKey,
|
||||
from: admin.currentSealerSecret(),
|
||||
to: admin.currentSealerID()._unsafeUnwrap(),
|
||||
to: admin.currentSealerID(),
|
||||
nOnceMaterial: {
|
||||
in: groupCore.id,
|
||||
tx: groupCore.nextTransactionID(),
|
||||
@@ -1533,7 +1533,7 @@ test("ReaderInvites can not invite writers", () => {
|
||||
const revelation = Crypto.seal({
|
||||
message: readKey,
|
||||
from: admin.currentSealerSecret(),
|
||||
to: admin.currentSealerID()._unsafeUnwrap(),
|
||||
to: admin.currentSealerID(),
|
||||
nOnceMaterial: {
|
||||
in: groupCore.id,
|
||||
tx: groupCore.nextTransactionID(),
|
||||
@@ -1587,7 +1587,7 @@ test("WriteOnlyInvites can not invite writers", () => {
|
||||
const revelation = Crypto.seal({
|
||||
message: readKey,
|
||||
from: admin.currentSealerSecret(),
|
||||
to: admin.currentSealerID()._unsafeUnwrap(),
|
||||
to: admin.currentSealerID(),
|
||||
nOnceMaterial: {
|
||||
in: groupCore.id,
|
||||
tx: groupCore.nextTransactionID(),
|
||||
@@ -1641,7 +1641,7 @@ test("WriteOnlyInvites can not invite admins", () => {
|
||||
const revelation = Crypto.seal({
|
||||
message: readKey,
|
||||
from: admin.currentSealerSecret(),
|
||||
to: admin.currentSealerID()._unsafeUnwrap(),
|
||||
to: admin.currentSealerID(),
|
||||
nOnceMaterial: {
|
||||
in: groupCore.id,
|
||||
tx: groupCore.nextTransactionID(),
|
||||
@@ -1695,7 +1695,7 @@ test("WriteOnlyInvites can invite writeOnly", () => {
|
||||
const revelation = Crypto.seal({
|
||||
message: readKey,
|
||||
from: admin.currentSealerSecret(),
|
||||
to: admin.currentSealerID()._unsafeUnwrap(),
|
||||
to: admin.currentSealerID(),
|
||||
nOnceMaterial: {
|
||||
in: groupCore.id,
|
||||
tx: groupCore.nextTransactionID(),
|
||||
@@ -1749,7 +1749,7 @@ test("WriteOnlyInvites can set writeKeys", () => {
|
||||
const revelation = Crypto.seal({
|
||||
message: readKey,
|
||||
from: admin.currentSealerSecret(),
|
||||
to: admin.currentSealerID()._unsafeUnwrap(),
|
||||
to: admin.currentSealerID(),
|
||||
nOnceMaterial: {
|
||||
in: groupCore.id,
|
||||
tx: groupCore.nextTransactionID(),
|
||||
@@ -1800,7 +1800,7 @@ test("Invites can't override key revelations", () => {
|
||||
const revelation = Crypto.seal({
|
||||
message: readKey,
|
||||
from: admin.currentSealerSecret(),
|
||||
to: admin.currentSealerID()._unsafeUnwrap(),
|
||||
to: admin.currentSealerID(),
|
||||
nOnceMaterial: {
|
||||
in: groupCore.id,
|
||||
tx: groupCore.nextTransactionID(),
|
||||
@@ -1855,7 +1855,7 @@ test("WriteOnlyInvites can't override writeKeys", () => {
|
||||
const revelation = Crypto.seal({
|
||||
message: readKey,
|
||||
from: admin.currentSealerSecret(),
|
||||
to: admin.currentSealerID()._unsafeUnwrap(),
|
||||
to: admin.currentSealerID(),
|
||||
nOnceMaterial: {
|
||||
in: groupCore.id,
|
||||
tx: groupCore.nextTransactionID(),
|
||||
@@ -1929,7 +1929,7 @@ test("Can give read permission to 'everyone'", () => {
|
||||
childObject
|
||||
.testWithDifferentAccount(
|
||||
newAccount,
|
||||
Crypto.newRandomSessionID(newAccount.currentAgentID()._unsafeUnwrap()),
|
||||
Crypto.newRandomSessionID(newAccount.currentAgentID()),
|
||||
)
|
||||
.getCurrentContent(),
|
||||
);
|
||||
@@ -1955,7 +1955,7 @@ test("Can give read permissions to 'everyone' (high-level)", async () => {
|
||||
childObject.core
|
||||
.testWithDifferentAccount(
|
||||
new ControlledAgent(Crypto.newRandomAgentSecret(), Crypto),
|
||||
Crypto.newRandomSessionID(newAccount.currentAgentID()._unsafeUnwrap()),
|
||||
Crypto.newRandomSessionID(newAccount.currentAgentID()),
|
||||
)
|
||||
.getCurrentContent(),
|
||||
);
|
||||
@@ -1993,7 +1993,7 @@ test("Can give write permission to 'everyone'", async () => {
|
||||
childObject
|
||||
.testWithDifferentAccount(
|
||||
newAccount,
|
||||
Crypto.newRandomSessionID(newAccount.currentAgentID()._unsafeUnwrap()),
|
||||
Crypto.newRandomSessionID(newAccount.currentAgentID()),
|
||||
)
|
||||
.getCurrentContent(),
|
||||
);
|
||||
@@ -2025,7 +2025,7 @@ test("Can give write permissions to 'everyone' (high-level)", async () => {
|
||||
childObject.core
|
||||
.testWithDifferentAccount(
|
||||
newAccount,
|
||||
Crypto.newRandomSessionID(newAccount.currentAgentID()._unsafeUnwrap()),
|
||||
Crypto.newRandomSessionID(newAccount.currentAgentID()),
|
||||
)
|
||||
.getCurrentContent(),
|
||||
);
|
||||
@@ -2087,7 +2087,7 @@ test("Writers, readers and invitees can not set parent extensions", () => {
|
||||
group.core
|
||||
.testWithDifferentAccount(
|
||||
adminInvite,
|
||||
Crypto.newRandomSessionID(adminInvite.currentAgentID()._unsafeUnwrap()),
|
||||
Crypto.newRandomSessionID(adminInvite.currentAgentID()),
|
||||
)
|
||||
.getCurrentContent(),
|
||||
);
|
||||
@@ -2099,9 +2099,7 @@ test("Writers, readers and invitees can not set parent extensions", () => {
|
||||
group.core
|
||||
.testWithDifferentAccount(
|
||||
writerInvite,
|
||||
Crypto.newRandomSessionID(
|
||||
writerInvite.currentAgentID()._unsafeUnwrap(),
|
||||
),
|
||||
Crypto.newRandomSessionID(writerInvite.currentAgentID()),
|
||||
)
|
||||
.getCurrentContent(),
|
||||
);
|
||||
@@ -2113,9 +2111,7 @@ test("Writers, readers and invitees can not set parent extensions", () => {
|
||||
group.core
|
||||
.testWithDifferentAccount(
|
||||
readerInvite,
|
||||
Crypto.newRandomSessionID(
|
||||
readerInvite.currentAgentID()._unsafeUnwrap(),
|
||||
),
|
||||
Crypto.newRandomSessionID(readerInvite.currentAgentID()),
|
||||
)
|
||||
.getCurrentContent(),
|
||||
);
|
||||
@@ -2209,7 +2205,7 @@ test("Invitees can not set child extensions", () => {
|
||||
group.core
|
||||
.testWithDifferentAccount(
|
||||
adminInvite,
|
||||
Crypto.newRandomSessionID(adminInvite.currentAgentID()._unsafeUnwrap()),
|
||||
Crypto.newRandomSessionID(adminInvite.currentAgentID()),
|
||||
)
|
||||
.getCurrentContent(),
|
||||
);
|
||||
@@ -2221,9 +2217,7 @@ test("Invitees can not set child extensions", () => {
|
||||
group.core
|
||||
.testWithDifferentAccount(
|
||||
writerInvite,
|
||||
Crypto.newRandomSessionID(
|
||||
writerInvite.currentAgentID()._unsafeUnwrap(),
|
||||
),
|
||||
Crypto.newRandomSessionID(writerInvite.currentAgentID()),
|
||||
)
|
||||
.getCurrentContent(),
|
||||
);
|
||||
@@ -2235,9 +2229,7 @@ test("Invitees can not set child extensions", () => {
|
||||
group.core
|
||||
.testWithDifferentAccount(
|
||||
readerInvite,
|
||||
Crypto.newRandomSessionID(
|
||||
readerInvite.currentAgentID()._unsafeUnwrap(),
|
||||
),
|
||||
Crypto.newRandomSessionID(readerInvite.currentAgentID()),
|
||||
)
|
||||
.getCurrentContent(),
|
||||
);
|
||||
@@ -2422,7 +2414,7 @@ test("Writers, readers and invites can't reveal parent read keys to child groups
|
||||
group.core
|
||||
.testWithDifferentAccount(
|
||||
adminInvite,
|
||||
Crypto.newRandomSessionID(adminInvite.currentAgentID()._unsafeUnwrap()),
|
||||
Crypto.newRandomSessionID(adminInvite.currentAgentID()),
|
||||
)
|
||||
.getCurrentContent(),
|
||||
);
|
||||
@@ -2440,9 +2432,7 @@ test("Writers, readers and invites can't reveal parent read keys to child groups
|
||||
group.core
|
||||
.testWithDifferentAccount(
|
||||
writerInvite,
|
||||
Crypto.newRandomSessionID(
|
||||
writerInvite.currentAgentID()._unsafeUnwrap(),
|
||||
),
|
||||
Crypto.newRandomSessionID(writerInvite.currentAgentID()),
|
||||
)
|
||||
.getCurrentContent(),
|
||||
);
|
||||
@@ -2460,9 +2450,7 @@ test("Writers, readers and invites can't reveal parent read keys to child groups
|
||||
group.core
|
||||
.testWithDifferentAccount(
|
||||
readerInvite,
|
||||
Crypto.newRandomSessionID(
|
||||
readerInvite.currentAgentID()._unsafeUnwrap(),
|
||||
),
|
||||
Crypto.newRandomSessionID(readerInvite.currentAgentID()),
|
||||
)
|
||||
.getCurrentContent(),
|
||||
);
|
||||
@@ -2941,7 +2929,7 @@ test("Can revoke read permission from 'everyone'", async () => {
|
||||
childObject.core
|
||||
.testWithDifferentAccount(
|
||||
newAccount,
|
||||
Crypto.newRandomSessionID(newAccount.currentAgentID()._unsafeUnwrap()),
|
||||
Crypto.newRandomSessionID(newAccount.currentAgentID()),
|
||||
)
|
||||
.getCurrentContent(),
|
||||
);
|
||||
@@ -2963,7 +2951,7 @@ test("Can revoke read permission from 'everyone'", async () => {
|
||||
childObject.core
|
||||
.testWithDifferentAccount(
|
||||
newAccount2,
|
||||
Crypto.newRandomSessionID(newAccount2.currentAgentID()._unsafeUnwrap()),
|
||||
Crypto.newRandomSessionID(newAccount2.currentAgentID()),
|
||||
)
|
||||
.getCurrentContent(),
|
||||
);
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# create-jazz-app
|
||||
|
||||
## 0.1.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- e2e0af3: Handle workspace devDependencies of cloned apps when using create-jazz-app
|
||||
|
||||
## 0.1.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"types": "src/index.ts",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"version": "0.1.11",
|
||||
"version": "0.1.12",
|
||||
"bin": {
|
||||
"create-jazz-app": "./dist/index.js"
|
||||
},
|
||||
|
||||
@@ -136,19 +136,30 @@ async function scaffoldProject({
|
||||
const packageJsonPath = `${projectName}/package.json`;
|
||||
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
||||
|
||||
// Replace workspace: dependencies with latest
|
||||
if (packageJson.dependencies) {
|
||||
const latestVersions = await getLatestPackageVersions(
|
||||
packageJson.dependencies,
|
||||
);
|
||||
// Helper function to update workspace dependencies
|
||||
async function updateWorkspaceDependencies(
|
||||
dependencyType: "dependencies" | "devDependencies",
|
||||
) {
|
||||
if (packageJson[dependencyType]) {
|
||||
const latestVersions = await getLatestPackageVersions(
|
||||
packageJson[dependencyType],
|
||||
);
|
||||
|
||||
Object.entries(packageJson.dependencies).forEach(([pkg, version]) => {
|
||||
if (typeof version === "string" && version.includes("workspace:")) {
|
||||
packageJson.dependencies[pkg] = latestVersions[pkg];
|
||||
}
|
||||
});
|
||||
Object.entries(packageJson[dependencyType]).forEach(
|
||||
([pkg, version]) => {
|
||||
if (typeof version === "string" && version.includes("workspace:")) {
|
||||
packageJson[dependencyType][pkg] = latestVersions[pkg];
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all([
|
||||
updateWorkspaceDependencies("dependencies"),
|
||||
updateWorkspaceDependencies("devDependencies"),
|
||||
]);
|
||||
|
||||
packageJson.name = projectName;
|
||||
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
||||
depsSpinner.succeed(chalk.green("Dependencies updated"));
|
||||
|
||||
946
packages/cursor-docs/.cursor/docs/1_jazz_docs.md
Normal file
946
packages/cursor-docs/.cursor/docs/1_jazz_docs.md
Normal file
@@ -0,0 +1,946 @@
|
||||
---
|
||||
|
||||
## **CoMap Overview**
|
||||
**CoMap** is a collaborative object mapping system from `jazz-tools`, mapping string keys to values.
|
||||
|
||||
### **1. Basic Definition**
|
||||
```typescript
|
||||
import { CoMap, co } from "jazz-tools";
|
||||
class Person extends CoMap {
|
||||
name = co.string;
|
||||
age = co.number;
|
||||
isActive = co.boolean;
|
||||
}
|
||||
```
|
||||
|
||||
### **2. Field Types**
|
||||
- **Basic:** `co.string`, `co.number`, `co.boolean`
|
||||
- **Optional:** `co.optional.string`, `co.optional.number`
|
||||
- **Literals (Enums):** `co.literal("draft", "published", "archived")`
|
||||
- **Dates:** `co.Date`
|
||||
- **Custom Encoded:**
|
||||
```typescript
|
||||
customField = co.encoded({
|
||||
encode: (v: string) => v.toUpperCase(),
|
||||
decode: (v: unknown) => String(v).toLowerCase()
|
||||
});
|
||||
```
|
||||
|
||||
### **3. References to Other CoMaps**
|
||||
```typescript
|
||||
class Comment extends CoMap {
|
||||
text = co.string;
|
||||
createdAt = co.Date;
|
||||
}
|
||||
class Post extends CoMap {
|
||||
title = co.string;
|
||||
content = co.string;
|
||||
mainComment = co.ref(Comment);
|
||||
pinnedComment = co.optional.ref(Comment);
|
||||
}
|
||||
```
|
||||
|
||||
### **4. Lists with CoList**
|
||||
```typescript
|
||||
import { CoList, CoMap, co } from "jazz-tools";
|
||||
class Task extends CoMap {
|
||||
title = co.string;
|
||||
completed = co.boolean;
|
||||
}
|
||||
class TaskList extends CoList.Of(co.ref(Task)) {}
|
||||
class Project extends CoMap {
|
||||
name = co.string;
|
||||
tasks = co.ref(TaskList);
|
||||
}
|
||||
```
|
||||
|
||||
### **5. Validation & Custom Methods**
|
||||
```typescript
|
||||
class DraftPost extends CoMap {
|
||||
title = co.optional.string;
|
||||
content = co.optional.string;
|
||||
validate() {
|
||||
const errors: string[] = [];
|
||||
if (!this.title) errors.push("Title is required");
|
||||
if (!this.content) errors.push("Content is required");
|
||||
return { errors };
|
||||
}
|
||||
get summary() { return this.content?.slice(0, 100) + "..."; }
|
||||
}
|
||||
```
|
||||
|
||||
### **Real-World Examples**
|
||||
- **Chat Schema**
|
||||
```typescript
|
||||
class Message extends CoMap { text = co.string; image = co.optional.ref(ImageDefinition); }
|
||||
class Chat extends CoList.Of(co.ref(Message)) {}
|
||||
```
|
||||
- **Organization Schema**
|
||||
```typescript
|
||||
class Project extends CoMap { name = co.string; }
|
||||
class ListOfProjects extends CoList.Of(co.ref(Project)) {}
|
||||
class Organization extends CoMap {
|
||||
name = co.string;
|
||||
projects = co.ref(ListOfProjects);
|
||||
}
|
||||
```
|
||||
- **Issue Tracking**
|
||||
```typescript
|
||||
class Issue extends CoMap {
|
||||
title = co.string;
|
||||
description = co.string;
|
||||
estimate = co.number;
|
||||
status? = co.literal("backlog", "in progress", "done");
|
||||
}
|
||||
class ListOfIssues extends CoList.Of(co.ref(Issue)) {}
|
||||
class Project extends CoMap {
|
||||
name = co.string;
|
||||
issues = co.ref(ListOfIssues);
|
||||
}
|
||||
```
|
||||
|
||||
### **Testing Example**
|
||||
```typescript
|
||||
class TestMap extends CoMap {
|
||||
color = co.string;
|
||||
_height = co.number;
|
||||
birthday = co.Date;
|
||||
name? = co.string;
|
||||
nullable = co.optional.encoded<string | undefined>({
|
||||
encode: (v: string | undefined) => v || null,
|
||||
decode: (v: unknown) => (v as string) || undefined,
|
||||
});
|
||||
optionalDate = co.optional.encoded(Encoders.Date);
|
||||
get roughColor() { return this.color + "ish"; }
|
||||
}
|
||||
```
|
||||
|
||||
### **Key Takeaways**
|
||||
- Extend **CoMap** for schemas.
|
||||
- Use `co.ref()` for references, `co.optional` for optional fields.
|
||||
- Use `CoList.Of()` for collections.
|
||||
- Fields auto-sync across clients.
|
||||
- Add computed properties & validation methods.
|
||||
|
||||
---
|
||||
|
||||
## **CoList Overview**
|
||||
**CoList** is a collaborative array in `jazz-tools`.
|
||||
|
||||
### **1. Basic Definition**
|
||||
```typescript
|
||||
import { CoList, co } from "jazz-tools";
|
||||
class ColorList extends CoList.Of(co.string) {}
|
||||
class NumberList extends CoList.Of(co.number) {}
|
||||
class BooleanList extends CoList.Of(co.boolean) {}
|
||||
```
|
||||
|
||||
### **2. Lists of CoMaps**
|
||||
```typescript
|
||||
import { CoList, CoMap, co } from "jazz-tools";
|
||||
class Task extends CoMap { title = co.string; completed = co.boolean; }
|
||||
class ListOfTasks extends CoList.Of(co.ref(Task)) {}
|
||||
```
|
||||
|
||||
### **3. CoList Operations**
|
||||
```typescript
|
||||
const taskList = ListOfTasks.create([], { owner: me });
|
||||
taskList.push(Task.create({ title: "New task", completed: false }, { owner: me }));
|
||||
const firstTask = taskList[0];
|
||||
taskList.filter(task => !task.completed);
|
||||
taskList.splice(1, 1);
|
||||
```
|
||||
|
||||
### **4. Nested Lists**
|
||||
```typescript
|
||||
class Comment extends CoMap { text = co.string; createdAt = co.Date; }
|
||||
class ListOfComments extends CoList.Of(co.ref(Comment)) {}
|
||||
class Post extends CoMap {
|
||||
title = co.string;
|
||||
content = co.string;
|
||||
comments = co.ref(ListOfComments);
|
||||
}
|
||||
class ListOfPosts extends CoList.Of(co.ref(Post)) {}
|
||||
```
|
||||
|
||||
### **Real-World Examples**
|
||||
- **Chat Schema**
|
||||
```typescript
|
||||
class Message extends CoMap { text = co.string; image = co.optional.ref(ImageDefinition); }
|
||||
class Chat extends CoList.Of(co.ref(Message)) {}
|
||||
```
|
||||
- **Todo App Schema**
|
||||
```typescript
|
||||
class Task extends CoMap { done = co.boolean; text = co.string; }
|
||||
class ListOfTasks extends CoList.Of(co.ref(Task)) {}
|
||||
```
|
||||
- **Organization Schema**
|
||||
```typescript
|
||||
class Project extends CoMap { name = co.string; }
|
||||
class ListOfProjects extends CoList.Of(co.ref(Project)) {}
|
||||
class Organization extends CoMap {
|
||||
name = co.string;
|
||||
projects = co.ref(ListOfProjects);
|
||||
}
|
||||
```
|
||||
|
||||
### **5. Advanced Features**
|
||||
```typescript
|
||||
class TaskList extends CoList.Of(co.ref(Task)) {
|
||||
getCompletedTasks() { return this.filter(task => task.completed); }
|
||||
getPendingTasks() { return this.filter(task => !task.completed); }
|
||||
}
|
||||
```
|
||||
|
||||
### **Key Takeaways**
|
||||
- `CoList.Of()` for list definitions.
|
||||
- `co.ref()` for CoMap references.
|
||||
- Acts like arrays with real-time sync.
|
||||
- Supports custom methods & nested lists.
|
||||
|
||||
---
|
||||
|
||||
## **CoFeed Overview**
|
||||
**CoFeed** is an append-only event stream, ideal for time-ordered data.
|
||||
|
||||
### **1. Basic Definition**
|
||||
```typescript
|
||||
import { CoFeed, co } from "jazz-tools";
|
||||
class ActivityFeed extends CoFeed.Of(co.string) {}
|
||||
class MetricsFeed extends CoFeed.Of(co.number) {}
|
||||
```
|
||||
|
||||
### **2. Feeds with Complex Types**
|
||||
```typescript
|
||||
interface LogEvent {
|
||||
timestamp: number;
|
||||
level: "info" | "warn" | "error";
|
||||
message: string;
|
||||
}
|
||||
class LogFeed extends CoFeed.Of(co.json<LogEvent>()) {}
|
||||
```
|
||||
|
||||
### **3. Pet Reactions Example**
|
||||
```typescript
|
||||
export const ReactionTypes = ["aww","love","haha","wow","tiny","chonkers"] as const;
|
||||
export class PetReactions extends CoFeed.Of(co.json<ReactionType>()) {}
|
||||
```
|
||||
|
||||
### **4. Working with CoFeeds**
|
||||
```typescript
|
||||
const reactions = PetReactions.create({ owner: me });
|
||||
reactions.post("love");
|
||||
reactions.subscribe(feedId, me, {}, (feed) => console.log(feed.latest()));
|
||||
```
|
||||
|
||||
### **5. Common Use Cases**
|
||||
- **Activity Streams**
|
||||
```typescript
|
||||
class ActivityStream extends CoFeed.Of(co.json<{ type:"comment"|"like"|"share";userId:string;timestamp:number;}>) {}
|
||||
```
|
||||
- **Chat Messages**
|
||||
```typescript
|
||||
class ChatFeed extends CoFeed.Of(co.json<{ type:"message"|"join"|"leave";userId:string; content?:string;timestamp:number;}>) {}
|
||||
```
|
||||
- **Audit Logs**
|
||||
```typescript
|
||||
class AuditLog extends CoFeed.Of(co.json<{ action:string; user:string; details:Record<string,unknown>;timestamp:number;}>) {}
|
||||
```
|
||||
|
||||
### **6. Differences: CoFeed vs. CoList**
|
||||
| Feature | CoFeed (append-only) | CoList (mutable) |
|
||||
|----------|----------------------|------------------|
|
||||
| Order | Time-ordered | Arbitrary |
|
||||
| Use Case | Logs, streams | Collections |
|
||||
|
||||
### **Key Takeaways**
|
||||
- **CoFeed**: event logs, activity streams, append-only.
|
||||
- **CoList**: modifiable lists.
|
||||
- Real-time updates, easy to subscribe.
|
||||
|
||||
---
|
||||
|
||||
## **SchemaUnion Overview**
|
||||
**SchemaUnion** handles runtime-discriminated union types of `CoMap` instances.
|
||||
|
||||
### **1. Basic Definition**
|
||||
```typescript
|
||||
import { SchemaUnion, CoMap, co } from "jazz-tools";
|
||||
class BaseShape extends CoMap { type = co.string; }
|
||||
class Circle extends BaseShape { type = co.literal("circle"); radius = co.number; }
|
||||
class Rectangle extends BaseShape { type = co.literal("rectangle"); width = co.number; height = co.number; }
|
||||
const Shape = SchemaUnion.Of<BaseShape>((raw) => {
|
||||
switch (raw.get("type")) {
|
||||
case "circle": return Circle;
|
||||
case "rectangle": return Rectangle;
|
||||
default: throw new Error("Unknown shape");
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### **2. Nested Discriminators**
|
||||
```typescript
|
||||
class BaseButton extends CoMap { type = co.literal("button"); variant = co.string; }
|
||||
class PrimaryButton extends BaseButton { variant = co.literal("primary"); label = co.string; size = co.literal("small","medium","large"); }
|
||||
class SecondaryButton extends BaseButton { variant = co.literal("secondary"); label = co.string; outline = co.boolean; }
|
||||
const Button = SchemaUnion.Of<BaseButton>((raw) => {
|
||||
switch (raw.get("variant")) {
|
||||
case "primary": return PrimaryButton;
|
||||
case "secondary": return SecondaryButton;
|
||||
default: throw new Error("Unknown variant");
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### **3. Using SchemaUnion with CoLists**
|
||||
```typescript
|
||||
class BaseWidget extends CoMap { type = co.string; }
|
||||
class ButtonWidget extends BaseWidget { type = co.literal("button"); label = co.string; }
|
||||
class SliderWidget extends BaseWidget { type = co.literal("slider"); min = co.number; max = co.number; }
|
||||
const Widget = SchemaUnion.Of<BaseWidget>((raw) => {
|
||||
switch (raw.get("type")) {
|
||||
case "button": return ButtonWidget;
|
||||
case "slider": return SliderWidget;
|
||||
default: throw new Error("Unknown widget");
|
||||
}
|
||||
});
|
||||
class WidgetList extends CoList.Of(co.ref(Widget)) {}
|
||||
```
|
||||
|
||||
### **4. Working with SchemaUnion Instances**
|
||||
```typescript
|
||||
const button = ButtonWidget.create({ type:"button", label:"Click me" }, { owner: me });
|
||||
const widget = await loadCoValue(Widget, widgetId, me, {});
|
||||
if (widget instanceof ButtonWidget) console.log(widget.label);
|
||||
if (widget instanceof SliderWidget) console.log(widget.min, widget.max);
|
||||
```
|
||||
|
||||
### **5. Validation Example**
|
||||
```typescript
|
||||
class BaseFormField extends CoMap {
|
||||
type = co.string;
|
||||
label = co.string;
|
||||
required = co.boolean;
|
||||
}
|
||||
class TextField extends BaseFormField {
|
||||
type = co.literal("text");
|
||||
minLength = co.optional.number;
|
||||
maxLength = co.optional.number;
|
||||
validate(value: string){/* ... */}
|
||||
}
|
||||
class NumberField extends BaseFormField {
|
||||
type = co.literal("number");
|
||||
min = co.optional.number;
|
||||
max = co.optional.number;
|
||||
validate(value: number){/* ... */}
|
||||
}
|
||||
const FormField = SchemaUnion.Of<BaseFormField>((raw) => {
|
||||
switch (raw.get("type")) {
|
||||
case "text": return TextField;
|
||||
case "number": return NumberField;
|
||||
default: throw new Error("Unknown type");
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### **Key Takeaways**
|
||||
- **SchemaUnion** = polymorphic CoMaps.
|
||||
- Use `co.literal()` for discriminators.
|
||||
- `instanceof` for type-narrowing.
|
||||
- Great for complex forms & dynamic components.
|
||||
|
||||
---
|
||||
|
||||
## **Groups, Accounts, Owners, Roles & Permissions in Jazz**
|
||||
|
||||
### **1. Ownership & Groups**
|
||||
Every `CoValue` has an owner (an `Account` or `Group`):
|
||||
```typescript
|
||||
import { Account, Group, CoMap, co } from "jazz-tools";
|
||||
const privateDoc = Document.create({ title:"Private" }, { owner: me });
|
||||
const group = Group.create({ owner: me });
|
||||
const sharedDoc = Document.create({ title:"Shared" }, { owner: group });
|
||||
```
|
||||
|
||||
### **2. Roles & Permissions**
|
||||
Built-in roles: `"admin"`, `"writer"`, `"reader"`, `"readerInvite"`, `"writerInvite"`.
|
||||
```typescript
|
||||
group.addMember(bob, "writer");
|
||||
group.addMember(alice, "reader");
|
||||
group.addMember("everyone","reader");
|
||||
```
|
||||
|
||||
### **3. Organizations & Memberships**
|
||||
```typescript
|
||||
import { Account, CoMap, CoList, Group, co } from "jazz-tools";
|
||||
class Project extends CoMap { name = co.string; description = co.string; }
|
||||
class Organization extends CoMap {
|
||||
name = co.string;
|
||||
projects = co.ref(CoList.Of(co.ref(Project)));
|
||||
static create(name: string, owner: Account) {
|
||||
const group = Group.create({ owner });
|
||||
return super.create({ name, projects: CoList.Of(co.ref(Project)).create([], { owner: group }) }, { owner: group });
|
||||
}
|
||||
addMember(account: Account, role: "admin"|"writer"|"reader") {
|
||||
this._owner.castAs(Group).addMember(account, role);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### **4. Account Root & Migration Pattern**
|
||||
```typescript
|
||||
class TodoAccountRoot extends CoMap {
|
||||
projects = co.ref(ListOfProjects);
|
||||
}
|
||||
export class UserProfile extends Profile { someProperty = co.string; }
|
||||
class TodoAccount extends Account {
|
||||
root = co.ref(TodoAccountRoot);
|
||||
profile = co.ref(UserProfile);
|
||||
migrate() {
|
||||
if (!this._refs.root) {
|
||||
this.root = TodoAccountRoot.create({ projects: ListOfProjects.create([], { owner: this }) }, { owner: this });
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### **5. Public Sharing Example**
|
||||
```typescript
|
||||
class SharedFile extends CoMap {
|
||||
name = co.string;
|
||||
file = co.ref(FileStream);
|
||||
createdAt = co.Date;
|
||||
size = co.number;
|
||||
}
|
||||
class FileShareAccountRoot extends CoMap {
|
||||
type = co.string;
|
||||
sharedFiles = co.ref(ListOfSharedFiles);
|
||||
publicGroup = co.ref(Group);
|
||||
}
|
||||
export class UserProfile extends Profile { someProperty = co.string; }
|
||||
class FileShareAccount extends Account {
|
||||
root = co.ref(FileShareAccountRoot);
|
||||
profile = co.ref(UserProfile);
|
||||
async migrate() {
|
||||
await this._refs.root?.load();
|
||||
if (!this.root || this.root.type !== "file-share-account") {
|
||||
const publicGroup = Group.create({ owner: this });
|
||||
publicGroup.addMember("everyone","reader");
|
||||
this.root = FileShareAccountRoot.create({
|
||||
type:"file-share-account",
|
||||
sharedFiles: ListOfSharedFiles.create([], { owner: publicGroup }),
|
||||
publicGroup
|
||||
}, { owner: this });
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### **6. Group Extensions**
|
||||
```typescript
|
||||
const parentGroup = Group.create({ owner: me });
|
||||
parentGroup.addMember(bob, "reader");
|
||||
const childGroup = Group.create({ owner: me });
|
||||
childGroup.extend(parentGroup);
|
||||
const doc = Document.create({ title:"Inherited Access" }, { owner: childGroup });
|
||||
```
|
||||
|
||||
### **7. Checking Permissions**
|
||||
```typescript
|
||||
const group = document._owner.castAs(Group);
|
||||
const myRole = group.myRole();
|
||||
const hasWriteAccess = myRole === "admin" || myRole === "writer";
|
||||
```
|
||||
|
||||
### **8. Invitation Pattern**
|
||||
```typescript
|
||||
class TeamInvite extends CoMap {
|
||||
email = co.string;
|
||||
role = co.literal("admin","writer","reader");
|
||||
accepted = co.boolean;
|
||||
}
|
||||
class Team extends CoMap {
|
||||
invites = co.ref(CoList.Of(co.ref(TeamInvite)));
|
||||
async inviteMember(email: string, role: "admin"|"writer"|"reader") {
|
||||
const group = this._owner.castAs(Group);
|
||||
const invite = TeamInvite.create({ email, role, accepted:false }, { owner: group });
|
||||
this.invites.push(invite);
|
||||
group.addMember(email, (role+"Invite") as const);
|
||||
}
|
||||
acceptInvite(account: Account) {
|
||||
const group = this._owner.castAs(Group);
|
||||
const invite = this.invites.find(i => i.email === account.email);
|
||||
if (invite) {
|
||||
invite.accepted = true;
|
||||
group.addMember(account, invite.role);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### **Key Takeaways**
|
||||
- Every `CoValue` has an owner (Account or Group).
|
||||
- Groups enable sharing/role-based access (`"admin"`, `"writer"`, `"reader"`, etc.).
|
||||
- Groups can inherit permissions.
|
||||
- Use account roots for private per-user data.
|
||||
- Public sharing via `"everyone"` role.
|
||||
- Invites allow controlled membership.
|
||||
|
||||
---
|
||||
|
||||
## **Inbox Pattern in Jazz**
|
||||
Enables message exchange between accounts using `CoMap`, `CoList`, `Group`.
|
||||
|
||||
### **1. Basic Inbox Setup**
|
||||
```typescript
|
||||
import { CoMap, co, Group } from "jazz-tools";
|
||||
class Message extends CoMap {
|
||||
text = co.string;
|
||||
createdAt = co.Date;
|
||||
read = co.boolean;
|
||||
}
|
||||
class ChatInbox extends CoMap {
|
||||
messages = co.ref(CoList.Of(co.ref(Message)));
|
||||
lastReadAt = co.Date;
|
||||
}
|
||||
```
|
||||
|
||||
### **2. Sending Messages**
|
||||
```typescript
|
||||
async function sendMessage(sender: Account, receiverId: ID<Account>, text: string) {
|
||||
const message = Message.create(
|
||||
{ text, createdAt:new Date(), read:false },
|
||||
{ owner: Group.create({ owner: sender }) }
|
||||
);
|
||||
const inboxSender = await InboxSender.load(receiverId, sender);
|
||||
inboxSender.sendMessage(message);
|
||||
}
|
||||
```
|
||||
|
||||
### **3. Receiving Messages**
|
||||
```typescript
|
||||
async function setupInbox(receiver: Account) {
|
||||
const inbox = await Inbox.load(receiver);
|
||||
return inbox.subscribe(Message,(message,senderId)=>console.log("New:",message.text));
|
||||
}
|
||||
```
|
||||
|
||||
### **4. Chat Application Example**
|
||||
```typescript
|
||||
class ChatMessage extends CoMap { text = co.string; createdAt=co.Date; read=co.boolean; }
|
||||
class ChatThread extends CoMap {
|
||||
participants = co.json<string[]>();
|
||||
messages = co.ref(CoList.Of(co.ref(ChatMessage)));
|
||||
lastReadAt = co.optional.Date;
|
||||
}
|
||||
class ChatRoot extends CoMap {
|
||||
threads = co.ref(CoList.Of(co.ref(ChatThread)));
|
||||
inbox = co.ref(Inbox);
|
||||
}
|
||||
export class UserProfile extends Profile { someProperty=co.string; }
|
||||
class ChatAccount extends Account {
|
||||
root = co.ref(ChatRoot);
|
||||
profile = co.ref(UserProfile);
|
||||
async migrate() {
|
||||
if(!this._refs.root) {
|
||||
const group = Group.create({ owner:this });
|
||||
this.root = ChatRoot.create({
|
||||
threads: CoList.Of(co.ref(ChatThread)).create([], { owner: group }),
|
||||
inbox: await Inbox.create(this)
|
||||
},{ owner:this });
|
||||
}
|
||||
}
|
||||
async sendMessage(to: ID<Account>, text:string) {
|
||||
const message = ChatMessage.create({ text, createdAt:new Date(), read:false },
|
||||
{ owner: Group.create({ owner:this }) });
|
||||
const inboxSender=await InboxSender.load(to,this);
|
||||
inboxSender.sendMessage(message);
|
||||
}
|
||||
async setupInboxListener() {
|
||||
const inbox = await Inbox.load(this);
|
||||
return inbox.subscribe(ChatMessage, async (message, senderId) => {
|
||||
const thread = await this.findOrCreateThread(senderId);
|
||||
thread.messages.push(message);
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### **5. Testing the Inbox Pattern**
|
||||
```typescript
|
||||
describe("Inbox", () => {
|
||||
it("should allow message exchange", async () => {
|
||||
const { clientAccount:sender, serverAccount:receiver } = await setupTwoNodes();
|
||||
const receiverInbox = await Inbox.load(receiver);
|
||||
const message = Message.create({ text:"Hello" },{ owner:Group.create({ owner:sender }) });
|
||||
const inboxSender = await InboxSender.load(receiver.id, sender);
|
||||
inboxSender.sendMessage(message);
|
||||
const receivedMessages: Message[] = [];
|
||||
let senderAccountID: unknown;
|
||||
const unsubscribe = receiverInbox.subscribe(Message, (msg, id) => {
|
||||
senderAccountID=id; receivedMessages.push(msg);
|
||||
});
|
||||
await waitFor(() => receivedMessages.length===1);
|
||||
expect(receivedMessages[0]?.text).toBe("Hello");
|
||||
expect(senderAccountID).toBe(sender.id);
|
||||
unsubscribe();
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### **6. Message Status Tracking**
|
||||
```typescript
|
||||
class MessageStatus extends CoMap {
|
||||
messageId = co.string;
|
||||
delivered = co.boolean;
|
||||
read = co.boolean;
|
||||
readAt = co.optional.Date;
|
||||
}
|
||||
class EnhancedMessage extends CoMap {
|
||||
text = co.string;
|
||||
createdAt = co.Date;
|
||||
status = co.ref(MessageStatus);
|
||||
}
|
||||
async function sendMessageWithStatus(sender: Account, receiverId: ID<Account>, text:string) {
|
||||
const group = Group.create({ owner:sender });
|
||||
const status = MessageStatus.create({ messageId:crypto.randomUUID(), delivered:false, read:false }, { owner:group });
|
||||
const message = EnhancedMessage.create({ text, createdAt:new Date(), status }, { owner:group });
|
||||
const inboxSender = await InboxSender.load(receiverId,sender);
|
||||
inboxSender.sendMessage(message);
|
||||
return message;
|
||||
}
|
||||
```
|
||||
|
||||
### **Key Takeaways**
|
||||
- Messages owned by a `Group` from the sender.
|
||||
- Use `InboxSender.load()` to send, `Inbox.load()` to receive.
|
||||
- Subscribe for real-time updates.
|
||||
- Append status tracking as needed.
|
||||
|
||||
---
|
||||
|
||||
## **Invite Pattern in Jazz**
|
||||
Sharing access to `CoValues` with other users via invites.
|
||||
|
||||
### **1. Creating & Handling Invites**
|
||||
```typescript
|
||||
import { CoMap, Group, co, createInviteLink } from "jazz-tools";
|
||||
class Project extends CoMap { name = co.string; members = co.ref(CoList.Of(co.ref(Member))); }
|
||||
const group = Group.create({ owner: me });
|
||||
const project = Project.create({ name:"New Project", members:CoList.Of(co.ref(Member)).create([],{owner:group}) }, { owner:group });
|
||||
const readerInvite = createInviteLink(project,"reader");
|
||||
const writerInvite = createInviteLink(project,"writer");
|
||||
const adminInvite = createInviteLink(project,"admin");
|
||||
```
|
||||
|
||||
### **2. Accepting Invites in UI**
|
||||
```typescript
|
||||
import { useAcceptInvite } from "jazz-react";
|
||||
useAcceptInvite({
|
||||
invitedObjectSchema:Project,
|
||||
onAccept:(projectId)=>navigate(`/projects/${projectId}`)
|
||||
});
|
||||
```
|
||||
|
||||
### **3. Organization Example**
|
||||
```typescript
|
||||
class Organization extends CoMap {
|
||||
name = co.string;
|
||||
projects = co.ref(ListOfProjects);
|
||||
createInvite(role:"reader"|"writer"|"admin"){ return createInviteLink(this,role); }
|
||||
}
|
||||
useAcceptInvite({
|
||||
invitedObjectSchema:Organization,
|
||||
onAccept:async(orgId)=>{/* ... */}
|
||||
});
|
||||
```
|
||||
|
||||
### **4. Value Hints in Invites**
|
||||
```typescript
|
||||
class Team extends CoMap {
|
||||
name = co.string;
|
||||
generateInvite(role:"reader"|"writer"|"admin"){ return createInviteLink(this, role, window.location.origin, "team"); }
|
||||
}
|
||||
useAcceptInvite({
|
||||
invitedObjectSchema:Team,
|
||||
forValueHint:"team",
|
||||
onAccept:(teamId)=>navigate(`/teams/${teamId}`)
|
||||
});
|
||||
```
|
||||
|
||||
### **5. Testing Invites**
|
||||
```typescript
|
||||
describe("Invite Links", () => {
|
||||
test("generate and parse invites", async () => {
|
||||
const inviteLink = createInviteLink(group, "writer","https://example.com","myGroup");
|
||||
const parsed = parseInviteLink(inviteLink);
|
||||
expect(parsed?.valueID).toBe(group.id);
|
||||
expect(parsed?.valueHint).toBe("myGroup");
|
||||
});
|
||||
test("accept invite", async () => {
|
||||
const newAccount = await createJazzTestAccount();
|
||||
const inviteLink = createInviteLink(group, "writer");
|
||||
const result = await consumeInviteLink({ inviteURL: inviteLink, as:newAccount, invitedObjectSchema:Group });
|
||||
expect(result?.valueID).toBe(group.id);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### **6. File Sharing with Invites**
|
||||
```typescript
|
||||
class SharedFile extends CoMap {
|
||||
name = co.string;
|
||||
sharedWith = co.ref(CoList.Of(co.ref(SharedWith)));
|
||||
}
|
||||
class SharedWith extends CoMap {
|
||||
email = co.string;
|
||||
role = co.literal("reader","writer");
|
||||
acceptedAt = co.optional.Date;
|
||||
}
|
||||
class FileShareAccount extends Account {
|
||||
async shareFile(file:SharedFile, email:string, role:"reader"|"writer") {
|
||||
const inviteLink = createInviteLink(file,role);
|
||||
file.sharedWith.push(SharedWith.create({ email, role, acceptedAt:null },{ owner:file._owner }));
|
||||
await sendInviteEmail(email, inviteLink);
|
||||
}
|
||||
}
|
||||
useAcceptInvite({
|
||||
invitedObjectSchema:SharedFile,
|
||||
onAccept:async(fileId)=>{
|
||||
const file = await SharedFile.load(fileId,{});
|
||||
const shareRecord = file.sharedWith.find(s=>s.email===currentUser.email);
|
||||
if(shareRecord) shareRecord.acceptedAt=new Date();
|
||||
navigate(`/files/${fileId}`);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### **Key Takeaways**
|
||||
- Use Groups for shared ownership.
|
||||
- `createInviteLink()` generates invites.
|
||||
- `useAcceptInvite()` handles acceptance.
|
||||
- Value hints (`forValueHint`) differentiate invite types.
|
||||
|
||||
---
|
||||
|
||||
## **CoValue Types & Patterns in Jazz**
|
||||
|
||||
### **1. CoMap**
|
||||
- Use for structured data with named fields.
|
||||
- Example:
|
||||
```typescript
|
||||
class UserProfile extends CoMap {
|
||||
name = co.string;
|
||||
email = co.string;
|
||||
avatar = co.ref(FileStream);
|
||||
preferences = co.json<{ theme:string; notifications:boolean }>();
|
||||
}
|
||||
class TagColors extends CoMap.Record(co.string) {}
|
||||
```
|
||||
|
||||
### **2. CoList**
|
||||
- Use for ordered, real-time collaborative arrays.
|
||||
```typescript
|
||||
class TodoList extends CoList.Of(co.ref(TodoItem)) {}
|
||||
class StringList extends CoList.Of(co.string) {}
|
||||
```
|
||||
|
||||
### **3. CoFeed**
|
||||
- Use for append-only event/log data.
|
||||
```typescript
|
||||
class UserActivity extends CoFeed.Of(co.json<{ type:string; timestamp:number; text?:string }>) {}
|
||||
```
|
||||
|
||||
### **4. SchemaUnion**
|
||||
- Use for polymorphic objects with a runtime discriminator.
|
||||
```typescript
|
||||
class BaseWidget extends CoMap { type=co.string; }
|
||||
class ButtonWidget extends BaseWidget { type=co.literal("button"); label=co.string; }
|
||||
const Widget=SchemaUnion.Of<BaseWidget>(raw=>raw.get("type")==="button"?ButtonWidget:null);
|
||||
```
|
||||
|
||||
### **5. Groups & Permissions**
|
||||
- Owner can be an Account or Group.
|
||||
- Roles: `"admin"|"writer"|"reader"|"readerInvite"|"writerInvite"`.
|
||||
```typescript
|
||||
const group=Group.create({owner:me});
|
||||
group.addMember("everyone","reader");
|
||||
```
|
||||
|
||||
### **6. Accounts**
|
||||
- Per-user data storage with migrations.
|
||||
```typescript
|
||||
class JazzAccount extends Account {
|
||||
root=co.ref(JazzAccountRoot);
|
||||
profile=co.ref(UserProfile);
|
||||
async migrate(){/*...*/}
|
||||
}
|
||||
```
|
||||
|
||||
### **7. Migrations**
|
||||
- Update/initialize user data on account creation/login.
|
||||
|
||||
### **8. Invites**
|
||||
- Role-based sharing through invite links.
|
||||
|
||||
### **Common Patterns**
|
||||
- **Account Root Pattern**: store user’s top-level data.
|
||||
- **Shared Document Pattern**: CoMap for doc, CoList for collaborators, CoFeed for history.
|
||||
- **Draft Pattern**: CoMap with partial fields, validation.
|
||||
- **Public Sharing**: set `Group.addMember("everyone","reader")`.
|
||||
|
||||
---
|
||||
|
||||
## **Examples**
|
||||
|
||||
1. **User Profile Storage (CoMap)**
|
||||
**JSON**:
|
||||
```json
|
||||
{
|
||||
"name":"John Doe","email":"john@example.com",
|
||||
"avatar":{"url":"...","size":"..."},
|
||||
"preferences":{"theme":"dark","notifications":true}
|
||||
}
|
||||
```
|
||||
**Jazz**:
|
||||
```typescript
|
||||
class UserProfile extends CoMap {
|
||||
name = co.string;
|
||||
email = co.string;
|
||||
avatar = co.ref(FileStream);
|
||||
preferences = co.json<{theme:string;notifications:boolean}>();
|
||||
}
|
||||
```
|
||||
|
||||
2. **To-Do List (CoList)**
|
||||
**JSON**:
|
||||
```json
|
||||
{"tasks":[{"id":1,"title":"Buy groceries","completed":false},{"id":2,"title":"Call mom","completed":true}]}
|
||||
```
|
||||
**Jazz**:
|
||||
```typescript
|
||||
class TodoItem extends CoMap { title=co.string; completed=co.boolean; }
|
||||
class TodoList extends CoList.Of(co.ref(TodoItem)) {}
|
||||
```
|
||||
|
||||
3. **Activity Feed (CoFeed)**
|
||||
**JSON**:
|
||||
```json
|
||||
{"activities":[{"type":"login","timestamp":1700000000},{"type":"logout","timestamp":1700000500},{"type":"comment","timestamp":1700001000,"text":"Great post!"}]}
|
||||
```
|
||||
**Jazz**:
|
||||
```typescript
|
||||
class UserActivity extends CoFeed.Of(co.json<{type:string;timestamp:number;text?:string}>()) {}
|
||||
```
|
||||
|
||||
4. **Polymorphic Widgets (SchemaUnion)**
|
||||
**JSON**:
|
||||
```json
|
||||
{"widgets":[{"type":"button","label":"Click Me"},{"type":"slider","min":0,"max":100}]}
|
||||
```
|
||||
**Jazz**:
|
||||
```typescript
|
||||
class BaseWidget extends CoMap { type=co.string; }
|
||||
class ButtonWidget extends BaseWidget { type=co.literal("button"); label=co.string; }
|
||||
class SliderWidget extends BaseWidget { type=co.literal("slider"); min=co.number; max=co.number; }
|
||||
const Widget=SchemaUnion.Of<BaseWidget>((raw)=>{...});
|
||||
```
|
||||
|
||||
5. **Access Control via Groups**
|
||||
**JSON**:
|
||||
```json
|
||||
{"group":{"owner":"user123","members":[{"id":"user456","role":"admin"},{"id":"user789","role":"writer"}]}}
|
||||
```
|
||||
**Jazz**:
|
||||
```typescript
|
||||
const group=Group.create({owner:user123});
|
||||
group.addMember(user456,"admin");
|
||||
group.addMember(user789,"writer");
|
||||
```
|
||||
|
||||
6. **User Account with Root Data (Accounts)**
|
||||
**JSON**:
|
||||
```json
|
||||
{"user":{"profile":{"name":"Jane Doe"},"documents":[{"title":"My Notes","content":"This is a note."}],"activities":[{"type":"login"}]}}
|
||||
```
|
||||
**Jazz**:
|
||||
```typescript
|
||||
class AppAccountRoot extends CoMap {
|
||||
profile=co.ref(UserProfile);
|
||||
documents=co.ref(CoList.Of(co.ref(Document)));
|
||||
activities=co.ref(UserActivity);
|
||||
}
|
||||
class AppAccount extends Account {
|
||||
root=co.ref(AppAccountRoot);
|
||||
profile=co.ref(UserProfile);
|
||||
}
|
||||
```
|
||||
|
||||
7. **Document Collaboration**
|
||||
**JSON**:
|
||||
```json
|
||||
{
|
||||
"document":{
|
||||
"title":"Project Plan","content":"Detailed...","collaborators":[{"id":"user1","role":"editor"},{"id":"user2","role":"viewer"}],
|
||||
"history":[{"user":"user1","timestamp":1700000000,"change":"Edited content"}]
|
||||
}
|
||||
}
|
||||
```
|
||||
**Jazz**:
|
||||
```typescript
|
||||
class Document extends CoMap {
|
||||
title=co.string;content=co.string;
|
||||
collaborators=co.ref(CoList.Of(co.ref(UserProfile)));
|
||||
history=co.ref(CoFeed.Of(co.json<{user:string;timestamp:number;change:string}>()));
|
||||
}
|
||||
```
|
||||
|
||||
8. **Draft System**
|
||||
**JSON**:
|
||||
```json
|
||||
{"draft":{"name":"New Project","tasks":[],"valid":false,"errors":["Project name required"]}}
|
||||
```
|
||||
**Jazz**:
|
||||
```typescript
|
||||
class DraftProject extends CoMap {
|
||||
name=co.optional.string;
|
||||
tasks=co.ref(CoList.Of(co.ref(TodoItem)));
|
||||
validate(){/*...*/}
|
||||
}
|
||||
```
|
||||
|
||||
9. **Public File Sharing**
|
||||
**JSON**:
|
||||
```json
|
||||
{"file":{"name":"Presentation.pdf","size":2048,"uploadedAt":1700000000,"sharedWith":["everyone"]}}
|
||||
```
|
||||
**Jazz**:
|
||||
```typescript
|
||||
class SharedFile extends CoMap {
|
||||
name=co.string;
|
||||
file=co.ref(FileStream);
|
||||
uploadedAt=co.Date;
|
||||
}
|
||||
const publicGroup=Group.create({owner:me});
|
||||
publicGroup.addMember("everyone","reader");
|
||||
```
|
||||
|
||||
10. **Invite System**
|
||||
**JSON**:
|
||||
```json
|
||||
{"invites":[{"email":"user@example.com","role":"writer","status":"pending"}]}
|
||||
```
|
||||
**Jazz**:
|
||||
```typescript
|
||||
class Invite extends CoMap {
|
||||
email=co.string;
|
||||
role=co.literal("reader","writer","admin");
|
||||
status=co.literal("pending","accepted");
|
||||
}
|
||||
const inviteLink=createInviteLink(project,"writer");
|
||||
useAcceptInvite({ invitedObjectSchema:Project, onAccept:(id)=>navigate(`/projects/${id}`) });
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Continue: 2_jazz_schema_template.md
|
||||
216
packages/cursor-docs/.cursor/docs/2_jazz_schema_template.md
Normal file
216
packages/cursor-docs/.cursor/docs/2_jazz_schema_template.md
Normal file
@@ -0,0 +1,216 @@
|
||||
---
|
||||
|
||||
import { Account, CoList, CoMap, Group, Profile, co } from "jazz-tools";
|
||||
|
||||
/**
|
||||
* Represents a main data item in the app’s domain.
|
||||
*
|
||||
* Properties:
|
||||
* - name: Required field identifying the item.
|
||||
* - metadata_field: Optional metadata (string).
|
||||
* - container: Reference to a parent Container.
|
||||
* - deleted: Soft delete flag for archiving/removing without permanent deletion.
|
||||
*/
|
||||
export class MainItem extends CoMap {
|
||||
/** A required, identifying name. */
|
||||
name = co.string;
|
||||
|
||||
/** An optional string field for metadata. */
|
||||
metadata_field = co.optional.string;
|
||||
|
||||
/** Reference to the parent container. */
|
||||
container = co.ref(Container);
|
||||
|
||||
/** Soft-delete flag: if true, treat this item as removed. */
|
||||
deleted = co.boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* A list/array of MainItem references.
|
||||
* Provides real-time collaboration features (insertion, removal, ordering).
|
||||
*/
|
||||
export class MainItemList extends CoList.Of(co.ref(MainItem)) {}
|
||||
|
||||
/**
|
||||
* A container/organizational structure for grouping MainItem objects.
|
||||
*
|
||||
* Properties:
|
||||
* - name: A human-friendly name for the container.
|
||||
* - items: A CoList of MainItem references.
|
||||
*/
|
||||
export class Container extends CoMap {
|
||||
/** Human-friendly name for this container. */
|
||||
name = co.string;
|
||||
|
||||
/** A list of MainItems held by this container. */
|
||||
items = co.ref(MainItemList);
|
||||
}
|
||||
|
||||
/**
|
||||
* The top-level structure in the user’s account, representing all stored data.
|
||||
*
|
||||
* Properties:
|
||||
* - container: The default or root container for MainItems.
|
||||
* - version: An optional version number for supporting migrations.
|
||||
*/
|
||||
export class AccountRoot extends CoMap {
|
||||
/** A single container to hold or organize items. */
|
||||
container = co.ref(Container);
|
||||
|
||||
/** Tracks schema version for migrations. */
|
||||
version = co.optional.number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a user’s profile data.
|
||||
*
|
||||
* Properties:
|
||||
* - email: Required email field for identification/contact.
|
||||
*
|
||||
* Static method:
|
||||
* - validate: Enforces that both name and email are provided.
|
||||
*/
|
||||
export class UserProfile extends Profile {
|
||||
/** Required user email. */
|
||||
email = co.string;
|
||||
|
||||
/**
|
||||
* Validate user profile data, ensuring both "name" and "email" exist and are non-empty.
|
||||
*/
|
||||
static validate(data: { name?: string; email?: string }) {
|
||||
const errors: string[] = [];
|
||||
if (!data.name?.trim()) {
|
||||
errors.push("Please enter a name.");
|
||||
}
|
||||
if (!data.email?.trim()) {
|
||||
errors.push("Please enter an email.");
|
||||
}
|
||||
return { errors };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The main Account class that holds the user’s data (AccountRoot) and profile.
|
||||
* Handles initial migrations (setting up default Container, etc.) and can be extended
|
||||
* to run future schema migrations.
|
||||
*/
|
||||
export class JazzAccount extends Account {
|
||||
/** Reference to the user’s profile. */
|
||||
profile = co.ref(UserProfile);
|
||||
|
||||
/** Reference to the account root data (container, version, etc.). */
|
||||
root = co.ref(AccountRoot);
|
||||
|
||||
/**
|
||||
* Migrate is run on creation and each login. If there is no root, creates initial data.
|
||||
* Otherwise, you can add version-based migrations (below).
|
||||
*/
|
||||
async migrate(creationProps?: { name: string; other?: Record<string, unknown> }) {
|
||||
if (!this._refs.root && creationProps) {
|
||||
await this.initialMigration(creationProps);
|
||||
return;
|
||||
}
|
||||
|
||||
// KEEP THESE COMMENTS IN THE GENERATED SCHEMA CODE
|
||||
// uncomment this to add migrations
|
||||
// Check the current version and run subsequent migrations
|
||||
// const currentVersion = this.root?.version || 0;
|
||||
// if (currentVersion < 1) {
|
||||
// await this.migrationV1();
|
||||
// }
|
||||
// Add more version checks and migrations as needed
|
||||
// if (currentVersion < 2) {
|
||||
// await this.migrationV2();
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the initial migration logic when the account is first created:
|
||||
* - Validates the user’s profile data (name, email).
|
||||
* - Sets up a public group (readable by "everyone") for the user’s profile.
|
||||
* - Sets up a private group to own private resources.
|
||||
* - Creates a default Container with a single MainItem.
|
||||
*/
|
||||
private async initialMigration(
|
||||
creationProps: { name: string; other?: Record<string, unknown> }
|
||||
) {
|
||||
const { name, other } = creationProps;
|
||||
|
||||
// Validate profile data
|
||||
const profileErrors = UserProfile.validate({ name, ...other });
|
||||
if (profileErrors.errors.length > 0) {
|
||||
throw new Error(
|
||||
"Invalid profile data: " + profileErrors.errors.join(", "),
|
||||
);
|
||||
}
|
||||
|
||||
// Create a public group for the profile
|
||||
const publicGroup = Group.create({ owner: this });
|
||||
publicGroup.addMember("everyone", "reader");
|
||||
|
||||
// Create the user profile with validated data
|
||||
this.profile = UserProfile.create(
|
||||
{
|
||||
name,
|
||||
...other,
|
||||
},
|
||||
{ owner: publicGroup },
|
||||
);
|
||||
|
||||
// Create a private group for data that should not be publicly readable
|
||||
const privateGroup = Group.create({ owner: this });
|
||||
|
||||
// Create a default container with one default item
|
||||
const defaultContainer = Container.create(
|
||||
{
|
||||
name: this.profile?.name
|
||||
? \`\${this.profile.name}'s items\`
|
||||
: "Your items",
|
||||
items: MainItemList.create(
|
||||
[
|
||||
MainItem.create({ name: "Default item" }, privateGroup),
|
||||
],
|
||||
privateGroup,
|
||||
),
|
||||
},
|
||||
privateGroup,
|
||||
);
|
||||
|
||||
// Initialize the account root with version tracking
|
||||
this.root = AccountRoot.create(
|
||||
{
|
||||
container: defaultContainer,
|
||||
version: 0, // Start at version 0
|
||||
},
|
||||
{ owner: this },
|
||||
);
|
||||
}
|
||||
|
||||
// KEEP THESE COMMENTS IN THE GENERATED SCHEMA CODE
|
||||
// uncomment this to add migrations
|
||||
// private async migrationV1() {
|
||||
// Example migration logic:
|
||||
// if (this.root) {
|
||||
// // e.g., add a new field to all items
|
||||
// // for (const container of this.root.containers || []) {
|
||||
// // for (const item of container.items || []) {
|
||||
// // item.newField = "default value";
|
||||
// // }
|
||||
// // }
|
||||
// this.root.version = 1;
|
||||
// }
|
||||
// }
|
||||
|
||||
// KEEP THESE COMMENTS IN THE GENERATED SCHEMA CODE
|
||||
// uncomment this to add migrations
|
||||
// private async migrationV2() {
|
||||
// if (this.root) {
|
||||
// // Future migration logic goes here
|
||||
// this.root.version = 2;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
Continue: 3_jazz_rules.md
|
||||
55
packages/cursor-docs/.cursor/docs/3_jazz_rules.md
Normal file
55
packages/cursor-docs/.cursor/docs/3_jazz_rules.md
Normal file
@@ -0,0 +1,55 @@
|
||||
---
|
||||
|
||||
**Jazz Schema Rules:**
|
||||
|
||||
1. **User Profile and Account**
|
||||
1.1. Define `export class UserProfile extends Profile` with exactly one property:
|
||||
```ts
|
||||
name = co.string;
|
||||
```
|
||||
1.2. Add a static `validate` method in `UserProfile` that checks `name` is present and non-empty.
|
||||
1.3. Define `export class JazzAccount extends Account` with exactly two properties:
|
||||
```ts
|
||||
profile = co.ref(UserProfile);
|
||||
root = co.ref(AccountRoot);
|
||||
```
|
||||
1.4. The `JazzAccount` class must have a `migrate(creationProps?: { name: string; other?: Record<string, unknown> })` method.
|
||||
- Within `migrate`, if `this._refs.root` is undefined and `creationProps` is provided, run `initialMigration`.
|
||||
- The `creationProps` **must** include a `name` property; `other` is optional but do not define more fields.
|
||||
|
||||
2. **Container, Root & Ownership**
|
||||
2.1. The `AccountRoot` class (extending `CoMap`) **must** have a `container` property referencing a `Container`.
|
||||
2.2. The `Container` class (extending `CoMap`) should contain the main domain entities of the app.
|
||||
2.3. **Never** define a `name` field in the `Container` class. The template shows an example `name` property for a Container, but these rules override that.
|
||||
2.4. Whenever the root structure is initialized, it is always owned by the current `JazzAccount`:
|
||||
```ts
|
||||
this.root = AccountRoot.create({ container: defaultContainer, version: 0 }, { owner: this });
|
||||
```
|
||||
|
||||
3. **Groups & Ownership**
|
||||
3.1. If the `UserProfile` is intended to be public, set its owner to a `publicGroup` that has `"everyone"` as `"reader"`. Otherwise, use a private group.
|
||||
3.2. When creating a group, no need to explicitly pass `owner: this`. That is implicit if it's the same account.
|
||||
3.3. **Do not** use properties like `user`, `users`, `group`, or `groups` in CoMaps or CoLists. Ownership is implicit.
|
||||
|
||||
4. **No Direct CoList Fields**
|
||||
4.1. **Never** do:
|
||||
```ts
|
||||
co.ref(CoList.Of(co.ref(SomeClass)));
|
||||
```
|
||||
4.2. Instead, define a CoList class (e.g. `export class SomeClassList extends CoList.Of(co.ref(SomeClass)) {}`) and reference it.
|
||||
|
||||
5. **Schema Structure & Fields**
|
||||
5.1. Follow the provided template patterns. **Do not** add extra entities or fields outside the user’s requirements or the template.
|
||||
5.2. Do **not** use properties like `createdAt` or `updatedAt`; they’re implicit in CoValue.
|
||||
5.3. If a property is optional, denote it with a question mark (`?`) in the field definition, or use `co.optional.*`.
|
||||
5.4. Keep comments from the template, especially around migration blocks, intact.
|
||||
5.5. Never set a property to "co.ref(UserProfile)".
|
||||
|
||||
6. **Output & Formatting**
|
||||
6.1. Generate the final schema in TypeScript with no extra markdown or triple backticks.
|
||||
6.2. Do **not** expand or alter the template’s classes beyond what is required.
|
||||
6.3. Avoid redundant or conflicting rules from the template; these revised rules take priority.
|
||||
|
||||
---
|
||||
|
||||
Continue: 4_1_jazz_example.md
|
||||
229
packages/cursor-docs/.cursor/docs/4_1_jazz_example.md
Normal file
229
packages/cursor-docs/.cursor/docs/4_1_jazz_example.md
Normal file
@@ -0,0 +1,229 @@
|
||||
# Example app 1: A secure and organized password manager app that allows users to store, manage, and categorize their credentials in folders
|
||||
|
||||
```typescript
|
||||
import { Account, CoList, CoMap, Group, Profile, co } from "jazz-tools";
|
||||
|
||||
/**
|
||||
* Represents a password item in the Password Manager.
|
||||
*
|
||||
* Properties:
|
||||
* - name: The required name identifying the password item.
|
||||
* - username: Optional username.
|
||||
* - username_input_selector: Optional selector for the username input field.
|
||||
* - password: The required password.
|
||||
* - password_input_selector: Optional selector for the password input.
|
||||
* - uri: Optional URI associated with the item.
|
||||
* - folder: Reference to the parent Folder.
|
||||
* - deleted: Soft delete flag.
|
||||
*/
|
||||
export class PasswordItem extends CoMap {
|
||||
name = co.string;
|
||||
username = co.optional.string;
|
||||
username_input_selector = co.optional.string;
|
||||
password = co.string;
|
||||
password_input_selector = co.optional.string;
|
||||
uri = co.optional.string;
|
||||
folder = co.ref(Folder);
|
||||
deleted = co.boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of PasswordItem references.
|
||||
*/
|
||||
export class PasswordList extends CoList.Of(co.ref(PasswordItem)) {}
|
||||
|
||||
/**
|
||||
* Represents a folder that groups password items.
|
||||
*
|
||||
* Properties:
|
||||
* - name: The folder's name.
|
||||
* - items: A list of PasswordItems contained in the folder.
|
||||
*/
|
||||
export class Folder extends CoMap {
|
||||
name = co.string;
|
||||
items = co.ref(PasswordList);
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of Folder references.
|
||||
*/
|
||||
export class FolderList extends CoList.Of(co.ref(Folder)) {}
|
||||
|
||||
/**
|
||||
* Top-level container for the Password Manager.
|
||||
* This container holds the main entities of the app.
|
||||
*
|
||||
* Properties:
|
||||
* - folders: A list of Folder entities.
|
||||
*/
|
||||
export class Container extends CoMap {
|
||||
folders = co.ref(FolderList);
|
||||
}
|
||||
|
||||
/**
|
||||
* The account root holds all user data.
|
||||
*
|
||||
* Properties:
|
||||
* - container: The main container that organizes the app’s data.
|
||||
* - version: An optional version number used for migrations.
|
||||
*/
|
||||
export class PasswordManagerAccountRoot extends CoMap {
|
||||
container = co.ref(Container);
|
||||
version = co.optional.number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the user's profile.
|
||||
*
|
||||
* Properties:
|
||||
* - name: The required user name.
|
||||
*
|
||||
* Static method:
|
||||
* - validate: Ensures that a non-empty name is provided.
|
||||
*/
|
||||
export class UserProfile extends Profile {
|
||||
name = co.string;
|
||||
|
||||
static validate(data: { name?: string; email?: string }) {
|
||||
const errors: string[] = [];
|
||||
if (!data.name?.trim()) {
|
||||
errors.push("Please enter a name.");
|
||||
}
|
||||
// Note: In this schema, only 'name' is required.
|
||||
return { errors };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main account class for the Password Manager.
|
||||
* Contains only the profile and root properties.
|
||||
* Handles data initialization and migrations.
|
||||
*/
|
||||
export class PasswordManagerAccount extends Account {
|
||||
profile = co.ref(UserProfile);
|
||||
root = co.ref(PasswordManagerAccountRoot);
|
||||
|
||||
/**
|
||||
* The migrate method is called on account creation and login.
|
||||
* If the root is not initialized, it runs the initial migration.
|
||||
* Otherwise, you can add version-based migrations as needed.
|
||||
*/
|
||||
async migrate(creationProps?: { name: string; other?: Record<string, unknown> }) {
|
||||
if (!this._refs.root && creationProps) {
|
||||
await this.initialMigration(creationProps);
|
||||
return;
|
||||
}
|
||||
|
||||
// KEEP THESE COMMENTS IN THE GENERATED SCHEMA CODE
|
||||
// Uncomment the following lines to add migrations:
|
||||
// const currentVersion = this.root?.version || 0;
|
||||
// if (currentVersion < 1) {
|
||||
// await this.migrationV1();
|
||||
// }
|
||||
// if (currentVersion < 2) {
|
||||
// await this.migrationV2();
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the initial migration logic when the account is first created.
|
||||
* - Validates the user's profile data.
|
||||
* - Sets up a public group for the profile (accessible by "everyone").
|
||||
* - Sets up a private group for private resources.
|
||||
* - Creates a default Container with a default Folder and a default PasswordItem.
|
||||
*/
|
||||
private async initialMigration(creationProps: { name: string; other?: Record<string, unknown> }) {
|
||||
const { name, other } = creationProps;
|
||||
const profileErrors = UserProfile.validate({ name, ...other });
|
||||
if (profileErrors.errors.length > 0) {
|
||||
throw new Error("Invalid profile data: " + profileErrors.errors.join(", "));
|
||||
}
|
||||
|
||||
// Create a public group for the user profile.
|
||||
const publicGroup = Group.create({ owner: this });
|
||||
publicGroup.addMember("everyone", "reader");
|
||||
|
||||
// Create the user profile with validated data.
|
||||
this.profile = UserProfile.create(
|
||||
{
|
||||
name,
|
||||
...other,
|
||||
},
|
||||
{ owner: publicGroup }
|
||||
);
|
||||
|
||||
// Create a private group for private data.
|
||||
const privateGroup = Group.create({ owner: this });
|
||||
|
||||
// Create a default Folder with one default PasswordItem.
|
||||
const defaultFolder = Folder.create(
|
||||
{
|
||||
name: "Default",
|
||||
items: PasswordList.create(
|
||||
[
|
||||
PasswordItem.create(
|
||||
{
|
||||
name: "Gmail",
|
||||
username: "user@gmail.com",
|
||||
password: "password123",
|
||||
uri: "https://gmail.com",
|
||||
// The folder reference will be set after defaultFolder creation.
|
||||
folder: null as any,
|
||||
deleted: false,
|
||||
},
|
||||
privateGroup
|
||||
),
|
||||
],
|
||||
privateGroup
|
||||
),
|
||||
},
|
||||
privateGroup
|
||||
);
|
||||
// Set the folder reference for the default PasswordItem.
|
||||
defaultFolder.items[0].folder = defaultFolder;
|
||||
|
||||
// Create a default container that holds the FolderList.
|
||||
const defaultContainer = Container.create(
|
||||
{
|
||||
folders: FolderList.create([defaultFolder], privateGroup),
|
||||
},
|
||||
privateGroup
|
||||
);
|
||||
|
||||
// Initialize the account root with version tracking.
|
||||
this.root = PasswordManagerAccountRoot.create(
|
||||
{
|
||||
container: defaultContainer,
|
||||
version: 0, // Set initial version
|
||||
},
|
||||
{ owner: this }
|
||||
);
|
||||
}
|
||||
|
||||
// KEEP THESE COMMENTS IN THE GENERATED SCHEMA CODE
|
||||
// Uncomment the following methods to add migrations:
|
||||
|
||||
// private async migrationV1() {
|
||||
// if (this.root) {
|
||||
// // Example migration logic: add a new field to all password items.
|
||||
// // for (const folder of this.root.container.folders || []) {
|
||||
// // for (const item of folder.items || []) {
|
||||
// // item.newField = "default value";
|
||||
// // }
|
||||
// // }
|
||||
// this.root.version = 1;
|
||||
// }
|
||||
// }
|
||||
|
||||
// private async migrationV2() {
|
||||
// if (this.root) {
|
||||
// // Future migration logic goes here.
|
||||
// this.root.version = 2;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Continue: 4_2_jazz_example.md
|
||||
177
packages/cursor-docs/.cursor/docs/4_2_jazz_example.md
Normal file
177
packages/cursor-docs/.cursor/docs/4_2_jazz_example.md
Normal file
@@ -0,0 +1,177 @@
|
||||
# Example app 2: A feature-rich music player app that allows users to manage playlists, store tracks, and visualize audio waveforms
|
||||
|
||||
```typescript
|
||||
export class MusicTrack extends CoMap {
|
||||
title = co.string;
|
||||
duration = co.number;
|
||||
sourceTrack = co.optional.ref(MusicTrack);
|
||||
file = co.ref(FileStream);
|
||||
waveform = co.ref(MusicTrackWaveform);
|
||||
container = co.ref(Playlist);
|
||||
deleted = co.boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents waveform data for a music track.
|
||||
*
|
||||
* Properties:
|
||||
* - data: A JSON array of numbers representing the waveform.
|
||||
*/
|
||||
export class MusicTrackWaveform extends CoMap {
|
||||
data = co.json<number[]>();
|
||||
}
|
||||
|
||||
/**
|
||||
* A collaborative list of MusicTrack references.
|
||||
*/
|
||||
export class MusicTrackList extends CoList.Of(co.ref(MusicTrack)) {}
|
||||
|
||||
/**
|
||||
* Acts as a container for music tracks.
|
||||
*
|
||||
* Properties:
|
||||
* - name: The name of the playlist.
|
||||
* - items: A list of MusicTracks in this playlist.
|
||||
*/
|
||||
export class Playlist extends CoMap {
|
||||
name = co.string;
|
||||
items = co.ref(MusicTrackList);
|
||||
}
|
||||
|
||||
/**
|
||||
* The top-level account root for the music app.
|
||||
*
|
||||
* Properties:
|
||||
* - container: The main playlist (acting as the container for music tracks).
|
||||
* - version: Optional version number for migrations.
|
||||
*/
|
||||
export class MusicAccountRoot extends CoMap {
|
||||
container = co.ref(Playlist);
|
||||
version = co.optional.number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a user's profile.
|
||||
*
|
||||
* Properties:
|
||||
* - name: The required user name.
|
||||
*
|
||||
* Static method:
|
||||
* - validate: Ensures that a non-empty name and email are provided.
|
||||
*/
|
||||
export class UserProfile extends Profile {
|
||||
name = co.string;
|
||||
|
||||
static validate(data: { name?: string; email?: string }) {
|
||||
const errors: string[] = [];
|
||||
if (!data.name?.trim()) {
|
||||
errors.push("Please enter a name.");
|
||||
}
|
||||
if (!data.email?.trim()) {
|
||||
errors.push("Please enter an email.");
|
||||
}
|
||||
return { errors };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The main Account class for the music app.
|
||||
* Contains only the profile and root properties.
|
||||
* Handles data initialization and migrations.
|
||||
*/
|
||||
export class MusicAccount extends Account {
|
||||
profile = co.ref(UserProfile);
|
||||
root = co.ref(MusicAccountRoot);
|
||||
|
||||
/**
|
||||
* Migrate is run on account creation and each login.
|
||||
* If the root is not initialized, run initial migration.
|
||||
* Otherwise, version-based migrations can be added.
|
||||
*/
|
||||
async migrate(creationProps?: { name: string; other?: Record<string, unknown> }) {
|
||||
if (!this._refs.root && creationProps) {
|
||||
await this.initialMigration(creationProps);
|
||||
return;
|
||||
}
|
||||
|
||||
// KEEP THESE COMMENTS IN THE GENERATED SCHEMA CODE
|
||||
// Uncomment to add migrations:
|
||||
// const currentVersion = this.root?.version || 0;
|
||||
// if (currentVersion < 1) {
|
||||
// await this.migrationV1();
|
||||
// }
|
||||
// if (currentVersion < 2) {
|
||||
// await this.migrationV2();
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes initial migration when the account is first created:
|
||||
* - Validates the user's profile data (name, email).
|
||||
* - Sets up a public group (with "everyone" as reader) for the profile.
|
||||
* - Creates a default Playlist with an empty MusicTrackList.
|
||||
* - Initializes the account root with version 0.
|
||||
*/
|
||||
private async initialMigration(creationProps: { name: string; other?: Record<string, unknown> }) {
|
||||
const { name, other } = creationProps;
|
||||
const profileErrors = UserProfile.validate({ name, ...other });
|
||||
if (profileErrors.errors.length > 0) {
|
||||
throw new Error("Invalid profile data: " + profileErrors.errors.join(", "));
|
||||
}
|
||||
|
||||
// Create a public group for the profile.
|
||||
const publicGroup = Group.create({ owner: this });
|
||||
publicGroup.addMember("everyone", "reader");
|
||||
|
||||
// Create the user profile with validated data.
|
||||
this.profile = UserProfile.create(
|
||||
{ name, ...other },
|
||||
{ owner: publicGroup }
|
||||
);
|
||||
|
||||
// Create a private group for the user's music data.
|
||||
const privateGroup = Group.create({ owner: this });
|
||||
|
||||
// Create a default Playlist as the main container.
|
||||
const defaultPlaylist = Playlist.create(
|
||||
{
|
||||
name: this.profile.name + "'s playlist",
|
||||
items: MusicTrackList.create([], privateGroup),
|
||||
},
|
||||
privateGroup
|
||||
);
|
||||
|
||||
// Initialize the account root with version tracking.
|
||||
this.root = MusicAccountRoot.create(
|
||||
{
|
||||
container: defaultPlaylist,
|
||||
version: 0, // Set initial version
|
||||
},
|
||||
{ owner: this }
|
||||
);
|
||||
}
|
||||
|
||||
// KEEP THESE COMMENTS IN THE GENERATED SCHEMA CODE
|
||||
// Uncomment to add migrations:
|
||||
// private async migrationV1() {
|
||||
// if (this.root) {
|
||||
// // Example migration logic: add a new field to all music tracks.
|
||||
// // for (const track of this.root.container.items || []) {
|
||||
// // track.newField = "default value";
|
||||
// // }
|
||||
// this.root.version = 1;
|
||||
// }
|
||||
// }
|
||||
|
||||
// private async migrationV2() {
|
||||
// if (this.root) {
|
||||
// // Future migration logic goes here.
|
||||
// this.root.version = 2;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Continue: 4_3_jazz_example.md
|
||||
182
packages/cursor-docs/.cursor/docs/4_3_jazz_example.md
Normal file
182
packages/cursor-docs/.cursor/docs/4_3_jazz_example.md
Normal file
@@ -0,0 +1,182 @@
|
||||
# Example app 3: A social pet app where users can share pet photos, react with fun emojis, and organize posts in a collaborative feed
|
||||
|
||||
```typescript
|
||||
import { Account, CoFeed, CoList, CoMap, Group, ImageDefinition, Profile, co } from "jazz-tools";
|
||||
|
||||
export const ReactionTypes = [
|
||||
"aww",
|
||||
"love",
|
||||
"haha",
|
||||
"wow",
|
||||
"tiny",
|
||||
"chonkers",
|
||||
] as const;
|
||||
|
||||
export type ReactionType = (typeof ReactionTypes)[number];
|
||||
|
||||
/**
|
||||
* Represents an append-only feed of reactions for a pet post.
|
||||
*/
|
||||
export class PetReactions extends CoFeed.Of(co.json<ReactionType>()) {}
|
||||
|
||||
/**
|
||||
* Represents a pet post.
|
||||
*
|
||||
* Properties:
|
||||
* - name: The title or caption for the pet post.
|
||||
* - image: A reference to an ImageDefinition containing the pet's image.
|
||||
* - reactions: A feed of reactions (of type ReactionType) for the post.
|
||||
*/
|
||||
export class PetPost extends CoMap {
|
||||
name = co.string;
|
||||
image = co.ref(ImageDefinition);
|
||||
reactions = co.ref(PetReactions);
|
||||
}
|
||||
|
||||
/**
|
||||
* A collaborative list of PetPost references.
|
||||
*/
|
||||
export class ListOfPosts extends CoList.Of(co.ref(PetPost)) {}
|
||||
|
||||
/**
|
||||
* Container for the pet posts.
|
||||
*
|
||||
* This container acts as the main organizational structure holding the posts.
|
||||
*
|
||||
* Properties:
|
||||
* - posts: A list of pet posts.
|
||||
*/
|
||||
export class PetContainer extends CoMap {
|
||||
posts = co.ref(ListOfPosts);
|
||||
}
|
||||
|
||||
/**
|
||||
* The top-level account root for the pet app.
|
||||
*
|
||||
* Properties:
|
||||
* - container: The main container that organizes pet posts.
|
||||
* - version: An optional version number for supporting migrations.
|
||||
*/
|
||||
export class PetAccountRoot extends CoMap {
|
||||
container = co.ref(PetContainer);
|
||||
version = co.optional.number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a user’s profile.
|
||||
*
|
||||
* Properties:
|
||||
* - name: The required user name.
|
||||
*
|
||||
* Static method:
|
||||
* - validate: Ensures that both "name" and "email" (if provided) are non-empty.
|
||||
*/
|
||||
export class UserProfile extends Profile {
|
||||
name = co.string;
|
||||
|
||||
static validate(data: { name?: string; email?: string }) {
|
||||
const errors: string[] = [];
|
||||
if (!data.name?.trim()) {
|
||||
errors.push("Please enter a name.");
|
||||
}
|
||||
if (data.email !== undefined && !data.email?.trim()) {
|
||||
errors.push("Please enter an email.");
|
||||
}
|
||||
return { errors };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main account class for the pet app.
|
||||
*
|
||||
* Contains only the profile and root properties, and handles account initialization
|
||||
* and migrations.
|
||||
*/
|
||||
export class PetAccount extends Account {
|
||||
profile = co.ref(UserProfile);
|
||||
root = co.ref(PetAccountRoot);
|
||||
|
||||
/**
|
||||
* Migrate is run on account creation and on every log-in.
|
||||
* If the root is not initialized, it runs the initial migration.
|
||||
* Otherwise, version-based migrations can be added.
|
||||
*/
|
||||
async migrate(creationProps?: { name: string; other?: Record<string, unknown> }) {
|
||||
if (!this._refs.root && creationProps) {
|
||||
await this.initialMigration(creationProps);
|
||||
return;
|
||||
}
|
||||
|
||||
// KEEP THESE COMMENTS IN THE GENERATED SCHEMA CODE
|
||||
// Uncomment the following lines to add migrations:
|
||||
// const currentVersion = this.root?.version || 0;
|
||||
// if (currentVersion < 1) {
|
||||
// await this.migrationV1();
|
||||
// }
|
||||
// if (currentVersion < 2) {
|
||||
// await this.migrationV2();
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the initial migration logic when the account is first created:
|
||||
* - Validates the user's profile data (name, email).
|
||||
* - Sets up a public group (accessible by "everyone") for the user’s profile.
|
||||
* - Sets up a private group for the user's pet posts.
|
||||
* - Creates a default container with an empty list of posts.
|
||||
* - Initializes the account root with version 0.
|
||||
*/
|
||||
private async initialMigration(creationProps: { name: string; other?: Record<string, unknown> }) {
|
||||
const { name, other } = creationProps;
|
||||
const profileErrors = UserProfile.validate({ name, ...other });
|
||||
if (profileErrors.errors.length > 0) {
|
||||
throw new Error("Invalid profile data: " + profileErrors.errors.join(", "));
|
||||
}
|
||||
|
||||
// Create a public group for the user profile.
|
||||
const publicGroup = Group.create({ owner: this });
|
||||
publicGroup.addMember("everyone", "reader");
|
||||
|
||||
// Create the user profile with validated data.
|
||||
this.profile = UserProfile.create(
|
||||
{ name, ...other },
|
||||
{ owner: publicGroup }
|
||||
);
|
||||
|
||||
// Create a private group for pet data.
|
||||
const privateGroup = Group.create({ owner: this });
|
||||
|
||||
// Create a default container holding an empty list of posts.
|
||||
const defaultContainer = PetContainer.create(
|
||||
{ posts: ListOfPosts.create([], privateGroup) },
|
||||
privateGroup
|
||||
);
|
||||
|
||||
// Initialize the account root with version tracking.
|
||||
this.root = PetAccountRoot.create(
|
||||
{ container: defaultContainer, version: 0 },
|
||||
{ owner: this }
|
||||
);
|
||||
}
|
||||
|
||||
// KEEP THESE COMMENTS IN THE GENERATED SCHEMA CODE
|
||||
// Uncomment to add migrations:
|
||||
// private async migrationV1() {
|
||||
// if (this.root) {
|
||||
// // Example migration logic: update pet posts if needed.
|
||||
// this.root.version = 1;
|
||||
// }
|
||||
// }
|
||||
|
||||
// private async migrationV2() {
|
||||
// if (this.root) {
|
||||
// // Future migration logic goes here.
|
||||
// this.root.version = 2;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Continue: 4_4_jazz_example.md
|
||||
240
packages/cursor-docs/.cursor/docs/4_4_jazz_example.md
Normal file
240
packages/cursor-docs/.cursor/docs/4_4_jazz_example.md
Normal file
@@ -0,0 +1,240 @@
|
||||
# Example app 4: A bubble tea ordering app that lets users customize drinks with different tea bases, add-ons, and delivery preferences
|
||||
|
||||
```typescript
|
||||
import { Account, CoList, CoMap, Group, Profile, co } from "jazz-tools";
|
||||
|
||||
export const BubbleTeaAddOnTypes = [
|
||||
"Pearl",
|
||||
"Lychee jelly",
|
||||
"Red bean",
|
||||
"Brown sugar",
|
||||
"Taro",
|
||||
] as const;
|
||||
|
||||
export const BubbleTeaBaseTeaTypes = [
|
||||
"Black",
|
||||
"Oolong",
|
||||
"Jasmine",
|
||||
"Thai",
|
||||
] as const;
|
||||
|
||||
/**
|
||||
* A list of Bubble Tea add-ons.
|
||||
* Provides a computed property to check for insertions.
|
||||
*/
|
||||
export class ListOfBubbleTeaAddOns extends CoList.Of(co.literal(...BubbleTeaAddOnTypes)) {
|
||||
get hasChanges() {
|
||||
return Object.entries(this._raw.insertions).length > 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a finalized Bubble Tea order.
|
||||
*
|
||||
* Properties:
|
||||
* - baseTea: Selected base tea type.
|
||||
* - addOns: Selected add-ons.
|
||||
* - deliveryDate: Delivery date for the order.
|
||||
* - withMilk: Indicates if the order includes milk.
|
||||
* - instructions: Optional additional instructions.
|
||||
*/
|
||||
export class BubbleTeaOrder extends CoMap {
|
||||
baseTea = co.literal(...BubbleTeaBaseTeaTypes);
|
||||
addOns = co.ref(ListOfBubbleTeaAddOns);
|
||||
deliveryDate = co.Date;
|
||||
withMilk = co.boolean;
|
||||
instructions = co.optional.string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a draft (in-progress) Bubble Tea order.
|
||||
*
|
||||
* Properties:
|
||||
* - baseTea: Optional base tea type.
|
||||
* - addOns: Optional reference to selected add-ons.
|
||||
* - deliveryDate: Optional delivery date.
|
||||
* - withMilk: Optional milk preference.
|
||||
* - instructions: Optional instructions.
|
||||
*
|
||||
* Methods:
|
||||
* - validate: Checks that required fields are present.
|
||||
* Computed:
|
||||
* - hasChanges: Indicates if there have been modifications.
|
||||
*/
|
||||
export class DraftBubbleTeaOrder extends CoMap {
|
||||
baseTea = co.optional.literal(...BubbleTeaBaseTeaTypes);
|
||||
addOns = co.optional.ref(ListOfBubbleTeaAddOns);
|
||||
deliveryDate = co.optional.Date;
|
||||
withMilk = co.optional.boolean;
|
||||
instructions = co.optional.string;
|
||||
|
||||
get hasChanges() {
|
||||
return Object.keys(this._edits).length > 1 || this.addOns?.hasChanges;
|
||||
}
|
||||
|
||||
validate() {
|
||||
const errors: string[] = [];
|
||||
if (!this.baseTea) {
|
||||
errors.push("Please select your preferred base tea.");
|
||||
}
|
||||
if (!this.deliveryDate) {
|
||||
errors.push("Please select a delivery date.");
|
||||
}
|
||||
return { errors };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A collaborative list of finalized Bubble Tea orders.
|
||||
*/
|
||||
export class ListOfBubbleTeaOrders extends CoList.Of(co.ref(BubbleTeaOrder)) {}
|
||||
|
||||
/**
|
||||
* Container for Bubble Tea orders.
|
||||
* Holds the draft order and the list of finalized orders.
|
||||
*/
|
||||
export class BubbleTeaContainer extends CoMap {
|
||||
draft = co.ref(DraftBubbleTeaOrder);
|
||||
orders = co.ref(ListOfBubbleTeaOrders);
|
||||
}
|
||||
|
||||
/**
|
||||
* The top-level account root for the Bubble Tea app.
|
||||
*
|
||||
* Properties:
|
||||
* - container: The main container that organizes the Bubble Tea orders.
|
||||
* - version: Optional version number for migration tracking.
|
||||
*/
|
||||
export class BubbleTeaAccountRoot extends CoMap {
|
||||
container = co.ref(BubbleTeaContainer);
|
||||
version = co.optional.number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a user's profile.
|
||||
*
|
||||
* Properties:
|
||||
* - name: Required user name.
|
||||
*
|
||||
* Static method:
|
||||
* - validate: Ensures that a non-empty name and email (if provided) are present.
|
||||
*/
|
||||
export class UserProfile extends Profile {
|
||||
name = co.string;
|
||||
|
||||
static validate(data: { name?: string; email?: string }) {
|
||||
const errors: string[] = [];
|
||||
if (!data.name?.trim()) {
|
||||
errors.push("Please enter a name.");
|
||||
}
|
||||
if (data.email !== undefined && !data.email.trim()) {
|
||||
errors.push("Please enter an email.");
|
||||
}
|
||||
return { errors };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main account class for the Bubble Tea app.
|
||||
* Contains only the profile and root properties.
|
||||
* Handles account initialization and migrations.
|
||||
*/
|
||||
export class BubbleTeaAccount extends Account {
|
||||
profile = co.ref(UserProfile);
|
||||
root = co.ref(BubbleTeaAccountRoot);
|
||||
|
||||
/**
|
||||
* The migrate method is run on account creation and login.
|
||||
* If the root is not initialized, it runs the initial migration.
|
||||
* Otherwise, version-based migrations can be added.
|
||||
*/
|
||||
async migrate(creationProps?: { name: string; other?: Record<string, unknown> }) {
|
||||
if (!this._refs.root && creationProps) {
|
||||
await this.initialMigration(creationProps);
|
||||
return;
|
||||
}
|
||||
|
||||
// KEEP THESE COMMENTS IN THE GENERATED SCHEMA CODE
|
||||
// Uncomment the following lines to add migrations:
|
||||
// const currentVersion = this.root?.version || 0;
|
||||
// if (currentVersion < 1) {
|
||||
// await this.migrationV1();
|
||||
// }
|
||||
// if (currentVersion < 2) {
|
||||
// await this.migrationV2();
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the initial migration logic when the account is first created:
|
||||
* - Validates the user's profile data (name, email).
|
||||
* - Sets up a public group (accessible by "everyone") for the user's profile.
|
||||
* - Sets up a private group for the Bubble Tea data.
|
||||
* - Creates a default BubbleTeaContainer with an empty draft and order list.
|
||||
* - Initializes the account root with version 0.
|
||||
*/
|
||||
private async initialMigration(creationProps: { name: string; other?: Record<string, unknown> }) {
|
||||
const { name, other } = creationProps;
|
||||
const profileErrors = UserProfile.validate({ name, ...other });
|
||||
if (profileErrors.errors.length > 0) {
|
||||
throw new Error("Invalid profile data: " + profileErrors.errors.join(", "));
|
||||
}
|
||||
|
||||
// Create a public group for the user profile.
|
||||
const publicGroup = Group.create({ owner: this });
|
||||
publicGroup.addMember("everyone", "reader");
|
||||
|
||||
// Create the user profile with validated data.
|
||||
this.profile = UserProfile.create(
|
||||
{ name, ...other },
|
||||
{ owner: publicGroup }
|
||||
);
|
||||
|
||||
// Create a private group for Bubble Tea data.
|
||||
const privateGroup = Group.create({ owner: this });
|
||||
|
||||
// Create a default container with an empty draft order and empty list of finalized orders.
|
||||
const defaultContainer = BubbleTeaContainer.create(
|
||||
{
|
||||
draft: DraftBubbleTeaOrder.create(
|
||||
{
|
||||
addOns: ListOfBubbleTeaAddOns.create([], privateGroup),
|
||||
},
|
||||
privateGroup
|
||||
),
|
||||
orders: ListOfBubbleTeaOrders.create([], privateGroup),
|
||||
},
|
||||
privateGroup
|
||||
);
|
||||
|
||||
// Initialize the account root with version tracking.
|
||||
this.root = BubbleTeaAccountRoot.create(
|
||||
{
|
||||
container: defaultContainer,
|
||||
version: 0, // Set initial version
|
||||
},
|
||||
{ owner: this }
|
||||
);
|
||||
}
|
||||
|
||||
// KEEP THESE COMMENTS IN THE GENERATED SCHEMA CODE
|
||||
// Uncomment to add migrations:
|
||||
// private async migrationV1() {
|
||||
// if (this.root) {
|
||||
// // Example migration logic: update orders if needed.
|
||||
// this.root.version = 1;
|
||||
// }
|
||||
// }
|
||||
|
||||
// private async migrationV2() {
|
||||
// if (this.root) {
|
||||
// // Future migration logic goes here.
|
||||
// this.root.version = 2;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Continue: 4_5_jazz_example.md
|
||||
215
packages/cursor-docs/.cursor/docs/4_5_jazz_example.md
Normal file
215
packages/cursor-docs/.cursor/docs/4_5_jazz_example.md
Normal file
@@ -0,0 +1,215 @@
|
||||
# Example app 5: An employee onboarding app that streamlines the hiring process through structured steps, including initial data collection, document uploads, and final approvals
|
||||
|
||||
```typescript
|
||||
import { Account, CoList, CoMap, Group, ImageDefinition, Profile, co } from "jazz-tools";
|
||||
|
||||
type Steps = "initial" | "upload" | "final";
|
||||
|
||||
interface Step {
|
||||
type: Steps;
|
||||
prevStep: ReturnType<typeof co.ref> | undefined;
|
||||
done: boolean;
|
||||
isCurrentStep(): boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the initial onboarding step.
|
||||
*
|
||||
* Properties:
|
||||
* - type: Always "initial".
|
||||
* - ssn: Optional Social Security Number.
|
||||
* - address: Optional address.
|
||||
* - done: Indicates if this step is completed.
|
||||
* - prevStep: Not applicable for the initial step.
|
||||
*/
|
||||
export class CoInitialStep extends CoMap implements Step {
|
||||
type = co.literal("initial");
|
||||
ssn? = co.string;
|
||||
address? = co.string;
|
||||
done = co.boolean;
|
||||
prevStep = co.null;
|
||||
isCurrentStep() {
|
||||
return !this.done;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the document upload step.
|
||||
*
|
||||
* Properties:
|
||||
* - type: Always "upload".
|
||||
* - prevStep: Reference to the completed initial step.
|
||||
* - photo: Optional reference to an image (e.g. document photo).
|
||||
* - done: Indicates if this step is completed.
|
||||
*/
|
||||
export class CoDocUploadStep extends CoMap implements Step {
|
||||
type = co.literal("upload");
|
||||
prevStep = co.ref(CoInitialStep);
|
||||
photo = co.ref(ImageDefinition, { optional: true });
|
||||
done = co.boolean;
|
||||
isCurrentStep() {
|
||||
return !!(this.prevStep?.done && !this.done);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the final onboarding step.
|
||||
*
|
||||
* Properties:
|
||||
* - type: Always "final".
|
||||
* - prevStep: Reference to the completed document upload step.
|
||||
* - done: Indicates if this step is completed.
|
||||
*/
|
||||
export class CoFinalStep extends CoMap implements Step {
|
||||
type = co.literal("final");
|
||||
prevStep = co.ref(CoDocUploadStep);
|
||||
done = co.boolean;
|
||||
isCurrentStep() {
|
||||
return !!(this.prevStep?.done && !this.done);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an employee undergoing the onboarding process.
|
||||
*
|
||||
* Properties:
|
||||
* - name: The employee's name.
|
||||
* - deleted: Optional soft-delete flag.
|
||||
* - initialStep: Reference to the initial step.
|
||||
* - docUploadStep: Reference to the document upload step.
|
||||
* - finalStep: Reference to the final step.
|
||||
*/
|
||||
export class CoEmployee extends CoMap {
|
||||
name = co.string;
|
||||
deleted? = co.boolean;
|
||||
initialStep = co.ref(CoInitialStep);
|
||||
docUploadStep = co.ref(CoDocUploadStep);
|
||||
finalStep = co.ref(CoFinalStep);
|
||||
}
|
||||
|
||||
/**
|
||||
* A collaborative list of employee references.
|
||||
*/
|
||||
export class EmployeeList extends CoList.Of(co.ref(CoEmployee)) {}
|
||||
|
||||
/**
|
||||
* The top-level account root for the HR app.
|
||||
*
|
||||
* Properties:
|
||||
* - employees: A list of employees.
|
||||
* - version: Optional version number for migrations.
|
||||
*/
|
||||
export class HRAccountRoot extends CoMap {
|
||||
employees = co.ref(EmployeeList);
|
||||
version = co.optional.number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a user's profile.
|
||||
*
|
||||
* Properties:
|
||||
* - name: The required user name.
|
||||
*
|
||||
* Static method:
|
||||
* - validate: Ensures that a non-empty name (and email, if provided) is present.
|
||||
*/
|
||||
export class UserProfile extends Profile {
|
||||
name = co.string;
|
||||
|
||||
static validate(data: { name?: string; email?: string }) {
|
||||
const errors: string[] = [];
|
||||
if (!data.name?.trim()) {
|
||||
errors.push("Please enter a name.");
|
||||
}
|
||||
if (data.email !== undefined && !data.email.trim()) {
|
||||
errors.push("Please enter an email.");
|
||||
}
|
||||
return { errors };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The main account class for the HR app.
|
||||
* Contains only the profile and root properties.
|
||||
* Handles account initialization and migrations.
|
||||
*/
|
||||
export class HRAccount extends Account {
|
||||
profile = co.ref(UserProfile);
|
||||
root = co.ref(HRAccountRoot);
|
||||
|
||||
/**
|
||||
* Migrate is run on account creation and on every log-in.
|
||||
* If the account root is not initialized, it runs the initial migration.
|
||||
*/
|
||||
async migrate(creationProps?: { name: string; other?: Record<string, unknown> }) {
|
||||
if (!this._refs.root && creationProps) {
|
||||
await this.initialMigration(creationProps);
|
||||
return;
|
||||
}
|
||||
|
||||
// KEEP THESE COMMENTS IN THE GENERATED SCHEMA CODE
|
||||
// Uncomment the following lines to add migrations:
|
||||
// const currentVersion = this.root?.version || 0;
|
||||
// if (currentVersion < 1) {
|
||||
// await this.migrationV1();
|
||||
// }
|
||||
// if (currentVersion < 2) {
|
||||
// await this.migrationV2();
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the initial migration logic when the account is first created:
|
||||
* - Validates the user's profile data.
|
||||
* - Sets up a public group (accessible by "everyone") for the user profile.
|
||||
* - Creates a default HRAccountRoot with an empty employee list.
|
||||
* - Initializes the account root with version 0.
|
||||
*/
|
||||
private async initialMigration(creationProps: { name: string; other?: Record<string, unknown> }) {
|
||||
const { name, other } = creationProps;
|
||||
const profileErrors = UserProfile.validate({ name, ...other });
|
||||
if (profileErrors.errors.length > 0) {
|
||||
throw new Error("Invalid profile data: " + profileErrors.errors.join(", "));
|
||||
}
|
||||
|
||||
// Create a public group for the user profile.
|
||||
const publicGroup = Group.create({ owner: this });
|
||||
publicGroup.addMember("everyone", "reader");
|
||||
|
||||
// Create the user profile with validated data.
|
||||
this.profile = UserProfile.create({ name, ...other }, { owner: publicGroup });
|
||||
|
||||
// Create a private group for HR data.
|
||||
const privateGroup = Group.create({ owner: this });
|
||||
|
||||
// Create a default employee list (empty).
|
||||
const employees = EmployeeList.create([], privateGroup);
|
||||
|
||||
// Initialize the account root with version tracking.
|
||||
this.root = HRAccountRoot.create(
|
||||
{ employees, version: 0 },
|
||||
{ owner: this }
|
||||
);
|
||||
}
|
||||
|
||||
// KEEP THESE COMMENTS IN THE GENERATED SCHEMA CODE
|
||||
// Uncomment to add migrations:
|
||||
// private async migrationV1() {
|
||||
// if (this.root) {
|
||||
// // Example migration logic: update employee records if needed.
|
||||
// this.root.version = 1;
|
||||
// }
|
||||
// }
|
||||
|
||||
// private async migrationV2() {
|
||||
// if (this.root) {
|
||||
// // Future migration logic goes here.
|
||||
// this.root.version = 2;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Continue: 4_6_jazz_example.md
|
||||
148
packages/cursor-docs/.cursor/docs/4_6_jazz_example.md
Normal file
148
packages/cursor-docs/.cursor/docs/4_6_jazz_example.md
Normal file
@@ -0,0 +1,148 @@
|
||||
# Example app 6: A task management app that helps users organize their to-dos with categories, tags, due dates, and priority levels
|
||||
|
||||
```typescript
|
||||
import { Account, CoList, CoMap, Group, Profile, co } from "jazz-tools";
|
||||
|
||||
// Task priority levels
|
||||
export type PriorityLevel = "Low" | "Medium" | "High";
|
||||
|
||||
// Represents a tag that can be associated with tasks
|
||||
export class Tag extends CoMap {
|
||||
name = co.string;
|
||||
deleted = co.boolean;
|
||||
}
|
||||
|
||||
export class TagList extends CoList.Of(co.ref(Tag)) {}
|
||||
|
||||
// Represents a category that can group tasks
|
||||
export class Category extends CoMap {
|
||||
name = co.string;
|
||||
deleted = co.boolean;
|
||||
}
|
||||
|
||||
export class CategoryList extends CoList.Of(co.ref(Category)) {}
|
||||
|
||||
// Represents a single task in the todo app
|
||||
export class Task extends CoMap {
|
||||
title = co.string;
|
||||
description = co.optional.string;
|
||||
dueDate = co.optional.Date;
|
||||
isCompleted = co.boolean;
|
||||
priority = co.literal("Low", "Medium", "High");
|
||||
tags = co.ref(TagList);
|
||||
category = co.optional.ref(Category);
|
||||
deleted = co.boolean;
|
||||
}
|
||||
|
||||
export class TaskList extends CoList.Of(co.ref(Task)) {}
|
||||
|
||||
// Container for organizing tasks, categories and tags
|
||||
export class Container extends CoMap {
|
||||
tasks = co.ref(TaskList);
|
||||
categories = co.ref(CategoryList);
|
||||
tags = co.ref(TagList);
|
||||
}
|
||||
|
||||
// Root structure holding all data
|
||||
export class AccountRoot extends CoMap {
|
||||
container = co.ref(Container);
|
||||
version = co.optional.number;
|
||||
}
|
||||
|
||||
export class UserProfile extends Profile {
|
||||
name = co.string;
|
||||
|
||||
static validate(data: { name?: string; other?: Record<string, unknown> }) {
|
||||
const errors: string[] = [];
|
||||
if (!data.name?.trim()) {
|
||||
errors.push("Please enter a name.");
|
||||
}
|
||||
return { errors };
|
||||
}
|
||||
}
|
||||
|
||||
// Main account class that handles data initialization
|
||||
export class JazzAccount extends Account {
|
||||
profile = co.ref(UserProfile);
|
||||
root = co.ref(AccountRoot);
|
||||
|
||||
async migrate(creationProps?: {
|
||||
name: string;
|
||||
other?: Record<string, unknown>;
|
||||
}) {
|
||||
if (!this._refs.root && creationProps) {
|
||||
await this.initialMigration(creationProps);
|
||||
return;
|
||||
}
|
||||
|
||||
// uncomment this to add migrations
|
||||
// const currentVersion = this.root?.version || 0;
|
||||
// if (currentVersion < 1) {
|
||||
// await this.migrationV1();
|
||||
// }
|
||||
// if (currentVersion < 2) {
|
||||
// await this.migrationV2();
|
||||
// }
|
||||
}
|
||||
|
||||
private async initialMigration(
|
||||
creationProps: {
|
||||
name: string;
|
||||
other?: Record<string, unknown>;
|
||||
}
|
||||
) {
|
||||
const { name, other } = creationProps;
|
||||
const profileErrors = UserProfile.validate({ name, ...other });
|
||||
if (profileErrors.errors.length > 0) {
|
||||
throw new Error(
|
||||
"Invalid profile data: " + profileErrors.errors.join(", "),
|
||||
);
|
||||
}
|
||||
|
||||
const publicGroup = Group.create({ owner: this });
|
||||
publicGroup.addMember("everyone", "reader");
|
||||
|
||||
this.profile = UserProfile.create(
|
||||
{
|
||||
name,
|
||||
...other,
|
||||
},
|
||||
{ owner: publicGroup },
|
||||
);
|
||||
|
||||
const privateGroup = Group.create({ owner: this });
|
||||
|
||||
// Create default container with empty lists
|
||||
const defaultContainer = Container.create(
|
||||
{
|
||||
tasks: TaskList.create([], privateGroup),
|
||||
categories: CategoryList.create([], privateGroup),
|
||||
tags: TagList.create([], privateGroup),
|
||||
},
|
||||
privateGroup,
|
||||
);
|
||||
|
||||
// Initialize root structure with version
|
||||
this.root = AccountRoot.create({
|
||||
container: defaultContainer,
|
||||
version: 0, // Set initial version
|
||||
// here owner is always "this" Account
|
||||
}, { owner: this });
|
||||
}
|
||||
|
||||
// uncomment this to add migrations
|
||||
// private async migrationV1() {
|
||||
// if (this.root) {
|
||||
// // Add migration logic here
|
||||
// this.root.version = 1;
|
||||
// }
|
||||
// }
|
||||
|
||||
// private async migrationV2() {
|
||||
// if (this.root) {
|
||||
// // Future migration logic here
|
||||
// this.root.version = 2;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,67 @@
|
||||
---
|
||||
description: Creating Jazz Schema Rule
|
||||
globs: schema.ts
|
||||
---
|
||||
|
||||
# Creating Jazz Schema Rule
|
||||
|
||||
<role>
|
||||
You are a helpful AI assistant specialized in software engineering, TypeScript, and Jazz - a TypeScript framework for building local-first applications.
|
||||
</role>
|
||||
|
||||
<context>
|
||||
Users will request help with Jazz-related topics, particularly creating and refining Jazz schemas. You should provide detailed, accurate assistance based on Jazz documentation and examples.
|
||||
</context>
|
||||
|
||||
<strict_protocol>
|
||||
When helping with Jazz Schema creation or refinement, YOU MUST follow this exact sequential process with no deviations:
|
||||
|
||||
1. YOU MUST read and process each file IN ORDER. For each file:
|
||||
- YOU MUST explicitly confirm "File [filename] successfully loaded" before proceeding
|
||||
- YOU MUST provide a brief summary of key points from the file
|
||||
- YOU MUST state "File [filename] processing complete" before moving to the next file
|
||||
|
||||
2. File processing sequence (MANDATORY - NO SKIPPING):
|
||||
a. Jazz Docs: [1_jazz_docs.md](mdc:.cursor/docs/1_jazz_docs.md)
|
||||
- YOU MUST confirm reading before proceeding
|
||||
|
||||
b. Example Applications (process ONE BY ONE in sequence):
|
||||
- [4_1_jazz_example.md](mdc:.cursor/docs/4_1_jazz_example.md) - Password manager app
|
||||
- [4_2_jazz_example.md](mdc:.cursor/docs/4_2_jazz_example.md) - Music player app
|
||||
- [4_3_jazz_example.md](mdc:.cursor/docs/4_3_jazz_example.md) - Social pet app
|
||||
- [4_4_jazz_example.md](mdc:.cursor/docs/4_4_jazz_example.md) - Bubble tea ordering app
|
||||
- [4_5_jazz_example.md](mdc:.cursor/docs/4_5_jazz_example.md) - Employee onboarding app
|
||||
- [4_6_jazz_example.md](mdc:.cursor/docs/4_6_jazz_example.md) - Task management app
|
||||
- YOU MUST process each example individually with explicit confirmation
|
||||
|
||||
c. Jazz Schema Template: [2_jazz_schema_template.md](mdc:.cursor/docs/2_jazz_schema_template.md)
|
||||
- YOU MUST confirm reading before proceeding
|
||||
|
||||
d. Jazz Schema Rules: [3_jazz_rules.md](mdc:.cursor/docs/3_jazz_rules.md)
|
||||
- YOU MUST confirm reading before proceeding
|
||||
|
||||
3. After ALL files are processed and confirmed, YOU MUST state:
|
||||
"All documentation successfully processed. Now creating Jazz schema based on requirements."
|
||||
</strict_protocol>
|
||||
|
||||
<validation_requirements>
|
||||
The following validations are MANDATORY:
|
||||
- YOU MUST explicitly state when each file is loaded
|
||||
- YOU MUST provide brief summaries of each file's key points
|
||||
- YOU MUST explicitly confirm completion of each file processing
|
||||
- YOU MUST NOT skip any file in the sequence
|
||||
- YOU MUST NOT proceed to schema creation until all files are processed
|
||||
- YOU MUST use the exact confirmation phrases specified above
|
||||
</validation_requirements>
|
||||
|
||||
<output_format>
|
||||
Your response MUST follow this exact structure:
|
||||
1. File processing confirmations and summaries (for each file)
|
||||
2. Final confirmation that all files were processed
|
||||
3. Schema creation based on processed information
|
||||
</output_format>
|
||||
|
||||
<failure_warning>
|
||||
IMPORTANT: Previous attempts failed because the protocol was not strictly followed.
|
||||
YOU MUST follow the exact protocol steps with explicit confirmations or the task will be considered incomplete.
|
||||
</failure_warning>
|
||||
171
packages/cursor-docs/.gitignore
vendored
Normal file
171
packages/cursor-docs/.gitignore
vendored
Normal file
@@ -0,0 +1,171 @@
|
||||
# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore
|
||||
|
||||
# Logs
|
||||
|
||||
logs
|
||||
_.log
|
||||
npm-debug.log_
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
|
||||
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
||||
|
||||
# Runtime data
|
||||
|
||||
pids
|
||||
_.pid
|
||||
_.seed
|
||||
\*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
|
||||
coverage
|
||||
\*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
|
||||
\*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
|
||||
\*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
|
||||
.cache/
|
||||
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
|
||||
.temp
|
||||
.cache
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.\*
|
||||
|
||||
.DS_Store
|
||||
2
packages/cursor-docs/.npmignore
Normal file
2
packages/cursor-docs/.npmignore
Normal file
@@ -0,0 +1,2 @@
|
||||
coverage
|
||||
node_modules
|
||||
7
packages/cursor-docs/CHANGELOG.md
Normal file
7
packages/cursor-docs/CHANGELOG.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# cursor-docs
|
||||
|
||||
## 0.0.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 3fa276c: Added Cursor docs
|
||||
19
packages/cursor-docs/LICENSE.txt
Normal file
19
packages/cursor-docs/LICENSE.txt
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright 2024, Garden Computing, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
6
packages/cursor-docs/package.json
Normal file
6
packages/cursor-docs/package.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "cursor-docs",
|
||||
"license": "MIT",
|
||||
"version": "0.0.2",
|
||||
"scripts": {}
|
||||
}
|
||||
@@ -1,5 +1,28 @@
|
||||
# jazz-browser-media-images
|
||||
|
||||
## 0.10.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [07feedd]
|
||||
- jazz-tools@0.10.13
|
||||
- jazz-browser@0.10.13
|
||||
|
||||
## 0.10.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [4612e05]
|
||||
- jazz-tools@0.10.12
|
||||
- jazz-browser@0.10.12
|
||||
|
||||
## 0.10.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [834203f]
|
||||
- jazz-browser@0.10.9
|
||||
|
||||
## 0.10.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
{
|
||||
"name": "jazz-auth-clerk",
|
||||
"version": "0.10.8",
|
||||
"version": "0.10.13",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.ts",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cojson": "workspace:0.10.8",
|
||||
"jazz-browser": "workspace:0.10.8",
|
||||
"jazz-tools": "workspace:0.10.8"
|
||||
"jazz-browser": "workspace:0.10.13",
|
||||
"jazz-tools": "workspace:0.10.13"
|
||||
},
|
||||
"scripts": {
|
||||
"format-and-lint": "biome check .",
|
||||
|
||||
@@ -1,5 +1,28 @@
|
||||
# jazz-browser-media-images
|
||||
|
||||
## 0.10.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [07feedd]
|
||||
- jazz-tools@0.10.13
|
||||
- jazz-browser@0.10.13
|
||||
|
||||
## 0.10.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [4612e05]
|
||||
- jazz-tools@0.10.12
|
||||
- jazz-browser@0.10.12
|
||||
|
||||
## 0.10.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [834203f]
|
||||
- jazz-browser@0.10.9
|
||||
|
||||
## 0.10.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-browser-media-images",
|
||||
"version": "0.10.8",
|
||||
"version": "0.10.13",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.ts",
|
||||
@@ -8,8 +8,8 @@
|
||||
"dependencies": {
|
||||
"@types/image-blob-reduce": "^4.1.1",
|
||||
"image-blob-reduce": "^4.1.0",
|
||||
"jazz-browser": "workspace:0.10.8",
|
||||
"jazz-tools": "workspace:0.10.8",
|
||||
"jazz-browser": "workspace:0.10.13",
|
||||
"jazz-tools": "workspace:0.10.13",
|
||||
"pica": "^9.0.1",
|
||||
"typescript": "~5.6.2"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,25 @@
|
||||
# jazz-browser
|
||||
|
||||
## 0.10.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [07feedd]
|
||||
- jazz-tools@0.10.13
|
||||
|
||||
## 0.10.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [4612e05]
|
||||
- jazz-tools@0.10.12
|
||||
|
||||
## 0.10.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 834203f: Fix the attachment setting to restore the faceid login
|
||||
|
||||
## 0.10.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-browser",
|
||||
"version": "0.10.8",
|
||||
"version": "0.10.13",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.ts",
|
||||
|
||||
@@ -135,7 +135,6 @@ export class BrowserPasskeyAuth {
|
||||
{ alg: -257, type: "public-key" },
|
||||
],
|
||||
authenticatorSelection: {
|
||||
authenticatorAttachment: "cross-platform",
|
||||
requireResidentKey: true,
|
||||
residentKey: "required",
|
||||
userVerification: "preferred",
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
# jazz-inspector
|
||||
|
||||
## 0.10.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [07feedd]
|
||||
- jazz-tools@0.10.13
|
||||
- jazz-react-core@0.10.10
|
||||
|
||||
## 0.10.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [4612e05]
|
||||
- jazz-react-core@0.10.9
|
||||
- jazz-tools@0.10.12
|
||||
|
||||
## 0.10.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-inspector",
|
||||
"version": "0.10.8",
|
||||
"version": "0.10.10",
|
||||
"type": "module",
|
||||
"main": "./dist/app.js",
|
||||
"types": "./dist/app.d.ts",
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
# jazz-autosub
|
||||
|
||||
## 0.10.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [07feedd]
|
||||
- jazz-tools@0.10.13
|
||||
|
||||
## 0.10.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [4612e05]
|
||||
- jazz-tools@0.10.12
|
||||
|
||||
## 0.10.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"types": "src/index.ts",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"version": "0.10.8",
|
||||
"version": "0.10.13",
|
||||
"dependencies": {
|
||||
"cojson": "workspace:*",
|
||||
"cojson-transport-ws": "workspace:*",
|
||||
|
||||
@@ -1,5 +1,34 @@
|
||||
# jazz-browser-media-images
|
||||
|
||||
## 0.10.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [07feedd]
|
||||
- jazz-tools@0.10.13
|
||||
- jazz-auth-clerk@0.10.13
|
||||
- jazz-browser@0.10.13
|
||||
- jazz-react@0.10.13
|
||||
|
||||
## 0.10.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [4612e05]
|
||||
- jazz-tools@0.10.12
|
||||
- jazz-react@0.10.12
|
||||
- jazz-auth-clerk@0.10.12
|
||||
- jazz-browser@0.10.12
|
||||
|
||||
## 0.10.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [834203f]
|
||||
- jazz-browser@0.10.9
|
||||
- jazz-auth-clerk@0.10.9
|
||||
- jazz-react@0.10.9
|
||||
|
||||
## 0.10.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-react-auth-clerk",
|
||||
"version": "0.10.8",
|
||||
"version": "0.10.13",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.tsx",
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
# jazz-react-core
|
||||
|
||||
## 0.10.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [07feedd]
|
||||
- jazz-tools@0.10.13
|
||||
|
||||
## 0.10.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 4612e05: Fix type inference on `useCoState`
|
||||
- Updated dependencies [4612e05]
|
||||
- jazz-tools@0.10.12
|
||||
|
||||
## 0.10.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-react-core",
|
||||
"version": "0.10.8",
|
||||
"version": "0.10.10",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.ts",
|
||||
|
||||
@@ -29,6 +29,7 @@ export function usePassphraseAuth({
|
||||
return new PassphraseAuth(
|
||||
context.node.crypto,
|
||||
context.authenticate,
|
||||
context.register,
|
||||
authSecretStorage,
|
||||
wordlist,
|
||||
);
|
||||
@@ -50,6 +51,8 @@ export function usePassphraseAuth({
|
||||
state: isAuthenticated ? "signedIn" : "anonymous",
|
||||
logIn: authMethod.logIn,
|
||||
signUp: authMethod.signUp,
|
||||
registerNewAccount: authMethod.registerNewAccount,
|
||||
generateRandomPassphrase: authMethod.generateRandomPassphrase,
|
||||
passphrase,
|
||||
} as const;
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ function useCoValueObservable<V extends CoValue, D>() {
|
||||
export function useCoState<V extends CoValue, D>(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
Schema: CoValueClass<V>,
|
||||
id: ID<V> | undefined,
|
||||
id: ID<CoValue> | undefined,
|
||||
depth: D & DepthsIn<V> = [] as D & DepthsIn<V>,
|
||||
): DeeplyLoaded<V, D> | undefined | null {
|
||||
const contextManager = useJazzContextManager();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// @vitest-environment happy-dom
|
||||
|
||||
import { cojsonInternals } from "cojson";
|
||||
import { CoMap, co } from "jazz-tools";
|
||||
import { beforeEach, describe, expect, it } from "vitest";
|
||||
import { CoMap, CoValue, ID, co } from "jazz-tools";
|
||||
import { beforeEach, describe, expect, expectTypeOf, it } from "vitest";
|
||||
import { useCoState } from "../index.js";
|
||||
import { createJazzTestAccount, setupJazzTestSync } from "../testing.js";
|
||||
import { act, renderHook, waitFor } from "./testUtils.js";
|
||||
@@ -157,4 +157,21 @@ describe("useCoState", () => {
|
||||
expect(result.current).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
it("should return the same type as Schema", () => {
|
||||
class TestMap extends CoMap {
|
||||
value = co.string;
|
||||
}
|
||||
|
||||
const map = TestMap.create({
|
||||
value: "123",
|
||||
});
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useCoState(TestMap, map.id as ID<CoValue>, []),
|
||||
);
|
||||
expectTypeOf(result).toEqualTypeOf<{
|
||||
current: TestMap | null | undefined;
|
||||
}>();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,43 @@
|
||||
# jazz-react-native-auth-clerk
|
||||
|
||||
## 0.10.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [07feedd]
|
||||
- jazz-tools@0.10.13
|
||||
- jazz-auth-clerk@0.10.13
|
||||
- jazz-react-native@0.10.13
|
||||
|
||||
## 0.10.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [4612e05]
|
||||
- jazz-tools@0.10.12
|
||||
- jazz-react-native@0.10.12
|
||||
- jazz-auth-clerk@0.10.12
|
||||
|
||||
## 0.10.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5a54e4a]
|
||||
- jazz-react-native@0.10.11
|
||||
|
||||
## 0.10.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3405d8f]
|
||||
- jazz-react-native@0.10.10
|
||||
|
||||
## 0.10.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-auth-clerk@0.10.9
|
||||
|
||||
## 0.10.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-react-native-auth-clerk",
|
||||
"version": "0.10.8",
|
||||
"version": "0.10.13",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.tsx",
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
# jazz-browser-media-images
|
||||
|
||||
## 0.10.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [07feedd]
|
||||
- jazz-tools@0.10.13
|
||||
|
||||
## 0.10.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [4612e05]
|
||||
- jazz-tools@0.10.12
|
||||
|
||||
## 0.10.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-react-native-media-images",
|
||||
"version": "0.10.8",
|
||||
"version": "0.10.13",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.ts",
|
||||
|
||||
@@ -1,5 +1,33 @@
|
||||
# jazz-browser
|
||||
|
||||
## 0.10.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [07feedd]
|
||||
- jazz-tools@0.10.13
|
||||
- jazz-react-core@0.10.10
|
||||
|
||||
## 0.10.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [4612e05]
|
||||
- jazz-react-core@0.10.9
|
||||
- jazz-tools@0.10.12
|
||||
|
||||
## 0.10.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 5a54e4a: Don't export RNQuickCrypto to avoid install errors
|
||||
|
||||
## 0.10.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 3405d8f: read provided kvStore instead of falling back to the default in-memory store
|
||||
|
||||
## 0.10.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-react-native",
|
||||
"version": "0.10.8",
|
||||
"version": "0.10.13",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
createJazzReactNativeContext,
|
||||
createJazzReactNativeGuestContext,
|
||||
} from "./platform.js";
|
||||
import { KvStoreContext } from "./storage/kv-store-context.js";
|
||||
|
||||
export type JazzContextManagerProps<Acc extends Account> = {
|
||||
guestMode?: boolean;
|
||||
@@ -59,6 +60,10 @@ export class ReactNativeContextManager<
|
||||
await this.updateContext(props, currentContext, authProps);
|
||||
}
|
||||
|
||||
getKvStore(): KvStore {
|
||||
return KvStoreContext.getInstance().getStorage();
|
||||
}
|
||||
|
||||
propsChanged(props: JazzContextManagerProps<Acc>) {
|
||||
if (!this.props) {
|
||||
return true;
|
||||
|
||||
@@ -3,7 +3,6 @@ export * from "./auth/auth.js";
|
||||
export * from "./storage/kv-store-context.js";
|
||||
export * from "./hooks.js";
|
||||
export * from "./media.js";
|
||||
export * from "./crypto/index.js";
|
||||
|
||||
export { parseInviteLink } from "jazz-tools";
|
||||
export { createInviteLink, setupKvStore } from "./platform.js";
|
||||
|
||||
@@ -1,5 +1,30 @@
|
||||
# jazz-react
|
||||
|
||||
## 0.10.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [07feedd]
|
||||
- jazz-tools@0.10.13
|
||||
- jazz-browser@0.10.13
|
||||
- jazz-react-core@0.10.10
|
||||
|
||||
## 0.10.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [4612e05]
|
||||
- jazz-react-core@0.10.9
|
||||
- jazz-tools@0.10.12
|
||||
- jazz-browser@0.10.12
|
||||
|
||||
## 0.10.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [834203f]
|
||||
- jazz-browser@0.10.9
|
||||
|
||||
## 0.10.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user