@@ -57,10 +57,18 @@ function fromBase64(b64: string): Uint8Array {
5757 return bytes ;
5858}
5959
60+ export interface PersistableSessionState {
61+ active : boolean ;
62+ sessionToken : string ;
63+ registeredAt : number | null ;
64+ }
65+
6066export function createGitServeSession ( ) : {
6167 state : GitServeSessionState ;
6268 fetch : ( request : Request ) => Promise < Response > ;
6369 cleanup : ( ) => void ;
70+ persistableState : ( ) => PersistableSessionState ;
71+ restore : ( saved : PersistableSessionState ) => void ;
6472} {
6573 const state : GitServeSessionState = {
6674 active : false ,
@@ -351,18 +359,72 @@ export function createGitServeSession(): {
351359 return Response . json ( { ok : false , error : 'not found' } , { status : 404 } ) ;
352360 }
353361
354- return { state, fetch, cleanup } ;
362+ function persistableState ( ) : PersistableSessionState {
363+ return {
364+ active : state . active ,
365+ sessionToken : state . sessionToken ,
366+ registeredAt : state . registeredAt ,
367+ } ;
368+ }
369+
370+ function restore ( saved : PersistableSessionState ) : void {
371+ state . active = saved . active ;
372+ state . sessionToken = saved . sessionToken ;
373+ state . registeredAt = saved . registeredAt ;
374+ if ( state . active ) {
375+ resetSessionTimer ( ) ;
376+ }
377+ }
378+
379+ return { state, fetch, cleanup, persistableState, restore } ;
355380}
356381
382+ // Durable Object class used by Cloudflare Workers.
383+ // Accepts a state object with storage for persistence.
384+ // Also works without storage (e.g. in tests / Deno).
357385export class GitServeSession {
358386 private session : ReturnType < typeof createGitServeSession > ;
387+ // deno-lint-ignore no-explicit-any
388+ private storage : any ;
389+ private ready : Promise < void > ;
359390
360- constructor ( ) {
391+ // deno-lint-ignore no-explicit-any
392+ constructor ( state ?: any , _env ?: any ) {
361393 this . session = createGitServeSession ( ) ;
394+ this . storage = state ?. storage ?? null ;
395+
396+ const restoreFromStorage = async ( ) => {
397+ if ( ! this . storage ) return ;
398+ const saved = await this . storage . get ( 'session_state' ) ;
399+ if ( saved && typeof saved === 'object' ) {
400+ this . session . restore ( saved as PersistableSessionState ) ;
401+ }
402+ } ;
403+
404+ if ( state && typeof state . blockConcurrencyWhile === 'function' ) {
405+ this . ready = state . blockConcurrencyWhile ( restoreFromStorage ) ;
406+ } else {
407+ this . ready = restoreFromStorage ( ) ;
408+ }
409+ }
410+
411+ private async persist ( ) : Promise < void > {
412+ if ( ! this . storage ) return ;
413+ await this . storage . put ( 'session_state' , this . session . persistableState ( ) ) ;
362414 }
363415
364- // deno-lint-ignore require-await
365416 async fetch ( request : Request ) : Promise < Response > {
366- return this . session . fetch ( request ) ;
417+ await this . ready ;
418+ const url = new URL ( request . url ) ;
419+ const path = url . pathname ;
420+
421+ const response = await this . session . fetch ( request ) ;
422+
423+ // Persist state after register or cleanup-triggering operations
424+ if ( path === '/register' && request . method === 'POST' ) {
425+ await this . persist ( ) ;
426+ }
427+
428+ return response ;
367429 }
368430}
0 commit comments