@signaltree/enterprise 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.
@@ -0,0 +1 @@
1
+ export * from "./src/index";
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export { ChangeType, DiffEngine } from './lib/diff-engine.js';
2
+ export { PathIndex } from './lib/path-index.js';
3
+ export { OptimizedUpdateEngine } from './lib/update-engine.js';
4
+ export { withEnterprise } from './lib/enterprise-enhancer.js';
5
+ export { postTask } from './lib/scheduler.js';
6
+ export { createMockPool } from './lib/thread-pools.js';
@@ -0,0 +1,155 @@
1
+ var ChangeType;
2
+ (function (ChangeType) {
3
+ ChangeType["ADD"] = "add";
4
+ ChangeType["UPDATE"] = "update";
5
+ ChangeType["DELETE"] = "delete";
6
+ ChangeType["REPLACE"] = "replace";
7
+ })(ChangeType || (ChangeType = {}));
8
+ class DiffEngine {
9
+ constructor() {
10
+ this.defaultOptions = {
11
+ maxDepth: 100,
12
+ detectDeletions: false,
13
+ ignoreArrayOrder: false,
14
+ equalityFn: (a, b) => a === b,
15
+ keyValidator: undefined
16
+ };
17
+ }
18
+ diff(current, updates, options = {}) {
19
+ const opts = Object.assign(Object.assign({}, this.defaultOptions), options);
20
+ const changes = [];
21
+ const visited = new WeakSet();
22
+ this.traverse(current, updates, [], changes, visited, opts, 0);
23
+ return {
24
+ changes,
25
+ hasChanges: changes.length > 0
26
+ };
27
+ }
28
+ traverse(curr, upd, path, changes, visited, opts, depth) {
29
+ if (depth > opts.maxDepth) {
30
+ return;
31
+ }
32
+ if (typeof upd !== 'object' || upd === null) {
33
+ if (!opts.equalityFn(curr, upd)) {
34
+ changes.push({
35
+ type: curr === undefined ? ChangeType.ADD : ChangeType.UPDATE,
36
+ path: [...path],
37
+ value: upd,
38
+ oldValue: curr
39
+ });
40
+ }
41
+ return;
42
+ }
43
+ if (visited.has(upd)) {
44
+ return;
45
+ }
46
+ visited.add(upd);
47
+ if (Array.isArray(upd)) {
48
+ this.diffArrays(curr, upd, path, changes, visited, opts, depth);
49
+ return;
50
+ }
51
+ if (!curr || typeof curr !== 'object' || Array.isArray(curr)) {
52
+ changes.push({
53
+ type: ChangeType.REPLACE,
54
+ path: [...path],
55
+ value: upd,
56
+ oldValue: curr
57
+ });
58
+ return;
59
+ }
60
+ const currObj = curr;
61
+ const updObj = upd;
62
+ for (const key in updObj) {
63
+ if (Object.prototype.hasOwnProperty.call(updObj, key)) {
64
+ if (opts.keyValidator && !opts.keyValidator(key)) {
65
+ continue;
66
+ }
67
+ this.traverse(currObj[key], updObj[key], [...path, key], changes, visited, opts, depth + 1);
68
+ }
69
+ }
70
+ if (opts.detectDeletions) {
71
+ for (const key in currObj) {
72
+ if (Object.prototype.hasOwnProperty.call(currObj, key) && !(key in updObj)) {
73
+ changes.push({
74
+ type: ChangeType.DELETE,
75
+ path: [...path, key],
76
+ oldValue: currObj[key]
77
+ });
78
+ }
79
+ }
80
+ }
81
+ }
82
+ diffArrays(curr, upd, path, changes, visited, opts, depth) {
83
+ if (!Array.isArray(curr)) {
84
+ changes.push({
85
+ type: ChangeType.REPLACE,
86
+ path: [...path],
87
+ value: upd,
88
+ oldValue: curr
89
+ });
90
+ return;
91
+ }
92
+ if (opts.ignoreArrayOrder) {
93
+ this.diffArraysUnordered(curr, upd, path, changes, opts);
94
+ } else {
95
+ this.diffArraysOrdered(curr, upd, path, changes, visited, opts, depth);
96
+ }
97
+ }
98
+ diffArraysOrdered(curr, upd, path, changes, visited, opts, depth) {
99
+ const maxLength = Math.max(curr.length, upd.length);
100
+ for (let i = 0; i < maxLength; i++) {
101
+ if (i >= upd.length) {
102
+ if (opts.detectDeletions) {
103
+ changes.push({
104
+ type: ChangeType.DELETE,
105
+ path: [...path, i],
106
+ oldValue: curr[i]
107
+ });
108
+ }
109
+ } else if (i >= curr.length) {
110
+ changes.push({
111
+ type: ChangeType.ADD,
112
+ path: [...path, i],
113
+ value: upd[i]
114
+ });
115
+ } else {
116
+ this.traverse(curr[i], upd[i], [...path, i], changes, visited, opts, depth + 1);
117
+ }
118
+ }
119
+ }
120
+ diffArraysUnordered(curr, upd, path, changes, opts) {
121
+ const currSet = new Set(curr.map(v => this.stringify(v)));
122
+ const updSet = new Set(upd.map(v => this.stringify(v)));
123
+ upd.forEach((value, index) => {
124
+ const str = this.stringify(value);
125
+ if (!currSet.has(str)) {
126
+ changes.push({
127
+ type: ChangeType.ADD,
128
+ path: [...path, index],
129
+ value
130
+ });
131
+ }
132
+ });
133
+ if (opts.detectDeletions) {
134
+ curr.forEach((value, index) => {
135
+ const str = this.stringify(value);
136
+ if (!updSet.has(str)) {
137
+ changes.push({
138
+ type: ChangeType.DELETE,
139
+ path: [...path, index],
140
+ oldValue: value
141
+ });
142
+ }
143
+ });
144
+ }
145
+ }
146
+ stringify(value) {
147
+ try {
148
+ return JSON.stringify(value);
149
+ } catch (_a) {
150
+ return String(value);
151
+ }
152
+ }
153
+ }
154
+
155
+ export { ChangeType, DiffEngine };
@@ -0,0 +1,28 @@
1
+ import { PathIndex } from './path-index.js';
2
+ import { OptimizedUpdateEngine } from './update-engine.js';
3
+
4
+ function withEnterprise() {
5
+ return tree => {
6
+ let pathIndex = null;
7
+ let updateEngine = null;
8
+ const signalTree = tree;
9
+ const enhancedTree = tree;
10
+ enhancedTree.updateOptimized = (updates, options) => {
11
+ if (!updateEngine) {
12
+ pathIndex = new PathIndex();
13
+ pathIndex.buildFromTree(signalTree.state);
14
+ updateEngine = new OptimizedUpdateEngine(signalTree.state);
15
+ }
16
+ const result = updateEngine.update(signalTree.state, updates, options);
17
+ if (result.changed && result.stats && pathIndex) {
18
+ pathIndex.clear();
19
+ pathIndex.buildFromTree(signalTree.state);
20
+ }
21
+ return result;
22
+ };
23
+ enhancedTree.getPathIndex = () => pathIndex;
24
+ return enhancedTree;
25
+ };
26
+ }
27
+
28
+ export { withEnterprise };
@@ -0,0 +1,159 @@
1
+ import { isSignal } from '@angular/core';
2
+
3
+ class TrieNode {
4
+ constructor() {
5
+ this.value = null;
6
+ this.children = new Map();
7
+ }
8
+ }
9
+ class PathIndex {
10
+ constructor() {
11
+ this.root = new TrieNode();
12
+ this.pathCache = new Map();
13
+ this.stats = {
14
+ hits: 0,
15
+ misses: 0,
16
+ sets: 0,
17
+ cleanups: 0
18
+ };
19
+ }
20
+ set(path, signal) {
21
+ const pathStr = this.pathToString(path);
22
+ const ref = new WeakRef(signal);
23
+ let node = this.root;
24
+ for (const segment of path) {
25
+ const key = String(segment);
26
+ if (!node.children.has(key)) {
27
+ node.children.set(key, new TrieNode());
28
+ }
29
+ const nextNode = node.children.get(key);
30
+ if (!nextNode) {
31
+ throw new Error(`Failed to get node for key: ${key}`);
32
+ }
33
+ node = nextNode;
34
+ }
35
+ node.value = ref;
36
+ this.pathCache.set(pathStr, ref);
37
+ this.stats.sets++;
38
+ }
39
+ get(path) {
40
+ const pathStr = this.pathToString(path);
41
+ const cached = this.pathCache.get(pathStr);
42
+ if (cached) {
43
+ const value = cached.deref();
44
+ if (value) {
45
+ this.stats.hits++;
46
+ return value;
47
+ }
48
+ this.pathCache.delete(pathStr);
49
+ this.stats.cleanups++;
50
+ }
51
+ let node = this.root;
52
+ for (const segment of path) {
53
+ const key = String(segment);
54
+ node = node.children.get(key);
55
+ if (!node) {
56
+ this.stats.misses++;
57
+ return null;
58
+ }
59
+ }
60
+ if (node.value) {
61
+ const value = node.value.deref();
62
+ if (value) {
63
+ this.pathCache.set(pathStr, node.value);
64
+ this.stats.hits++;
65
+ return value;
66
+ }
67
+ node.value = null;
68
+ this.stats.cleanups++;
69
+ }
70
+ this.stats.misses++;
71
+ return null;
72
+ }
73
+ has(path) {
74
+ return this.get(path) !== null;
75
+ }
76
+ getByPrefix(prefix) {
77
+ const results = new Map();
78
+ let node = this.root;
79
+ for (const segment of prefix) {
80
+ const key = String(segment);
81
+ node = node.children.get(key);
82
+ if (!node) {
83
+ return results;
84
+ }
85
+ }
86
+ this.collectDescendants(node, [], results);
87
+ return results;
88
+ }
89
+ delete(path) {
90
+ const pathStr = this.pathToString(path);
91
+ this.pathCache.delete(pathStr);
92
+ let node = this.root;
93
+ const nodes = [node];
94
+ for (const segment of path) {
95
+ const key = String(segment);
96
+ node = node.children.get(key);
97
+ if (!node) {
98
+ return false;
99
+ }
100
+ nodes.push(node);
101
+ }
102
+ const hadValue = node.value !== null;
103
+ node.value = null;
104
+ for (let i = nodes.length - 1; i > 0; i--) {
105
+ const current = nodes[i];
106
+ if (current.value === null && current.children.size === 0) {
107
+ const parent = nodes[i - 1];
108
+ const segment = path[i - 1];
109
+ parent.children.delete(String(segment));
110
+ } else {
111
+ break;
112
+ }
113
+ }
114
+ return hadValue;
115
+ }
116
+ clear() {
117
+ this.root = new TrieNode();
118
+ this.pathCache.clear();
119
+ }
120
+ getStats() {
121
+ const total = this.stats.hits + this.stats.misses;
122
+ const hitRate = total > 0 ? this.stats.hits / total : 0;
123
+ return Object.assign(Object.assign({}, this.stats), {
124
+ hitRate,
125
+ cacheSize: this.pathCache.size
126
+ });
127
+ }
128
+ buildFromTree(tree, path = []) {
129
+ if (!tree) {
130
+ return;
131
+ }
132
+ if (isSignal(tree)) {
133
+ this.set(path, tree);
134
+ return;
135
+ }
136
+ if (typeof tree !== 'object') {
137
+ return;
138
+ }
139
+ for (const [key, value] of Object.entries(tree)) {
140
+ this.buildFromTree(value, [...path, key]);
141
+ }
142
+ }
143
+ pathToString(path) {
144
+ return path.join('.');
145
+ }
146
+ collectDescendants(node, currentPath, results) {
147
+ if (node.value) {
148
+ const value = node.value.deref();
149
+ if (value) {
150
+ results.set(this.pathToString(currentPath), value);
151
+ }
152
+ }
153
+ for (const [key, child] of node.children) {
154
+ this.collectDescendants(child, [...currentPath, key], results);
155
+ }
156
+ }
157
+ }
158
+
159
+ export { PathIndex };
@@ -0,0 +1,22 @@
1
+ import { __awaiter } from 'tslib';
2
+
3
+ const q = [];
4
+ function postTask(t) {
5
+ q.push(t);
6
+ if (q.length === 1) flush();
7
+ }
8
+ function flush() {
9
+ return __awaiter(this, void 0, void 0, function* () {
10
+ while (q.length) {
11
+ const t = q.shift();
12
+ try {
13
+ t();
14
+ } catch (e) {
15
+ console.error('[EnterpriseScheduler]', e);
16
+ }
17
+ yield Promise.resolve();
18
+ }
19
+ });
20
+ }
21
+
22
+ export { postTask };
@@ -0,0 +1,13 @@
1
+ import { __awaiter } from 'tslib';
2
+
3
+ function createMockPool() {
4
+ return {
5
+ run(fn, ...args) {
6
+ return __awaiter(this, void 0, void 0, function* () {
7
+ return fn(...args);
8
+ });
9
+ }
10
+ };
11
+ }
12
+
13
+ export { createMockPool };
@@ -0,0 +1,186 @@
1
+ import { isSignal } from '@angular/core';
2
+ import { DiffEngine, ChangeType } from './diff-engine.js';
3
+ import { PathIndex } from './path-index.js';
4
+
5
+ class OptimizedUpdateEngine {
6
+ constructor(tree) {
7
+ this.pathIndex = new PathIndex();
8
+ this.diffEngine = new DiffEngine();
9
+ this.pathIndex.buildFromTree(tree);
10
+ }
11
+ update(tree, updates, options = {}) {
12
+ const startTime = performance.now();
13
+ const diffOptions = {};
14
+ if (options.maxDepth !== undefined) diffOptions.maxDepth = options.maxDepth;
15
+ if (options.ignoreArrayOrder !== undefined) diffOptions.ignoreArrayOrder = options.ignoreArrayOrder;
16
+ if (options.equalityFn !== undefined) diffOptions.equalityFn = options.equalityFn;
17
+ const diff = this.diffEngine.diff(tree, updates, diffOptions);
18
+ if (diff.changes.length === 0) {
19
+ return {
20
+ changed: false,
21
+ duration: performance.now() - startTime,
22
+ changedPaths: []
23
+ };
24
+ }
25
+ const patches = this.createPatches(diff.changes);
26
+ const sortedPatches = this.sortPatches(patches);
27
+ const result = options.batch ? this.batchApplyPatches(tree, sortedPatches, options.batchSize) : this.applyPatches(tree, sortedPatches);
28
+ const duration = performance.now() - startTime;
29
+ return {
30
+ changed: true,
31
+ duration,
32
+ changedPaths: result.appliedPaths,
33
+ stats: {
34
+ totalPaths: diff.changes.length,
35
+ optimizedPaths: patches.length,
36
+ batchedUpdates: result.batchCount
37
+ }
38
+ };
39
+ }
40
+ rebuildIndex(tree) {
41
+ this.pathIndex.clear();
42
+ this.pathIndex.buildFromTree(tree);
43
+ }
44
+ getIndexStats() {
45
+ return this.pathIndex.getStats();
46
+ }
47
+ createPatches(changes) {
48
+ const patches = [];
49
+ const processedPaths = new Set();
50
+ for (const change of changes) {
51
+ const pathStr = change.path.join('.');
52
+ let skipPath = false;
53
+ for (const processed of processedPaths) {
54
+ if (pathStr.startsWith(processed + '.')) {
55
+ skipPath = true;
56
+ break;
57
+ }
58
+ }
59
+ if (skipPath) {
60
+ continue;
61
+ }
62
+ const patch = this.createPatch(change);
63
+ patches.push(patch);
64
+ processedPaths.add(pathStr);
65
+ if (change.type === ChangeType.REPLACE && typeof change.value === 'object') {
66
+ processedPaths.add(pathStr);
67
+ }
68
+ }
69
+ return patches;
70
+ }
71
+ createPatch(change) {
72
+ return {
73
+ type: change.type,
74
+ path: change.path,
75
+ value: change.value,
76
+ oldValue: change.oldValue,
77
+ priority: this.calculatePriority(change),
78
+ signal: this.pathIndex.get(change.path)
79
+ };
80
+ }
81
+ calculatePriority(change) {
82
+ let priority = 0;
83
+ priority += (10 - change.path.length) * 10;
84
+ if (change.path.some(p => typeof p === 'number')) {
85
+ priority -= 20;
86
+ }
87
+ if (change.type === ChangeType.REPLACE) {
88
+ priority += 30;
89
+ }
90
+ return priority;
91
+ }
92
+ sortPatches(patches) {
93
+ return patches.sort((a, b) => {
94
+ if (a.priority !== b.priority) {
95
+ return b.priority - a.priority;
96
+ }
97
+ return a.path.length - b.path.length;
98
+ });
99
+ }
100
+ applyPatches(tree, patches) {
101
+ const appliedPaths = [];
102
+ let updateCount = 0;
103
+ for (const patch of patches) {
104
+ if (this.applyPatch(patch, tree)) {
105
+ appliedPaths.push(patch.path.join('.'));
106
+ updateCount++;
107
+ }
108
+ }
109
+ return {
110
+ appliedPaths,
111
+ updateCount,
112
+ batchCount: 1
113
+ };
114
+ }
115
+ batchApplyPatches(tree, patches, batchSize = 50) {
116
+ const batches = [];
117
+ for (let i = 0; i < patches.length; i += batchSize) {
118
+ batches.push(patches.slice(i, i + batchSize));
119
+ }
120
+ const appliedPaths = [];
121
+ let updateCount = 0;
122
+ for (const currentBatch of batches) {
123
+ for (const patch of currentBatch) {
124
+ if (this.applyPatch(patch, tree)) {
125
+ appliedPaths.push(patch.path.join('.'));
126
+ updateCount++;
127
+ }
128
+ }
129
+ }
130
+ return {
131
+ appliedPaths,
132
+ updateCount,
133
+ batchCount: batches.length
134
+ };
135
+ }
136
+ applyPatch(patch, tree) {
137
+ try {
138
+ if (patch.signal && isSignal(patch.signal) && 'set' in patch.signal) {
139
+ const currentValue = patch.signal();
140
+ if (this.isEqual(currentValue, patch.value)) {
141
+ return false;
142
+ }
143
+ patch.signal.set(patch.value);
144
+ if (patch.type === ChangeType.ADD && patch.value !== undefined) {
145
+ this.pathIndex.set(patch.path, patch.signal);
146
+ }
147
+ return true;
148
+ }
149
+ let current = tree;
150
+ for (let i = 0; i < patch.path.length - 1; i++) {
151
+ const key = patch.path[i];
152
+ current = current[key];
153
+ if (!current || typeof current !== 'object') {
154
+ return false;
155
+ }
156
+ }
157
+ const lastKey = patch.path[patch.path.length - 1];
158
+ if (this.isEqual(current[lastKey], patch.value)) {
159
+ return false;
160
+ }
161
+ current[lastKey] = patch.value;
162
+ return true;
163
+ } catch (error) {
164
+ console.error(`Failed to apply patch at ${patch.path.join('.')}:`, error);
165
+ return false;
166
+ }
167
+ }
168
+ isEqual(a, b) {
169
+ if (a === b) {
170
+ return true;
171
+ }
172
+ if (typeof a !== typeof b) {
173
+ return false;
174
+ }
175
+ if (typeof a !== 'object' || a === null || b === null) {
176
+ return false;
177
+ }
178
+ try {
179
+ return JSON.stringify(a) === JSON.stringify(b);
180
+ } catch (_a) {
181
+ return false;
182
+ }
183
+ }
184
+ }
185
+
186
+ export { OptimizedUpdateEngine };
package/package.json CHANGED
@@ -1,17 +1,47 @@
1
1
  {
2
2
  "name": "@signaltree/enterprise",
3
- "version": "4.0.15",
3
+ "version": "4.1.0",
4
4
  "description": "Enterprise-grade optimizations for SignalTree. Provides diff-based updates, bulk operation optimization, and advanced change tracking for large-scale applications.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
- "main": "./src/index.js",
8
- "module": "./src/index.js",
7
+ "main": "./dist/index.js",
8
+ "module": "./dist/index.js",
9
9
  "types": "./src/index.d.ts",
10
10
  "exports": {
11
11
  ".": {
12
- "import": "./src/index.js",
13
- "require": "./src/index.js",
12
+ "import": "./dist/index.js",
13
+ "default": "./dist/index.js",
14
14
  "types": "./src/index.d.ts"
15
+ },
16
+ "./diff-engine": {
17
+ "import": "./dist/lib/diff-engine.js",
18
+ "default": "./dist/lib/diff-engine.js",
19
+ "types": "./src/lib/diff-engine.d.ts"
20
+ },
21
+ "./path-index": {
22
+ "import": "./dist/lib/path-index.js",
23
+ "default": "./dist/lib/path-index.js",
24
+ "types": "./src/lib/path-index.d.ts"
25
+ },
26
+ "./update-engine": {
27
+ "import": "./dist/lib/update-engine.js",
28
+ "default": "./dist/lib/update-engine.js",
29
+ "types": "./src/lib/update-engine.d.ts"
30
+ },
31
+ "./enterprise-enhancer": {
32
+ "import": "./dist/lib/enterprise-enhancer.js",
33
+ "default": "./dist/lib/enterprise-enhancer.js",
34
+ "types": "./src/lib/enterprise-enhancer.d.ts"
35
+ },
36
+ "./scheduler": {
37
+ "import": "./dist/lib/scheduler.js",
38
+ "default": "./dist/lib/scheduler.js",
39
+ "types": "./src/lib/scheduler.d.ts"
40
+ },
41
+ "./thread-pools": {
42
+ "import": "./dist/lib/thread-pools.js",
43
+ "default": "./dist/lib/thread-pools.js",
44
+ "types": "./src/lib/thread-pools.d.ts"
15
45
  }
16
46
  },
17
47
  "keywords": [
@@ -38,17 +68,19 @@
38
68
  },
39
69
  "peerDependencies": {
40
70
  "@angular/core": "^20.3.0",
41
- "@signaltree/core": "^4.0.0"
42
- },
43
- "dependencies": {
44
- "tslib": "^2.3.0"
71
+ "@signaltree/core": "^4.1.0",
72
+ "tslib": "^2.0.0"
45
73
  },
46
74
  "devDependencies": {
47
75
  "@signaltree/core": "workspace:*"
48
76
  },
77
+ "publishConfig": {
78
+ "access": "public"
79
+ },
49
80
  "files": [
81
+ "dist",
50
82
  "src",
51
83
  "README.md",
52
84
  "package.json"
53
85
  ]
54
- }
86
+ }