@signaltree/core 7.1.4 → 7.1.6

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 (99) hide show
  1. package/dist/constants.js +6 -0
  2. package/dist/deep-equal.js +41 -0
  3. package/dist/enhancers/batching/batching.js +230 -0
  4. package/dist/enhancers/devtools/devtools.js +318 -0
  5. package/dist/enhancers/effects/effects.js +66 -0
  6. package/dist/enhancers/entities/entities.js +7 -0
  7. package/dist/enhancers/index.js +72 -0
  8. package/dist/enhancers/memoization/memoization.js +420 -0
  9. package/dist/enhancers/presets/lib/presets.js +27 -0
  10. package/dist/enhancers/serialization/constants.js +15 -0
  11. package/dist/enhancers/serialization/serialization.js +656 -0
  12. package/dist/enhancers/time-travel/time-travel.js +283 -0
  13. package/dist/enhancers/time-travel/utils.js +11 -0
  14. package/dist/enhancers/utils/copy-tree-properties.js +20 -0
  15. package/dist/index.js +26 -0
  16. package/dist/is-built-in-object.js +23 -0
  17. package/dist/lib/async-helpers.js +77 -0
  18. package/dist/lib/constants.js +56 -0
  19. package/dist/lib/edit-session.js +84 -0
  20. package/dist/lib/entity-signal.js +544 -0
  21. package/dist/lib/internals/batch-scope.js +8 -0
  22. package/dist/lib/internals/derived-types.js +6 -0
  23. package/dist/lib/internals/materialize-markers.js +72 -0
  24. package/dist/lib/internals/merge-derived.js +59 -0
  25. package/dist/lib/markers/derived.js +6 -0
  26. package/dist/lib/markers/entity-map.js +20 -0
  27. package/dist/lib/markers/status.js +71 -0
  28. package/dist/lib/markers/stored.js +111 -0
  29. package/dist/lib/memory/memory-manager.js +164 -0
  30. package/dist/lib/path-notifier.js +178 -0
  31. package/dist/lib/presets.js +20 -0
  32. package/dist/lib/security/security-validator.js +121 -0
  33. package/dist/lib/signal-tree.js +415 -0
  34. package/dist/lib/types.js +3 -0
  35. package/dist/lib/utils.js +264 -0
  36. package/dist/lru-cache.js +64 -0
  37. package/dist/parse-path.js +13 -0
  38. package/package.json +1 -1
  39. package/src/enhancers/batching/batching.d.ts +10 -0
  40. package/src/enhancers/batching/batching.types.d.ts +1 -0
  41. package/src/enhancers/batching/index.d.ts +1 -0
  42. package/src/enhancers/batching/test-setup.d.ts +3 -0
  43. package/src/enhancers/devtools/devtools.d.ts +68 -0
  44. package/src/enhancers/devtools/devtools.types.d.ts +1 -0
  45. package/src/enhancers/devtools/index.d.ts +1 -0
  46. package/src/enhancers/devtools/test-setup.d.ts +3 -0
  47. package/src/enhancers/effects/effects.d.ts +9 -0
  48. package/src/enhancers/effects/effects.types.d.ts +1 -0
  49. package/src/enhancers/effects/index.d.ts +1 -0
  50. package/src/enhancers/entities/entities.d.ts +7 -0
  51. package/src/enhancers/entities/entities.types.d.ts +1 -0
  52. package/src/enhancers/entities/index.d.ts +1 -0
  53. package/src/enhancers/entities/test-setup.d.ts +3 -0
  54. package/src/enhancers/index.d.ts +3 -0
  55. package/src/enhancers/memoization/index.d.ts +1 -0
  56. package/src/enhancers/memoization/memoization.d.ts +54 -0
  57. package/src/enhancers/memoization/memoization.types.d.ts +1 -0
  58. package/src/enhancers/memoization/test-setup.d.ts +3 -0
  59. package/src/enhancers/presets/index.d.ts +1 -0
  60. package/src/enhancers/presets/lib/presets.d.ts +8 -0
  61. package/src/enhancers/serialization/constants.d.ts +14 -0
  62. package/src/enhancers/serialization/index.d.ts +2 -0
  63. package/src/enhancers/serialization/serialization.d.ts +68 -0
  64. package/src/enhancers/serialization/test-setup.d.ts +3 -0
  65. package/src/enhancers/test-helpers/types-equals.d.ts +2 -0
  66. package/src/enhancers/time-travel/index.d.ts +1 -0
  67. package/src/enhancers/time-travel/test-setup.d.ts +3 -0
  68. package/src/enhancers/time-travel/time-travel.d.ts +10 -0
  69. package/src/enhancers/time-travel/time-travel.types.d.ts +1 -0
  70. package/src/enhancers/time-travel/utils.d.ts +2 -0
  71. package/src/enhancers/types.d.ts +1 -0
  72. package/src/enhancers/typing/helpers-types.d.ts +2 -0
  73. package/src/enhancers/utils/copy-tree-properties.d.ts +1 -0
  74. package/src/index.d.ts +26 -0
  75. package/src/lib/async-helpers.d.ts +8 -0
  76. package/src/lib/constants.d.ts +41 -0
  77. package/src/lib/dev-proxy.d.ts +3 -0
  78. package/src/lib/edit-session.d.ts +21 -0
  79. package/src/lib/entity-signal.d.ts +1 -0
  80. package/src/lib/internals/batch-scope.d.ts +3 -0
  81. package/src/lib/internals/builder-types.d.ts +13 -0
  82. package/src/lib/internals/derived-types.d.ts +19 -0
  83. package/src/lib/internals/materialize-markers.d.ts +5 -0
  84. package/src/lib/internals/merge-derived.d.ts +4 -0
  85. package/src/lib/markers/derived.d.ts +9 -0
  86. package/src/lib/markers/entity-map.d.ts +4 -0
  87. package/src/lib/markers/index.d.ts +3 -0
  88. package/src/lib/markers/status.d.ts +32 -0
  89. package/src/lib/markers/stored.d.ts +23 -0
  90. package/src/lib/memory/memory-manager.d.ts +30 -0
  91. package/src/lib/path-notifier.d.ts +34 -0
  92. package/src/lib/performance/diff-engine.d.ts +33 -0
  93. package/src/lib/performance/path-index.d.ts +25 -0
  94. package/src/lib/performance/update-engine.d.ts +32 -0
  95. package/src/lib/presets.d.ts +34 -0
  96. package/src/lib/security/security-validator.d.ts +33 -0
  97. package/src/lib/signal-tree.d.ts +6 -0
  98. package/src/lib/types.d.ts +300 -0
  99. package/src/lib/utils.d.ts +25 -0
@@ -0,0 +1,6 @@
1
+ const SHARED_DEFAULTS = Object.freeze({
2
+ PATH_CACHE_SIZE: 1000
3
+ });
4
+ const DEFAULT_PATH_CACHE_SIZE = SHARED_DEFAULTS.PATH_CACHE_SIZE;
5
+
6
+ export { DEFAULT_PATH_CACHE_SIZE, SHARED_DEFAULTS };
@@ -0,0 +1,41 @@
1
+ function deepEqual(a, b) {
2
+ if (a === b) return true;
3
+ if (a == null || b == null) return a === b;
4
+ const typeA = typeof a;
5
+ const typeB = typeof b;
6
+ if (typeA !== typeB) return false;
7
+ if (typeA !== 'object') return false;
8
+ if (a instanceof Date && b instanceof Date) {
9
+ return a.getTime() === b.getTime();
10
+ }
11
+ if (a instanceof RegExp && b instanceof RegExp) {
12
+ return a.source === b.source && a.flags === b.flags;
13
+ }
14
+ if (a instanceof Map && b instanceof Map) {
15
+ if (a.size !== b.size) return false;
16
+ for (const [key, value] of a) {
17
+ if (!b.has(key) || !deepEqual(value, b.get(key))) return false;
18
+ }
19
+ return true;
20
+ }
21
+ if (a instanceof Set && b instanceof Set) {
22
+ if (a.size !== b.size) return false;
23
+ for (const value of a) {
24
+ if (!b.has(value)) return false;
25
+ }
26
+ return true;
27
+ }
28
+ if (Array.isArray(a)) {
29
+ if (!Array.isArray(b) || a.length !== b.length) return false;
30
+ return a.every((item, index) => deepEqual(item, b[index]));
31
+ }
32
+ if (Array.isArray(b)) return false;
33
+ const objA = a;
34
+ const objB = b;
35
+ const keysA = Object.keys(objA);
36
+ const keysB = Object.keys(objB);
37
+ if (keysA.length !== keysB.length) return false;
38
+ return keysA.every(key => key in objB && deepEqual(objA[key], objB[key]));
39
+ }
40
+
41
+ export { deepEqual };
@@ -0,0 +1,230 @@
1
+ import { copyTreeProperties } from '../utils/copy-tree-properties.js';
2
+
3
+ function batching(config = {}) {
4
+ const enabled = config.enabled ?? true;
5
+ const notificationDelayMs = config.notificationDelayMs ?? 0;
6
+ return tree => {
7
+ if (!enabled) {
8
+ const passthrough = {
9
+ batch: fn => fn(),
10
+ coalesce: fn => fn(),
11
+ hasPendingNotifications: () => false,
12
+ flushNotifications: () => {}
13
+ };
14
+ const enhanced = tree;
15
+ Object.assign(enhanced, passthrough);
16
+ enhanced.batchUpdate = updater => {
17
+ if (typeof tree.batchUpdate === 'function') {
18
+ tree.batchUpdate(updater);
19
+ } else {
20
+ updater(tree());
21
+ }
22
+ };
23
+ return enhanced;
24
+ }
25
+ let notificationPending = false;
26
+ let notificationTimeoutId;
27
+ let inBatch = false;
28
+ let inCoalesce = false;
29
+ const coalescedUpdates = new Map();
30
+ const scheduleNotification = () => {
31
+ if (notificationPending) return;
32
+ notificationPending = true;
33
+ if (notificationDelayMs > 0) {
34
+ notificationTimeoutId = setTimeout(flushNotificationsInternal, notificationDelayMs);
35
+ } else {
36
+ queueMicrotask(flushNotificationsInternal);
37
+ }
38
+ };
39
+ const flushNotificationsInternal = () => {
40
+ if (!notificationPending) return;
41
+ notificationPending = false;
42
+ if (notificationTimeoutId !== undefined) {
43
+ clearTimeout(notificationTimeoutId);
44
+ notificationTimeoutId = undefined;
45
+ }
46
+ if (tree.__notifyChangeDetection) {
47
+ tree.__notifyChangeDetection();
48
+ }
49
+ };
50
+ const flushCoalescedUpdates = () => {
51
+ const updates = Array.from(coalescedUpdates.values());
52
+ coalescedUpdates.clear();
53
+ updates.forEach(fn => {
54
+ try {
55
+ fn();
56
+ } catch (e) {
57
+ console.error('[SignalTree] Error in coalesced update:', e);
58
+ }
59
+ });
60
+ };
61
+ const wrapSignalSetters = (node, path = '') => {
62
+ if (!node || typeof node !== 'object') return;
63
+ if (typeof node.set === 'function' && !node.__batchingWrapped) {
64
+ const originalSet = node.set.bind(node);
65
+ node.set = value => {
66
+ if (inCoalesce) {
67
+ coalescedUpdates.set(path, () => originalSet(value));
68
+ } else {
69
+ originalSet(value);
70
+ }
71
+ if (!inBatch) {
72
+ scheduleNotification();
73
+ }
74
+ };
75
+ node.__batchingWrapped = true;
76
+ }
77
+ if (typeof node.update === 'function' && !node.__batchingUpdateWrapped) {
78
+ const originalUpdate = node.update.bind(node);
79
+ node.update = updater => {
80
+ if (inCoalesce) {
81
+ coalescedUpdates.set(`${path}:update:${Date.now()}`, () => originalUpdate(updater));
82
+ } else {
83
+ originalUpdate(updater);
84
+ }
85
+ if (!inBatch) {
86
+ scheduleNotification();
87
+ }
88
+ };
89
+ node.__batchingUpdateWrapped = true;
90
+ }
91
+ for (const key of Object.keys(node)) {
92
+ if (key.startsWith('_') || key === 'set' || key === 'update') continue;
93
+ const child = node[key];
94
+ if (child && typeof child === 'object') {
95
+ wrapSignalSetters(child, path ? `${path}.${key}` : key);
96
+ }
97
+ }
98
+ };
99
+ if (tree.$) {
100
+ wrapSignalSetters(tree.$);
101
+ }
102
+ const batchingMethods = {
103
+ batch(fn) {
104
+ const wasBatching = inBatch;
105
+ inBatch = true;
106
+ try {
107
+ fn();
108
+ } finally {
109
+ inBatch = wasBatching;
110
+ if (!inBatch) {
111
+ scheduleNotification();
112
+ }
113
+ }
114
+ },
115
+ coalesce(fn) {
116
+ const wasCoalescing = inCoalesce;
117
+ const wasBatching = inBatch;
118
+ inCoalesce = true;
119
+ inBatch = true;
120
+ try {
121
+ fn();
122
+ } finally {
123
+ inCoalesce = wasCoalescing;
124
+ inBatch = wasBatching;
125
+ if (!wasCoalescing) {
126
+ flushCoalescedUpdates();
127
+ }
128
+ if (!inBatch) {
129
+ scheduleNotification();
130
+ }
131
+ }
132
+ },
133
+ hasPendingNotifications() {
134
+ return notificationPending;
135
+ },
136
+ flushNotifications() {
137
+ flushNotificationsInternal();
138
+ }
139
+ };
140
+ const originalTreeCall = tree.bind(tree);
141
+ const enhancedTree = function (...args) {
142
+ if (args.length === 0) {
143
+ return originalTreeCall();
144
+ } else {
145
+ if (args.length === 1) {
146
+ const arg = args[0];
147
+ if (typeof arg === 'function') {
148
+ originalTreeCall(arg);
149
+ } else {
150
+ originalTreeCall(arg);
151
+ }
152
+ }
153
+ if (!inBatch) {
154
+ scheduleNotification();
155
+ }
156
+ }
157
+ };
158
+ Object.setPrototypeOf(enhancedTree, Object.getPrototypeOf(tree));
159
+ Object.assign(enhancedTree, tree);
160
+ try {
161
+ copyTreeProperties(tree, enhancedTree);
162
+ } catch {}
163
+ Object.defineProperty(enhancedTree, 'with', {
164
+ value: function (enhancer) {
165
+ if (typeof enhancer !== 'function') {
166
+ throw new Error('Enhancer must be a function');
167
+ }
168
+ return enhancer(enhancedTree);
169
+ },
170
+ writable: false,
171
+ enumerable: false,
172
+ configurable: true
173
+ });
174
+ if ('state' in tree) {
175
+ Object.defineProperty(enhancedTree, 'state', {
176
+ value: tree.state,
177
+ enumerable: false,
178
+ configurable: true
179
+ });
180
+ }
181
+ if ('$' in tree) {
182
+ Object.defineProperty(enhancedTree, '$', {
183
+ value: tree.$,
184
+ enumerable: false,
185
+ configurable: true
186
+ });
187
+ }
188
+ Object.assign(enhancedTree, batchingMethods);
189
+ enhancedTree.batchUpdate = updater => {
190
+ enhancedTree.batch(() => {
191
+ const current = originalTreeCall();
192
+ const updates = updater(current);
193
+ Object.entries(updates).forEach(([key, value]) => {
194
+ const property = enhancedTree.state[key];
195
+ if (property && typeof property.set === 'function') {
196
+ property.set(value);
197
+ } else if (typeof property === 'function') {
198
+ property(value);
199
+ }
200
+ });
201
+ });
202
+ };
203
+ return enhancedTree;
204
+ };
205
+ }
206
+ function highPerformanceBatching() {
207
+ return batching({
208
+ enabled: true,
209
+ notificationDelayMs: 0
210
+ });
211
+ }
212
+ function batchingWithConfig(config = {}) {
213
+ return batching(config);
214
+ }
215
+ function flushBatchedUpdates() {
216
+ console.warn('[SignalTree] flushBatchedUpdates() is deprecated. Use tree.flushNotifications() instead.');
217
+ }
218
+ function hasPendingUpdates() {
219
+ console.warn('[SignalTree] hasPendingUpdates() is deprecated. Use tree.hasPendingNotifications() instead.');
220
+ return false;
221
+ }
222
+ function getBatchQueueSize() {
223
+ console.warn('[SignalTree] getBatchQueueSize() is deprecated. Signal writes are now synchronous.');
224
+ return 0;
225
+ }
226
+ Object.assign((config = {}) => batching(config), {
227
+ highPerformance: highPerformanceBatching
228
+ });
229
+
230
+ export { batching, batchingWithConfig, flushBatchedUpdates, getBatchQueueSize, hasPendingUpdates, highPerformanceBatching };
@@ -0,0 +1,318 @@
1
+ import { signal } from '@angular/core';
2
+ import { copyTreeProperties } from '../utils/copy-tree-properties.js';
3
+
4
+ function createActivityTracker() {
5
+ const modules = new Map();
6
+ return {
7
+ trackMethodCall: (module, method, duration) => {
8
+ const existing = modules.get(module);
9
+ if (existing) {
10
+ existing.lastActivity = new Date();
11
+ existing.operationCount++;
12
+ existing.averageExecutionTime = (existing.averageExecutionTime * (existing.operationCount - 1) + duration) / existing.operationCount;
13
+ } else {
14
+ modules.set(module, {
15
+ name: module,
16
+ methods: [method],
17
+ addedAt: new Date(),
18
+ lastActivity: new Date(),
19
+ operationCount: 1,
20
+ averageExecutionTime: duration,
21
+ errorCount: 0
22
+ });
23
+ }
24
+ },
25
+ trackError: (module, error, context) => {
26
+ const existing = modules.get(module);
27
+ if (existing) {
28
+ existing.errorCount++;
29
+ }
30
+ console.error(`❌ [${module}] Error${context ? ` in ${context}` : ''}:`, error);
31
+ },
32
+ getModuleActivity: module => modules.get(module),
33
+ getAllModules: () => Array.from(modules.values())
34
+ };
35
+ }
36
+ function createCompositionLogger() {
37
+ const logs = [];
38
+ const addLog = (module, type, data) => {
39
+ logs.push({
40
+ timestamp: new Date(),
41
+ module,
42
+ type,
43
+ data
44
+ });
45
+ if (logs.length > 1000) {
46
+ logs.splice(0, logs.length - 1000);
47
+ }
48
+ };
49
+ return {
50
+ logComposition: (modules, action) => {
51
+ addLog('core', 'composition', {
52
+ modules,
53
+ action
54
+ });
55
+ console.log(`🔗 Composition ${action}:`, modules.join(' → '));
56
+ },
57
+ logMethodExecution: (module, method, args, result) => {
58
+ addLog(module, 'method', {
59
+ method,
60
+ args,
61
+ result
62
+ });
63
+ console.debug(`🔧 [${module}] ${method}`, {
64
+ args,
65
+ result
66
+ });
67
+ },
68
+ logStateChange: (module, path, oldValue, newValue) => {
69
+ addLog(module, 'state', {
70
+ path,
71
+ oldValue,
72
+ newValue
73
+ });
74
+ console.debug(`📝 [${module}] State change at ${path}:`, {
75
+ from: oldValue,
76
+ to: newValue
77
+ });
78
+ },
79
+ logPerformanceWarning: (module, operation, duration, threshold) => {
80
+ addLog(module, 'performance', {
81
+ operation,
82
+ duration,
83
+ threshold
84
+ });
85
+ console.warn(`⚠️ [${module}] Slow ${operation}: ${duration.toFixed(2)}ms (threshold: ${threshold}ms)`);
86
+ },
87
+ exportLogs: () => [...logs]
88
+ };
89
+ }
90
+ function createNoopLogger() {
91
+ return {
92
+ logComposition: () => {},
93
+ logMethodExecution: () => {},
94
+ logStateChange: () => {},
95
+ logPerformanceWarning: () => {},
96
+ exportLogs: () => []
97
+ };
98
+ }
99
+ function createModularMetrics() {
100
+ const metricsSignal = signal({
101
+ totalUpdates: 0,
102
+ moduleUpdates: {},
103
+ modulePerformance: {},
104
+ compositionChain: [],
105
+ signalGrowth: {},
106
+ memoryDelta: {},
107
+ moduleCacheStats: {}
108
+ });
109
+ return {
110
+ signal: metricsSignal.asReadonly(),
111
+ updateMetrics: updates => {
112
+ metricsSignal.update(current => ({
113
+ ...current,
114
+ ...updates
115
+ }));
116
+ },
117
+ trackModuleUpdate: (module, duration) => {
118
+ metricsSignal.update(current => ({
119
+ ...current,
120
+ totalUpdates: current.totalUpdates + 1,
121
+ moduleUpdates: {
122
+ ...current.moduleUpdates,
123
+ [module]: (current.moduleUpdates[module] || 0) + 1
124
+ },
125
+ modulePerformance: {
126
+ ...current.modulePerformance,
127
+ [module]: duration
128
+ }
129
+ }));
130
+ }
131
+ };
132
+ }
133
+ function devTools(config = {}) {
134
+ const {
135
+ enabled = true,
136
+ treeName = 'SignalTree',
137
+ name,
138
+ enableBrowserDevTools = true,
139
+ enableLogging = true,
140
+ performanceThreshold = 16
141
+ } = config;
142
+ const displayName = name ?? treeName;
143
+ return tree => {
144
+ if (!enabled) {
145
+ const noopMethods = {
146
+ connectDevTools() {},
147
+ disconnectDevTools() {}
148
+ };
149
+ return Object.assign(tree, noopMethods);
150
+ }
151
+ const activityTracker = createActivityTracker();
152
+ const logger = enableLogging ? createCompositionLogger() : createNoopLogger();
153
+ const metrics = createModularMetrics();
154
+ const compositionHistory = [];
155
+ const activeProfiles = new Map();
156
+ let browserDevTools = null;
157
+ const initBrowserDevTools = () => {
158
+ if (!enableBrowserDevTools || typeof window === 'undefined' || !('__REDUX_DEVTOOLS_EXTENSION__' in window)) {
159
+ return;
160
+ }
161
+ try {
162
+ const devToolsExt = window['__REDUX_DEVTOOLS_EXTENSION__'];
163
+ const connection = devToolsExt.connect({
164
+ name: displayName,
165
+ features: {
166
+ dispatch: true,
167
+ jump: true,
168
+ skip: true
169
+ }
170
+ });
171
+ browserDevTools = {
172
+ send: connection.send
173
+ };
174
+ browserDevTools.send('@@INIT', tree());
175
+ console.log(`🔗 Connected to Redux DevTools as "${displayName}"`);
176
+ } catch (e) {
177
+ console.warn('[SignalTree] Failed to connect to Redux DevTools:', e);
178
+ }
179
+ };
180
+ const originalTreeCall = tree.bind(tree);
181
+ const enhancedTree = function (...args) {
182
+ if (args.length === 0) {
183
+ return originalTreeCall();
184
+ }
185
+ const startTime = performance.now();
186
+ let result;
187
+ if (args.length === 1) {
188
+ const arg = args[0];
189
+ if (typeof arg === 'function') {
190
+ result = originalTreeCall(arg);
191
+ } else {
192
+ result = originalTreeCall(arg);
193
+ }
194
+ }
195
+ const duration = performance.now() - startTime;
196
+ const newState = originalTreeCall();
197
+ metrics.trackModuleUpdate('core', duration);
198
+ if (duration > performanceThreshold) {
199
+ logger.logPerformanceWarning('core', 'update', duration, performanceThreshold);
200
+ }
201
+ if (browserDevTools) {
202
+ browserDevTools.send('UPDATE', newState);
203
+ }
204
+ return result;
205
+ };
206
+ Object.setPrototypeOf(enhancedTree, Object.getPrototypeOf(tree));
207
+ copyTreeProperties(tree, enhancedTree);
208
+ Object.defineProperty(enhancedTree, 'with', {
209
+ value: function (enhancer) {
210
+ if (typeof enhancer !== 'function') {
211
+ throw new Error('Enhancer must be a function');
212
+ }
213
+ return enhancer(enhancedTree);
214
+ },
215
+ writable: false,
216
+ enumerable: false,
217
+ configurable: true
218
+ });
219
+ if ('state' in tree) {
220
+ Object.defineProperty(enhancedTree, 'state', {
221
+ value: tree.state,
222
+ enumerable: false,
223
+ configurable: true
224
+ });
225
+ }
226
+ if ('$' in tree) {
227
+ Object.defineProperty(enhancedTree, '$', {
228
+ value: tree.$,
229
+ enumerable: false,
230
+ configurable: true
231
+ });
232
+ }
233
+ const devToolsInterface = {
234
+ activityTracker,
235
+ logger,
236
+ metrics: metrics.signal,
237
+ trackComposition: modules => {
238
+ compositionHistory.push({
239
+ timestamp: new Date(),
240
+ chain: [...modules]
241
+ });
242
+ metrics.updateMetrics({
243
+ compositionChain: modules
244
+ });
245
+ logger.logComposition(modules, 'with');
246
+ },
247
+ startModuleProfiling: module => {
248
+ const profileId = `${module}_${Date.now()}`;
249
+ activeProfiles.set(profileId, {
250
+ module,
251
+ operation: 'profile',
252
+ startTime: performance.now()
253
+ });
254
+ return profileId;
255
+ },
256
+ endModuleProfiling: profileId => {
257
+ const profile = activeProfiles.get(profileId);
258
+ if (profile) {
259
+ const duration = performance.now() - profile.startTime;
260
+ activityTracker.trackMethodCall(profile.module, profile.operation, duration);
261
+ activeProfiles.delete(profileId);
262
+ }
263
+ },
264
+ connectDevTools: name => {
265
+ if (browserDevTools) {
266
+ browserDevTools.send('@@INIT', originalTreeCall());
267
+ console.log(`🔗 Connected to Redux DevTools as "${name}"`);
268
+ }
269
+ },
270
+ exportDebugSession: () => ({
271
+ metrics: metrics.signal(),
272
+ modules: activityTracker.getAllModules(),
273
+ logs: logger.exportLogs(),
274
+ compositionHistory: [...compositionHistory]
275
+ })
276
+ };
277
+ const methods = {
278
+ connectDevTools() {
279
+ initBrowserDevTools();
280
+ },
281
+ disconnectDevTools() {
282
+ browserDevTools = null;
283
+ }
284
+ };
285
+ enhancedTree['__devTools'] = devToolsInterface;
286
+ return Object.assign(enhancedTree, methods);
287
+ };
288
+ }
289
+ function enableDevTools(treeName = 'SignalTree') {
290
+ return devTools({
291
+ treeName,
292
+ enabled: true
293
+ });
294
+ }
295
+ function fullDevTools(treeName = 'SignalTree') {
296
+ return devTools({
297
+ treeName,
298
+ enabled: true,
299
+ enableBrowserDevTools: true,
300
+ enableLogging: true,
301
+ performanceThreshold: 10
302
+ });
303
+ }
304
+ function productionDevTools() {
305
+ return devTools({
306
+ enabled: true,
307
+ enableBrowserDevTools: false,
308
+ enableLogging: false,
309
+ performanceThreshold: 50
310
+ });
311
+ }
312
+ Object.assign(devTools, {
313
+ production: productionDevTools,
314
+ full: fullDevTools,
315
+ enable: enableDevTools
316
+ });
317
+
318
+ export { devTools, enableDevTools, fullDevTools, productionDevTools };
@@ -0,0 +1,66 @@
1
+ import { effect, untracked } from '@angular/core';
2
+
3
+ function effects(config = {}) {
4
+ const {
5
+ enabled = true
6
+ } = config;
7
+ return tree => {
8
+ const cleanupFns = [];
9
+ const methods = {
10
+ effect(effectFn) {
11
+ if (!enabled) {
12
+ return () => {};
13
+ }
14
+ let innerCleanup;
15
+ const effectRef = effect(() => {
16
+ const state = tree();
17
+ if (innerCleanup) {
18
+ untracked(() => innerCleanup());
19
+ }
20
+ innerCleanup = untracked(() => effectFn(state));
21
+ });
22
+ const cleanup = () => {
23
+ if (innerCleanup) {
24
+ innerCleanup();
25
+ }
26
+ effectRef.destroy();
27
+ };
28
+ cleanupFns.push(cleanup);
29
+ return cleanup;
30
+ },
31
+ subscribe(fn) {
32
+ if (!enabled) {
33
+ return () => {};
34
+ }
35
+ const effectRef = effect(() => {
36
+ const state = tree();
37
+ untracked(() => fn(state));
38
+ });
39
+ const cleanup = () => {
40
+ effectRef.destroy();
41
+ };
42
+ cleanupFns.push(cleanup);
43
+ return cleanup;
44
+ }
45
+ };
46
+ const originalDestroy = tree.destroy?.bind(tree);
47
+ tree.destroy = () => {
48
+ cleanupFns.forEach(fn => fn());
49
+ cleanupFns.length = 0;
50
+ if (originalDestroy) {
51
+ originalDestroy();
52
+ }
53
+ };
54
+ return Object.assign(tree, methods);
55
+ };
56
+ }
57
+ function enableEffects() {
58
+ return effects({
59
+ enabled: true
60
+ });
61
+ }
62
+ Object.assign((config = {}) => effects(config), {
63
+ enable: enableEffects
64
+ });
65
+
66
+ export { effects, enableEffects };
@@ -0,0 +1,7 @@
1
+ function entities(config = {}) {
2
+ throw new Error('entities() has been removed. Remove `.with(entities())` from your code; v7+ auto-processes EntityMap markers.');
3
+ }
4
+ const enableEntities = entities;
5
+ const highPerformanceEntities = entities;
6
+
7
+ export { enableEntities, entities, highPerformanceEntities };