ngx-signal-plus 2.2.0 → 2.4.1
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 +27 -1
- package/fesm2022/ngx-signal-plus.mjs +185 -3
- package/fesm2022/ngx-signal-plus.mjs.map +1 -1
- package/index.d.ts +71 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -382,6 +382,32 @@ const darkMode = spPresets.toggle({
|
|
|
382
382
|
});
|
|
383
383
|
```
|
|
384
384
|
|
|
385
|
+
### Schema Validation (Zod/Yup/Joi)
|
|
386
|
+
|
|
387
|
+
Use any schema validation library with signals:
|
|
388
|
+
|
|
389
|
+
```typescript
|
|
390
|
+
import { sp, spSchema, spSchemaValidator } from "ngx-signal-plus";
|
|
391
|
+
import { z } from "zod";
|
|
392
|
+
|
|
393
|
+
const userSchema = z.object({
|
|
394
|
+
name: z.string().min(1),
|
|
395
|
+
email: z.string().email(),
|
|
396
|
+
age: z.number().min(18),
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
// Basic boolean validation with SignalBuilder
|
|
400
|
+
const user = sp({ name: "", email: "", age: 0 }).validate(spSchema(userSchema)).build();
|
|
401
|
+
|
|
402
|
+
// Advanced: Get detailed error messages
|
|
403
|
+
const validator = spSchemaValidator(userSchema);
|
|
404
|
+
const result = validator.validateWithErrors({ name: "", email: "invalid", age: 10 });
|
|
405
|
+
// result: { valid: false, errors: ["name: String must contain at least 1 character(s)", "email: Invalid email", "age: Number must be greater than or equal to 18"] }
|
|
406
|
+
|
|
407
|
+
// Use with SignalBuilder for boolean validation
|
|
408
|
+
const validatedSignal = sp({ name: "", email: "", age: 0 }).validate(validator.validate).build();
|
|
409
|
+
```
|
|
410
|
+
|
|
385
411
|
### Middleware/Plugin System
|
|
386
412
|
|
|
387
413
|
Intercept signal operations for logging, analytics, and error tracking:
|
|
@@ -533,7 +559,7 @@ What happens during SSR:
|
|
|
533
559
|
| **Async State Management** | `spAsync` - Manage asynchronous operations with loading, error, retry, and caching |
|
|
534
560
|
| **Collection Management** | `spCollection` - Manage arrays of entities with ID-based CRUD, queries, transforms, and history |
|
|
535
561
|
| **Transactions & Batching** | `spTransaction`, `spBatch`, `spIsTransactionActive`, `spIsInTransaction`, `spIsInBatch`, `spGetModifiedSignals` |
|
|
536
|
-
| **Utilities** | `spValidators`, `spPresets`
|
|
562
|
+
| **Utilities** | `spValidators`, `spPresets`, `spSchema`, `spSchemaValidator` |
|
|
537
563
|
| **Middleware/Plugins** | `spUseMiddleware`, `spRemoveMiddleware`, `spLoggerMiddleware`, `spAnalyticsMiddleware` |
|
|
538
564
|
| **State Management** | `spHistoryManager`, `spStorageManager` |
|
|
539
565
|
| **Components** | `spSignalPlusComponent`, `spSignalPlusService`, `spSignalBuilder` |
|
|
@@ -1342,6 +1342,106 @@ class SignalBuilder {
|
|
|
1342
1342
|
}
|
|
1343
1343
|
}
|
|
1344
1344
|
|
|
1345
|
+
var SpErrorCode;
|
|
1346
|
+
(function (SpErrorCode) {
|
|
1347
|
+
SpErrorCode["INIT_001"] = "INIT_001";
|
|
1348
|
+
SpErrorCode["VAL_001"] = "VAL_001";
|
|
1349
|
+
SpErrorCode["VAL_002"] = "VAL_002";
|
|
1350
|
+
SpErrorCode["TRX_001"] = "TRX_001";
|
|
1351
|
+
SpErrorCode["TRX_002"] = "TRX_002";
|
|
1352
|
+
SpErrorCode["STOR_001"] = "STOR_001";
|
|
1353
|
+
SpErrorCode["STOR_002"] = "STOR_002";
|
|
1354
|
+
SpErrorCode["HIST_001"] = "HIST_001";
|
|
1355
|
+
SpErrorCode["HIST_002"] = "HIST_002";
|
|
1356
|
+
})(SpErrorCode || (SpErrorCode = {}));
|
|
1357
|
+
|
|
1358
|
+
const SP_ERRORS = {
|
|
1359
|
+
[SpErrorCode.INIT_001]: {
|
|
1360
|
+
code: SpErrorCode.INIT_001,
|
|
1361
|
+
message: 'Initial value cannot be undefined',
|
|
1362
|
+
suggestion: 'Provide a defined initial value when creating a signal',
|
|
1363
|
+
},
|
|
1364
|
+
[SpErrorCode.VAL_001]: {
|
|
1365
|
+
code: SpErrorCode.VAL_001,
|
|
1366
|
+
message: 'Validation failed',
|
|
1367
|
+
suggestion: 'Check the value against the validator requirements',
|
|
1368
|
+
},
|
|
1369
|
+
[SpErrorCode.VAL_002]: {
|
|
1370
|
+
code: SpErrorCode.VAL_002,
|
|
1371
|
+
message: 'Invalid email format',
|
|
1372
|
+
suggestion: 'Ensure the email includes "@" and a valid domain (e.g., user@example.com)',
|
|
1373
|
+
},
|
|
1374
|
+
[SpErrorCode.TRX_001]: {
|
|
1375
|
+
code: SpErrorCode.TRX_001,
|
|
1376
|
+
message: 'Nested transactions are not allowed',
|
|
1377
|
+
suggestion: 'Complete the current transaction before starting a new one',
|
|
1378
|
+
},
|
|
1379
|
+
[SpErrorCode.TRX_002]: {
|
|
1380
|
+
code: SpErrorCode.TRX_002,
|
|
1381
|
+
message: 'Transaction rollback failed',
|
|
1382
|
+
suggestion: 'Check that all signals in the transaction support rollback',
|
|
1383
|
+
},
|
|
1384
|
+
[SpErrorCode.STOR_001]: {
|
|
1385
|
+
code: SpErrorCode.STOR_001,
|
|
1386
|
+
message: 'Failed to save to storage',
|
|
1387
|
+
suggestion: 'Check storage quota and browser permissions',
|
|
1388
|
+
},
|
|
1389
|
+
[SpErrorCode.STOR_002]: {
|
|
1390
|
+
code: SpErrorCode.STOR_002,
|
|
1391
|
+
message: 'Failed to load from storage',
|
|
1392
|
+
suggestion: 'Verify the storage key exists and data is valid JSON',
|
|
1393
|
+
},
|
|
1394
|
+
[SpErrorCode.HIST_001]: {
|
|
1395
|
+
code: SpErrorCode.HIST_001,
|
|
1396
|
+
message: 'Cannot undo - no history available',
|
|
1397
|
+
suggestion: 'Ensure history is enabled and changes have been made',
|
|
1398
|
+
},
|
|
1399
|
+
[SpErrorCode.HIST_002]: {
|
|
1400
|
+
code: SpErrorCode.HIST_002,
|
|
1401
|
+
message: 'Cannot redo - no future states available',
|
|
1402
|
+
suggestion: 'Redo is only available after an undo operation',
|
|
1403
|
+
},
|
|
1404
|
+
};
|
|
1405
|
+
class SpError extends Error {
|
|
1406
|
+
code;
|
|
1407
|
+
context;
|
|
1408
|
+
suggestion;
|
|
1409
|
+
constructor(code, context, customMessage) {
|
|
1410
|
+
const errorInfo = SP_ERRORS[code];
|
|
1411
|
+
const message = customMessage || errorInfo.message;
|
|
1412
|
+
const formattedMessage = formatSpError(code, message, context, errorInfo.suggestion);
|
|
1413
|
+
super(formattedMessage);
|
|
1414
|
+
this.name = 'SpError';
|
|
1415
|
+
this.code = code;
|
|
1416
|
+
this.context = context;
|
|
1417
|
+
this.suggestion = errorInfo.suggestion;
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
function formatSpError(code, message, context, suggestion) {
|
|
1421
|
+
let result = `[SP-${code}] ${message}`;
|
|
1422
|
+
if (context) {
|
|
1423
|
+
if (context.signalName) {
|
|
1424
|
+
result += `\n Signal: '${context.signalName}'`;
|
|
1425
|
+
}
|
|
1426
|
+
if (context.validatorName) {
|
|
1427
|
+
result += `\n Validator: ${context.validatorName}`;
|
|
1428
|
+
}
|
|
1429
|
+
if (context.currentValue !== undefined) {
|
|
1430
|
+
result += `\n Current value: ${JSON.stringify(context.currentValue)}`;
|
|
1431
|
+
}
|
|
1432
|
+
if (context.expectedValue) {
|
|
1433
|
+
result += `\n Expected: ${context.expectedValue}`;
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
if (suggestion) {
|
|
1437
|
+
result += `\n Suggestion: ${suggestion}`;
|
|
1438
|
+
}
|
|
1439
|
+
return result;
|
|
1440
|
+
}
|
|
1441
|
+
function spCreateError(code, context, customMessage) {
|
|
1442
|
+
return new SpError(code, context, customMessage);
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1345
1445
|
/**
|
|
1346
1446
|
* @fileoverview Utility functions for creating enhanced signals
|
|
1347
1447
|
* @description
|
|
@@ -1415,7 +1515,7 @@ class SignalBuilder {
|
|
|
1415
1515
|
*/
|
|
1416
1516
|
function sp(initialValue) {
|
|
1417
1517
|
if (initialValue === undefined) {
|
|
1418
|
-
throw
|
|
1518
|
+
throw spCreateError(SpErrorCode.INIT_001);
|
|
1419
1519
|
}
|
|
1420
1520
|
return new SignalBuilder(initialValue);
|
|
1421
1521
|
}
|
|
@@ -4423,6 +4523,88 @@ function spAnalyticsMiddleware(tracker) {
|
|
|
4423
4523
|
};
|
|
4424
4524
|
}
|
|
4425
4525
|
|
|
4526
|
+
function extractErrorMessages(error) {
|
|
4527
|
+
if (!error) {
|
|
4528
|
+
return [];
|
|
4529
|
+
}
|
|
4530
|
+
if ('issues' in error && Array.isArray(error.issues)) {
|
|
4531
|
+
return error.issues.map((issue) => {
|
|
4532
|
+
const path = issue.path?.join('.') || '';
|
|
4533
|
+
return path ? `${path}: ${issue.message}` : issue.message;
|
|
4534
|
+
});
|
|
4535
|
+
}
|
|
4536
|
+
if ('errors' in error && Array.isArray(error.errors)) {
|
|
4537
|
+
return error.errors.map((e) => {
|
|
4538
|
+
const path = e.path?.join('.') || '';
|
|
4539
|
+
return path ? `${path}: ${e.message}` : e.message;
|
|
4540
|
+
});
|
|
4541
|
+
}
|
|
4542
|
+
if (error.message) {
|
|
4543
|
+
return [error.message];
|
|
4544
|
+
}
|
|
4545
|
+
return ['Validation failed'];
|
|
4546
|
+
}
|
|
4547
|
+
function spSchema(schema) {
|
|
4548
|
+
return (value) => {
|
|
4549
|
+
if ('safeParse' in schema) {
|
|
4550
|
+
return schema.safeParse(value).success;
|
|
4551
|
+
}
|
|
4552
|
+
try {
|
|
4553
|
+
schema.parse(value);
|
|
4554
|
+
return true;
|
|
4555
|
+
}
|
|
4556
|
+
catch {
|
|
4557
|
+
return false;
|
|
4558
|
+
}
|
|
4559
|
+
};
|
|
4560
|
+
}
|
|
4561
|
+
function spSchemaWithErrors(schema) {
|
|
4562
|
+
return (value) => {
|
|
4563
|
+
const result = schema.safeParse(value);
|
|
4564
|
+
if (result.success) {
|
|
4565
|
+
return { valid: true, errors: [] };
|
|
4566
|
+
}
|
|
4567
|
+
return {
|
|
4568
|
+
valid: false,
|
|
4569
|
+
errors: extractErrorMessages(result.error),
|
|
4570
|
+
};
|
|
4571
|
+
};
|
|
4572
|
+
}
|
|
4573
|
+
function spSchemaValidator(schema) {
|
|
4574
|
+
const validate = (value) => {
|
|
4575
|
+
if ('safeParse' in schema) {
|
|
4576
|
+
return schema.safeParse(value).success;
|
|
4577
|
+
}
|
|
4578
|
+
try {
|
|
4579
|
+
schema.parse(value);
|
|
4580
|
+
return true;
|
|
4581
|
+
}
|
|
4582
|
+
catch {
|
|
4583
|
+
return false;
|
|
4584
|
+
}
|
|
4585
|
+
};
|
|
4586
|
+
const validateWithErrors = (value) => {
|
|
4587
|
+
if ('safeParse' in schema) {
|
|
4588
|
+
const result = schema.safeParse(value);
|
|
4589
|
+
if (result.success) {
|
|
4590
|
+
return { valid: true, errors: [] };
|
|
4591
|
+
}
|
|
4592
|
+
return { valid: false, errors: extractErrorMessages(result.error) };
|
|
4593
|
+
}
|
|
4594
|
+
try {
|
|
4595
|
+
schema.parse(value);
|
|
4596
|
+
return { valid: true, errors: [] };
|
|
4597
|
+
}
|
|
4598
|
+
catch (error) {
|
|
4599
|
+
if (error && typeof error === 'object') {
|
|
4600
|
+
return { valid: false, errors: extractErrorMessages(error) };
|
|
4601
|
+
}
|
|
4602
|
+
return { valid: false, errors: ['Validation failed'] };
|
|
4603
|
+
}
|
|
4604
|
+
};
|
|
4605
|
+
return { validate, validateWithErrors };
|
|
4606
|
+
}
|
|
4607
|
+
|
|
4426
4608
|
/**
|
|
4427
4609
|
* @fileoverview Transaction and batching utilities for ngx-signal-plus
|
|
4428
4610
|
* @description Provides functionality for atomic operations and batched updates
|
|
@@ -4672,7 +4854,7 @@ function spTransaction(fn) {
|
|
|
4672
4854
|
const txState = state.transaction;
|
|
4673
4855
|
// Prevent nested transactions
|
|
4674
4856
|
if (txState.active) {
|
|
4675
|
-
throw
|
|
4857
|
+
throw spCreateError(SpErrorCode.TRX_001);
|
|
4676
4858
|
}
|
|
4677
4859
|
// Initialize transaction state
|
|
4678
4860
|
const transactionStartTime = new Date();
|
|
@@ -5809,5 +5991,5 @@ function createQuery(queryKey, queryFn, options) {
|
|
|
5809
5991
|
* Generated bundle index. Do not edit.
|
|
5810
5992
|
*/
|
|
5811
5993
|
|
|
5812
|
-
export { QueryClient, createMutation, createQuery, enhance, getGlobalQueryClient, setGlobalQueryClient, sp, spAnalyticsMiddleware, spAsync, spBatch, spClearMiddleware, spCollection, combineLatest as spCombineLatest, spComputed, spCounter, debounceTime as spDebounceTime, delay$1 as spDelay, distinctUntilChanged as spDistinctUntilChanged, 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, 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 };
|
|
5994
|
+
export { QueryClient, SP_ERRORS, SpError, createMutation, createQuery, enhance, formatSpError, getGlobalQueryClient, setGlobalQueryClient, sp, spAnalyticsMiddleware, spAsync, spBatch, spClearMiddleware, spCollection, combineLatest as spCombineLatest, spComputed, spCounter, spCreateError, debounceTime as spDebounceTime, delay$1 as spDelay, distinctUntilChanged as spDistinctUntilChanged, 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 };
|
|
5813
5995
|
//# sourceMappingURL=ngx-signal-plus.mjs.map
|