@signaltree/core 4.0.15 → 4.1.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.
Files changed (109) hide show
  1. package/README.md +106 -38
  2. package/dist/constants.js +6 -0
  3. package/dist/deep-clone.js +80 -0
  4. package/dist/deep-equal.js +41 -0
  5. package/dist/enhancers/batching/lib/batching.js +141 -135
  6. package/dist/enhancers/computed/lib/computed.js +18 -16
  7. package/dist/enhancers/devtools/lib/devtools.js +303 -260
  8. package/dist/enhancers/entities/lib/entities.js +109 -104
  9. package/dist/enhancers/index.js +65 -77
  10. package/dist/enhancers/memoization/lib/memoization.js +339 -351
  11. package/dist/enhancers/middleware/lib/async-helpers.js +71 -79
  12. package/dist/enhancers/middleware/lib/middleware.js +126 -169
  13. package/dist/enhancers/presets/lib/presets.js +82 -71
  14. package/dist/enhancers/serialization/constants.js +14 -13
  15. package/dist/enhancers/serialization/lib/serialization.js +615 -623
  16. package/dist/enhancers/time-travel/lib/time-travel.js +178 -177
  17. package/dist/index.d.ts +1 -17
  18. package/dist/index.js +19 -16
  19. package/dist/is-built-in-object.js +23 -0
  20. package/dist/lib/constants.js +51 -55
  21. package/dist/lib/memory/memory-manager.js +152 -154
  22. package/dist/lib/performance/diff-engine.js +141 -141
  23. package/dist/lib/performance/path-index.js +139 -137
  24. package/dist/lib/performance/update-engine.js +171 -176
  25. package/dist/lib/security/security-validator.js +110 -128
  26. package/dist/lib/signal-tree.js +577 -611
  27. package/dist/lib/types.js +3 -9
  28. package/dist/lib/utils.js +236 -268
  29. package/dist/lru-cache.js +64 -0
  30. package/dist/parse-path.js +13 -0
  31. package/package.json +30 -16
  32. package/src/index.d.ts +17 -0
  33. package/{dist → src}/lib/utils.d.ts +1 -0
  34. package/dist/enhancers/batching/index.js +0 -1
  35. package/dist/enhancers/batching/jest.config.js +0 -21
  36. package/dist/enhancers/batching/test-setup.js +0 -5
  37. package/dist/enhancers/computed/index.js +0 -1
  38. package/dist/enhancers/computed/jest.config.js +0 -21
  39. package/dist/enhancers/devtools/index.js +0 -1
  40. package/dist/enhancers/devtools/jest.config.js +0 -21
  41. package/dist/enhancers/devtools/test-setup.js +0 -5
  42. package/dist/enhancers/entities/index.js +0 -1
  43. package/dist/enhancers/entities/jest.config.js +0 -21
  44. package/dist/enhancers/entities/test-setup.js +0 -5
  45. package/dist/enhancers/memoization/index.js +0 -1
  46. package/dist/enhancers/memoization/jest.config.js +0 -21
  47. package/dist/enhancers/memoization/test-setup.js +0 -5
  48. package/dist/enhancers/middleware/index.js +0 -2
  49. package/dist/enhancers/middleware/jest.config.js +0 -21
  50. package/dist/enhancers/middleware/test-setup.js +0 -5
  51. package/dist/enhancers/presets/index.js +0 -1
  52. package/dist/enhancers/presets/jest.config.js +0 -21
  53. package/dist/enhancers/presets/test-setup.js +0 -5
  54. package/dist/enhancers/serialization/index.js +0 -2
  55. package/dist/enhancers/serialization/jest.config.js +0 -21
  56. package/dist/enhancers/serialization/test-setup.js +0 -5
  57. package/dist/enhancers/time-travel/index.js +0 -1
  58. package/dist/enhancers/time-travel/jest.config.js +0 -21
  59. package/dist/enhancers/time-travel/lib/utils.js +0 -1
  60. package/dist/enhancers/time-travel/test-setup.js +0 -5
  61. package/dist/enhancers/types.js +0 -0
  62. /package/{dist → src}/enhancers/batching/index.d.ts +0 -0
  63. /package/{dist → src}/enhancers/batching/jest.config.d.ts +0 -0
  64. /package/{dist → src}/enhancers/batching/lib/batching.d.ts +0 -0
  65. /package/{dist → src}/enhancers/batching/test-setup.d.ts +0 -0
  66. /package/{dist → src}/enhancers/computed/index.d.ts +0 -0
  67. /package/{dist → src}/enhancers/computed/jest.config.d.ts +0 -0
  68. /package/{dist → src}/enhancers/computed/lib/computed.d.ts +0 -0
  69. /package/{dist → src}/enhancers/devtools/index.d.ts +0 -0
  70. /package/{dist → src}/enhancers/devtools/jest.config.d.ts +0 -0
  71. /package/{dist → src}/enhancers/devtools/lib/devtools.d.ts +0 -0
  72. /package/{dist → src}/enhancers/devtools/test-setup.d.ts +0 -0
  73. /package/{dist → src}/enhancers/entities/index.d.ts +0 -0
  74. /package/{dist → src}/enhancers/entities/jest.config.d.ts +0 -0
  75. /package/{dist → src}/enhancers/entities/lib/entities.d.ts +0 -0
  76. /package/{dist → src}/enhancers/entities/test-setup.d.ts +0 -0
  77. /package/{dist → src}/enhancers/index.d.ts +0 -0
  78. /package/{dist → src}/enhancers/memoization/index.d.ts +0 -0
  79. /package/{dist → src}/enhancers/memoization/jest.config.d.ts +0 -0
  80. /package/{dist → src}/enhancers/memoization/lib/memoization.d.ts +0 -0
  81. /package/{dist → src}/enhancers/memoization/test-setup.d.ts +0 -0
  82. /package/{dist → src}/enhancers/middleware/index.d.ts +0 -0
  83. /package/{dist → src}/enhancers/middleware/jest.config.d.ts +0 -0
  84. /package/{dist → src}/enhancers/middleware/lib/async-helpers.d.ts +0 -0
  85. /package/{dist → src}/enhancers/middleware/lib/middleware.d.ts +0 -0
  86. /package/{dist → src}/enhancers/middleware/test-setup.d.ts +0 -0
  87. /package/{dist → src}/enhancers/presets/index.d.ts +0 -0
  88. /package/{dist → src}/enhancers/presets/jest.config.d.ts +0 -0
  89. /package/{dist → src}/enhancers/presets/lib/presets.d.ts +0 -0
  90. /package/{dist → src}/enhancers/presets/test-setup.d.ts +0 -0
  91. /package/{dist → src}/enhancers/serialization/constants.d.ts +0 -0
  92. /package/{dist → src}/enhancers/serialization/index.d.ts +0 -0
  93. /package/{dist → src}/enhancers/serialization/jest.config.d.ts +0 -0
  94. /package/{dist → src}/enhancers/serialization/lib/serialization.d.ts +0 -0
  95. /package/{dist → src}/enhancers/serialization/test-setup.d.ts +0 -0
  96. /package/{dist → src}/enhancers/time-travel/index.d.ts +0 -0
  97. /package/{dist → src}/enhancers/time-travel/jest.config.d.ts +0 -0
  98. /package/{dist → src}/enhancers/time-travel/lib/time-travel.d.ts +0 -0
  99. /package/{dist → src}/enhancers/time-travel/lib/utils.d.ts +0 -0
  100. /package/{dist → src}/enhancers/time-travel/test-setup.d.ts +0 -0
  101. /package/{dist → src}/enhancers/types.d.ts +0 -0
  102. /package/{dist → src}/lib/constants.d.ts +0 -0
  103. /package/{dist → src}/lib/memory/memory-manager.d.ts +0 -0
  104. /package/{dist → src}/lib/performance/diff-engine.d.ts +0 -0
  105. /package/{dist → src}/lib/performance/path-index.d.ts +0 -0
  106. /package/{dist → src}/lib/performance/update-engine.d.ts +0 -0
  107. /package/{dist → src}/lib/security/security-validator.d.ts +0 -0
  108. /package/{dist → src}/lib/signal-tree.d.ts +0 -0
  109. /package/{dist → src}/lib/types.d.ts +0 -0
@@ -1,139 +1,121 @@
1
1
  const DANGEROUS_KEYS = ['__proto__', 'constructor', 'prototype'];
2
2
  const HTML_TAG_PATTERN = /<[^>]*>/g;
3
- const DANGEROUS_HTML_PATTERNS = [
4
- /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
5
- /on\w+\s*=/gi,
6
- /javascript:/gi,
7
- /<iframe\b/gi,
8
- /<object\b/gi,
9
- /<embed\b/gi,
10
- ];
11
- export class SecurityValidator {
12
- config;
13
- dangerousKeys;
14
- constructor(config = {}) {
15
- this.config = {
16
- preventPrototypePollution: config.preventPrototypePollution ?? true,
17
- preventXSS: config.preventXSS ?? false,
18
- preventFunctions: config.preventFunctions ?? true,
19
- customDangerousKeys: config.customDangerousKeys ?? [],
20
- onSecurityEvent: config.onSecurityEvent ??
21
- (() => {
22
- }),
23
- sanitizationMode: config.sanitizationMode ?? 'strict',
24
- };
25
- this.dangerousKeys = new Set([
26
- ...DANGEROUS_KEYS,
27
- ...this.config.customDangerousKeys,
28
- ]);
3
+ const DANGEROUS_HTML_PATTERNS = [/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, /on\w+\s*=/gi, /javascript:/gi, /<iframe\b/gi, /<object\b/gi, /<embed\b/gi];
4
+ class SecurityValidator {
5
+ config;
6
+ dangerousKeys;
7
+ constructor(config = {}) {
8
+ this.config = {
9
+ preventPrototypePollution: config.preventPrototypePollution ?? true,
10
+ preventXSS: config.preventXSS ?? false,
11
+ preventFunctions: config.preventFunctions ?? true,
12
+ customDangerousKeys: config.customDangerousKeys ?? [],
13
+ onSecurityEvent: config.onSecurityEvent ?? (() => {}),
14
+ sanitizationMode: config.sanitizationMode ?? 'strict'
15
+ };
16
+ this.dangerousKeys = new Set([...DANGEROUS_KEYS, ...this.config.customDangerousKeys]);
17
+ }
18
+ validateKey(key) {
19
+ if (!this.config.preventPrototypePollution) {
20
+ return;
29
21
  }
30
- validateKey(key) {
31
- if (!this.config.preventPrototypePollution) {
32
- return;
33
- }
34
- if (this.dangerousKeys.has(key)) {
35
- const event = {
36
- type: 'dangerous-key-blocked',
37
- key,
38
- reason: `Dangerous key "${key}" blocked to prevent prototype pollution`,
39
- timestamp: Date.now(),
40
- };
41
- this.config.onSecurityEvent(event);
42
- throw new Error(`[SignalTree Security] Dangerous key "${key}" is not allowed. ` +
43
- `This key can lead to prototype pollution attacks. ` +
44
- `Blocked keys: ${Array.from(this.dangerousKeys).join(', ')}`);
45
- }
22
+ if (this.dangerousKeys.has(key)) {
23
+ const event = {
24
+ type: 'dangerous-key-blocked',
25
+ key,
26
+ reason: `Dangerous key "${key}" blocked to prevent prototype pollution`,
27
+ timestamp: Date.now()
28
+ };
29
+ this.config.onSecurityEvent(event);
30
+ throw new Error(`[SignalTree Security] Dangerous key "${key}" is not allowed. ` + `This key can lead to prototype pollution attacks. ` + `Blocked keys: ${Array.from(this.dangerousKeys).join(', ')}`);
46
31
  }
47
- validateValue(value) {
48
- if (this.config.preventFunctions && typeof value === 'function') {
49
- const event = {
50
- type: 'function-value-blocked',
51
- value,
52
- reason: 'Function values are not allowed - state must be serializable',
53
- timestamp: Date.now(),
54
- };
55
- this.config.onSecurityEvent(event);
56
- throw new Error(`[SignalTree Security] Function values are not allowed in state trees. ` +
57
- `Functions cannot be serialized, breaking features like time-travel, ` +
58
- `persistence, debugging, and SSR. ` +
59
- `\n\nTo fix this:` +
60
- `\n - Store function references outside the tree` +
61
- `\n - Use method names (strings) and a function registry` +
62
- `\n - Use computed signals for derived values` +
63
- `\n\nBlocked value: ${value.toString().substring(0, 100)}...`);
64
- }
65
- if (!this.config.preventXSS || typeof value !== 'string') {
66
- return value;
67
- }
68
- let hasDangerousPattern = false;
69
- for (const pattern of DANGEROUS_HTML_PATTERNS) {
70
- pattern.lastIndex = 0;
71
- if (pattern.test(value)) {
72
- hasDangerousPattern = true;
73
- break;
74
- }
75
- }
76
- if (hasDangerousPattern) {
77
- const event = {
78
- type: 'xss-attempt-blocked',
79
- value,
80
- reason: 'Dangerous HTML pattern detected and sanitized',
81
- timestamp: Date.now(),
82
- };
83
- this.config.onSecurityEvent(event);
84
- return this.sanitize(value);
85
- }
86
- return this.sanitize(value);
32
+ }
33
+ validateValue(value) {
34
+ if (this.config.preventFunctions && typeof value === 'function') {
35
+ const event = {
36
+ type: 'function-value-blocked',
37
+ value,
38
+ reason: 'Function values are not allowed - state must be serializable',
39
+ timestamp: Date.now()
40
+ };
41
+ this.config.onSecurityEvent(event);
42
+ throw new Error(`[SignalTree Security] Function values are not allowed in state trees. ` + `Functions cannot be serialized, breaking features like time-travel, ` + `persistence, debugging, and SSR. ` + `\n\nTo fix this:` + `\n - Store function references outside the tree` + `\n - Use method names (strings) and a function registry` + `\n - Use computed signals for derived values` + `\n\nBlocked value: ${value.toString().substring(0, 100)}...`);
87
43
  }
88
- sanitize(value) {
89
- if (this.config.sanitizationMode === 'strict') {
90
- let sanitized = value;
91
- for (const pattern of DANGEROUS_HTML_PATTERNS) {
92
- pattern.lastIndex = 0;
93
- sanitized = sanitized.replace(pattern, '');
94
- }
95
- return sanitized.replace(HTML_TAG_PATTERN, '');
96
- }
97
- else {
98
- let sanitized = value;
99
- for (const pattern of DANGEROUS_HTML_PATTERNS) {
100
- pattern.lastIndex = 0;
101
- sanitized = sanitized.replace(pattern, '');
102
- }
103
- return sanitized;
104
- }
44
+ if (!this.config.preventXSS || typeof value !== 'string') {
45
+ return value;
105
46
  }
106
- validateKeyValue(key, value) {
107
- this.validateKey(key);
108
- return this.validateValue(value);
47
+ let hasDangerousPattern = false;
48
+ for (const pattern of DANGEROUS_HTML_PATTERNS) {
49
+ pattern.lastIndex = 0;
50
+ if (pattern.test(value)) {
51
+ hasDangerousPattern = true;
52
+ break;
53
+ }
109
54
  }
110
- isDangerousKey(key) {
111
- return this.dangerousKeys.has(key);
55
+ if (hasDangerousPattern) {
56
+ const event = {
57
+ type: 'xss-attempt-blocked',
58
+ value,
59
+ reason: 'Dangerous HTML pattern detected and sanitized',
60
+ timestamp: Date.now()
61
+ };
62
+ this.config.onSecurityEvent(event);
63
+ return this.sanitize(value);
112
64
  }
113
- getConfig() {
114
- return { ...this.config };
65
+ return this.sanitize(value);
66
+ }
67
+ sanitize(value) {
68
+ if (this.config.sanitizationMode === 'strict') {
69
+ let sanitized = value;
70
+ for (const pattern of DANGEROUS_HTML_PATTERNS) {
71
+ pattern.lastIndex = 0;
72
+ sanitized = sanitized.replace(pattern, '');
73
+ }
74
+ return sanitized.replace(HTML_TAG_PATTERN, '');
75
+ } else {
76
+ let sanitized = value;
77
+ for (const pattern of DANGEROUS_HTML_PATTERNS) {
78
+ pattern.lastIndex = 0;
79
+ sanitized = sanitized.replace(pattern, '');
80
+ }
81
+ return sanitized;
115
82
  }
83
+ }
84
+ validateKeyValue(key, value) {
85
+ this.validateKey(key);
86
+ return this.validateValue(value);
87
+ }
88
+ isDangerousKey(key) {
89
+ return this.dangerousKeys.has(key);
90
+ }
91
+ getConfig() {
92
+ return {
93
+ ...this.config
94
+ };
95
+ }
116
96
  }
117
- export const SecurityPresets = {
118
- strict: () => new SecurityValidator({
119
- preventPrototypePollution: true,
120
- preventXSS: true,
121
- preventFunctions: true,
122
- sanitizationMode: 'strict',
123
- }),
124
- standard: () => new SecurityValidator({
125
- preventPrototypePollution: true,
126
- preventXSS: false,
127
- preventFunctions: true,
128
- }),
129
- permissive: () => new SecurityValidator({
130
- preventPrototypePollution: true,
131
- preventXSS: false,
132
- preventFunctions: false,
133
- }),
134
- disabled: () => new SecurityValidator({
135
- preventPrototypePollution: false,
136
- preventXSS: false,
137
- preventFunctions: false,
138
- }),
97
+ const SecurityPresets = {
98
+ strict: () => new SecurityValidator({
99
+ preventPrototypePollution: true,
100
+ preventXSS: true,
101
+ preventFunctions: true,
102
+ sanitizationMode: 'strict'
103
+ }),
104
+ standard: () => new SecurityValidator({
105
+ preventPrototypePollution: true,
106
+ preventXSS: false,
107
+ preventFunctions: true
108
+ }),
109
+ permissive: () => new SecurityValidator({
110
+ preventPrototypePollution: true,
111
+ preventXSS: false,
112
+ preventFunctions: false
113
+ }),
114
+ disabled: () => new SecurityValidator({
115
+ preventPrototypePollution: false,
116
+ preventXSS: false,
117
+ preventFunctions: false
118
+ })
139
119
  };
120
+
121
+ export { SecurityPresets, SecurityValidator };