@@ -30,9 +30,11 @@ import {assertInInjectionContext} from '../di/contextual';
3030import { Injector } from '../di/injector' ;
3131import { inject } from '../di/injector_compatibility' ;
3232import { RuntimeError , RuntimeErrorCode } from '../errors' ;
33+ import { CACHE_ACTIVE } from '../hydration/cache' ;
3334import { DestroyRef } from '../linker/destroy_ref' ;
3435import { PendingTasks } from '../pending_tasks' ;
3536import { linkedSignal } from '../render3/reactivity/linked_signal' ;
37+ import { StateKey , TransferState } from '../transfer_state' ;
3638
3739/**
3840 * Constructs a `Resource` that projects a reactive request to an asynchronous operation defined by
@@ -78,6 +80,7 @@ export function resource<T, R>(options: ResourceOptions<T, R>): ResourceRef<T |
7880 options . equal ? wrapEqualityFn ( options . equal ) : undefined ,
7981 options . debugName ,
8082 options . injector ?? inject ( Injector ) ,
83+ options . id as StateKey < T > ,
8184 ) ;
8285}
8386
@@ -195,6 +198,7 @@ export class ResourceImpl<T, R> extends BaseWritableResource<T> implements Resou
195198
196199 override readonly status : Signal < ResourceStatus > ;
197200 override readonly error : Signal < Error | undefined > ;
201+ private readonly transferState : TransferState | undefined ;
198202
199203 constructor (
200204 request : ( ctx : ResourceParamsContext ) => R ,
@@ -203,6 +207,7 @@ export class ResourceImpl<T, R> extends BaseWritableResource<T> implements Resou
203207 private readonly equal : ValueEqualityFn < T > | undefined ,
204208 private readonly debugName : string | undefined ,
205209 injector : Injector ,
210+ private transferCacheKey : StateKey < T > | undefined ,
206211 getInitialStream ?: ( request : R ) => Signal < ResourceStreamItem < T > > | undefined ,
207212 ) {
208213 if ( isInParamsFunction ( ) ) {
@@ -236,6 +241,10 @@ export class ResourceImpl<T, R> extends BaseWritableResource<T> implements Resou
236241 debugName ,
237242 ) ;
238243
244+ const cacheState = injector . get ( CACHE_ACTIVE , undefined , { optional : true } ) ?? { isActive : false } ;
245+
246+ this . transferState = injector . get ( TransferState , undefined , { optional : true } ) ?? undefined ;
247+
239248 this . extRequest = linkedSignal < WrappedRequest > (
240249 ( ) => {
241250 try {
@@ -274,7 +283,21 @@ export class ResourceImpl<T, R> extends BaseWritableResource<T> implements Resou
274283 ) ;
275284 } else if ( ! status ) {
276285 if ( ! previous ) {
277- stream = getInitialStream ?.( extRequest . request as R ) ;
286+ const transferState = this . transferState ;
287+ const cacheKey = this . transferCacheKey ;
288+ if ( cacheState . isActive && cacheKey && transferState && request !== undefined ) {
289+ const key = this . transferCacheKey ;
290+ if ( transferState . hasKey ( cacheKey ) ) {
291+ stream = signal (
292+ { value : transferState . get ( cacheKey , defaultValue ) } ,
293+ ngDevMode ? createDebugNameObject ( this . debugName , 'stream' ) : undefined ,
294+ ) ;
295+ }
296+ }
297+
298+ if ( ! stream ) {
299+ stream = getInitialStream ?.( extRequest . request as R ) ;
300+ }
278301 // Clear getInitialStream so it doesn't hold onto memory
279302 getInitialStream = undefined ;
280303 status = request === undefined ? 'idle' : stream ? 'resolved' : 'loading' ;
@@ -446,6 +469,11 @@ export class ResourceImpl<T, R> extends BaseWritableResource<T> implements Resou
446469 previousStatus : 'resolved' ,
447470 stream,
448471 } ) ;
472+
473+ const result = untracked ( stream ) ;
474+ if ( typeof ngServerMode !== 'undefined' && ngServerMode ) {
475+ saveToTransferState ( result , this . transferCacheKey , this . transferState ) ;
476+ }
449477 } else {
450478 const resolvedStream = await stream ;
451479 if ( shouldDiscard ( ) ) {
@@ -458,6 +486,12 @@ export class ResourceImpl<T, R> extends BaseWritableResource<T> implements Resou
458486 previousStatus : 'resolved' ,
459487 stream : resolvedStream ,
460488 } ) ;
489+
490+ // Use a local variable for the result so TypeScript can narrow `resolvedStream` correctly.
491+ const result = resolvedStream ? untracked ( resolvedStream ) : undefined ;
492+ if ( typeof ngServerMode !== 'undefined' && ngServerMode ) {
493+ saveToTransferState ( result , this . transferCacheKey , this . transferState ) ;
494+ }
461495 }
462496 } catch ( err ) {
463497 rethrowFatalErrors ( err ) ;
@@ -491,6 +525,16 @@ export class ResourceImpl<T, R> extends BaseWritableResource<T> implements Resou
491525 }
492526}
493527
528+ function saveToTransferState < R , T > (
529+ result : ResourceStreamItem < T > | undefined ,
530+ transferCacheKey : StateKey < T > | undefined ,
531+ transferState : TransferState | undefined ,
532+ ) : void {
533+ if ( transferCacheKey && transferState && result && isResolved ( result ) ) {
534+ transferState . set ( transferCacheKey , result . value ) ;
535+ }
536+ }
537+
494538/**
495539 * Wraps an equality function to handle either value being `undefined`.
496540 */
0 commit comments