1+ import { sep } from "@tauri-apps/api/path" ;
12import {
23 exists ,
34 readDir ,
45 readTextFile ,
6+ remove ,
57 writeTextFile ,
68} from "@tauri-apps/plugin-fs" ;
79import { createCustomPersister } from "tinybase/persisters/with-schemas" ;
@@ -23,6 +25,7 @@ import {
2325 ensureDirsExist ,
2426 getDataDir ,
2527 getSessionDir ,
28+ isUUID ,
2629 type PersisterMode ,
2730} from "./utils" ;
2831
@@ -52,7 +55,10 @@ async function loadSessionMetaRecursively(
5255 result : LoadedData ,
5356 now : string ,
5457) : Promise < void > {
55- const fullPath = currentPath ? `${ sessionsDir } /${ currentPath } ` : sessionsDir ;
58+ const s = sep ( ) ;
59+ const fullPath = currentPath
60+ ? [ sessionsDir , currentPath ] . join ( s )
61+ : sessionsDir ;
5662
5763 let entries : { name : string ; isDirectory : boolean } [ ] ;
5864 try {
@@ -64,8 +70,10 @@ async function loadSessionMetaRecursively(
6470 for ( const entry of entries ) {
6571 if ( ! entry . isDirectory ) continue ;
6672
67- const entryPath = currentPath ? `${ currentPath } /${ entry . name } ` : entry . name ;
68- const metaPath = `${ sessionsDir } /${ entryPath } /_meta.json` ;
73+ const entryPath = currentPath
74+ ? [ currentPath , entry . name ] . join ( s )
75+ : entry . name ;
76+ const metaPath = [ sessionsDir , entryPath , "_meta.json" ] . join ( s ) ;
6977 const hasMetaJson = await exists ( metaPath ) ;
7078
7179 if ( hasMetaJson ) {
@@ -130,14 +138,75 @@ async function loadAllSessionMeta(dataDir: string): Promise<LoadedData> {
130138 mapping_tag_session : { } ,
131139 } ;
132140
133- const sessionsDir = ` ${ dataDir } / sessions` ;
141+ const sessionsDir = [ dataDir , " sessions" ] . join ( sep ( ) ) ;
134142 const now = new Date ( ) . toISOString ( ) ;
135143
136144 await loadSessionMetaRecursively ( sessionsDir , "" , result , now ) ;
137145
138146 return result ;
139147}
140148
149+ async function collectSessionDirsRecursively (
150+ sessionsDir : string ,
151+ currentPath : string ,
152+ result : Array < { path : string ; name : string } > ,
153+ ) : Promise < void > {
154+ const s = sep ( ) ;
155+ const fullPath = currentPath
156+ ? [ sessionsDir , currentPath ] . join ( s )
157+ : sessionsDir ;
158+
159+ let entries : { name : string ; isDirectory : boolean } [ ] ;
160+ try {
161+ entries = await readDir ( fullPath ) ;
162+ } catch {
163+ return ;
164+ }
165+
166+ for ( const entry of entries ) {
167+ if ( ! entry . isDirectory ) continue ;
168+
169+ const entryPath = currentPath
170+ ? [ currentPath , entry . name ] . join ( s )
171+ : entry . name ;
172+ const metaPath = [ sessionsDir , entryPath , "_meta.json" ] . join ( s ) ;
173+ const hasMetaJson = await exists ( metaPath ) ;
174+
175+ if ( hasMetaJson ) {
176+ result . push ( { path : [ sessionsDir , entryPath ] . join ( s ) , name : entry . name } ) ;
177+ } else {
178+ await collectSessionDirsRecursively ( sessionsDir , entryPath , result ) ;
179+ }
180+ }
181+ }
182+
183+ async function cleanupOrphanSessionDirs (
184+ dataDir : string ,
185+ validSessionIds : Set < string > ,
186+ ) : Promise < void > {
187+ const sessionsDir = [ dataDir , "sessions" ] . join ( sep ( ) ) ;
188+ const existingDirs : Array < { path : string ; name : string } > = [ ] ;
189+
190+ try {
191+ await collectSessionDirsRecursively ( sessionsDir , "" , existingDirs ) ;
192+ } catch {
193+ return ;
194+ }
195+
196+ for ( const dir of existingDirs ) {
197+ if ( isUUID ( dir . name ) && ! validSessionIds . has ( dir . name ) ) {
198+ try {
199+ await remove ( dir . path , { recursive : true } ) ;
200+ } catch ( e ) {
201+ console . error (
202+ `[SessionPersister] Failed to remove orphan dir ${ dir . path } :` ,
203+ e ,
204+ ) ;
205+ }
206+ }
207+ }
208+ }
209+
141210export function collectSessionMeta < Schemas extends OptionalSchemas > (
142211 store : MergeableStore < Schemas > ,
143212) : Map < string , SessionMetaJson > {
@@ -230,10 +299,6 @@ export function createSessionPersister<Schemas extends OptionalSchemas>(
230299 const dataDir = await getDataDir ( ) ;
231300 const sessionMetas = collectSessionMeta ( store ) ;
232301
233- if ( sessionMetas . size === 0 ) {
234- return ;
235- }
236-
237302 const dirs = new Set < string > ( ) ;
238303 const writeOperations : Array < { path : string ; content : string } > =
239304 [ ] ;
@@ -247,16 +312,23 @@ export function createSessionPersister<Schemas extends OptionalSchemas>(
247312 dirs . add ( sessionDir ) ;
248313
249314 writeOperations . push ( {
250- path : ` ${ sessionDir } / _meta.json` ,
315+ path : [ sessionDir , " _meta.json" ] . join ( sep ( ) ) ,
251316 content : JSON . stringify ( meta , null , 2 ) ,
252317 } ) ;
253318 }
254319
255- await ensureDirsExist ( dirs ) ;
320+ if ( writeOperations . length > 0 ) {
321+ await ensureDirsExist ( dirs ) ;
256322
257- for ( const op of writeOperations ) {
258- await writeTextFile ( op . path , op . content ) ;
323+ for ( const op of writeOperations ) {
324+ await writeTextFile ( op . path , op . content ) ;
325+ }
259326 }
327+
328+ await cleanupOrphanSessionDirs (
329+ dataDir ,
330+ new Set ( sessionMetas . keys ( ) ) ,
331+ ) ;
260332 } catch ( error ) {
261333 console . error ( "[SessionPersister] save error:" , error ) ;
262334 }
@@ -266,8 +338,8 @@ export function createSessionPersister<Schemas extends OptionalSchemas>(
266338 store ,
267339 loadFn ,
268340 saveFn ,
269- ( listener ) => setInterval ( listener , 1000 ) ,
270- ( handle ) => clearInterval ( handle ) ,
341+ ( ) => null ,
342+ ( ) => { } ,
271343 ( error ) => console . error ( "[SessionPersister]:" , error ) ,
272344 StoreOrMergeableStore ,
273345 ) ;
0 commit comments