@angular-cool/repository 21.0.5 → 21.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -42,7 +42,7 @@ export class MyService {
42
42
  ```typescript
43
43
  import { signal } from '@angular/common';
44
44
  import { MyService } from './my-service.service';
45
- import { ItemId, ItemDTO } from './my-item.model';
45
+ import { ItemDTO, ItemId } from './my-item.model';
46
46
 
47
47
  @Component(/*...*/)
48
48
  export class MyComponent {
@@ -55,13 +55,17 @@ export class MyComponent {
55
55
  protected async updateItem() {
56
56
  // Update item on the server here
57
57
 
58
- await this._myService.items.reload(this.idParam()); // This reloads the item from the server and updates the signal for all subscribers
58
+ await this.myItem.reload(); // This reloads the item from the server and updates the signal for all subscribers
59
59
  }
60
60
  }
61
61
  ```
62
62
 
63
63
  ### Manually update value if needed
64
64
 
65
+ ```typescript
66
+ this.myItem.set(newValue); // Updates the signal for all subscribers
67
+ ```
68
+ OR
65
69
  ```typescript
66
70
  await this._myService.items.setValue(this.idParam(), myValue); // Updates the signal for all subscribers
67
71
  ```
@@ -1,5 +1,5 @@
1
1
  import { rxResource } from '@angular/core/rxjs-interop';
2
- import { ReplaySubject, firstValueFrom } from 'rxjs';
2
+ import { ReplaySubject, BehaviorSubject, firstValueFrom } from 'rxjs';
3
3
 
4
4
  class ResourceCache {
5
5
  get isInvalid() {
@@ -8,44 +8,57 @@ class ResourceCache {
8
8
  constructor(loader, maxAge) {
9
9
  this.loader = loader;
10
10
  this.maxAge = maxAge;
11
- this._subject = new ReplaySubject();
11
+ this._valueSubject = new ReplaySubject(1);
12
+ this._isLoading = false;
13
+ this._isLoadingSubject = new BehaviorSubject(false);
12
14
  this._validUntil = null;
13
15
  }
14
- getObservable() {
15
- return this._subject.asObservable();
16
+ getValueObservable() {
17
+ return this._valueSubject.asObservable();
16
18
  }
17
19
  async keepDataFreshAsync() {
18
20
  if (!this.isInvalid) {
19
21
  return;
20
22
  }
21
- const data = await this.loader();
22
- await this.setValueAsync(data);
23
+ await this._reloadValueAsync();
24
+ }
25
+ async _reloadValueAsync() {
26
+ if (this._isLoading) {
27
+ await firstValueFrom(this._isLoadingSubject);
28
+ return;
29
+ }
30
+ this._setIsLoading(true);
31
+ try {
32
+ const data = await this.loader();
33
+ await this.setValueAsync(data);
34
+ }
35
+ finally {
36
+ this._setIsLoading(false);
37
+ }
23
38
  }
24
39
  async setValueAsync(value) {
25
40
  this._validUntil = Date.now() + this.maxAge;
26
- this._subject.next(value);
41
+ this._valueSubject.next(value);
27
42
  }
28
43
  async getValueAsync() {
29
44
  await this.keepDataFreshAsync();
30
- return firstValueFrom(this._subject);
45
+ return firstValueFrom(this._valueSubject);
31
46
  }
32
47
  invalidate() {
33
48
  this._validUntil = null;
34
49
  }
50
+ _setIsLoading(isLoading) {
51
+ this._isLoading = isLoading;
52
+ this._isLoadingSubject.next(isLoading);
53
+ }
35
54
  }
36
55
 
37
56
  const DEFAULT_MAX_AGE = 5 * 60 * 1000;
38
- class ResourceRepository {
57
+ class ResourceRepositoryImpl {
39
58
  constructor(options) {
40
59
  this.options = options;
41
60
  this._cacheStore = new Map();
42
61
  }
43
- /**
44
- * Retrieves a resource cache
45
- *
46
- * @param {Signal<TParams>} key - A signal function used to resolve the parameters for retrieving or initializing the resource.
47
- * @return {ReloadableSignal<TItem | undefined>} A read-only signal containing the item associated with the provided key, already cached.
48
- */
49
62
  get(key) {
50
63
  const resource = rxResource({
51
64
  params: () => key(),
@@ -58,22 +71,18 @@ class ResourceRepository {
58
71
  }
59
72
  // Do not wait for it to load
60
73
  cache.keepDataFreshAsync();
61
- return cache.getObservable();
74
+ return cache.getValueObservable();
75
+ }
76
+ });
77
+ return Object.assign(resource.asReadonly().value, {
78
+ reload: async () => {
79
+ await this.reload(key());
80
+ },
81
+ set: async (value) => {
82
+ await this.setValue(key(), value);
62
83
  }
63
84
  });
64
- const resultSignal = resource.asReadonly().value;
65
- resultSignal.reload = async () => {
66
- await this.reload(key());
67
- };
68
- return resultSignal;
69
- }
70
- /**
71
- * Reloads the cache associated with the given key, ensuring it is updated with fresh data.
72
- *
73
- * @param {TParams} key - The key used to identify the specific cache entry to reload.
74
- * @return {Promise<void>} A promise that resolves when the cache has been refreshed.
75
- * @throws {Error} If no cache is found for the provided key or if data loading fails.
76
- */
85
+ }
77
86
  async reload(key) {
78
87
  const cacheKey = this._createCacheKey(key);
79
88
  const cache = this._cacheStore.get(cacheKey);
@@ -83,14 +92,6 @@ class ResourceRepository {
83
92
  cache.invalidate();
84
93
  await cache.keepDataFreshAsync();
85
94
  }
86
- /**
87
- * Retrieves a value from the cache associated with the specified key.
88
- *
89
- * @param {TParams} key - The key used to identify the cache entry.
90
- * @return {Promise<TItem | undefined>} A promise that resolves with the retrieved value
91
- * or undefined if the value does not exist.
92
- * @throws {Error} If no cache is found for the given key.
93
- */
94
95
  async getValue(key) {
95
96
  const cacheKey = this._createCacheKey(key);
96
97
  const cache = this._cacheStore.get(cacheKey);
@@ -99,13 +100,6 @@ class ResourceRepository {
99
100
  }
100
101
  return await cache.getValueAsync();
101
102
  }
102
- /**
103
- * Sets a value in the cache associated with the given key and update all value subscribers
104
- *
105
- * @param {TParams} key - The unique key used to identify the cache.
106
- * @param {TItem} value - The value to be stored in the cache
107
- * @return {Promise<void>} A promise that resolves when the value is successfully set.
108
- */
109
103
  async setValue(key, value) {
110
104
  const cacheKey = this._createCacheKey(key);
111
105
  let cache = this._cacheStore.get(cacheKey);
@@ -114,13 +108,6 @@ class ResourceRepository {
114
108
  }
115
109
  await cache.setValueAsync(value);
116
110
  }
117
- /**
118
- * Updates the value associated with the given key by applying an update function and update all value subscribers
119
- *
120
- * @param {TParams} key - The key whose value needs to be updated.
121
- * @param {function(value: TItem | undefined): Promise<TItem>} updateFn - An asynchronous function that takes the current value (or undefined if not present) and returns the updated value.
122
- * @return {Promise<void>} A promise that resolves when the update operation is complete.
123
- */
124
111
  async updateValue(key, updateFn) {
125
112
  const value = await this.getValue(key);
126
113
  await this.setValue(key, await updateFn(value));
@@ -129,6 +116,7 @@ class ResourceRepository {
129
116
  return (this.options.cacheKeyGenerator ?? JSON.stringify)(params);
130
117
  }
131
118
  }
119
+
132
120
  /**
133
121
  * Creates a new instance of a ResourceRepository with the specified options.
134
122
  *
@@ -136,7 +124,7 @@ class ResourceRepository {
136
124
  * @return {ResourceRepository<TParams, TItem>} A new instance of ResourceRepository configured with the given options.
137
125
  */
138
126
  function resourceRepository(options) {
139
- return new ResourceRepository(options);
127
+ return new ResourceRepositoryImpl(options);
140
128
  }
141
129
 
142
130
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"angular-cool-repository.mjs","sources":["../../../projects/repository/src/lib/resource-cache.ts","../../../projects/repository/src/lib/resource-repository.ts","../../../projects/repository/src/angular-cool-repository.ts"],"sourcesContent":["import { firstValueFrom, ReplaySubject } from 'rxjs';\n\nexport class ResourceCache<TItem> {\n private _subject = new ReplaySubject<TItem | undefined>();\n private _validUntil: number | null = null;\n\n public get isInvalid(): boolean {\n return this._validUntil === null || Date.now() > this._validUntil;\n }\n\n constructor(\n private loader: () => Promise<TItem | undefined>,\n private maxAge: number,\n ) {\n }\n\n public getObservable() {\n return this._subject.asObservable();\n }\n\n public async keepDataFreshAsync() {\n if (!this.isInvalid) {\n return;\n }\n\n const data = await this.loader();\n\n await this.setValueAsync(data);\n }\n\n public async setValueAsync(value: TItem | undefined) {\n this._validUntil = Date.now() + this.maxAge;\n this._subject.next(value);\n }\n\n public async getValueAsync(): Promise<TItem | undefined> {\n await this.keepDataFreshAsync();\n\n return firstValueFrom(this._subject);\n }\n\n public invalidate() {\n this._validUntil = null;\n }\n}\n","import { Signal } from '@angular/core';\nimport { rxResource } from '@angular/core/rxjs-interop';\nimport { ResourceCache } from './resource-cache';\n\nconst DEFAULT_MAX_AGE = 5 * 60 * 1000;\n\ntype CacheKey = string;\n\nexport type ReloadableSignal<T> = Signal<T> & { reload: () => Promise<void> };\n\nclass ResourceRepository<TParams, TItem> {\n private _cacheStore = new Map<CacheKey, ResourceCache<TItem>>();\n\n constructor(private options: ResourceRepositoryOptions<TParams, TItem>) {\n }\n\n /**\n * Retrieves a resource cache\n *\n * @param {Signal<TParams>} key - A signal function used to resolve the parameters for retrieving or initializing the resource.\n * @return {ReloadableSignal<TItem | undefined>} A read-only signal containing the item associated with the provided key, already cached.\n */\n public get(key: Signal<TParams>): ReloadableSignal<TItem | undefined> {\n const resource = rxResource<TItem | undefined, TParams>({\n params: () => key(),\n stream: ({ params }) => {\n const cacheKey = this._createCacheKey(params);\n\n let cache = this._cacheStore.get(cacheKey);\n\n if (!cache) {\n cache = new ResourceCache(\n ((p) => () => this.options.loader(p))(params),\n this.options.maxAge ?? DEFAULT_MAX_AGE,\n );\n\n this._cacheStore.set(cacheKey, cache);\n }\n\n // Do not wait for it to load\n cache.keepDataFreshAsync();\n\n return cache.getObservable();\n }\n });\n\n const resultSignal = resource.asReadonly().value as ReloadableSignal<TItem | undefined>;\n\n resultSignal.reload = async () => {\n await this.reload(key());\n };\n\n return resultSignal;\n }\n\n /**\n * Reloads the cache associated with the given key, ensuring it is updated with fresh data.\n *\n * @param {TParams} key - The key used to identify the specific cache entry to reload.\n * @return {Promise<void>} A promise that resolves when the cache has been refreshed.\n * @throws {Error} If no cache is found for the provided key or if data loading fails.\n */\n public async reload(key: TParams): Promise<void> {\n const cacheKey = this._createCacheKey(key);\n\n const cache = this._cacheStore.get(cacheKey);\n\n if (!cache) {\n throw new Error(`No cache found for the given key: ${key}`);\n }\n\n cache.invalidate();\n\n await cache.keepDataFreshAsync();\n }\n\n /**\n * Retrieves a value from the cache associated with the specified key.\n *\n * @param {TParams} key - The key used to identify the cache entry.\n * @return {Promise<TItem | undefined>} A promise that resolves with the retrieved value\n * or undefined if the value does not exist.\n * @throws {Error} If no cache is found for the given key.\n */\n public async getValue(key: TParams): Promise<TItem | undefined> {\n const cacheKey = this._createCacheKey(key);\n\n const cache = this._cacheStore.get(cacheKey);\n\n if (!cache) {\n throw new Error(`No cache found for the given key: ${key}`);\n }\n\n return await cache.getValueAsync();\n }\n\n /**\n * Sets a value in the cache associated with the given key and update all value subscribers\n *\n * @param {TParams} key - The unique key used to identify the cache.\n * @param {TItem} value - The value to be stored in the cache\n * @return {Promise<void>} A promise that resolves when the value is successfully set.\n */\n public async setValue(key: TParams, value: TItem): Promise<void> {\n const cacheKey = this._createCacheKey(key);\n\n let cache = this._cacheStore.get(cacheKey);\n\n if (!cache) {\n throw new Error(`No cache found for the given key: ${key}`);\n }\n\n await cache.setValueAsync(value);\n }\n\n /**\n * Updates the value associated with the given key by applying an update function and update all value subscribers\n *\n * @param {TParams} key - The key whose value needs to be updated.\n * @param {function(value: TItem | undefined): Promise<TItem>} updateFn - An asynchronous function that takes the current value (or undefined if not present) and returns the updated value.\n * @return {Promise<void>} A promise that resolves when the update operation is complete.\n */\n public async updateValue(key: TParams, updateFn: (value: TItem | undefined) => Promise<TItem>): Promise<void> {\n const value = await this.getValue(key);\n\n await this.setValue(key, await updateFn(value));\n }\n\n private _createCacheKey(params: TParams): CacheKey {\n return (this.options.cacheKeyGenerator ?? JSON.stringify)(params) as CacheKey;\n }\n}\n\n/**\n * Configuration options for a ResourceRepository.\n * This interface provides options for customizing the behavior\n * of resource loading and caching mechanisms.\n *\n * @template TParams The type of the parameters used to load a resource.\n * @template TItem The type of the items being loaded or cached.\n *\n * @property loader A function responsible for loading a resource. It receives\n * parameters of type `TParams` and returns a Promise resolving to an item of type `TItem`.\n *\n * @property [maxAge] Optional. Specifies the maximum duration (in milliseconds)\n * for which a cached resource remains valid. Default: 5 minutes\n *\n * @property [cacheKeyGenerator] Optional. A function used to generate a unique\n * cache key based on the input parameters of type `TParams`. Default: JSON.stringify()\n */\nexport interface ResourceRepositoryOptions<TParams, TItem> {\n loader: (params: TParams) => Promise<TItem>;\n\n maxAge?: number;\n\n cacheKeyGenerator?: (params: TParams) => string;\n}\n\n/**\n * Creates a new instance of a ResourceRepository with the specified options.\n *\n * @param {ResourceRepositoryOptions<TParams, TItem>} options - The configuration options for the resource repository.\n * @return {ResourceRepository<TParams, TItem>} A new instance of ResourceRepository configured with the given options.\n */\nexport function resourceRepository<TParams, TItem>(options: ResourceRepositoryOptions<TParams, TItem>): ResourceRepository<TParams, TItem> {\n return new ResourceRepository<TParams, TItem>(options);\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"angular-cool-repository.mjs","sources":["../../../projects/repository/src/lib/resource-cache.ts","../../../projects/repository/src/lib/resource-repository.ts","../../../projects/repository/src/public-api.ts","../../../projects/repository/src/angular-cool-repository.ts"],"sourcesContent":["import { BehaviorSubject, firstValueFrom, ReplaySubject } from 'rxjs';\n\nexport class ResourceCache<TItem> {\n private _valueSubject = new ReplaySubject<TItem | undefined>(1);\n\n private _isLoading = false;\n private _isLoadingSubject = new BehaviorSubject<boolean>(false);\n\n private _validUntil: number | null = null;\n\n public get isInvalid(): boolean {\n return this._validUntil === null || Date.now() > this._validUntil;\n }\n\n constructor(\n private loader: () => Promise<TItem | undefined>,\n private maxAge: number,\n ) {\n }\n\n public getValueObservable() {\n return this._valueSubject.asObservable();\n }\n\n public async keepDataFreshAsync() {\n if (!this.isInvalid) {\n return;\n }\n\n await this._reloadValueAsync();\n }\n\n private async _reloadValueAsync(): Promise<void> {\n if (this._isLoading) {\n await firstValueFrom(this._isLoadingSubject);\n\n return;\n }\n\n this._setIsLoading(true);\n\n try {\n const data = await this.loader();\n\n await this.setValueAsync(data);\n } finally {\n this._setIsLoading(false);\n }\n }\n\n public async setValueAsync(value: TItem | undefined) {\n this._validUntil = Date.now() + this.maxAge;\n this._valueSubject.next(value);\n }\n\n public async getValueAsync(): Promise<TItem | undefined> {\n await this.keepDataFreshAsync();\n\n return firstValueFrom(this._valueSubject);\n }\n\n public invalidate() {\n this._validUntil = null;\n }\n\n private _setIsLoading(isLoading: boolean) {\n this._isLoading = isLoading;\n this._isLoadingSubject.next(isLoading);\n }\n}\n","import { Signal } from '@angular/core';\nimport { rxResource } from '@angular/core/rxjs-interop';\nimport { ResourceCache } from './resource-cache';\nimport { ResourceRepositorySignal } from '../interface/resource-repository-signal.interface';\nimport { ResourceRepository, ResourceRepositoryOptions } from '../interface/resource-repository.interface';\n\nconst DEFAULT_MAX_AGE = 5 * 60 * 1000;\n\ntype CacheKey = string;\n\nexport class ResourceRepositoryImpl<TParams, TItem> implements ResourceRepository<TParams, TItem> {\n private _cacheStore = new Map<CacheKey, ResourceCache<TItem>>();\n\n constructor(private options: ResourceRepositoryOptions<TParams, TItem>) {\n }\n\n public get(key: Signal<TParams>): ResourceRepositorySignal<TItem | undefined> {\n const resource = rxResource<TItem | undefined, TParams>({\n params: () => key(),\n stream: ({ params }) => {\n const cacheKey = this._createCacheKey(params);\n\n let cache = this._cacheStore.get(cacheKey);\n\n if (!cache) {\n cache = new ResourceCache(\n ((p) => () => this.options.loader(p))(params),\n this.options.maxAge ?? DEFAULT_MAX_AGE,\n );\n\n this._cacheStore.set(cacheKey, cache);\n }\n\n // Do not wait for it to load\n cache.keepDataFreshAsync();\n\n return cache.getValueObservable();\n }\n });\n\n return Object.assign(resource.asReadonly().value as ResourceRepositorySignal<TItem | undefined>, {\n reload: async () => {\n await this.reload(key());\n },\n set: async (value: TItem | undefined) => {\n await this.setValue(key(), value as TItem);\n }\n });\n }\n\n public async reload(key: TParams): Promise<void> {\n const cacheKey = this._createCacheKey(key);\n\n const cache = this._cacheStore.get(cacheKey);\n\n if (!cache) {\n throw new Error(`No cache found for the given key: ${key}`);\n }\n\n cache.invalidate();\n\n await cache.keepDataFreshAsync();\n }\n\n public async getValue(key: TParams): Promise<TItem | undefined> {\n const cacheKey = this._createCacheKey(key);\n\n const cache = this._cacheStore.get(cacheKey);\n\n if (!cache) {\n throw new Error(`No cache found for the given key: ${key}`);\n }\n\n return await cache.getValueAsync();\n }\n\n public async setValue(key: TParams, value: TItem): Promise<void> {\n const cacheKey = this._createCacheKey(key);\n\n let cache = this._cacheStore.get(cacheKey);\n\n if (!cache) {\n throw new Error(`No cache found for the given key: ${key}`);\n }\n\n await cache.setValueAsync(value);\n }\n\n public async updateValue(key: TParams, updateFn: (value: TItem | undefined) => Promise<TItem>): Promise<void> {\n const value = await this.getValue(key);\n\n await this.setValue(key, await updateFn(value));\n }\n\n private _createCacheKey(params: TParams): CacheKey {\n return (this.options.cacheKeyGenerator ?? JSON.stringify)(params) as CacheKey;\n }\n}\n","import { ResourceRepository, ResourceRepositoryOptions } from './interface/resource-repository.interface';\nimport { ResourceRepositoryImpl } from './lib/resource-repository';\n\nexport * from './interface/resource-repository-signal.interface';\nexport * from './interface/resource-repository.interface';\n\n/**\n * Creates a new instance of a ResourceRepository with the specified options.\n *\n * @param {ResourceRepositoryOptions<TParams, TItem>} options - The configuration options for the resource repository.\n * @return {ResourceRepository<TParams, TItem>} A new instance of ResourceRepository configured with the given options.\n */\nexport function resourceRepository<TParams, TItem>(options: ResourceRepositoryOptions<TParams, TItem>): ResourceRepository<TParams, TItem> {\n return new ResourceRepositoryImpl<TParams, TItem>(options);\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@angular-cool/repository",
3
3
  "description": "Cool stateful signal repository for angular",
4
- "version": "21.0.5",
4
+ "version": "21.0.6",
5
5
  "peerDependencies": {
6
6
  "@angular/common": ">=21.0.0",
7
7
  "@angular/core": ">=21.0.0"
@@ -1,19 +1,24 @@
1
1
  import { Signal } from '@angular/core';
2
2
 
3
- type ReloadableSignal<T> = Signal<T> & {
4
- reload: () => Promise<void>;
5
- };
6
- declare class ResourceRepository<TParams, TItem> {
7
- private options;
8
- private _cacheStore;
9
- constructor(options: ResourceRepositoryOptions<TParams, TItem>);
3
+ interface ResourceRepositorySignal<T> extends Signal<T> {
4
+ /**
5
+ * Reloads or refreshes the current value.
6
+ */
7
+ readonly reload: () => Promise<void>;
8
+ /**
9
+ * Sets current value.
10
+ */
11
+ readonly set: (value: T) => Promise<void>;
12
+ }
13
+
14
+ interface ResourceRepository<TParams, TItem> {
10
15
  /**
11
16
  * Retrieves a resource cache
12
17
  *
13
18
  * @param {Signal<TParams>} key - A signal function used to resolve the parameters for retrieving or initializing the resource.
14
- * @return {ReloadableSignal<TItem | undefined>} A read-only signal containing the item associated with the provided key, already cached.
19
+ * @return {ResourceRepositorySignal<TItem | undefined>} A read-only signal containing the item associated with the provided key, already cached.
15
20
  */
16
- get(key: Signal<TParams>): ReloadableSignal<TItem | undefined>;
21
+ get(key: Signal<TParams>): ResourceRepositorySignal<TItem | undefined>;
17
22
  /**
18
23
  * Reloads the cache associated with the given key, ensuring it is updated with fresh data.
19
24
  *
@@ -47,7 +52,6 @@ declare class ResourceRepository<TParams, TItem> {
47
52
  * @return {Promise<void>} A promise that resolves when the update operation is complete.
48
53
  */
49
54
  updateValue(key: TParams, updateFn: (value: TItem | undefined) => Promise<TItem>): Promise<void>;
50
- private _createCacheKey;
51
55
  }
52
56
  /**
53
57
  * Configuration options for a ResourceRepository.
@@ -71,6 +75,7 @@ interface ResourceRepositoryOptions<TParams, TItem> {
71
75
  maxAge?: number;
72
76
  cacheKeyGenerator?: (params: TParams) => string;
73
77
  }
78
+
74
79
  /**
75
80
  * Creates a new instance of a ResourceRepository with the specified options.
76
81
  *
@@ -80,4 +85,4 @@ interface ResourceRepositoryOptions<TParams, TItem> {
80
85
  declare function resourceRepository<TParams, TItem>(options: ResourceRepositoryOptions<TParams, TItem>): ResourceRepository<TParams, TItem>;
81
86
 
82
87
  export { resourceRepository };
83
- export type { ReloadableSignal, ResourceRepositoryOptions };
88
+ export type { ResourceRepository, ResourceRepositoryOptions, ResourceRepositorySignal };