ngx-signal-plus 2.6.0 → 2.7.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 +6 -3
- package/fesm2022/ngx-signal-plus.mjs +237 -6
- package/fesm2022/ngx-signal-plus.mjs.map +1 -1
- package/index.d.ts +251 -288
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://angular.dev/)
|
|
4
4
|
[](https://www.npmjs.com/package/ngx-signal-plus)
|
|
5
|
-

|
|
6
6
|
|
|
7
7
|
Bring validation, persistence, undo/redo, and reactive queries to Angular Signals on Angular 16+.
|
|
8
8
|
|
|
@@ -70,10 +70,10 @@ export class CounterComponent {
|
|
|
70
70
|
- Signal creation: `sp`, `spCounter`, `spToggle`, `spForm`, `spComputed`
|
|
71
71
|
- Signal enhancement: `enhance`
|
|
72
72
|
- Operators: `spMap`, `spFilter`, `spDebounceTime`, `spThrottleTime`, `spDelay`, `spDistinctUntilChanged`
|
|
73
|
-
- Developer experience: `spCombine`, `spAll`, `spAny`, `spEffect`, `spDebug`, `sp().debug(label)`
|
|
73
|
+
- Developer experience: `spCombine`, `spAll`, `spAny`, `spEffect`, `spDebug`, `spMonitor`, `sp().debug(label)`
|
|
74
74
|
- Forms and groups: `spForm`, `spFormGroup`
|
|
75
75
|
- Async helpers: `spAsync`, `spCollection`
|
|
76
|
-
- Reactive queries: `spQuery`, `spMutation`, `QueryClient`, `setGlobalQueryClient`
|
|
76
|
+
- Reactive queries: `spQuery`, `createDependentQuery`, `spInfiniteQuery`, `spMutation`, `QueryClient`, `setGlobalQueryClient`, `getGlobalQueryClient`
|
|
77
77
|
- Transactions: `spTransaction`, `spBatch`
|
|
78
78
|
- Schema validation: `spSchema`, `spSchemaValidator`
|
|
79
79
|
- Middleware: `spUseMiddleware`, `spRemoveMiddleware`, `spLoggerMiddleware`, `spAnalyticsMiddleware`
|
|
@@ -118,3 +118,6 @@ export class CounterComponent {
|
|
|
118
118
|
## License
|
|
119
119
|
|
|
120
120
|
MIT
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
|
|
@@ -264,7 +264,7 @@ function safeAddEventListener(event, handler) {
|
|
|
264
264
|
}
|
|
265
265
|
|
|
266
266
|
const states = new Map();
|
|
267
|
-
let globalEnabled = true;
|
|
267
|
+
let globalEnabled$1 = true;
|
|
268
268
|
const ensureState = (name) => {
|
|
269
269
|
const existing = states.get(name);
|
|
270
270
|
if (existing) {
|
|
@@ -291,7 +291,7 @@ const spDebug = {
|
|
|
291
291
|
recordUpdate(name, value) {
|
|
292
292
|
const state = ensureState(name);
|
|
293
293
|
state.lastValue = value;
|
|
294
|
-
if (!globalEnabled || !state.enabled) {
|
|
294
|
+
if (!globalEnabled$1 || !state.enabled) {
|
|
295
295
|
return;
|
|
296
296
|
}
|
|
297
297
|
state.updates += 1;
|
|
@@ -304,10 +304,10 @@ const spDebug = {
|
|
|
304
304
|
ensureState(name).enabled = false;
|
|
305
305
|
},
|
|
306
306
|
enableAll() {
|
|
307
|
-
globalEnabled = true;
|
|
307
|
+
globalEnabled$1 = true;
|
|
308
308
|
},
|
|
309
309
|
disableAll() {
|
|
310
|
-
globalEnabled = false;
|
|
310
|
+
globalEnabled$1 = false;
|
|
311
311
|
},
|
|
312
312
|
getActiveSignals() {
|
|
313
313
|
return Array.from(states.values())
|
|
@@ -319,7 +319,7 @@ const spDebug = {
|
|
|
319
319
|
},
|
|
320
320
|
clear() {
|
|
321
321
|
states.clear();
|
|
322
|
-
globalEnabled = true;
|
|
322
|
+
globalEnabled$1 = true;
|
|
323
323
|
},
|
|
324
324
|
};
|
|
325
325
|
|
|
@@ -4180,6 +4180,77 @@ function spComputed(fn, options = {}) {
|
|
|
4180
4180
|
};
|
|
4181
4181
|
}
|
|
4182
4182
|
|
|
4183
|
+
const metrics = new Map();
|
|
4184
|
+
let globalEnabled = true;
|
|
4185
|
+
const ensureMetric = (name) => {
|
|
4186
|
+
const existing = metrics.get(name);
|
|
4187
|
+
if (existing) {
|
|
4188
|
+
return existing;
|
|
4189
|
+
}
|
|
4190
|
+
const created = {
|
|
4191
|
+
name,
|
|
4192
|
+
updates: 0,
|
|
4193
|
+
enabled: true,
|
|
4194
|
+
totalDurationMs: 0,
|
|
4195
|
+
averageDurationMs: 0,
|
|
4196
|
+
maxDurationMs: 0,
|
|
4197
|
+
lastDurationMs: 0,
|
|
4198
|
+
lastUpdated: null,
|
|
4199
|
+
};
|
|
4200
|
+
metrics.set(name, created);
|
|
4201
|
+
return created;
|
|
4202
|
+
};
|
|
4203
|
+
const spMonitor = {
|
|
4204
|
+
trackSignal(name) {
|
|
4205
|
+
ensureMetric(name);
|
|
4206
|
+
},
|
|
4207
|
+
recordUpdate(name, durationMs = 0) {
|
|
4208
|
+
const metric = ensureMetric(name);
|
|
4209
|
+
if (!globalEnabled || !metric.enabled) {
|
|
4210
|
+
return;
|
|
4211
|
+
}
|
|
4212
|
+
metric.updates += 1;
|
|
4213
|
+
metric.lastDurationMs = durationMs;
|
|
4214
|
+
metric.totalDurationMs += durationMs;
|
|
4215
|
+
metric.averageDurationMs = metric.totalDurationMs / metric.updates;
|
|
4216
|
+
metric.maxDurationMs = Math.max(metric.maxDurationMs, durationMs);
|
|
4217
|
+
metric.lastUpdated = Date.now();
|
|
4218
|
+
},
|
|
4219
|
+
enable(name) {
|
|
4220
|
+
ensureMetric(name).enabled = true;
|
|
4221
|
+
},
|
|
4222
|
+
disable(name) {
|
|
4223
|
+
ensureMetric(name).enabled = false;
|
|
4224
|
+
},
|
|
4225
|
+
enableAll() {
|
|
4226
|
+
globalEnabled = true;
|
|
4227
|
+
},
|
|
4228
|
+
disableAll() {
|
|
4229
|
+
globalEnabled = false;
|
|
4230
|
+
},
|
|
4231
|
+
getHotSignals(limit = 10) {
|
|
4232
|
+
return Array.from(metrics.values())
|
|
4233
|
+
.filter((metric) => metric.enabled)
|
|
4234
|
+
.sort((a, b) => b.updates - a.updates)
|
|
4235
|
+
.slice(0, limit)
|
|
4236
|
+
.map((metric) => ({ ...metric }));
|
|
4237
|
+
},
|
|
4238
|
+
getSlowSignals(thresholdMs = 16) {
|
|
4239
|
+
return Array.from(metrics.values())
|
|
4240
|
+
.filter((metric) => metric.enabled && metric.averageDurationMs >= thresholdMs)
|
|
4241
|
+
.sort((a, b) => b.averageDurationMs - a.averageDurationMs)
|
|
4242
|
+
.map((metric) => ({ ...metric }));
|
|
4243
|
+
},
|
|
4244
|
+
exportMetrics(format = 'object') {
|
|
4245
|
+
const exported = Array.from(metrics.values()).map((metric) => ({ ...metric }));
|
|
4246
|
+
return format === 'json' ? JSON.stringify(exported) : exported;
|
|
4247
|
+
},
|
|
4248
|
+
clear() {
|
|
4249
|
+
metrics.clear();
|
|
4250
|
+
globalEnabled = true;
|
|
4251
|
+
},
|
|
4252
|
+
};
|
|
4253
|
+
|
|
4183
4254
|
function spEffect(callback, options = {}) {
|
|
4184
4255
|
const paused = signal(false, ...(ngDevMode ? [{ debugName: "paused" }] : []));
|
|
4185
4256
|
let timeoutId = null;
|
|
@@ -5739,6 +5810,7 @@ function spMutation(options) {
|
|
|
5739
5810
|
catch {
|
|
5740
5811
|
// Not in injection context - cleanup will be manual via reset()
|
|
5741
5812
|
}
|
|
5813
|
+
const queryClient = getGlobalQueryClient();
|
|
5742
5814
|
const dataSignal = signal(undefined, ...(ngDevMode ? [{ debugName: "dataSignal" }] : []));
|
|
5743
5815
|
const errorSignal = signal(null, ...(ngDevMode ? [{ debugName: "errorSignal" }] : []));
|
|
5744
5816
|
const isLoadingSignal = signal(false, ...(ngDevMode ? [{ debugName: "isLoadingSignal" }] : []));
|
|
@@ -5782,6 +5854,12 @@ function spMutation(options) {
|
|
|
5782
5854
|
const { retry = 0, retryDelay = 10 } = options;
|
|
5783
5855
|
let attempt = 0;
|
|
5784
5856
|
let lastError = null;
|
|
5857
|
+
let optimisticPreviousData;
|
|
5858
|
+
const optimistic = options.optimisticUpdate;
|
|
5859
|
+
if (optimistic) {
|
|
5860
|
+
optimisticPreviousData = queryClient.getQueryData(optimistic.queryKey);
|
|
5861
|
+
queryClient.setQueryData(optimistic.queryKey, (oldValue) => optimistic.updater(oldValue, variables));
|
|
5862
|
+
}
|
|
5785
5863
|
if (options.onMutate) {
|
|
5786
5864
|
await options.onMutate(variables);
|
|
5787
5865
|
}
|
|
@@ -5801,6 +5879,9 @@ function spMutation(options) {
|
|
|
5801
5879
|
if (options.onSettled) {
|
|
5802
5880
|
options.onSettled(result, null, variables);
|
|
5803
5881
|
}
|
|
5882
|
+
if (optimistic?.invalidateOnSettled) {
|
|
5883
|
+
queryClient.invalidateQueries(optimistic.queryKey);
|
|
5884
|
+
}
|
|
5804
5885
|
return result;
|
|
5805
5886
|
}
|
|
5806
5887
|
catch (error) {
|
|
@@ -5810,6 +5891,9 @@ function spMutation(options) {
|
|
|
5810
5891
|
? retry(attempt, lastError)
|
|
5811
5892
|
: attempt <= retry;
|
|
5812
5893
|
if (!shouldRetry) {
|
|
5894
|
+
if (optimistic && optimistic.rollbackOnError !== false) {
|
|
5895
|
+
queryClient.setQueryData(optimistic.queryKey, optimisticPreviousData);
|
|
5896
|
+
}
|
|
5813
5897
|
updateState({
|
|
5814
5898
|
error: lastError,
|
|
5815
5899
|
isLoading: false,
|
|
@@ -5823,6 +5907,9 @@ function spMutation(options) {
|
|
|
5823
5907
|
if (options.onSettled) {
|
|
5824
5908
|
options.onSettled(undefined, lastError, variables);
|
|
5825
5909
|
}
|
|
5910
|
+
if (optimistic?.invalidateOnSettled) {
|
|
5911
|
+
queryClient.invalidateQueries(optimistic.queryKey);
|
|
5912
|
+
}
|
|
5826
5913
|
throw lastError;
|
|
5827
5914
|
}
|
|
5828
5915
|
const delay = typeof retryDelay === 'function'
|
|
@@ -6066,6 +6153,18 @@ function createQuery(queryKey, queryFn, options) {
|
|
|
6066
6153
|
...options,
|
|
6067
6154
|
});
|
|
6068
6155
|
}
|
|
6156
|
+
function createDependentQuery(queryKey, queryFn, dependencies, options) {
|
|
6157
|
+
const enabled = computed(() => dependencies.every((dependency) => {
|
|
6158
|
+
const value = dependency();
|
|
6159
|
+
return value !== undefined && value !== null && value !== false;
|
|
6160
|
+
}), ...(ngDevMode ? [{ debugName: "enabled" }] : []));
|
|
6161
|
+
return spQuery({
|
|
6162
|
+
queryKey,
|
|
6163
|
+
queryFn,
|
|
6164
|
+
...options,
|
|
6165
|
+
enabled,
|
|
6166
|
+
});
|
|
6167
|
+
}
|
|
6069
6168
|
|
|
6070
6169
|
/**
|
|
6071
6170
|
* Reactive Queries Interfaces
|
|
@@ -6073,6 +6172,138 @@ function createQuery(queryKey, queryFn, options) {
|
|
|
6073
6172
|
* This module exports all interface definitions for reactive queries
|
|
6074
6173
|
*/
|
|
6075
6174
|
|
|
6175
|
+
function spInfiniteQuery(options) {
|
|
6176
|
+
const queryClient = getGlobalQueryClient();
|
|
6177
|
+
let destroyRef = null;
|
|
6178
|
+
try {
|
|
6179
|
+
destroyRef = inject(DestroyRef, { optional: true });
|
|
6180
|
+
}
|
|
6181
|
+
catch {
|
|
6182
|
+
// Not in injection context
|
|
6183
|
+
}
|
|
6184
|
+
const queryKey = Array.isArray(options.queryKey)
|
|
6185
|
+
? options.queryKey
|
|
6186
|
+
: options.queryKey.key;
|
|
6187
|
+
const pagesSignal = signal([], ...(ngDevMode ? [{ debugName: "pagesSignal" }] : []));
|
|
6188
|
+
const errorSignal = signal(null, ...(ngDevMode ? [{ debugName: "errorSignal" }] : []));
|
|
6189
|
+
const isLoadingSignal = signal(false, ...(ngDevMode ? [{ debugName: "isLoadingSignal" }] : []));
|
|
6190
|
+
const isFetchingSignal = signal(false, ...(ngDevMode ? [{ debugName: "isFetchingSignal" }] : []));
|
|
6191
|
+
const isFetchingNextPageSignal = signal(false, ...(ngDevMode ? [{ debugName: "isFetchingNextPageSignal" }] : []));
|
|
6192
|
+
const hasNextPageSignal = signal(true, ...(ngDevMode ? [{ debugName: "hasNextPageSignal" }] : []));
|
|
6193
|
+
const setPages = (pages) => {
|
|
6194
|
+
untracked(() => {
|
|
6195
|
+
pagesSignal.set(pages);
|
|
6196
|
+
const lastPage = pages.length > 0 ? pages[pages.length - 1] : undefined;
|
|
6197
|
+
hasNextPageSignal.set(lastPage !== undefined
|
|
6198
|
+
? options.getNextPageParam(lastPage, pages) !== undefined
|
|
6199
|
+
: true);
|
|
6200
|
+
});
|
|
6201
|
+
};
|
|
6202
|
+
const runPageFetch = async (pageParam) => {
|
|
6203
|
+
isFetchingSignal.set(true);
|
|
6204
|
+
errorSignal.set(null);
|
|
6205
|
+
try {
|
|
6206
|
+
return await options.queryFn(pageParam);
|
|
6207
|
+
}
|
|
6208
|
+
catch (error) {
|
|
6209
|
+
errorSignal.set(error);
|
|
6210
|
+
throw error;
|
|
6211
|
+
}
|
|
6212
|
+
finally {
|
|
6213
|
+
isFetchingSignal.set(false);
|
|
6214
|
+
}
|
|
6215
|
+
};
|
|
6216
|
+
const refetch = async () => {
|
|
6217
|
+
isLoadingSignal.set(true);
|
|
6218
|
+
try {
|
|
6219
|
+
const firstPage = await runPageFetch(options.initialPageParam);
|
|
6220
|
+
setPages([firstPage]);
|
|
6221
|
+
queryClient.setQueryData(queryKey, [firstPage]);
|
|
6222
|
+
}
|
|
6223
|
+
finally {
|
|
6224
|
+
isLoadingSignal.set(false);
|
|
6225
|
+
}
|
|
6226
|
+
};
|
|
6227
|
+
const fetchNextPage = async () => {
|
|
6228
|
+
const pages = pagesSignal();
|
|
6229
|
+
const lastPage = pages[pages.length - 1];
|
|
6230
|
+
const nextPageParam = pages.length === 0
|
|
6231
|
+
? options.initialPageParam
|
|
6232
|
+
: options.getNextPageParam(lastPage, pages);
|
|
6233
|
+
if (nextPageParam === undefined) {
|
|
6234
|
+
hasNextPageSignal.set(false);
|
|
6235
|
+
return;
|
|
6236
|
+
}
|
|
6237
|
+
isFetchingNextPageSignal.set(true);
|
|
6238
|
+
try {
|
|
6239
|
+
const page = await runPageFetch(nextPageParam);
|
|
6240
|
+
const nextPages = [...pagesSignal(), page];
|
|
6241
|
+
setPages(nextPages);
|
|
6242
|
+
queryClient.setQueryData(queryKey, nextPages);
|
|
6243
|
+
}
|
|
6244
|
+
finally {
|
|
6245
|
+
isFetchingNextPageSignal.set(false);
|
|
6246
|
+
}
|
|
6247
|
+
};
|
|
6248
|
+
let enabledWatcher = null;
|
|
6249
|
+
let previousEnabledState = false;
|
|
6250
|
+
const getEnabled = () => {
|
|
6251
|
+
if (typeof options.enabled === 'boolean') {
|
|
6252
|
+
return options.enabled;
|
|
6253
|
+
}
|
|
6254
|
+
if (options.enabled) {
|
|
6255
|
+
return options.enabled();
|
|
6256
|
+
}
|
|
6257
|
+
return true;
|
|
6258
|
+
};
|
|
6259
|
+
const runIfEnabled = () => {
|
|
6260
|
+
const enabled = getEnabled();
|
|
6261
|
+
if (!enabled) {
|
|
6262
|
+
previousEnabledState = false;
|
|
6263
|
+
return;
|
|
6264
|
+
}
|
|
6265
|
+
if (previousEnabledState) {
|
|
6266
|
+
return;
|
|
6267
|
+
}
|
|
6268
|
+
previousEnabledState = true;
|
|
6269
|
+
const cached = queryClient.getQueryData(queryKey);
|
|
6270
|
+
if (cached && cached.length > 0) {
|
|
6271
|
+
setPages(cached);
|
|
6272
|
+
return;
|
|
6273
|
+
}
|
|
6274
|
+
refetch().catch(() => undefined);
|
|
6275
|
+
};
|
|
6276
|
+
runIfEnabled();
|
|
6277
|
+
if (typeof options.enabled !== 'boolean' && options.enabled) {
|
|
6278
|
+
enabledWatcher = setInterval(runIfEnabled, 100);
|
|
6279
|
+
}
|
|
6280
|
+
if (destroyRef) {
|
|
6281
|
+
destroyRef.onDestroy(() => {
|
|
6282
|
+
if (enabledWatcher) {
|
|
6283
|
+
clearInterval(enabledWatcher);
|
|
6284
|
+
enabledWatcher = null;
|
|
6285
|
+
}
|
|
6286
|
+
});
|
|
6287
|
+
}
|
|
6288
|
+
return {
|
|
6289
|
+
pages: computed(() => pagesSignal()),
|
|
6290
|
+
error: computed(() => errorSignal()),
|
|
6291
|
+
isLoading: computed(() => isLoadingSignal()),
|
|
6292
|
+
isFetching: computed(() => isFetchingSignal()),
|
|
6293
|
+
isFetchingNextPage: computed(() => isFetchingNextPageSignal()),
|
|
6294
|
+
hasNextPage: computed(() => hasNextPageSignal()),
|
|
6295
|
+
refetch,
|
|
6296
|
+
fetchNextPage,
|
|
6297
|
+
};
|
|
6298
|
+
}
|
|
6299
|
+
function createInfiniteQuery(queryKey, queryFn, options) {
|
|
6300
|
+
return spInfiniteQuery({
|
|
6301
|
+
...options,
|
|
6302
|
+
queryKey,
|
|
6303
|
+
queryFn,
|
|
6304
|
+
});
|
|
6305
|
+
}
|
|
6306
|
+
|
|
6076
6307
|
/**
|
|
6077
6308
|
* Public API Surface of ngx-signal-plus
|
|
6078
6309
|
*
|
|
@@ -6085,5 +6316,5 @@ function createQuery(queryKey, queryFn, options) {
|
|
|
6085
6316
|
* Generated bundle index. Do not edit.
|
|
6086
6317
|
*/
|
|
6087
6318
|
|
|
6088
|
-
export { QueryClient, SP_ERRORS, SpError, createMutation, createQuery, enhance, formatSpError, getGlobalQueryClient, setGlobalQueryClient, sp, spAll, spAnalyticsMiddleware, spAny, spAsync, spBatch, spClearMiddleware, spCollection, spCombine, combineLatest as spCombineLatest, spComputed, spCounter, spCreateError, debounceTime as spDebounceTime, spDebug, delay$1 as spDelay, distinctUntilChanged as spDistinctUntilChanged, spEffect, filter as spFilter, spForm, spFormGroup, spGetMiddlewareCount, spGetModifiedSignals, HistoryManager as spHistoryManager, spIsInBatch, spIsInTransaction, spIsTransactionActive, spLoggerMiddleware, map as spMap, merge as spMerge, spMutation, presets as spPresets, spQuery, spRemoveMiddleware, spSchema, spSchemaValidator, spSchemaWithErrors, SignalBuilder as spSignalBuilder, SignalPlusComponent as spSignalPlusComponent, SignalPlusService as spSignalPlusService, skip as spSkip, StorageManager as spStorageManager, take as spTake, throttleTime as spThrottleTime, spToggle, spTransaction, spUseMiddleware, validators as spValidators };
|
|
6319
|
+
export { QueryClient, SP_ERRORS, SpError, createDependentQuery, createInfiniteQuery, createMutation, createQuery, enhance, formatSpError, getGlobalQueryClient, setGlobalQueryClient, sp, spAll, spAnalyticsMiddleware, spAny, spAsync, spBatch, spClearMiddleware, spCollection, spCombine, combineLatest as spCombineLatest, spComputed, spCounter, spCreateError, debounceTime as spDebounceTime, spDebug, delay$1 as spDelay, distinctUntilChanged as spDistinctUntilChanged, spEffect, filter as spFilter, spForm, spFormGroup, spGetMiddlewareCount, spGetModifiedSignals, HistoryManager as spHistoryManager, spInfiniteQuery, spIsInBatch, spIsInTransaction, spIsTransactionActive, spLoggerMiddleware, map as spMap, merge as spMerge, spMonitor, spMutation, presets as spPresets, spQuery, spRemoveMiddleware, spSchema, spSchemaValidator, spSchemaWithErrors, SignalBuilder as spSignalBuilder, SignalPlusComponent as spSignalPlusComponent, SignalPlusService as spSignalPlusService, skip as spSkip, StorageManager as spStorageManager, take as spTake, throttleTime as spThrottleTime, spToggle, spTransaction, spUseMiddleware, validators as spValidators };
|
|
6089
6320
|
//# sourceMappingURL=ngx-signal-plus.mjs.map
|