Compare commits

...

199 Commits

Author SHA1 Message Date
Emil Sayahi
c563c3675f fix(multiauth): update lockfile 2025-03-03 13:09:39 -05:00
Emil Sayahi
05372df4be Merge branch 'main' into example-counter 2025-03-03 13:03:33 -05:00
Emil Sayahi
c32c405bfd feat(examples): empty multi-auth example 2025-03-03 13:00:27 -05:00
Anselm Eickhoff
3c0b2cb689 Merge pull request #1557 from garden-co/1556-update-homepage-typedoc-to-027
1556-update-homepage-typedoc-to-027
2025-03-03 11:49:54 +00:00
Benjamin S. Leveritt
6a2a176361 Fix typing 2025-03-03 11:35:37 +00:00
Benjamin S. Leveritt
824bbc8bab Bumps typedoc and typescript versions 2025-03-03 11:35:15 +00:00
Emil Sayahi
e7ae6d95ba feat(examples): multi-auth counter example 2025-02-28 14:55:45 -05:00
Guido D'Orsi
8bdf6908b8 Merge pull request #1529 from garden-co/changeset-release/main
Version Packages
2025-02-28 20:18:53 +01:00
github-actions[bot]
12bc4fb2d3 Version Packages 2025-02-28 19:08:19 +00:00
Guido D'Orsi
75211e3c82 fix: fixes invalid authentication state when logging out after signUp (#1549) 2025-02-28 14:06:18 -05:00
Benjamin S. Leveritt
3185a20777 Merge pull request #1447 from garden-co/jazz-496-draft-using-coValue-docs
jazz-496-draft-using-coValue-docs
2025-02-28 13:42:26 +00:00
Benjamin S. Leveritt
0c3905f93f Add example, fix copy 2025-02-28 13:40:38 +00:00
Guido D'Orsi
8a31f56770 Merge pull request #1542 from garden-co/jazz-731-prevent-duplicate-accounts-on-inspector
Prevent duplicate accounts from being added in inspector
2025-02-28 14:10:21 +01:00
Guido D'Orsi
4373e290fe Merge pull request #1543 from garden-co/typecheck-tests
fix: include tests in the typescript validation on cojson and jazz-tools
2025-02-28 11:19:18 +01:00
Guido D'Orsi
037ed4d59d fix: include tests in the typescript validation on cojson and jazz-tools 2025-02-28 11:01:57 +01:00
Benjamin S. Leveritt
c030128b28 Fix comment 2025-02-28 09:53:15 +00:00
Nikita
34946c18bc prevent duplicate accounts from being added in inspector 2025-02-28 10:52:21 +01:00
Benjamin S. Leveritt
fcca08655a Update nav for new organisation 2025-02-28 09:46:13 +00:00
Benjamin S. Leveritt
adcd08c95a Copy tweaks from feedback 2025-02-28 09:46:13 +00:00
Benjamin S. Leveritt
937e20b248 Add back-references to example 2025-02-28 09:46:13 +00:00
Benjamin S. Leveritt
a759d9a7aa Partially hide unused pages
Signed-off-by: Benjamin S. Leveritt <benjamin@leveritt.co.uk>
2025-02-28 09:46:13 +00:00
Benjamin S. Leveritt
38af5b8dc8 Tweak copy
Signed-off-by: Benjamin S. Leveritt <benjamin@leveritt.co.uk>
2025-02-28 09:46:13 +00:00
Benjamin S. Leveritt
b9473da159 Adds CoFeeds doc
Signed-off-by: Benjamin S. Leveritt <benjamin@leveritt.co.uk>
2025-02-28 09:46:13 +00:00
Benjamin S. Leveritt
cb1126ac15 Updates CoList examples
Signed-off-by: Benjamin S. Leveritt <benjamin@leveritt.co.uk>
2025-02-28 09:46:13 +00:00
Benjamin S. Leveritt
989211f02f Removes performance section
Signed-off-by: Benjamin S. Leveritt <benjamin@leveritt.co.uk>
2025-02-28 09:46:13 +00:00
Benjamin S. Leveritt
49a1a9d6f0 Adds CoLists doc
Signed-off-by: Benjamin S. Leveritt <benjamin@leveritt.co.uk>
2025-02-28 09:46:13 +00:00
Benjamin S. Leveritt
2f32599987 Improve ComputedProject example
Signed-off-by: Benjamin S. Leveritt <benjamin@leveritt.co.uk>
2025-02-28 09:46:13 +00:00
Benjamin S. Leveritt
9ede39c229 Rework into CoMaps doc
Signed-off-by: Benjamin S. Leveritt <benjamin@leveritt.co.uk>
2025-02-28 09:46:13 +00:00
Benjamin S. Leveritt
ca3eb9dbd8 Adds Subscription docs
Signed-off-by: Benjamin S. Leveritt <benjamin@leveritt.co.uk>
2025-02-28 09:46:13 +00:00
Benjamin S. Leveritt
12273cf35b Typo
Signed-off-by: Benjamin S. Leveritt <benjamin@leveritt.co.uk>
2025-02-28 09:46:13 +00:00
Benjamin S. Leveritt
b3d4944608 Removes error handling section
Signed-off-by: Benjamin S. Leveritt <benjamin@leveritt.co.uk>
2025-02-28 09:46:13 +00:00
Benjamin S. Leveritt
2068aaff13 Removes owners
Signed-off-by: Benjamin S. Leveritt <benjamin@leveritt.co.uk>
2025-02-28 09:46:13 +00:00
Benjamin S. Leveritt
9dfad43433 Fix link
Signed-off-by: Benjamin S. Leveritt <benjamin@leveritt.co.uk>
2025-02-28 09:46:13 +00:00
Benjamin S. Leveritt
db4986059e Updates metadata for latest copy
Signed-off-by: Benjamin S. Leveritt <benjamin@leveritt.co.uk>
2025-02-28 09:46:13 +00:00
Benjamin S. Leveritt
ed5369e99b Updates the metadata copy and examples
Signed-off-by: Benjamin S. Leveritt <benjamin@leveritt.co.uk>
2025-02-28 09:46:13 +00:00
Benjamin S. Leveritt
2671fca1dc Adds reviewed copy for Reading + examples
Signed-off-by: Benjamin S. Leveritt <benjamin@leveritt.co.uk>
2025-02-28 09:46:13 +00:00
Benjamin S. Leveritt
c81a2dcd0b Updates Writing examples and copy
Signed-off-by: Benjamin S. Leveritt <benjamin@leveritt.co.uk>
2025-02-28 09:46:13 +00:00
Benjamin S. Leveritt
b7a19c0693 Updates creation examples
Signed-off-by: Benjamin S. Leveritt <benjamin@leveritt.co.uk>
2025-02-28 09:46:13 +00:00
Benjamin S. Leveritt
2678bdbcca Updates examples
Signed-off-by: Benjamin S. Leveritt <benjamin@leveritt.co.uk>
2025-02-28 09:46:13 +00:00
Benjamin S. Leveritt
ee7823b33e Fix Account and Group casing
Signed-off-by: Benjamin S. Leveritt <benjamin@leveritt.co.uk>
2025-02-28 09:46:13 +00:00
Benjamin S. Leveritt
46683235ba Removes transactions content 2025-02-28 09:46:13 +00:00
Benjamin S. Leveritt
8ce7bb808c Improves accuracy of examples 2025-02-28 09:46:13 +00:00
Benjamin S. Leveritt
3a69928ebc Adds CodeGroups, improves copy 2025-02-28 09:46:13 +00:00
Benjamin S. Leveritt
00790a1535 Adds drafts to nav 2025-02-28 09:46:13 +00:00
Benjamin S. Leveritt
56ccf5ea65 Adds metadata and writing drafts 2025-02-28 09:46:13 +00:00
Benjamin S. Leveritt
4814595724 Adds Reading draft 2025-02-28 09:46:13 +00:00
Benjamin S. Leveritt
706357ac4f Adds Creation draft 2025-02-28 09:46:13 +00:00
Benjamin S. Leveritt
4fcc8edc70 Merge pull request #1539 from garden-co/1538-fix-linting-for-cojson-transport-ws
1538-fix-linting-for-cojson-transport-ws
2025-02-28 08:24:47 +00:00
Benjamin S. Leveritt
0f8ba9966b Fix resolve returning void (which biome doesn't like)
Closes #1538
2025-02-27 17:18:57 +00:00
Benjamin S. Leveritt
91fa2e092a Add comment about using any 2025-02-27 17:08:06 +00:00
Benjamin S. Leveritt
ff52d6df3e Adds linting to cojson-transport-ws
Fixes #1538
2025-02-27 17:08:06 +00:00
Benjamin S. Leveritt
a539b9e26b Merge pull request #1536 from garden-co/1533-fix-linting-for-cojson-storage-packages
Closes #1533
2025-02-27 17:05:44 +00:00
Benjamin S. Leveritt
f3129a7914 Fix return on transaction 2025-02-27 16:40:25 +00:00
Guido D'Orsi
d349b794e1 Merge pull request #1534 from boorad/feat/clerk-expo-offline
feat: Clerk and Expo offline support
2025-02-27 17:24:20 +01:00
Benjamin S. Leveritt
fad14dcff6 Fix imports in jazz-nodejs 2025-02-27 16:21:54 +00:00
Benjamin S. Leveritt
b99f13c948 Adds a few more tweaks to the linter, and fixes 2025-02-27 16:21:54 +00:00
Benjamin S. Leveritt
e7cb337a24 Fix linting issues in cojson-storage-rn-sqlite 2025-02-27 16:21:54 +00:00
Benjamin S. Leveritt
85c9a432c3 Adds linting checks and fixes to cojson-storage 2025-02-27 16:21:54 +00:00
Brad Anderson
c28d1c331c feat: Clerk and Expo offline support 2025-02-27 10:01:46 -05:00
Guido D'Orsi
474ea89b81 Merge pull request #1532 from juliusv/patch-2
Fix missing useAccount import + incorrect "old" marker in React guide
2025-02-27 15:10:48 +01:00
Julius Volz
4772309fb6 Fix missing useAccount import + incorrect "old" marker in React guide 2025-02-27 13:43:20 +01:00
pax
1501510cfc Merge pull request #1493 from garden-co/cursor-rules-with-create-jazz-app
Cursor rules with create-jazz-app
2025-02-27 12:09:01 +02:00
Guido D'Orsi
eda1588907 Merge pull request #1527 from garden-co/changeset-release/main
Version Packages
2025-02-27 09:56:16 +01:00
github-actions[bot]
b14e0bfe24 Version Packages 2025-02-26 17:58:41 +00:00
Guido D'Orsi
87aa43b46b Merge pull request #1507 from garden-co/jazz-747-passphrase-auth-restore-support-for-rerolling-the-passphrase
feat(PasskeyAuth): support random passphrase generation
2025-02-26 18:54:42 +01:00
Guido D'Orsi
b93ce9fb7e chore: rever to use property initializers 2025-02-26 18:54:32 +01:00
Guido D'Orsi
a7d83e1c10 feat(passphrase-example): inline the UI and implement the passphrase reroll flow 2025-02-26 16:29:05 +01:00
Guido D'Orsi
76a693da15 Update packages/jazz-tools/src/tests/PassphraseAuth.test.ts
Co-authored-by: Giordano Ricci <me@giordanoricci.com>
2025-02-26 16:23:02 +01:00
pax
df2f021cfd Merge pull request #1509 from garden-co/changeset-release/main
Version Packages
2025-02-26 16:25:01 +02:00
github-actions[bot]
50b15d2d1d Version Packages 2025-02-26 13:43:02 +00:00
pax
48bf7cb188 Merge pull request #1519 from garden-co/create-jazz-app-dev-deps-handling
fix(create-jazz-app): handle workspace devDependencies
2025-02-26 15:40:53 +02:00
pax-k
e2e0af34b5 chore: changeset 2025-02-26 15:04:57 +02:00
Trisha Lim
6a5bcd3063 Fix missing og meta tags 2025-02-26 19:34:15 +07:00
pax-k
bf7e62ec76 fix(create-jazz-app): handle workspace devDependencies 2025-02-26 11:58:44 +02:00
Guido D'Orsi
71dda6b10b docs(contributing): fix the build step 2025-02-25 21:33:35 +01:00
Emil Sayahi
4612e0545e fix: type inference on useCoState (#1489)
* fix: type inference on `useCoState`

The `id` parameter should be `ID<CoValue>` so the type inference picks up the CoValue type only from the `Schema` parameter.

See: #1476

* fix: type inference on loading & subscribing API

* test: add type inference `useCoState` tests

* chore: changeset
2025-02-25 10:35:53 -05:00
Guido D'Orsi
07feedd641 feat(PasskeyAuth): support random passphrase generation 2025-02-25 15:35:48 +01:00
Guido D'Orsi
edbd567f11 Merge pull request #1491 from garden-co/remove-neverthrow-from-agent
chore: remove neverthrow from the agent/sealer id getters
2025-02-25 15:22:07 +01:00
Guido D'Orsi
4d8bb9cdb8 Merge pull request #1495 from garden-co/changeset-release/main
Version Packages
2025-02-25 12:37:27 +01:00
github-actions[bot]
1971448f5d Version Packages 2025-02-25 11:31:50 +00:00
Guido D'Orsi
0e861e7df8 Merge pull request #1501 from garden-co/revert-1487-jazz-730-passkeyauth-update-the-profile-name-only-if-the-value-on
Revert "fix(PasskeyAuth): set name iff username given"
2025-02-25 12:30:04 +01:00
Guido D'Orsi
6e3f1efcd0 Revert "fix(PasskeyAuth): set name iff username given (#1487)"
This reverts commit d469d68771.
2025-02-25 12:29:28 +01:00
Guido D'Orsi
eb87d10783 Merge pull request #1498 from garden-co/remove-quick-crypto
fix: don't export RNQuickCrypto to avoid install errors
2025-02-25 12:14:22 +01:00
Guido D'Orsi
5a54e4aa50 fix: don't export RNQuickCrypto to avoid install errors 2025-02-25 11:51:57 +01:00
Guido D'Orsi
16b0a22ded chore: remove RNQuickCrypto from the chat example 2025-02-25 10:42:16 +01:00
pax-k
6a8ce1e32d chore(create-jazz-app): only copy .cursor dir 2025-02-24 23:58:32 +02:00
pax-k
10a4b0e888 Merge branch 'main' into cursor-rules-with-create-jazz-app 2025-02-24 23:44:06 +02:00
pax-k
3c973c84ce fix(create-jazz-app): git init inside new project 2025-02-24 23:40:50 +02:00
Emil Sayahi
d469d68771 fix(PasskeyAuth): set name iff username given (#1487)
* 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

* chore: changeset

* docs(changeset): mention PasskeyAuth

Co-authored-by: Guido D'Orsi <gu.dorsi@gmail.com>

---------

Co-authored-by: Guido D'Orsi <gu.dorsi@gmail.com>
2025-02-24 16:23:09 -05:00
pax
c068d7a369 Merge pull request #1494 from garden-co/update-cursor-docs
fix: cursor docs structure
2025-02-24 22:31:15 +02:00
pax-k
25088ed5db fix: cursor docs structure 2025-02-24 22:29:04 +02:00
pax-k
21e74998e8 fix(cursor-docs): markdown references to examples 2025-02-24 20:53:17 +02:00
pax-k
a2e9ae4731 chore: refactored cursor rule name 2025-02-24 20:47:39 +02:00
pax-k
0514a7e64b chore: run pre-publish workflow when PR labels change 2025-02-24 20:35:40 +02:00
pax-k
b063cccdfc chore: changeset 2025-02-24 19:16:37 +02:00
pax-k
d89d2978ff feat: added cursor-docs to create-jazz-app 2025-02-24 19:16:00 +02:00
Guido D'Orsi
221ca30790 chore: remove neverthrow from the agent/sealer id getters 2025-02-24 18:11:06 +01:00
pax
caa6c147c8 Merge pull request #1490 from garden-co/changeset-release/main
Version Packages
2025-02-24 18:40:15 +02:00
github-actions[bot]
4da51e8f9c Version Packages 2025-02-24 16:37:25 +00:00
pax
a2076b179b Merge pull request #1488 from garden-co/cursor-docs
Cursor docs
2025-02-24 18:35:06 +02:00
pax-k
3fa276c18d chore: changeset 2025-02-24 18:18:33 +02:00
pax-k
1928519d39 feat: added cursor docs 2025-02-24 18:16:39 +02:00
Anselm Eickhoff
f4fa80b782 Merge pull request #1469 from garden-co/changeset-release/main 2025-02-23 10:13:07 +00:00
github-actions[bot]
782df5d4b8 Version Packages 2025-02-23 10:12:42 +00:00
Anselm Eickhoff
9db20ad630 Merge pull request #1468 from Schniz/schniz/fix-rn-kv-persistence 2025-02-23 10:11:09 +00:00
Gal Schlezinger
3405d8f275 Add changeset 2025-02-23 12:09:48 +02:00
Gal Schlezinger
0a64dca0cd [jazz-react-native] override Context#getKvStore to actually use the declared kvStore in the provider 2025-02-23 11:42:46 +02:00
Guido D'Orsi
4d0b9b1bf1 Merge pull request #1466 from garden-co/changeset-release/main
Version Packages
2025-02-22 17:59:46 +01:00
github-actions[bot]
403d61c8e8 Version Packages 2025-02-22 16:58:53 +00:00
Guido D'Orsi
0685c1cd5f Merge pull request #1465 from garden-co/passkey-attachment
fix: remove the attachment setting to restore the faceid login
2025-02-22 17:57:16 +01:00
Guido D'Orsi
834203f270 fix: remove the attachment setting to restore the faceid login 2025-02-22 17:54:02 +01:00
Guido D'Orsi
f01bc19257 test: remove .only 2025-02-21 16:07:33 +01:00
Guido D'Orsi
6405a77aa2 chore: fix tsconfig module value on the root 2025-02-21 11:30:43 +01:00
Guido D'Orsi
ace6486075 test: add deepLoading tests on CoMap.Record 2025-02-21 11:29:51 +01:00
Guido D'Orsi
23c3e5a125 Merge pull request #1456 from garden-co/changeset-release/main
Version Packages
2025-02-21 11:08:01 +01:00
github-actions[bot]
e3d75e5c97 Version Packages 2025-02-21 10:05:16 +00:00
Guido D'Orsi
7838075bd6 Merge pull request #1446 from garden-co/yubikey-asskey-support
feat: support external authenticators on passkey authentication
2025-02-21 11:02:04 +01:00
Guido D'Orsi
dd792bf0ca Merge pull request #1450 from garden-co/fix-missing-or-undefined-values
fix: improve subscribe behavoir on missing required and undeclared fields
2025-02-21 11:01:46 +01:00
Guido D'Orsi
233aae1deb fix: throw when a requested field on CoMap.Record is missing 2025-02-21 10:41:53 +01:00
Guido D'Orsi
153dc996a5 fix: catch errors on CoValueCore subscribers to avoid effects on the sync 2025-02-21 10:35:07 +01:00
Anselm Eickhoff
2b548c1758 Merge pull request #1442 from garden-co/jazz-734-add-docs-about-inspector
Add jazz inspector docs
2025-02-21 09:10:14 +00:00
Anselm Eickhoff
167b588553 Merge pull request #1455 from garden-co/jazz-738-table-of-contents-not-showing-in-upgrade-guides
Fix missing table of contents on upgrade guides
2025-02-21 09:09:18 +00:00
Anselm Eickhoff
424930d06d Merge pull request #1454 from garden-co/jazz-737-font-mono-doesnt-look-right-in-safari
Fix monospace font in safari
2025-02-21 09:08:07 +00:00
Giordano Ricci
d624a676d8 Merge pull request #1449 from garden-co/gio/docs/anchors-headings
add anchor to headings in mdx components
2025-02-21 10:05:05 +01:00
Trisha Lim
06f2af465d Fix missing table of contents on upgrade guides 2025-02-21 13:33:54 +07:00
Trisha Lim
c7332f84b9 Fix monospace font in safari 2025-02-21 11:39:22 +07:00
Trisha Lim
68fdbfbe94 Clean up 2025-02-21 11:22:32 +07:00
Trisha Lim
e98d0e0c7f Match # size with heading text 2025-02-21 11:19:45 +07:00
Trisha Lim
7efe89df31 Spacing and sizing adjustments 2025-02-21 10:56:51 +07:00
Guido D'Orsi
2fb6428ea1 fix: improve subscribe behavoir on missing required and undeclared fields 2025-02-20 20:05:36 +01:00
Giordano Ricci
5a8f5d8bc2 add anchor to headings in mdx components 2025-02-20 17:46:53 +00:00
Guido D'Orsi
2cc9daab37 test: add unsubscribe test 2025-02-20 18:02:03 +01:00
Guido D'Orsi
e0276f42ee Merge pull request #1448 from garden-co/revert-1445-gio/docs/add-profile-migration-example
Revert "docs: enrich migration example with profile"
2025-02-20 17:17:49 +01:00
Guido D'Orsi
363be52022 Revert "docs: enrich migration example with profile" 2025-02-20 17:17:35 +01:00
Guido D'Orsi
1e87fc7772 feat: support external authenticators on passkey authentication 2025-02-20 17:01:03 +01:00
Giordano Ricci
03ec5d3ec8 Merge pull request #1445 from garden-co/gio/docs/add-profile-migration-example
docs: enrich migration example with profile
2025-02-20 16:43:27 +01:00
Giordano Ricci
5f272ff6ba docs: enrich migration example with profile 2025-02-20 15:15:25 +00:00
Guido D'Orsi
12392424dd feat(llms-txt): add the music player example code 2025-02-20 21:14:57 +07:00
Guido D'Orsi
58eb3c0a98 test: fix the cloudflare test code 2025-02-20 14:55:05 +01:00
Guido D'Orsi
adf965d53d test: increase the timeout to 10s on the cloudflare integration test 2025-02-20 14:48:10 +01:00
Guido D'Orsi
3baa951bb9 Merge pull request #1424 from garden-co/changeset-release/main
Version Packages
2025-02-20 14:46:21 +01:00
github-actions[bot]
b726e31669 Version Packages 2025-02-20 13:44:04 +00:00
Guido D'Orsi
63bb31e0ad Merge pull request #1431 from garden-co/auth-state-sync
fix: isAuthenticated out-of-sync with the account state
2025-02-20 14:42:07 +01:00
Guido D'Orsi
d059460abd Merge pull request #1414 from garden-co/fix/examples-auth
Remove log out button in form and image upload example
2025-02-20 14:32:12 +01:00
Guido D'Orsi
ba81951331 test: fix the mobile e2e tests 2025-02-20 14:22:21 +01:00
Guido D'Orsi
bd132bb9ad Merge pull request #1437 from garden-co/feat/enable-cmd-j-default
enable Cmd+J by default to launch inspector on dev
2025-02-20 14:10:31 +01:00
Benjamin S. Leveritt
1ee435ad95 Merge pull request #1439 from garden-co/feat/big-llms-txt
Make llms.txt more complete
2025-02-20 12:51:42 +00:00
Trisha Lim
07273f7ab8 Add jazz inspector docs 2025-02-20 19:17:06 +07:00
Guido D'Orsi
646ad330ae Merge pull request #1430 from garden-co/tests/cloudflare
test: add a cloudflare workers integration test
2025-02-20 13:06:58 +01:00
Guido D'Orsi
af8e6e3f82 chore: inline callbacks
Co-authored-by: Giordano Ricci <me@giordanoricci.com>
2025-02-20 13:05:05 +01:00
Guido D'Orsi
f247525dfe test: fix cloudflare tests on CI 2025-02-20 13:01:42 +01:00
Guido D'Orsi
012022db2b chore: changeset 2025-02-20 12:46:58 +01:00
Guido D'Orsi
da92891498 test: add cloudflare integration tests 2025-02-20 12:46:24 +01:00
Guido D'Orsi
b0c55720f8 feat: crypto option to provide PureJSCrypto on the runtimes where wasm is not supported 2025-02-20 12:45:59 +01:00
Trisha Lim
a3d825fc6f make llms.txt more complete 2025-02-20 18:34:01 +07:00
Guido D'Orsi
29ca2e6f65 update lockfile again 2025-02-20 12:07:29 +01:00
Guido D'Orsi
07a683c13d chore: remove cloudflare tests for compat issues with Vitest 3 2025-02-20 12:07:28 +01:00
Guido D'Orsi
e53f02d6d7 chore: update lockfile and format vitest config 2025-02-20 12:07:28 +01:00
Guido D'Orsi
4915bfa26d test: add a cloudflare workers integration test 2025-02-20 12:07:27 +01:00
Guido D'Orsi
af45aac5f2 feat: improve error logging on sync errors 2025-02-20 12:07:27 +01:00
Guido D'Orsi
1136d9b744 chore: changeset 2025-02-20 12:01:35 +01:00
Guido D'Orsi
92e78dc262 chore: clean up debug code 2025-02-20 11:54:57 +01:00
Trisha Lim
bf76d798c4 add changeset 2025-02-20 17:54:21 +07:00
Benjamin S. Leveritt
627f8c4c28 Merge pull request #1413 from garden-co/jazz-720-fix-jazz-crypto-rs-imports-for-nextjs
Bumps crypto version
2025-02-20 10:52:41 +00:00
Trisha Lim
a1e0410863 enable Cmd+J to launch inspector on dev 2025-02-20 17:52:41 +07:00
Guido D'Orsi
03897a2689 fix: clean up AuthSecretStorage notification management and fix the tests 2025-02-20 11:45:25 +01:00
Guido D'Orsi
823f546028 test: cover the logOut and authenticate updates with tests 2025-02-20 11:45:24 +01:00
Guido D'Orsi
9bc54d1939 chore: clean up the useCoState code 2025-02-20 11:45:24 +01:00
Guido D'Orsi
30780c05f0 fix: initial fix for logIn/logOut state sync 2025-02-20 11:45:24 +01:00
Guido D'Orsi
118b6294ac Merge pull request #1435 from garden-co/jazz-725-react-starter-profile-name-is-lost-on-signup
Fix wrong name field on form in starter app
2025-02-20 11:45:08 +01:00
Trisha Lim
6dc9b9d2ec Improve llms.txt documentation (#1436)
* Make download link to llms-full.txt more prominent

* add separate section for llms.txt convention
2025-02-20 17:41:38 +07:00
Benjamin S. Leveritt
ccbcee5102 Bumps crypto version 2025-02-20 10:41:31 +00:00
Giordano Ricci
0ae2067c3c Merge pull request #1403 from garden-co/gio/auth-fixes
fix: fixes clerk auth flow
2025-02-20 11:33:17 +01:00
Trisha Lim
2137938ead Fix wrong name field on form in starter app 2025-02-20 16:31:08 +07:00
Benjamin S. Leveritt
d14bb57ff5 Tweaks LLMs docs copy (#1423) 2025-02-20 11:17:19 +07:00
Giordano Ricci
3f42a4ddf9 add test 2025-02-19 15:25:02 +00:00
Giordano Ricci
0eed228170 add changeset 2025-02-19 14:32:48 +00:00
Giordano Ricci
a519537701 remove unused import 2025-02-19 13:52:14 +00:00
Giordano Ricci
43c79cac2a add simple test 2025-02-19 13:46:37 +00:00
Giordano Ricci
44dbaa00d4 revert wrong change 2025-02-19 12:12:01 +00:00
Giordano Ricci
a0df32e81a add types test 2025-02-19 12:08:42 +00:00
Giordano Ricci
236d8226d8 remove prevContext promise 2025-02-19 10:39:32 +00:00
Giordano Ricci
1220fa5d97 remove reset 2025-02-19 10:37:33 +00:00
Benjamin S. Leveritt
f3a5f83f25 Initializes the wasm module on creation 2025-02-19 10:36:29 +00:00
Benjamin S. Leveritt
a1bd6fc79b Bumps crypto version 2025-02-19 10:36:29 +00:00
Benjamin S. Leveritt
0f83320222 Chore: changeset 2025-02-19 10:36:29 +00:00
Benjamin S. Leveritt
a3c4067de3 Bumps crypto version 2025-02-19 10:36:29 +00:00
Benjamin S. Leveritt
3042627748 Merge pull request #1419 from garden-co/docs/llms
Add LLM page to docs
2025-02-19 10:18:44 +00:00
Guido D'Orsi
8cea1e96cf Merge pull request #1418 from garden-co/fix/idb-transactions
fix: improve the rollback on failure when handling new content in storage
2025-02-19 11:12:25 +01:00
Giordano Ricci
5cc58c8e02 whoopsie 2025-02-18 22:40:01 +00:00
Giordano Ricci
9df644c578 fix 2025-02-18 22:35:35 +00:00
Guido D'Orsi
1e625f3c12 chore: changeset 2025-02-18 18:14:39 +01:00
Guido D'Orsi
8b3686c7ce feat: use the uniqueSessions index to get the single coValue session 2025-02-18 18:12:47 +01:00
Guido D'Orsi
bce04ee06d chore: restore the transactions autobatching 2025-02-18 18:05:53 +01:00
Guido D'Orsi
f2e9115f4c fix: improve transactions management on IDB 2025-02-18 17:51:32 +01:00
Giordano Ricci
6854f9930c wip: fix clerk auth flow 2025-02-18 16:47:49 +00:00
Guido D'Orsi
ee0897d9a8 fix: improve the rollback on failure when handling new content in storage 2025-02-18 14:42:29 +01:00
Trisha Lim
243ab074eb Remove non-functional nav from image upload example 2025-02-18 20:21:55 +07:00
Trisha Lim
938f9256db Remove non-functional nav from form example 2025-02-18 15:31:53 +07:00
279 changed files with 14266 additions and 2023 deletions

View File

@@ -1,7 +1,7 @@
name: Pre-Publish tagged Pull Requests
on:
pull_request:
types: [opened, synchronize, reopened]
types: [opened, synchronize, reopened, labeled]
jobs:
pre-release:
@@ -99,4 +99,4 @@ jobs:
);
await logPublishInfo();
}
}
}

View File

@@ -36,7 +36,7 @@ We welcome all ideas! If you have suggestions, feel free to open an issue marked
### 5. Local Setup
You'll need Node.js 20.x or 22.x installed (we're working on support for 23.x), and pnpm 9.x installed. If you're using nix, run `nix develop` to get a shell with the correct versions of everything installed.
You'll need Node.js 22.x installed (we're working on support for 23.x), and pnpm 9.x installed. If you're using nix, run `nix develop` to get a shell with the correct versions of everything installed.
1. **Clone the repository**:
```bash
@@ -54,6 +54,12 @@ You'll need Node.js 20.x or 22.x installed (we're working on support for 23.x),
cd homepage && pnpm install
```
4. **Go back to the project root**:
```bash
cd ..
```
4. **Build the packages**:
```bash

View File

@@ -12,7 +12,9 @@
"**/ios/**",
"**/android/**",
"packages/jazz-svelte/**",
"examples/*svelte*/**"
"examples/*svelte*/**",
"homepage/homepage/**",
"**/package.json"
]
},
"formatter": {
@@ -42,15 +44,6 @@
}
},
"overrides": [
{
"include": ["**/package.json"],
"linter": {
"enabled": false
},
"formatter": {
"enabled": false
}
},
{
"include": ["packages/**/src/**"],
"linter": {
@@ -61,21 +54,24 @@
}
},
{
"include": ["packages/**/src/tests/**", "packages/**/src/test/**"],
"include": ["packages/cojson-storage*/**", "cojson-transport-ws/**"],
"linter": {
"enabled": true,
"rules": {
"correctness": {
"useImportExtensions": "off"
}
"recommended": true
}
}
},
{
"include": ["packages/cojson-storage-indexeddb/**"],
"include": ["packages/**/src/tests/**"],
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"correctness": {
"useImportExtensions": "off"
},
"style": {
"noNonNullAssertion": "off"
},
"suspicious": {
"noExplicitAny": "info"
}

View File

@@ -1,5 +1,78 @@
# chat-rn-clerk
## 1.0.79
### Patch Changes
- Updated dependencies [75211e3]
- jazz-tools@0.10.14
- jazz-react-native@0.10.14
- jazz-react-native-auth-clerk@0.10.14
- jazz-react-native-media-images@0.10.14
## 1.0.78
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-react-native@0.10.13
- jazz-react-native-auth-clerk@0.10.13
- jazz-react-native-media-images@0.10.13
## 1.0.77
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
- jazz-react-native@0.10.12
- jazz-react-native-auth-clerk@0.10.12
- jazz-react-native-media-images@0.10.12
## 1.0.76
### Patch Changes
- Updated dependencies [5a54e4a]
- jazz-react-native@0.10.11
- jazz-react-native-auth-clerk@0.10.11
## 1.0.75
### Patch Changes
- Updated dependencies [3405d8f]
- jazz-react-native@0.10.10
- jazz-react-native-auth-clerk@0.10.10
## 1.0.74
### Patch Changes
- jazz-react-native-auth-clerk@0.10.9
## 1.0.73
### Patch Changes
- Updated dependencies [2fb6428]
- jazz-tools@0.10.8
- jazz-react-native@0.10.8
- jazz-react-native-auth-clerk@0.10.8
- jazz-react-native-media-images@0.10.8
## 1.0.72
### Patch Changes
- Updated dependencies [1136d9b]
- Updated dependencies [0eed228]
- jazz-react-native@0.10.7
- jazz-tools@0.10.7
- jazz-react-native-auth-clerk@0.10.7
- jazz-react-native-media-images@0.10.7
## 1.0.71
### Patch Changes

View File

@@ -1,5 +1,6 @@
import "../global.css";
import { ClerkLoaded, ClerkProvider } from "@clerk/clerk-expo";
import { secureStore } from "@clerk/clerk-expo/secure-store";
import { useFonts } from "expo-font";
import { Slot } from "expo-router";
import * as SplashScreen from "expo-splash-screen";
@@ -33,7 +34,11 @@ export default function RootLayout() {
}
return (
<ClerkProvider tokenCache={tokenCache} publishableKey={publishableKey}>
<ClerkProvider
tokenCache={tokenCache}
publishableKey={publishableKey}
__experimental_resourceCache={secureStore}
>
<ClerkLoaded>
<JazzAndAuth>
<Slot />

View File

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

View File

@@ -1,5 +1,60 @@
# chat-rn
## 1.0.75
### Patch Changes
- Updated dependencies [75211e3]
- jazz-tools@0.10.14
- jazz-react-native@0.10.14
## 1.0.74
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-react-native@0.10.13
## 1.0.73
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
- jazz-react-native@0.10.12
## 1.0.72
### Patch Changes
- Updated dependencies [5a54e4a]
- jazz-react-native@0.10.11
## 1.0.71
### Patch Changes
- Updated dependencies [3405d8f]
- jazz-react-native@0.10.10
## 1.0.70
### Patch Changes
- Updated dependencies [2fb6428]
- jazz-tools@0.10.8
- jazz-react-native@0.10.8
## 1.0.69
### Patch Changes
- Updated dependencies [1136d9b]
- Updated dependencies [0eed228]
- jazz-react-native@0.10.7
- jazz-tools@0.10.7
## 1.0.68
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "chat-rn",
"version": "1.0.68",
"version": "1.0.75",
"main": "index.js",
"scripts": {
"build": "expo export -p ios",
@@ -35,8 +35,6 @@
"react": "^18.3.1",
"react-native": "~0.76.3",
"react-native-get-random-values": "^1.11.0",
"react-native-nitro-modules": "0.21.0",
"react-native-quick-crypto": "1.0.0-beta.12",
"react-native-safe-area-context": "4.12.0",
"react-native-screens": "4.1.0",
"react-native-url-polyfill": "^2.0.0",

View File

@@ -9,7 +9,7 @@ import * as Linking from "expo-linking";
import React, { StrictMode, useEffect, useState } from "react";
import HandleInviteScreen from "./invite";
import { JazzProvider, RNQuickCrypto } from "jazz-react-native";
import { JazzProvider } from "jazz-react-native";
import { apiKey } from "./apiKey";
import ChatScreen from "./chat";
@@ -50,7 +50,6 @@ function App() {
sync={{
peer: `wss://cloud.jazz.tools/?key=${apiKey}`,
}}
CryptoProvider={RNQuickCrypto}
>
<NavigationContainer linking={linking} ref={navigationRef}>
<Stack.Navigator initialRouteName={initialRoute}>

View File

@@ -44,4 +44,5 @@ appId: com.jazz.chatrn
# logout
- tapOn: "Logout"
- assertVisible: "Anonymous user"
- assertVisible: "boorad"
- assertVisible: "bro, low key, it do be like that tho"

View File

@@ -1,5 +1,61 @@
# chat-vue
## 0.0.61
### Patch Changes
- Updated dependencies [75211e3]
- jazz-tools@0.10.14
- jazz-browser@0.10.14
- jazz-vue@0.10.14
## 0.0.60
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-browser@0.10.13
- jazz-vue@0.10.13
## 0.0.59
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
- jazz-vue@0.10.12
- jazz-browser@0.10.12
## 0.0.58
### Patch Changes
- Updated dependencies [834203f]
- jazz-browser@0.10.9
- jazz-vue@0.10.9
## 0.0.57
### Patch Changes
- Updated dependencies [1e87fc7]
- Updated dependencies [2fb6428]
- jazz-browser@0.10.8
- jazz-tools@0.10.8
- jazz-vue@0.10.8
## 0.0.56
### Patch Changes
- Updated dependencies [1136d9b]
- Updated dependencies [bf76d79]
- Updated dependencies [0eed228]
- jazz-browser@0.10.7
- jazz-tools@0.10.7
- jazz-vue@0.10.7
## 0.0.55
### Patch Changes

View File

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

View File

@@ -1,5 +1,58 @@
# jazz-example-chat
## 0.0.157
### Patch Changes
- Updated dependencies [75211e3]
- jazz-tools@0.10.14
- jazz-react@0.10.14
- jazz-browser-media-images@0.10.14
## 0.0.156
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-browser-media-images@0.10.13
- jazz-react@0.10.13
## 0.0.155
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
- jazz-react@0.10.12
- jazz-browser-media-images@0.10.12
## 0.0.154
### Patch Changes
- jazz-browser-media-images@0.10.9
- jazz-react@0.10.9
## 0.0.153
### Patch Changes
- Updated dependencies [2fb6428]
- jazz-tools@0.10.8
- jazz-react@0.10.8
- jazz-browser-media-images@0.10.8
## 0.0.152
### Patch Changes
- Updated dependencies [1136d9b]
- Updated dependencies [0eed228]
- jazz-react@0.10.7
- jazz-tools@0.10.7
- jazz-browser-media-images@0.10.7
## 0.0.151
### Patch Changes

View File

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

View File

@@ -1,5 +1,58 @@
# minimal-auth-clerk
## 0.0.56
### Patch Changes
- Updated dependencies [75211e3]
- jazz-tools@0.10.14
- jazz-react@0.10.14
- jazz-react-auth-clerk@0.10.14
## 0.0.55
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-react@0.10.13
- jazz-react-auth-clerk@0.10.13
## 0.0.54
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
- jazz-react@0.10.12
- jazz-react-auth-clerk@0.10.12
## 0.0.53
### Patch Changes
- jazz-react@0.10.9
- jazz-react-auth-clerk@0.10.9
## 0.0.52
### Patch Changes
- Updated dependencies [2fb6428]
- jazz-tools@0.10.8
- jazz-react@0.10.8
- jazz-react-auth-clerk@0.10.8
## 0.0.51
### Patch Changes
- Updated dependencies [1136d9b]
- Updated dependencies [0eed228]
- jazz-react-auth-clerk@0.10.7
- jazz-react@0.10.7
- jazz-tools@0.10.7
## 0.0.50
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "clerk",
"private": true,
"version": "0.0.50",
"version": "0.0.56",
"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.6",
"jazz-react-auth-clerk": "workspace:0.10.14",
"jazz-tools": "workspace:*",
"react": "^18.3.1",
"react-dom": "^18.3.1"

View File

@@ -1,5 +1,52 @@
# file-share-svelte
## 0.0.41
### Patch Changes
- Updated dependencies [75211e3]
- jazz-tools@0.10.14
- jazz-svelte@0.10.14
## 0.0.40
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-svelte@0.10.13
## 0.0.39
### Patch Changes
- Updated dependencies [4612e05]
- jazz-svelte@0.10.12
- jazz-tools@0.10.12
## 0.0.38
### Patch Changes
- jazz-svelte@0.10.9
## 0.0.37
### Patch Changes
- Updated dependencies [2fb6428]
- jazz-tools@0.10.8
- jazz-svelte@0.10.8
## 0.0.36
### Patch Changes
- Updated dependencies [1136d9b]
- Updated dependencies [0eed228]
- jazz-svelte@0.10.7
- jazz-tools@0.10.7
## 0.0.35
### Patch Changes

View File

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

View File

@@ -1,5 +1,58 @@
# form
## 0.0.52
### Patch Changes
- Updated dependencies [75211e3]
- jazz-tools@0.10.14
- jazz-react@0.10.14
- jazz-browser-media-images@0.10.14
## 0.0.51
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-browser-media-images@0.10.13
- jazz-react@0.10.13
## 0.0.50
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
- jazz-react@0.10.12
- jazz-browser-media-images@0.10.12
## 0.0.49
### Patch Changes
- jazz-browser-media-images@0.10.9
- jazz-react@0.10.9
## 0.0.48
### Patch Changes
- Updated dependencies [2fb6428]
- jazz-tools@0.10.8
- jazz-react@0.10.8
- jazz-browser-media-images@0.10.8
## 0.0.47
### Patch Changes
- Updated dependencies [1136d9b]
- Updated dependencies [0eed228]
- jazz-react@0.10.7
- jazz-tools@0.10.7
- jazz-browser-media-images@0.10.7
## 0.0.46
### Patch Changes

View File

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

View File

@@ -1,5 +1,4 @@
import { useIframeHashRouter } from "hash-slash";
import { useAccount } from "jazz-react";
import { ID } from "jazz-tools";
import { CreateOrder } from "./CreateOrder.tsx";
import { EditOrder } from "./EditOrder.tsx";
@@ -7,25 +6,10 @@ import { Orders } from "./Orders.tsx";
import { BubbleTeaOrder } from "./schema.ts";
function App() {
const { me, logOut } = useAccount();
const router = useIframeHashRouter();
return (
<>
<header>
<nav className="container py-2 border-b flex items-center justify-between">
<span>
You're logged in as <strong>{me?.profile?.name}</strong>
</span>
<button
className="bg-stone-100 py-1.5 px-3 text-sm rounded-md dark:bg-stone-900 dark:text-white"
onClick={() => logOut()}
>
Log out
</button>
</nav>
</header>
<main className="container py-8 space-y-8">
{router.route({
"/": () => <Orders />,

View File

@@ -1,5 +1,58 @@
# image-upload
## 0.0.54
### Patch Changes
- Updated dependencies [75211e3]
- jazz-tools@0.10.14
- jazz-react@0.10.14
- jazz-browser-media-images@0.10.14
## 0.0.53
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-browser-media-images@0.10.13
- jazz-react@0.10.13
## 0.0.52
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
- jazz-react@0.10.12
- jazz-browser-media-images@0.10.12
## 0.0.51
### Patch Changes
- jazz-browser-media-images@0.10.9
- jazz-react@0.10.9
## 0.0.50
### Patch Changes
- Updated dependencies [2fb6428]
- jazz-tools@0.10.8
- jazz-react@0.10.8
- jazz-browser-media-images@0.10.8
## 0.0.49
### Patch Changes
- Updated dependencies [1136d9b]
- Updated dependencies [0eed228]
- jazz-react@0.10.7
- jazz-tools@0.10.7
- jazz-browser-media-images@0.10.7
## 0.0.48
### Patch Changes

View File

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

View File

@@ -1,19 +1,8 @@
import { useAccount } from "jazz-react";
import ImageUpload from "./ImageUpload.tsx";
function App() {
const { me, logOut } = useAccount();
return (
<>
<header>
<nav className="container">
<span>
You're logged in as <strong>{me?.profile?.name}</strong>
</span>
<button onClick={() => logOut()}>Log out</button>
</nav>
</header>
<main className="container">
<ImageUpload />
</main>

View File

@@ -46,7 +46,12 @@ export default function ImageUpload() {
) : (
<div>
<label>Upload image</label>
<input ref={inputRef} type="file" onChange={onImageChange} />
<input
ref={inputRef}
type="file"
accept="image/png, image/jpeg, image/gif"
onChange={onImageChange}
/>
</div>
)}
</div>

View File

@@ -72,8 +72,7 @@ nav {
.container {
margin-right: auto;
margin-left: auto;
padding-right: 0.75rem;
padding-left: 0.75rem;
padding: 2rem 0.75rem;
max-width: 800px;
}

View File

@@ -1,5 +1,22 @@
# jazz-example-inspector
## 0.0.109
### Patch Changes
- Updated dependencies [153dc99]
- cojson@0.10.8
- cojson-transport-ws@0.10.8
## 0.0.108
### Patch Changes
- Updated dependencies [0f83320]
- Updated dependencies [012022d]
- cojson@0.10.7
- cojson-transport-ws@0.10.7
## 0.0.107
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-inspector-app",
"private": true,
"version": "0.0.107",
"version": "0.0.109",
"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.6",
"cojson-transport-ws": "workspace:0.10.6",
"cojson": "workspace:0.10.8",
"cojson-transport-ws": "workspace:0.10.8",
"hash-slash": "workspace:0.2.2",
"lucide-react": "^0.274.0",
"qrcode": "^1.5.3",

View File

@@ -83,7 +83,10 @@ export default function CoJsonViewerApp() {
const addAccount = (id: RawAccountID, secret: AgentSecret) => {
const newAccount = { id, secret };
setAccounts([...accounts, newAccount]);
const accountExists = accounts.some((account) => account.id === id);
if (!accountExists) {
setAccounts([...accounts, newAccount]);
}
setCurrentAccount(newAccount);
};

1
examples/multiauth/.env Normal file
View File

@@ -0,0 +1 @@
VITE_CLERK_PUBLISHABLE_KEY=pk_test_ZXZpZGVudC1kYW5lLTg5LmNsZXJrLmFjY291bnRzLmRldiQ

24
examples/multiauth/.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

View File

@@ -0,0 +1,43 @@
# Multi-auth example with Jazz and React
This example demonstrates using Jazz with multiple authentication methods; in this case, Clerk and passphrases are able to be used.
## Getting started
To run this example, you may either:
* Clone the Jazz monorepo and run this example from within.
* Create a new Jazz project using this example as a template, and run that new project.
### Using this example as a template
1. Create a new Jazz project, and use this example as a template.
```bash
npx create-jazz-app@latest --example counter --project-name counter
```
2. Navigate to the new project and start the development server.
```bash
cd counter
npm run dev
```
### Using the monorepo
This requires `pnpm` to be installed; see [https://pnpm.io/installation](https://pnpm.io/installation).
1. Clone the `jazz` repository.
```bash
git clone https://github.com/garden-co/jazz.git
```
2. Install dependencies.
```bash
cd jazz
pnpm install
```
3. Navigate to the example and start the development server.
```bash
cd examples/counter
pnpm dev
```
The example should be running at [http://localhost:5173](http://localhost:5173) by default.

View File

@@ -0,0 +1,28 @@
import js from "@eslint/js";
import reactHooks from "eslint-plugin-react-hooks";
import reactRefresh from "eslint-plugin-react-refresh";
import globals from "globals";
import tseslint from "typescript-eslint";
export default tseslint.config(
{ ignores: ["dist"] },
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ["**/*.{ts,tsx}"],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
plugins: {
"react-hooks": reactHooks,
"react-refresh": reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
"react-refresh/only-export-components": [
"warn",
{ allowConstantExport: true },
],
},
},
);

View File

@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Jazz Multi-auth (React)</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

View File

@@ -0,0 +1,30 @@
{
"name": "multiauth",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"preview": "vite preview",
"format-and-lint": "biome check .",
"format-and-lint:fix": "biome check . --write"
},
"dependencies": {
"@clerk/clerk-react": "^5.4.1",
"jazz-react": "workspace:*",
"jazz-react-auth-clerk": "workspace:*",
"jazz-tools": "workspace:*",
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
"devDependencies": {
"@biomejs/biome": "1.9.4",
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
"@vitejs/plugin-react": "^4.3.3",
"globals": "^15.11.0",
"typescript": "~5.6.2",
"vite": "^6.0.11"
}
}

View File

@@ -0,0 +1,12 @@
#root {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
.card {
padding: 2em;
display: flex;
flex-direction: column;
}

View File

@@ -0,0 +1,12 @@
import { Home } from "./components/Home.tsx";
import "./App.css";
export default function App() {
return (
<div className="card">
<h1>Jazz Multi-auth (React)</h1>
<Home />
</div>
);
}

View File

@@ -0,0 +1 @@
export const apiKey = "counter-example@garden.co";

View File

@@ -0,0 +1,26 @@
import { useAccount, useIsAuthenticated } from "jazz-react";
export function Home() {
const { me, logOut } = useAccount({ root: {} });
const isAuthenticated = useIsAuthenticated();
if (!me) return;
if (!isAuthenticated) return;
return (
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
gap: "1rem",
}}
>
<div className="container">
<h1>You're logged in</h1>
<p>Welcome back, {me?.profile?.name}</p>
<button onClick={() => logOut()}>Logout</button>
</div>
</div>
);
}

View File

@@ -0,0 +1,184 @@
import { SignInButton } from "@clerk/clerk-react";
import { usePassphraseAuth } from "jazz-react";
import { JazzProviderProps, useIsAuthenticated } from "jazz-react";
import { useState } from "react";
import "../index.css";
import { useClerk } from "@clerk/clerk-react";
import { JazzProviderWithClerk } from "jazz-react-auth-clerk";
import { wordlist } from "../wordlist.ts";
export function OmniAuthContainer(props: {
appName: string;
wordlist: string[];
children?: React.ReactNode;
}) {
const isAuthenticated = useIsAuthenticated();
const passphraseAuth = usePassphraseAuth({
wordlist: props.wordlist,
});
const [step, setStep] = useState<
"initial" | "create" | "loginWithPassphrase" | "loginWithClerk"
>("initial");
const [loginPassphrase, setLoginPassphrase] = useState("");
const [isCopied, setIsCopied] = useState(false);
const [currentPassphrase, setCurrentPassphrase] = useState(() =>
passphraseAuth.generateRandomPassphrase(),
);
if (passphraseAuth.state === "signedIn" || isAuthenticated) {
return props.children ?? null;
}
const handleCreateAccount = async () => {
setStep("create");
};
const handleLoginWithPassphrase = () => {
setStep("loginWithPassphrase");
};
const handleLoginWithClerk = () => {
setStep("loginWithClerk");
};
const handleReroll = () => {
const newPassphrase = passphraseAuth.generateRandomPassphrase();
setCurrentPassphrase(newPassphrase);
setIsCopied(false);
};
const handleBack = () => {
setStep("initial");
setLoginPassphrase("");
};
const handleCopy = async () => {
await navigator.clipboard.writeText(passphraseAuth.passphrase);
setIsCopied(true);
};
const handleLoginSubmit = async () => {
await passphraseAuth.logIn(loginPassphrase);
setStep("initial");
setLoginPassphrase("");
};
const handleNext = async () => {
await passphraseAuth.registerNewAccount(currentPassphrase, "My Account");
setStep("initial");
setLoginPassphrase("");
};
return (
<div className="auth-container">
<div className="auth-card">
{step === "initial" && (
<div>
<h1 className="auth-heading">{props.appName}</h1>
<button
onClick={handleCreateAccount}
className="auth-button-primary"
>
Signup with passphrase
</button>
<button
onClick={handleLoginWithPassphrase}
className="auth-button-secondary"
>
Login with passphrase
</button>
<button
onClick={handleLoginWithClerk}
className="auth-button-secondary"
>
Login with Clerk
</button>
</div>
)}
{step === "create" && (
<>
<h1 className="auth-heading">Your Passphrase</h1>
<p className="auth-description">
Please copy and store this passphrase somewhere safe. You'll need
it to log in.
</p>
<textarea
readOnly
value={currentPassphrase}
className="auth-textarea"
rows={5}
/>
<button onClick={handleCopy} className="auth-button-primary">
{isCopied ? "Copied!" : "Copy"}
</button>
<div className="auth-button-group">
<button onClick={handleBack} className="auth-button-secondary">
Back
</button>
<button onClick={handleReroll} className="auth-button-secondary">
Generate New Passphrase
</button>
<button onClick={handleNext} className="auth-button-primary">
Register
</button>
</div>
</>
)}
{step === "loginWithPassphrase" && (
<div>
<h1 className="auth-heading">Log In</h1>
<textarea
value={loginPassphrase}
onChange={(e) => setLoginPassphrase(e.target.value)}
placeholder="Enter your passphrase"
className="auth-textarea"
rows={5}
/>
<div className="auth-button-group">
<button onClick={handleBack} className="auth-button-secondary">
Back
</button>
<button
onClick={handleLoginSubmit}
className="auth-button-primary"
>
Log In
</button>
</div>
</div>
)}
{step === "loginWithClerk" && <SignInButton />}
</div>
</div>
);
}
export function OmniAuth({
children,
AccountSchema,
sync,
}: {
children: React.ReactNode;
} & JazzProviderProps) {
const clerk = useClerk();
return (
<JazzProviderWithClerk
clerk={clerk}
sync={sync}
AccountSchema={AccountSchema}
>
<OmniAuthContainer
appName="Jazz Multi-Authentication Example"
wordlist={wordlist}
>
{children}
</OmniAuthContainer>
</JazzProviderWithClerk>
);
}

View File

@@ -0,0 +1,151 @@
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
body {
margin: auto 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
}
h1 {
font-size: 2.2em;
line-height: 1.1;
font-weight: 500;
letter-spacing: 0.6px;
}
button {
border-radius: 6px;
border: 1px solid white;
color: inherit;
padding: 0.5rem 1rem;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: transparent;
cursor: pointer;
transition: all 0.05s ease, border-color 0.1s ease;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}
button:hover {
background-color: #313131;
}
button:active {
border-color: #3313f7;
transform: translateY(2px);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
@media (prefers-color-scheme: light) {
:root {
color: rgb(21, 20, 20);
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
border-color: #e5e4e2;
}
button:hover {
border-color: rgb(47, 46, 46);
background-color: white;
}
button:active {
border-color: #3313f7;
}
}
.auth-container {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background-color: #f3f4f6;
}
.auth-card {
background-color: white;
padding: 2rem;
border-radius: 0.5rem;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px
rgba(0, 0, 0, 0.06);
width: 28rem;
}
.auth-button-primary,
.auth-button-secondary {
width: 100%;
padding: 0.5rem 1rem;
border-radius: 0.25rem;
font-weight: bold;
cursor: pointer;
margin-bottom: 1rem;
}
.auth-button-primary {
background-color: black;
color: white;
border: none;
}
.auth-button-secondary {
background-color: white;
color: black;
border: 1px solid black;
}
.auth-heading {
color: black;
font-size: 1.5rem;
font-weight: bold;
text-align: center;
margin-bottom: 1rem;
}
.auth-textarea {
width: 100%;
padding: 0.5rem;
border: 1px solid #d1d5db;
border-radius: 0.25rem;
margin-bottom: 1rem;
box-sizing: border-box;
}
.auth-description {
font-size: 0.875rem;
color: #4b5563;
text-align: center;
margin-bottom: 1rem;
}
.auth-button-group {
display: flex;
justify-content: space-between;
gap: 1rem;
}

View File

@@ -0,0 +1,29 @@
import { ClerkProvider } from "@clerk/clerk-react";
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App";
import "./index.css";
import { apiKey } from "./apiKey.ts";
import { OmniAuth } from "./components/OmniAuth.tsx";
// Import your publishable key
const PUBLISHABLE_KEY = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY;
if (!PUBLISHABLE_KEY) {
throw new Error("Add your Clerk publishable key to the .env.local file");
}
createRoot(document.getElementById("root")!).render(
<StrictMode>
<ClerkProvider publishableKey={PUBLISHABLE_KEY} afterSignOutUrl="/">
<OmniAuth
sync={{
peer: `wss://cloud.jazz.tools/?key=${apiKey}`,
when: "signedUp", // This makes the app work in local mode when the user is not authenticated
}}
>
<App />
</OmniAuth>
</ClerkProvider>
</StrictMode>,
);

1
examples/multiauth/src/vite-env.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
/// <reference types="vite/client" />

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,24 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2023", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
"moduleResolution": "Bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["src"]
}

View File

@@ -0,0 +1,7 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}

View File

@@ -0,0 +1,22 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "ES2022",
"lib": ["ES2023"],
"module": "ESNext",
"skipLibCheck": true,
"moduleResolution": "Bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["vite.config.ts"]
}

View File

@@ -0,0 +1,7 @@
import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";
// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
});

View File

@@ -1,5 +1,57 @@
# jazz-example-musicplayer
## 0.0.78
### Patch Changes
- Updated dependencies [75211e3]
- jazz-tools@0.10.14
- jazz-inspector@0.10.11
- jazz-react@0.10.14
## 0.0.77
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-inspector@0.10.10
- jazz-react@0.10.13
## 0.0.76
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
- jazz-inspector@0.10.9
- jazz-react@0.10.12
## 0.0.75
### Patch Changes
- jazz-react@0.10.9
## 0.0.74
### Patch Changes
- Updated dependencies [2fb6428]
- jazz-tools@0.10.8
- jazz-inspector@0.10.8
- jazz-react@0.10.8
## 0.0.73
### Patch Changes
- Updated dependencies [1136d9b]
- Updated dependencies [0eed228]
- jazz-react@0.10.7
- jazz-tools@0.10.7
- jazz-inspector@0.10.7
## 0.0.72
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-music-player",
"private": true,
"version": "0.0.72",
"version": "0.0.78",
"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.6",
"jazz-tools": "workspace:0.10.6",
"jazz-react": "workspace:0.10.14",
"jazz-tools": "workspace:0.10.14",
"lucide-react": "^0.274.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",

View File

@@ -1,5 +1,52 @@
# organization
## 0.0.50
### Patch Changes
- Updated dependencies [75211e3]
- jazz-tools@0.10.14
- jazz-react@0.10.14
## 0.0.49
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-react@0.10.13
## 0.0.48
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
- jazz-react@0.10.12
## 0.0.47
### Patch Changes
- jazz-react@0.10.9
## 0.0.46
### Patch Changes
- Updated dependencies [2fb6428]
- jazz-tools@0.10.8
- jazz-react@0.10.8
## 0.0.45
### Patch Changes
- Updated dependencies [1136d9b]
- Updated dependencies [0eed228]
- jazz-react@0.10.7
- jazz-tools@0.10.7
## 0.0.44
### Patch Changes

View File

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

View File

@@ -1,5 +1,43 @@
# passkey-svelte
## 0.0.45
### Patch Changes
- jazz-svelte@0.10.14
## 0.0.44
### Patch Changes
- jazz-svelte@0.10.13
## 0.0.43
### Patch Changes
- Updated dependencies [4612e05]
- jazz-svelte@0.10.12
## 0.0.42
### Patch Changes
- jazz-svelte@0.10.9
## 0.0.41
### Patch Changes
- jazz-svelte@0.10.8
## 0.0.40
### Patch Changes
- Updated dependencies [1136d9b]
- jazz-svelte@0.10.7
## 0.0.39
### Patch Changes

View File

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

View File

@@ -1,5 +1,52 @@
# minimal-auth-passkey
## 0.0.55
### Patch Changes
- Updated dependencies [75211e3]
- jazz-tools@0.10.14
- jazz-react@0.10.14
## 0.0.54
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-react@0.10.13
## 0.0.53
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
- jazz-react@0.10.12
## 0.0.52
### Patch Changes
- jazz-react@0.10.9
## 0.0.51
### Patch Changes
- Updated dependencies [2fb6428]
- jazz-tools@0.10.8
- jazz-react@0.10.8
## 0.0.50
### Patch Changes
- Updated dependencies [1136d9b]
- Updated dependencies [0eed228]
- jazz-react@0.10.7
- jazz-tools@0.10.7
## 0.0.49
### Patch Changes

View File

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

View File

@@ -1,5 +1,52 @@
# passphrase
## 0.0.52
### Patch Changes
- Updated dependencies [75211e3]
- jazz-tools@0.10.14
- jazz-react@0.10.14
## 0.0.51
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-react@0.10.13
## 0.0.50
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
- jazz-react@0.10.12
## 0.0.49
### Patch Changes
- jazz-react@0.10.9
## 0.0.48
### Patch Changes
- Updated dependencies [2fb6428]
- jazz-tools@0.10.8
- jazz-react@0.10.8
## 0.0.47
### Patch Changes
- Updated dependencies [1136d9b]
- Updated dependencies [0eed228]
- jazz-react@0.10.7
- jazz-tools@0.10.7
## 0.0.46
### Patch Changes

View File

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

View File

@@ -66,3 +66,72 @@ main {
margin: 0 auto;
text-align: center;
}
.auth-container {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background-color: #f3f4f6;
}
.auth-card {
background-color: white;
padding: 2rem;
border-radius: 0.5rem;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px
rgba(0, 0, 0, 0.06);
width: 28rem;
}
.auth-button-primary,
.auth-button-secondary {
width: 100%;
padding: 0.5rem 1rem;
border-radius: 0.25rem;
font-weight: bold;
cursor: pointer;
margin-bottom: 1rem;
}
.auth-button-primary {
background-color: black;
color: white;
border: none;
}
.auth-button-secondary {
background-color: white;
color: black;
border: 1px solid black;
}
.auth-heading {
color: black;
font-size: 1.5rem;
font-weight: bold;
text-align: center;
margin-bottom: 1rem;
}
.auth-textarea {
width: 100%;
padding: 0.5rem;
border: 1px solid #d1d5db;
border-radius: 0.25rem;
margin-bottom: 1rem;
box-sizing: border-box;
}
.auth-description {
font-size: 0.875rem;
color: #4b5563;
text-align: center;
margin-bottom: 1rem;
}
.auth-button-group {
display: flex;
justify-content: space-between;
gap: 1rem;
}

View File

@@ -1,10 +1,142 @@
import { JazzProvider, PassphraseAuthBasicUI } from "jazz-react";
import { StrictMode } from "react";
import { JazzProvider, usePassphraseAuth } from "jazz-react";
import { StrictMode, useState } from "react";
import { createRoot } from "react-dom/client";
import App from "./App.tsx";
import "./index.css";
import { wordlist } from "./wordlist.ts";
function PassphraseAuthBasicUI(props: {
appName: string;
wordlist: string[];
children?: React.ReactNode;
}) {
const auth = usePassphraseAuth({
wordlist: props.wordlist,
});
const [step, setStep] = useState<"initial" | "create" | "login">("initial");
const [loginPassphrase, setLoginPassphrase] = useState("");
const [isCopied, setIsCopied] = useState(false);
const [currentPassphrase, setCurrentPassphrase] = useState(() =>
auth.generateRandomPassphrase(),
);
if (auth.state === "signedIn") {
return props.children ?? null;
}
const handleCreateAccount = async () => {
setStep("create");
};
const handleLogin = () => {
setStep("login");
};
const handleReroll = () => {
const newPassphrase = auth.generateRandomPassphrase();
setCurrentPassphrase(newPassphrase);
setIsCopied(false);
};
const handleBack = () => {
setStep("initial");
setLoginPassphrase("");
};
const handleCopy = async () => {
await navigator.clipboard.writeText(auth.passphrase);
setIsCopied(true);
};
const handleLoginSubmit = async () => {
await auth.logIn(loginPassphrase);
setStep("initial");
setLoginPassphrase("");
};
const handleNext = async () => {
await auth.registerNewAccount(currentPassphrase, "My Account");
setStep("initial");
setLoginPassphrase("");
};
return (
<div className="auth-container">
<div className="auth-card">
{step === "initial" && (
<div>
<h1 className="auth-heading">{props.appName}</h1>
<button
onClick={handleCreateAccount}
className="auth-button-primary"
>
Create new account
</button>
<button onClick={handleLogin} className="auth-button-secondary">
Log in
</button>
</div>
)}
{step === "create" && (
<>
<h1 className="auth-heading">Your Passphrase</h1>
<p className="auth-description">
Please copy and store this passphrase somewhere safe. You'll need
it to log in.
</p>
<textarea
readOnly
value={currentPassphrase}
className="auth-textarea"
rows={5}
/>
<button onClick={handleCopy} className="auth-button-primary">
{isCopied ? "Copied!" : "Copy"}
</button>
<div className="auth-button-group">
<button onClick={handleBack} className="auth-button-secondary">
Back
</button>
<button onClick={handleReroll} className="auth-button-secondary">
Generate New Passphrase
</button>
<button onClick={handleNext} className="auth-button-primary">
Register
</button>
</div>
</>
)}
{step === "login" && (
<div>
<h1 className="auth-heading">Log In</h1>
<textarea
value={loginPassphrase}
onChange={(e) => setLoginPassphrase(e.target.value)}
placeholder="Enter your passphrase"
className="auth-textarea"
rows={5}
/>
<div className="auth-button-group">
<button onClick={handleBack} className="auth-button-secondary">
Back
</button>
<button
onClick={handleLoginSubmit}
className="auth-button-primary"
>
Log In
</button>
</div>
</div>
)}
</div>
</div>
);
}
function JazzAndAuth({ children }: { children: React.ReactNode }) {
return (
<JazzProvider

View File

@@ -1,5 +1,52 @@
# jazz-password-manager
## 0.0.76
### Patch Changes
- Updated dependencies [75211e3]
- jazz-tools@0.10.14
- jazz-react@0.10.14
## 0.0.75
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-react@0.10.13
## 0.0.74
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
- jazz-react@0.10.12
## 0.0.73
### Patch Changes
- jazz-react@0.10.9
## 0.0.72
### Patch Changes
- Updated dependencies [2fb6428]
- jazz-tools@0.10.8
- jazz-react@0.10.8
## 0.0.71
### Patch Changes
- Updated dependencies [1136d9b]
- Updated dependencies [0eed228]
- jazz-react@0.10.7
- jazz-tools@0.10.7
## 0.0.70
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-password-manager",
"private": true,
"version": "0.0.70",
"version": "0.0.76",
"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.6",
"jazz-tools": "workspace:0.10.6",
"jazz-react": "workspace:0.10.14",
"jazz-tools": "workspace:0.10.14",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-hook-form": "^7.41.5",

View File

@@ -1,5 +1,58 @@
# jazz-example-pets
## 0.0.174
### Patch Changes
- Updated dependencies [75211e3]
- jazz-tools@0.10.14
- jazz-react@0.10.14
- jazz-browser-media-images@0.10.14
## 0.0.173
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-browser-media-images@0.10.13
- jazz-react@0.10.13
## 0.0.172
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
- jazz-react@0.10.12
- jazz-browser-media-images@0.10.12
## 0.0.171
### Patch Changes
- jazz-browser-media-images@0.10.9
- jazz-react@0.10.9
## 0.0.170
### Patch Changes
- Updated dependencies [2fb6428]
- jazz-tools@0.10.8
- jazz-react@0.10.8
- jazz-browser-media-images@0.10.8
## 0.0.169
### Patch Changes
- Updated dependencies [1136d9b]
- Updated dependencies [0eed228]
- jazz-react@0.10.7
- jazz-tools@0.10.7
- jazz-browser-media-images@0.10.7
## 0.0.168
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-pets",
"private": true,
"version": "0.0.168",
"version": "0.0.174",
"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.6",
"jazz-react": "workspace:0.10.6",
"jazz-tools": "workspace:0.10.6",
"jazz-browser-media-images": "workspace:0.10.14",
"jazz-react": "workspace:0.10.14",
"jazz-tools": "workspace:0.10.14",
"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.6",
"jazz-run": "workspace:0.10.14",
"postcss": "^8.4.27",
"tailwindcss": "^3.4.17",
"typescript": "~5.6.2",

View File

@@ -1,5 +1,58 @@
# reactions
## 0.0.54
### Patch Changes
- Updated dependencies [75211e3]
- jazz-tools@0.10.14
- jazz-react@0.10.14
- jazz-browser-media-images@0.10.14
## 0.0.53
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-browser-media-images@0.10.13
- jazz-react@0.10.13
## 0.0.52
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
- jazz-react@0.10.12
- jazz-browser-media-images@0.10.12
## 0.0.51
### Patch Changes
- jazz-browser-media-images@0.10.9
- jazz-react@0.10.9
## 0.0.50
### Patch Changes
- Updated dependencies [2fb6428]
- jazz-tools@0.10.8
- jazz-react@0.10.8
- jazz-browser-media-images@0.10.8
## 0.0.49
### Patch Changes
- Updated dependencies [1136d9b]
- Updated dependencies [0eed228]
- jazz-react@0.10.7
- jazz-tools@0.10.7
- jazz-browser-media-images@0.10.7
## 0.0.48
### Patch Changes

View File

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

View File

@@ -1,5 +1,61 @@
# todo-vue
## 0.0.59
### Patch Changes
- Updated dependencies [75211e3]
- jazz-tools@0.10.14
- jazz-browser@0.10.14
- jazz-vue@0.10.14
## 0.0.58
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-browser@0.10.13
- jazz-vue@0.10.13
## 0.0.57
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
- jazz-vue@0.10.12
- jazz-browser@0.10.12
## 0.0.56
### Patch Changes
- Updated dependencies [834203f]
- jazz-browser@0.10.9
- jazz-vue@0.10.9
## 0.0.55
### Patch Changes
- Updated dependencies [1e87fc7]
- Updated dependencies [2fb6428]
- jazz-browser@0.10.8
- jazz-tools@0.10.8
- jazz-vue@0.10.8
## 0.0.54
### Patch Changes
- Updated dependencies [1136d9b]
- Updated dependencies [bf76d79]
- Updated dependencies [0eed228]
- jazz-browser@0.10.7
- jazz-tools@0.10.7
- jazz-vue@0.10.7
## 0.0.53
### Patch Changes

View File

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

View File

@@ -1,5 +1,52 @@
# jazz-example-todo
## 0.0.173
### Patch Changes
- Updated dependencies [75211e3]
- jazz-tools@0.10.14
- jazz-react@0.10.14
## 0.0.172
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-react@0.10.13
## 0.0.171
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
- jazz-react@0.10.12
## 0.0.170
### Patch Changes
- jazz-react@0.10.9
## 0.0.169
### Patch Changes
- Updated dependencies [2fb6428]
- jazz-tools@0.10.8
- jazz-react@0.10.8
## 0.0.168
### Patch Changes
- Updated dependencies [1136d9b]
- Updated dependencies [0eed228]
- jazz-react@0.10.7
- jazz-tools@0.10.7
## 0.0.167
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-todo",
"private": true,
"version": "0.0.167",
"version": "0.0.173",
"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.6",
"jazz-tools": "workspace:0.10.6",
"jazz-react": "workspace:0.10.14",
"jazz-tools": "workspace:0.10.14",
"lucide-react": "^0.274.0",
"qrcode": "^1.5.3",
"react": "^18.3.1",

View File

@@ -1,5 +1,52 @@
# version-history
## 0.0.51
### Patch Changes
- Updated dependencies [75211e3]
- jazz-tools@0.10.14
- jazz-react@0.10.14
## 0.0.50
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-react@0.10.13
## 0.0.49
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
- jazz-react@0.10.12
## 0.0.48
### Patch Changes
- jazz-react@0.10.9
## 0.0.47
### Patch Changes
- Updated dependencies [2fb6428]
- jazz-tools@0.10.8
- jazz-react@0.10.8
## 0.0.46
### Patch Changes
- Updated dependencies [1136d9b]
- Updated dependencies [0eed228]
- jazz-react@0.10.7
- jazz-tools@0.10.7
## 0.0.45
### Patch Changes

View File

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

View File

@@ -14,6 +14,7 @@ import {
FolderArchiveIcon,
GaugeIcon,
GlobeIcon,
HashIcon,
ImageIcon,
LinkIcon,
LockKeyholeIcon,
@@ -53,6 +54,7 @@ const icons = {
encryption: LockKeyholeIcon,
faceId: ScanFace,
file: FileTextIcon,
hash: HashIcon,
help: MessageCircleQuestionIcon,
image: ImageIcon,
instant: GaugeIcon,

View File

@@ -136,6 +136,7 @@ const config = {
padding: "0.15rem 0.25rem",
borderRadius: "2px",
whiteSpace: "nowrap",
fontWeight: 400,
},
p: {
marginBottom: theme("spacing.3"),

View File

@@ -1,8 +1,16 @@
import { packages } from "@/lib/packages";
import { clsx } from "clsx";
import { Icon } from "gcmp-design-system/src/app/components/atoms/Icon";
import type { Metadata } from "next";
import Link from "next/link";
export const metadata: Metadata = {
title: "API reference",
openGraph: {
title: "API reference",
},
};
const CardHeading = ({
children,
className,

View File

@@ -1,5 +1,12 @@
import { CodeGroup, ContentByFramework, JazzLogo } from '@/components/forMdx'
export const metadata = {
title: "Learn some Jazz",
openGraph: {
title: "Learn some Jazz",
},
};
# Learn some <span className="sr-only">Jazz</span> <JazzLogo className="h-[41px] -ml-0.5 -mt-[3px] inline" />
Welcome to the Jazz documentation!

View File

@@ -1,24 +1,26 @@
import { ContentByFramework, CodeGroup } from '@/components/forMdx'
import { ContentByFramework, FileDownloadLink, CodeGroup } from '@/components/forMdx'
# Using AI tools to build Jazz apps
# Using AI to build Jazz apps
AI tools, particularly large language models (LLMs), can enhance the speed at which you build apps with Jazz.
However, for these tools to work effectively and provide better results, they need access to the relevant documentation.
AI tools, particularly large language models (LLMs), can accelerate your development with Jazz. Searching docs, responding to questions and even helping you write code are all things that LLMs are starting to get good at.
## LLM docs
However, Jazz is a rapidly evolving framework, so sometimes AI might get things a little wrong.
To provide AI tools, like Cursor, with access to Jazz docs, we've compressed the docs into a single txt file that is
more LLM-readable than a full website, which you can easily upload to your tool of choice.
To help the LLMs, we provide the Jazz documentation in a txt file that is optimized for use with AI tools, like Cursor.
## Adding docs to ChatGPT and v0
<FileDownloadLink href="/llms-full.txt">llms-full.txt</FileDownloadLink>
<a href="/llms-full.txt" download>Download llms-full.txt</a>
## Setting up AI tools
Every tool is different, but generally, you'll need to either paste the contents of the [llms-full.txt](https://jazz.tools/llms-full.txt) file directly in your prompt, or attach the file to the tool.
### ChatGPT and v0
Upload the txt file in your prompt.
![ChatGPT prompt with llms-full.txt attached](/chatgpt-with-llms-full-txt.jpg)
## Adding docs to Cursor
### Cursor
1. Go to Settings > Cursor Settings > Features > Docs
2. Click "Add new doc"
@@ -30,3 +32,12 @@ https://jazz.tools/llms-full.txt
```
</CodeGroup>
## llms.txt convention
We follow the llms.txt [proposed standard](https://llmstxt.org/) for providing documentation to AI tools at inference time that helps them understand the context of the code you're writing.
## Limitations and considerations
AI is amazing, but it's not perfect. What works well this week could break next week (or be twice as good).
We're keen to keep up with changes in tooling to help support you building the best apps, but if you need help from humans (or you have issues getting set up), please let us know on [Discord](https://discord.gg/utDMjHYg42).

View File

@@ -211,7 +211,7 @@ The clerk provider is not built into `jazz-react` and needs the `jazz-react-auth
</ContentByFramework>
<ContentByFramework framework="react-native">
The clerk provider is not built into `jazz-react-native` and needs the `jazz-react-native-auth-clerk` package to be installed.
The clerk provider is not built into `jazz-react-native` and needs the `jazz-react-native-auth-clerk` package to be installed. Note the `__experimental_resourceCache` option. This helps render Clerk components when offline.
</ContentByFramework>
After installing the package you can use the `JazzProviderWithClerk` component to wrap your app:
@@ -249,6 +249,7 @@ createRoot(document.getElementById("root")!).render(
<CodeGroup>
```tsx
import { JazzProviderWithClerk } from "jazz-react-native-auth-clerk";
import { secureStore } from "@clerk/clerk-expo/secure-store";
function JazzAndAuth({ children }: { children: React.ReactNode }) {
const clerk = useClerk();
@@ -275,7 +276,11 @@ export default function RootLayout() {
}
return (
<ClerkProvider tokenCache={tokenCache} publishableKey={publishableKey}>
<ClerkProvider
tokenCache={tokenCache}
publishableKey={publishableKey}
__experimental_resourceCache={secureStore}
>
<ClerkLoaded>
<JazzAndAuth>
<Slot />
@@ -380,7 +385,7 @@ export async function onAnonymousAccountDiscarded(
```
</CodeGroup>
To see how this works in reality we suggest you to try
To see how this works in reality we suggest you to try
to upload a song in the [music player demo](https://music-demo.jazz.tools/) and then
try to log in with an existing account.

View File

@@ -442,11 +442,11 @@ All we have to do is create a new group to own each new issue and add "everyone"
import { useState } from "react"; // old
import { Issue } from "./schema"; // old
import { IssueComponent } from "./components/Issue.tsx"; // old
import { useCoState } from "jazz-react"; // old
import { useAccount, useCoState } from "jazz-react";
import { ID, Group } from "jazz-tools"
// old
function App() { // old
const { me } = useAccount(); // old
const { me } = useAccount();
const [issueID, setIssueID] = useState<ID<Issue> | undefined>(// old
(window.location.search?.replace("?issue=", "") || undefined) as ID<Issue> | undefined,// old
); // old
@@ -665,7 +665,7 @@ export function ProjectComponent({ projectID }: { projectID: ID<Project> }) {//
The loading-depth spec `{ issues: [{}] }` means "in `Project`, load `issues` and load each item in `issues` shallowly". (Since an `Issue` doesn't have any further references, "shallowly" actually means all its properties will be available).
- Now, we can get rid of a lot of coniditional accesses because we know that once `project` is loaded, `project.issues` and each `Issue` in it will be loaded as well.
- Now, we can get rid of a lot of conditional accesses because we know that once `project` is loaded, `project.issues` and each `Issue` in it will be loaded as well.
- This also results in only one rerender and visual update when everything is loaded, which is faster (especially for long lists) and gives you more control over the loading UX.
{/* TODO: explain about not loaded vs not set/defined and `_refs` basics */}

View File

@@ -0,0 +1,39 @@
import { CodeGroup } from '@/components/forMdx'
# Jazz Inspector
[Jazz Inspector](https://inspector.jazz.tools) is a tool to visually inspect a Jazz account or other CoValues.
For now, you can get your account credentials from the `jazz-logged-in-secret` local storage key from within your Jazz app.
[https://inspector.jazz.tools](https://inspector.jazz.tools)
## Exporting current account to Inspector from your app
In development mode, you can launch the Inspector from your Jazz app to inspect your account by pressing `Cmd+J`.
## Embedding the Inspector widget into your app
Alternatively, you can embed the Inspector directly into your app, so you don't need to open a separate window.
Install the package.
<CodeGroup>
```sh
npm install jazz-inspector
```
</CodeGroup>
Render the component within your `JazzProvider`.
<CodeGroup>
```sh
import { JazzInspector } from "jazz-inspector";
<JazzProvider> // old
<JazzInspector />
</JazzProvider> // old
```
</CodeGroup>
Check out the [music player app](https://github.com/garden-co/jazz/blob/main/examples/music-player/src/2_main.tsx) for a full example.

View File

@@ -6,6 +6,38 @@ import { Framework, frameworks } from "@/lib/framework";
import type { Toc } from "@stefanprobst/rehype-extract-toc";
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
async function getMdxSource(slugPath: string, framework: string) {
try {
return await import(`./${slugPath}.mdx`);
} catch (error) {
return await import(`./${slugPath}/${framework}.mdx`);
}
}
export async function generateMetadata({
params: { slug, framework },
}: { params: { slug: string[]; framework: string } }) {
const slugPath = slug.join("/");
try {
const mdxSource = await getMdxSource(slugPath, framework);
const title = mdxSource.tableOfContents?.[0].value || "Documentation";
return {
title,
openGraph: {
title,
},
};
} catch (error) {
return {
title: "Documentation",
openGraph: {
title: "Documentation",
},
};
}
}
export default async function Page({
params: { slug, framework },
}: { params: { slug: string[]; framework: string } }) {
@@ -13,13 +45,7 @@ export default async function Page({
const bodyClassName = "overflow-x-hidden lg:flex-1 py-10 max-w-3xl mx-auto";
try {
let mdxSource;
try {
mdxSource = await import(`./${slugPath}.mdx`);
} catch (error) {
mdxSource = await import(`./${slugPath}/${framework}.mdx`);
}
const mdxSource = await getMdxSource(slugPath, framework);
const { default: Content, tableOfContents } = mdxSource;
// Exclude h1 from table of contents

View File

@@ -4,31 +4,24 @@ export const metadata = { title: "Jazz 0.10.0 is out!" };
# Jazz 0.10.0 is out!
<h2 className="not-prose text-sm text-stone-600 dark:text-stone-400 mb-5 pb-2 border-b">
11 February 2025
</h2>
For Jazz 0.10.0 we have been focusing on enhancing authentication to make it optional, more flexible and easier to use.
<div>
For Jazz 0.10.0 we have been focusing on enhancing authentication to make it optional, more flexible and easier to use.
The default is now anonymous auth, which means that you can build the functionality of your app first and figure out auth later. For users this means that they can start using your app right away on one device -- and once you integrate an auth method, users can sign up and their anonymous accounts are transparently upgraded to authenticated accounts that work across devices.
The default is now anonymous auth, which means that you can build the functionality of your app first and figure out auth later. For users this means that they can start using your app right away on one device -- and once you integrate an auth method, users can sign up and their anonymous accounts are transparently upgraded to authenticated accounts that work across devices.
There are also some other minor improvements that will make your Jazz experience even better!
There are also some other minor improvements that will make your Jazz experience even better!
<h3>What's new?</h3>
Here is what's changed in this release:
- [New authentication flow](#new-authentication-flow): Now with anonymous auth, redesigned to make Jazz easier to start with and be more flexible.
- [Local-only mode](#local-only-mode): Users can now explore your app in local-only mode before signing up.
- [Improvements on the loading APIs](#improved-loading-api); `ensureLoaded` now always returns a value and `useCoState` now returns `null` if the value is not found.
- [Jazz Workers on native WebSockets](#native-websocket-for-jazz-workers): Improves compatibility with a wider set of Javascript runtimes.
- [Group inheritance with role mapping](#group-inheritance): Groups can now inherit members from other groups with a fixed role.
- Support for Node 14 dropped on cojson.
- Bugfix: `Group.removeMember` now returns a promise.
- Now `cojson` and `jazz-tools` don't export directly the crypto providers anymore. Replace the import with `cojson/crypto/WasmCrypto` or `cojson/crypto/PureJSCrypto` depending on your use case.
</div>
## What's new?
Here is what's changed in this release:
- [New authentication flow](#new-authentication-flow): Now with anonymous auth, redesigned to make Jazz easier to start with and be more flexible.
- [Local-only mode](#local-only-mode): Users can now explore your app in local-only mode before signing up.
- [Improvements on the loading APIs](#improved-loading-api); `ensureLoaded` now always returns a value and `useCoState` now returns `null` if the value is not found.
- [Jazz Workers on native WebSockets](#native-websocket-for-jazz-workers): Improves compatibility with a wider set of Javascript runtimes.
- [Group inheritance with role mapping](#group-inheritance): Groups can now inherit members from other groups with a fixed role.
- Support for Node 14 dropped on cojson.
- Bugfix: `Group.removeMember` now returns a promise.
- Now `cojson` and `jazz-tools` don't export directly the crypto providers anymore. Replace the import with `cojson/crypto/WasmCrypto` or `cojson/crypto/PureJSCrypto` depending on your use case.
<h3 id="new-authentication-flow">New authentication flow</h3>
<div>
## New authentication flow
Up until now authentication has been the first part to figure out when building a Jazz app, and this was a stumbling block for many.
Now it is no longer required and setting up a Jazz app is as easy as writing this:
@@ -191,8 +184,8 @@ export function AuthButton() {
if (isAuthenticated) {
return (
<Button
variant="outline"
<Button
variant="outline"
onClick={logOut}
>
Sign out
@@ -224,8 +217,8 @@ export function AuthButton() {
if (isAuthenticated) {
return (
<Button
variant="outline"
<Button
variant="outline"
onPress={logOut}
>
Sign out
@@ -267,16 +260,16 @@ ReactDOM.createRoot(document.getElementById("root")!).render(
```
</CodeGroup>
</ContentByFramework>
For the changes related to the specific auth providers see the updated [authentication docs](/docs/authentication/overview).
</div>
<h3 id="local-only-mode">Local-only mode</h3>
<div>
For the changes related to the specific auth providers see the updated [authentication docs](/docs/authentication/overview).
## Local-only mode
If you are ok with data not being persisted on the sync server for anonymous users, you can now set your app to local-only depending on the user's authentication state.
With `sync.when` set to `"signedUp"` the app will work in local-only mode when the user is anonymous and unlock the multiplayer/multi-device features and cloud persistence when they sign up:
<CodeGroup>
```ts
```tsx
<JazzProvider
sync={{
peer: `wss://cloud.jazz.tools/?key=${apiKey}`,
@@ -290,10 +283,9 @@ With `sync.when` set to `"signedUp"` the app will work in local-only mode when t
</CodeGroup>
You can control when Jazz will sync by switching the `when` config to `"always"` or `"never"`.
</div>
<h3 id="improved-loading-api">Improvements on the loading APIs</h3>
<div>
## Improvements on the loading APIs
Before 0.10.0 `ensureLoaded` was returning a nullable value forcing the Typescript code to always include null checks:
<CodeGroup>
```ts
@@ -336,17 +328,17 @@ if (value === null) {
return <div>Track not found</div>;
}
```
</CodeGroup>
</div>
</CodeGroup>
<h3 id="native-websocket-for-jazz-workers">Jazz Workers on native WebSockets</h3>
<div>
We have removed the dependency on `ws` and switched to the native WebSocket API for Jazz Workers.
## Jazz Workers on native WebSockets
This improves the compatibility with a wider set of Javascript runtimes adding drop-in support for Deno, Bun, Browsers and Cloudflare Durable Objects.
We have removed the dependency on `ws` and switched to the native WebSocket API for Jazz Workers.
If you are using a Node.js version lower than 22 you will need to install the `ws` package and provide the WebSocket constructor:
<CodeGroup>
This improves the compatibility with a wider set of Javascript runtimes adding drop-in support for Deno, Bun, Browsers and Cloudflare Durable Objects.
If you are using a Node.js version lower than 22 you will need to install the `ws` package and provide the WebSocket constructor:
<CodeGroup>
```ts
import { WebSocket } from "ws";
import { startWorker } from "jazz-nodejs";
@@ -355,24 +347,21 @@ const { worker } = await startWorker({
WebSocket,
});
```
</CodeGroup>
</div>
</CodeGroup>
<h3 id="group-inheritance">Group inheritance with role mapping</h3>
<div>
You can override the inherited role by passing a second argument to `extend`.
## Group inheritance with role mapping
You can override the inherited role by passing a second argument to `extend`.
This can be used to give users limited access to a child group:
<CodeGroup>
```ts
const organization = Group.create();
const billing = Group.create();
This can be used to give users limited access to a child group:
<CodeGroup>
```ts
const organization = Group.create();
const billing = Group.create();
billing.extend(organization, "reader");
```
</CodeGroup>
billing.extend(organization, "reader");
```
</CodeGroup>
This way the members of the organization can only read the billing data, even if they are admins in the organization group.
This way the members of the organization can only read the billing data, even if they are admins in the organization group.
More about the group inheritance can be found in the [dedicated docs page](/docs/groups/inheritance).
</div>
More about the group inheritance can be found in the [dedicated docs page](/docs/groups/inheritance).

View File

@@ -1,545 +0,0 @@
import { ContentByFramework, CodeGroup } from '@/components/forMdx'
export const metadata = { title: "Upgrade to Jazz 0.9.0" };
# Upgrade to Jazz 0.9.0
<h2 className="not-prose text-sm text-stone-600 dark:text-stone-400 mb-5 pb-2 border-b">
08 January 2025
</h2>
<ContentByFramework framework="react">
<div>
Version 0.9.0 simplifies the application setup and makes Jazz more intellisense friendly by
replacing the `createJazzReactApp` API with top-level imports.
We have also introduced some new API to make testing Jazz components a breeze. 🌬️
</div>
<h3>New provider setup</h3>
<div>
The `JazzProvider` is now imported from `jazz-react` instead of `createJazzReactApp`.
While `createJazzReactApp` was originally designed to setup strong typing for custom Account schemas in `useAccount`,
we found that this approach made the Jazz setup awkward and confusing for some users.
So we decided to remove `createJazzReactApp` step and to provide the types through namespace declarations:
</div>
<CodeGroup>
{/* prettier-ignore */}
```tsx
import { JazzProvider, usePasskeyAuth, PasskeyAuthBasicUI } from "jazz-react";
import { MyAppAccount } from "./schema";
// Remove these lines // *bin*
const Jazz = createJazzReactApp({ AccountSchema: MyAppAccount }); // *bin*
export const { useAccount, useCoState } = Jazz; // *bin*
export function JazzAndAuth({ children }: { children: React.ReactNode }) { // old
const [passkeyAuth, passKeyState] = usePasskeyAuth({ appName }); // old
return (
<>
{/* Replace Jazz.Provider with provider from jazz-react */}
<JazzProvider
auth={passkeyAuth} // old
peer="wss://cloud.jazz.tools/?key=you@example.com" // old
AccountSchema={MyAppAccount} {/* The custom Account schema is passed here */} // *add*
>
{children} // old
</JazzProvider>
<PasskeyAuthBasicUI state={passKeyState} /> // old
</> // old
);
}
// Register the Account schema so `useAccount` returns our custom `MyAppAccount`
declare module "jazz-react" { // *add*
interface Register { // *add*
Account: MyAppAccount; // *add*
} // *add*
} // *add*
```
</CodeGroup>
<h3>Top level imports for hooks</h3>
<div>
All Jazz hooks are now available as top-level imports from the `jazz-react` package.
This change improves IDE intellisense support and simplifies imports:
</div>
<CodeGroup>
{/* prettier-ignore */}
```tsx
// Replace local imports with "jazz-react" imports
import { useAccount } from "./main"; // *bin*
import { useAccount } from "jazz-react"; // *add*
export function Hello() {
const { me } = useAccount();
return (
<>
Hello {me.profile?.name}
</>
);
}
```
</CodeGroup>
<h3>New testing utilities</h3>
<div>
Removing `createJazzReactApp` also makes testing way easier!
We can now use `createJazzTestAccount` to setup accounts and testing data and pass it to
your components and hooks using `JazzTestProvider`:
</div>
<CodeGroup>
{/* prettier-ignore */}
```tsx
import { createJazzTestAccount, JazzTestProvider } from "jazz-react/testing";
import { renderHook } from "@testing-library/react"; // old
import { usePlaylist } from "./usePlaylist"; // old
import { Playlist, MusicAccount } from "./schema"; // old
test("should load the playlist", async () => {
// ✅ Create a test account with your schema
const account = await createJazzTestAccount({ AccountSchema: MusicAccount });
// ✅ Set up test data
const playlist = Playlist.create({
name: "My playlist",
}, account);
// ✅ Use JazzTestProvider in your tests
const { result } = renderHook(() => usePlaylist(playlist.id), {
wrapper: ({ children }) => (
<JazzTestProvider account={account}>
{children}
</JazzTestProvider>
),
});
// The result is resolved synchronously, so you can assert the value immediately
expect(result.current?.name).toBe("My playlist");
});
```
</CodeGroup>
</ContentByFramework>
<ContentByFramework framework="react-native">
<div>
Version 0.9.0 simplifies the application setup and makes Jazz more intellisense friendly by
replacing the `createJazzRNApp` API with top-level imports.
We have also introduced some new API to make testing Jazz components a breeze. 🌬️
</div>
<h3>New provider setup</h3>
<div>
The `JazzProvider` is now imported from `jazz-react-native` instead of `createJazzRNApp`.
While `createJazzRNApp` was originally designed to setup strong typing for custom Account schemas in `useAccount`,
we found that this approach made the Jazz setup awkward and confusing for some users.
So we decided to remove `createJazzRNApp` step and to provide the types through namespace declarations:
</div>
<CodeGroup>
{/* prettier-ignore */}
```tsx
import { JazzProvider, useDemoAuth, DemoAuthBasicUI } from "jazz-react-native";
import { MyAppAccount } from "./schema";
// Remove these lines // *bin*
const Jazz = createJazzRNApp({ AccountSchema: MyAppAccount }); // *bin*
export const { useAccount, useCoState } = Jazz; // *bin*
export function JazzAndAuth({ children }: { children: React.ReactNode }) { // old
const [auth, state] = useDemoAuth(); // old
return (
<>
{/* Replace Jazz.Provider with provider from jazz-react */}
<JazzProvider
auth={auth} // old
peer="wss://cloud.jazz.tools/?key=you@example.com" // old
AccountSchema={MyAppAccount} {/* The custom Account schema is passed here */}
>
{children} // old
</JazzProvider>
<DemoAuthBasicUI appName="My App" state={state} /> // old
</> // old
);
}
// Register the Account schema so `useAccount` returns our custom `MyAppAccount`
declare module "jazz-react-native" {
interface Register {
Account: MyAppAccount;
}
}
```
</CodeGroup>
<h3>Top level imports for hooks</h3>
<div>
All Jazz hooks are now available as top-level imports from the `jazz-react-native` package.
This change improves IDE intellisense support and simplifies imports:
</div>
<CodeGroup>
{/* prettier-ignore */}
```tsx
// Replace local imports with "jazz-react-native" imports
import { useAccount } from "./main"; // *bin*
import { useAccount } from "jazz-react-native"; // *add*
export function Hello() {
const { me } = useAccount();
return (
<>
Hello {me.profile?.name}
</>
);
}
```
</CodeGroup>
<h3>New testing utilities</h3>
<div>
Removing `createJazzRNApp` also makes testing way easier!
We can now use `createJazzTestAccount` to setup accounts and testing data and pass it to
your components and hooks using `JazzTestProvider`:
</div>
<CodeGroup>
{/* prettier-ignore */}
```tsx
import { createJazzTestAccount, JazzTestProvider } from "jazz-react-native/testing";
import { renderHook } from "@testing-library/react-native"; // old
import { usePlaylist } from "./usePlaylist"; // old
import { Playlist, MusicAccount } from "./schema"; // old
test("should load the playlist", async () => {
// ✅ Create a test account with your schema
const account = await createJazzTestAccount({ AccountSchema: MusicAccount });
// ✅ Set up test data
const playlist = Playlist.create({
name: "My playlist",
}, account);
// ✅ Use JazzTestProvider in your tests
const { result } = renderHook(() => usePlaylist(playlist.id), {
wrapper: ({ children }) => (
<JazzTestProvider account={account}>
{children}
</JazzTestProvider>
),
});
// The result is resolved synchronously, so you can assert the value immediately
expect(result.current?.name).toBe("My playlist");
});
```
</CodeGroup>
</ContentByFramework>
<ContentByFramework framework="svelte">
<div>
Version 0.9.0 simplifies the application setup and makes Jazz more intellisense friendly by
replacing the `createJazzApp` API with top-level imports.
We have also introduced some new API to make testing Jazz components a breeze. 🌬️
</div>
<h3>New provider setup</h3>
<div>
The `JazzProvider` is now imported from `jazz-svelte` instead of `createJazzApp`.
While `createJazzApp` was originally designed to setup strong typing for custom Account schemas in `useAccount`,
we found that this approach made the Jazz setup awkward and confusing for some users.
So we decided to remove `createJazzApp` step and to provide the types through namespace declarations:
</div>
<CodeGroup>
{/* prettier-ignore */}
```svelte
<!-- src/routes/+layout.svelte -->
<script lang="ts" module>
// Register the Account schema so `useAccount` returns our custom `MyAppAccount`
declare module 'jazz-svelte' {
interface Register {
Account: MyAccount;
}
}
</script>
<script lang="ts">
import { Provider } from '$lib/jazz'; // *bin*
import { JazzProvider } from 'jazz-svelte';
// Example configuration for authentication and peer connection
let auth = null; // Replace with your auth implementation
let peer = "wss://your-peer-endpoint";
// The custom Account schema is passed now as a prop
let AccountSchema = MyAccount;
</script>
<JazzProvider {auth} {peer} {AccountSchema}>
<App />
</JazzProvider>
```
</CodeGroup>
<h3>Top level imports for hooks</h3>
<div>
All Jazz hooks are now available as top-level imports from the `jazz-svelte` package.
This change improves IDE intellisense support and simplifies imports:
</div>
<CodeGroup>
{/* prettier-ignore */}
```svelte
<script lang="ts">
import { useAccount } from '$lib/jazz'; // *bin*
import { useAccount } from 'jazz-svelte'; // *add*
const { me } = useAccount();
</script>
<div>
Hello {me.profile?.name}
</div>
```
</CodeGroup>
<h3>New testing utilities</h3>
<div>
Removing `createJazzApp` also makes testing way easier!
We can now use `createJazzTestAccount` to setup accounts and testing data and pass it to
your components and hooks using `JazzTestProvider`:
</div>
<CodeGroup>
{/* prettier-ignore */}
```ts
import { useCoState } from "jazz-svelte";
import { createJazzTestAccount, JazzTestProvider } from "jazz-svelte/testing";
import { render } from "@testing-library/svelte"; // old
import { Playlist, MusicAccount } from "./schema"; // old
test("should load the playlist", async () => {
// ✅ Create a test account with your schema
const account = await createJazzTestAccount({ AccountSchema: MusicAccount });
// ✅ Set up test data
const playlist = Playlist.create({
name: "My playlist",
}, account);
// ✅ Use createJazzTestContext in your tests
render(PlaylistComponent, {
context: createJazzTestContext({ account: options.account }),
props: {
id: playlist.id,
},
});
expect(await screen.findByRole("heading", { name: "My playlist" })).toBeInTheDocument();
});
```
</CodeGroup>
</ContentByFramework>
<ContentByFramework framework="vue">
<div>
Version 0.9.0 simplifies the application setup and makes Jazz more intellisense friendly by
replacing the `createJazzVueApp` API with top-level imports.
We have also introduced some new API to make testing Jazz components a breeze. 🌬️
</div>
<h3>New provider setup</h3>
<div>
The `JazzProvider` is now imported from `jazz-vue` instead of `createJazzVueApp`.
While `createJazzReactApp` was originally designed to setup strong typing for custom Account schemas in `useAccount`,
we found that this approach made the Jazz setup awkward and confusing for some users.
So we decided to remove `createJazzReactApp` step and to provide the types through namespace declarations:
</div>
<CodeGroup>
{/* prettier-ignore */}
```typescript
import "./assets/main.css"; // old
import { DemoAuthBasicUI, useDemoAuth, JazzProvider } from "jazz-vue";
import { createApp, defineComponent, h } from "vue"; // old
import App from "./App.vue"; // old
import router from "./router"; // old
import { ToDoAccount } from "./schema"; // old
// Remove these lines // *bin*
const Jazz = createJazzVueApp<ToDoAccount>({ AccountSchema: ToDoAccount }); // *bin*
export const { useAccount, useCoState } = Jazz; // *bin*
const { JazzProvider } = Jazz; // *bin*
const RootComponent = defineComponent({ // old
name: "RootComponent", // old
setup() { // old
const { authMethod, state } = useDemoAuth(); // old
return () => [ // old
h( // old
JazzProvider, // old
{ // old
AccountSchema: ToDoAccount, // The custom Account schema is passed here now
auth: authMethod.value, // old
peer: "wss://cloud.jazz.tools/?key=vue-todo-example-jazz@garden.co", // old
}, // old
{ // old
default: () => h(App), // old
}, // old
), // old
state.state !== "signedIn" && // old
h(DemoAuthBasicUI, { // old
appName: "Jazz Vue Todo", // old
state, // old
}), // old
]; // old
}, // old
}); // old
// Register the Account schema so `useAccount` returns our custom `MyAppAccount`
declare module "jazz-vue" {
interface Register {
Account: ToDoAccount;
}
}
const app = createApp(RootComponent); // old
app.use(router); // old
app.mount("#app"); // old
```
</CodeGroup>
<h3>Top level imports for hooks</h3>
<div>
All Jazz hooks are now available as top-level imports from the `jazz-vue` package.
This change improves IDE intellisense support and simplifies imports:
</div>
<CodeGroup>
{/* prettier-ignore */}
```typescript
<template>
Hello {{ me.profile?.name }}
</template>
<script setup lang="ts">
// Replace local imports with "jazz-vue" imports
import { useAccount } from "./main"; // *bin*
import { useAccount } from "jazz-vue"; // *add*
const { me, logOut } = useAccount();
</script>
```
</CodeGroup>
<h3>New testing utilities</h3>
<div>
Removing `createJazzTestApp` also makes testing way easier!
We can now use `createJazzTestAccount` to setup accounts and testing data and pass it to
your components and hooks using `JazzTestProvider`:
</div>
<CodeGroup>
{/* prettier-ignore */}
```tsx
import { createJazzTestAccount, JazzTestProvider } from "jazz-vue/testing";
import { createApp, defineComponent, h } from "vue";
import { usePlaylist } from "./usePlaylist";
import { Playlist, MusicAccount } from "./schema"; // old
// This can be reused on other tests!
export const renderComposableWithJazz = <C extends (...args: any[]) => any>(
composable: C,
{ account }: { account: Account | { guest: AnonymousJazzAgent } },
) => {
let result;
const wrapper = defineComponent({
setup() {
result = composable();
// suppress missing template warning
return () => {};
},
});
// ✅ Use JazzTestProvider in your tests
const app = createApp({
setup() {
return () =>
h(
JazzTestProvider,
{
account,
},
{
default: () => h(wrapper),
},
);
},
});
app.mount(document.createElement("div"));
return [result, app] as [ReturnType<C>, ReturnType<typeof createApp>];
};
test("should load the playlist", async () => {
// ✅ Create a test account with your schema
const account = await createJazzTestAccount({ AccountSchema: MusicAccount });
// ✅ Set up test data
const playlist = Playlist.create({
name: "My playlist",
}, account);
// ✅ Set up test data
const { result } = renderComposableWithJazz(() => usePlaylist(playlist.id), {
account,
});
// The result is resolved synchronously, so you can assert the value immediately
expect(result?.name).toBe("My playlist");
});
```
</CodeGroup>
</ContentByFramework>

View File

@@ -0,0 +1,123 @@
import { ContentByFramework, CodeGroup } from '@/components/forMdx'
export const metadata = { title: "Upgrade to Jazz 0.9.0" };
# Upgrade to Jazz 0.9.0
Version 0.9.0 simplifies the application setup and makes Jazz more intellisense friendly by
replacing the `createJazzRNApp` API with top-level imports.
We have also introduced some new API to make testing Jazz components a breeze. 🌬️
## New provider setup
The `JazzProvider` is now imported from `jazz-react-native` instead of `createJazzRNApp`.
While `createJazzRNApp` was originally designed to setup strong typing for custom Account schemas in `useAccount`,
we found that this approach made the Jazz setup awkward and confusing for some users.
So we decided to remove `createJazzRNApp` step and to provide the types through namespace declarations:
<CodeGroup>
{/* prettier-ignore */}
```tsx
import { JazzProvider, useDemoAuth, DemoAuthBasicUI } from "jazz-react-native";
import { MyAppAccount } from "./schema";
// Remove these lines // *bin*
const Jazz = createJazzRNApp({ AccountSchema: MyAppAccount }); // *bin*
export const { useAccount, useCoState } = Jazz; // *bin*
export function JazzAndAuth({ children }: { children: React.ReactNode }) { // old
const [auth, state] = useDemoAuth(); // old
return (
<>
{/* Replace Jazz.Provider with provider from jazz-react */}
<JazzProvider
auth={auth} // old
peer="wss://cloud.jazz.tools/?key=you@example.com" // old
AccountSchema={MyAppAccount} {/* The custom Account schema is passed here */}
>
{children} // old
</JazzProvider>
<DemoAuthBasicUI appName="My App" state={state} /> // old
</> // old
);
}
// Register the Account schema so `useAccount` returns our custom `MyAppAccount`
declare module "jazz-react-native" {
interface Register {
Account: MyAppAccount;
}
}
```
</CodeGroup>
## Top level imports for hooks
All Jazz hooks are now available as top-level imports from the `jazz-react-native` package.
This change improves IDE intellisense support and simplifies imports:
<CodeGroup>
{/* prettier-ignore */}
```tsx
// Replace local imports with "jazz-react-native" imports
import { useAccount } from "./main"; // *bin*
import { useAccount } from "jazz-react-native"; // *add*
export function Hello() {
const { me } = useAccount();
return (
<>
Hello {me.profile?.name}
</>
);
}
```
</CodeGroup>
## New testing utilities
<div>
Removing `createJazzRNApp` also makes testing way easier!
We can now use `createJazzTestAccount` to setup accounts and testing data and pass it to
your components and hooks using `JazzTestProvider`:
</div>
<CodeGroup>
{/* prettier-ignore */}
```tsx
import { createJazzTestAccount, JazzTestProvider } from "jazz-react-native/testing";
import { renderHook } from "@testing-library/react-native"; // old
import { usePlaylist } from "./usePlaylist"; // old
import { Playlist, MusicAccount } from "./schema"; // old
test("should load the playlist", async () => {
// ✅ Create a test account with your schema
const account = await createJazzTestAccount({ AccountSchema: MusicAccount });
// ✅ Set up test data
const playlist = Playlist.create({
name: "My playlist",
}, account);
// ✅ Use JazzTestProvider in your tests
const { result } = renderHook(() => usePlaylist(playlist.id), {
wrapper: ({ children }) => (
<JazzTestProvider account={account}>
{children}
</JazzTestProvider>
),
});
// The result is resolved synchronously, so you can assert the value immediately
expect(result.current?.name).toBe("My playlist");
});
```
</CodeGroup>

View File

@@ -0,0 +1,120 @@
import { ContentByFramework, CodeGroup } from '@/components/forMdx'
export const metadata = { title: "Upgrade to Jazz 0.9.0" };
# Upgrade to Jazz 0.9.0
Version 0.9.0 simplifies the application setup and makes Jazz more intellisense friendly by
replacing the `createJazzReactApp` API with top-level imports.
We have also introduced some new API to make testing Jazz components a breeze. 🌬️
## New provider setup
The `JazzProvider` is now imported from `jazz-react` instead of `createJazzReactApp`.
While `createJazzReactApp` was originally designed to setup strong typing for custom Account schemas in `useAccount`,
we found that this approach made the Jazz setup awkward and confusing for some users.
So we decided to remove `createJazzReactApp` step and to provide the types through namespace declarations:
<CodeGroup>
{/* prettier-ignore */}
```tsx
import { JazzProvider, usePasskeyAuth, PasskeyAuthBasicUI } from "jazz-react";
import { MyAppAccount } from "./schema";
// Remove these lines // *bin*
const Jazz = createJazzReactApp({ AccountSchema: MyAppAccount }); // *bin*
export const { useAccount, useCoState } = Jazz; // *bin*
export function JazzAndAuth({ children }: { children: React.ReactNode }) { // old
const [passkeyAuth, passKeyState] = usePasskeyAuth({ appName }); // old
return (
<>
{/* Replace Jazz.Provider with provider from jazz-react */}
<JazzProvider
auth={passkeyAuth} // old
peer="wss://cloud.jazz.tools/?key=you@example.com" // old
AccountSchema={MyAppAccount} {/* The custom Account schema is passed here */} // *add*
>
{children} // old
</JazzProvider>
<PasskeyAuthBasicUI state={passKeyState} /> // old
</> // old
);
}
// Register the Account schema so `useAccount` returns our custom `MyAppAccount`
declare module "jazz-react" { // *add*
interface Register { // *add*
Account: MyAppAccount; // *add*
} // *add*
} // *add*
```
</CodeGroup>
## Top level imports for hooks
All Jazz hooks are now available as top-level imports from the `jazz-react` package.
This change improves IDE intellisense support and simplifies imports:
<CodeGroup>
{/* prettier-ignore */}
```tsx
// Replace local imports with "jazz-react" imports
import { useAccount } from "./main"; // *bin*
import { useAccount } from "jazz-react"; // *add*
export function Hello() {
const { me } = useAccount();
return (
<>
Hello {me.profile?.name}
</>
);
}
```
</CodeGroup>
## New testing utilities
Removing `createJazzReactApp` also makes testing way easier!
We can now use `createJazzTestAccount` to setup accounts and testing data and pass it to
your components and hooks using `JazzTestProvider`:
<CodeGroup>
{/* prettier-ignore */}
```tsx
import { createJazzTestAccount, JazzTestProvider } from "jazz-react/testing";
import { renderHook } from "@testing-library/react"; // old
import { usePlaylist } from "./usePlaylist"; // old
import { Playlist, MusicAccount } from "./schema"; // old
test("should load the playlist", async () => {
// ✅ Create a test account with your schema
const account = await createJazzTestAccount({ AccountSchema: MusicAccount });
// ✅ Set up test data
const playlist = Playlist.create({
name: "My playlist",
}, account);
// ✅ Use JazzTestProvider in your tests
const { result } = renderHook(() => usePlaylist(playlist.id), {
wrapper: ({ children }) => (
<JazzTestProvider account={account}>
{children}
</JazzTestProvider>
),
});
// The result is resolved synchronously, so you can assert the value immediately
expect(result.current?.name).toBe("My playlist");
});
```
</CodeGroup>

View File

@@ -0,0 +1,113 @@
import { ContentByFramework, CodeGroup } from '@/components/forMdx'
export const metadata = { title: "Upgrade to Jazz 0.9.0" };
# Upgrade to Jazz 0.9.0
Version 0.9.0 simplifies the application setup and makes Jazz more intellisense friendly by
replacing the `createJazzApp` API with top-level imports.
We have also introduced some new API to make testing Jazz components a breeze. 🌬️
## New provider setup
The `JazzProvider` is now imported from `jazz-svelte` instead of `createJazzApp`.
While `createJazzApp` was originally designed to setup strong typing for custom Account schemas in `useAccount`,
we found that this approach made the Jazz setup awkward and confusing for some users.
So we decided to remove `createJazzApp` step and to provide the types through namespace declarations:
<CodeGroup>
{/* prettier-ignore */}
```svelte
<!-- src/routes/+layout.svelte -->
<script lang="ts" module>
// Register the Account schema so `useAccount` returns our custom `MyAppAccount`
declare module 'jazz-svelte' {
interface Register {
Account: MyAccount;
}
}
</script>
<script lang="ts">
import { Provider } from '$lib/jazz'; // *bin*
import { JazzProvider } from 'jazz-svelte';
// Example configuration for authentication and peer connection
let auth = null; // Replace with your auth implementation
let peer = "wss://your-peer-endpoint";
// The custom Account schema is passed now as a prop
let AccountSchema = MyAccount;
</script>
<JazzProvider {auth} {peer} {AccountSchema}>
<App />
</JazzProvider>
```
</CodeGroup>
## Top level imports for hooks
<div>
All Jazz hooks are now available as top-level imports from the `jazz-svelte` package.
This change improves IDE intellisense support and simplifies imports:
</div>
<CodeGroup>
{/* prettier-ignore */}
```svelte
<script lang="ts">
import { useAccount } from '$lib/jazz'; // *bin*
import { useAccount } from 'jazz-svelte'; // *add*
const { me } = useAccount();
</script>
<div>
Hello {me.profile?.name}
</div>
```
</CodeGroup>
## New testing utilities
Removing `createJazzApp` also makes testing way easier!
We can now use `createJazzTestAccount` to setup accounts and testing data and pass it to
your components and hooks using `JazzTestProvider`:
<CodeGroup>
{/* prettier-ignore */}
```ts
import { useCoState } from "jazz-svelte";
import { createJazzTestAccount, JazzTestProvider } from "jazz-svelte/testing";
import { render } from "@testing-library/svelte"; // old
import { Playlist, MusicAccount } from "./schema"; // old
test("should load the playlist", async () => {
// ✅ Create a test account with your schema
const account = await createJazzTestAccount({ AccountSchema: MusicAccount });
// ✅ Set up test data
const playlist = Playlist.create({
name: "My playlist",
}, account);
// ✅ Use createJazzTestContext in your tests
render(PlaylistComponent, {
context: createJazzTestContext({ account: options.account }),
props: {
id: playlist.id,
},
});
expect(await screen.findByRole("heading", { name: "My playlist" })).toBeInTheDocument();
});
```
</CodeGroup>

View File

@@ -0,0 +1,165 @@
import { ContentByFramework, CodeGroup } from '@/components/forMdx'
export const metadata = { title: "Upgrade to Jazz 0.9.0" };
# Upgrade to Jazz 0.9.0
Version 0.9.0 simplifies the application setup and makes Jazz more intellisense friendly by
replacing the `createJazzVueApp` API with top-level imports.
We have also introduced some new API to make testing Jazz components a breeze. 🌬️
## New provider setup
The `JazzProvider` is now imported from `jazz-vue` instead of `createJazzVueApp`.
While `createJazzReactApp` was originally designed to setup strong typing for custom Account schemas in `useAccount`,
we found that this approach made the Jazz setup awkward and confusing for some users.
So we decided to remove `createJazzReactApp` step and to provide the types through namespace declarations:
<CodeGroup>
{/* prettier-ignore */}
```typescript
import "./assets/main.css"; // old
import { DemoAuthBasicUI, useDemoAuth, JazzProvider } from "jazz-vue";
import { createApp, defineComponent, h } from "vue"; // old
import App from "./App.vue"; // old
import router from "./router"; // old
import { ToDoAccount } from "./schema"; // old
// Remove these lines // *bin*
const Jazz = createJazzVueApp<ToDoAccount>({ AccountSchema: ToDoAccount }); // *bin*
export const { useAccount, useCoState } = Jazz; // *bin*
const { JazzProvider } = Jazz; // *bin*
const RootComponent = defineComponent({ // old
name: "RootComponent", // old
setup() { // old
const { authMethod, state } = useDemoAuth(); // old
return () => [ // old
h( // old
JazzProvider, // old
{ // old
AccountSchema: ToDoAccount, // The custom Account schema is passed here now
auth: authMethod.value, // old
peer: "wss://cloud.jazz.tools/?key=vue-todo-example-jazz@garden.co", // old
}, // old
{ // old
default: () => h(App), // old
}, // old
), // old
state.state !== "signedIn" && // old
h(DemoAuthBasicUI, { // old
appName: "Jazz Vue Todo", // old
state, // old
}), // old
]; // old
}, // old
}); // old
// Register the Account schema so `useAccount` returns our custom `MyAppAccount`
declare module "jazz-vue" {
interface Register {
Account: ToDoAccount;
}
}
const app = createApp(RootComponent); // old
app.use(router); // old
app.mount("#app"); // old
```
</CodeGroup>
## Top level imports for hooks
All Jazz hooks are now available as top-level imports from the `jazz-vue` package.
This change improves IDE intellisense support and simplifies imports:
<CodeGroup>
{/* prettier-ignore */}
```typescript
<template>
Hello {{ me.profile?.name }}
</template>
<script setup lang="ts">
// Replace local imports with "jazz-vue" imports
import { useAccount } from "./main"; // *bin*
import { useAccount } from "jazz-vue"; // *add*
const { me, logOut } = useAccount();
</script>
```
</CodeGroup>
## New testing utilities
Removing `createJazzTestApp` also makes testing way easier!
We can now use `createJazzTestAccount` to setup accounts and testing data and pass it to
your components and hooks using `JazzTestProvider`:
<CodeGroup>
{/* prettier-ignore */}
```tsx
import { createJazzTestAccount, JazzTestProvider } from "jazz-vue/testing";
import { createApp, defineComponent, h } from "vue";
import { usePlaylist } from "./usePlaylist";
import { Playlist, MusicAccount } from "./schema"; // old
// This can be reused on other tests!
export const renderComposableWithJazz = <C extends (...args: any[]) => any>(
composable: C,
{ account }: { account: Account | { guest: AnonymousJazzAgent } },
) => {
let result;
const wrapper = defineComponent({
setup() {
result = composable();
// suppress missing template warning
return () => {};
},
});
// ✅ Use JazzTestProvider in your tests
const app = createApp({
setup() {
return () =>
h(
JazzTestProvider,
{
account,
},
{
default: () => h(wrapper),
},
);
},
});
app.mount(document.createElement("div"));
return [result, app] as [ReturnType<C>, ReturnType<typeof createApp>];
};
test("should load the playlist", async () => {
// ✅ Create a test account with your schema
const account = await createJazzTestAccount({ AccountSchema: MusicAccount });
// ✅ Set up test data
const playlist = Playlist.create({
name: "My playlist",
}, account);
// ✅ Set up test data
const { result } = renderComposableWithJazz(() => usePlaylist(playlist.id), {
account,
});
// The result is resolved synchronously, so you can assert the value immediately
expect(result?.name).toBe("My playlist");
});
```
</CodeGroup>

View File

@@ -4,15 +4,9 @@ export const metadata = { title: "Upgrade to Jazz 0.9.0" };
# Jazz 0.9.8 - Without me!
<h2 className="not-prose text-sm text-stone-600 dark:text-stone-400 mb-5 pb-2 border-b">
14 January 2025
</h2>
We have simplified the API to make the "me" value always optional!
<div>
We have simplified the API to make the "me" value always optional!
This removes the need of using `useAccount` like the 90% of the time!
</div>
This removes the need of using `useAccount` like the 90% of the time!
<CodeGroup>
{/* prettier-ignore */}
@@ -20,6 +14,7 @@ export const metadata = { title: "Upgrade to Jazz 0.9.0" };
import { useState } from "react";
import { Issue } from "./schema";
import { IssueComponent } from "./components/Issue.tsx";
function App() {
const [issue, setIssue] = useState<Issue>();
@@ -43,9 +38,7 @@ export const metadata = { title: "Upgrade to Jazz 0.9.0" };
```
</CodeGroup>
<div>
This also applies to the load API:
</div>
This also applies to the load API:
<CodeGroup>
{/* prettier-ignore */}
@@ -54,9 +47,7 @@ export const metadata = { title: "Upgrade to Jazz 0.9.0" };
```
</CodeGroup>
<div>
And `Group.create`:
</div>
And `Group.create`:
<CodeGroup>
{/* prettier-ignore */}
@@ -67,8 +58,6 @@ export const metadata = { title: "Upgrade to Jazz 0.9.0" };
```
</CodeGroup>
<div>
Everything is backward compatible, so no upgrade steps are required.
With this Jazz API becomes way more lean and more is coming!
</div>

View File

@@ -4,46 +4,34 @@ export const metadata = { title: "Enable local persistence" };
# Enable local persistence
<h2 className="not-prose text-sm text-stone-600 dark:text-stone-400 mb-5 pb-2 border-b">
10 January 2025
</h2>
Version 0.9.2 introduces local persistence for React Native apps using SQLite.
<ContentByFramework framework="react-native">
<div>
Version 0.9.2 introduces local persistence for React Native apps using SQLite.
If you are upgrading from a version before 0.9.2, you need to enable local persistence by following the steps below.
If you are upgrading from a version before 0.9.2, you need to enable local persistence by following the steps below.
Local persistence will become the default in 0.10.0.
Local persistence will become the default in 0.10.0.
</div>
## Add the required dependencies
<h3>Add the required dependencies</h3>
As SQLite package we now use `@op-engineering/op-sqlite`.
<div>
As SQLite package we now use `@op-engineering/op-sqlite`.
To get local persistence working, you need to add `@op-engineering/op-sqlite` as a direct dependency to your project and run `npx pod-install`.
To get local persistence working, you need to add `@op-engineering/op-sqlite` as a direct dependency to your project and run `npx pod-install`.
</div>
## Update your JazzProvider to enable local persistence
<h3>Update your JazzProvider to enable local persistence</h3>
Local persistence is now disabled by default.
<div>
Local persistence is now disabled by default.
To enable it, you need to pass the `storage` option to the `JazzProvider` component:
To enable it, you need to pass the `storage` option to the `JazzProvider` component:
</div>
<CodeGroup>
```tsx
<JazzProvider
auth={auto}
storage="sqlite"
peer="wss://cloud.jazz.tools/?key=you@example.com"
AccountSchema={MyAppAccount}
>
<App />
</JazzProvider>
```
</CodeGroup>
</ContentByFramework>
<CodeGroup>
```tsx
<JazzProvider
auth={auto}
storage="sqlite"
peer="wss://cloud.jazz.tools/?key=you@example.com"
AccountSchema={MyAppAccount}
>
<App />
</JazzProvider>
```
</CodeGroup>

View File

@@ -0,0 +1,225 @@
export const metadata = { title: "CoFeeds" };
import { CodeGroup, ComingSoon, ContentByFramework } from "@/components/forMdx";
# CoFeeds
CoFeeds are append-only data structures that track entries from different user sessions and accounts. Unlike other CoValues where everyone edits the same data, CoFeeds maintain separate streams for each session.
Each account can have multiple sessions (different browser tabs, devices, or app instances), making CoFeeds ideal for building features like activity logs, presence indicators, and notification systems.
The [Reactions example](https://github.com/garden-co/jazz/tree/main/examples/reactions) demonstrates a practical use of CoFeeds.
## Creating CoFeeds
CoFeeds are defined by specifying the type of items they'll contain, similar to how you define CoLists:
<CodeGroup>
```ts
// Define a schema for feed items
class Activity extends CoMap {
timestamp = co.Date;
action = co.literal("watering", "planting", "harvesting", "maintenance");
notes = co.optional.string;
}
// Define a feed of garden activities
class ActivityFeed extends CoFeed.Of(co.ref(Activity)) {}
// Create a feed instance
const activityFeed = ActivityFeed.create([]);
```
</CodeGroup>
Like other CoValues, you can specify [ownership](/docs/using-covalues/ownership) when creating CoFeeds.
## Reading from CoFeeds
Since CoFeeds are made of entries from users over multiple sessions, you can access entries in different ways - from a specific user's session or from their account as a whole.
### Per-Session Access
To retrieve entries from a session:
<CodeGroup>
```ts
// Get the feed for a specific session
const sessionFeed = activityFeed.perSession[sessionId];
// Latest entry from a session
console.log(sessionFeed.value.action); // "watering"
```
</CodeGroup>
For convenience, you can also access the latest entry from the current session with `inCurrentSession`:
<CodeGroup>
```ts
// Get the feed for the current session
const currentSessionFeed = activityFeed.inCurrentSession;
// Latest entry from the current session
console.log(currentSessionFeed.value.action); // "harvesting"
```
</CodeGroup>
### Per-Account Access
To retrieve entries from a specific account you can use bracket notation with the account ID:
<CodeGroup>
```ts
// Get the feed for a specific account
const accountFeed = activityFeed[accountId];
// Latest entry from the account
console.log(accountFeed.value.action); // "watering"
```
</CodeGroup>
For convenience, you can also access the latest entry from the current account with `byMe`:
<CodeGroup>
```ts
// Get the feed for the current account
const myLatestEntry = activityFeed.byMe;
// Latest entry from the current account
console.log(myLatestEntry.value.action); // "harvesting"
```
</CodeGroup>
### Feed Entries
#### All Entries
To retrieve all entries from a CoFeed:
<CodeGroup>
```ts
// Get the feeds for a specific account and session
const accountFeed = activityFeed[accountId];
const sessionFeed = activityFeed.perSession[sessionId];
// Iterate over all entries from the account
for (const entry of accountFeed.all) {
console.log(entry.value);
}
// Iterate over all entries from the session
for (const entry of sessionFeed.all) {
console.log(entry.value);
}
```
</CodeGroup>
#### Latest Entry
To retrieve the latest entry from a CoFeed, ie. the last update:
<CodeGroup>
```ts
// Get the latest entry from the current account
const latestEntry = activityFeed.byMe;
console.log(`My last action was ${latestEntry.value.action}`);
// "My last action was harvesting"
// Get the latest entry from each account
const latestEntriesByAccount = Object.values(activityFeed).map(entry => ({
accountName: entry.by?.profile?.name,
value: entry.value,
}));
```
</CodeGroup>
## Writing to CoFeeds
CoFeeds are append-only; you can add new items, but not modify existing ones. This creates a chronological record of events or activities.
### Adding Items
<CodeGroup>
```ts
// Log a new activity
activityFeed.push(Activity.create({
timestamp: new Date(),
action: "watering",
notes: "Extra water for new seedlings"
}));
```
</CodeGroup>
Each item is automatically associated with the current user's session. You don't need to specify which session the item belongs to - Jazz handles this automatically.
### Understanding Session Context
Each entry is automatically added to the current session's feed. When a user has multiple open sessions (like both a mobile app and web browser), each session creates its own separate entries:
<CodeGroup>
```ts
// On mobile device:
fromMobileFeed.push(Activity.create({
timestamp: new Date(),
action: "harvesting",
location: "Vegetable patch"
}));
// On web browser (same user):
fromBrowserFeed.push(Activity.create({
timestamp: new Date(),
action: "planting",
location: "Flower bed"
}));
// These are separate entries in the same feed, from the same account
```
</CodeGroup>
## Metadata
CoFeeds support metadata, which is useful for tracking information about the feed itself.
### By
The `by` property is the account that made the entry.
<CodeGroup>
```ts
const accountFeed = activityFeed[accountId];
// Get the account that made the last entry
console.log(accountFeed?.by);
```
</CodeGroup>
### MadeAt
The `madeAt` property is a timestamp of when the entry was added to the feed.
<CodeGroup>
```ts
const accountFeed = activityFeed[accountId];
// Get the timestamp of the last update
console.log(accountFeed?.madeAt);
// Get the timestamp of each entry
for (const entry of accountFeed.all) {
console.log(entry.madeAt);
}
```
</CodeGroup>
## Best Practices
### When to Use CoFeeds
- **Use CoFeeds when**:
- You need to track per-user/per-session data
- Time-based information matters (activity logs, presence)
- **Consider alternatives when**:
- Data needs to be collaboratively edited (use CoMaps or CoLists)
- You need structured relationships (use CoMaps/CoLists with references)

View File

@@ -0,0 +1,192 @@
import { CodeGroup, ContentByFramework } from "@/components/forMdx";
export const metadata = { title: "CoLists" };
# CoLists
CoLists are ordered collections that work like JavaScript arrays. They provide indexed access, iteration methods, and length properties, making them perfect for managing sequences of items.
## Creating CoLists
CoLists are defined by specifying the type of items they contain:
<CodeGroup>
```ts
class ListOfResources extends CoList.Of(co.string) {}
class ListOfTasks extends CoList.Of(co.ref(Task)) {}
```
</CodeGroup>
To instantiate a CoList:
<CodeGroup>
```ts
// Create an empty list
const resources = ListOfResources.create([]);
// Create a list with initial items
const tasks = ListOfTasks.create([
Task.create({ title: "Prepare soil beds", status: "in-progress" }),
Task.create({ title: "Order compost", status: "todo" })
]);
```
</CodeGroup>
Like other CoValues, you can specify [ownership](/docs/using-covalues/ownership) when creating CoLists.
## Reading from CoLists
CoLists support standard array access patterns:
<CodeGroup>
```ts
// Access by index
const firstTask = tasks[0];
console.log(firstTask.title); // "Prepare soil beds"
// Get list length
console.log(tasks.length); // 2
// Iteration
tasks.forEach(task => {
console.log(task.title);
// "Prepare soil beds"
// "Order compost"
});
// Array methods
const todoTasks = tasks.filter(task => task.status === "todo");
console.log(todoTasks.length); // 1
```
</CodeGroup>
## Writing to CoLists
CoLists support all the standard JavaScript array mutation methods:
<CodeGroup>
```ts
// Add items
resources.push("Courgette"); // Add to end
resources.unshift("Lettuce"); // Add to beginning
console.log(resources);
// ["Lettuce", "Tomatoes", "Basil", "Peppers", "Courgette"]
// Add complex items to lists of references
tasks.push(Task.create({
title: "Install irrigation system",
status: "todo"
}));
// Remove items
resources.pop(); // Remove last item
resources.shift(); // Remove first item
resources.splice(1, 2); // Remove items at index 1 and 2
// Replace items
resources[0] = "Cucumber"; // Replace first item
console.log(resources); // ["Cucumber"]
// Modify items in a list of references
tasks[0].status = "in-progress"; // Modify a property of an item
```
</CodeGroup>
### Type Safety
CoLists maintain type safety for their items:
<CodeGroup>
```ts
// TypeScript knows each item's type
resources.push("Carrots"); // ✓ Valid string
resources.push(42); // ✗ Type error: expected string
// For lists of references, TypeScript knows the referenced type
tasks.forEach(task => {
console.log(task.title); // TypeScript knows 'task' has a 'title'
});
```
</CodeGroup>
### Array Methods
CoLists support many standard JavaScript array methods:
<CodeGroup>
```ts
resources.push("Tomatoes", "Basil", "Peppers");
// Filter
const springResources = resources.filter(resource => resource.includes("Tomato"));
console.log(springResources); // ["Tomatoes"]
// Map (creates a regular array, not a CoList)
const resourceNames = resources.map(resource => resource.toUpperCase());
console.log(resourceNames); // ["TOMATOES", "BASIL", "PEPPERS"]
// Find
const basil = resources.find(resource => resource === "Basil");
// Sort (modifies the CoList in-place)
resources.sort();
console.log(resources); // ["Basil", "Cucumber", "Peppers", "Tomatoes"]
```
</CodeGroup>
## Best Practices
### Common Patterns
#### List Rendering
CoLists work well with UI rendering libraries:
<CodeGroup>
```tsx
// React example
function TaskList({ tasks }) {
return (
<ul>
{tasks.map(task => (
<li key={task.id}>
{task.title} - {task.status}
</li>
))}
</ul>
);
}
```
</CodeGroup>
#### Managing Relations
CoLists can be used to create one-to-many relationships:
<CodeGroup>
```ts
class Project extends CoMap {
name = co.string;
tasks = co.ref(ListOfTasks);
}
// ...
const task = Task.create({
title: "Plant seedlings",
status: "todo",
project: project, // Add a reference to the project
});
// Add a task to a garden project
project.tasks.push(task);
// Access the project from the task
console.log(task.project); // { name: "Garden Project", tasks: [task] }
```
</CodeGroup>

View File

@@ -0,0 +1,199 @@
import { CodeGroup, ContentByFramework } from "@/components/forMdx";
export const metadata = { title: "CoMaps" };
# CoMaps
CoMaps are key-value objects that work like JavaScript objects. You can access properties with dot notation and define typed fields that provide TypeScript safety. They're ideal for structured data that needs type validation.
## Creating CoMaps
CoMaps are typically defined by extending the `CoMap` class and specifying primitive fields using the `co` declarer (see [Defining schemas: CoValues](/docs/schemas/covalues) for more details on primitive fields):
<CodeGroup>
```ts
class Project extends CoMap {
name = co.string;
startDate = co.Date;
status = co.literal("planning", "active", "completed");
coordinator = co.optional.ref(Member);
}
```
</CodeGroup>
You can create either struct-like CoMaps with fixed fields (as above) or record-like CoMaps for key-value pairs:
<CodeGroup>
```ts
class Inventory extends CoMap.Record(co.number) {}
```
</CodeGroup>
To instantiate a CoMap:
<CodeGroup>
```ts
const project = Project.create({
name: "Spring Planting",
startDate: new Date("2025-03-15"),
status: "planning",
});
const inventory = Inventory.create({
tomatoes: 48,
basil: 12,
});
```
</CodeGroup>
### Ownership
When creating CoMaps, you can specify ownership to control access:
<CodeGroup>
```ts
// Create with default owner (current user)
const privateProject = Project.create({
name: "My Herb Garden",
startDate: new Date("2025-04-01"),
status: "planning",
});
// Create with shared ownership
const gardenGroup = Group.create();
gardenGroup.addMember(memberAccount, "writer");
const communityProject = Project.create(
{
name: "Community Vegetable Plot",
startDate: new Date("2025-03-20"),
status: "planning",
},
{ owner: gardenGroup },
);
```
</CodeGroup>
## Reading from CoMaps
CoMaps can be accessed using familiar JavaScript object notation:
<CodeGroup>
```ts
console.log(project.name); // "Spring Planting"
console.log(project.status); // "planning"
```
</CodeGroup>
### Handling Optional Fields
Optional fields require checks before access:
<CodeGroup>
```ts
if (project.coordinator) {
console.log(project.coordinator.name); // Safe access
}
```
</CodeGroup>
### Working with Record CoMaps
For record-type CoMaps, you can access values using bracket notation:
<CodeGroup>
```ts
const inventory = Inventory.create({
tomatoes: 48,
peppers: 24,
basil: 12
});
console.log(inventory["tomatoes"]); // 48
```
</CodeGroup>
## Updating CoMaps
Updating CoMap properties uses standard JavaScript assignment:
<CodeGroup>
```ts
project.name = "Spring Vegetable Garden"; // Update name
project.startDate = new Date("2025-03-20"); // Update date
```
</CodeGroup>
### Type Safety
CoMaps are fully typed in TypeScript, giving you autocomplete and error checking:
<CodeGroup>
```ts
project.name = "Spring Vegetable Planting"; // ✓ Valid string
project.startDate = "2025-03-15"; // ✗ Type error: expected Date
```
</CodeGroup>
### Deleting Properties
You can delete properties from CoMaps:
<CodeGroup>
```ts
delete inventory["basil"]; // Remove a key-value pair
// For optional fields in struct-like CoMaps
project.coordinator = null; // Remove the reference
```
</CodeGroup>
## Best Practices
### Structuring Data
- Use struct-like CoMaps for entities with fixed, known properties
- Use record-like CoMaps for dynamic key-value collections
- Group related properties into nested CoMaps for better organization
### Common Patterns
#### Using Computed Properties
CoMaps support computed properties and methods:
<CodeGroup>
```ts
class ComputedProject extends CoMap {
name = co.string;
startDate = co.Date;
endDate = co.optional.Date;
get isActive() {
const now = new Date();
return now >= this.startDate && (!this.endDate || now <= this.endDate);
}
formatDuration(format: "short" | "full") {
const start = this.startDate.toLocaleDateString();
if (!this.endDate) {
return format === "full"
? `Started on ${start}, ongoing`
: `From ${start}`;
}
const end = this.endDate.toLocaleDateString();
return format === "full"
? `From ${start} to ${end}`
: `${(this.endDate.getTime() - this.startDate.getTime()) / 86400000} days`;
}
}
// ...
console.log(computedProject.isActive); // false
console.log(computedProject.formatDuration("short")); // "3 days"
```
</CodeGroup>

View File

@@ -0,0 +1,107 @@
import { CodeGroup, ComingSoon, ContentByFramework } from "@/components/forMdx";
export const metadata = { title: "Creation & ownership" };
# Creation & Ownership
CoValues are collaborative by nature - anything you create can be shared and synced with others. Who gets to read or change each CoValue is controlled by its owner - either an individual `Account` or a shared `Group`. This foundation of ownership is what enables Jazz applications to support real-time collaboration while maintaining proper access control. Understanding how to create and manage ownership of CoValues is essential for building effectively with Jazz.
## Creating CoValues
Every CoValue starts with a [schema](/docs/schemas/covalues#start-your-app-with-a-schema). From there you can create CoValues with the `create` method. Creating CoValues is straightforward - you define the structure in your schema and then instantiate instances with initial values. These newly created CoValues automatically sync across devices and users who have access to them.
Here's a simple example:
<CodeGroup>
```ts
class Task extends CoMap {
title = co.string;
description = co.string;
status = co.literal("todo", "in-progress", "completed");
}
// Create a new task
const task = Task.create({
title: "Plant spring vegetables",
description: "Plant peas, carrots, and lettuce",
status: "todo",
});
```
</CodeGroup>
When you create a CoValue, you provide its initial data and optionally specify who owns it. The data must match the schema you defined - TypeScript will help ensure you get this right.
For more examples of creating different types of CoValues:
<CodeGroup>
```ts
// Creating a CoFeed for activity notifications
class ActivityNotification extends CoMap {
message = co.string;
type = co.literal("info", "warning", "success");
timestamp = co.Date;
}
class ActivityFeed extends CoFeed.Of(co.ref(ActivityNotification)) {}
const feed = ActivityFeed.create();
// Adding an item to the feed
feed.addItem(ActivityNotification.create({
message: "New task created",
type: "info",
timestamp: new Date()
}));
```
</CodeGroup>
## Ownership & Access Control
Every CoValue needs an owner to control who can access it. An owner can be an individual `Account`, but it's usually a `Group` since that lets you share with multiple people. The ownership model in Jazz provides fine-grained control over who can read, write, or administer your collaborative data. This system makes it easy to implement common patterns like shared workspaces, personal data, or public resources.
### Groups & Roles
Groups have members with different roles that control what they can do. These roles provide a permission system that's both simple to understand and powerful enough for complex collaboration scenarios. By assigning appropriate roles, you can control exactly who can view, edit, or manage access to your data.
<CodeGroup>
```ts
// Create a group
const gardenTeam = Group.create();
// Add garden members with different roles
gardenTeam.addMember(coordinator, "admin"); // Garden coordinator manages everything
gardenTeam.addMember(gardener, "writer"); // Gardeners can update tasks
gardenTeam.addMember(visitor, "reader"); // Visitors can view progress
// Create a list of tasks with the same owner
const taskList = ListOfTasks.create([]);
// Create a garden project with nested tasks, all with the same ownership
const springProject = Project.create({
name: "Spring Planting",
description: "Preparing the community garden for spring vegetables",
tasks: taskList
});
// Add tasks to the list
taskList.push(Task.create({
title: "Start tomato seedlings",
description: "Plant Roma and Cherry varieties in seed trays",
status: "todo"
});
taskList.push(Task.create({
title: "Prepare herb garden",
description: "Clear old growth and add fresh compost",
status: "todo"
});
```
</CodeGroup>
Each role has specific permissions:
- `admin`: Full control including managing members
- `writer`: Can modify content
- `reader`: Can only read content
- `writerOnly`: Can only write to the CoValue, not read it
For more information on groups and roles, see the [Groups](/docs/groups/intro) documentation.

View File

@@ -0,0 +1,269 @@
import { CodeGroup } from "@/components/forMdx";
export const metadata = { title: "Metadata & time-travel" };
# Metadata & time-travel
One of Jazz's most powerful features is that every CoValue automatically tracks its complete edit history. This means you can see who changed what and when, examine the state of your data at any point in time, and build features like audit logs, activity feeds, and undo/redo functionality. This page explores how to access and work with the rich metadata that comes with every CoValue.
## Understanding Edit History
Every CoValue in Jazz maintains a full history of all changes made to it. This edit history is accessible through two main APIs:
`CoValue._edits` provides a structured, field-by-field view of a CoValue's edit history. It organizes edits by property name and makes them easily accessible. For each field:
- `_edits.fieldName` gives you the most recent edit
- `_edits.fieldName.all` provides all historical edits as an array
- `_edits.fieldName.madeAt` gives you the timestamp of the last edit
- Each edit contains the value, who made the change, and when it happened
`CoValue._raw` gives you access to the internal state and lower-level operations on a CoValue. As this is an internal API, it should be used with caution. If you find yourself using `_raw`, consider letting us know so we can consider adding a public API for your use case.
## Working with Edit History Metadata
CoValues track who made each change and when. Every edit has metadata attached to it, including the author, timestamp, value, and transaction ID. This metadata enables you to build powerful audit and history features without having to implement your own tracking system.
<CodeGroup>
```ts
class Task extends CoMap {
title = co.string;
description = co.string;
status = co.literal("todo", "in-progress", "completed");
priority = co.literal("low", "medium", "high");
subtasks = co.optional.ref(ListOfTasks);
}
class ListOfTasks extends CoList.Of(co.ref(Task)) {}
const task = Task.create({
title: "Plant spring vegetables",
description: "Plant peas, carrots, and lettuce in the south garden bed",
status: "todo",
priority: "medium",
});
// Change the status
task.status = "in-progress";
// Get the latest edit for a field
console.log("Latest edit:", task._edits.status);
// { value: "in-progress", by: Account, madeAt: Date, ... }
// Get when a field was last edited (timestamp)
const lastEditTime = task._edits.status.madeAt;
console.log(`Status was last changed at: ${lastEditTime?.toLocaleString()}`);
// Get the full edit history for a field
for (const edit of task._edits.status.all) {
console.log({
author: edit.by, // Account that made the change
timestamp: edit.madeAt, // When the change happened
value: edit.value, // Value of the change
});
}
```
</CodeGroup>
### Common Patterns
With knowledge of the edit history, you can build all sorts of useful features that enhance your application's user experience and administrative capabilities. Here are some common patterns that leverage CoValue metadata.
#### Audit Log
Getting all the changes to a CoValue in order allows you to build an audit log. This is especially useful for tracking important changes in collaborative environments or for compliance purposes:
<CodeGroup>
```ts
function getAuditLog(task: Task) {
const changes = [];
for (const field of Object.keys(task)) {
// Check if the field has edits to avoid accessing non-existent properties
if (task._edits[field as keyof typeof task._edits]) {
for (const edit of task._edits[field as keyof typeof task._edits].all) {
changes.push({
field,
...edit,
timestamp: edit.madeAt,
at: edit.madeAt,
by: edit.by,
});
}
}
}
// Sort by timestamp
return changes.sort((a, b) => b.at.getTime() - a.at.getTime());
}
// Example usage
const auditLog = getAuditLog(task);
auditLog.forEach((entry) => {
console.log(
`${entry.timestamp} - ${entry.field} changed to "${entry.value}" by ${entry.by?.id}`,
);
});
```
</CodeGroup>
#### Activity Feeds
Activity feeds are a great way to see recent changes to a CoValue, helping users understand what's happening in a collaborative workspace. They can show who did what and when, creating transparency in team environments:
<CodeGroup>
```ts
function getRecentActivity(project: Project) {
const activity = [];
const hourAgo = new Date(Date.now() - 3600000);
for (const field of Object.keys(project)) {
// Skip if the field doesn't have edits
if (!project._edits[field as keyof typeof project._edits]) continue;
for (const edit of project._edits[field as keyof typeof project._edits].all) {
if (edit.madeAt > hourAgo) {
activity.push({
field,
value: edit.value,
by: edit.by,
at: edit.madeAt
});
}
}
}
return activity.sort((a, b) => b.at.getTime() - a.at.getTime());
}
// Example usage
const recentActivity = getRecentActivity(gardenProject);
console.log("Recent Garden Activity:");
recentActivity.forEach(activity => {
console.log(`${activity.at.toLocaleString()} - ${activity.field} updated by ${activity.by?.id}`);
});
```
</CodeGroup>
## Edit History & Time Travel
CoValues track their entire history of changes, creating a timeline you can explore. You can see who changed what and when, or even view past states of the data. This capability enables powerful debugging tools and user-facing features like history browsing and restoration of previous versions:
<CodeGroup>
```ts
class Task extends CoMap {
title = co.string;
description = co.string;
status = co.literal("todo", "in-progress", "completed");
priority = co.literal("low", "medium", "high");
}
// Create a new task
const task = Task.create({
title: "Plant spring vegetables",
description: "Plant peas, carrots, and lettuce in the south garden bed",
status: "todo",
priority: "medium",
});
// Make some changes
task.status = "in-progress";
task.priority = "high";
// See all edits for a field
for (const edit of task._edits.status.all) {
console.log(
`${edit.madeAt.toISOString()}: Status changed to "${edit.value}" by ${edit.by?.id}`,
);
}
// Get the initial value
const initialStatus = task._edits.status.all[0]?.value;
console.log(`Original status: ${initialStatus}`);
// Get a specific edit by index
const previousEdit = task._edits.status.all[1]; // Second edit
console.log(`Previous status: ${previousEdit?.value}`);
// Check who made the most recent change
const latestEdit = task._edits.status;
console.log(`Latest change made by: ${latestEdit?.by?.id}`);
```
</CodeGroup>
## Time Travel
The ability to view a CoValue as it existed at any point in time is one of Jazz's most powerful features. Looking into the past can help you understand how things changed - perfect for audit logs, debugging, or showing user activity. You can reconstruct the exact state of any CoValue at any moment in its history:
<CodeGroup>
```ts
class Project extends CoMap {
name = co.string;
status = co.literal("planning", "active", "completed");
lastUpdate = co.Date;
}
// See when a project was started
function findStatusChange(project: Project, targetStatus: string) {
// Get all the edits for the status field
const statusEdits = project._edits.status.all;
for (const edit of statusEdits) {
if (edit.value === targetStatus) {
console.log({
changeTime: edit.madeAt,
lastUpdate: project.lastUpdate,
changedBy: edit.by,
});
}
}
}
// Example usage
findStatusChange(gardenProject, "active");
```
</CodeGroup>
### Common Use Cases
The time travel capabilities of CoValues enable several practical use cases that would otherwise require complex custom implementations. Here are some examples of how you can use time travel in your applications:
<CodeGroup>
```ts
// Track task progress over time
function getTaskStatusHistory(task: Task, days: number = 7) {
const statusHistory = [];
const dayInMs = 86400000;
// Check every day for the past week
for (let day = 0; day < days; day++) {
const timePoint = new Date(Date.now() - day * dayInMs);
// Using the internal _raw API to get state at a specific point in time
const state = task._raw.atTime(timePoint);
statusHistory.push({
date: timePoint.toLocaleDateString(),
status: state.status,
priority: state.priority
});
}
return statusHistory;
}
// Example usage
const history = getTaskStatusHistory(plantingTask);
history.forEach(entry => {
console.log(`${entry.date}: Status was "${entry.status}" with ${entry.priority} priority`);
});
```
</CodeGroup>
### Best Practices
- Check field existence before accessing edits (`if (task._edits.fieldName)`)
- Access the most recent edit directly with `_edits.fieldName` instead of using any `.latest` property
- Cache historical queries if you're displaying them in UI
- Be specific about time ranges you care about
- Remember that accessing history requires loading the CoValue
- Consider using timestamps from your data rather than scanning all edits
Time travel is great for understanding how you got here, but keep queries focused on the range of time that matters to your use case.

View File

@@ -0,0 +1,272 @@
import { CodeGroup, ComingSoon, ContentByFramework } from "@/components/forMdx";
export const metadata = { title: "Reading from CoValues" };
# Reading from CoValues
Jazz lets you access your collaborative data with familiar JavaScript patterns while providing TypeScript type safety. Once you have a CoValue, you can read its values, traverse references, and iterate through collections using the same syntax you'd use with regular objects and arrays. This page covers how to read from different types of CoValues and handle loading states effectively.
<CodeGroup>
```ts
class Project extends CoMap {
name = co.string;
tasks = co.ref(ListOfTasks);
lead = co.optional.ref(TeamMember);
status = co.literal("planning", "active", "completed");
}
// Reading basic fields
console.log(project.name); // "Spring Garden Planning"
console.log(project.status); // "active"
// Reading from lists
for (const task of project.tasks) {
console.log(task.title); // "Plant tomato seedlings"
}
// Checking if an optional field exists
if (project.lead) {
console.log(project.lead.name); // "Maria Chen"
}
```
</CodeGroup>
## Different Types of CoValues
Jazz provides several CoValue types to represent different kinds of data. Each type has its own access patterns, but they all maintain the familiar JavaScript syntax you already know.
### CoMaps
`CoMap`s work like JavaScript objects, providing named properties you can access with dot notation. These are the most common CoValue type and form the foundation of most Jazz data models:
<CodeGroup>
```ts
class TeamMember extends CoMap {
name = co.string;
role = co.string;
active = co.boolean;
}
console.log(member.name); // "Maria Chen"
console.log(member.role); // "Garden Coordinator"
console.log(member.active); // true
```
</CodeGroup>
### CoLists
`CoList`s work like JavaScript arrays, supporting indexed access, iteration methods, and length properties. They're perfect for ordered collections of items where the order matters:
<CodeGroup>
```ts
class ListOfTasks extends CoList.Of(co.ref(Task)) {}
// Access items by index
console.log(tasks[0].title); // "Plant tomato seedlings"
// Use array methods
tasks.forEach(task => {
console.log(task.title); // "Plant tomato seedlings"
});
// Get list length
console.log(tasks.length); // 3
```
</CodeGroup>
### CoFeeds
`CoFeed`s provide a specialized way to track data from different sessions (tabs, devices, app instances). They're ideal for activity logs, presence indicators, or other session-specific streams of information. Each account can have multiple sessions, and each session maintains its own append-only log.
## Type Safety with CoValues
CoValues are fully typed in TypeScript, giving you the same autocomplete and error checking you'd expect from regular objects. This type safety helps catch errors at compile time rather than runtime, making your application more robust. Here's how the type system works with CoValues:
<CodeGroup>
```ts
class Project extends CoMap {
name = co.string;
memberCount = co.number;
priority = co.literal("low", "medium", "high");
lead = co.optional.ref(TeamMember);
tasks = co.ref(ListOfTasks);
}
// TypeScript knows exactly what fields exist
const project = await Project.load(gardenProjectId);
project.name = "Community Garden"; // ✓ string
project.memberCount = "few"; // ✗ Type error: expected number
project.priority = "urgent"; // ✗ Type error: must be low/medium/high
// Optional fields are handled safely
if (project.lead) {
console.log(project.lead.name); // Type safe
}
// Lists with specific item types
project.tasks.forEach(task => {
// TypeScript knows each task's structure
console.log(`${task.title}: ${task.status}`); // "Plant herbs: in-progress"
});
```
</CodeGroup>
## Loading States
When you load a CoValue, it might not be immediately available due to network latency or data size. Jazz provides patterns to handle these loading states gracefully, and TypeScript helps ensure you check for availability before accessing properties:
<CodeGroup>
```ts
const project = await Project.load(gardenProjectId);
if (!project) {
return "Data still loading";
}
```
</CodeGroup>
<ContentByFramework framework="react">
And in React, `useCoState` provides a similar pattern to allow you to wait for a CoValue to be loaded before accessing it:
<CodeGroup>
```tsx
// Type shows this might be `undefined` while loading
const project = useCoState(Project, gardenProjectId, {
tasks: [{}]
});
if (!project) {
return <div>Loading project data...</div>;
}
// TypeScript now knows project exists and has tasks loaded
return <div>{project.tasks.length}</div>;
```
</CodeGroup>
</ContentByFramework>
### Accessing Nested CoValues
Nested CoValues need special handling for loading and access. Since each reference might need to be loaded separately, you need patterns to manage these dependencies and handle loading states appropriately throughout your object graph.
<CodeGroup>
```ts
class Project extends CoMap {
name = co.string;
tasks = co.ref(ListOfTasks);
resources = co.optional.ref(ResourceList);
}
class Task extends CoMap {
title = co.string;
status = co.literal("todo", "in-progress", "completed");
subtasks = co.ref(ListOfSubtasks);
}
```
</CodeGroup>
### Loading
Loading nested data efficiently is important for performance. Jazz provides depth specifications to control exactly how much of your object graph is loaded, from shallow loading of just the top-level object to deep loading of complex nested structures:
<CodeGroup>
```ts
// Basic load - tasks won't be loaded yet
const project = await Project.load(gardenProjectId);
// Load with nested data
const projectWithTasks = await Project.load(gardenProjectId, { tasks: {} });
// Deep load pattern
const fullyLoaded = await Project.load(gardenProjectId, {
tasks: {
subtasks: {}
}
});
```
</CodeGroup>
More details on loading and subscribing to CoValues can be found in [Subscribing](/docs/using-covalues/subscribing-and-deep-loading).
### Handling Loading States
Unloaded references return `undefined`. This means you need to check for undefined values before trying to access properties of nested CoValues.
For general JavaScript/TypeScript usage, here's a pattern that works across any context:
<CodeGroup>
```ts
// Generic pattern for handling nested data
function processTaskData(project) {
// Check if project and its tasks are loaded
if (!project || !project.tasks) {
return "Data still loading";
}
// Safe to process tasks
const completedTasks = project.tasks.filter(task =>
task && task.status === "completed"
);
// Check for subtasks before accessing them
const subtaskCount = completedTasks.reduce((count, task) => {
if (!(task && task.subtasks)) return count
return count + task.subtasks.length;
}, 0);
return {
completedCount: completedTasks.length,
subtaskCount: subtaskCount
};
}
```
</CodeGroup>
<ContentByFramework framework="react">
Handle these loading states in your components:
<CodeGroup>
```tsx
// React pattern for handling nested data
function TaskList({ project }: { project: Project }) {
if (!project.tasks) {
return <div>Loading tasks...</div>;
}
return (
<div>
{project.tasks.map(task => {
// Handle potentially missing nested data
if (!task.subtasks) {
return <div key={task.id}>Loading subtasks...</div>;
}
return (
<div key={task.id}>
{task.title}: {task.subtasks.length} subtasks
</div>
);
})}
</div>
);
}
```
</CodeGroup>
</ContentByFramework>
Note: We're working on making these patterns more explicit and robust. We'll provide clearer loading states and better error handling patterns. For now, be defensive with your checks for `undefined`.
<CodeGroup>
```ts
// Current safest pattern for deep access
function getSubtasks(project: Project, taskTitle: string) {
const task = project.tasks?.find(t => t.title === taskTitle);
const subtasks = task?.subtasks;
if (!subtasks) {
return null; // Could mean loading or error
}
return subtasks.map(st => st.title);
}
```
</CodeGroup>
Stay tuned for updates to this API - we're working on making these patterns more robust and explicit.

View File

@@ -0,0 +1,340 @@
import { CodeGroup, ContentByFramework } from "@/components/forMdx";
export const metadata = { title: "Subscriptions & Deep Loading" };
# Subscriptions & Deep Loading
When working with collaborative applications, you need to know when data changes and ensure you have all the necessary related data. Jazz provides powerful subscription and deep loading capabilities that make it easy to keep your UI in sync with the underlying data and efficiently load complex object graphs.
## Understanding Subscriptions
Subscriptions in Jazz allow you to react to changes in CoValues. When a CoValue changes, all subscribers are notified with the updated value. This is essential for building reactive UIs that stay in sync with collaborative data.
<CodeGroup>
```ts
class Task extends CoMap {
title = co.string;
description = co.string;
status = co.literal("todo", "in-progress", "completed");
assignedTo = co.optional.string;
}
// ...
// Subscribe to a Task by ID
const unsubscribe = Task.subscribe(taskId, { /* loading depth */ }, (updatedTask) => {
console.log("Task updated:", updatedTask.title);
console.log("New status:", updatedTask.status);
});
// Later, when you're done:
unsubscribe();
```
</CodeGroup>
### Static vs. Instance Subscriptions
There are two main ways to subscribe to CoValues:
1. **Static Subscription** - When you have an ID but don't have the CoValue loaded yet:
<CodeGroup>
```ts
// Subscribe by ID (static method)
const unsubscribe = Task.subscribe(taskId, { /* loading depth */ }, (task) => {
if (task) {
console.log("Task loaded/updated:", task.title);
}
});
```
</CodeGroup>
2. **Instance Subscription** - When you already have a CoValue instance:
<CodeGroup>
```ts
// Subscribe to an instance (instance method)
const task = Task.create({
status: "todo",
title: "Cut the grass",
});
if (task) {
const unsubscribe = task.subscribe({ /* loading depth */ }, (updatedTask) => {
console.log("Task updated:", updatedTask.title);
});
}
```
</CodeGroup>
## Deep Loading
When working with related CoValues (like tasks in a project), you often need to load not just the top-level object but also its nested references. Jazz provides a flexible mechanism for specifying exactly how much of the object graph to load.
### Loading Depth Specifications
Loading depth specifications let you declare exactly which references to load and how deep to go:
<CodeGroup>
```ts
class Project extends CoMap {
name = co.string;
tasks = co.ref(ListOfTasks);
owner = co.ref(TeamMember);
}
class Task extends CoMap {
title = co.string;
subtasks = co.ref(ListOfSubtasks);
assignee = co.optional.ref(TeamMember);
}
// Load just the project, not its tasks
const project = await Project.load(projectId, {});
// Load the project and its tasks (but not subtasks)
const projectWithTasks = await Project.load(projectId, {
tasks: {}
});
// Load the project, its tasks, and their subtasks
const projectDeep = await Project.load(projectId, {
tasks: {
subtasks: {}
}
});
// Load the project, its tasks, and task assignees
const projectWithAssignees = await Project.load(projectId, {
tasks: {
assignee: {}
}
});
// Complex loading pattern: load project, tasks with their subtasks, and the project owner
const fullyLoaded = await Project.load(projectId, {
tasks: {
subtasks: {}
},
owner: {}
});
```
</CodeGroup>
The depth specification object mirrors the structure of your data model, making it intuitive to express which parts of the graph you want to load.
### Array Notation for Lists
For lists, you can use array notation to specify how to load the items:
<CodeGroup>
```ts
// Load project with all tasks but load each task shallowly
const project = await Project.load(projectId, {
tasks: [{}]
});
// Load project with tasks and load subtasks for each task
const project = await Project.load(projectId, {
tasks: [{
subtasks: [{}]
}]
});
```
</CodeGroup>
## Framework Integration
<ContentByFramework framework="react">
### React Integration with useCoState
In React applications, the `useCoState` hook provides a convenient way to subscribe to CoValues and handle loading states:
<CodeGroup>
```tsx
function GardenPlanner({ projectId }: { projectId: ID<Project> }) {
// Subscribe to a project and its tasks
const project = useCoState(Project, projectId, {
tasks: [{}]
});
// Handle loading state
if (!project) {
return <div>Loading garden project...</div>;
}
return (
<div>
<h1>{project.name}</h1>
<TaskList tasks={project.tasks} />
</div>
);
}
function TaskList({ tasks }: { tasks: Task[] }) {
return (
<ul>
{tasks.map(task => (
<li key={task.id}>
<span>{task.title}</span>
<span>{task.status}</span>
</li>
))}
</ul>
);
}
```
</CodeGroup>
The `useCoState` hook handles subscribing when the component mounts and unsubscribing when it unmounts, making it easy to keep your UI in sync with the underlying data.
</ContentByFramework>
<ContentByFramework framework="vue">
### Vue Integration
In Vue applications, you can use the `useCoState` composable to subscribe to CoValues:
<CodeGroup>
```vue
<script setup>
import { useCoState } from 'jazz-vue';
const props = defineProps({
projectId: String
});
// Subscribe to a project and its tasks
const project = useCoState(Project, props.projectId, {
tasks: [{}]
});
</script>
<template>
<div v-if="project">
<h1>{{ project.name }}</h1>
<ul>
<li v-for="task in project.tasks" :key="task.id">
{{ task.title }} - {{ task.status }}
</li>
</ul>
</div>
<div v-else>
Loading garden project...
</div>
</template>
```
</CodeGroup>
</ContentByFramework>
<ContentByFramework framework="svelte">
### Svelte Integration
In Svelte applications, you can use the `useCoState` function to subscribe to CoValues:
<CodeGroup>
```svelte
<script>
import { useCoState } from 'jazz-svelte';
export let projectId;
// Subscribe to a project and its tasks
const project = useCoState(Project, projectId, {
tasks: [{}]
});
</script>
{#if $project}
<h1>{$project.name}</h1>
<ul>
{#each $project.tasks as task (task.id)}
<li>{task.title} - {task.status}</li>
{/each}
</ul>
{:else}
<div>Loading garden project...</div>
{/if}
```
</CodeGroup>
</ContentByFramework>
## Ensuring Data is Loaded
Sometimes you need to make sure data is loaded before proceeding with an operation. The `ensureLoaded` method lets you guarantee that a CoValue and its referenced data are loaded to a specific depth:
<CodeGroup>
```ts
async function completeAllTasks(projectId: ID<Project>) {
// Ensure the project and its tasks are loaded
const project = await Project.load(projectId, {});
if (!project) return;
const loadedProject = await project.ensureLoaded({
tasks: [{}]
});
// Now we can safely access and modify tasks
loadedProject.tasks.forEach(task => {
task.status = "completed";
});
}
```
</CodeGroup>
## Performance Considerations
Loading depth is directly related to performance. Loading too much data can slow down your application, while loading too little can lead to "undefined" references. Here are some guidelines:
- **Load only what you need** for the current view or operation
- **Preload data** that will be needed soon to improve perceived performance
- Use **caching** to avoid reloading data that hasn't changed
{/* TODO: Add a note about supporting pagination */}
<CodeGroup>
```ts
// Bad: Loading everything deeply
const project = await Project.load(projectId, {
tasks: [{
subtasks: [{
comments: [{}]
}]
}],
members: [{}],
resources: [{}]
});
// Better: Loading only what's needed for the current view
const project = await Project.load(projectId, {
tasks: [{}] // Just load the tasks shallowly
});
// Later, when a task is selected:
const task = await Task.load(selectedTaskId, {
subtasks: [{}] // Now load its subtasks
});
```
</CodeGroup>
## Using a Loading Cache
By default, Jazz maintains a cache of loaded CoValues to avoid unnecessary network requests. This means that if you've already loaded a CoValue, subsequent load requests will use the cached version unless you explicitly request a refresh.
<CodeGroup>
```ts
// First load: fetches from network or local storage
const project = await Project.load(projectId, {});
// Later loads: uses cached version if available
const sameProject = await Project.load(projectId, {});
```
</CodeGroup>
## Best Practices
1. **Be explicit about loading depths**: Always specify exactly what you need
2. **Clean up subscriptions**: Always store and call the unsubscribe function when you're done
3. **Handle loading states**: Check for undefined/null before accessing properties
4. **Use framework integrations**: They handle subscription lifecycle automatically
5. **Balance depth and performance**: Load only what you need for the current view
By effectively using subscriptions and deep loading, you can build responsive, collaborative applications that handle complex data relationships while maintaining good performance.

View File

@@ -0,0 +1,175 @@
export const metadata = { title: "Writing & deleting CoValues" };
import { CodeGroup } from "@/components/forMdx";
# Writing & deleting CoValues
Collaborative applications need ways to update and remove data. Jazz makes this simple by treating CoValues like regular JavaScript objects while handling all the complexity of syncing changes in the background. This page covers how to modify CoValues, work with collections, handle concurrent edits, and properly remove data when needed.
## Writing to CoValues
Once you have a CoValue, modifying it is straightforward. You can update fields like regular JavaScript properties. Changes are applied locally first for immediate feedback, then synchronized to other users with access to the same CoValues. This approach provides a natural programming model while handling all the distributed systems complexity behind the scenes.
<CodeGroup>
```ts
class Task extends CoMap {
title = co.string;
status = co.literal("todo", "in-progress", "completed");
assignee = co.optional.string;
}
//...
// Update fields
task.status = "in-progress"; // Direct assignment
task.assignee = "Alex"; // Optional field
```
</CodeGroup>
### Working with Lists
CoLists support familiar array operations, making it easy to work with collections of data. You can add, remove, and modify items using the standard JavaScript array methods, while Jazz handles the collaborative aspects automatically. These operations work correctly even when multiple users are making changes simultaneously.
<CodeGroup>
```ts
class Project extends CoMap {
name = co.string;
tasks = co.ref(ListOfTasks);
}
//...
// Add items
project.tasks.push(Task.create({
title: "Build raised beds",
status: "todo"
}));
// Remove items
project.tasks.splice(2, 1); // Remove third task
// Update items
project.tasks[0].status = "in-progress";
// Bulk updates
project.tasks.forEach(task => {
if (task.status === "todo") {
task.status = "in-progress";
}
});
```
</CodeGroup>
Changes sync automatically to everyone with access. Any edits you make are immediately visible in your local view and propagate to other users as they sync.
## Concurrent Edits
CoValues use [CRDTs](/docs/schemas/covalues#defining-schemas-covalues) to handle concurrent edits smoothly. In most cases, you don't need to think about conflicts - Jazz handles them automatically. This conflict resolution happens transparently, allowing multiple users to make changes simultaneously without disruption or data loss.
<CodeGroup>
```ts
class Dashboard extends CoMap {
activeProjects = co.number;
status = co.literal("active", "maintenance");
notifications = co.ref(ListOfNotifications);
}
//...
// Multiple users can edit simultaneously
// Last-write-wins for simple fields
dashboard.status = "maintenance"; // Local change is immediate
dashboard.activeProjects = 5; // Syncs automatically
// Lists handle concurrent edits too
dashboard.notifications.push(Notification.create({
timestamp: new Date(),
message: "System update scheduled"
}));
```
</CodeGroup>
## Deleting CoValues
There are a few ways to delete CoValues, from simple field removal to full cleanup. Jazz provides flexible options for removing data depending on your needs. You can remove references while keeping the underlying data, remove items from lists, or completely delete CoValues when they're no longer needed.
<CodeGroup>
```ts
class Project extends CoMap {
tasks = co.ref(ListOfTasks);
resources = co.optional.ref(ListOfResources);
}
//...
// Remove a reference
project.resources = null; // Removes the reference but resources still exist
// Remove from a list
project.tasks.splice(2, 1); // Removes third team member from list
```
</CodeGroup>
### Best Practices
- Load everything you plan to delete
- Check permissions before attempting deletes
- Consider soft deletes for recoverable data
## Removing Data in CoValues
You can delete fields from any `CoMap` to remove specific properties while keeping the CoValue itself. This is useful when you need to clear certain data without affecting the rest of your object structure. The deletion operations are also synchronized to all users with access.
<CodeGroup>
```ts
class Project extends CoMap {
name = co.string;
team = co.ref(ListOfMembers);
budget = co.optional.ref(Budget);
}
//...
// Delete fields from a regular CoMap
delete project.budget; // Removes the budget reference
// Delete from a record-type CoMap
class ProjectTags extends CoMap.Record(co.string) {}
const projectTags = ProjectTags.create({
"priority-high": "High priority tasks",
});
delete projectTags["priority-high"]; // Removes specific tag
```
</CodeGroup>
For `CoList`s, use array methods:
<CodeGroup>
```ts
// Remove from lists using splice
project.team.splice(2, 1); // Removes third team member
```
</CodeGroup>
### Restoring Data
For data you might want to restore later, consider using status fields instead of permanent deletion. This "soft delete" pattern is common in applications where users might need to recover previously removed items. By using a boolean field to mark items as archived or deleted, you maintain the ability to restore them later.
<CodeGroup>
```ts
class Task extends CoMap {
title = co.string;
archived = co.boolean;
}
// Mark as archived
task.archived = true;
// Restore later
task.archived = false; // Task is back in the active list!
```
</CodeGroup>
Removed data remains in the edit history. If you need to handle sensitive information, plan your data model accordingly.

View File

@@ -1,25 +1,27 @@
import { Pricing } from "@/components/Pricing";
import { LatencyMap } from "@/components/cloud/latencyMap";
import { GridCard } from "gcmp-design-system/src/app/components/atoms/GridCard";
import {
H2,
H3,
H4,
} from "gcmp-design-system/src/app/components/atoms/Headings";
import { LI } from "gcmp-design-system/src/app/components/atoms/ListItem";
import { H2, H3 } from "gcmp-design-system/src/app/components/atoms/Headings";
import { P } from "gcmp-design-system/src/app/components/atoms/Paragraph";
import { GappedGrid } from "gcmp-design-system/src/app/components/molecules/GappedGrid";
import { HeroHeader } from "gcmp-design-system/src/app/components/molecules/HeroHeader";
import { UL } from "gcmp-design-system/src/app/components/molecules/List";
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
import { SectionHeader } from "gcmp-design-system/src/app/components/molecules/SectionHeader";
import type { Metadata } from "next";
import CloudPlusBackup from "./cloudPlusBackup.mdx";
import CloudPlusDIY from "./cloudPlusDIY.mdx";
import CompletelyDIY from "./completelyDIY.mdx";
export const metadata = {
title: "Jazz Cloud",
description: "Serverless sync & storage for Jazz apps.",
const title = "Jazz Cloud";
const description = "Serverless sync & storage for Jazz apps.";
export const metadata: Metadata = {
title,
description,
openGraph: {
title,
description,
},
};
export default function Cloud() {

View File

@@ -11,6 +11,20 @@ import { H2 } from "gcmp-design-system/src/app/components/atoms/Headings";
import { Icon } from "gcmp-design-system/src/app/components/atoms/Icon";
import { GappedGrid } from "gcmp-design-system/src/app/components/molecules/GappedGrid";
import { HeroHeader } from "gcmp-design-system/src/app/components/molecules/HeroHeader";
import type { Metadata } from "next";
const title = "Examples";
const description =
"Find an example app with code most similar to what you want to build.";
export const metadata: Metadata = {
title,
description,
openGraph: {
title,
description,
},
};
const MockButton = ({ children }: { children: React.ReactNode }) => (
<p className="bg-blue-100 text-blue-800 py-1 px-3 rounded-full font-medium text-xs inline-flex items-center justify-center">

View File

@@ -1,11 +1,19 @@
import { products } from "@/lib/showcase";
import { HeroHeader } from "gcmp-design-system/src/app/components/molecules/HeroHeader";
import type { Metadata } from "next";
import Image from "next/image";
import Link from "next/link";
export const metadata = {
title: "Built with Jazz",
description: "Great apps by smart people.",
const title = "Built with Jazz";
const description = "Great apps by smart people.";
export const metadata: Metadata = {
title,
description,
openGraph: {
title,
description,
},
};
export default function Page() {

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