@angular-architects/ngrx-toolkit 20.1.0 → 20.4.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.
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Injectable, InjectionToken, signal, effect, inject, PLATFORM_ID, computed, isSignal, untracked, isDevMode as isDevMode$1 } from '@angular/core';
|
|
2
|
+
import { Injectable, InjectionToken, signal, effect, inject, PLATFORM_ID, computed, isSignal, untracked, isDevMode as isDevMode$1, DestroyRef } from '@angular/core';
|
|
3
3
|
import { watchState, getState, signalStoreFeature, withMethods, withHooks, patchState as patchState$1, withState, withComputed, withProps, withLinkedState } from '@ngrx/signals';
|
|
4
4
|
import { isPlatformBrowser, isPlatformServer } from '@angular/common';
|
|
5
|
-
import { Subject } from 'rxjs';
|
|
5
|
+
import { Subject, switchMap, mergeMap, concatMap, exhaustMap, defer, tap, catchError, EMPTY, finalize, filter, map } from 'rxjs';
|
|
6
6
|
import { removeEntity, setAllEntities, updateEntity, addEntity } from '@ngrx/signals/entities';
|
|
7
|
+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
8
|
+
import { HttpClient, HttpEventType, HttpResponse } from '@angular/common/http';
|
|
7
9
|
|
|
8
10
|
const DEVTOOLS_FEATURE = Symbol('DEVTOOLS_FEATURE');
|
|
9
11
|
function createDevtoolsFeature(options) {
|
|
@@ -1781,6 +1783,214 @@ function withFeatureFactory(factoryFn) {
|
|
|
1781
1783
|
};
|
|
1782
1784
|
}
|
|
1783
1785
|
|
|
1786
|
+
const switchOp = {
|
|
1787
|
+
rxJsOperator: switchMap,
|
|
1788
|
+
exhaustSemantics: false,
|
|
1789
|
+
};
|
|
1790
|
+
const mergeOp = {
|
|
1791
|
+
rxJsOperator: mergeMap,
|
|
1792
|
+
exhaustSemantics: false,
|
|
1793
|
+
};
|
|
1794
|
+
const concatOp = {
|
|
1795
|
+
rxJsOperator: concatMap,
|
|
1796
|
+
exhaustSemantics: false,
|
|
1797
|
+
};
|
|
1798
|
+
const exhaustOp = {
|
|
1799
|
+
rxJsOperator: exhaustMap,
|
|
1800
|
+
exhaustSemantics: true,
|
|
1801
|
+
};
|
|
1802
|
+
|
|
1803
|
+
/**
|
|
1804
|
+
* Creates a mutation that leverages RxJS.
|
|
1805
|
+
*
|
|
1806
|
+
* For each mutation the following options can be defined:
|
|
1807
|
+
* - `operation`: A function that defines the mutation logic. It returns an Observable.
|
|
1808
|
+
* - `onSuccess`: A callback that is called when the mutation is successful.
|
|
1809
|
+
* - `onError`: A callback that is called when the mutation fails.
|
|
1810
|
+
* - `operator`: An optional wrapper of an RxJS flattening operator. By default `concat` sematics are used.
|
|
1811
|
+
* - `injector`: An optional Angular injector to use for dependency injection.
|
|
1812
|
+
*
|
|
1813
|
+
* The `operation` is the only mandatory option.
|
|
1814
|
+
*
|
|
1815
|
+
* The returned mutation can be called as an async function and returns a Promise.
|
|
1816
|
+
* This promise informs about whether the mutation was successful, failed, or aborted
|
|
1817
|
+
* (due to switchMap or exhaustMap semantics).
|
|
1818
|
+
*
|
|
1819
|
+
* The mutation also provides several Signals such as error, status or isPending (see below).
|
|
1820
|
+
*
|
|
1821
|
+
* Example usage without Store:
|
|
1822
|
+
*
|
|
1823
|
+
* ```typescript
|
|
1824
|
+
* const counterSignal = signal(0);
|
|
1825
|
+
*
|
|
1826
|
+
* const increment = rxMutation({
|
|
1827
|
+
* operation: (param: Param) => {
|
|
1828
|
+
* return calcSum(this.counterSignal(), param.value);
|
|
1829
|
+
* },
|
|
1830
|
+
* operator: concatOp,
|
|
1831
|
+
* onSuccess: (result) => {
|
|
1832
|
+
* this.counterSignal.set(result);
|
|
1833
|
+
* },
|
|
1834
|
+
* onError: (error) => {
|
|
1835
|
+
* console.error('Error occurred:', error);
|
|
1836
|
+
* },
|
|
1837
|
+
* });
|
|
1838
|
+
*
|
|
1839
|
+
* const error = increment.error;
|
|
1840
|
+
* const isPending = increment.isPending;
|
|
1841
|
+
* const status = increment.status;
|
|
1842
|
+
* const value = increment.value;
|
|
1843
|
+
* const hasValue = increment.hasValue;
|
|
1844
|
+
*
|
|
1845
|
+
* async function incrementCounter() {
|
|
1846
|
+
* const result = await increment({ value: 1 });
|
|
1847
|
+
* if (result.status === 'success') {
|
|
1848
|
+
* console.log('Success:', result.value);
|
|
1849
|
+
* }
|
|
1850
|
+
* if (result.status === 'error') {
|
|
1851
|
+
* console.log('Error:', result.error);
|
|
1852
|
+
* }
|
|
1853
|
+
* if (result.status === 'aborted') {
|
|
1854
|
+
* console.log('Operation aborted');
|
|
1855
|
+
* }
|
|
1856
|
+
* }
|
|
1857
|
+
*
|
|
1858
|
+
* function calcSum(a: number, b: number): Observable<number> {
|
|
1859
|
+
* return of(result).pipe(delay(500));
|
|
1860
|
+
* }
|
|
1861
|
+
* ```
|
|
1862
|
+
*
|
|
1863
|
+
* @param options
|
|
1864
|
+
* @returns the actual mutation function along tracking data as properties/methods
|
|
1865
|
+
*/
|
|
1866
|
+
function rxMutation(optionsOrOperation) {
|
|
1867
|
+
const inputSubject = new Subject();
|
|
1868
|
+
const options = typeof optionsOrOperation === 'function'
|
|
1869
|
+
? { operation: optionsOrOperation }
|
|
1870
|
+
: optionsOrOperation;
|
|
1871
|
+
const flatteningOp = options.operator ?? concatOp;
|
|
1872
|
+
const destroyRef = options.injector?.get(DestroyRef) ?? inject(DestroyRef);
|
|
1873
|
+
const callCount = signal(0, ...(ngDevMode ? [{ debugName: "callCount" }] : []));
|
|
1874
|
+
const errorSignal = signal(undefined, ...(ngDevMode ? [{ debugName: "errorSignal" }] : []));
|
|
1875
|
+
const idle = signal(true, ...(ngDevMode ? [{ debugName: "idle" }] : []));
|
|
1876
|
+
const isPending = computed(() => callCount() > 0, ...(ngDevMode ? [{ debugName: "isPending" }] : []));
|
|
1877
|
+
const value = signal(undefined, ...(ngDevMode ? [{ debugName: "value" }] : []));
|
|
1878
|
+
const isSuccess = computed(() => !idle() && !isPending() && !errorSignal(), ...(ngDevMode ? [{ debugName: "isSuccess" }] : []));
|
|
1879
|
+
const hasValue = function () {
|
|
1880
|
+
return typeof value() !== 'undefined';
|
|
1881
|
+
};
|
|
1882
|
+
const status = computed(() => {
|
|
1883
|
+
if (idle()) {
|
|
1884
|
+
return 'idle';
|
|
1885
|
+
}
|
|
1886
|
+
if (callCount() > 0) {
|
|
1887
|
+
return 'pending';
|
|
1888
|
+
}
|
|
1889
|
+
if (errorSignal()) {
|
|
1890
|
+
return 'error';
|
|
1891
|
+
}
|
|
1892
|
+
return 'success';
|
|
1893
|
+
}, ...(ngDevMode ? [{ debugName: "status" }] : []));
|
|
1894
|
+
const initialInnerStatus = 'idle';
|
|
1895
|
+
let innerStatus = initialInnerStatus;
|
|
1896
|
+
inputSubject
|
|
1897
|
+
.pipe(flatteningOp.rxJsOperator((input) => defer(() => {
|
|
1898
|
+
callCount.update((c) => c + 1);
|
|
1899
|
+
idle.set(false);
|
|
1900
|
+
return options.operation(input.param).pipe(tap((result) => {
|
|
1901
|
+
options.onSuccess?.(result, input.param);
|
|
1902
|
+
innerStatus = 'success';
|
|
1903
|
+
errorSignal.set(undefined);
|
|
1904
|
+
value.set(result);
|
|
1905
|
+
}), catchError((error) => {
|
|
1906
|
+
options.onError?.(error, input.param);
|
|
1907
|
+
errorSignal.set(error);
|
|
1908
|
+
value.set(undefined);
|
|
1909
|
+
innerStatus = 'error';
|
|
1910
|
+
return EMPTY;
|
|
1911
|
+
}), finalize(() => {
|
|
1912
|
+
callCount.update((c) => c - 1);
|
|
1913
|
+
if (innerStatus === 'success') {
|
|
1914
|
+
input.resolve({
|
|
1915
|
+
status: 'success',
|
|
1916
|
+
value: value(),
|
|
1917
|
+
});
|
|
1918
|
+
}
|
|
1919
|
+
else if (innerStatus === 'error') {
|
|
1920
|
+
input.resolve({
|
|
1921
|
+
status: 'error',
|
|
1922
|
+
error: errorSignal(),
|
|
1923
|
+
});
|
|
1924
|
+
}
|
|
1925
|
+
else {
|
|
1926
|
+
input.resolve({
|
|
1927
|
+
status: 'aborted',
|
|
1928
|
+
});
|
|
1929
|
+
}
|
|
1930
|
+
innerStatus = initialInnerStatus;
|
|
1931
|
+
}));
|
|
1932
|
+
})), takeUntilDestroyed(destroyRef))
|
|
1933
|
+
.subscribe();
|
|
1934
|
+
const mutationFn = (param) => {
|
|
1935
|
+
return new Promise((resolve) => {
|
|
1936
|
+
if (callCount() > 0 && flatteningOp.exhaustSemantics) {
|
|
1937
|
+
resolve({
|
|
1938
|
+
status: 'aborted',
|
|
1939
|
+
});
|
|
1940
|
+
}
|
|
1941
|
+
else {
|
|
1942
|
+
inputSubject.next({
|
|
1943
|
+
param,
|
|
1944
|
+
resolve,
|
|
1945
|
+
});
|
|
1946
|
+
}
|
|
1947
|
+
});
|
|
1948
|
+
};
|
|
1949
|
+
const mutation = mutationFn;
|
|
1950
|
+
mutation.status = status;
|
|
1951
|
+
mutation.isPending = isPending;
|
|
1952
|
+
mutation.error = errorSignal;
|
|
1953
|
+
mutation.value = value;
|
|
1954
|
+
mutation.hasValue = hasValue;
|
|
1955
|
+
mutation.isSuccess = isSuccess;
|
|
1956
|
+
return mutation;
|
|
1957
|
+
}
|
|
1958
|
+
|
|
1959
|
+
function withMutations(mutationsFactory) {
|
|
1960
|
+
return (store) => {
|
|
1961
|
+
// TODO: Is this the correct usage?
|
|
1962
|
+
const source = store;
|
|
1963
|
+
const mutations = mutationsFactory({
|
|
1964
|
+
...source,
|
|
1965
|
+
...store.props,
|
|
1966
|
+
...store.methods,
|
|
1967
|
+
...store.stateSignals,
|
|
1968
|
+
});
|
|
1969
|
+
const feature = createMutationsFeature(mutations);
|
|
1970
|
+
return feature(store);
|
|
1971
|
+
};
|
|
1972
|
+
}
|
|
1973
|
+
function createMutationsFeature(mutations) {
|
|
1974
|
+
const keys = Object.keys(mutations);
|
|
1975
|
+
const feature = signalStoreFeature(withMethods(() => keys.reduce((acc, key) => ({
|
|
1976
|
+
...acc,
|
|
1977
|
+
[key]: async (params) => {
|
|
1978
|
+
const mutation = mutations[key];
|
|
1979
|
+
if (!mutation) {
|
|
1980
|
+
throw new Error(`Mutation ${key} not found`);
|
|
1981
|
+
}
|
|
1982
|
+
const result = await mutation(params);
|
|
1983
|
+
return result;
|
|
1984
|
+
},
|
|
1985
|
+
}), {})), withComputed(() => keys.reduce((acc, key) => ({
|
|
1986
|
+
...acc,
|
|
1987
|
+
[`${key}IsPending`]: mutations[key].isPending,
|
|
1988
|
+
[`${key}Status`]: mutations[key].status,
|
|
1989
|
+
[`${key}Error`]: mutations[key].error,
|
|
1990
|
+
}), {})));
|
|
1991
|
+
return feature;
|
|
1992
|
+
}
|
|
1993
|
+
|
|
1784
1994
|
//** Types for `withResource` */
|
|
1785
1995
|
function withResource(resourceFactory) {
|
|
1786
1996
|
return (store) => {
|
|
@@ -1880,9 +2090,107 @@ function mapToResource(store, name) {
|
|
|
1880
2090
|
};
|
|
1881
2091
|
}
|
|
1882
2092
|
|
|
2093
|
+
/**
|
|
2094
|
+
* Creates an HTTP mutation.
|
|
2095
|
+
*
|
|
2096
|
+
* ```typescript
|
|
2097
|
+
* export type Params = {
|
|
2098
|
+
* value: number;
|
|
2099
|
+
* };
|
|
2100
|
+
*
|
|
2101
|
+
* export type CounterResponse = {
|
|
2102
|
+
* // httpbin.org echos the request using the
|
|
2103
|
+
* // json property
|
|
2104
|
+
* json: { counter: number };
|
|
2105
|
+
* };
|
|
2106
|
+
*
|
|
2107
|
+
* const simpleSaveUser = httpMutation({
|
|
2108
|
+
* request: (userData: AddUserEntry) => ({
|
|
2109
|
+
* url: 'api/users',
|
|
2110
|
+
* body: userData,
|
|
2111
|
+
* }),
|
|
2112
|
+
* parse: Boolean,
|
|
2113
|
+
* })
|
|
2114
|
+
*
|
|
2115
|
+
* const saveUser = httpMutation({
|
|
2116
|
+
* request: (p: Params) => ({
|
|
2117
|
+
* url: `https://httpbin.org/post`,
|
|
2118
|
+
* method: 'POST',
|
|
2119
|
+
* body: { counter: p.value },
|
|
2120
|
+
* headers: { 'Content-Type': 'application/json' },
|
|
2121
|
+
* }),
|
|
2122
|
+
* onSuccess: (response: CounterResponse) => {
|
|
2123
|
+
* console.log('Counter sent to server:', response);
|
|
2124
|
+
* },
|
|
2125
|
+
* onError: (error) => {
|
|
2126
|
+
* console.error('Failed to send counter:', error);
|
|
2127
|
+
* },
|
|
2128
|
+
* });
|
|
2129
|
+
*
|
|
2130
|
+
* const result = await this.saveUser({ value: 17 });
|
|
2131
|
+
* if (result.status === 'success') {
|
|
2132
|
+
* console.log('Successfully saved to server:', result.value);
|
|
2133
|
+
* }
|
|
2134
|
+
* else if (result.status === 'error') {
|
|
2135
|
+
* console.log('Failed to save:', result.error);
|
|
2136
|
+
* }
|
|
2137
|
+
* else {
|
|
2138
|
+
* console.log('Operation aborted');
|
|
2139
|
+
* }
|
|
2140
|
+
* ```
|
|
2141
|
+
*
|
|
2142
|
+
* @param options The options for the HTTP mutation.
|
|
2143
|
+
* @returns The HTTP mutation.
|
|
2144
|
+
*/
|
|
2145
|
+
function httpMutation(optionsOrRequest) {
|
|
2146
|
+
const httpClient = inject(HttpClient);
|
|
2147
|
+
const options = typeof optionsOrRequest === 'function'
|
|
2148
|
+
? { request: optionsOrRequest }
|
|
2149
|
+
: optionsOrRequest;
|
|
2150
|
+
const parse = options.parse ?? ((raw) => raw);
|
|
2151
|
+
const uploadProgress = signal(undefined, ...(ngDevMode ? [{ debugName: "uploadProgress" }] : []));
|
|
2152
|
+
const downloadProgress = signal(undefined, ...(ngDevMode ? [{ debugName: "downloadProgress" }] : []));
|
|
2153
|
+
const headers = signal(undefined, ...(ngDevMode ? [{ debugName: "headers" }] : []));
|
|
2154
|
+
const statusCode = signal(undefined, ...(ngDevMode ? [{ debugName: "statusCode" }] : []));
|
|
2155
|
+
const mutation = rxMutation({
|
|
2156
|
+
...options,
|
|
2157
|
+
operation: (param) => {
|
|
2158
|
+
const httpRequest = options.request(param);
|
|
2159
|
+
return defer(() => {
|
|
2160
|
+
uploadProgress.set(undefined);
|
|
2161
|
+
downloadProgress.set(undefined);
|
|
2162
|
+
headers.set(undefined);
|
|
2163
|
+
statusCode.set(undefined);
|
|
2164
|
+
return httpClient
|
|
2165
|
+
.request(httpRequest.method, httpRequest.url, {
|
|
2166
|
+
...httpRequest,
|
|
2167
|
+
observe: 'events',
|
|
2168
|
+
responseType: 'json',
|
|
2169
|
+
})
|
|
2170
|
+
.pipe(tap((response) => {
|
|
2171
|
+
if (response.type === HttpEventType.UploadProgress) {
|
|
2172
|
+
uploadProgress.set(response);
|
|
2173
|
+
}
|
|
2174
|
+
else if (response.type === HttpEventType.DownloadProgress) {
|
|
2175
|
+
downloadProgress.set(response);
|
|
2176
|
+
}
|
|
2177
|
+
}), filter((event) => event instanceof HttpResponse), tap((response) => {
|
|
2178
|
+
headers.set(response.headers);
|
|
2179
|
+
statusCode.set(response.status.toString());
|
|
2180
|
+
}), map((event) => parse(event.body)));
|
|
2181
|
+
});
|
|
2182
|
+
},
|
|
2183
|
+
});
|
|
2184
|
+
mutation.uploadProgress = uploadProgress;
|
|
2185
|
+
mutation.downloadProgress = downloadProgress;
|
|
2186
|
+
mutation.statusCode = statusCode;
|
|
2187
|
+
mutation.headers = headers;
|
|
2188
|
+
return mutation;
|
|
2189
|
+
}
|
|
2190
|
+
|
|
1883
2191
|
/**
|
|
1884
2192
|
* Generated bundle index. Do not edit.
|
|
1885
2193
|
*/
|
|
1886
2194
|
|
|
1887
|
-
export { capitalize, createEffects, createPageArray, createReducer, deriveCallStateKeys, emptyFeature, firstPage, getCallStateKeys, getCollectionArray, getDataServiceKeys, getUndoRedoKeys, gotoPage, mapToResource, nextPage, noPayload, patchState, payload, previousPage, provideDevtoolsConfig, renameDevtoolsName, setError, setLoaded, setLoading, setMaxPageNavigationArrayItems, setPageSize, setResetState, updateState, withCallState, withConditional, withDataService, withDevToolsStub, withDevtools, withDisabledNameIndices, withFeatureFactory, withGlitchTracking, withImmutableState, withIndexedDB, withIndexedDB as withIndexeddb, withLocalStorage, withMapper, withPagination, withRedux, withReset, withResource, withSessionStorage, withStorageSync, withUndoRedo };
|
|
2195
|
+
export { capitalize, concatOp, createEffects, createPageArray, createReducer, deriveCallStateKeys, emptyFeature, exhaustOp, firstPage, getCallStateKeys, getCollectionArray, getDataServiceKeys, getUndoRedoKeys, gotoPage, httpMutation, mapToResource, mergeOp, nextPage, noPayload, patchState, payload, previousPage, provideDevtoolsConfig, renameDevtoolsName, rxMutation, setError, setLoaded, setLoading, setMaxPageNavigationArrayItems, setPageSize, setResetState, switchOp, updateState, withCallState, withConditional, withDataService, withDevToolsStub, withDevtools, withDisabledNameIndices, withFeatureFactory, withGlitchTracking, withImmutableState, withIndexedDB, withIndexedDB as withIndexeddb, withLocalStorage, withMapper, withMutations, withPagination, withRedux, withReset, withResource, withSessionStorage, withStorageSync, withUndoRedo };
|
|
1888
2196
|
//# sourceMappingURL=angular-architects-ngrx-toolkit.mjs.map
|