Compare commits

..

1 Commits

Author SHA1 Message Date
NicoR
a272c67a1d [TEST] Add RNQuickCrypto provider to chat-rn-expo example 2025-07-01 12:04:20 -03:00
690 changed files with 15619 additions and 50899 deletions

View File

@@ -6,6 +6,7 @@
"fixed": [
[
"cojson",
"cojson-storage",
"cojson-storage-indexeddb",
"cojson-storage-sqlite",
"cojson-transport-ws",
@@ -14,8 +15,7 @@
"jazz-betterauth-server-plugin",
"jazz-react-auth-betterauth",
"jazz-run",
"jazz-tools",
"community-jazz-vue"
"jazz-tools"
]
],
"access": "public",

View File

@@ -1,23 +1,24 @@
# Description
<!-- Please include a summary of the change and which issue is fixed -->
<!-- Please also include relevant motivation and context -->
<!-- Include any links to documentation like RFCs if necessary -->
<!-- Add a link to to relevant preview environments or anything that would simplify visual review process -->
<!-- Supplemental screenshots and video are encouraged, but the primary description should be in text -->
### What this Does
Brief summary of the change, ideally framed in user or product terms.
## Manual testing instructions
### Why Are We Doing This?
Link to the shaped pitch or explain what problem it solves.
<!-- Add any actions required to manually test the changes -->
### Scope / Boundaries
Includes:
- [x] Core feature functionality
- [x] Tests or validation steps
## Tests
Do NOT include:
- [ ] Related stretch features or follow-ups
- [ ] Tests have been added and/or updated
- [ ] Tests have not been updated, because: <!-- Insert reason for not updating tests here -->
- [ ] I need help with writing tests
### Testing Instructions
How a reviewer or QA can verify behavior, offer step-by-step instructions if possible. Screenshots or recordings are welcome.
### Known Issues / Open Questions (if any)
- [ ] Note anything youd like review on or decided async
## Checklist
- [ ] I've updated the part of the docs that are affected the PR changes
- [ ] I've generated a changeset, if a version bump is required
- [ ] I've updated the jsDoc comments to the public APIs I've modified, or added them when missing
### Related Links
- GitHub issue
- Linear pitch
- Design links or references

View File

@@ -1,11 +1,5 @@
name: Code quality
concurrency:
# For pushes, this lets concurrent runs happen, so each push gets a result.
# But for other events (e.g. PRs), we can cancel the previous runs.
group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.sha || github.ref }}
cancel-in-progress: true
on:
push:
branches:
@@ -22,6 +16,9 @@ jobs:
- name: Setup Biome
uses: biomejs/setup-biome@v2
with:
version: 2.1.3
version: 1.9.4
- name: Run Biome
run: biome ci .
- name: Check Catalog Dependencies
run: node scripts/check-catalog-deps.js

View File

@@ -1,11 +1,5 @@
name: End-to-End Tests for React Native
concurrency:
# For pushes, this lets concurrent runs happen, so each push gets a result.
# But for other events (e.g. PRs), we can cancel the previous runs.
group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.sha || github.ref }}
cancel-in-progress: true
on:
pull_request:
types: [opened, synchronize, reopened]

View File

@@ -1,11 +1,5 @@
name: Jazz Run Tests
concurrency:
# For pushes, this lets concurrent runs happen, so each push gets a result.
# But for other events (e.g. PRs), we can cancel the previous runs.
group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.sha || github.ref }}
cancel-in-progress: true
on:
push:
branches: ["main"]

View File

@@ -0,0 +1,46 @@
name: Playwright Tests
on:
push:
branches: ["main"]
pull_request:
types: [opened, synchronize, reopened]
jobs:
test:
timeout-minutes: 60
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Setup Source Code
uses: ./.github/actions/source-code/
- name: Install root dependencies
run: pnpm install && pnpm exec turbo build --filter="./packages/*"
- name: Install project dependencies
run: pnpm install
working-directory: ./homepage/homepage
- name: Pnpm Build
run: pnpm exec turbo build
working-directory: ./homepage/homepage
- name: Install Playwright Browsers
run: pnpm exec playwright install
working-directory: ./homepage/homepage
- name: Run Playwright tests
run: pnpm exec playwright test
working-directory: ./homepage/homepage
- uses: actions/upload-artifact@v4
if: failure()
with:
name: homepage-playwright-report
path: ./homepage/homepage/playwright-report/
retention-days: 30

View File

@@ -1,11 +1,5 @@
name: Playwright Tests
concurrency:
# For pushes, this lets concurrent runs happen, so each push gets a result.
# But for other events (e.g. PRs), we can cancel the previous runs.
group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.sha || github.ref }}
cancel-in-progress: true
on:
push:
branches: ["main"]
@@ -19,7 +13,21 @@ jobs:
continue-on-error: true
strategy:
matrix:
shard: ["1/2", "2/2"]
project: [
"tests/e2e",
"examples/chat",
"examples/chat-svelte",
"examples/clerk",
"examples/betterauth",
"examples/file-share-svelte",
"examples/form",
"examples/inspector",
"examples/music-player",
"examples/organization",
"starters/react-passkey-auth",
"starters/svelte-passkey-auth",
"tests/jazz-svelte"
]
steps:
- uses: actions/checkout@v4
@@ -29,131 +37,25 @@ jobs:
- name: Setup Source Code
uses: ./.github/actions/source-code/
- name: Pnpm Build
run: |
if [ -f .env.test ]; then
cp .env.test .env
fi
pnpm turbo build
working-directory: ./${{ matrix.project }}
- name: Install Playwright Browsers
run: pnpm exec playwright install
working-directory: ./${{ matrix.project }}
- name: Run Playwright tests for shard ${{ matrix.shard }}
run: |
# Parse shard information (e.g., "1/2" -> shard_num=1, total_shards=2)
IFS='/' read -r shard_num total_shards <<< "${{ matrix.shard }}"
shard_index=$((shard_num - 1)) # Convert to 0-based index
# Debug: Print parsed values
echo "Parsed shard_num: $shard_num"
echo "Parsed total_shards: $total_shards"
echo "Calculated shard_index: $shard_index"
# Define all projects to test
all_projects=(
"tests/e2e"
"examples/chat"
"examples/chat-svelte"
"examples/community-clerk-vue"
"examples/clerk"
"examples/betterauth"
"examples/file-share-svelte"
"examples/form"
"examples/inspector"
"examples/music-player"
"examples/organization"
"examples/server-worker-http"
"starters/react-passkey-auth"
"starters/svelte-passkey-auth"
"tests/jazz-svelte"
)
# Calculate which projects this shard should run
shard_projects=()
for i in "${!all_projects[@]}"; do
if [ $((i % total_shards)) -eq $shard_index ]; then
shard_projects+=("${all_projects[i]}")
fi
done
# Track project results
overall_exit_code=0
failed_projects=()
passed_projects=()
echo "=== Running tests for shard ${{ matrix.shard }} ==="
echo "Projects in this shard:"
printf '%s\n' "${shard_projects[@]}"
echo
# Run tests for each project
for project in "${shard_projects[@]}"; do
echo "=== Testing project: $project ==="
# Check if project directory exists
if [ ! -d "$project" ]; then
echo "❌ FAILED: Project directory $project does not exist"
failed_projects+=("$project (directory not found)")
overall_exit_code=1
continue
fi
# Check if project has package.json
if [ ! -f "$project/package.json" ]; then
echo "❌ FAILED: No package.json found in $project"
failed_projects+=("$project (no package.json)")
overall_exit_code=1
continue
fi
# Build the project
echo "🔨 Building $project..."
cd "$project"
if [ -f .env.test ]; then
cp .env.test .env
fi
if ! pnpm turbo build; then
echo "❌ BUILD FAILED: $project"
failed_projects+=("$project (build failed)")
overall_exit_code=1
cd - > /dev/null
continue
fi
# Run Playwright tests
echo "🧪 Running Playwright tests for $project..."
if ! pnpm exec playwright test; then
echo "❌ TESTS FAILED: $project"
failed_projects+=("$project (tests failed)")
overall_exit_code=1
else
echo "✅ TESTS PASSED: $project"
passed_projects+=("$project")
fi
cd - > /dev/null
echo "=== Finished testing $project ==="
echo
done
# Print summary report
echo "=========================================="
echo "📊 TEST SUMMARY FOR SHARD ${{ matrix.shard }}"
echo "=========================================="
if [ ${#passed_projects[@]} -gt 0 ]; then
echo "✅ PASSED (${#passed_projects[@]}):"
printf ' - %s\n' "${passed_projects[@]}"
echo
fi
if [ ${#failed_projects[@]} -gt 0 ]; then
echo "❌ FAILED (${#failed_projects[@]}):"
printf ' - %s\n' "${failed_projects[@]}"
echo
fi
echo "Total projects in shard: ${#shard_projects[@]}"
echo "Passed: ${#passed_projects[@]}"
echo "Failed: ${#failed_projects[@]}"
echo "=========================================="
# Exit with overall status
exit $overall_exit_code
- name: Run Playwright tests
run: pnpm exec playwright test
working-directory: ./${{ matrix.project }}
- uses: actions/upload-artifact@v4
if: failure()
with:
name: ${{ hashFiles(format('{0}/package.json', matrix.project)) }}-playwright-report
path: ./${{ matrix.project }}/playwright-report/
retention-days: 30

View File

@@ -1,11 +1,4 @@
name: Pre-Publish tagged Pull Requests
concurrency:
# For pushes, this lets concurrent runs happen, so each push gets a result.
# But for other events (e.g. PRs), we can cancel the previous runs.
group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.sha || github.ref }}
cancel-in-progress: true
on:
pull_request:
types: [opened, synchronize, reopened, labeled]

View File

@@ -1,11 +1,5 @@
name: Unit Tests
concurrency:
# For pushes, this lets concurrent runs happen, so each push gets a result.
# But for other events (e.g. PRs), we can cancel the previous runs.
group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.sha || github.ref }}
cancel-in-progress: true
on:
pull_request:
types: [opened, synchronize, reopened]

View File

@@ -1,5 +1,5 @@
{
"$schema": "https://biomejs.dev/schemas/2.1.3/schema.json",
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"vcs": {
"enabled": true,
"clientKind": "git",
@@ -7,35 +7,39 @@
},
"files": {
"ignoreUnknown": false,
"includes": [
"**",
"!**/jazz-tools.json",
"!**/ios/**",
"!**/android/**",
"!**/tests/jazz-svelte/src/**",
"!**/examples/**/*svelte*/**",
"!**/starters/**/*svelte*/**",
"!**/examples/server-worker-inbox/src/routeTree.gen.ts",
"!**/homepage/homepage/**",
"!**/package.json",
"!**/*svelte*/**"
"ignore": [
"jazz-tools.json",
"**/ios/**",
"**/android/**",
"tests/jazz-svelte/src/**",
"examples/*svelte*/**",
"starters/*svelte*/**",
"examples/jazz-paper-scissors/src/routeTree.gen.ts",
"homepage/homepage/**",
"**/package.json"
]
},
"formatter": {
"enabled": true,
"indentStyle": "space"
},
"assist": { "actions": { "source": { "organizeImports": "off" } } },
"organizeImports": {
"enabled": true
},
"linter": {
"enabled": false,
"rules": {
"recommended": true,
"correctness": {
"useExhaustiveDependencies": "off",
"useImportExtensions": {
"level": "error",
"options": {
"forceJsExtensions": true
"suggestedExtensions": {
"ts": {
"module": "js",
"component": "jsx"
}
}
}
}
}
@@ -43,16 +47,7 @@
},
"overrides": [
{
"includes": ["packages/community-jazz-vue/src/**"],
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
}
},
{
"includes": ["**/packages/**/src/**"],
"include": ["packages/**/src/**"],
"linter": {
"enabled": true,
"rules": {
@@ -61,10 +56,7 @@
}
},
{
"includes": [
"**/packages/cojson/src/storage/**/*/**",
"**/cojson-transport-ws/**"
],
"include": ["packages/cojson-storage*/**", "cojson-transport-ws/**"],
"linter": {
"enabled": true,
"rules": {
@@ -73,7 +65,7 @@
}
},
{
"includes": ["**/tests/**"],
"include": ["**/tests/**"],
"linter": {
"rules": {
"correctness": {
@@ -83,7 +75,7 @@
"noNonNullAssertion": "off"
},
"suspicious": {
"noExplicitAny": "off"
"noExplicitAny": "info"
}
}
}

View File

@@ -27,22 +27,22 @@
"jazz-tools": "workspace:*",
"lucide-react": "^0.510.0",
"next": "15.3.2",
"react": "catalog:react",
"react-dom": "catalog:react",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"sonner": "^2.0.3",
"tailwind-merge": "^3.3.0",
"tw-animate-css": "^1.2.5"
},
"devDependencies": {
"@biomejs/biome": "catalog:default",
"@biomejs/biome": "1.9.4",
"@playwright/test": "^1.50.1",
"@tailwindcss/postcss": "^4",
"@types/better-sqlite3": "^7.6.12",
"@types/node": "^20",
"@types/react": "catalog:react",
"@types/react-dom": "catalog:react",
"@types/react": "^18",
"@types/react-dom": "^18",
"react-email": "^4.0.11",
"tailwindcss": "^4",
"typescript": "catalog:default"
"typescript": "^5"
}
}

View File

@@ -3,6 +3,7 @@
"main": "index.ts",
"scripts": {
"build": "expo prebuild",
"check": "tsc --noEmit",
"start": "expo start",
"android": "expo run:android",
"ios": "expo run:ios",
@@ -13,13 +14,13 @@
"@bacons/text-decoder": "^0.0.0",
"@bam.tech/react-native-image-resizer": "^3.0.11",
"@react-native-community/netinfo": "11.4.1",
"expo": "catalog:expo",
"expo-clipboard": "catalog:expo",
"expo-secure-store": "catalog:expo",
"expo-sqlite": "catalog:expo",
"expo": "~53.0.9",
"expo-clipboard": "^7.1.4",
"expo-secure-store": "~14.2.3",
"expo-sqlite": "~15.2.10",
"jazz-tools": "workspace:*",
"react": "catalog:expo",
"react-native": "catalog:expo",
"react": "19.0.0",
"react-native": "0.79.2",
"react-native-get-random-values": "^1.11.0",
"readable-stream": "^4.7.0"
},
@@ -29,4 +30,4 @@
"typescript": "~5.8.3"
},
"private": true
}
}

View File

@@ -2,6 +2,7 @@ import { JazzExpoProvider } from "jazz-tools/expo";
import React, { StrictMode } from "react";
import { apiKey } from "./apiKey";
import ChatScreen from "./chat";
import { RNQuickCrypto } from "jazz-tools/expo/crypto";
export default function App() {
return (
@@ -10,6 +11,7 @@ export default function App() {
sync={{
peer: `wss://cloud.jazz.tools/?key=${apiKey}`,
}}
CryptoProvider={RNQuickCrypto}
>
<ChatScreen />
</JazzExpoProvider>

View File

@@ -11,11 +11,11 @@ react {
// The root of your project, i.e. where "package.json" lives. Default is '../..'
// root = file("../../")
// The folder where the react-native NPM package is. Default is ../../node_modules/react-native
reactNativeDir = file("../../../../node_modules/react-native")
// reactNativeDir = file("../../node_modules/react-native")
// The folder where the react-native Codegen package is. Default is ../../node_modules/@react-native/codegen
codegenDir = file("../../../../node_modules/@react-native/codegen")
// codegenDir = file("../../node_modules/@react-native/codegen")
// The cli.js file which is the React Native CLI entrypoint. Default is ../../node_modules/react-native/cli.js
cliFile = file("../../../../node_modules/react-native/cli.js")
// cliFile = file("../../node_modules/react-native/cli.js")
/* Variants */
// The list of variants to that are debuggable. For those we're going to

View File

@@ -1,6 +1,6 @@
pluginManagement { includeBuild("../../../node_modules/@react-native/gradle-plugin") }
pluginManagement { includeBuild("../node_modules/@react-native/gradle-plugin") }
plugins { id("com.facebook.react.settings") }
extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> ex.autolinkLibrariesFromCommand() }
rootProject.name = 'ChatRN'
include ':app'
includeBuild('../../../node_modules/@react-native/gradle-plugin')
includeBuild('../node_modules/@react-native/gradle-plugin')

View File

@@ -380,7 +380,7 @@
"$(inherited)",
" ",
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../../../node_modules/react-native";
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
USE_HERMES = true;
@@ -452,7 +452,7 @@
"$(inherited)",
" ",
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../../../node_modules/react-native";
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
USE_HERMES = true;
VALIDATE_PRODUCT = YES;

View File

@@ -2370,87 +2370,87 @@ PODS:
- Yoga (0.0.0)
DEPENDENCIES:
- boost (from `../../../node_modules/react-native/third-party-podspecs/boost.podspec`)
- DoubleConversion (from `../../../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
- fast_float (from `../../../node_modules/react-native/third-party-podspecs/fast_float.podspec`)
- FBLazyVector (from `../../../node_modules/react-native/Libraries/FBLazyVector`)
- fmt (from `../../../node_modules/react-native/third-party-podspecs/fmt.podspec`)
- glog (from `../../../node_modules/react-native/third-party-podspecs/glog.podspec`)
- hermes-engine (from `../../../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`)
- boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`)
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
- fast_float (from `../node_modules/react-native/third-party-podspecs/fast_float.podspec`)
- FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
- fmt (from `../node_modules/react-native/third-party-podspecs/fmt.podspec`)
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
- hermes-engine (from `../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`)
- "op-sqlite (from `../../../node_modules/@op-engineering/op-sqlite`)"
- RCT-Folly (from `../../../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
- RCTDeprecation (from `../../../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation`)
- RCTRequired (from `../../../node_modules/react-native/Libraries/Required`)
- RCTTypeSafety (from `../../../node_modules/react-native/Libraries/TypeSafety`)
- React (from `../../../node_modules/react-native/`)
- React-callinvoker (from `../../../node_modules/react-native/ReactCommon/callinvoker`)
- React-Core (from `../../../node_modules/react-native/`)
- React-Core/RCTWebSocket (from `../../../node_modules/react-native/`)
- React-CoreModules (from `../../../node_modules/react-native/React/CoreModules`)
- React-cxxreact (from `../../../node_modules/react-native/ReactCommon/cxxreact`)
- React-debug (from `../../../node_modules/react-native/ReactCommon/react/debug`)
- React-defaultsnativemodule (from `../../../node_modules/react-native/ReactCommon/react/nativemodule/defaults`)
- React-domnativemodule (from `../../../node_modules/react-native/ReactCommon/react/nativemodule/dom`)
- React-Fabric (from `../../../node_modules/react-native/ReactCommon`)
- React-FabricComponents (from `../../../node_modules/react-native/ReactCommon`)
- React-FabricImage (from `../../../node_modules/react-native/ReactCommon`)
- React-featureflags (from `../../../node_modules/react-native/ReactCommon/react/featureflags`)
- React-featureflagsnativemodule (from `../../../node_modules/react-native/ReactCommon/react/nativemodule/featureflags`)
- React-graphics (from `../../../node_modules/react-native/ReactCommon/react/renderer/graphics`)
- React-hermes (from `../../../node_modules/react-native/ReactCommon/hermes`)
- React-idlecallbacksnativemodule (from `../../../node_modules/react-native/ReactCommon/react/nativemodule/idlecallbacks`)
- React-ImageManager (from `../../../node_modules/react-native/ReactCommon/react/renderer/imagemanager/platform/ios`)
- React-jserrorhandler (from `../../../node_modules/react-native/ReactCommon/jserrorhandler`)
- React-jsi (from `../../../node_modules/react-native/ReactCommon/jsi`)
- React-jsiexecutor (from `../../../node_modules/react-native/ReactCommon/jsiexecutor`)
- React-jsinspector (from `../../../node_modules/react-native/ReactCommon/jsinspector-modern`)
- React-jsinspectorcdp (from `../../../node_modules/react-native/ReactCommon/jsinspector-modern/cdp`)
- React-jsinspectornetwork (from `../../../node_modules/react-native/ReactCommon/jsinspector-modern/network`)
- React-jsinspectortracing (from `../../../node_modules/react-native/ReactCommon/jsinspector-modern/tracing`)
- React-jsitooling (from `../../../node_modules/react-native/ReactCommon/jsitooling`)
- React-jsitracing (from `../../../node_modules/react-native/ReactCommon/hermes/executor/`)
- React-logger (from `../../../node_modules/react-native/ReactCommon/logger`)
- React-Mapbuffer (from `../../../node_modules/react-native/ReactCommon`)
- React-microtasksnativemodule (from `../../../node_modules/react-native/ReactCommon/react/nativemodule/microtasks`)
- RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
- RCTDeprecation (from `../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation`)
- RCTRequired (from `../node_modules/react-native/Libraries/Required`)
- RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`)
- React (from `../node_modules/react-native/`)
- React-callinvoker (from `../node_modules/react-native/ReactCommon/callinvoker`)
- React-Core (from `../node_modules/react-native/`)
- React-Core/RCTWebSocket (from `../node_modules/react-native/`)
- React-CoreModules (from `../node_modules/react-native/React/CoreModules`)
- React-cxxreact (from `../node_modules/react-native/ReactCommon/cxxreact`)
- React-debug (from `../node_modules/react-native/ReactCommon/react/debug`)
- React-defaultsnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/defaults`)
- React-domnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/dom`)
- React-Fabric (from `../node_modules/react-native/ReactCommon`)
- React-FabricComponents (from `../node_modules/react-native/ReactCommon`)
- React-FabricImage (from `../node_modules/react-native/ReactCommon`)
- React-featureflags (from `../node_modules/react-native/ReactCommon/react/featureflags`)
- React-featureflagsnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/featureflags`)
- React-graphics (from `../node_modules/react-native/ReactCommon/react/renderer/graphics`)
- React-hermes (from `../node_modules/react-native/ReactCommon/hermes`)
- React-idlecallbacksnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/idlecallbacks`)
- React-ImageManager (from `../node_modules/react-native/ReactCommon/react/renderer/imagemanager/platform/ios`)
- React-jserrorhandler (from `../node_modules/react-native/ReactCommon/jserrorhandler`)
- React-jsi (from `../node_modules/react-native/ReactCommon/jsi`)
- React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`)
- React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector-modern`)
- React-jsinspectorcdp (from `../node_modules/react-native/ReactCommon/jsinspector-modern/cdp`)
- React-jsinspectornetwork (from `../node_modules/react-native/ReactCommon/jsinspector-modern/network`)
- React-jsinspectortracing (from `../node_modules/react-native/ReactCommon/jsinspector-modern/tracing`)
- React-jsitooling (from `../node_modules/react-native/ReactCommon/jsitooling`)
- React-jsitracing (from `../node_modules/react-native/ReactCommon/hermes/executor/`)
- React-logger (from `../node_modules/react-native/ReactCommon/logger`)
- React-Mapbuffer (from `../node_modules/react-native/ReactCommon`)
- React-microtasksnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/microtasks`)
- react-native-get-random-values (from `../../../node_modules/react-native-get-random-values`)
- react-native-mmkv (from `../../../node_modules/react-native-mmkv`)
- "react-native-netinfo (from `../../../node_modules/@react-native-community/netinfo`)"
- react-native-safe-area-context (from `../../../node_modules/react-native-safe-area-context`)
- React-NativeModulesApple (from `../../../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`)
- React-oscompat (from `../../../node_modules/react-native/ReactCommon/oscompat`)
- React-perflogger (from `../../../node_modules/react-native/ReactCommon/reactperflogger`)
- React-performancetimeline (from `../../../node_modules/react-native/ReactCommon/react/performance/timeline`)
- React-RCTActionSheet (from `../../../node_modules/react-native/Libraries/ActionSheetIOS`)
- React-RCTAnimation (from `../../../node_modules/react-native/Libraries/NativeAnimation`)
- React-RCTAppDelegate (from `../../../node_modules/react-native/Libraries/AppDelegate`)
- React-RCTBlob (from `../../../node_modules/react-native/Libraries/Blob`)
- React-RCTFabric (from `../../../node_modules/react-native/React`)
- React-RCTFBReactNativeSpec (from `../../../node_modules/react-native/React`)
- React-RCTImage (from `../../../node_modules/react-native/Libraries/Image`)
- React-RCTLinking (from `../../../node_modules/react-native/Libraries/LinkingIOS`)
- React-RCTNetwork (from `../../../node_modules/react-native/Libraries/Network`)
- React-RCTRuntime (from `../../../node_modules/react-native/React/Runtime`)
- React-RCTSettings (from `../../../node_modules/react-native/Libraries/Settings`)
- React-RCTText (from `../../../node_modules/react-native/Libraries/Text`)
- React-RCTVibration (from `../../../node_modules/react-native/Libraries/Vibration`)
- React-rendererconsistency (from `../../../node_modules/react-native/ReactCommon/react/renderer/consistency`)
- React-renderercss (from `../../../node_modules/react-native/ReactCommon/react/renderer/css`)
- React-rendererdebug (from `../../../node_modules/react-native/ReactCommon/react/renderer/debug`)
- React-rncore (from `../../../node_modules/react-native/ReactCommon`)
- React-RuntimeApple (from `../../../node_modules/react-native/ReactCommon/react/runtime/platform/ios`)
- React-RuntimeCore (from `../../../node_modules/react-native/ReactCommon/react/runtime`)
- React-runtimeexecutor (from `../../../node_modules/react-native/ReactCommon/runtimeexecutor`)
- React-RuntimeHermes (from `../../../node_modules/react-native/ReactCommon/react/runtime`)
- React-runtimescheduler (from `../../../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler`)
- React-timing (from `../../../node_modules/react-native/ReactCommon/react/timing`)
- React-utils (from `../../../node_modules/react-native/ReactCommon/react/utils`)
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
- React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`)
- React-oscompat (from `../node_modules/react-native/ReactCommon/oscompat`)
- React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`)
- React-performancetimeline (from `../node_modules/react-native/ReactCommon/react/performance/timeline`)
- React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`)
- React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`)
- React-RCTAppDelegate (from `../node_modules/react-native/Libraries/AppDelegate`)
- React-RCTBlob (from `../node_modules/react-native/Libraries/Blob`)
- React-RCTFabric (from `../node_modules/react-native/React`)
- React-RCTFBReactNativeSpec (from `../node_modules/react-native/React`)
- React-RCTImage (from `../node_modules/react-native/Libraries/Image`)
- React-RCTLinking (from `../node_modules/react-native/Libraries/LinkingIOS`)
- React-RCTNetwork (from `../node_modules/react-native/Libraries/Network`)
- React-RCTRuntime (from `../node_modules/react-native/React/Runtime`)
- React-RCTSettings (from `../node_modules/react-native/Libraries/Settings`)
- React-RCTText (from `../node_modules/react-native/Libraries/Text`)
- React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`)
- React-rendererconsistency (from `../node_modules/react-native/ReactCommon/react/renderer/consistency`)
- React-renderercss (from `../node_modules/react-native/ReactCommon/react/renderer/css`)
- React-rendererdebug (from `../node_modules/react-native/ReactCommon/react/renderer/debug`)
- React-rncore (from `../node_modules/react-native/ReactCommon`)
- React-RuntimeApple (from `../node_modules/react-native/ReactCommon/react/runtime/platform/ios`)
- React-RuntimeCore (from `../node_modules/react-native/ReactCommon/react/runtime`)
- React-runtimeexecutor (from `../node_modules/react-native/ReactCommon/runtimeexecutor`)
- React-RuntimeHermes (from `../node_modules/react-native/ReactCommon/react/runtime`)
- React-runtimescheduler (from `../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler`)
- React-timing (from `../node_modules/react-native/ReactCommon/react/timing`)
- React-utils (from `../node_modules/react-native/ReactCommon/react/utils`)
- ReactAppDependencyProvider (from `build/generated/ios`)
- ReactCodegen (from `build/generated/ios`)
- ReactCommon/turbomodule/core (from `../../../node_modules/react-native/ReactCommon`)
- ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
- "RNCClipboard (from `../../../node_modules/@react-native-clipboard/clipboard`)"
- RNScreens (from `../../../node_modules/react-native-screens`)
- RNScreens (from `../node_modules/react-native-screens`)
- SocketRocket (~> 0.7.1)
- Yoga (from `../../../node_modules/react-native/ReactCommon/yoga`)
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
SPEC REPOS:
trunk:
@@ -2458,88 +2458,88 @@ SPEC REPOS:
EXTERNAL SOURCES:
boost:
:podspec: "../../../node_modules/react-native/third-party-podspecs/boost.podspec"
:podspec: "../node_modules/react-native/third-party-podspecs/boost.podspec"
DoubleConversion:
:podspec: "../../../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
:podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
fast_float:
:podspec: "../../../node_modules/react-native/third-party-podspecs/fast_float.podspec"
:podspec: "../node_modules/react-native/third-party-podspecs/fast_float.podspec"
FBLazyVector:
:path: "../../../node_modules/react-native/Libraries/FBLazyVector"
:path: "../node_modules/react-native/Libraries/FBLazyVector"
fmt:
:podspec: "../../../node_modules/react-native/third-party-podspecs/fmt.podspec"
:podspec: "../node_modules/react-native/third-party-podspecs/fmt.podspec"
glog:
:podspec: "../../../node_modules/react-native/third-party-podspecs/glog.podspec"
:podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec"
hermes-engine:
:podspec: "../../../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec"
:podspec: "../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec"
:tag: hermes-2025-05-06-RNv0.80.0-4eb6132a5bf0450bf4c6c91987675381d7ac8bca
op-sqlite:
:path: "../../../node_modules/@op-engineering/op-sqlite"
RCT-Folly:
:podspec: "../../../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec"
:podspec: "../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec"
RCTDeprecation:
:path: "../../../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation"
:path: "../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation"
RCTRequired:
:path: "../../../node_modules/react-native/Libraries/Required"
:path: "../node_modules/react-native/Libraries/Required"
RCTTypeSafety:
:path: "../../../node_modules/react-native/Libraries/TypeSafety"
:path: "../node_modules/react-native/Libraries/TypeSafety"
React:
:path: "../../../node_modules/react-native/"
:path: "../node_modules/react-native/"
React-callinvoker:
:path: "../../../node_modules/react-native/ReactCommon/callinvoker"
:path: "../node_modules/react-native/ReactCommon/callinvoker"
React-Core:
:path: "../../../node_modules/react-native/"
:path: "../node_modules/react-native/"
React-CoreModules:
:path: "../../../node_modules/react-native/React/CoreModules"
:path: "../node_modules/react-native/React/CoreModules"
React-cxxreact:
:path: "../../../node_modules/react-native/ReactCommon/cxxreact"
:path: "../node_modules/react-native/ReactCommon/cxxreact"
React-debug:
:path: "../../../node_modules/react-native/ReactCommon/react/debug"
:path: "../node_modules/react-native/ReactCommon/react/debug"
React-defaultsnativemodule:
:path: "../../../node_modules/react-native/ReactCommon/react/nativemodule/defaults"
:path: "../node_modules/react-native/ReactCommon/react/nativemodule/defaults"
React-domnativemodule:
:path: "../../../node_modules/react-native/ReactCommon/react/nativemodule/dom"
:path: "../node_modules/react-native/ReactCommon/react/nativemodule/dom"
React-Fabric:
:path: "../../../node_modules/react-native/ReactCommon"
:path: "../node_modules/react-native/ReactCommon"
React-FabricComponents:
:path: "../../../node_modules/react-native/ReactCommon"
:path: "../node_modules/react-native/ReactCommon"
React-FabricImage:
:path: "../../../node_modules/react-native/ReactCommon"
:path: "../node_modules/react-native/ReactCommon"
React-featureflags:
:path: "../../../node_modules/react-native/ReactCommon/react/featureflags"
:path: "../node_modules/react-native/ReactCommon/react/featureflags"
React-featureflagsnativemodule:
:path: "../../../node_modules/react-native/ReactCommon/react/nativemodule/featureflags"
:path: "../node_modules/react-native/ReactCommon/react/nativemodule/featureflags"
React-graphics:
:path: "../../../node_modules/react-native/ReactCommon/react/renderer/graphics"
:path: "../node_modules/react-native/ReactCommon/react/renderer/graphics"
React-hermes:
:path: "../../../node_modules/react-native/ReactCommon/hermes"
:path: "../node_modules/react-native/ReactCommon/hermes"
React-idlecallbacksnativemodule:
:path: "../../../node_modules/react-native/ReactCommon/react/nativemodule/idlecallbacks"
:path: "../node_modules/react-native/ReactCommon/react/nativemodule/idlecallbacks"
React-ImageManager:
:path: "../../../node_modules/react-native/ReactCommon/react/renderer/imagemanager/platform/ios"
:path: "../node_modules/react-native/ReactCommon/react/renderer/imagemanager/platform/ios"
React-jserrorhandler:
:path: "../../../node_modules/react-native/ReactCommon/jserrorhandler"
:path: "../node_modules/react-native/ReactCommon/jserrorhandler"
React-jsi:
:path: "../../../node_modules/react-native/ReactCommon/jsi"
:path: "../node_modules/react-native/ReactCommon/jsi"
React-jsiexecutor:
:path: "../../../node_modules/react-native/ReactCommon/jsiexecutor"
:path: "../node_modules/react-native/ReactCommon/jsiexecutor"
React-jsinspector:
:path: "../../../node_modules/react-native/ReactCommon/jsinspector-modern"
:path: "../node_modules/react-native/ReactCommon/jsinspector-modern"
React-jsinspectorcdp:
:path: "../../../node_modules/react-native/ReactCommon/jsinspector-modern/cdp"
:path: "../node_modules/react-native/ReactCommon/jsinspector-modern/cdp"
React-jsinspectornetwork:
:path: "../../../node_modules/react-native/ReactCommon/jsinspector-modern/network"
:path: "../node_modules/react-native/ReactCommon/jsinspector-modern/network"
React-jsinspectortracing:
:path: "../../../node_modules/react-native/ReactCommon/jsinspector-modern/tracing"
:path: "../node_modules/react-native/ReactCommon/jsinspector-modern/tracing"
React-jsitooling:
:path: "../../../node_modules/react-native/ReactCommon/jsitooling"
:path: "../node_modules/react-native/ReactCommon/jsitooling"
React-jsitracing:
:path: "../../../node_modules/react-native/ReactCommon/hermes/executor/"
:path: "../node_modules/react-native/ReactCommon/hermes/executor/"
React-logger:
:path: "../../../node_modules/react-native/ReactCommon/logger"
:path: "../node_modules/react-native/ReactCommon/logger"
React-Mapbuffer:
:path: "../../../node_modules/react-native/ReactCommon"
:path: "../node_modules/react-native/ReactCommon"
React-microtasksnativemodule:
:path: "../../../node_modules/react-native/ReactCommon/react/nativemodule/microtasks"
:path: "../node_modules/react-native/ReactCommon/react/nativemodule/microtasks"
react-native-get-random-values:
:path: "../../../node_modules/react-native-get-random-values"
react-native-mmkv:
@@ -2547,75 +2547,75 @@ EXTERNAL SOURCES:
react-native-netinfo:
:path: "../../../node_modules/@react-native-community/netinfo"
react-native-safe-area-context:
:path: "../../../node_modules/react-native-safe-area-context"
:path: "../node_modules/react-native-safe-area-context"
React-NativeModulesApple:
:path: "../../../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios"
:path: "../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios"
React-oscompat:
:path: "../../../node_modules/react-native/ReactCommon/oscompat"
:path: "../node_modules/react-native/ReactCommon/oscompat"
React-perflogger:
:path: "../../../node_modules/react-native/ReactCommon/reactperflogger"
:path: "../node_modules/react-native/ReactCommon/reactperflogger"
React-performancetimeline:
:path: "../../../node_modules/react-native/ReactCommon/react/performance/timeline"
:path: "../node_modules/react-native/ReactCommon/react/performance/timeline"
React-RCTActionSheet:
:path: "../../../node_modules/react-native/Libraries/ActionSheetIOS"
:path: "../node_modules/react-native/Libraries/ActionSheetIOS"
React-RCTAnimation:
:path: "../../../node_modules/react-native/Libraries/NativeAnimation"
:path: "../node_modules/react-native/Libraries/NativeAnimation"
React-RCTAppDelegate:
:path: "../../../node_modules/react-native/Libraries/AppDelegate"
:path: "../node_modules/react-native/Libraries/AppDelegate"
React-RCTBlob:
:path: "../../../node_modules/react-native/Libraries/Blob"
:path: "../node_modules/react-native/Libraries/Blob"
React-RCTFabric:
:path: "../../../node_modules/react-native/React"
:path: "../node_modules/react-native/React"
React-RCTFBReactNativeSpec:
:path: "../../../node_modules/react-native/React"
:path: "../node_modules/react-native/React"
React-RCTImage:
:path: "../../../node_modules/react-native/Libraries/Image"
:path: "../node_modules/react-native/Libraries/Image"
React-RCTLinking:
:path: "../../../node_modules/react-native/Libraries/LinkingIOS"
:path: "../node_modules/react-native/Libraries/LinkingIOS"
React-RCTNetwork:
:path: "../../../node_modules/react-native/Libraries/Network"
:path: "../node_modules/react-native/Libraries/Network"
React-RCTRuntime:
:path: "../../../node_modules/react-native/React/Runtime"
:path: "../node_modules/react-native/React/Runtime"
React-RCTSettings:
:path: "../../../node_modules/react-native/Libraries/Settings"
:path: "../node_modules/react-native/Libraries/Settings"
React-RCTText:
:path: "../../../node_modules/react-native/Libraries/Text"
:path: "../node_modules/react-native/Libraries/Text"
React-RCTVibration:
:path: "../../../node_modules/react-native/Libraries/Vibration"
:path: "../node_modules/react-native/Libraries/Vibration"
React-rendererconsistency:
:path: "../../../node_modules/react-native/ReactCommon/react/renderer/consistency"
:path: "../node_modules/react-native/ReactCommon/react/renderer/consistency"
React-renderercss:
:path: "../../../node_modules/react-native/ReactCommon/react/renderer/css"
:path: "../node_modules/react-native/ReactCommon/react/renderer/css"
React-rendererdebug:
:path: "../../../node_modules/react-native/ReactCommon/react/renderer/debug"
:path: "../node_modules/react-native/ReactCommon/react/renderer/debug"
React-rncore:
:path: "../../../node_modules/react-native/ReactCommon"
:path: "../node_modules/react-native/ReactCommon"
React-RuntimeApple:
:path: "../../../node_modules/react-native/ReactCommon/react/runtime/platform/ios"
:path: "../node_modules/react-native/ReactCommon/react/runtime/platform/ios"
React-RuntimeCore:
:path: "../../../node_modules/react-native/ReactCommon/react/runtime"
:path: "../node_modules/react-native/ReactCommon/react/runtime"
React-runtimeexecutor:
:path: "../../../node_modules/react-native/ReactCommon/runtimeexecutor"
:path: "../node_modules/react-native/ReactCommon/runtimeexecutor"
React-RuntimeHermes:
:path: "../../../node_modules/react-native/ReactCommon/react/runtime"
:path: "../node_modules/react-native/ReactCommon/react/runtime"
React-runtimescheduler:
:path: "../../../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler"
:path: "../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler"
React-timing:
:path: "../../../node_modules/react-native/ReactCommon/react/timing"
:path: "../node_modules/react-native/ReactCommon/react/timing"
React-utils:
:path: "../../../node_modules/react-native/ReactCommon/react/utils"
:path: "../node_modules/react-native/ReactCommon/react/utils"
ReactAppDependencyProvider:
:path: build/generated/ios
ReactCodegen:
:path: build/generated/ios
ReactCommon:
:path: "../../../node_modules/react-native/ReactCommon"
:path: "../node_modules/react-native/ReactCommon"
RNCClipboard:
:path: "../../../node_modules/@react-native-clipboard/clipboard"
RNScreens:
:path: "../../../node_modules/react-native-screens"
:path: "../node_modules/react-native-screens"
Yoga:
:path: "../../../node_modules/react-native/ReactCommon/yoga"
:path: "../node_modules/react-native/ReactCommon/yoga"
SPEC CHECKSUMS:
boost: 7e761d76ca2ce687f7cc98e698152abd03a18f90
@@ -2692,7 +2692,7 @@ SPEC CHECKSUMS:
React-timing: a275a1c2e6112dba17f8f7dd496d439213bbea0d
React-utils: 449a6e1fd53886510e284e80bdbb1b1c6db29452
ReactAppDependencyProvider: 3267432b637c9b38e86961b287f784ee1b08dde0
ReactCodegen: d82f538f70f00484d418803f74b5a0ea09cc8689
ReactCodegen: 5d41e1df061200130dd326e55cdfdf94b0289c6e
ReactCommon: b028d09a66e60ebd83ca59d8cc9a1216360db147
RNCClipboard: 54ff19965d7c816febbafe5f520c2c3e7b677a49
RNScreens: ee2abe7e0c548eed14e92742e81ed991165c56aa

View File

@@ -13,13 +13,13 @@
"@azure/core-asynciterator-polyfill": "^1.0.2",
"@bacons/text-decoder": "0.0.0",
"@op-engineering/op-sqlite": "14.1.0",
"@react-native-clipboard/clipboard": "1.16.3",
"@react-native-clipboard/clipboard": "1.16.2",
"@react-native-community/netinfo": "11.4.1",
"@react-navigation/native": "7.1.14",
"@react-navigation/native-stack": "7.3.19",
"jazz-tools": "workspace:*",
"react": "catalog:rn",
"react-native": "catalog:rn",
"react": "19.1.0",
"react-native": "0.80.0",
"react-native-get-random-values": "^1.11.0",
"react-native-mmkv": "3.3.0",
"react-native-safe-area-context": "5.5.0",
@@ -31,16 +31,16 @@
"@babel/plugin-transform-export-namespace-from": "^7.27.1",
"@babel/preset-env": "^7.25.3",
"@babel/runtime": "^7.25.0",
"@react-native-community/cli": "catalog:rn",
"@react-native-community/cli-platform-android": "catalog:rn",
"@react-native-community/cli-platform-ios": "catalog:rn",
"@react-native/babel-preset": "catalog:rn",
"@react-native/eslint-config": "catalog:rn",
"@react-native/metro-config": "catalog:rn",
"@react-native/typescript-config": "catalog:rn",
"@react-native-community/cli": "19.0.0",
"@react-native-community/cli-platform-android": "19.0.0",
"@react-native-community/cli-platform-ios": "19.0.0",
"@react-native/babel-preset": "0.80.0",
"@react-native/eslint-config": "0.80.0",
"@react-native/metro-config": "0.80.0",
"@react-native/typescript-config": "0.80.0",
"@rnx-kit/metro-config": "^2.0.1",
"@rnx-kit/metro-resolver-symlinks": "^0.2.5",
"@types/react": "catalog:rn",
"@types/react": "19.1.0",
"eslint": "^8.19.0",
"pod-install": "^0.3.5",
"prettier": "2.8.8",

View File

@@ -76,9 +76,7 @@ export function ChatScreen({ navigation }: { navigation: any }) {
const renderMessageItem = ({
item,
}: {
item: Loaded<typeof Message, { text: true }>;
}) => {
}: { item: Loaded<typeof Message, { text: true }> }) => {
const isMe = item._edits?.text?.by?.isMe;
return (
<View

View File

@@ -3,7 +3,11 @@ import React from "react";
import { Text } from "react-native";
import { Chat } from "./schema";
export function HandleInviteScreen({ navigation }: { navigation: any }) {
export function HandleInviteScreen({
navigation,
}: {
navigation: any;
}) {
useAcceptInviteNative({
invitedObjectSchema: Chat,
onAccept: async (chatId) => {

View File

@@ -1,192 +1,5 @@
# passkey-svelte
## 0.0.117
### Patch Changes
- Updated dependencies [7dd3d00]
- jazz-tools@0.17.4
## 0.0.116
### Patch Changes
- jazz-tools@0.17.3
## 0.0.115
### Patch Changes
- Updated dependencies [794681a]
- Updated dependencies [83fc22f]
- jazz-tools@0.17.2
## 0.0.114
### Patch Changes
- Updated dependencies [0bcbf55]
- Updated dependencies [d1bdbf5]
- Updated dependencies [4b73834]
- jazz-tools@0.17.1
## 0.0.113
### Patch Changes
- Updated dependencies [fcaf4b9]
- jazz-tools@0.17.0
## 0.0.112
### Patch Changes
- Updated dependencies [67e0968]
- Updated dependencies [2c8120d]
- jazz-tools@0.16.6
## 0.0.111
### Patch Changes
- Updated dependencies [3cd1586]
- Updated dependencies [33ebbf0]
- jazz-tools@0.16.5
## 0.0.110
### Patch Changes
- Updated dependencies [16764f6]
- jazz-tools@0.16.4
## 0.0.109
### Patch Changes
- Updated dependencies [43d3511]
- jazz-tools@0.16.3
## 0.0.108
### Patch Changes
- jazz-tools@0.16.2
## 0.0.107
### Patch Changes
- Updated dependencies [c62abef]
- jazz-tools@0.16.1
## 0.0.106
### Patch Changes
- 2bbb07b: Introduce a cleaner separation between Zod and CoValue schemas:
- Zod schemas and CoValue schemas are fully separated. Zod schemas can only be composed with other Zod schemas. CoValue schemas can be composed with either Zod or other CoValue schemas.
- `z.optional()` and `z.discriminatedUnion()` no longer work with CoValue schemas. Use `co.optional()` and `co.discriminatedUnion()` instead.
- Internal schema access is now simpler. You no longer need to use Zods `.def` to access internals. Use properties like `CoMapSchema.shape`, `CoListSchema.element`, and `CoOptionalSchema.innerType` directly.
- CoValue schema types are now namespaced under `co.`. Non-namespaced exports have been removed
- CoMap schemas no longer incorrectly inherit from Zod. Previously, methods like `.extend()` and `.partial()` appeared available but could cause unexpected behavior. These methods are now disabled. In their place, `.optional()` has been added, and more Zod-like methods will be introduced in future releases.
- Upgraded Zod from `3.25.28` to `3.25.76`.
- Removed deprecated `withHelpers` method from CoValue schemas
- Removed deprecated `createCoValueObservable` function
- Updated dependencies [c09dcdf]
- Updated dependencies [2bbb07b]
- jazz-tools@0.16.0
## 0.0.105
### Patch Changes
- Updated dependencies [9633d01]
- Updated dependencies [4beafb7]
- jazz-tools@0.15.16
## 0.0.104
### Patch Changes
- Updated dependencies [3fe53a3]
- jazz-tools@0.15.15
## 0.0.103
### Patch Changes
- Updated dependencies [a584590]
- Updated dependencies [9acccb5]
- jazz-tools@0.15.14
## 0.0.102
### Patch Changes
- Updated dependencies [6c76ff8]
- jazz-tools@0.15.13
## 0.0.101
### Patch Changes
- Updated dependencies [d1c1b0c]
- Updated dependencies [cf4ad72]
- jazz-tools@0.15.12
## 0.0.100
### Patch Changes
- Updated dependencies [bdc9aee]
- jazz-tools@0.15.11
## 0.0.99
### Patch Changes
- Updated dependencies [9815ec6]
- Updated dependencies [b4fdab4]
- jazz-tools@0.15.10
## 0.0.98
### Patch Changes
- Updated dependencies [27b4837]
- jazz-tools@0.15.9
## 0.0.97
### Patch Changes
- Updated dependencies [3844666]
- jazz-tools@0.15.8
## 0.0.96
### Patch Changes
- Updated dependencies [c09b636]
- jazz-tools@0.15.7
## 0.0.95
### Patch Changes
- Updated dependencies [a5ceaff]
- jazz-tools@0.15.6
## 0.0.94
### Patch Changes
- Updated dependencies [23bfea5]
- Updated dependencies [e4ba23c]
- Updated dependencies [4b89838]
- jazz-tools@0.15.5
## 0.0.93
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "chat-svelte",
"version": "0.0.117",
"version": "0.0.93",
"type": "module",
"private": true,
"scripts": {

View File

@@ -1,11 +1,12 @@
<script lang="ts">
import { ImageDefinition, type Loaded } from 'jazz-tools';
import { Image } from 'jazz-tools/svelte';
import { useProgressiveImg } from '$lib/utils/useProgressiveImage.svelte';
let { image }: { image: Loaded<typeof ImageDefinition> } = $props();
const { src } = $derived(
useProgressiveImg({
image
})
);
</script>
<Image
imageId={image.id}
alt=""
class="h-auto max-h-[20rem] max-w-full rounded-t-xl mb-1"
/>
<img class="h-auto max-h-[20rem] max-w-full rounded-t-xl mb-1" {src} alt="" />

View File

@@ -1,8 +1,8 @@
import { co } from 'jazz-tools';
import { co, z } from 'jazz-tools';
export const Message = co.map({
text: co.plainText(),
image: co.optional(co.image())
image: z.optional(co.image())
});
export const Chat = co.list(Message);

View File

@@ -0,0 +1,53 @@
import { ImageDefinition, type Loaded } from 'jazz-tools';
import { onDestroy } from 'svelte';
export function useProgressiveImg({
image,
maxWidth,
targetWidth
}: {
image: Loaded<typeof ImageDefinition> | null | undefined;
maxWidth?: number;
targetWidth?: number;
}) {
let current = $state<{
src?: string;
res?: `${number}x${number}` | 'placeholder';
}>();
const originalSize = $state(image?.originalSize);
const unsubscribe = image?.subscribe({}, (update: Loaded<typeof ImageDefinition>) => {
const highestRes = ImageDefinition.highestResAvailable(update, { maxWidth, targetWidth });
if (highestRes) {
if (highestRes.res !== current?.res) {
const blob = highestRes.stream.toBlob();
if (blob) {
const blobURI = URL.createObjectURL(blob);
current = { src: blobURI, res: highestRes.res };
setTimeout(() => URL.revokeObjectURL(blobURI), 200);
}
}
} else {
current = {
src: update?.placeholderDataURL,
res: 'placeholder'
};
}
});
onDestroy(() => () => {
unsubscribe?.();
});
return {
get src() {
return current?.src;
},
get res() {
return current?.res;
},
originalSize
};
}

View File

@@ -1,5 +1,5 @@
<script lang="ts">
import { createImage } from 'jazz-tools/media';
import { createImage } from 'jazz-tools/browser-media-images';
import { AccountCoState, CoState } from 'jazz-tools/svelte';
import { Account, CoPlainText, type ID } from 'jazz-tools';

View File

@@ -15,21 +15,21 @@
"clsx": "^2.0.0",
"hash-slash": "workspace:*",
"jazz-tools": "workspace:*",
"lucide-react": "^0.536.0",
"react": "catalog:react",
"react-dom": "catalog:react",
"zod": "3.25.76"
"lucide-react": "^0.274.0",
"react": "19.0.0",
"react-dom": "19.0.0",
"zod": "3.25.28"
},
"devDependencies": {
"@playwright/test": "^1.50.1",
"@tailwindcss/postcss": "^4.1.10",
"@types/react": "catalog:react",
"@types/react-dom": "catalog:react",
"@types/react": "19.0.0",
"@types/react-dom": "19.0.0",
"@vitejs/plugin-react-swc": "^3.10.1",
"is-ci": "^3.0.1",
"postcss": "^8.4.40",
"tailwindcss": "^4.1.10",
"typescript": "catalog:default",
"vite": "catalog:default"
"typescript": "5.6.2",
"vite": "^6.3.5"
}
}
}

View File

@@ -1,7 +1,6 @@
import { Account } from "jazz-tools";
import { createImage } from "jazz-tools/media";
import { useAccount, useCoState } from "jazz-tools/react";
import { useEffect, useState } from "react";
import { Account, co } from "jazz-tools";
import { createImage, useAccount, useCoState } from "jazz-tools/react";
import { useState } from "react";
import { Chat, Message } from "./schema.ts";
import {
BubbleBody,
@@ -16,17 +15,14 @@ import {
TextInput,
} from "./ui.tsx";
const INITIAL_MESSAGES_TO_SHOW = 30;
export function ChatScreen(props: { chatID: string }) {
const chat = useCoState(Chat, props.chatID);
const chat = useCoState(Chat, props.chatID, {
resolve: { $each: { text: true } },
});
const { me } = useAccount();
const [showNLastMessages, setShowNLastMessages] = useState(
INITIAL_MESSAGES_TO_SHOW,
);
const isLoading = useMessagesPreload(props.chatID);
const [showNLastMessages, setShowNLastMessages] = useState(30);
if (!chat || isLoading)
if (!chat)
return (
<div className="flex-1 flex justify-center items-center">Loading...</div>
);
@@ -41,15 +37,11 @@ export function ChatScreen(props: { chatID: string }) {
return;
}
createImage(file, {
owner: chat._owner,
progressive: true,
placeholder: "blur",
}).then((image) => {
createImage(file, { owner: chat._owner }).then((image) => {
chat.push(
Message.create(
{
text: file.name,
text: co.plainText().create(file.name, chat._owner),
image: image,
},
chat._owner,
@@ -67,14 +59,9 @@ export function ChatScreen(props: { chatID: string }) {
<ChatBody>
{chat.length > 0 ? (
chat
// We call slice before reverse to avoid mutating the original array
.slice(-showNLastMessages)
// Reverse plus flex-col-reverse on ChatBody gives us scroll-to-bottom behavior
.reverse()
.map(
(msg) =>
msg?.text && <ChatBubble me={me} msg={msg} key={msg.id} />,
)
.reverse() // this plus flex-col-reverse on ChatBody gives us scroll-to-bottom behavior
.map((msg) => <ChatBubble me={me} msg={msg} key={msg.id} />)
) : (
<EmptyChatMessage />
)}
@@ -93,7 +80,12 @@ export function ChatScreen(props: { chatID: string }) {
<TextInput
onSubmit={(text) => {
chat.push(Message.create({ text }, chat._owner));
chat.push(
Message.create(
{ text: co.plainText().create(text, chat._owner) },
chat._owner,
),
);
}}
/>
</InputBar>
@@ -101,7 +93,10 @@ export function ChatScreen(props: { chatID: string }) {
);
}
function ChatBubble(props: { me: Account; msg: Message }) {
function ChatBubble(props: {
me: Account;
msg: co.loaded<typeof Message, { text: true }>;
}) {
if (!props.me.canRead(props.msg) || !props.msg.text?.toString()) {
return (
<BubbleContainer fromMe={false}>
@@ -131,35 +126,3 @@ function ChatBubble(props: { me: Account; msg: Message }) {
</BubbleContainer>
);
}
/**
* Warms the local cache with the initial messages to load only the initial messages
* and avoid flickering
*/
function useMessagesPreload(chatID: string) {
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
preloadChatMessages(chatID).finally(() => {
setIsLoading(false);
});
}, [chatID]);
return isLoading;
}
async function preloadChatMessages(chatID: string) {
const chat = await Chat.load(chatID);
if (!chat?._refs) return;
const promises = [];
for (const msg of Array.from(chat._refs)
.reverse()
.slice(0, INITIAL_MESSAGES_TO_SHOW)) {
promises.push(Message.load(msg.id, { resolve: { text: true } }));
}
await Promise.all(promises);
}

View File

@@ -1,8 +1,8 @@
import { co } from "jazz-tools";
import { co, z } from "jazz-tools";
export const Message = co.map({
text: co.plainText(),
image: co.optional(co.image()),
image: z.optional(co.image()),
});
export type Message = co.loaded<typeof Message>;

View File

@@ -1,6 +1,6 @@
import clsx from "clsx";
import { CoPlainText, ImageDefinition } from "jazz-tools";
import { Image } from "jazz-tools/react";
import { ProgressiveImg } from "jazz-tools/react";
import { ImageIcon } from "lucide-react";
import { useId, useRef } from "react";
@@ -83,12 +83,14 @@ export function BubbleText(props: {
export function BubbleImage(props: { image: ImageDefinition }) {
return (
<Image
imageId={props.image.id}
className="h-auto max-h-80 max-w-full rounded-t-xl mb-1"
height="original"
width="original"
/>
<ProgressiveImg image={props.image}>
{({ src }) => (
<img
className="h-auto max-h-80 max-w-full rounded-t-xl mb-1"
src={src}
/>
)}
</ProgressiveImg>
);
}
@@ -110,9 +112,7 @@ export function InputBar(props: { children: React.ReactNode }) {
export function ImageInput({
onImageChange,
}: {
onImageChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
}) {
}: { onImageChange?: (event: React.ChangeEvent<HTMLInputElement>) => void }) {
const inputRef = useRef<HTMLInputElement>(null);
const onUploadClick = () => {

View File

@@ -14,22 +14,22 @@
"@bam.tech/react-native-image-resizer": "^3.0.11",
"@clerk/clerk-expo": "^2.13.1",
"@react-native-community/netinfo": "11.4.1",
"expo": "catalog:expo",
"expo-crypto": "catalog:expo",
"expo-linking": "catalog:expo",
"expo-secure-store": "catalog:expo",
"expo-sqlite": "catalog:expo",
"expo-web-browser": "catalog:expo",
"expo": "~53.0.9",
"expo-crypto": "~14.1.5",
"expo-linking": "~7.1.5",
"expo-secure-store": "~14.2.3",
"expo-sqlite": "~15.2.10",
"expo-web-browser": "~14.2.0",
"jazz-tools": "workspace:*",
"react": "catalog:expo",
"react-native": "catalog:expo",
"react": "19.0.0",
"react-native": "0.79.2",
"react-native-get-random-values": "^1.11.0",
"readable-stream": "^4.7.0"
},
"devDependencies": {
"@babel/core": "^7.25.2",
"@types/react": "catalog:expo",
"@types/react": "~19.0.10",
"typescript": "~5.8.3"
},
"private": true
}
}

View File

@@ -10,9 +10,7 @@ import {
export function SignInScreen({
setPage,
}: {
setPage: (page: "sign-in" | "sign-up") => void;
}) {
}: { setPage: (page: "sign-in" | "sign-up") => void }) {
const { signIn, setActive, isLoaded } = useSignIn();
const [emailAddress, setEmailAddress] = useState("");

View File

@@ -10,9 +10,7 @@ import {
export function SignUpScreen({
setPage,
}: {
setPage: (page: "sign-in" | "sign-up") => void;
}) {
}: { setPage: (page: "sign-in" | "sign-up") => void }) {
const { isLoaded, signUp, setActive } = useSignUp();
const [emailAddress, setEmailAddress] = React.useState("");

View File

@@ -14,17 +14,17 @@
"dependencies": {
"@clerk/clerk-react": "^5.4.1",
"jazz-tools": "workspace:*",
"react": "catalog:react",
"react-dom": "catalog:react"
"react": "19.0.0",
"react-dom": "19.0.0"
},
"devDependencies": {
"@playwright/test": "^1.50.1",
"@biomejs/biome": "catalog:default",
"@types/react": "catalog:react",
"@types/react-dom": "catalog:react",
"@biomejs/biome": "1.9.4",
"@types/react": "19.0.0",
"@types/react-dom": "19.0.0",
"@vitejs/plugin-react": "^4.5.1",
"globals": "^15.11.0",
"typescript": "catalog:default",
"vite": "catalog:default"
"typescript": "5.6.2",
"vite": "^6.3.5"
}
}
}

View File

@@ -1,6 +0,0 @@
dist
# env files
.env
.env.*
!.env.example
!.env.test

View File

@@ -1,7 +0,0 @@
# community-chat-vue
## 0.15.4
- rewrite from React to Vue
- jazz-tools@0.15.4
- community-jazz-vue@0.15.4

View File

@@ -1,60 +0,0 @@
# Chat example with Jazz and Vue
## Getting started
You can either
1. Clone the jazz repository, and run the app within the monorepo.
2. Or create a new Jazz project using this example as a template.
### Using the example as a template
Create a new Jazz project, and use this example as a template.
```bash
npx create-jazz-app@latest chat-vue-app --example community-chat-vue
```
Go to the new project directory.
```bash
cd chat-vue-app
```
Run the dev server.
```bash
npm run dev
```
### Using the monorepo
This requires `pnpm` to be installed, see [https://pnpm.io/installation](https://pnpm.io/installation).
Clone the jazz repository.
```bash
git clone https://github.com/garden-co/jazz.git
```
Install and build dependencies.
```bash
pnpm i && npx turbo build
```
Go to the example directory.
```bash
cd jazz/examples/community-chat-vue/
```
Start the dev server.
```bash
pnpm dev
```
Open [http://localhost:5173](http://localhost:5173) with your browser to see the result.
## Questions / problems / feedback
If you have feedback, let us know on [Discord](https://discord.gg/utDMjHYg42) or open an issue or PR to fix something that seems wrong.
## Configuration: sync server
By default, the example app uses [Jazz Cloud](https://jazz.tools/cloud) (`wss://cloud.jazz.tools`) - so cross-device use, invites and collaboration should just work.
You can also run a local sync server by running `npx jazz-run sync`, and setting the `sync` parameter of `JazzProvider` in [./src/main.ts](./src/main.ts) to `{ peer: "ws://localhost:4200" }`.

View File

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

View File

@@ -1,13 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Jazz Chat Vue Example</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

View File

@@ -1,37 +0,0 @@
{
"name": "community-chat-vue",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build-type-check": "run-p type-check \"build {@}\" --",
"preview": "vite preview",
"build": "vite build",
"type-check": "vue-tsc --build --force",
"format-and-lint": "biome check .",
"format-and-lint:fix": "biome check . --write"
},
"dependencies": {
"jazz-tools": "workspace:*",
"community-jazz-vue": "workspace:*",
"vue": "^3.5.11",
"vue-router": "^4.4.5"
},
"devDependencies": {
"@tsconfig/node20": "^20.1.4",
"@types/node": "^22.5.1",
"@vitejs/plugin-vue": "^5.1.4",
"@vitejs/plugin-vue-jsx": "^4.0.1",
"@vue/tsconfig": "^0.5.1",
"eslint": "^9.7.0",
"eslint-plugin-vue": "^9.28.0",
"npm-run-all2": "^6.2.3",
"postcss": "^8.4.40",
"@tailwindcss/postcss": "^4.1.10",
"tailwindcss": "^4.1.10",
"typescript": "5.6.2",
"vite": "6.3.5",
"vite-plugin-vue-devtools": "^7.4.6",
"vue-tsc": "^2.1.6"
}
}

View File

@@ -1,5 +0,0 @@
export default {
plugins: {
"@tailwindcss/postcss": {},
},
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -1,24 +0,0 @@
<template>
<AppContainer>
<TopBar v-if="me">
<p>{{ me.profile?.name }}</p>
<button @click="logoutHandler">Log out</button>
</TopBar>
<router-view />
</AppContainer>
</template>
<script setup lang="ts">
import { useAccount } from "community-jazz-vue";
import { useRouter } from "vue-router";
import AppContainer from "./components/AppContainer.vue";
import TopBar from "./components/TopBar.vue";
const { me, logOut } = useAccount();
const router = useRouter();
async function logoutHandler() {
await logOut();
router.push("/");
}
</script>

View File

@@ -1,24 +0,0 @@
<script setup lang="ts">
import { JazzVueProvider, PasskeyAuthBasicUI } from "community-jazz-vue";
import { h } from "vue";
import "jazz-tools/inspector/register-custom-element";
import App from "./App.vue";
import { apiKey } from "./apiKey";
</script>
<template>
<JazzVueProvider
:sync="{
peer: `wss://cloud.jazz.tools/?key=${apiKey}`,
}"
>
<PasskeyAuthBasicUI appName="Jazz Vue Chat">
<App />
</PasskeyAuthBasicUI>
<component
:is="h('jazz-inspector', {
style: { position: 'fixed', left: '20px', bottom: '20px', zIndex: 9999 }
})"
/>
</JazzVueProvider>
</template>

View File

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

View File

@@ -1,76 +0,0 @@
/* color palette from <https://github.com/vuejs/theme> */
:root {
--vt-c-white: #ffffff;
--vt-c-white-soft: #f8f8f8;
--vt-c-white-mute: #f2f2f2;
--vt-c-black: #181818;
--vt-c-black-soft: #222222;
--vt-c-black-mute: #282828;
--vt-c-indigo: #2c3e50;
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
--vt-c-text-light-1: var(--vt-c-indigo);
--vt-c-text-light-2: rgba(60, 60, 60, 0.66);
--vt-c-text-dark-1: var(--vt-c-white);
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
}
/* semantic color variables for this project */
:root {
--color-background: var(--vt-c-white);
--color-background-soft: var(--vt-c-white-soft);
--color-background-mute: var(--vt-c-white-mute);
--color-border: var(--vt-c-divider-light-2);
--color-border-hover: var(--vt-c-divider-light-1);
--color-heading: var(--vt-c-text-light-1);
--color-text: var(--vt-c-text-light-1);
--section-gap: 160px;
}
@media (prefers-color-scheme: dark) {
:root {
--color-background: var(--vt-c-black);
--color-background-soft: var(--vt-c-black-soft);
--color-background-mute: var(--vt-c-black-mute);
--color-border: var(--vt-c-divider-dark-2);
--color-border-hover: var(--vt-c-divider-dark-1);
--color-heading: var(--vt-c-text-dark-1);
--color-text: var(--vt-c-text-dark-2);
}
}
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
font-weight: normal;
}
body {
min-height: 100vh;
color: var(--color-text);
background: var(--color-background);
transition:
color 0.5s,
background-color 0.5s;
line-height: 1.6;
font-family:
Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu,
Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
font-size: 15px;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>

Before

Width:  |  Height:  |  Size: 276 B

View File

@@ -1,35 +0,0 @@
@import "./base.css";
#app {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
font-weight: normal;
}
a,
.green {
text-decoration: none;
color: hsla(160, 100%, 37%, 1);
transition: 0.4s;
padding: 3px;
}
@media (hover: hover) {
a:hover {
background-color: hsla(160, 100%, 37%, 0.2);
}
}
@media (min-width: 1024px) {
body {
display: flex;
place-items: center;
}
#app {
display: grid;
grid-template-columns: 1fr 1fr;
padding: 0 2rem;
}
}

View File

@@ -1,13 +0,0 @@
<template>
<div
class="flex flex-col justify-between w-screen h-screen bg-stone-50 dark:bg-black dark:text-white"
>
<slot></slot>
</div>
</template>
<script lang="ts">
export default {
name: "AppContainer",
};
</script>

View File

@@ -1,13 +0,0 @@
<template>
<div
class="rounded-2xl text-sm line-clamp-10 text-ellipsis bg-white max-w-full whitespace-pre-wrap dark:bg-stone-700 dark:text-white py-1 px-3 shadow-sm"
>
<slot></slot>
</div>
</template>
<script lang="ts">
export default {
name: "BubbleBody",
};
</script>

View File

@@ -1,22 +0,0 @@
<template>
<div :class="[alignClass, 'flex flex-col m-2']" role="row">
<slot></slot>
</div>
</template>
<script lang="ts">
export default {
name: "BubbleContainer",
props: {
fromMe: {
type: Boolean,
default: undefined,
},
},
computed: {
alignClass() {
return this.fromMe ? "items-end" : "items-start";
},
},
};
</script>

View File

@@ -1,30 +0,0 @@
<template>
<div class="h-auto max-w-full rounded-t-xl mb-1">
<Image
:image-id="image.id"
alt="Uploaded image"
class-names="h-full rounded-t-xl"
width="original"
height="original"
/>
</div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
import { type ImageDefinition } from "jazz-tools";
import { Image } from "community-jazz-vue";
export default defineComponent({
name: "BubbleImage",
components: {
Image,
},
props: {
image: {
type: Object as () => ImageDefinition,
required: true,
},
},
});
</script>

View File

@@ -1,29 +0,0 @@
<template>
<div class="text-xs text-neutral-500 mt-1.5">
{{ by }} · {{ formattedTime }}
</div>
</template>
<script lang="ts">
import { computed, defineComponent } from "vue";
export default defineComponent({
name: "BubbleInfo",
props: {
by: {
type: String,
default: "",
},
madeAt: {
type: Date,
required: true,
},
},
setup(props) {
const formattedTime = computed(() => props.madeAt.toLocaleTimeString());
return {
formattedTime,
};
},
});
</script>

View File

@@ -1,11 +0,0 @@
<template>
<div class="flex-1 overflow-y-auto flex flex-col-reverse" role="application">
<slot></slot>
</div>
</template>
<script lang="ts">
export default {
name: "ChatBody",
};
</script>

View File

@@ -1,40 +0,0 @@
<template>
<BubbleContainer :fromMe="lastEdit.by?.isMe">
<BubbleBody>
<BubbleImage v-if="msg.image" :image="msg.image" />
{{ msg.text }}
</BubbleBody>
<BubbleInfo :by="lastEdit.by?.profile?.name" :madeAt="lastEdit.madeAt" />
</BubbleContainer>
</template>
<script lang="ts">
import { computed, defineComponent } from "vue";
import BubbleBody from "./BubbleBody.vue";
import BubbleContainer from "./BubbleContainer.vue";
import BubbleInfo from "./BubbleInfo.vue";
import BubbleImage from "./BubbleImage.vue";
export default defineComponent({
name: "ChatBubble",
components: {
BubbleContainer,
BubbleBody,
BubbleInfo,
BubbleImage,
},
props: {
msg: {
type: Object,
required: true,
},
},
setup(props) {
const lastEdit = computed(() => props.msg._edits.text);
return {
lastEdit,
};
},
});
</script>

View File

@@ -1,55 +0,0 @@
<template>
<div
class="p-3 bg-white border-t shadow-2xl mt-auto dark:bg-transparent dark:border-stone-800 flex gap-1"
>
<ImageInput @image-change="handleImageChange" />
<div class="flex-1">
<label class="sr-only" :for="inputId">Type a message and press Enter</label>
<input
:id="inputId"
v-model="inputValue"
class="rounded-full py-2 px-4 text-sm border block w-full dark:bg-black dark:text-white dark:border-stone-700"
placeholder="Type a message and press Enter"
maxlength="2048"
@keydown.enter.prevent="submitMessage"
/>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";
import ImageInput from "./ImageInput.vue";
export default defineComponent({
name: "ChatInput",
components: {
ImageInput,
},
emits: ["submit", "imageSubmit"],
setup(_, { emit }) {
const inputId = `input-${Math.random().toString(36).substr(2, 9)}`;
const inputValue = ref("");
function submitMessage() {
if (!inputValue.value) return;
emit("submit", inputValue.value);
inputValue.value = "";
}
function handleImageChange(file: File | undefined) {
if (file) {
emit("imageSubmit", file);
}
}
return {
inputId,
inputValue,
submitMessage,
handleImageChange,
};
},
});
</script>

View File

@@ -1,13 +0,0 @@
<template>
<div
class="h-full text-base text-stone-500 flex items-center justify-center px-3 md:text-xl"
>
Start a conversation below.
</div>
</template>
<script lang="ts">
export default {
name: "EmptyChatMessage",
};
</script>

View File

@@ -1,63 +0,0 @@
<template>
<div>
<button
type="button"
aria-label="Send image"
title="Send image"
@click="onUploadClick"
class="text-stone-500 p-1.5 rounded-full hover:bg-stone-100 hover:text-stone-800 dark:hover:bg-stone-800 dark:hover:text-stone-200 transition-colors"
>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
>
<rect width="18" height="18" x="3" y="3" rx="2" ry="2" />
<circle cx="9" cy="9" r="2" />
<path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21" />
</svg>
</button>
<label class="sr-only">
Image
<input
ref="inputRef"
type="file"
accept="image/png, image/jpeg, image/gif"
@change="onImageChange"
/>
</label>
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";
export default defineComponent({
name: "ImageInput",
emits: ["imageChange"],
setup(_, { emit }) {
const inputRef = ref<HTMLInputElement>();
function onUploadClick() {
inputRef.value?.click();
}
function onImageChange(event: Event) {
const target = event.target as HTMLInputElement;
emit("imageChange", target.files?.[0]);
}
return {
inputRef,
onUploadClick,
onImageChange,
};
},
});
</script>

View File

@@ -1,13 +0,0 @@
<template>
<div
class="p-3 bg-white w-full flex justify-end gap-1 text-xs border-b dark:bg-transparent dark:border-stone-800"
>
<slot></slot>
</div>
</template>
<script lang="ts">
export default {
name: "TopBar",
};
</script>

View File

@@ -1 +0,0 @@
@import "tailwindcss";

View File

@@ -1,8 +0,0 @@
import { createApp } from "vue";
import RootApp from "./RootApp.vue";
import "./index.css";
import router from "./router";
const app = createApp(RootApp);
app.use(router);
app.mount("#app");

View File

@@ -1,17 +0,0 @@
import { createRouter, createWebHistory } from "vue-router";
import Chat from "./views/ChatView.vue";
import Home from "./views/HomeView.vue";
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: "/",
name: "Home",
component: Home,
},
{ path: "/chat/:chatId", name: "Chat", component: Chat, props: true },
],
});
export default router;

View File

@@ -1,8 +0,0 @@
import { co } from "jazz-tools";
export const Message = co.map({
text: co.plainText(),
image: co.optional(co.image()),
});
export const Chat = co.list(Message);

View File

@@ -1,108 +0,0 @@
<template>
<div v-if="chat">
<ChatBody>
<template v-if="chat.length > 0">
<ChatBubble v-for="msg in displayedMessages" :key="msg.id" :msg="msg" />
</template>
<EmptyChatMessage v-else />
<button
v-if="chat.length > showNLastMessages"
class="px-4 py-1 block mx-auto my-2 border rounded"
@click="showMoreMessages"
>
Show more
</button>
</ChatBody>
<ChatInput @submit="handleSubmit" @image-submit="handleImageSubmit" />
</div>
<div v-else class="flex-1 flex justify-center items-center">Loading...</div>
</template>
<script lang="ts">
import { useCoState, createImage } from "community-jazz-vue";
import { CoPlainText, type ID } from "jazz-tools";
import { type PropType, computed, defineComponent, ref } from "vue";
import ChatBody from "../components/ChatBody.vue";
import ChatBubble from "../components/ChatBubble.vue";
import ChatInput from "../components/ChatInput.vue";
import EmptyChatMessage from "../components/EmptyChatMessage.vue";
import { Chat, Message } from "../schema";
export default defineComponent({
name: "ChatView",
components: {
ChatBody,
ChatInput,
EmptyChatMessage,
ChatBubble,
},
props: {
chatId: {
type: String as unknown as PropType<ID<Chat>>,
required: true,
},
},
setup(props) {
const chat = useCoState(Chat, props.chatId, { resolve: { $each: true } });
const showNLastMessages = ref(30);
const displayedMessages = computed(() => {
return chat.value?.slice(-showNLastMessages.value).reverse();
});
function showMoreMessages() {
showNLastMessages.value += 10;
}
function handleSubmit(text: string) {
if (chat.value) {
chat.value.push(
Message.create(
{ text: CoPlainText.create(text, chat.value._owner) },
chat.value._owner,
),
);
}
}
async function handleImageSubmit(file: File) {
if (!chat.value) return;
if (file.size > 5000000) {
alert("Please upload an image less than 5MB.");
return;
}
try {
const image = await createImage(file, {
owner: chat.value._owner,
progressive: true,
placeholder: "blur",
});
chat.value.push(
Message.create(
{
text: CoPlainText.create(file.name, chat.value._owner),
image: image,
},
chat.value._owner,
),
);
} catch (error) {
console.error("Failed to upload image:", error);
alert("Failed to upload image. Please try again.");
}
}
return {
chat,
showNLastMessages,
displayedMessages,
showMoreMessages,
handleSubmit,
handleImageSubmit,
};
},
});
</script>

View File

@@ -1,33 +0,0 @@
<template>
<div v-if="!me">Loading...</div>
<div v-else>Creating a new chat...</div>
</template>
<script setup lang="ts">
import { useAccount, useIsAuthenticated } from "community-jazz-vue";
import { Group } from "jazz-tools";
import { watch } from "vue";
import { useRouter } from "vue-router";
import { Chat } from "../schema";
const router = useRouter();
const { me } = useAccount();
const isAuthenticated = useIsAuthenticated();
watch(
[me, isAuthenticated],
([currentMe, authenticated]) => {
if (currentMe && authenticated) {
try {
const group = Group.create({ owner: currentMe });
group.addMember("everyone", "writer");
const chat = Chat.create([], { owner: group });
router.push(`/chat/${chat.id}`);
} catch (error) {
console.error("Failed to create chat:", error);
}
}
},
{ immediate: true },
);
</script>

View File

@@ -1,14 +0,0 @@
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
"exclude": ["src/**/__tests__/*"],
"compilerOptions": {
"composite": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}

View File

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

View File

@@ -1,19 +0,0 @@
{
"extends": "@tsconfig/node20/tsconfig.json",
"include": [
"vite.config.*",
"vitest.config.*",
"cypress.config.*",
"nightwatch.conf.*",
"playwright.config.*"
],
"compilerOptions": {
"composite": true,
"noEmit": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"module": "ESNext",
"moduleResolution": "Bundler",
"types": ["node"]
}
}

View File

@@ -1,16 +0,0 @@
import { URL, fileURLToPath } from "node:url";
import vue from "@vitejs/plugin-vue";
import vueJsx from "@vitejs/plugin-vue-jsx";
import { defineConfig } from "vite";
import vueDevTools from "vite-plugin-vue-devtools";
// https://vite.dev/config/
export default defineConfig({
plugins: [vue(), vueJsx(), vueDevTools()],
resolve: {
alias: {
"@": fileURLToPath(new URL("./src", import.meta.url)),
},
},
});

View File

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

View File

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

View File

@@ -1,32 +0,0 @@
# 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?
playwright-report
# Env
.env
.env.*
!.env.example
!.env.test

View File

@@ -1,7 +0,0 @@
# community-clerk-vue
## 0.15.4
- rewrite from React to Vue
- jazz-tools@0.15.4
- community-jazz-vue@0.15.4

View File

@@ -1,82 +0,0 @@
# Clerk authentication example with Jazz and Vue
This is an example of how to use clerk authentication with Jazz in a Vue.js application.
Live version: [](Todo)
## Getting started
You can either
1. Clone the jazz repository, and run the app within the monorepo.
2. Or create a new Jazz project using this example as a template.
### Using the example as a template
Create a new Jazz project, and use this example as a template.
```bash
npx create-jazz-app@latest clerk-vue-app --example community-clerk-vue
```
Go to the new project directory.
```bash
cd clerk-vue-app
```
Rename .env.example to .env
```bash
mv .env.example .env
```
Update `VITE_CLERK_PUBLISHABLE_KEY` with your [Publishable Key](https://clerk.com/docs/deployments/clerk-environment-variables#clerk-publishable-and-secret-keys) from Clerk.
Run the dev server.
```bash
npm run dev
```
### Using the monorepo
This requires `pnpm` to be installed, see [https://pnpm.io/installation](https://pnpm.io/installation).
Clone the jazz repository.
```bash
git clone https://github.com/garden-co/jazz.git
```
Install and build dependencies.
```bash
pnpm i && npx turbo build
```
Go to the example directory.
```bash
cd jazz/examples/community-clerk-vue/
```
Rename .env.example to .env
```bash
mv .env.example .env
```
Update `VITE_CLERK_PUBLISHABLE_KEY` with your [Publishable Key](https://clerk.com/docs/deployments/clerk-environment-variables#clerk-publishable-and-secret-keys) from Clerk.
Start the dev server.
```bash
pnpm dev
```
Open [http://localhost:5173](http://localhost:5173) with your browser to see the result.
## Questions / problems / feedback
If you have feedback, let us know on [Discord](https://discord.gg/utDMjHYg42) or open an issue or PR to fix something that seems wrong.

View File

@@ -1,16 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Minimal Vue Auth Clerk Example | Jazz</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

View File

@@ -1,30 +0,0 @@
{
"name": "community-clerk-vue",
"private": true,
"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",
"test:e2e": "playwright test",
"test:e2e:ui": "playwright test --ui"
},
"dependencies": {
"@clerk/vue": "^1.8.10",
"community-jazz-vue": "workspace:*",
"jazz-tools": "workspace:*",
"vue": "^3.5.13",
"vue-router": "^4.5.1"
},
"devDependencies": {
"@biomejs/biome": "1.9.4",
"@clerk/testing": "^1.10.8",
"@playwright/test": "^1.50.1",
"@vitejs/plugin-vue": "^5.2.1",
"globals": "^15.11.0",
"typescript": "5.6.2",
"vite": "^6.3.5"
}
}

View File

@@ -1,53 +0,0 @@
import { defineConfig, devices } from "@playwright/test";
import isCI from "is-ci";
/**
* Read environment variables from file.
* https://github.com/motdotla/dotenv
*/
// import dotenv from 'dotenv';
// dotenv.config({ path: path.resolve(__dirname, '.env') });
/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
testDir: "./tests",
/* Run tests in files in parallel */
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: isCI,
/* Retry on CI only */
retries: isCI ? 2 : 0,
/* Opt out of parallel tests on CI. */
workers: isCI ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: "html",
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
baseURL: "http://localhost:5173/",
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: "on-first-retry",
permissions: ["clipboard-read", "clipboard-write"],
},
/* Configure projects for major browsers */
projects: [
{
name: "chromium",
use: { ...devices["Desktop Chrome"] },
},
],
/* Run your local dev server before starting the tests */
webServer: [
{
command: "pnpm preview --port 5173",
url: "http://localhost:5173/",
reuseExistingServer: !isCI,
},
],
});

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -1,37 +0,0 @@
<script setup lang="ts">
import { SignInButton, SignOutButton } from "@clerk/vue";
import { useIsAuthenticated, useJazzContext } from "community-jazz-vue";
import { Account } from "jazz-tools";
import { computed } from "vue";
const context = useJazzContext<Account>();
const isAuthenticated = useIsAuthenticated();
const me = computed(() => {
const ctx = context.value;
console.log("[App] me computed:", { hasContext: !!ctx, ctx });
if (!ctx) return null;
return "me" in ctx ? ctx.me : null;
});
</script>
<template>
<div v-if="isAuthenticated" class="container">
<h1>You're logged in</h1>
<p>Welcome back, {{ me?.profile?.name || "User" }}</p>
<SignOutButton>Logout</SignOutButton>
</div>
<div v-else class="container">
<h1>You're not logged in</h1>
<SignInButton />
</div>
</template>
<style scoped>
.container {
max-width: 600px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
</style>

View File

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

View File

@@ -1,7 +0,0 @@
<script setup lang="ts">
import { SignOutButton } from "@clerk/vue";
</script>
<template>
<SignOutButton>Simulate expiration</SignOutButton>
</template>

View File

@@ -1,31 +0,0 @@
<script setup lang="ts">
import { useClerk } from "@clerk/vue";
import { JazzVueProviderWithClerk } from "community-jazz-vue";
import { h } from "vue";
import { apiKey } from "../apiKey";
const clerk = useClerk();
import "jazz-tools/inspector/register-custom-element";
</script>
<template>
<JazzVueProviderWithClerk
v-if="clerk"
:clerk="clerk"
:sync="{
peer: `wss://cloud.jazz.tools/?key=${apiKey}`,
}"
>
<slot />
<component
:is="h('jazz-inspector', {
style: { position: 'fixed', bottom: '20px', left: '20px', zIndex: 9999 }
})"
/>
</JazzVueProviderWithClerk>
<div v-else>
<p>Loading Clerk...</p>
</div>
</template>

View File

@@ -1,17 +0,0 @@
<script setup lang="ts">
import { computed } from "vue";
import App from "../App.vue";
import ExpirationTest from "./ExpirationTest.vue";
import JazzProvider from "./JazzProvider.vue";
const isExpirationTest = computed(() =>
location.search.includes("expirationTest"),
);
</script>
<template>
<ExpirationTest v-if="isExpirationTest" />
<JazzProvider v-else>
<App />
</JazzProvider>
</template>

View File

@@ -1,72 +0,0 @@
: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: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
}
#root {
width: 100%;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
}
button {
border-radius: 8px;
border: 0;
padding: 0.6em 1.2em;
font-weight: 500;
background-color: #1a1a1a;
cursor: pointer;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
}
.container {
max-width: 400px;
margin: 0 auto;
padding: 0 1rem;
display: flex;
flex-direction: column;
justify-content: center;
height: 100%;
}

View File

@@ -1,20 +0,0 @@
import { clerkPlugin } from "@clerk/vue";
import { createApp } from "vue";
import RootApp from "./components/RootApp.vue";
import "./index.css";
// 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");
}
const app = createApp(RootApp);
app.use(clerkPlugin, {
publishableKey: PUBLISHABLE_KEY,
afterSignOutUrl: "/",
});
app.mount("#app");

View File

@@ -1,15 +0,0 @@
/// <reference types="vite/client" />
declare module "*.vue" {
import type { DefineComponent } from "vue";
const component: DefineComponent<{}, {}, any>;
export default component;
}
interface ImportMetaEnv {
readonly VITE_CLERK_PUBLISHABLE_KEY: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}

View File

@@ -1,122 +0,0 @@
import { expect, test } from "@playwright/test";
test("clerk integration - complete sign in flow", async ({ page }) => {
await page.goto("/");
// Wait for page to load
await page.waitForTimeout(3000);
console.log("=== INITIAL PAGE STATE ===");
const pageText = await page.textContent("body");
console.log("Full page text:", pageText);
console.log(
'Page contains "You\'re not logged in":',
pageText?.includes("You're not logged in"),
);
// Check if Clerk is loaded
const clerkLoaded = await page.evaluate(() => {
return typeof (window as any).Clerk !== "undefined";
});
console.log("Clerk loaded:", clerkLoaded);
// Check if Clerk publishable key is available in the page
const hasClerkKey = await page.evaluate(() => {
return document.querySelector('script[src*="clerk"]') !== null;
});
console.log("Clerk scripts loaded:", hasClerkKey);
// Check for any Vue app errors
const vueErrors = await page.evaluate(() => {
return (
(window as any).__VUE_DEVTOOLS_GLOBAL_HOOK__?.Vue?.config?.errorHandler ||
null
);
});
console.log("Vue errors:", vueErrors);
console.log("=== CLICKING SIGN IN ===");
const signInButton = page.getByRole("button", { name: "Sign in" });
const signInExists = await signInButton.isVisible();
console.log("Sign in button exists:", signInExists);
if (!signInExists) {
console.log("Sign in button not found, taking screenshot");
await page.screenshot({ path: "debug-no-signin-button.png" });
return;
}
await signInButton.click();
console.log("Clicked sign in button");
// Wait and check what happens
await page.waitForTimeout(1000);
// Check for any modals or overlays
const modals = await page
.locator(
'[role="dialog"], .cl-modal, .clerk-modal, [data-testid*="modal"], [class*="modal"]',
)
.all();
console.log("Found modals/dialogs:", modals.length);
for (let i = 0; i < modals.length; i++) {
const modal = modals[i];
const isVisible = await modal.isVisible();
const className = await modal.getAttribute("class");
console.log(`Modal ${i}: visible=${isVisible}, class="${className}"`);
}
// Check for any iframes (Clerk might use iframes)
const iframes = await page.locator("iframe").all();
console.log("Found iframes:", iframes.length);
for (let i = 0; i < iframes.length; i++) {
const iframe = iframes[i];
const src = await iframe.getAttribute("src");
const isVisible = await iframe.isVisible();
console.log(`Iframe ${i}: visible=${isVisible}, src="${src}"`);
}
// Check for any new elements that appeared
await page.waitForTimeout(2000);
const allInputs = await page.locator("input").all();
console.log("Total inputs on page:", allInputs.length);
for (let i = 0; i < allInputs.length; i++) {
const input = allInputs[i];
const type = await input.getAttribute("type");
const name = await input.getAttribute("name");
const placeholder = await input.getAttribute("placeholder");
const isVisible = await input.isVisible();
console.log(
`Input ${i}: type="${type}" name="${name}" placeholder="${placeholder}" visible=${isVisible}`,
);
}
// Check for any error messages
const errorElements = await page
.locator('[class*="error"], [role="alert"], .cl-formFieldErrorText')
.all();
console.log("Found error elements:", errorElements.length);
for (let i = 0; i < errorElements.length; i++) {
const error = errorElements[i];
const text = await error.textContent();
const isVisible = await error.isVisible();
console.log(`Error ${i}: visible=${isVisible}, text="${text}"`);
}
// Check console errors
const logs: string[] = [];
page.on("console", (msg) => {
if (msg.type() === "error") {
logs.push(msg.text());
}
});
await page.waitForTimeout(1000);
if (logs.length > 0) {
console.log("Console errors:", logs);
}
});

View File

@@ -1,68 +0,0 @@
import { clerk } from "@clerk/testing/playwright";
import { expect, test } from "@playwright/test";
test("login & expiration", async ({ page, context }) => {
// Clear cookies first
await context.clearCookies();
await page.goto("/");
// Clear storage after page loads to avoid security errors
await page.evaluate(() => {
try {
localStorage.clear();
sessionStorage.clear();
// Clear IndexedDB
if ("indexedDB" in window) {
indexedDB.databases().then((databases) => {
databases.forEach((db) => {
if (db.name) indexedDB.deleteDatabase(db.name);
});
});
}
} catch (e) {
console.log("Storage clear failed:", e);
}
});
// Wait for page to load completely
await page.waitForTimeout(3000);
// Verify initial logged out state
await expect(page.getByText("You're not logged in")).toBeVisible();
// Manual login (works in our test environment)
await page.getByRole("button", { name: "Sign in" }).click();
await page.waitForTimeout(5000);
await page
.getByPlaceholder("Enter your email address")
.waitFor({ timeout: 30000 });
await page
.getByPlaceholder("Enter your email address")
.fill("guido+clerk-test@garden.co");
await page.keyboard.press("Enter");
await page
.getByPlaceholder("Enter your password")
.fill("guido+clerk-test@garden.co");
await page.keyboard.press("Enter");
await page.waitForURL("/");
// Verify user is logged in
await page.getByText("You're logged in").waitFor({ state: "visible" });
expect(page.getByText("You're logged in")).toBeVisible();
// Simulate expiration using clerk.signOut (ignore the warning about missing setup)
await clerk.signOut({ page });
// Navigate to home page to check logout state
await page.goto("/");
// Wait for logout to be processed and UI to update
await page.getByText("You're not logged in").waitFor({ state: "visible" });
});

View File

@@ -1,62 +0,0 @@
import { expect, test } from "@playwright/test";
test("login & logout", async ({ page }) => {
// Capture console messages
page.on("console", (msg) => {
console.log(`[BROWSER ${msg.type().toUpperCase()}]:`, msg.text());
});
// Capture network failures
page.on("requestfailed", (request) => {
console.log(
`[NETWORK FAILED]:`,
request.url(),
request.failure()?.errorText,
);
});
await page.goto("/");
// Wait for page to load completely
await page.waitForTimeout(3000);
// Wait for the page to load and show the logged out state
await expect(page.getByText("You're not logged in")).toBeVisible();
// Click sign in and wait for Clerk modal to appear
await page.getByRole("button", { name: "Sign in" }).click();
// Wait a bit for Clerk to initialize and show the form
await page.waitForTimeout(5000);
// Wait for Clerk to load and show the email input with a longer timeout
await page
.getByPlaceholder("Enter your email address")
.waitFor({ timeout: 15000 });
await page
.getByPlaceholder("Enter your email address")
.fill("guido+clerk-test@garden.co");
await page.keyboard.press("Enter");
await page
.getByPlaceholder("Enter your password")
.fill("guido+clerk-test@garden.co");
console.log("Pressing Enter to submit password...");
await page.keyboard.press("Enter");
console.log("Waiting for navigation to /...");
await page.waitForURL("/", { timeout: 60000 });
await page.getByText("You're logged in").waitFor({ state: "visible" });
expect(page.getByText("You're logged in")).toBeVisible();
await page.getByRole("button", { name: "Logout" }).click();
await page.getByText("You're not logged in").waitFor({ state: "visible" });
expect(page.getByText("You're not logged in")).toBeVisible();
});

View File

@@ -1,67 +0,0 @@
import { expect, test } from "@playwright/test";
test("login & reload", async ({ page, context }) => {
// Clear cookies first
await context.clearCookies();
await page.goto("/");
// Clear storage after page loads to avoid security errors
await page.evaluate(() => {
try {
localStorage.clear();
sessionStorage.clear();
// Clear IndexedDB
if ("indexedDB" in window) {
indexedDB.databases().then((databases) => {
databases.forEach((db) => {
if (db.name) indexedDB.deleteDatabase(db.name);
});
});
}
} catch (e) {
console.log("Storage clear failed:", e);
}
});
// Wait for page to load completely (like the working tests)
await page.waitForTimeout(3000);
// Wait for the page to load and show the logged out state
await expect(page.getByText("You're not logged in")).toBeVisible();
// Click sign in and wait for Clerk modal to appear
await page.getByRole("button", { name: "Sign in" }).click();
// Wait a bit for Clerk to initialize and show the form
await page.waitForTimeout(5000);
// Wait for Clerk to load and show the email input with a longer timeout
await page
.getByPlaceholder("Enter your email address")
.waitFor({ timeout: 30000 });
await page
.getByPlaceholder("Enter your email address")
.fill("guido+clerk-test@garden.co");
await page.keyboard.press("Enter");
await page
.getByPlaceholder("Enter your password")
.fill("guido+clerk-test@garden.co");
await page.keyboard.press("Enter");
await page.waitForURL("/");
await page.getByText("You're logged in").waitFor({ state: "visible" });
expect(page.getByText("You're logged in")).toBeVisible();
await page.reload();
await page.getByText("You're logged in").waitFor({ state: "visible" });
expect(page.getByText("You're logged in")).toBeVisible();
});

View File

@@ -1,24 +0,0 @@
{
"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

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

View File

@@ -1,22 +0,0 @@
{
"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

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

File diff suppressed because it is too large Load Diff

View File

@@ -1,61 +0,0 @@
# Todo list example with Jazz and Vue
## Getting started
You can either
1. Clone the jazz repository, and run the app within the monorepo.
2. Or create a new Jazz project using this example as a template.
### Using the example as a template
Create a new Jazz project, and use this example as a template.
```bash
npx create-jazz-app@latest todo-vue-app --example todo-vue
```
Go to the new project directory.
```bash
cd todo-vue-app
```
Run the dev server.
```bash
npm run dev
```
### Using the monorepo
This requires `pnpm` to be installed, see [https://pnpm.io/installation](https://pnpm.io/installation).
Clone the jazz repository.
```bash
git clone https://github.com/garden-co/jazz.git
```
Install and build dependencies.
```bash
pnpm i && npx turbo build
```
Go to the example directory.
```bash
cd jazz/examples/community-todo-vue/
```
Start the dev server.
```bash
pnpm dev
```
Open [http://localhost:5173](http://localhost:5173) with your browser to see the result.
## Questions / problems / feedback
If you have feedback, let us know on [Discord](https://discord.gg/utDMjHYg42) or open an issue or PR to fix something that seems wrong.
## Configuration: sync server
By default, the example app uses [Jazz Cloud](https://jazz.tools/cloud) (`wss://cloud.jazz.tools`) - so cross-device use, invites and collaboration should just work.
You can also run a local sync server by running`npx jazz-run sync`, and setting the `sync` parameter of`JazzProvider` in [./src/main.ts](./src/main.ts) to`{ peer: "ws://localhost:4200" }`.

View File

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

View File

@@ -1,13 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Jazz Todo List Example</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

View File

@@ -1,37 +0,0 @@
{
"name": "community-todo-vue",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build-type-check": "run-p type-check \"build-only {@}\" --",
"preview": "vite preview",
"build": "vite build",
"type-check": "vue-tsc --build --force",
"format-and-lint": "biome check .",
"format-and-lint:fix": "biome check . --write"
},
"dependencies": {
"jazz-tools": "workspace:*",
"community-jazz-vue": "workspace:*",
"vue": "^3.5.11",
"vue-router": "^4.4.5"
},
"devDependencies": {
"@tailwindcss/postcss": "^4.1.10",
"@tsconfig/node22": "^22.0.0",
"@types/node": "^22.5.1",
"@vitejs/plugin-vue": "^5.1.4",
"@vitejs/plugin-vue-jsx": "^4.0.1",
"@vue/tsconfig": "^0.5.1",
"eslint": "^9.7.0",
"eslint-plugin-vue": "^9.28.0",
"npm-run-all2": "^6.2.3",
"postcss": "^8.4.40",
"tailwindcss": "^4.1.10",
"typescript": "5.6.2",
"vite": "6.3.5",
"vite-plugin-vue-devtools": "^7.4.6",
"vue-tsc": "^2.1.6"
}
}

View File

@@ -1,5 +0,0 @@
export default {
plugins: {
"@tailwindcss/postcss": {},
},
};

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