@jscutlery/rx-computed 0.2.2 → 0.2.5
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/.eslintrc.json +36 -0
- package/CHANGELOG.md +43 -0
- package/README.md +3 -4
- package/jest.config.ts +22 -0
- package/ng-package.json +7 -0
- package/package.json +3 -16
- package/project.json +50 -0
- package/src/index.ts +1 -0
- package/src/lib/rx-computed.spec.ts +153 -0
- package/src/lib/rx-computed.ts +47 -0
- package/src/test-setup.ts +8 -0
- package/tsconfig.json +29 -0
- package/tsconfig.lib.json +17 -0
- package/tsconfig.lib.prod.json +9 -0
- package/tsconfig.spec.json +16 -0
- package/esm2022/index.mjs +0 -2
- package/esm2022/jscutlery-rx-computed.mjs +0 -5
- package/esm2022/lib/rx-computed.mjs +0 -25
- package/fesm2022/jscutlery-rx-computed.mjs +0 -32
- package/fesm2022/jscutlery-rx-computed.mjs.map +0 -1
- package/index.d.ts +0 -1
- package/lib/rx-computed.d.ts +0 -6
package/.eslintrc.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": ["../../.eslintrc.json"],
|
|
3
|
+
"ignorePatterns": ["!**/*"],
|
|
4
|
+
"overrides": [
|
|
5
|
+
{
|
|
6
|
+
"files": ["*.ts"],
|
|
7
|
+
"rules": {
|
|
8
|
+
"@angular-eslint/directive-selector": [
|
|
9
|
+
"error",
|
|
10
|
+
{
|
|
11
|
+
"type": "attribute",
|
|
12
|
+
"prefix": "jscutlery",
|
|
13
|
+
"style": "camelCase"
|
|
14
|
+
}
|
|
15
|
+
],
|
|
16
|
+
"@angular-eslint/component-selector": [
|
|
17
|
+
"error",
|
|
18
|
+
{
|
|
19
|
+
"type": "element",
|
|
20
|
+
"prefix": "jscutlery",
|
|
21
|
+
"style": "kebab-case"
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
"extends": [
|
|
26
|
+
"plugin:@nx/angular",
|
|
27
|
+
"plugin:@angular-eslint/template/process-inline-templates"
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"files": ["*.html"],
|
|
32
|
+
"extends": ["plugin:@nx/angular-template"],
|
|
33
|
+
"rules": {}
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
}
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
## 0.2.5 (2024-03-24)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### 🩹 Fixes
|
|
5
|
+
|
|
6
|
+
- import of zone.js and zone.js/testing ([f14b091](https://github.com/jscutlery/devkit/commit/f14b091))
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### ❤️ Thank You
|
|
10
|
+
|
|
11
|
+
- Adrian Baran
|
|
12
|
+
- Younes Jaaidi
|
|
13
|
+
|
|
14
|
+
## [0.2.4](https://github.com/jscutlery/devkit/compare/rx-computed-0.2.3...rx-computed-0.2.4) (2023-10-25)
|
|
15
|
+
|
|
16
|
+
## [0.2.3](https://github.com/jscutlery/devkit/compare/rx-computed-0.2.2...rx-computed-0.2.3) (2023-07-17)
|
|
17
|
+
|
|
18
|
+
## [0.2.2](https://github.com/jscutlery/devkit/compare/rx-computed-0.2.1...rx-computed-0.2.2) (2023-06-02)
|
|
19
|
+
|
|
20
|
+
## [0.2.1](https://github.com/jscutlery/devkit/compare/rx-computed-0.2.0...rx-computed-0.2.1) (2023-05-25)
|
|
21
|
+
|
|
22
|
+
### Bug Fixes
|
|
23
|
+
|
|
24
|
+
- **rx-computed:** adjust peer dependencies ([30277ca](https://github.com/jscutlery/devkit/commit/30277ca6e14d68c19497fa280e95ed6a262d562b))
|
|
25
|
+
|
|
26
|
+
# [0.2.0](https://github.com/jscutlery/devkit/compare/rx-computed-0.1.1...rx-computed-0.2.0) (2023-05-25)
|
|
27
|
+
|
|
28
|
+
### Features
|
|
29
|
+
|
|
30
|
+
- **rx-computed:** allow rxComputed to accept a custom Injector ([6f3228a](https://github.com/jscutlery/devkit/commit/6f3228a0413070c3f4fd0178420b204a2e825f04))
|
|
31
|
+
|
|
32
|
+
## [0.1.1](https://github.com/jscutlery/devkit/compare/rx-computed-0.1.0...rx-computed-0.1.1) (2023-05-25)
|
|
33
|
+
|
|
34
|
+
# 0.1.0 (2023-05-25)
|
|
35
|
+
|
|
36
|
+
### Features
|
|
37
|
+
|
|
38
|
+
- **rx-computed:** ✨ add rxComputed ([994ed28](https://github.com/jscutlery/devkit/commit/994ed283c5ee4068eac424ceb5c1b4d3f09b891d))
|
|
39
|
+
- **rx-computed:** ✨ handle async source ([3870ebb](https://github.com/jscutlery/devkit/commit/3870ebbbabd6fba0f2491c9cbe45d1feba723b47))
|
|
40
|
+
- **rx-computed:** ✨ handle custom initial value ([87f8273](https://github.com/jscutlery/devkit/commit/87f827394461431ebb7e2766b7dae6a418c98a46))
|
|
41
|
+
- **rx-computed:** ✨ throw error if source emits error ([e75ee40](https://github.com/jscutlery/devkit/commit/e75ee408720a88e1be0582cd89eb782fbf92efe7))
|
|
42
|
+
- **rx-computed:** ✨ unsubscribe when depdency changes ([ddcb24a](https://github.com/jscutlery/devkit/commit/ddcb24ac4f8438008638661963c81e8b46ad8424))
|
|
43
|
+
- **rx-computed:** ✨return undefined as default initial value ([b9b525d](https://github.com/jscutlery/devkit/commit/b9b525d08b50eb0bca6f5d5386f4982eba152f06))
|
package/README.md
CHANGED
|
@@ -35,8 +35,7 @@ class MyCmp {
|
|
|
35
35
|
|
|
36
36
|
### Custom Injector
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
`rxComputed` utilizes `effect()` underneath so it is **required** to be invoked within an Injection Context. However, a custom `Injector`
|
|
38
|
+
`rxComputed` utilizes `effect()` underneath so it is **required** to be invoked within an [Injection Context](https://angular.io/guide/dependency-injection-context). However, a custom `Injector`
|
|
40
39
|
can be passed in and `rxComputed` will invoke within that Injector context.
|
|
41
40
|
|
|
42
41
|
```ts
|
|
@@ -51,7 +50,7 @@ class MyCmp {
|
|
|
51
50
|
|
|
52
51
|
## Motivation
|
|
53
52
|
|
|
54
|
-
Synchronously computed signals in Angular are relatively
|
|
53
|
+
Synchronously computed signals in Angular are relatively straightforward. However, when dealing with an asynchronous source of data like an `Observable`, there is no primitive to derive a signal from it.
|
|
55
54
|
|
|
56
55
|
There are two common ways of dealing with this:
|
|
57
56
|
|
|
@@ -74,7 +73,7 @@ const recipes$ = combineLatest({
|
|
|
74
73
|
const recipes = toSignal(recipes$, []);
|
|
75
74
|
```
|
|
76
75
|
|
|
77
|
-
2. Using `
|
|
76
|
+
2. Using `effect` explicitly _(which is not the recommended way of using it, cf. https://angular.io/guide/signals#when-not-to-use-effects)_:
|
|
78
77
|
|
|
79
78
|
```typescript
|
|
80
79
|
const keywords = signal(...);
|
package/jest.config.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
export default {
|
|
3
|
+
displayName: 'rx-computed',
|
|
4
|
+
preset: '../../jest.preset.js',
|
|
5
|
+
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
|
|
6
|
+
coverageDirectory: '../../coverage/packages/rx-computed',
|
|
7
|
+
transform: {
|
|
8
|
+
'^.+\\.(ts|mjs|js|html)$': [
|
|
9
|
+
'jest-preset-angular',
|
|
10
|
+
{
|
|
11
|
+
tsconfig: '<rootDir>/tsconfig.spec.json',
|
|
12
|
+
stringifyContentPathRegex: '\\.(html|svg)$',
|
|
13
|
+
},
|
|
14
|
+
],
|
|
15
|
+
},
|
|
16
|
+
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
|
|
17
|
+
snapshotSerializers: [
|
|
18
|
+
'jest-preset-angular/build/serializers/no-ng-attributes',
|
|
19
|
+
'jest-preset-angular/build/serializers/ng-snapshot',
|
|
20
|
+
'jest-preset-angular/build/serializers/html-comment',
|
|
21
|
+
],
|
|
22
|
+
};
|
package/ng-package.json
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jscutlery/rx-computed",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"peerDependencies": {
|
|
5
5
|
"@angular/core": "^16.0.0",
|
|
6
6
|
"@jscutlery/operators": "^2.0.0"
|
|
@@ -8,18 +8,5 @@
|
|
|
8
8
|
"dependencies": {
|
|
9
9
|
"tslib": "^2.4.0"
|
|
10
10
|
},
|
|
11
|
-
"sideEffects": false
|
|
12
|
-
|
|
13
|
-
"typings": "index.d.ts",
|
|
14
|
-
"exports": {
|
|
15
|
-
"./package.json": {
|
|
16
|
-
"default": "./package.json"
|
|
17
|
-
},
|
|
18
|
-
".": {
|
|
19
|
-
"types": "./index.d.ts",
|
|
20
|
-
"esm2022": "./esm2022/jscutlery-rx-computed.mjs",
|
|
21
|
-
"esm": "./esm2022/jscutlery-rx-computed.mjs",
|
|
22
|
-
"default": "./fesm2022/jscutlery-rx-computed.mjs"
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
11
|
+
"sideEffects": false
|
|
12
|
+
}
|
package/project.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "rx-computed",
|
|
3
|
+
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
|
4
|
+
"sourceRoot": "packages/rx-computed/src",
|
|
5
|
+
"prefix": "jscutlery",
|
|
6
|
+
"tags": [],
|
|
7
|
+
"projectType": "library",
|
|
8
|
+
"targets": {
|
|
9
|
+
"build": {
|
|
10
|
+
"executor": "@nx/angular:package",
|
|
11
|
+
"outputs": ["{workspaceRoot}/dist/{projectRoot}"],
|
|
12
|
+
"options": {
|
|
13
|
+
"project": "packages/rx-computed/ng-package.json"
|
|
14
|
+
},
|
|
15
|
+
"configurations": {
|
|
16
|
+
"production": {
|
|
17
|
+
"tsConfig": "packages/rx-computed/tsconfig.lib.prod.json"
|
|
18
|
+
},
|
|
19
|
+
"development": {
|
|
20
|
+
"tsConfig": "packages/rx-computed/tsconfig.lib.json"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"defaultConfiguration": "production"
|
|
24
|
+
},
|
|
25
|
+
"test": {
|
|
26
|
+
"executor": "@nx/jest:jest",
|
|
27
|
+
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
|
|
28
|
+
"options": {
|
|
29
|
+
"jestConfig": "packages/rx-computed/jest.config.ts"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"lint": {
|
|
33
|
+
"executor": "@nx/eslint:lint",
|
|
34
|
+
"outputs": ["{options.outputFile}"]
|
|
35
|
+
},
|
|
36
|
+
"github": {
|
|
37
|
+
"executor": "@jscutlery/semver:github",
|
|
38
|
+
"options": {
|
|
39
|
+
"notes": "{notes}",
|
|
40
|
+
"tag": "{tag}"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"npm": {
|
|
44
|
+
"executor": "ngx-deploy-npm:deploy",
|
|
45
|
+
"options": {
|
|
46
|
+
"access": "public"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { rxComputed } from './lib/rx-computed';
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Component,
|
|
3
|
+
EnvironmentInjector,
|
|
4
|
+
Injector,
|
|
5
|
+
runInInjectionContext,
|
|
6
|
+
signal
|
|
7
|
+
} from '@angular/core';
|
|
8
|
+
import { TestBed } from '@angular/core/testing';
|
|
9
|
+
import { NEVER, Observable, of, Subject, throwError } from 'rxjs';
|
|
10
|
+
import { rxComputed } from './rx-computed';
|
|
11
|
+
|
|
12
|
+
describe(rxComputed.name, () => {
|
|
13
|
+
it('should return undefined as default initial value', () => {
|
|
14
|
+
const { rxComputed } = setUp();
|
|
15
|
+
|
|
16
|
+
const signal = rxComputed(() => NEVER);
|
|
17
|
+
|
|
18
|
+
expect(signal()).toBeUndefined();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should return custom initial value', () => {
|
|
22
|
+
const { rxComputed } = setUp();
|
|
23
|
+
|
|
24
|
+
const signal = rxComputed(() => NEVER, { initialValue: null });
|
|
25
|
+
|
|
26
|
+
expect(signal()).toBeNull();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should return emitted sync value', () => {
|
|
30
|
+
const { rxComputed } = setUp();
|
|
31
|
+
|
|
32
|
+
const signal = rxComputed(() => of(42));
|
|
33
|
+
|
|
34
|
+
expect(signal()).toBe(42);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should return emitted async value', () => {
|
|
38
|
+
const { rxComputed } = setUp();
|
|
39
|
+
|
|
40
|
+
const subject = new Subject<number>();
|
|
41
|
+
|
|
42
|
+
const signal = rxComputed(() => subject);
|
|
43
|
+
|
|
44
|
+
subject.next(42);
|
|
45
|
+
|
|
46
|
+
expect(signal()).toBe(42);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should throw error when observable throws error', () => {
|
|
50
|
+
const { rxComputed } = setUp();
|
|
51
|
+
|
|
52
|
+
const signal = rxComputed(() => throwError(() => new Error('💥 Oups!')));
|
|
53
|
+
|
|
54
|
+
expect(() => signal()).toThrow('💥 Oups!');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should unsubscribe when dependency changes', () => {
|
|
58
|
+
const { rxComputed, flushEffects } = setUp();
|
|
59
|
+
const unsubscribe = jest.fn();
|
|
60
|
+
|
|
61
|
+
const value = signal(0);
|
|
62
|
+
|
|
63
|
+
rxComputed(
|
|
64
|
+
() =>
|
|
65
|
+
new Observable((observer) => {
|
|
66
|
+
observer.next(value() * 2);
|
|
67
|
+
return unsubscribe;
|
|
68
|
+
})
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
/* Update dependency and flush effects... */
|
|
72
|
+
value.set(1);
|
|
73
|
+
flushEffects();
|
|
74
|
+
|
|
75
|
+
/* ... and make sure that observable tear down is called. */
|
|
76
|
+
expect(unsubscribe).toBeCalled();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe('without injection context', () => {
|
|
80
|
+
it('should throw when invoked without injection context', () => {
|
|
81
|
+
const { rxComputed } = setUpWithoutInjectionContext();
|
|
82
|
+
expect(() => rxComputed(() => NEVER)).toThrow(
|
|
83
|
+
/rxComputed\(\) can only be used within an injection context/
|
|
84
|
+
);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('should be able to use a custom Injector', () => {
|
|
88
|
+
const { rxComputed } = setUpWithoutInjectionContext();
|
|
89
|
+
|
|
90
|
+
const envInjector = TestBed.inject(EnvironmentInjector);
|
|
91
|
+
|
|
92
|
+
const signal = rxComputed(() => of(42), {
|
|
93
|
+
injector: envInjector
|
|
94
|
+
});
|
|
95
|
+
expect(signal()).toEqual(42);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
function setUp() {
|
|
100
|
+
const { flushEffects, runInTestingInjectionContext } = setUpSignalTesting();
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
flushEffects,
|
|
104
|
+
rxComputed<T>(...args: Parameters<typeof rxComputed<T>>) {
|
|
105
|
+
const signal = runInTestingInjectionContext(() =>
|
|
106
|
+
rxComputed(...args)
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
flushEffects();
|
|
110
|
+
|
|
111
|
+
return signal;
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
function setUpWithoutInjectionContext() {
|
|
118
|
+
const { flushEffects } = setUpSignalTesting();
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
flushEffects,
|
|
122
|
+
rxComputed<T>(...args: Parameters<typeof rxComputed<T>>) {
|
|
123
|
+
const signal = rxComputed(...args);
|
|
124
|
+
|
|
125
|
+
flushEffects();
|
|
126
|
+
|
|
127
|
+
return signal;
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
function setUpSignalTesting() {
|
|
134
|
+
const injector = TestBed.inject(Injector);
|
|
135
|
+
const fixture = TestBed.createComponent(NoopComponent);
|
|
136
|
+
|
|
137
|
+
/* Inspiration: https://github.com/angular/angular/blob/06b498f67f2ad16bb465ef378bdb16da84e41a1c/packages/core/rxjs-interop/test/to_observable_spec.ts#LL30C25-L30C25 */
|
|
138
|
+
return {
|
|
139
|
+
flushEffects() {
|
|
140
|
+
fixture.detectChanges();
|
|
141
|
+
},
|
|
142
|
+
runInTestingInjectionContext<T>(fn: () => T): T {
|
|
143
|
+
return runInInjectionContext(injector, fn);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
@Component({
|
|
149
|
+
standalone: true,
|
|
150
|
+
template: ''
|
|
151
|
+
})
|
|
152
|
+
class NoopComponent {
|
|
153
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import {
|
|
2
|
+
assertInInjectionContext,
|
|
3
|
+
computed,
|
|
4
|
+
effect,
|
|
5
|
+
inject,
|
|
6
|
+
Injector,
|
|
7
|
+
signal,
|
|
8
|
+
Signal,
|
|
9
|
+
} from '@angular/core';
|
|
10
|
+
import { pending, Suspense, suspensify } from '@jscutlery/operators';
|
|
11
|
+
import { Observable } from 'rxjs';
|
|
12
|
+
|
|
13
|
+
export function rxComputed<T, INITIAL_VALUE = T | null | undefined>(
|
|
14
|
+
fn: () => Observable<T>,
|
|
15
|
+
{
|
|
16
|
+
initialValue,
|
|
17
|
+
injector,
|
|
18
|
+
}: { initialValue?: INITIAL_VALUE; injector?: Injector } = {}
|
|
19
|
+
): Signal<T | INITIAL_VALUE> {
|
|
20
|
+
if (!injector) assertInInjectionContext(rxComputed);
|
|
21
|
+
injector ??= inject(Injector);
|
|
22
|
+
|
|
23
|
+
const sig = signal<Suspense<T | INITIAL_VALUE>>(pending);
|
|
24
|
+
|
|
25
|
+
effect(
|
|
26
|
+
(onCleanup) => {
|
|
27
|
+
const sub = fn()
|
|
28
|
+
.pipe(suspensify())
|
|
29
|
+
.subscribe((value) => sig.set(value));
|
|
30
|
+
onCleanup(() => sub.unsubscribe());
|
|
31
|
+
},
|
|
32
|
+
{ allowSignalWrites: true, injector }
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
return computed(() => {
|
|
36
|
+
const suspense = sig();
|
|
37
|
+
if (suspense.hasError) {
|
|
38
|
+
throw suspense.error;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (suspense.hasValue) {
|
|
42
|
+
return suspense.value;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return initialValue as INITIAL_VALUE;
|
|
46
|
+
});
|
|
47
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// @ts-expect-error https://thymikee.github.io/jest-preset-angular/docs/getting-started/test-environment
|
|
2
|
+
globalThis.ngJest = {
|
|
3
|
+
testEnvironmentOptions: {
|
|
4
|
+
errorOnUnknownElements: true,
|
|
5
|
+
errorOnUnknownProperties: true,
|
|
6
|
+
},
|
|
7
|
+
};
|
|
8
|
+
import 'jest-preset-angular/setup-jest';
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "es2022",
|
|
4
|
+
"useDefineForClassFields": false,
|
|
5
|
+
"forceConsistentCasingInFileNames": true,
|
|
6
|
+
"strict": true,
|
|
7
|
+
"noImplicitOverride": true,
|
|
8
|
+
"noPropertyAccessFromIndexSignature": true,
|
|
9
|
+
"noImplicitReturns": true,
|
|
10
|
+
"noFallthroughCasesInSwitch": true
|
|
11
|
+
},
|
|
12
|
+
"files": [],
|
|
13
|
+
"include": [],
|
|
14
|
+
"references": [
|
|
15
|
+
{
|
|
16
|
+
"path": "./tsconfig.lib.json"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"path": "./tsconfig.spec.json"
|
|
20
|
+
}
|
|
21
|
+
],
|
|
22
|
+
"extends": "../../tsconfig.base.json",
|
|
23
|
+
"angularCompilerOptions": {
|
|
24
|
+
"enableI18nLegacyMessageIdFormat": false,
|
|
25
|
+
"strictInjectionParameters": true,
|
|
26
|
+
"strictInputAccessModifiers": true,
|
|
27
|
+
"strictTemplates": true
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "./tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "../../dist/out-tsc",
|
|
5
|
+
"declaration": true,
|
|
6
|
+
"declarationMap": true,
|
|
7
|
+
"inlineSources": true,
|
|
8
|
+
"types": []
|
|
9
|
+
},
|
|
10
|
+
"exclude": [
|
|
11
|
+
"src/**/*.spec.ts",
|
|
12
|
+
"src/test-setup.ts",
|
|
13
|
+
"jest.config.ts",
|
|
14
|
+
"src/**/*.test.ts"
|
|
15
|
+
],
|
|
16
|
+
"include": ["src/**/*.ts"]
|
|
17
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "./tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "../../dist/out-tsc",
|
|
5
|
+
"module": "commonjs",
|
|
6
|
+
"target": "es2016",
|
|
7
|
+
"types": ["jest", "node"]
|
|
8
|
+
},
|
|
9
|
+
"files": ["src/test-setup.ts"],
|
|
10
|
+
"include": [
|
|
11
|
+
"jest.config.ts",
|
|
12
|
+
"src/**/*.test.ts",
|
|
13
|
+
"src/**/*.spec.ts",
|
|
14
|
+
"src/**/*.d.ts"
|
|
15
|
+
]
|
|
16
|
+
}
|
package/esm2022/index.mjs
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
export { rxComputed } from './lib/rx-computed';
|
|
2
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9wYWNrYWdlcy9yeC1jb21wdXRlZC9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLG1CQUFtQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IHsgcnhDb21wdXRlZCB9IGZyb20gJy4vbGliL3J4LWNvbXB1dGVkJzsiXX0=
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Generated bundle index. Do not edit.
|
|
3
|
-
*/
|
|
4
|
-
export * from './index';
|
|
5
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoianNjdXRsZXJ5LXJ4LWNvbXB1dGVkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vcGFja2FnZXMvcngtY29tcHV0ZWQvc3JjL2pzY3V0bGVyeS1yeC1jb21wdXRlZC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7R0FFRztBQUVILGNBQWMsU0FBUyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBHZW5lcmF0ZWQgYnVuZGxlIGluZGV4LiBEbyBub3QgZWRpdC5cbiAqL1xuXG5leHBvcnQgKiBmcm9tICcuL2luZGV4JztcbiJdfQ==
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { assertInInjectionContext, computed, effect, inject, Injector, signal, } from '@angular/core';
|
|
2
|
-
import { pending, suspensify } from '@jscutlery/operators';
|
|
3
|
-
export function rxComputed(fn, { initialValue, injector, } = {}) {
|
|
4
|
-
if (!injector)
|
|
5
|
-
assertInInjectionContext(rxComputed);
|
|
6
|
-
injector ??= inject(Injector);
|
|
7
|
-
const sig = signal(pending);
|
|
8
|
-
effect((onCleanup) => {
|
|
9
|
-
const sub = fn()
|
|
10
|
-
.pipe(suspensify())
|
|
11
|
-
.subscribe((value) => sig.set(value));
|
|
12
|
-
onCleanup(() => sub.unsubscribe());
|
|
13
|
-
}, { allowSignalWrites: true, injector });
|
|
14
|
-
return computed(() => {
|
|
15
|
-
const suspense = sig();
|
|
16
|
-
if (suspense.hasError) {
|
|
17
|
-
throw suspense.error;
|
|
18
|
-
}
|
|
19
|
-
if (suspense.hasValue) {
|
|
20
|
-
return suspense.value;
|
|
21
|
-
}
|
|
22
|
-
return initialValue;
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicngtY29tcHV0ZWQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9wYWNrYWdlcy9yeC1jb21wdXRlZC9zcmMvbGliL3J4LWNvbXB1dGVkLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFDTCx3QkFBd0IsRUFDeEIsUUFBUSxFQUNSLE1BQU0sRUFDTixNQUFNLEVBQ04sUUFBUSxFQUNSLE1BQU0sR0FFUCxNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQUUsT0FBTyxFQUFZLFVBQVUsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBR3JFLE1BQU0sVUFBVSxVQUFVLENBQ3hCLEVBQXVCLEVBQ3ZCLEVBQ0UsWUFBWSxFQUNaLFFBQVEsTUFDaUQsRUFBRTtJQUU3RCxJQUFJLENBQUMsUUFBUTtRQUFFLHdCQUF3QixDQUFDLFVBQVUsQ0FBQyxDQUFDO0lBQ3BELFFBQVEsS0FBSyxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUM7SUFFOUIsTUFBTSxHQUFHLEdBQUcsTUFBTSxDQUE4QixPQUFPLENBQUMsQ0FBQztJQUV6RCxNQUFNLENBQ0osQ0FBQyxTQUFTLEVBQUUsRUFBRTtRQUNaLE1BQU0sR0FBRyxHQUFHLEVBQUUsRUFBRTthQUNiLElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQzthQUNsQixTQUFTLENBQUMsQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztRQUN4QyxTQUFTLENBQUMsR0FBRyxFQUFFLENBQUMsR0FBRyxDQUFDLFdBQVcsRUFBRSxDQUFDLENBQUM7SUFDckMsQ0FBQyxFQUNELEVBQUUsaUJBQWlCLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRSxDQUN0QyxDQUFDO0lBRUYsT0FBTyxRQUFRLENBQUMsR0FBRyxFQUFFO1FBQ25CLE1BQU0sUUFBUSxHQUFHLEdBQUcsRUFBRSxDQUFDO1FBQ3ZCLElBQUksUUFBUSxDQUFDLFFBQVEsRUFBRTtZQUNyQixNQUFNLFFBQVEsQ0FBQyxLQUFLLENBQUM7U0FDdEI7UUFFRCxJQUFJLFFBQVEsQ0FBQyxRQUFRLEVBQUU7WUFDckIsT0FBTyxRQUFRLENBQUMsS0FBSyxDQUFDO1NBQ3ZCO1FBRUQsT0FBTyxZQUE2QixDQUFDO0lBQ3ZDLENBQUMsQ0FBQyxDQUFDO0FBQ0wsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7XG4gIGFzc2VydEluSW5qZWN0aW9uQ29udGV4dCxcbiAgY29tcHV0ZWQsXG4gIGVmZmVjdCxcbiAgaW5qZWN0LFxuICBJbmplY3RvcixcbiAgc2lnbmFsLFxuICBTaWduYWwsXG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgcGVuZGluZywgU3VzcGVuc2UsIHN1c3BlbnNpZnkgfSBmcm9tICdAanNjdXRsZXJ5L29wZXJhdG9ycyc7XG5pbXBvcnQgeyBPYnNlcnZhYmxlIH0gZnJvbSAncnhqcyc7XG5cbmV4cG9ydCBmdW5jdGlvbiByeENvbXB1dGVkPFQsIElOSVRJQUxfVkFMVUUgPSBUIHwgbnVsbCB8IHVuZGVmaW5lZD4oXG4gIGZuOiAoKSA9PiBPYnNlcnZhYmxlPFQ+LFxuICB7XG4gICAgaW5pdGlhbFZhbHVlLFxuICAgIGluamVjdG9yLFxuICB9OiB7IGluaXRpYWxWYWx1ZT86IElOSVRJQUxfVkFMVUU7IGluamVjdG9yPzogSW5qZWN0b3IgfSA9IHt9XG4pOiBTaWduYWw8VCB8IElOSVRJQUxfVkFMVUU+IHtcbiAgaWYgKCFpbmplY3RvcikgYXNzZXJ0SW5JbmplY3Rpb25Db250ZXh0KHJ4Q29tcHV0ZWQpO1xuICBpbmplY3RvciA/Pz0gaW5qZWN0KEluamVjdG9yKTtcblxuICBjb25zdCBzaWcgPSBzaWduYWw8U3VzcGVuc2U8VCB8IElOSVRJQUxfVkFMVUU+PihwZW5kaW5nKTtcblxuICBlZmZlY3QoXG4gICAgKG9uQ2xlYW51cCkgPT4ge1xuICAgICAgY29uc3Qgc3ViID0gZm4oKVxuICAgICAgICAucGlwZShzdXNwZW5zaWZ5KCkpXG4gICAgICAgIC5zdWJzY3JpYmUoKHZhbHVlKSA9PiBzaWcuc2V0KHZhbHVlKSk7XG4gICAgICBvbkNsZWFudXAoKCkgPT4gc3ViLnVuc3Vic2NyaWJlKCkpO1xuICAgIH0sXG4gICAgeyBhbGxvd1NpZ25hbFdyaXRlczogdHJ1ZSwgaW5qZWN0b3IgfVxuICApO1xuXG4gIHJldHVybiBjb21wdXRlZCgoKSA9PiB7XG4gICAgY29uc3Qgc3VzcGVuc2UgPSBzaWcoKTtcbiAgICBpZiAoc3VzcGVuc2UuaGFzRXJyb3IpIHtcbiAgICAgIHRocm93IHN1c3BlbnNlLmVycm9yO1xuICAgIH1cblxuICAgIGlmIChzdXNwZW5zZS5oYXNWYWx1ZSkge1xuICAgICAgcmV0dXJuIHN1c3BlbnNlLnZhbHVlO1xuICAgIH1cblxuICAgIHJldHVybiBpbml0aWFsVmFsdWUgYXMgSU5JVElBTF9WQUxVRTtcbiAgfSk7XG59XG4iXX0=
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { assertInInjectionContext, inject, Injector, signal, effect, computed } from '@angular/core';
|
|
2
|
-
import { pending, suspensify } from '@jscutlery/operators';
|
|
3
|
-
|
|
4
|
-
function rxComputed(fn, { initialValue, injector, } = {}) {
|
|
5
|
-
if (!injector)
|
|
6
|
-
assertInInjectionContext(rxComputed);
|
|
7
|
-
injector ??= inject(Injector);
|
|
8
|
-
const sig = signal(pending);
|
|
9
|
-
effect((onCleanup) => {
|
|
10
|
-
const sub = fn()
|
|
11
|
-
.pipe(suspensify())
|
|
12
|
-
.subscribe((value) => sig.set(value));
|
|
13
|
-
onCleanup(() => sub.unsubscribe());
|
|
14
|
-
}, { allowSignalWrites: true, injector });
|
|
15
|
-
return computed(() => {
|
|
16
|
-
const suspense = sig();
|
|
17
|
-
if (suspense.hasError) {
|
|
18
|
-
throw suspense.error;
|
|
19
|
-
}
|
|
20
|
-
if (suspense.hasValue) {
|
|
21
|
-
return suspense.value;
|
|
22
|
-
}
|
|
23
|
-
return initialValue;
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Generated bundle index. Do not edit.
|
|
29
|
-
*/
|
|
30
|
-
|
|
31
|
-
export { rxComputed };
|
|
32
|
-
//# sourceMappingURL=jscutlery-rx-computed.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"jscutlery-rx-computed.mjs","sources":["../../../../packages/rx-computed/src/lib/rx-computed.ts","../../../../packages/rx-computed/src/jscutlery-rx-computed.ts"],"sourcesContent":["import {\n assertInInjectionContext,\n computed,\n effect,\n inject,\n Injector,\n signal,\n Signal,\n} from '@angular/core';\nimport { pending, Suspense, suspensify } from '@jscutlery/operators';\nimport { Observable } from 'rxjs';\n\nexport function rxComputed<T, INITIAL_VALUE = T | null | undefined>(\n fn: () => Observable<T>,\n {\n initialValue,\n injector,\n }: { initialValue?: INITIAL_VALUE; injector?: Injector } = {}\n): Signal<T | INITIAL_VALUE> {\n if (!injector) assertInInjectionContext(rxComputed);\n injector ??= inject(Injector);\n\n const sig = signal<Suspense<T | INITIAL_VALUE>>(pending);\n\n effect(\n (onCleanup) => {\n const sub = fn()\n .pipe(suspensify())\n .subscribe((value) => sig.set(value));\n onCleanup(() => sub.unsubscribe());\n },\n { allowSignalWrites: true, injector }\n );\n\n return computed(() => {\n const suspense = sig();\n if (suspense.hasError) {\n throw suspense.error;\n }\n\n if (suspense.hasValue) {\n return suspense.value;\n }\n\n return initialValue as INITIAL_VALUE;\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;AAYM,SAAU,UAAU,CACxB,EAAuB,EACvB,EACE,YAAY,EACZ,QAAQ,GAAA,GACiD,EAAE,EAAA;AAE7D,IAAA,IAAI,CAAC,QAAQ;QAAE,wBAAwB,CAAC,UAAU,CAAC,CAAC;AACpD,IAAA,QAAQ,KAAK,MAAM,CAAC,QAAQ,CAAC,CAAC;AAE9B,IAAA,MAAM,GAAG,GAAG,MAAM,CAA8B,OAAO,CAAC,CAAC;AAEzD,IAAA,MAAM,CACJ,CAAC,SAAS,KAAI;QACZ,MAAM,GAAG,GAAG,EAAE,EAAE;aACb,IAAI,CAAC,UAAU,EAAE,CAAC;AAClB,aAAA,SAAS,CAAC,CAAC,KAAK,KAAK,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;QACxC,SAAS,CAAC,MAAM,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;KACpC,EACD,EAAE,iBAAiB,EAAE,IAAI,EAAE,QAAQ,EAAE,CACtC,CAAC;IAEF,OAAO,QAAQ,CAAC,MAAK;AACnB,QAAA,MAAM,QAAQ,GAAG,GAAG,EAAE,CAAC;QACvB,IAAI,QAAQ,CAAC,QAAQ,EAAE;YACrB,MAAM,QAAQ,CAAC,KAAK,CAAC;AACtB,SAAA;QAED,IAAI,QAAQ,CAAC,QAAQ,EAAE;YACrB,OAAO,QAAQ,CAAC,KAAK,CAAC;AACvB,SAAA;AAED,QAAA,OAAO,YAA6B,CAAC;AACvC,KAAC,CAAC,CAAC;AACL;;AC9CA;;AAEG;;;;"}
|
package/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { rxComputed } from './lib/rx-computed';
|
package/lib/rx-computed.d.ts
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { Injector, Signal } from '@angular/core';
|
|
2
|
-
import { Observable } from 'rxjs';
|
|
3
|
-
export declare function rxComputed<T, INITIAL_VALUE = T | null | undefined>(fn: () => Observable<T>, { initialValue, injector, }?: {
|
|
4
|
-
initialValue?: INITIAL_VALUE;
|
|
5
|
-
injector?: Injector;
|
|
6
|
-
}): Signal<T | INITIAL_VALUE>;
|