Compare commits
115 Commits
jazz-496-d
...
jazz-react
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cc5aea708a | ||
|
|
87c7306632 | ||
|
|
a65219ba52 | ||
|
|
689595fd0a | ||
|
|
efbf2b65a8 | ||
|
|
912232192f | ||
|
|
11b5d77064 | ||
|
|
63b88b591d | ||
|
|
87d0544d98 | ||
|
|
d43dcc2c94 | ||
|
|
093a166db1 | ||
|
|
593f3ed83e | ||
|
|
6c6c89e068 | ||
|
|
c1646f6bbb | ||
|
|
b7deb3c937 | ||
|
|
9b425701e4 | ||
|
|
07dcf54aca | ||
|
|
8ed33c18ca | ||
|
|
6a96d8b53b | ||
|
|
a1bcbda72e | ||
|
|
0f67e0a988 | ||
|
|
6c7a3ebf82 | ||
|
|
27ce186152 | ||
|
|
77ae2cc186 | ||
|
|
66600c0c27 | ||
|
|
81d5261f4d | ||
|
|
d748dea90f | ||
|
|
707e544b83 | ||
|
|
f3cbaee890 | ||
|
|
60ce483db7 | ||
|
|
c08a41398d | ||
|
|
d6d9218b3e | ||
|
|
667e301f12 | ||
|
|
b15f904347 | ||
|
|
0c150a4c74 | ||
|
|
66de2dacb6 | ||
|
|
ae1425db8a | ||
|
|
a164b102f5 | ||
|
|
a6db9a438e | ||
|
|
a35249a831 | ||
|
|
36e246be79 | ||
|
|
b894455c9e | ||
|
|
11f7277675 | ||
|
|
d992f5c552 | ||
|
|
a5ec31f079 | ||
|
|
2f99de0976 | ||
|
|
d5503b4581 | ||
|
|
a462254199 | ||
|
|
f86e278b00 | ||
|
|
34cbdc3e4c | ||
|
|
09e2090939 | ||
|
|
2ffe10cc79 | ||
|
|
9a4cef30cf | ||
|
|
3c0b2cb689 | ||
|
|
6a2a176361 | ||
|
|
824bbc8bab | ||
|
|
71b9517eda | ||
|
|
8bdf6908b8 | ||
|
|
12bc4fb2d3 | ||
|
|
75211e3c82 | ||
|
|
029c32d86c | ||
|
|
3185a20777 | ||
|
|
45bed3ea80 | ||
|
|
8a31f56770 | ||
|
|
43fa1ecba4 | ||
|
|
4373e290fe | ||
|
|
037ed4d59d | ||
|
|
34946c18bc | ||
|
|
4fcc8edc70 | ||
|
|
141ea11bf1 | ||
|
|
0f8ba9966b | ||
|
|
91fa2e092a | ||
|
|
ff52d6df3e | ||
|
|
273b478381 | ||
|
|
1d29f74df6 | ||
|
|
393d066b25 | ||
|
|
45bff625c4 | ||
|
|
a8fca7b5f7 | ||
|
|
658a0602ea | ||
|
|
f039e8f097 | ||
|
|
dc9da28bf9 | ||
|
|
e6db8f2a02 | ||
|
|
f0c8f7b3eb | ||
|
|
945163b7bc | ||
|
|
3142e503ae | ||
|
|
932a21e62a | ||
|
|
628a33eb12 | ||
|
|
20eef64b47 | ||
|
|
c64f38b6c9 | ||
|
|
e5e047660b | ||
|
|
a65d6bed73 | ||
|
|
d4f7891890 | ||
|
|
6eef92b602 | ||
|
|
dfbb4b8c69 | ||
|
|
5f2d8e143c | ||
|
|
240f071951 | ||
|
|
d560b4ddfb | ||
|
|
73d6fd1a85 | ||
|
|
46f5133510 | ||
|
|
b3f2e67d9d | ||
|
|
f689cd20fc | ||
|
|
e22de9ff4c | ||
|
|
735bd41242 | ||
|
|
71998bde62 | ||
|
|
f01b714e29 | ||
|
|
028eca9f81 | ||
|
|
d3d4118b86 | ||
|
|
45f1fe19ac | ||
|
|
ad2db5e6cb | ||
|
|
480890d2e9 | ||
|
|
18428eaaa1 | ||
|
|
5ed31f2678 | ||
|
|
b9d194a80e | ||
|
|
2359e85ebd | ||
|
|
a4713df30c |
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"create-jazz-app": patch
|
||||
---
|
||||
|
||||
Added Cursor docs to create-jazz-app
|
||||
3
.github/workflows/release.yml
vendored
3
.github/workflows/release.yml
vendored
@@ -25,6 +25,9 @@ jobs:
|
||||
- name: Setup Source Code
|
||||
uses: ./.github/actions/source-code/
|
||||
|
||||
- name: Build packages
|
||||
run: pnpm exec turbo run build --filter='./packages/*'
|
||||
|
||||
- name: Create Release Pull Request or Publish to npm
|
||||
id: changesets
|
||||
uses: changesets/action@v1
|
||||
|
||||
14
biome.json
14
biome.json
@@ -13,7 +13,8 @@
|
||||
"**/android/**",
|
||||
"packages/jazz-svelte/**",
|
||||
"examples/*svelte*/**",
|
||||
"homepage/homepage/**"
|
||||
"homepage/homepage/**",
|
||||
"**/package.json"
|
||||
]
|
||||
},
|
||||
"formatter": {
|
||||
@@ -43,15 +44,6 @@
|
||||
}
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"include": ["**/package.json"],
|
||||
"linter": {
|
||||
"enabled": false
|
||||
},
|
||||
"formatter": {
|
||||
"enabled": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"include": ["packages/**/src/**"],
|
||||
"linter": {
|
||||
@@ -62,7 +54,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"include": ["packages/cojson-storage*/**"],
|
||||
"include": ["packages/cojson-storage*/**", "cojson-transport-ws/**"],
|
||||
"linter": {
|
||||
"enabled": true,
|
||||
"rules": {
|
||||
|
||||
@@ -1,5 +1,41 @@
|
||||
# chat-rn-clerk
|
||||
|
||||
## 1.0.81
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [6a96d8b]
|
||||
- Updated dependencies [a35249a]
|
||||
- Updated dependencies [b9d194a]
|
||||
- Updated dependencies [a4713df]
|
||||
- Updated dependencies [34cbdc3]
|
||||
- Updated dependencies [f039e8f]
|
||||
- Updated dependencies [e22de9f]
|
||||
- jazz-tools@0.11.0
|
||||
- jazz-react-native-media-images@0.11.0
|
||||
- jazz-react-native-auth-clerk@0.11.0
|
||||
- jazz-react-native@0.11.0
|
||||
|
||||
## 1.0.80
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [2f99de0]
|
||||
- jazz-tools@0.10.15
|
||||
- jazz-react-native@0.10.15
|
||||
- jazz-react-native-auth-clerk@0.10.15
|
||||
- jazz-react-native-media-images@0.10.15
|
||||
|
||||
## 1.0.79
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [75211e3]
|
||||
- jazz-tools@0.10.14
|
||||
- jazz-react-native@0.10.14
|
||||
- jazz-react-native-auth-clerk@0.10.14
|
||||
- jazz-react-native-media-images@0.10.14
|
||||
|
||||
## 1.0.78
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "chat-rn-clerk",
|
||||
"main": "index.js",
|
||||
"version": "1.0.78",
|
||||
"version": "1.0.81",
|
||||
"scripts": {
|
||||
"build": "expo export -p ios",
|
||||
"start": "expo start",
|
||||
|
||||
@@ -1,5 +1,35 @@
|
||||
# chat-rn
|
||||
|
||||
## 1.0.77
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [6a96d8b]
|
||||
- Updated dependencies [a35249a]
|
||||
- Updated dependencies [b9d194a]
|
||||
- Updated dependencies [a4713df]
|
||||
- Updated dependencies [34cbdc3]
|
||||
- Updated dependencies [f039e8f]
|
||||
- Updated dependencies [e22de9f]
|
||||
- jazz-tools@0.11.0
|
||||
- jazz-react-native@0.11.0
|
||||
|
||||
## 1.0.76
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [2f99de0]
|
||||
- jazz-tools@0.10.15
|
||||
- jazz-react-native@0.10.15
|
||||
|
||||
## 1.0.75
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [75211e3]
|
||||
- jazz-tools@0.10.14
|
||||
- jazz-react-native@0.10.14
|
||||
|
||||
## 1.0.74
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "chat-rn",
|
||||
"version": "1.0.74",
|
||||
"version": "1.0.77",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "expo export -p ios",
|
||||
|
||||
@@ -1,5 +1,39 @@
|
||||
# chat-vue
|
||||
|
||||
## 0.0.63
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [6a96d8b]
|
||||
- Updated dependencies [a35249a]
|
||||
- Updated dependencies [b9d194a]
|
||||
- Updated dependencies [a4713df]
|
||||
- Updated dependencies [34cbdc3]
|
||||
- Updated dependencies [18428ea]
|
||||
- Updated dependencies [f039e8f]
|
||||
- Updated dependencies [e22de9f]
|
||||
- jazz-tools@0.11.0
|
||||
- jazz-browser@0.11.0
|
||||
- jazz-vue@0.11.0
|
||||
|
||||
## 0.0.62
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [2f99de0]
|
||||
- jazz-tools@0.10.15
|
||||
- jazz-browser@0.10.15
|
||||
- jazz-vue@0.10.15
|
||||
|
||||
## 0.0.61
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [75211e3]
|
||||
- jazz-tools@0.10.14
|
||||
- jazz-browser@0.10.14
|
||||
- jazz-vue@0.10.14
|
||||
|
||||
## 0.0.60
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "chat-vue",
|
||||
"version": "0.0.60",
|
||||
"version": "0.0.63",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,38 @@
|
||||
# jazz-example-chat
|
||||
|
||||
## 0.0.159
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [6a96d8b]
|
||||
- Updated dependencies [a35249a]
|
||||
- Updated dependencies [b9d194a]
|
||||
- Updated dependencies [a4713df]
|
||||
- Updated dependencies [34cbdc3]
|
||||
- Updated dependencies [f039e8f]
|
||||
- Updated dependencies [e22de9f]
|
||||
- jazz-tools@0.11.0
|
||||
- jazz-browser-media-images@0.11.0
|
||||
- jazz-react@0.11.0
|
||||
|
||||
## 0.0.158
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [2f99de0]
|
||||
- jazz-tools@0.10.15
|
||||
- jazz-browser-media-images@0.10.15
|
||||
- jazz-react@0.10.15
|
||||
|
||||
## 0.0.157
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [75211e3]
|
||||
- jazz-tools@0.10.14
|
||||
- jazz-react@0.10.14
|
||||
- jazz-browser-media-images@0.10.14
|
||||
|
||||
## 0.0.156
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-example-chat",
|
||||
"private": true,
|
||||
"version": "0.0.156",
|
||||
"version": "0.0.159",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,38 @@
|
||||
# minimal-auth-clerk
|
||||
|
||||
## 0.0.58
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [6a96d8b]
|
||||
- Updated dependencies [a35249a]
|
||||
- Updated dependencies [b9d194a]
|
||||
- Updated dependencies [a4713df]
|
||||
- Updated dependencies [34cbdc3]
|
||||
- Updated dependencies [f039e8f]
|
||||
- Updated dependencies [e22de9f]
|
||||
- jazz-tools@0.11.0
|
||||
- jazz-react-auth-clerk@0.11.0
|
||||
- jazz-react@0.11.0
|
||||
|
||||
## 0.0.57
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [2f99de0]
|
||||
- jazz-tools@0.10.15
|
||||
- jazz-react@0.10.15
|
||||
- jazz-react-auth-clerk@0.10.15
|
||||
|
||||
## 0.0.56
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [75211e3]
|
||||
- jazz-tools@0.10.14
|
||||
- jazz-react@0.10.14
|
||||
- jazz-react-auth-clerk@0.10.14
|
||||
|
||||
## 0.0.55
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "clerk",
|
||||
"private": true,
|
||||
"version": "0.0.55",
|
||||
"version": "0.0.58",
|
||||
"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.13",
|
||||
"jazz-react-auth-clerk": "workspace:0.11.0",
|
||||
"jazz-tools": "workspace:*",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1"
|
||||
|
||||
@@ -1,5 +1,35 @@
|
||||
# file-share-svelte
|
||||
|
||||
## 0.0.43
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [6a96d8b]
|
||||
- Updated dependencies [a35249a]
|
||||
- Updated dependencies [b9d194a]
|
||||
- Updated dependencies [a4713df]
|
||||
- Updated dependencies [34cbdc3]
|
||||
- Updated dependencies [f039e8f]
|
||||
- Updated dependencies [e22de9f]
|
||||
- jazz-tools@0.11.0
|
||||
- jazz-svelte@0.11.0
|
||||
|
||||
## 0.0.42
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [2f99de0]
|
||||
- jazz-tools@0.10.15
|
||||
- jazz-svelte@0.10.15
|
||||
|
||||
## 0.0.41
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [75211e3]
|
||||
- jazz-tools@0.10.14
|
||||
- jazz-svelte@0.10.14
|
||||
|
||||
## 0.0.40
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "file-share-svelte",
|
||||
"version": "0.0.40",
|
||||
"version": "0.0.43",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,42 @@
|
||||
# form
|
||||
|
||||
## 0.1.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 18428ea: PasskeyAuth: Sets `profile.name` only if a non-empty username is passed to `signUp`
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [6a96d8b]
|
||||
- Updated dependencies [a35249a]
|
||||
- Updated dependencies [b9d194a]
|
||||
- Updated dependencies [a4713df]
|
||||
- Updated dependencies [34cbdc3]
|
||||
- Updated dependencies [f039e8f]
|
||||
- Updated dependencies [e22de9f]
|
||||
- jazz-tools@0.11.0
|
||||
- jazz-browser-media-images@0.11.0
|
||||
- jazz-react@0.11.0
|
||||
|
||||
## 0.0.53
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [2f99de0]
|
||||
- jazz-tools@0.10.15
|
||||
- jazz-browser-media-images@0.10.15
|
||||
- jazz-react@0.10.15
|
||||
|
||||
## 0.0.52
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [75211e3]
|
||||
- jazz-tools@0.10.14
|
||||
- jazz-react@0.10.14
|
||||
- jazz-browser-media-images@0.10.14
|
||||
|
||||
## 0.0.51
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "form",
|
||||
"private": true,
|
||||
"version": "0.0.51",
|
||||
"version": "0.1.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,38 @@
|
||||
# image-upload
|
||||
|
||||
## 0.0.56
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [6a96d8b]
|
||||
- Updated dependencies [a35249a]
|
||||
- Updated dependencies [b9d194a]
|
||||
- Updated dependencies [a4713df]
|
||||
- Updated dependencies [34cbdc3]
|
||||
- Updated dependencies [f039e8f]
|
||||
- Updated dependencies [e22de9f]
|
||||
- jazz-tools@0.11.0
|
||||
- jazz-browser-media-images@0.11.0
|
||||
- jazz-react@0.11.0
|
||||
|
||||
## 0.0.55
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [2f99de0]
|
||||
- jazz-tools@0.10.15
|
||||
- jazz-browser-media-images@0.10.15
|
||||
- jazz-react@0.10.15
|
||||
|
||||
## 0.0.54
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [75211e3]
|
||||
- jazz-tools@0.10.14
|
||||
- jazz-react@0.10.14
|
||||
- jazz-browser-media-images@0.10.14
|
||||
|
||||
## 0.0.53
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "image-upload",
|
||||
"private": true,
|
||||
"version": "0.0.53",
|
||||
"version": "0.0.56",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,25 @@
|
||||
# jazz-example-inspector
|
||||
|
||||
## 0.0.111
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [b9d194a]
|
||||
- Updated dependencies [a4713df]
|
||||
- Updated dependencies [e22de9f]
|
||||
- Updated dependencies [34cbdc3]
|
||||
- Updated dependencies [0f67e0a]
|
||||
- cojson@0.11.0
|
||||
- cojson-transport-ws@0.11.0
|
||||
|
||||
## 0.0.110
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [f86e278]
|
||||
- cojson@0.10.15
|
||||
- cojson-transport-ws@0.10.15
|
||||
|
||||
## 0.0.109
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-inspector-app",
|
||||
"private": true,
|
||||
"version": "0.0.109",
|
||||
"version": "0.0.111",
|
||||
"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",
|
||||
"cojson": "workspace:0.10.8",
|
||||
"cojson-transport-ws": "workspace:0.10.8",
|
||||
"cojson": "workspace:0.11.0",
|
||||
"cojson-transport-ws": "workspace:0.11.0",
|
||||
"hash-slash": "workspace:0.2.2",
|
||||
"lucide-react": "^0.274.0",
|
||||
"qrcode": "^1.5.3",
|
||||
|
||||
@@ -83,7 +83,10 @@ export default function CoJsonViewerApp() {
|
||||
|
||||
const addAccount = (id: RawAccountID, secret: AgentSecret) => {
|
||||
const newAccount = { id, secret };
|
||||
setAccounts([...accounts, newAccount]);
|
||||
const accountExists = accounts.some((account) => account.id === id);
|
||||
if (!accountExists) {
|
||||
setAccounts([...accounts, newAccount]);
|
||||
}
|
||||
setCurrentAccount(newAccount);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,39 @@
|
||||
# jazz-example-musicplayer
|
||||
|
||||
## 0.0.80
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [6a96d8b]
|
||||
- Updated dependencies [a35249a]
|
||||
- Updated dependencies [b9d194a]
|
||||
- Updated dependencies [a4713df]
|
||||
- Updated dependencies [34cbdc3]
|
||||
- Updated dependencies [b7deb3c]
|
||||
- Updated dependencies [f039e8f]
|
||||
- Updated dependencies [e22de9f]
|
||||
- jazz-tools@0.11.0
|
||||
- jazz-react@0.11.0
|
||||
- jazz-inspector@0.10.13
|
||||
|
||||
## 0.0.79
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [2f99de0]
|
||||
- jazz-tools@0.10.15
|
||||
- jazz-inspector@0.10.12
|
||||
- jazz-react@0.10.15
|
||||
|
||||
## 0.0.78
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [75211e3]
|
||||
- jazz-tools@0.10.14
|
||||
- jazz-inspector@0.10.11
|
||||
- jazz-react@0.10.14
|
||||
|
||||
## 0.0.77
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-example-music-player",
|
||||
"private": true,
|
||||
"version": "0.0.77",
|
||||
"version": "0.0.80",
|
||||
"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.13",
|
||||
"jazz-tools": "workspace:0.10.13",
|
||||
"jazz-react": "workspace:0.11.0",
|
||||
"jazz-tools": "workspace:0.11.0",
|
||||
"lucide-react": "^0.274.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
|
||||
@@ -112,34 +112,31 @@ export async function addTrackToPlaylist(
|
||||
playlist.tracks?.push(track);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Since musicTracks are created as private values (see uploadMusicTracks)
|
||||
* to make them shareable as part of the playlist we are cloning them
|
||||
* and setting the playlist group as owner of the clone
|
||||
*
|
||||
* Doing this for backwards compatibility for when the Group inheritance wasn't possible
|
||||
*/
|
||||
const blob = await FileStream.loadAsBlob(track._refs.file.id);
|
||||
const waveform = await MusicTrackWaveform.load(track._refs.waveform.id, {});
|
||||
|
||||
if (!blob || !waveform) return;
|
||||
|
||||
const trackClone = MusicTrack.create(
|
||||
{
|
||||
file: await FileStream.createFromBlob(blob, playlist._owner),
|
||||
duration: track.duration,
|
||||
waveform: MusicTrackWaveform.create(
|
||||
{ data: waveform.data },
|
||||
playlist._owner,
|
||||
),
|
||||
title: track.title,
|
||||
sourceTrack: track,
|
||||
},
|
||||
playlist._owner,
|
||||
export async function removeTrackFromPlaylist(
|
||||
playlist: Playlist,
|
||||
track: MusicTrack,
|
||||
) {
|
||||
const notAdded = !playlist.tracks?.some(
|
||||
(t) => t?.id === track.id || t?._refs.sourceTrack?.id === track.id,
|
||||
);
|
||||
|
||||
playlist.tracks?.push(trackClone);
|
||||
if (notAdded) return;
|
||||
|
||||
if (track._owner._type === "Group" && playlist._owner._type === "Group") {
|
||||
const trackGroup = track._owner;
|
||||
await trackGroup.revokeExtend(playlist._owner);
|
||||
|
||||
const index =
|
||||
playlist.tracks?.findIndex(
|
||||
(t) => t?.id === track.id || t?._refs.sourceTrack?.id === track.id,
|
||||
) ?? -1;
|
||||
if (index > -1) {
|
||||
playlist.tracks?.splice(index, 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
export async function updatePlaylistTitle(playlist: Playlist, title: string) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useAcceptInvite } from "jazz-react";
|
||||
import { useAcceptInvite, useIsAuthenticated } from "jazz-react";
|
||||
import { ID } from "jazz-tools";
|
||||
import { useCallback } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
@@ -7,6 +7,8 @@ import { MusicaAccount, Playlist } from "./1_schema";
|
||||
export function InvitePage() {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const isAuthenticated = useIsAuthenticated();
|
||||
|
||||
useAcceptInvite({
|
||||
invitedObjectSchema: Playlist,
|
||||
onAccept: useCallback(
|
||||
@@ -32,5 +34,9 @@ export function InvitePage() {
|
||||
),
|
||||
});
|
||||
|
||||
return <p>Accepting invite....</p>;
|
||||
return isAuthenticated ? (
|
||||
<p>Accepting invite....</p>
|
||||
) : (
|
||||
<p>Please sign in to accept the invite.</p>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { MusicTrack, Playlist } from "@/1_schema";
|
||||
import { addTrackToPlaylist } from "@/4_actions";
|
||||
import { addTrackToPlaylist, removeTrackFromPlaylist } from "@/4_actions";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
@@ -10,6 +10,7 @@ import { cn } from "@/lib/utils";
|
||||
import { useAccount, useCoState } from "jazz-react";
|
||||
import { ID } from "jazz-tools";
|
||||
import { MoreHorizontal } from "lucide-react";
|
||||
import { Fragment } from "react/jsx-runtime";
|
||||
import { MusicTrackTitleInput } from "./MusicTrackTitleInput";
|
||||
import { Button } from "./ui/button";
|
||||
|
||||
@@ -46,6 +47,11 @@ export function MusicTrackRow({
|
||||
addTrackToPlaylist(playlist, track);
|
||||
}
|
||||
|
||||
function handleRemoveFromPlaylist(playlist: Playlist) {
|
||||
if (!track) return;
|
||||
removeTrackFromPlaylist(playlist, track);
|
||||
}
|
||||
|
||||
return (
|
||||
<li
|
||||
className={
|
||||
@@ -85,12 +91,20 @@ export function MusicTrackRow({
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
{playlists.map((playlist, index) => (
|
||||
<DropdownMenuItem
|
||||
key={index}
|
||||
onSelect={() => handleAddToPlaylist(playlist)}
|
||||
>
|
||||
Add to {playlist.title}
|
||||
</DropdownMenuItem>
|
||||
<Fragment key={index}>
|
||||
<DropdownMenuItem
|
||||
key={`add-${index}`}
|
||||
onSelect={() => handleAddToPlaylist(playlist)}
|
||||
>
|
||||
Add to {playlist.title}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
key={`remove-${index}`}
|
||||
onSelect={() => handleRemoveFromPlaylist(playlist)}
|
||||
>
|
||||
Remove from {playlist.title}
|
||||
</DropdownMenuItem>
|
||||
</Fragment>
|
||||
))}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
|
||||
@@ -82,3 +82,47 @@ test("create a new playlist and share", async ({
|
||||
await luigiHome.playMusicTrack("Super Mario World");
|
||||
await luigiHome.expectActiveTrackPlaying();
|
||||
});
|
||||
|
||||
test("create a new playlist, share, then remove track", async ({
|
||||
page: marioPage,
|
||||
browser,
|
||||
}) => {
|
||||
// Create playlist with a song and share
|
||||
await marioPage.goto("/");
|
||||
const marioHome = new HomePage(marioPage);
|
||||
await marioHome.expectMusicTrack("Example song");
|
||||
await marioHome.editTrackTitle("Example song", "Super Mario World");
|
||||
await marioHome.createPlaylist();
|
||||
await marioHome.editPlaylistTitle("Save the princess");
|
||||
await marioHome.navigateToPlaylist("All tracks");
|
||||
await marioHome.addTrackToPlaylist("Super Mario World", "Save the princess");
|
||||
await marioHome.navigateToPlaylist("Save the princess");
|
||||
await marioHome.expectMusicTrack("Super Mario World");
|
||||
await marioHome.signUp("Mario");
|
||||
const url = await marioHome.getShareLink();
|
||||
|
||||
await sleep(4000); // Wait for the sync to complete
|
||||
|
||||
// Retrieve shared playlist
|
||||
const luigiContext = await browser.newContext();
|
||||
await mockAuthenticator(luigiContext);
|
||||
const luigiPage = await luigiContext.newPage();
|
||||
await luigiPage.goto("/");
|
||||
const luigiHome = new HomePage(luigiPage);
|
||||
await luigiHome.signUp("Luigi");
|
||||
await luigiPage.goto(url);
|
||||
await luigiHome.expectMusicTrack("Super Mario World");
|
||||
|
||||
// Remove track from playlist
|
||||
await marioHome.navigateToHome();
|
||||
await marioHome.removeTrackFromPlaylist(
|
||||
"Super Mario World",
|
||||
"Save the princess",
|
||||
);
|
||||
await sleep(4000); // Wait for the sync to complete
|
||||
|
||||
// Expect that the track is removed from the playlist
|
||||
await marioHome.navigateToPlaylist("Save the princess");
|
||||
await marioHome.notExpectMusicTrack("Super Mario World");
|
||||
await luigiHome.notExpectMusicTrack("Super Mario World");
|
||||
});
|
||||
|
||||
@@ -33,6 +33,14 @@ export class HomePage {
|
||||
).toBeVisible();
|
||||
}
|
||||
|
||||
async notExpectMusicTrack(trackName: string) {
|
||||
await expect(
|
||||
this.page.getByRole("button", {
|
||||
name: `Play ${trackName}`,
|
||||
}),
|
||||
).not.toBeVisible();
|
||||
}
|
||||
|
||||
async playMusicTrack(trackName: string) {
|
||||
await this.page
|
||||
.getByRole("button", {
|
||||
@@ -65,6 +73,14 @@ export class HomePage {
|
||||
.click();
|
||||
}
|
||||
|
||||
async navigateToHome() {
|
||||
await this.page
|
||||
.getByRole("link", {
|
||||
name: "All tracks",
|
||||
})
|
||||
.click();
|
||||
}
|
||||
|
||||
async getShareLink() {
|
||||
await this.page
|
||||
.getByRole("button", {
|
||||
@@ -95,6 +111,20 @@ export class HomePage {
|
||||
.click();
|
||||
}
|
||||
|
||||
async removeTrackFromPlaylist(trackTitle: string, playlistTitle: string) {
|
||||
await this.page
|
||||
.getByRole("button", {
|
||||
name: `Open ${trackTitle} menu`,
|
||||
})
|
||||
.click();
|
||||
|
||||
await this.page
|
||||
.getByRole("menuitem", {
|
||||
name: `Remove from ${playlistTitle}`,
|
||||
})
|
||||
.click();
|
||||
}
|
||||
|
||||
async signUp(name: string) {
|
||||
await this.page.getByRole("button", { name: "Sign up" }).click();
|
||||
await this.page.getByRole("textbox", { name: "Username" }).fill(name);
|
||||
|
||||
@@ -1,5 +1,35 @@
|
||||
# organization
|
||||
|
||||
## 0.0.52
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [6a96d8b]
|
||||
- Updated dependencies [a35249a]
|
||||
- Updated dependencies [b9d194a]
|
||||
- Updated dependencies [a4713df]
|
||||
- Updated dependencies [34cbdc3]
|
||||
- Updated dependencies [f039e8f]
|
||||
- Updated dependencies [e22de9f]
|
||||
- jazz-tools@0.11.0
|
||||
- jazz-react@0.11.0
|
||||
|
||||
## 0.0.51
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [2f99de0]
|
||||
- jazz-tools@0.10.15
|
||||
- jazz-react@0.10.15
|
||||
|
||||
## 0.0.50
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [75211e3]
|
||||
- jazz-tools@0.10.14
|
||||
- jazz-react@0.10.14
|
||||
|
||||
## 0.0.49
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "organization",
|
||||
"private": true,
|
||||
"version": "0.0.49",
|
||||
"version": "0.0.52",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -23,7 +23,7 @@ export function CreateOrganization() {
|
||||
return;
|
||||
}
|
||||
|
||||
const group = Group.create({ owner: me });
|
||||
const group = Group.create();
|
||||
|
||||
me.root.organizations.push(draft as Organization);
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useCoState } from "jazz-react";
|
||||
import { Account, Group, ID } from "jazz-tools";
|
||||
import { Group } from "jazz-tools";
|
||||
import { Organization } from "../schema.ts";
|
||||
|
||||
export function OrganizationMembers({
|
||||
@@ -10,26 +9,13 @@ export function OrganizationMembers({
|
||||
return (
|
||||
<>
|
||||
{group.members.map((member) => (
|
||||
<Member
|
||||
key={member.id}
|
||||
accountId={member.id as ID<Account>}
|
||||
role={member.role}
|
||||
/>
|
||||
<div key={member.id} className="px-4 py-5 sm:px-6">
|
||||
<strong className="font-medium">
|
||||
{member.account.profile?.name}
|
||||
</strong>{" "}
|
||||
({member.role})
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
function Member({
|
||||
accountId,
|
||||
role,
|
||||
}: { accountId: ID<Account>; role?: string }) {
|
||||
const account = useCoState(Account, accountId, { profile: {} });
|
||||
|
||||
if (!account?.profile) return;
|
||||
|
||||
return (
|
||||
<div className="px-4 py-5 sm:px-6">
|
||||
<strong className="font-medium">{account.profile.name}</strong> ({role})
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
# passkey-svelte
|
||||
|
||||
## 0.0.47
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.11.0
|
||||
|
||||
## 0.0.46
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.10.15
|
||||
|
||||
## 0.0.45
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.10.14
|
||||
|
||||
## 0.0.44
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "passkey-svelte",
|
||||
"version": "0.0.44",
|
||||
"version": "0.0.47",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,35 @@
|
||||
# minimal-auth-passkey
|
||||
|
||||
## 0.0.57
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [6a96d8b]
|
||||
- Updated dependencies [a35249a]
|
||||
- Updated dependencies [b9d194a]
|
||||
- Updated dependencies [a4713df]
|
||||
- Updated dependencies [34cbdc3]
|
||||
- Updated dependencies [f039e8f]
|
||||
- Updated dependencies [e22de9f]
|
||||
- jazz-tools@0.11.0
|
||||
- jazz-react@0.11.0
|
||||
|
||||
## 0.0.56
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [2f99de0]
|
||||
- jazz-tools@0.10.15
|
||||
- jazz-react@0.10.15
|
||||
|
||||
## 0.0.55
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [75211e3]
|
||||
- jazz-tools@0.10.14
|
||||
- jazz-react@0.10.14
|
||||
|
||||
## 0.0.54
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "passkey",
|
||||
"private": true,
|
||||
"version": "0.0.54",
|
||||
"version": "0.0.57",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,35 @@
|
||||
# passphrase
|
||||
|
||||
## 0.0.54
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [6a96d8b]
|
||||
- Updated dependencies [a35249a]
|
||||
- Updated dependencies [b9d194a]
|
||||
- Updated dependencies [a4713df]
|
||||
- Updated dependencies [34cbdc3]
|
||||
- Updated dependencies [f039e8f]
|
||||
- Updated dependencies [e22de9f]
|
||||
- jazz-tools@0.11.0
|
||||
- jazz-react@0.11.0
|
||||
|
||||
## 0.0.53
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [2f99de0]
|
||||
- jazz-tools@0.10.15
|
||||
- jazz-react@0.10.15
|
||||
|
||||
## 0.0.52
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [75211e3]
|
||||
- jazz-tools@0.10.14
|
||||
- jazz-react@0.10.14
|
||||
|
||||
## 0.0.51
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "passphrase",
|
||||
"private": true,
|
||||
"version": "0.0.51",
|
||||
"version": "0.0.54",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,35 @@
|
||||
# jazz-password-manager
|
||||
|
||||
## 0.0.78
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [6a96d8b]
|
||||
- Updated dependencies [a35249a]
|
||||
- Updated dependencies [b9d194a]
|
||||
- Updated dependencies [a4713df]
|
||||
- Updated dependencies [34cbdc3]
|
||||
- Updated dependencies [f039e8f]
|
||||
- Updated dependencies [e22de9f]
|
||||
- jazz-tools@0.11.0
|
||||
- jazz-react@0.11.0
|
||||
|
||||
## 0.0.77
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [2f99de0]
|
||||
- jazz-tools@0.10.15
|
||||
- jazz-react@0.10.15
|
||||
|
||||
## 0.0.76
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [75211e3]
|
||||
- jazz-tools@0.10.14
|
||||
- jazz-react@0.10.14
|
||||
|
||||
## 0.0.75
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-password-manager",
|
||||
"private": true,
|
||||
"version": "0.0.75",
|
||||
"version": "0.0.78",
|
||||
"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.13",
|
||||
"jazz-tools": "workspace:0.10.13",
|
||||
"jazz-react": "workspace:0.11.0",
|
||||
"jazz-tools": "workspace:0.11.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-hook-form": "^7.41.5",
|
||||
|
||||
@@ -6,7 +6,7 @@ import NewItemModal from "./components/new-item-modal";
|
||||
import Table from "./components/table";
|
||||
|
||||
import { useAccount, useCoState } from "jazz-react";
|
||||
import { CoMapInit, Group, ID } from "jazz-tools";
|
||||
import { CoMapInit, ID } from "jazz-tools";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { Folder, FolderList, PasswordItem } from "./1_schema";
|
||||
import {
|
||||
@@ -136,20 +136,14 @@ const VaultPage: React.FC = () => {
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => setEditingItem(item)}
|
||||
disabled={
|
||||
item._owner.castAs(Group).myRole() !== "admin" &&
|
||||
item._owner.castAs(Group).myRole() !== "writer"
|
||||
}
|
||||
disabled={!me.canWrite(item)}
|
||||
>
|
||||
Edit
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => handleDeleteItem(item)}
|
||||
variant="danger"
|
||||
disabled={
|
||||
item._owner.castAs(Group).myRole() !== "admin" &&
|
||||
item._owner.castAs(Group).myRole() !== "writer"
|
||||
}
|
||||
disabled={!me.canWrite(item)}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
@@ -210,21 +204,13 @@ const VaultPage: React.FC = () => {
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
onClick={() => setIsNewItemModalOpen(true)}
|
||||
disabled={
|
||||
!selectedFolder ||
|
||||
(selectedFolder._owner.castAs(Group).myRole() !== "admin" &&
|
||||
selectedFolder._owner.castAs(Group).myRole() !== "writer")
|
||||
}
|
||||
disabled={!selectedFolder || !me.canWrite(selectedFolder)}
|
||||
>
|
||||
New Item
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => setIsInviteModalOpen(true)}
|
||||
disabled={
|
||||
!selectedFolder ||
|
||||
(selectedFolder._owner.castAs(Group).myRole() !== "admin" &&
|
||||
selectedFolder._owner.castAs(Group).myRole() !== "writer")
|
||||
}
|
||||
disabled={!selectedFolder || !me.canWrite(selectedFolder)}
|
||||
>
|
||||
Share Folder
|
||||
</Button>
|
||||
|
||||
@@ -21,11 +21,9 @@ const InviteModal: React.FC<InviteModalProps> = ({
|
||||
>("reader");
|
||||
const [inviteLink, setInviteLink] = useState("");
|
||||
|
||||
const members = selectedFolder?._owner.castAs(Group).members;
|
||||
const members = selectedFolder?._owner.members;
|
||||
const invitedMembers = members
|
||||
? members
|
||||
.filter((m) => !m.account?.isMe && m.role !== "revoked")
|
||||
.map((m) => m.account)
|
||||
? members.filter((m) => !m.account?.isMe).map((m) => m.account)
|
||||
: [];
|
||||
|
||||
const handleCreateInviteLink = () => {
|
||||
|
||||
@@ -1,5 +1,38 @@
|
||||
# jazz-example-pets
|
||||
|
||||
## 0.0.176
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [6a96d8b]
|
||||
- Updated dependencies [a35249a]
|
||||
- Updated dependencies [b9d194a]
|
||||
- Updated dependencies [a4713df]
|
||||
- Updated dependencies [34cbdc3]
|
||||
- Updated dependencies [f039e8f]
|
||||
- Updated dependencies [e22de9f]
|
||||
- jazz-tools@0.11.0
|
||||
- jazz-browser-media-images@0.11.0
|
||||
- jazz-react@0.11.0
|
||||
|
||||
## 0.0.175
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [2f99de0]
|
||||
- jazz-tools@0.10.15
|
||||
- jazz-browser-media-images@0.10.15
|
||||
- jazz-react@0.10.15
|
||||
|
||||
## 0.0.174
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [75211e3]
|
||||
- jazz-tools@0.10.14
|
||||
- jazz-react@0.10.14
|
||||
- jazz-browser-media-images@0.10.14
|
||||
|
||||
## 0.0.173
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-example-pets",
|
||||
"private": true,
|
||||
"version": "0.0.173",
|
||||
"version": "0.0.176",
|
||||
"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.13",
|
||||
"jazz-react": "workspace:0.10.13",
|
||||
"jazz-tools": "workspace:0.10.13",
|
||||
"jazz-browser-media-images": "workspace:0.11.0",
|
||||
"jazz-react": "workspace:0.11.0",
|
||||
"jazz-tools": "workspace:0.11.0",
|
||||
"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.13",
|
||||
"jazz-run": "workspace:0.11.0",
|
||||
"postcss": "^8.4.27",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"typescript": "~5.6.2",
|
||||
|
||||
@@ -34,7 +34,7 @@ export function NewPetPostForm() {
|
||||
if (newPetPost) {
|
||||
newPetPost.name = name;
|
||||
} else {
|
||||
const petPostGroup = Group.create({ owner: me });
|
||||
const petPostGroup = Group.create();
|
||||
const petPost = PartialPetPost.create(
|
||||
{
|
||||
name,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useParams } from "react-router";
|
||||
|
||||
import { PetPost, PetReactions, ReactionTypes } from "./1_schema";
|
||||
|
||||
import { ProgressiveImg } from "jazz-react";
|
||||
import { ProgressiveImg, useAccount } from "jazz-react";
|
||||
import { useCoState } from "jazz-react";
|
||||
import { ID } from "jazz-tools";
|
||||
import uniqolor from "uniqolor";
|
||||
@@ -26,6 +26,7 @@ const reactionEmojiMap: {
|
||||
export function RatePetPostUI() {
|
||||
const petPostID = useParams<{ petPostId: ID<PetPost> }>().petPostId;
|
||||
|
||||
const { me } = useAccount();
|
||||
const petPost = useCoState(PetPost, petPostID);
|
||||
|
||||
return (
|
||||
@@ -60,7 +61,7 @@ export function RatePetPostUI() {
|
||||
))}
|
||||
</div>
|
||||
|
||||
{petPost?._owner.myRole() === "admin" && petPost.reactions && (
|
||||
{petPost && me.canAdmin(petPost) && petPost.reactions && (
|
||||
<ReactionOverview petReactions={petPost.reactions} />
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useState } from "react";
|
||||
|
||||
import { PetPost } from "../1_schema";
|
||||
|
||||
import { createInviteLink } from "jazz-react";
|
||||
import { createInviteLink, useAccount } from "jazz-react";
|
||||
import QRCode from "qrcode";
|
||||
|
||||
import { Button, useToast } from "../basicComponents";
|
||||
@@ -10,9 +10,11 @@ import { Button, useToast } from "../basicComponents";
|
||||
export function ShareButton({ petPost }: { petPost?: PetPost | null }) {
|
||||
const [existingInviteLink, setExistingInviteLink] = useState<string>();
|
||||
const { toast } = useToast();
|
||||
const { me } = useAccount();
|
||||
|
||||
return (
|
||||
petPost?._owner.myRole() === "admin" && (
|
||||
petPost &&
|
||||
me.canAdmin(petPost) && (
|
||||
<Button
|
||||
size="sm"
|
||||
className="py-0"
|
||||
|
||||
@@ -1,5 +1,38 @@
|
||||
# reactions
|
||||
|
||||
## 0.0.56
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [6a96d8b]
|
||||
- Updated dependencies [a35249a]
|
||||
- Updated dependencies [b9d194a]
|
||||
- Updated dependencies [a4713df]
|
||||
- Updated dependencies [34cbdc3]
|
||||
- Updated dependencies [f039e8f]
|
||||
- Updated dependencies [e22de9f]
|
||||
- jazz-tools@0.11.0
|
||||
- jazz-browser-media-images@0.11.0
|
||||
- jazz-react@0.11.0
|
||||
|
||||
## 0.0.55
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [2f99de0]
|
||||
- jazz-tools@0.10.15
|
||||
- jazz-browser-media-images@0.10.15
|
||||
- jazz-react@0.10.15
|
||||
|
||||
## 0.0.54
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [75211e3]
|
||||
- jazz-tools@0.10.14
|
||||
- jazz-react@0.10.14
|
||||
- jazz-browser-media-images@0.10.14
|
||||
|
||||
## 0.0.53
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "reactions",
|
||||
"private": true,
|
||||
"version": "0.0.53",
|
||||
"version": "0.0.56",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -9,8 +9,7 @@ function App() {
|
||||
const router = useIframeHashRouter();
|
||||
|
||||
const createReactions = () => {
|
||||
if (!me) return;
|
||||
const group = Group.create({ owner: me });
|
||||
const group = Group.create();
|
||||
group.addMember("everyone", "writer");
|
||||
const chat = Reactions.create([], { owner: group });
|
||||
router.navigate("/#/reactions/" + chat.id);
|
||||
|
||||
@@ -1,5 +1,39 @@
|
||||
# todo-vue
|
||||
|
||||
## 0.0.61
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [6a96d8b]
|
||||
- Updated dependencies [a35249a]
|
||||
- Updated dependencies [b9d194a]
|
||||
- Updated dependencies [a4713df]
|
||||
- Updated dependencies [34cbdc3]
|
||||
- Updated dependencies [18428ea]
|
||||
- Updated dependencies [f039e8f]
|
||||
- Updated dependencies [e22de9f]
|
||||
- jazz-tools@0.11.0
|
||||
- jazz-browser@0.11.0
|
||||
- jazz-vue@0.11.0
|
||||
|
||||
## 0.0.60
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [2f99de0]
|
||||
- jazz-tools@0.10.15
|
||||
- jazz-browser@0.10.15
|
||||
- jazz-vue@0.10.15
|
||||
|
||||
## 0.0.59
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [75211e3]
|
||||
- jazz-tools@0.10.14
|
||||
- jazz-browser@0.10.14
|
||||
- jazz-vue@0.10.14
|
||||
|
||||
## 0.0.58
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "todo-vue",
|
||||
"version": "0.0.58",
|
||||
"version": "0.0.61",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,35 @@
|
||||
# jazz-example-todo
|
||||
|
||||
## 0.0.175
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [6a96d8b]
|
||||
- Updated dependencies [a35249a]
|
||||
- Updated dependencies [b9d194a]
|
||||
- Updated dependencies [a4713df]
|
||||
- Updated dependencies [34cbdc3]
|
||||
- Updated dependencies [f039e8f]
|
||||
- Updated dependencies [e22de9f]
|
||||
- jazz-tools@0.11.0
|
||||
- jazz-react@0.11.0
|
||||
|
||||
## 0.0.174
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [2f99de0]
|
||||
- jazz-tools@0.10.15
|
||||
- jazz-react@0.10.15
|
||||
|
||||
## 0.0.173
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [75211e3]
|
||||
- jazz-tools@0.10.14
|
||||
- jazz-react@0.10.14
|
||||
|
||||
## 0.0.172
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-example-todo",
|
||||
"private": true,
|
||||
"version": "0.0.172",
|
||||
"version": "0.0.175",
|
||||
"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.13",
|
||||
"jazz-tools": "workspace:0.10.13",
|
||||
"jazz-react": "workspace:0.11.0",
|
||||
"jazz-tools": "workspace:0.11.0",
|
||||
"lucide-react": "^0.274.0",
|
||||
"qrcode": "^1.5.3",
|
||||
"react": "^18.3.1",
|
||||
|
||||
@@ -21,7 +21,7 @@ export function NewProjectForm() {
|
||||
// To create a new todo project, we first create a `Group`,
|
||||
// which is a scope for defining access rights (reader/writer/admin)
|
||||
// of its members, which will apply to all CoValues owned by that group.
|
||||
const projectGroup = Group.create({ owner: me });
|
||||
const projectGroup = Group.create();
|
||||
|
||||
// Then we create an empty todo project within that group
|
||||
const project = TodoProject.create(
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useState } from "react";
|
||||
|
||||
import QRCode from "qrcode";
|
||||
|
||||
import { createInviteLink } from "jazz-react";
|
||||
import { createInviteLink, useAccount } from "jazz-react";
|
||||
import { CoValue } from "jazz-tools";
|
||||
import { Button, useToast } from "../basicComponents";
|
||||
|
||||
@@ -15,9 +15,11 @@ export function InviteButton<T extends CoValue>({
|
||||
}) {
|
||||
const [existingInviteLink, setExistingInviteLink] = useState<string>();
|
||||
const { toast } = useToast();
|
||||
const { me } = useAccount();
|
||||
|
||||
return (
|
||||
value?._owner?.myRole() === "admin" && (
|
||||
value &&
|
||||
me.canAdmin(value) && (
|
||||
<Button
|
||||
size="sm"
|
||||
className="py-0"
|
||||
|
||||
@@ -1,5 +1,35 @@
|
||||
# version-history
|
||||
|
||||
## 0.0.53
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [6a96d8b]
|
||||
- Updated dependencies [a35249a]
|
||||
- Updated dependencies [b9d194a]
|
||||
- Updated dependencies [a4713df]
|
||||
- Updated dependencies [34cbdc3]
|
||||
- Updated dependencies [f039e8f]
|
||||
- Updated dependencies [e22de9f]
|
||||
- jazz-tools@0.11.0
|
||||
- jazz-react@0.11.0
|
||||
|
||||
## 0.0.52
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [2f99de0]
|
||||
- jazz-tools@0.10.15
|
||||
- jazz-react@0.10.15
|
||||
|
||||
## 0.0.51
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [75211e3]
|
||||
- jazz-tools@0.10.14
|
||||
- jazz-react@0.10.14
|
||||
|
||||
## 0.0.50
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "version-history",
|
||||
"private": true,
|
||||
"version": "0.0.50",
|
||||
"version": "0.0.53",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -15,7 +15,7 @@ function App() {
|
||||
const issue = useCoState(Issue, issueID);
|
||||
|
||||
const createIssue = () => {
|
||||
const group = Group.create({ owner: me });
|
||||
const group = Group.create();
|
||||
group.addMember("everyone", "writer");
|
||||
|
||||
const newIssue = Issue.create(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { createInviteLink } from "jazz-react";
|
||||
import { createInviteLink, useAccount } from "jazz-react";
|
||||
import { useCoState } from "jazz-react";
|
||||
import { ID } from "jazz-tools";
|
||||
import { IssueComponent } from "./Issue.tsx";
|
||||
@@ -21,14 +21,15 @@ export function ProjectComponent({ projectID }: { projectID: ID<Project> }) {
|
||||
estimate: 0,
|
||||
status: "backlog",
|
||||
},
|
||||
{ owner: project._owner },
|
||||
project._owner,
|
||||
),
|
||||
);
|
||||
};
|
||||
const { me } = useAccount();
|
||||
return project ? (
|
||||
<div>
|
||||
<h1>{project.name}</h1>
|
||||
{project._owner?.myRole() === "admin" && (
|
||||
{me.canAdmin(project) && (
|
||||
<>
|
||||
<button onClick={() => invite("reader")}>Invite Guest</button>
|
||||
<button onClick={() => invite("writer")}>Invite Member</button>
|
||||
|
||||
49
homepage/design-system/src/app/components/atoms/Alert.tsx
Normal file
49
homepage/design-system/src/app/components/atoms/Alert.tsx
Normal file
@@ -0,0 +1,49 @@
|
||||
import clsx from "clsx";
|
||||
import type { ReactNode } from "react";
|
||||
import { Icon } from "./Icon";
|
||||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
variant?: "warning" | "info";
|
||||
title: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function Alert({
|
||||
children,
|
||||
variant = "warning",
|
||||
title,
|
||||
className,
|
||||
}: Props) {
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
"border-l-4 p-4 pl-6 dark:bg-red-200/5 overflow-hidden relative rounded",
|
||||
{
|
||||
"border-yellow-400 bg-yellow-50 dark:border-yellow-500 dark:bg-yellow-200/5":
|
||||
variant === "warning",
|
||||
"border-blue-400 bg-blue-50 dark:border-blue-500 dark:bg-blue-200/5":
|
||||
variant === "info",
|
||||
},
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<span
|
||||
className={clsx(
|
||||
"text-sm font-bold flex items-center gap-1",
|
||||
variant === "warning" && "text-yellow-700 dark:text-yellow-400",
|
||||
variant === "info" && "text-blue-700 dark:text-blue-400",
|
||||
)}
|
||||
>
|
||||
<Icon
|
||||
name={variant}
|
||||
size="7xl"
|
||||
className="absolute -z-10 right-0 opacity-5 top-0 rotate-12 pointer-events-none"
|
||||
/>
|
||||
<Icon name={variant} size="xs" />
|
||||
{title}
|
||||
</span>
|
||||
<span className={clsx("text-sm")}>{children}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
import {
|
||||
AlertCircleIcon,
|
||||
AlertTriangleIcon,
|
||||
ArrowDownIcon,
|
||||
ArrowRightIcon,
|
||||
BookTextIcon,
|
||||
@@ -16,9 +18,10 @@ import {
|
||||
GlobeIcon,
|
||||
HashIcon,
|
||||
ImageIcon,
|
||||
InfoIcon,
|
||||
LinkIcon,
|
||||
LockKeyholeIcon,
|
||||
LucideIcon,
|
||||
type LucideIcon,
|
||||
MailIcon,
|
||||
MenuIcon,
|
||||
MessageCircleQuestionIcon,
|
||||
@@ -71,6 +74,8 @@ const icons = {
|
||||
touchId: FingerprintIcon,
|
||||
upload: UploadCloudIcon,
|
||||
zip: FolderArchiveIcon,
|
||||
warning: AlertTriangleIcon,
|
||||
info: InfoIcon,
|
||||
};
|
||||
|
||||
// copied from tailwind line height https://tailwindcss.com/docs/font-size
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# Frequently Asked Questions
|
||||
|
||||
## How established is Jazz?
|
||||
|
||||
Jazz is backed by fantastic angel and institutional investors with experience and know-how in devtools and has been in development since 2020.
|
||||
|
||||
## Will Jazz be around long-term?
|
||||
|
||||
We're committed to Jazz being around for a long time! We understand that when you choose Jazz for your projects, you're investing time and making a significant architectural choice, and we take that responsibility seriously.
|
||||
That's why we've designed Jazz with longevity in mind from the start:
|
||||
|
||||
- The open source nature of our sync server means you'll always be able to run your own infrastructure
|
||||
- Your data remains accessible even if our cloud services change
|
||||
- We're designing the protocol as an open specification
|
||||
|
||||
This approach creates a foundation that can continue regardless of any single company's involvement. The local-first architecture means your apps will always work, even offline, and your data remains yours.
|
||||
|
||||
@@ -133,6 +133,36 @@ teamGroup.extend(companyGroup);
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
## Revoking a group extension
|
||||
|
||||
You can revoke a group extension by using the `revokeExtend` method:
|
||||
|
||||
<CodeGroup>
|
||||
```typescript
|
||||
const parentGroup = Group.create();
|
||||
const childGroup = Group.create();
|
||||
|
||||
childGroup.extend(parentGroup);
|
||||
|
||||
// Revoke the extension
|
||||
await childGroup.revokeExtend(parentGroup);
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
## Getting all parent groups
|
||||
|
||||
You can get all the parent groups of a group by calling the `getParentGroups` method:
|
||||
|
||||
<CodeGroup>
|
||||
```typescript
|
||||
const childGroup = Group.create();
|
||||
const parentGroup = Group.create();
|
||||
childGroup.extend(parentGroup);
|
||||
|
||||
console.log(childGroup.getParentGroups()); // [parentGroup]
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
## Example: Team Hierarchy
|
||||
|
||||
Here's a practical example of using group inheritance for team permissions:
|
||||
|
||||
@@ -9,8 +9,6 @@ Every CoValue has an owner, which can be a `Group` or an `Account`.
|
||||
You can use a `Group` to grant access to a CoValue to multiple users. These users can
|
||||
have different roles, such as "writer", "reader" or "admin".
|
||||
|
||||
...more docs coming soon
|
||||
|
||||
## Creating a Group
|
||||
|
||||
Here's how you can create a `Group`.
|
||||
@@ -19,7 +17,7 @@ Here's how you can create a `Group`.
|
||||
```tsx
|
||||
import { Group } from "jazz-tools";
|
||||
|
||||
const group = Group.create({ owner: me });
|
||||
const group = Group.create();
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
@@ -53,7 +51,6 @@ const bob = await Account.load(bobsID as ID<Account>, []);
|
||||
group.addMember(bob, "writer");
|
||||
```
|
||||
</CodeGroup>
|
||||
...more docs coming soon
|
||||
|
||||
## Getting the Group of an existing CoValue
|
||||
|
||||
@@ -77,7 +74,47 @@ import { Group } from "jazz-tools";
|
||||
|
||||
const group = existingCoValue._owner.castAs(Group);
|
||||
group.addMember(bob, "writer");
|
||||
group.myRole();
|
||||
|
||||
const role = group.getRoleOf(bob);
|
||||
```
|
||||
</CodeGroup>
|
||||
...more docs coming soon
|
||||
|
||||
## Checking the permissions
|
||||
|
||||
You can check the permissions of an account on a CoValue by using the `canRead`, `canWrite` and `canAdmin` methods.
|
||||
|
||||
<CodeGroup>
|
||||
```tsx
|
||||
const value = await MyCoMap.load(valueID, {});
|
||||
const me = Account.getMe();
|
||||
|
||||
if (me.canAdmin(value)) {
|
||||
console.log("I can share value with others");
|
||||
} else if (me.canWrite(value)) {
|
||||
console.log("I can edit value");
|
||||
} else if (me.canRead(value)) {
|
||||
console.log("I can view value");
|
||||
} else {
|
||||
console.log("I cannot access value");
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
To check the permissions of another account, you need to load it first:
|
||||
|
||||
<CodeGroup>
|
||||
```tsx
|
||||
const value = await MyCoMap.load(valueID, {});
|
||||
const bob = await Account.load(accountID, []);
|
||||
|
||||
if (bob.canAdmin(value)) {
|
||||
console.log("Bob can share value with others");
|
||||
} else if (bob.canWrite(value)) {
|
||||
console.log("Bob can edit value");
|
||||
} else if (bob.canRead(value)) {
|
||||
console.log("Bob can view value");
|
||||
} else {
|
||||
console.log("Bob cannot access value");
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
@@ -749,6 +749,8 @@ import { createInviteLink } from "jazz-react";
|
||||
export function ProjectComponent({ projectID }: { projectID: ID<Project> }) {// old
|
||||
const project = useCoState(Project, projectID, { issues: [{}] }); // old
|
||||
|
||||
const { me } = useAccount();
|
||||
|
||||
const invite = (role: "reader" | "writer") => {
|
||||
const link = createInviteLink(project, role, { valueHint: "project" });
|
||||
navigator.clipboard.writeText(link);
|
||||
@@ -766,7 +768,7 @@ export function ProjectComponent({ projectID }: { projectID: ID<Project> }) {//
|
||||
return project ? (// old
|
||||
<div>// old
|
||||
<h1>{project.name}</h1>// old
|
||||
{project._owner?.myRole() === "admin" && (
|
||||
{me.canAdmin(project) && (
|
||||
<>
|
||||
<button onClick={() => invite("reader")}>Invite Guest</button>
|
||||
<button onClick={() => invite("writer")}>Invite Member</button>
|
||||
|
||||
@@ -146,8 +146,9 @@ Jazz waits for the migration to finish before passing the account to your app's
|
||||
```ts
|
||||
export class MyAppAccount extends Account {
|
||||
root = co.ref(MyAppRoot);
|
||||
profile = co.ref(MyAppProfile);
|
||||
|
||||
async migrate() {
|
||||
async migrate(this: MyAppAccount, creationProps?: { name: string }) {
|
||||
// we specifically need to check for undefined,
|
||||
// because the root might simply be not loaded (`null`) yet
|
||||
if (this.root === undefined) {
|
||||
@@ -159,6 +160,17 @@ export class MyAppAccount extends Account {
|
||||
myContacts: ListOfAccounts.create([], Group.create())
|
||||
});
|
||||
}
|
||||
|
||||
if (this.profile === undefined) {
|
||||
const profileGroup = Group.create();
|
||||
// Unlike the root, we want the profile to be publicly readable.
|
||||
profileGroup.addMember("everyone", "reader");
|
||||
|
||||
this.profile = MyAppProfile.create({
|
||||
name: creationProps?.name,
|
||||
bookmarks: ListOfBookmarks.create([], profileGroup),
|
||||
}, profileGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -179,7 +191,7 @@ Now let's say we want to add a `myBookmarks` field to the `root` schema:
|
||||
export class MyAppAccount extends Account {
|
||||
root = co.ref(MyAppRoot);// old
|
||||
|
||||
async migrate() { // old
|
||||
async migrate(this: MyAppAccount) {
|
||||
if (this.root === undefined) { // old
|
||||
this.root = MyAppRoot.create({ // old
|
||||
myChats: ListOfChats.create([], Group.create()), // old
|
||||
@@ -188,12 +200,10 @@ export class MyAppAccount extends Account {
|
||||
} // old
|
||||
|
||||
// We need to load the root field to check for the myContacts field
|
||||
const result = await this.ensureLoaded({
|
||||
const { root } = await this.ensureLoaded({
|
||||
root: {},
|
||||
});
|
||||
|
||||
const { root } = result;
|
||||
|
||||
// we specifically need to check for undefined,
|
||||
// because myBookmarks might simply be not loaded (`null`) yet
|
||||
if (root.myBookmarks === undefined) {
|
||||
|
||||
@@ -0,0 +1,247 @@
|
||||
import { ContentByFramework, CodeGroup } from '@/components/forMdx'
|
||||
import { Alert } from "gcmp-design-system/src/app/components/atoms/Alert";
|
||||
|
||||
export const metadata = { title: "Upgrade to Jazz 0.11.0" };
|
||||
|
||||
# Jazz 0.11.0 is out!
|
||||
|
||||
Jazz 0.11.0 brings several improvements to member handling, roles, and permissions management. This guide will help you upgrade your application to the latest version.
|
||||
|
||||
## What's new?
|
||||
Here is what's changed in this release:
|
||||
- [New permissions check APIs](#new-permissions-check-apis): New methods like `canRead`, `canWrite`, `canAdmin`, and `getRoleOf` to simplify permission checks.
|
||||
- [Group.revokeExtend](#grouprevokeextend): New method to revoke group extension permissions.
|
||||
- [Group.getParentGroups](#accountgetparentgroups): New method to get all the parent groups of an account.
|
||||
- [Account Profile & Migrations](#account-profile--migrations): Fixed issues with custom account profile migrations for a more consistent experience
|
||||
- [Dropped support for Accounts owning Profiles](#dropped-support-for-accounts-owning-profiles): Profiles can now only be owned by Groups.
|
||||
- [Group.members now includes inherited members](#member-inheritance-changes): Updated behavior for the `members` getter method to include inherited members and have a more intuitive type definition.
|
||||
|
||||
## New Features
|
||||
|
||||
### New permissions check APIs
|
||||
|
||||
New methods have been added to both `Account` and `Group` classes to improve permission handling:
|
||||
|
||||
#### Permission checks
|
||||
|
||||
The new `canRead`, `canWrite` and `canAdmin` methods on `Account` allow you to easily check if the account has specific permissions on a CoValue:
|
||||
|
||||
<CodeGroup>
|
||||
{/* prettier-ignore */}
|
||||
```typescript
|
||||
const me = Account.getMe();
|
||||
|
||||
if (me.canAdmin(value)) {
|
||||
console.log("I can share value with others");
|
||||
} else if (me.canWrite(value)) {
|
||||
console.log("I can edit value");
|
||||
} else if (me.canRead(value)) {
|
||||
console.log("I can view value");
|
||||
} else {
|
||||
console.log("I cannot access value");
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
#### Getting the role of an account
|
||||
|
||||
The `getRoleOf` method has been added to query the role of specific entities:
|
||||
|
||||
<CodeGroup>
|
||||
```typescript
|
||||
const group = Group.create();
|
||||
group.getRoleOf(me); // admin
|
||||
group.getRoleOf(Eve); // undefined
|
||||
|
||||
group.addMember(Eve, "writer");
|
||||
group.getRoleOf(Eve); // writer
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
#### Group.revokeExtend
|
||||
|
||||
We added a new method to revoke the extend of a Group:
|
||||
<CodeGroup>
|
||||
```typescript
|
||||
function addTrackToPlaylist(playlist: Playlist, track: MusicTrack) {
|
||||
const trackGroup = track._owner.castAs(Group);
|
||||
trackGroup.extend(playlist._owner, "reader"); // Grant read access to the track to the playlist accounts
|
||||
|
||||
playlist.tracks.push(track);
|
||||
}
|
||||
|
||||
function removeTrackFromPlaylist(playlist: Playlist, track: MusicTrack) {
|
||||
const trackGroup = track._owner.castAs(Group);
|
||||
trackGroup.revokeExtend(playlist._owner); // Revoke access to the track to the playlist accounts
|
||||
|
||||
const index = playlist.tracks.findIndex(t => t.id === track.id);
|
||||
if (index !== -1) {
|
||||
playlist.tracks.splice(index, 1);
|
||||
}
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### Group.getParentGroups
|
||||
|
||||
The `getParentGroups` method has been added to `Group` to get all the parent groups of a group.
|
||||
|
||||
<CodeGroup>
|
||||
```ts
|
||||
const childGroup = Group.create();
|
||||
const parentGroup = Group.create();
|
||||
childGroup.extend(parentGroup);
|
||||
|
||||
console.log(childGroup.getParentGroups()); // [parentGroup]
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
### Account Profile & Migrations
|
||||
|
||||
The previous way of making the `Profile` migration work was to assume that the profile was always already there:
|
||||
|
||||
<CodeGroup>
|
||||
```ts
|
||||
export class MyAppAccount extends Account {
|
||||
profile = co.ref(MyAppProfile);
|
||||
|
||||
async migrate(this: MyAppAccount, creationProps: { name: string, lastName: string }) {
|
||||
if (creationProps) {
|
||||
const { profile } = await this.ensureLoaded({ profile: {} });
|
||||
|
||||
profile.name = creationProps.name;
|
||||
profile.bookmarks = ListOfBookmarks.create([], profileGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
This was kind-of tricky to picture, and having different migration strategies for different CoValues was confusing.
|
||||
|
||||
We changed the logic so the default profile is created only if you didn't provide one in your migration.
|
||||
|
||||
This way you can use the same pattern for both `root` and `profile` migrations:
|
||||
<CodeGroup>
|
||||
```ts
|
||||
export class MyAppAccount extends Account {
|
||||
profile = co.ref(MyAppProfile);
|
||||
|
||||
async migrate(this: MyAppAccount, creationProps?: { name: string }) {
|
||||
if (this.profile === undefined) {
|
||||
const profileGroup = Group.create();
|
||||
profileGroup.addMember("everyone", "reader");
|
||||
|
||||
this.profile = MyAppProfile.create({
|
||||
name: creationProps?.name,
|
||||
bookmarks: ListOfBookmarks.create([], profileGroup),
|
||||
}, profileGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
<Alert variant="warning" title="Warning" className="mt-4">
|
||||
If you provide a custom `Profile` in your `Account` schema and migration for a Worker account,
|
||||
make sure to also add `everyone` as member with `reader` role to the owning group.
|
||||
Failing to do so will prevent any account from sending messages to the Worker's Inbox.
|
||||
</Alert>
|
||||
|
||||
### Dropped support for Accounts owning Profiles
|
||||
Starting from `0.11.0` `Profile`s can only be owned by `Group`s.
|
||||
|
||||
<Alert variant="info" title="Note" className="mt-4">
|
||||
Existing profiles owned by `Account`s will still work, but you will get incorrect types when accessing a `Profile`'s `_owner`.
|
||||
</Alert>
|
||||
|
||||
### Member Inheritance Changes
|
||||
|
||||
The behavior of groups' `members` getter method has been updated to return both direct members and inherited ones from ancestor groups.
|
||||
This might affect your application if you were relying on only direct members being returned.
|
||||
|
||||
<CodeGroup>
|
||||
```ts
|
||||
/**
|
||||
* The following pseudocode only illustrates the inheritance logic,
|
||||
* the actual implementation is different.
|
||||
*/
|
||||
|
||||
const parentGroup = Group.create();
|
||||
parentGroup.addMember(John, "admin");
|
||||
|
||||
const childGroup = Group.create();
|
||||
childGroup.addMember(Eve, "admin");
|
||||
|
||||
childGroup.extend(parentGroup);
|
||||
|
||||
console.log(childGroup.members);
|
||||
// Before 0.11.0
|
||||
// [Eve]
|
||||
|
||||
// After 0.11.0
|
||||
// [Eve, John]
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
Additionally:
|
||||
- now `Group.members` doesn't include the `everyone` member anymore
|
||||
- the account type in `Group.members` is now the globally registered Account schema and we have removed the `co.members` way to define an AccountSchema for members
|
||||
|
||||
If you need to explicitly check if "everyone" is a member of a group, you can use the `getRoleOf` method instead:
|
||||
<CodeGroup>
|
||||
```ts
|
||||
if (group.getRoleOf("everyone")) {
|
||||
console.log("Everyone has access to the group");
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
|
||||
#### Migration Steps
|
||||
|
||||
1. Review your member querying logic to account for inherited members.
|
||||
2. Update your permission checking code to utilize the new [`hasPermissions` and `getRoleOf`](#enhanced-permission-management) methods.
|
||||
3. Consider implementing `"everyone"` role checks where appropriate.
|
||||
|
||||
### Removed auto-update of `profile.name` in `usePasskeyAuth`
|
||||
|
||||
The `usePasskeyAuth` hook now doesn't update the `profile.name` if the provided username is empty.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
> I'm getting the following error: `Error: Profile must be owned by a Group`
|
||||
|
||||
If you previously forced a migration of your `Account` schema to include a custom `Profile`,
|
||||
and assigned its ownership to an `Account`, you need to recreate your profile code and assign it to a `Group` instead.
|
||||
|
||||
<CodeGroup>
|
||||
```ts
|
||||
export class MyAppAccount extends Account {
|
||||
profile = co.ref(MyAppProfile);
|
||||
|
||||
override async migrate() {
|
||||
// ...
|
||||
|
||||
const me = await this.ensureLoaded({
|
||||
profile: {},
|
||||
});
|
||||
|
||||
if ((me.profile._owner as Group | Account)._type === "Account") {
|
||||
const profileGroup = Group.create();
|
||||
profileGroup.addMember("everyone", "reader");
|
||||
|
||||
// recreate your profile here...
|
||||
me.profile = Profile.create(
|
||||
{
|
||||
name: me.profile.name,
|
||||
},
|
||||
profileGroup,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
@@ -13,6 +13,10 @@ export function JazzFooter() {
|
||||
{
|
||||
title: "About",
|
||||
links: [
|
||||
{
|
||||
href: "/status",
|
||||
label: "Status",
|
||||
},
|
||||
{
|
||||
href: "https://garden.co/team",
|
||||
label: "Team",
|
||||
@@ -23,11 +27,6 @@ export function JazzFooter() {
|
||||
label: "Blog",
|
||||
newTab: true,
|
||||
},
|
||||
{
|
||||
href: "https://github.com/garden-co/jazz/releases",
|
||||
label: "Releases",
|
||||
newTab: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -38,13 +38,8 @@ export function JazzNav({ sections }: { sections?: NavSection[] }) {
|
||||
newTab: true,
|
||||
},
|
||||
{
|
||||
title: "Releases",
|
||||
href: "https://github.com/garden-co/jazz/releases",
|
||||
newTab: true,
|
||||
},
|
||||
{
|
||||
title: "Status",
|
||||
href: "/status",
|
||||
title: "Status",
|
||||
},
|
||||
]}
|
||||
socials={socials}
|
||||
|
||||
@@ -34,6 +34,7 @@ export const docNavigationItems = [
|
||||
href: "/docs/inspector",
|
||||
done: 100,
|
||||
},
|
||||
{ name: "FAQ", href: "/docs/faq", done: 100 },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -65,6 +66,12 @@ export const docNavigationItems = [
|
||||
{
|
||||
name: "Upgrade guides",
|
||||
items: [
|
||||
{
|
||||
// upgrade guides
|
||||
name: "0.11.0 - Roles and permissions",
|
||||
href: "/docs/upgrade/0-11-0",
|
||||
done: 100,
|
||||
},
|
||||
{
|
||||
// upgrade guides
|
||||
name: "0.10.0 - New authentication flow",
|
||||
|
||||
@@ -1,5 +1,26 @@
|
||||
# cojson-storage-indexeddb
|
||||
|
||||
## 0.11.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- a4713df: Moving to the d.ts files for the exported type definitions
|
||||
- Updated dependencies [b9d194a]
|
||||
- Updated dependencies [a4713df]
|
||||
- Updated dependencies [e22de9f]
|
||||
- Updated dependencies [34cbdc3]
|
||||
- Updated dependencies [0f67e0a]
|
||||
- cojson@0.11.0
|
||||
- cojson-storage@0.11.0
|
||||
|
||||
## 0.10.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [f86e278]
|
||||
- cojson@0.10.15
|
||||
- cojson-storage@0.10.15
|
||||
|
||||
## 0.10.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "cojson-storage-indexeddb",
|
||||
"version": "0.10.8",
|
||||
"version": "0.11.0",
|
||||
"main": "dist/index.js",
|
||||
"type": "module",
|
||||
"types": "src/index.ts",
|
||||
"types": "dist/index.d.ts",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cojson": "workspace:*",
|
||||
@@ -21,8 +21,7 @@
|
||||
"test:watch": "vitest --watch --root ../../ --project cojson-storage-indexeddb",
|
||||
"format-and-lint": "biome check .",
|
||||
"format-and-lint:fix": "biome check . --write",
|
||||
"build": "rm -rf ./dist && tsc --sourceMap --outDir dist",
|
||||
"prepublishOnly": "npm run build"
|
||||
"build": "rm -rf ./dist && tsc --sourceMap --outDir dist"
|
||||
},
|
||||
"gitHead": "33c27053293b4801b968c61d5c4c989f93a67d13"
|
||||
}
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"esModuleInterop": true
|
||||
"esModuleInterop": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true
|
||||
},
|
||||
"include": ["./src/**/*"]
|
||||
}
|
||||
|
||||
@@ -1,5 +1,26 @@
|
||||
# cojson-storage-sqlite
|
||||
|
||||
## 0.8.68
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- a4713df: Moving to the d.ts files for the exported type definitions
|
||||
- Updated dependencies [b9d194a]
|
||||
- Updated dependencies [a4713df]
|
||||
- Updated dependencies [e22de9f]
|
||||
- Updated dependencies [34cbdc3]
|
||||
- Updated dependencies [0f67e0a]
|
||||
- cojson@0.11.0
|
||||
- cojson-storage@0.11.0
|
||||
|
||||
## 0.8.67
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [f86e278]
|
||||
- cojson@0.10.15
|
||||
- cojson-storage@0.10.15
|
||||
|
||||
## 0.8.66
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "cojson-storage-rn-sqlite",
|
||||
"type": "module",
|
||||
"version": "0.8.66",
|
||||
"version": "0.8.68",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.ts",
|
||||
"types": "dist/index.d.ts",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cojson": "workspace:*",
|
||||
@@ -17,7 +17,6 @@
|
||||
"dev": "tsc --watch --sourceMap --outDir dist",
|
||||
"format-and-lint": "biome check .",
|
||||
"format-and-lint:fix": "biome check . --write",
|
||||
"build": "rm -rf ./dist && tsc --sourceMap --outDir dist",
|
||||
"prepublishOnly": "npm run build"
|
||||
"build": "rm -rf ./dist && tsc --sourceMap --outDir dist"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"esModuleInterop": true
|
||||
"esModuleInterop": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true
|
||||
},
|
||||
"include": ["./src/**/*"]
|
||||
}
|
||||
|
||||
@@ -1,5 +1,26 @@
|
||||
# cojson-storage-sqlite
|
||||
|
||||
## 0.11.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- a4713df: Moving to the d.ts files for the exported type definitions
|
||||
- Updated dependencies [b9d194a]
|
||||
- Updated dependencies [a4713df]
|
||||
- Updated dependencies [e22de9f]
|
||||
- Updated dependencies [34cbdc3]
|
||||
- Updated dependencies [0f67e0a]
|
||||
- cojson@0.11.0
|
||||
- cojson-storage@0.11.0
|
||||
|
||||
## 0.10.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [f86e278]
|
||||
- cojson@0.10.15
|
||||
- cojson-storage@0.10.15
|
||||
|
||||
## 0.10.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"name": "cojson-storage-sqlite",
|
||||
"type": "module",
|
||||
"version": "0.10.8",
|
||||
"version": "0.11.0",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.ts",
|
||||
"types": "dist/index.d.ts",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"better-sqlite3": "^11.7.0",
|
||||
"cojson": "workspace:0.10.8",
|
||||
"cojson": "workspace:0.11.0",
|
||||
"cojson-storage": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -18,8 +18,7 @@
|
||||
"dev": "tsc --watch --sourceMap --outDir dist",
|
||||
"format-and-lint": "biome check .",
|
||||
"format-and-lint:fix": "biome check . --write",
|
||||
"build": "rm -rf ./dist && tsc --sourceMap --outDir dist",
|
||||
"prepublishOnly": "npm run build"
|
||||
"build": "rm -rf ./dist && tsc --sourceMap --outDir dist"
|
||||
},
|
||||
"gitHead": "33c27053293b4801b968c61d5c4c989f93a67d13"
|
||||
}
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"esModuleInterop": true
|
||||
"esModuleInterop": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true
|
||||
},
|
||||
"include": ["./src/**/*"]
|
||||
}
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
# cojson-storage
|
||||
|
||||
## 0.11.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- a4713df: Moving to the d.ts files for the exported type definitions
|
||||
- Updated dependencies [b9d194a]
|
||||
- Updated dependencies [a4713df]
|
||||
- Updated dependencies [e22de9f]
|
||||
- Updated dependencies [34cbdc3]
|
||||
- Updated dependencies [0f67e0a]
|
||||
- cojson@0.11.0
|
||||
|
||||
## 0.10.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [f86e278]
|
||||
- cojson@0.10.15
|
||||
|
||||
## 0.10.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "cojson-storage",
|
||||
"version": "0.10.8",
|
||||
"version": "0.11.0",
|
||||
"main": "dist/index.js",
|
||||
"type": "module",
|
||||
"types": "src/index.ts",
|
||||
"types": "dist/index.d.ts",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cojson": "workspace:*"
|
||||
@@ -18,7 +18,6 @@
|
||||
"test:watch": "vitest --watch --root ../../ --project cojson-storage",
|
||||
"format-and-lint": "biome check .",
|
||||
"format-and-lint:fix": "biome check . --write",
|
||||
"build": "rm -rf ./dist && tsc --sourceMap --outDir dist",
|
||||
"prepublishOnly": "npm run build"
|
||||
"build": "rm -rf ./dist && tsc --sourceMap --outDir dist"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"esModuleInterop": true
|
||||
"esModuleInterop": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true
|
||||
},
|
||||
"include": ["./src/**/*"]
|
||||
}
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
# cojson-transport-nodejs-ws
|
||||
|
||||
## 0.11.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- a4713df: Moving to the d.ts files for the exported type definitions
|
||||
- Updated dependencies [b9d194a]
|
||||
- Updated dependencies [a4713df]
|
||||
- Updated dependencies [e22de9f]
|
||||
- Updated dependencies [34cbdc3]
|
||||
- Updated dependencies [0f67e0a]
|
||||
- cojson@0.11.0
|
||||
|
||||
## 0.10.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [f86e278]
|
||||
- cojson@0.10.15
|
||||
|
||||
## 0.10.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "cojson-transport-ws",
|
||||
"type": "module",
|
||||
"version": "0.10.8",
|
||||
"version": "0.11.0",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.ts",
|
||||
"types": "dist/index.d.ts",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cojson": "workspace:*",
|
||||
@@ -15,8 +15,7 @@
|
||||
"format-and-lint:fix": "biome check . --write",
|
||||
"test": "vitest --run --root ../../ --project cojson-transport-ws",
|
||||
"test:watch": "vitest --watch --root ../../ --project cojson-transport-ws",
|
||||
"build": "rm -rf ./dist && tsc --sourceMap --outDir dist",
|
||||
"prepublishOnly": "npm run build"
|
||||
"build": "rm -rf ./dist && tsc --sourceMap --outDir dist"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/ws": "8.5.10",
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { SyncMessage } from "cojson";
|
||||
import type { SyncMessage } from "cojson";
|
||||
import { addMessageToBacklog } from "./serialization.js";
|
||||
|
||||
export const MAX_OUTGOING_MESSAGES_CHUNK_BYTES = 25_000;
|
||||
|
||||
export class BatchedOutgoingMessages {
|
||||
private backlog: string = "";
|
||||
private backlog = "";
|
||||
private timeout: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
constructor(private send: (messages: string) => void) {}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Peer, logger } from "cojson";
|
||||
import { type Peer, logger } from "cojson";
|
||||
import { createWebSocketPeer } from "./createWebSocketPeer.js";
|
||||
|
||||
export class WebSocketPeerWithReconnection {
|
||||
@@ -61,7 +61,7 @@ export class WebSocketPeerWithReconnection {
|
||||
const timeout = this.reconnectionTimeout * this.reconnectionAttempts;
|
||||
|
||||
logger.debug(
|
||||
"Websocket disconnected, trying to reconnect in " + timeout + "ms",
|
||||
`Websocket disconnected, trying to reconnect in ${timeout}ms`,
|
||||
);
|
||||
|
||||
await this.waitForOnline(timeout);
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import {
|
||||
DisconnectedError,
|
||||
Peer,
|
||||
PingTimeoutError,
|
||||
SyncMessage,
|
||||
type DisconnectedError,
|
||||
type Peer,
|
||||
type PingTimeoutError,
|
||||
type SyncMessage,
|
||||
cojsonInternals,
|
||||
logger,
|
||||
} from "cojson";
|
||||
import { BatchedOutgoingMessages } from "./BatchedOutgoingMessages.js";
|
||||
import { deserializeMessages, getErrorMessage } from "./serialization.js";
|
||||
import { AnyWebSocket } from "./types.js";
|
||||
import type { AnyWebSocket } from "./types.js";
|
||||
|
||||
export const BUFFER_LIMIT = 100_000;
|
||||
export const BUFFER_LIMIT_POLLING_INTERVAL = 10;
|
||||
@@ -52,7 +52,7 @@ function waitForWebSocketOpen(websocket: AnyWebSocket) {
|
||||
if (websocket.readyState === 1) {
|
||||
resolve();
|
||||
} else {
|
||||
websocket.addEventListener("open", resolve, { once: true });
|
||||
websocket.addEventListener("open", () => resolve(), { once: true });
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -144,6 +144,8 @@ export function createWebSocketPeer({
|
||||
}
|
||||
|
||||
websocket.addEventListener("close", handleClose);
|
||||
// TODO (#1537): Remove this any once the WebSocket error event type is fixed
|
||||
// biome-ignore lint/suspicious/noExplicitAny: WebSocket error event type
|
||||
websocket.addEventListener("error" as any, (err) => {
|
||||
if (err.message) {
|
||||
logger.warn(err.message);
|
||||
@@ -174,7 +176,7 @@ export function createWebSocketPeer({
|
||||
|
||||
if (!result.ok) {
|
||||
logger.warn(
|
||||
"Error while deserializing messages: " + getErrorMessage(result.error),
|
||||
`Error while deserializing messages: ${getErrorMessage(result.error)}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -227,7 +229,7 @@ export function createWebSocketPeer({
|
||||
},
|
||||
{ once: true },
|
||||
);
|
||||
} else if (websocket.readyState == 1) {
|
||||
} else if (websocket.readyState === 1) {
|
||||
websocket.close();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { SyncMessage, logger } from "cojson";
|
||||
import { PingMsg } from "./types.js";
|
||||
import { type SyncMessage, logger } from "cojson";
|
||||
import type { PingMsg } from "./types.js";
|
||||
|
||||
export function getErrorMessage(error: unknown) {
|
||||
return error instanceof Error ? error.message : "Unknown error";
|
||||
@@ -28,7 +28,7 @@ export function deserializeMessages(messages: unknown) {
|
||||
| PingMsg[],
|
||||
} as const;
|
||||
} catch (e) {
|
||||
logger.error("Error while deserializing messages: " + getErrorMessage(e));
|
||||
logger.error(`Error while deserializing messages: ${getErrorMessage(e)}`);
|
||||
return {
|
||||
ok: false,
|
||||
error: e,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { SyncMessage } from "cojson";
|
||||
import { CojsonInternalTypes } from "cojson";
|
||||
import type { SyncMessage } from "cojson";
|
||||
import type { CojsonInternalTypes } from "cojson";
|
||||
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
|
||||
import {
|
||||
BatchedOutgoingMessages,
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { SyncMessage } from "cojson";
|
||||
import type { SyncMessage } from "cojson";
|
||||
import type { Channel } from "queueueue";
|
||||
import { Mocked, describe, expect, test, vi } from "vitest";
|
||||
import { type Mocked, describe, expect, test, vi } from "vitest";
|
||||
import { MAX_OUTGOING_MESSAGES_CHUNK_BYTES } from "../BatchedOutgoingMessages.js";
|
||||
import {
|
||||
BUFFER_LIMIT,
|
||||
BUFFER_LIMIT_POLLING_INTERVAL,
|
||||
CreateWebSocketPeerOpts,
|
||||
type CreateWebSocketPeerOpts,
|
||||
createWebSocketPeer,
|
||||
} from "../createWebSocketPeer.js";
|
||||
import { AnyWebSocket } from "../types.js";
|
||||
import type { AnyWebSocket } from "../types.js";
|
||||
|
||||
function setup(opts: Partial<CreateWebSocketPeerOpts> = {}) {
|
||||
const listeners = new Map<string, (event: MessageEvent) => void>();
|
||||
@@ -472,6 +472,7 @@ describe("createWebSocketPeer", () => {
|
||||
});
|
||||
});
|
||||
|
||||
// biome-ignore lint/suspicious/noConfusingVoidType: Test helper
|
||||
function waitFor(callback: () => boolean | void) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const checkPassed = () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createServer } from "http";
|
||||
import { createServer } from "node:http";
|
||||
import { ControlledAgent, LocalNode } from "cojson";
|
||||
import { WasmCrypto } from "cojson/crypto/WasmCrypto";
|
||||
import { WebSocket, WebSocketServer } from "ws";
|
||||
import { type WebSocket, WebSocketServer } from "ws";
|
||||
import { createWebSocketPeer } from "../createWebSocketPeer";
|
||||
|
||||
export const startSyncServer = async (port?: number) => {
|
||||
@@ -75,16 +75,18 @@ export const startSyncServer = async (port?: number) => {
|
||||
|
||||
server.listen(port ?? 0);
|
||||
|
||||
port = (server.address() as { port: number }).port;
|
||||
const syncServer = `ws://localhost:${port}`;
|
||||
const actualPort = (server.address() as { port: number }).port;
|
||||
const syncServer = `ws://localhost:${actualPort}`;
|
||||
|
||||
return {
|
||||
close: () => {
|
||||
connections.forEach((ws) => ws.close());
|
||||
for (const ws of connections) {
|
||||
ws.close();
|
||||
}
|
||||
server.close();
|
||||
},
|
||||
syncServer,
|
||||
port,
|
||||
port: actualPort,
|
||||
localNode,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// biome-ignore lint/suspicious/noConfusingVoidType: Test helper
|
||||
export function waitFor(callback: () => boolean | void) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const checkPassed = () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
export interface WebsocketEvents {
|
||||
close: { code: number; reason: string };
|
||||
message: { data: unknown };
|
||||
open: void;
|
||||
open: unknown;
|
||||
}
|
||||
|
||||
export interface PingMsg {
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"esModuleInterop": true
|
||||
"esModuleInterop": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true
|
||||
},
|
||||
"include": ["./src/**/*"]
|
||||
}
|
||||
|
||||
@@ -1,5 +1,25 @@
|
||||
# cojson
|
||||
|
||||
## 0.11.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- e22de9f: Fix roleOf resolution for "everyone"
|
||||
- 34cbdc3: Added revokeExtend method to Group
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- b9d194a: Added getAllMemberKeysSet method on RawGroup
|
||||
Add everyone to the possible inputs of Group.roleOf
|
||||
- a4713df: Moving to the d.ts files for the exported type definitions
|
||||
- 0f67e0a: Allow optional fields in types passed to co.json
|
||||
|
||||
## 0.10.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- f86e278: Downgrade the permissions error logs to debug
|
||||
|
||||
## 0.10.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -2,22 +2,22 @@
|
||||
"name": "cojson",
|
||||
"module": "dist/index.js",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.ts",
|
||||
"types": "dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./src/index.ts",
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/index.js"
|
||||
},
|
||||
"./dist/crypto/PureJSCrypto": {
|
||||
"types": "./src/crypto/PureJSCrypto.ts",
|
||||
"types": "./dist/crypto/PureJSCrypto.d.ts",
|
||||
"default": "./dist/crypto/PureJSCrypto.js"
|
||||
},
|
||||
"./crypto/PureJSCrypto": {
|
||||
"types": "./src/crypto/PureJSCrypto.ts",
|
||||
"types": "./dist/crypto/PureJSCrypto.d.ts",
|
||||
"default": "./dist/crypto/PureJSCrypto.js"
|
||||
},
|
||||
"./crypto/WasmCrypto": {
|
||||
"types": "./src/crypto/WasmCrypto.ts",
|
||||
"types": "./dist/crypto/WasmCrypto.d.ts",
|
||||
"default": "./dist/crypto/WasmCrypto.js"
|
||||
},
|
||||
"./dist/*": "./dist/*",
|
||||
@@ -25,7 +25,7 @@
|
||||
},
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"version": "0.10.8",
|
||||
"version": "0.11.0",
|
||||
"devDependencies": {
|
||||
"@opentelemetry/sdk-metrics": "^1.29.0",
|
||||
"typescript": "~5.6.2",
|
||||
@@ -47,8 +47,7 @@
|
||||
"test:watch": "vitest --watch --root ../../ --project cojson",
|
||||
"format-and-lint": "biome check .",
|
||||
"format-and-lint:fix": "biome check . --write",
|
||||
"build": "rm -rf ./dist && tsc --sourceMap --outDir dist",
|
||||
"prepublishOnly": "npm run build"
|
||||
"build": "rm -rf ./dist && tsc --sourceMap --outDir dist"
|
||||
},
|
||||
"gitHead": "33c27053293b4801b968c61d5c4c989f93a67d13"
|
||||
}
|
||||
|
||||
@@ -29,7 +29,12 @@ import { RawBinaryCoStream, RawCoStream } from "./coStream.js";
|
||||
export const EVERYONE = "everyone" as const;
|
||||
export type Everyone = "everyone";
|
||||
|
||||
export type ParentGroupReferenceRole = "extend" | "reader" | "writer" | "admin";
|
||||
export type ParentGroupReferenceRole =
|
||||
| "revoked"
|
||||
| "extend"
|
||||
| "reader"
|
||||
| "writer"
|
||||
| "admin";
|
||||
|
||||
export type GroupShape = {
|
||||
profile: CoID<RawCoMap> | null;
|
||||
@@ -45,7 +50,7 @@ export type GroupShape = {
|
||||
{ encryptedID: KeyID; encryptingID: KeyID }
|
||||
>;
|
||||
[parent: ParentGroupReference]: ParentGroupReferenceRole;
|
||||
[child: ChildGroupReference]: "extend";
|
||||
[child: ChildGroupReference]: "revoked" | "extend";
|
||||
};
|
||||
|
||||
/** A `Group` is a scope for permissions of its members (`"reader" | "writer" | "admin"`), applying to objects owned by that group.
|
||||
@@ -77,7 +82,7 @@ export class RawGroup<
|
||||
*
|
||||
* @category 1. Role reading
|
||||
*/
|
||||
roleOf(accountID: RawAccountID): Role | undefined {
|
||||
roleOf(accountID: RawAccountID | typeof EVERYONE): Role | undefined {
|
||||
return this.roleOfInternal(accountID)?.role;
|
||||
}
|
||||
|
||||
@@ -85,15 +90,15 @@ export class RawGroup<
|
||||
roleOfInternal(
|
||||
accountID: RawAccountID | AgentID | typeof EVERYONE,
|
||||
): { role: Role; via: CoID<RawGroup> | undefined } | undefined {
|
||||
const roleHere = this.get(accountID);
|
||||
let roleHere = this.get(accountID);
|
||||
|
||||
if (roleHere === "revoked") {
|
||||
return undefined;
|
||||
roleHere = undefined;
|
||||
}
|
||||
|
||||
let roleInfo:
|
||||
| {
|
||||
role: Exclude<Role, "revoked">;
|
||||
role: Role;
|
||||
via: CoID<RawGroup> | undefined;
|
||||
}
|
||||
| undefined = roleHere && { role: roleHere, via: undefined };
|
||||
@@ -114,6 +119,12 @@ export class RawGroup<
|
||||
}
|
||||
}
|
||||
|
||||
if (!roleInfo && accountID !== "everyone") {
|
||||
const everyoneRole = this.get("everyone");
|
||||
|
||||
if (everyoneRole) return { role: everyoneRole, via: undefined };
|
||||
}
|
||||
|
||||
return roleInfo;
|
||||
}
|
||||
|
||||
@@ -122,6 +133,11 @@ export class RawGroup<
|
||||
|
||||
for (const key of this.keys()) {
|
||||
if (isParentGroupReference(key)) {
|
||||
// Check if the parent group reference is revoked
|
||||
if (this.get(key) === "revoked") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const parent = this.core.node.expectCoValueLoaded(
|
||||
getParentGroupId(key),
|
||||
"Expected parent group to be loaded",
|
||||
@@ -183,6 +199,11 @@ export class RawGroup<
|
||||
|
||||
for (const key of this.keys()) {
|
||||
if (isChildGroupReference(key)) {
|
||||
// Check if the child group reference is revoked
|
||||
if (this.get(key) === "revoked") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const child = this.core.node.expectCoValueLoaded(
|
||||
getChildGroupId(key),
|
||||
"Expected child group to be loaded",
|
||||
@@ -385,6 +406,18 @@ export class RawGroup<
|
||||
});
|
||||
}
|
||||
|
||||
getAllMemberKeysSet() {
|
||||
const memberKeys = new Set(this.getMemberKeys());
|
||||
|
||||
for (const { group } of this.getParentGroups()) {
|
||||
for (const key of group.getAllMemberKeysSet()) {
|
||||
memberKeys.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
return memberKeys;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
rotateReadKey(removedMemberKey?: RawAccountID | AgentID | "everyone") {
|
||||
const memberKeys = this.getMemberKeys().filter(
|
||||
@@ -606,6 +639,43 @@ export class RawGroup<
|
||||
);
|
||||
}
|
||||
|
||||
async revokeExtend(parent: RawGroup) {
|
||||
if (this.myRole() !== "admin") {
|
||||
throw new Error(
|
||||
"To unextend a group, the current account must be an admin in the child group",
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
parent.myRole() !== "admin" &&
|
||||
parent.myRole() !== "writer" &&
|
||||
parent.myRole() !== "reader" &&
|
||||
parent.myRole() !== "writeOnly"
|
||||
) {
|
||||
throw new Error(
|
||||
"To unextend a group, the current account must be a member of the parent group",
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
!this.get(`parent_${parent.id}`) ||
|
||||
this.get(`parent_${parent.id}`) === "revoked"
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the parent key on the child group to `revoked`
|
||||
this.set(`parent_${parent.id}`, "revoked", "trusting");
|
||||
|
||||
// Set the child key on the parent group to `revoked`
|
||||
parent.set(`child_${this.id}`, "revoked", "trusting");
|
||||
|
||||
await this.loadAllChildGroups();
|
||||
|
||||
// Rotate the keys on the child group
|
||||
this.rotateReadKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* Strips the specified member of all roles (preventing future writes in
|
||||
* the group and owned values) and rotates the read encryption key for that group
|
||||
@@ -781,8 +851,9 @@ export class RawGroup<
|
||||
|
||||
export function isInheritableRole(
|
||||
roleInParent: Role | undefined,
|
||||
): roleInParent is "admin" | "writer" | "reader" {
|
||||
): roleInParent is "revoked" | "admin" | "writer" | "reader" {
|
||||
return (
|
||||
roleInParent === "revoked" ||
|
||||
roleInParent === "admin" ||
|
||||
roleInParent === "writer" ||
|
||||
roleInParent === "reader"
|
||||
@@ -790,9 +861,13 @@ export function isInheritableRole(
|
||||
}
|
||||
|
||||
function isMorePermissiveAndShouldInherit(
|
||||
roleInParent: "admin" | "writer" | "reader",
|
||||
roleInChild: Exclude<Role, "revoked"> | undefined,
|
||||
roleInParent: "revoked" | "admin" | "writer" | "reader",
|
||||
roleInChild: Role | undefined,
|
||||
) {
|
||||
if (roleInParent === "revoked") {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (roleInParent === "admin") {
|
||||
return !roleInChild || roleInChild !== "admin";
|
||||
}
|
||||
|
||||
@@ -8,11 +8,15 @@ export type JsonObject = { [key: string]: JsonValue | undefined };
|
||||
type AtLeastOne<T, U = { [K in keyof T]: Pick<T, K> }> = Partial<T> &
|
||||
U[keyof U];
|
||||
type ExcludeEmpty<T> = T extends AtLeastOne<T> ? T : never;
|
||||
type ExcludeNull<T> = T extends null ? never : T;
|
||||
type IsFunction<T> = T extends (...args: any[]) => any ? true : false;
|
||||
type ContainsSymbolKeys<T> = keyof ExcludeNull<T> extends symbol ? true : false;
|
||||
|
||||
export type CoJsonValue<T> =
|
||||
| JsonValue
|
||||
| CoJsonObjectWithIndex<T>
|
||||
| CoJsonArray<T>;
|
||||
export type CoJsonValue<T> = IsFunction<T> extends true
|
||||
? "Functions are not allowed"
|
||||
: ContainsSymbolKeys<T> extends true
|
||||
? "Only string or number keys are allowed"
|
||||
: JsonValue | CoJsonObjectWithIndex<T> | CoJsonArray<T>;
|
||||
export type CoJsonArray<T> = CoJsonValue<T>[] | readonly CoJsonValue<T>[];
|
||||
|
||||
/**
|
||||
@@ -25,7 +29,7 @@ export type CoJsonArray<T> = CoJsonValue<T>[] | readonly CoJsonValue<T>[];
|
||||
* Applying the ExcludeEmpty type here to make sure we don't accept functions or non-serializable values
|
||||
*/
|
||||
export type CoJsonObjectWithIndex<T> = ExcludeEmpty<{
|
||||
[K in keyof T & string]: CoJsonValue1L<T[K]> | undefined;
|
||||
[K in keyof T]: CoJsonValue1L<T[K]> | undefined;
|
||||
}>;
|
||||
|
||||
/**
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user