Compare commits
181 Commits
jazz-bette
...
cojson-tra
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
867cb6b7a5 | ||
|
|
0401fcf2a8 | ||
|
|
139a649279 | ||
|
|
9acccb5df2 | ||
|
|
fd90cdb49a | ||
|
|
df487d5335 | ||
|
|
1efe84c691 | ||
|
|
063553090e | ||
|
|
6dffe73bd2 | ||
|
|
68cb357a94 | ||
|
|
4f7bc91502 | ||
|
|
f61a120560 | ||
|
|
2f1307a0ba | ||
|
|
fa15ea56d1 | ||
|
|
1e58ecb3ac | ||
|
|
ceeabfaf89 | ||
|
|
70ce7c5736 | ||
|
|
6afdb16739 | ||
|
|
b0b2b85a6f | ||
|
|
28c19c134f | ||
|
|
0924c9baaa | ||
|
|
b2712e18a2 | ||
|
|
66894b63d7 | ||
|
|
b1a05143e3 | ||
|
|
fb761ce66d | ||
|
|
07a6c340dc | ||
|
|
0fea904dd0 | ||
|
|
373aef313f | ||
|
|
a584590ed8 | ||
|
|
0a830e29a9 | ||
|
|
efff4d0f4f | ||
|
|
ea2b01d8a2 | ||
|
|
55cb83e6e0 | ||
|
|
6290088fec | ||
|
|
b9c17b37db | ||
|
|
6c76ff8fbf | ||
|
|
3c6a2a6092 | ||
|
|
e8a950e61a | ||
|
|
e2cbf035de | ||
|
|
47599b6307 | ||
|
|
901d0762ee | ||
|
|
d1c1b0c5cc | ||
|
|
cf4ad7285d | ||
|
|
2983c7bd58 | ||
|
|
ab6328f767 | ||
|
|
e0555debde | ||
|
|
247f4556e7 | ||
|
|
7903c737f4 | ||
|
|
6145da5525 | ||
|
|
fc0a2e77a3 | ||
|
|
334fbbbb7f | ||
|
|
eaac1e6580 | ||
|
|
114898d8a9 | ||
|
|
cbc3f0cc65 | ||
|
|
29c487e288 | ||
|
|
0b0590a364 | ||
|
|
1eb01997d8 | ||
|
|
0dc8d511a1 | ||
|
|
962213c712 | ||
|
|
427df8fcbb | ||
|
|
c40aad55dc | ||
|
|
dfca5926de | ||
|
|
9815ec61f0 | ||
|
|
fca60d213e | ||
|
|
b4fdab475b | ||
|
|
2b043abffa | ||
|
|
958c122c36 | ||
|
|
5842838371 | ||
|
|
e136e1b696 | ||
|
|
2475a46578 | ||
|
|
44f653a64b | ||
|
|
f8437042a6 | ||
|
|
acd908fbc2 | ||
|
|
4e61d1d191 | ||
|
|
db23582b4c | ||
|
|
4b0b6d8a69 | ||
|
|
d450b394fa | ||
|
|
0abc96e400 | ||
|
|
7562354b29 | ||
|
|
6c085a3919 | ||
|
|
6afff848bc | ||
|
|
47059845cc | ||
|
|
a1735a8232 | ||
|
|
1f5750d8c4 | ||
|
|
f756ce26b5 | ||
|
|
84f5bdda74 | ||
|
|
ee7aefa97c | ||
|
|
b0895981ba | ||
|
|
94f636b2ee | ||
|
|
331ab070f6 | ||
|
|
13e73adfb9 | ||
|
|
9f6079b6c6 | ||
|
|
4033d78fa6 | ||
|
|
83af94c850 | ||
|
|
70fe856713 | ||
|
|
42e4afc42b | ||
|
|
0e6797b222 | ||
|
|
3634eaf8e9 | ||
|
|
58dfda3d0f | ||
|
|
d304b0bcb5 | ||
|
|
44f5a3f5a2 | ||
|
|
ebb3ce1c25 | ||
|
|
a67bba0dcf | ||
|
|
4a72c26e42 | ||
|
|
084cb5936d | ||
|
|
f1552b8262 | ||
|
|
6826ad8e45 | ||
|
|
7c1b757b62 | ||
|
|
326e1734a4 | ||
|
|
0cf027c91b | ||
|
|
e358881b76 | ||
|
|
cee8010918 | ||
|
|
cc877139ef | ||
|
|
56a9b89538 | ||
|
|
199c463e28 | ||
|
|
50e523d19c | ||
|
|
796ea24288 | ||
|
|
c8be86e823 | ||
|
|
41b7054aab | ||
|
|
101adcd024 | ||
|
|
a2854aeec9 | ||
|
|
bdc9aee689 | ||
|
|
2f53ae0ab8 | ||
|
|
f76c05448c | ||
|
|
585e7e8177 | ||
|
|
82d8d1d873 | ||
|
|
2c523c86ff | ||
|
|
6616668d4a | ||
|
|
8a3be85e97 | ||
|
|
1a7f2b7379 | ||
|
|
caac82dffd | ||
|
|
27b48378e5 | ||
|
|
cfd3c3ca5c | ||
|
|
41f26b7a4f | ||
|
|
c57ebb1cea | ||
|
|
259aded5cc | ||
|
|
1f5e091dd7 | ||
|
|
bbb1c44977 | ||
|
|
114c10bc77 | ||
|
|
cecdf29721 | ||
|
|
bd717fc0d7 | ||
|
|
739fff68b3 | ||
|
|
d49cab0afa | ||
|
|
ffebb4fdaf | ||
|
|
e2bb3b8015 | ||
|
|
0b09d23bd1 | ||
|
|
8ff3e234c1 | ||
|
|
6f6663d825 | ||
|
|
85dc6ba148 | ||
|
|
9b1d52d183 | ||
|
|
6247fac6c5 | ||
|
|
f27a2c541e | ||
|
|
2317a23fd4 | ||
|
|
26994684d7 | ||
|
|
14a5e036a4 | ||
|
|
5b1c1ca522 | ||
|
|
a9c8458c51 | ||
|
|
5f31d6cbe1 | ||
|
|
477fd8a62d | ||
|
|
90999ee709 | ||
|
|
38065f0cdf | ||
|
|
c77d16cdb3 | ||
|
|
9410084e6a | ||
|
|
e67c5838a9 | ||
|
|
a141cbc7f7 | ||
|
|
6a5352cf3a | ||
|
|
27762637ee | ||
|
|
dcebe34891 | ||
|
|
99d510815f | ||
|
|
928962c08b | ||
|
|
cdadd6db1d | ||
|
|
d45b8ae70b | ||
|
|
445a58c864 | ||
|
|
1895b474ea | ||
|
|
8990ff39a5 | ||
|
|
71e4c97255 | ||
|
|
577e960e28 | ||
|
|
f232f75d40 | ||
|
|
e1a7f829b4 | ||
|
|
f82177b9da | ||
|
|
c1c553bad0 |
@@ -6,7 +6,6 @@
|
||||
"fixed": [
|
||||
[
|
||||
"cojson",
|
||||
"cojson-storage",
|
||||
"cojson-storage-indexeddb",
|
||||
"cojson-storage-sqlite",
|
||||
"cojson-transport-ws",
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"tests/jazz-svelte/src/**",
|
||||
"examples/*svelte*/**",
|
||||
"starters/*svelte*/**",
|
||||
"examples/jazz-paper-scissors/src/routeTree.gen.ts",
|
||||
"examples/server-worker-inbox/src/routeTree.gen.ts",
|
||||
"homepage/homepage/**",
|
||||
"**/package.json"
|
||||
]
|
||||
@@ -56,7 +56,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"include": ["packages/cojson-storage*/**", "cojson-transport-ws/**"],
|
||||
"include": ["packages/cojson/src/storage/*/**", "cojson-transport-ws/**"],
|
||||
"linter": {
|
||||
"enabled": true,
|
||||
"rules": {
|
||||
|
||||
@@ -13,13 +13,13 @@
|
||||
"@bacons/text-decoder": "^0.0.0",
|
||||
"@bam.tech/react-native-image-resizer": "^3.0.11",
|
||||
"@react-native-community/netinfo": "11.4.1",
|
||||
"expo": "~53.0.9",
|
||||
"expo": "54.0.0-canary-20250701-6a945c5",
|
||||
"expo-clipboard": "^7.1.4",
|
||||
"expo-secure-store": "~14.2.3",
|
||||
"expo-sqlite": "~15.2.10",
|
||||
"jazz-tools": "workspace:*",
|
||||
"react": "19.0.0",
|
||||
"react-native": "0.79.2",
|
||||
"react": "19.1.0",
|
||||
"react-native": "0.80.0",
|
||||
"react-native-get-random-values": "^1.11.0",
|
||||
"readable-stream": "^4.7.0"
|
||||
},
|
||||
@@ -29,4 +29,4 @@
|
||||
"typescript": "~5.8.3"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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: 5d41e1df061200130dd326e55cdfdf94b0289c6e
|
||||
ReactCodegen: d82f538f70f00484d418803f74b5a0ea09cc8689
|
||||
ReactCommon: b028d09a66e60ebd83ca59d8cc9a1216360db147
|
||||
RNCClipboard: 54ff19965d7c816febbafe5f520c2c3e7b677a49
|
||||
RNScreens: ee2abe7e0c548eed14e92742e81ed991165c56aa
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
"@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.2",
|
||||
"@react-native-clipboard/clipboard": "1.16.3",
|
||||
"@react-native-community/netinfo": "11.4.1",
|
||||
"@react-navigation/native": "7.1.14",
|
||||
"@react-navigation/native-stack": "7.3.19",
|
||||
@@ -40,7 +40,7 @@
|
||||
"@react-native/typescript-config": "0.80.0",
|
||||
"@rnx-kit/metro-config": "^2.0.1",
|
||||
"@rnx-kit/metro-resolver-symlinks": "^0.2.5",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react": "^19.1.0",
|
||||
"eslint": "^8.19.0",
|
||||
"pod-install": "^0.3.5",
|
||||
"prettier": "2.8.8",
|
||||
|
||||
@@ -1,5 +1,50 @@
|
||||
# passkey-svelte
|
||||
|
||||
## 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "chat-svelte",
|
||||
"version": "0.0.97",
|
||||
"version": "0.0.103",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -16,15 +16,15 @@
|
||||
"hash-slash": "workspace:*",
|
||||
"jazz-tools": "workspace:*",
|
||||
"lucide-react": "^0.274.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"zod": "3.25.28"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.50.1",
|
||||
"@tailwindcss/postcss": "^4.1.10",
|
||||
"@types/react": "19.0.0",
|
||||
"@types/react-dom": "19.0.0",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.0",
|
||||
"@vitejs/plugin-react-swc": "^3.10.1",
|
||||
"is-ci": "^3.0.1",
|
||||
"postcss": "^8.4.40",
|
||||
@@ -32,4 +32,4 @@
|
||||
"typescript": "5.6.2",
|
||||
"vite": "^6.3.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import { co, z } from "jazz-tools";
|
||||
import { co } from "jazz-tools";
|
||||
|
||||
export const Message = co.map({
|
||||
text: co.plainText(),
|
||||
image: z.optional(co.image()),
|
||||
image: co.optional(co.image()),
|
||||
});
|
||||
export type Message = co.loaded<typeof Message>;
|
||||
|
||||
|
||||
@@ -14,15 +14,15 @@
|
||||
"@bam.tech/react-native-image-resizer": "^3.0.11",
|
||||
"@clerk/clerk-expo": "^2.13.1",
|
||||
"@react-native-community/netinfo": "11.4.1",
|
||||
"expo": "~53.0.9",
|
||||
"expo": "54.0.0-canary-20250701-6a945c5",
|
||||
"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": "19.0.0",
|
||||
"react-native": "0.79.2",
|
||||
"react": "19.1.0",
|
||||
"react-native": "0.80.0",
|
||||
"react-native-get-random-values": "^1.11.0",
|
||||
"readable-stream": "^4.7.0"
|
||||
},
|
||||
@@ -32,4 +32,4 @@
|
||||
"typescript": "~5.8.3"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
}
|
||||
@@ -14,17 +14,17 @@
|
||||
"dependencies": {
|
||||
"@clerk/clerk-react": "^5.4.1",
|
||||
"jazz-tools": "workspace:*",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0"
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.50.1",
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@types/react": "19.0.0",
|
||||
"@types/react-dom": "19.0.0",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.0",
|
||||
"@vitejs/plugin-react": "^4.5.1",
|
||||
"globals": "^15.11.0",
|
||||
"typescript": "5.6.2",
|
||||
"vite": "^6.3.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,14 +11,14 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"jazz-tools": "workspace:*",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0"
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@tailwindcss/postcss": "^4.1.10",
|
||||
"@types/react": "19.0.0",
|
||||
"@types/react-dom": "19.0.0",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.0",
|
||||
"@vitejs/plugin-react": "^4.5.1",
|
||||
"globals": "^15.11.0",
|
||||
"is-ci": "^3.0.1",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { co, z } from "jazz-tools";
|
||||
import { co } from "jazz-tools";
|
||||
|
||||
export const JazzProfile = co.profile({
|
||||
file: z.optional(co.fileStream()),
|
||||
file: co.optional(co.fileStream()),
|
||||
});
|
||||
|
||||
export const JazzAccount = co.account({
|
||||
|
||||
@@ -12,16 +12,16 @@
|
||||
"dependencies": {
|
||||
"hash-slash": "workspace:*",
|
||||
"jazz-tools": "workspace:*",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0"
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@playwright/test": "^1.50.1",
|
||||
"@tailwindcss/forms": "^0.5.10",
|
||||
"@tailwindcss/postcss": "^4.1.10",
|
||||
"@types/react": "19.0.0",
|
||||
"@types/react-dom": "19.0.0",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.0",
|
||||
"@vitejs/plugin-react": "^4.5.1",
|
||||
"globals": "^15.11.0",
|
||||
"is-ci": "^3.0.1",
|
||||
|
||||
@@ -28,16 +28,16 @@ export const BubbleTeaOrder = co.map({
|
||||
addOns: ListOfBubbleTeaAddOns,
|
||||
deliveryDate: z.date(),
|
||||
withMilk: z.boolean(),
|
||||
instructions: z.optional(co.plainText()),
|
||||
instructions: co.optional(co.plainText()),
|
||||
});
|
||||
|
||||
export const DraftBubbleTeaOrder = co
|
||||
.map({
|
||||
baseTea: z.optional(z.literal([...BubbleTeaBaseTeaTypes])),
|
||||
addOns: z.optional(ListOfBubbleTeaAddOns),
|
||||
addOns: co.optional(ListOfBubbleTeaAddOns),
|
||||
deliveryDate: z.optional(z.date()),
|
||||
withMilk: z.optional(z.boolean()),
|
||||
instructions: z.optional(co.plainText()),
|
||||
instructions: co.optional(co.plainText()),
|
||||
})
|
||||
.withHelpers((Self) => ({
|
||||
hasChanges(order: Loaded<typeof Self> | undefined) {
|
||||
|
||||
@@ -11,14 +11,14 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"jazz-tools": "workspace:*",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0"
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@tailwindcss/postcss": "^4.1.10",
|
||||
"@types/react": "19.0.0",
|
||||
"@types/react-dom": "19.0.0",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.0",
|
||||
"@vitejs/plugin-react": "^4.5.1",
|
||||
"globals": "^15.11.0",
|
||||
"typescript": "5.6.2",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { co, z } from "jazz-tools";
|
||||
import { co } from "jazz-tools";
|
||||
|
||||
export const JazzProfile = co.profile({
|
||||
image: z.optional(co.image()),
|
||||
image: co.optional(co.image()),
|
||||
});
|
||||
|
||||
export const JazzAccount = co.account({
|
||||
|
||||
@@ -17,15 +17,15 @@
|
||||
"cojson-transport-ws": "workspace:*",
|
||||
"hash-slash": "workspace:*",
|
||||
"jazz-tools": "workspace:*",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"react-use": "^17.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.50.1",
|
||||
"@tailwindcss/postcss": "^4.1.10",
|
||||
"@types/react": "19.0.0",
|
||||
"@types/react-dom": "19.0.0",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.0",
|
||||
"@vitejs/plugin-react-swc": "^3.10.1",
|
||||
"postcss": "^8.4.40",
|
||||
"tailwindcss": "^4.1.10",
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
"dependencies": {
|
||||
"jazz-tools": "workspace:*",
|
||||
"next": "15.3.2",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0"
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4.1.10",
|
||||
@@ -21,4 +21,4 @@
|
||||
"tailwindcss": "^4.1.10",
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,14 +13,14 @@
|
||||
"dependencies": {
|
||||
"@react-spring/web": "^9.7.5",
|
||||
"jazz-tools": "workspace:*",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"zod": "3.25.28"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@types/react": "19.0.0",
|
||||
"@types/react-dom": "19.0.0",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.0",
|
||||
"@vitejs/plugin-react": "^4.5.1",
|
||||
"globals": "^15.11.0",
|
||||
"is-ci": "^3.0.1",
|
||||
|
||||
@@ -12,14 +12,14 @@
|
||||
"dependencies": {
|
||||
"@clerk/clerk-react": "^5.4.1",
|
||||
"jazz-tools": "workspace:*",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"tailwindcss": "^4.1.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@types/react": "19.0.0",
|
||||
"@types/react-dom": "19.0.0",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.0",
|
||||
"@vitejs/plugin-react": "^4.5.1",
|
||||
"globals": "^15.11.0",
|
||||
"typescript": "5.6.2",
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
"clsx": "^2.1.1",
|
||||
"jazz-tools": "workspace:*",
|
||||
"lucide-react": "^0.274.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"react-router": "^6.16.0",
|
||||
"react-router-dom": "^6.16.0",
|
||||
"tailwind-merge": "^1.14.0"
|
||||
@@ -32,8 +32,8 @@
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.50.1",
|
||||
"@tailwindcss/postcss": "^4.1.10",
|
||||
"@types/react": "19.0.0",
|
||||
"@types/react-dom": "19.0.0",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.0",
|
||||
"@vitejs/plugin-react-swc": "^3.10.1",
|
||||
"postcss": "^8.4.27",
|
||||
"tailwindcss": "^4.1.10",
|
||||
|
||||
@@ -66,7 +66,7 @@ export const MusicaAccountRoot = co.map({
|
||||
// track and playlist
|
||||
// You can also add the position in time if you want make it possible
|
||||
// to resume the song
|
||||
activeTrack: z.optional(MusicTrack),
|
||||
activeTrack: co.optional(MusicTrack),
|
||||
activePlaylist: Playlist,
|
||||
|
||||
exampleDataLoaded: z.optional(z.boolean()),
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
"dependencies": {
|
||||
"jazz-tools": "workspace:*",
|
||||
"lucide-react": "^0.274.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"react-router": "^6.16.0",
|
||||
"react-router-dom": "^6.16.0"
|
||||
},
|
||||
@@ -24,8 +24,8 @@
|
||||
"@playwright/test": "^1.50.1",
|
||||
"@tailwindcss/forms": "^0.5.10",
|
||||
"@tailwindcss/postcss": "^4.1.10",
|
||||
"@types/react": "19.0.0",
|
||||
"@types/react-dom": "19.0.0",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.0",
|
||||
"@vitejs/plugin-react": "^4.5.1",
|
||||
"globals": "^15.11.0",
|
||||
"postcss": "^8.4.40",
|
||||
|
||||
@@ -11,14 +11,14 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"jazz-tools": "workspace:*",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"tailwindcss": "^4.1.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@types/react": "19.0.0",
|
||||
"@types/react-dom": "19.0.0",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.0",
|
||||
"@vitejs/plugin-react": "^4.5.1",
|
||||
"globals": "^15.11.0",
|
||||
"typescript": "5.6.2",
|
||||
|
||||
@@ -12,14 +12,14 @@
|
||||
"dependencies": {
|
||||
"hash-slash": "workspace:*",
|
||||
"jazz-tools": "workspace:*",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"tailwindcss": "^4.1.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@types/react": "19.0.0",
|
||||
"@types/react-dom": "19.0.0",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.0",
|
||||
"@vitejs/plugin-react": "^4.5.1",
|
||||
"globals": "^15.11.0",
|
||||
"typescript": "5.6.2",
|
||||
|
||||
@@ -19,15 +19,15 @@
|
||||
"prosemirror-schema-list": "^1.5.1",
|
||||
"prosemirror-state": "^1.4.3",
|
||||
"prosemirror-view": "^1.39.1",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0"
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@playwright/test": "^1.50.1",
|
||||
"@tailwindcss/postcss": "^4.1.10",
|
||||
"@types/react": "19.0.0",
|
||||
"@types/react-dom": "19.0.0",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.0",
|
||||
"@vitejs/plugin-react": "^4.5.1",
|
||||
"globals": "^15.11.0",
|
||||
"is-ci": "^3.0.1",
|
||||
|
||||
@@ -22,15 +22,15 @@
|
||||
"clsx": "^2.1.1",
|
||||
"jazz-tools": "workspace:*",
|
||||
"lucide-react": "^0.509.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0"
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@playwright/test": "^1.50.1",
|
||||
"@tailwindcss/postcss": "^4.1.10",
|
||||
"@types/react": "19.0.0",
|
||||
"@types/react-dom": "19.0.0",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.0",
|
||||
"@vitejs/plugin-react": "^4.5.1",
|
||||
"globals": "^15.11.0",
|
||||
"is-ci": "^3.0.1",
|
||||
|
||||
3
examples/server-worker-http/vercel.json
Normal file
3
examples/server-worker-http/vercel.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"ignoreCommand": "echo true"
|
||||
}
|
||||
@@ -24,15 +24,15 @@
|
||||
"clsx": "^2.1.1",
|
||||
"jazz-tools": "workspace:*",
|
||||
"lucide-react": "^0.485.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"tailwind-merge": "^3.0.2",
|
||||
"tailwindcss": "^4.0.17",
|
||||
"tw-animate-css": "^1.2.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "19.0.0",
|
||||
"@types/react-dom": "19.0.0",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.0",
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"jazz-run": "workspace:*",
|
||||
"npm-run-all": "^4.1.5",
|
||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@@ -8,7 +8,7 @@ export type Player = co.loaded<typeof Player>;
|
||||
|
||||
export const Game = co.map({
|
||||
player1: Player,
|
||||
player2: z.optional(Player),
|
||||
player2: co.optional(Player),
|
||||
outcome: z.optional(z.literal(["player1", "player2", "draw"])),
|
||||
player1Score: z.number(),
|
||||
player2Score: z.number(),
|
||||
@@ -17,8 +17,8 @@ export type Game = co.loaded<typeof Game>;
|
||||
|
||||
export const WaitingRoom = co.map({
|
||||
account1: co.account(),
|
||||
account2: z.optional(co.account()),
|
||||
game: z.optional(Game),
|
||||
account2: co.optional(co.account()),
|
||||
game: co.optional(Game),
|
||||
});
|
||||
export type WaitingRoom = co.loaded<typeof WaitingRoom>;
|
||||
|
||||
@@ -47,7 +47,7 @@ export const JoinGameRequest = co.map({
|
||||
});
|
||||
export type JoinGameRequest = co.loaded<typeof JoinGameRequest>;
|
||||
|
||||
export const InboxMessage = z.discriminatedUnion("type", [
|
||||
export const InboxMessage = co.discriminatedUnion("type", [
|
||||
PlayIntent,
|
||||
NewGameIntent,
|
||||
CreateGameRequest,
|
||||
@@ -10,7 +10,6 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@faker-js/faker": "^9.7.0",
|
||||
"@radix-ui/react-checkbox": "^1.3.2",
|
||||
"@radix-ui/react-slot": "^1.2.3",
|
||||
"@radix-ui/react-toast": "^1.2.14",
|
||||
@@ -19,8 +18,8 @@
|
||||
"jazz-tools": "workspace:*",
|
||||
"lucide-react": "^0.274.0",
|
||||
"qrcode": "^1.5.3",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"react-router": "^6.16.0",
|
||||
"react-router-dom": "^6.16.0",
|
||||
"tailwind-merge": "^1.14.0",
|
||||
@@ -30,8 +29,8 @@
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4.1.10",
|
||||
"@types/qrcode": "^1.5.1",
|
||||
"@types/react": "19.0.0",
|
||||
"@types/react-dom": "19.0.0",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.0",
|
||||
"@vitejs/plugin-react-swc": "^3.10.1",
|
||||
"postcss": "^8.4.27",
|
||||
"tailwindcss": "^4.1.10",
|
||||
|
||||
@@ -23,7 +23,6 @@ import {
|
||||
ThemeProvider,
|
||||
TitleAndLogo,
|
||||
} from "./basicComponents/index.ts";
|
||||
import { TaskGenerator } from "./components/TaskGenerator.tsx";
|
||||
import { wordlist } from "./wordlist.ts";
|
||||
|
||||
/**
|
||||
@@ -93,10 +92,6 @@ export default function App() {
|
||||
path: "/invite/*",
|
||||
element: <p>Accepting invite...</p>,
|
||||
},
|
||||
{
|
||||
path: "/generate",
|
||||
element: <TaskGenerator />,
|
||||
},
|
||||
]);
|
||||
|
||||
// `useAcceptInvite()` is a hook that accepts an invite link from the URL hash,
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
import { TodoAccount } from "@/1_schema";
|
||||
import { FormEvent, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { generateRandomProject } from "../generate";
|
||||
|
||||
export function TaskGenerator() {
|
||||
const [isGenerating, setIsGenerating] = useState(false);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(e.currentTarget);
|
||||
const numTasks = Math.max(
|
||||
1,
|
||||
parseInt(formData.get("numTasks") as string) || 1,
|
||||
);
|
||||
|
||||
setIsGenerating(true);
|
||||
const project = generateRandomProject(numTasks);
|
||||
|
||||
const { root } = await TodoAccount.getMe().ensureLoaded({
|
||||
resolve: {
|
||||
root: {
|
||||
projects: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
root.projects.push(project.value);
|
||||
|
||||
await project.done;
|
||||
|
||||
navigate(`/project/${project.value.id}`);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="p-4 border rounded-lg shadow-xs bg-white">
|
||||
<h2 className="text-lg font-semibold mb-4">Generate Random Tasks</h2>
|
||||
<form onSubmit={handleSubmit} className="flex flex-col gap-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<label htmlFor="numTasks" className="text-sm font-medium">
|
||||
Number of tasks:
|
||||
</label>
|
||||
<input
|
||||
id="numTasks"
|
||||
name="numTasks"
|
||||
type="number"
|
||||
min="1"
|
||||
defaultValue={5}
|
||||
className="w-20 px-2 py-1 border rounded"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isGenerating}
|
||||
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 disabled:bg-blue-300"
|
||||
>
|
||||
{isGenerating ? "Generating..." : "Generate Tasks"}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -12,14 +12,14 @@
|
||||
"dependencies": {
|
||||
"@tailwindcss/forms": "^0.5.9",
|
||||
"jazz-tools": "workspace:*",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0"
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@tailwindcss/postcss": "^4.1.10",
|
||||
"@types/react": "19.0.0",
|
||||
"@types/react-dom": "19.0.0",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.0",
|
||||
"@vitejs/plugin-react": "^4.5.1",
|
||||
"globals": "^15.11.0",
|
||||
"tailwindcss": "^4.1.10",
|
||||
|
||||
@@ -27,28 +27,26 @@ export default function ButtonsPage() {
|
||||
return (
|
||||
<>
|
||||
<h3 className="text-lg mt-5 mb-2 font-bold">Variants</h3>
|
||||
<p className="mb-3">
|
||||
For compatibility the shadcn/ui variants are mapped to the design
|
||||
system.
|
||||
</p>
|
||||
|
||||
<p className="my-3">Buttons are styled with the variant prop.</p>
|
||||
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<Button variant="default">default</Button>
|
||||
<Button variant="link">link</Button>
|
||||
<Button variant="ghost">ghost</Button>
|
||||
<Button variant="outline">outline</Button>
|
||||
<Button variant="secondary">secondary</Button>
|
||||
<Button variant="destructive">destructive</Button>
|
||||
</div>
|
||||
|
||||
<h3 className="text-lg mt-5 mb-2 font-bold">Intents</h3>
|
||||
<p>
|
||||
We have extended the shadcn/ui variants to include more styles via the
|
||||
intent prop.
|
||||
<h3 className="text-lg mt-5 font-bold">Intents</h3>
|
||||
<p className="my-3">
|
||||
We have extended the variants to include more styles via the intent
|
||||
prop.
|
||||
</p>
|
||||
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
{/* <Button intent="default">default</Button> */}
|
||||
<Button intent="default">default</Button>
|
||||
<Button intent="muted">muted</Button>
|
||||
<Button intent="strong">strong</Button>
|
||||
<Button intent="primary">primary</Button>
|
||||
<Button intent="tip">tip</Button>
|
||||
<Button intent="info">info</Button>
|
||||
@@ -56,8 +54,6 @@ export default function ButtonsPage() {
|
||||
<Button intent="warning">warning</Button>
|
||||
<Button intent="alert">alert</Button>
|
||||
<Button intent="danger">danger</Button>
|
||||
<Button intent="muted">muted</Button>
|
||||
<Button intent="strong">strong</Button>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between items-center w-48 mt-10">
|
||||
@@ -89,7 +85,7 @@ export default function ButtonsPage() {
|
||||
|
||||
<p className="text-sm mt-2 mb-5">
|
||||
<strong>NB:</strong> Variants and styles are interchangeable. See the
|
||||
intent on each variant with the dropdown
|
||||
intent on each variant with the dropdown.
|
||||
</p>
|
||||
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
@@ -107,9 +103,19 @@ export default function ButtonsPage() {
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<p className="my-3">
|
||||
For compatibility the shadcn/ui variants are mapped to the design
|
||||
system.
|
||||
</p>
|
||||
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<Button variant="secondary">secondary</Button>
|
||||
<Button variant="destructive">destructive</Button>
|
||||
</div>
|
||||
|
||||
<h3 className="text-lg font-bold mt-5">Icons</h3>
|
||||
|
||||
<p>Buttons can also contain an icon and text.</p>
|
||||
<p className="my-3">Buttons can also contain an icon and text.</p>
|
||||
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<Button
|
||||
@@ -130,7 +136,7 @@ export default function ButtonsPage() {
|
||||
>
|
||||
outline info with icon
|
||||
</Button>
|
||||
<p className="col-span-2">
|
||||
<p className="col-span-2 my-2">
|
||||
Or just use the icon prop with any of the button variants, style
|
||||
variants and colors.
|
||||
</p>
|
||||
@@ -151,6 +157,7 @@ const buttonPropsTableData = {
|
||||
{
|
||||
prop: "intent?",
|
||||
types: [
|
||||
"default",
|
||||
"primary",
|
||||
"tip",
|
||||
"info",
|
||||
@@ -174,7 +181,7 @@ const buttonPropsTableData = {
|
||||
"secondary",
|
||||
"destructive",
|
||||
],
|
||||
default: "undefined",
|
||||
default: "default",
|
||||
},
|
||||
{
|
||||
prop: "icon?",
|
||||
|
||||
@@ -159,8 +159,8 @@ const styleClasses = (intent: Style, variant: Variant | undefined) => {
|
||||
inverted: `${styleToTextMap[intent]} ${colorToBgHoverMap30[styleToColorMap[intent] as VariantColor]} ${colorToBgMap[styleToColorMap[intent] as VariantColor]} ${colorToBgActiveMap50[styleToColorMap[intent] as VariantColor]} ${shadowClassesBase}`,
|
||||
ghost: `bg-transparent ${styleToTextMap[intent]} ${colorToBgHoverMap10[styleToColorMap[intent] as VariantColor]} ${colorToBgActiveMap25[styleToColorMap[intent] as VariantColor]}`,
|
||||
link: `bg-transparent ${styleToTextMap[intent]} underline underline-offset-2 p-0 hover:bg-transparent ${styleToTextHoverMap[intent]} ${styleToTextActiveMap[intent]} active:underline-stone-500`,
|
||||
secondary: `bg-stone-300 ${styleToTextMap[intent]} hover:bg-stone-400/80 active:bg-stone-500/80`,
|
||||
destructive: `bg-danger text-white hover:bg-red/80 active:bg-red/70`,
|
||||
secondary: variantClass("muted"),
|
||||
destructive: variantClass("danger"),
|
||||
default: `${styleToBgGradientColorMap["default"]} ${styleToBgGradientHoverMap["default"]} ${textColorVariant("default")} ${styleToButtonStateMap["default"]} ${shadowClassesBase} shadow-stone-400/20`,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -44,7 +44,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
|
||||
: icon && iconPosition === "right";
|
||||
|
||||
const inputClassName = clsx(
|
||||
"w-full rounded-md border px-3.5 py-2 shadow-sm",
|
||||
"w-full rounded-md border px-2.5 py-1 shadow-sm h-[36px]",
|
||||
"font-medium text-stone-900",
|
||||
"dark:text-white dark:bg-stone-925",
|
||||
);
|
||||
|
||||
@@ -21,7 +21,7 @@ export type Style =
|
||||
|
||||
export const sizeClasses = {
|
||||
sm: "text-sm py-1 px-2",
|
||||
md: "py-1.5 px-3",
|
||||
md: "py-1.5 px-3 h-[36px]",
|
||||
lg: "py-2 px-5 md:px-6 md:py-2.5",
|
||||
};
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ const MusicaAccountRoot = co.map({
|
||||
|
||||
const MusicaAccount = co.account({
|
||||
root: MusicaAccountRoot,
|
||||
profile: co.profile({}),
|
||||
profile: co.profile(),
|
||||
});
|
||||
type MusicaAccount = co.loaded<typeof MusicaAccount>
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ export const JazzAccountRoot = co.map({
|
||||
export const JazzAccount = co
|
||||
.account({
|
||||
root: JazzAccountRoot,
|
||||
profile: co.profile({}),
|
||||
profile: co.profile(),
|
||||
})
|
||||
.withMigration((account) => {
|
||||
if (account.root === undefined) {
|
||||
@@ -130,7 +130,7 @@ const JazzAccountRoot = co.map({
|
||||
|
||||
const JazzAccount = co.account({
|
||||
root: JazzAccountRoot,
|
||||
profile: co.profile({}),
|
||||
profile: co.profile(),
|
||||
});
|
||||
|
||||
// ---cut---
|
||||
|
||||
@@ -11,7 +11,7 @@ The main detail to understand when using Jazz server-side is that Server Workers
|
||||
|
||||
This lets you share CoValues with Server Workers, having precise access control by adding the Worker to `Groups` with specific roles just like you would with other users.
|
||||
|
||||
[See the full example here.](https://github.com/garden-co/jazz/tree/main/examples/jazz-paper-scissors)
|
||||
[See the full example here.](https://github.com/garden-co/jazz/tree/main/examples/server-worker-inbox)
|
||||
|
||||
<Alert variant="info" className="mt-4 flex gap-2 items-center">Requires at least Node.js v20.</Alert>
|
||||
|
||||
|
||||
@@ -202,7 +202,7 @@ See the corresponding sections for [creating](/docs/using-covalues/filestreams#c
|
||||
|
||||
### Unions of CoMaps (declaration)
|
||||
|
||||
You can declare unions of CoMaps that have discriminating fields, using `z.discriminatedUnion()`.
|
||||
You can declare unions of CoMaps that have discriminating fields, using `co.discriminatedUnion()`.
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
@@ -220,7 +220,7 @@ const SliderWidget = co.map({
|
||||
max: z.number(),
|
||||
});
|
||||
|
||||
const WidgetUnion = z.discriminatedUnion("type", [ButtonWidget, SliderWidget]);
|
||||
const WidgetUnion = co.discriminatedUnion("type", [ButtonWidget, SliderWidget]);
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
@@ -321,7 +321,7 @@ const Company = co.map({
|
||||
|
||||
#### Optional References
|
||||
|
||||
You can make references optional with `z.optional()`:
|
||||
You can make references optional with `co.optional()`:
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
@@ -331,7 +331,7 @@ const Pet = co.map({
|
||||
});
|
||||
// ---cut---
|
||||
const Person = co.map({
|
||||
pet: z.optional(Pet),
|
||||
pet: co.optional(Pet),
|
||||
});
|
||||
```
|
||||
</CodeGroup>
|
||||
@@ -372,7 +372,7 @@ const ListOfPeople = co.list(Person);
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
Note: similarly, if you use modifiers like `z.optional()` you'll need to help TypeScript along:
|
||||
Note: similarly, if you use modifiers like `co.optional()` you'll need to help TypeScript along:
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
@@ -381,7 +381,7 @@ import { co, z } from "jazz-tools";
|
||||
const Person = co.map({
|
||||
name: z.string(),
|
||||
get bestFriend(): z.ZodOptional<typeof Person> {
|
||||
return z.optional(Person);
|
||||
return co.optional(Person);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
@@ -24,7 +24,7 @@ const Project = co.map({
|
||||
name: z.string(),
|
||||
startDate: z.date(),
|
||||
status: z.literal(["planning", "active", "completed"]),
|
||||
coordinator: z.optional(Member),
|
||||
coordinator: co.optional(Member),
|
||||
});
|
||||
export type Project = co.loaded<typeof Project>;
|
||||
```
|
||||
@@ -54,7 +54,7 @@ const Project = co.map({
|
||||
name: z.string(),
|
||||
startDate: z.date(),
|
||||
status: z.literal(["planning", "active", "completed"]),
|
||||
coordinator: z.optional(Member),
|
||||
coordinator: co.optional(Member),
|
||||
});
|
||||
const Inventory = co.record(z.string(), z.number());
|
||||
// ---cut---
|
||||
@@ -90,7 +90,7 @@ const Project = co.map({
|
||||
name: z.string(),
|
||||
startDate: z.date(),
|
||||
status: z.literal(["planning", "active", "completed"]),
|
||||
coordinator: z.optional(Member),
|
||||
coordinator: co.optional(Member),
|
||||
});
|
||||
|
||||
// ---cut---
|
||||
@@ -134,7 +134,7 @@ const Project = co.map({
|
||||
name: z.string(),
|
||||
startDate: z.date(),
|
||||
status: z.literal(["planning", "active", "completed"]),
|
||||
coordinator: z.optional(Member),
|
||||
coordinator: co.optional(Member),
|
||||
});
|
||||
const project = Project.create(
|
||||
{
|
||||
@@ -165,7 +165,7 @@ const Project = co.map({
|
||||
name: z.string(),
|
||||
startDate: z.date(),
|
||||
status: z.literal(["planning", "active", "completed"]),
|
||||
coordinator: z.optional(Member),
|
||||
coordinator: co.optional(Member),
|
||||
});
|
||||
const project = Project.create(
|
||||
{
|
||||
@@ -197,7 +197,7 @@ const Project = co.map({
|
||||
name: z.string(),
|
||||
startDate: z.date(),
|
||||
status: z.literal(["planning", "active", "completed"]),
|
||||
coordinator: z.optional(Member),
|
||||
coordinator: co.optional(Member),
|
||||
get subProject() {
|
||||
return Project.optional();
|
||||
}
|
||||
@@ -219,9 +219,9 @@ const Project = co.map({
|
||||
name: z.string(),
|
||||
startDate: z.date(),
|
||||
status: z.literal(["planning", "active", "completed"]),
|
||||
coordinator: z.optional(Member),
|
||||
coordinator: co.optional(Member),
|
||||
get subProjects(): z.ZodOptional<CoListSchema<typeof Project>> {
|
||||
return z.optional(co.list(Project));
|
||||
return co.optional(co.list(Project));
|
||||
}
|
||||
});
|
||||
export type Project = co.loaded<typeof Project>;
|
||||
@@ -265,7 +265,7 @@ const Project = co.map({
|
||||
name: z.string(),
|
||||
startDate: z.date(),
|
||||
status: z.literal(["planning", "active", "completed"]),
|
||||
coordinator: z.optional(Member),
|
||||
coordinator: co.optional(Member),
|
||||
});
|
||||
const Inventory = co.record(z.string(), z.number());
|
||||
const project = Project.create(
|
||||
@@ -297,7 +297,7 @@ const Project = co.map({
|
||||
name: z.string(),
|
||||
startDate: z.date(),
|
||||
status: z.literal(["planning", "active", "completed"]),
|
||||
coordinator: z.optional(Member),
|
||||
coordinator: co.optional(Member),
|
||||
});
|
||||
const Inventory = co.record(z.string(), z.number());
|
||||
const project = Project.create(
|
||||
@@ -347,7 +347,7 @@ const Project = co.map({
|
||||
name: z.string(),
|
||||
startDate: z.date(),
|
||||
status: z.literal(["planning", "active", "completed"]),
|
||||
coordinator: z.optional(Member),
|
||||
coordinator: co.optional(Member),
|
||||
});
|
||||
const Inventory = co.record(z.string(), z.number());
|
||||
const project = Project.create(
|
||||
@@ -445,7 +445,7 @@ const TaskV2 = co.map({
|
||||
|
||||
// Export the discriminated union; because some users might
|
||||
// not be able to run the migration
|
||||
export const Task = z.discriminatedUnion("version", [
|
||||
export const Task = co.discriminatedUnion("version", [
|
||||
TaskV1,
|
||||
TaskV2,
|
||||
]);
|
||||
|
||||
@@ -6,7 +6,7 @@ export const metadata = {
|
||||
|
||||
# Connecting CoValues with direct linking
|
||||
CoValues can form relationships with each other by **linking directly to other CoValues**. This creates a powerful connection where one CoValue can point to the unique identity of another.
|
||||
Instead of embedding all of the details of one coValue directly within another, you use its Jazz-Tools schema as the field type. This allows multiple CoValues to point to the same piece of data effortlessly.
|
||||
Instead of embedding all the details of one CoValue directly within another, you use its Jazz-Tools schema as the field type. This allows multiple CoValues to point to the same piece of data effortlessly.
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
@@ -50,3 +50,51 @@ export type User = co.loaded<typeof User>;
|
||||
This direct linking approach offers a single source of truth. When you update a referenced CoValue, all other CoValues that point to it are automatically updated, ensuring data consistency across your application.
|
||||
|
||||
By connecting CoValues through these direct references, you can build robust and collaborative applications where data is consistent, efficient to manage, and relationships are clearly defined. The ability to link different CoValue types to the same underlying data is fundamental to building complex applications with Jazz.
|
||||
|
||||
|
||||
## Recursive references with DiscriminatedUnion
|
||||
In advanced schemas, you may want a CoValue that recursively references itself. For example, a `ReferenceItem` that contains a list of other items like `NoteItem` or `AttachmentItem`. This is common in tree-like structures such as threaded comments or nested project outlines.
|
||||
|
||||
You can model this with a Zod `z.discriminatedUnion`, but TypeScript’s type inference doesn't handle recursive unions well without a workaround.
|
||||
|
||||
Here’s how to structure your schema to avoid circular reference errors.
|
||||
|
||||
### Use this pattern for recursive discriminated unions
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
import { CoListSchema, co, z } from "jazz-tools";
|
||||
|
||||
// Recursive item modeling pattern using discriminated unions
|
||||
// First, define the non-recursive types
|
||||
export const NoteItem = co.map({
|
||||
type: z.literal("note"),
|
||||
internal: z.boolean(),
|
||||
content: co.plainText(),
|
||||
});
|
||||
|
||||
export const AttachmentItem = co.map({
|
||||
type: z.literal("attachment"),
|
||||
internal: z.boolean(),
|
||||
content: co.fileStream(),
|
||||
});
|
||||
|
||||
export const ReferenceItem = co.map({
|
||||
type: z.literal("reference"),
|
||||
internal: z.boolean(),
|
||||
content: z.string(),
|
||||
|
||||
// Workaround: declare the field type using CoListSchema and ZodDiscriminatedUnion so TS can safely recurse
|
||||
get children(): CoListSchema<z.ZodDiscriminatedUnion<[typeof NoteItem, typeof AttachmentItem, typeof ReferenceItem]>> {
|
||||
return ProjectContextItemList;
|
||||
},
|
||||
});
|
||||
|
||||
// Create the recursive union
|
||||
export const ProjectContextItem = z.discriminatedUnion("type", [NoteItem, AttachmentItem, ReferenceItem]);
|
||||
|
||||
// Final list of recursive types
|
||||
export const ProjectContextItemList = co.list(ProjectContextItem);
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
Even though this seems like a shortcut, TypeScript and Zod can't resolve the circular reference this way. Always define the discriminated union before introducing recursive links.
|
||||
|
||||
1543
homepage/pnpm-lock.yaml
generated
1543
homepage/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,7 @@ packages:
|
||||
- "gcmp"
|
||||
|
||||
catalog:
|
||||
"react": "19.0.0"
|
||||
"react-dom": "19.0.0"
|
||||
"@types/react": "19.0.0"
|
||||
"@types/react-dom": "19.0.0"
|
||||
"react": "19.1.0"
|
||||
"react-dom": "19.1.0"
|
||||
"@types/react": "19.1.0"
|
||||
"@types/react-dom": "19.1.0"
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
"changeset-version": "changeset version && pnpm i --no-frozen-lockfile",
|
||||
"release": "turbo run build --filter='./packages/*' && pnpm changeset publish && git push --follow-tags",
|
||||
"clean": "rm -rf ./packages/*/dist && rm -rf ./packages/*/node_modules && rm -rf ./examples/*/node_modules && rm -rf ./examples/*/dist",
|
||||
"postinstall": "lefthook install",
|
||||
"check-catalog-deps": "node scripts/check-catalog-deps.js"
|
||||
},
|
||||
"version": "0.0.0",
|
||||
@@ -50,10 +51,10 @@
|
||||
"ignoreMissing": ["@babel/*", "expo-modules-*", "typescript"]
|
||||
},
|
||||
"overrides": {
|
||||
"@types/react": "19.0.0",
|
||||
"@types/react-dom": "19.0.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.0",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"vite": "6.3.5",
|
||||
"esbuild": "0.24.0"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,44 @@
|
||||
# cojson-storage-indexeddb
|
||||
|
||||
## 0.15.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [70ce7c5]
|
||||
- cojson@0.15.14
|
||||
|
||||
## 0.15.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cojson@0.15.13
|
||||
|
||||
## 0.15.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cojson@0.15.12
|
||||
|
||||
## 0.15.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cojson@0.15.11
|
||||
|
||||
## 0.15.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cojson@0.15.10
|
||||
|
||||
## 0.15.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [27b4837]
|
||||
- Updated dependencies [2776263]
|
||||
- cojson@0.15.9
|
||||
|
||||
## 0.15.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
{
|
||||
"name": "cojson-storage-indexeddb",
|
||||
"version": "0.15.8",
|
||||
"version": "0.15.14",
|
||||
"main": "dist/index.js",
|
||||
"type": "module",
|
||||
"types": "dist/index.d.ts",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cojson": "workspace:*",
|
||||
"cojson-storage": "workspace:*"
|
||||
"cojson": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "catalog:",
|
||||
|
||||
@@ -7,7 +7,7 @@ import type {
|
||||
StoredCoValueRow,
|
||||
StoredSessionRow,
|
||||
TransactionRow,
|
||||
} from "cojson-storage";
|
||||
} from "cojson";
|
||||
import { CoJsonIDBTransaction } from "./CoJsonIDBTransaction.js";
|
||||
|
||||
export class IDBClient implements DBClientInterfaceAsync {
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
import {
|
||||
type IncomingSyncStream,
|
||||
type OutgoingSyncQueue,
|
||||
type Peer,
|
||||
cojsonInternals,
|
||||
} from "cojson";
|
||||
import { StorageManagerAsync } from "cojson-storage";
|
||||
import { StorageApiAsync } from "cojson";
|
||||
import { IDBClient } from "./idbClient.js";
|
||||
|
||||
let DATABASE_NAME = "jazz-storage";
|
||||
@@ -13,132 +7,50 @@ export function internal_setDatabaseName(name: string) {
|
||||
DATABASE_NAME = name;
|
||||
}
|
||||
|
||||
function createParallelOpsRunner() {
|
||||
const ops = new Set<Promise<unknown>>();
|
||||
export async function getIndexedDBStorage(name = DATABASE_NAME) {
|
||||
const dbPromise = new Promise<IDBDatabase>((resolve, reject) => {
|
||||
const request = indexedDB.open(name, 4);
|
||||
request.onerror = () => {
|
||||
reject(request.error);
|
||||
};
|
||||
request.onsuccess = () => {
|
||||
resolve(request.result);
|
||||
};
|
||||
request.onupgradeneeded = async (ev) => {
|
||||
const db = request.result;
|
||||
if (ev.oldVersion === 0) {
|
||||
const coValues = db.createObjectStore("coValues", {
|
||||
autoIncrement: true,
|
||||
keyPath: "rowID",
|
||||
});
|
||||
|
||||
return {
|
||||
add: (op: Promise<unknown>) => {
|
||||
ops.add(op);
|
||||
op.finally(() => {
|
||||
ops.delete(op);
|
||||
});
|
||||
},
|
||||
wait() {
|
||||
return Promise.race(ops);
|
||||
},
|
||||
get size() {
|
||||
return ops.size;
|
||||
},
|
||||
};
|
||||
}
|
||||
coValues.createIndex("coValuesById", "id", {
|
||||
unique: true,
|
||||
});
|
||||
|
||||
export class IDBNode {
|
||||
private readonly dbClient: IDBClient;
|
||||
private readonly syncManager: StorageManagerAsync;
|
||||
const sessions = db.createObjectStore("sessions", {
|
||||
autoIncrement: true,
|
||||
keyPath: "rowID",
|
||||
});
|
||||
|
||||
constructor(
|
||||
db: IDBDatabase,
|
||||
fromLocalNode: IncomingSyncStream,
|
||||
toLocalNode: OutgoingSyncQueue,
|
||||
) {
|
||||
this.dbClient = new IDBClient(db);
|
||||
this.syncManager = new StorageManagerAsync(this.dbClient, toLocalNode);
|
||||
sessions.createIndex("sessionsByCoValue", "coValue");
|
||||
sessions.createIndex("uniqueSessions", ["coValue", "sessionID"], {
|
||||
unique: true,
|
||||
});
|
||||
|
||||
const processMessages = async () => {
|
||||
const batch = createParallelOpsRunner();
|
||||
|
||||
for await (const msg of fromLocalNode) {
|
||||
try {
|
||||
if (msg === "Disconnected" || msg === "PingTimeout") {
|
||||
throw new Error("Unexpected Disconnected message");
|
||||
}
|
||||
|
||||
if (msg.action === "content") {
|
||||
await this.syncManager.handleSyncMessage(msg);
|
||||
} else {
|
||||
batch.add(this.syncManager.handleSyncMessage(msg));
|
||||
}
|
||||
|
||||
if (batch.size > 10) {
|
||||
await batch.wait();
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
db.createObjectStore("transactions", {
|
||||
keyPath: ["ses", "idx"],
|
||||
});
|
||||
}
|
||||
if (ev.oldVersion <= 1) {
|
||||
db.createObjectStore("signatureAfter", {
|
||||
keyPath: ["ses", "idx"],
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
processMessages().catch((e) =>
|
||||
console.error("Error in processMessages in IndexedDB", e),
|
||||
);
|
||||
}
|
||||
const db = await dbPromise;
|
||||
|
||||
static async asPeer(
|
||||
{ localNodeName = "local" }: { localNodeName?: string } | undefined = {
|
||||
localNodeName: "local",
|
||||
},
|
||||
): Promise<Peer> {
|
||||
const [localNodeAsPeer, storageAsPeer] = cojsonInternals.connectedPeers(
|
||||
localNodeName,
|
||||
"indexedDB",
|
||||
{
|
||||
peer1role: "client",
|
||||
peer2role: "storage",
|
||||
crashOnClose: true,
|
||||
},
|
||||
);
|
||||
|
||||
await IDBNode.open(localNodeAsPeer.incoming, localNodeAsPeer.outgoing);
|
||||
|
||||
return { ...storageAsPeer, priority: 100 };
|
||||
}
|
||||
|
||||
static async open(
|
||||
fromLocalNode: IncomingSyncStream,
|
||||
toLocalNode: OutgoingSyncQueue,
|
||||
) {
|
||||
const dbPromise = new Promise<IDBDatabase>((resolve, reject) => {
|
||||
const request = indexedDB.open(DATABASE_NAME, 4);
|
||||
request.onerror = () => {
|
||||
reject(request.error);
|
||||
};
|
||||
request.onsuccess = () => {
|
||||
resolve(request.result);
|
||||
};
|
||||
request.onupgradeneeded = async (ev) => {
|
||||
const db = request.result;
|
||||
if (ev.oldVersion === 0) {
|
||||
const coValues = db.createObjectStore("coValues", {
|
||||
autoIncrement: true,
|
||||
keyPath: "rowID",
|
||||
});
|
||||
|
||||
coValues.createIndex("coValuesById", "id", {
|
||||
unique: true,
|
||||
});
|
||||
|
||||
const sessions = db.createObjectStore("sessions", {
|
||||
autoIncrement: true,
|
||||
keyPath: "rowID",
|
||||
});
|
||||
|
||||
sessions.createIndex("sessionsByCoValue", "coValue");
|
||||
sessions.createIndex("uniqueSessions", ["coValue", "sessionID"], {
|
||||
unique: true,
|
||||
});
|
||||
|
||||
db.createObjectStore("transactions", {
|
||||
keyPath: ["ses", "idx"],
|
||||
});
|
||||
}
|
||||
if (ev.oldVersion <= 1) {
|
||||
db.createObjectStore("signatureAfter", {
|
||||
keyPath: ["ses", "idx"],
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
return new IDBNode(await dbPromise, fromLocalNode, toLocalNode);
|
||||
}
|
||||
return new StorageApiAsync(new IDBClient(db));
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
export {
|
||||
IDBNode,
|
||||
IDBNode as IDBStorage,
|
||||
internal_setDatabaseName,
|
||||
getIndexedDBStorage,
|
||||
} from "./idbNode.js";
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
import { LocalNode } from "cojson";
|
||||
import { WasmCrypto } from "cojson/crypto/WasmCrypto";
|
||||
import { expect, test } from "vitest";
|
||||
import { IDBStorage } from "../index.js";
|
||||
|
||||
const Crypto = await WasmCrypto.create();
|
||||
|
||||
test("Should be able to initialize and load from empty DB", async () => {
|
||||
const agentSecret = Crypto.newRandomAgentSecret();
|
||||
|
||||
const node = new LocalNode(
|
||||
agentSecret,
|
||||
Crypto.newRandomSessionID(Crypto.getAgentID(agentSecret)),
|
||||
Crypto,
|
||||
);
|
||||
|
||||
node.syncManager.addPeer(await IDBStorage.asPeer({}));
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
|
||||
expect(node.syncManager.peers.indexedDB).toBeDefined();
|
||||
});
|
||||
|
||||
test("Should be able to sync data to database and then load that from a new node", async () => {
|
||||
const agentSecret = Crypto.newRandomAgentSecret();
|
||||
|
||||
const node1 = new LocalNode(
|
||||
agentSecret,
|
||||
Crypto.newRandomSessionID(Crypto.getAgentID(agentSecret)),
|
||||
Crypto,
|
||||
);
|
||||
|
||||
node1.syncManager.addPeer(
|
||||
await IDBStorage.asPeer({ localNodeName: "node1" }),
|
||||
);
|
||||
|
||||
const group = node1.createGroup();
|
||||
|
||||
const map = group.createMap();
|
||||
|
||||
map.set("hello", "world");
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
|
||||
const node2 = new LocalNode(
|
||||
agentSecret,
|
||||
Crypto.newRandomSessionID(Crypto.getAgentID(agentSecret)),
|
||||
Crypto,
|
||||
);
|
||||
|
||||
node2.syncManager.addPeer(
|
||||
await IDBStorage.asPeer({ localNodeName: "node2" }),
|
||||
);
|
||||
|
||||
const map2 = await node2.load(map.id);
|
||||
if (map2 === "unavailable") {
|
||||
throw new Error("Map is unavailable");
|
||||
}
|
||||
|
||||
expect(map2.get("hello")).toBe("world");
|
||||
});
|
||||
@@ -1,8 +1,7 @@
|
||||
import { LocalNode } from "cojson";
|
||||
import { StorageManagerAsync } from "cojson-storage";
|
||||
import { LocalNode, StorageApiAsync } from "cojson";
|
||||
import { WasmCrypto } from "cojson/crypto/WasmCrypto";
|
||||
import { afterEach, beforeEach, expect, test, vi } from "vitest";
|
||||
import { IDBStorage } from "../index.js";
|
||||
import { getIndexedDBStorage } from "../index.js";
|
||||
import { toSimplifiedMessages } from "./messagesTestUtils.js";
|
||||
import { trackMessages, waitFor } from "./testUtils.js";
|
||||
|
||||
@@ -17,22 +16,6 @@ afterEach(() => {
|
||||
syncMessages.restore();
|
||||
});
|
||||
|
||||
test("Should be able to initialize and load from empty DB", async () => {
|
||||
const agentSecret = Crypto.newRandomAgentSecret();
|
||||
|
||||
const node = new LocalNode(
|
||||
agentSecret,
|
||||
Crypto.newRandomSessionID(Crypto.getAgentID(agentSecret)),
|
||||
Crypto,
|
||||
);
|
||||
|
||||
node.syncManager.addPeer(await IDBStorage.asPeer());
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
|
||||
expect(node.syncManager.peers.indexedDB).toBeDefined();
|
||||
});
|
||||
|
||||
test("should sync and load data from storage", async () => {
|
||||
const agentSecret = Crypto.newRandomAgentSecret();
|
||||
|
||||
@@ -41,18 +24,14 @@ test("should sync and load data from storage", async () => {
|
||||
Crypto.newRandomSessionID(Crypto.getAgentID(agentSecret)),
|
||||
Crypto,
|
||||
);
|
||||
|
||||
const peer = await IDBStorage.asPeer();
|
||||
|
||||
node1.syncManager.addPeer(peer);
|
||||
node1.setStorage(await getIndexedDBStorage());
|
||||
|
||||
const group = node1.createGroup();
|
||||
|
||||
const map = group.createMap();
|
||||
|
||||
map.set("hello", "world");
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
await map.core.waitForSync();
|
||||
|
||||
expect(
|
||||
toSimplifiedMessages(
|
||||
@@ -65,9 +44,7 @@ test("should sync and load data from storage", async () => {
|
||||
).toMatchInlineSnapshot(`
|
||||
[
|
||||
"client -> CONTENT Group header: true new: After: 0 New: 3",
|
||||
"storage -> KNOWN Group sessions: header/3",
|
||||
"client -> CONTENT Map header: true new: After: 0 New: 1",
|
||||
"storage -> KNOWN Map sessions: header/1",
|
||||
]
|
||||
`);
|
||||
|
||||
@@ -80,9 +57,7 @@ test("should sync and load data from storage", async () => {
|
||||
Crypto,
|
||||
);
|
||||
|
||||
const peer2 = await IDBStorage.asPeer();
|
||||
|
||||
node2.syncManager.addPeer(peer2);
|
||||
node2.setStorage(await getIndexedDBStorage());
|
||||
|
||||
const map2 = await node2.load(map.id);
|
||||
if (map2 === "unavailable") {
|
||||
@@ -103,9 +78,7 @@ test("should sync and load data from storage", async () => {
|
||||
[
|
||||
"client -> LOAD Map sessions: empty",
|
||||
"storage -> CONTENT Group header: true new: After: 0 New: 3",
|
||||
"client -> KNOWN Group sessions: header/3",
|
||||
"storage -> CONTENT Map header: true new: After: 0 New: 1",
|
||||
"client -> KNOWN Map sessions: header/1",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -119,15 +92,12 @@ test("should send an empty content message if there is no content", async () =>
|
||||
Crypto,
|
||||
);
|
||||
|
||||
const peer = await IDBStorage.asPeer();
|
||||
|
||||
node1.syncManager.addPeer(peer);
|
||||
node1.setStorage(await getIndexedDBStorage());
|
||||
|
||||
const group = node1.createGroup();
|
||||
|
||||
const map = group.createMap();
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
await map.core.waitForSync();
|
||||
|
||||
expect(
|
||||
toSimplifiedMessages(
|
||||
@@ -140,9 +110,7 @@ test("should send an empty content message if there is no content", async () =>
|
||||
).toMatchInlineSnapshot(`
|
||||
[
|
||||
"client -> CONTENT Group header: true new: After: 0 New: 3",
|
||||
"storage -> KNOWN Group sessions: header/3",
|
||||
"client -> CONTENT Map header: true new: ",
|
||||
"storage -> KNOWN Map sessions: header/0",
|
||||
]
|
||||
`);
|
||||
|
||||
@@ -155,9 +123,7 @@ test("should send an empty content message if there is no content", async () =>
|
||||
Crypto,
|
||||
);
|
||||
|
||||
const peer2 = await IDBStorage.asPeer();
|
||||
|
||||
node2.syncManager.addPeer(peer2);
|
||||
node2.setStorage(await getIndexedDBStorage());
|
||||
|
||||
const map2 = await node2.load(map.id);
|
||||
if (map2 === "unavailable") {
|
||||
@@ -176,9 +142,7 @@ test("should send an empty content message if there is no content", async () =>
|
||||
[
|
||||
"client -> LOAD Map sessions: empty",
|
||||
"storage -> CONTENT Group header: true new: After: 0 New: 3",
|
||||
"client -> KNOWN Group sessions: header/3",
|
||||
"storage -> CONTENT Map header: true new: ",
|
||||
"client -> KNOWN Map sessions: header/0",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -192,10 +156,7 @@ test("should load dependencies correctly (group inheritance)", async () => {
|
||||
Crypto,
|
||||
);
|
||||
|
||||
const peer = await IDBStorage.asPeer();
|
||||
|
||||
node1.syncManager.addPeer(peer);
|
||||
|
||||
node1.setStorage(await getIndexedDBStorage());
|
||||
const group = node1.createGroup();
|
||||
const parentGroup = node1.createGroup();
|
||||
|
||||
@@ -205,7 +166,7 @@ test("should load dependencies correctly (group inheritance)", async () => {
|
||||
|
||||
map.set("hello", "world");
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
await map.core.waitForSync();
|
||||
|
||||
expect(
|
||||
toSimplifiedMessages(
|
||||
@@ -218,12 +179,9 @@ test("should load dependencies correctly (group inheritance)", async () => {
|
||||
),
|
||||
).toMatchInlineSnapshot(`
|
||||
[
|
||||
"client -> CONTENT ParentGroup header: true new: After: 0 New: 4",
|
||||
"storage -> KNOWN ParentGroup sessions: header/4",
|
||||
"client -> CONTENT Group header: true new: After: 0 New: 5",
|
||||
"storage -> KNOWN Group sessions: header/5",
|
||||
"client -> CONTENT ParentGroup header: true new: After: 0 New: 4",
|
||||
"client -> CONTENT Map header: true new: After: 0 New: 1",
|
||||
"storage -> KNOWN Map sessions: header/1",
|
||||
]
|
||||
`);
|
||||
|
||||
@@ -236,9 +194,7 @@ test("should load dependencies correctly (group inheritance)", async () => {
|
||||
Crypto,
|
||||
);
|
||||
|
||||
const peer2 = await IDBStorage.asPeer();
|
||||
|
||||
node2.syncManager.addPeer(peer2);
|
||||
node2.setStorage(await getIndexedDBStorage());
|
||||
|
||||
await node2.load(map.id);
|
||||
|
||||
@@ -259,11 +215,8 @@ test("should load dependencies correctly (group inheritance)", async () => {
|
||||
[
|
||||
"client -> LOAD Map sessions: empty",
|
||||
"storage -> CONTENT ParentGroup header: true new: After: 0 New: 4",
|
||||
"client -> KNOWN ParentGroup sessions: header/4",
|
||||
"storage -> CONTENT Group header: true new: After: 0 New: 5",
|
||||
"client -> KNOWN Group sessions: header/5",
|
||||
"storage -> CONTENT Map header: true new: After: 0 New: 1",
|
||||
"client -> KNOWN Map sessions: header/1",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -277,9 +230,7 @@ test("should not send the same dependency value twice", async () => {
|
||||
Crypto,
|
||||
);
|
||||
|
||||
const peer = await IDBStorage.asPeer();
|
||||
|
||||
node1.syncManager.addPeer(peer);
|
||||
node1.setStorage(await getIndexedDBStorage());
|
||||
|
||||
const group = node1.createGroup();
|
||||
const parentGroup = node1.createGroup();
|
||||
@@ -292,7 +243,8 @@ test("should not send the same dependency value twice", async () => {
|
||||
map.set("hello", "world");
|
||||
mapFromParent.set("hello", "world");
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
await map.core.waitForSync();
|
||||
await mapFromParent.core.waitForSync();
|
||||
|
||||
syncMessages.clear();
|
||||
node1.gracefulShutdown();
|
||||
@@ -303,9 +255,7 @@ test("should not send the same dependency value twice", async () => {
|
||||
Crypto,
|
||||
);
|
||||
|
||||
const peer2 = await IDBStorage.asPeer();
|
||||
|
||||
node2.syncManager.addPeer(peer2);
|
||||
node2.setStorage(await getIndexedDBStorage());
|
||||
|
||||
await node2.load(map.id);
|
||||
await node2.load(mapFromParent.id);
|
||||
@@ -329,14 +279,10 @@ test("should not send the same dependency value twice", async () => {
|
||||
[
|
||||
"client -> LOAD Map sessions: empty",
|
||||
"storage -> CONTENT ParentGroup header: true new: After: 0 New: 4",
|
||||
"client -> KNOWN ParentGroup sessions: header/4",
|
||||
"storage -> CONTENT Group header: true new: After: 0 New: 5",
|
||||
"client -> KNOWN Group sessions: header/5",
|
||||
"storage -> CONTENT Map header: true new: After: 0 New: 1",
|
||||
"client -> KNOWN Map sessions: header/1",
|
||||
"client -> LOAD MapFromParent sessions: empty",
|
||||
"storage -> CONTENT MapFromParent header: true new: After: 0 New: 1",
|
||||
"client -> KNOWN MapFromParent sessions: header/1",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -350,9 +296,8 @@ test("should recover from data loss", async () => {
|
||||
Crypto,
|
||||
);
|
||||
|
||||
const peer = await IDBStorage.asPeer();
|
||||
|
||||
node1.syncManager.addPeer(peer);
|
||||
const storage = await getIndexedDBStorage();
|
||||
node1.setStorage(storage);
|
||||
|
||||
const group = node1.createGroup();
|
||||
|
||||
@@ -360,22 +305,25 @@ test("should recover from data loss", async () => {
|
||||
|
||||
map.set("0", 0);
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
await map.core.waitForSync();
|
||||
|
||||
const mock = vi
|
||||
.spyOn(StorageManagerAsync.prototype, "handleSyncMessage")
|
||||
.mockImplementation(() => Promise.resolve());
|
||||
.spyOn(StorageApiAsync.prototype, "store")
|
||||
.mockImplementation(() => Promise.resolve(undefined));
|
||||
|
||||
map.set("1", 1);
|
||||
map.set("2", 2);
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
|
||||
const knownState = storage.getKnownState(map.id);
|
||||
Object.assign(knownState, map.core.knownState());
|
||||
|
||||
mock.mockReset();
|
||||
|
||||
map.set("3", 3);
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
await map.core.waitForSync();
|
||||
|
||||
expect(
|
||||
toSimplifiedMessages(
|
||||
@@ -388,13 +336,10 @@ test("should recover from data loss", async () => {
|
||||
).toMatchInlineSnapshot(`
|
||||
[
|
||||
"client -> CONTENT Group header: true new: After: 0 New: 3",
|
||||
"storage -> KNOWN Group sessions: header/3",
|
||||
"client -> CONTENT Map header: true new: After: 0 New: 1",
|
||||
"storage -> KNOWN Map sessions: header/1",
|
||||
"client -> CONTENT Map header: false new: After: 3 New: 1",
|
||||
"storage -> KNOWN CORRECTION Map sessions: header/1",
|
||||
"storage -> KNOWN CORRECTION Map sessions: header/4",
|
||||
"client -> CONTENT Map header: false new: After: 1 New: 3",
|
||||
"storage -> KNOWN Map sessions: header/4",
|
||||
]
|
||||
`);
|
||||
|
||||
@@ -407,9 +352,7 @@ test("should recover from data loss", async () => {
|
||||
Crypto,
|
||||
);
|
||||
|
||||
const peer2 = await IDBStorage.asPeer();
|
||||
|
||||
node2.syncManager.addPeer(peer2);
|
||||
node2.setStorage(await getIndexedDBStorage());
|
||||
|
||||
const map2 = await node2.load(map.id);
|
||||
|
||||
@@ -436,9 +379,7 @@ test("should recover from data loss", async () => {
|
||||
[
|
||||
"client -> LOAD Map sessions: empty",
|
||||
"storage -> CONTENT Group header: true new: After: 0 New: 3",
|
||||
"client -> KNOWN Group sessions: header/3",
|
||||
"storage -> CONTENT Map header: true new: After: 0 New: 4",
|
||||
"client -> KNOWN Map sessions: header/4",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -452,7 +393,7 @@ test("should sync multiple sessions in a single content message", async () => {
|
||||
Crypto,
|
||||
);
|
||||
|
||||
node1.syncManager.addPeer(await IDBStorage.asPeer());
|
||||
node1.setStorage(await getIndexedDBStorage());
|
||||
|
||||
const group = node1.createGroup();
|
||||
|
||||
@@ -460,7 +401,7 @@ test("should sync multiple sessions in a single content message", async () => {
|
||||
|
||||
map.set("hello", "world");
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
await map.core.waitForSync();
|
||||
|
||||
node1.gracefulShutdown();
|
||||
|
||||
@@ -470,7 +411,7 @@ test("should sync multiple sessions in a single content message", async () => {
|
||||
Crypto,
|
||||
);
|
||||
|
||||
node2.syncManager.addPeer(await IDBStorage.asPeer());
|
||||
node2.setStorage(await getIndexedDBStorage());
|
||||
|
||||
const map2 = await node2.load(map.id);
|
||||
if (map2 === "unavailable") {
|
||||
@@ -493,7 +434,7 @@ test("should sync multiple sessions in a single content message", async () => {
|
||||
|
||||
syncMessages.clear();
|
||||
|
||||
node3.syncManager.addPeer(await IDBStorage.asPeer());
|
||||
node3.setStorage(await getIndexedDBStorage());
|
||||
|
||||
const map3 = await node3.load(map.id);
|
||||
if (map3 === "unavailable") {
|
||||
@@ -514,9 +455,7 @@ test("should sync multiple sessions in a single content message", async () => {
|
||||
[
|
||||
"client -> LOAD Map sessions: empty",
|
||||
"storage -> CONTENT Group header: true new: After: 0 New: 3",
|
||||
"client -> KNOWN Group sessions: header/3",
|
||||
"storage -> CONTENT Map header: true new: After: 0 New: 1 | After: 0 New: 1",
|
||||
"client -> KNOWN Map sessions: header/2",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -530,7 +469,7 @@ test("large coValue upload streaming", async () => {
|
||||
Crypto,
|
||||
);
|
||||
|
||||
node1.syncManager.addPeer(await IDBStorage.asPeer());
|
||||
node1.setStorage(await getIndexedDBStorage());
|
||||
|
||||
const group = node1.createGroup();
|
||||
const largeMap = group.createMap();
|
||||
@@ -547,6 +486,7 @@ test("large coValue upload streaming", async () => {
|
||||
largeMap.set(key, value, "trusting");
|
||||
}
|
||||
|
||||
// TODO: Wait for storage to be updated
|
||||
await largeMap.core.waitForSync();
|
||||
|
||||
const knownState = largeMap.core.knownState();
|
||||
@@ -561,7 +501,7 @@ test("large coValue upload streaming", async () => {
|
||||
|
||||
syncMessages.clear();
|
||||
|
||||
node2.syncManager.addPeer(await IDBStorage.asPeer());
|
||||
node2.setStorage(await getIndexedDBStorage());
|
||||
|
||||
const largeMapOnNode2 = await node2.load(largeMap.id);
|
||||
|
||||
@@ -586,15 +526,10 @@ test("large coValue upload streaming", async () => {
|
||||
).toMatchInlineSnapshot(`
|
||||
[
|
||||
"client -> LOAD Map sessions: empty",
|
||||
"storage -> KNOWN Map sessions: header/200",
|
||||
"storage -> CONTENT Group header: true new: After: 0 New: 3",
|
||||
"client -> KNOWN Group sessions: header/3",
|
||||
"storage -> CONTENT Map header: true new: After: 0 New: 97",
|
||||
"client -> KNOWN Map sessions: header/97",
|
||||
"storage -> CONTENT Map header: true new: After: 97 New: 97",
|
||||
"client -> KNOWN Map sessions: header/194",
|
||||
"storage -> CONTENT Map header: true new: After: 194 New: 6",
|
||||
"client -> KNOWN Map sessions: header/200",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -605,7 +540,7 @@ test("should sync and load accounts from storage", async () => {
|
||||
const { node: node1, accountID } = await LocalNode.withNewlyCreatedAccount({
|
||||
crypto: Crypto,
|
||||
initialAgentSecret: agentSecret,
|
||||
peersToLoadFrom: [await IDBStorage.asPeer()],
|
||||
storage: await getIndexedDBStorage(),
|
||||
creationProps: {
|
||||
name: "test",
|
||||
},
|
||||
@@ -615,8 +550,6 @@ test("should sync and load accounts from storage", async () => {
|
||||
const profile = node1.expectProfileLoaded(accountID);
|
||||
const profileGroup = profile.group;
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
|
||||
expect(
|
||||
toSimplifiedMessages(
|
||||
{
|
||||
@@ -629,11 +562,8 @@ test("should sync and load accounts from storage", async () => {
|
||||
).toMatchInlineSnapshot(`
|
||||
[
|
||||
"client -> CONTENT Account header: true new: After: 0 New: 4",
|
||||
"storage -> KNOWN Account sessions: header/4",
|
||||
"client -> CONTENT ProfileGroup header: true new: After: 0 New: 5",
|
||||
"storage -> KNOWN ProfileGroup sessions: header/5",
|
||||
"client -> CONTENT Profile header: true new: After: 0 New: 1",
|
||||
"storage -> KNOWN Profile sessions: header/1",
|
||||
]
|
||||
`);
|
||||
|
||||
@@ -645,12 +575,11 @@ test("should sync and load accounts from storage", async () => {
|
||||
crypto: Crypto,
|
||||
accountSecret: agentSecret,
|
||||
accountID,
|
||||
peersToLoadFrom: [await IDBStorage.asPeer()],
|
||||
peersToLoadFrom: [],
|
||||
storage: await getIndexedDBStorage(),
|
||||
sessionID: Crypto.newRandomSessionID(Crypto.getAgentID(agentSecret)),
|
||||
});
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
|
||||
expect(
|
||||
toSimplifiedMessages(
|
||||
{
|
||||
@@ -664,12 +593,9 @@ test("should sync and load accounts from storage", async () => {
|
||||
[
|
||||
"client -> LOAD Account sessions: empty",
|
||||
"storage -> CONTENT Account header: true new: After: 0 New: 4",
|
||||
"client -> KNOWN Account sessions: header/4",
|
||||
"client -> LOAD Profile sessions: empty",
|
||||
"storage -> CONTENT ProfileGroup header: true new: After: 0 New: 5",
|
||||
"client -> KNOWN ProfileGroup sessions: header/5",
|
||||
"storage -> CONTENT Profile header: true new: After: 0 New: 1",
|
||||
"client -> KNOWN Profile sessions: header/1",
|
||||
]
|
||||
`);
|
||||
|
||||
|
||||
@@ -1,39 +1,63 @@
|
||||
import type { LocalNode, SyncMessage } from "cojson";
|
||||
import { cojsonInternals } from "cojson";
|
||||
import { StorageManagerAsync } from "cojson-storage";
|
||||
import type { RawCoID, SyncMessage } from "cojson";
|
||||
import { StorageApiAsync } from "cojson";
|
||||
import { onTestFinished } from "vitest";
|
||||
|
||||
const { SyncManager } = cojsonInternals;
|
||||
|
||||
export function trackMessages() {
|
||||
const messages: {
|
||||
from: "client" | "server" | "storage";
|
||||
msg: SyncMessage;
|
||||
}[] = [];
|
||||
|
||||
const originalHandleSyncMessage =
|
||||
StorageManagerAsync.prototype.handleSyncMessage;
|
||||
const originalNodeSyncMessage = SyncManager.prototype.handleSyncMessage;
|
||||
const originalLoad = StorageApiAsync.prototype.load;
|
||||
const originalStore = StorageApiAsync.prototype.store;
|
||||
|
||||
StorageManagerAsync.prototype.handleSyncMessage = async function (msg) {
|
||||
StorageApiAsync.prototype.load = async function (id, callback, done) {
|
||||
messages.push({
|
||||
from: "client",
|
||||
msg,
|
||||
msg: {
|
||||
action: "load",
|
||||
id: id as RawCoID,
|
||||
header: false,
|
||||
sessions: {},
|
||||
},
|
||||
});
|
||||
return originalHandleSyncMessage.call(this, msg);
|
||||
return originalLoad.call(
|
||||
this,
|
||||
id,
|
||||
(msg) => {
|
||||
messages.push({
|
||||
from: "storage",
|
||||
msg,
|
||||
});
|
||||
callback(msg);
|
||||
},
|
||||
done,
|
||||
);
|
||||
};
|
||||
|
||||
SyncManager.prototype.handleSyncMessage = async function (msg, peer) {
|
||||
messages.push({
|
||||
from: "storage",
|
||||
msg,
|
||||
StorageApiAsync.prototype.store = async function (data, correctionCallback) {
|
||||
for (const msg of data) {
|
||||
messages.push({
|
||||
from: "client",
|
||||
msg,
|
||||
});
|
||||
}
|
||||
return originalStore.call(this, data, (msg) => {
|
||||
messages.push({
|
||||
from: "storage",
|
||||
msg: {
|
||||
action: "known",
|
||||
isCorrection: true,
|
||||
...msg,
|
||||
},
|
||||
});
|
||||
correctionCallback(msg);
|
||||
});
|
||||
return originalNodeSyncMessage.call(this, msg, peer);
|
||||
};
|
||||
|
||||
const restore = () => {
|
||||
StorageManagerAsync.prototype.handleSyncMessage = originalHandleSyncMessage;
|
||||
SyncManager.prototype.handleSyncMessage = originalNodeSyncMessage;
|
||||
StorageApiAsync.prototype.load = originalLoad;
|
||||
StorageApiAsync.prototype.store = originalStore;
|
||||
messages.length = 0;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,44 @@
|
||||
# cojson-storage-sqlite
|
||||
|
||||
## 0.15.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [70ce7c5]
|
||||
- cojson@0.15.14
|
||||
|
||||
## 0.15.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cojson@0.15.13
|
||||
|
||||
## 0.15.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cojson@0.15.12
|
||||
|
||||
## 0.15.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cojson@0.15.11
|
||||
|
||||
## 0.15.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cojson@0.15.10
|
||||
|
||||
## 0.15.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [27b4837]
|
||||
- Updated dependencies [2776263]
|
||||
- cojson@0.15.9
|
||||
|
||||
## 0.15.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
{
|
||||
"name": "cojson-storage-sqlite",
|
||||
"type": "module",
|
||||
"version": "0.15.8",
|
||||
"version": "0.15.14",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"better-sqlite3": "^11.7.0",
|
||||
"cojson": "workspace:0.15.8",
|
||||
"cojson-storage": "workspace:*"
|
||||
"cojson": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/better-sqlite3": "^7.6.12",
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
import Database, { type Database as DatabaseT } from "better-sqlite3";
|
||||
import type { SQLiteDatabaseDriver } from "cojson-storage";
|
||||
|
||||
export class BetterSqliteDriver implements SQLiteDatabaseDriver {
|
||||
private readonly db: DatabaseT;
|
||||
|
||||
constructor(filename: string) {
|
||||
const db = new Database(filename);
|
||||
this.db = db;
|
||||
db.pragma("journal_mode = WAL");
|
||||
}
|
||||
|
||||
run(sql: string, params: unknown[]) {
|
||||
this.db.prepare(sql).run(params);
|
||||
}
|
||||
|
||||
query<T>(sql: string, params: unknown[]): T[] {
|
||||
return this.db.prepare(sql).all(params) as T[];
|
||||
}
|
||||
|
||||
get<T>(sql: string, params: unknown[]): T | undefined {
|
||||
return this.db.prepare(sql).get(params) as T | undefined;
|
||||
}
|
||||
|
||||
transaction(callback: () => unknown) {
|
||||
return this.db.transaction(callback)();
|
||||
}
|
||||
|
||||
closeDb() {
|
||||
this.db.close();
|
||||
}
|
||||
}
|
||||
@@ -1 +1,39 @@
|
||||
export { SQLiteNode, SQLiteNode as SQLiteStorage } from "./sqliteNode.js";
|
||||
import Database, { type Database as DatabaseT } from "better-sqlite3";
|
||||
import type { SQLiteDatabaseDriver } from "cojson";
|
||||
import { getSqliteStorage } from "cojson";
|
||||
|
||||
export class BetterSqliteDriver implements SQLiteDatabaseDriver {
|
||||
private readonly db: DatabaseT;
|
||||
|
||||
constructor(filename: string) {
|
||||
const db = new Database(filename);
|
||||
this.db = db;
|
||||
db.pragma("journal_mode = WAL");
|
||||
}
|
||||
|
||||
run(sql: string, params: unknown[]) {
|
||||
this.db.prepare(sql).run(params);
|
||||
}
|
||||
|
||||
query<T>(sql: string, params: unknown[]): T[] {
|
||||
return this.db.prepare(sql).all(params) as T[];
|
||||
}
|
||||
|
||||
get<T>(sql: string, params: unknown[]): T | undefined {
|
||||
return this.db.prepare(sql).get(params) as T | undefined;
|
||||
}
|
||||
|
||||
transaction(callback: () => unknown) {
|
||||
return this.db.transaction(callback)();
|
||||
}
|
||||
|
||||
closeDb() {
|
||||
this.db.close();
|
||||
}
|
||||
}
|
||||
|
||||
export function getBetterSqliteStorage(filename: string) {
|
||||
const db = new BetterSqliteDriver(filename);
|
||||
|
||||
return getSqliteStorage(db);
|
||||
}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
import type { Peer } from "cojson";
|
||||
import { SQLiteNodeBase } from "cojson-storage";
|
||||
import { BetterSqliteDriver } from "./betterSqliteDriver.js";
|
||||
|
||||
export class SQLiteNode extends SQLiteNodeBase {
|
||||
static async asPeer({
|
||||
filename,
|
||||
localNodeName = "local",
|
||||
}: {
|
||||
filename: string;
|
||||
localNodeName?: string;
|
||||
}): Promise<Peer> {
|
||||
const db = new BetterSqliteDriver(filename);
|
||||
|
||||
return SQLiteNodeBase.create({
|
||||
db,
|
||||
localNodeName,
|
||||
maxBlockingTime: 500,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -2,18 +2,16 @@ import { randomUUID } from "node:crypto";
|
||||
import { unlinkSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { LocalNode, cojsonInternals } from "cojson";
|
||||
import { SQLiteNodeBase, StorageManagerSync } from "cojson-storage";
|
||||
import { LocalNode, StorageApiSync, cojsonInternals } from "cojson";
|
||||
import { WasmCrypto } from "cojson/crypto/WasmCrypto";
|
||||
import { expect, onTestFinished, test, vi } from "vitest";
|
||||
import { BetterSqliteDriver } from "../betterSqliteDriver.js";
|
||||
import { SQLiteNode } from "../index.js";
|
||||
import { getBetterSqliteStorage } from "../index.js";
|
||||
import { toSimplifiedMessages } from "./messagesTestUtils.js";
|
||||
import { trackMessages, waitFor } from "./testUtils.js";
|
||||
|
||||
const Crypto = await WasmCrypto.create();
|
||||
|
||||
async function createSQLiteStorage(defaultDbPath?: string) {
|
||||
function createSQLiteStorage(defaultDbPath?: string) {
|
||||
const dbPath = defaultDbPath ?? join(tmpdir(), `test-${randomUUID()}.db`);
|
||||
|
||||
if (!defaultDbPath) {
|
||||
@@ -23,29 +21,11 @@ async function createSQLiteStorage(defaultDbPath?: string) {
|
||||
}
|
||||
|
||||
return {
|
||||
peer: await SQLiteNode.asPeer({
|
||||
filename: dbPath,
|
||||
}),
|
||||
storage: getBetterSqliteStorage(dbPath),
|
||||
dbPath,
|
||||
};
|
||||
}
|
||||
|
||||
test("Should be able to initialize and load from empty DB", async () => {
|
||||
const agentSecret = Crypto.newRandomAgentSecret();
|
||||
|
||||
const node = new LocalNode(
|
||||
agentSecret,
|
||||
Crypto.newRandomSessionID(Crypto.getAgentID(agentSecret)),
|
||||
Crypto,
|
||||
);
|
||||
|
||||
node.syncManager.addPeer((await createSQLiteStorage()).peer);
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
|
||||
expect(node.syncManager.peers.storage).toBeDefined();
|
||||
});
|
||||
|
||||
test("should sync and load data from storage", async () => {
|
||||
const agentSecret = Crypto.newRandomAgentSecret();
|
||||
|
||||
@@ -55,11 +35,11 @@ test("should sync and load data from storage", async () => {
|
||||
Crypto,
|
||||
);
|
||||
|
||||
const node1Sync = trackMessages(node1);
|
||||
const node1Sync = trackMessages();
|
||||
|
||||
const { peer, dbPath } = await createSQLiteStorage();
|
||||
const { storage, dbPath } = createSQLiteStorage();
|
||||
|
||||
node1.syncManager.addPeer(peer);
|
||||
node1.setStorage(storage);
|
||||
|
||||
const group = node1.createGroup();
|
||||
|
||||
@@ -80,9 +60,7 @@ test("should sync and load data from storage", async () => {
|
||||
).toMatchInlineSnapshot(`
|
||||
[
|
||||
"client -> CONTENT Group header: true new: After: 0 New: 3",
|
||||
"storage -> KNOWN Group sessions: header/3",
|
||||
"client -> CONTENT Map header: true new: After: 0 New: 1",
|
||||
"storage -> KNOWN Map sessions: header/1",
|
||||
]
|
||||
`);
|
||||
|
||||
@@ -94,11 +72,9 @@ test("should sync and load data from storage", async () => {
|
||||
Crypto,
|
||||
);
|
||||
|
||||
const node2Sync = trackMessages(node2);
|
||||
const node2Sync = trackMessages();
|
||||
|
||||
const { peer: peer2 } = await createSQLiteStorage(dbPath);
|
||||
|
||||
node2.syncManager.addPeer(peer2);
|
||||
node2.setStorage(createSQLiteStorage(dbPath).storage);
|
||||
|
||||
const map2 = await node2.load(map.id);
|
||||
if (map2 === "unavailable") {
|
||||
@@ -119,9 +95,7 @@ test("should sync and load data from storage", async () => {
|
||||
[
|
||||
"client -> LOAD Map sessions: empty",
|
||||
"storage -> CONTENT Group header: true new: After: 0 New: 3",
|
||||
"client -> KNOWN Group sessions: header/3",
|
||||
"storage -> CONTENT Map header: true new: After: 0 New: 1",
|
||||
"client -> KNOWN Map sessions: header/1",
|
||||
]
|
||||
`);
|
||||
|
||||
@@ -137,11 +111,11 @@ test("should send an empty content message if there is no content", async () =>
|
||||
Crypto,
|
||||
);
|
||||
|
||||
const node1Sync = trackMessages(node1);
|
||||
const node1Sync = trackMessages();
|
||||
|
||||
const { peer, dbPath } = await createSQLiteStorage();
|
||||
const { storage, dbPath } = createSQLiteStorage();
|
||||
|
||||
node1.syncManager.addPeer(peer);
|
||||
node1.setStorage(storage);
|
||||
|
||||
const group = node1.createGroup();
|
||||
|
||||
@@ -160,9 +134,7 @@ test("should send an empty content message if there is no content", async () =>
|
||||
).toMatchInlineSnapshot(`
|
||||
[
|
||||
"client -> CONTENT Group header: true new: After: 0 New: 3",
|
||||
"storage -> KNOWN Group sessions: header/3",
|
||||
"client -> CONTENT Map header: true new: ",
|
||||
"storage -> KNOWN Map sessions: header/0",
|
||||
]
|
||||
`);
|
||||
|
||||
@@ -174,11 +146,9 @@ test("should send an empty content message if there is no content", async () =>
|
||||
Crypto,
|
||||
);
|
||||
|
||||
const node2Sync = trackMessages(node2);
|
||||
const node2Sync = trackMessages();
|
||||
|
||||
const { peer: peer2 } = await createSQLiteStorage(dbPath);
|
||||
|
||||
node2.syncManager.addPeer(peer2);
|
||||
node2.setStorage(createSQLiteStorage(dbPath).storage);
|
||||
|
||||
const map2 = await node2.load(map.id);
|
||||
if (map2 === "unavailable") {
|
||||
@@ -197,9 +167,7 @@ test("should send an empty content message if there is no content", async () =>
|
||||
[
|
||||
"client -> LOAD Map sessions: empty",
|
||||
"storage -> CONTENT Group header: true new: After: 0 New: 3",
|
||||
"client -> KNOWN Group sessions: header/3",
|
||||
"storage -> CONTENT Map header: true new: ",
|
||||
"client -> KNOWN Map sessions: header/0",
|
||||
]
|
||||
`);
|
||||
|
||||
@@ -215,11 +183,11 @@ test("should load dependencies correctly (group inheritance)", async () => {
|
||||
Crypto,
|
||||
);
|
||||
|
||||
const node1Sync = trackMessages(node1);
|
||||
const node1Sync = trackMessages();
|
||||
|
||||
const { peer, dbPath } = await createSQLiteStorage();
|
||||
const { storage, dbPath } = createSQLiteStorage();
|
||||
|
||||
node1.syncManager.addPeer(peer);
|
||||
node1.setStorage(storage);
|
||||
|
||||
const group = node1.createGroup();
|
||||
const parentGroup = node1.createGroup();
|
||||
@@ -243,12 +211,9 @@ test("should load dependencies correctly (group inheritance)", async () => {
|
||||
),
|
||||
).toMatchInlineSnapshot(`
|
||||
[
|
||||
"client -> CONTENT ParentGroup header: true new: After: 0 New: 4",
|
||||
"storage -> KNOWN ParentGroup sessions: header/4",
|
||||
"client -> CONTENT Group header: true new: After: 0 New: 5",
|
||||
"storage -> KNOWN Group sessions: header/5",
|
||||
"client -> CONTENT ParentGroup header: true new: After: 0 New: 4",
|
||||
"client -> CONTENT Map header: true new: After: 0 New: 1",
|
||||
"storage -> KNOWN Map sessions: header/1",
|
||||
]
|
||||
`);
|
||||
|
||||
@@ -260,11 +225,9 @@ test("should load dependencies correctly (group inheritance)", async () => {
|
||||
Crypto,
|
||||
);
|
||||
|
||||
const node2Sync = trackMessages(node2);
|
||||
const node2Sync = trackMessages();
|
||||
|
||||
const { peer: peer2 } = await createSQLiteStorage(dbPath);
|
||||
|
||||
node2.syncManager.addPeer(peer2);
|
||||
node2.setStorage(createSQLiteStorage(dbPath).storage);
|
||||
|
||||
await node2.load(map.id);
|
||||
|
||||
@@ -285,11 +248,8 @@ test("should load dependencies correctly (group inheritance)", async () => {
|
||||
[
|
||||
"client -> LOAD Map sessions: empty",
|
||||
"storage -> CONTENT ParentGroup header: true new: After: 0 New: 4",
|
||||
"client -> KNOWN ParentGroup sessions: header/4",
|
||||
"storage -> CONTENT Group header: true new: After: 0 New: 5",
|
||||
"client -> KNOWN Group sessions: header/5",
|
||||
"storage -> CONTENT Map header: true new: After: 0 New: 1",
|
||||
"client -> KNOWN Map sessions: header/1",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -303,11 +263,11 @@ test("should not send the same dependency value twice", async () => {
|
||||
Crypto,
|
||||
);
|
||||
|
||||
const node1Sync = trackMessages(node1);
|
||||
const node1Sync = trackMessages();
|
||||
|
||||
const { peer, dbPath } = await createSQLiteStorage();
|
||||
const { storage, dbPath } = createSQLiteStorage();
|
||||
|
||||
node1.syncManager.addPeer(peer);
|
||||
node1.setStorage(storage);
|
||||
|
||||
const group = node1.createGroup();
|
||||
const parentGroup = node1.createGroup();
|
||||
@@ -330,11 +290,9 @@ test("should not send the same dependency value twice", async () => {
|
||||
Crypto,
|
||||
);
|
||||
|
||||
const node2Sync = trackMessages(node2);
|
||||
const node2Sync = trackMessages();
|
||||
|
||||
const { peer: peer2 } = await createSQLiteStorage(dbPath);
|
||||
|
||||
node2.syncManager.addPeer(peer2);
|
||||
node2.setStorage(createSQLiteStorage(dbPath).storage);
|
||||
|
||||
await node2.load(map.id);
|
||||
await node2.load(mapFromParent.id);
|
||||
@@ -358,14 +316,10 @@ test("should not send the same dependency value twice", async () => {
|
||||
[
|
||||
"client -> LOAD Map sessions: empty",
|
||||
"storage -> CONTENT ParentGroup header: true new: After: 0 New: 4",
|
||||
"client -> KNOWN ParentGroup sessions: header/4",
|
||||
"storage -> CONTENT Group header: true new: After: 0 New: 5",
|
||||
"client -> KNOWN Group sessions: header/5",
|
||||
"storage -> CONTENT Map header: true new: After: 0 New: 1",
|
||||
"client -> KNOWN Map sessions: header/1",
|
||||
"client -> LOAD MapFromParent sessions: empty",
|
||||
"storage -> CONTENT MapFromParent header: true new: After: 0 New: 1",
|
||||
"client -> KNOWN MapFromParent sessions: header/1",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -379,11 +333,11 @@ test("should recover from data loss", async () => {
|
||||
Crypto,
|
||||
);
|
||||
|
||||
const node1Sync = trackMessages(node1);
|
||||
const node1Sync = trackMessages();
|
||||
|
||||
const { peer, dbPath } = await createSQLiteStorage();
|
||||
const { storage, dbPath } = createSQLiteStorage();
|
||||
|
||||
node1.syncManager.addPeer(peer);
|
||||
node1.setStorage(storage);
|
||||
|
||||
const group = node1.createGroup();
|
||||
|
||||
@@ -394,8 +348,8 @@ test("should recover from data loss", async () => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
|
||||
const mock = vi
|
||||
.spyOn(StorageManagerSync.prototype, "handleSyncMessage")
|
||||
.mockImplementation(() => Promise.resolve());
|
||||
.spyOn(StorageApiSync.prototype, "store")
|
||||
.mockImplementation(() => false);
|
||||
|
||||
map.set("1", 1);
|
||||
map.set("2", 2);
|
||||
@@ -419,13 +373,8 @@ test("should recover from data loss", async () => {
|
||||
).toMatchInlineSnapshot(`
|
||||
[
|
||||
"client -> CONTENT Group header: true new: After: 0 New: 3",
|
||||
"storage -> KNOWN Group sessions: header/3",
|
||||
"client -> CONTENT Map header: true new: After: 0 New: 1",
|
||||
"storage -> KNOWN Map sessions: header/1",
|
||||
"client -> CONTENT Map header: false new: After: 3 New: 1",
|
||||
"storage -> KNOWN CORRECTION Map sessions: header/1",
|
||||
"client -> CONTENT Map header: false new: After: 1 New: 3",
|
||||
"storage -> KNOWN Map sessions: header/4",
|
||||
]
|
||||
`);
|
||||
|
||||
@@ -437,11 +386,9 @@ test("should recover from data loss", async () => {
|
||||
Crypto,
|
||||
);
|
||||
|
||||
const node2Sync = trackMessages(node2);
|
||||
const node2Sync = trackMessages();
|
||||
|
||||
const { peer: peer2 } = await createSQLiteStorage(dbPath);
|
||||
|
||||
node2.syncManager.addPeer(peer2);
|
||||
node2.setStorage(createSQLiteStorage(dbPath).storage);
|
||||
|
||||
const map2 = await node2.load(map.id);
|
||||
|
||||
@@ -468,9 +415,7 @@ test("should recover from data loss", async () => {
|
||||
[
|
||||
"client -> LOAD Map sessions: empty",
|
||||
"storage -> CONTENT Group header: true new: After: 0 New: 3",
|
||||
"client -> KNOWN Group sessions: header/3",
|
||||
"storage -> CONTENT Map header: true new: After: 0 New: 4",
|
||||
"client -> KNOWN Map sessions: header/4",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -501,24 +446,28 @@ test("should recover missing dependencies from storage", async () => {
|
||||
node1.syncManager.addPeer(serverPeer);
|
||||
serverNode.syncManager.addPeer(clientPeer);
|
||||
|
||||
const handleSyncMessage = StorageManagerSync.prototype.handleSyncMessage;
|
||||
const store = StorageApiSync.prototype.store;
|
||||
|
||||
const mock = vi
|
||||
.spyOn(StorageManagerSync.prototype, "handleSyncMessage")
|
||||
.mockImplementation(function (this: StorageManagerSync, msg) {
|
||||
.spyOn(StorageApiSync.prototype, "store")
|
||||
.mockImplementation(function (
|
||||
this: StorageApiSync,
|
||||
data,
|
||||
correctionCallback,
|
||||
) {
|
||||
if (
|
||||
msg.action === "content" &&
|
||||
[group.core.id, account.core.id].includes(msg.id)
|
||||
data[0]?.id &&
|
||||
[group.core.id, account.core.id as string].includes(data[0].id)
|
||||
) {
|
||||
return Promise.resolve();
|
||||
return false;
|
||||
}
|
||||
|
||||
return handleSyncMessage.call(this, msg);
|
||||
return store.call(this, data, correctionCallback);
|
||||
});
|
||||
|
||||
const { peer, dbPath } = await createSQLiteStorage();
|
||||
const { storage, dbPath } = createSQLiteStorage();
|
||||
|
||||
node1.syncManager.addPeer(peer);
|
||||
node1.setStorage(storage);
|
||||
|
||||
const group = node1.createGroup();
|
||||
group.addMember("everyone", "writer");
|
||||
@@ -549,9 +498,7 @@ test("should recover missing dependencies from storage", async () => {
|
||||
node2.syncManager.addPeer(serverPeer2);
|
||||
serverNode.syncManager.addPeer(clientPeer2);
|
||||
|
||||
const { peer: peer2 } = await createSQLiteStorage(dbPath);
|
||||
|
||||
node2.syncManager.addPeer(peer2);
|
||||
node2.setStorage(createSQLiteStorage(dbPath).storage);
|
||||
|
||||
const map2 = await node2.load(map.id);
|
||||
|
||||
@@ -573,9 +520,9 @@ test("should sync multiple sessions in a single content message", async () => {
|
||||
Crypto,
|
||||
);
|
||||
|
||||
const { peer, dbPath } = await createSQLiteStorage();
|
||||
const { storage, dbPath } = createSQLiteStorage();
|
||||
|
||||
node1.syncManager.addPeer(peer);
|
||||
node1.setStorage(storage);
|
||||
|
||||
const group = node1.createGroup();
|
||||
|
||||
@@ -593,7 +540,7 @@ test("should sync multiple sessions in a single content message", async () => {
|
||||
Crypto,
|
||||
);
|
||||
|
||||
node2.syncManager.addPeer((await createSQLiteStorage(dbPath)).peer);
|
||||
node2.setStorage(createSQLiteStorage(dbPath).storage);
|
||||
|
||||
const map2 = await node2.load(map.id);
|
||||
if (map2 === "unavailable") {
|
||||
@@ -614,9 +561,9 @@ test("should sync multiple sessions in a single content message", async () => {
|
||||
Crypto,
|
||||
);
|
||||
|
||||
const node3Sync = trackMessages(node3);
|
||||
const node3Sync = trackMessages();
|
||||
|
||||
node3.syncManager.addPeer((await createSQLiteStorage(dbPath)).peer);
|
||||
node3.setStorage(createSQLiteStorage(dbPath).storage);
|
||||
|
||||
const map3 = await node3.load(map.id);
|
||||
if (map3 === "unavailable") {
|
||||
@@ -637,9 +584,7 @@ test("should sync multiple sessions in a single content message", async () => {
|
||||
[
|
||||
"client -> LOAD Map sessions: empty",
|
||||
"storage -> CONTENT Group header: true new: After: 0 New: 3",
|
||||
"client -> KNOWN Group sessions: header/3",
|
||||
"storage -> CONTENT Map header: true new: After: 0 New: 1 | After: 0 New: 1",
|
||||
"client -> KNOWN Map sessions: header/2",
|
||||
]
|
||||
`);
|
||||
|
||||
@@ -655,9 +600,9 @@ test("large coValue upload streaming", async () => {
|
||||
Crypto,
|
||||
);
|
||||
|
||||
const { peer, dbPath } = await createSQLiteStorage();
|
||||
const { storage, dbPath } = createSQLiteStorage();
|
||||
|
||||
node1.syncManager.addPeer(peer);
|
||||
node1.setStorage(storage);
|
||||
|
||||
const group = node1.createGroup();
|
||||
const largeMap = group.createMap();
|
||||
@@ -683,11 +628,9 @@ test("large coValue upload streaming", async () => {
|
||||
Crypto,
|
||||
);
|
||||
|
||||
const node2Sync = trackMessages(node2);
|
||||
const node2Sync = trackMessages();
|
||||
|
||||
const { peer: peer2 } = await createSQLiteStorage(dbPath);
|
||||
|
||||
node2.syncManager.addPeer(peer2);
|
||||
node2.setStorage(createSQLiteStorage(dbPath).storage);
|
||||
|
||||
const largeMapOnNode2 = await node2.load(largeMap.id);
|
||||
|
||||
@@ -714,51 +657,10 @@ test("large coValue upload streaming", async () => {
|
||||
).toMatchInlineSnapshot(`
|
||||
[
|
||||
"client -> LOAD Map sessions: empty",
|
||||
"storage -> KNOWN Map sessions: header/200",
|
||||
"storage -> CONTENT Group header: true new: After: 0 New: 3",
|
||||
"client -> KNOWN Group sessions: header/3",
|
||||
"storage -> CONTENT Map header: true new: After: 0 New: 97",
|
||||
"client -> KNOWN Map sessions: header/97",
|
||||
"storage -> CONTENT Map header: true new: After: 97 New: 97",
|
||||
"client -> KNOWN Map sessions: header/194",
|
||||
"storage -> CONTENT Map header: true new: After: 194 New: 6",
|
||||
"client -> KNOWN Map sessions: header/200",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test("should close the db when the node is closed", async () => {
|
||||
const agentSecret = Crypto.newRandomAgentSecret();
|
||||
|
||||
const node1 = new LocalNode(
|
||||
agentSecret,
|
||||
Crypto.newRandomSessionID(Crypto.getAgentID(agentSecret)),
|
||||
Crypto,
|
||||
);
|
||||
|
||||
const dbPath = join(tmpdir(), `test-${randomUUID()}.db`);
|
||||
|
||||
const db = new BetterSqliteDriver(dbPath);
|
||||
|
||||
const peer = SQLiteNodeBase.create({
|
||||
db,
|
||||
localNodeName: "test",
|
||||
maxBlockingTime: 500,
|
||||
});
|
||||
|
||||
const spy = vi.spyOn(db, "closeDb");
|
||||
|
||||
node1.syncManager.addPeer(peer);
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
||||
|
||||
expect(spy).not.toHaveBeenCalled();
|
||||
|
||||
node1.gracefulShutdown();
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
||||
|
||||
expect(spy).toHaveBeenCalled();
|
||||
|
||||
unlinkSync(dbPath);
|
||||
});
|
||||
|
||||
@@ -1,36 +1,64 @@
|
||||
import type { LocalNode, SyncMessage } from "cojson";
|
||||
import { StorageManagerSync } from "cojson-storage";
|
||||
import type { LocalNode, RawCoID, SyncMessage } from "cojson";
|
||||
import { StorageApiSync } from "cojson";
|
||||
import { onTestFinished } from "vitest";
|
||||
|
||||
export function trackMessages(node: LocalNode) {
|
||||
export function trackMessages() {
|
||||
const messages: {
|
||||
from: "client" | "server" | "storage";
|
||||
msg: SyncMessage;
|
||||
}[] = [];
|
||||
|
||||
const originalHandleSyncMessage =
|
||||
StorageManagerSync.prototype.handleSyncMessage;
|
||||
const originalNodeSyncMessage = node.syncManager.handleSyncMessage;
|
||||
const originalLoad = StorageApiSync.prototype.load;
|
||||
const originalStore = StorageApiSync.prototype.store;
|
||||
|
||||
StorageManagerSync.prototype.handleSyncMessage = async function (msg) {
|
||||
StorageApiSync.prototype.load = async function (id, callback, done) {
|
||||
messages.push({
|
||||
from: "client",
|
||||
msg,
|
||||
msg: {
|
||||
action: "load",
|
||||
id: id as RawCoID,
|
||||
header: false,
|
||||
sessions: {},
|
||||
},
|
||||
});
|
||||
return originalHandleSyncMessage.call(this, msg);
|
||||
return originalLoad.call(
|
||||
this,
|
||||
id,
|
||||
(msg) => {
|
||||
messages.push({
|
||||
from: "storage",
|
||||
msg,
|
||||
});
|
||||
callback(msg);
|
||||
},
|
||||
done,
|
||||
);
|
||||
};
|
||||
|
||||
node.syncManager.handleSyncMessage = async function (msg, peer) {
|
||||
messages.push({
|
||||
from: "storage",
|
||||
msg,
|
||||
StorageApiSync.prototype.store = function (data, correctionCallback) {
|
||||
for (const msg of data) {
|
||||
messages.push({
|
||||
from: "client",
|
||||
msg,
|
||||
});
|
||||
}
|
||||
return originalStore.call(this, data, (msg) => {
|
||||
messages.push({
|
||||
from: "storage",
|
||||
msg: {
|
||||
action: "known",
|
||||
isCorrection: true,
|
||||
...msg,
|
||||
},
|
||||
});
|
||||
correctionCallback(msg);
|
||||
});
|
||||
return originalNodeSyncMessage.call(this, msg, peer);
|
||||
};
|
||||
|
||||
const restore = () => {
|
||||
StorageManagerSync.prototype.handleSyncMessage = originalHandleSyncMessage;
|
||||
node.syncManager.handleSyncMessage = originalNodeSyncMessage;
|
||||
StorageApiSync.prototype.load = originalLoad;
|
||||
StorageApiSync.prototype.store = originalStore;
|
||||
messages.length = 0;
|
||||
};
|
||||
|
||||
onTestFinished(() => {
|
||||
|
||||
171
packages/cojson-storage/.gitignore
vendored
171
packages/cojson-storage/.gitignore
vendored
@@ -1,171 +0,0 @@
|
||||
# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore
|
||||
|
||||
# Logs
|
||||
|
||||
logs
|
||||
_.log
|
||||
npm-debug.log_
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
|
||||
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
||||
|
||||
# Runtime data
|
||||
|
||||
pids
|
||||
_.pid
|
||||
_.seed
|
||||
\*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
|
||||
coverage
|
||||
\*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
|
||||
\*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
|
||||
\*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
|
||||
.cache/
|
||||
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
|
||||
.temp
|
||||
.cache
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.\*
|
||||
|
||||
.DS_Store
|
||||
@@ -1,2 +0,0 @@
|
||||
coverage
|
||||
node_modules
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user