@angular-cool/repository 21.0.0
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
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
[npm-url]: https://npmjs.org/package/@angular-cool/repository
|
|
2
|
+
[npm-image]: https://img.shields.io/npm/v/@angular-cool/repository.svg
|
|
3
|
+
[downloads-image]: https://img.shields.io/npm/dm/@angular-cool/repository.svg
|
|
4
|
+
[total-downloads-image]: https://img.shields.io/npm/dt/@angular-cool/repository.svg
|
|
5
|
+
|
|
6
|
+
# @angular-cool/repository [![NPM version][npm-image]][npm-url] [![Downloads][downloads-image]][npm-url] [![Total Downloads][total-downloads-image]][npm-url]
|
|
7
|
+
Cool stateful signal repository for angular
|
|
8
|
+
|
|
9
|
+
An easy-to-use signal repository that helps you manage your data loading and caching in your angular applications.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
> npm install --save @angular-cool/repository
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
### Create a Repository in your service layer
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
import { resourceRepository } from '@angular-cool/repository';
|
|
20
|
+
import { inject, Injectable } from '@angular/core';
|
|
21
|
+
import { ItemId } from './my-item.model';
|
|
22
|
+
|
|
23
|
+
@Injectable()
|
|
24
|
+
export class MyService {
|
|
25
|
+
private _http = inject(HttpClient);
|
|
26
|
+
|
|
27
|
+
private items = resourceRepository<ItemId>({
|
|
28
|
+
loader: ({ params }) => this._http.get(`https://myapi.com/items/${params}`).toPromise(),
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Query from repository
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import { signal } from '@angular/common';
|
|
37
|
+
import { MyService } from './my-service.service';
|
|
38
|
+
|
|
39
|
+
@Component(/*...*/)
|
|
40
|
+
export class MyComponent {
|
|
41
|
+
private _myService = inject(MyService);
|
|
42
|
+
|
|
43
|
+
private idParam = signal('1');
|
|
44
|
+
|
|
45
|
+
protected myItem = this._myService.items.get(this.idParam);
|
|
46
|
+
|
|
47
|
+
protected updateItem() {
|
|
48
|
+
// Update item on the server here
|
|
49
|
+
|
|
50
|
+
this._myService.items.reload(this.idParam());
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## License
|
|
56
|
+
> The MIT License (MIT)
|
|
57
|
+
|
|
58
|
+
> Copyright (c) 2025 Hacklone
|
|
59
|
+
> https://github.com/Hacklone
|
|
60
|
+
|
|
61
|
+
> Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
62
|
+
> of this software and associated documentation files (the "Software"), to deal
|
|
63
|
+
> in the Software without restriction, including without limitation the rights
|
|
64
|
+
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
65
|
+
> copies of the Software, and to permit persons to whom the Software is
|
|
66
|
+
> furnished to do so, subject to the following conditions:
|
|
67
|
+
|
|
68
|
+
> The above copyright notice and this permission notice shall be included in all
|
|
69
|
+
> copies or substantial portions of the Software.
|
|
70
|
+
|
|
71
|
+
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
72
|
+
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
73
|
+
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
74
|
+
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
75
|
+
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
76
|
+
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
77
|
+
> SOFTWARE.
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { rxResource } from '@angular/core/rxjs-interop';
|
|
2
|
+
import { ReplaySubject } from 'rxjs';
|
|
3
|
+
|
|
4
|
+
class ResourceCache {
|
|
5
|
+
get isInvalid() {
|
|
6
|
+
return this._validUntil === null || Date.now() > this._validUntil;
|
|
7
|
+
}
|
|
8
|
+
constructor(loader, maxAge) {
|
|
9
|
+
this.loader = loader;
|
|
10
|
+
this.maxAge = maxAge;
|
|
11
|
+
this._subject = new ReplaySubject();
|
|
12
|
+
this._validUntil = null;
|
|
13
|
+
}
|
|
14
|
+
getObservable() {
|
|
15
|
+
return this._subject.asObservable();
|
|
16
|
+
}
|
|
17
|
+
async keepDataFreshAsync() {
|
|
18
|
+
if (!this.isInvalid) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const data = await this.loader();
|
|
22
|
+
this._validUntil = Date.now() + this.maxAge;
|
|
23
|
+
this._subject.next(data);
|
|
24
|
+
}
|
|
25
|
+
invalidate() {
|
|
26
|
+
this._validUntil = null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const DEFAULT_MAX_AGE = 5 * 60 * 1000;
|
|
31
|
+
class ResourceRepository {
|
|
32
|
+
constructor(options) {
|
|
33
|
+
this.options = options;
|
|
34
|
+
this._cacheStore = new Map();
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Retrieves a resource cache
|
|
38
|
+
*
|
|
39
|
+
* @param {Signal<TParams>} key - A signal function used to resolve the parameters for retrieving or initializing the resource.
|
|
40
|
+
* @return {Resource<TItem | undefined>} A read-only resource containing the item associated with the provided key, already cached.
|
|
41
|
+
*/
|
|
42
|
+
get(key) {
|
|
43
|
+
return rxResource({
|
|
44
|
+
params: () => key(),
|
|
45
|
+
stream: ({ params }) => {
|
|
46
|
+
let cache = this._cacheStore.get(params);
|
|
47
|
+
if (!cache) {
|
|
48
|
+
cache = new ResourceCache(((p) => () => this.options.loader(p))(params), this.options.maxAge ?? DEFAULT_MAX_AGE);
|
|
49
|
+
this._cacheStore.set(params, cache);
|
|
50
|
+
}
|
|
51
|
+
// Do not wait for it to load
|
|
52
|
+
cache.keepDataFreshAsync();
|
|
53
|
+
return cache.getObservable();
|
|
54
|
+
}
|
|
55
|
+
}).asReadonly();
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Reloads the cache associated with the given key, ensuring it is updated with fresh data.
|
|
59
|
+
*
|
|
60
|
+
* @param {TParams} key - The key used to identify the specific cache entry to reload.
|
|
61
|
+
* @return {Promise<void>} A promise that resolves when the cache has been refreshed.
|
|
62
|
+
* @throws {Error} If no cache is found for the provided key or if data loading fails.
|
|
63
|
+
*/
|
|
64
|
+
async reload(key) {
|
|
65
|
+
const cache = this._cacheStore.get(key);
|
|
66
|
+
if (!cache) {
|
|
67
|
+
throw new Error(`No cache found for the given key: ${key}`);
|
|
68
|
+
}
|
|
69
|
+
cache.invalidate();
|
|
70
|
+
await cache.keepDataFreshAsync();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Creates a new instance of a ResourceRepository with the specified options.
|
|
75
|
+
*
|
|
76
|
+
* @param {ResourceRepositoryOptions<TParams, TItem>} options - The configuration options for the resource repository.
|
|
77
|
+
* @return {ResourceRepository<TParams, TItem>} A new instance of ResourceRepository configured with the given options.
|
|
78
|
+
*/
|
|
79
|
+
function resourceRepository(options) {
|
|
80
|
+
return new ResourceRepository(options);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Generated bundle index. Do not edit.
|
|
85
|
+
*/
|
|
86
|
+
|
|
87
|
+
export { resourceRepository };
|
|
88
|
+
//# sourceMappingURL=angular-cool-repository.mjs.map
|
|
@@ -0,0 +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 { 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 this._validUntil = Date.now() + this.maxAge;\n this._subject.next(data);\n }\n\n public invalidate() {\n this._validUntil = null;\n }\n}\n","import { Resource, 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\nclass ResourceRepository<TParams, TItem> {\n private _cacheStore = new Map<TParams, 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 {Resource<TItem | undefined>} A read-only resource containing the item associated with the provided key, already cached.\n */\n public get(key: Signal<TParams>): Resource<TItem | undefined> {\n return rxResource<TItem | undefined, TParams>({\n params: () => key(),\n stream: ({ params }) => {\n let cache = this._cacheStore.get(params);\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(params, cache);\n }\n\n // Do not wait for it to load\n cache.keepDataFreshAsync();\n\n return cache.getObservable();\n }\n }).asReadonly();\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 cache = this._cacheStore.get(key);\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/**\n * Options to configure the behavior of the ResourceRepository.\n *\n * @template TParams The type of the parameters used by the loader function.\n * @template TItem The type of the items returned by the loader function.\n *\n * @property {function(TParams): Promise<TItem>} loader\n * A function that loads a resource based on provided parameters and returns a promise that resolves to the resource.\n *\n * @property {number} [maxAge]\n * The optional maximum age (in milliseconds) for caching the loaded resource. Default: 5 minutes\n */\nexport interface ResourceRepositoryOptions<TParams, TItem> {\n loader: (params: TParams) => Promise<TItem>;\n\n maxAge?: number;\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":";;;MAEa,aAAa,CAAA;AAIxB,IAAA,IAAW,SAAS,GAAA;AAClB,QAAA,OAAO,IAAI,CAAC,WAAW,KAAK,IAAI,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,WAAW;IACnE;IAEA,WAAA,CACU,MAAwC,EACxC,MAAc,EAAA;QADd,IAAA,CAAA,MAAM,GAAN,MAAM;QACN,IAAA,CAAA,MAAM,GAAN,MAAM;AATR,QAAA,IAAA,CAAA,QAAQ,GAAG,IAAI,aAAa,EAAqB;QACjD,IAAA,CAAA,WAAW,GAAkB,IAAI;IAUzC;IAEO,aAAa,GAAA;AAClB,QAAA,OAAO,IAAI,CAAC,QAAQ,CAAC,YAAY,EAAE;IACrC;AAEO,IAAA,MAAM,kBAAkB,GAAA;AAC7B,QAAA,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;YACnB;QACF;AAEA,QAAA,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE;QAEhC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,MAAM;AAC3C,QAAA,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;IAC1B;IAEO,UAAU,GAAA;AACf,QAAA,IAAI,CAAC,WAAW,GAAG,IAAI;IACzB;AACD;;AC9BD,MAAM,eAAe,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI;AAErC,MAAM,kBAAkB,CAAA;AAGtB,IAAA,WAAA,CAAoB,OAAkD,EAAA;QAAlD,IAAA,CAAA,OAAO,GAAP,OAAO;AAFnB,QAAA,IAAA,CAAA,WAAW,GAAG,IAAI,GAAG,EAAiC;IAG9D;AAEA;;;;;AAKG;AACI,IAAA,GAAG,CAAC,GAAoB,EAAA;AAC7B,QAAA,OAAO,UAAU,CAA6B;AAC5C,YAAA,MAAM,EAAE,MAAM,GAAG,EAAE;AACnB,YAAA,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,KAAI;gBACrB,IAAI,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC;gBAExC,IAAI,CAAC,KAAK,EAAE;AACV,oBAAA,KAAK,GAAG,IAAI,aAAa,CACvB,CAAC,CAAC,CAAC,KAAK,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,EAC7C,IAAI,CAAC,OAAO,CAAC,MAAM,IAAI,eAAe,CACvC;oBAED,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC;gBACrC;;gBAGA,KAAK,CAAC,kBAAkB,EAAE;AAE1B,gBAAA,OAAO,KAAK,CAAC,aAAa,EAAE;YAC9B;SACD,CAAC,CAAC,UAAU,EAAE;IACjB;AAEA;;;;;;AAMG;IACI,MAAM,MAAM,CAAC,GAAY,EAAA;QAC9B,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC;QAEvC,IAAI,CAAC,KAAK,EAAE;AACV,YAAA,MAAM,IAAI,KAAK,CAAC,qCAAqC,GAAG,CAAA,CAAE,CAAC;QAC7D;QAEA,KAAK,CAAC,UAAU,EAAE;AAElB,QAAA,MAAM,KAAK,CAAC,kBAAkB,EAAE;IAClC;AACD;AAoBD;;;;;AAKG;AACG,SAAU,kBAAkB,CAAiB,OAAkD,EAAA;AACnG,IAAA,OAAO,IAAI,kBAAkB,CAAiB,OAAO,CAAC;AACxD;;ACvFA;;AAEG;;;;"}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@angular-cool/repository",
|
|
3
|
+
"description": "Cool stateful signal repository for angular",
|
|
4
|
+
"version": "21.0.0",
|
|
5
|
+
"peerDependencies": {
|
|
6
|
+
"@angular/common": ">=21.0.0",
|
|
7
|
+
"@angular/core": ">=21.0.0"
|
|
8
|
+
},
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"tslib": "^2.3.0"
|
|
11
|
+
},
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/Hacklone/angular-cool.git"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"angular",
|
|
18
|
+
"repository",
|
|
19
|
+
"cache",
|
|
20
|
+
"pattern",
|
|
21
|
+
"cool"
|
|
22
|
+
],
|
|
23
|
+
"author": "Hacklone <toakak@gmail.com>",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"licenses": [
|
|
26
|
+
{
|
|
27
|
+
"type": "MIT",
|
|
28
|
+
"url": "https://github.com/Hacklone/angular-cool/raw/master/LICENSE"
|
|
29
|
+
}
|
|
30
|
+
],
|
|
31
|
+
"bugs": {
|
|
32
|
+
"url": "https://github.com/Hacklone/angular-cool/issues"
|
|
33
|
+
},
|
|
34
|
+
"homepage": "https://github.com/Hacklone/angular-cool/blob/master/projects/repository/README.md",
|
|
35
|
+
"sideEffects": false,
|
|
36
|
+
"module": "fesm2022/angular-cool-repository.mjs",
|
|
37
|
+
"typings": "types/angular-cool-repository.d.ts",
|
|
38
|
+
"exports": {
|
|
39
|
+
"./package.json": {
|
|
40
|
+
"default": "./package.json"
|
|
41
|
+
},
|
|
42
|
+
".": {
|
|
43
|
+
"types": "./types/angular-cool-repository.d.ts",
|
|
44
|
+
"default": "./fesm2022/angular-cool-repository.mjs"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Signal, Resource } from '@angular/core';
|
|
2
|
+
|
|
3
|
+
declare class ResourceRepository<TParams, TItem> {
|
|
4
|
+
private options;
|
|
5
|
+
private _cacheStore;
|
|
6
|
+
constructor(options: ResourceRepositoryOptions<TParams, TItem>);
|
|
7
|
+
/**
|
|
8
|
+
* Retrieves a resource cache
|
|
9
|
+
*
|
|
10
|
+
* @param {Signal<TParams>} key - A signal function used to resolve the parameters for retrieving or initializing the resource.
|
|
11
|
+
* @return {Resource<TItem | undefined>} A read-only resource containing the item associated with the provided key, already cached.
|
|
12
|
+
*/
|
|
13
|
+
get(key: Signal<TParams>): Resource<TItem | undefined>;
|
|
14
|
+
/**
|
|
15
|
+
* Reloads the cache associated with the given key, ensuring it is updated with fresh data.
|
|
16
|
+
*
|
|
17
|
+
* @param {TParams} key - The key used to identify the specific cache entry to reload.
|
|
18
|
+
* @return {Promise<void>} A promise that resolves when the cache has been refreshed.
|
|
19
|
+
* @throws {Error} If no cache is found for the provided key or if data loading fails.
|
|
20
|
+
*/
|
|
21
|
+
reload(key: TParams): Promise<void>;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Options to configure the behavior of the ResourceRepository.
|
|
25
|
+
*
|
|
26
|
+
* @template TParams The type of the parameters used by the loader function.
|
|
27
|
+
* @template TItem The type of the items returned by the loader function.
|
|
28
|
+
*
|
|
29
|
+
* @property {function(TParams): Promise<TItem>} loader
|
|
30
|
+
* A function that loads a resource based on provided parameters and returns a promise that resolves to the resource.
|
|
31
|
+
*
|
|
32
|
+
* @property {number} [maxAge]
|
|
33
|
+
* The optional maximum age (in milliseconds) for caching the loaded resource. Default: 5 minutes
|
|
34
|
+
*/
|
|
35
|
+
interface ResourceRepositoryOptions<TParams, TItem> {
|
|
36
|
+
loader: (params: TParams) => Promise<TItem>;
|
|
37
|
+
maxAge?: number;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Creates a new instance of a ResourceRepository with the specified options.
|
|
41
|
+
*
|
|
42
|
+
* @param {ResourceRepositoryOptions<TParams, TItem>} options - The configuration options for the resource repository.
|
|
43
|
+
* @return {ResourceRepository<TParams, TItem>} A new instance of ResourceRepository configured with the given options.
|
|
44
|
+
*/
|
|
45
|
+
declare function resourceRepository<TParams, TItem>(options: ResourceRepositoryOptions<TParams, TItem>): ResourceRepository<TParams, TItem>;
|
|
46
|
+
|
|
47
|
+
export { resourceRepository };
|
|
48
|
+
export type { ResourceRepositoryOptions };
|