Skip to content

Commit c617a51

Browse files
committed
feat(config-files): implement persistent caching for config files
1 parent e66ff0e commit c617a51

1 file changed

Lines changed: 61 additions & 6 deletions

File tree

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,80 @@
1+
import { inject, Injectable, REQUEST_CONTEXT } from '@angular/core';
12
import { Observable, shareReplay } from 'rxjs';
23

34
import { ConfigFileDefinition } from './definition';
45
import { ConfigFileLoader } from './loader';
56

7+
@Injectable({
8+
providedIn: 'root',
9+
useFactory: () =>
10+
ConfigFilesPersistentCache.useFromRequestContext() ??
11+
new ConfigFilesPersistentCache(),
12+
})
13+
export class ConfigFilesPersistentCache extends Map<
14+
string,
15+
Observable<unknown>
16+
> {
17+
static readonly key = Symbol('ConfigFilesPersistentCache');
18+
19+
static useFromRequestContext(): ConfigFilesPersistentCache | null {
20+
const context = inject(REQUEST_CONTEXT);
21+
if (!context) return null;
22+
if (typeof context !== 'object') return null;
23+
const cache = Reflect.get(context, this.key);
24+
if (!cache) return null;
25+
return cache;
26+
}
27+
28+
constructor() {
29+
super();
30+
}
31+
}
32+
33+
@Injectable({ providedIn: 'root' })
34+
export class ConfigFilesSessionCache extends Map<
35+
ConfigFileDefinition<unknown, unknown>,
36+
Observable<unknown>
37+
> {
38+
constructor() {
39+
super();
40+
}
41+
}
42+
643
/**
744
* Decorator for {@link ConfigFileLoader}
845
* that caches the loaded configuration objects of each definition.
46+
*
47+
* In SSR applications, a persistent cache can be provided from the server
48+
* so that config files are only loaded once throughout the application
49+
* lifecycle:
50+
* ```ts
51+
* const app = express();
52+
* const angularApp = new AngularNodeAppEngine();
53+
* const configFilesCache = new ConfigFilesPersistentCache();
54+
* ```
55+
* ```ts
56+
* angularApp
57+
* .handle(req, { [ConfigFilesPersistentCache.key]: configFilesCache })
58+
* .then((response) =>
59+
* response ? writeResponseToNodeResponse(response, res) : next(),
60+
* )
61+
* .catch(next);
62+
* ```
963
*/
1064
export class CacheConfigFiles implements ConfigFileLoader {
11-
#cache = new Map<
12-
ConfigFileDefinition<unknown, unknown>,
13-
Observable<unknown>
14-
>();
65+
#sessionCache = inject(ConfigFilesSessionCache);
66+
#persistentCache = inject(ConfigFilesPersistentCache);
1567

1668
constructor(private kernel: ConfigFileLoader) {}
1769

1870
load<T, Schema>(def: ConfigFileDefinition<T, Schema>): Observable<T> {
19-
const cached = this.#cache.get(def);
71+
const cached =
72+
(def.id && this.#persistentCache.get(def.id)) ??
73+
this.#sessionCache.get(def);
2074
if (cached) return cached as Observable<T>;
2175
const result$ = this.kernel.load(def).pipe(shareReplay(1));
22-
this.#cache.set(def, result$);
76+
if (def.id) this.#persistentCache.set(def.id, result$);
77+
this.#sessionCache.set(def, result$);
2378
return result$;
2479
}
2580
}

0 commit comments

Comments
 (0)