Compare commits

..

93 Commits

Author SHA1 Message Date
Guido D'Orsi
6c94c2cd3d Merge pull request #1605 from garden-co/changeset-release/main
Version Packages
2025-03-06 13:54:08 +01:00
github-actions[bot]
98c4221340 Version Packages 2025-03-06 12:46:19 +00:00
Guido D'Orsi
7b00a8155f fix: align packages versions to 0.11 2025-03-06 13:44:06 +01:00
Guido D'Orsi
cc5aea708a fix: remove the prepublish hook 2025-03-06 13:35:35 +01:00
Guido D'Orsi
87c7306632 fix(release): build packages with turbo before publishing 2025-03-06 13:30:59 +01:00
Guido D'Orsi
a65219ba52 Merge pull request #1578 from garden-co/changeset-release/main
Version Packages
2025-03-06 13:23:02 +01:00
github-actions[bot]
689595fd0a Version Packages 2025-03-06 12:21:42 +00:00
Guido D'Orsi
efbf2b65a8 Merge pull request #1492 from garden-co/0-11-0
Jazz 0.11.0 - Better permissions checks and members API
2025-03-06 13:19:38 +01:00
Guido D'Orsi
912232192f docs: release notes final touches 2025-03-06 13:18:24 +01:00
Guido D'Orsi
11b5d77064 fix: check auth state when accepting an invite 2025-03-06 13:04:07 +01:00
Guido D'Orsi
63b88b591d test(music-player): uncomment remove track test 2025-03-06 13:02:21 +01:00
Guido D'Orsi
87d0544d98 chore: fix type errors 2025-03-05 18:55:37 +01:00
Guido D'Orsi
d43dcc2c94 Merge pull request #1592 from garden-co/revert-1557-1556-update-homepage-typedoc-to-027
Revert "1556-update-homepage-typedoc-to-027"
2025-03-05 18:55:09 +01:00
Guido D'Orsi
093a166db1 Revert "1556-update-homepage-typedoc-to-027" 2025-03-05 18:54:57 +01:00
Guido D'Orsi
593f3ed83e Revert "Fix typing"
This reverts commit 6a2a176361.
2025-03-05 18:48:55 +01:00
Guido D'Orsi
6c6c89e068 Revert "Bumps typedoc and typescript versions"
This reverts commit 824bbc8bab.
2025-03-05 18:45:30 +01:00
Guido D'Orsi
c1646f6bbb Merge pull request #1570 from garden-co/trisha/1224-add-status-to-footer
Add status to footer, remove releases
2025-03-05 18:03:51 +01:00
Guido D'Orsi
b7deb3c937 chore: changeset 2025-03-05 17:59:56 +01:00
Guido D'Orsi
9b425701e4 Merge pull request #1589 from 3f765d5e-ac23-4170-97a5-68b8feee155b/3f765d5e-ac23-4170-97a5-68b8feee155b-patch-3
Add React 19 to peer deps
2025-03-05 17:58:51 +01:00
Guido D'Orsi
07dcf54aca chore: fix type errors 2025-03-05 17:52:50 +01:00
Guido D'Orsi
8ed33c18ca Merge remote-tracking branch 'origin/main' into 0-11-0 2025-03-05 17:48:33 +01:00
Guido D'Orsi
6a96d8b53b feat: add getParentGroups API to Group and Account 2025-03-05 17:27:28 +01:00
Nikos Papadopoulos
a1bcbda72e Merge pull request #1522 from garden-co/jazz-758-allow-optional-fields-in-types-passed-to-cojson-see
Fix and test cases for reported issue with optional fields
2025-03-05 16:03:25 +00:00
Nikos Papadopoulos
0f67e0a988 adds changeset 2025-03-05 15:54:29 +00:00
3f765d5e-ac23-4170-97a5-68b8feee155b
6c7a3ebf82 Add React 19 to peer deps
Hope that's enough to make the peer dep warning go away. Been running inspector with react 19 and no problems so far
2025-03-05 16:46:31 +01:00
Guido D'Orsi
27ce186152 fix(co.json): accept null values and improve the function check 2025-03-05 15:51:17 +01:00
Nikos Papadopoulos
77ae2cc186 Revert "fixes CoJsonValue type"
This reverts commit d6d9218b3e.
2025-03-05 10:53:56 +00:00
Guido D'Orsi
66600c0c27 Merge pull request #1584 from garden-co/clarification-profile-changes
docs: clarify better the situation on profile migrations
2025-03-05 11:06:42 +01:00
Guido D'Orsi
81d5261f4d docs: clarify better the situation on profile migrations 2025-03-05 11:01:02 +01:00
Guido D'Orsi
d748dea90f test: cover invalid actions on revokeExtend 2025-03-05 10:24:03 +01:00
Guido D'Orsi
707e544b83 docs: add the 0.11.0 group APIs 2025-03-05 10:17:00 +01:00
Guido D'Orsi
f3cbaee890 Merge pull request #1500 from garden-co/jazz-760-jazz-0110-upgrade-guide
jazz 0.11.0 upgrade guide
2025-03-05 10:03:04 +01:00
Trisha Lim
60ce483db7 add changeset 2025-03-05 15:51:33 +07:00
Trisha Lim
c08a41398d Make initial commit on create-jazz-app apps 2025-03-05 15:51:33 +07:00
Nikos Papadopoulos
d6d9218b3e fixes CoJsonValue type 2025-03-04 19:20:08 +00:00
Anselm
667e301f12 Simplify JSON related types and test for more cases 2025-03-04 17:33:02 +00:00
Guido D'Orsi
b15f904347 chore: fix a type error 2025-03-04 18:02:48 +01:00
Nikos Papadopoulos
0c150a4c74 fix attempt 0 2025-03-04 16:50:01 +00:00
Guido D'Orsi
66de2dacb6 docs: whats new 2025-03-04 17:13:57 +01:00
Guido D'Orsi
ae1425db8a Merge remote-tracking branch 'origin/main' into 0-11-0 2025-03-04 17:08:49 +01:00
Guido D'Orsi
a164b102f5 docs: add revokeExtend to the migration docs 2025-03-04 16:49:25 +01:00
Guido D'Orsi
a6db9a438e Merge remote-tracking branch 'origin/0-11-0' into jazz-760-jazz-0110-upgrade-guide 2025-03-04 16:38:49 +01:00
Nikita
a35249a831 Fix CoMap.toJSON() with encoded fields (#1577)
* encode -> encoded

* add test and changeset

* patch
2025-03-04 15:56:08 +01:00
Guido D'Orsi
36e246be79 docs: add the this type to the migration examples 2025-03-04 15:37:29 +01:00
Trisha Lim
b894455c9e Remove unnecessary owner argument in react examples 2025-03-04 19:28:16 +07:00
Trisha Lim
a462254199 Add status to footer, remove releases 2025-03-04 17:21:22 +07:00
Emil Sayahi
34cbdc3e4c feat!(Groups): API to unextend group (#1512)
* feat!(Groups): API to unextend group

See: #1504

* feat!(Groups): API to unextend group

See: #1504

* test(groupsAndAccounts): revoking group extensions

* test(cojson): group, permissions test for unextend

* chore: add changeset

* test(permissions): unextend reading & grandparents

* feat!(Groups): API to unextend group

See: #1504

* test(groupsAndAccounts): revoking group extensions

* test(cojson): group, permissions test for unextend

* chore: add changeset

* test(permissions): unextend reading & grandparents

* feat(music-player): removing tracks from playlists

* fix(music-player): remove unused import
2025-03-03 14:13:35 -05:00
Guido D'Orsi
029c32d86c chore: fix type errors 2025-02-28 18:44:16 +01:00
Guido D'Orsi
45bed3ea80 Merge remote-tracking branch 'origin/main' into 0-11-0 2025-02-28 14:12:02 +01:00
Guido D'Orsi
43fa1ecba4 chore: correctly rename the interface 2025-02-28 11:20:47 +01:00
Nikos Papadopoulos
141ea11bf1 Rename interface 2025-02-27 19:08:52 +00:00
Guido D'Orsi
273b478381 Merge pull request #1535 from garden-co/test/group-members-sync
test(groupAndAccounts): enable sync and add members test
2025-02-27 17:24:01 +01:00
Guido D'Orsi
1d29f74df6 test(groupAndAccounts): enable sync and add members test 2025-02-27 17:16:27 +01:00
Guido D'Orsi
393d066b25 docs: add the Group.members schema and everyone changes 2025-02-27 11:45:51 +01:00
Guido D'Orsi
45bff625c4 Merge branch '0-11-0' into jazz-760-jazz-0110-upgrade-guide 2025-02-27 11:40:42 +01:00
Guido D'Orsi
a8fca7b5f7 Merge pull request #1459 from garden-co/declaration-files-migration
feat: migrate to declaration files
2025-02-27 11:28:21 +01:00
Guido D'Orsi
658a0602ea Merge pull request #1528 from garden-co/fix/group-members-type
feat(Group.members): use the global AccountSchema and remove everyone from the returned values
2025-02-27 11:28:04 +01:00
Guido D'Orsi
f039e8f097 feat(Group.members): use the global AccountSchema and remove everyone from the returned values 2025-02-27 11:20:57 +01:00
Guido D'Orsi
dc9da28bf9 Merge remote-tracking branch 'origin/0-11-0' into declaration-files-migration 2025-02-27 09:55:15 +01:00
Guido D'Orsi
e6db8f2a02 test(permissions): rewrite flaky tests 2025-02-26 18:53:32 +01:00
Guido D'Orsi
f0c8f7b3eb Merge pull request #1526 from garden-co/upgrade-guide-permissions-docs
docs: update the 0.11 upgrade guide with the new permissions APIs
2025-02-26 17:46:09 +01:00
Guido D'Orsi
945163b7bc docs: fix typos in the upgrade guide 2025-02-26 15:34:20 +01:00
Guido D'Orsi
3142e503ae docs: update the 0.11 upgrade guide with the new permissions APIs 2025-02-26 15:33:00 +01:00
Guido D'Orsi
932a21e62a Merge pull request #1521 from garden-co/permission-checks-simplified
feat: add shortcut methods to account to check permissions on covalues
2025-02-26 14:45:33 +01:00
Guido D'Orsi
628a33eb12 feat: add shortcut methods to account to check permissions on covalues 2025-02-26 14:45:22 +01:00
Nikos Papadopoulos
20eef64b47 Added a new test case to validate a reported issue with optional fields 2025-02-26 13:16:05 +00:00
Giordano Ricci
c64f38b6c9 docs for usePassKeyAuth changes 2025-02-26 12:02:39 +00:00
Giordano Ricci
e5e047660b nicer alert 2025-02-26 11:57:20 +00:00
Giordano Ricci
a65d6bed73 reorganize sections 2025-02-26 11:17:48 +00:00
Giordano Ricci
d4f7891890 add more docs 2025-02-25 16:39:37 +00:00
Giordano Ricci
6eef92b602 jazz 0.11.0 upgrade guide 2025-02-25 13:01:16 +00:00
Guido D'Orsi
dfbb4b8c69 Merge pull request #1478 from garden-co/jazz-749-migrations-make-migrations-work-with-profiles
fix: allow profile migrations, enforce profile ownership
2025-02-25 13:58:59 +01:00
Giordano Ricci
5f2d8e143c remove profile.name forced write 2025-02-25 12:35:39 +00:00
Guido D'Orsi
240f071951 Merge pull request #1502 from garden-co/jazz-730-passkeyauth-update-the-profile-name-only-if-the-value-on
Jazz 730 passkeyauth update the profile name only if the value on
2025-02-25 12:30:43 +01:00
Giordano Ricci
d560b4ddfb fixpermission resolution chain 2025-02-25 11:19:34 +00:00
Guido D'Orsi
73d6fd1a85 tests: add more permissions tests on inheritance 2025-02-25 11:42:58 +01:00
Guido D'Orsi
46f5133510 test: revoking access on a child group doesn't block access to that group if a more permissive role is inheritable 2025-02-24 20:17:58 +01:00
Giordano Ricci
b3f2e67d9d restore correct behavior in test 2025-02-24 19:17:30 +00:00
Giordano Ricci
f689cd20fc add tests 2025-02-24 19:05:34 +00:00
Giordano Ricci
e22de9ff4c add changeset 2025-02-24 18:45:37 +00:00
Giordano Ricci
735bd41242 fix permsissions checks 2025-02-24 18:41:23 +00:00
Giordano Ricci
71998bde62 throw a meaningful error when trying to access an inbox with inusfficient permissions 2025-02-24 18:40:44 +00:00
Giordano Ricci
f01b714e29 remove profile visibility requirements 2025-02-24 18:40:44 +00:00
Giordano Ricci
028eca9f81 throw error is profile has an account owner 2025-02-24 18:40:44 +00:00
Giordano Ricci
d3d4118b86 fix tests 2025-02-24 18:40:43 +00:00
Giordano Ricci
45f1fe19ac fix: allow profile migrations, enforce profile ownership 2025-02-24 18:40:43 +00:00
Emil Sayahi
ad2db5e6cb docs(changeset): mention PasskeyAuth
Co-authored-by: Guido D'Orsi <gu.dorsi@gmail.com>
2025-02-24 12:57:16 -05:00
Guido D'Orsi
480890d2e9 Merge pull request #1467 from garden-co/feat/return-inherited-members
feat: return inherited members and add getRoleOf and hasPermissions methods to Group
2025-02-24 17:49:18 +01:00
Emil Sayahi
18428eaaa1 chore: changeset 2025-02-24 11:47:41 -05:00
Emil Sayahi
5ed31f2678 fix(PasskeyAuth): set name iff username given
If an empty username is passed to `signUp`, the `profile.name` of the user is not updated.

See: #1433
2025-02-24 11:11:57 -05:00
Guido D'Orsi
b9d194a80e feat: return inherited members and add getRoleOf and hasPermissions methods to Group 2025-02-24 12:55:14 +01:00
Guido D'Orsi
2359e85ebd feat: enable the declarationMap option 2025-02-21 18:55:20 +01:00
Guido D'Orsi
a4713df30c feat: migrate to declaration files 2025-02-21 16:07:51 +01:00
175 changed files with 3229 additions and 945 deletions

View File

@@ -9,12 +9,15 @@
"cojson-storage",
"cojson-storage-indexeddb",
"cojson-storage-sqlite",
"cojson-storage-rn-sqlite",
"cojson-transport-ws",
"jazz-browser",
"jazz-auth-clerk",
"jazz-browser-media-images",
"jazz-inspector",
"jazz-nodejs",
"jazz-react",
"jazz-react-core",
"jazz-react-auth-clerk",
"jazz-react-native",
"jazz-react-native-auth-clerk",

View File

@@ -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

View File

@@ -1,5 +1,28 @@
# chat-rn-clerk
## 1.0.82
### Patch Changes
- jazz-react-native@0.11.1
- jazz-react-native-auth-clerk@0.11.1
## 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

View File

@@ -1,7 +1,7 @@
{
"name": "chat-rn-clerk",
"main": "index.js",
"version": "1.0.80",
"version": "1.0.82",
"scripts": {
"build": "expo export -p ios",
"start": "expo start",

View File

@@ -1,5 +1,25 @@
# chat-rn
## 1.0.78
### Patch Changes
- jazz-react-native@0.11.1
## 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

View File

@@ -1,6 +1,6 @@
{
"name": "chat-rn",
"version": "1.0.76",
"version": "1.0.78",
"main": "index.js",
"scripts": {
"build": "expo export -p ios",

View File

@@ -1,5 +1,21 @@
# 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

View File

@@ -1,6 +1,6 @@
{
"name": "chat-vue",
"version": "0.0.62",
"version": "0.0.63",
"private": true,
"type": "module",
"scripts": {

View File

@@ -1,5 +1,26 @@
# jazz-example-chat
## 0.0.160
### Patch Changes
- jazz-react@0.11.1
## 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

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-chat",
"private": true,
"version": "0.0.158",
"version": "0.0.160",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,5 +1,27 @@
# minimal-auth-clerk
## 0.0.59
### Patch Changes
- jazz-react@0.11.1
- jazz-react-auth-clerk@0.11.1
## 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

View File

@@ -1,7 +1,7 @@
{
"name": "clerk",
"private": true,
"version": "0.0.57",
"version": "0.0.59",
"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.15",
"jazz-react-auth-clerk": "workspace:0.11.1",
"jazz-tools": "workspace:*",
"react": "^18.3.1",
"react-dom": "^18.3.1"

View File

@@ -1,5 +1,19 @@
# 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

View File

@@ -1,6 +1,6 @@
{
"name": "file-share-svelte",
"version": "0.0.42",
"version": "0.0.43",
"private": true,
"type": "module",
"scripts": {

View File

@@ -1,5 +1,30 @@
# form
## 0.1.1
### Patch Changes
- jazz-react@0.11.1
## 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

View File

@@ -1,7 +1,7 @@
{
"name": "form",
"private": true,
"version": "0.0.53",
"version": "0.1.1",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,5 +1,26 @@
# image-upload
## 0.0.57
### Patch Changes
- jazz-react@0.11.1
## 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

View File

@@ -1,7 +1,7 @@
{
"name": "image-upload",
"private": true,
"version": "0.0.55",
"version": "0.0.57",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,5 +1,17 @@
# 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

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-inspector-app",
"private": true,
"version": "0.0.110",
"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.15",
"cojson-transport-ws": "workspace:0.10.15",
"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",

View File

@@ -1,5 +1,29 @@
# jazz-example-musicplayer
## 0.0.81
### Patch Changes
- Updated dependencies [7b00a81]
- jazz-inspector@0.11.1
- jazz-react@0.11.1
## 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

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-music-player",
"private": true,
"version": "0.0.79",
"version": "0.0.81",
"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.15",
"jazz-tools": "workspace:0.10.15",
"jazz-react": "workspace:0.11.1",
"jazz-tools": "workspace:0.11.0",
"lucide-react": "^0.274.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",

View File

@@ -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) {

View File

@@ -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>
);
}

View File

@@ -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>

View File

@@ -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");
});

View File

@@ -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);

View File

@@ -1,5 +1,25 @@
# organization
## 0.0.53
### Patch Changes
- jazz-react@0.11.1
## 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

View File

@@ -1,7 +1,7 @@
{
"name": "organization",
"private": true,
"version": "0.0.51",
"version": "0.0.53",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -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);

View File

@@ -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>
);
}

View File

@@ -1,5 +1,11 @@
# passkey-svelte
## 0.0.47
### Patch Changes
- jazz-svelte@0.11.0
## 0.0.46
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "passkey-svelte",
"version": "0.0.46",
"version": "0.0.47",
"type": "module",
"private": true,
"scripts": {

View File

@@ -1,5 +1,25 @@
# minimal-auth-passkey
## 0.0.58
### Patch Changes
- jazz-react@0.11.1
## 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

View File

@@ -1,7 +1,7 @@
{
"name": "passkey",
"private": true,
"version": "0.0.56",
"version": "0.0.58",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,5 +1,25 @@
# passphrase
## 0.0.55
### Patch Changes
- jazz-react@0.11.1
## 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

View File

@@ -1,7 +1,7 @@
{
"name": "passphrase",
"private": true,
"version": "0.0.53",
"version": "0.0.55",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,5 +1,25 @@
# jazz-password-manager
## 0.0.79
### Patch Changes
- jazz-react@0.11.1
## 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

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-password-manager",
"private": true,
"version": "0.0.77",
"version": "0.0.79",
"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.15",
"jazz-tools": "workspace:0.10.15",
"jazz-react": "workspace:0.11.1",
"jazz-tools": "workspace:0.11.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-hook-form": "^7.41.5",

View File

@@ -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>

View File

@@ -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 = () => {

View File

@@ -1,5 +1,26 @@
# jazz-example-pets
## 0.0.177
### Patch Changes
- jazz-react@0.11.1
## 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

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-pets",
"private": true,
"version": "0.0.175",
"version": "0.0.177",
"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.15",
"jazz-react": "workspace:0.10.15",
"jazz-tools": "workspace:0.10.15",
"jazz-browser-media-images": "workspace:0.11.0",
"jazz-react": "workspace:0.11.1",
"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.15",
"jazz-run": "workspace:0.11.0",
"postcss": "^8.4.27",
"tailwindcss": "^3.4.17",
"typescript": "~5.6.2",

View File

@@ -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,

View File

@@ -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>

View File

@@ -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"

View File

@@ -1,5 +1,26 @@
# reactions
## 0.0.57
### Patch Changes
- jazz-react@0.11.1
## 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

View File

@@ -1,7 +1,7 @@
{
"name": "reactions",
"private": true,
"version": "0.0.55",
"version": "0.0.57",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -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);

View File

@@ -1,5 +1,21 @@
# 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

View File

@@ -1,6 +1,6 @@
{
"name": "todo-vue",
"version": "0.0.60",
"version": "0.0.61",
"private": true,
"type": "module",
"scripts": {

View File

@@ -1,5 +1,25 @@
# jazz-example-todo
## 0.0.176
### Patch Changes
- jazz-react@0.11.1
## 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

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-todo",
"private": true,
"version": "0.0.174",
"version": "0.0.176",
"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.15",
"jazz-tools": "workspace:0.10.15",
"jazz-react": "workspace:0.11.1",
"jazz-tools": "workspace:0.11.0",
"lucide-react": "^0.274.0",
"qrcode": "^1.5.3",
"react": "^18.3.1",

View File

@@ -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(

View File

@@ -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"

View File

@@ -1,5 +1,25 @@
# version-history
## 0.0.54
### Patch Changes
- jazz-react@0.11.1
## 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

View File

@@ -1,7 +1,7 @@
{
"name": "version-history",
"private": true,
"version": "0.0.52",
"version": "0.0.54",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -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(

View File

@@ -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>

View 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>
);
}

View File

@@ -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

View File

@@ -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:

View File

@@ -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>

View File

@@ -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>

View File

@@ -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) {

View File

@@ -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>

View File

@@ -18,20 +18,6 @@ import {
PropDecl,
} from "./tags";
function isDeclarationReflection(child: any): child is DeclarationReflection {
return (
'kind' in child &&
'name' in child &&
'flags' in child &&
(child.kind === ReflectionKind.Class ||
child.kind === ReflectionKind.Interface ||
child.kind === ReflectionKind.TypeAlias ||
child.kind === ReflectionKind.Function ||
child.kind === ReflectionKind.Property ||
child.kind === ReflectionKind.Method)
);
}
export async function PackageDocs({
package: packageName,
}: {
@@ -50,7 +36,7 @@ export async function PackageDocs({
return (
<section key={category.title}>
<h2>{category.title}</h2>
{category.children.filter(isDeclarationReflection).map((child) => (
{category.children.map((child) => (
<RenderPackageChild
child={child}
key={child.id}
@@ -203,7 +189,7 @@ function RenderClassOrInterface({
),
)}
/>
{category.children.filter(isDeclarationReflection).map((prop) => (
{category.children.map((prop) => (
<RenderProp prop={prop} klass={classOrInterface} key={prop.id} />
))}
</div>

View File

@@ -1,4 +1,4 @@
import { Deserializer, FileRegistry, JSONOutput, ProjectReflection } from "typedoc";
import { Deserializer, JSONOutput, ProjectReflection } from "typedoc";
import JazzBrowserMediaImagesDocs from "../../typedoc/jazz-browser-media-images.json";
import JazzBrowserDocs from "../../typedoc/jazz-browser.json";
@@ -7,19 +7,17 @@ import JazzReactDocs from "../../typedoc/jazz-react.json";
import JazzToolsDocs from "../../typedoc/jazz-tools.json";
const docs = {
"jazz-tools": JazzToolsDocs,
"jazz-react": JazzReactDocs,
"jazz-browser": JazzBrowserDocs,
"jazz-browser-media-images": JazzBrowserMediaImagesDocs,
"jazz-nodejs": JazzNodejsDocs,
} as const;
"jazz-tools": JazzToolsDocs as JSONOutput.ProjectReflection,
"jazz-react": JazzReactDocs as JSONOutput.ProjectReflection,
"jazz-browser": JazzBrowserDocs as JSONOutput.ProjectReflection,
"jazz-browser-media-images":
JazzBrowserMediaImagesDocs as JSONOutput.ProjectReflection,
"jazz-nodejs": JazzNodejsDocs as JSONOutput.ProjectReflection,
};
export async function requestProject(
packageName: keyof typeof docs,
): Promise<ProjectReflection> {
const deserializer = new Deserializer({} as any);
return deserializer.reviveProject(packageName, docs[packageName] as unknown as JSONOutput.ProjectReflection, {
projectRoot: ".",
registry: new FileRegistry(),
});
return deserializer.reviveProject(docs[packageName], packageName);
}

View File

@@ -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,
},
],
},
{

View File

@@ -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}

View File

@@ -66,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",

View File

@@ -61,7 +61,7 @@
"autoprefixer": "^10",
"postcss": "^8",
"tailwindcss": "^3",
"typedoc": "^0.27.9",
"typescript": "~5.6.2"
"typedoc": "^0.25.13",
"typescript": "^5.3.3"
}
}

288
homepage/pnpm-lock.yaml generated
View File

@@ -267,7 +267,7 @@ importers:
version: 0.14.7
shiki-twoslash:
specifier: ^3.1.2
version: 3.1.2(typescript@5.6.3)
version: 3.1.2(typescript@5.4.5)
tailwind-merge:
specifier: ^1.14.0
version: 1.14.0
@@ -303,11 +303,11 @@ importers:
specifier: ^3
version: 3.4.3
typedoc:
specifier: ^0.27.9
version: 0.27.9(typescript@5.6.3)
specifier: ^0.25.13
version: 0.25.13(typescript@5.4.5)
typescript:
specifier: ~5.6.2
version: 5.6.3
specifier: ^5.3.3
version: 5.4.5
packages:
@@ -438,9 +438,6 @@ packages:
'@floating-ui/utils@0.2.8':
resolution: {integrity: sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==}
'@gerrit0/mini-shiki@1.27.2':
resolution: {integrity: sha512-GeWyHz8ao2gBiUW4OJnQDxXQnFgZQwwQk05t/CVVgNBN7/rK8XZ7xY6YhLVv9tH3VppWWmr9DCl3MwemB/i+Og==}
'@headlessui/react@2.2.0':
resolution: {integrity: sha512-RzCEg+LXsuI7mHiSomsu/gBJSjpupm6A1qIZ5sWjd7JhARNlMiSA4kKfJpCKwU9tE+zMRterhhrP74PvfJrpXQ==}
engines: {node: '>=10'}
@@ -461,10 +458,6 @@ packages:
resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==}
engines: {node: '>=6.0.0'}
'@jridgewell/gen-mapping@0.3.8':
resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==}
engines: {node: '>=6.0.0'}
'@jridgewell/resolve-uri@3.1.2':
resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
engines: {node: '>=6.0.0'}
@@ -479,9 +472,6 @@ packages:
'@jridgewell/sourcemap-codec@1.4.15':
resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==}
'@jridgewell/sourcemap-codec@1.5.0':
resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
'@jridgewell/trace-mapping@0.3.25':
resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
@@ -883,15 +873,6 @@ packages:
'@selderee/plugin-htmlparser2@0.11.0':
resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==}
'@shikijs/engine-oniguruma@1.29.2':
resolution: {integrity: sha512-7iiOx3SG8+g1MnlzZVDYiaeHe7Ez2Kf2HrJzdmGwkRisT7r4rak0e655AcM/tF9JG/kg5fMNYlLLKglbN7gBqA==}
'@shikijs/types@1.29.2':
resolution: {integrity: sha512-VJjK0eIijTZf0QSTODEXCqinjBn0joAHQ+aPSBzrv4O2d/QSbsMw+ZeSRx03kV34Hy7NzUvV/7NqfYGRLrASmw==}
'@shikijs/vscode-textmate@10.0.2':
resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==}
'@stefanprobst/rehype-extract-toc@2.2.0':
resolution: {integrity: sha512-/4UjstX8ploZklY8MmlOQoXB1jWIo3Go4MP0R39sbkoWuN6rJ7Zt6l4bjkDPM4hKsjoODh9SSKn2otlp3XL3/A==}
engines: {node: '>=14.17'}
@@ -1309,8 +1290,8 @@ packages:
'@types/node@20.12.11':
resolution: {integrity: sha512-vDg9PZ/zi+Nqp6boSOT7plNuthRugEKixDv5sFTIpkE89MmNtEArAShI4mxuX2+UrLEe9pxC1vm2cjm9YlWbJw==}
'@types/node@20.17.22':
resolution: {integrity: sha512-9RV2zST+0s3EhfrMZIhrz2bhuhBwxgkbHEwP2gtGWPjBzVQjifMzJ9exw7aDZhR1wbpj8zBrfp3bo8oJcGiUUw==}
'@types/node@20.17.6':
resolution: {integrity: sha512-VEI7OdvK2wP7XHnsuXbAJnEpEkF6NjSN45QJlL4VGqZSXsnicpesdTWsg9RISeSdYd3yeRj/y3k5KGjUXYnFwQ==}
'@types/prop-types@15.7.12':
resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==}
@@ -1452,30 +1433,14 @@ packages:
engines: {node: '>=0.4.0'}
hasBin: true
ajv-formats@2.1.1:
resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==}
peerDependencies:
ajv: ^8.0.0
peerDependenciesMeta:
ajv:
optional: true
ajv-keywords@3.5.2:
resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==}
peerDependencies:
ajv: ^6.9.1
ajv-keywords@5.1.0:
resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==}
peerDependencies:
ajv: ^8.8.2
ajv@6.12.6:
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
ajv@8.17.1:
resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==}
ansi-regex@5.0.1:
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
engines: {node: '>=8'}
@@ -1505,9 +1470,6 @@ packages:
arg@5.0.2:
resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
astring@1.8.6:
resolution: {integrity: sha512-ISvCdHdlTDlH5IpxQJIex7BWBywFWgjJSVdwst+/iQCoEYnyOaQ95+X1JGshuBjGp6nxKUy1jMgE3zPqN7fQdg==}
hasBin: true
@@ -1541,8 +1503,8 @@ packages:
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
browserslist@4.24.4:
resolution: {integrity: sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==}
browserslist@4.24.2:
resolution: {integrity: sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
@@ -1567,9 +1529,6 @@ packages:
caniuse-lite@1.0.30001683:
resolution: {integrity: sha512-iqmNnThZ0n70mNwvxpEC2nBJ037ZHZUoBI5Gorh1Mw6IlEAZujEoU1tXA628iZfzm7R9FvFzxbfdgml82a3k8Q==}
caniuse-lite@1.0.30001701:
resolution: {integrity: sha512-faRs/AW3jA9nTwmJBSO1PQ6L/EOgsB5HMQQq4iCu5zhPgVVgO/pZRHlmatwijZKetFw8/Pr4q6dEN8sJuq8qTw==}
ccount@2.0.1:
resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
@@ -1725,8 +1684,8 @@ packages:
electron-to-chromium@1.4.761:
resolution: {integrity: sha512-PIbxpiJGx6Bb8dQaonNc6CGTRlVntdLg/2nMa1YhnrwYOORY9a3ZgGN0UQYE6lAcj/lkyduJN7BPt/JiY+jAQQ==}
electron-to-chromium@1.5.109:
resolution: {integrity: sha512-AidaH9JETVRr9DIPGfp1kAarm/W6hRJTPuCnkF+2MqhF4KaAgRIcBc8nvjk+YMXZhwfISof/7WG29eS4iGxQLQ==}
electron-to-chromium@1.5.64:
resolution: {integrity: sha512-IXEuxU+5ClW2IGEYFC2T7szbyVgehupCWQe5GNh+H065CD6U6IFN0s4KeAMFGNmQolRU4IV7zGBWSYMmZ8uuqQ==}
emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
@@ -1734,16 +1693,16 @@ packages:
emoji-regex@9.2.2:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
enhanced-resolve@5.18.1:
resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==}
enhanced-resolve@5.17.1:
resolution: {integrity: sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==}
engines: {node: '>=10.13.0'}
entities@4.5.0:
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
engines: {node: '>=0.12'}
es-module-lexer@1.6.0:
resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==}
es-module-lexer@1.5.4:
resolution: {integrity: sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==}
escalade@3.1.2:
resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==}
@@ -1817,9 +1776,6 @@ packages:
fast-json-stable-stringify@2.1.0:
resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
fast-uri@3.0.6:
resolution: {integrity: sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==}
fastq@1.17.1:
resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==}
@@ -2004,9 +1960,6 @@ packages:
json-schema-traverse@0.4.1:
resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
json-schema-traverse@1.0.0:
resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
jsonc-parser@3.2.1:
resolution: {integrity: sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==}
@@ -2032,9 +1985,6 @@ packages:
lines-and-columns@1.2.4:
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
linkify-it@5.0.0:
resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==}
loader-runner@4.3.0:
resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==}
engines: {node: '>=6.11.5'}
@@ -2082,10 +2032,6 @@ packages:
resolution: {integrity: sha512-WWC0ZuMzCyDHYCasEGs4IPvLyTGftYwh6wIEOULOF0HXcqZlhwRzrK0w2VUlxWA98xnvb/jszw4ZSkJ6ADpM6Q==}
engines: {node: '>=0.10.0'}
markdown-it@14.1.0:
resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==}
hasBin: true
marked@4.3.0:
resolution: {integrity: sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==}
engines: {node: '>= 12'}
@@ -2145,9 +2091,6 @@ packages:
mdast-util-to-string@4.0.0:
resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==}
mdurl@2.0.0:
resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==}
merge-stream@2.0.0:
resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
@@ -2343,10 +2286,6 @@ packages:
resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==}
engines: {node: '>=16 || 14 >=14.17'}
minimatch@9.0.5:
resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
engines: {node: '>=16 || 14 >=14.17'}
minipass@7.1.0:
resolution: {integrity: sha512-oGZRv2OT1lO2UF1zUcwdTb3wqUwI0kBGTgt/T7OdSj6M6N5m3o5uPf0AIW6lVxGGoiWUR7e2AwTE+xiwK8WQig==}
engines: {node: '>=16 || 14 >=14.17'}
@@ -2415,8 +2354,8 @@ packages:
node-releases@2.0.14:
resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==}
node-releases@2.0.19:
resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==}
node-releases@2.0.18:
resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==}
nopt@7.2.1:
resolution: {integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==}
@@ -2564,10 +2503,6 @@ packages:
proto-list@1.2.4:
resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==}
punycode.js@2.3.1:
resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==}
engines: {node: '>=6'}
punycode@2.3.1:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
@@ -2630,10 +2565,6 @@ packages:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
engines: {node: '>=0.10.0'}
require-from-string@2.0.2:
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
engines: {node: '>=0.10.0'}
require-main-filename@2.0.0:
resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==}
@@ -2672,10 +2603,6 @@ packages:
resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==}
engines: {node: '>= 10.13.0'}
schema-utils@4.3.0:
resolution: {integrity: sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==}
engines: {node: '>= 10.13.0'}
selderee@0.11.0:
resolution: {integrity: sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==}
@@ -2819,8 +2746,8 @@ packages:
resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
engines: {node: '>=6'}
terser-webpack-plugin@5.3.12:
resolution: {integrity: sha512-jDLYqo7oF8tJIttjXO6jBY5Hk8p3A8W4ttih7cCEq64fQFWmgJ4VqAQjKr7WwIDlmXKEc6QeoRb5ecjZ+2afcg==}
terser-webpack-plugin@5.3.10:
resolution: {integrity: sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==}
engines: {node: '>= 10.13.0'}
peerDependencies:
'@swc/core': '*'
@@ -2835,8 +2762,8 @@ packages:
uglify-js:
optional: true
terser@5.39.0:
resolution: {integrity: sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==}
terser@5.36.0:
resolution: {integrity: sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w==}
engines: {node: '>=10'}
hasBin: true
@@ -2881,26 +2808,11 @@ packages:
peerDependencies:
typescript: 4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x
typedoc@0.27.9:
resolution: {integrity: sha512-/z585740YHURLl9DN2jCWe6OW7zKYm6VoQ93H0sxZ1cwHQEQrUn5BJrEnkWhfzUdyO+BLGjnKUZ9iz9hKloFDw==}
engines: {node: '>= 18'}
hasBin: true
peerDependencies:
typescript: 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x
typescript@5.4.5:
resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==}
engines: {node: '>=14.17'}
hasBin: true
typescript@5.6.3:
resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==}
engines: {node: '>=14.17'}
hasBin: true
uc.micro@2.1.0:
resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==}
undici-types@5.26.5:
resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
@@ -2958,8 +2870,8 @@ packages:
peerDependencies:
browserslist: '>= 4.21.0'
update-browserslist-db@1.1.3:
resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==}
update-browserslist-db@1.1.1:
resolution: {integrity: sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==}
hasBin: true
peerDependencies:
browserslist: '>= 4.21.0'
@@ -3039,11 +2951,6 @@ packages:
engines: {node: '>= 14'}
hasBin: true
yaml@2.7.0:
resolution: {integrity: sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==}
engines: {node: '>= 14'}
hasBin: true
yargs-parser@18.1.3:
resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==}
engines: {node: '>=6'}
@@ -3159,12 +3066,6 @@ snapshots:
'@floating-ui/utils@0.2.8': {}
'@gerrit0/mini-shiki@1.27.2':
dependencies:
'@shikijs/engine-oniguruma': 1.29.2
'@shikijs/types': 1.29.2
'@shikijs/vscode-textmate': 10.0.2
'@headlessui/react@2.2.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@floating-ui/react': 0.26.27(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -3193,25 +3094,17 @@ snapshots:
'@jridgewell/sourcemap-codec': 1.4.15
'@jridgewell/trace-mapping': 0.3.25
'@jridgewell/gen-mapping@0.3.8':
dependencies:
'@jridgewell/set-array': 1.2.1
'@jridgewell/sourcemap-codec': 1.5.0
'@jridgewell/trace-mapping': 0.3.25
'@jridgewell/resolve-uri@3.1.2': {}
'@jridgewell/set-array@1.2.1': {}
'@jridgewell/source-map@0.3.6':
dependencies:
'@jridgewell/gen-mapping': 0.3.8
'@jridgewell/gen-mapping': 0.3.5
'@jridgewell/trace-mapping': 0.3.25
'@jridgewell/sourcemap-codec@1.4.15': {}
'@jridgewell/sourcemap-codec@1.5.0': {}
'@jridgewell/trace-mapping@0.3.25':
dependencies:
'@jridgewell/resolve-uri': 3.1.2
@@ -3556,18 +3449,6 @@ snapshots:
domhandler: 5.0.3
selderee: 0.11.0
'@shikijs/engine-oniguruma@1.29.2':
dependencies:
'@shikijs/types': 1.29.2
'@shikijs/vscode-textmate': 10.0.2
'@shikijs/types@1.29.2':
dependencies:
'@shikijs/vscode-textmate': 10.0.2
'@types/hast': 3.0.4
'@shikijs/vscode-textmate@10.0.2': {}
'@stefanprobst/rehype-extract-toc@2.2.0':
dependencies:
estree-util-is-identifier-name: 2.1.0
@@ -4755,7 +4636,7 @@ snapshots:
dependencies:
undici-types: 5.26.5
'@types/node@20.17.22':
'@types/node@20.17.6':
dependencies:
undici-types: 6.19.8
@@ -4909,19 +4790,10 @@ snapshots:
acorn@8.14.0: {}
ajv-formats@2.1.1(ajv@8.17.1):
optionalDependencies:
ajv: 8.17.1
ajv-keywords@3.5.2(ajv@6.12.6):
dependencies:
ajv: 6.12.6
ajv-keywords@5.1.0(ajv@8.17.1):
dependencies:
ajv: 8.17.1
fast-deep-equal: 3.1.3
ajv@6.12.6:
dependencies:
fast-deep-equal: 3.1.3
@@ -4929,13 +4801,6 @@ snapshots:
json-schema-traverse: 0.4.1
uri-js: 4.4.1
ajv@8.17.1:
dependencies:
fast-deep-equal: 3.1.3
fast-uri: 3.0.6
json-schema-traverse: 1.0.0
require-from-string: 2.0.2
ansi-regex@5.0.1: {}
ansi-regex@6.0.1: {}
@@ -4957,8 +4822,6 @@ snapshots:
arg@5.0.2: {}
argparse@2.0.1: {}
astring@1.8.6: {}
autoprefixer@10.4.19(postcss@8.4.38):
@@ -4992,12 +4855,12 @@ snapshots:
node-releases: 2.0.14
update-browserslist-db: 1.0.15(browserslist@4.23.0)
browserslist@4.24.4:
browserslist@4.24.2:
dependencies:
caniuse-lite: 1.0.30001701
electron-to-chromium: 1.5.109
node-releases: 2.0.19
update-browserslist-db: 1.1.3(browserslist@4.24.4)
caniuse-lite: 1.0.30001683
electron-to-chromium: 1.5.64
node-releases: 2.0.18
update-browserslist-db: 1.1.1(browserslist@4.24.2)
buffer-from@1.1.2: {}
@@ -5013,8 +4876,6 @@ snapshots:
caniuse-lite@1.0.30001683: {}
caniuse-lite@1.0.30001701: {}
ccount@2.0.1: {}
character-entities-html4@2.1.0: {}
@@ -5156,20 +5017,20 @@ snapshots:
electron-to-chromium@1.4.761: {}
electron-to-chromium@1.5.109: {}
electron-to-chromium@1.5.64: {}
emoji-regex@8.0.0: {}
emoji-regex@9.2.2: {}
enhanced-resolve@5.18.1:
enhanced-resolve@5.17.1:
dependencies:
graceful-fs: 4.2.11
tapable: 2.2.1
entities@4.5.0: {}
es-module-lexer@1.6.0: {}
es-module-lexer@1.5.4: {}
escalade@3.1.2: {}
@@ -5244,8 +5105,6 @@ snapshots:
fast-json-stable-stringify@2.1.0: {}
fast-uri@3.0.6: {}
fastq@1.17.1:
dependencies:
reusify: 1.0.4
@@ -5417,7 +5276,7 @@ snapshots:
jest-worker@27.5.1:
dependencies:
'@types/node': 20.17.22
'@types/node': 20.17.6
merge-stream: 2.0.0
supports-color: 8.1.1
@@ -5439,8 +5298,6 @@ snapshots:
json-schema-traverse@0.4.1: {}
json-schema-traverse@1.0.0: {}
jsonc-parser@3.2.1: {}
jsts@2.7.1: {}
@@ -5455,10 +5312,6 @@ snapshots:
lines-and-columns@1.2.4: {}
linkify-it@5.0.0:
dependencies:
uc.micro: 2.1.0
loader-runner@4.3.0: {}
locate-path@5.0.0:
@@ -5491,15 +5344,6 @@ snapshots:
markdown-extensions@1.1.1: {}
markdown-it@14.1.0:
dependencies:
argparse: 2.0.1
entities: 4.5.0
linkify-it: 5.0.0
mdurl: 2.0.0
punycode.js: 2.3.1
uc.micro: 2.1.0
marked@4.3.0: {}
mdast-util-definitions@5.1.2:
@@ -5690,8 +5534,6 @@ snapshots:
dependencies:
'@types/mdast': 4.0.3
mdurl@2.0.0: {}
merge-stream@2.0.0: {}
merge2@1.4.1: {}
@@ -6127,10 +5969,6 @@ snapshots:
dependencies:
brace-expansion: 2.0.1
minimatch@9.0.5:
dependencies:
brace-expansion: 2.0.1
minipass@7.1.0: {}
mri@1.2.0: {}
@@ -6211,7 +6049,7 @@ snapshots:
node-releases@2.0.14: {}
node-releases@2.0.19: {}
node-releases@2.0.18: {}
nopt@7.2.1:
dependencies:
@@ -6345,8 +6183,6 @@ snapshots:
proto-list@1.2.4: {}
punycode.js@2.3.1: {}
punycode@2.3.1: {}
qrcode@1.5.4:
@@ -6427,8 +6263,6 @@ snapshots:
require-directory@2.1.1: {}
require-from-string@2.0.2: {}
require-main-filename@2.0.0: {}
resend@4.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
@@ -6470,13 +6304,6 @@ snapshots:
ajv: 6.12.6
ajv-keywords: 3.5.2(ajv@6.12.6)
schema-utils@4.3.0:
dependencies:
'@types/json-schema': 7.0.15
ajv: 8.17.1
ajv-formats: 2.1.1(ajv@8.17.1)
ajv-keywords: 5.1.0(ajv@8.17.1)
selderee@0.11.0:
dependencies:
parseley: 0.12.1
@@ -6507,16 +6334,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
shiki-twoslash@3.1.2(typescript@5.6.3):
dependencies:
'@typescript/twoslash': 3.1.0
'@typescript/vfs': 1.3.4
fenceparser: 1.1.1
shiki: 0.10.1
typescript: 5.6.3
transitivePeerDependencies:
- supports-color
shiki@0.10.1:
dependencies:
jsonc-parser: 3.2.1
@@ -6644,16 +6461,16 @@ snapshots:
tapable@2.2.1: {}
terser-webpack-plugin@5.3.12(webpack@5.91.0):
terser-webpack-plugin@5.3.10(webpack@5.91.0):
dependencies:
'@jridgewell/trace-mapping': 0.3.25
jest-worker: 27.5.1
schema-utils: 4.3.0
schema-utils: 3.3.0
serialize-javascript: 6.0.2
terser: 5.39.0
terser: 5.36.0
webpack: 5.91.0
terser@5.39.0:
terser@5.36.0:
dependencies:
'@jridgewell/source-map': 0.3.6
acorn: 8.14.0
@@ -6698,21 +6515,8 @@ snapshots:
shiki: 0.14.7
typescript: 5.4.5
typedoc@0.27.9(typescript@5.6.3):
dependencies:
'@gerrit0/mini-shiki': 1.27.2
lunr: 2.3.9
markdown-it: 14.1.0
minimatch: 9.0.5
typescript: 5.6.3
yaml: 2.7.0
typescript@5.4.5: {}
typescript@5.6.3: {}
uc.micro@2.1.0: {}
undici-types@5.26.5: {}
undici-types@6.19.8: {}
@@ -6795,9 +6599,9 @@ snapshots:
escalade: 3.1.2
picocolors: 1.0.0
update-browserslist-db@1.1.3(browserslist@4.24.4):
update-browserslist-db@1.1.1(browserslist@4.24.2):
dependencies:
browserslist: 4.24.4
browserslist: 4.24.2
escalade: 3.2.0
picocolors: 1.1.1
@@ -6853,10 +6657,10 @@ snapshots:
'@webassemblyjs/wasm-parser': 1.14.1
acorn: 8.14.0
acorn-import-assertions: 1.9.0(acorn@8.14.0)
browserslist: 4.24.4
browserslist: 4.24.2
chrome-trace-event: 1.0.4
enhanced-resolve: 5.18.1
es-module-lexer: 1.6.0
enhanced-resolve: 5.17.1
es-module-lexer: 1.5.4
eslint-scope: 5.1.1
events: 3.3.0
glob-to-regexp: 0.4.1
@@ -6867,7 +6671,7 @@ snapshots:
neo-async: 2.6.2
schema-utils: 3.3.0
tapable: 2.2.1
terser-webpack-plugin: 5.3.12(webpack@5.91.0)
terser-webpack-plugin: 5.3.10(webpack@5.91.0)
watchpack: 2.4.2
webpack-sources: 3.2.3
transitivePeerDependencies:
@@ -6903,8 +6707,6 @@ snapshots:
yaml@2.4.2: {}
yaml@2.7.0: {}
yargs-parser@18.1.3:
dependencies:
camelcase: 5.3.1

View File

@@ -1,5 +1,18 @@
# 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

View File

@@ -1,9 +1,9 @@
{
"name": "cojson-storage-indexeddb",
"version": "0.10.15",
"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"
}

View File

@@ -9,7 +9,9 @@
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"noUncheckedIndexedAccess": true,
"esModuleInterop": true
"esModuleInterop": true,
"declaration": true,
"declarationMap": true
},
"include": ["./src/**/*"]
}

View File

@@ -1,5 +1,24 @@
# cojson-storage-sqlite
## 0.11.1
### Patch Changes
- 7b00a81: Version bump
## 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

View File

@@ -1,9 +1,9 @@
{
"name": "cojson-storage-rn-sqlite",
"type": "module",
"version": "0.8.67",
"version": "0.11.1",
"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"
}
}

View File

@@ -9,7 +9,9 @@
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"noUncheckedIndexedAccess": true,
"esModuleInterop": true
"esModuleInterop": true,
"declaration": true,
"declarationMap": true
},
"include": ["./src/**/*"]
}

View File

@@ -1,5 +1,18 @@
# 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

View File

@@ -1,13 +1,13 @@
{
"name": "cojson-storage-sqlite",
"type": "module",
"version": "0.10.15",
"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.15",
"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"
}

View File

@@ -9,7 +9,9 @@
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"noUncheckedIndexedAccess": true,
"esModuleInterop": true
"esModuleInterop": true,
"declaration": true,
"declarationMap": true
},
"include": ["./src/**/*"]
}

View File

@@ -1,5 +1,17 @@
# 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

View File

@@ -1,9 +1,9 @@
{
"name": "cojson-storage",
"version": "0.10.15",
"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"
}
}

View File

@@ -9,7 +9,9 @@
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"noUncheckedIndexedAccess": true,
"esModuleInterop": true
"esModuleInterop": true,
"declaration": true,
"declarationMap": true
},
"include": ["./src/**/*"]
}

View File

@@ -1,5 +1,17 @@
# 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

View File

@@ -1,9 +1,9 @@
{
"name": "cojson-transport-ws",
"type": "module",
"version": "0.10.15",
"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",

View File

@@ -9,7 +9,9 @@
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"noUncheckedIndexedAccess": true,
"esModuleInterop": true
"esModuleInterop": true,
"declaration": true,
"declarationMap": true
},
"include": ["./src/**/*"]
}

View File

@@ -1,5 +1,19 @@
# 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

View File

@@ -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.15",
"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"
}

View File

@@ -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";
}

View File

@@ -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;
}>;
/**

View File

@@ -101,8 +101,7 @@ export function determineValidTransactions(
}
const transactorRoleAtTxTime =
groupAtTime.roleOfInternal(effectiveTransactor)?.role ||
groupAtTime.roleOfInternal(EVERYONE)?.role;
groupAtTime.roleOfInternal(effectiveTransactor)?.role;
if (
transactorRoleAtTxTime !== "admin" &&
@@ -135,8 +134,8 @@ export function determineValidTransactions(
}
function isHigherRole(a: Role, b: Role | undefined) {
if (a === undefined) return false;
if (b === undefined) return true;
if (a === undefined || a === "revoked") return false;
if (b === undefined || b === "revoked") return true;
if (b === "admin") return false;
if (a === "admin") return true;

View File

@@ -657,6 +657,79 @@ describe("extend", () => {
});
});
describe("unextend", () => {
test("should revoke roles", async () => {
const { node1, node2, node3 } = await createThreeConnectedNodes(
"server",
"server",
"server",
);
// `parentGroup` has `alice` as a writer
const parentGroup = node1.node.createGroup();
const alice = await loadCoValueOrFail(node1.node, node2.accountID);
parentGroup.addMember(alice, "writer");
// `alice`'s role in `parentGroup` is `"writer"`
expect(parentGroup.roleOf(alice.id)).toBe("writer");
// `childGroup` has `bob` as a reader
const childGroup = node1.node.createGroup();
const bob = await loadCoValueOrFail(node1.node, node3.accountID);
childGroup.addMember(bob, "reader");
// `bob`'s role in `childGroup` is `"reader"`
expect(childGroup.roleOf(bob.id)).toBe("reader");
// `childGroup` has `parentGroup`'s members (in this case, `alice` as a writer)
childGroup.extend(parentGroup);
expect(childGroup.roleOf(alice.id)).toBe("writer");
// `childGroup` no longer has `parentGroup`'s members
await childGroup.revokeExtend(parentGroup);
expect(childGroup.roleOf(bob.id)).toBe("reader");
expect(childGroup.roleOf(alice.id)).toBe(undefined);
});
test("should do nothing if applied to a group that is not extended", async () => {
const { node1, node2, node3 } = await createThreeConnectedNodes(
"server",
"server",
"server",
);
const parentGroup = node1.node.createGroup();
const alice = await loadCoValueOrFail(node1.node, node2.accountID);
parentGroup.addMember(alice, "writer");
const childGroup = node1.node.createGroup();
const bob = await loadCoValueOrFail(node1.node, node3.accountID);
childGroup.addMember(bob, "reader");
await childGroup.revokeExtend(parentGroup);
expect(childGroup.roleOf(bob.id)).toBe("reader");
expect(childGroup.roleOf(alice.id)).toBe(undefined);
});
test("should not throw if the revokeExtend is called twice", async () => {
const { node1, node2, node3 } = await createThreeConnectedNodes(
"server",
"server",
"server",
);
const parentGroup = node1.node.createGroup();
const alice = await loadCoValueOrFail(node1.node, node2.accountID);
parentGroup.addMember(alice, "writer");
const childGroup = node1.node.createGroup();
const bob = await loadCoValueOrFail(node1.node, node3.accountID);
childGroup.addMember(bob, "reader");
childGroup.extend(parentGroup);
await childGroup.revokeExtend(parentGroup);
await childGroup.revokeExtend(parentGroup);
expect(childGroup.roleOf(bob.id)).toBe("reader");
expect(childGroup.roleOf(alice.id)).toBe(undefined);
});
});
describe("extend with role mapping", () => {
test("mapping to writer should add the ability to write", async () => {
const { node1, node2 } = await createTwoConnectedNodes("server", "server");
@@ -842,3 +915,117 @@ describe("extend with role mapping", () => {
expect(mapOnNode2.get("test")).toEqual("Written from the admin");
});
});
test("roleOf should prioritize explicit account role over everyone role in same group", async () => {
const { node1, node2 } = await createTwoConnectedNodes("server", "server");
const group = node1.node.createGroup();
const account2 = await loadCoValueOrFail(node1.node, node2.accountID);
// Add both everyone and specific account
group.addMember("everyone", "reader");
group.addMember(account2, "writer");
// Should return the explicit role, not everyone's role
expect(group.roleOf(node2.accountID)).toEqual("writer");
// Change everyone's role
group.addMember("everyone", "writer");
// Should still return the explicit role
expect(group.roleOf(node2.accountID)).toEqual("writer");
});
test("roleOf should prioritize inherited everyone role over explicit account role", async () => {
const { node1, node2 } = await createTwoConnectedNodes("server", "server");
const parentGroup = node1.node.createGroup();
const childGroup = node1.node.createGroup();
const account2 = await loadCoValueOrFail(node1.node, node2.accountID);
// Set up inheritance
childGroup.extend(parentGroup);
// Add everyone to parent and account to child
parentGroup.addMember("everyone", "writer");
childGroup.addMember(account2, "reader");
// Should return the explicit role from child, not inherited everyone role
expect(childGroup.roleOf(node2.accountID)).toEqual("writer");
});
test("roleOf should use everyone role when no explicit role exists", async () => {
const { node1, node2 } = await createTwoConnectedNodes("server", "server");
const group = node1.node.createGroup();
// Add only everyone role
group.addMember("everyone", "reader");
// Should return everyone's role when no explicit role exists
expect(group.roleOf(node2.accountID)).toEqual("reader");
});
test("roleOf should inherit everyone role from parent when no explicit roles exist", async () => {
const { node1, node2 } = await createTwoConnectedNodes("server", "server");
const parentGroup = node1.node.createGroup();
const childGroup = node1.node.createGroup();
// Set up inheritance
childGroup.extend(parentGroup);
// Add everyone to parent only
parentGroup.addMember("everyone", "reader");
// Should inherit everyone's role from parent
expect(childGroup.roleOf(node2.accountID)).toEqual("reader");
});
test("roleOf should handle everyone role inheritance through multiple levels", async () => {
const { node1, node2 } = await createTwoConnectedNodes("server", "server");
const grandParentGroup = node1.node.createGroup();
const parentGroup = node1.node.createGroup();
const childGroup = node1.node.createGroup();
const childGroupOnNode2 = await loadCoValueOrFail(node2.node, childGroup.id);
const account2 = await loadCoValueOrFail(node1.node, node2.accountID);
// Set up inheritance chain
parentGroup.extend(grandParentGroup);
childGroup.extend(parentGroup);
// Add everyone to grandparent
grandParentGroup.addMember("everyone", "writer");
// Should inherit everyone's role from grandparent
expect(childGroup.roleOf(node2.accountID)).toEqual("writer");
// Add explicit role in parent
parentGroup.addMember(account2, "reader");
// Should use parent's explicit role instead of grandparent's everyone role
expect(childGroup.roleOf(node2.accountID)).toEqual("writer");
// Add explicit role in child
childGroup.addMember(account2, "admin");
await childGroup.core.waitForSync();
// Should use child's explicit role
expect(childGroup.roleOf(node2.accountID)).toEqual("admin");
// Remove child's explicit role
await childGroupOnNode2.removeMember(account2);
await childGroupOnNode2.core.waitForSync();
// Should fall back to parent's explicit role
expect(childGroup.roleOf(node2.accountID)).toEqual("writer");
// Remove parent's explicit role
await parentGroup.removeMember(account2);
await childGroup.core.waitForSync();
// Should fall back to grandparent's everyone role
expect(childGroup.roleOf(node2.accountID)).toEqual("writer");
});

View File

@@ -5,6 +5,7 @@ import { WasmCrypto } from "../crypto/WasmCrypto.js";
import { expectGroup } from "../typeUtils/expectGroup.js";
import {
connectTwoPeers,
createThreeConnectedNodes,
createTwoConnectedNodes,
groupWithTwoAdmins,
groupWithTwoAdminsHighLevel,
@@ -893,6 +894,26 @@ test("Admins can set group read key, make a private transaction in an owned obje
expect(childContentAsReader.get("foo2")).toEqual("bar2");
});
test("only admins can add agent ids", () => {
const { groupCore } = newGroup();
const inviteSecret = Crypto.newRandomAgentSecret();
const inviteID = Crypto.getAgentID(inviteSecret);
const groupAsInvite = expectGroup(
groupCore
.testWithDifferentAccount(
new ControlledAgent(inviteSecret, Crypto),
Crypto.newRandomSessionID(inviteID),
)
.getCurrentContent(),
);
groupAsInvite.set(inviteID, "adminInvite", "trusting");
expect(groupAsInvite.get(inviteID)).toEqual(undefined);
});
test("Admins can set group read rey, make a private transaction in an owned object, rotate the read key, add two readers, rotate the read key again to kick out one reader, make another private transaction in the owned object, and only the remaining reader can read both transactions", () => {
const { node, groupCore, admin } = newGroup();
@@ -2802,6 +2823,270 @@ test("Calling extend to create grand-child groups parent and child references an
expect(childContentAsReader.get("foo")).toEqual("bar");
});
test("revoking access on a child group doesn't block access to that group if a more permissive role is inheritable", async () => {
const { node1, node2 } = await createTwoConnectedNodes("server", "server");
const group = node1.node.createGroup();
const parentGroup = node1.node.createGroup();
group.extend(parentGroup);
const randomUser = await loadCoValueOrFail(node1.node, node2.accountID);
parentGroup.addMember(randomUser, "writer");
group.addMember(randomUser, "writer");
await group.removeMember(randomUser);
const childMap = group.createMap();
childMap.set("foo", "bar", "private");
const mapOnNode2 = await loadCoValueOrFail(node2.node, childMap.id);
mapOnNode2.set("foo", "baz", "private");
expect(mapOnNode2.get("foo")).toEqual("baz");
});
test("revoking access on a parent group doesn't block access to the child group if the same role is inheritable from a grand-parent group", async () => {
const { node1, node2 } = await createTwoConnectedNodes("server", "server");
const group = node1.node.createGroup();
const parentGroup = node1.node.createGroup();
const grandParentGroup = node1.node.createGroup();
group.extend(parentGroup);
parentGroup.extend(grandParentGroup);
const randomUser = await loadCoValueOrFail(node1.node, node2.accountID);
grandParentGroup.addMember(randomUser, "writer");
parentGroup.addMember(randomUser, "writer");
await parentGroup.removeMember(randomUser);
const childMap = group.createMap();
childMap.set("foo", "bar", "private");
const mapOnNode2 = await loadCoValueOrFail(node2.node, childMap.id);
mapOnNode2.set("foo", "baz", "private");
expect(mapOnNode2.get("foo")).toEqual("baz");
});
test("revoking access on a parent group doesn't block access to the child group if the same role is inheritable from another parent group", async () => {
const { node1, node2 } = await createTwoConnectedNodes("server", "server");
const group = node1.node.createGroup();
const parentGroup1 = node1.node.createGroup();
const parentGroup2 = node1.node.createGroup();
group.extend(parentGroup1);
group.extend(parentGroup2);
const randomUser = await loadCoValueOrFail(node1.node, node2.accountID);
parentGroup1.addMember(randomUser, "writer");
parentGroup2.addMember(randomUser, "writer");
await parentGroup1.removeMember(randomUser);
const childMap = group.createMap();
childMap.set("foo", "bar", "private");
const mapOnNode2 = await loadCoValueOrFail(node2.node, childMap.id);
mapOnNode2.set("foo", "baz", "private");
expect(mapOnNode2.get("foo")).toEqual("baz");
});
test("revoking write access to parent group", async () => {
// Start with a node and a group
const { group, node } = newGroupHighLevel();
// Create a parent group and relate it to the existing group
const parentGroup = node.createGroup();
group.extend(parentGroup);
// Create an account (`alice`) that can write to the parent group
// Create an account (`bob`) that can write to the child group
const alice = node.createAccount();
const bob = node.createAccount();
parentGroup.addMember(alice, "writer");
group.addMember(bob, "writer");
// The child group has a map that can be written to by `bob`
const mapCore = node.createCoValue({
type: "comap",
ruleset: { type: "ownedByGroup", group: group.id },
meta: null,
...Crypto.createdNowUnique(),
});
const bobMap = expectMap(
mapCore
.testWithDifferentAccount(bob, Crypto.newRandomSessionID(bob.id))
.getCurrentContent(),
);
// `bob` sets `foo` to `bar`
bobMap.set("foo", "bar", "private");
// `bob`'s change is made successfully
expect(bobMap.get("foo")).toEqual("bar");
const aliceMap = expectMap(
mapCore
.testWithDifferentAccount(alice, Crypto.newRandomSessionID(alice.id))
.getCurrentContent(),
);
// `alice` sets `foo` to `baz`
aliceMap.set("foo", "baz", "private");
// `alice`'s change is made successfully
expect(aliceMap.get("foo")).toEqual("baz");
// The two groups are no longer related
await group.revokeExtend(parentGroup);
// `bob` sets `foo` to `abc`
bobMap.set("foo", "abc", "private");
// `bob`'s change is made successfully
expect(bobMap.get("foo")).toEqual("abc");
const aliceMapAfterUnextend = expectMap(
mapCore
.testWithDifferentAccount(alice, Crypto.newRandomSessionID(alice.id))
.getCurrentContent(),
);
// `alice` attempts to set `foo` to `def`, but fails
expect(() => aliceMapAfterUnextend.set("foo", "def", "private")).toThrow(
"Can't make transaction without read key secret",
);
// `alice`'s change is not made successfully
expect(aliceMapAfterUnextend.get("foo")).not.toEqual("def");
});
test("revoking read access to parent group", async () => {
// Start with two nodes
const { node1, node2 } = await createTwoConnectedNodes("server", "server");
const group = node1.node.createGroup();
// Create a parent group and relate it to the existing group
const parentGroup = node1.node.createGroup();
group.extend(parentGroup);
// Create an account (`alice`) that can read from the parent group
// Create an account (`bob`) that can write to the child group
const alice = await loadCoValueOrFail(node1.node, node2.accountID);
const bob = await loadCoValueOrFail(node1.node, node1.accountID);
parentGroup.addMember(alice, "reader");
group.addMember(bob, "writer");
// The child group has a map that can be written to by `bob`
const bobMap = group.createMap();
// `bob` sets `foo` to `bar`
bobMap.set("foo", "bar", "private");
// `bob`'s change is made successfully
expect(bobMap.get("foo")).toEqual("bar");
const aliceMap = await loadCoValueOrFail(node2.node, bobMap.id);
// `alice` reads `foo` as `bar`
expect(aliceMap.get("foo")).toEqual("bar");
// The two groups are no longer related
await group.revokeExtend(parentGroup);
// `bob` sets `foo` to `abc`
bobMap.set("foo", "abc", "private");
// `bob`'s change is made successfully
expect(bobMap.get("foo")).toEqual("abc");
// `alice` reads `foo` as `bar`
expect(aliceMap.get("foo")).toEqual("bar");
});
test("revoking read access to grandparent group", async () => {
// Start with two nodes
const { node1, node2, node3 } = await createThreeConnectedNodes(
"server",
"server",
"server",
);
const group = node1.node.createGroup();
// Create group hierarchy
const parentGroup = node1.node.createGroup();
const grandParentGroup = node1.node.createGroup();
group.extend(parentGroup);
parentGroup.extend(grandParentGroup);
// Create an account (`alice`) that can read from the parent group
// Create an account (`bob`) that can write to the child group
// Create an account (`charlie`) that can read from the grandparent group
const alice = await loadCoValueOrFail(node1.node, node2.accountID);
const bob = await loadCoValueOrFail(node1.node, node1.accountID);
const charlie = await loadCoValueOrFail(node1.node, node3.accountID);
parentGroup.addMember(alice, "reader");
group.addMember(bob, "writer");
grandParentGroup.addMember(charlie, "reader");
// The child group has a map that can be written to by `bob`
const bobMap = group.createMap();
// `bob` sets `foo` to `bar`
bobMap.set("foo", "bar", "private");
// `bob`'s change is made successfully
expect(bobMap.get("foo")).toEqual("bar");
const aliceMap = await loadCoValueOrFail(node2.node, bobMap.id);
// `alice` reads `foo` as `bar`
expect(aliceMap.get("foo")).toEqual("bar");
const charlieMap = await loadCoValueOrFail(node3.node, bobMap.id);
// `charlie` reads `foo` as `bar`
expect(charlieMap.get("foo")).toEqual("bar");
// The groups are no longer related
await parentGroup.revokeExtend(grandParentGroup);
await group.revokeExtend(parentGroup);
// `bob` sets `foo` to `abc`
bobMap.set("foo", "abc", "private");
// `bob`'s change is made successfully
expect(bobMap.get("foo")).toEqual("abc");
// `alice` reads `foo` as `bar`
expect(aliceMap.get("foo")).toEqual("bar");
// `charlie` reads `foo` as `bar`
expect(charlieMap.get("foo")).toEqual("bar");
});
test("a user should have write access if the parent group has everyone as a writer", async () => {
const { node1, node2 } = await createTwoConnectedNodes("server", "server");
const group = node1.node.createGroup();
const parentGroup = node1.node.createGroup();
group.extend(parentGroup);
parentGroup.addMember("everyone", "writer");
const randomUser = await loadCoValueOrFail(node1.node, node2.accountID);
group.addMember(randomUser, "reader");
const childMap = group.createMap();
childMap.set("foo", "bar", "private");
const mapOnNode2 = await loadCoValueOrFail(node2.node, childMap.id);
mapOnNode2.set("foo", "baz", "private");
expect(mapOnNode2.get("foo")).toEqual("baz");
});
test("High-level permissions work correctly when a group is extended", async () => {
const { group, node } = newGroupHighLevel();
const parentGroup = node.createGroup();

View File

@@ -5,7 +5,7 @@ import {
MeterProvider,
MetricReader,
} from "@opentelemetry/sdk-metrics";
import { expect, vi } from "vitest";
import { expect, onTestFinished, vi } from "vitest";
import { ControlledAgent } from "../coValues/account.js";
import { WasmCrypto } from "../crypto/WasmCrypto.js";
import type { CoID, RawCoValue } from "../exports.js";
@@ -200,6 +200,9 @@ export function newGroupHighLevel() {
const group = node.createGroup();
onTestFinished(() => {
node.gracefulShutdown();
});
return { admin, node, group };
}

View File

@@ -9,7 +9,9 @@
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"noUncheckedIndexedAccess": true,
"esModuleInterop": true
"esModuleInterop": true,
"declaration": true,
"declarationMap": true
},
"include": ["./src/**/*.ts"],
"exclude": ["./node_modules"]

View File

@@ -1,5 +1,11 @@
# create-jazz-app
## 0.1.14
### Patch Changes
- 60ce483: create initial commit on create-jazz-app apps
## 0.1.13
### Patch Changes

View File

@@ -5,7 +5,7 @@
"types": "src/index.ts",
"type": "module",
"license": "MIT",
"version": "0.1.13",
"version": "0.1.14",
"bin": {
"create-jazz-app": "./dist/index.js"
},
@@ -26,7 +26,6 @@
"dev": "tsc --watch",
"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"
}
}

View File

@@ -292,7 +292,10 @@ module.exports = withNativeWind(config, { input: "./src/global.css" });
}).start();
try {
execSync(`cd "${projectName}" && git init`, { stdio: "pipe" });
execSync(
`cd "${projectName}" && git init && git add . && git commit -m "Initial commit from create-jazz-app"`,
{ stdio: "pipe" },
);
gitSpinner.succeed(chalk.green("Git repository initialized"));
} catch (error) {
gitSpinner.fail(chalk.red("Failed to initialize git repository"));

Some files were not shown because too many files have changed in this diff Show More