11import { Global } from "@/global"
22import { Filesystem } from "@/util"
3+ import { Flock } from "@opencode-ai/shared/util/flock"
4+ import { rename , rm } from "fs/promises"
35import { createSignal , type Setter } from "solid-js"
4- import { createStore } from "solid-js/store"
6+ import { createStore , unwrap } from "solid-js/store"
57import { createSimpleContext } from "./helper"
68import path from "path"
79
@@ -11,12 +13,29 @@ export const { use: useKV, provider: KVProvider } = createSimpleContext({
1113 const [ ready , setReady ] = createSignal ( false )
1214 const [ store , setStore ] = createStore < Record < string , any > > ( )
1315 const filePath = path . join ( Global . Path . state , "kv.json" )
16+ const lock = `tui-kv:${ filePath } `
17+ // Queue same-process writes so rapid updates persist in order.
18+ let write = Promise . resolve ( )
1419
15- Filesystem . readJson < Record < string , any > > ( filePath )
20+ // Write to a temp file first so kv.json is only replaced once the JSON is complete, avoiding partial writes if shutdown interrupts persistence.
21+ function writeSnapshot ( snapshot : Record < string , any > ) {
22+ const tempPath = `${ filePath } .${ process . pid } .${ Date . now ( ) } .tmp`
23+ return Filesystem . writeJson ( tempPath , snapshot )
24+ . then ( ( ) => rename ( tempPath , filePath ) )
25+ . catch ( async ( error ) => {
26+ await rm ( tempPath , { force : true } ) . catch ( ( ) => undefined )
27+ throw error
28+ } )
29+ }
30+
31+ // Read under the same lock used for writes because kv.json is shared across processes.
32+ Flock . withLock ( lock , ( ) => Filesystem . readJson < Record < string , any > > ( filePath ) )
1633 . then ( ( x ) => {
1734 setStore ( x )
1835 } )
19- . catch ( ( ) => { } )
36+ . catch ( ( error ) => {
37+ console . error ( "Failed to read KV state" , { filePath, error } )
38+ } )
2039 . finally ( ( ) => {
2140 setReady ( true )
2241 } )
@@ -44,7 +63,12 @@ export const { use: useKV, provider: KVProvider } = createSimpleContext({
4463 } ,
4564 set ( key : string , value : any ) {
4665 setStore ( key , value )
47- void Filesystem . writeJson ( filePath , store )
66+ const snapshot = structuredClone ( unwrap ( store ) )
67+ write = write
68+ . then ( ( ) => Flock . withLock ( lock , ( ) => writeSnapshot ( snapshot ) ) )
69+ . catch ( ( error ) => {
70+ console . error ( "Failed to write KV state" , { filePath, error } )
71+ } )
4872 } ,
4973 }
5074 return result
0 commit comments