ngx-signal-plus 2.6.0 → 2.8.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 +10 -3
- package/fesm2022/ngx-signal-plus.mjs +335 -74
- package/fesm2022/ngx-signal-plus.mjs.map +1 -1
- package/index.d.ts +266 -299
- 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)`, `sp().monitor(options)`
|
|
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`
|
|
@@ -82,6 +82,8 @@ export class CounterComponent {
|
|
|
82
82
|
|
|
83
83
|
- `spComputed()` now exposes a read-only surface via `ReadonlySignalPlus<T>`.
|
|
84
84
|
- `SignalPlus<T>` now includes `errors: Signal<string[]>` for consistent validation error access.
|
|
85
|
+
- Builder monitoring is available via `sp().monitor(options)` and records updates through `spMonitor`.
|
|
86
|
+
- Registered middleware now runs in normal `set`/`setValue`/`update` runtime paths for built signals.
|
|
85
87
|
|
|
86
88
|
## Comparisons
|
|
87
89
|
|
|
@@ -118,3 +120,8 @@ export class CounterComponent {
|
|
|
118
120
|
## License
|
|
119
121
|
|
|
120
122
|
MIT
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
|
|
@@ -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,10 +319,142 @@ const spDebug = {
|
|
|
319
319
|
},
|
|
320
320
|
clear() {
|
|
321
321
|
states.clear();
|
|
322
|
+
globalEnabled$1 = true;
|
|
323
|
+
},
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
const metrics = new Map();
|
|
327
|
+
let globalEnabled = true;
|
|
328
|
+
const ensureMetric = (name) => {
|
|
329
|
+
const existing = metrics.get(name);
|
|
330
|
+
if (existing) {
|
|
331
|
+
return existing;
|
|
332
|
+
}
|
|
333
|
+
const created = {
|
|
334
|
+
name,
|
|
335
|
+
updates: 0,
|
|
336
|
+
enabled: true,
|
|
337
|
+
totalDurationMs: 0,
|
|
338
|
+
averageDurationMs: 0,
|
|
339
|
+
maxDurationMs: 0,
|
|
340
|
+
lastDurationMs: 0,
|
|
341
|
+
lastUpdated: null,
|
|
342
|
+
};
|
|
343
|
+
metrics.set(name, created);
|
|
344
|
+
return created;
|
|
345
|
+
};
|
|
346
|
+
const spMonitor = {
|
|
347
|
+
trackSignal(name) {
|
|
348
|
+
ensureMetric(name);
|
|
349
|
+
},
|
|
350
|
+
recordUpdate(name, durationMs = 0) {
|
|
351
|
+
const metric = ensureMetric(name);
|
|
352
|
+
if (!globalEnabled || !metric.enabled) {
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
metric.updates += 1;
|
|
356
|
+
metric.lastDurationMs = durationMs;
|
|
357
|
+
metric.totalDurationMs += durationMs;
|
|
358
|
+
metric.averageDurationMs = metric.totalDurationMs / metric.updates;
|
|
359
|
+
metric.maxDurationMs = Math.max(metric.maxDurationMs, durationMs);
|
|
360
|
+
metric.lastUpdated = Date.now();
|
|
361
|
+
},
|
|
362
|
+
enable(name) {
|
|
363
|
+
ensureMetric(name).enabled = true;
|
|
364
|
+
},
|
|
365
|
+
disable(name) {
|
|
366
|
+
ensureMetric(name).enabled = false;
|
|
367
|
+
},
|
|
368
|
+
enableAll() {
|
|
369
|
+
globalEnabled = true;
|
|
370
|
+
},
|
|
371
|
+
disableAll() {
|
|
372
|
+
globalEnabled = false;
|
|
373
|
+
},
|
|
374
|
+
getHotSignals(limit = 10) {
|
|
375
|
+
return Array.from(metrics.values())
|
|
376
|
+
.filter((metric) => metric.enabled)
|
|
377
|
+
.sort((a, b) => b.updates - a.updates)
|
|
378
|
+
.slice(0, limit)
|
|
379
|
+
.map((metric) => ({ ...metric }));
|
|
380
|
+
},
|
|
381
|
+
getSlowSignals(thresholdMs = 16) {
|
|
382
|
+
return Array.from(metrics.values())
|
|
383
|
+
.filter((metric) => metric.enabled && metric.averageDurationMs >= thresholdMs)
|
|
384
|
+
.sort((a, b) => b.averageDurationMs - a.averageDurationMs)
|
|
385
|
+
.map((metric) => ({ ...metric }));
|
|
386
|
+
},
|
|
387
|
+
exportMetrics(format = 'object') {
|
|
388
|
+
const exported = Array.from(metrics.values()).map((metric) => ({ ...metric }));
|
|
389
|
+
return format === 'json' ? JSON.stringify(exported) : exported;
|
|
390
|
+
},
|
|
391
|
+
clear() {
|
|
392
|
+
metrics.clear();
|
|
322
393
|
globalEnabled = true;
|
|
323
394
|
},
|
|
324
395
|
};
|
|
325
396
|
|
|
397
|
+
/**
|
|
398
|
+
* Middleware system for intercepting signal operations.
|
|
399
|
+
*/
|
|
400
|
+
const middlewareRegistry = [];
|
|
401
|
+
function spUseMiddleware(middleware) {
|
|
402
|
+
if (!middlewareRegistry.some((m) => m.name === middleware.name)) {
|
|
403
|
+
middlewareRegistry.push(middleware);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
function spRemoveMiddleware(name) {
|
|
407
|
+
const index = middlewareRegistry.findIndex((m) => m.name === name);
|
|
408
|
+
if (index === -1)
|
|
409
|
+
return false;
|
|
410
|
+
middlewareRegistry.splice(index, 1);
|
|
411
|
+
return true;
|
|
412
|
+
}
|
|
413
|
+
function spClearMiddleware() {
|
|
414
|
+
middlewareRegistry.length = 0;
|
|
415
|
+
}
|
|
416
|
+
function spGetMiddlewareCount() {
|
|
417
|
+
return middlewareRegistry.length;
|
|
418
|
+
}
|
|
419
|
+
function spRunMiddleware(context) {
|
|
420
|
+
for (const m of middlewareRegistry) {
|
|
421
|
+
try {
|
|
422
|
+
m.onSet?.(context);
|
|
423
|
+
}
|
|
424
|
+
catch {
|
|
425
|
+
/* ignore */
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
function spRunMiddlewareError(error, context) {
|
|
430
|
+
for (const m of middlewareRegistry) {
|
|
431
|
+
try {
|
|
432
|
+
m.onError?.(error, context);
|
|
433
|
+
}
|
|
434
|
+
catch {
|
|
435
|
+
/* ignore */
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
function spLoggerMiddleware(prefix = '[Signal]') {
|
|
440
|
+
return {
|
|
441
|
+
name: 'sp-logger',
|
|
442
|
+
onSet: (ctx) => console.log(`${prefix} ${ctx.signalName || 'signal'}: ${JSON.stringify(ctx.oldValue)} -> ${JSON.stringify(ctx.newValue)}`),
|
|
443
|
+
onError: (error, ctx) => console.error(`${prefix} Error in ${ctx.signalName || 'signal'}:`, error.message),
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
function spAnalyticsMiddleware(tracker) {
|
|
447
|
+
return {
|
|
448
|
+
name: 'sp-analytics',
|
|
449
|
+
onSet: (ctx) => tracker({
|
|
450
|
+
name: ctx.signalName || 'unknown',
|
|
451
|
+
oldValue: ctx.oldValue,
|
|
452
|
+
newValue: ctx.newValue,
|
|
453
|
+
timestamp: ctx.timestamp,
|
|
454
|
+
}),
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
|
|
326
458
|
/**
|
|
327
459
|
* @fileoverview Builder class for creating enhanced Angular signals
|
|
328
460
|
* Provides a fluent API for configuring signals with features like:
|
|
@@ -345,6 +477,7 @@ const spDebug = {
|
|
|
345
477
|
*/
|
|
346
478
|
class SignalBuilder {
|
|
347
479
|
options;
|
|
480
|
+
static monitorCounter = 0;
|
|
348
481
|
/**
|
|
349
482
|
* Creates a new SignalBuilder instance
|
|
350
483
|
* @param initialValue Initial value for the signal
|
|
@@ -448,6 +581,10 @@ class SignalBuilder {
|
|
|
448
581
|
this.options.debugLabel = label;
|
|
449
582
|
return this;
|
|
450
583
|
}
|
|
584
|
+
monitor(options = {}) {
|
|
585
|
+
this.options.monitorOptions = { ...options };
|
|
586
|
+
return this;
|
|
587
|
+
}
|
|
451
588
|
/**
|
|
452
589
|
* Transforms signal value to a different type
|
|
453
590
|
* @param fn Transform function from T to R
|
|
@@ -479,6 +616,9 @@ class SignalBuilder {
|
|
|
479
616
|
newBuilder.options.storageKey = this.options.storageKey;
|
|
480
617
|
newBuilder.options.debounceTime = this.options.debounceTime;
|
|
481
618
|
newBuilder.options.autoCleanup = this.options.autoCleanup;
|
|
619
|
+
newBuilder.options.monitorOptions = this.options.monitorOptions
|
|
620
|
+
? { ...this.options.monitorOptions }
|
|
621
|
+
: undefined;
|
|
482
622
|
return newBuilder;
|
|
483
623
|
}
|
|
484
624
|
/**
|
|
@@ -549,6 +689,20 @@ class SignalBuilder {
|
|
|
549
689
|
const asyncErrorsSignal = signal([], ...(ngDevMode ? [{ debugName: "asyncErrorsSignal" }] : []));
|
|
550
690
|
const enforceHistorySize = (histArray) => this.limitByHistorySize(histArray, this.options.historySize);
|
|
551
691
|
const enforceRedoStackSize = (redoArray) => this.limitByHistorySize(redoArray, this.options.historySize);
|
|
692
|
+
const monitorOptions = this.options.monitorOptions;
|
|
693
|
+
const monitorEnabled = Boolean(monitorOptions &&
|
|
694
|
+
((monitorOptions.trackUpdates ?? true) || monitorOptions.trackPerformance));
|
|
695
|
+
const trackPerformance = Boolean(monitorOptions?.trackPerformance);
|
|
696
|
+
const signalName = monitorOptions?.label ||
|
|
697
|
+
this.options.debugLabel ||
|
|
698
|
+
this.options.storageKey ||
|
|
699
|
+
`signal-${SignalBuilder.monitorCounter++}`;
|
|
700
|
+
const createMiddlewareContext = (oldValue, newValue) => ({
|
|
701
|
+
signalName,
|
|
702
|
+
oldValue,
|
|
703
|
+
newValue,
|
|
704
|
+
timestamp: Date.now(),
|
|
705
|
+
});
|
|
552
706
|
/**
|
|
553
707
|
* Helper function to run async validation with debouncing and cancellation
|
|
554
708
|
* @param value The value to validate
|
|
@@ -803,21 +957,20 @@ class SignalBuilder {
|
|
|
803
957
|
}
|
|
804
958
|
// Only update if value has changed
|
|
805
959
|
if (hasChanged) {
|
|
806
|
-
|
|
960
|
+
const oldValue = conditionalClone(writable());
|
|
961
|
+
spRunMiddleware(createMiddlewareContext(oldValue, conditionalClone(transformedValue)));
|
|
962
|
+
const monitorStart = monitorEnabled && trackPerformance ? performance.now() : 0;
|
|
963
|
+
previousValue = oldValue;
|
|
807
964
|
writable.set(transformedValue);
|
|
808
|
-
// Update history if enabled and value has changed
|
|
809
965
|
if (this.options.enableHistory && !isProcessingDebounce) {
|
|
810
|
-
// Clear redo stack when new value is set
|
|
811
966
|
redoStack = [];
|
|
812
967
|
const currentHistory = history();
|
|
813
968
|
const newHistory = [
|
|
814
969
|
...currentHistory,
|
|
815
970
|
conditionalClone(transformedValue),
|
|
816
971
|
];
|
|
817
|
-
// Enforce history size limit using helper function
|
|
818
972
|
history.set(enforceHistorySize(newHistory));
|
|
819
973
|
}
|
|
820
|
-
// Handle storage
|
|
821
974
|
if (this.options.storageKey && isBrowser()) {
|
|
822
975
|
try {
|
|
823
976
|
isProcessingStorage = true;
|
|
@@ -835,7 +988,10 @@ class SignalBuilder {
|
|
|
835
988
|
isProcessingStorage = false;
|
|
836
989
|
}
|
|
837
990
|
}
|
|
838
|
-
|
|
991
|
+
if (monitorEnabled) {
|
|
992
|
+
const duration = trackPerformance ? performance.now() - monitorStart : 0;
|
|
993
|
+
spMonitor.recordUpdate(signalName, duration);
|
|
994
|
+
}
|
|
839
995
|
if (!isCleanedUp) {
|
|
840
996
|
notifySubscribers(transformedValue);
|
|
841
997
|
}
|
|
@@ -851,6 +1007,9 @@ class SignalBuilder {
|
|
|
851
1007
|
if (this.options.debugLabel) {
|
|
852
1008
|
spDebug.trackSignal(this.options.debugLabel, writable());
|
|
853
1009
|
}
|
|
1010
|
+
if (monitorEnabled) {
|
|
1011
|
+
spMonitor.trackSignal(signalName);
|
|
1012
|
+
}
|
|
854
1013
|
const processValue = (value) => {
|
|
855
1014
|
// Prevent setValue after destroy
|
|
856
1015
|
if (isCleanedUp) {
|
|
@@ -896,6 +1055,7 @@ class SignalBuilder {
|
|
|
896
1055
|
}
|
|
897
1056
|
}
|
|
898
1057
|
catch (error) {
|
|
1058
|
+
spRunMiddlewareError(error, createMiddlewareContext(conditionalClone(writable()), conditionalClone(value)));
|
|
899
1059
|
this.handleError(error);
|
|
900
1060
|
throw error;
|
|
901
1061
|
}
|
|
@@ -915,14 +1075,16 @@ class SignalBuilder {
|
|
|
915
1075
|
set: processValue,
|
|
916
1076
|
setValue: processValue,
|
|
917
1077
|
update: (fn) => {
|
|
1078
|
+
let newValue;
|
|
918
1079
|
try {
|
|
919
|
-
|
|
920
|
-
processValue(newValue);
|
|
1080
|
+
newValue = fn(writable());
|
|
921
1081
|
}
|
|
922
1082
|
catch (error) {
|
|
1083
|
+
spRunMiddlewareError(error, createMiddlewareContext(conditionalClone(writable()), conditionalClone(writable())));
|
|
923
1084
|
this.handleError(error);
|
|
924
1085
|
throw error;
|
|
925
1086
|
}
|
|
1087
|
+
processValue(newValue);
|
|
926
1088
|
},
|
|
927
1089
|
reset: () => {
|
|
928
1090
|
try {
|
|
@@ -4556,67 +4718,6 @@ function spFormGroup(config, options) {
|
|
|
4556
4718
|
};
|
|
4557
4719
|
}
|
|
4558
4720
|
|
|
4559
|
-
/**
|
|
4560
|
-
* Middleware system for intercepting signal operations.
|
|
4561
|
-
*/
|
|
4562
|
-
const middlewareRegistry = [];
|
|
4563
|
-
function spUseMiddleware(middleware) {
|
|
4564
|
-
if (!middlewareRegistry.some((m) => m.name === middleware.name)) {
|
|
4565
|
-
middlewareRegistry.push(middleware);
|
|
4566
|
-
}
|
|
4567
|
-
}
|
|
4568
|
-
function spRemoveMiddleware(name) {
|
|
4569
|
-
const index = middlewareRegistry.findIndex((m) => m.name === name);
|
|
4570
|
-
if (index === -1)
|
|
4571
|
-
return false;
|
|
4572
|
-
middlewareRegistry.splice(index, 1);
|
|
4573
|
-
return true;
|
|
4574
|
-
}
|
|
4575
|
-
function spClearMiddleware() {
|
|
4576
|
-
middlewareRegistry.length = 0;
|
|
4577
|
-
}
|
|
4578
|
-
function spGetMiddlewareCount() {
|
|
4579
|
-
return middlewareRegistry.length;
|
|
4580
|
-
}
|
|
4581
|
-
function spRunMiddleware(context) {
|
|
4582
|
-
for (const m of middlewareRegistry) {
|
|
4583
|
-
try {
|
|
4584
|
-
m.onSet?.(context);
|
|
4585
|
-
}
|
|
4586
|
-
catch {
|
|
4587
|
-
/* ignore */
|
|
4588
|
-
}
|
|
4589
|
-
}
|
|
4590
|
-
}
|
|
4591
|
-
function spRunMiddlewareError(error, context) {
|
|
4592
|
-
for (const m of middlewareRegistry) {
|
|
4593
|
-
try {
|
|
4594
|
-
m.onError?.(error, context);
|
|
4595
|
-
}
|
|
4596
|
-
catch {
|
|
4597
|
-
/* ignore */
|
|
4598
|
-
}
|
|
4599
|
-
}
|
|
4600
|
-
}
|
|
4601
|
-
function spLoggerMiddleware(prefix = '[Signal]') {
|
|
4602
|
-
return {
|
|
4603
|
-
name: 'sp-logger',
|
|
4604
|
-
onSet: (ctx) => console.log(`${prefix} ${ctx.signalName || 'signal'}: ${JSON.stringify(ctx.oldValue)} -> ${JSON.stringify(ctx.newValue)}`),
|
|
4605
|
-
onError: (error, ctx) => console.error(`${prefix} Error in ${ctx.signalName || 'signal'}:`, error.message),
|
|
4606
|
-
};
|
|
4607
|
-
}
|
|
4608
|
-
function spAnalyticsMiddleware(tracker) {
|
|
4609
|
-
return {
|
|
4610
|
-
name: 'sp-analytics',
|
|
4611
|
-
onSet: (ctx) => tracker({
|
|
4612
|
-
name: ctx.signalName || 'unknown',
|
|
4613
|
-
oldValue: ctx.oldValue,
|
|
4614
|
-
newValue: ctx.newValue,
|
|
4615
|
-
timestamp: ctx.timestamp,
|
|
4616
|
-
}),
|
|
4617
|
-
};
|
|
4618
|
-
}
|
|
4619
|
-
|
|
4620
4721
|
function extractErrorMessages(error) {
|
|
4621
4722
|
if (!error) {
|
|
4622
4723
|
return [];
|
|
@@ -5739,6 +5840,7 @@ function spMutation(options) {
|
|
|
5739
5840
|
catch {
|
|
5740
5841
|
// Not in injection context - cleanup will be manual via reset()
|
|
5741
5842
|
}
|
|
5843
|
+
const queryClient = getGlobalQueryClient();
|
|
5742
5844
|
const dataSignal = signal(undefined, ...(ngDevMode ? [{ debugName: "dataSignal" }] : []));
|
|
5743
5845
|
const errorSignal = signal(null, ...(ngDevMode ? [{ debugName: "errorSignal" }] : []));
|
|
5744
5846
|
const isLoadingSignal = signal(false, ...(ngDevMode ? [{ debugName: "isLoadingSignal" }] : []));
|
|
@@ -5782,6 +5884,12 @@ function spMutation(options) {
|
|
|
5782
5884
|
const { retry = 0, retryDelay = 10 } = options;
|
|
5783
5885
|
let attempt = 0;
|
|
5784
5886
|
let lastError = null;
|
|
5887
|
+
let optimisticPreviousData;
|
|
5888
|
+
const optimistic = options.optimisticUpdate;
|
|
5889
|
+
if (optimistic) {
|
|
5890
|
+
optimisticPreviousData = queryClient.getQueryData(optimistic.queryKey);
|
|
5891
|
+
queryClient.setQueryData(optimistic.queryKey, (oldValue) => optimistic.updater(oldValue, variables));
|
|
5892
|
+
}
|
|
5785
5893
|
if (options.onMutate) {
|
|
5786
5894
|
await options.onMutate(variables);
|
|
5787
5895
|
}
|
|
@@ -5801,6 +5909,9 @@ function spMutation(options) {
|
|
|
5801
5909
|
if (options.onSettled) {
|
|
5802
5910
|
options.onSettled(result, null, variables);
|
|
5803
5911
|
}
|
|
5912
|
+
if (optimistic?.invalidateOnSettled) {
|
|
5913
|
+
queryClient.invalidateQueries(optimistic.queryKey);
|
|
5914
|
+
}
|
|
5804
5915
|
return result;
|
|
5805
5916
|
}
|
|
5806
5917
|
catch (error) {
|
|
@@ -5810,6 +5921,9 @@ function spMutation(options) {
|
|
|
5810
5921
|
? retry(attempt, lastError)
|
|
5811
5922
|
: attempt <= retry;
|
|
5812
5923
|
if (!shouldRetry) {
|
|
5924
|
+
if (optimistic && optimistic.rollbackOnError !== false) {
|
|
5925
|
+
queryClient.setQueryData(optimistic.queryKey, optimisticPreviousData);
|
|
5926
|
+
}
|
|
5813
5927
|
updateState({
|
|
5814
5928
|
error: lastError,
|
|
5815
5929
|
isLoading: false,
|
|
@@ -5823,6 +5937,9 @@ function spMutation(options) {
|
|
|
5823
5937
|
if (options.onSettled) {
|
|
5824
5938
|
options.onSettled(undefined, lastError, variables);
|
|
5825
5939
|
}
|
|
5940
|
+
if (optimistic?.invalidateOnSettled) {
|
|
5941
|
+
queryClient.invalidateQueries(optimistic.queryKey);
|
|
5942
|
+
}
|
|
5826
5943
|
throw lastError;
|
|
5827
5944
|
}
|
|
5828
5945
|
const delay = typeof retryDelay === 'function'
|
|
@@ -6066,6 +6183,18 @@ function createQuery(queryKey, queryFn, options) {
|
|
|
6066
6183
|
...options,
|
|
6067
6184
|
});
|
|
6068
6185
|
}
|
|
6186
|
+
function createDependentQuery(queryKey, queryFn, dependencies, options) {
|
|
6187
|
+
const enabled = computed(() => dependencies.every((dependency) => {
|
|
6188
|
+
const value = dependency();
|
|
6189
|
+
return value !== undefined && value !== null && value !== false;
|
|
6190
|
+
}), ...(ngDevMode ? [{ debugName: "enabled" }] : []));
|
|
6191
|
+
return spQuery({
|
|
6192
|
+
queryKey,
|
|
6193
|
+
queryFn,
|
|
6194
|
+
...options,
|
|
6195
|
+
enabled,
|
|
6196
|
+
});
|
|
6197
|
+
}
|
|
6069
6198
|
|
|
6070
6199
|
/**
|
|
6071
6200
|
* Reactive Queries Interfaces
|
|
@@ -6073,6 +6202,138 @@ function createQuery(queryKey, queryFn, options) {
|
|
|
6073
6202
|
* This module exports all interface definitions for reactive queries
|
|
6074
6203
|
*/
|
|
6075
6204
|
|
|
6205
|
+
function spInfiniteQuery(options) {
|
|
6206
|
+
const queryClient = getGlobalQueryClient();
|
|
6207
|
+
let destroyRef = null;
|
|
6208
|
+
try {
|
|
6209
|
+
destroyRef = inject(DestroyRef, { optional: true });
|
|
6210
|
+
}
|
|
6211
|
+
catch {
|
|
6212
|
+
// Not in injection context
|
|
6213
|
+
}
|
|
6214
|
+
const queryKey = Array.isArray(options.queryKey)
|
|
6215
|
+
? options.queryKey
|
|
6216
|
+
: options.queryKey.key;
|
|
6217
|
+
const pagesSignal = signal([], ...(ngDevMode ? [{ debugName: "pagesSignal" }] : []));
|
|
6218
|
+
const errorSignal = signal(null, ...(ngDevMode ? [{ debugName: "errorSignal" }] : []));
|
|
6219
|
+
const isLoadingSignal = signal(false, ...(ngDevMode ? [{ debugName: "isLoadingSignal" }] : []));
|
|
6220
|
+
const isFetchingSignal = signal(false, ...(ngDevMode ? [{ debugName: "isFetchingSignal" }] : []));
|
|
6221
|
+
const isFetchingNextPageSignal = signal(false, ...(ngDevMode ? [{ debugName: "isFetchingNextPageSignal" }] : []));
|
|
6222
|
+
const hasNextPageSignal = signal(true, ...(ngDevMode ? [{ debugName: "hasNextPageSignal" }] : []));
|
|
6223
|
+
const setPages = (pages) => {
|
|
6224
|
+
untracked(() => {
|
|
6225
|
+
pagesSignal.set(pages);
|
|
6226
|
+
const lastPage = pages.length > 0 ? pages[pages.length - 1] : undefined;
|
|
6227
|
+
hasNextPageSignal.set(lastPage !== undefined
|
|
6228
|
+
? options.getNextPageParam(lastPage, pages) !== undefined
|
|
6229
|
+
: true);
|
|
6230
|
+
});
|
|
6231
|
+
};
|
|
6232
|
+
const runPageFetch = async (pageParam) => {
|
|
6233
|
+
isFetchingSignal.set(true);
|
|
6234
|
+
errorSignal.set(null);
|
|
6235
|
+
try {
|
|
6236
|
+
return await options.queryFn(pageParam);
|
|
6237
|
+
}
|
|
6238
|
+
catch (error) {
|
|
6239
|
+
errorSignal.set(error);
|
|
6240
|
+
throw error;
|
|
6241
|
+
}
|
|
6242
|
+
finally {
|
|
6243
|
+
isFetchingSignal.set(false);
|
|
6244
|
+
}
|
|
6245
|
+
};
|
|
6246
|
+
const refetch = async () => {
|
|
6247
|
+
isLoadingSignal.set(true);
|
|
6248
|
+
try {
|
|
6249
|
+
const firstPage = await runPageFetch(options.initialPageParam);
|
|
6250
|
+
setPages([firstPage]);
|
|
6251
|
+
queryClient.setQueryData(queryKey, [firstPage]);
|
|
6252
|
+
}
|
|
6253
|
+
finally {
|
|
6254
|
+
isLoadingSignal.set(false);
|
|
6255
|
+
}
|
|
6256
|
+
};
|
|
6257
|
+
const fetchNextPage = async () => {
|
|
6258
|
+
const pages = pagesSignal();
|
|
6259
|
+
const lastPage = pages[pages.length - 1];
|
|
6260
|
+
const nextPageParam = pages.length === 0
|
|
6261
|
+
? options.initialPageParam
|
|
6262
|
+
: options.getNextPageParam(lastPage, pages);
|
|
6263
|
+
if (nextPageParam === undefined) {
|
|
6264
|
+
hasNextPageSignal.set(false);
|
|
6265
|
+
return;
|
|
6266
|
+
}
|
|
6267
|
+
isFetchingNextPageSignal.set(true);
|
|
6268
|
+
try {
|
|
6269
|
+
const page = await runPageFetch(nextPageParam);
|
|
6270
|
+
const nextPages = [...pagesSignal(), page];
|
|
6271
|
+
setPages(nextPages);
|
|
6272
|
+
queryClient.setQueryData(queryKey, nextPages);
|
|
6273
|
+
}
|
|
6274
|
+
finally {
|
|
6275
|
+
isFetchingNextPageSignal.set(false);
|
|
6276
|
+
}
|
|
6277
|
+
};
|
|
6278
|
+
let enabledWatcher = null;
|
|
6279
|
+
let previousEnabledState = false;
|
|
6280
|
+
const getEnabled = () => {
|
|
6281
|
+
if (typeof options.enabled === 'boolean') {
|
|
6282
|
+
return options.enabled;
|
|
6283
|
+
}
|
|
6284
|
+
if (options.enabled) {
|
|
6285
|
+
return options.enabled();
|
|
6286
|
+
}
|
|
6287
|
+
return true;
|
|
6288
|
+
};
|
|
6289
|
+
const runIfEnabled = () => {
|
|
6290
|
+
const enabled = getEnabled();
|
|
6291
|
+
if (!enabled) {
|
|
6292
|
+
previousEnabledState = false;
|
|
6293
|
+
return;
|
|
6294
|
+
}
|
|
6295
|
+
if (previousEnabledState) {
|
|
6296
|
+
return;
|
|
6297
|
+
}
|
|
6298
|
+
previousEnabledState = true;
|
|
6299
|
+
const cached = queryClient.getQueryData(queryKey);
|
|
6300
|
+
if (cached && cached.length > 0) {
|
|
6301
|
+
setPages(cached);
|
|
6302
|
+
return;
|
|
6303
|
+
}
|
|
6304
|
+
refetch().catch(() => undefined);
|
|
6305
|
+
};
|
|
6306
|
+
runIfEnabled();
|
|
6307
|
+
if (typeof options.enabled !== 'boolean' && options.enabled) {
|
|
6308
|
+
enabledWatcher = setInterval(runIfEnabled, 100);
|
|
6309
|
+
}
|
|
6310
|
+
if (destroyRef) {
|
|
6311
|
+
destroyRef.onDestroy(() => {
|
|
6312
|
+
if (enabledWatcher) {
|
|
6313
|
+
clearInterval(enabledWatcher);
|
|
6314
|
+
enabledWatcher = null;
|
|
6315
|
+
}
|
|
6316
|
+
});
|
|
6317
|
+
}
|
|
6318
|
+
return {
|
|
6319
|
+
pages: computed(() => pagesSignal()),
|
|
6320
|
+
error: computed(() => errorSignal()),
|
|
6321
|
+
isLoading: computed(() => isLoadingSignal()),
|
|
6322
|
+
isFetching: computed(() => isFetchingSignal()),
|
|
6323
|
+
isFetchingNextPage: computed(() => isFetchingNextPageSignal()),
|
|
6324
|
+
hasNextPage: computed(() => hasNextPageSignal()),
|
|
6325
|
+
refetch,
|
|
6326
|
+
fetchNextPage,
|
|
6327
|
+
};
|
|
6328
|
+
}
|
|
6329
|
+
function createInfiniteQuery(queryKey, queryFn, options) {
|
|
6330
|
+
return spInfiniteQuery({
|
|
6331
|
+
...options,
|
|
6332
|
+
queryKey,
|
|
6333
|
+
queryFn,
|
|
6334
|
+
});
|
|
6335
|
+
}
|
|
6336
|
+
|
|
6076
6337
|
/**
|
|
6077
6338
|
* Public API Surface of ngx-signal-plus
|
|
6078
6339
|
*
|
|
@@ -6085,5 +6346,5 @@ function createQuery(queryKey, queryFn, options) {
|
|
|
6085
6346
|
* Generated bundle index. Do not edit.
|
|
6086
6347
|
*/
|
|
6087
6348
|
|
|
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 };
|
|
6349
|
+
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
6350
|
//# sourceMappingURL=ngx-signal-plus.mjs.map
|