@signaltree/enterprise 4.0.7 → 4.0.9
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/LICENSE +54 -0
- package/package.json +1 -1
- package/src/lib/diff-engine.spec.ts +384 -0
- package/src/lib/diff-engine.ts +351 -0
- package/src/lib/enterprise-enhancer.ts +136 -0
- package/src/lib/enterprise.spec.ts +7 -0
- package/src/lib/enterprise.ts +3 -0
- package/src/lib/path-index.spec.ts +290 -0
- package/src/lib/path-index.ts +320 -0
- package/src/lib/scheduler.ts +16 -0
- package/src/lib/thread-pools.ts +11 -0
- package/src/lib/update-engine.spec.ts +93 -0
- package/src/lib/update-engine.ts +399 -0
- package/src/test-setup.ts +6 -0
- package/src/types/signaltree-core.d.ts +4 -0
- package/src/index.js +0 -10
- package/src/index.js.map +0 -1
- package/src/lib/diff-engine.d.ts +0 -108
- package/src/lib/diff-engine.js +0 -236
- package/src/lib/diff-engine.js.map +0 -1
- package/src/lib/enterprise-enhancer.d.ts +0 -81
- package/src/lib/enterprise-enhancer.js +0 -78
- package/src/lib/enterprise-enhancer.js.map +0 -1
- package/src/lib/enterprise.d.ts +0 -1
- package/src/lib/enterprise.js +0 -7
- package/src/lib/enterprise.js.map +0 -1
- package/src/lib/path-index.d.ts +0 -119
- package/src/lib/path-index.js +0 -265
- package/src/lib/path-index.js.map +0 -1
- package/src/lib/scheduler.d.ts +0 -2
- package/src/lib/scheduler.js +0 -25
- package/src/lib/scheduler.js.map +0 -1
- package/src/lib/thread-pools.d.ts +0 -4
- package/src/lib/thread-pools.js +0 -14
- package/src/lib/thread-pools.js.map +0 -1
- package/src/lib/update-engine.d.ts +0 -115
- package/src/lib/update-engine.js +0 -287
- package/src/lib/update-engine.js.map +0 -1
- package/src/test-setup.d.ts +0 -1
- package/src/test-setup.js +0 -8
- package/src/test-setup.js.map +0 -1
- /package/src/{index.d.ts → index.ts} +0 -0
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DiffEngine - Efficient change detection for tree updates
|
|
3
|
+
* @packageDocumentation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Path } from './path-index';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Type of change detected
|
|
10
|
+
*/
|
|
11
|
+
export enum ChangeType {
|
|
12
|
+
ADD = 'add',
|
|
13
|
+
UPDATE = 'update',
|
|
14
|
+
DELETE = 'delete',
|
|
15
|
+
REPLACE = 'replace',
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* A detected change
|
|
20
|
+
*/
|
|
21
|
+
export interface Change {
|
|
22
|
+
/** Type of change */
|
|
23
|
+
type: ChangeType;
|
|
24
|
+
|
|
25
|
+
/** Path to the changed value */
|
|
26
|
+
path: Path;
|
|
27
|
+
|
|
28
|
+
/** New value */
|
|
29
|
+
value?: unknown;
|
|
30
|
+
|
|
31
|
+
/** Old value (for updates/deletes) */
|
|
32
|
+
oldValue?: unknown;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Diff result
|
|
37
|
+
*/
|
|
38
|
+
export interface Diff {
|
|
39
|
+
/** List of changes */
|
|
40
|
+
changes: Change[];
|
|
41
|
+
|
|
42
|
+
/** Whether any changes were detected */
|
|
43
|
+
hasChanges: boolean;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Configuration for diff operation
|
|
48
|
+
*/
|
|
49
|
+
export interface DiffOptions {
|
|
50
|
+
/** Maximum depth to traverse */
|
|
51
|
+
maxDepth?: number;
|
|
52
|
+
|
|
53
|
+
/** Whether to detect deletions */
|
|
54
|
+
detectDeletions?: boolean;
|
|
55
|
+
|
|
56
|
+
/** Whether to ignore array order */
|
|
57
|
+
ignoreArrayOrder?: boolean;
|
|
58
|
+
|
|
59
|
+
/** Custom equality function */
|
|
60
|
+
equalityFn?: (a: unknown, b: unknown) => boolean;
|
|
61
|
+
|
|
62
|
+
/** Optional key validator for security (e.g., to prevent prototype pollution) */
|
|
63
|
+
keyValidator?: (key: string) => boolean;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Internal resolved options with required fields
|
|
68
|
+
*/
|
|
69
|
+
type ResolvedDiffOptions = Required<Omit<DiffOptions, 'keyValidator'>> & {
|
|
70
|
+
keyValidator?: (key: string) => boolean;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* DiffEngine
|
|
75
|
+
*
|
|
76
|
+
* Efficiently detects changes between two objects to minimize unnecessary updates.
|
|
77
|
+
*
|
|
78
|
+
* Features:
|
|
79
|
+
* - Deep object comparison
|
|
80
|
+
* - Circular reference detection
|
|
81
|
+
* - Configurable equality checking
|
|
82
|
+
* - Array diffing (ordered and unordered)
|
|
83
|
+
* - Path tracking for precise updates
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```ts
|
|
87
|
+
* const engine = new DiffEngine();
|
|
88
|
+
*
|
|
89
|
+
* const current = { user: { name: 'Alice', age: 30 } };
|
|
90
|
+
* const updates = { user: { name: 'Alice', age: 31 } };
|
|
91
|
+
*
|
|
92
|
+
* const diff = engine.diff(current, updates);
|
|
93
|
+
*
|
|
94
|
+
* console.log(diff.changes);
|
|
95
|
+
* // [{ type: 'update', path: ['user', 'age'], value: 31, oldValue: 30 }]
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
export class DiffEngine {
|
|
99
|
+
private defaultOptions: ResolvedDiffOptions = {
|
|
100
|
+
maxDepth: 100,
|
|
101
|
+
detectDeletions: false,
|
|
102
|
+
ignoreArrayOrder: false,
|
|
103
|
+
equalityFn: (a, b) => a === b,
|
|
104
|
+
keyValidator: undefined,
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Diff two objects and return changes
|
|
109
|
+
*
|
|
110
|
+
* @param current - Current state
|
|
111
|
+
* @param updates - Updated state
|
|
112
|
+
* @param options - Diff options
|
|
113
|
+
* @returns Diff result with all changes
|
|
114
|
+
*/
|
|
115
|
+
diff(current: unknown, updates: unknown, options: DiffOptions = {}): Diff {
|
|
116
|
+
const opts = { ...this.defaultOptions, ...options };
|
|
117
|
+
const changes: Change[] = [];
|
|
118
|
+
const visited = new WeakSet();
|
|
119
|
+
|
|
120
|
+
this.traverse(current, updates, [], changes, visited, opts, 0);
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
changes,
|
|
124
|
+
hasChanges: changes.length > 0,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Traverse and compare objects recursively
|
|
130
|
+
*/
|
|
131
|
+
private traverse(
|
|
132
|
+
curr: unknown,
|
|
133
|
+
upd: unknown,
|
|
134
|
+
path: Path,
|
|
135
|
+
changes: Change[],
|
|
136
|
+
visited: WeakSet<object>,
|
|
137
|
+
opts: ResolvedDiffOptions,
|
|
138
|
+
depth: number
|
|
139
|
+
): void {
|
|
140
|
+
// Depth limit
|
|
141
|
+
if (depth > opts.maxDepth) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Handle primitives
|
|
146
|
+
if (typeof upd !== 'object' || upd === null) {
|
|
147
|
+
if (!opts.equalityFn(curr, upd)) {
|
|
148
|
+
changes.push({
|
|
149
|
+
type: curr === undefined ? ChangeType.ADD : ChangeType.UPDATE,
|
|
150
|
+
path: [...path],
|
|
151
|
+
value: upd,
|
|
152
|
+
oldValue: curr,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Circular reference detection
|
|
159
|
+
if (visited.has(upd)) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
visited.add(upd);
|
|
163
|
+
|
|
164
|
+
// Handle arrays
|
|
165
|
+
if (Array.isArray(upd)) {
|
|
166
|
+
this.diffArrays(curr, upd, path, changes, visited, opts, depth);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Handle objects
|
|
171
|
+
if (!curr || typeof curr !== 'object' || Array.isArray(curr)) {
|
|
172
|
+
// Type mismatch - replace entire subtree
|
|
173
|
+
changes.push({
|
|
174
|
+
type: ChangeType.REPLACE,
|
|
175
|
+
path: [...path],
|
|
176
|
+
value: upd,
|
|
177
|
+
oldValue: curr,
|
|
178
|
+
});
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Diff object properties
|
|
183
|
+
const currObj = curr as Record<string, unknown>;
|
|
184
|
+
const updObj = upd as Record<string, unknown>;
|
|
185
|
+
|
|
186
|
+
for (const key in updObj) {
|
|
187
|
+
if (Object.prototype.hasOwnProperty.call(updObj, key)) {
|
|
188
|
+
// Validate key for security (e.g., prevent prototype pollution)
|
|
189
|
+
if (opts.keyValidator && !opts.keyValidator(key)) {
|
|
190
|
+
// Skip dangerous keys silently
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
this.traverse(
|
|
195
|
+
currObj[key],
|
|
196
|
+
updObj[key],
|
|
197
|
+
[...path, key],
|
|
198
|
+
changes,
|
|
199
|
+
visited,
|
|
200
|
+
opts,
|
|
201
|
+
depth + 1
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Check for deletions if enabled
|
|
207
|
+
if (opts.detectDeletions) {
|
|
208
|
+
for (const key in currObj) {
|
|
209
|
+
if (
|
|
210
|
+
Object.prototype.hasOwnProperty.call(currObj, key) &&
|
|
211
|
+
!(key in updObj)
|
|
212
|
+
) {
|
|
213
|
+
changes.push({
|
|
214
|
+
type: ChangeType.DELETE,
|
|
215
|
+
path: [...path, key],
|
|
216
|
+
oldValue: currObj[key],
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Diff arrays
|
|
225
|
+
*/
|
|
226
|
+
private diffArrays(
|
|
227
|
+
curr: unknown,
|
|
228
|
+
upd: unknown[],
|
|
229
|
+
path: Path,
|
|
230
|
+
changes: Change[],
|
|
231
|
+
visited: WeakSet<object>,
|
|
232
|
+
opts: ResolvedDiffOptions,
|
|
233
|
+
depth: number
|
|
234
|
+
): void {
|
|
235
|
+
if (!Array.isArray(curr)) {
|
|
236
|
+
// Not an array - replace
|
|
237
|
+
changes.push({
|
|
238
|
+
type: ChangeType.REPLACE,
|
|
239
|
+
path: [...path],
|
|
240
|
+
value: upd,
|
|
241
|
+
oldValue: curr,
|
|
242
|
+
});
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (opts.ignoreArrayOrder) {
|
|
247
|
+
this.diffArraysUnordered(curr, upd, path, changes, opts);
|
|
248
|
+
} else {
|
|
249
|
+
this.diffArraysOrdered(curr, upd, path, changes, visited, opts, depth);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Diff arrays in order (index-based)
|
|
255
|
+
*/
|
|
256
|
+
private diffArraysOrdered(
|
|
257
|
+
curr: unknown[],
|
|
258
|
+
upd: unknown[],
|
|
259
|
+
path: Path,
|
|
260
|
+
changes: Change[],
|
|
261
|
+
visited: WeakSet<object>,
|
|
262
|
+
opts: ResolvedDiffOptions,
|
|
263
|
+
depth: number
|
|
264
|
+
): void {
|
|
265
|
+
// Check each index
|
|
266
|
+
const maxLength = Math.max(curr.length, upd.length);
|
|
267
|
+
|
|
268
|
+
for (let i = 0; i < maxLength; i++) {
|
|
269
|
+
if (i >= upd.length) {
|
|
270
|
+
// Deletion (if enabled)
|
|
271
|
+
if (opts.detectDeletions) {
|
|
272
|
+
changes.push({
|
|
273
|
+
type: ChangeType.DELETE,
|
|
274
|
+
path: [...path, i],
|
|
275
|
+
oldValue: curr[i],
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
} else if (i >= curr.length) {
|
|
279
|
+
// Addition
|
|
280
|
+
changes.push({
|
|
281
|
+
type: ChangeType.ADD,
|
|
282
|
+
path: [...path, i],
|
|
283
|
+
value: upd[i],
|
|
284
|
+
});
|
|
285
|
+
} else {
|
|
286
|
+
// Potential update
|
|
287
|
+
this.traverse(
|
|
288
|
+
curr[i],
|
|
289
|
+
upd[i],
|
|
290
|
+
[...path, i],
|
|
291
|
+
changes,
|
|
292
|
+
visited,
|
|
293
|
+
opts,
|
|
294
|
+
depth + 1
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Diff arrays ignoring order (value-based)
|
|
302
|
+
*/
|
|
303
|
+
private diffArraysUnordered(
|
|
304
|
+
curr: unknown[],
|
|
305
|
+
upd: unknown[],
|
|
306
|
+
path: Path,
|
|
307
|
+
changes: Change[],
|
|
308
|
+
opts: ResolvedDiffOptions
|
|
309
|
+
): void {
|
|
310
|
+
// Build value sets
|
|
311
|
+
const currSet = new Set(curr.map((v) => this.stringify(v)));
|
|
312
|
+
const updSet = new Set(upd.map((v) => this.stringify(v)));
|
|
313
|
+
|
|
314
|
+
// Find additions
|
|
315
|
+
upd.forEach((value, index) => {
|
|
316
|
+
const str = this.stringify(value);
|
|
317
|
+
if (!currSet.has(str)) {
|
|
318
|
+
changes.push({
|
|
319
|
+
type: ChangeType.ADD,
|
|
320
|
+
path: [...path, index],
|
|
321
|
+
value,
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
// Find deletions (if enabled)
|
|
327
|
+
if (opts.detectDeletions) {
|
|
328
|
+
curr.forEach((value, index) => {
|
|
329
|
+
const str = this.stringify(value);
|
|
330
|
+
if (!updSet.has(str)) {
|
|
331
|
+
changes.push({
|
|
332
|
+
type: ChangeType.DELETE,
|
|
333
|
+
path: [...path, index],
|
|
334
|
+
oldValue: value,
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Stringify value for set comparison
|
|
343
|
+
*/
|
|
344
|
+
private stringify(value: unknown): string {
|
|
345
|
+
try {
|
|
346
|
+
return JSON.stringify(value);
|
|
347
|
+
} catch {
|
|
348
|
+
return String(value);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { PathIndex } from './path-index';
|
|
2
|
+
import { OptimizedUpdateEngine, UpdateResult } from './update-engine';
|
|
3
|
+
|
|
4
|
+
import type { Signal } from '@angular/core';
|
|
5
|
+
import type { Enhancer } from '@signaltree/core';
|
|
6
|
+
/**
|
|
7
|
+
* Enterprise-grade optimizations for large-scale applications.
|
|
8
|
+
*
|
|
9
|
+
* **Includes:**
|
|
10
|
+
* - Diff-based updates (only update changed signals)
|
|
11
|
+
* - Bulk operation optimization (2-5x faster)
|
|
12
|
+
* - Advanced change tracking
|
|
13
|
+
* - Update statistics and monitoring
|
|
14
|
+
*
|
|
15
|
+
* **Use when:**
|
|
16
|
+
* - 500+ signals in state tree
|
|
17
|
+
* - Bulk updates at high frequency (60Hz+)
|
|
18
|
+
* - Real-time dashboards or data feeds
|
|
19
|
+
* - Enterprise-scale applications
|
|
20
|
+
*
|
|
21
|
+
* **Skip when:**
|
|
22
|
+
* - Small to medium apps (<100 signals)
|
|
23
|
+
* - Infrequent state updates
|
|
24
|
+
* - Startup/prototype projects
|
|
25
|
+
*
|
|
26
|
+
* **Bundle cost:** +2.4KB gzipped
|
|
27
|
+
* **Performance gain:** 2-5x faster bulk updates, detailed monitoring
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```typescript
|
|
31
|
+
* import { signalTree } from '@signaltree/core';
|
|
32
|
+
* import { withEnterprise } from '@signaltree/enterprise';
|
|
33
|
+
*
|
|
34
|
+
* const tree = signalTree(largeState).with(withEnterprise());
|
|
35
|
+
*
|
|
36
|
+
* // Now available: optimized bulk updates
|
|
37
|
+
* const result = tree.updateOptimized(newData, {
|
|
38
|
+
* ignoreArrayOrder: true,
|
|
39
|
+
* maxDepth: 10
|
|
40
|
+
* });
|
|
41
|
+
*
|
|
42
|
+
* console.log(result.stats);
|
|
43
|
+
* // { totalChanges: 45, adds: 10, updates: 30, deletes: 5 }
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* @public
|
|
47
|
+
*/
|
|
48
|
+
export function withEnterprise<T extends Record<string, unknown>>(): Enhancer<
|
|
49
|
+
T,
|
|
50
|
+
T & EnterpriseEnhancedTree<T>
|
|
51
|
+
> {
|
|
52
|
+
return (tree: T): T & EnterpriseEnhancedTree<T> => {
|
|
53
|
+
// Lazy initialization - only create when first needed
|
|
54
|
+
let pathIndex: PathIndex<Signal<unknown>> | null = null;
|
|
55
|
+
let updateEngine: OptimizedUpdateEngine | null = null;
|
|
56
|
+
|
|
57
|
+
// Type assertion to access SignalTree properties
|
|
58
|
+
const signalTree = tree as unknown as { state: unknown };
|
|
59
|
+
|
|
60
|
+
// Cast tree to enhanced type for safe property assignment
|
|
61
|
+
const enhancedTree = tree as T & EnterpriseEnhancedTree<T>;
|
|
62
|
+
|
|
63
|
+
// Add updateOptimized method to tree
|
|
64
|
+
enhancedTree.updateOptimized = (
|
|
65
|
+
updates: Partial<T>,
|
|
66
|
+
options?: {
|
|
67
|
+
maxDepth?: number;
|
|
68
|
+
ignoreArrayOrder?: boolean;
|
|
69
|
+
equalityFn?: (a: unknown, b: unknown) => boolean;
|
|
70
|
+
batch?: boolean;
|
|
71
|
+
batchSize?: number;
|
|
72
|
+
}
|
|
73
|
+
): UpdateResult => {
|
|
74
|
+
// Lazy initialize on first use
|
|
75
|
+
if (!updateEngine) {
|
|
76
|
+
pathIndex = new PathIndex<Signal<unknown>>();
|
|
77
|
+
pathIndex.buildFromTree(signalTree.state);
|
|
78
|
+
updateEngine = new OptimizedUpdateEngine(signalTree.state);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const result = updateEngine.update(signalTree.state, updates, options);
|
|
82
|
+
|
|
83
|
+
// Rebuild index if changes were made
|
|
84
|
+
if (result.changed && result.stats && pathIndex) {
|
|
85
|
+
pathIndex.clear();
|
|
86
|
+
pathIndex.buildFromTree(signalTree.state);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return result;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
// Add PathIndex access for debugging/monitoring
|
|
93
|
+
enhancedTree.getPathIndex = () => pathIndex;
|
|
94
|
+
|
|
95
|
+
return enhancedTree;
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Type augmentation for trees enhanced with enterprise features.
|
|
101
|
+
* This is applied when using withEnterprise().
|
|
102
|
+
*
|
|
103
|
+
* @public
|
|
104
|
+
*/
|
|
105
|
+
export interface EnterpriseEnhancedTree<T> {
|
|
106
|
+
/**
|
|
107
|
+
* Optimized bulk update method using diff-based change detection.
|
|
108
|
+
* Only available when using withEnterprise().
|
|
109
|
+
*
|
|
110
|
+
* @param newValue - The new state value
|
|
111
|
+
* @param options - Update options
|
|
112
|
+
* @returns Update result with statistics
|
|
113
|
+
*/
|
|
114
|
+
updateOptimized(
|
|
115
|
+
newValue: T,
|
|
116
|
+
options?: {
|
|
117
|
+
/** Maximum depth to traverse (default: 100) */
|
|
118
|
+
maxDepth?: number;
|
|
119
|
+
/** Ignore array element order (default: false) */
|
|
120
|
+
ignoreArrayOrder?: boolean;
|
|
121
|
+
/** Custom equality function */
|
|
122
|
+
equalityFn?: (a: unknown, b: unknown) => boolean;
|
|
123
|
+
/** Automatically batch updates (default: true) */
|
|
124
|
+
autoBatch?: boolean;
|
|
125
|
+
/** Number of patches per batch (default: 10) */
|
|
126
|
+
batchSize?: number;
|
|
127
|
+
}
|
|
128
|
+
): UpdateResult;
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Get the PathIndex for debugging/monitoring.
|
|
132
|
+
* Only available when using withEnterprise().
|
|
133
|
+
* Returns null if updateOptimized hasn't been called yet (lazy initialization).
|
|
134
|
+
*/
|
|
135
|
+
getPathIndex(): PathIndex<Signal<unknown>> | null;
|
|
136
|
+
}
|